Merge "chan_sip.c: Validation on module reload"
authorMatt Jordan <mjordan@digium.com>
Fri, 11 Sep 2015 17:40:43 +0000 (12:40 -0500)
committerGerrit Code Review <gerrit2@gerrit.digium.api>
Fri, 11 Sep 2015 17:40:43 +0000 (12:40 -0500)
30 files changed:
CHANGES
README
README-SERIOUSLY.bestpractices.txt
README-addons.txt
addons/chan_ooh323.c
addons/ooh323c/src/ooq931.c
channels/chan_pjsip.c
channels/chan_sip.c
channels/pjsip/dialplan_functions.c
contrib/ast-db-manage/config/versions/154177371065_add_default_from_user.py [new file with mode: 0644]
include/asterisk/res_pjsip.h
include/asterisk/res_pjsip_session.h
main/bridge_channel.c
main/endpoints.c
main/pbx.c
main/sched.c
main/taskprocessor.c
main/utils.c
res/parking/parking_applications.c
res/res_pjsip.c
res/res_pjsip/config_global.c
res/res_pjsip/location.c
res/res_pjsip/pjsip_configuration.c
res/res_pjsip_multihomed.c
res/res_pjsip_pubsub.c
res/res_pjsip_sdp_rtp.c
res/res_pjsip_session.c
res/res_pjsip_t38.c
res/res_pjsip_transport_websocket.c
res/res_rtp_asterisk.c

diff --git a/CHANGES b/CHANGES
index 322419d..8c8329c 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -177,6 +177,18 @@ cdr_adaptive_odbc
    names. This setting is configurable for cdr_adaptive_odbc via the
    quoted_identifiers in configuration file cdr_adaptive_odbc.conf.
 
+
+------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 13.5.0 to Asterisk 13.6.0 ------------
+------------------------------------------------------------------------------
+
+Dialplan Functions
+------------------
+ * The CHANNEL function, when used on a PJSIP channel, now exposes a 'call-id'
+   extraction option when using with the 'pjsip' signalling option. It will
+   return the SIP Call-ID associated with the INVITE request that established
+   the PJSIP channel.
+
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 13.4.0 to Asterisk 13.5.0 ------------
 ------------------------------------------------------------------------------
diff --git a/README b/README
index ebd0712..d5f86c1 100644 (file)
--- a/README
+++ b/README
@@ -105,7 +105,7 @@ libraries are being looked for, see ./configure --help, or run
 "make menuselect" to view the dependencies for specific modules.
 
   On many distributions, these dependencies are installed by packages with names
-like 'glibc-devel', 'ncurses-devel', 'openssl-devel' and 'zlib-devel' 
+like 'glibc-devel', 'ncurses-devel', 'openssl-devel' and 'zlib-devel'
 or similar.
 
   So, let's proceed:
@@ -174,7 +174,7 @@ delimited by ';' (since '#' of course, being a DTMF digit, may occur in
 many places).  A configuration file is divided into sections whose names
 appear in []'s.  Each section typically contains two types of statements,
 those of the form 'variable = value', and those of the form 'object =>
-parameters'.  Internally the use of '=' and '=>' is exactly the same, so 
+parameters'.  Internally the use of '=' and '=>' is exactly the same, so
 they're used only to help make the configuration file easier to
 understand, and do not affect how it is actually parsed.
 
@@ -197,7 +197,7 @@ configuration file read:
   The "national" switchtype would be applied to channels one through
 four and channels 10 through 12, whereas the "dms100" switchtype would
 apply to channels 25 through 47.
-  
+
   The "object => parameters" instantiates an object with the given
 parameters.  For example, the line "channel => 25-47" creates objects for
 the channels 25 through 47 of the card, obtaining the settings
@@ -206,7 +206,7 @@ from the variables specified above.
 
 -------------------------------------------------------------------------------
 --- SPECIAL NOTE ON TIME ------------------------------------------------------
-  
+
   Those using SIP phones should be aware that Asterisk is sensitive to
 large jumps in time.  Manually changing the system time using date(1)
 (or other similar commands) may cause SIP registrations and other
index 108adce..b6b418d 100644 (file)
@@ -4,23 +4,23 @@
 
 The purpose of this document is to define best practices when working with
 Asterisk in order to minimize possible security breaches and to provide tried
-examples in field deployments. This is a living document and is subject to 
+examples in field deployments. This is a living document and is subject to
 change over time as best practices are defined.
 
 --------
 Sections
 --------
 
-* Filtering Data: 
+* Filtering Data:
         How to protect yourself from redial attacks
 
-* Proper Device Naming: 
+* Proper Device Naming:
         Why to not use numbered extensions for devices
 
-* Secure Passwords: 
+* Secure Passwords:
         Secure passwords limit your risk to brute force attacks
 
-* Reducing Pattern Match Typos: 
+* Reducing Pattern Match Typos:
         Using the 'same' prefix, or using Goto()
 
 * Manager Class Authorizations:
@@ -47,9 +47,9 @@ security are listed below.
 Filtering Data
 ==============
 
-In the Asterisk dialplan, several channel variables contain data potentially 
-supplied by outside sources. This could lead to a potential security concern 
-where those outside sources may send cleverly crafted strings of data which 
+In the Asterisk dialplan, several channel variables contain data potentially
+supplied by outside sources. This could lead to a potential security concern
+where those outside sources may send cleverly crafted strings of data which
 could be utilized, e.g. to place calls to unexpected locations.
 
 An example of this can be found in the use of pattern matching and the ${EXTEN}
@@ -57,14 +57,14 @@ channel variable. Note that ${EXTEN} is not the only system created channel
 variable, so it is important to be aware of where the data you're using is
 coming from.
 
-For example, this common dialplan takes 2 or more characters of data, starting 
+For example, this common dialplan takes 2 or more characters of data, starting
 with a number 0-9, and then accepts any additional information supplied by the
 request.
 
 [NOTE: We use SIP in this example, but is not limited to SIP only; protocols
        such as Jabber/XMPP or IAX2 are also susceptible to the same sort of
        injection problem.]
-       
+
 
 [incoming]
 exten => _X.,1,Verbose(2,Incoming call to extension ${EXTEN})
@@ -83,7 +83,7 @@ to dial extension 500 (which in our example above would create the string
 SIP/500 and is then used by the Dial() application to place a call), someone
 could potentially send a string like "500&SIP/itsp/14165551212".
 
-The string "500&SIP/itsp/14165551212" would then be contained within the 
+The string "500&SIP/itsp/14165551212" would then be contained within the
 ${EXTEN} channel variable, which is then utilized by the Dial() application in
 our example, thereby giving you the dialplan line of:
 
@@ -98,7 +98,7 @@ Strict Pattern Matching
 -----------------------
 
 The simple way to mitigate this problem is with a strict pattern match that does
-not utilize the period (.) or bang (!) characters to match on one-or-more 
+not utilize the period (.) or bang (!) characters to match on one-or-more
 characters or zero-or-more characters (respectively). To fine tune our example
 to only accept three digit extensions, we could change our pattern match to
 be:
@@ -121,8 +121,8 @@ application which will contain dynamic information passed to Asterisk from an
 external source. Lets take a look at how we can use FILTER() to control what
 data we allow.
 
-Using our previous example to accept any string length of 2 or more characters, 
-starting with a number of zero through nine, we can use FILTER() to limit what 
+Using our previous example to accept any string length of 2 or more characters,
+starting with a number of zero through nine, we can use FILTER() to limit what
 we will accept to just numbers. Our example would then change to something like:
 
 [incoming]
