[Asterisk-code-review] res pjsip: Implement additional SIP RFCs for Google Voice tr... (asterisk[master])

Nick French asteriskteam at digium.com
Wed Jul 18 07:53:40 CDT 2018


Nick French has uploaded this change for review. ( https://gerrit.asterisk.org/9505


Change subject: res_pjsip: Implement additional SIP RFCs for Google Voice trunk compatability
......................................................................

res_pjsip: Implement additional SIP RFCs for Google Voice trunk compatability

Implement the following additional SIP features that are required
by the Google Voice SIP registrar:

- Service-Routes (RFC 3608) and P-Preferred-Identity (RFC 3325), by storing
  headers returned from a REGISTER response and using them in subsequent
  INVITE/CANCELs
- OAuth / Bearer token authentication (draft-ietf-sipcore-sip-authn-02), by
  using similar token requests as currently found in chan_motif but piping
  them through to pjsip via REGISTER Authorization header
- Mechanisms to use separate TLS transports for separate registrations and
  their associated message dialog, by creating a transport without going
  through the pjsip transport pool and storing it in the client_state for reuse
- User-configurable additions to SIP Contact header

All functionality changes are controlled by pjsip.conf configuration options
and do not affect non-configured pjsip endpoints otherwise.

Change-Id: Id214c2d1c550a41fcf564b7df8f3da7be565bd58
---
M include/asterisk/res_pjsip.h
M res/res_pjsip.c
M res/res_pjsip/config_auth.c
M res/res_pjsip/pjsip_configuration.c
M res/res_pjsip_outbound_authenticator_digest.c
M res/res_pjsip_outbound_registration.c
A third-party/pjproject/patches/0110-oauth.patch
A third-party/pjproject/patches/0120-contact-params.patch
8 files changed, 789 insertions(+), 22 deletions(-)



  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/05/9505/1

diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index 849f087..493fb94 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -401,6 +401,8 @@
 	AST_SIP_AUTH_TYPE_USER_PASS,
 	/*! Credentials stored as an MD5 sum */
 	AST_SIP_AUTH_TYPE_MD5,
+	/*! Oauth */
+	AST_SIP_AUTH_TYPE_OAUTH,
 	/*! Credentials not stored this is a fake auth */
 	AST_SIP_AUTH_TYPE_ARTIFICIAL
 };
@@ -419,6 +421,12 @@
 		AST_STRING_FIELD(auth_pass);
 		/*! Authentication credentials in MD5 format (hash of user:realm:pass) */
 		AST_STRING_FIELD(md5_creds);
+		/*! Refresh token to use for OAuth authentication */
+		AST_STRING_FIELD(refresh_token);
+		/*! Client ID to use for OAuth authentication */
+		AST_STRING_FIELD(oauth_clientid);
+		/*! Secret to use for OAuth authentication */
+		AST_STRING_FIELD(oauth_secret);
 	);
 	/*! The time period (in seconds) that a nonce may be reused */
 	unsigned int nonce_lifetime;
@@ -734,6 +742,8 @@
 		AST_STRING_FIELD(transport);
 		/*! Outbound proxy to use */
 		AST_STRING_FIELD(outbound_proxy);
+		/*! Outbound registration associated with this endpoint */
+		AST_STRING_FIELD(outbound_registration);
 		/*! Explicit AORs to dial if none are specified */
 		AST_STRING_FIELD(aors);
 		/*! Musiconhold class to suggest that the other side use when placing on hold */
@@ -3186,4 +3196,13 @@
  */
 void ast_sip_transport_state_unregister(struct ast_sip_tpmgr_state_callback *element);
 
+/*!
+ * \brief Register an override to the default selection of transports based on endpoint name
+ * \since gvsip
+ *
+ * \param callback Callback to evoke when determining the transport when creating a new dialog
+ */
+typedef int (*transport_from_endpoint_callback)(const struct ast_sip_endpoint *endpoint, pjsip_transport** transport);
+void ast_sip_set_transport_from_endpoint_override(transport_from_endpoint_callback callback);
+
 #endif /* _RES_PJSIP_H */
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index 507267a..e0686d7 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -398,6 +398,9 @@
 				<configOption name="outbound_proxy">
 					<synopsis>Full SIP URI of the outbound proxy used to send requests</synopsis>
 				</configOption>
+				<configOption name="outbound_registration">
+					<synopsis>Name of the registration config associated with this endpoint</synopsis>
+				</configOption>
 				<configOption name="rewrite_contact">
 					<synopsis>Allow Contact header to be rewritten with the source IP address-port</synopsis>
 					<description><para>
@@ -1133,11 +1136,12 @@
 						This option specifies which of the password style config options should be read
 						when trying to authenticate an endpoint inbound request. If set to <literal>userpass</literal>
 						then we'll read from the 'password' option. For <literal>md5</literal> we'll read
-						from 'md5_cred'.
+						from 'md5_cred'. If set to <literal>oauth</literal> then we'll read from the refresh_toke/oauth_client_id/oauth_secret fields.
 						</para>
 						<enumlist>
 							<enum name="md5"/>
 							<enum name="userpass"/>
+							<enum name="oauth"/>
 						</enumlist>
 					</description>
 				</configOption>
@@ -1152,6 +1156,15 @@
 					<synopsis>Plain text password used for authentication.</synopsis>
 					<description><para>Only used when auth_type is <literal>userpass</literal>.</para></description>
 				</configOption>
+				<configOption name="refresh_token">
+					<synopsis>Google OAuth 2.0 refresh token</synopsis>
+				</configOption>
+				<configOption name="oauth_clientid">
+					<synopsis>Google OAuth 2.0 application's client id</synopsis>
+				</configOption>
+				<configOption name="oauth_secret">
+					<synopsis>Google OAuth 2.0 application's secret</synopsis>
+				</configOption>
 				<configOption name="realm">
 					<synopsis>SIP realm for endpoint</synopsis>
 					<description><para>
@@ -2134,6 +2147,9 @@
 				<parameter name="OutboundProxy">
 					<para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='endpoint']/configOption[@name='outbound_proxy']/synopsis/node())"/></para>
 				</parameter>
+				<parameter name="OutboundRegistration">
+					<para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='endpoint']/configOption[@name='outbound_registration']/synopsis/node())"/></para>
+				</parameter>
 				<parameter name="MohSuggest">
 					<para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='endpoint']/configOption[@name='moh_suggest']/synopsis/node())"/></para>
 				</parameter>
