summaryrefslogtreecommitdiff
path: root/pjsip
diff options
context:
space:
mode:
authorLiong Sauw Ming <ming@teluu.com>2013-09-04 10:07:45 +0000
committerLiong Sauw Ming <ming@teluu.com>2013-09-04 10:07:45 +0000
commit387149bb4509fa04c19f71e7bfad587a6c9fc843 (patch)
treecd79753bfc6b686453f354e4f9c4161beed2ce04 /pjsip
parentef6704f279e8a331a8783f067bec99718940c83a (diff)
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
Diffstat (limited to 'pjsip')
-rw-r--r--pjsip/include/pjsip-ua/sip_regc.h31
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h71
-rw-r--r--pjsip/src/pjsip-ua/sip_reg.c134
-rw-r--r--pjsip/src/pjsua-lib/pjsua_acc.c55
4 files changed, 243 insertions, 48 deletions
diff --git a/pjsip/include/pjsip-ua/sip_regc.h b/pjsip/include/pjsip-ua/sip_regc.h
index 9e393813..80c7a331 100644
--- a/pjsip/include/pjsip-ua/sip_regc.h
+++ b/pjsip/include/pjsip-ua/sip_regc.h
@@ -84,6 +84,22 @@ struct pjsip_regc_cbparam
typedef void pjsip_regc_cb(struct pjsip_regc_cbparam *param);
/**
+ * Structure to hold parameters when calling application's callback
+ * specified in #pjsip_regc_set_reg_tsx_cb().
+ * To update contact address, application can set the field contact_cnt
+ * and contact inside the callback.
+ */
+struct pjsip_regc_tsx_cb_param
+{
+ struct pjsip_regc_cbparam cbparam;
+ int contact_cnt;
+ pj_str_t contact[PJSIP_REGC_MAX_CONTACT];
+};
+
+/** Type declaration for callback set in #pjsip_regc_set_reg_tsx_cb(). */
+typedef void pjsip_regc_tsx_cb(struct pjsip_regc_tsx_cb_param *param);
+
+/**
* Client registration information.
*/
struct pjsip_regc_info
@@ -191,6 +207,21 @@ PJ_DECL(pj_status_t) pjsip_regc_init(pjsip_regc *regc,
pj_uint32_t expires);
/**
+ * Set callback to be called when the registration received a final response.
+ * This callback is different with the one specified during creation via
+ * #pjsip_regc_create(). This callback will be called for any final response
+ * (including 401/407/423) and before any subsequent requests are sent.
+ * In case of unregistration, this callback will not be called.
+ *
+ * @param regc The client registration structure.
+ * @param tsx_cb Pointer to callback function to receive registration status.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_regc_set_reg_tsx_cb(pjsip_regc *regc,
+ pjsip_regc_tsx_cb *tsx_cb);
+
+/**
* Set the "sent-by" field of the Via header for outgoing requests.
*
* @param regc The client registration structure.
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h
index ad261561..beb0762e 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -569,6 +569,39 @@ typedef enum pjsua_create_media_transport_flag
/**
+ * This enumeration specifies the contact rewrite method.
+ */
+typedef enum pjsua_contact_rewrite_method
+{
+ /**
+ * The Contact update will be done by sending unregistration
+ * to the currently registered Contact, while simultaneously sending new
+ * registration (with different Call-ID) for the updated Contact.
+ */
+ PJSUA_CONTACT_REWRITE_UNREGISTER = 1,
+
+ /**
+ * The Contact update will be done in a single, current
+ * registration session, by removing the current binding (by setting its
+ * Contact's expires parameter to zero) and adding a new Contact binding,
+ * all done in a single request.
+ */
+ PJSUA_CONTACT_REWRITE_NO_UNREG = 2,
+
+ /**
+ * The Contact update will be done when receiving any registration final
+ * response. If this flag is not specified, contact update will only be
+ * done upon receiving 2xx response. This flag MUST be used with
+ * PJSUA_CONTACT_REWRITE_UNREGISTER or PJSUA_CONTACT_REWRITE_NO_UNREG
+ * above to specify how the Contact update should be performed when
+ * receiving 2xx response.
+ */
+ PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE = 4
+
+} pjsua_contact_rewrite_method;
+
+
+/**
* Call settings.
*/
typedef struct pjsua_call_setting
@@ -2540,25 +2573,18 @@ PJ_DECL(pj_status_t) pjsua_transport_close( pjsua_transport_id id,
/**
* This macro specifies the default value for \a contact_rewrite_method
- * field in pjsua_acc_config. I specifies how Contact update will be
+ * field in pjsua_acc_config. It specifies how Contact update will be
* done with the registration, if \a allow_contact_rewrite is enabled in
- * the account config.
- *
- * If set to 1, the Contact update will be done by sending unregistration
- * to the currently registered Contact, while simultaneously sending new
- * registration (with different Call-ID) for the updated Contact.
+ * the account config. See \a pjsua_contact_rewrite_method for the options.
*
- * If set to 2, the Contact update will be done in a single, current
- * registration session, by removing the current binding (by setting its
- * Contact's expires parameter to zero) and adding a new Contact binding,
- * all done in a single request.
+ * Value PJSUA_CONTACT_REWRITE_UNREGISTER(1) is the legacy behavior.
*
- * Value 1 is the legacy behavior.
- *
- * Default value: 2
+ * Default value: PJSUA_CONTACT_REWRITE_NO_UNREG(2) |
+ * PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE(4)
*/
#ifndef PJSUA_CONTACT_REWRITE_METHOD
-# define PJSUA_CONTACT_REWRITE_METHOD 2
+# define PJSUA_CONTACT_REWRITE_METHOD (PJSUA_CONTACT_REWRITE_NO_UNREG | \
+ PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE)
#endif
@@ -3010,20 +3036,13 @@ typedef struct pjsua_acc_config
/**
* Specify how Contact update will be done with the registration, if
- * \a allow_contact_rewrite is enabled.
- *
- * If set to 1, the Contact update will be done by sending unregistration
- * to the currently registered Contact, while simultaneously sending new
- * registration (with different Call-ID) for the updated Contact.
- *
- * If set to 2, the Contact update will be done in a single, current
- * registration session, by removing the current binding (by setting its
- * Contact's expires parameter to zero) and adding a new Contact binding,
- * all done in a single request.
+ * \a allow_contact_rewrite is enabled. The value is bitmask combination of
+ * \a pjsua_contact_rewrite_method. See also pjsua_contact_rewrite_method.
*
- * Value 1 is the legacy behavior.
+ * Value PJSUA_CONTACT_REWRITE_UNREGISTER(1) is the legacy behavior.
*
- * Default value: PJSUA_CONTACT_REWRITE_METHOD (2)
+ * Default value: PJSUA_CONTACT_REWRITE_METHOD
+ * (PJSUA_CONTACT_REWRITE_NO_UNREG | PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE)
*/
int contact_rewrite_method;
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(&param.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)(&param);
+ 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 != &regc->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( &regc->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 != &regc->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(&regc->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( &regc->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,
+ &param->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.
*/