@@ -234,21 +234,21 @@ first ones added to the dictionary for brute force attacks.
 Secure Passwords
 ================
 
-Secure passwords are necessary in many (if not all) environments, and Asterisk 
+Secure passwords are necessary in many (if not all) environments, and Asterisk
 is certainly no exception, especially when it comes to expensive long distance
 calls that could potentially cost your company hundreds or thousands of dollars
 on an expensive monthly phone bill, with little to no recourse to fight the
 charges.
 
 Whenever you are positioned to add a password to your system, whether that is
-for a device configuration, a database connection, or any other secure 
+for a device configuration, a database connection, or any other secure
 connection, be sure to use a secure password. A good example of a secure
 password would be something like:
 
 aE3%B8*$jk^G
 
 Our password also contains 12 characters with a mixture of upper and
-lower case characters, numbers, and symbols. Because these passwords are likely 
+lower case characters, numbers, and symbols. Because these passwords are likely
 to only be entered once, or loaded via a configuration file, there is
 no need to create simple passwords, even in testing. Some of the holes found in
 production systems used for exploitations involve finding the one test extension
index 4d5e198..9954115 100644 (file)
@@ -9,18 +9,18 @@ potential licensing and/or patent implications that has on your usage and
 distribution of Asterisk.
 
     Even though Asterisk is released as open source under the terms of the
-GPLv2 (see LICENSE for details), no core functionality in Asterisk has any 
+GPLv2 (see LICENSE for details), no core functionality in Asterisk has any
 dependencies on libraries that are licensed under the GPL.  One reason a module
 may be in the add-ons category is that it may have a GPL dependency.  Since
 these dependencies are not compatible with dual licensing of Asterisk, the
-dependant modules are set aside to make it clear that they may not be used 
-with commercial versions of Asterisk, unless other licensing arrangements are 
+dependant modules are set aside to make it clear that they may not be used
+with commercial versions of Asterisk, unless other licensing arrangements are
 made with the copyright holders of those dependencies.
 
     Another reason that modules may be set aside is that there may be
 additional restrictions on the usage of the code imposed by the license or
 related patents.  The MySQL and MP3 modules are examples of this.
-       
+
     If you have any questions, contact your lawyer.
 
 ===============================================================================
index 58db56f..aa9cbaf 100644 (file)
@@ -4115,6 +4115,7 @@ int ooh323_destroy(struct ooh323_pvt *p)
                }
 
                if (cur->rtp) {
+                       ast_rtp_instance_stop(cur->rtp);
                        ast_rtp_instance_destroy(cur->rtp);
                        cur->rtp = NULL;
                }
index 86ecd92..cbc4afb 100644 (file)
@@ -758,14 +758,14 @@ int ooEncodeH225Message(OOH323CallData *call, Q931Message *pq931Msg,
       i += pq931Msg->causeIE->length;
    } 
       
-   /*Add progress indicator IE 
-   if(pq931Msg->messageType == Q931AlertingMsg || pq931Msg->messageType == Q931CallProceedingMsg)
+   /* Add progress indicator IE */
+   if(pq931Msg->messageType == Q931AlertingMsg || pq931Msg->messageType == Q931ProgressMsg)
    {
       msgbuf[i++] = Q931ProgressIndicatorIE;
       msgbuf[i++] = 2; //Length is 2 octet
       msgbuf[i++] = 0x80; //PI=8
       msgbuf[i++] = 0x88;
-  }*/
+   }
 
    /*Add display ie. for all but Status message as per ASTERISK-18748 */
    if(!ooUtilsIsStrEmpty(call->ourCallerId) && (pq931Msg->messageType != Q931StatusMsg))
