summaryrefslogtreecommitdiff
path: root/res
diff options
context:
space:
mode:
authorKinsey Moore <kmoore@digium.com>2014-01-15 13:16:10 +0000
committerKinsey Moore <kmoore@digium.com>2014-01-15 13:16:10 +0000
commit7cbb6eab15a29af562d1ef3f174f9af0b64b83b1 (patch)
treef0f7a87a9ace19f0af7d1e158e645f848f4a5e27 /res
parentaa9db707c56fa673560e25663f581954a66f3974 (diff)
PJSIP: Add Path header support
This adds Path support to chan_pjsip in res_pjsip_path.c with minimal additions in res_pjsip_registrar.c to store the path and additions in res_pjsip_outbound_registration.c to enable advertisement of path support to registrars and intervening proxies. Path information is stored on contacts and is enabled via Address of Record (AoRs) and Registration configuration sections. While adding path support, it became necessary to be able to add SIP supplements that handled messages outside of sessions, so a framework for handling these types of hooks was added in parallel to the already-existing session supplements and several senders of out-of-dialog requests were refactored as a result. (closes issue ASTERISK-21084) Review: https://reviewboard.asterisk.org/r/3050/ ........ Merged revisions 405565 from http://svn.asterisk.org/svn/asterisk/branches/12 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@405566 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'res')
-rw-r--r--res/res_pjsip.c234
-rw-r--r--res/res_pjsip/location.c7
-rw-r--r--res/res_pjsip/pjsip_distributor.c2
-rw-r--r--res/res_pjsip/pjsip_options.c74
-rw-r--r--res/res_pjsip_caller_id.c2
-rw-r--r--res/res_pjsip_diversion.c2
-rw-r--r--res/res_pjsip_header_funcs.c2
-rw-r--r--res/res_pjsip_messaging.c10
-rw-r--r--res/res_pjsip_mwi.c4
-rw-r--r--res/res_pjsip_nat.c2
-rw-r--r--res/res_pjsip_notify.c4
-rw-r--r--res/res_pjsip_outbound_registration.c34
-rw-r--r--res/res_pjsip_path.c250
-rw-r--r--res/res_pjsip_refer.c2
-rw-r--r--res/res_pjsip_registrar.c113
-rw-r--r--res/res_pjsip_session.c33
-rw-r--r--res/res_pjsip_t38.c2
17 files changed, 671 insertions, 106 deletions
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index e6f00a8e9..bd8418152 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -883,6 +883,9 @@
OPTIONS request is sent to a contact for qualify purposes.
</para></description>
</configOption>
+ <configOption name="path">
+ <synopsis>Stored Path vector for use in Route headers on outgoing requests.</synopsis>
+ </configOption>
</configObject>
<configObject name="aor">
<synopsis>The configuration for a location of an endpoint</synopsis>
@@ -986,6 +989,15 @@
OPTIONS request is sent to a contact for qualify purposes.
</para></description>
</configOption>
+ <configOption name="support_path">
+ <synopsis>Enables Path support for REGISTER requests and Route support for other requests.</synopsis>
+ <description><para>
+ When this option is enabled, the Path headers in register requests will be saved
+ and its contents will be used in Route headers for outbound out-of-dialog requests
+ and in Path headers for outbound 200 responses. Path support will also be indicated
+ in the Supported header.
+ </para></description>
+ </configOption>
</configObject>
<configObject name="system">
<synopsis>Options that apply to the SIP stack as well as other system-wide settings</synopsis>
@@ -1105,6 +1117,7 @@
</manager>
***/
+#define MOD_DATA_CONTACT "contact"
static pjsip_endpoint *ast_pjsip_endpoint;
@@ -1598,10 +1611,18 @@ static int create_in_dialog_request(const pjsip_method *method, struct pjsip_dia
return 0;
}
+static pj_bool_t supplement_on_rx_request(pjsip_rx_data *rdata);
+static pjsip_module supplement_module = {
+ .name = { "Out of dialog supplement hook", 29 },
+ .id = -1,
+ .priority = PJSIP_MOD_PRIORITY_APPLICATION - 1,
+ .on_rx_request = supplement_on_rx_request,
+};
+
static int create_out_of_dialog_request(const pjsip_method *method, struct ast_sip_endpoint *endpoint,
- const char *uri, pjsip_tx_data **tdata)
+ const char *uri, struct ast_sip_contact *provided_contact, pjsip_tx_data **tdata)
{
- RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
+ RAII_VAR(struct ast_sip_contact *, contact, ao2_bump(provided_contact), ao2_cleanup);
pj_str_t remote_uri;
pj_str_t from;
pj_pool_t *pool;
@@ -1613,7 +1634,9 @@ static int create_out_of_dialog_request(const pjsip_method *method, struct ast_s
return -1;
}
- contact = ast_sip_location_retrieve_contact_from_aor_list(endpoint->aors);
+ if (!contact) {
+ contact = ast_sip_location_retrieve_contact_from_aor_list(endpoint->aors);
+ }
if (!contact || ast_strlen_zero(contact->uri)) {
ast_log(LOG_ERROR, "Unable to retrieve contact for endpoint %s\n",
ast_sorcery_object_get_id(endpoint));
@@ -1665,6 +1688,8 @@ static int create_out_of_dialog_request(const pjsip_method *method, struct ast_s
return -1;
}
+ ast_sip_mod_data_set((*tdata)->pool, (*tdata)->mod_data, supplement_module.id, MOD_DATA_CONTACT, ao2_bump(contact));
+
/* We can release this pool since request creation copied all the necessary
* data into the outbound request's pool
*/
@@ -1674,7 +1699,7 @@ static int create_out_of_dialog_request(const pjsip_method *method, struct ast_s
int ast_sip_create_request(const char *method, struct pjsip_dialog *dlg,
struct ast_sip_endpoint *endpoint, const char *uri,
- pjsip_tx_data **tdata)
+ struct ast_sip_contact *contact, pjsip_tx_data **tdata)
{
const pjsip_method *pmethod = get_pjsip_method(method);
@@ -1686,8 +1711,46 @@ int ast_sip_create_request(const char *method, struct pjsip_dialog *dlg,
if (dlg) {
return create_in_dialog_request(pmethod, dlg, tdata);
} else {
- return create_out_of_dialog_request(pmethod, endpoint, uri, tdata);
+ return create_out_of_dialog_request(pmethod, endpoint, uri, contact, tdata);
+ }
+}
+
+AST_RWLIST_HEAD_STATIC(supplements, ast_sip_supplement);
+
+int ast_sip_register_supplement(struct ast_sip_supplement *supplement)
+{
+ struct ast_sip_supplement *iter;
+ int inserted = 0;
+ SCOPED_LOCK(lock, &supplements, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
+
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&supplements, iter, next) {
+ if (iter->priority > supplement->priority) {
+ AST_RWLIST_INSERT_BEFORE_CURRENT(supplement, next);
+ inserted = 1;
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+
+ if (!inserted) {
+ AST_RWLIST_INSERT_TAIL(&supplements, supplement, next);
+ }
+ ast_module_ref(ast_module_info->self);
+ return 0;
+}
+
+void ast_sip_unregister_supplement(struct ast_sip_supplement *supplement)
+{
+ struct ast_sip_supplement *iter;
+ SCOPED_LOCK(lock, &supplements, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&supplements, iter, next) {
+ if (supplement == iter) {
+ AST_RWLIST_REMOVE_CURRENT(next);
+ ast_module_unref(ast_module_info->self);
+ break;
+ }
}
+ AST_RWLIST_TRAVERSE_SAFE_END;
}
static int send_in_dialog_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg)
@@ -1699,45 +1762,120 @@ static int send_in_dialog_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg
return 0;
}
+static pj_bool_t does_method_match(const pj_str_t *message_method, const char *supplement_method)
+{
+ pj_str_t method;
+
+ if (ast_strlen_zero(supplement_method)) {
+ return PJ_TRUE;
+ }
+
+ pj_cstr(&method, supplement_method);
+
+ return pj_stristr(&method, message_method) ? PJ_TRUE : PJ_FALSE;
+}
+
+/*! \brief Structure to hold information about an outbound request */
+struct send_request_data {
+ struct ast_sip_endpoint *endpoint; /*! The endpoint associated with this request */
+ void *token; /*! Information to be provided to the callback upon receipt of a response */
+ void (*callback)(void *token, pjsip_event *e); /*! The callback to be called upon receipt of a response */
+};
+
+static void send_request_data_destroy(void *obj)
+{
+ struct send_request_data *req_data = obj;
+ ao2_cleanup(req_data->endpoint);
+}
+
+static struct send_request_data *send_request_data_alloc(struct ast_sip_endpoint *endpoint,
+ void *token, void (*callback)(void *token, pjsip_event *e))
+{
+ struct send_request_data *req_data = ao2_alloc(sizeof(*req_data), send_request_data_destroy);
+
+ if (!req_data) {
+ return NULL;
+ }
+
+ req_data->endpoint = ao2_bump(endpoint);
+ req_data->token = token;
+ req_data->callback = callback;
+
+ return req_data;
+}
+
static void send_request_cb(void *token, pjsip_event *e)
{
- RAII_VAR(struct ast_sip_endpoint *, endpoint, token, ao2_cleanup);
+ RAII_VAR(struct send_request_data *, req_data, token, ao2_cleanup);
pjsip_transaction *tsx = e->body.tsx_state.tsx;
pjsip_rx_data *challenge = e->body.tsx_state.src.rdata;
pjsip_tx_data *tdata;
+ struct ast_sip_supplement *supplement;
+
+ AST_RWLIST_RDLOCK(&supplements);
+ AST_LIST_TRAVERSE(&supplements, supplement, next) {
+ if (supplement->incoming_response && does_method_match(&challenge->msg_info.cseq->method.name, supplement->method)) {
+ supplement->incoming_response(req_data->endpoint, challenge);
+ }
+ }
+ AST_RWLIST_UNLOCK(&supplements);
- if (tsx->status_code != 401 && tsx->status_code != 407) {
+ if (tsx->status_code == 401 || tsx->status_code == 407) {
+ if (!ast_sip_create_request_with_auth(&req_data->endpoint->outbound_auths, challenge, tsx, &tdata)) {
+ pjsip_endpt_send_request(ast_sip_get_pjsip_endpoint(), tdata, -1, req_data->token, req_data->callback);
+ }
return;
}
- if (!ast_sip_create_request_with_auth(&endpoint->outbound_auths, challenge, tsx, &tdata)) {
- pjsip_endpt_send_request(ast_sip_get_pjsip_endpoint(), tdata, -1, NULL, NULL);
+ if (req_data->callback) {
+ req_data->callback(req_data->token, e);
}
}
-static int send_out_of_dialog_request(pjsip_tx_data *tdata, struct ast_sip_endpoint *endpoint)
+static int send_out_of_dialog_request(pjsip_tx_data *tdata, struct ast_sip_endpoint *endpoint,
+ void *token, void (*callback)(void *token, pjsip_event *e))
{
- ao2_ref(endpoint, +1);
- if (pjsip_endpt_send_request(ast_sip_get_pjsip_endpoint(), tdata, -1, endpoint, send_request_cb) != PJ_SUCCESS) {
+ struct ast_sip_supplement *supplement;
+ struct send_request_data *req_data = send_request_data_alloc(endpoint, token, callback);
+ struct ast_sip_contact *contact = ast_sip_mod_data_get(tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT);
+
+ if (!req_data) {
+ return -1;
+ }
+
+ AST_RWLIST_RDLOCK(&supplements);
+ AST_LIST_TRAVERSE(&supplements, supplement, next) {
+ if (supplement->outgoing_request && does_method_match(&tdata->msg->line.req.method.name, supplement->method)) {
+ supplement->outgoing_request(endpoint, contact, tdata);
+ }
+ }
+ AST_RWLIST_UNLOCK(&supplements);
+
+ ast_sip_mod_data_set(tdata->pool, tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT, NULL);
+ ao2_cleanup(contact);
+
+ if (pjsip_endpt_send_request(ast_sip_get_pjsip_endpoint(), tdata, -1, req_data, send_request_cb) != PJ_SUCCESS) {
ast_log(LOG_ERROR, "Error attempting to send outbound %.*s request to endpoint %s\n",
(int) pj_strlen(&tdata->msg->line.req.method.name),
pj_strbuf(&tdata->msg->line.req.method.name),
ast_sorcery_object_get_id(endpoint));
- ao2_ref(endpoint, -1);
+ ao2_cleanup(req_data);
return -1;
}
return 0;
}
-int ast_sip_send_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg, struct ast_sip_endpoint *endpoint)
+int ast_sip_send_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg,
+ struct ast_sip_endpoint *endpoint, void *token,
+ void (*callback)(void *token, pjsip_event *e))
{
ast_assert(tdata->msg->type == PJSIP_REQUEST_MSG);
if (dlg) {
return send_in_dialog_request(tdata, dlg);
} else {
- return send_out_of_dialog_request(tdata, endpoint);
+ return send_out_of_dialog_request(tdata, endpoint, token, callback);
}
}
@@ -2011,6 +2149,57 @@ void *ast_sip_dict_set(pj_pool_t* pool, void *ht,
return ht;
}
+static pj_bool_t supplement_on_rx_request(pjsip_rx_data *rdata)
+{
+ struct ast_sip_supplement *supplement;
+
+ if (pjsip_rdata_get_dlg(rdata)) {
+ return PJ_FALSE;
+ }
+
+ AST_RWLIST_RDLOCK(&supplements);
+ AST_LIST_TRAVERSE(&supplements, supplement, next) {
+ if (supplement->incoming_request && does_method_match(&rdata->msg_info.msg->line.req.method.name, supplement->method)) {
+ supplement->incoming_request(ast_pjsip_rdata_get_endpoint(rdata), rdata);
+ }
+ }
+ AST_RWLIST_UNLOCK(&supplements);
+
+ return PJ_FALSE;
+}
+
+int ast_sip_send_response(pjsip_response_addr *res_addr, pjsip_tx_data *tdata, struct ast_sip_endpoint *sip_endpoint)
+{
+ struct ast_sip_supplement *supplement;
+ pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
+ struct ast_sip_contact *contact = ast_sip_mod_data_get(tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT);
+
+ AST_RWLIST_RDLOCK(&supplements);
+ AST_LIST_TRAVERSE(&supplements, supplement, next) {
+ if (supplement->outgoing_response && does_method_match(&cseq->method.name, supplement->method)) {
+ supplement->outgoing_response(sip_endpoint, contact, tdata);
+ }
+ }
+ AST_RWLIST_UNLOCK(&supplements);
+
+ ast_sip_mod_data_set(tdata->pool, tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT, NULL);
+ ao2_cleanup(contact);
+
+ return pjsip_endpt_send_response(ast_sip_get_pjsip_endpoint(), res_addr, tdata, NULL, NULL);
+}
+
+int ast_sip_create_response(const pjsip_rx_data *rdata, int st_code,
+ struct ast_sip_contact *contact, pjsip_tx_data **tdata)
+{
+ int res = pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, st_code, NULL, tdata);
+
+ if (!res) {
+ ast_sip_mod_data_set((*tdata)->pool, (*tdata)->mod_data, supplement_module.id, MOD_DATA_CONTACT, ao2_bump(contact));
+ }
+
+ return res;
+}
+
static void remove_request_headers(pjsip_endpoint *endpt)
{
const pjsip_hdr *request_headers = pjsip_endpt_get_request_headers(endpt);
@@ -2128,8 +2317,23 @@ static int load_module(void)
return AST_MODULE_LOAD_DECLINE;
}
+ if (ast_sip_register_service(&supplement_module)) {
+ ast_log(LOG_ERROR, "Failed to initialize supplement hooks. Aborting load\n");
+ ast_sip_destroy_distributor();
+ ast_res_pjsip_destroy_configuration();
+ ast_sip_destroy_global_headers();
+ stop_monitor_thread();
+ pj_pool_release(memory_pool);
+ memory_pool = NULL;
+ pjsip_endpt_destroy(ast_pjsip_endpoint);
+ ast_pjsip_endpoint = NULL;
+ pj_caching_pool_destroy(&caching_pool);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
if (ast_sip_initialize_outbound_authentication()) {
ast_log(LOG_ERROR, "Failed to initialize outbound authentication. Aborting load\n");
+ ast_sip_unregister_service(&supplement_module);
ast_sip_destroy_distributor();
ast_res_pjsip_destroy_configuration();
ast_sip_destroy_global_headers();
diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c
index 31eaeeee4..499ee9a6c 100644
--- a/res/res_pjsip/location.c
+++ b/res/res_pjsip/location.c
@@ -178,7 +178,7 @@ struct ast_sip_contact *ast_sip_location_retrieve_contact(const char *contact_na
return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "contact", contact_name);
}
-int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri, struct timeval expiration_time)
+int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri, struct timeval expiration_time, const char *path_info)
{
char name[AST_UUID_STR_LEN];
RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
@@ -193,6 +193,9 @@ int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri, struc
contact->expiration_time = expiration_time;
contact->qualify_frequency = aor->qualify_frequency;
contact->authenticate_qualify = aor->authenticate_qualify;
+ if (path_info && aor->support_path) {
+ ast_string_field_set(contact, path, path_info);
+ }
if (!ast_strlen_zero(aor->outbound_proxy)) {
ast_string_field_set(contact, outbound_proxy, aor->outbound_proxy);
@@ -610,6 +613,7 @@ int ast_sip_initialize_sorcery_location(struct ast_sorcery *sorcery)
ast_sorcery_object_field_register(sorcery, "contact", "type", "", OPT_NOOP_T, 0, 0);
ast_sorcery_object_field_register(sorcery, "contact", "uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, uri));
+ ast_sorcery_object_field_register(sorcery, "contact", "path", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, path));
ast_sorcery_object_field_register_custom(sorcery, "contact", "expiration_time", "", expiration_str2struct, expiration_struct2str, 0, 0);
ast_sorcery_object_field_register(sorcery, "contact", "qualify_frequency", 0, OPT_UINT_T,
PARSE_IN_RANGE, FLDSET(struct ast_sip_contact, qualify_frequency), 0, 86400);
@@ -626,6 +630,7 @@ int ast_sip_initialize_sorcery_location(struct ast_sorcery *sorcery)
ast_sorcery_object_field_register_custom(sorcery, "aor", "contact", "", permanent_uri_handler, NULL, 0, 0);
ast_sorcery_object_field_register(sorcery, "aor", "mailboxes", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_aor, mailboxes));
ast_sorcery_object_field_register(sorcery, "aor", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_aor, outbound_proxy));
+ ast_sorcery_object_field_register(sorcery, "aor", "support_path", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, support_path));
ast_sip_register_endpoint_formatter(&endpoint_aor_formatter);
ast_sip_register_cli_formatter(&cli_contact_formatter);
diff --git a/res/res_pjsip/pjsip_distributor.c b/res/res_pjsip/pjsip_distributor.c
index 5cc645cc3..69cbed784 100644
--- a/res/res_pjsip/pjsip_distributor.c
+++ b/res/res_pjsip/pjsip_distributor.c
@@ -326,7 +326,7 @@ static pj_bool_t authenticate(pjsip_rx_data *rdata)
static pjsip_module auth_mod = {
.name = {"Request Authenticator", 21},
- .priority = PJSIP_MOD_PRIORITY_APPLICATION - 1,
+ .priority = PJSIP_MOD_PRIORITY_APPLICATION - 2,
.on_rx_request = authenticate,
};
diff --git a/res/res_pjsip/pjsip_options.c b/res/res_pjsip/pjsip_options.c
index 0409c1557..2f7998d3b 100644
--- a/res/res_pjsip/pjsip_options.c
+++ b/res/res_pjsip/pjsip_options.c
@@ -34,7 +34,7 @@
#define DEFAULT_ENCODING "text/plain"
#define QUALIFIED_BUCKETS 211
-static int qualify_contact(struct ast_sip_contact *contact);
+static int qualify_contact(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact);
/*!
* \internal
@@ -186,7 +186,7 @@ static int on_endpoint(void *obj, void *arg, int flags)
* \internal
* \brief Find endpoints associated with the given contact.
*/
-static struct ao2_container *find_endpoints(struct ast_sip_contact *contact)
+static struct ao2_iterator *find_endpoints(struct ast_sip_contact *contact)
{
RAII_VAR(struct ao2_container *, endpoints,
ast_sip_get_endpoints(), ao2_cleanup);
@@ -201,43 +201,15 @@ static struct ao2_container *find_endpoints(struct ast_sip_contact *contact)
static void qualify_contact_cb(void *token, pjsip_event *e)
{
RAII_VAR(struct ast_sip_contact *, contact, token, ao2_cleanup);
- RAII_VAR(struct ao2_container *, endpoints, NULL, ao2_cleanup);
- RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
-
- pjsip_transaction *tsx = e->body.tsx_state.tsx;
- pjsip_rx_data *challenge = e->body.tsx_state.src.rdata;
- pjsip_tx_data *tdata;
switch(e->body.tsx_state.type) {
case PJSIP_EVENT_TRANSPORT_ERROR:
case PJSIP_EVENT_TIMER:
update_contact_status(contact, UNAVAILABLE);
- return;
- default:
break;
- }
-
- if (!contact->authenticate_qualify || (tsx->status_code != 401 &&
- tsx->status_code != 407)) {
+ default:
update_contact_status(contact, AVAILABLE);
- return;
- }
-
- /* try to find endpoints that are associated with the contact */
- if (!(endpoints = find_endpoints(contact))) {
- ast_log(LOG_ERROR, "No endpoints found for contact %s, cannot authenticate",
- contact->uri);
- return;
- }
-
- /* find "first" endpoint in order to authenticate - actually any
- endpoint should do that matched on the contact */
- endpoint = ao2_callback(endpoints, 0, NULL, NULL);
-
- if (!ast_sip_create_request_with_auth(&endpoint->outbound_auths,
- challenge, tsx, &tdata)) {
- pjsip_endpt_send_request(ast_sip_get_pjsip_endpoint(), tdata,
- -1, NULL, NULL);
+ break;
}
}
@@ -248,11 +220,25 @@ static void qualify_contact_cb(void *token, pjsip_event *e)
* \detail Sends a SIP OPTIONS request to the given contact in order to make
* sure that contact is available.
*/
-static int qualify_contact(struct ast_sip_contact *contact)
+static int qualify_contact(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact)
{
pjsip_tx_data *tdata;
+ RAII_VAR(struct ast_sip_endpoint *, endpoint_local, ao2_bump(endpoint), ao2_cleanup);
+
+
+ if (!endpoint_local) {
+ struct ao2_iterator *endpoint_iterator = find_endpoints(contact);
+
+ /* try to find endpoints that are associated with the contact */
+ if (endpoint_iterator) {
+ /* find "first" endpoint in order to authenticate - actually any
+ endpoint should do that matched on the contact */
+ endpoint_local = ao2_iterator_next(endpoint_iterator);
+ ao2_iterator_destroy(endpoint_iterator);
+ }
+ }
- if (ast_sip_create_request("OPTIONS", NULL, NULL, contact->uri, &tdata)) {
+ if (ast_sip_create_request("OPTIONS", NULL, NULL, NULL, contact, &tdata)) {
ast_log(LOG_ERROR, "Unable to create request to qualify contact %s\n",
contact->uri);
return -1;
@@ -270,8 +256,8 @@ static int qualify_contact(struct ast_sip_contact *contact)
init_start_time(contact);
ao2_ref(contact, +1);
- if (pjsip_endpt_send_request(ast_sip_get_pjsip_endpoint(),
- tdata, -1, contact, qualify_contact_cb) != PJ_SUCCESS) {
+ if (ast_sip_send_request(tdata, NULL, endpoint_local, contact,
+ qualify_contact_cb) != PJ_SUCCESS) {
/* The callback will be called so we don't need to drop the contact ref*/
ast_log(LOG_ERROR, "Unable to send request to qualify contact %s\n",
contact->uri);
@@ -339,7 +325,7 @@ static struct sched_data *sched_data_create(struct ast_sip_contact *contact)
static int qualify_contact_task(void *obj)
{
RAII_VAR(struct ast_sip_contact *, contact, obj, ao2_cleanup);
- return qualify_contact(contact);
+ return qualify_contact(NULL, contact);
}
/*!
@@ -489,8 +475,7 @@ static pj_status_t send_options_response(pjsip_rx_data *rdata, int code)
pj_status_t status;
/* Make the response object */
- if ((status = pjsip_endpt_create_response(
- endpt, rdata, code, NULL, &tdata) != PJ_SUCCESS)) {
+ if ((status = ast_sip_create_response(rdata, code, NULL, &tdata) != PJ_SUCCESS)) {
ast_log(LOG_ERROR, "Unable to create response (%d)\n", status);
return status;
}
@@ -527,8 +512,8 @@ static pj_status_t send_options_response(pjsip_rx_data *rdata, int code)
pjsip_tx_data_dec_ref(tdata);
return status;
}
- status = pjsip_endpt_send_response(endpt, &res_addr, tdata,
- NULL, NULL);
+ status = ast_sip_send_response(&res_addr, tdata,
+ ast_pjsip_rdata_get_endpoint(rdata));
}
if (status != PJ_SUCCESS) {
@@ -586,12 +571,13 @@ static pjsip_module options_module = {
* \internal
* \brief Send qualify request to the given contact.
*/
-static int cli_on_contact(void *obj, void *arg, int flags)
+static int cli_on_contact(void *obj, void *arg, void *data, int flags)
{
struct ast_sip_contact *contact = obj;
+ struct ast_sip_endpoint *endpoint = data;
int *cli_fd = arg;
ast_cli(*cli_fd, " contact %s\n", contact->uri);
- qualify_contact(contact);
+ qualify_contact(endpoint, contact);
return 0;
}
@@ -655,7 +641,7 @@ static int cli_qualify_contacts(void *data)
}
ast_cli(cli_fd, "Sending qualify to endpoint %s\n", endpoint_name);
- ao2_callback(contacts, OBJ_NODATA, cli_on_contact, &cli_fd);
+ ao2_callback_data(contacts, OBJ_NODATA, cli_on_contact, &cli_fd, endpoint);
}
return 0;
}
diff --git a/res/res_pjsip_caller_id.c b/res/res_pjsip_caller_id.c
index 1b25be28c..5996f13f3 100644
--- a/res/res_pjsip_caller_id.c
+++ b/res/res_pjsip_caller_id.c
@@ -689,7 +689,7 @@ static void caller_id_outgoing_response(struct ast_sip_session *session, pjsip_t
static struct ast_sip_session_supplement caller_id_supplement = {
.method = "INVITE,UPDATE",
- .priority = AST_SIP_SESSION_SUPPLEMENT_PRIORITY_CHANNEL - 1000,
+ .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL - 1000,
.incoming_request = caller_id_incoming_request,
.incoming_response = caller_id_incoming_response,
.outgoing_request = caller_id_outgoing_request,
diff --git a/res/res_pjsip_diversion.c b/res/res_pjsip_diversion.c
index 358ef2644..0b4e2106c 100644
--- a/res/res_pjsip_diversion.c
+++ b/res/res_pjsip_diversion.c
@@ -320,7 +320,7 @@ static struct ast_sip_session_supplement diversion_supplement = {
.method = "INVITE",
/* this supplement needs to be called after caller id
and after the channel has been created */
- .priority = AST_SIP_SESSION_SUPPLEMENT_PRIORITY_CHANNEL + 100,
+ .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL + 100,
.incoming_request = diversion_incoming_request,
.incoming_response = diversion_incoming_response,
.outgoing_request = diversion_outgoing_request,
diff --git a/res/res_pjsip_header_funcs.c b/res/res_pjsip_header_funcs.c
index 063fbb761..4861cd0a9 100644
--- a/res/res_pjsip_header_funcs.c
+++ b/res/res_pjsip_header_funcs.c
@@ -596,7 +596,7 @@ static void outgoing_request(struct ast_sip_session *session, pjsip_tx_data * td
static struct ast_sip_session_supplement header_funcs_supplement = {
.method = "INVITE",
- .priority = AST_SIP_SESSION_SUPPLEMENT_PRIORITY_CHANNEL - 1000,
+ .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL - 1000,
.incoming_request = incoming_request,
.outgoing_request = outgoing_request,
};
diff --git a/res/res_pjsip_messaging.c b/res/res_pjsip_messaging.c
index bb3a51cdf..0f884aeda 100644
--- a/res/res_pjsip_messaging.c
+++ b/res/res_pjsip_messaging.c
@@ -577,7 +577,7 @@ static int msg_send(void *data)
return -1;
}
- if (ast_sip_create_request("MESSAGE", NULL, endpoint, uri, &tdata)) {
+ if (ast_sip_create_request("MESSAGE", NULL, endpoint, uri, NULL, &tdata)) {
ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not create request\n");
return -1;
}
@@ -593,7 +593,7 @@ static int msg_send(void *data)
vars_to_headers(mdata->msg, tdata);
- if (ast_sip_send_request(tdata, NULL, endpoint)) {
+ if (ast_sip_send_request(tdata, NULL, endpoint, NULL, NULL)) {
ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not send request\n");
return -1;
}
@@ -630,9 +630,7 @@ static pj_status_t send_response(pjsip_rx_data *rdata, enum pjsip_status_code co
pj_status_t status;
pjsip_response_addr res_addr;
- pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
-
- status = pjsip_endpt_create_response(endpt, rdata, code, NULL, &tdata);
+ status = ast_sip_create_response(rdata, code, NULL, &tdata);
if (status != PJ_SUCCESS) {
ast_log(LOG_ERROR, "Unable to create response (%d)\n", status);
return status;
@@ -647,7 +645,7 @@ static pj_status_t send_response(pjsip_rx_data *rdata, enum pjsip_status_code co
ast_log(LOG_ERROR, "Unable to get response address (%d)\n", status);
return status;
}
- status = pjsip_endpt_send_response(endpt, &res_addr, tdata, NULL, NULL);
+ status = ast_sip_send_response(&res_addr, tdata, ast_pjsip_rdata_get_endpoint(rdata));
}
if (status != PJ_SUCCESS) {
diff --git a/res/res_pjsip_mwi.c b/res/res_pjsip_mwi.c
index 4f32f382a..42aa34f1e 100644
--- a/res/res_pjsip_mwi.c
+++ b/res/res_pjsip_mwi.c
@@ -273,7 +273,7 @@ static int send_unsolicited_mwi_notify_to_contact(void *obj, void *arg, int flag
pjsip_event_hdr *event;
const pjsip_hdr *allow_events = pjsip_evsub_get_allow_events_hdr(NULL);
- if (ast_sip_create_request("NOTIFY", NULL, endpoint, contact->uri, &tdata)) {
+ if (ast_sip_create_request("NOTIFY", NULL, endpoint, NULL, contact, &tdata)) {
ast_log(LOG_WARNING, "Unable to create unsolicited NOTIFY request to endpoint %s URI %s\n", sub->id, contact->uri);
return 0;
}
@@ -310,7 +310,7 @@ static int send_unsolicited_mwi_notify_to_contact(void *obj, void *arg, int flag
pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, allow_events));
msg_body = pjsip_msg_body_create(tdata->pool, &mwi_type->type, &mwi_type->subtype, body_text);
tdata->msg->body = msg_body;
- ast_sip_send_request(tdata, NULL, endpoint);
+ ast_sip_send_request(tdata, NULL, endpoint, NULL, NULL);
return 0;
}
diff --git a/res/res_pjsip_nat.c b/res/res_pjsip_nat.c
index 3416fffc6..092ff008b 100644
--- a/res/res_pjsip_nat.c
+++ b/res/res_pjsip_nat.c
@@ -241,7 +241,7 @@ static void nat_outgoing_invite_request(struct ast_sip_session *session, struct
/*! \brief Supplement for adding NAT functionality to dialog */
static struct ast_sip_session_supplement nat_supplement = {
.method = "INVITE",
- .priority = AST_SIP_SESSION_SUPPLEMENT_PRIORITY_FIRST + 1,
+ .priority = AST_SIP_SUPPLEMENT_PRIORITY_FIRST + 1,
.incoming_request = nat_incoming_invite_request,
.outgoing_request = nat_outgoing_invite_request,
};
diff --git a/res/res_pjsip_notify.c b/res/res_pjsip_notify.c
index 8e9b20261..b2f24cbf2 100644
--- a/res/res_pjsip_notify.c
+++ b/res/res_pjsip_notify.c
@@ -470,7 +470,7 @@ static int notify_contact(void *obj, void *arg, int flags)
pjsip_tx_data *tdata;
if (ast_sip_create_request("NOTIFY", NULL, data->endpoint,
- contact->uri, &tdata)) {
+ NULL, contact, &tdata)) {
ast_log(LOG_WARNING, "SIP NOTIFY - Unable to create request for "
"contact %s\n", contact->uri);
return -1;
@@ -479,7 +479,7 @@ static int notify_contact(void *obj, void *arg, int flags)
ast_sip_add_header(tdata, "Subscription-State", "terminated");
data->build_notify(tdata, data->info);
- if (ast_sip_send_request(tdata, NULL, data->endpoint)) {
+ if (ast_sip_send_request(tdata, NULL, data->endpoint, NULL, NULL)) {
ast_log(LOG_ERROR, "SIP NOTIFY - Unable to send request for "
"contact %s\n", contact->uri);
return -1;
diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c
index 63f34a014..55279c58c 100644
--- a/res/res_pjsip_outbound_registration.c
+++ b/res/res_pjsip_outbound_registration.c
@@ -116,6 +116,14 @@
<configOption name="type">
<synopsis>Must be of type 'registration'.</synopsis>
</configOption>
+ <configOption name="support_path">
+ <synopsis>Enables Path support for outbound REGISTER requests.</synopsis>
+ <description><para>
+ When this option is enabled, outbound REGISTER requests will advertise
+ support for Path headers so that intervening proxies can add to the Path
+ header as necessary.
+ </para></description>
+ </configOption>
</configObject>
</configFile>
</configInfo>
@@ -189,6 +197,8 @@ struct sip_outbound_registration_client_state {
unsigned int forbidden_retry_interval;
/*! \brief Treat authentication challenges that we cannot handle as permanent failures */
unsigned int auth_rejection_permanent;
+ /*! \brief Determines whether SIP Path support should be advertised */
+ unsigned int support_path;
/*! \brief Serializer for stuff and things */
struct ast_taskprocessor *serializer;
/*! \brief Configured authentication credentials */
@@ -234,6 +244,8 @@ struct sip_outbound_registration {
struct sip_outbound_registration_state *state;
/*! \brief Configured authentication credentials */
struct ast_sip_auth_vector outbound_auths;
+ /*! \brief Whether Path support is enabled */
+ unsigned int support_path;
};
/*! \brief Helper function which cancels the timer on a client */
@@ -245,6 +257,8 @@ static void cancel_registration(struct sip_outbound_registration_client_state *c
}
}
+static pj_str_t PATH_NAME = { "path", 4 };
+
/*! \brief Callback function for registering */
static int handle_client_registration(void *data)
{
@@ -266,6 +280,24 @@ static int handle_client_registration(void *data)
ast_debug(3, "REGISTER attempt %d to '%s' with client '%s'\n",
client_state->retries + 1, server_uri, client_uri);
+ if (client_state->support_path) {
+ pjsip_supported_hdr *hdr;
+
+ hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_SUPPORTED, NULL);
+ if (!hdr) {
+ /* insert a new Supported header */
+ hdr = pjsip_supported_hdr_create(tdata->pool);
+ if (!hdr) {
+ return -1;
+ }
+
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);
+ }
+
+ /* add on to the existing Supported header */
+ pj_strassign(&hdr->values[hdr->count++], &PATH_NAME);
+ }
+
/* Due to the registration the callback may now get called, so bump the ref count */
ao2_ref(client_state, +1);
if (pjsip_regc_send(client_state->client, tdata) != PJ_SUCCESS) {
@@ -818,6 +850,7 @@ static int sip_outbound_registration_perform(void *data)
registration->state->client_state->forbidden_retry_interval = registration->forbidden_retry_interval;
registration->state->client_state->max_retries = registration->max_retries;
registration->state->client_state->retries = 0;
+ registration->state->client_state->support_path = registration->support_path;
pjsip_regc_update_expires(registration->state->client_state->client, registration->expiration);
@@ -1100,6 +1133,7 @@ static int load_module(void)
ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "max_retries", "10", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, max_retries));
ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "auth_rejection_permanent", "yes", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, auth_rejection_permanent));
ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "registration", "outbound_auth", "", outbound_auth_handler, outbound_auths_to_str, 0, 0);
+ ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "support_path", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, support_path));
ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration");
sip_outbound_registration_perform_all();
diff --git a/res/res_pjsip_path.c b/res/res_pjsip_path.c
new file mode 100644
index 000000000..28d8b589f
--- /dev/null
+++ b/res/res_pjsip_path.c
@@ -0,0 +1,250 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Kinsey Moore <kmoore@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*** MODULEINFO
+ <depend>pjproject</depend>
+ <depend>res_pjsip</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include <pjsip.h>
+#include <pjsip_ua.h>
+
+#include "asterisk/res_pjsip.h"
+#include "asterisk/res_pjsip_session.h"
+#include "asterisk/module.h"
+#include "asterisk/strings.h"
+
+static const pj_str_t PATH_NAME = { "Path", 4 };
+static pj_str_t PATH_SUPPORTED_NAME = { "path", 4 };
+
+static struct ast_sip_aor *find_aor(struct ast_sip_endpoint *endpoint, pjsip_uri *uri)
+{
+ char *configured_aors, *aor_name;
+ pjsip_sip_uri *sip_uri;
+ char *domain_name;
+ RAII_VAR(struct ast_str *, id, NULL, ast_free);
+
+ if (ast_strlen_zero(endpoint->aors)) {
+ return NULL;
+ }
+
+ sip_uri = pjsip_uri_get_uri(uri);
+ domain_name = ast_alloca(sip_uri->host.slen + 1);
+ ast_copy_pj_str(domain_name, &sip_uri->host, sip_uri->host.slen + 1);
+
+ configured_aors = ast_strdupa(endpoint->aors);
+
+ /* Iterate the configured AORs to see if the user or the user+domain match */
+ while ((aor_name = strsep(&configured_aors, ","))) {
+ struct ast_sip_domain_alias *alias = NULL;
+
+ if (!pj_strcmp2(&sip_uri->user, aor_name)) {
+ break;
+ }
+
+ if (!id && !(id = ast_str_create(sip_uri->user.slen + sip_uri->host.slen + 2))) {
+ return NULL;
+ }
+
+ ast_str_set(&id, 0, "%.*s@", (int)sip_uri->user.slen, sip_uri->user.ptr);
+ if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) {
+ ast_str_append(&id, 0, "%s", alias->domain);
+ ao2_cleanup(alias);
+ } else {
+ ast_str_append(&id, 0, "%s", domain_name);
+ }
+
+ if (!strcmp(aor_name, ast_str_buffer(id))) {
+ ast_free(id);
+ break;
+ }
+ }
+
+ if (ast_strlen_zero(aor_name)) {
+ return NULL;
+ }
+
+ return ast_sip_location_retrieve_aor(aor_name);
+}
+
+/*!
+ * \brief Get the path string associated with this contact and tdata
+ *
+ * \param endpoint The endpoint from which to pull associated path data
+ * \param contact_uri The URI identifying the associated contact
+ * \param path_str The place to store the retrieved path information
+ *
+ * \retval zero on success
+ * \retval non-zero on failure or no available path information
+ */
+static int path_get_string(pj_pool_t *pool, struct ast_sip_contact *contact, pj_str_t *path_str)
+{
+ if (!contact || ast_strlen_zero(contact->path)) {
+ return -1;
+ }
+
+ *path_str = pj_strdup3(pool, contact->path);
+ return 0;
+}
+
+static int add_supported(pjsip_tx_data *tdata)
+{
+ pjsip_supported_hdr *hdr;
+
+ hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_SUPPORTED, NULL);
+ if (!hdr) {
+ /* insert a new Supported header */
+ hdr = pjsip_supported_hdr_create(tdata->pool);
+ if (!hdr) {
+ return -1;
+ }
+
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);
+ }
+
+ /* add on to the existing Supported header */
+ pj_strassign(&hdr->values[hdr->count++], &PATH_SUPPORTED_NAME);
+
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Adds a Route header to an outgoing request if
+ * path information is available.
+ *
+ * \param endpoint The endpoint with which this request is associated
+ * \param contact The contact to which this request is being sent
+ * \param tdata The outbound request
+ */
+static void path_outgoing_request(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, pjsip_tx_data *tdata)
+{
+ RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
+
+ if (!endpoint) {
+ return;
+ }
+
+ aor = find_aor(endpoint, tdata->msg->line.req.uri);
+ if (!aor || !aor->support_path) {
+ return;
+ }
+
+ if (add_supported(tdata)) {
+ return;
+ }
+
+ if (contact && !ast_strlen_zero(contact->path)) {
+ ast_sip_set_outbound_proxy(tdata, contact->path);
+ }
+}
+
+static void path_session_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
+{
+ path_outgoing_request(session->endpoint, session->contact, tdata);
+}
+
+/*!
+ * \internal
+ * \brief Adds a path header to an outgoing 2XX response
+ *
+ * \param endpoint The endpoint to which the INVITE response is to be sent
+ * \param contact The contact to which the INVITE response is to be sent
+ * \param tdata The outbound INVITE response
+ */
+static void path_outgoing_response(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, pjsip_tx_data *tdata)
+{
+ struct pjsip_status_line status = tdata->msg->line.status;
+ pj_str_t path_dup;
+ pjsip_generic_string_hdr *path_hdr;
+ pjsip_contact_hdr *contact_hdr;
+ RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
+ pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
+ const pj_str_t REGISTER_METHOD = {"REGISTER", 8};
+
+ if (!endpoint
+ || !pj_stristr(&REGISTER_METHOD, &cseq->method.name)
+ || !PJSIP_IS_STATUS_IN_CLASS(status.code, 200)) {
+ return;
+ }
+
+ contact_hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
+ if (!contact_hdr) {
+ return;
+ }
+
+ aor = find_aor(endpoint, contact_hdr->uri);
+ if (!aor || !aor->support_path || add_supported(tdata)
+ || path_get_string(tdata->pool, contact, &path_dup)) {
+ return;
+ }
+
+ path_hdr = pjsip_generic_string_hdr_create(tdata->pool, &PATH_NAME, &path_dup);
+ if (!path_hdr) {
+ return;
+ }
+
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)path_hdr);
+}
+
+static void path_session_outgoing_response(struct ast_sip_session *session, pjsip_tx_data *tdata)
+{
+ path_outgoing_response(session->endpoint, session->contact, tdata);
+}
+
+static struct ast_sip_supplement path_supplement = {
+ .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL - 100,
+ .outgoing_request = path_outgoing_request,
+ .outgoing_response = path_outgoing_response,
+};
+
+static struct ast_sip_session_supplement path_session_supplement = {
+ .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL - 100,
+ .outgoing_request = path_session_outgoing_request,
+ .outgoing_response = path_session_outgoing_response,
+};
+
+static int load_module(void)
+{
+ if (ast_sip_register_supplement(&path_supplement)) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ if (ast_sip_session_register_supplement(&path_session_supplement)) {
+ ast_sip_unregister_supplement(&path_supplement);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ ast_sip_unregister_supplement(&path_supplement);
+ ast_sip_session_unregister_supplement(&path_session_supplement);
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Path Header Support",
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_APP_DEPEND,
+);
diff --git a/res/res_pjsip_refer.c b/res/res_pjsip_refer.c
index 7664f1a22..916cf5eb9 100644
--- a/res/res_pjsip_refer.c
+++ b/res/res_pjsip_refer.c
@@ -919,7 +919,7 @@ static void refer_outgoing_request(struct ast_sip_session *session, struct pjsip
}
static struct ast_sip_session_supplement refer_supplement = {
- .priority = AST_SIP_SESSION_SUPPLEMENT_PRIORITY_CHANNEL + 1,
+ .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL + 1,
.incoming_request = refer_incoming_request,
.outgoing_request = refer_outgoing_request,
};
diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c
index 89e8cd141..7624472fc 100644
--- a/res/res_pjsip_registrar.c
+++ b/res/res_pjsip_registrar.c
@@ -347,6 +347,64 @@ static struct rx_task_data *rx_task_data_create(pjsip_rx_data *rdata,
return task_data;
}
+static const pj_str_t path_hdr_name = { "Path", 4 };
+
+static int build_path_data(struct rx_task_data *task_data, struct ast_str **path_str)
+{
+ pjsip_generic_string_hdr *path_hdr = pjsip_msg_find_hdr_by_name(task_data->rdata->msg_info.msg, &path_hdr_name, NULL);
+
+ if (!path_hdr) {
+ return 0;
+ }
+
+ *path_str = ast_str_create(64);
+ if (!path_str) {
+ return -1;
+ }
+
+ ast_str_set(path_str, 0, "%.*s", (int)path_hdr->hvalue.slen, path_hdr->hvalue.ptr);
+
+ while ((path_hdr = (pjsip_generic_string_hdr *) pjsip_msg_find_hdr_by_name(task_data->rdata->msg_info.msg, &path_hdr_name, path_hdr->next))) {
+ ast_str_append(path_str, 0, ",%.*s", (int)path_hdr->hvalue.slen, path_hdr->hvalue.ptr);
+ }
+
+ return 0;
+}
+
+static int registrar_validate_path(struct rx_task_data *task_data, struct ast_str **path_str)
+{
+ const pj_str_t path_supported_name = { "path", 4 };
+ pjsip_supported_hdr *supported_hdr;
+ int i;
+
+ if (!task_data->aor->support_path) {
+ return 0;
+ }
+
+ if (build_path_data(task_data, path_str)) {
+ return -1;
+ }
+
+ if (!*path_str) {
+ return 0;
+ }
+
+ supported_hdr = pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_SUPPORTED, NULL);
+ if (!supported_hdr) {
+ return -1;
+ }
+
+ /* Find advertised path support */
+ for (i = 0; i < supported_hdr->count; i++) {
+ if (!pj_stricmp(&supported_hdr->values[i], &path_supported_name)) {
+ return 0;
+ }
+ }
+
+ /* Path header present, but support not advertised */
+ return -1;
+}
+
static int rx_task(void *data)
{
RAII_VAR(struct rx_task_data *, task_data, data, ao2_cleanup);
@@ -358,6 +416,8 @@ static int rx_task(void *data)
pjsip_tx_data *tdata;
pjsip_response_addr addr;
const char *aor_name = ast_sorcery_object_get_id(task_data->aor);
+ RAII_VAR(struct ast_str *, path_str, NULL, ast_free);
+ struct ast_sip_contact *response_contact;
/* Retrieve the current contacts, we'll need to know whether to update or not */
contacts = ast_sip_location_retrieve_aor_contacts(task_data->aor);
@@ -374,6 +434,14 @@ static int rx_task(void *data)
return PJ_TRUE;
}
+ if (registrar_validate_path(task_data, &path_str)) {
+ /* Ensure that intervening proxies did not make invalid modifications to the request */
+ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 420, NULL, NULL, NULL);
+ ast_log(LOG_WARNING, "Invalid modifications made to REGISTER request from '%s' by intervening proxy\n",
+ ast_sorcery_object_get_id(task_data->endpoint));
+ return PJ_TRUE;
+ }
+
if ((MAX(added - deleted, 0) + (!task_data->aor->remove_existing ? ao2_container_count(contacts) : 0)) > task_data->aor->max_contacts) {
/* Enforce the maximum number of contacts */
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 403, NULL, NULL, NULL);
@@ -417,7 +485,8 @@ static int rx_task(void *data)
continue;
}
- ast_sip_location_add_contact(task_data->aor, contact_uri, ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)));
+ ast_sip_location_add_contact(task_data->aor, contact_uri, ast_tvadd(ast_tvnow(),
+ ast_samp2tv(expiration, 1)), path_str ? ast_str_buffer(path_str) : NULL);
ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n",
contact_uri, aor_name, expiration);
ast_test_suite_event_notify("AOR_CONTACT_ADDED",
@@ -432,6 +501,9 @@ static int rx_task(void *data)
updated->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1));
updated->qualify_frequency = task_data->aor->qualify_frequency;
updated->authenticate_qualify = task_data->aor->authenticate_qualify;
+ if (path_str) {
+ ast_string_field_set(updated, path, ast_str_buffer(path_str));
+ }
ast_sip_location_update_contact(updated);
ast_debug(3, "Refreshed contact '%s' on AOR '%s' with new expiration of %d seconds\n",
@@ -465,12 +537,16 @@ static int rx_task(void *data)
/* Update the contacts as things will probably have changed */
ao2_cleanup(contacts);
+
contacts = ast_sip_location_retrieve_aor_contacts(task_data->aor);
+ response_contact = ao2_callback(contacts, 0, NULL, NULL);
/* Send a response containing all of the contacts (including static) that are present on this AOR */
- if (pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), task_data->rdata, 200, NULL, &tdata) != PJ_SUCCESS) {
+ if (ast_sip_create_response(task_data->rdata, 200, response_contact, &tdata) != PJ_SUCCESS) {
+ ao2_cleanup(response_contact);
return PJ_TRUE;
}
+ ao2_cleanup(response_contact);
/* Add the date header to the response, some UAs use this to set their date and time */
registrar_add_date_header(tdata);
@@ -478,7 +554,7 @@ static int rx_task(void *data)
ao2_callback(contacts, 0, registrar_add_contact, tdata);
if (pjsip_get_response_addr(tdata->pool, task_data->rdata, &addr) == PJ_SUCCESS) {
- pjsip_endpt_send_response(ast_sip_get_pjsip_endpoint(), &addr, tdata, NULL, NULL);
+ ast_sip_send_response(&addr, tdata, task_data->endpoint);
} else {
pjsip_tx_data_dec_ref(tdata);
}
@@ -495,8 +571,9 @@ static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata)
ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup);
RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
pjsip_sip_uri *uri;
- char user_name[64], domain_name[64];
+ char *domain_name;
char *configured_aors, *aor_name;
+ RAII_VAR(struct ast_str *, id, NULL, ast_free);
if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method) || !endpoint) {
return PJ_FALSE;
@@ -518,29 +595,33 @@ static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata)
}
uri = pjsip_uri_get_uri(rdata->msg_info.to->uri);
- ast_copy_pj_str(user_name, &uri->user, sizeof(user_name));
- ast_copy_pj_str(domain_name, &uri->host, sizeof(domain_name));
+ domain_name = ast_alloca(uri->host.slen + 1);
+ ast_copy_pj_str(domain_name, &uri->host, uri->host.slen + 1);
configured_aors = ast_strdupa(endpoint->aors);
/* Iterate the configured AORs to see if the user or the user+domain match */
while ((aor_name = strsep(&configured_aors, ","))) {
- char id[AST_UUID_STR_LEN];
- RAII_VAR(struct ast_sip_domain_alias *, alias, NULL, ao2_cleanup);
+ struct ast_sip_domain_alias *alias = NULL;
- snprintf(id, sizeof(id), "%s@%s", user_name, domain_name);
- if (!strcmp(aor_name, id)) {
+ if (!pj_strcmp2(&uri->user, aor_name)) {
break;
}
+ if (!id && !(id = ast_str_create(uri->user.slen + uri->host.slen + 2))) {
+ return PJ_TRUE;
+ }
+
+ ast_str_set(&id, 0, "%.*s@", (int)uri->user.slen, uri->user.ptr);
if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) {
- snprintf(id, sizeof(id), "%s@%s", user_name, alias->domain);
- if (!strcmp(aor_name, id)) {
- break;
- }
+ ast_str_append(&id, 0, "%s", alias->domain);
+ ao2_cleanup(alias);
+ } else {
+ ast_str_append(&id, 0, "%s", domain_name);
}
- if (!strcmp(aor_name, user_name)) {
+ if (!strcmp(aor_name, ast_str_buffer(id))) {
+ ast_free(id);
break;
}
}
@@ -549,7 +630,7 @@ static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata)
/* The provided AOR name was not found (be it within the configuration or sorcery itself) */
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL);
ast_sip_report_req_no_support(endpoint, rdata, "registrar_requested_aor_not_found");
- ast_log(LOG_WARNING, "AOR '%s' not found for endpoint '%s'\n", user_name, ast_sorcery_object_get_id(endpoint));
+ ast_log(LOG_WARNING, "AOR '%.*s' not found for endpoint '%s'\n", (int)uri->user.slen, uri->user.ptr, ast_sorcery_object_get_id(endpoint));
return PJ_TRUE;
}
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index a00299ebb..03d2e577a 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -1045,6 +1045,7 @@ static void session_destructor(void *obj)
}
ast_party_id_free(&session->id);
ao2_cleanup(session->endpoint);
+ ao2_cleanup(session->contact);
ast_format_cap_destroy(session->req_caps);
ast_format_cap_destroy(session->direct_media_cap);
@@ -1112,7 +1113,8 @@ struct ast_sip_channel_pvt *ast_sip_channel_pvt_alloc(void *pvt, struct ast_sip_
return channel;
}
-struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint, pjsip_inv_session *inv_session)
+struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint,
+ struct ast_sip_contact *contact, pjsip_inv_session *inv_session)
{
RAII_VAR(struct ast_sip_session *, session, ao2_alloc(sizeof(*session), session_destructor), ao2_cleanup);
struct ast_sip_session_supplement *iter;
@@ -1140,10 +1142,9 @@ struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint,
ast_sip_dialog_set_serializer(inv_session->dlg, session->serializer);
ast_sip_dialog_set_endpoint(inv_session->dlg, endpoint);
pjsip_dlg_inc_session(inv_session->dlg, &session_module);
- ao2_ref(session, +1);
- inv_session->mod_data[session_module.id] = session;
- ao2_ref(endpoint, +1);
- session->endpoint = endpoint;
+ inv_session->mod_data[session_module.id] = ao2_bump(session);
+ session->endpoint = ao2_bump(endpoint);
+ session->contact = ao2_bump(contact);
session->inv_session = inv_session;
session->req_caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK);
@@ -1192,21 +1193,27 @@ static int session_outbound_auth(pjsip_dialog *dlg, pjsip_tx_data *tdata, void *
return 0;
}
-struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint *endpoint, const char *location, const char *request_user, struct ast_format_cap *req_caps)
+struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint *endpoint,
+ struct ast_sip_contact *contact, const char *location, const char *request_user,
+ struct ast_format_cap *req_caps)
{
const char *uri = NULL;
- RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
+ RAII_VAR(struct ast_sip_contact *, found_contact, NULL, ao2_cleanup);
pjsip_timer_setting timer;
pjsip_dialog *dlg;
struct pjsip_inv_session *inv_session;
RAII_VAR(struct ast_sip_session *, session, NULL, ao2_cleanup);
/* If no location has been provided use the AOR list from the endpoint itself */
- location = S_OR(location, endpoint->aors);
+ if (location || !contact) {
+ location = S_OR(location, endpoint->aors);
- contact = ast_sip_location_retrieve_contact_from_aor_list(location);
- if (!contact || ast_strlen_zero(contact->uri)) {
- uri = location;
+ found_contact = ast_sip_location_retrieve_contact_from_aor_list(location);
+ if (!found_contact || ast_strlen_zero(found_contact->uri)) {
+ uri = location;
+ } else {
+ uri = found_contact->uri;
+ }
} else {
uri = contact->uri;
}
@@ -1238,7 +1245,7 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint
timer.sess_expires = endpoint->extensions.timer.sess_expires;
pjsip_timer_init_session(inv_session, &timer);
- if (!(session = ast_sip_session_alloc(endpoint, inv_session))) {
+ if (!(session = ast_sip_session_alloc(endpoint, found_contact ? found_contact : contact, inv_session))) {
pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
return NULL;
}
@@ -1534,7 +1541,7 @@ static void handle_new_invite_request(pjsip_rx_data *rdata)
return;
}
- session = ast_sip_session_alloc(endpoint, inv_session);
+ session = ast_sip_session_alloc(endpoint, NULL, inv_session);
if (!session) {
if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) == PJ_SUCCESS) {
pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
diff --git a/res/res_pjsip_t38.c b/res/res_pjsip_t38.c
index afe12505d..1bb36ca1d 100644
--- a/res/res_pjsip_t38.c
+++ b/res/res_pjsip_t38.c
@@ -498,7 +498,7 @@ static unsigned int t38_get_rate(enum ast_control_t38_rate rate)
/*! \brief Supplement for adding framehook to session channel */
static struct ast_sip_session_supplement t38_supplement = {
.method = "INVITE",
- .priority = AST_SIP_SESSION_SUPPLEMENT_PRIORITY_CHANNEL + 1,
+ .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL + 1,
.incoming_request = t38_incoming_invite_request,
.outgoing_request = t38_outgoing_invite_request,
};