@@ -2792,6 +2808,14 @@
 /*! Local host address for IPv6 (string form) */
 static char host_ip_ipv6_string[PJ_INET6_ADDRSTRLEN];
 
+static transport_from_endpoint_callback transport_from_endpoint_override_callback;
+
+void ast_sip_set_transport_from_endpoint_override(transport_from_endpoint_callback callback)
+{
+	ast_log(LOG_DEBUG, "Transport override set!\n");
+	transport_from_endpoint_override_callback = callback;
+}
+
 static int register_service(void *data)
 {
 	pjsip_module **module = data;
@@ -3266,6 +3290,7 @@
 	pjsip_tpselector *selector)
 {
 	pjsip_sip_uri *uri;
+	pjsip_transport* transport;
 	pjsip_tpselector sel = { .type = PJSIP_TPSELECTOR_NONE, };
 
 	uri = pjsip_uri_get_uri(dlg->target);
@@ -3274,6 +3299,14 @@
 	}
 
 	ast_sip_set_tpselector_from_ep_or_uri(endpoint, uri, selector);
+
+	if (transport_from_endpoint_override_callback && transport_from_endpoint_override_callback(endpoint, &transport)) {
+		ast_log(LOG_DEBUG, "Overriding endpoint transport to use %p\n", (void*)transport);
+
+		selector->type = PJSIP_TPSELECTOR_TRANSPORT;
+		selector->u.transport = transport;
+	}
+
 	pjsip_dlg_set_transport(dlg, selector);
 
 	return 0;
diff --git a/res/res_pjsip/config_auth.c b/res/res_pjsip/config_auth.c
index b1bf9c4..9d87f4d 100644
--- a/res/res_pjsip/config_auth.c
+++ b/res/res_pjsip/config_auth.c
@@ -56,6 +56,8 @@
 		auth->type = AST_SIP_AUTH_TYPE_USER_PASS;
 	} else if (!strcasecmp(var->value, "md5")) {
 		auth->type = AST_SIP_AUTH_TYPE_MD5;
+	} else if (!strcasecmp(var->value, "oauth")) {
+		auth->type = AST_SIP_AUTH_TYPE_OAUTH;
 	} else {
 		ast_log(LOG_WARNING, "Unknown authentication storage type '%s' specified for %s\n",
 				var->value, var->name);
@@ -66,7 +68,8 @@
 
 static const char *auth_types_map[] = {
 	[AST_SIP_AUTH_TYPE_USER_PASS] = "userpass",
-	[AST_SIP_AUTH_TYPE_MD5] = "md5"
+	[AST_SIP_AUTH_TYPE_MD5] = "md5",
+	[AST_SIP_AUTH_TYPE_OAUTH] = "oauth"
 };
 
 const char *ast_sip_auth_type_to_str(enum ast_sip_auth_type type)
@@ -106,6 +109,13 @@
 			res = -1;
 		}
 		break;
+	case AST_SIP_AUTH_TYPE_OAUTH:
+		if (ast_strlen_zero(auth->refresh_token) || ast_strlen_zero(auth->oauth_clientid) || ast_strlen_zero(auth->oauth_secret)) {
+			ast_log(LOG_ERROR, "'oauth' authentication specified but refresh_token, oauth_clientid, or "
+					"oauth_secret not specified for auth '%s'\n", ast_sorcery_object_get_id(auth));
+			res = -1;
+		}
+		break;
 	case AST_SIP_AUTH_TYPE_USER_PASS:
 	case AST_SIP_AUTH_TYPE_ARTIFICIAL:
 		break;
@@ -365,6 +375,12 @@
 			"", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, auth_user));
 	ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "password",
 			"", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, auth_pass));
+	ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "refresh_token",
+			"", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, refresh_token));
+	ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "oauth_clientid",
+			"", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, oauth_clientid));
+	ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "oauth_secret",
+			"", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, oauth_secret));
 	ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "md5_cred",
 			"", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, md5_creds));
 	ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "realm",
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index f4a9ecb..8e09bb5 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -1797,6 +1797,7 @@
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rewrite_contact", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, nat.rewrite_contact));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, transport));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, outbound_proxy));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "outbound_registration", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, outbound_registration));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "moh_suggest", "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, mohsuggest));
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "100rel", "yes", prack_handler, prack_to_str, NULL, 0, 0);
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "timers", "yes", timers_handler, timers_to_str, NULL, 0, 0);
diff --git a/res/res_pjsip_outbound_authenticator_digest.c b/res/res_pjsip_outbound_authenticator_digest.c
index 063b4d3..49b3884 100644
--- a/res/res_pjsip_outbound_authenticator_digest.c
+++ b/res/res_pjsip_outbound_authenticator_digest.c
@@ -83,6 +83,9 @@
 			pj_cstr(&auth_creds[i].data, auths[i]->md5_creds);
 			auth_creds[i].data_type = PJSIP_CRED_DATA_DIGEST;
 			break;
+		case AST_SIP_AUTH_TYPE_OAUTH:
+			/* nothing to do. handled seperately in res_pjsip_outbound_registration */
+			break;
 		case AST_SIP_AUTH_TYPE_ARTIFICIAL:
 			ast_log(LOG_ERROR, "Trying to set artificial outbound auth credentials shouldn't happen.\n");
 			break;
diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c
index 0d815ad..40f070a 100644
--- a/res/res_pjsip_outbound_registration.c
+++ b/res/res_pjsip_outbound_registration.c
@@ -27,6 +27,7 @@
 
 #include <pjsip.h>
 #include <pjsip_ua.h>
+#include <pjsip/sip_dialog.h>
 
 #include "asterisk/res_pjsip.h"
 #include "asterisk/res_pjsip_cli.h"
@@ -37,7 +38,11 @@
 #include "asterisk/threadstorage.h"
 #include "asterisk/threadpool.h"
 #include "asterisk/statsd.h"
+#include "asterisk/pbx.h"
 #include "res_pjsip/include/res_pjsip_private.h"
