PJSIP: Add Path header support
authorKinsey Moore <kmoore@digium.com>
Wed, 15 Jan 2014 13:16:10 +0000 (13:16 +0000)
committerKinsey Moore <kmoore@digium.com>
Wed, 15 Jan 2014 13:16:10 +0000 (13:16 +0000)
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

21 files changed:
channels/chan_pjsip.c
contrib/ast-db-manage/config/versions/2fc7930b41b3_add_pjsip_endpoint_options_for_12_1.py [new file with mode: 0755]
include/asterisk/res_pjsip.h
include/asterisk/res_pjsip_session.h
res/res_pjsip.c
res/res_pjsip/location.c
res/res_pjsip/pjsip_distributor.c
res/res_pjsip/pjsip_options.c
res/res_pjsip_caller_id.c
res/res_pjsip_diversion.c
res/res_pjsip_header_funcs.c
res/res_pjsip_messaging.c
res/res_pjsip_mwi.c
res/res_pjsip_nat.c
res/res_pjsip_notify.c
res/res_pjsip_outbound_registration.c
res/res_pjsip_path.c [new file with mode: 0644]
res/res_pjsip_refer.c
res/res_pjsip_registrar.c
res/res_pjsip_session.c
res/res_pjsip_t38.c

index bab4be5..4a1a6aa 100644 (file)
@@ -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 (executable)
index 0000000..979aea8
--- /dev/null
@@ -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')
index 04a24cd..1fea42f 100644 (file)
@@ -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 */
index 615a621..0d79c6b 100644 (file)
@@ -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
index e6f00a8..bd84181 100644 (file)
                                                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>
                                                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>
        </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();
index 31eaeee..499ee9a 100644 (file)
@@ -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);
index 5cc645c..69cbed7 100644 (file)
@@ -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,
 };
 
index 0409c15..2f7998d 100644 (file)
@@ -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;
 }
index 1b25be2..5996f13 100644 (file)
@@ -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,
index 358ef26..0b4e210 100644 (file)
@@ -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,
index 063fbb7..4861cd0 100644 (file)
@@ -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,
 };
index bb3a51c..0f884ae 100644 (file)
@@ -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) {
index 4f32f38..42aa34f 100644 (file)
@@ -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;
 }
index 3416fff..092ff00 100644 (file)
@@ -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,
 };
index 8e9b202..b2f24cb 100644 (file)
@@ -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;
index 63f34a0..55279c5 100644 (file)
                                <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 (file)
index 0000000..28d8b58
--- /dev/null
@@ -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,
+);
index 7664f1a..916cf5e 100644 (file)
@@ -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,
 };
index 89e8cd1..7624472 100644 (file)
@@ -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;
        }
 
index a00299e..03d2e57 100644 (file)
@@ -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);
index afe1250..1bb36ca 100644 (file)
@@ -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,
 };