[asterisk-commits] res pjsip/config transport: Allow reloading transports. (asterisk[13])

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Sat Feb 27 10:26:48 CST 2016


Anonymous Coward #1000019 has submitted this change and it was merged.

Change subject: res_pjsip/config_transport: Allow reloading transports.
......................................................................


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
---
M CHANGES
M configs/samples/pjsip.conf.sample
A contrib/ast-db-manage/config/versions/3bcc0b5bc2c9_add_allow_reload_to_ps_transports.py
M include/asterisk/res_pjsip.h
M res/res_pjsip.c
M res/res_pjsip/config_transport.c
M res/res_pjsip_outbound_registration.c
M res/res_pjsip_pubsub.c
M res/res_pjsip_session.c
9 files changed, 284 insertions(+), 70 deletions(-)

Approvals:
  Kevin Harwell: Looks good to me, but someone else must approve
  Anonymous Coward #1000019: Verified
  Joshua Colp: Looks good to me, approved



diff --git a/CHANGES b/CHANGES
index 84ecff5..2f970b9 100644
--- a/CHANGES
+++ b/CHANGES
@@ -33,6 +33,13 @@
 
 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 5d128cb..ebbd199 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 0000000..377179b
--- /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 bea469f..fc921c8 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -185,6 +185,8 @@
 	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 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 6250161..e0af0b0 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 @@
 	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 @@
 	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 0fcd7d9..e7bda5f 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 @@
 	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 @@
 	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 @@
 	 */
 	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 @@
 		} 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 @@
 		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 @@
 
 		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 @@
 	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 daa4f3e..59c3db5 100644
--- a/res/res_pjsip_outbound_registration.c
+++ b/res/res_pjsip_outbound_registration.c
@@ -346,6 +346,8 @@
 	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 @@
 {
 	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 @@
 
 	/* 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 @@
 {
 	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 @@
 	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 @@
 
 	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 @@
 		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 8b37dd0..643ed85 100644
--- a/res/res_pjsip_pubsub.c
+++ b/res/res_pjsip_pubsub.c
@@ -1559,6 +1559,28 @@
 	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 @@
 	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 @@
 {
 #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 @@
 		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 @@
 		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 aad2438..1de2461 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -888,10 +888,32 @@
 	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 @@
 	}
 
 	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 @@
 		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 @@
 		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 @@
 		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);
 	}

-- 
To view, visit https://gerrit.asterisk.org/2125
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I5e759850e25958117d4c02f62ceb7244d7ec9edf
Gerrit-PatchSet: 14
Gerrit-Project: asterisk
Gerrit-Branch: 13
Gerrit-Owner: George Joseph <george.joseph at fairview5.com>
Gerrit-Reviewer: Anonymous Coward #1000019
Gerrit-Reviewer: George Joseph <george.joseph at fairview5.com>
Gerrit-Reviewer: Joshua Colp <jcolp at digium.com>
Gerrit-Reviewer: Kevin Harwell <kharwell at digium.com>
Gerrit-Reviewer: Mark Michelson <mmichelson at digium.com>



More information about the asterisk-commits mailing list