diff options
-rw-r--r-- | channels/chan_pjsip.c | 16 | ||||
-rwxr-xr-x | contrib/ast-db-manage/config/versions/2fc7930b41b3_add_pjsip_endpoint_options_for_12_1.py | 32 | ||||
-rw-r--r-- | include/asterisk/res_pjsip.h | 153 | ||||
-rw-r--r-- | include/asterisk/res_pjsip_session.h | 27 | ||||
-rw-r--r-- | res/res_pjsip.c | 234 | ||||
-rw-r--r-- | res/res_pjsip/location.c | 7 | ||||
-rw-r--r-- | res/res_pjsip/pjsip_distributor.c | 2 | ||||
-rw-r--r-- | res/res_pjsip/pjsip_options.c | 74 | ||||
-rw-r--r-- | res/res_pjsip_caller_id.c | 2 | ||||
-rw-r--r-- | res/res_pjsip_diversion.c | 2 | ||||
-rw-r--r-- | res/res_pjsip_header_funcs.c | 2 | ||||
-rw-r--r-- | res/res_pjsip_messaging.c | 10 | ||||
-rw-r--r-- | res/res_pjsip_mwi.c | 4 | ||||
-rw-r--r-- | res/res_pjsip_nat.c | 2 | ||||
-rw-r--r-- | res/res_pjsip_notify.c | 4 | ||||
-rw-r--r-- | res/res_pjsip_outbound_registration.c | 34 | ||||
-rw-r--r-- | res/res_pjsip_path.c | 250 | ||||
-rw-r--r-- | res/res_pjsip_refer.c | 2 | ||||
-rw-r--r-- | res/res_pjsip_registrar.c | 113 | ||||
-rw-r--r-- | res/res_pjsip_session.c | 33 | ||||
-rw-r--r-- | res/res_pjsip_t38.c | 2 |
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(®ISTER_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, }; |