+#include "asterisk/res_pjsip_session.h"
+#include "asterisk/vector.h"
+#include "asterisk/res_pjproject.h"
 
 /*** DOCUMENTATION
 	<configInfo name="res_pjsip_outbound_registration" language="en_US">
@@ -76,6 +81,9 @@
 				<configOption name="contact_user">
 					<synopsis>Contact User to use in request</synopsis>
 				</configOption>
+				<configOption name="contact_additional_params">
+					<synopsis>Additional parameters for contact</synopsis>
+				</configOption>
 				<configOption name="expiration" default="3600">
 					<synopsis>Expiration time for registrations in seconds</synopsis>
 				</configOption>
@@ -144,7 +152,11 @@
 						<literal>pjsip.conf</literal>. As with other <literal>res_pjsip</literal> modules, this will use the first available transport of the appropriate type if unconfigured.</para></note>
 					</description>
 				</configOption>
+				<configOption name="transport_reuse" default="yes">
+					<synopsis>Determine if same transport can be re-used by different registrations</synopsis>
+				</configOption>
 				<configOption name="line">
+
 					<synopsis>Whether to add a 'line' parameter to the Contact for inbound call matching</synopsis>
 					<description><para>
 						When enabled this option will cause a 'line' parameter to be added to the Contact
@@ -171,6 +183,9 @@
 						header as necessary.
 					</para></description>
 				</configOption>
+				<configOption name="support_outbound">
+					<synopsis>Enables Outbound support for outbound REGISTER requests.</synopsis>
+				</configOption>
 			</configObject>
 		</configFile>
 	</configInfo>
@@ -224,6 +239,11 @@
 	</manager>
  ***/
 
+/* forward declarations */
+static int set_outbound_initial_authentication_credentials(pjsip_regc *regc,
+		const struct ast_sip_auth_vector *auth_vector);
+static int transport_from_endpoint_override(const struct ast_sip_endpoint *endpoint, pjsip_transport** transport);
+
 /*! \brief Some thread local storage used to determine if the running thread invoked the callback */
 AST_THREADSTORAGE(register_callback_invoked);
 
@@ -291,6 +311,8 @@
 		AST_STRING_FIELD(client_uri);
 		/*! \brief Optional user for contact header */
 		AST_STRING_FIELD(contact_user);
+		/*! \bried Optional additional parameters for contact */
+		AST_STRING_FIELD(contact_additional_params);
 		/*! \brief Explicit transport to use for registration */
 		AST_STRING_FIELD(transport);
 		/*! \brief Outbound proxy to use */
@@ -316,8 +338,21 @@
 	struct ast_sip_auth_vector outbound_auths;
 	/*! \brief Whether Path support is enabled */
 	unsigned int support_path;
+	/*! \brief Whether Outbound support is enabled */
+	unsigned int support_outbound;
+	/*! \brief Determine if same transport can be re-used by different registrations */
+	unsigned int transport_reuse;
 };
 
+/* \brief Vector type to store service routes */
+AST_VECTOR(service_route_vector_type, pj_str_t);
+
+/* \brief Caching pool to use to create pool to store saved pjsip strings */
+static pj_caching_pool cachingpool;
+
+/* \brief Pool to use to store saved pjsip strings */
+static pj_pool_t *reg_pool;
+
 /*! \brief Outbound registration client state information (persists for lifetime of regc) */
 struct sip_outbound_registration_client_state {
 	/*! \brief Current state of this registration */
@@ -347,18 +382,28 @@
 	unsigned int auth_rejection_permanent;
 	/*! \brief Determines whether SIP Path support should be advertised */
 	unsigned int support_path;
+	/*! \brief Determines whether SIP Outbound support should be advertised */
+	unsigned int support_outbound;
 	/*! CSeq number of last sent auth request. */
 	unsigned int auth_cseq;
 	/*! \brief Serializer for stuff and things */
 	struct ast_taskprocessor *serializer;
 	/*! \brief Configured authentication credentials */
 	struct ast_sip_auth_vector outbound_auths;
+	/*! \brief List of service-routes in register response */
+	struct service_route_vector_type service_route_vector;
+	/*! \brief P-Associated-URI from register response */
+	pj_str_t associated_uri;
 	/*! \brief Registration should be destroyed after completion of transaction */
 	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 The transport used by the registration */
+	pjsip_transport *transport;
+	/*! \brief Determine if same transport can be re-used by different registrations */
+	unsigned int transport_reuse;
 	/*! \brief The name of the registration sorcery object */
 	char *registration_name;
 };
@@ -520,9 +565,112 @@
 }
 
 static pj_str_t PATH_NAME = { "path", 4 };
+static pj_str_t OUTBOUND_NAME = { "outbound", 8 };
 
-/*! \brief Helper function which sends a message and cleans up, if needed, on failure */
-static pj_status_t registration_client_send(struct sip_outbound_registration_client_state *client_state,
+/*! \brief Helper to send message on specified transport */
+static pj_status_t send_on_transport(struct sip_outbound_registration_client_state *client_state, 
+	pjsip_tx_data *tdata)
+{
+	pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_TRANSPORT, };
+	selector.u.transport = client_state->transport;
+
+	pjsip_regc_set_transport(client_state->client, &selector);
+	return pjsip_regc_send(client_state->client, tdata);
+}
+
+struct stateless_send_resolver_callback_data
+{
+	struct sip_outbound_registration_client_state *client_state;
+	pjsip_tx_data *tdata;
+};
+
+/*! \brief Callback used to manually select transport when transport_reuse is off */
+static void
+stateless_send_resolver_callback( pj_status_t status, void *token, const struct pjsip_server_addresses *addr)
+{
+	RAII_VAR(struct stateless_send_resolver_callback_data *, data, token, ast_free);
+	pjsip_tpselector orig_selector = { .type = PJSIP_TPSELECTOR_NONE, };
+
+	struct sip_outbound_registration_client_state *client_state = data->client_state;
+	pjsip_tx_data *tdata = data->tdata;
+
+	if (status != PJ_SUCCESS) {
+		ast_log(LOG_ERROR, "Resolver failed. Cannot send message\n");
+		return;
+	}
+
+	ast_sip_set_tpselector_from_transport_name(client_state->transport_name, &orig_selector);
+
+	if (orig_selector.type != PJSIP_TPSELECTOR_LISTENER)
+	{
+		ast_log(LOG_ERROR, "transport_reuse requires setting a transport\n");
+		status = PJ_EUNKNOWN;
+		return;
+	}
+
+	/* Copy server addresses */
+	if (addr && addr != &tdata->dest_info.addr) {
+		pj_memcpy( &tdata->dest_info.addr, addr, sizeof(pjsip_server_addresses));
+	}
+
+	if (orig_selector.u.listener->create_transport2) {
+		orig_selector.u.listener->create_transport2(orig_selector.u.listener,
+			pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()),
+			ast_sip_get_pjsip_endpoint(),
+			&tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].addr,
+			tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].addr_len,
+			tdata,
+			&client_state->transport);
+	} else {
+		orig_selector.u.listener->create_transport(orig_selector.u.listener,
+			pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()),
+			ast_sip_get_pjsip_endpoint(),
+			&tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].addr,
+			tdata->dest_info.addr.entry[tdata->dest_info.cur_addr].addr_len,
+			&client_state->transport);
+	}
+
+	ast_log(LOG_DEBUG, "Registration using newly created transport %p\n", (void*)client_state->transport);
+	send_on_transport(client_state, tdata);
+}
+
+/*! \brief Send a message using a manually-built transport */
+static pj_status_t registration_client_send_manual(struct sip_outbound_registration_client_state *client_state,
+	pjsip_tx_data *tdata)
+{
+	pj_status_t status;
+	pjsip_host_info dest_info;
+	struct stateless_send_resolver_callback_data* data;
+
+	/* Due to the message going out the callback may now be invoked, so bump the count */
+	ao2_ref(client_state, +1);
+
+	/* If we already have a transport, just use it. */
+	if (client_state->transport && !client_state->transport->is_shutdown) {
+		ast_log(LOG_DEBUG, "Registration re-using transport %p\n", (void*)client_state->transport);
+		return send_on_transport(client_state, tdata);
+	}
+
+	/* If not, then create a new one. First, resolve the endpoint's host */
+	status = pjsip_process_route_set(tdata, &dest_info);
+
+	if (status != PJ_SUCCESS)
+		return status;
+
+	data = ast_malloc(sizeof(struct stateless_send_resolver_callback_data));
+	data->client_state = client_state;
+	data->tdata = tdata;
+
+	pj_strdup(tdata->pool, &tdata->dest_info.name, &dest_info.addr.host);
+	pjsip_endpt_resolve( ast_sip_get_pjsip_endpoint(), tdata->pool, &dest_info, data,
+				&stateless_send_resolver_callback);
+
+	return status;
+}
+
+
+/*! \brief Send a message using a transport from the normal pjsip transport factory */
+static pj_status_t registration_client_send_normal(struct sip_outbound_registration_client_state *client_state,
 	pjsip_tx_data *tdata)
 {
 	pj_status_t status;
@@ -545,6 +693,8 @@
 	 */
 	ast_sip_set_tpselector_from_transport_name(client_state->transport_name, &selector);
 	pjsip_regc_set_transport(client_state->client, &selector);
+
+	ast_log(LOG_DEBUG, "Registration using factory-chosen transport\n");
 	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
@@ -557,12 +707,69 @@
 	return status;
 }
 
+/*! \brief Helper function which sends a message and cleans up, if needed, on failure */
+static pj_status_t registration_client_send(struct sip_outbound_registration_client_state *client_state,
+	pjsip_tx_data *tdata)
+{
+	if (!client_state->transport_reuse) {
+		return registration_client_send_manual(client_state, tdata);
+	} else {
+		return registration_client_send_normal(client_state, tdata);
+	}
+}
+
+/*! \brief Helper function to add string to Supported header */
+static int add_to_supported_header(pjsip_tx_data *tdata, pj_str_t *name)
+{
+	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) {
+			pjsip_tx_data_dec_ref(tdata);
+			return 0;
+		}
+
+		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);
+	}
+
+	/* add on to the existing Supported header */
+	pj_strassign(&hdr->values[hdr->count++], name);
+
+	return 1;
+}
+
+/*! \brief Helper function to add configured supported headers */
+static int add_configured_supported_headers(struct sip_outbound_registration_client_state *client_state, pjsip_tx_data *tdata)
+{
+	if (client_state->support_path) {
+		if (!add_to_supported_header(tdata, &PATH_NAME)) {
+			return 0;
+		}
+	}
+
+	if (client_state->support_outbound) {
+		if (!add_to_supported_header(tdata, &OUTBOUND_NAME)) {
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
 /*! \brief Callback function for registering */
 static int handle_client_registration(void *data)
 {
 	RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup);
 	pjsip_tx_data *tdata;
 
+	if (set_outbound_initial_authentication_credentials(client_state->client, &client_state->outbound_auths)) {
+		ast_log(LOG_WARNING, "Failed to set initial authentication credentials\n");
+		return -1;
+	}
+
 	if (client_state->status == SIP_REGISTRATION_STOPPED
 		|| pjsip_regc_register(client_state->client, PJ_FALSE, &tdata) != PJ_SUCCESS) {
 		return 0;
@@ -578,23 +785,9 @@
 			(int) info.client_uri.slen, info.client_uri.ptr);
 	}
 
-	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) {
-				pjsip_tx_data_dec_ref(tdata);
-				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);
+	if (!add_configured_supported_headers(client_state, tdata)) {
+		ast_log(LOG_WARNING, "Failed to set supported headers\n");
+		return -1;
 	}
 
 	registration_client_send(client_state, tdata);
@@ -707,6 +900,7 @@
 			update_client_state_status(client_state, SIP_REGISTRATION_STOPPING);
 			client_state->destroy = 1;
 			if (pjsip_regc_unregister(client_state->client, &tdata) == PJ_SUCCESS
+				&& add_configured_supported_headers(client_state, tdata)
 				&& registration_client_send(client_state, tdata) == PJ_SUCCESS) {
 				ao2_ref(client_state, -1);
 				return 0;
@@ -885,6 +1079,32 @@
 	ao2_ref(monitor, -1);
 }
 
+static void save_response_fields_to_client_state(struct registration_response *response)
+{
+	static const pj_str_t associated_uri_str = { "P-Associated-URI", 16 };
+	static const pj_str_t service_route_str = { "Service-Route", 13 };
+
+	pjsip_hdr *service_route_hdr = NULL;
+	pjsip_msg *msg = response->rdata->msg_info.msg;
+	pjsip_hdr *associated_uri_hdr;
+
+	AST_VECTOR_INIT(&response->client_state->service_route_vector, 0);
+	while((service_route_hdr = (pjsip_hdr*)pjsip_msg_find_hdr_by_name(msg, &service_route_str, service_route_hdr ? service_route_hdr->next : NULL))) {
+		pj_str_t value = ((pjsip_generic_string_hdr*)service_route_hdr)->hvalue;
+		pj_str_t copy;
+		pj_strdup_with_null(reg_pool, &copy, &value);
+		AST_VECTOR_APPEND(&response->client_state->service_route_vector, copy);
+		ast_log(LOG_DEBUG, "Stored service-route: %s\n", copy.ptr);
+	}
+
+	if ((associated_uri_hdr = (pjsip_hdr*)pjsip_msg_find_hdr_by_name(msg, &associated_uri_str, NULL))) {
+		pj_str_t value = ((pjsip_generic_string_hdr*)associated_uri_hdr)->hvalue;
+		pj_strdup_with_null(reg_pool, &response->client_state->associated_uri, &value);
+		ast_log(LOG_DEBUG, "Stored associated uri: %s\n", response->client_state->associated_uri.ptr);
+	}
+}
+
+
 /*! \brief Callback function for handling a response to a registration attempt */
 static int handle_registration_response(void *data)
 {
@@ -964,6 +1184,9 @@
 				registration_transport_shutdown_cb, response->client_state->registration_name,
 				monitor_matcher);
 		}
