summaryrefslogtreecommitdiff
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
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
-rw-r--r--channels/chan_pjsip.c16
-rwxr-xr-xcontrib/ast-db-manage/config/versions/2fc7930b41b3_add_pjsip_endpoint_options_for_12_1.py32
-rw-r--r--include/asterisk/res_pjsip.h153
-rw-r--r--include/asterisk/res_pjsip_session.h27
-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
21 files changed, 869 insertions, 136 deletions
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index bab4be581..4a1a6aa2f 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -129,7 +129,7 @@ static void chan_pjsip_incoming_response(struct ast_sip_session *session, struct
/*! \brief SIP session supplement structure */
static struct ast_sip_session_supplement chan_pjsip_supplement = {
.method = "INVITE",
- .priority = AST_SIP_SESSION_SUPPLEMENT_PRIORITY_CHANNEL,
+ .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL,
.session_begin = chan_pjsip_session_begin,
.session_end = chan_pjsip_session_end,
.incoming_request = chan_pjsip_incoming_request,
@@ -140,7 +140,7 @@ static int chan_pjsip_incoming_ack(struct ast_sip_session *session, struct pjsip
static struct ast_sip_session_supplement chan_pjsip_ack_supplement = {
.method = "ACK",
- .priority = AST_SIP_SESSION_SUPPLEMENT_PRIORITY_CHANNEL,
+ .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL,
.incoming_request = chan_pjsip_incoming_ack,
};
@@ -863,7 +863,7 @@ static int transmit_info_with_vidupdate(void *data)
RAII_VAR(struct ast_sip_session *, session, data, ao2_cleanup);
struct pjsip_tx_data *tdata;
- if (ast_sip_create_request("INFO", session->inv_session->dlg, session->endpoint, NULL, &tdata)) {
+ if (ast_sip_create_request("INFO", session->inv_session->dlg, session->endpoint, NULL, NULL, &tdata)) {
ast_log(LOG_ERROR, "Could not create text video update INFO request\n");
return -1;
}
@@ -1261,7 +1261,7 @@ static int transmit_info_dtmf(void *data)
body.body_text = ast_str_buffer(body_text);
- if (ast_sip_create_request("INFO", session->inv_session->dlg, session->endpoint, NULL, &tdata)) {
+ if (ast_sip_create_request("INFO", session->inv_session->dlg, session->endpoint, NULL, NULL, &tdata)) {
ast_log(LOG_ERROR, "Could not create DTMF INFO request\n");
return -1;
}
@@ -1539,7 +1539,7 @@ static int request(void *obj)
return -1;
}
- if (!(session = ast_sip_session_create_outgoing(endpoint, args.aor, request_user, req_data->caps))) {
+ if (!(session = ast_sip_session_create_outgoing(endpoint, NULL, args.aor, request_user, req_data->caps))) {
req_data->cause = AST_CAUSE_NO_ROUTE_DESTINATION;
return -1;
}
@@ -1618,9 +1618,9 @@ static int sendtext(void *obj)
ast_debug(3, "Sending in dialog SIP message\n");
- ast_sip_create_request("MESSAGE", data->session->inv_session->dlg, data->session->endpoint, NULL, &tdata);
+ ast_sip_create_request("MESSAGE", data->session->inv_session->dlg, data->session->endpoint, NULL, NULL, &tdata);
ast_sip_add_body(tdata, &body);
- ast_sip_send_request(tdata, data->session->inv_session->dlg, data->session->endpoint);
+ ast_sip_send_request(tdata, data->session->inv_session->dlg, data->session->endpoint, NULL, NULL);
return 0;
}
@@ -1831,7 +1831,7 @@ static int pbx_start_incoming_request(struct ast_sip_session *session, pjsip_rx_
static struct ast_sip_session_supplement pbx_start_supplement = {
.method = "INVITE",
- .priority = AST_SIP_SESSION_SUPPLEMENT_PRIORITY_LAST,
+ .priority = AST_SIP_SUPPLEMENT_PRIORITY_LAST,
.incoming_request = pbx_start_incoming_request,
};
diff --git a/contrib/ast-db-manage/config/versions/2fc7930b41b3_add_pjsip_endpoint_options_for_12_1.py b/contrib/ast-db-manage/config/versions/2fc7930b41b3_add_pjsip_endpoint_options_for_12_1.py
new file mode 100755
index 000000000..979aea8f9
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/2fc7930b41b3_add_pjsip_endpoint_options_for_12_1.py
@@ -0,0 +1,32 @@
+"""Add pjsip endpoint options for 12.1
+
+Revision ID: 2fc7930b41b3
+Revises: 581a4264e537
+Create Date: 2014-01-14 09:23:53.923454
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '2fc7930b41b3'
+down_revision = '581a4264e537'
+
+from alembic import op
+import sqlalchemy as sa
+
+YESNO_VALUES = ['yes', 'no']
+REDIRECT_METHODS = ['user', 'uri_core', 'uri_pjsip']
+
+def upgrade():
+ op.add_column('ps_endpoints', sa.Column('redirect_method', sa.Enum(*REDIRECT_METHODS, name='redirect_methods')))
+ op.add_column('ps_endpoints', sa.Column('set_var', sa.Text()))
+ op.add_column('ps_contacts', sa.Column('path', sa.Text()))
+ op.add_column('ps_aors', sa.Column('support_path', sa.Enum(*YESNO_VALUES, name='yesno_values')))
+ op.add_column('ps_registrations', sa.Column('support_path', sa.Enum(*YESNO_VALUES, name='yesno_values')))
+
+
+def downgrade():
+ op.drop_column('ps_endpoints', 'redirect_method')
+ op.drop_column('ps_endpoints', 'set_var')
+ op.drop_column('ps_contacts', 'path')
+ op.drop_column('ps_aors', 'support_path')
+ op.drop_column('ps_registrations', 'support_path')
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index 04a24cde3..1fea42f2b 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -151,6 +151,8 @@ struct ast_sip_contact {
AST_STRING_FIELD(uri);
/*! Outbound proxy to use for qualify */
AST_STRING_FIELD(outbound_proxy);
+ /*! Path information to place in Route headers */
+ AST_STRING_FIELD(path);
);
/*! Absolute time that this contact is no longer valid after */
struct timeval expiration_time;
@@ -214,6 +216,8 @@ struct ast_sip_aor {
unsigned int remove_existing;
/*! Any permanent configured contacts */
struct ao2_container *permanent_contacts;
+ /*! Determines whether SIP Path headers are supported */
+ unsigned int support_path;
};
/*!
@@ -901,11 +905,13 @@ struct ast_sip_contact *ast_sip_location_retrieve_contact(const char *contact_na
* \param aor Pointer to the AOR
* \param uri Full contact URI
* \param expiration_time Optional expiration time of the contact
+ * \param path_info Path information
*
* \retval -1 failure
* \retval 0 success
*/
-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);
/*!
* \brief Update a contact
@@ -1209,18 +1215,22 @@ pjsip_dialog *ast_sip_create_dialog_uas(const struct ast_sip_endpoint *endpoint,
*
* \param method The method of the SIP request to send
* \param dlg Optional. If specified, the dialog on which to request the message.
- * \param endpoint Optional. If specified, the request will be created out-of-dialog
- * to the endpoint.
+ * \param endpoint Optional. If specified, the request will be created out-of-dialog to the endpoint.
* \param uri Optional. If specified, the request will be sent to this URI rather
- * this value.
* than one configured for the endpoint.
+ * \param contact The contact with which this request is associated for out-of-dialog requests.
* \param[out] tdata The newly-created request
+ *
+ * The provided contact is attached to tdata with its reference bumped, but will
+ * not survive for the entire lifetime of tdata since the contact is cleaned up
+ * when all supplements have completed execution.
+ *
* \retval 0 Success
* \retval -1 Failure
*/
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);
/*!
* \brief General purpose method for sending a SIP request
@@ -1235,10 +1245,48 @@ int ast_sip_create_request(const char *method, struct pjsip_dialog *dlg,
* \param tdata The request to send
* \param dlg Optional. If specified, the dialog on which the request should be sent
* \param endpoint Optional. If specified, the request is sent out-of-dialog to the endpoint.
+ * \param token Data to be passed to the callback upon receipt of response
+ * \param callback Callback to be called upon receipt of response
+ *
* \retval 0 Success
* \retval -1 Failure
*/
-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));
+
+/*!
+ * \brief General purpose method for creating a SIP response
+ *
+ * Its typical use would be to create responses for out of dialog
+ * requests.
+ *
+ * \param rdata The rdata from the incoming request.
+ * \param st_code The response code to transmit.
+ * \param contact The contact with which this request is associated.
+ * \param[out] tdata The newly-created response
+ *
+ * The provided contact is attached to tdata with its reference bumped, but will
+ * not survive for the entire lifetime of tdata since the contact is cleaned up
+ * when all supplements have completed execution.
+ *
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_create_response(const pjsip_rx_data *rdata, int st_code,
+ struct ast_sip_contact *contact, pjsip_tx_data **p_tdata);
+
+/*!
+ * \brief Send a response to an out of dialog request
+ *
+ * \param res_addr The response address for this response
+ * \param tdata The response to send
+ * \param endpoint The ast_sip_endpoint associated with this response
+ *
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_send_response(pjsip_response_addr *res_addr, pjsip_tx_data *tdata, struct ast_sip_endpoint *sip_endpoint);
/*!
* \brief Determine if an incoming request requires authentication
@@ -1745,4 +1793,97 @@ int ast_sip_for_each_channel(const struct ast_sip_endpoint *endpoint,
ao2_callback_fn on_channel_snapshot,
void *arg);
+enum ast_sip_supplement_priority {
+ /*! Top priority. Supplements with this priority are those that need to run before any others */
+ AST_SIP_SUPPLEMENT_PRIORITY_FIRST = 0,
+ /*! Channel creation priority.
+ * chan_pjsip creates a channel at this priority. If your supplement depends on being run before
+ * or after channel creation, then set your priority to be lower or higher than this value.
+ */
+ AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL = 1000000,
+ /*! Lowest priority. Supplements with this priority should be run after all other supplements */
+ AST_SIP_SUPPLEMENT_PRIORITY_LAST = INT_MAX,
+};
+
+/*!
+ * \brief A supplement to SIP message processing
+ *
+ * These can be registered by any module in order to add
+ * processing to incoming and outgoing SIP out of dialog
+ * requests and responses
+ */
+struct ast_sip_supplement {
+ /*! Method on which to call the callbacks. If NULL, call on all methods */
+ const char *method;
+ /*! Priority for this supplement. Lower numbers are visited before higher numbers */
+ enum ast_sip_supplement_priority priority;
+ /*!
+ * \brief Called on incoming SIP request
+ * This method can indicate a failure in processing in its return. If there
+ * is a failure, it is required that this method sends a response to the request.
+ * This method is always called from a SIP servant thread.
+ *
+ * \note
+ * The following PJSIP methods will not work properly:
+ * pjsip_rdata_get_dlg()
+ * pjsip_rdata_get_tsx()
+ * The reason is that the rdata passed into this function is a cloned rdata structure,
+ * and its module data is not copied during the cloning operation.
+ * If you need to get the dialog, you can get it via session->inv_session->dlg.
+ *
+ * \note
+ * There is no guarantee that a channel will be present on the session when this is called.
+ */
+ int (*incoming_request)(struct ast_sip_endpoint *endpoint, struct pjsip_rx_data *rdata);
+ /*!
+ * \brief Called on an incoming SIP response
+ * This method is always called from a SIP servant thread.
+ *
+ * \note
+ * The following PJSIP methods will not work properly:
+ * pjsip_rdata_get_dlg()
+ * pjsip_rdata_get_tsx()
+ * The reason is that the rdata passed into this function is a cloned rdata structure,
+ * and its module data is not copied during the cloning operation.
+ * If you need to get the dialog, you can get it via session->inv_session->dlg.
+ *
+ * \note
+ * There is no guarantee that a channel will be present on the session when this is called.
+ */
+ void (*incoming_response)(struct ast_sip_endpoint *endpoint, struct pjsip_rx_data *rdata);
+ /*!
+ * \brief Called on an outgoing SIP request
+ * This method is always called from a SIP servant thread.
+ */
+ void (*outgoing_request)(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, struct pjsip_tx_data *tdata);
+ /*!
+ * \brief Called on an outgoing SIP response
+ * This method is always called from a SIP servant thread.
+ */
+ void (*outgoing_response)(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, struct pjsip_tx_data *tdata);
+ /*! Next item in the list */
+ AST_LIST_ENTRY(ast_sip_supplement) next;
+};
+
+/*!
+ * \brief Register a supplement to SIP out of dialog processing
+ *
+ * This allows for someone to insert themselves in the processing of out
+ * of dialog SIP requests and responses. This, for example could allow for
+ * a module to set channel data based on headers in an incoming message.
+ * Similarly, a module could reject an incoming request if desired.
+ *
+ * \param supplement The supplement to register
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_register_supplement(struct ast_sip_supplement *supplement);
+
+/*!
+ * \brief Unregister a an supplement to SIP out of dialog processing
+ *
+ * \param supplement The supplement to unregister
+ */
+void ast_sip_unregister_supplement(struct ast_sip_supplement *supplement);
+
#endif /* _RES_PJSIP_H */
diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h
index 615a6214c..0d79c6b3a 100644
--- a/include/asterisk/res_pjsip_session.h
+++ b/include/asterisk/res_pjsip_session.h
@@ -98,6 +98,8 @@ struct ast_sip_session {
char exten[AST_MAX_EXTENSION];
/* The endpoint with which Asterisk is communicating */
struct ast_sip_endpoint *endpoint;
+ /* The contact associated with this session */
+ struct ast_sip_contact *contact;
/* The PJSIP details of the session, which includes the dialog */
struct pjsip_inv_session *inv_session;
/* The Asterisk channel associated with the session */
@@ -138,18 +140,6 @@ typedef int (*ast_sip_session_request_creation_cb)(struct ast_sip_session *sessi
typedef int (*ast_sip_session_response_cb)(struct ast_sip_session *session, pjsip_rx_data *rdata);
typedef int (*ast_sip_session_sdp_creation_cb)(struct ast_sip_session *session, pjmedia_sdp_session *sdp);
-enum ast_sip_session_supplement_priority {
- /*! Top priority. Supplements with this priority are those that need to run before any others */
- AST_SIP_SESSION_SUPPLEMENT_PRIORITY_FIRST = 0,
- /*! Channel creation priority.
- * chan_pjsip creates a channel at this priority. If your supplement depends on being run before
- * or after channel creation, then set your priority to be lower or higher than this value.
- */
- AST_SIP_SESSION_SUPPLEMENT_PRIORITY_CHANNEL = 1000000,
- /*! Lowest priority. Supplements with this priority should be run after all other supplements */
- AST_SIP_SESSION_SUPPLEMENT_PRIORITY_LAST = INT_MAX,
-};
-
/*!
* \brief A supplement to SIP message processing
*
@@ -160,7 +150,7 @@ struct ast_sip_session_supplement {
/*! Method on which to call the callbacks. If NULL, call on all methods */
const char *method;
/*! Priority for this supplement. Lower numbers are visited before higher numbers */
- enum ast_sip_session_supplement_priority priority;
+ enum ast_sip_supplement_priority priority;
/*!
* \brief Notification that the session has begun
* This method will always be called from a SIP servant thread.
@@ -342,9 +332,11 @@ struct ast_sip_channel_pvt *ast_sip_channel_pvt_alloc(void *pvt, struct ast_sip_
* this reference when the session is destroyed.
*
* \param endpoint The endpoint that this session communicates with
+ * \param contact The contact associated with this session
* \param inv_session The PJSIP INVITE session data
*/
-struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint, pjsip_inv_session *inv);
+struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint,
+ struct ast_sip_contact *contact, pjsip_inv_session *inv);
/*!
* \brief Create a new outgoing SIP session
@@ -354,11 +346,14 @@ struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint,
* this reference when the session is destroyed.
*
* \param endpoint The endpoint that this session uses for settings
- * \param location Optional name of the location to call, be it named location or explicit URI
+ * \param contact The contact that this session will communicate with
+ * \param location Name of the location to call, be it named location or explicit URI. Overrides contact if present.
* \param request_user Optional request user to place in the request URI if permitted
* \param req_caps The requested capabilities
*/
-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);
/*!
* \brief Defer local termination of a session until remote side terminates, or an amount of time passes
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,
};