diff options
author | Benny Prijono <bennylp@teluu.com> | 2008-09-24 10:10:15 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2008-09-24 10:10:15 +0000 |
commit | c2476cfffd0fa7f90ac72295de24ba6dea4ea161 (patch) | |
tree | 0a76d346be96d75d14e4ce627f689f76c1914a07 /pjsip/src | |
parent | 8fa5079ad7ee39493cd3231de2030335ad56eccc (diff) |
Ticket #635: Disconnect the other call leg when multiple 2xx/OK responses to INVITE are received due to forking
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2315 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip/src')
-rw-r--r-- | pjsip/src/pjsip/sip_dialog.c | 74 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_ua_layer.c | 4 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_call.c | 41 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_core.c | 8 |
4 files changed, 111 insertions, 16 deletions
diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c index 2b3203f4..4489cb8e 100644 --- a/pjsip/src/pjsip/sip_dialog.c +++ b/pjsip/src/pjsip/sip_dialog.c @@ -580,31 +580,39 @@ PJ_DEF(pj_status_t) pjsip_dlg_fork( const pjsip_dialog *first_dlg, pjsip_dialog **new_dlg ) { pjsip_dialog *dlg; - const pjsip_route_hdr *r; + const pjsip_msg *msg = rdata->msg_info.msg; + const pjsip_hdr *end_hdr, *hdr; + const pjsip_contact_hdr *contact; pj_status_t status; /* Check arguments. */ PJ_ASSERT_RETURN(first_dlg && rdata && new_dlg, PJ_EINVAL); /* rdata must be response message. */ - PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG, + PJ_ASSERT_RETURN(msg->type == PJSIP_RESPONSE_MSG, PJSIP_ENOTRESPONSEMSG); /* Status code MUST be 1xx (but not 100), or 2xx */ - status = rdata->msg_info.msg->line.status.code; + status = msg->line.status.code; PJ_ASSERT_RETURN( (status/100==1 && status!=100) || (status/100==2), PJ_EBUG); /* To tag must present in the response. */ PJ_ASSERT_RETURN(rdata->msg_info.to->tag.slen != 0, PJSIP_EMISSINGTAG); + /* Find Contact header in the response */ + contact = (const pjsip_contact_hdr*) + pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL); + if (contact == NULL) + return PJSIP_EMISSINGHDR; + /* Create the dialog. */ status = create_dialog((pjsip_user_agent*)first_dlg->ua, &dlg); if (status != PJ_SUCCESS) return status; - /* Clone remote target. */ - dlg->target = (pjsip_uri*) pjsip_uri_clone(dlg->pool, first_dlg->target); + /* Set remote target from the response. */ + dlg->target = (pjsip_uri*) pjsip_uri_clone(dlg->pool, contact->uri); /* Clone local info. */ dlg->local.info = (pjsip_fromto_hdr*) @@ -636,7 +644,7 @@ PJ_DEF(pj_status_t) pjsip_dlg_fork( const pjsip_dialog *first_dlg, dlg->role = PJSIP_ROLE_UAC; /* Dialog state depends on the response. */ - status = rdata->msg_info.msg->line.status.code/100; + status = msg->line.status.code/100; if (status == 1 || status == 2) dlg->state = PJSIP_DIALOG_STATE_ESTABLISHED; else { @@ -651,17 +659,18 @@ PJ_DEF(pj_status_t) pjsip_dlg_fork( const pjsip_dialog *first_dlg, dlg->call_id = (pjsip_cid_hdr*) pjsip_hdr_clone(dlg->pool, first_dlg->call_id); - /* Duplicate Route-Set. */ + /* Get route-set from the response. */ pj_list_init(&dlg->route_set); - r = first_dlg->route_set.next; - while (r != &first_dlg->route_set) { - pjsip_route_hdr *h; - - h = (pjsip_route_hdr*) pjsip_hdr_clone(dlg->pool, r); - pj_list_push_back(&dlg->route_set, h); - - r = r->next; + end_hdr = &msg->hdr; + for (hdr=msg->hdr.prev; hdr!=end_hdr; hdr=hdr->prev) { + if (hdr->type == PJSIP_H_RECORD_ROUTE) { + pjsip_route_hdr *r; + r = (pjsip_route_hdr*) pjsip_hdr_clone(dlg->pool, hdr); + pjsip_routing_hdr_set_route(r); + pj_list_push_back(&dlg->route_set, r); + } } + //dlg->route_set_frozen = PJ_TRUE; /* Clone client authentication session. */ @@ -1813,6 +1822,41 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata ) break; } + /* Handle the case of forked response, when the application creates + * the forked dialog but not the invite session. In this case, the + * forked 200/OK response will be unhandled, and we must send ACK + * here. + */ + if (dlg->usage_cnt==0) { + pj_status_t status; + + if (rdata->msg_info.cseq->method.id==PJSIP_INVITE_METHOD && + rdata->msg_info.msg->line.status.code/100 == 2) + { + pjsip_tx_data *ack; + + status = pjsip_dlg_create_request(dlg, &pjsip_ack_method, + rdata->msg_info.cseq->cseq, + &ack); + if (status == PJ_SUCCESS) + status = pjsip_dlg_send_request(dlg, ack, -1, NULL); + } else if (rdata->msg_info.msg->line.status.code==401 || + rdata->msg_info.msg->line.status.code==407) + { + pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata); + pjsip_tx_data *tdata; + + status = pjsip_auth_clt_reinit_req( &dlg->auth_sess, + rdata, tsx->last_tx, + &tdata); + + if (status == PJ_SUCCESS) { + /* Re-send request. */ + status = pjsip_dlg_send_request(dlg, tdata, -1, NULL); + } + } + } + /* Unhandled response does not necessarily mean error because dialog usages may choose to process the transaction state instead. if (i==dlg->usage_cnt) { diff --git a/pjsip/src/pjsip/sip_ua_layer.c b/pjsip/src/pjsip/sip_ua_layer.c index 21ff5f39..a614d16c 100644 --- a/pjsip/src/pjsip/sip_ua_layer.c +++ b/pjsip/src/pjsip/sip_ua_layer.c @@ -832,6 +832,10 @@ retry_on_deadlock: if (mod_ua.param.on_dlg_forked) { dlg = (*mod_ua.param.on_dlg_forked)(dlg_set->dlg_list.next, rdata); + if (dlg == NULL) { + pj_mutex_unlock(mod_ua.mutex); + return PJ_TRUE; + } } else { dlg = dlg_set->dlg_list.next; diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index 7eb061cc..546be666 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -2943,6 +2943,47 @@ static void pjsua_call_on_forked( pjsip_inv_session *inv, /* + * Callback from UA layer when forked dialog response is received. + */ +pjsip_dialog* on_dlg_forked(pjsip_dialog *dlg, pjsip_rx_data *res) +{ + if (dlg->uac_has_2xx && + res->msg_info.cseq->method.id == PJSIP_INVITE_METHOD && + pjsip_rdata_get_tsx(res) == NULL && + res->msg_info.msg->line.status.code/100 == 2) + { + pjsip_dialog *forked_dlg; + pjsip_tx_data *bye; + pj_status_t status; + + /* Create forked dialog */ + status = pjsip_dlg_fork(dlg, res, &forked_dlg); + if (status != PJ_SUCCESS) + return NULL; + + pjsip_dlg_inc_lock(forked_dlg); + + /* Disconnect the call */ + status = pjsip_dlg_create_request(forked_dlg, &pjsip_bye_method, + -1, &bye); + if (status == PJ_SUCCESS) { + status = pjsip_dlg_send_request(forked_dlg, bye, -1, NULL); + } + + pjsip_dlg_dec_lock(forked_dlg); + + if (status != PJ_SUCCESS) { + return NULL; + } + + return forked_dlg; + + } else { + return dlg; + } +} + +/* * Disconnect call upon error. */ static void call_disconnect( pjsip_inv_session *inv, diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 40830d73..650217a2 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -96,6 +96,7 @@ PJ_DEF(void) pjsua_config_default(pjsua_config *cfg) cfg->use_srtp = PJSUA_DEFAULT_USE_SRTP; cfg->srtp_secure_signaling = PJSUA_DEFAULT_SRTP_SECURE_SIGNALING; #endif + cfg->hangup_forked_call = PJ_TRUE; } PJ_DEF(void) pjsua_config_dup(pj_pool_t *pool, @@ -622,6 +623,7 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, pjsua_config default_cfg; pjsua_media_config default_media_cfg; const pj_str_t STR_OPTIONS = { "OPTIONS", 7 }; + pjsip_ua_init_param ua_init_param; pj_status_t status; @@ -694,7 +696,11 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, /* Initialize UA layer module: */ - status = pjsip_ua_init_module( pjsua_var.endpt, NULL ); + pj_bzero(&ua_init_param, sizeof(ua_init_param)); + if (ua_cfg->hangup_forked_call) { + ua_init_param.on_dlg_forked = &on_dlg_forked; + } + status = pjsip_ua_init_module( pjsua_var.endpt, &ua_init_param); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); |