Commit 2dd5a108 authored by Simon Morlat's avatar Simon Morlat

* implement retransmissions of 200Ok and ACKs at dialog level.

* implement RFC6026 for ICT (transaction state machine bugfixes)
parent db034bc8
......@@ -37,7 +37,7 @@ belle_sip_request_t *belle_sip_dialog_create_ack(belle_sip_dialog_t *dialog, uns
belle_sip_request_t *belle_sip_dialog_create_request(belle_sip_dialog_t *dialog, const char *method);
/**
* Create a request within a dialog keeping non system header from an initial request. This function is very usefull to resend request after expiration or chalange.
* Create a request within a dialog keeping non system header from an initial request. This function is very useful to resend request after expiration or chalenge.
* @param obj dialog associated to the request
* @param initial_req, all headers + body are re-used from this request except: Via,From, To, Allows, CSeq, Call-ID, Max-Forwards
*
......
......@@ -32,6 +32,7 @@ const char *belle_sip_listening_point_get_ip_address(const belle_sip_listening_
*/
const belle_sip_uri_t* belle_sip_listening_point_get_uri(const belle_sip_listening_point_t *ip);
int belle_sip_listening_point_is_reliable(const belle_sip_listening_point_t *lp);
void belle_sip_listening_point_clean_channels(belle_sip_listening_point_t *lp);
int belle_sip_listening_point_get_well_known_port(const char *transport);
BELLE_SIP_END_DECLS
......
......@@ -72,7 +72,7 @@ belle_sip_object_t *_belle_sip_message_get_header_by_type_id(const belle_sip_mes
(header_type*)_belle_sip_message_get_header_by_type_id(BELLE_SIP_MESSAGE(msg),BELLE_SIP_TYPE_ID(header_type))
const belle_sip_list_t* belle_sip_message_get_headers(const belle_sip_message_t *message,const char* header_name);
/*
/**
* Get list of all headers present in the message.
* @param message
* @return a newly allocated list of belle_sip_header_t
......
......@@ -50,7 +50,9 @@ void belle_sip_provider_send_request(belle_sip_provider_t *p, belle_sip_request_
void belle_sip_provider_send_response(belle_sip_provider_t *p, belle_sip_response_t *resp);
/*
void belle_sip_provider_clean_channels(belle_sip_provider_t *p);
/**
* Add auth info to the request if found
* @param p object
* @param request to be updated
......@@ -59,7 +61,7 @@ void belle_sip_provider_send_response(belle_sip_provider_t *p, belle_sip_respons
* a newly allocated belle_sip_auth_info_t object is added to this list. These object contains useful information like realm and username.
* @returns 0 in case of success,
*
* */
**/
int belle_sip_provider_add_authorization(belle_sip_provider_t *p, belle_sip_request_t* request,belle_sip_response_t *resp,belle_sip_list_t** auth_infos);
BELLE_SIP_END_DECLS
......
......@@ -605,6 +605,7 @@ struct belle_sip_ict{
belle_sip_source_t *timer_A;
belle_sip_source_t *timer_B;
belle_sip_source_t *timer_D;
belle_sip_source_t *timer_M;
belle_sip_request_t *ack;
};
......@@ -686,12 +687,15 @@ belle_sip_nist_t * belle_sip_nist_new(belle_sip_provider_t *prov, belle_sip_requ
*/
struct belle_sip_dialog{
belle_sip_object_t base;
void *appdata;
belle_sip_provider_t *provider;
belle_sip_request_t *last_out_invite;
belle_sip_request_t *last_out_ack; /*so that it can retransmitted when needed*/
belle_sip_request_t *last_out_ack; /*so that it can be retransmitted when needed*/
belle_sip_response_t *last_200Ok;
belle_sip_source_t *timer_200Ok;
belle_sip_source_t *timer_200Ok_end;
belle_sip_dialog_state_t state;
belle_sip_dialog_state_t previous_state;
void *appdata;
belle_sip_header_call_id_t *call_id;
belle_sip_header_address_t *local_party;
belle_sip_header_address_t *remote_party;
......
......@@ -18,6 +18,9 @@
#include "belle_sip_internal.h"
static void belle_sip_dialog_init_200Ok_retrans(belle_sip_dialog_t *obj, belle_sip_response_t *resp);
static void belle_sip_dialog_stop_200Ok_retrans(belle_sip_dialog_t *obj);
static void belle_sip_dialog_handle_200Ok(belle_sip_dialog_t *obj, belle_sip_response_t *msg);
static void belle_sip_dialog_uninit(belle_sip_dialog_t *obj){
if (obj->route_set)
......@@ -126,6 +129,10 @@ static int belle_sip_dialog_init_as_uas(belle_sip_dialog_t *obj, belle_sip_reque
/*call id already set */
/*remote party already set */
obj->local_party=(belle_sip_header_address_t*)belle_sip_object_ref(to);
if (strcmp(belle_sip_request_get_method(req),"INVITE")==0){
belle_sip_dialog_init_200Ok_retrans(obj,resp);
obj->needs_ack=TRUE;
}
return 0;
}
......@@ -183,15 +190,19 @@ static int belle_sip_dialog_init_as_uac(belle_sip_dialog_t *obj, belle_sip_reque
/*local party is already set*/
if (strcmp(belle_sip_request_get_method(req),"INVITE")==0){
set_last_out_invite(obj,req);
obj->needs_ack=TRUE;
}
return 0;
}
int belle_sip_dialog_establish_full(belle_sip_dialog_t *obj, belle_sip_request_t *req, belle_sip_response_t *resp){
int err;
if (obj->is_server)
return belle_sip_dialog_init_as_uas(obj,req,resp);
err= belle_sip_dialog_init_as_uas(obj,req,resp);
else
return belle_sip_dialog_init_as_uac(obj,req,resp);
err= belle_sip_dialog_init_as_uac(obj,req,resp);
if (err==0) set_state(obj,BELLE_SIP_DIALOG_CONFIRMED);
return err;
}
int belle_sip_dialog_establish(belle_sip_dialog_t *obj, belle_sip_request_t *req, belle_sip_response_t *resp){
......@@ -220,10 +231,9 @@ int belle_sip_dialog_establish(belle_sip_dialog_t *obj, belle_sip_request_t *req
set_to_tag(obj,to);
obj->call_id=(belle_sip_header_call_id_t*)belle_sip_object_ref(call_id);
}
if (belle_sip_dialog_establish_full(obj,req,resp)==0){
set_state(obj,BELLE_SIP_DIALOG_CONFIRMED);
obj->needs_ack=(strcmp("INVITE",belle_sip_request_get_method(req))==0); /*only for invite*/
}else return -1;
if (belle_sip_dialog_establish_full(obj,req,resp)==-1){
return -1;
}
} else if (code>=300 && obj->state!=BELLE_SIP_DIALOG_CONFIRMED) {
/*12.3 Termination of a Dialog
Independent of the method, if a request outside of a dialog generates
......@@ -247,6 +257,59 @@ int belle_sip_dialog_check_incoming_request_ordering(belle_sip_dialog_t *obj, be
return -1;
}
static int dialog_on_200Ok_timer(belle_sip_dialog_t *dialog){
/*reset the timer */
const belle_sip_timer_config_t *cfg=belle_sip_stack_get_timer_config(dialog->provider->stack);
unsigned int prev_timeout=belle_sip_source_get_timeout(dialog->timer_200Ok);
belle_sip_source_set_timeout(dialog->timer_200Ok,MIN(2*prev_timeout,cfg->T2));
belle_sip_message("Dialog sending retransmission of 200Ok");
belle_sip_provider_send_response(dialog->provider,dialog->last_200Ok);
return BELLE_SIP_CONTINUE;
}
static int dialog_on_200Ok_end(belle_sip_dialog_t *dialog){
belle_sip_request_t *bye;
belle_sip_client_transaction_t *trn;
belle_sip_dialog_stop_200Ok_retrans(dialog);
belle_sip_error("Dialog was not ACK'd within T1*64 seconds, it is going to be terminated.");
dialog->state=BELLE_SIP_DIALOG_CONFIRMED;
bye=belle_sip_dialog_create_request(dialog,"BYE");
trn=belle_sip_provider_create_client_transaction(dialog->provider,bye);
belle_sip_client_transaction_send_request(trn);
return BELLE_SIP_STOP;
}
static void belle_sip_dialog_init_200Ok_retrans(belle_sip_dialog_t *obj, belle_sip_response_t *resp){
const belle_sip_timer_config_t *cfg=belle_sip_stack_get_timer_config(obj->provider->stack);
obj->timer_200Ok=belle_sip_timeout_source_new((belle_sip_source_func_t)dialog_on_200Ok_timer,obj,cfg->T1);
belle_sip_object_set_name((belle_sip_object_t*)obj->timer_200Ok,"dialog_200Ok_timer");
belle_sip_main_loop_add_source(obj->provider->stack->ml,obj->timer_200Ok);
obj->timer_200Ok_end=belle_sip_timeout_source_new((belle_sip_source_func_t)dialog_on_200Ok_end,obj,cfg->T1*64);
belle_sip_object_set_name((belle_sip_object_t*)obj->timer_200Ok_end,"dialog_200Ok_timer_end");
belle_sip_main_loop_add_source(obj->provider->stack->ml,obj->timer_200Ok_end);
obj->last_200Ok=(belle_sip_response_t*)belle_sip_object_ref(resp);
}
static void belle_sip_dialog_stop_200Ok_retrans(belle_sip_dialog_t *obj){
belle_sip_main_loop_t *ml=obj->provider->stack->ml;
if (obj->timer_200Ok){
belle_sip_main_loop_remove_source(ml,obj->timer_200Ok);
belle_sip_object_unref(obj->timer_200Ok);
obj->timer_200Ok=NULL;
}
if (obj->timer_200Ok_end){
belle_sip_main_loop_remove_source(ml,obj->timer_200Ok_end);
belle_sip_object_unref(obj->timer_200Ok_end);
obj->timer_200Ok_end=NULL;
}
if (obj->last_200Ok){
belle_sip_object_unref(obj->last_200Ok);
obj->last_200Ok=NULL;
}
}
int belle_sip_dialog_update(belle_sip_dialog_t *obj,belle_sip_request_t *req, belle_sip_response_t *resp, int as_uas){
int code;
switch (obj->state){
......@@ -256,19 +319,25 @@ int belle_sip_dialog_update(belle_sip_dialog_t *obj,belle_sip_request_t *req, be
case BELLE_SIP_DIALOG_CONFIRMED:
code=belle_sip_response_get_status_code(resp);
if (strcmp(belle_sip_request_get_method(req),"INVITE")==0 && code>=200 && code<300){
/*refresh the remote_target*/
belle_sip_header_contact_t *ct;
if (as_uas){
ct=belle_sip_message_get_header_by_type(req,belle_sip_header_contact_t);
}else{
set_last_out_invite(obj,req);
ct=belle_sip_message_get_header_by_type(resp,belle_sip_header_contact_t);
}
if (ct){
belle_sip_object_unref(obj->remote_target);
obj->remote_target=(belle_sip_header_address_t*)belle_sip_object_ref(ct);
if (!obj->needs_ack){
/*case of a target refresh request */
/*refresh the remote_target*/
belle_sip_header_contact_t *ct;
if (as_uas){
ct=belle_sip_message_get_header_by_type(req,belle_sip_header_contact_t);
}else{
set_last_out_invite(obj,req);
ct=belle_sip_message_get_header_by_type(resp,belle_sip_header_contact_t);
}
if (ct){
belle_sip_object_unref(obj->remote_target);
obj->remote_target=(belle_sip_header_address_t*)belle_sip_object_ref(ct);
}
obj->needs_ack=TRUE;
} else {
/*retransmission of 200Ok */
if (!as_uas) belle_sip_dialog_handle_200Ok(obj,resp);
}
obj->needs_ack=TRUE;
}else if (strcmp(belle_sip_request_get_method(req),"BYE")==0 && ((code>=200 && code<300) || code==481 || code==408)){
/*15.1.1 UAC Behavior
......@@ -412,10 +481,10 @@ static unsigned int is_system_header(belle_sip_header_t* header) {
}
static void copy_non_system_headers(belle_sip_header_t* header,belle_sip_request_t* req ) {
if (!is_system_header(header)) {
belle_sip_object_ref(header);
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),header);
}
}
belle_sip_request_t *belle_sip_dialog_create_request_from(belle_sip_dialog_t *obj, const belle_sip_request_t *initial_req){
belle_sip_request_t* req = belle_sip_dialog_create_request(obj, belle_sip_request_get_method(initial_req));
belle_sip_header_content_length_t* content_lenth = belle_sip_message_get_header_by_type(initial_req,belle_sip_header_content_length_t);
......@@ -431,6 +500,7 @@ belle_sip_request_t *belle_sip_dialog_create_request_from(belle_sip_dialog_t *ob
}
void belle_sip_dialog_delete(belle_sip_dialog_t *obj){
belle_sip_dialog_stop_200Ok_retrans(obj); /*if any*/
set_state(obj,BELLE_SIP_DIALOG_TERMINATED);
belle_sip_provider_remove_dialog(obj->provider,obj);
}
......@@ -552,7 +622,7 @@ void belle_sip_dialog_check_ack_sent(belle_sip_dialog_t*obj){
}
}
void belle_sip_dialog_handle_200Ok(belle_sip_dialog_t *obj, belle_sip_message_t *msg){
static void belle_sip_dialog_handle_200Ok(belle_sip_dialog_t *obj, belle_sip_response_t *msg){
if (obj->last_out_ack){
belle_sip_header_cseq_t *cseq=belle_sip_message_get_header_by_type(msg,belle_sip_header_cseq_t);
if (cseq){
......@@ -571,7 +641,9 @@ int belle_sip_dialog_handle_ack(belle_sip_dialog_t *obj, belle_sip_request_t *ac
if (obj->needs_ack && belle_sip_header_cseq_get_seq_number(cseq)==obj->remote_cseq){
belle_sip_message("Incoming INVITE has ACK, dialog is happy");
obj->needs_ack=FALSE;
belle_sip_dialog_stop_200Ok_retrans(obj);
return 0;
}
belle_sip_message("Dialog ignoring incoming ACK (surely a retransmission)");
return -1;
}
......@@ -42,6 +42,11 @@ static void on_ict_terminate(belle_sip_ict_t *obj){
belle_sip_object_unref(obj->timer_D);
obj->timer_D=NULL;
}
if (obj->timer_M){
belle_sip_transaction_stop_timer(base,obj->timer_M);
belle_sip_object_unref(obj->timer_M);
obj->timer_M=NULL;
}
if (obj->ack){
belle_sip_object_unref(obj->ack);
obj->ack=NULL;
......@@ -78,9 +83,18 @@ static int ict_on_timer_D(belle_sip_ict_t *obj){
return BELLE_SIP_STOP;
}
static int ict_on_timer_M(belle_sip_ict_t *obj){
belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj;
if (base->state==BELLE_SIP_TRANSACTION_ACCEPTED){
belle_sip_transaction_terminate(base);
}
return BELLE_SIP_STOP;
}
static void ict_on_response(belle_sip_ict_t *obj, belle_sip_response_t *resp){
belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj;
int code=belle_sip_response_get_status_code(resp);
const belle_sip_timer_config_t *cfg=belle_sip_transaction_get_timer_config(base);
switch (base->state){
case BELLE_SIP_TRANSACTION_CALLING:
......@@ -91,15 +105,22 @@ static void ict_on_response(belle_sip_ict_t *obj, belle_sip_response_t *resp){
base->state=BELLE_SIP_TRANSACTION_COMPLETED;
belle_sip_channel_queue_message(base->channel,(belle_sip_message_t*)make_ack(obj,resp));
belle_sip_client_transaction_notify_response((belle_sip_client_transaction_t*)obj,resp);
obj->timer_D=belle_sip_timeout_source_new((belle_sip_source_func_t)ict_on_timer_D,obj,32000);
obj->timer_D=belle_sip_timeout_source_new((belle_sip_source_func_t)ict_on_timer_D,obj,cfg->T1*64);
belle_sip_transaction_start_timer(base,obj->timer_D);
}else if (code>=200){
belle_sip_transaction_terminate(base);
obj->timer_M=belle_sip_timeout_source_new((belle_sip_source_func_t)ict_on_timer_M,obj,cfg->T1*64);
belle_sip_transaction_start_timer(base,obj->timer_M);
base->state=BELLE_SIP_TRANSACTION_ACCEPTED;
belle_sip_client_transaction_notify_response((belle_sip_client_transaction_t*)obj,resp);
}else if (code>=100){
belle_sip_client_transaction_notify_response((belle_sip_client_transaction_t*)obj,resp);
}
break;
case BELLE_SIP_TRANSACTION_ACCEPTED:
if (code>=200 && code<300){
belle_sip_client_transaction_notify_response((belle_sip_client_transaction_t*)obj,resp);
}
break;
case BELLE_SIP_TRANSACTION_COMPLETED:
if (code>=300 && obj->ack){
belle_sip_channel_queue_message(base->channel,(belle_sip_message_t*)obj->ack);
......
......@@ -28,11 +28,7 @@ void belle_sip_listening_point_init(belle_sip_listening_point_t *lp, belle_sip_s
}
static void belle_sip_listening_point_uninit(belle_sip_listening_point_t *lp){
int existing_channels;
if ((existing_channels=belle_sip_list_size(lp->channels)) > 0) {
belle_sip_warning("Listening point destroying [%i] channels",existing_channels);
}
belle_sip_list_free_with_data(lp->channels,(void (*)(void*))belle_sip_object_unref);
belle_sip_listening_point_clean_channels(lp);
belle_sip_object_unref(lp->listening_uri);
lp->channel_listener=NULL; /*does not unref provider*/
}
......@@ -58,6 +54,14 @@ void belle_sip_listening_point_remove_channel(belle_sip_listening_point_t *lp, b
belle_sip_object_unref(chan);
}
void belle_sip_listening_point_clean_channels(belle_sip_listening_point_t *lp){
int existing_channels;
if ((existing_channels=belle_sip_list_size(lp->channels)) > 0) {
belle_sip_warning("Listening point destroying [%i] channels",existing_channels);
}
belle_sip_list_free_with_data(lp->channels,(void (*)(void*))belle_sip_object_unref);
}
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_listening_point_t);
BELLE_SIP_INSTANCIATE_CUSTOM_VPTR(belle_sip_listening_point_t)={
......
......@@ -235,11 +235,13 @@ static void belle_sip_message_for_each_header(const belle_sip_message_t *message
static void append_header(const belle_sip_header_t* header,void* user_data) {
*(belle_sip_list_t**)user_data=belle_sip_list_append((*(belle_sip_list_t**)user_data),(void*)header);
}
belle_sip_list_t* belle_sip_message_get_all_headers(const belle_sip_message_t *message) {
belle_sip_list_t* headers=NULL;
belle_sip_message_for_each_header(message,append_header,&headers);
return headers;
}
int belle_sip_headers_marshal(belle_sip_message_t *message, char* buff,unsigned int offset,unsigned int buff_size) {
unsigned int current_offset=offset;
/*FIXME, replace this code by belle_sip_message_for_each_header*/
......
......@@ -89,7 +89,12 @@ static void belle_sip_provider_dispatch_request(belle_sip_provider_t* prov, bell
/*Search for a dialog if exist */
ev.dialog=belle_sip_provider_find_dialog(prov,req,1/*request=uas*/);
if (strcmp("ACK",belle_sip_request_get_method(req))==0 && ev.dialog) belle_sip_dialog_handle_ack(ev.dialog,req);
if (strcmp("ACK",belle_sip_request_get_method(req))==0 && ev.dialog){
if (belle_sip_dialog_handle_ack(ev.dialog,req)==-1){
/*absorbed ACK retransmission, ignore */
return;
}
}
ev.source=prov;
ev.server_transaction=NULL;
ev.request=req;
......@@ -369,9 +374,12 @@ belle_sip_client_transaction_t *belle_sip_provider_create_client_transaction(bel
belle_sip_server_transaction_t *belle_sip_provider_create_server_transaction(belle_sip_provider_t *prov, belle_sip_request_t *req){
belle_sip_server_transaction_t* t;
if (strcmp(belle_sip_request_get_method(req),"INVITE")==0)
if (strcmp(belle_sip_request_get_method(req),"INVITE")==0){
t=(belle_sip_server_transaction_t*)belle_sip_ist_new(prov,req);
else
}else if (strcmp(belle_sip_request_get_method(req),"ACK")==0){
belle_sip_error("Creating a server transaction for an ACK is not a good idea, probably");
return NULL;
}else
t=(belle_sip_server_transaction_t*)belle_sip_nist_new(prov,req);
t->base.dialog=belle_sip_provider_find_dialog(prov,req,TRUE);
belle_sip_provider_add_server_transaction(prov,t);
......@@ -410,6 +418,16 @@ void belle_sip_provider_release_channel(belle_sip_provider_t *p, belle_sip_chann
belle_sip_listening_point_remove_channel(chan->lp,chan);
}
void belle_sip_provider_clean_channels(belle_sip_provider_t *p){
belle_sip_list_t *l;
belle_sip_listening_point_t *lp;
for(l=p->lps;l!=NULL;l=l->next){
lp=(belle_sip_listening_point_t*)l->data;
belle_sip_listening_point_clean_channels(lp);
}
}
void belle_sip_provider_send_request(belle_sip_provider_t *p, belle_sip_request_t *req){
belle_sip_hop_t hop={0};
belle_sip_channel_t *chan;
......
......@@ -140,13 +140,13 @@ static void callee_process_request_event(void *user_ctx, const belle_sip_request
if (!belle_sip_uri_equals(BELLE_SIP_URI(user_ctx),belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(to)))) {
return; /*not for the callee*/
}
if (!server_transaction) {
method = belle_sip_request_get_method(belle_sip_request_event_get_request(event));
if (!server_transaction && strcmp(method,"ACK")!=0) {
server_transaction= belle_sip_provider_create_server_transaction(prov,belle_sip_request_event_get_request(event));
}
method = belle_sip_request_get_method(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(server_transaction)));
belle_sip_message("callee_process_request_event received [%s] message",method);
belle_sip_dialog_t* dialog = belle_sip_transaction_get_dialog(BELLE_SIP_TRANSACTION(server_transaction));
belle_sip_dialog_t* dialog = belle_sip_request_event_get_dialog(event);
belle_sip_response_t* ringing_response;
belle_sip_header_content_type_t* content_type ;
belle_sip_header_content_length_t* content_length;
......@@ -247,7 +247,7 @@ static void process_transaction_terminated(void *user_ctx, const belle_sip_trans
static void simple_call(void) {
static void do_simple_call(void) {
#define CALLER "marie"
#define CALLEE "pauline"
belle_sip_request_t *pauline_register_req;
......@@ -322,12 +322,26 @@ static void simple_call(void) {
unregister_user(stack, prov, marie_register_req ,1);
}
static void simple_call(void){
belle_sip_stack_set_tx_delay(stack,0);
do_simple_call();
}
static void simple_call_with_delay(){
belle_sip_stack_set_tx_delay(stack,2000);
do_simple_call();
belle_sip_stack_set_tx_delay(stack,0);
}
int belle_sip_dialog_test_suite(){
CU_pSuite pSuite = CU_add_suite("Dialog", init, uninit);
if (NULL == CU_add_test(pSuite, "simple-call", simple_call)) {
return CU_get_error();
}
if (NULL == CU_add_test(pSuite, "simple-call-with-delay", simple_call_with_delay)) {
return CU_get_error();
}
return 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment