summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2010-11-16 03:07:46 +0000
committerBenny Prijono <bennylp@teluu.com>2010-11-16 03:07:46 +0000
commite27b2a2975a378643ff03e36176be3c378b9bc19 (patch)
treee7f95b0d411d78b71fe4e651ddb5ebf7bfbf2785
parent160111f12b0fe593ab26fd09a1b222a6f40a9caa (diff)
Implemented SIP outbound (closed #1020)
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@3366 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h35
-rw-r--r--pjsip/include/pjsua-lib/pjsua_internal.h11
-rw-r--r--pjsip/src/pjsua-lib/pjsua_acc.c199
-rw-r--r--pjsip/src/pjsua-lib/pjsua_core.c1
4 files changed, 240 insertions, 6 deletions
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h
index 0661bb2f..0a6cbdd8 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -2333,6 +2333,41 @@ typedef struct pjsua_acc_config
int contact_rewrite_method;
/**
+ * Control the use of SIP outbound feature. SIP outbound is described in
+ * RFC 5626 to enable proxies or registrar to send inbound requests back
+ * to UA using the same connection initiated by the UA for its
+ * registration. This feature is highly useful in NAT-ed deployemtns,
+ * hence it is enabled by default.
+ *
+ * Note: currently SIP outbound can only be used with TCP and TLS
+ * transports. If UDP is used for the registration, the SIP outbound
+ * feature will be silently ignored for the account.
+ *
+ * Default: PJ_TRUE
+ */
+ unsigned use_rfc5626;
+
+ /**
+ * Specify SIP outbound (RFC 5626) instance ID to be used by this
+ * application. If empty, an instance ID will be generated based on
+ * the hostname of this agent. If application specifies this parameter, the
+ * value will look like "<urn:uuid:00000000-0000-1000-8000-AABBCCDDEEFF>"
+ * without the doublequote.
+ *
+ * Default: empty
+ */
+ pj_str_t rfc5626_instance_id;
+
+ /**
+ * Specify SIP outbound (RFC 5626) registration ID. The default value
+ * is empty, which would cause the library to automatically generate
+ * a suitable value.
+ *
+ * Default: empty
+ */
+ pj_str_t rfc5626_reg_id;
+
+ /**
* Set the interval for periodic keep-alive transmission for this account.
* If this value is zero, keep-alive will be disabled for this account.
* The keep-alive transmission will be sent to the registrar's address,
diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h
index 3126b889..dc91d231 100644
--- a/pjsip/include/pjsua-lib/pjsua_internal.h
+++ b/pjsip/include/pjsua-lib/pjsua_internal.h
@@ -114,7 +114,6 @@ struct pjsua_srv_pres
int expires; /**< "expires" value in the request. */
};
-
/**
* Account
*/
@@ -128,6 +127,9 @@ typedef struct pjsua_acc
pj_str_t display; /**< Display name, if any. */
pj_str_t user_part; /**< User part of local URI. */
pj_str_t contact; /**< Our Contact header. */
+ pj_str_t reg_contact; /**< Contact header for REGISTER.
+ It may be different than acc
+ contact if outbound is used */
pj_str_t srv_domain; /**< Host part of reg server. */
int srv_port; /**< Port number of reg server. */
@@ -152,6 +154,13 @@ typedef struct pjsua_acc
pj_uint32_t global_route_crc; /** CRC of global route setting. */
pj_uint32_t local_route_crc; /** CRC of account route setting.*/
+ unsigned rfc5626_status;/**< SIP outbound status:
+ 0: not used
+ 1: requested
+ 2: acknowledged by servers */
+ pj_str_t rfc5626_instprm;/**< SIP outbound instance param. */
+ pj_str_t rfc5626_regprm;/**< SIP outbound reg param. */
+
unsigned cred_cnt; /**< Number of credentials. */
pjsip_cred_info cred[PJSUA_ACC_MAX_PROXIES]; /**< Complete creds. */
diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c
index 027b88de..f7629877 100644
--- a/pjsip/src/pjsua-lib/pjsua_acc.c
+++ b/pjsip/src/pjsua-lib/pjsua_acc.c
@@ -23,6 +23,13 @@
#define THIS_FILE "pjsua_acc.c"
+enum
+{
+ OUTBOUND_NONE,
+ OUTBOUND_WANTED,
+ OUTBOUND_ACTIVE
+};
+
static void schedule_reregistration(pjsua_acc *acc);
@@ -79,6 +86,8 @@ PJ_DEF(void) pjsua_acc_config_dup( pj_pool_t *pool,
pj_strdup_with_null(pool, &dst->reg_uri, &src->reg_uri);
pj_strdup_with_null(pool, &dst->force_contact, &src->force_contact);
pj_strdup_with_null(pool, &dst->pidf_tuple_id, &src->pidf_tuple_id);
+ pj_strdup_with_null(pool, &dst->rfc5626_instance_id, &src->rfc5626_instance_id);
+ pj_strdup_with_null(pool, &dst->rfc5626_reg_id, &src->rfc5626_reg_id);
dst->proxy_cnt = src->proxy_cnt;
for (i=0; i<src->proxy_cnt; ++i)
@@ -269,6 +278,55 @@ static pj_status_t initialize_acc(unsigned acc_id)
if (status != PJ_SUCCESS)
return status;
+ /* If SIP outbound is enabled, generate instance and reg ID if they are
+ * not specified
+ */
+ if (acc_cfg->use_rfc5626) {
+ if (acc_cfg->rfc5626_instance_id.slen==0) {
+ const pj_str_t *hostname;
+ pj_uint32_t hval, pos;
+ char instprm[] = ";+sip.instance=\"<urn:uuid:00000000-0000-0000-0000-0000CCDDEEFF>\"";
+
+ hostname = pj_gethostname();
+ pos = pj_ansi_strlen(instprm) - 10;
+ hval = pj_hash_calc(0, hostname->ptr, hostname->slen);
+ pj_val_to_hex_digit( ((char*)&hval)[0], instprm+pos+0);
+ pj_val_to_hex_digit( ((char*)&hval)[1], instprm+pos+2);
+ pj_val_to_hex_digit( ((char*)&hval)[2], instprm+pos+4);
+ pj_val_to_hex_digit( ((char*)&hval)[3], instprm+pos+6);
+
+ pj_strdup2(acc->pool, &acc->rfc5626_instprm, instprm);
+ } else {
+ const char *prmname = ";+sip.instance=\"";
+ unsigned len;
+
+ len = pj_ansi_strlen(prmname) + acc_cfg->rfc5626_instance_id.slen + 1;
+ acc->rfc5626_instprm.ptr = (char*)pj_pool_alloc(acc->pool, len+1);
+ pj_ansi_snprintf(acc->rfc5626_instprm.ptr, len+1,
+ "%s%.*s\"",
+ prmname,
+ (int)acc_cfg->rfc5626_instance_id.slen,
+ acc_cfg->rfc5626_instance_id.ptr);
+ acc->rfc5626_instprm.slen = len;
+ }
+
+ if (acc_cfg->rfc5626_reg_id.slen==0) {
+ acc->rfc5626_regprm = pj_str(";reg-id=1");
+ } else {
+ const char *prmname = ";reg-id=";
+ unsigned len;
+
+ len = pj_ansi_strlen(prmname) + acc_cfg->rfc5626_reg_id.slen;
+ acc->rfc5626_regprm.ptr = (char*)pj_pool_alloc(acc->pool, len+1);
+ pj_ansi_snprintf(acc->rfc5626_regprm.ptr, len+1,
+ "%s%.*s\"",
+ prmname,
+ (int)acc_cfg->rfc5626_reg_id.slen,
+ acc_cfg->rfc5626_reg_id.ptr);
+ acc->rfc5626_regprm.slen = len;
+ }
+ }
+
/* Mark account as valid */
pjsua_var.acc[acc_id].valid = PJ_TRUE;
@@ -899,6 +957,14 @@ PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id,
update_reg = PJ_TRUE;
}
+ /* SIP outbound setting */
+ if (acc->cfg.use_rfc5626 != cfg->use_rfc5626 ||
+ pj_strcmp(&acc->cfg.rfc5626_instance_id, &cfg->rfc5626_instance_id) ||
+ pj_strcmp(&acc->cfg.rfc5626_reg_id, &cfg->rfc5626_reg_id))
+ {
+ update_reg = PJ_TRUE;
+ }
+
/* Update registration */
if (update_reg) {
/* If accounts has registration enabled, start registration */
@@ -955,6 +1021,62 @@ PJ_DEF(pj_status_t) pjsua_acc_set_online_status2( pjsua_acc_id acc_id,
return PJ_SUCCESS;
}
+/* Create reg_contact, mainly for SIP outbound */
+static void update_regc_contact(pjsua_acc *acc)
+{
+ pjsua_acc_config *acc_cfg = &acc->cfg;
+ pj_bool_t need_outbound = PJ_FALSE;
+ const pj_str_t tcp_param = pj_str(";transport=tcp");
+ const pj_str_t tls_param = pj_str(";transport=tls");
+
+ if (!acc_cfg->use_rfc5626)
+ goto done;
+
+ if (pj_stristr(&acc->contact, &tcp_param)==NULL &&
+ pj_stristr(&acc->contact, &tls_param)==NULL)
+ {
+ /* Currently we can only do SIP outbound for TCP
+ * and TLS.
+ */
+ goto done;
+ }
+
+ /* looks like we can use outbound */
+ need_outbound = PJ_TRUE;
+
+done:
+ if (!need_outbound) {
+ /* Outbound is not needed/wanted for the account. acc->reg_contact
+ * is set to the same as acc->contact.
+ */
+ acc->reg_contact = acc->contact;
+ acc->rfc5626_status = OUTBOUND_NONE;
+ } else {
+ /* Need to use outbound, append the contact with +sip.instance and
+ * reg-id parameters.
+ */
+ unsigned len;
+ pj_str_t reg_contact;
+
+ acc->rfc5626_status = OUTBOUND_WANTED;
+ len = acc->contact.slen + acc->rfc5626_instprm.slen +
+ acc->rfc5626_regprm.slen;
+ reg_contact.ptr = (char*) pj_pool_alloc(acc->pool, reg_contact.slen);
+
+ pj_strcpy(&reg_contact, &acc->contact);
+ pj_strcat(&reg_contact, &acc->rfc5626_regprm);
+ pj_strcat(&reg_contact, &acc->rfc5626_instprm);
+
+ acc->reg_contact = reg_contact;
+
+ PJ_LOG(4,(THIS_FILE,
+ "Contact for acc %d updated for SIP outbound: %.*s",
+ acc->index,
+ (int)acc->reg_contact.slen,
+ acc->reg_contact.ptr));
+ }
+}
+
/* Check if IP is private IP address */
static pj_bool_t is_private_ip(const pj_str_t *addr)
{
@@ -999,6 +1121,13 @@ static pj_bool_t acc_check_nat_addr(pjsua_acc *acc,
if (acc->cfg.allow_contact_rewrite == PJ_FALSE)
return PJ_FALSE;
+ /* If SIP outbound is active, no need to update */
+ if (acc->rfc5626_status == OUTBOUND_ACTIVE) {
+ PJ_LOG(4,(THIS_FILE, "Acc %d has SIP outbound active, no need to "
+ "update registration Contact", acc->index));
+ return PJ_FALSE;
+ }
+
#if 0
// Always update
// See http://lists.pjsip.org/pipermail/pjsip_lists.pjsip.org/2008-March/002178.html
@@ -1142,6 +1271,7 @@ static pj_bool_t acc_check_nat_addr(pjsua_acc *acc,
* Build new Contact header
*/
{
+ const char *ob = ";ob";
char *tmp;
const char *beginquote, *endquote;
int len;
@@ -1156,7 +1286,7 @@ static pj_bool_t acc_check_nat_addr(pjsua_acc *acc,
tmp = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
len = pj_ansi_snprintf(tmp, PJSIP_MAX_URL_SIZE,
- "<sip:%.*s%s%s%.*s%s:%d;transport=%s%.*s>%.*s",
+ "<sip:%.*s%s%s%.*s%s:%d;transport=%s%.*s%s>%.*s",
(int)acc->user_part.slen,
acc->user_part.ptr,
(acc->user_part.slen? "@" : ""),
@@ -1168,6 +1298,7 @@ static pj_bool_t acc_check_nat_addr(pjsua_acc *acc,
tp->type_name,
(int)acc->cfg.contact_uri_params.slen,
acc->cfg.contact_uri_params.ptr,
+ ob,
(int)acc->cfg.contact_params.slen,
acc->cfg.contact_params.ptr);
if (len < 1) {
@@ -1177,6 +1308,8 @@ static pj_bool_t acc_check_nat_addr(pjsua_acc *acc,
}
pj_strdup2_with_null(acc->pool, &acc->contact, tmp);
+ update_regc_contact(acc);
+
/* Always update, by http://trac.pjsip.org/repos/ticket/864. */
pj_strdup_with_null(tp->pool, &tp->local_name.host, via_addr);
tp->local_name.port = rport;
@@ -1184,7 +1317,7 @@ static pj_bool_t acc_check_nat_addr(pjsua_acc *acc,
}
if (acc->cfg.contact_rewrite_method == 2 && acc->regc != NULL) {
- pjsip_regc_update_contact(acc->regc, 1, &acc->contact);
+ pjsip_regc_update_contact(acc->regc, 1, &acc->reg_contact);
}
/* Perform new registration */
@@ -1418,6 +1551,39 @@ static void update_keep_alive(pjsua_acc *acc, pj_bool_t start,
}
+/* Update the status of SIP outbound registration request */
+static void update_rfc5626_status(pjsua_acc *acc, pjsip_rx_data *rdata)
+{
+ pjsip_require_hdr *hreq;
+ const pj_str_t STR_OUTBOUND = {"outbound", 8};
+ unsigned i;
+
+ if (acc->rfc5626_status == OUTBOUND_NONE) {
+ goto on_return;
+ }
+
+ hreq = rdata->msg_info.require;
+ if (!hreq) {
+ acc->rfc5626_status = OUTBOUND_NONE;
+ goto on_return;
+ }
+
+ for (i=0; i<hreq->count; ++i) {
+ if (pj_stricmp(&hreq->values[i], &STR_OUTBOUND)==0) {
+ acc->rfc5626_status = OUTBOUND_ACTIVE;
+ goto on_return;
+ }
+ }
+
+ /* Server does not support outbound */
+ acc->rfc5626_status = OUTBOUND_NONE;
+
+on_return:
+ PJ_LOG(4,(THIS_FILE, "SIP outbound status for acc %d is %s",
+ acc->index, (acc->rfc5626_status==OUTBOUND_ACTIVE?
+ "active": "not active")));
+}
+
/*
* This callback is called by pjsip_regc when outgoing register
* request has completed.
@@ -1473,9 +1639,13 @@ static void regc_cb(struct pjsip_regc_cbparam *param)
PJ_LOG(3,(THIS_FILE, "%s: unregistration success",
pjsua_var.acc[acc->index].cfg.id.ptr));
} else {
+ /* Check and update SIP outbound status first, since the result
+ * will determine if we should update re-registration
+ */
+ update_rfc5626_status(acc, param->rdata);
+
/* Check NAT bound address */
if (acc_check_nat_addr(acc, param)) {
- /* Update address, don't notify application yet */
PJSUA_UNLOCK();
return;
}
@@ -1595,13 +1765,14 @@ static pj_status_t pjsua_regc_init(int acc_id)
}
pj_strdup_with_null(acc->pool, &acc->contact, &tmp_contact);
+ update_regc_contact(acc);
}
status = pjsip_regc_init( acc->regc,
&acc->cfg.reg_uri,
&acc->cfg.id,
&acc->cfg.id,
- 1, &acc->contact,
+ 1, &acc->reg_contact,
acc->cfg.reg_timeout);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE,
@@ -1686,6 +1857,22 @@ static pj_status_t pjsua_regc_init(int acc_id)
pjsip_regc_add_headers(acc->regc, &hdr_list);
}
+ /* If SIP outbound is used, add "Supported: outbound, path header" */
+ if (acc->rfc5626_status == OUTBOUND_WANTED) {
+ pjsip_hdr hdr_list;
+ pjsip_supported_hdr *hsup;
+
+ pj_list_init(&hdr_list);
+ hsup = pjsip_supported_hdr_create(pool);
+ pj_list_push_back(&hdr_list, hsup);
+
+ hsup->count = 2;
+ hsup->values[0] = pj_str("outbound");
+ hsup->values[1] = pj_str("path");
+
+ pjsip_regc_add_headers(acc->regc, &hdr_list);
+ }
+
pj_pool_release(pool);
return PJ_SUCCESS;
@@ -2146,6 +2333,7 @@ PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool,
int local_port;
const char *beginquote, *endquote;
char transport_param[32];
+ const char *ob = ";ob";
PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
@@ -2231,7 +2419,7 @@ PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool,
/* Create the contact header */
contact->ptr = (char*)pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE,
- "%.*s%s<%s:%.*s%s%s%.*s%s:%d%s%.*s>%.*s",
+ "%.*s%s<%s:%.*s%s%s%.*s%s:%d%s%.*s%s>%.*s",
(int)acc->display.slen,
acc->display.ptr,
(acc->display.slen?" " : ""),
@@ -2247,6 +2435,7 @@ PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool,
transport_param,
(int)acc->cfg.contact_uri_params.slen,
acc->cfg.contact_uri_params.ptr,
+ ob,
(int)acc->cfg.contact_params.slen,
acc->cfg.contact_params.ptr);
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index ac9bbfd5..fd8c57f5 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -183,6 +183,7 @@ PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg)
#endif
cfg->reg_retry_interval = PJSUA_REG_RETRY_INTERVAL;
cfg->contact_rewrite_method = PJSUA_CONTACT_REWRITE_METHOD;
+ cfg->use_rfc5626 = PJ_TRUE;
cfg->reg_use_proxy = PJSUA_REG_USE_OUTBOUND_PROXY |
PJSUA_REG_USE_ACC_PROXY;
#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0