summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Joseph <george.joseph@fairview5.com>2016-02-11 10:01:05 -0700
committerGeorge Joseph <george.joseph@fairview5.com>2016-02-19 17:56:27 -0700
commitd2a1457e0b4ecdd512fe58fdb55ecc07fd141bea (patch)
treedb97ca4b365f3bd4bad2110c69a0e4ff9fe268a7
parentb4fdf93d06753c580b4ef7e34fe07670a8e4aff4 (diff)
res_pjsip/config_transport: Allow reloading transports.
The 'reload' mechanism actually involves closing the underlying socket and calling the appropriate udp, tcp or tls start functions again. Only outbound_registration, pubsub and session needed work to reset the transport before sending requests to insure that the pjsip transport didn't get pulled out from under them. In my testing, no calls were dropped when a transport was changed for any of the 3 transport types even if ip addresses or ports were changed. To be on the safe side however, a new transport option was added (allow_reload) which defaults to 'no'. Unless it's explicitly set to 'yes' for a transport, changes to that transport will be ignored on a reload of res_pjsip. This should preserve the current behavior. Change-Id: I5e759850e25958117d4c02f62ceb7244d7ec9edf
-rw-r--r--CHANGES7
-rw-r--r--configs/samples/pjsip.conf.sample6
-rw-r--r--contrib/ast-db-manage/config/versions/3bcc0b5bc2c9_add_allow_reload_to_ps_transports.py27
-rw-r--r--include/asterisk/res_pjsip.h24
-rw-r--r--res/res_pjsip.c55
-rw-r--r--res/res_pjsip/config_transport.c139
-rw-r--r--res/res_pjsip_outbound_registration.c32
-rw-r--r--res/res_pjsip_pubsub.c31
-rw-r--r--res/res_pjsip_session.c33
9 files changed, 284 insertions, 70 deletions
diff --git a/CHANGES b/CHANGES
index 84ecff500..2f970b9c8 100644
--- a/CHANGES
+++ b/CHANGES
@@ -33,6 +33,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 5d128cbf1..ebbd199e7 100644
--- a/configs/samples/pjsip.conf.sample
+++ b/configs/samples/pjsip.conf.sample
@@ -810,6 +810,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 000000000..377179b04
--- /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 bea469ffd..fc921c879 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"
@@ -2260,4 +2262,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 625016191..e0af0b084 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -1026,6 +1026,14 @@
Value is in milliseconds; default is 100 ms.</para>
</description>
</configOption>
+ <configOption name="allow_reload" default="no">
+ <synopsis>Allow this transport to be reloaded.</synopsis>
+ <description>
+ <para>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.</para>
+ </description>
+ </configOption>
</configObject>
<configObject name="contact">
<synopsis>A way of creating an aliased name to a SIP URI</synopsis>
@@ -2483,22 +2491,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;
}
@@ -2521,6 +2521,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 0fcd7d923..e7bda5f05 100644
--- a/res/res_pjsip/config_transport.c
+++ b/res/res_pjsip/config_transport.c
@@ -18,6 +18,7 @@
#include "asterisk.h"
+#include <math.h>
#include <pjsip.h>
#include <pjlib.h>
@@ -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 daa4f3e50..59c3db553 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(&register_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 8b37dd07b..643ed850f 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 aad24382f..1de246135 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);
}