summaryrefslogtreecommitdiff
path: root/res/res_pjsip.c
diff options
context:
space:
mode:
Diffstat (limited to 'res/res_pjsip.c')
-rw-r--r--res/res_pjsip.c234
1 files changed, 219 insertions, 15 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();