+
+		save_response_fields_to_client_state(response);
+
 	} else if (response->client_state->destroy) {
 		/* We need to deal with the pending destruction instead. */
 	} else if (response->retry_after) {
@@ -1283,6 +1506,132 @@
 	return rc;
 }
 
+/* \brief Get google oauth2 access token using refresh token */
+static int fetch_access_token(struct ast_sip_auth *auth)
+{
+	RAII_VAR(char *, cmd, NULL, ast_free);
+	char cBuf[4096] = "";
+	const char *url = "https://www.googleapis.com/oauth2/v3/token";
+	struct ast_json_error error;
+	RAII_VAR(struct ast_json *, jobj, NULL, ast_json_unref);
+
+	/* set timeout to be shorter than default 180s (also checks func_curl is available) */
+	if (ast_func_write(NULL, "CURLOPT(conntimeout)", "10")) {
+		ast_log(LOG_ERROR, "CURL is unavailable. This is required for OAuth 2.0 authentication. Please ensure it is loaded.\n");
+		return -1;
+	}
+
+	ast_asprintf(&cmd, "CURL(%s,client_id=%s&client_secret=%s&refresh_token=%s&grant_type=refresh_token)",
+		     url, auth->oauth_clientid, auth->oauth_secret, auth->refresh_token);
+
+	ast_debug(2, "Performing OAuth 2.0 authentication using command: %s\n", cmd);
+
+	if (ast_func_read(NULL, cmd, cBuf, sizeof(cBuf) - 1)) {
+		ast_log(LOG_ERROR, "An error occurred while retreiving OAuth 2.0 access token\n");
+		return -1;
+	}
+
+	ast_debug(2, "OAuth 2.0 authentication returned: %s\n", cBuf);
+
+	jobj = ast_json_load_string(cBuf, &error);
+	if (jobj) {
+		const char *token = ast_json_string_get(ast_json_object_get(jobj, "access_token"));
+		if (token) {
+			ast_string_field_set(auth, auth_pass, token);
+			return 0;
+		}
+	}
+
+	ast_log(LOG_ERROR, "An error occurred while performing OAuth 2.0 authentication: %s\n", cBuf);
+
+	return -1;
+}
+
+/*!
+ * \internal
+ * \brief Set pjsip registration context with any authentication credientials that need to be
+ * sent in the initial registration request
+ *
+ * \param regc The pjsip registration context
+ * \param auth_vector The vector of configured authentication credientials
+ */
+static int set_outbound_initial_authentication_credentials(pjsip_regc *regc,
+		const struct ast_sip_auth_vector *auth_vector)
+{
+	size_t auth_size = AST_VECTOR_SIZE(auth_vector);
+	struct ast_sip_auth **auths = ast_alloca(auth_size * sizeof(*auths));
+	pjsip_cred_info *auth_creds = ast_alloca(1 * sizeof(*auth_creds));
+	pjsip_auth_clt_pref prefs;
+	int res = 0;
+	int i;
+
+	if (ast_sip_retrieve_auths(auth_vector, auths)) {
+		res = -1;
+		goto cleanup;
+	}
+
+	for (i = 0; i < auth_size; ++i) {
+		switch (auths[i]->type) {
+		case AST_SIP_AUTH_TYPE_OAUTH:
+			pj_cstr(&auth_creds[0].username, auths[i]->auth_user);
+			pj_cstr(&auth_creds[0].scheme, "Bearer");
+			pj_cstr(&auth_creds[0].realm, auths[i]->realm);
+			ast_debug(2, "Obtaining OAuth access token\n");
+			if (fetch_access_token(auths[i])) {
+				ast_log(LOG_WARNING, "Obtaining OAuth access token failed\n");
+				res = -1;
+			}
+			ast_debug(2, "Setting data to %s\n", auths[i]->auth_pass);
+
+			pj_cstr(&auth_creds[0].data, auths[i]->auth_pass);
+			auth_creds[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
+
+			pjsip_regc_set_credentials(regc, 1, auth_creds);
+
+			/* for oauth, send auth without waiting for unauthorized response */
+			prefs.initial_auth = PJ_TRUE;
+			pj_cstr(&prefs.algorithm, "oauth");
+			pjsip_regc_set_prefs(regc, &prefs);
+
+			break;
+		default:
+			/* other cases handled after receiving auth rejection */
+			break;
+		}
+	}
+
+cleanup:
+	ast_sip_cleanup_auths(auths, auth_size);
+	return res;
+}
+
+/*! \brief Helper to convert ; seperated list to pjsip_param list */
+static pjsip_param* get_params_list_from_string(const char* param_string)
+{
+	pjsip_param *params;
+	char *buf;
+	char *word;
+	char *next;
+
+	params = PJ_POOL_ALLOC_T(reg_pool, pjsip_param);
+	pj_list_init(params);
+
+	buf = ast_strdupa(param_string);
+	next = buf;
+	while ((word = strsep(&next, ";"))) {
+		char name[31];
+		char value[31];
+		if (sscanf(word, "%30[^=]=%30[^=]", name, value) == 2) {
+			pjsip_param *param = PJ_POOL_ALLOC_T(reg_pool, pjsip_param);
+			pj_strdup2_with_null(reg_pool, &param->name, name);
+			pj_strdup2_with_null(reg_pool, &param->value, value);
+			pj_list_insert_after(params, param);
+		}
+	}
+
+	return params;
+}
+
 /*! \brief Helper function that allocates a pjsip registration client and configures it */
 static int sip_outbound_registration_regc_alloc(void *data)
 {
@@ -1382,6 +1731,15 @@
 		return -1;
 	}
 
+	if (!ast_strlen_zero(registration->contact_additional_params)) {
+		pjsip_param *params = get_params_list_from_string(registration->contact_additional_params);
+		pjsip_regc_update_contact(state->client_state->client, 1, &contact_uri, params);
+	}
+
+	if (!registration->transport_reuse) {
+		ast_sip_set_transport_from_endpoint_override(&transport_from_endpoint_override);
+	}
+
 	return 0;
 }
 
