From 387149bb4509fa04c19f71e7bfad587a6c9fc843 Mon Sep 17 00:00:00 2001 From: Liong Sauw Ming Date: Wed, 4 Sep 2013 10:07:45 +0000 Subject: Closed #1696: IP change detection (Contact rewrite method) based on REGISTER final response git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@4586 74dad513-b988-da41-8d7b-12977e46ad98 --- pjsip/src/pjsip-ua/sip_reg.c | 134 +++++++++++++++++++++++++++++++++++----- pjsip/src/pjsua-lib/pjsua_acc.c | 55 ++++++++++++++--- 2 files changed, 167 insertions(+), 22 deletions(-) (limited to 'pjsip/src') diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c index 3333317b..ca907bab 100644 --- a/pjsip/src/pjsip-ua/sip_reg.c +++ b/pjsip/src/pjsip-ua/sip_reg.c @@ -75,6 +75,7 @@ struct pjsip_regc void *token; pjsip_regc_cb *cb; + pjsip_regc_tsx_cb *tsx_cb; pj_str_t str_srv_url; pjsip_uri *srv_url; @@ -727,6 +728,26 @@ PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc, return PJ_SUCCESS; } +static void cbparam_init( struct pjsip_regc_cbparam *cbparam, + pjsip_regc *regc, + pj_status_t status, int st_code, + const pj_str_t *reason, + pjsip_rx_data *rdata, pj_int32_t expiration, + int contact_cnt, pjsip_contact_hdr *contact[]) +{ + cbparam->regc = regc; + cbparam->token = regc->token; + cbparam->status = status; + cbparam->code = st_code; + cbparam->reason = *reason; + cbparam->rdata = rdata; + cbparam->contact_cnt = contact_cnt; + cbparam->expiration = expiration; + if (contact_cnt) { + pj_memcpy( cbparam->contact, contact, + contact_cnt*sizeof(pjsip_contact_hdr*)); + } +} static void call_callback(pjsip_regc *regc, pj_status_t status, int st_code, const pj_str_t *reason, @@ -735,23 +756,11 @@ static void call_callback(pjsip_regc *regc, pj_status_t status, int st_code, { struct pjsip_regc_cbparam cbparam; - if (!regc->cb) return; - cbparam.regc = regc; - cbparam.token = regc->token; - cbparam.status = status; - cbparam.code = st_code; - cbparam.reason = *reason; - cbparam.rdata = rdata; - cbparam.contact_cnt = contact_cnt; - cbparam.expiration = expiration; - if (contact_cnt) { - pj_memcpy( cbparam.contact, contact, - contact_cnt*sizeof(pjsip_contact_hdr*)); - } - + cbparam_init(&cbparam, regc, status, st_code, reason, rdata, expiration, + contact_cnt, contact); (*regc->cb)(&cbparam); } @@ -813,6 +822,15 @@ static void schedule_registration ( pjsip_regc *regc, pj_int32_t expiration ) } } +PJ_DEF(pj_status_t) pjsip_regc_set_reg_tsx_cb( pjsip_regc *regc, + pjsip_regc_tsx_cb *tsx_cb) +{ + PJ_ASSERT_RETURN(regc, PJ_EINVAL); + regc->tsx_cb = tsx_cb; + return PJ_SUCCESS; +} + + PJ_DEF(pj_status_t) pjsip_regc_set_via_sent_by( pjsip_regc *regc, pjsip_host_port *via_addr, pjsip_transport *via_tp) @@ -1035,6 +1053,7 @@ static void regc_tsx_callback(void *token, pjsip_event *event) pjsip_regc *regc = (pjsip_regc*) token; pjsip_transaction *tsx = event->body.tsx_state.tsx; pj_bool_t handled = PJ_TRUE; + pj_bool_t update_contact = PJ_FALSE; pj_atomic_inc(regc->busy_ctr); pj_lock_acquire(regc->lock); @@ -1056,6 +1075,49 @@ static void regc_tsx_callback(void *token, pjsip_event *event) } } + if (regc->_delete_flag == 0 && regc->tsx_cb && + regc->current_op == REGC_REGISTERING) + { + struct pjsip_regc_tsx_cb_param param; + + param.contact_cnt = -1; + cbparam_init(¶m.cbparam, regc, PJ_SUCCESS, tsx->status_code, + &tsx->status_text, + (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ? + event->body.tsx_state.src.rdata : NULL, + -1, 0, NULL); + + /* Call regc tsx callback before handling any response */ + pj_lock_release(regc->lock); + (*regc->tsx_cb)(¶m); + pj_lock_acquire(regc->lock); + + if (param.contact_cnt >= 0) { + /* Since we receive non-2xx response, it means that (some) contact + * bindings haven't been established so we can safely remove these + * contact headers. This is to avoid removing non-existent contact + * bindings later. + */ + if (tsx->status_code/100 != 2) { + pjsip_contact_hdr *h; + + h = regc->contact_hdr_list.next; + while (h != ®c->contact_hdr_list) { + pjsip_contact_hdr *next = h->next; + + if (h->expires == -1) { + pj_list_erase(h); + } + h = next; + } + } + + /* Update contact address */ + pjsip_regc_update_contact(regc, param.contact_cnt, param.contact); + update_contact = PJ_TRUE; + } + } + /* Handle 401/407 challenge (even when _delete_flag is set) */ if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED || tsx->status_code == PJSIP_SC_UNAUTHORIZED) @@ -1066,7 +1128,49 @@ static void regc_tsx_callback(void *token, pjsip_event *event) /* reset current op */ regc->current_op = REGC_IDLE; - status = pjsip_auth_clt_reinit_req( ®c->auth_sess, + if (update_contact) { + pjsip_msg *msg; + pjsip_hdr *hdr, *ins_hdr; + pjsip_contact_hdr *chdr; + + /* Delete Contact headers, but we shouldn't delete headers + * which are supposed to remove contact bindings since + * we cannot reconstruct those headers. + */ + msg = tsx->last_tx->msg; + hdr = msg->hdr.next; + ins_hdr = &msg->hdr; + while (hdr != &msg->hdr) { + pjsip_hdr *next = hdr->next; + + if (hdr->type == PJSIP_H_CONTACT) { + chdr = (pjsip_contact_hdr *)hdr; + if (chdr->expires != 0) { + pj_list_erase(hdr); + ins_hdr = next; + } + } + hdr = next; + } + + /* Add Contact headers. */ + chdr = regc->contact_hdr_list.next; + while (chdr != ®c->contact_hdr_list) { + pj_list_insert_before(ins_hdr, (pjsip_hdr*) + pjsip_hdr_shallow_clone(tsx->last_tx->pool, chdr)); + chdr = chdr->next; + } + + /* Also add bindings which are to be removed */ + while (!pj_list_empty(®c->removed_contact_hdr_list)) { + chdr = regc->removed_contact_hdr_list.next; + pj_list_insert_before(ins_hdr, (pjsip_hdr*) + pjsip_hdr_clone(tsx->last_tx->pool, chdr)); + pj_list_erase(chdr); + } + } + + status = pjsip_auth_clt_reinit_req( ®c->auth_sess, rdata, tsx->last_tx, &tdata); diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index e501791d..328d41e6 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -1494,6 +1494,7 @@ static pj_bool_t is_private_ip(const pj_str_t *addr) /* Update NAT address from the REGISTER response */ static pj_bool_t acc_check_nat_addr(pjsua_acc *acc, + int contact_rewrite_method, struct pjsip_regc_cbparam *param) { pjsip_transport *tp; @@ -1678,12 +1679,13 @@ static pj_bool_t acc_check_nat_addr(pjsua_acc *acc, (int)via_addr->slen, via_addr->ptr, rport, - acc->cfg.contact_rewrite_method)); + contact_rewrite_method)); - pj_assert(acc->cfg.contact_rewrite_method == 1 || - acc->cfg.contact_rewrite_method == 2); + pj_assert(contact_rewrite_method == PJSUA_CONTACT_REWRITE_UNREGISTER || + contact_rewrite_method == PJSUA_CONTACT_REWRITE_NO_UNREG || + contact_rewrite_method == PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE); - if (acc->cfg.contact_rewrite_method == 1) { + if (contact_rewrite_method == PJSUA_CONTACT_REWRITE_UNREGISTER) { /* Unregister current contact */ pjsua_acc_set_registration(acc->index, PJ_FALSE); if (acc->regc != NULL) { @@ -1761,12 +1763,16 @@ static pj_bool_t acc_check_nat_addr(pjsua_acc *acc, } - if (acc->cfg.contact_rewrite_method == 2 && acc->regc != NULL) { + if (contact_rewrite_method == PJSUA_CONTACT_REWRITE_NO_UNREG && + acc->regc != NULL) + { pjsip_regc_update_contact(acc->regc, 1, &acc->reg_contact); } /* Perform new registration */ - pjsua_acc_set_registration(acc->index, PJ_TRUE); + if (contact_rewrite_method < PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE) { + pjsua_acc_set_registration(acc->index, PJ_TRUE); + } pj_pool_release(pool); @@ -2058,6 +2064,37 @@ on_return: "active": "not active"))); } +static void regc_tsx_cb(struct pjsip_regc_tsx_cb_param *param) +{ + pjsua_acc *acc = (pjsua_acc*) param->cbparam.token; + + PJSUA_LOCK(); + + if (param->cbparam.regc != acc->regc) { + PJSUA_UNLOCK(); + return; + } + + pj_log_push_indent(); + + if ((acc->cfg.contact_rewrite_method & + PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE) == + PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE && + param->cbparam.code >= 400 && + param->cbparam.rdata) + { + if (acc_check_nat_addr(acc, PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE, + ¶m->cbparam)) + { + param->contact_cnt = 1; + param->contact[0] = acc->reg_contact; + } + } + + PJSUA_UNLOCK(); + pj_log_pop_indent(); +} + /* * This callback is called by pjsip_regc when outgoing register * request has completed. @@ -2126,7 +2163,9 @@ static void regc_cb(struct pjsip_regc_cbparam *param) update_rfc5626_status(acc, param->rdata); /* Check NAT bound address */ - if (acc_check_nat_addr(acc, param)) { + if (acc_check_nat_addr(acc, (acc->cfg.contact_rewrite_method & 3), + param)) + { PJSUA_UNLOCK(); pj_log_pop_indent(); return; @@ -2271,6 +2310,8 @@ static pj_status_t pjsua_regc_init(int acc_id) return status; } + pjsip_regc_set_reg_tsx_cb(acc->regc, regc_tsx_cb); + /* If account is locked to specific transport, then set transport to * the client registration. */ -- cgit v1.2.3