<p>Joshua Colp <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/9505">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  George Joseph: Looks good to me, approved
  Joshua Colp: Approved for Submit

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_pjsip: Implement additional SIP RFCs for Google Voice trunk compatability<br><br>This change implements a few different generic things which were brought<br>on by Google Voice SIP.<br><br>1.  The concept of flow transports have been introduced.  These are<br>configurable transports in pjsip.conf which can be used to reference a<br>flow of signaling to a target.  These have runtime configuration that can<br>be changed by the signaling itself (such as Service-Routes and<br>P-Preferred-Identity).  When used these guarantee an individual connection<br>(in the case of TCP or TLS) even if multiple flow transports exist to the<br>same target.<br><br>2.  Service-Routes (RFC 3608) support has been added to the outbound<br>registration module which when received will be stored on the flow<br>transport and used for requests referencing it.<br><br>3.  P-Associated-URI / P-Preferred-Identity (RFC 3325) support has been<br>added to the outbound registration module.  If a P-Associated-URI header<br>is received it will be used on requests as the P-Preferred-Identity.<br><br>4.  Configurable outbound extension support has been added to the outbound<br>registration module.  When set the extension will be placed in the<br>Supported header.<br><br>5.  Header parameters can now be configured on an outbound registration<br>which will be placed in the Contact header.<br><br>6.  Google specific OAuth / Bearer token authentication<br>(draft-ietf-sipcore-sip-authn-02) has been added to the outbound<br>registration module.<br><br>All functionality changes are controlled by pjsip.conf configuration<br>options and do not affect non-configured pjsip endpoints otherwise.<br><br>ASTERISK-27971 #close<br><br>Change-Id: Id214c2d1c550a41fcf564b7df8f3da7be565bd58<br>---<br>M configs/samples/pjsip.conf.sample<br>M configure<br>M configure.ac<br>A contrib/ast-db-manage/config/versions/465f47f880be_add_pjsip_google_voice_sip_options.py<br>M include/asterisk/autoconfig.h.in<br>M include/asterisk/res_pjsip.h<br>M res/res_pjsip.c<br>M res/res_pjsip/config_auth.c<br>M res/res_pjsip/config_transport.c<br>M res/res_pjsip_outbound_authenticator_digest.c<br>M res/res_pjsip_outbound_publish.c<br>M res/res_pjsip_outbound_registration.c<br>M res/res_pjsip_session.c<br>M third-party/pjproject/configure.m4<br>A third-party/pjproject/patches/0020-oauth.patch<br>A third-party/pjproject/patches/0030-allow-disabling-of-connection-reuse.patch<br>16 files changed, 1,190 insertions(+), 52 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample</span><br><span>index fa16557..dc0679b 100644</span><br><span>--- a/configs/samples/pjsip.conf.sample</span><br><span>+++ b/configs/samples/pjsip.conf.sample</span><br><span>@@ -127,7 +127,7 @@</span><br><span> ;</span><br><span> ;[transport-udp]</span><br><span> ;type=transport</span><br><span style="color: hsl(0, 100%, 40%);">-;protocol=udp    ;udp,tcp,tls,ws,wss</span><br><span style="color: hsl(120, 100%, 40%);">+;protocol=udp    ;udp,tcp,tls,ws,wss,flow</span><br><span> ;bind=0.0.0.0</span><br><span> </span><br><span> ; UDP transport behind NAT</span><br><span>@@ -158,6 +158,18 @@</span><br><span> ;cipher=ADH-AES256-SHA,ADH-AES128-SHA</span><br><span> ;method=tlsv1</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+; Example flow transport</span><br><span style="color: hsl(120, 100%, 40%);">+;</span><br><span style="color: hsl(120, 100%, 40%);">+; A flow transport is used to reference a flow of signaling with a specific</span><br><span style="color: hsl(120, 100%, 40%);">+; target. All endpoints or other objects that reference the transport will use</span><br><span style="color: hsl(120, 100%, 40%);">+; the same underlying transport and can share runtime discovered transport</span><br><span style="color: hsl(120, 100%, 40%);">+; configuration (such as service routes). The protocol in use will be determined</span><br><span style="color: hsl(120, 100%, 40%);">+; based on the URI used to establish the connection. Currently only TCP and TLS</span><br><span style="color: hsl(120, 100%, 40%);">+; are supported.</span><br><span style="color: hsl(120, 100%, 40%);">+;</span><br><span style="color: hsl(120, 100%, 40%);">+;[transport-flow]</span><br><span style="color: hsl(120, 100%, 40%);">+;type=transport</span><br><span style="color: hsl(120, 100%, 40%);">+;protocol=flow</span><br><span> </span><br><span> ;===============OUTBOUND REGISTRATION WITH OUTBOUND AUTHENTICATION============</span><br><span> ;</span><br><span>diff --git a/configure b/configure</span><br><span>index 6b71e19..47cf7e7 100755</span><br><span>--- a/configure</span><br><span>+++ b/configure</span><br><span>@@ -930,6 +930,14 @@</span><br><span> POPT_DIR</span><br><span> POPT_INCLUDE</span><br><span> POPT_LIB</span><br><span style="color: hsl(120, 100%, 40%);">+PBX_PJSIP_OAUTH_AUTHENTICATION</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_OAUTH_AUTHENTICATION_DIR</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_OAUTH_AUTHENTICATION_INCLUDE</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_OAUTH_AUTHENTICATION_LIB</span><br><span style="color: hsl(120, 100%, 40%);">+PBX_PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE_DIR</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE_INCLUDE</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE_LIB</span><br><span> PBX_PJSIP_ENDPOINT_COMPACT_FORM</span><br><span> PJSIP_ENDPOINT_COMPACT_FORM_DIR</span><br><span> PJSIP_ENDPOINT_COMPACT_FORM_INCLUDE</span><br><span>@@ -9470,6 +9478,12 @@</span><br><span> $as_echo "#define HAVE_PJSIP_ENDPOINT_COMPACT_FORM 1" >>confdefs.h</span><br><span> </span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+$as_echo "#define HAVE_PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE 1" >>confdefs.h</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+$as_echo "#define HAVE_PJSIP_OAUTH_AUTHENTICATION 1" >>confdefs.h</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> </span><br><span> </span><br><span> </span><br><span>@@ -11624,6 +11638,30 @@</span><br><span> </span><br><span> </span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE_DESCRIP="PJSIP Transport Connection Reuse Disabling"</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE_OPTION=pjsip</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE_DIR=${PJPROJECT_DIR}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+PBX_PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE=0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_OAUTH_AUTHENTICATION_DESCRIP="PJSIP OAuth Authentication Support"</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_OAUTH_AUTHENTICATION_OPTION=pjsip</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_OAUTH_AUTHENTICATION_DIR=${PJPROJECT_DIR}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+PBX_PJSIP_OAUTH_AUTHENTICATION=0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> fi</span><br><span> </span><br><span> </span><br><span>@@ -25577,6 +25615,86 @@</span><br><span>      CPPFLAGS="${saved_cppflags}"</span><br><span>     fi</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if test "x${PBX_PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE}" != "x1" -a "${USE_PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE}" != "no"; then</span><br><span style="color: hsl(120, 100%, 40%);">+     { $as_echo "$as_me:${as_lineno-$LINENO}: checking if \"struct pjsip_tpselector sel; sel.disable_connection_reuse = PJ_TRUE;\" compiles using pjsip.h" >&5</span><br><span style="color: hsl(120, 100%, 40%);">+$as_echo_n "checking if \"struct pjsip_tpselector sel; sel.disable_connection_reuse = PJ_TRUE;\" compiles using pjsip.h... " >&6; }</span><br><span style="color: hsl(120, 100%, 40%);">+  saved_cppflags="${CPPFLAGS}"</span><br><span style="color: hsl(120, 100%, 40%);">+        if test "x${PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE_DIR}" != "x"; then</span><br><span style="color: hsl(120, 100%, 40%);">+       PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE_INCLUDE="-I${PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE_DIR}/include"</span><br><span style="color: hsl(120, 100%, 40%);">+    fi</span><br><span style="color: hsl(120, 100%, 40%);">+    CPPFLAGS="${CPPFLAGS} ${PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE_INCLUDE}"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext</span><br><span style="color: hsl(120, 100%, 40%);">+/* end confdefs.h.  */</span><br><span style="color: hsl(120, 100%, 40%);">+ #include <pjsip.h></span><br><span style="color: hsl(120, 100%, 40%);">+int</span><br><span style="color: hsl(120, 100%, 40%);">+main ()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct pjsip_tpselector sel; sel.disable_connection_reuse = PJ_TRUE;;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  ;</span><br><span style="color: hsl(120, 100%, 40%);">+  return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+_ACEOF</span><br><span style="color: hsl(120, 100%, 40%);">+if ac_fn_c_try_compile "$LINENO"; then :</span><br><span style="color: hsl(120, 100%, 40%);">+     { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5</span><br><span style="color: hsl(120, 100%, 40%);">+$as_echo "yes" >&6; }</span><br><span style="color: hsl(120, 100%, 40%);">+          PBX_PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE=1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+$as_echo "#define HAVE_PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE 1" >>confdefs.h</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+else</span><br><span style="color: hsl(120, 100%, 40%);">+         { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5</span><br><span style="color: hsl(120, 100%, 40%);">+$as_echo "no" >&6; }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+fi</span><br><span style="color: hsl(120, 100%, 40%);">+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext</span><br><span style="color: hsl(120, 100%, 40%);">+  CPPFLAGS="${saved_cppflags}"</span><br><span style="color: hsl(120, 100%, 40%);">+    fi</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if test "x${PBX_PJSIP_OAUTH_AUTHENTICATION}" != "x1" -a "${USE_PJSIP_OAUTH_AUTHENTICATION}" != "no"; then</span><br><span style="color: hsl(120, 100%, 40%);">+     { $as_echo "$as_me:${as_lineno-$LINENO}: checking if \"struct pjsip_oauth_credential credential;\" compiles using pjsip.h" >&5</span><br><span style="color: hsl(120, 100%, 40%);">+$as_echo_n "checking if \"struct pjsip_oauth_credential credential;\" compiles using pjsip.h... " >&6; }</span><br><span style="color: hsl(120, 100%, 40%);">+        saved_cppflags="${CPPFLAGS}"</span><br><span style="color: hsl(120, 100%, 40%);">+        if test "x${PJSIP_OAUTH_AUTHENTICATION_DIR}" != "x"; then</span><br><span style="color: hsl(120, 100%, 40%);">+     PJSIP_OAUTH_AUTHENTICATION_INCLUDE="-I${PJSIP_OAUTH_AUTHENTICATION_DIR}/include"</span><br><span style="color: hsl(120, 100%, 40%);">+        fi</span><br><span style="color: hsl(120, 100%, 40%);">+    CPPFLAGS="${CPPFLAGS} ${PJSIP_OAUTH_AUTHENTICATION_INCLUDE}"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext</span><br><span style="color: hsl(120, 100%, 40%);">+/* end confdefs.h.  */</span><br><span style="color: hsl(120, 100%, 40%);">+ #include <pjsip.h></span><br><span style="color: hsl(120, 100%, 40%);">+int</span><br><span style="color: hsl(120, 100%, 40%);">+main ()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct pjsip_oauth_credential credential;;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  ;</span><br><span style="color: hsl(120, 100%, 40%);">+  return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+_ACEOF</span><br><span style="color: hsl(120, 100%, 40%);">+if ac_fn_c_try_compile "$LINENO"; then :</span><br><span style="color: hsl(120, 100%, 40%);">+     { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5</span><br><span style="color: hsl(120, 100%, 40%);">+$as_echo "yes" >&6; }</span><br><span style="color: hsl(120, 100%, 40%);">+             PBX_PJSIP_OAUTH_AUTHENTICATION=1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+$as_echo "#define HAVE_PJSIP_OAUTH_AUTHENTICATION 1" >>confdefs.h</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+else</span><br><span style="color: hsl(120, 100%, 40%);">+         { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5</span><br><span style="color: hsl(120, 100%, 40%);">+$as_echo "no" >&6; }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+fi</span><br><span style="color: hsl(120, 100%, 40%);">+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext</span><br><span style="color: hsl(120, 100%, 40%);">+      CPPFLAGS="${saved_cppflags}"</span><br><span style="color: hsl(120, 100%, 40%);">+    fi</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>       LIBS="${saved_libs}"</span><br><span>       CPPFLAGS="${saved_cppflags}"</span><br><span> </span><br><span>diff --git a/configure.ac b/configure.ac</span><br><span>index dd0c8ed..73727b9 100644</span><br><span>--- a/configure.ac</span><br><span>+++ b/configure.ac</span><br><span>@@ -537,6 +537,8 @@</span><br><span> AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_TSX_LAYER_FIND_TSX2], [pjsip_tsx_layer_find_tsx2 support], [PJPROJECT], [pjsip])</span><br><span> AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS], [PJSIP INVITE Accept Multiple SDP Answers], [PJPROJECT], [pjsip])</span><br><span> AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_ENDPOINT_COMPACT_FORM], [PJSIP Compact Form Support on Endpoint], [PJPROJECT], [pjsip])</span><br><span style="color: hsl(120, 100%, 40%);">+AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE], [PJSIP Transport Connection Reuse Disabling], [PJPROJECT], [pjsip])</span><br><span style="color: hsl(120, 100%, 40%);">+AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_OAUTH_AUTHENTICATION], [PJSIP OAuth Authentication Support], [PJPROJECT], [pjsip])</span><br><span> fi</span><br><span> </span><br><span> AST_EXT_LIB_SETUP([POPT], [popt], [popt])</span><br><span>@@ -2376,6 +2378,8 @@</span><br><span>       AST_C_COMPILE_CHECK([PJSIP_TLS_TRANSPORT_PROTO], [struct pjsip_tls_setting setting; int proto; proto = setting.proto;], [pjsip.h])</span><br><span>       AST_C_COMPILE_CHECK([PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS], [pjsip_cfg()->endpt.accept_multiple_sdp_answers = 0;], [pjsip.h])</span><br><span>       AST_C_COMPILE_CHECK([PJSIP_ENDPOINT_COMPACT_FORM], [pjsip_cfg()->endpt.use_compact_form = PJ_TRUE;], [pjsip.h])</span><br><span style="color: hsl(120, 100%, 40%);">+      AST_C_COMPILE_CHECK([PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE], [struct pjsip_tpselector sel; sel.disable_connection_reuse = PJ_TRUE;], [pjsip.h])</span><br><span style="color: hsl(120, 100%, 40%);">+      AST_C_COMPILE_CHECK([PJSIP_OAUTH_AUTHENTICATION], [struct pjsip_oauth_credential credential;], [pjsip.h])</span><br><span>       LIBS="${saved_libs}"</span><br><span>       CPPFLAGS="${saved_cppflags}"</span><br><span> </span><br><span>diff --git a/contrib/ast-db-manage/config/versions/465f47f880be_add_pjsip_google_voice_sip_options.py b/contrib/ast-db-manage/config/versions/465f47f880be_add_pjsip_google_voice_sip_options.py</span><br><span>new file mode 100644</span><br><span>index 0000000..0f9e8b9</span><br><span>--- /dev/null</span><br><span>+++ b/contrib/ast-db-manage/config/versions/465f47f880be_add_pjsip_google_voice_sip_options.py</span><br><span>@@ -0,0 +1,115 @@</span><br><span style="color: hsl(120, 100%, 40%);">+"""add pjsip google voice sip options</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Revision ID: 465f47f880be</span><br><span style="color: hsl(120, 100%, 40%);">+Revises: 7f85dd44c775</span><br><span style="color: hsl(120, 100%, 40%);">+Create Date: 2018-09-25 17:26:12.892161</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# revision identifiers, used by Alembic.</span><br><span style="color: hsl(120, 100%, 40%);">+revision = '465f47f880be'</span><br><span style="color: hsl(120, 100%, 40%);">+down_revision = '7f85dd44c775'</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from alembic import op</span><br><span style="color: hsl(120, 100%, 40%);">+from sqlalchemy.dialects.postgresql import ENUM</span><br><span style="color: hsl(120, 100%, 40%);">+import sqlalchemy as sa</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_BOOL_NAME = 'ast_bool_values'</span><br><span style="color: hsl(120, 100%, 40%);">+# We'll just ignore the n/y and f/t abbreviations as Asterisk does not write</span><br><span style="color: hsl(120, 100%, 40%);">+# those aliases.</span><br><span style="color: hsl(120, 100%, 40%);">+AST_BOOL_VALUES = [ '0', '1',</span><br><span style="color: hsl(120, 100%, 40%);">+                    'off', 'on',</span><br><span style="color: hsl(120, 100%, 40%);">+                    'false', 'true',</span><br><span style="color: hsl(120, 100%, 40%);">+                    'no', 'yes' ]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_TRANSPORT_PROTOCOL_OLD_NAME = 'pjsip_transport_protocol_values'</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_TRANSPORT_PROTOCOL_NEW_NAME = 'pjsip_transport_protocol_values_v2'</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_TRANSPORT_PROTOCOL_OLD_VALUES = ['udp', 'tcp', 'tls', 'ws', 'wss']</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_TRANSPORT_PROTOCOL_NEW_VALUES = ['udp', 'tcp', 'tls', 'ws', 'wss', 'flow']</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_TRANSPORT_PROTOCOL_OLD_TYPE = sa.Enum(*PJSIP_TRANSPORT_PROTOCOL_OLD_VALUES,</span><br><span style="color: hsl(120, 100%, 40%);">+                                            name=PJSIP_TRANSPORT_PROTOCOL_OLD_NAME)</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_TRANSPORT_PROTOCOL_NEW_TYPE = sa.Enum(*PJSIP_TRANSPORT_PROTOCOL_NEW_VALUES,</span><br><span style="color: hsl(120, 100%, 40%);">+                                            name=PJSIP_TRANSPORT_PROTOCOL_NEW_NAME)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_AUTH_TYPE_OLD_NAME = 'pjsip_auth_type_values'</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_AUTH_TYPE_NEW_NAME = 'pjsip_auth_type_values_v2'</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_AUTH_TYPE_OLD_VALUES = ['md5', 'userpass']</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_AUTH_TYPE_NEW_VALUES = ['md5', 'userpass', 'google_oauth']</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_AUTH_TYPE_OLD_TYPE = sa.Enum(*PJSIP_AUTH_TYPE_OLD_VALUES,</span><br><span style="color: hsl(120, 100%, 40%);">+                                   name=PJSIP_AUTH_TYPE_OLD_NAME)</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_AUTH_TYPE_NEW_TYPE = sa.Enum(*PJSIP_AUTH_TYPE_NEW_VALUES,</span><br><span style="color: hsl(120, 100%, 40%);">+                                   name=PJSIP_AUTH_TYPE_NEW_NAME)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def upgrade():</span><br><span style="color: hsl(120, 100%, 40%);">+    if op.get_context().bind.dialect.name == 'postgresql':</span><br><span style="color: hsl(120, 100%, 40%);">+        enum = PJSIP_TRANSPORT_PROTOCOL_NEW_TYPE</span><br><span style="color: hsl(120, 100%, 40%);">+        enum.create(op.get_bind(), checkfirst=False)</span><br><span style="color: hsl(120, 100%, 40%);">+        op.execute('ALTER TABLE ps_transports ALTER COLUMN protocol TYPE'</span><br><span style="color: hsl(120, 100%, 40%);">+                   ' ' + PJSIP_TRANSPORT_PROTOCOL_NEW_NAME + ' USING'</span><br><span style="color: hsl(120, 100%, 40%);">+                   ' protocol::text::' + PJSIP_TRANSPORT_PROTOCOL_NEW_NAME)</span><br><span style="color: hsl(120, 100%, 40%);">+        ENUM(name=PJSIP_TRANSPORT_PROTOCOL_OLD_NAME).drop(op.get_bind(), checkfirst=False)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        enum = PJSIP_AUTH_TYPE_NEW_TYPE</span><br><span style="color: hsl(120, 100%, 40%);">+        enum.create(op.get_bind(), checkfirst=False)</span><br><span style="color: hsl(120, 100%, 40%);">+        op.execute('ALTER TABLE ps_auths ALTER COLUMN auth_type TYPE'</span><br><span style="color: hsl(120, 100%, 40%);">+                   ' ' + PJSIP_AUTH_TYPE_NEW_NAME + ' USING'</span><br><span style="color: hsl(120, 100%, 40%);">+                   ' auth_type::text::' + PJSIP_AUTH_TYPE_NEW_NAME)</span><br><span style="color: hsl(120, 100%, 40%);">+        ENUM(name=PJSIP_AUTH_TYPE_OLD_NAME).drop(op.get_bind(), checkfirst=False)</span><br><span style="color: hsl(120, 100%, 40%);">+    else:</span><br><span style="color: hsl(120, 100%, 40%);">+        op.alter_column('ps_transports', 'protocol',</span><br><span style="color: hsl(120, 100%, 40%);">+                        type_=PJSIP_TRANSPORT_PROTOCOL_NEW_TYPE,</span><br><span style="color: hsl(120, 100%, 40%);">+                        existing_type=PJSIP_TRANSPORT_PROTOCOL_OLD_TYPE)</span><br><span style="color: hsl(120, 100%, 40%);">+        op.alter_column('ps_auths', 'auth_type',</span><br><span style="color: hsl(120, 100%, 40%);">+                        type_=PJSIP_AUTH_TYPE_NEW_TYPE,</span><br><span style="color: hsl(120, 100%, 40%);">+                        existing_type=PJSIP_AUTH_TYPE_OLD_TYPE)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    # ast_bool_values have already been created, so use postgres enum object</span><br><span style="color: hsl(120, 100%, 40%);">+    # type to get around "already created" issue - works okay with mysql</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_bool_values = ENUM(*AST_BOOL_VALUES, name=AST_BOOL_NAME, create_type=False)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    op.add_column('ps_registrations', sa.Column('support_outbound', ast_bool_values))</span><br><span style="color: hsl(120, 100%, 40%);">+    op.add_column('ps_registrations', sa.Column('contact_header_params', sa.String(255)))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    op.add_column('ps_auths', sa.Column('refresh_token', sa.String(255)))</span><br><span style="color: hsl(120, 100%, 40%);">+    op.add_column('ps_auths', sa.Column('oauth_clientid', sa.String(255)))</span><br><span style="color: hsl(120, 100%, 40%);">+    op.add_column('ps_auths', sa.Column('oauth_secret', sa.String(255)))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def downgrade():</span><br><span style="color: hsl(120, 100%, 40%);">+    # First we need to ensure that columns are not using the enum values</span><br><span style="color: hsl(120, 100%, 40%);">+    # that are going away.</span><br><span style="color: hsl(120, 100%, 40%);">+    op.execute("UPDATE ps_transports SET protocol='udp' WHERE protocol='flow'")</span><br><span style="color: hsl(120, 100%, 40%);">+    op.execute("UPDATE ps_auths SET auth_type='userpass' WHERE auth_type='google_oauth'")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if op.get_context().bind.dialect.name == 'postgresql':</span><br><span style="color: hsl(120, 100%, 40%);">+        enum = PJSIP_TRANSPORT_PROTOCOL_OLD_TYPE</span><br><span style="color: hsl(120, 100%, 40%);">+        enum.create(op.get_bind(), checkfirst=False)</span><br><span style="color: hsl(120, 100%, 40%);">+        op.execute('ALTER TABLE ps_transports ALTER COLUMN protocol TYPE'</span><br><span style="color: hsl(120, 100%, 40%);">+                   ' ' + PJSIP_TRANSPORT_PROTOCOL_OLD_NAME + ' USING'</span><br><span style="color: hsl(120, 100%, 40%);">+                   ' protocol::text::' + PJSIP_TRANSPORT_PROTOCOL_OLD_NAME)</span><br><span style="color: hsl(120, 100%, 40%);">+        ENUM(name=PJSIP_TRANSPORT_PROTOCOL_NEW_NAME).drop(op.get_bind(), checkfirst=False)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        enum = PJSIP_AUTH_TYPE_OLD_TYPE</span><br><span style="color: hsl(120, 100%, 40%);">+        enum.create(op.get_bind(), checkfirst=False)</span><br><span style="color: hsl(120, 100%, 40%);">+        op.execute('ALTER TABLE ps_auths ALTER COLUMN auth_type TYPE'</span><br><span style="color: hsl(120, 100%, 40%);">+                   ' ' + PJSIP_AUTH_TYPE_OLD_NAME + ' USING'</span><br><span style="color: hsl(120, 100%, 40%);">+                   ' auth_type::text::' + PJSIP_AUTH_TYPE_OLD_NAME)</span><br><span style="color: hsl(120, 100%, 40%);">+        ENUM(name=PJSIP_AUTH_TYPE_NEW_NAME).drop(op.get_bind(), checkfirst=False)</span><br><span style="color: hsl(120, 100%, 40%);">+    else:</span><br><span style="color: hsl(120, 100%, 40%);">+        op.alter_column('ps_transports', 'protocol',</span><br><span style="color: hsl(120, 100%, 40%);">+                        type_=PJSIP_TRANSPORT_PROTOCOL_OLD_TYPE,</span><br><span style="color: hsl(120, 100%, 40%);">+                        existing_type=PJSIP_TRANSPORT_PROTOCOL_NEW_TYPE)</span><br><span style="color: hsl(120, 100%, 40%);">+        op.alter_column('ps_auths', 'auth_type',</span><br><span style="color: hsl(120, 100%, 40%);">+                        type_=PJSIP_AUTH_TYPE_OLD_TYPE,</span><br><span style="color: hsl(120, 100%, 40%);">+                        existing_type=PJSIP_AUTH_TYPE_NEW_TYPE)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    op.drop_column('ps_registrations', 'support_outbound')</span><br><span style="color: hsl(120, 100%, 40%);">+    op.drop_column('ps_registrations', 'contact_header_params')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    op.drop_column('ps_auths', 'refresh_token')</span><br><span style="color: hsl(120, 100%, 40%);">+    op.drop_column('ps_auths', 'oauth_clientid')</span><br><span style="color: hsl(120, 100%, 40%);">+    op.drop_column('ps_auths', 'oauth_secret')</span><br><span>diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in</span><br><span>index 30bb907..cce24eb 100644</span><br><span>--- a/include/asterisk/autoconfig.h.in</span><br><span>+++ b/include/asterisk/autoconfig.h.in</span><br><span>@@ -640,12 +640,19 @@</span><br><span>    support feature. */</span><br><span> #undef HAVE_PJSIP_INV_SESSION_REF</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* Define if your system has the PJSIP_OAUTH_AUTHENTICATION headers. */</span><br><span style="color: hsl(120, 100%, 40%);">+#undef HAVE_PJSIP_OAUTH_AUTHENTICATION</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Define if your system has the PJSIP_REPLACE_MEDIA_STREAM headers. */</span><br><span> #undef HAVE_PJSIP_REPLACE_MEDIA_STREAM</span><br><span> </span><br><span> /* Define if your system has the PJSIP_TLS_TRANSPORT_PROTO headers. */</span><br><span> #undef HAVE_PJSIP_TLS_TRANSPORT_PROTO</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* Define if your system has the PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE</span><br><span style="color: hsl(120, 100%, 40%);">+   headers. */</span><br><span style="color: hsl(120, 100%, 40%);">+#undef HAVE_PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Define to 1 if PJPROJECT has the pjsip_tsx_layer_find_tsx2 support feature.</span><br><span>    */</span><br><span> #undef HAVE_PJSIP_TSX_LAYER_FIND_TSX2</span><br><span>diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h</span><br><span>index b323bc1..20af039 100644</span><br><span>--- a/include/asterisk/res_pjsip.h</span><br><span>+++ b/include/asterisk/res_pjsip.h</span><br><span>@@ -63,6 +63,8 @@</span><br><span> /*! \brief Maximum number of ciphers supported for a TLS transport */</span><br><span> #define SIP_TLS_MAX_CIPHERS 64</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+AST_VECTOR(ast_sip_service_route_vector, char *);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*!</span><br><span>  * \brief Structure for SIP transport information</span><br><span>  */</span><br><span>@@ -124,6 +126,21 @@</span><br><span>      * \since 13.18.0</span><br><span>     */</span><br><span>  struct ast_sockaddr external_media_address;</span><br><span style="color: hsl(120, 100%, 40%);">+   /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * Set when this transport is a flow of signaling to a target</span><br><span style="color: hsl(120, 100%, 40%);">+  * \since 17.0.0</span><br><span style="color: hsl(120, 100%, 40%);">+       */</span><br><span style="color: hsl(120, 100%, 40%);">+   int flow;</span><br><span style="color: hsl(120, 100%, 40%);">+     /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * The P-Preferred-Identity to use on traffic using this transport</span><br><span style="color: hsl(120, 100%, 40%);">+     * \since 17.0.0</span><br><span style="color: hsl(120, 100%, 40%);">+       */</span><br><span style="color: hsl(120, 100%, 40%);">+   char *preferred_identity;</span><br><span style="color: hsl(120, 100%, 40%);">+     /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * The Service Routes to use on traffic using this transport</span><br><span style="color: hsl(120, 100%, 40%);">+   * \since 17.0.0</span><br><span style="color: hsl(120, 100%, 40%);">+       */</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_sip_service_route_vector *service_routes;</span><br><span> };</span><br><span> </span><br><span> #define ast_sip_transport_is_nonlocal(transport_state, addr) \</span><br><span>@@ -214,6 +231,8 @@</span><br><span>       int allow_reload;</span><br><span>    /*! Automatically send requests out the same transport requests have come in on */</span><br><span>   int symmetric_transport;</span><br><span style="color: hsl(120, 100%, 40%);">+      /*! This is a flow to another target */</span><br><span style="color: hsl(120, 100%, 40%);">+       int flow;</span><br><span> };</span><br><span> </span><br><span> #define SIP_SORCERY_DOMAIN_ALIAS_TYPE "domain_alias"</span><br><span>@@ -400,6 +419,8 @@</span><br><span>  AST_SIP_AUTH_TYPE_USER_PASS,</span><br><span>         /*! Credentials stored as an MD5 sum */</span><br><span>      AST_SIP_AUTH_TYPE_MD5,</span><br><span style="color: hsl(120, 100%, 40%);">+        /*! Google Oauth */</span><br><span style="color: hsl(120, 100%, 40%);">+   AST_SIP_AUTH_TYPE_GOOGLE_OAUTH,</span><br><span>      /*! Credentials not stored this is a fake auth */</span><br><span>    AST_SIP_AUTH_TYPE_ARTIFICIAL</span><br><span> };</span><br><span>@@ -418,6 +439,12 @@</span><br><span>            AST_STRING_FIELD(auth_pass);</span><br><span>                 /*! Authentication credentials in MD5 format (hash of user:realm:pass) */</span><br><span>            AST_STRING_FIELD(md5_creds);</span><br><span style="color: hsl(120, 100%, 40%);">+          /*! Refresh token to use for OAuth authentication */</span><br><span style="color: hsl(120, 100%, 40%);">+          AST_STRING_FIELD(refresh_token);</span><br><span style="color: hsl(120, 100%, 40%);">+              /*! Client ID to use for OAuth authentication */</span><br><span style="color: hsl(120, 100%, 40%);">+              AST_STRING_FIELD(oauth_clientid);</span><br><span style="color: hsl(120, 100%, 40%);">+             /*! Secret to use for OAuth authentication */</span><br><span style="color: hsl(120, 100%, 40%);">+         AST_STRING_FIELD(oauth_secret);</span><br><span>      );</span><br><span>   /*! The time period (in seconds) that a nonce may be reused */</span><br><span>       unsigned int nonce_lifetime;</span><br><span>@@ -2952,6 +2979,8 @@</span><br><span>  * \param selector The selector to be populated</span><br><span>  * \retval 0 success</span><br><span>  * \retval -1 failure</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note The transport selector must be unreffed using ast_sip_tpselector_unref</span><br><span>  */</span><br><span> int ast_sip_set_tpselector_from_transport(const struct ast_sip_transport *transport, pjsip_tpselector *selector);</span><br><span> </span><br><span>@@ -2963,10 +2992,81 @@</span><br><span>  * \param selector The selector to be populated</span><br><span>  * \retval 0 success</span><br><span>  * \retval -1 failure</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note The transport selector must be unreffed using ast_sip_tpselector_unref</span><br><span>  */</span><br><span> int ast_sip_set_tpselector_from_transport_name(const char *transport_name, pjsip_tpselector *selector);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Unreference a pjsip_tpselector</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 17.0.0</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param selector The selector to be unreffed</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_sip_tpselector_unref(pjsip_tpselector *selector);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Sets the PJSIP transport on a child transport</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 17.0.0</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param transport_name The name of the transport to be updated</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param transport The PJSIP transport</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 failure</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sip_transport_state_set_transport(const char *transport_name, pjsip_transport *transport);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Sets the P-Preferred-Identity on a child transport</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 17.0.0</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param transport_name The name of the transport to be set on</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param identity The P-Preferred-Identity to use on requests on this transport</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 failure</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sip_transport_state_set_preferred_identity(const char *transport_name, const char *identity);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Sets the service routes on a child transport</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 17.0.0</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param transport_name The name of the transport to be set on</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param service_routes A vector of service routes</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 failure</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note This assumes ownership of the service routes in both success and failure scenarios</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sip_transport_state_set_service_routes(const char *transport_name, struct ast_sip_service_route_vector *service_routes);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Apply the configuration for a transport to an outgoing message</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 17.0.0</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param transport_name The name of the transport to apply configuration from</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param tdata The SIP message</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_sip_message_apply_transport(const char *transport_name, pjsip_tx_data *tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Allocate a vector of service routes</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 17.0.0</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval non-NULL success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL failure</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_sip_service_route_vector *ast_sip_service_route_vector_alloc(void);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Destroy a vector of service routes</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 17.0.0</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param service_routes A vector of service routes</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_sip_service_route_vector_destroy(struct ast_sip_service_route_vector *service_routes);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span>  * \brief Set name and number information on an identity header.</span><br><span>  *</span><br><span>  * \param pool Memory pool to use for string duplication</span><br><span>@@ -3033,6 +3133,9 @@</span><br><span>  * This API calls ast_sip_get_transport_name(endpoint, dlg->target) and if the result is</span><br><span>  * non-NULL, calls pjsip_dlg_set_transport.  If 'selector' is non-NULL, it is updated with</span><br><span>  * the selector used.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note</span><br><span style="color: hsl(120, 100%, 40%);">+ * It is the responsibility of the caller to unref the passed in selector if one is provided.</span><br><span>  */</span><br><span> int ast_sip_dlg_set_transport(const struct ast_sip_endpoint *endpoint, pjsip_dialog *dlg,</span><br><span>  pjsip_tpselector *selector);</span><br><span>diff --git a/res/res_pjsip.c b/res/res_pjsip.c</span><br><span>index c2e77cb..a61208f 100644</span><br><span>--- a/res/res_pjsip.c</span><br><span>+++ b/res/res_pjsip.c</span><br><span>@@ -1136,11 +1136,12 @@</span><br><span>                                              This option specifies which of the password style config options should be read</span><br><span>                                              when trying to authenticate an endpoint inbound request. If set to <literal>userpass</literal></span><br><span>                                           then we'll read from the 'password' option. For <literal>md5</literal> we'll read</span><br><span style="color: hsl(0, 100%, 40%);">-                                           from 'md5_cred'.</span><br><span style="color: hsl(120, 100%, 40%);">+                                              from 'md5_cred'. If set to <literal>google_oauth</literal> then we'll read from the refresh_token/oauth_clientid/oauth_secret fields.</span><br><span>                                                </para></span><br><span>                                                <enumlist></span><br><span>                                                     <enum name="md5"/></span><br><span>                                                   <enum name="userpass"/></span><br><span style="color: hsl(120, 100%, 40%);">+                                                       <enum name="google_oauth"/></span><br><span>                                          </enumlist></span><br><span>                                    </description></span><br><span>                                 </configOption></span><br><span>@@ -1155,6 +1156,15 @@</span><br><span>                                       <synopsis>Plain text password used for authentication.</synopsis></span><br><span>                                        <description><para>Only used when auth_type is <literal>userpass</literal>.</para></description></span><br><span>                                 </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="refresh_token"></span><br><span style="color: hsl(120, 100%, 40%);">+                                   <synopsis>OAuth 2.0 refresh token</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                              </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="oauth_clientid"></span><br><span style="color: hsl(120, 100%, 40%);">+                                  <synopsis>OAuth 2.0 application's client id</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                                </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="oauth_secret"></span><br><span style="color: hsl(120, 100%, 40%);">+                                    <synopsis>OAuth 2.0 application's secret</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                           </configOption></span><br><span>                                <configOption name="realm"></span><br><span>                                  <synopsis>SIP realm for endpoint</synopsis></span><br><span>                                      <description><para></span><br><span>@@ -1307,6 +1317,7 @@</span><br><span>                                                      <enum name="tls" /></span><br><span>                                                  <enum name="ws" /></span><br><span>                                                   <enum name="wss" /></span><br><span style="color: hsl(120, 100%, 40%);">+                                                   <enum name="flow" /></span><br><span>                                                 </enumlist></span><br><span>                                    </description></span><br><span>                                 </configOption></span><br><span>@@ -3277,8 +3288,13 @@</span><br><span>       }</span><br><span> </span><br><span>        ast_sip_set_tpselector_from_ep_or_uri(endpoint, uri, selector);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>    pjsip_dlg_set_transport(dlg, selector);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+   if (selector == &sel) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_sip_tpselector_unref(&sel);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  return 0;</span><br><span> }</span><br><span> </span><br><span>@@ -3367,7 +3383,8 @@</span><br><span> </span><br><span> int ast_sip_set_tpselector_from_transport(const struct ast_sip_transport *transport, pjsip_tpselector *selector)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- RAII_VAR(struct ast_sip_transport_state *, transport_state, NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+       int res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_sip_transport_state *transport_state;</span><br><span> </span><br><span>         transport_state = ast_sip_get_transport_state(ast_sorcery_object_get_id(transport));</span><br><span>         if (!transport_state) {</span><br><span>@@ -3376,9 +3393,15 @@</span><br><span>             return -1;</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* Only flows maintain dynamic state which needs protection */</span><br><span style="color: hsl(120, 100%, 40%);">+        if (transport_state->flow) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ao2_lock(transport_state);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  if (transport_state->transport) {</span><br><span>                 selector->type = PJSIP_TPSELECTOR_TRANSPORT;</span><br><span>              selector->u.transport = transport_state->transport;</span><br><span style="color: hsl(120, 100%, 40%);">+             pjsip_transport_add_ref(selector->u.transport);</span><br><span>   } else if (transport_state->factory) {</span><br><span>            selector->type = PJSIP_TPSELECTOR_LISTENER;</span><br><span>               selector->u.listener = transport_state->factory;</span><br><span>@@ -3387,12 +3410,25 @@</span><br><span>              * even if an endpoint is locked to a WebSocket transport we let the PJSIP logic</span><br><span>              * find the existing connection if available and use it.</span><br><span>              */</span><br><span style="color: hsl(0, 100%, 40%);">-             return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     } else if (transport->flow) {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* This is a child of another transport, so we need to establish a new connection */</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef HAVE_PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE</span><br><span style="color: hsl(120, 100%, 40%);">+            selector->disable_connection_reuse = PJ_TRUE;</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_WARNING, "Connection reuse could not be disabled on transport '%s' as support is not available\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_sorcery_object_get_id(transport));</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span>       } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+            res = -1;</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     if (transport_state->flow) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ao2_unlock(transport_state);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_ref(transport_state, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return res;</span><br><span> }</span><br><span> </span><br><span> int ast_sip_set_tpselector_from_transport_name(const char *transport_name, pjsip_tpselector *selector)</span><br><span>@@ -3425,6 +3461,13 @@</span><br><span>      return ast_sip_set_tpselector_from_transport_name(transport_name, selector);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+void ast_sip_tpselector_unref(pjsip_tpselector *selector)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   if (selector->type == PJSIP_TPSELECTOR_TRANSPORT && selector->u.transport) {</span><br><span style="color: hsl(120, 100%, 40%);">+            pjsip_transport_dec_ref(selector->u.transport);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> void ast_sip_add_usereqphone(const struct ast_sip_endpoint *endpoint, pj_pool_t *pool, pjsip_uri *uri)</span><br><span> {</span><br><span>        pjsip_sip_uri *sip_uri;</span><br><span>@@ -3493,9 +3536,12 @@</span><br><span>     if (sip_dialog_create_from(dlg->pool, &local_uri, endpoint->fromuser, endpoint->fromdomain, &remote_uri, &selector)) {</span><br><span>          dlg->sess_count--;</span><br><span>                pjsip_dlg_terminate(dlg);</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_sip_tpselector_unref(&selector);</span><br><span>             return NULL;</span><br><span>         }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_tpselector_unref(&selector);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>   /* Update the dialog with the new local URI, we do it afterwards so we can use the dialog pool for construction */</span><br><span>   pj_strdup_with_null(dlg->pool, &dlg->local.info_str, &local_uri);</span><br><span>      dlg->local.info->uri = pjsip_parse_uri(dlg->pool, dlg->local.info_str.ptr, dlg->local.info_str.slen, 0);</span><br><span>@@ -3642,12 +3688,16 @@</span><br><span>            pj_strerror(*status, err, sizeof(err));</span><br><span>              ast_log(LOG_ERROR, "Could not create dialog with endpoint %s. %s\n",</span><br><span>                               ast_sorcery_object_get_id(endpoint), err);</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_sip_tpselector_unref(&selector);</span><br><span>             return NULL;</span><br><span>         }</span><br><span> </span><br><span>        dlg->sess_count++;</span><br><span>        pjsip_dlg_set_transport(dlg, &selector);</span><br><span>         dlg->sess_count--;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_sip_tpselector_unref(&selector);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #ifdef HAVE_PJSIP_DLG_CREATE_UAS_AND_INC_LOCK</span><br><span>  pjsip_dlg_dec_lock(dlg);</span><br><span> #endif</span><br><span>@@ -3817,6 +3867,7 @@</span><br><span>                           (int) pj_strlen(&method->name), pj_strbuf(&method->name),</span><br><span>                              endpoint ? ast_sorcery_object_get_id(endpoint) : "<none>");</span><br><span>          pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_sip_tpselector_unref(&selector);</span><br><span>             return -1;</span><br><span>   }</span><br><span> </span><br><span>@@ -3826,11 +3877,14 @@</span><br><span>                              (int) pj_strlen(&method->name), pj_strbuf(&method->name),</span><br><span>                              endpoint ? ast_sorcery_object_get_id(endpoint) : "<none>");</span><br><span>          pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_sip_tpselector_unref(&selector);</span><br><span>             return -1;</span><br><span>   }</span><br><span> </span><br><span>        pjsip_tx_data_set_transport(*tdata, &selector);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+       ast_sip_tpselector_unref(&selector);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>   if (endpoint && !ast_strlen_zero(endpoint->contact_user)){</span><br><span>                pjsip_contact_hdr *contact_hdr;</span><br><span>              pjsip_sip_uri *contact_uri;</span><br><span>@@ -4385,6 +4439,10 @@</span><br><span>                 return -1;</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (endpoint) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_sip_message_apply_transport(endpoint->transport, tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  contact = ast_sip_mod_data_get(tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT);</span><br><span> </span><br><span>      AST_RWLIST_RDLOCK(&supplements);</span><br><span>@@ -4828,6 +4886,10 @@</span><br><span>        pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);</span><br><span>        struct ast_sip_contact *contact = ast_sip_mod_data_get(tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+       if (sip_endpoint) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_sip_message_apply_transport(sip_endpoint->transport, tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  AST_RWLIST_RDLOCK(&supplements);</span><br><span>         AST_LIST_TRAVERSE(&supplements, supplement, next) {</span><br><span>              if (supplement->outgoing_response && does_method_match(&cseq->method.name, supplement->method)) {</span><br><span>diff --git a/res/res_pjsip/config_auth.c b/res/res_pjsip/config_auth.c</span><br><span>index b1bf9c4..2350140 100644</span><br><span>--- a/res/res_pjsip/config_auth.c</span><br><span>+++ b/res/res_pjsip/config_auth.c</span><br><span>@@ -56,6 +56,13 @@</span><br><span>                 auth->type = AST_SIP_AUTH_TYPE_USER_PASS;</span><br><span>         } else if (!strcasecmp(var->value, "md5")) {</span><br><span>            auth->type = AST_SIP_AUTH_TYPE_MD5;</span><br><span style="color: hsl(120, 100%, 40%);">+        } else if (!strcasecmp(var->value, "google_oauth")) {</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef HAVE_PJSIP_OAUTH_AUTHENTICATION</span><br><span style="color: hsl(120, 100%, 40%);">+            auth->type = AST_SIP_AUTH_TYPE_GOOGLE_OAUTH;</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_log(LOG_WARNING, "OAuth support is not available in the version of PJSIP in use\n");</span><br><span style="color: hsl(120, 100%, 40%);">+            return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span>   } else {</span><br><span>             ast_log(LOG_WARNING, "Unknown authentication storage type '%s' specified for %s\n",</span><br><span>                                var->value, var->name);</span><br><span>@@ -66,7 +73,8 @@</span><br><span> </span><br><span> static const char *auth_types_map[] = {</span><br><span>     [AST_SIP_AUTH_TYPE_USER_PASS] = "userpass",</span><br><span style="color: hsl(0, 100%, 40%);">-   [AST_SIP_AUTH_TYPE_MD5] = "md5"</span><br><span style="color: hsl(120, 100%, 40%);">+     [AST_SIP_AUTH_TYPE_MD5] = "md5",</span><br><span style="color: hsl(120, 100%, 40%);">+    [AST_SIP_AUTH_TYPE_GOOGLE_OAUTH] = "google_oauth"</span><br><span> };</span><br><span> </span><br><span> const char *ast_sip_auth_type_to_str(enum ast_sip_auth_type type)</span><br><span>@@ -106,6 +114,16 @@</span><br><span>                    res = -1;</span><br><span>            }</span><br><span>            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case AST_SIP_AUTH_TYPE_GOOGLE_OAUTH:</span><br><span style="color: hsl(120, 100%, 40%);">+          if (ast_strlen_zero(auth->refresh_token)</span><br><span style="color: hsl(120, 100%, 40%);">+                   || ast_strlen_zero(auth->oauth_clientid)</span><br><span style="color: hsl(120, 100%, 40%);">+                   || ast_strlen_zero(auth->oauth_secret)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  ast_log(LOG_ERROR, "'google_oauth' authentication specified but refresh_token,"</span><br><span style="color: hsl(120, 100%, 40%);">+                             " oauth_clientid, or oauth_secret not specified for auth '%s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                           ast_sorcery_object_get_id(auth));</span><br><span style="color: hsl(120, 100%, 40%);">+                     res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span>       case AST_SIP_AUTH_TYPE_USER_PASS:</span><br><span>    case AST_SIP_AUTH_TYPE_ARTIFICIAL:</span><br><span>           break;</span><br><span>@@ -365,6 +383,12 @@</span><br><span>                        "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, auth_user));</span><br><span>      ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "password",</span><br><span>                      "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, auth_pass));</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "refresh_token",</span><br><span style="color: hsl(120, 100%, 40%);">+                  "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, refresh_token));</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "oauth_clientid",</span><br><span style="color: hsl(120, 100%, 40%);">+                 "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, oauth_clientid));</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "oauth_secret",</span><br><span style="color: hsl(120, 100%, 40%);">+                   "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, oauth_secret));</span><br><span>   ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "md5_cred",</span><br><span>                      "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, md5_creds));</span><br><span>      ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "realm",</span><br><span>diff --git a/res/res_pjsip/config_transport.c b/res/res_pjsip/config_transport.c</span><br><span>index 13a9ff8..a84fee0 100644</span><br><span>--- a/res/res_pjsip/config_transport.c</span><br><span>+++ b/res/res_pjsip/config_transport.c</span><br><span>@@ -203,6 +203,176 @@</span><br><span>    .format_ami = format_ami_endpoint_transport</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sip_transport_state_set_transport(const char *transport_name, pjsip_transport *transport)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_sip_transport_state *transport_state;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /* To make it easier on callers we allow an empty transport name */</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_strlen_zero(transport_name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   transport_state = ast_sip_get_transport_state(transport_name);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!transport_state) {</span><br><span style="color: hsl(120, 100%, 40%);">+               return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!transport_state->flow) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ao2_ref(transport_state, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+         return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_lock(transport_state);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (transport_state->transport != transport) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (transport_state->transport) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  pjsip_transport_dec_ref(transport_state->transport);</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+             transport_state->transport = transport;</span><br><span style="color: hsl(120, 100%, 40%);">+            if (transport_state->transport) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  pjsip_transport_add_ref(transport_state->transport);</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     ao2_unlock(transport_state);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        ao2_ref(transport_state, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sip_transport_state_set_preferred_identity(const char *transport_name, const char *identity)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ast_sip_transport_state *transport_state;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (ast_strlen_zero(transport_name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   transport_state = ast_sip_get_transport_state(transport_name);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!transport_state) {</span><br><span style="color: hsl(120, 100%, 40%);">+               return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!transport_state->flow) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ao2_ref(transport_state, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+         return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_lock(transport_state);</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_free(transport_state->preferred_identity);</span><br><span style="color: hsl(120, 100%, 40%);">+     transport_state->preferred_identity = ast_strdup(identity);</span><br><span style="color: hsl(120, 100%, 40%);">+        ao2_unlock(transport_state);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        ao2_ref(transport_state, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sip_transport_state_set_service_routes(const char *transport_name, struct ast_sip_service_route_vector *service_routes)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_sip_transport_state *transport_state;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (ast_strlen_zero(transport_name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_sip_service_route_vector_destroy(service_routes);</span><br><span style="color: hsl(120, 100%, 40%);">+         return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   transport_state = ast_sip_get_transport_state(transport_name);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!transport_state) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_sip_service_route_vector_destroy(service_routes);</span><br><span style="color: hsl(120, 100%, 40%);">+         return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!transport_state->flow) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ao2_ref(transport_state, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_sip_service_route_vector_destroy(service_routes);</span><br><span style="color: hsl(120, 100%, 40%);">+         return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_lock(transport_state);</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_sip_service_route_vector_destroy(transport_state->service_routes);</span><br><span style="color: hsl(120, 100%, 40%);">+     transport_state->service_routes = service_routes;</span><br><span style="color: hsl(120, 100%, 40%);">+  ao2_unlock(transport_state);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        ao2_ref(transport_state, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_sip_message_apply_transport(const char *transport_name, pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct ast_sip_transport_state *transport_state;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (ast_strlen_zero(transport_name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* We only currently care about requests that are of the INVITE, CANCEL, or OPTIONS</span><br><span style="color: hsl(120, 100%, 40%);">+    * type but in the future we could support other messages.</span><br><span style="color: hsl(120, 100%, 40%);">+     */</span><br><span style="color: hsl(120, 100%, 40%);">+   if (tdata->msg->type != PJSIP_REQUEST_MSG ||</span><br><span style="color: hsl(120, 100%, 40%);">+            (pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_invite_method) &&</span><br><span style="color: hsl(120, 100%, 40%);">+                pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_cancel_method) &&</span><br><span style="color: hsl(120, 100%, 40%);">+         pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_options_method))) {</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   transport_state = ast_sip_get_transport_state(transport_name);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!transport_state) {</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!transport_state->flow) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ao2_ref(transport_state, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+         return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_lock(transport_state);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* If a Preferred Identity has been set then add it to the request */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (transport_state->preferred_identity) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_sip_add_header(tdata, "P-Preferred-Identity", transport_state->preferred_identity);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* If Service Routes have been set then add them to the request */</span><br><span style="color: hsl(120, 100%, 40%);">+    if (transport_state->service_routes) {</span><br><span style="color: hsl(120, 100%, 40%);">+             int idx;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            for (idx = 0; idx < AST_VECTOR_SIZE(transport_state->service_routes); ++idx) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  char *service_route = AST_VECTOR_GET(transport_state->service_routes, idx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_sip_add_header(tdata, "Route", service_route);</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_unlock(transport_state);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        ao2_ref(transport_state, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_sip_service_route_vector *ast_sip_service_route_vector_alloc(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_sip_service_route_vector *service_routes;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        service_routes = ast_calloc(1, sizeof(*service_routes));</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!service_routes) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   AST_VECTOR_INIT(service_routes, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return service_routes;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_sip_service_route_vector_destroy(struct ast_sip_service_route_vector *service_routes)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!service_routes) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   AST_VECTOR_CALLBACK_VOID(service_routes, ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_free(service_routes);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void set_qos(struct ast_sip_transport *transport, pj_qos_params *qos)</span><br><span> {</span><br><span>  int tos_as_dscp = transport->tos >> 2;</span><br><span>@@ -500,7 +670,7 @@</span><br><span>                        return 0;</span><br><span>            }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-           if (!transport->allow_reload) {</span><br><span style="color: hsl(120, 100%, 40%);">+            if (!transport->allow_reload && !transport->flow) {</span><br><span>                    if (!perm_state->change_detected) {</span><br><span>                               perm_state->change_detected = 1;</span><br><span>                          ast_log(LOG_WARNING, "Transport '%s' is not reloadable, maintaining previous values\n", transport_id);</span><br><span>@@ -513,14 +683,16 @@</span><br><span>             }</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (temp_state->state->host.addr.sa_family != PJ_AF_INET && temp_state->state->host.addr.sa_family != PJ_AF_INET6) {</span><br><span style="color: hsl(0, 100%, 40%);">-                ast_log(LOG_ERROR, "Transport '%s' could not be started as binding not specified\n", transport_id);</span><br><span style="color: hsl(0, 100%, 40%);">-           return -1;</span><br><span style="color: hsl(0, 100%, 40%);">-      }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!transport->flow) {</span><br><span style="color: hsl(120, 100%, 40%);">+            if (temp_state->state->host.addr.sa_family != PJ_AF_INET && temp_state->state->host.addr.sa_family != PJ_AF_INET6) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_log(LOG_ERROR, "Transport '%s' could not be started as binding not specified\n", transport_id);</span><br><span style="color: hsl(120, 100%, 40%);">+                 return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   /* Set default port if not present */</span><br><span style="color: hsl(0, 100%, 40%);">-   if (!pj_sockaddr_get_port(&temp_state->state->host)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                pj_sockaddr_set_port(&temp_state->state->host, (transport->type == AST_TRANSPORT_TLS) ? 5061 : 5060);</span><br><span style="color: hsl(120, 100%, 40%);">+            /* Set default port if not present */</span><br><span style="color: hsl(120, 100%, 40%);">+         if (!pj_sockaddr_get_port(&temp_state->state->host)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      pj_sockaddr_set_port(&temp_state->state->host, (transport->type == AST_TRANSPORT_TLS) ? 5061 : 5060);</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span>    }</span><br><span> </span><br><span>        /* Now that we know what address family we can set up a dnsmgr refresh for the external addresses if present */</span><br><span>@@ -558,7 +730,16 @@</span><br><span>               }</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (transport->type == AST_TRANSPORT_UDP) {</span><br><span style="color: hsl(120, 100%, 40%);">+        if (transport->flow) {</span><br><span style="color: hsl(120, 100%, 40%);">+             pj_str_t address;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_debug(1, "Ignoring any bind configuration on transport '%s' as it is a child of another\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                     transport_id);</span><br><span style="color: hsl(120, 100%, 40%);">+                pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&address, "0.0.0.0"), &temp_state->state->host);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+               temp_state->state->flow = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+            res = PJ_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+     } else if (transport->type == AST_TRANSPORT_UDP) {</span><br><span> </span><br><span>            for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) {</span><br><span>                   if (perm_state && perm_state->state && perm_state->state->transport) {</span><br><span>@@ -775,18 +956,23 @@</span><br><span>              return -1;</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (!strcasecmp(var->value, "udp")) {</span><br><span style="color: hsl(0, 100%, 40%);">-              transport->type = AST_TRANSPORT_UDP;</span><br><span style="color: hsl(0, 100%, 40%);">- } else if (!strcasecmp(var->value, "tcp")) {</span><br><span style="color: hsl(0, 100%, 40%);">-               transport->type = AST_TRANSPORT_TCP;</span><br><span style="color: hsl(0, 100%, 40%);">- } else if (!strcasecmp(var->value, "tls")) {</span><br><span style="color: hsl(0, 100%, 40%);">-               transport->type = AST_TRANSPORT_TLS;</span><br><span style="color: hsl(0, 100%, 40%);">- } else if (!strcasecmp(var->value, "ws")) {</span><br><span style="color: hsl(0, 100%, 40%);">-                transport->type = AST_TRANSPORT_WS;</span><br><span style="color: hsl(0, 100%, 40%);">-  } else if (!strcasecmp(var->value, "wss")) {</span><br><span style="color: hsl(0, 100%, 40%);">-               transport->type = AST_TRANSPORT_WSS;</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!strcasecmp(var->value, "flow")) {</span><br><span style="color: hsl(120, 100%, 40%);">+           transport->flow = 1;</span><br><span>      } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+            if (!strcasecmp(var->value, "udp")) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    transport->type = AST_TRANSPORT_UDP;</span><br><span style="color: hsl(120, 100%, 40%);">+               } else if (!strcasecmp(var->value, "tcp")) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     transport->type = AST_TRANSPORT_TCP;</span><br><span style="color: hsl(120, 100%, 40%);">+               } else if (!strcasecmp(var->value, "tls")) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     transport->type = AST_TRANSPORT_TLS;</span><br><span style="color: hsl(120, 100%, 40%);">+               } else if (!strcasecmp(var->value, "ws")) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      transport->type = AST_TRANSPORT_WS;</span><br><span style="color: hsl(120, 100%, 40%);">+                } else if (!strcasecmp(var->value, "wss")) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     transport->type = AST_TRANSPORT_WSS;</span><br><span style="color: hsl(120, 100%, 40%);">+               } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+             transport->flow = 0;</span><br><span>      }</span><br><span> </span><br><span>        state->type = transport->type;</span><br><span>@@ -806,7 +992,9 @@</span><br><span> {</span><br><span>      const struct ast_sip_transport *transport = obj;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    if (ARRAY_IN_BOUNDS(transport->type, transport_types)) {</span><br><span style="color: hsl(120, 100%, 40%);">+   if (transport->flow) {</span><br><span style="color: hsl(120, 100%, 40%);">+             *buf = ast_strdup("flow");</span><br><span style="color: hsl(120, 100%, 40%);">+  } else if (ARRAY_IN_BOUNDS(transport->type, transport_types)) {</span><br><span>           *buf = ast_strdup(transport_types[transport->type]);</span><br><span>      }</span><br><span> </span><br><span>@@ -1362,6 +1550,16 @@</span><br><span>       trans_state = ao2_bump(state->state);</span><br><span>     ao2_ref(state, -1);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+       /* If this is a child transport see if the transport is actually dead */</span><br><span style="color: hsl(120, 100%, 40%);">+      if (trans_state->flow) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ao2_lock(trans_state);</span><br><span style="color: hsl(120, 100%, 40%);">+                if (trans_state->transport && trans_state->transport->is_shutdown == PJ_TRUE) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      pjsip_transport_dec_ref(trans_state->transport);</span><br><span style="color: hsl(120, 100%, 40%);">+                   trans_state->transport = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+             ao2_unlock(trans_state);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  return trans_state;</span><br><span> }</span><br><span> </span><br><span>diff --git a/res/res_pjsip_outbound_authenticator_digest.c b/res/res_pjsip_outbound_authenticator_digest.c</span><br><span>index 5f6d994..b1011b0 100644</span><br><span>--- a/res/res_pjsip_outbound_authenticator_digest.c</span><br><span>+++ b/res/res_pjsip_outbound_authenticator_digest.c</span><br><span>@@ -83,6 +83,9 @@</span><br><span>                    pj_cstr(&auth_creds[i].data, auths[i]->md5_creds);</span><br><span>                    auth_creds[i].data_type = PJSIP_CRED_DATA_DIGEST;</span><br><span>                    break;</span><br><span style="color: hsl(120, 100%, 40%);">+                case AST_SIP_AUTH_TYPE_GOOGLE_OAUTH:</span><br><span style="color: hsl(120, 100%, 40%);">+                  /* nothing to do. handled seperately in res_pjsip_outbound_registration */</span><br><span style="color: hsl(120, 100%, 40%);">+                    break;</span><br><span>               case AST_SIP_AUTH_TYPE_ARTIFICIAL:</span><br><span>                   ast_log(LOG_ERROR, "Trying to set artificial outbound auth credentials shouldn't happen.\n");</span><br><span>                  break;</span><br><span>diff --git a/res/res_pjsip_outbound_publish.c b/res/res_pjsip_outbound_publish.c</span><br><span>index 4894e55..f8594ab 100644</span><br><span>--- a/res/res_pjsip_outbound_publish.c</span><br><span>+++ b/res/res_pjsip_outbound_publish.c</span><br><span>@@ -428,9 +428,11 @@</span><br><span> {</span><br><span>      if (!ast_strlen_zero(publisher->owner->publish->transport)) {</span><br><span>               pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>            ast_sip_set_tpselector_from_transport_name(</span><br><span>                  publisher->owner->publish->transport, &selector);</span><br><span>               pjsip_tx_data_set_transport(tdata, &selector);</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_sip_tpselector_unref(&selector);</span><br><span>     }</span><br><span> }</span><br><span> </span><br><span>diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c</span><br><span>index 0d815ad..648deee 100644</span><br><span>--- a/res/res_pjsip_outbound_registration.c</span><br><span>+++ b/res/res_pjsip_outbound_registration.c</span><br><span>@@ -38,6 +38,8 @@</span><br><span> #include "asterisk/threadpool.h"</span><br><span> #include "asterisk/statsd.h"</span><br><span> #include "res_pjsip/include/res_pjsip_private.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/vector.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/pbx.h"</span><br><span> </span><br><span> /*** DOCUMENTATION</span><br><span>         <configInfo name="res_pjsip_outbound_registration" language="en_US"></span><br><span>@@ -76,6 +78,9 @@</span><br><span>                           <configOption name="contact_user"></span><br><span>                                   <synopsis>Contact User to use in request</synopsis></span><br><span>                              </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="contact_header_params"></span><br><span style="color: hsl(120, 100%, 40%);">+                                   <synopsis>Header parameters to place in the Contact header</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                             </configOption></span><br><span>                                <configOption name="expiration" default="3600"></span><br><span>                                    <synopsis>Expiration time for registrations in seconds</synopsis></span><br><span>                                </configOption></span><br><span>@@ -164,13 +169,16 @@</span><br><span>                                        <synopsis>Must be of type 'registration'.</synopsis></span><br><span>                             </configOption></span><br><span>                                <configOption name="support_path"></span><br><span style="color: hsl(0, 100%, 40%);">-                                      <synopsis>Enables Path support for outbound REGISTER requests.</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                                 <synopsis>Enables advertising SIP Path support for outbound REGISTER requests.</synopsis></span><br><span>                                        <description><para></span><br><span>                                              When this option is enabled, outbound REGISTER requests will advertise</span><br><span>                                               support for Path headers so that intervening proxies can add to the Path</span><br><span>                                             header as necessary.</span><br><span>                                         </para></description></span><br><span>                            </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="support_outbound"></span><br><span style="color: hsl(120, 100%, 40%);">+                                        <synopsis>Enables advertising SIP Outbound support (RFC5626) for outbound REGISTER requests.</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                           </configOption></span><br><span>                        </configObject></span><br><span>                </configFile></span><br><span>  </configInfo></span><br><span>@@ -224,6 +232,10 @@</span><br><span>   </manager></span><br><span>  ***/</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* forward declarations */</span><br><span style="color: hsl(120, 100%, 40%);">+static int set_outbound_initial_authentication_credentials(pjsip_regc *regc,</span><br><span style="color: hsl(120, 100%, 40%);">+               const struct ast_sip_auth_vector *auth_vector);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! \brief Some thread local storage used to determine if the running thread invoked the callback */</span><br><span> AST_THREADSTORAGE(register_callback_invoked);</span><br><span> </span><br><span>@@ -291,6 +303,8 @@</span><br><span>                 AST_STRING_FIELD(client_uri);</span><br><span>                /*! \brief Optional user for contact header */</span><br><span>               AST_STRING_FIELD(contact_user);</span><br><span style="color: hsl(120, 100%, 40%);">+               /*! \bried Optional header parameters for contact */</span><br><span style="color: hsl(120, 100%, 40%);">+          AST_STRING_FIELD(contact_header_params);</span><br><span>             /*! \brief Explicit transport to use for registration */</span><br><span>             AST_STRING_FIELD(transport);</span><br><span>                 /*! \brief Outbound proxy to use */</span><br><span>@@ -316,6 +330,8 @@</span><br><span>    struct ast_sip_auth_vector outbound_auths;</span><br><span>   /*! \brief Whether Path support is enabled */</span><br><span>        unsigned int support_path;</span><br><span style="color: hsl(120, 100%, 40%);">+    /*! \brief Whether Outbound support is enabled */</span><br><span style="color: hsl(120, 100%, 40%);">+     unsigned int support_outbound;</span><br><span> };</span><br><span> </span><br><span> /*! \brief Outbound registration client state information (persists for lifetime of regc) */</span><br><span>@@ -347,6 +363,8 @@</span><br><span>       unsigned int auth_rejection_permanent;</span><br><span>       /*! \brief Determines whether SIP Path support should be advertised */</span><br><span>       unsigned int support_path;</span><br><span style="color: hsl(120, 100%, 40%);">+    /*! \brief Determines whether SIP Outbound support should be advertised */</span><br><span style="color: hsl(120, 100%, 40%);">+    unsigned int support_outbound;</span><br><span>       /*! CSeq number of last sent auth request. */</span><br><span>        unsigned int auth_cseq;</span><br><span>      /*! \brief Serializer for stuff and things */</span><br><span>@@ -520,6 +538,7 @@</span><br><span> }</span><br><span> </span><br><span> static pj_str_t PATH_NAME = { "path", 4 };</span><br><span style="color: hsl(120, 100%, 40%);">+static pj_str_t OUTBOUND_NAME = { "outbound", 8 };</span><br><span> </span><br><span> /*! \brief Helper function which sends a message and cleans up, if needed, on failure */</span><br><span> static pj_status_t registration_client_send(struct sip_outbound_registration_client_state *client_state,</span><br><span>@@ -545,6 +564,8 @@</span><br><span>          */</span><br><span>  ast_sip_set_tpselector_from_transport_name(client_state->transport_name, &selector);</span><br><span>  pjsip_regc_set_transport(client_state->client, &selector);</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_sip_tpselector_unref(&selector);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>   status = pjsip_regc_send(client_state->client, tdata);</span><br><span> </span><br><span>        /* If the attempt to send the message failed and the callback was not invoked we need to</span><br><span>@@ -557,12 +578,58 @@</span><br><span>     return status;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Helper function to add string to Supported header */</span><br><span style="color: hsl(120, 100%, 40%);">+static int add_to_supported_header(pjsip_tx_data *tdata, pj_str_t *name)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ pjsip_supported_hdr *hdr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_SUPPORTED, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!hdr) {</span><br><span style="color: hsl(120, 100%, 40%);">+           /* insert a new Supported header */</span><br><span style="color: hsl(120, 100%, 40%);">+           hdr = pjsip_supported_hdr_create(tdata->pool);</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!hdr) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   pjsip_tx_data_dec_ref(tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+                 return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* add on to the existing Supported header */</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_strassign(&hdr->values[hdr->count++], name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Helper function to add configured supported headers */</span><br><span style="color: hsl(120, 100%, 40%);">+static int add_configured_supported_headers(struct sip_outbound_registration_client_state *client_state, pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  if (client_state->support_path) {</span><br><span style="color: hsl(120, 100%, 40%);">+          if (!add_to_supported_header(tdata, &PATH_NAME)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (client_state->support_outbound) {</span><br><span style="color: hsl(120, 100%, 40%);">+              if (!add_to_supported_header(tdata, &OUTBOUND_NAME)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! \brief Callback function for registering */</span><br><span> static int handle_client_registration(void *data)</span><br><span> {</span><br><span>  RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup);</span><br><span>  pjsip_tx_data *tdata;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+     if (set_outbound_initial_authentication_credentials(client_state->client, &client_state->outbound_auths)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_WARNING, "Failed to set initial authentication credentials\n");</span><br><span style="color: hsl(120, 100%, 40%);">+         return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  if (client_state->status == SIP_REGISTRATION_STOPPED</span><br><span>              || pjsip_regc_register(client_state->client, PJ_FALSE, &tdata) != PJ_SUCCESS) {</span><br><span>               return 0;</span><br><span>@@ -578,23 +645,9 @@</span><br><span>                     (int) info.client_uri.slen, info.client_uri.ptr);</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (client_state->support_path) {</span><br><span style="color: hsl(0, 100%, 40%);">-            pjsip_supported_hdr *hdr;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_SUPPORTED, NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-               if (!hdr) {</span><br><span style="color: hsl(0, 100%, 40%);">-                     /* insert a new Supported header */</span><br><span style="color: hsl(0, 100%, 40%);">-                     hdr = pjsip_supported_hdr_create(tdata->pool);</span><br><span style="color: hsl(0, 100%, 40%);">-                       if (!hdr) {</span><br><span style="color: hsl(0, 100%, 40%);">-                             pjsip_tx_data_dec_ref(tdata);</span><br><span style="color: hsl(0, 100%, 40%);">-                           return -1;</span><br><span style="color: hsl(0, 100%, 40%);">-                      }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)hdr);</span><br><span style="color: hsl(0, 100%, 40%);">-             }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               /* add on to the existing Supported header */</span><br><span style="color: hsl(0, 100%, 40%);">-           pj_strassign(&hdr->values[hdr->count++], &PATH_NAME);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!add_configured_supported_headers(client_state, tdata)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_WARNING, "Failed to set supported headers\n");</span><br><span style="color: hsl(120, 100%, 40%);">+          return -1;</span><br><span>   }</span><br><span> </span><br><span>        registration_client_send(client_state, tdata);</span><br><span>@@ -707,6 +760,7 @@</span><br><span>                         update_client_state_status(client_state, SIP_REGISTRATION_STOPPING);</span><br><span>                         client_state->destroy = 1;</span><br><span>                        if (pjsip_regc_unregister(client_state->client, &tdata) == PJ_SUCCESS</span><br><span style="color: hsl(120, 100%, 40%);">+                          && add_configured_supported_headers(client_state, tdata)</span><br><span>                             && registration_client_send(client_state, tdata) == PJ_SUCCESS) {</span><br><span>                            ao2_ref(client_state, -1);</span><br><span>                           return 0;</span><br><span>@@ -885,6 +939,75 @@</span><br><span>     ao2_ref(monitor, -1);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void save_response_fields_to_transport(struct registration_response *response)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      static const pj_str_t associated_uri_str = { "P-Associated-URI", 16 };</span><br><span style="color: hsl(120, 100%, 40%);">+      static const pj_str_t service_route_str = { "Service-Route", 13 };</span><br><span style="color: hsl(120, 100%, 40%);">+  pjsip_hdr *header = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     pjsip_msg *msg = response->rdata->msg_info.msg;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_service_route_vector *service_routes = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* If no transport is specified then we can't update any */</span><br><span style="color: hsl(120, 100%, 40%);">+       if (ast_strlen_zero(response->client_state->transport_name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_sip_transport_state_set_transport(response->client_state->transport_name, response->rdata->tp_info.transport);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      while ((header = pjsip_msg_find_hdr_by_name(msg, &service_route_str, header ? header->next : NULL))) {</span><br><span style="color: hsl(120, 100%, 40%);">+         char *service_route;</span><br><span style="color: hsl(120, 100%, 40%);">+          size_t size;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                /* The below code takes the approach that if we can't store all service routes then we</span><br><span style="color: hsl(120, 100%, 40%);">+             * store none at all. This gives a predictable failure condition instead of storing a</span><br><span style="color: hsl(120, 100%, 40%);">+          * partial list and having partial route headers.</span><br><span style="color: hsl(120, 100%, 40%);">+              */</span><br><span style="color: hsl(120, 100%, 40%);">+           size = pj_strlen(&((pjsip_generic_string_hdr*)header)->hvalue) + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+            service_route = ast_malloc(size);</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!service_route) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (service_routes) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_sip_service_route_vector_destroy(service_routes);</span><br><span style="color: hsl(120, 100%, 40%);">+                         service_routes = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+                        }</span><br><span style="color: hsl(120, 100%, 40%);">+                     break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_copy_pj_str(service_route, &((pjsip_generic_string_hdr*)header)->hvalue, size);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+          if (!service_routes) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        service_routes = ast_sip_service_route_vector_alloc();</span><br><span style="color: hsl(120, 100%, 40%);">+                        if (!service_routes) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                ast_free(service_route);</span><br><span style="color: hsl(120, 100%, 40%);">+                              break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           if (AST_VECTOR_APPEND(service_routes, service_route)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       ast_free(service_route);</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_sip_service_route_vector_destroy(service_routes);</span><br><span style="color: hsl(120, 100%, 40%);">+                 service_routes = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+                        break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* If any service routes were handled then store them on the transport */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (service_routes) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_sip_transport_state_set_service_routes(response->client_state->transport_name, service_routes);</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* If an associated URI is present in the response we need to use it on any outgoing</span><br><span style="color: hsl(120, 100%, 40%);">+   * traffic on the transport.</span><br><span style="color: hsl(120, 100%, 40%);">+   */</span><br><span style="color: hsl(120, 100%, 40%);">+   header = pjsip_msg_find_hdr_by_name(msg, &associated_uri_str, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (header) {</span><br><span style="color: hsl(120, 100%, 40%);">+         char value[pj_strlen(&((pjsip_generic_string_hdr*)header)->hvalue) + 1];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_copy_pj_str(value, &((pjsip_generic_string_hdr*)header)->hvalue, sizeof(value));</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_sip_transport_state_set_preferred_identity(response->client_state->transport_name, value);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! \brief Callback function for handling a response to a registration attempt */</span><br><span> static int handle_registration_response(void *data)</span><br><span> {</span><br><span>@@ -964,6 +1087,8 @@</span><br><span>                             registration_transport_shutdown_cb, response->client_state->registration_name,</span><br><span>                                 monitor_matcher);</span><br><span>            }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           save_response_fields_to_transport(response);</span><br><span>         } else if (response->client_state->destroy) {</span><br><span>          /* We need to deal with the pending destruction instead. */</span><br><span>  } else if (response->retry_after) {</span><br><span>@@ -1190,7 +1315,7 @@</span><br><span> </span><br><span> /*! \brief Helper function which populates a pj_str_t with a contact header */</span><br><span> static int sip_dialog_create_contact(pj_pool_t *pool, pj_str_t *contact, const char *user,</span><br><span style="color: hsl(0, 100%, 40%);">-      const pj_str_t *target, pjsip_tpselector *selector, const char *line)</span><br><span style="color: hsl(120, 100%, 40%);">+ const pj_str_t *target, pjsip_tpselector *selector, const char *line, const char *header_params)</span><br><span> {</span><br><span>        pj_str_t tmp, local_addr;</span><br><span>    pjsip_uri *uri;</span><br><span>@@ -1234,7 +1359,7 @@</span><br><span> </span><br><span>  contact->ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);</span><br><span>   contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE,</span><br><span style="color: hsl(0, 100%, 40%);">-                "<%s:%s@%s%.*s%s:%d%s%s%s%s>",</span><br><span style="color: hsl(120, 100%, 40%);">+                "<%s:%s@%s%.*s%s:%d%s%s%s%s>%s%s",</span><br><span>           ((pjsip_transport_get_flag_from_type(type) & PJSIP_TRANSPORT_SECURE) && PJSIP_URI_SCHEME_IS_SIPS(uri)) ? "sips" : "sip",</span><br><span>             user,</span><br><span>                (type & PJSIP_TRANSPORT_IPV6) ? "[" : "",</span><br><span>@@ -1245,7 +1370,9 @@</span><br><span>            (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? ";transport=" : "",</span><br><span>              (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? pjsip_transport_get_type_name(type) : "",</span><br><span>          !ast_strlen_zero(line) ? ";line=" : "",</span><br><span style="color: hsl(0, 100%, 40%);">-             S_OR(line, ""));</span><br><span style="color: hsl(120, 100%, 40%);">+            S_OR(line, ""),</span><br><span style="color: hsl(120, 100%, 40%);">+             !ast_strlen_zero(header_params) ? ";" : "",</span><br><span style="color: hsl(120, 100%, 40%);">+               S_OR(header_params, ""));</span><br><span> </span><br><span>      return 0;</span><br><span> }</span><br><span>@@ -1283,6 +1410,124 @@</span><br><span>     return rc;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* \brief Get google oauth2 access token using refresh token */</span><br><span style="color: hsl(120, 100%, 40%);">+static const char *fetch_google_access_token(const struct ast_sip_auth *auth)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        char *cmd = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     const char *token;</span><br><span style="color: hsl(120, 100%, 40%);">+    const char *url = "https://www.googleapis.com/oauth2/v3/token";</span><br><span style="color: hsl(120, 100%, 40%);">+     char buf[4096];</span><br><span style="color: hsl(120, 100%, 40%);">+       int res;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_json_error error;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_json *json;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* set timeout to be shorter than default 180s (also checks func_curl is available) */</span><br><span style="color: hsl(120, 100%, 40%);">+        if (ast_func_write(NULL, "CURLOPT(conntimeout)", "10")) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_ERROR, "CURL is unavailable. This is required for Google OAuth 2.0 authentication. Please ensure it is loaded.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   res = ast_asprintf(&cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+          "CURL(%s,client_id=%s&client_secret=%s&refresh_token=%s&grant_type=refresh_token)",</span><br><span style="color: hsl(120, 100%, 40%);">+             url, auth->oauth_clientid, auth->oauth_secret, auth->refresh_token);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_debug(2, "Performing Google OAuth 2.0 authentication using command: %s\n", cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      buf[0] = '\0';</span><br><span style="color: hsl(120, 100%, 40%);">+        res = ast_func_read(NULL, cmd, buf, sizeof(buf));</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_free(cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (res) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_log(LOG_ERROR, "Could not retrieve Google OAuth 2.0 authentication\n");</span><br><span style="color: hsl(120, 100%, 40%);">+         return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_debug(2, "Google OAuth 2.0 authentication returned: %s\n", buf);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      json = ast_json_load_string(buf, &error);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!json) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_ERROR, "Could not parse Google OAuth 2.0 authentication: %d(%d) %s: '%s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                    error.line, error.column, error.text, buf);</span><br><span style="color: hsl(120, 100%, 40%);">+           return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   token = ast_json_string_get(ast_json_object_get(json, "access_token"));</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!token) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_ERROR, "Could not find Google OAuth 2.0 access_token in: '%s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                       buf);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     token = ast_strdup(token);</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_json_unref(json);</span><br><span style="color: hsl(120, 100%, 40%);">+ return token;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Set pjsip registration context with any authentication credentials that need to be</span><br><span style="color: hsl(120, 100%, 40%);">+ * sent in the initial registration request</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param regc The pjsip registration context</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param auth_vector The vector of configured authentication credentials</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int set_outbound_initial_authentication_credentials(pjsip_regc *regc,</span><br><span style="color: hsl(120, 100%, 40%);">+             const struct ast_sip_auth_vector *auth_vector)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     size_t auth_size = AST_VECTOR_SIZE(auth_vector);</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_sip_auth *auths[auth_size];</span><br><span style="color: hsl(120, 100%, 40%);">+        const char *access_token;</span><br><span style="color: hsl(120, 100%, 40%);">+     pjsip_cred_info auth_creds[1];</span><br><span style="color: hsl(120, 100%, 40%);">+        pjsip_auth_clt_pref prefs;</span><br><span style="color: hsl(120, 100%, 40%);">+    int res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+  int idx;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    memset(auths, 0, sizeof(auths));</span><br><span style="color: hsl(120, 100%, 40%);">+      if (ast_sip_retrieve_auths(auth_vector, auths)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+             goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   for (idx = 0; idx < auth_size; ++idx) {</span><br><span style="color: hsl(120, 100%, 40%);">+            switch (auths[idx]->type) {</span><br><span style="color: hsl(120, 100%, 40%);">+                case AST_SIP_AUTH_TYPE_GOOGLE_OAUTH:</span><br><span style="color: hsl(120, 100%, 40%);">+                  pj_cstr(&auth_creds[0].username, auths[idx]->auth_user);</span><br><span style="color: hsl(120, 100%, 40%);">+                       pj_cstr(&auth_creds[0].scheme, "Bearer");</span><br><span style="color: hsl(120, 100%, 40%);">+                       pj_cstr(&auth_creds[0].realm, auths[idx]->realm);</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_debug(2, "Obtaining Google OAuth access token\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                      access_token = fetch_google_access_token(auths[idx]);</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (!access_token) {</span><br><span style="color: hsl(120, 100%, 40%);">+                          ast_log(LOG_WARNING, "Obtaining Google OAuth access token failed\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                               access_token = auths[idx]->auth_pass;</span><br><span style="color: hsl(120, 100%, 40%);">+                              res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_debug(2, "Setting data to '%s'\n", access_token);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                     pj_cstr(&auth_creds[0].data, access_token);</span><br><span style="color: hsl(120, 100%, 40%);">+                       auth_creds[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                     pjsip_regc_set_credentials(regc, 1, auth_creds);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                    /* for oauth, send auth without waiting for unauthorized response */</span><br><span style="color: hsl(120, 100%, 40%);">+                  prefs.initial_auth = PJ_TRUE;</span><br><span style="color: hsl(120, 100%, 40%);">+                 pj_cstr(&prefs.algorithm, "oauth");</span><br><span style="color: hsl(120, 100%, 40%);">+                     pjsip_regc_set_prefs(regc, &prefs);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (access_token != auths[idx]->auth_pass) {</span><br><span style="color: hsl(120, 100%, 40%);">+                               ast_free((char *) access_token);</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+                     break;</span><br><span style="color: hsl(120, 100%, 40%);">+                default:</span><br><span style="color: hsl(120, 100%, 40%);">+                      /* other cases handled after receiving auth rejection */</span><br><span style="color: hsl(120, 100%, 40%);">+                      break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+cleanup:</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_cleanup_auths(auths, auth_size);</span><br><span style="color: hsl(120, 100%, 40%);">+      return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! \brief Helper function that allocates a pjsip registration client and configures it */</span><br><span> static int sip_outbound_registration_regc_alloc(void *data)</span><br><span> {</span><br><span>@@ -1356,6 +1601,7 @@</span><br><span>           route = pjsip_parse_hdr(pjsip_regc_get_pool(state->client_state->client),</span><br><span>                      &ROUTE_HNAME, tmp.ptr, tmp.slen, NULL);</span><br><span>          if (!route) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 ast_sip_tpselector_unref(&selector);</span><br><span>                     return -1;</span><br><span>           }</span><br><span>            pj_list_insert_nodes_before(&route_set, route);</span><br><span>@@ -1369,13 +1615,15 @@</span><br><span> </span><br><span>    pj_cstr(&server_uri, registration->server_uri);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>   if (sip_dialog_create_contact(pjsip_regc_get_pool(state->client_state->client),</span><br><span>                &contact_uri, S_OR(registration->contact_user, "s"), &server_uri, &selector,</span><br><span style="color: hsl(0, 100%, 40%);">-           state->client_state->line)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           state->client_state->line, registration->contact_header_params)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_sip_tpselector_unref(&selector);</span><br><span>             return -1;</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_tpselector_unref(&selector);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>   pj_cstr(&client_uri, registration->client_uri);</span><br><span>       if (pjsip_regc_init(state->client_state->client, &server_uri, &client_uri,</span><br><span>             &client_uri, 1, &contact_uri, registration->expiration) != PJ_SUCCESS) {</span><br><span>@@ -1409,6 +1657,7 @@</span><br><span>  state->client_state->max_retries = registration->max_retries;</span><br><span>       state->client_state->retries = 0;</span><br><span>      state->client_state->support_path = registration->support_path;</span><br><span style="color: hsl(120, 100%, 40%);">+      state->client_state->support_outbound = registration->support_outbound;</span><br><span>     state->client_state->auth_rejection_permanent = registration->auth_rejection_permanent;</span><br><span> </span><br><span>         pjsip_regc_update_expires(state->client_state->client, registration->expiration);</span><br><span>@@ -1550,7 +1799,8 @@</span><br><span> </span><br><span>       cancel_registration(state->client_state);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        if (pjsip_regc_unregister(client, &tdata) == PJ_SUCCESS) {</span><br><span style="color: hsl(120, 100%, 40%);">+        if (pjsip_regc_unregister(client, &tdata) == PJ_SUCCESS</span><br><span style="color: hsl(120, 100%, 40%);">+           && add_configured_supported_headers(state->client_state, tdata)) {</span><br><span>                registration_client_send(state->client_state, tdata);</span><br><span>     }</span><br><span> </span><br><span>@@ -2219,6 +2469,7 @@</span><br><span>        ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "server_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, server_uri));</span><br><span>     ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "client_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, client_uri));</span><br><span>     ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "contact_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, contact_user));</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "contact_header_params", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, contact_header_params));</span><br><span>       ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, transport));</span><br><span>       ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, outbound_proxy));</span><br><span>     ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "expiration", "3600", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, expiration));</span><br><span>@@ -2229,6 +2480,7 @@</span><br><span>   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));</span><br><span>        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);</span><br><span>  ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "support_path", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, support_path));</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "support_outbound", "no", OPT_YESNO_T, 1, FLDSET(struct sip_outbound_registration, support_outbound));</span><br><span>        ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "line", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, line));</span><br><span>         ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, endpoint));</span><br><span> </span><br><span>diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c</span><br><span>index e681dcb..1dd8ce9 100644</span><br><span>--- a/res/res_pjsip_session.c</span><br><span>+++ b/res/res_pjsip_session.c</span><br><span>@@ -3344,6 +3344,9 @@</span><br><span>    struct pjsip_request_line req = tdata->msg->line.req;</span><br><span> </span><br><span>      ast_debug(3, "Method is %.*s\n", (int) pj_strlen(&req.method.name), pj_strbuf(&req.method.name));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_message_apply_transport(session->endpoint->transport, tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>        AST_LIST_TRAVERSE(&session->supplements, supplement, next) {</span><br><span>          if (supplement->outgoing_request && does_method_match(&req.method.name, supplement->method)) {</span><br><span>                     supplement->outgoing_request(session, tdata);</span><br><span>@@ -3366,6 +3369,8 @@</span><br><span>             pj_strbuf(&cseq->method.name), status.code, (int) pj_strlen(&status.reason),</span><br><span>              pj_strbuf(&status.reason));</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+   ast_sip_message_apply_transport(session->endpoint->transport, tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>        AST_LIST_TRAVERSE(&session->supplements, supplement, next) {</span><br><span>          if (supplement->outgoing_response && does_method_match(&cseq->method.name, supplement->method)) {</span><br><span>                       supplement->outgoing_response(session, tdata);</span><br><span>diff --git a/third-party/pjproject/configure.m4 b/third-party/pjproject/configure.m4</span><br><span>index 94be9b8..9e89098 100644</span><br><span>--- a/third-party/pjproject/configure.m4</span><br><span>+++ b/third-party/pjproject/configure.m4</span><br><span>@@ -106,6 +106,8 @@</span><br><span>         AC_DEFINE([HAVE_PJSIP_TSX_LAYER_FIND_TSX2], 1, [Define if your system has pjsip_tsx_layer_find_tsx2 declared.])</span><br><span>      AC_DEFINE([HAVE_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS], 1, [Define if your system has HAVE_PJSIP_INV_ACCEPT_MULTIPLE_SDP_ANSWERS declared.])</span><br><span>         AC_DEFINE([HAVE_PJSIP_ENDPOINT_COMPACT_FORM], 1, [Define if your system has HAVE_PJSIP_ENDPOINT_COMPACT_FORM declared.])</span><br><span style="color: hsl(120, 100%, 40%);">+      AC_DEFINE([HAVE_PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE], 1, [Define if your system has HAVE_PJSIP_TRANSPORT_DISABLE_CONNECTION_REUSE declared])</span><br><span style="color: hsl(120, 100%, 40%);">+     AC_DEFINE([HAVE_PJSIP_OAUTH_AUTHENTICATION], 1, [Define if your system has HAVE_PJSIP_OAUTH_AUTHENTICATION declared])</span><br><span> </span><br><span>    AC_SUBST([PJPROJECT_BUNDLED])</span><br><span>        AC_SUBST([PJPROJECT_DIR])</span><br><span>diff --git a/third-party/pjproject/patches/0020-oauth.patch b/third-party/pjproject/patches/0020-oauth.patch</span><br><span>new file mode 100644</span><br><span>index 0000000..927cb5c</span><br><span>--- /dev/null</span><br><span>+++ b/third-party/pjproject/patches/0020-oauth.patch</span><br><span>@@ -0,0 +1,129 @@</span><br><span style="color: hsl(120, 100%, 40%);">+diff -x '*.o' -x '*.a' -ru a/pjsip/include/pjsip/sip_auth_msg.h b/pjsip/include/pjsip/sip_auth_msg.h</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjsip/include/pjsip/sip_auth_msg.h     2011-05-05 02:14:19.000000000 -0400</span><br><span>++++ b/pjsip/include/pjsip/sip_auth_msg.h 2018-09-14 16:42:03.986813665 -0400</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -89,6 +89,23 @@</span><br><span style="color: hsl(120, 100%, 40%);">+ typedef struct pjsip_pgp_credential pjsip_pgp_credential;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ /**</span><br><span style="color: hsl(120, 100%, 40%);">++ * This structure describe credential used in Authorization and</span><br><span style="color: hsl(120, 100%, 40%);">++ * Proxy-Authorization header for OAuth authentication scheme.</span><br><span style="color: hsl(120, 100%, 40%);">++ */</span><br><span style="color: hsl(120, 100%, 40%);">++struct pjsip_oauth_credential</span><br><span style="color: hsl(120, 100%, 40%);">++{</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_str_t    realm;          /**< Realm of the credential    */</span><br><span style="color: hsl(120, 100%, 40%);">++    pjsip_param other_param;    /**< Other parameters.          */</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_str_t    username;       /**< Username parameter.        */</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_str_t    token;          /**< Token parameter.           */</span><br><span style="color: hsl(120, 100%, 40%);">++};</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++/**</span><br><span style="color: hsl(120, 100%, 40%);">++ * @see pjsip_oauth_credential</span><br><span style="color: hsl(120, 100%, 40%);">++ */</span><br><span style="color: hsl(120, 100%, 40%);">++typedef struct pjsip_oauth_credential pjsip_oauth_credential;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++/**</span><br><span style="color: hsl(120, 100%, 40%);">+  * This structure describes SIP Authorization header (and also SIP</span><br><span style="color: hsl(120, 100%, 40%);">+  * Proxy-Authorization header).</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -106,6 +123,7 @@</span><br><span style="color: hsl(120, 100%, 40%);">+     pjsip_common_credential common; /**< Common fields.      */</span><br><span style="color: hsl(120, 100%, 40%);">+        pjsip_digest_credential digest; /**< Digest credentials.    */</span><br><span style="color: hsl(120, 100%, 40%);">+     pjsip_pgp_credential    pgp;    /**< PGP credentials.            */</span><br><span style="color: hsl(120, 100%, 40%);">++       pjsip_oauth_credential  oauth;  /**< OAuth credentials.     */</span><br><span style="color: hsl(120, 100%, 40%);">+     } credential;</span><br><span style="color: hsl(120, 100%, 40%);">+ };</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+diff -x '*.o' -x '*.a' -ru a/pjsip/include/pjsip/sip_auth_parser.h b/pjsip/include/pjsip/sip_auth_parser.h</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjsip/include/pjsip/sip_auth_parser.h  2011-05-05 02:14:19.000000000 -0400</span><br><span>++++ b/pjsip/include/pjsip/sip_auth_parser.h      2018-09-14 16:42:11.982807508 -0400</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -64,6 +64,7 @@</span><br><span style="color: hsl(120, 100%, 40%);">+                        pjsip_FALSE_STR,    /**< "false" string const.         */</span><br><span style="color: hsl(120, 100%, 40%);">+                        pjsip_DIGEST_STR,   /**< "digest" string const.        */</span><br><span style="color: hsl(120, 100%, 40%);">+                        pjsip_PGP_STR,      /**< "pgp" string const.           */</span><br><span style="color: hsl(120, 100%, 40%);">++                       pjsip_BEARER_STR,   /**< "bearer" string const.     */</span><br><span style="color: hsl(120, 100%, 40%);">+                   pjsip_MD5_STR,      /**< "md5" string const.           */</span><br><span style="color: hsl(120, 100%, 40%);">+                        pjsip_AUTH_STR;     /**< "auth" string const.          */</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+diff -x '*.o' -x '*.a' -ru a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjsip/src/pjsip/sip_auth_client.c  2017-03-31 02:02:48.000000000 -0400</span><br><span>++++ b/pjsip/src/pjsip/sip_auth_client.c  2018-09-14 16:42:28.138795061 -0400</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -959,13 +959,22 @@</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+                 hs = pjsip_authorization_hdr_create(tdata->pool);</span><br><span style="color: hsl(120, 100%, 40%);">+          pj_strdup(tdata->pool, &hs->scheme, &c->scheme);</span><br><span style="color: hsl(120, 100%, 40%);">+-            pj_strdup(tdata->pool, &hs->credential.digest.username,</span><br><span style="color: hsl(120, 100%, 40%);">+-                      &c->username);</span><br><span style="color: hsl(120, 100%, 40%);">+-              pj_strdup(tdata->pool, &hs->credential.digest.realm,</span><br><span style="color: hsl(120, 100%, 40%);">+-                         &c->realm);</span><br><span style="color: hsl(120, 100%, 40%);">+-         pj_strdup(tdata->pool, &hs->credential.digest.uri, &uri);</span><br><span style="color: hsl(120, 100%, 40%);">+-              pj_strdup(tdata->pool, &hs->credential.digest.algorithm,</span><br><span style="color: hsl(120, 100%, 40%);">+-                     &sess->pref.algorithm);</span><br><span style="color: hsl(120, 100%, 40%);">++             if (pj_stricmp(&c->scheme, &pjsip_BEARER_STR)==0) {</span><br><span style="color: hsl(120, 100%, 40%);">++                       pj_strdup(tdata->pool, &hs->credential.oauth.username,</span><br><span style="color: hsl(120, 100%, 40%);">++                                  &c->username);</span><br><span style="color: hsl(120, 100%, 40%);">++                        pj_strdup(tdata->pool, &hs->credential.oauth.realm,</span><br><span style="color: hsl(120, 100%, 40%);">++                                  &c->realm);</span><br><span style="color: hsl(120, 100%, 40%);">++                        pj_strdup(tdata->pool, &hs->credential.oauth.token,</span><br><span style="color: hsl(120, 100%, 40%);">++                                  &c->data);</span><br><span style="color: hsl(120, 100%, 40%);">++              } else { //if (pj_stricmp(&c->scheme, &pjsip_DIGEST_STR)==0)</span><br><span style="color: hsl(120, 100%, 40%);">++                      pj_strdup(tdata->pool, &hs->credential.digest.username,</span><br><span style="color: hsl(120, 100%, 40%);">++                              &c->username);</span><br><span style="color: hsl(120, 100%, 40%);">++                      pj_strdup(tdata->pool, &hs->credential.digest.realm,</span><br><span style="color: hsl(120, 100%, 40%);">++                                 &c->realm);</span><br><span style="color: hsl(120, 100%, 40%);">++                 pj_strdup(tdata->pool,&hs->credential.digest.uri, &uri);</span><br><span style="color: hsl(120, 100%, 40%);">++                       pj_strdup(tdata->pool, &hs->credential.digest.algorithm,</span><br><span style="color: hsl(120, 100%, 40%);">++                             &sess->pref.algorithm);</span><br><span style="color: hsl(120, 100%, 40%);">++             }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+          pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hs);</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span style="color: hsl(120, 100%, 40%);">+diff -x '*.o' -x '*.a' -ru a/pjsip/src/pjsip/sip_auth_msg.c b/pjsip/src/pjsip/sip_auth_msg.c</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjsip/src/pjsip/sip_auth_msg.c       2016-01-27 00:42:20.000000000 -0500</span><br><span>++++ b/pjsip/src/pjsip/sip_auth_msg.c     2018-09-14 16:42:15.882804502 -0400</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -103,6 +103,23 @@</span><br><span style="color: hsl(120, 100%, 40%);">+     return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">++static int print_oauth_credential(pjsip_oauth_credential *cred, char *buf,</span><br><span style="color: hsl(120, 100%, 40%);">++                                 pj_size_t size)</span><br><span style="color: hsl(120, 100%, 40%);">++{</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_ssize_t printed;</span><br><span style="color: hsl(120, 100%, 40%);">++    char *startbuf = buf;</span><br><span style="color: hsl(120, 100%, 40%);">++    char *endbuf = buf + size;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++    copy_advance_pair_quote_cond_always(buf, "token=", 6, cred->token,</span><br><span style="color: hsl(120, 100%, 40%);">++                                        '"', '"');</span><br><span style="color: hsl(120, 100%, 40%);">++    copy_advance_pair_quote_cond_always(buf, ", username=", 11, cred->username,</span><br><span style="color: hsl(120, 100%, 40%);">++                                          '"', '"');</span><br><span style="color: hsl(120, 100%, 40%);">++    copy_advance_pair_quote_cond_always(buf, ", realm=", 8, cred->realm,</span><br><span style="color: hsl(120, 100%, 40%);">++                                         '"', '"');</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++    return (int) (buf-startbuf);</span><br><span style="color: hsl(120, 100%, 40%);">++}</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">+ static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr,</span><br><span style="color: hsl(120, 100%, 40%);">+                                     char *buf, pj_size_t size)</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -125,6 +142,11 @@</span><br><span style="color: hsl(120, 100%, 40%);">+     {</span><br><span style="color: hsl(120, 100%, 40%);">+        printed = print_pgp_credential(&hdr->credential.pgp, buf, endbuf - buf);</span><br><span style="color: hsl(120, 100%, 40%);">+     } </span><br><span style="color: hsl(120, 100%, 40%);">++    else if (pj_stricmp(&hdr->scheme, &pjsip_BEARER_STR) == 0)</span><br><span style="color: hsl(120, 100%, 40%);">++    {</span><br><span style="color: hsl(120, 100%, 40%);">++        printed = print_oauth_credential(&hdr->credential.oauth, buf,</span><br><span style="color: hsl(120, 100%, 40%);">++                                   endbuf - buf);</span><br><span style="color: hsl(120, 100%, 40%);">++    }</span><br><span style="color: hsl(120, 100%, 40%);">+     else {</span><br><span style="color: hsl(120, 100%, 40%);">+  pj_assert(0);</span><br><span style="color: hsl(120, 100%, 40%);">+         return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+diff -x '*.o' -x '*.a' -ru a/pjsip/src/pjsip/sip_auth_parser.c b/pjsip/src/pjsip/sip_auth_parser.c</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjsip/src/pjsip/sip_auth_parser.c 2014-06-09 22:56:56.000000000 -0400</span><br><span>++++ b/pjsip/src/pjsip/sip_auth_parser.c  2018-09-14 16:42:21.418800238 -0400</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -59,6 +59,7 @@</span><br><span style="color: hsl(120, 100%, 40%);">+                pjsip_QUOTED_DIGEST_STR =   { "\"Digest\"", 8},</span><br><span style="color: hsl(120, 100%, 40%);">+           pjsip_PGP_STR =             { "PGP", 3 },</span><br><span style="color: hsl(120, 100%, 40%);">+           pjsip_QUOTED_PGP_STR =      { "\"PGP\"", 5 },</span><br><span style="color: hsl(120, 100%, 40%);">++            pjsip_BEARER_STR =          { "Bearer", 6 },</span><br><span style="color: hsl(120, 100%, 40%);">+                pjsip_MD5_STR =             { "md5", 3 },</span><br><span style="color: hsl(120, 100%, 40%);">+           pjsip_QUOTED_MD5_STR =      { "\"md5\"", 5},</span><br><span style="color: hsl(120, 100%, 40%);">+              pjsip_AUTH_STR =            { "auth", 4},</span><br><span>diff --git a/third-party/pjproject/patches/0030-allow-disabling-of-connection-reuse.patch b/third-party/pjproject/patches/0030-allow-disabling-of-connection-reuse.patch</span><br><span>new file mode 100644</span><br><span>index 0000000..4727085</span><br><span>--- /dev/null</span><br><span>+++ b/third-party/pjproject/patches/0030-allow-disabling-of-connection-reuse.patch</span><br><span>@@ -0,0 +1,102 @@</span><br><span style="color: hsl(120, 100%, 40%);">+diff -x '*.o' -x '*.a' -ru a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjsip/include/pjsip/sip_transport.h      2017-02-19 20:16:58.000000000 -0500</span><br><span>++++ b/pjsip/include/pjsip/sip_transport.h        2018-09-14 16:47:25.145266710 -0400</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -221,12 +221,26 @@</span><br><span style="color: hsl(120, 100%, 40%);">+  * application specificly request that a particular transport/listener</span><br><span style="color: hsl(120, 100%, 40%);">+  * should be used to send request. This structure is used when calling</span><br><span style="color: hsl(120, 100%, 40%);">+  * pjsip_tsx_set_transport() and pjsip_dlg_set_transport().</span><br><span style="color: hsl(120, 100%, 40%);">++ *</span><br><span style="color: hsl(120, 100%, 40%);">++ * If application disables connection reuse and wants to force creating</span><br><span style="color: hsl(120, 100%, 40%);">++ * a new transport, it needs to consider the following couple of things:</span><br><span style="color: hsl(120, 100%, 40%);">++ * - If it still wants to reuse an existing transport (if any), it</span><br><span style="color: hsl(120, 100%, 40%);">++ *   needs to keep a reference to that transport and specifically set</span><br><span style="color: hsl(120, 100%, 40%);">++ *   the transport to be used for sending requests.</span><br><span style="color: hsl(120, 100%, 40%);">++ * - Delete those existing transports manually when no longer needed.</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+ typedef struct pjsip_tpselector</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+     /** The type of data in the union */</span><br><span style="color: hsl(120, 100%, 40%);">+     pjsip_tpselector_type   type;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">++    /**</span><br><span style="color: hsl(120, 100%, 40%);">++     * Whether to disable reuse of an existing connection.</span><br><span style="color: hsl(120, 100%, 40%);">++     * This setting will be ignored if (type == PJSIP_TPSELECTOR_TRANSPORT)</span><br><span style="color: hsl(120, 100%, 40%);">++     * and transport in the union below is set.</span><br><span style="color: hsl(120, 100%, 40%);">++     */</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_bool_t disable_connection_reuse;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">+     /** Union representing the transport/listener criteria to be used. */</span><br><span style="color: hsl(120, 100%, 40%);">+     union {</span><br><span style="color: hsl(120, 100%, 40%);">+         pjsip_transport *transport;</span><br><span style="color: hsl(120, 100%, 40%);">+diff -x '*.o' -x '*.a' -ru a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjsip/src/pjsip/sip_transport.c      2017-11-07 21:58:18.000000000 -0500</span><br><span>++++ b/pjsip/src/pjsip/sip_transport.c    2018-09-14 16:47:25.145266710 -0400</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -2118,7 +2118,7 @@</span><br><span style="color: hsl(120, 100%, 40%);">+     */</span><br><span style="color: hsl(120, 100%, 40%);">+   pjsip_transport_key key;</span><br><span style="color: hsl(120, 100%, 40%);">+      int key_len;</span><br><span style="color: hsl(120, 100%, 40%);">+- pjsip_transport *transport;</span><br><span style="color: hsl(120, 100%, 40%);">++  pjsip_transport *transport = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+         /* If listener is specified, verify that the listener type matches</span><br><span style="color: hsl(120, 100%, 40%);">+     * the destination type.</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -2131,17 +2131,21 @@</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+- pj_bzero(&key, sizeof(key));</span><br><span style="color: hsl(120, 100%, 40%);">+-     key_len = sizeof(key.type) + addr_len;</span><br><span style="color: hsl(120, 100%, 40%);">++       if (!sel || sel->disable_connection_reuse == PJ_FALSE) {</span><br><span style="color: hsl(120, 100%, 40%);">++      pj_bzero(&key, sizeof(key));</span><br><span style="color: hsl(120, 100%, 40%);">++     key_len = sizeof(key.type) + addr_len;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++            /* First try to get exact destination. */</span><br><span style="color: hsl(120, 100%, 40%);">++            key.type = type;</span><br><span style="color: hsl(120, 100%, 40%);">++     pj_memcpy(&key.rem_addr, remote, addr_len);</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+-       /* First try to get exact destination. */</span><br><span style="color: hsl(120, 100%, 40%);">+-    key.type = type;</span><br><span style="color: hsl(120, 100%, 40%);">+-     pj_memcpy(&key.rem_addr, remote, addr_len);</span><br><span style="color: hsl(120, 100%, 40%);">+-</span><br><span style="color: hsl(120, 100%, 40%);">+-   transport = (pjsip_transport*)</span><br><span style="color: hsl(120, 100%, 40%);">+-                   pj_hash_get(mgr->table, &key, key_len, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">++         transport = (pjsip_transport*)</span><br><span style="color: hsl(120, 100%, 40%);">++                   pj_hash_get(mgr->table, &key, key_len, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">++ }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+- if (transport == NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">++     if (transport == NULL &&</span><br><span style="color: hsl(120, 100%, 40%);">++         (!sel || sel->disable_connection_reuse == PJ_FALSE))</span><br><span style="color: hsl(120, 100%, 40%);">++  {</span><br><span style="color: hsl(120, 100%, 40%);">+         unsigned flag = pjsip_transport_get_flag_from_type(type);</span><br><span style="color: hsl(120, 100%, 40%);">+             const pj_sockaddr *remote_addr = (const pj_sockaddr*)remote;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+@@ -2179,9 +2183,7 @@</span><br><span style="color: hsl(120, 100%, 40%);">+        transport = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+             /* This will cause a new transport to be created which will be a</span><br><span style="color: hsl(120, 100%, 40%);">+       * 'duplicate' of the existing transport (same type & remote addr,</span><br><span style="color: hsl(120, 100%, 40%);">+-        * but different factory). Any future hash lookup will return</span><br><span style="color: hsl(120, 100%, 40%);">+-         * the new one, and eventually the old one will still be freed</span><br><span style="color: hsl(120, 100%, 40%);">+-        * (by application or #1774).</span><br><span style="color: hsl(120, 100%, 40%);">++         * but different factory).</span><br><span style="color: hsl(120, 100%, 40%);">+             */</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+@@ -2199,9 +2201,14 @@</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+    /*</span><br><span style="color: hsl(120, 100%, 40%);">+-    * Transport not found!</span><br><span style="color: hsl(120, 100%, 40%);">+-       * So we need to create one, find factory that can create</span><br><span style="color: hsl(120, 100%, 40%);">+-     * such transport.</span><br><span style="color: hsl(120, 100%, 40%);">++    * Either transport not found, or we don't want to use the existing</span><br><span style="color: hsl(120, 100%, 40%);">++       * transport (such as in the case of different factory or</span><br><span style="color: hsl(120, 100%, 40%);">++     * if connection reuse is disabled). So we need to create one,</span><br><span style="color: hsl(120, 100%, 40%);">++        * find factory that can create such transport.</span><br><span style="color: hsl(120, 100%, 40%);">++       *</span><br><span style="color: hsl(120, 100%, 40%);">++    * If there's an existing transport, its place in the hash table</span><br><span style="color: hsl(120, 100%, 40%);">++  * will be replaced by this new one. And eventually the existing</span><br><span style="color: hsl(120, 100%, 40%);">++      * transport will still be freed (by application or #1774).</span><br><span style="color: hsl(120, 100%, 40%);">+    */</span><br><span style="color: hsl(120, 100%, 40%);">+   if (sel && sel->type == PJSIP_TPSELECTOR_LISTENER && sel->u.listener)</span><br><span style="color: hsl(120, 100%, 40%);">+   {</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/9505">change 9505</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/9505"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: Id214c2d1c550a41fcf564b7df8f3da7be565bd58 </div>
<div style="display:none"> Gerrit-Change-Number: 9505 </div>
<div style="display:none"> Gerrit-PatchSet: 19 </div>
<div style="display:none"> Gerrit-Owner: Nick French <naf@ou.edu> </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins2 (1000185) </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Michael Kuron <m.kuron@gmx.de> </div>
<div style="display:none"> Gerrit-Reviewer: Michael L. Young <elgueromexicano@gmail.com> </div>
<div style="display:none"> Gerrit-Reviewer: Nick French <naf@ou.edu> </div>
<div style="display:none"> Gerrit-Reviewer: Richard Mudgett <rmudgett@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Sean Bright <sean.bright@gmail.com> </div>