@@ -1409,6 +1767,8 @@
 	state->client_state->max_retries = registration->max_retries;
 	state->client_state->retries = 0;
 	state->client_state->support_path = registration->support_path;
+	state->client_state->support_outbound = registration->support_outbound;
+	state->client_state->transport_reuse = registration->transport_reuse;
 	state->client_state->auth_rejection_permanent = registration->auth_rejection_permanent;
 
 	pjsip_regc_update_expires(state->client_state->client, registration->expiration);
@@ -1550,7 +1910,8 @@
 
 	cancel_registration(state->client_state);
 
-	if (pjsip_regc_unregister(client, &tdata) == PJ_SUCCESS) {
+	if (pjsip_regc_unregister(client, &tdata) == PJ_SUCCESS
+		&& add_configured_supported_headers(state->client_state, tdata)) {
 		registration_client_send(state->client_state, tdata);
 	}
 
@@ -2137,6 +2498,107 @@
 	reregister_all();
 }
 
+/*! \brief Callback function for matching an outbound registration based on name */
+static int find_registration(void *obj, void *arg, int flags)
+{
+	struct sip_outbound_registration_state *state = obj;
+	const char* target_name = arg;
+
+	const char* registration_name = ast_sorcery_object_get_id(state->registration);
+
+	return !strcmp(target_name, registration_name) ? CMP_MATCH : 0;
+}
+
+static int transport_from_endpoint_override(const struct ast_sip_endpoint *endpoint, pjsip_transport** transport)
+{
+	RAII_VAR(struct ao2_container *, states, NULL, ao2_cleanup);
+	RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup);
+
+	if (!endpoint || ast_strlen_zero(endpoint->outbound_registration)) {
+		ast_log(LOG_DEBUG, "Outgoing request not associated with a registration. No mangling necessary.\n");
+		return 0;
+	}
+
+	states = ao2_global_obj_ref(current_states);
+	if (!states) {
+		ast_log(LOG_ERROR, "Cannot find outbound registration states\n");
+		return 0;
+	}
+
+	state = ao2_callback(states, 0, find_registration, (void*)endpoint->outbound_registration);
+	if (!state) {
+		ast_log(LOG_ERROR, "Cannot find matching outbound registration state: %s\n", endpoint->outbound_registration);
+		return 0;
+	}
+
+	ast_log(LOG_DEBUG, "Setting transport to %p\n", (void*)state->client_state->transport);
+	*transport = state->client_state->transport;
+
+	return 1;
+}
+
+/*! \brief Mangle outgoing INVITEs by adding headers based on the response to the associated registration request */
+static void handle_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
+{
+	static const pj_str_t route_str = { "Route", 5 };
+	static const pj_str_t pj_pai_name = { "P-Preferred-Identity", 20 };
+
+	RAII_VAR(struct ao2_container *, states, NULL, ao2_cleanup);
+	RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup);
+	struct service_route_vector_type service_routes;
+	pjsip_generic_string_hdr *pai_hdr;
+	int i;
+
+	if (!session || !session->endpoint || ast_strlen_zero(session->endpoint->outbound_registration)) {
+		ast_log(LOG_DEBUG, "Outgoing request not associated with a registration. No mangling necessary.\n");
+		return;
+	}
+
+	states = ao2_global_obj_ref(current_states);
+	if (!states) {
+		ast_log(LOG_ERROR, "Cannot find outbound registration states\n");
+		return;
+	}
+
+	state = ao2_callback(states, 0, find_registration, (void*)session->endpoint->outbound_registration);
+	if (!state) {
+		ast_log(LOG_ERROR, "Cannot find matching outbound registration state\n");
+		return;
+	}
+
+	ast_log(LOG_DEBUG, "Found matching outbound registration state\n");
+
+	/* add Route for every Service-Route in associated registration response */
+	service_routes = state->client_state->service_route_vector;
+
+	for (i = 0; i < AST_VECTOR_SIZE(&service_routes); ++i)
+	{
+		pjsip_generic_string_hdr* route_hdr;
+
+		pj_str_t service_route_str = AST_VECTOR_GET(&service_routes, i);
+		ast_log(LOG_DEBUG, "Found service-route. Adding route header for %s\n", service_route_str.ptr);
+
+		route_hdr = pjsip_generic_string_hdr_create(tdata->pool, &route_str, &service_route_str);
+ 
+		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)route_hdr);
+	}
+
+
+	/* add ppi header for first Associated-URI in associated registration response */
+	pai_hdr = pjsip_generic_string_hdr_create(tdata->pool, &pj_pai_name, &state->client_state->associated_uri);
+	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)pai_hdr);
+
+	/* add outbound & path to supported header */
+	add_configured_supported_headers(state->client_state, tdata);
+}
+
+
+static struct ast_sip_session_supplement gvsip_supplement = {
+	.method = "INVITE, CANCEL",
+	.outgoing_request = handle_outgoing_request,
+	.priority = AST_SIP_SUPPLEMENT_PRIORITY_LAST,
+};
+
 static int unload_module(void)
 {
 	int remaining;
@@ -2178,6 +2640,8 @@
 
 	ast_debug(2, "Successful shutdown.\n");
 
+	ast_pjproject_caching_pool_destroy(&cachingpool);
+
 	ao2_cleanup(shutdown_group);
 	shutdown_group = NULL;
 
@@ -2204,6 +2668,9 @@
 	ao2_global_obj_replace_unref(current_states, new_states);
 	ao2_ref(new_states, -1);
 
+	ast_pjproject_caching_pool_init(&cachingpool, &pj_pool_factory_default_policy, 0);
+	reg_pool = pj_pool_create(&cachingpool.factory, "registration", 4096, 4096, NULL);
+
 	/*
 	 * Register sorcery object descriptions.
 	 */
@@ -2219,6 +2686,7 @@
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "server_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, server_uri));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "client_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, client_uri));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "contact_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, contact_user));
+	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "contact_additional_params", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, contact_additional_params));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, transport));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, outbound_proxy));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "expiration", "3600", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, expiration));
@@ -2229,6 +2697,8 @@
 	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, outbound_auths_to_var_list, 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_object_field_register(ast_sip_get_sorcery(), "registration", "support_outbound", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, support_outbound));
