Merge "logger.c: Fix default console logging when no logger.conf available."
authorGeorge Joseph <gjoseph@digium.com>
Thu, 25 Oct 2018 12:37:49 +0000 (07:37 -0500)
committerGerrit Code Review <gerrit2@gerrit.digium.api>
Thu, 25 Oct 2018 12:37:49 +0000 (07:37 -0500)
19 files changed:
UPGRADE.txt
configs/samples/modules.conf.sample
configs/samples/pjsip.conf.sample
configure
configure.ac
contrib/ast-db-manage/config/versions/465f47f880be_add_pjsip_google_voice_sip_options.py [new file with mode: 0644]
funcs/func_callerid.c
include/asterisk/autoconfig.h.in
include/asterisk/res_pjsip.h
res/res_pjsip.c
res/res_pjsip/config_auth.c
res/res_pjsip/config_transport.c
res/res_pjsip_outbound_authenticator_digest.c
res/res_pjsip_outbound_publish.c
res/res_pjsip_outbound_registration.c
res/res_pjsip_session.c
third-party/pjproject/configure.m4
third-party/pjproject/patches/0020-oauth.patch [new file with mode: 0644]
third-party/pjproject/patches/0030-allow-disabling-of-connection-reuse.patch [new file with mode: 0644]

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