index d3ba0a2..9603f05 100644 (file)
@@ -1811,9 +1811,17 @@ static int hangup(void *data)
 static int chan_pjsip_hangup(struct ast_channel *ast)
 {
        struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(ast);
-       struct chan_pjsip_pvt *pvt = channel->pvt;
-       int cause = hangup_cause2sip(ast_channel_hangupcause(channel->session->channel));
-       struct hangup_data *h_data = hangup_data_alloc(cause, ast);
+       struct chan_pjsip_pvt *pvt;
+       int cause;
+       struct hangup_data *h_data;
+
+       if (!channel || !channel->session) {
+               return -1;
+       }
+
+       pvt = channel->pvt;
+       cause = hangup_cause2sip(ast_channel_hangupcause(channel->session->channel));
+       h_data = hangup_data_alloc(cause, ast);
 
        if (!h_data) {
                goto failure;
index b4b3d02..f96a80f 100644 (file)
@@ -24527,10 +24527,9 @@ static void *sip_pickup_thread(void *stuff)
        struct ast_channel *chan;
        chan = stuff;
 
+       ast_channel_hangupcause_set(chan, AST_CAUSE_NORMAL_CLEARING);
        if (ast_pickup_call(chan)) {
                ast_channel_hangupcause_set(chan, AST_CAUSE_CALL_REJECTED);
-       } else {
-               ast_channel_hangupcause_set(chan, AST_CAUSE_NORMAL_CLEARING);
        }
        ast_hangup(chan);
        ast_channel_unref(chan);
index 24daac8..b86cfad 100644 (file)
                                <literal>type</literal> parameter must be provided. It specifies
                                which signalling parameter to read.</para>
                                <enumlist>
+                                       <enum name="call-id">
+                                               <para>The SIP call-id.</para>
+                                       </enum>
                                        <enum name="secure">
                                                <para>Whether or not the signalling uses a secure transport.</para>
                                                <enumlist>
@@ -594,6 +597,8 @@ static int channel_read_pjsip(struct ast_channel *chan, const char *type, const
        if (ast_strlen_zero(type)) {
                ast_log(LOG_WARNING, "You must supply a type field for 'pjsip' information\n");
                return -1;
+       } else if (!strcmp(type, "call-id")) {
+               snprintf(buf, buflen, "%.*s", (int) pj_strlen(&dlg->call_id->id), pj_strbuf(&dlg->call_id->id));
        } else if (!strcmp(type, "secure")) {
 #ifdef HAVE_PJSIP_GET_DEST_INFO
                pjsip_host_info dest;
diff --git a/contrib/ast-db-manage/config/versions/154177371065_add_default_from_user.py b/contrib/ast-db-manage/config/versions/154177371065_add_default_from_user.py
new file mode 100644 (file)
index 0000000..7e6cf99
--- /dev/null
@@ -0,0 +1,22 @@
+"""add default_from_user
+
+Revision ID: 154177371065
+Revises: 26f10cadc157
+Create Date: 2015-09-04 14:13:59.195013
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '154177371065'
+down_revision = '26f10cadc157'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    op.add_column('ps_globals', sa.Column('default_from_user', sa.String(80)))
+
+
+def downgrade():
+    op.drop_column('ps_globals', 'default_from_user')
index decb417..29edbd2 100644 (file)
@@ -2018,6 +2018,19 @@ char *ast_sip_get_debug(void);
  */
 char *ast_sip_get_endpoint_identifier_order(void);
 
+/*!
+ * \brief Retrieve the global default from user.
+ *
+ * This is the value placed in outbound requests' From header if there
+ * is no better option (such as an endpoint-configured from_user or
+ * caller ID number).
+ *
+ * \param[out] from_user The default from user
+ * \param size The buffer size of from_user
+ * \return nothing
+ */
+void ast_sip_get_default_from_user(char *from_user, size_t size);
+
 /*! \brief Determines whether the res_pjsip module is loaded */
 #define CHECK_PJSIP_MODULE_LOADED()                            \
        do {                                                    \
@@ -2059,4 +2072,31 @@ const char *ast_sip_get_contact_short_status_label(const enum ast_sip_contact_st
  */
 int ast_sip_failover_request(pjsip_tx_data *tdata);
 
+/*
+ * \brief Retrieve the local host address in IP form
+ *
+ * \param af The address family to retrieve
+ * \param addr A place to store the local host address
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \since 13.6.0
+ */
+int ast_sip_get_host_ip(int af, pj_sockaddr *addr);
+
+/*!
+ * \brief Retrieve the local host address in string form
+ *
+ * \param af The address family to retrieve
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ *
+ * \since 13.6.0
+ *
+ * \note An empty string may be returned if the address family is valid but no local address exists
+ */
+const char *ast_sip_get_host_ip_string(int af);
+
 #endif /* _RES_PJSIP_H */
index 734a887..6139847 100644 (file)
@@ -357,6 +357,12 @@ struct ast_sip_session_sdp_handler {
        int (*apply_negotiated_sdp_stream)(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *local, const struct pjmedia_sdp_media *local_stream,
                const struct pjmedia_sdp_session *remote, const struct pjmedia_sdp_media *remote_stream);
        /*!
+        * \brief Stop a session_media created by this handler but do not destroy resources
+        * \param session The session for which media is being stopped
+        * \param session_media The media to destroy
+        */
+       void (*stream_stop)(struct ast_sip_session_media *session_media);
+       /*!
         * \brief Destroy a session_media created by this handler
         * \param session The session for which media is being destroyed
         * \param session_media The media to destroy
index 28dfd9c..be0819a 100644 (file)
@@ -1914,6 +1914,13 @@ static void bridge_channel_handle_action(struct ast_bridge_channel *bridge_chann
        default:
                break;
        }
+
+       /* While invoking an action it is possible for the channel to be hung up. So
+        * that the bridge respects this we check here and if hung up kick it out.
+        */
+       if (bridge_channel->chan && ast_check_hangup_locked(bridge_channel->chan)) {
+               ast_bridge_channel_kick(bridge_channel, 0);
+       }
 }
 
 /*!
index df9d289..2132656 100644 (file)
@@ -464,7 +464,7 @@ static void endpoint_snapshot_dtor(void *obj)
 struct ast_endpoint_snapshot *ast_endpoint_snapshot_create(
        struct ast_endpoint *endpoint)
 {
-       RAII_VAR(struct ast_endpoint_snapshot *, snapshot, NULL, ao2_cleanup);
+       struct ast_endpoint_snapshot *snapshot;
        int channel_count;
        struct ao2_iterator i;
        void *obj;
@@ -500,7 +500,6 @@ struct ast_endpoint_snapshot *ast_endpoint_snapshot_create(
        }
        ao2_iterator_destroy(&i);
 
-       ao2_ref(snapshot, +1);
        return snapshot;
 }
 
index fcf0aa6..5e4f0a4 100644 (file)
@@ -7521,7 +7521,7 @@ static char *handle_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_
                        continue;
                }
                watchers = ao2_container_count(hint->callbacks);
-               sprintf(buf, "%s@%s",
+               snprintf(buf, sizeof(buf), "%s@%s",
                        ast_get_extension_name(hint->exten),
                        ast_get_context_name(ast_get_extension_context(hint->exten)));
 
index d50a31e..062b2fd 100644 (file)
@@ -497,7 +497,9 @@ int _ast_sched_del(struct ast_sched_context *con, int id, const char *file, int
                /* Wait for executing task to complete so that caller of ast_sched_del() does not
                 * free memory out from under the task.
                 */
-               ast_cond_wait(&s->cond, &con->lock);
+               while (con->currently_executing && (id == con->currently_executing->id)) {
+                       ast_cond_wait(&s->cond, &con->lock);
+               }
                /* Do not sched_release() here because ast_sched_runq() will do it */
        }
 
index 1edbaa3..5c513ee 100644 (file)
@@ -691,15 +691,25 @@ void *ast_taskprocessor_unreference(struct ast_taskprocessor *tps)
                return NULL;
        }
 
+       /* To prevent another thread from finding and getting a reference to this
+        * taskprocessor we hold the singletons lock. If we didn't do this then
+        * they may acquire it and find that the listener has been shut down.
+        */
+       ao2_lock(tps_singletons);
+
        if (ao2_ref(tps, -1) > 3) {
+               ao2_unlock(tps_singletons);
                return NULL;
        }
+
        /* If we're down to 3 references, then those must be:
         * 1. The reference we just got rid of
         * 2. The container
         * 3. The listener
         */
-       ao2_unlink(tps_singletons, tps);
+       ao2_unlink_flags(tps_singletons, tps, OBJ_NOLOCK);
+       ao2_unlock(tps_singletons);
+
        listener_shutdown(tps->listener);
        return NULL;
 }
index 4bcfa33..ba1a07c 100644 (file)
@@ -1251,8 +1251,8 @@ int ast_pthread_create_stack(pthread_t *thread, pthread_attr_t *attr, void *(*st
                pthread_attr_init(attr);
        }
 
-#ifdef __linux__
-       /* On Linux, pthread_attr_init() defaults to PTHREAD_EXPLICIT_SCHED,
+#if defined(__linux__) || defined(__FreeBSD__)
+       /* On Linux and FreeBSD , pthread_attr_init() defaults to PTHREAD_EXPLICIT_SCHED,
           which is kind of useless. Change this here to
           PTHREAD_INHERIT_SCHED; that way the -p option to set realtime
           priority will propagate down to new threads by default.
index 34b081b..0a0ea3c 100644 (file)
@@ -694,6 +694,68 @@ static struct park_announce_subscription_data *park_announce_subscription_data_c
        return pa_data;
 }
 
+/*! \internal
+ * \brief Gathers inheritable channel variables from a channel by name.
+ *
+ * \param oh outgoing helper struct we are bestowing inheritable variables to
+ * \param channel_id name or uniqueID of the channel to inherit variables from
+ *
+ * \return Nothing
+ */
+static void inherit_channel_vars_from_id(struct outgoing_helper *oh, const char *channel_id)
+{
+       struct ast_channel *chan = ast_channel_get_by_name(channel_id);
+       struct ast_var_t *current;
+       struct ast_variable *newvar;
+       const char *varname;
+       int vartype;
+
+
+       if (!chan) {
+               /* Already gone */
+               return;
+       }
+
+       ast_channel_lock(chan);
+
+       AST_LIST_TRAVERSE(ast_channel_varshead((struct ast_channel *) chan), current, entries) {
+               varname = ast_var_full_name(current);
+               if (!varname) {
+                       continue;
+               }
+
+               vartype = 0;
+               if (varname[0] == '_') {
+                       vartype = 1;
+                       if (varname[1] == '_') {
+                               vartype = 2;
+                       }
+               }
+
+               switch (vartype) {
+               case 1:
+                       newvar = ast_variable_new(&varname[1], ast_var_value(current), "");
+                       break;
+               case 2:
+                       newvar = ast_variable_new(varname, ast_var_value(current), "");
+                       break;
+               default:
+                       continue;
+               }
+               if (newvar) {
+                       ast_debug(1, "Inheriting variable %s from %s.\n",
+                               newvar->name, ast_channel_name(chan));
+                       if (oh->vars) {
+                               newvar->next = oh->vars;
+                               oh->vars = newvar;
+                       }
+               }
+       }
+
+       ast_channel_unlock(chan);
+       ast_channel_cleanup(chan);
+}
+
 static void announce_to_dial(char *dial_string, char *announce_string, int parkingspace, struct ast_channel_snapshot *parkee_snapshot)
 {
        struct ast_channel *dchan;
@@ -715,6 +777,9 @@ static void announce_to_dial(char *dial_string, char *announce_string, int parki
 
        snprintf(buf, sizeof(buf), "%d", parkingspace);
        oh.vars = ast_variable_new("_PARKEDAT", buf, "");
+
+       inherit_channel_vars_from_id(&oh, parkee_snapshot->uniqueid);
+
        dchan = __ast_request_and_dial(dial_tech, cap_slin, NULL, NULL, dial_string, 30000,
                &outstate,
                parkee_snapshot->caller_number,
index 04e16c4..3e93b6f 100644 (file)
                                         Identifier names are usually derived from and can be found in the endpoint
                                         identifier module itself (res_pjsip_endpoint_identifier_*)</synopsis>
                                </configOption>
+                               <configOption name="default_from_user" default="asterisk">
+                                       <synopsis>When Asterisk generates an outgoing SIP request, the From header username will be
+                                        set to this value if there is no better option (such as CallerID) to be
+                                        used.</synopsis>
+                               </configOption>
                        </configObject>
                </configFile>
        </configInfo>
@@ -1912,6 +1917,18 @@ static pjsip_endpoint *ast_pjsip_endpoint;
 
 static struct ast_threadpool *sip_threadpool;
 
+/*! Local host address for IPv4 */
+static pj_sockaddr host_ip_ipv4;
+
+/*! Local host address for IPv4 (string form) */
+static char host_ip_ipv4_string[PJ_INET6_ADDRSTRLEN + 2];
+
+/*! Local host address for IPv6 */
+static pj_sockaddr host_ip_ipv6;
+
+/*! Local host address for IPv6 (string form) */
+static char host_ip_ipv6_string[PJ_INET6_ADDRSTRLEN + 2];
+
 static int register_service_noref(void *data)
 {
        pjsip_module **module = data;
@@ -2321,10 +2338,11 @@ static int sip_dialog_create_from(pj_pool_t *pool, pj_str_t *from, const char *u
        pjsip_sip_uri *sip_uri;
        pjsip_transport_type_e type = PJSIP_TRANSPORT_UNSPECIFIED;
        int local_port;
-       char uuid_str[AST_UUID_STR_LEN];
+       char default_user[PJSIP_MAX_URL_SIZE];
 
        if (ast_strlen_zero(user)) {
-               user = ast_uuid_generate_str(uuid_str, sizeof(uuid_str));
+               ast_sip_get_default_from_user(default_user, sizeof(default_user));
+               user = default_user;
        }
 
        /* Parse the provided target URI so we can determine what transport it will end up using */
@@ -3724,6 +3742,14 @@ int ast_sip_send_stateful_response(pjsip_rx_data *rdata, pjsip_tx_data *tdata, s
        pjsip_transaction *tsx;
 
        if (pjsip_tsx_create_uas(NULL, rdata, &tsx) != PJ_SUCCESS) {
+               struct ast_sip_contact *contact;
+
+               /* ast_sip_create_response bumps the refcount of the contact and adds it to the tdata.
+                * We'll leak that reference if we don't get rid of it here.
+                */
+               contact = ast_sip_mod_data_get(tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT);
+               ao2_cleanup(contact);
+               ast_sip_mod_data_set(tdata->pool, tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT, NULL);
                pjsip_tx_data_dec_ref(tdata);
                return -1;
        }
@@ -3751,6 +3777,30 @@ int ast_sip_create_response(const pjsip_rx_data *rdata, int st_code,
        return res;
 }
 
+int ast_sip_get_host_ip(int af, pj_sockaddr *addr)
+{
+       if (af == pj_AF_INET() && !ast_strlen_zero(host_ip_ipv4_string)) {
+               pj_sockaddr_copy_addr(addr, &host_ip_ipv4);
+               return 0;
+       } else if (af == pj_AF_INET6() && !ast_strlen_zero(host_ip_ipv6_string)) {
+               pj_sockaddr_copy_addr(addr, &host_ip_ipv6);
+               return 0;
+       }
+
+       return -1;
+}
+
+const char *ast_sip_get_host_ip_string(int af)
+{
+       if (af == pj_AF_INET()) {
+               return host_ip_ipv4_string;
+       } else if (af == pj_AF_INET6()) {
+               return host_ip_ipv6_string;
+       }
+
+       return NULL;
+}
+
 static void remove_request_headers(pjsip_endpoint *endpt)
 {
        const pjsip_hdr *request_headers = pjsip_endpt_get_request_headers(endpt);
@@ -3865,6 +3915,16 @@ static int load_module(void)
                return AST_MODULE_LOAD_DECLINE;
        }
 
+       if (!pj_gethostip(pj_AF_INET(), &host_ip_ipv4)) {
+               pj_sockaddr_print(&host_ip_ipv4, host_ip_ipv4_string, sizeof(host_ip_ipv4_string), 2);
+               ast_verb(3, "Local IPv4 address determined to be: %s\n", host_ip_ipv4_string);
+       }
+
+       if (!pj_gethostip(pj_AF_INET6(), &host_ip_ipv6)) {
+               pj_sockaddr_print(&host_ip_ipv6, host_ip_ipv6_string, sizeof(host_ip_ipv6_string), 2);
+               ast_verb(3, "Local IPv6 address determined to be: %s\n", host_ip_ipv6_string);
+       }
+
        if (ast_sip_initialize_system()) {
                ast_log(LOG_ERROR, "Failed to initialize SIP 'system' configuration section. Aborting load\n");
                pj_pool_release(memory_pool);
index 42ba234..ef706f0 100644 (file)
@@ -34,6 +34,7 @@
 #define DEFAULT_DEBUG "no"
 #define DEFAULT_ENDPOINT_IDENTIFIER_ORDER "ip,username,anonymous"
 #define DEFAULT_MAX_INITIAL_QUALIFY_TIME 0
+#define DEFAULT_FROM_USER "asterisk"
 
 static char default_useragent[256];
 
@@ -46,6 +47,8 @@ struct global_config {
                AST_STRING_FIELD(debug);
                /*! Order by which endpoint identifiers are checked (comma separated list) */
                AST_STRING_FIELD(endpoint_identifier_order);
+               /*! User name to place in From header if there is no better option */
+               AST_STRING_FIELD(default_from_user);
        );
        /* Value to put in Max-Forwards header */
        unsigned int max_forwards;
@@ -179,6 +182,19 @@ unsigned int ast_sip_get_max_initial_qualify_time(void)
        return time;
 }
 
+void ast_sip_get_default_from_user(char *from_user, size_t size)
+{
+       struct global_config *cfg;
+
+       cfg = get_global_cfg();
+       if (!cfg) {
+               ast_copy_string(from_user, DEFAULT_FROM_USER, size);
+       } else {
+               ast_copy_string(from_user, cfg->default_from_user, size);
+               ao2_ref(cfg, -1);
+       }
+}
+
 /*!
  * \internal
  * \brief Observer to set default global object if none exist.
@@ -292,6 +308,8 @@ int ast_sip_initialize_sorcery_global(void)
        ast_sorcery_object_field_register(sorcery, "global", "max_initial_qualify_time",
                __stringify(DEFAULT_MAX_INITIAL_QUALIFY_TIME),
                OPT_UINT_T, 0, FLDSET(struct global_config, max_initial_qualify_time));
+       ast_sorcery_object_field_register(sorcery, "global", "default_from_user", DEFAULT_FROM_USER,
+               OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, default_from_user));
 
        if (ast_sorcery_instance_observer_add(sorcery, &observer_callbacks_global)) {
                return -1;
index d87410d..9625f04 100644 (file)
@@ -47,6 +47,41 @@ static void *aor_alloc(const char *name)
        return aor;
 }
 
+/*! \brief Internal callback function which destroys the specified contact */
+static int destroy_contact(void *obj, void *arg, int flags)
+{
+       struct ast_sip_contact *contact = obj;
+
+       ast_sip_location_delete_contact(contact);
+
+       return CMP_MATCH;
+}
+
+static void aor_deleted_observer(const void *object)
+{
+       const char *aor_id = ast_sorcery_object_get_id(object);
+       /* Give enough space for ^ at the beginning and ;@ at the end, since that is our object naming scheme */
+       char regex[strlen(aor_id) + 4];
+       struct ao2_container *contacts;
+
+       snprintf(regex, sizeof(regex), "^%s;@", aor_id);
+
+       if (!(contacts = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "contact", regex))) {
+               return;
+       }
+
+       /* Destroy any contacts that may still exist that were made for this AoR */
+       ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, destroy_contact, NULL);
+
+       ao2_ref(contacts, -1);
+}
+
+/*! \brief Observer for contacts so state can be updated on respective endpoints */
+static const struct ast_sorcery_observer aor_observer = {
+       .deleted = aor_deleted_observer,
+};
+
+
 /*! \brief Destructor for contact */
 static void contact_destroy(void *obj)
 {
@@ -102,12 +137,6 @@ static int contact_link_static(void *obj, void *arg, int flags)
        return 0;
 }
 
-/*! \brief Simple callback function which returns immediately, used to grab the first contact of an AOR */
-static int contact_find_first(void *obj, void *arg, int flags)
-{
-       return CMP_MATCH | CMP_STOP;
-}
-
 struct ast_sip_contact *ast_sip_location_retrieve_first_aor_contact(const struct ast_sip_aor *aor)
 {
        RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
@@ -118,7 +147,8 @@ struct ast_sip_contact *ast_sip_location_retrieve_first_aor_contact(const struct
                return NULL;
        }
 
-       contact = ao2_callback(contacts, 0, contact_find_first, NULL);
+       /* Get the first AOR contact in the container. */
+       contact = ao2_callback(contacts, 0, NULL, NULL);
        return contact;
 }
 
@@ -234,8 +264,10 @@ int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri,
 {
        char name[MAX_OBJECT_FIELD * 2 + 3];
        RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
+       char hash[33];
 
-       snprintf(name, sizeof(name), "%s;@%s", ast_sorcery_object_get_id(aor), uri);
+       ast_md5_hash(hash, uri);
+       snprintf(name, sizeof(name), "%s;@%s", ast_sorcery_object_get_id(aor), hash);
 
        if (!(contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", name))) {
                return -1;
@@ -910,6 +942,8 @@ int ast_sip_initialize_sorcery_location(void)
                return -1;
        }
 
+       ast_sorcery_observer_add(sorcery, "aor", &aor_observer);
+
        ast_sorcery_object_field_register(sorcery, "contact", "type", "", OPT_NOOP_T, 0, 0);
        ast_sorcery_object_field_register(sorcery, "contact", "uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, uri));
        ast_sorcery_object_field_register(sorcery, "contact", "path", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, path));
@@ -971,6 +1005,7 @@ int ast_sip_initialize_sorcery_location(void)
 
 int ast_sip_destroy_sorcery_location(void)
 {
+       ast_sorcery_observer_remove(ast_sip_get_sorcery(), "aor", &aor_observer);
        ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
        ast_sip_unregister_cli_formatter(contact_formatter);
        ast_sip_unregister_cli_formatter(aor_formatter);
index 64ffe15..cfb17cd 100644 (file)
@@ -402,6 +402,10 @@ int ast_sip_auth_vector_init(struct ast_sip_auth_vector *auths, const char *valu
        }
 
        while ((val = strsep(&auth_names, ","))) {
+               if (ast_strlen_zero(val)) {
+                       continue;
+               }
+
                val = ast_strdup(val);
                if (!val) {
                        goto failure;
index 68a4311..7062fc6 100644 (file)
 #include "asterisk/res_pjsip.h"
 #include "asterisk/module.h"
 
-/*! \brief Local host address for IPv4 */
-static char host_ipv4[PJ_INET_ADDRSTRLEN + 2];
-
-/*! \brief Local host address for IPv6 */
-static char host_ipv6[PJ_INET6_ADDRSTRLEN + 2];
-
 /*! \brief Helper function which returns a UDP transport bound to the given address and port */
 static pjsip_transport *multihomed_get_udp_transport(pj_str_t *address, int port)
 {
@@ -75,8 +69,10 @@ static int multihomed_rewrite_sdp(struct pjmedia_sdp_session *sdp)
        }
 
        /* If the host address is used in the SDP replace it with the address of what this is going out on */
-       if ((!pj_strcmp2(&sdp->conn->addr_type, "IP4") && !pj_strcmp2(&sdp->conn->addr, host_ipv4)) ||
-               (!pj_strcmp2(&sdp->conn->addr_type, "IP6") && !pj_strcmp2(&sdp->conn->addr, host_ipv6))) {
+       if ((!pj_strcmp2(&sdp->conn->addr_type, "IP4") && !pj_strcmp2(&sdp->conn->addr,
+               ast_sip_get_host_ip_string(pj_AF_INET()))) ||
+               (!pj_strcmp2(&sdp->conn->addr_type, "IP6") && !pj_strcmp2(&sdp->conn->addr,
+               ast_sip_get_host_ip_string(pj_AF_INET6())))) {
                return 1;
        }
 
@@ -204,7 +200,6 @@ static int unload_module(void)
 static int load_module(void)
 {
        char hostname[MAXHOSTNAMELEN] = "";
-       pj_sockaddr addr;
 
        CHECK_PJSIP_MODULE_LOADED();
 
@@ -213,16 +208,6 @@ static int load_module(void)
                        hostname);
        }
 
-       if (!pj_gethostip(pj_AF_INET(), &addr)) {
-               pj_sockaddr_print(&addr, host_ipv4, sizeof(host_ipv4), 2);
-               ast_verb(3, "Local IPv4 address determined to be: %s\n", host_ipv4);
-       }
-
-       if (!pj_gethostip(pj_AF_INET6(), &addr)) {
-               pj_sockaddr_print(&addr, host_ipv6, sizeof(host_ipv6), 2);
-               ast_verb(3, "Local IPv6 address determined to be: %s\n", host_ipv6);
-       }
-
        if (ast_sip_register_service(&multihomed_module)) {
                ast_log(LOG_ERROR, "Could not register multihomed module for incoming and outgoing requests\n");
                return AST_MODULE_LOAD_FAILURE;
index 57c85a4..e3b1024 100644 (file)
@@ -561,8 +561,19 @@ static void subscription_persistence_update(struct sip_subscription_tree *sub_tr
                expires = expires_hdr ? expires_hdr->ivalue : DEFAULT_PUBLISH_EXPIRES;
                sub_tree->persistence->expires = ast_tvadd(ast_tvnow(), ast_samp2tv(expires, 1));
 
-               pjsip_msg_print(rdata->msg_info.msg, sub_tree->persistence->packet,
-                               sizeof(sub_tree->persistence->packet));
+               /* When receiving a packet on an streaming transport, it's possible to receive more than one SIP
+                * message at a time into the rdata->pkt_info.packet buffer. However, the rdata->msg_info.msg_buf
+                * will always point to the proper SIP message that is to be processed. When updating subscription
+                * persistence that is pulled from persistent storage, though, the rdata->pkt_info.packet will
+                * only ever have a single SIP message on it, and so we base persistence on that.
+                */
+               if (rdata->msg_info.msg_buf) {
+                       ast_copy_string(sub_tree->persistence->packet, rdata->msg_info.msg_buf,
+                                       MIN(sizeof(sub_tree->persistence->packet), rdata->msg_info.len));
+               } else {
+                       ast_copy_string(sub_tree->persistence->packet, rdata->pkt_info.packet,
+                                       sizeof(sub_tree->persistence->packet));
+               }
                ast_copy_string(sub_tree->persistence->src_name, rdata->pkt_info.src_name,
                                sizeof(sub_tree->persistence->src_name));
                sub_tree->persistence->src_port = rdata->pkt_info.src_port;
@@ -971,6 +982,7 @@ static int build_resource_tree(struct ast_sip_endpoint *endpoint, const struct a
 
        tree->root = tree_node_alloc(resource, &visited, list->full_state);
        if (!tree->root) {
+               AST_VECTOR_FREE(&visited);
                return 500;
        }
 
@@ -1159,6 +1171,7 @@ static void shutdown_subscriptions(struct ast_sip_subscription *sub)
                return;
        }
 
+       /* We notify subscription shutdown only on the tree leaves. */
        if (sub->handler->subscription_shutdown) {
                sub->handler->subscription_shutdown(sub);
        }
@@ -1251,7 +1264,7 @@ static struct sip_subscription_tree *create_subscription_tree(const struct ast_s
 
        sub_tree = allocate_subscription_tree(endpoint);
        if (!sub_tree) {
-               pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
+               *dlg_status = PJ_ENOMEM;
                return NULL;
        }
        sub_tree->role = AST_SIP_NOTIFIER;
@@ -1347,7 +1360,7 @@ static int subscription_persistence_recreate(void *obj, void *arg, int flags)
 
        request_uri = pjsip_uri_get_uri(rdata.msg_info.msg->line.req.uri);
        resource_size = pj_strlen(&request_uri->user) + 1;
-       resource = alloca(resource_size);
+       resource = ast_alloca(resource_size);
        ast_copy_pj_str(resource, &request_uri->user, resource_size);
 
        /* Update the expiration header with the new expiration */
@@ -1393,8 +1406,9 @@ static int subscription_persistence_recreate(void *obj, void *arg, int flags)
                subscription_persistence_update(sub_tree, &rdata);
                if (generate_initial_notify(sub_tree->root)) {
                        pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
+               } else {
+                       send_notify(sub_tree, 1);
                }
-               send_notify(sub_tree, 1);
        } else {
                ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
        }
@@ -1514,6 +1528,7 @@ void *ast_sip_subscription_get_header(const struct ast_sip_subscription *sub, co
        return pjsip_msg_find_hdr_by_name(msg, &name, NULL);
 }
 
+/* XXX This function is not used. */
 struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_subscription_handler *handler,
                struct ast_sip_endpoint *endpoint, const char *resource)
 {
@@ -2510,6 +2525,7 @@ static int generate_initial_notify(struct ast_sip_subscription *sub)
                return 0;
        }
 
+       /* We notify subscription establishment only on the tree leaves. */
        if (sub->handler->notifier->subscription_established(sub)) {
                return -1;
        }
@@ -2566,7 +2582,7 @@ static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
 
        request_uri_sip = pjsip_uri_get_uri(request_uri);
        resource_size = pj_strlen(&request_uri_sip->user) + 1;
-       resource = alloca(resource_size);
+       resource = ast_alloca(resource_size);
        ast_copy_pj_str(resource, &request_uri_sip->user, resource_size);
 
        expires_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, rdata->msg_info.msg->hdr.next);
@@ -2783,7 +2799,7 @@ static struct ast_sip_publication *publish_request_initial(struct ast_sip_endpoi
 
        request_uri_sip = pjsip_uri_get_uri(request_uri);
        resource_size = pj_strlen(&request_uri_sip->user) + 1;
-       resource_name = alloca(resource_size);
+       resource_name = ast_alloca(resource_size);
        ast_copy_pj_str(resource_name, &request_uri_sip->user, resource_size);
 
        resource = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "inbound-publication", resource_name);
@@ -2983,16 +2999,13 @@ int ast_sip_pubsub_register_body_generator(struct ast_sip_pubsub_body_generator
        AST_LIST_INSERT_HEAD(&body_generators, generator, list);
        AST_RWLIST_UNLOCK(&body_generators);
 
-       /* Lengths of type and subtype plus space for a slash. pj_str_t is not
-        * null-terminated, so there is no need to allocate for the extra null
-        * byte
-        */
+       /* Lengths of type and subtype plus a slash. */
        accept_len = strlen(generator->type) + strlen(generator->subtype) + 1;
 
-       accept.ptr = alloca(accept_len);
-       accept.slen = accept_len;
-       /* Safe use of sprintf */
-       sprintf(accept.ptr, "%s/%s", generator->type, generator->subtype);
+       /* Add room for null terminator that sprintf() will set. */
+       pj_strset(&accept, ast_alloca(accept_len + 1), accept_len);
+       sprintf((char *) pj_strbuf(&accept), "%s/%s", generator->type, generator->subtype);/* Safe */
+
        pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), &pubsub_module,
                        PJSIP_H_ACCEPT, NULL, 1, &accept);
 
@@ -4258,6 +4271,7 @@ static int load_module(void)
        if (apply_list_configuration(sorcery)) {
                ast_sip_unregister_service(&pubsub_module);
                ast_sched_context_destroy(sched);
+               return AST_MODULE_LOAD_FAILURE;
        }
 
        ast_sorcery_apply_default(sorcery, "inbound-publication", "config", "pjsip.conf,criteria=type=inbound-publication");
index f1314f5..a66aebb 100644 (file)
@@ -611,6 +611,9 @@ static enum ast_sip_session_media_encryption get_media_encryption_type(pj_str_t
 
        *optimistic = 0;
 
+       if (!transport_str) {
+               return AST_SIP_MEDIA_TRANSPORT_INVALID;
+       }
        if (strstr(transport_str, "UDP/TLS")) {
                return AST_SIP_MEDIA_ENCRYPT_DTLS;
        } else if (strstr(transport_str, "SAVP")) {
@@ -1033,7 +1036,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
        static const pj_str_t STR_SENDRECV = { "sendrecv", 8 };
        static const pj_str_t STR_SENDONLY = { "sendonly", 8 };
        pjmedia_sdp_media *media;
-       char hostip[PJ_INET6_ADDRSTRLEN+2];
+       const char *hostip = NULL;
        struct ast_sockaddr addr;
        char tmp[512];
        pj_str_t stmp;
@@ -1081,16 +1084,16 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
 
        /* Add connection level details */
        if (direct_media_enabled) {
-               ast_copy_string(hostip, ast_sockaddr_stringify_fmt(&session_media->direct_media_addr, AST_SOCKADDR_STR_ADDR), sizeof(hostip));
+               hostip = ast_sockaddr_stringify_fmt(&session_media->direct_media_addr, AST_SOCKADDR_STR_ADDR);
        } else if (ast_strlen_zero(session->endpoint->media.address)) {
-               pj_sockaddr localaddr;
-
-               if (pj_gethostip(session->endpoint->media.rtp.ipv6 ? pj_AF_INET6() : pj_AF_INET(), &localaddr)) {
-                       return -1;
-               }
-               pj_sockaddr_print(&localaddr, hostip, sizeof(hostip), 2);
+               hostip = ast_sip_get_host_ip_string(session->endpoint->media.rtp.ipv6 ? pj_AF_INET6() : pj_AF_INET());
        } else {
-               ast_copy_string(hostip, session->endpoint->media.address, sizeof(hostip));
+               hostip = session->endpoint->media.address;
+       }
+
+       if (ast_strlen_zero(hostip)) {
+               ast_log(LOG_ERROR, "No local host IP available for stream %s\n", session_media->stream_type);
+               return -1;
        }
 
        media->conn->net_type = STR_IN;
@@ -1314,6 +1317,7 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
                 * a NAT. This way there won't be an awkward delay before media starts flowing in some
                 * scenarios.
                 */
+               AST_SCHED_DEL(sched, session_media->keepalive_sched_id);
                session_media->keepalive_sched_id = ast_sched_add_variable(sched, 500, send_keepalive,
                        session_media, 1);
        }
@@ -1365,13 +1369,23 @@ static void change_outgoing_sdp_stream_media_address(pjsip_tx_data *tdata, struc
        pj_strdup2(tdata->pool, &stream->conn->addr, transport->external_media_address);
 }
 
+/*! \brief Function which stops the RTP instance */
+static void stream_stop(struct ast_sip_session_media *session_media)
+{
+       if (!session_media->rtp) {
+               return;
+       }
+
+       AST_SCHED_DEL(sched, session_media->keepalive_sched_id);
+       AST_SCHED_DEL(sched, session_media->timeout_sched_id);
+       ast_rtp_instance_stop(session_media->rtp);
+}
+
 /*! \brief Function which destroys the RTP instance when session ends */
 static void stream_destroy(struct ast_sip_session_media *session_media)
 {
        if (session_media->rtp) {
-               AST_SCHED_DEL(sched, session_media->keepalive_sched_id);
-               AST_SCHED_DEL(sched, session_media->timeout_sched_id);
-               ast_rtp_instance_stop(session_media->rtp);
+               stream_stop(session_media);
                ast_rtp_instance_destroy(session_media->rtp);
        }
        session_media->rtp = NULL;
@@ -1384,6 +1398,7 @@ static struct ast_sip_session_sdp_handler audio_sdp_handler = {
        .create_outgoing_sdp_stream = create_outgoing_sdp_stream,
        .apply_negotiated_sdp_stream = apply_negotiated_sdp_stream,
        .change_outgoing_sdp_stream_media_address = change_outgoing_sdp_stream_media_address,
+       .stream_stop = stream_stop,
        .stream_destroy = stream_destroy,
 };
 
@@ -1394,6 +1409,7 @@ static struct ast_sip_session_sdp_handler video_sdp_handler = {
        .create_outgoing_sdp_stream = create_outgoing_sdp_stream,
        .apply_negotiated_sdp_stream = apply_negotiated_sdp_stream,
        .change_outgoing_sdp_stream_media_address = change_outgoing_sdp_stream_media_address,
+       .stream_stop = stream_stop,
        .stream_destroy = stream_destroy,
 };
 
index 625ab8b..fc87248 100644 (file)
@@ -362,6 +362,13 @@ static int handle_negotiated_sdp_session_media(void *obj, void *arg, int flags)
                        }
                }
        }
+
+       if (session_media->handler && session_media->handler->stream_stop) {
+               ast_debug(1, "Stopping SDP media stream '%s' as it is not currently negotiated\n",
+                       session_media->stream_type);
+               session_media->handler->stream_stop(session_media);
+       }
+
        return CMP_MATCH;
 }
 
@@ -1290,6 +1297,11 @@ struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint,
        session->contact = ao2_bump(contact);
        session->inv_session = inv_session;
        session->req_caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+       if (!session->req_caps) {
+               /* Release the ref held by session->inv_session */
+               ao2_ref(session, -1);
+               return NULL;
+       }
 
        if ((endpoint->dtmf == AST_SIP_DTMF_INBAND) || (endpoint->dtmf == AST_SIP_DTMF_AUTO)) {
                dsp_features |= DSP_FEATURE_DIGIT_DETECT;
@@ -2422,8 +2434,16 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
                break;
        case PJSIP_EVENT_RX_MSG:
                cb = ast_sip_mod_data_get(tsx->mod_data, session_module.id, MOD_DATA_ON_RESPONSE);
-               handle_incoming(session, e->body.tsx_state.src.rdata, e->type,
-                               AST_SIP_SESSION_AFTER_MEDIA);
+               /* As the PJSIP invite session implementation responds with a 200 OK before we have a
+                * chance to be invoked session supplements for BYE requests actually end up executing
+                * in the invite session state callback as well. To prevent session supplements from
+                * running on the BYE request again we explicitly squash invocation of them here.
+                */
+               if ((e->body.tsx_state.src.rdata->msg_info.msg->type != PJSIP_REQUEST_MSG) ||
+                       (tsx->method.id != PJSIP_BYE_METHOD)) {
+                       handle_incoming(session, e->body.tsx_state.src.rdata, e->type,
+                                                       AST_SIP_SESSION_AFTER_MEDIA);
+               }
                if (tsx->method.id == PJSIP_INVITE_METHOD) {
                        if (tsx->role == PJSIP_ROLE_UAC) {
                                if (tsx->state == PJSIP_TSX_STATE_COMPLETED) {
@@ -2675,12 +2695,7 @@ static struct pjmedia_sdp_session *create_local_sdp(pjsip_inv_session *inv, stru
                if (!ast_strlen_zero(session->endpoint->media.address)) {
                        pj_strdup2(inv->pool_prov, &local->origin.addr, session->endpoint->media.address);
                } else {
-                       pj_sockaddr localaddr;
-                       char our_ip[PJ_INET6_ADDRSTRLEN];
-
-                       pj_gethostip(session->endpoint->media.rtp.ipv6 ? pj_AF_INET6() : pj_AF_INET(), &localaddr);
-                       pj_sockaddr_print(&localaddr, our_ip, sizeof(our_ip), 0);
-                       pj_strdup2(inv->pool_prov, &local->origin.addr, our_ip);
+                       pj_strdup2(inv->pool_prov, &local->origin.addr, ast_sip_get_host_ip_string(session->endpoint->media.rtp.ipv6 ? pj_AF_INET6() : pj_AF_INET()));
                }
        }
 
index 43b6e92..40899bf 100644 (file)
@@ -707,7 +707,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
        static const pj_str_t STR_T38UDPREDUNDANCY = { "t38UDPRedundancy", 16 };
        struct t38_state *state;
        pjmedia_sdp_media *media;
-       char hostip[PJ_INET6_ADDRSTRLEN+2];
+       const char *hostip = NULL;
        struct ast_sockaddr addr;
        char tmp[512];
        pj_str_t stmp;
@@ -732,14 +732,13 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
        media->desc.transport = STR_UDPTL;
 
        if (ast_strlen_zero(session->endpoint->media.address)) {
-               pj_sockaddr localaddr;
-
-               if (pj_gethostip(session->endpoint->media.t38.ipv6 ? pj_AF_INET6() : pj_AF_INET(), &localaddr)) {
-                       return -1;
-               }
-               pj_sockaddr_print(&localaddr, hostip, sizeof(hostip), 2);
+               hostip = ast_sip_get_host_ip_string(session->endpoint->media.t38.ipv6 ? pj_AF_INET6() : pj_AF_INET());
        } else {
-               ast_copy_string(hostip, session->endpoint->media.address, sizeof(hostip));
+               hostip = session->endpoint->media.address;
+       }
+
+       if (ast_strlen_zero(hostip)) {
+               return -1;
        }
 
        media->conn->net_type = STR_IN;
index e48b630..3157a94 100644 (file)
@@ -419,7 +419,7 @@ static int load_module(void)
        CHECK_PJSIP_MODULE_LOADED();
 
        pjsip_transport_register_type(PJSIP_TRANSPORT_RELIABLE, "WS", 5060, &transport_type_ws);
-       pjsip_transport_register_type(PJSIP_TRANSPORT_RELIABLE, "WSS", 5060, &transport_type_wss);
+       pjsip_transport_register_type(PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE, "WSS", 5060, &transport_type_wss);
 
        if (ast_sip_register_service(&websocket_module) != PJ_SUCCESS) {
                return AST_MODULE_LOAD_DECLINE;
index 545e216..a51e07c 100644 (file)
@@ -590,12 +590,16 @@ static int ice_reset_session(struct ast_rtp_instance *instance)
        pj_ice_sess_role role = rtp->ice->role;
        int res;
 
+       ast_debug(3, "Resetting ICE for RTP instance '%p'\n", instance);
        if (!rtp->ice->is_nominating && !rtp->ice->is_complete) {
+               ast_debug(3, "Nevermind. ICE isn't ready for a reset\n");
                return 0;
        }
 
+       ast_debug(3, "Stopping ICE for RTP instance '%p'\n", instance);
        ast_rtp_ice_stop(instance);
 
+       ast_debug(3, "Recreating ICE session %s (%d) for RTP instance '%p'\n", ast_sockaddr_stringify(&rtp->ice_original_rtp_addr), rtp->ice_port, instance);
        res = ice_create(instance, &rtp->ice_original_rtp_addr, rtp->ice_port, 1);
        if (!res) {
                /* Preserve the role that the old ICE session used */
@@ -648,6 +652,7 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
        /* Check for equivalence in the lists */
        if (rtp->ice_active_remote_candidates &&
                        !ice_candidates_compare(rtp->ice_proposed_remote_candidates, rtp->ice_active_remote_candidates)) {
+               ast_debug(3, "Proposed == active candidates for RTP instance '%p'\n", instance);
                ao2_cleanup(rtp->ice_proposed_remote_candidates);
                rtp->ice_proposed_remote_candidates = NULL;
                return;
@@ -694,8 +699,10 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
                }
 
                if (candidate->id == AST_RTP_ICE_COMPONENT_RTP && rtp->turn_rtp) {
+                       ast_debug(3, "RTP candidate %s (%p)\n", ast_sockaddr_stringify(&candidate->address), instance);
                        pj_turn_sock_set_perm(rtp->turn_rtp, 1, &candidates[cand_cnt].addr, 1);
                } else if (candidate->id == AST_RTP_ICE_COMPONENT_RTCP && rtp->turn_rtcp) {
+                       ast_debug(3, "RTCP candidate %s (%p)\n", ast_sockaddr_stringify(&candidate->address), instance);
                        pj_turn_sock_set_perm(rtp->turn_rtcp, 1, &candidates[cand_cnt].addr, 1);
                }
 
@@ -705,21 +712,40 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
 
        ao2_iterator_destroy(&i);
 
-       if (has_rtp && has_rtcp &&
-           pj_ice_sess_create_check_list(rtp->ice, &ufrag, &passwd, ao2_container_count(
-                                                 rtp->ice_active_remote_candidates), &candidates[0]) == PJ_SUCCESS) {
-               ast_test_suite_event_notify("ICECHECKLISTCREATE", "Result: SUCCESS");
-               pj_ice_sess_start_check(rtp->ice);
-               pj_timer_heap_poll(timer_heap, NULL);
-               rtp->strict_rtp_state = STRICT_RTP_OPEN;
-               return;
+       if (cand_cnt < ao2_container_count(rtp->ice_active_remote_candidates)) {
+               ast_log(LOG_WARNING, "Lost %d ICE candidates. Consider increasing PJ_ICE_MAX_CAND in PJSIP (%p)\n",
+                       ao2_container_count(rtp->ice_active_remote_candidates) - cand_cnt, instance);
+       }
+
+       if (!has_rtp) {
+               ast_log(LOG_WARNING, "No RTP candidates; skipping ICE checklist (%p)\n", instance);
+       }
+
+       if (!has_rtcp) {
+               ast_log(LOG_WARNING, "No RTCP candidates; skipping ICE checklist (%p)\n", instance);
+       }
+
+       if (has_rtp && has_rtcp) {
+               pj_status_t res = pj_ice_sess_create_check_list(rtp->ice, &ufrag, &passwd, cand_cnt, &candidates[0]);
+               char reason[80];
+
+               if (res == PJ_SUCCESS) {
+                       ast_debug(3, "Successfully created ICE checklist (%p)\n", instance);
+                       ast_test_suite_event_notify("ICECHECKLISTCREATE", "Result: SUCCESS");
+                       pj_ice_sess_start_check(rtp->ice);
+                       pj_timer_heap_poll(timer_heap, NULL);
+                       rtp->strict_rtp_state = STRICT_RTP_OPEN;
+                       return;
+               }
+
+               pj_strerror(res, reason, sizeof(reason));
+               ast_log(LOG_WARNING, "Failed to create ICE session check list: %s (%p)\n", reason, instance);
        }
 
        ast_test_suite_event_notify("ICECHECKLISTCREATE", "Result: FAILURE");
 
        /* even though create check list failed don't stop ice as
           it might still work */
-       ast_debug(1, "Failed to create ICE session check list\n");
        /* however we do need to reset remote candidates since
           this function may be re-entered */
        ao2_ref(rtp->ice_active_remote_candidates, -1);
@@ -769,7 +795,11 @@ static void ast_rtp_ice_set_role(struct ast_rtp_instance *instance, enum ast_rtp
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 
+       ast_debug(3, "Set role to %s (%p)\n",
+               role == AST_RTP_ICE_ROLE_CONTROLLED ? "CONTROLLED" : "CONTROLLING", instance);
+
        if (!rtp->ice) {
+               ast_log(LOG_WARNING, "Set role failed; no ice instance (%p)\n", instance);
                return;
        }
 
@@ -2485,7 +2515,7 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
             create_new_socket("RTP",
                               ast_sockaddr_is_ipv4(addr) ? AF_INET  :
                               ast_sockaddr_is_ipv6(addr) ? AF_INET6 : -1)) < 0) {
-               ast_debug(1, "Failed to create a new socket for RTP instance '%p'\n", instance);
+               ast_log(LOG_WARNING, "Failed to create a new socket for RTP instance '%p'\n", instance);
                ast_free(rtp);
                return -1;
        }
@@ -2526,6 +2556,7 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
 #ifdef HAVE_PJPROJECT
        /* Create an ICE session for ICE negotiation */
        if (icesupport) {
+               ast_debug(3, "Creating ICE session %s (%d) for RTP instance '%p'\n", ast_sockaddr_stringify(addr), x, instance);
                if (ice_create(instance, addr, x, 0)) {
                        ast_log(LOG_NOTICE, "Failed to start ICE session\n");
                } else {