diff --git a/CHANGES b/CHANGES index 5e4c428469..260aa2f3cf 100644 --- a/CHANGES +++ b/CHANGES @@ -231,6 +231,13 @@ res_pjproject res_pjsip ------------------ + * Transports are now reloadable. In testing, no in-progress calls were + disrupted if the ip address or port weren't changed, but the possibility + still exists. To make sure there are no unintentional drops, a new option + 'allow_reload', which defaults to 'no' has been added to transport. If + left at the default, changes to the particular transport will be ignored. + If set to 'yes', changes (if any) will be applied. + * Added new global option (regcontext) to pjsip. When set, Asterisk will dynamically create and destroy a NoOp priority 1 extension for a given endpoint who registers or unregisters with us. diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample index 5c326f2c57..2d127a1dcd 100644 --- a/configs/samples/pjsip.conf.sample +++ b/configs/samples/pjsip.conf.sample @@ -818,6 +818,12 @@ ; clients are slow to process the received ; information. Value is in milliseconds; default ; is 100 ms. +;allow_reload=no ; Although transports can now be reloaded, that may not be + ; desirable because of the slight possibility of dropped + ; calls. To make sure there are no unintentional drops, if + ; this option is set to 'no' (the default) changes to the + ; particular transport will be ignored. If set to 'yes', + ; changes (if any) will be applied. ;==========================AOR SECTION OPTIONS========================= ;[aor] diff --git a/contrib/ast-db-manage/config/versions/3bcc0b5bc2c9_add_allow_reload_to_ps_transports.py b/contrib/ast-db-manage/config/versions/3bcc0b5bc2c9_add_allow_reload_to_ps_transports.py new file mode 100644 index 0000000000..377179b045 --- /dev/null +++ b/contrib/ast-db-manage/config/versions/3bcc0b5bc2c9_add_allow_reload_to_ps_transports.py @@ -0,0 +1,27 @@ +"""Add allow_reload to ps_transports + +Revision ID: 3bcc0b5bc2c9 +Revises: dbc44d5a908 +Create Date: 2016-02-05 17:43:39.183785 + +""" + +# revision identifiers, used by Alembic. +revision = '3bcc0b5bc2c9' +down_revision = 'dbc44d5a908' + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects.postgresql import ENUM + +YESNO_NAME = 'yesno_values' +YESNO_VALUES = ['yes', 'no'] + +def upgrade(): + yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False) + op.add_column('ps_transports', sa.Column('allow_reload', yesno_values)) + pass + +def downgrade(): + op.drop_column('ps_transports', 'allow_reload') + pass diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index ad34c8f5af..3008475c30 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -185,6 +185,8 @@ struct ast_sip_transport { unsigned int cos; /*! Write timeout */ int write_timeout; + /*! Allow reload */ + int allow_reload; }; #define SIP_SORCERY_DOMAIN_ALIAS_TYPE "domain_alias" @@ -2223,4 +2225,26 @@ struct ast_sip_transport_state *ast_sip_get_transport_state(const char *transpor */ struct ao2_container *ast_sip_get_transport_states(void); +/*! + * \brief Sets pjsip_tpselector from ast_sip_transport + * \since 13.8.0 + * + * \param transport The transport to be used + * \param selector The selector to be populated + * \retval 0 success + * \retval -1 failure + */ +int ast_sip_set_tpselector_from_transport(const struct ast_sip_transport *transport, pjsip_tpselector *selector); + +/*! + * \brief Sets pjsip_tpselector from ast_sip_transport + * \since 13.8.0 + * + * \param transport_name The name of the transport to be used + * \param selector The selector to be populated + * \retval 0 success + * \retval -1 failure + */ +int ast_sip_set_tpselector_from_transport_name(const char *transport_name, pjsip_tpselector *selector); + #endif /* _RES_PJSIP_H */ diff --git a/res/res_pjsip.c b/res/res_pjsip.c index e355292536..713d94ec50 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -1029,6 +1029,14 @@ Value is in milliseconds; default is 100 ms. + + Allow this transport to be reloaded. + + Allow this transport to be reloaded when res_pjsip is reloaded. + This option defaults to "no" because reloading a transport may disrupt + in-progress calls. + + A way of creating an aliased name to a SIP URI @@ -2479,22 +2487,14 @@ static int sip_dialog_create_from(pj_pool_t *pool, pj_str_t *from, const char *u return 0; } -static int sip_get_tpselector_from_endpoint(const struct ast_sip_endpoint *endpoint, pjsip_tpselector *selector) +int ast_sip_set_tpselector_from_transport(const struct ast_sip_transport *transport, pjsip_tpselector *selector) { - RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup); RAII_VAR(struct ast_sip_transport_state *, transport_state, NULL, ao2_cleanup); - const char *transport_name = endpoint->transport; - - if (ast_strlen_zero(transport_name)) { - return 0; - } - - transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", transport_name); - transport_state = ast_sip_get_transport_state(transport_name); - if (!transport || !transport_state) { - ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport '%s' for endpoint '%s'\n", - transport_name, ast_sorcery_object_get_id(endpoint)); + transport_state = ast_sip_get_transport_state(ast_sorcery_object_get_id(transport)); + if (!transport_state) { + ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport state for '%s'\n", + ast_sorcery_object_get_id(transport)); return -1; } @@ -2517,6 +2517,35 @@ static int sip_get_tpselector_from_endpoint(const struct ast_sip_endpoint *endpo return 0; } +int ast_sip_set_tpselector_from_transport_name(const char *transport_name, pjsip_tpselector *selector) +{ + RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup); + + if (ast_strlen_zero(transport_name)) { + return 0; + } + + transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", transport_name); + if (!transport) { + ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport '%s'\n", + transport_name); + return -1; + } + + return ast_sip_set_tpselector_from_transport(transport, selector); +} + +static int sip_get_tpselector_from_endpoint(const struct ast_sip_endpoint *endpoint, pjsip_tpselector *selector) +{ + const char *transport_name = endpoint->transport; + + if (ast_strlen_zero(transport_name)) { + return 0; + } + + return ast_sip_set_tpselector_from_transport_name(endpoint->transport, selector); +} + void ast_sip_add_usereqphone(const struct ast_sip_endpoint *endpoint, pj_pool_t *pool, pjsip_uri *uri) { pjsip_sip_uri *sip_uri; diff --git a/res/res_pjsip/config_transport.c b/res/res_pjsip/config_transport.c index 0fcd7d923c..e7bda5f056 100644 --- a/res/res_pjsip/config_transport.c +++ b/res/res_pjsip/config_transport.c @@ -18,6 +18,7 @@ #include "asterisk.h" +#include #include #include @@ -347,6 +348,44 @@ static void copy_state_to_transport(struct ast_sip_transport *transport) memcpy(&transport->external_address, &transport->state->external_address, sizeof(transport->external_address)); } +static int has_state_changed(struct ast_sip_transport_state *a, struct ast_sip_transport_state *b) +{ + if (a->type != b->type) { + return -1; + } + + if (pj_sockaddr_cmp(&a->host, &b->host)) { + return -1; + } + + if ((a->localnet || b->localnet) + && ((!a->localnet != !b->localnet) + || ast_sockaddr_cmp(&a->localnet->addr, &b->localnet->addr) + || ast_sockaddr_cmp(&a->localnet->netmask, &b->localnet->netmask))) + { + return -1; + } + + if (ast_sockaddr_cmp(&a->external_address, &b->external_address)) { + return -1; + } + + if (a->tls.method != b->tls.method + || a->tls.ciphers_num != b->tls.ciphers_num + || a->tls.proto != b->tls.proto + || a->tls.verify_client != b->tls.verify_client + || a->tls.verify_server != b->tls.verify_server + || a->tls.require_client_cert != b->tls.require_client_cert) { + return -1; + } + + if (memcmp(a->ciphers, b->ciphers, sizeof(pj_ssl_cipher) * fmax(a->tls.ciphers_num, b->tls.ciphers_num))) { + return -1; + } + + return 0; +} + static void states_cleanup(void *states) { if (states) { @@ -364,6 +403,9 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj) RAII_VAR(struct internal_state *, perm_state, NULL, ao2_cleanup); RAII_VAR(struct ast_variable *, changes, NULL, ast_variables_destroy); pj_status_t res = -1; + int i; +#define BIND_TRIES 3 +#define BIND_DELAY_US 100000 if (!states) { return -1; @@ -376,32 +418,39 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj) */ ao2_wrlock(states); + temp_state = internal_state_alloc(transport); + if (!temp_state) { + ast_log(LOG_ERROR, "Transport '%s' failed to allocate memory\n", transport_id); + return -1; + } + perm_state = find_internal_state_by_transport(transport); if (perm_state) { ast_sorcery_diff(sorcery, perm_state->transport, transport, &changes); - if (changes) { + if (!changes && !has_state_changed(perm_state->state, temp_state->state)) { + /* In case someone is using the deprecated fields, reset them */ + transport->state = perm_state->state; + copy_state_to_transport(transport); + ao2_replace(perm_state->transport, transport); + return 0; + } + + if (!transport->allow_reload) { if (!perm_state->change_detected) { perm_state->change_detected = 1; ast_log(LOG_WARNING, "Transport '%s' is not reloadable, maintaining previous values\n", transport_id); } + /* In case someone is using the deprecated fields, reset them */ + transport->state = perm_state->state; + copy_state_to_transport(transport); + ao2_replace(perm_state->transport, transport); + return 0; } - - /* In case someone is using the deprecated fields, reset them */ - transport->state = perm_state->state; - copy_state_to_transport(transport); - ao2_replace(perm_state->transport, transport); - return 0; - } - - temp_state = internal_state_alloc(transport); - if (!temp_state) { - ast_log(LOG_ERROR, "Transport '%s' failed to allocate memory\n", transport_id); - goto error; } if (temp_state->state->host.addr.sa_family != PJ_AF_INET && temp_state->state->host.addr.sa_family != PJ_AF_INET6) { ast_log(LOG_ERROR, "Transport '%s' could not be started as binding not specified\n", transport_id); - goto error; + return -1; } /* Set default port if not present */ @@ -418,20 +467,33 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj) } else { ast_log(LOG_ERROR, "Unknown address family for transport '%s', could not get external signaling address\n", transport_id); - goto error; + return -1; } if (ast_dnsmgr_lookup(transport->external_signaling_address, &temp_state->state->external_address, &temp_state->state->external_address_refresher, NULL) < 0) { ast_log(LOG_ERROR, "Could not create dnsmgr for external signaling address on '%s'\n", transport_id); - goto error; + return -1; } } if (transport->type == AST_TRANSPORT_UDP) { - if (temp_state->state->host.addr.sa_family == pj_AF_INET()) { - res = pjsip_udp_transport_start(ast_sip_get_pjsip_endpoint(), &temp_state->state->host.ipv4, NULL, transport->async_operations, &temp_state->state->transport); - } else if (temp_state->state->host.addr.sa_family == pj_AF_INET6()) { - res = pjsip_udp_transport_start6(ast_sip_get_pjsip_endpoint(), &temp_state->state->host.ipv6, NULL, transport->async_operations, &temp_state->state->transport); + + for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) { + if (perm_state && perm_state->state && perm_state->state->transport) { + pjsip_udp_transport_pause(perm_state->state->transport, + PJSIP_UDP_TRANSPORT_DESTROY_SOCKET); + usleep(BIND_DELAY_US); + } + + if (temp_state->state->host.addr.sa_family == pj_AF_INET()) { + res = pjsip_udp_transport_start(ast_sip_get_pjsip_endpoint(), + &temp_state->state->host.ipv4, NULL, transport->async_operations, + &temp_state->state->transport); + } else if (temp_state->state->host.addr.sa_family == pj_AF_INET6()) { + res = pjsip_udp_transport_start6(ast_sip_get_pjsip_endpoint(), + &temp_state->state->host.ipv6, NULL, transport->async_operations, + &temp_state->state->transport); + } } if (res == PJ_SUCCESS && (transport->tos || transport->cos)) { @@ -451,18 +513,37 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj) cfg.async_cnt = transport->async_operations; set_qos(transport, &cfg.qos_params); - res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg, &temp_state->state->factory); + for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) { + if (perm_state && perm_state->state && perm_state->state->factory + && perm_state->state->factory->destroy) { + perm_state->state->factory->destroy(perm_state->state->factory); + usleep(BIND_DELAY_US); + } + + res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg, + &temp_state->state->factory); + } } else if (transport->type == AST_TRANSPORT_TLS) { if (transport->async_operations > 1 && ast_compare_versions(pj_get_version(), "2.5.0") < 0) { ast_log(LOG_ERROR, "Transport: %s: When protocol=tls and pjproject version < 2.5.0, async_operations can't be > 1\n", ast_sorcery_object_get_id(obj)); - goto error; + return -1; } temp_state->state->tls.password = pj_str((char*)transport->password); set_qos(transport, &temp_state->state->tls.qos_params); - res = pjsip_tls_transport_start2(ast_sip_get_pjsip_endpoint(), &temp_state->state->tls, &temp_state->state->host, NULL, transport->async_operations, &temp_state->state->factory); + for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) { + if (perm_state && perm_state->state && perm_state->state->factory + && perm_state->state->factory->destroy) { + perm_state->state->factory->destroy(perm_state->state->factory); + usleep(BIND_DELAY_US); + } + + res = pjsip_tls_transport_start2(ast_sip_get_pjsip_endpoint(), &temp_state->state->tls, + &temp_state->state->host, NULL, transport->async_operations, + &temp_state->state->factory); + } } else if ((transport->type == AST_TRANSPORT_WS) || (transport->type == AST_TRANSPORT_WSS)) { if (transport->cos || transport->tos) { ast_log(LOG_WARNING, "TOS and COS values ignored for websocket transport\n"); @@ -475,17 +556,16 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj) pj_strerror(res, msg, sizeof(msg)); ast_log(LOG_ERROR, "Transport '%s' could not be started: %s\n", ast_sorcery_object_get_id(obj), msg); - goto error; + return -1; } copy_state_to_transport(transport); - ao2_link(states, temp_state); + if (perm_state) { + ao2_unlink_flags(states, perm_state, OBJ_NOLOCK); + } + ao2_link_flags(states, temp_state, OBJ_NOLOCK); return 0; - -error: - ao2_unlink(states, temp_state); - return -1; } /*! \brief Custom handler for type just makes sure the state is created */ @@ -1209,6 +1289,7 @@ int ast_sip_initialize_sorcery_transport(void) ast_sorcery_object_field_register_custom(sorcery, "transport", "tos", "0", transport_tos_handler, tos_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sorcery, "transport", "cos", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_transport, cos)); ast_sorcery_object_field_register(sorcery, "transport", "websocket_write_timeout", AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT_STR, OPT_INT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_transport, write_timeout), 1, INT_MAX); + ast_sorcery_object_field_register(sorcery, "transport", "allow_reload", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_transport, allow_reload)); internal_sip_register_endpoint_formatter(&endpoint_transport_formatter); diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c index 0ff609a63d..dd69ff20e8 100644 --- a/res/res_pjsip_outbound_registration.c +++ b/res/res_pjsip_outbound_registration.c @@ -346,6 +346,8 @@ struct sip_outbound_registration_client_state { unsigned int destroy:1; /*! \brief Non-zero if we have attempted sending a REGISTER with authentication */ unsigned int auth_attempted:1; + /*! \brief The name of the transport to be used for the registration */ + char *transport_name; }; /*! \brief Outbound registration state information (persists for lifetime that registration should exist) */ @@ -508,6 +510,7 @@ static pj_status_t registration_client_send(struct sip_outbound_registration_cli { pj_status_t status; int *callback_invoked; + pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, }; callback_invoked = ast_threadstorage_get(®ister_callback_invoked, sizeof(int)); if (!callback_invoked) { @@ -517,6 +520,13 @@ static pj_status_t registration_client_send(struct sip_outbound_registration_cli /* Due to the message going out the callback may now be invoked, so bump the count */ ao2_ref(client_state, +1); + /* + * Set the transport in case transports were reloaded. + * When pjproject removes the extraneous error messages produced, + * we can check status and only set the transport and resend if there was an error + */ + ast_sip_set_tpselector_from_transport_name(client_state->transport_name, &selector); + pjsip_regc_set_transport(client_state->client, &selector); status = pjsip_regc_send(client_state->client, tdata); /* If the attempt to send the message failed and the callback was not invoked we need to @@ -966,6 +976,7 @@ static void sip_outbound_registration_client_state_destroy(void *obj) { struct sip_outbound_registration_client_state *client_state = obj; + ast_free(client_state->transport_name); ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "-1", 1.0); ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "-1", 1.0, sip_outbound_registration_status_str(client_state->status)); @@ -1003,6 +1014,7 @@ static struct sip_outbound_registration_state *sip_outbound_registration_state_a state->client_state->status = SIP_REGISTRATION_UNREGISTERED; state->client_state->timer.user_data = state->client_state; state->client_state->timer.cb = sip_outbound_registration_timer_cb; + state->client_state->transport_name = ast_strdup(registration->transport); ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "+1", 1.0); ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "+1", 1.0, @@ -1171,25 +1183,6 @@ static int sip_outbound_registration_regc_alloc(void *data) pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool); - if (!ast_strlen_zero(registration->transport)) { - RAII_VAR(struct ast_sip_transport_state *, transport_state, ast_sip_get_transport_state(registration->transport), ao2_cleanup); - - if (!transport_state) { - ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport '%s' " - " for outbound registration", registration->transport); - return -1; - } - - if (transport_state->transport) { - selector.type = PJSIP_TPSELECTOR_TRANSPORT; - selector.u.transport = transport_state->transport; - } else if (transport_state->factory) { - selector.type = PJSIP_TPSELECTOR_LISTENER; - selector.u.listener = transport_state->factory; - } else { - return -1; - } - } ast_assert(state->client_state->client == NULL); if (pjsip_regc_create(ast_sip_get_pjsip_endpoint(), state->client_state, @@ -1198,6 +1191,7 @@ static int sip_outbound_registration_regc_alloc(void *data) return -1; } + ast_sip_set_tpselector_from_transport_name(registration->transport, &selector); pjsip_regc_set_transport(state->client_state->client, &selector); if (!ast_strlen_zero(registration->outbound_proxy)) { diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c index bde7075dd9..0da43190cc 100644 --- a/res/res_pjsip_pubsub.c +++ b/res/res_pjsip_pubsub.c @@ -1559,6 +1559,28 @@ void *ast_sip_subscription_get_header(const struct ast_sip_subscription *sub, co return pjsip_msg_find_hdr_by_name(msg, &name, NULL); } +/*! + * \internal + * \brief Wrapper for pjsip_evsub_send_request + * + * This function (re)sets the transport before sending to catch cases + * where the transport might have changed. + * + * If pjproject gives us the ability to resend, we'll only reset the transport + * if PJSIP_ETPNOTAVAIL is returned from send. + * + * \returns pj_status_t + */ +static pj_status_t internal_pjsip_evsub_send_request(struct sip_subscription_tree *sub_tree, pjsip_tx_data *tdata) +{ + pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, }; + + ast_sip_set_tpselector_from_transport_name(sub_tree->endpoint->transport, &selector); + pjsip_dlg_set_transport(sub_tree->dlg, &selector); + + return pjsip_evsub_send_request(sub_tree->evsub, tdata); +} + /* XXX This function is not used. */ struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_subscription_handler *handler, struct ast_sip_endpoint *endpoint, const char *resource) @@ -1606,7 +1628,7 @@ struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_su evsub = sub_tree->evsub; if (pjsip_evsub_initiate(evsub, NULL, -1, &tdata) == PJ_SUCCESS) { - pjsip_evsub_send_request(evsub, tdata); + internal_pjsip_evsub_send_request(sub_tree, tdata); } else { /* pjsip_evsub_terminate will result in pubsub_on_evsub_state, * being called and terminating the subscription. Therefore, we don't @@ -1687,8 +1709,8 @@ static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree, { #ifdef TEST_FRAMEWORK struct ast_sip_endpoint *endpoint = sub_tree->endpoint; -#endif pjsip_evsub *evsub = sub_tree->evsub; +#endif int res; if (allocate_tdata_buffer(tdata)) { @@ -1696,7 +1718,8 @@ static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree, return -1; } - res = pjsip_evsub_send_request(evsub, tdata) == PJ_SUCCESS ? 0 : -1; + res = internal_pjsip_evsub_send_request(sub_tree, tdata); + subscription_persistence_update(sub_tree, NULL); ast_test_suite_event_notify("SUBSCRIPTION_STATE_SET", @@ -1705,7 +1728,7 @@ static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree, pjsip_evsub_get_state_name(evsub), ast_sorcery_object_get_id(endpoint)); - return res; + return (res == PJ_SUCCESS ? 0 : -1); } /*! diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index e7dd5b91d0..983687174e 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -888,10 +888,32 @@ int ast_sip_session_refresh(struct ast_sip_session *session, return 0; } +/*! + * \internal + * \brief Wrapper for pjsip_inv_send_msg + * + * This function (re)sets the transport before sending to catch cases + * where the transport might have changed. + * + * If pjproject gives us the ability to resend, we'll only reset the transport + * if PJSIP_ETPNOTAVAIL is returned from send. + * + * \returns pj_status_t + */ +static pj_status_t internal_pjsip_inv_send_msg(pjsip_inv_session *inv, const char *transport_name, pjsip_tx_data *tdata) +{ + pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, }; + + ast_sip_set_tpselector_from_transport_name(transport_name, &selector); + pjsip_dlg_set_transport(inv->dlg, &selector); + + return pjsip_inv_send_msg(inv, tdata); +} + void ast_sip_session_send_response(struct ast_sip_session *session, pjsip_tx_data *tdata) { handle_outgoing_response(session, tdata); - pjsip_inv_send_msg(session->inv_session, tdata); + internal_pjsip_inv_send_msg(session->inv_session, session->endpoint->transport, tdata); return; } @@ -1087,7 +1109,8 @@ void ast_sip_session_send_request_with_cb(struct ast_sip_session *session, pjsip } handle_outgoing_request(session, tdata); - pjsip_inv_send_msg(session->inv_session, tdata); + internal_pjsip_inv_send_msg(session->inv_session, session->endpoint->transport, tdata); + return; } @@ -1852,7 +1875,7 @@ static pjsip_inv_session *pre_session_setup(pjsip_rx_data *rdata, const struct a if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) != PJ_SUCCESS) { pjsip_inv_terminate(inv_session, 500, PJ_FALSE); } - pjsip_inv_send_msg(inv_session, tdata); + internal_pjsip_inv_send_msg(inv_session, endpoint->transport, tdata); return NULL; } return inv_session; @@ -2005,7 +2028,7 @@ static void handle_new_invite_request(pjsip_rx_data *rdata) if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) == PJ_SUCCESS) { pjsip_inv_terminate(inv_session, 500, PJ_FALSE); } else { - pjsip_inv_send_msg(inv_session, tdata); + internal_pjsip_inv_send_msg(inv_session, endpoint->transport, tdata); } return; } @@ -2015,7 +2038,7 @@ static void handle_new_invite_request(pjsip_rx_data *rdata) if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) == PJ_SUCCESS) { pjsip_inv_terminate(inv_session, 500, PJ_FALSE); } else { - pjsip_inv_send_msg(inv_session, tdata); + internal_pjsip_inv_send_msg(inv_session, endpoint->transport, tdata); } ao2_cleanup(invite); }