+	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "transport_reuse", "yes", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, transport_reuse));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "line", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, line));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, endpoint));
 
@@ -2249,6 +2719,8 @@
 	/* Register how this module identifies endpoints. */
 	ast_sip_register_endpoint_identifier(&line_identifier);
 
+	ast_sip_session_register_supplement(&gvsip_supplement);
+
 	/* Register CLI commands. */
 	cli_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
 	if (!cli_formatter) {
diff --git a/third-party/pjproject/patches/0110-oauth.patch b/third-party/pjproject/patches/0110-oauth.patch
new file mode 100644
index 0000000..ac1f2d5
--- /dev/null
+++ b/third-party/pjproject/patches/0110-oauth.patch
@@ -0,0 +1,127 @@
+diff -x '*.o' -x '*.a' -ru a/pjsip/include/pjsip/sip_auth_msg.h b/pjsip/include/pjsip/sip_auth_msg.h
+--- a/pjsip/include/pjsip/sip_auth_msg.h	2011-05-05 01:14:19.000000000 -0500
++++ b/pjsip/include/pjsip/sip_auth_msg.h	2018-06-11 21:48:01.497904085 -0500
+@@ -89,6 +89,23 @@
+ typedef struct pjsip_pgp_credential pjsip_pgp_credential;
+ 
+ /**
++ * This structure describe credential used in Authorization and
++ * Proxy-Authorization header for oauth authentication scheme.
++ */
++struct pjsip_oauth_credential
++{
++    pj_str_t    realm;          /**< Realm of the credential    */
++    pjsip_param other_param;    /**< Other parameters.          */
++    pj_str_t    username;       /**< Username parameter.        */
++    pj_str_t    token;          /**< Token parameter.           */
++};
++
++/**
++ * @see pjsip_oauth_credential
++ */
++typedef struct pjsip_oauth_credential pjsip_oauth_credential;
++
++/**
+  * This structure describes SIP Authorization header (and also SIP
+  * Proxy-Authorization header).
+  */
+@@ -106,6 +123,8 @@
+ 	pjsip_common_credential common;	/**< Common fields.	    */
+ 	pjsip_digest_credential digest;	/**< Digest credentials.    */
+ 	pjsip_pgp_credential	pgp;	/**< PGP credentials.	    */
++	pjsip_oauth_credential  oauth;  /**< OAuth credentials.     */
++
+     } credential;
+ };
+ 
+diff -x '*.o' -x '*.a' -ru a/pjsip/include/pjsip/sip_auth_parser.h b/pjsip/include/pjsip/sip_auth_parser.h
+--- a/pjsip/include/pjsip/sip_auth_parser.h	2011-05-05 01:14:19.000000000 -0500
++++ b/pjsip/include/pjsip/sip_auth_parser.h	2018-06-11 21:37:18.105232901 -0500
+@@ -64,6 +64,7 @@
+ 			pjsip_FALSE_STR,    /**< "false" string const.	    */
+ 			pjsip_DIGEST_STR,   /**< "digest" string const.	    */
+ 			pjsip_PGP_STR,	    /**< "pgp" string const.	    */
++			pjsip_BEARER_STR,   /**< "bearer" string const.     */
+ 			pjsip_MD5_STR,	    /**< "md5" string const.	    */
+ 			pjsip_AUTH_STR;	    /**< "auth" string const.	    */
+ 
+diff -x '*.o' -x '*.a' -ru a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c
+--- a/pjsip/src/pjsip/sip_auth_client.c	2017-03-31 01:02:48.000000000 -0500
++++ b/pjsip/src/pjsip/sip_auth_client.c	2018-06-11 22:26:04.728266296 -0500
+@@ -959,13 +959,25 @@
+ 
+ 		hs = pjsip_authorization_hdr_create(tdata->pool);
+ 		pj_strdup(tdata->pool, &hs->scheme, &c->scheme);
+-		pj_strdup(tdata->pool, &hs->credential.digest.username,
+-			  &c->username);
+-		pj_strdup(tdata->pool, &hs->credential.digest.realm,
+-			  &c->realm);
+-		pj_strdup(tdata->pool, &hs->credential.digest.uri, &uri);
+-		pj_strdup(tdata->pool, &hs->credential.digest.algorithm,
++		if (pj_stricmp(&c->scheme, &pjsip_BEARER_STR)==0)
++		{
++			pj_strdup(tdata->pool, &hs->credential.oauth.username,
++                                  &c->username);
++                        pj_strdup(tdata->pool, &hs->credential.oauth.realm,
++                                  &c->realm);
++                        pj_strdup(tdata->pool, &hs->credential.oauth.token,
++                                  &c->data);
++		}
++		else //if (pj_stricmp(&c->scheme, &pjsip_DIGEST_STR)==0)
++		{
++			pj_strdup(tdata->pool, &hs->credential.digest.username,
++				  &c->username);
++			pj_strdup(tdata->pool, &hs->credential.digest.realm,
++				  &c->realm);
++			pj_strdup(tdata->pool, &hs->credential.digest.uri, &uri);
++			pj_strdup(tdata->pool, &hs->credential.digest.algorithm,
+ 			  &sess->pref.algorithm);
++		}
+ 
+ 		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hs);
+ 	    }
+diff -x '*.o' -x '*.a' -ru a/pjsip/src/pjsip/sip_auth_msg.c b/pjsip/src/pjsip/sip_auth_msg.c
+--- a/pjsip/src/pjsip/sip_auth_msg.c	2016-01-26 23:42:20.000000000 -0600
++++ b/pjsip/src/pjsip/sip_auth_msg.c	2018-06-11 22:31:08.414019962 -0500
+@@ -103,6 +103,19 @@
+     return -1;
+ }
+ 
++static int print_oauth_credential(pjsip_oauth_credential *cred, char *buf, pj_size_t size)
++{
++    pj_ssize_t printed;
++    char *startbuf = buf;
++    char *endbuf = buf + size;
++
++    copy_advance_pair_quote_cond_always(buf, "token=", 6, cred->token, '"', '"');
++    copy_advance_pair_quote_cond_always(buf, ", username=", 11, cred->username, '"', '"');
++    copy_advance_pair_quote_cond_always(buf, ", realm=", 8, cred->realm, '"', '"');
++
++    return (int) (buf-startbuf);
++}
++
+ static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr,
+ 					  char *buf, pj_size_t size)
+ {
+@@ -125,6 +138,10 @@
+     {
+ 	printed = print_pgp_credential(&hdr->credential.pgp, buf, endbuf - buf);
+     } 
++    else if (pj_stricmp(&hdr->scheme, &pjsip_BEARER_STR) == 0)
++    {
++        printed = print_oauth_credential(&hdr->credential.oauth, buf, endbuf - buf);
++    }
+     else {
+ 	pj_assert(0);
+ 	return -1;
+diff -x '*.o' -x '*.a' -ru a/pjsip/src/pjsip/sip_auth_parser.c b/pjsip/src/pjsip/sip_auth_parser.c
+--- a/pjsip/src/pjsip/sip_auth_parser.c	2014-06-09 21:56:56.000000000 -0500
++++ b/pjsip/src/pjsip/sip_auth_parser.c	2018-06-11 21:53:03.831715838 -0500
+@@ -59,6 +59,7 @@
+ 		pjsip_QUOTED_DIGEST_STR =   { "\"Digest\"", 8},
+ 		pjsip_PGP_STR =		    { "PGP", 3 },
+ 		pjsip_QUOTED_PGP_STR =	    { "\"PGP\"", 5 },
++		pjsip_BEARER_STR =          { "Bearer", 6 },
+ 		pjsip_MD5_STR =		    { "md5", 3 },
+ 		pjsip_QUOTED_MD5_STR =	    { "\"md5\"", 5},
+ 		pjsip_AUTH_STR =	    { "auth", 4},
diff --git a/third-party/pjproject/patches/0120-contact-params.patch b/third-party/pjproject/patches/0120-contact-params.patch
new file mode 100644
index 0000000..8fcc45b
--- /dev/null
+++ b/third-party/pjproject/patches/0120-contact-params.patch
@@ -0,0 +1,96 @@
+diff -ru a/pjsip/include/pjsip-ua/sip_regc.h b/pjsip/include/pjsip-ua/sip_regc.h
+--- a/pjsip/include/pjsip-ua/sip_regc.h	2016-06-24 08:03:25.000000000 -0500
++++ b/pjsip/include/pjsip-ua/sip_regc.h	2018-07-04 13:29:26.165775909 -0500
+@@ -413,7 +413,8 @@
+  */
+ PJ_DECL(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
+ 					        int ccnt,
+-						const pj_str_t contact[] );
++						const pj_str_t contact[],
++						const pjsip_param* params );
+ 
+ /**
+  * Update the expires value. The next REGISTER request will contain
+     /* Transaction settings */
+diff -ru a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c
+--- a/pjsip/src/pjsip-ua/sip_reg.c	2016-06-30 03:23:08.000000000 -0500
++++ b/pjsip/src/pjsip-ua/sip_reg.c	2018-07-04 13:30:23.440884417 -0500
+@@ -250,7 +250,8 @@
+ 
+ static pj_status_t set_contact( pjsip_regc *regc,
+ 			        int contact_cnt,
+-				const pj_str_t contact[] )
++				const pj_str_t contact[],
++				const pjsip_param *params )
+ {
+     const pj_str_t CONTACT = { "Contact", 7 };
+     pjsip_contact_hdr *h;
+@@ -321,6 +322,20 @@
+ 	    pj_list_push_back(&sip_uri->other_param, xuid_param);
+ 	}
+ 
++	/* Add additional contact params */
++	if (params)
++	{
++	    pjsip_param* param = params->next;
++	    while (param != params) {
++	        pjsip_param *param_copy;
++	        param_copy = PJ_POOL_ZALLOC_T(regc->pool, pjsip_param);
++	        param_copy->name = param->name;
++	        param_copy->value = param->value;
++	        pj_list_push_back(&hdr->other_param, param_copy);
++	        param = param->next;
++	    }
++	}
++
+ 	pj_list_push_back(&regc->contact_hdr_list, hdr);
+     }
+ 
+@@ -376,7 +391,7 @@
+ 
+ 
+     /* Set "Contact" header. */
+-    status = set_contact( regc, contact_cnt, contact);
++    status = set_contact( regc, contact_cnt, contact, NULL);
+     if (status != PJ_SUCCESS)
+ 	return status;
+ 
+@@ -709,14 +724,15 @@
+ 
+ PJ_DEF(pj_status_t) pjsip_regc_update_contact(  pjsip_regc *regc,
+ 					        int contact_cnt,
+-						const pj_str_t contact[] )
++						const pj_str_t contact[],
++					        const pjsip_param *params )
+ {
+     pj_status_t status;
+ 
+     PJ_ASSERT_RETURN(regc, PJ_EINVAL);
+ 
+     pj_lock_acquire(regc->lock);
+-    status = set_contact( regc, contact_cnt, contact );
++    status = set_contact( regc, contact_cnt, contact, params );
+     pj_lock_release(regc->lock);
+ 
+     return status;
+@@ -1125,7 +1141,7 @@
+ 	    }
+ 
+             /* Update contact address */
+-            pjsip_regc_update_contact(regc, param.contact_cnt, param.contact);
++            pjsip_regc_update_contact(regc, param.contact_cnt, param.contact, NULL);
+             update_contact = PJ_TRUE;
+         }
+     }
+diff -ru a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c
+--- a/pjsip/src/pjsua-lib/pjsua_acc.c	2017-09-15 00:32:08.000000000 -0500
++++ b/pjsip/src/pjsua-lib/pjsua_acc.c	2018-07-04 13:30:55.098286217 -0500
+@@ -1865,7 +1865,7 @@
+     if (contact_rewrite_method == PJSUA_CONTACT_REWRITE_NO_UNREG &&
+         acc->regc != NULL)
+     {
+-	pjsip_regc_update_contact(acc->regc, 1, &acc->reg_contact);
++	pjsip_regc_update_contact(acc->regc, 1, &acc->reg_contact, NULL);
+     }
+ 
+     /* Perform new registration */

-- 
To view, visit https://gerrit.asterisk.org/9505
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: Id214c2d1c550a41fcf564b7df8f3da7be565bd58
Gerrit-Change-Number: 9505
Gerrit-PatchSet: 1
Gerrit-Owner: Nick French <naf at ou.edu>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20180718/23b84be2/attachment-0001.html>


More information about the asterisk-code-review mailing list