Merge "Parking: Add documentation for AMI ParkedCallSwap event."
authorMatt Jordan <mjordan@digium.com>
Tue, 16 Jun 2015 16:40:34 +0000 (11:40 -0500)
committerGerrit Code Review <gerrit2@gerrit.digium.api>
Tue, 16 Jun 2015 16:40:34 +0000 (11:40 -0500)
31 files changed:
CHANGES
apps/app_directory.c
autoconf/ast_gcc_attribute.m4
channels/chan_iax2.c
channels/chan_pjsip.c
channels/chan_sip.c
configs/samples/pjsip.conf.sample
configure
contrib/ast-db-manage/config/versions/28b8e71e541f_add_g726_non_standard.py [new file with mode: 0644]
contrib/scripts/install_prereq
funcs/func_pjsip_aor.c
include/asterisk/dns_internal.h
include/asterisk/res_pjsip.h
include/asterisk/threadpool.h
main/bridge_channel.c
main/cdr.c
main/dns_query_set.c
main/sched.c
main/taskprocessor.c
main/threadpool.c
menuselect/configure
res/res_pjsip.c
res/res_pjsip/pjsip_configuration.c
res/res_pjsip/pjsip_distributor.c
res/res_pjsip/pjsip_resolver.c
res/res_pjsip_sdp_rtp.c
res/res_pjsip_transport_websocket.c
res/res_resolver_unbound.c
tests/test_dns.c
tests/test_dns_query_set.c
tests/test_dns_recurring.c

diff --git a/CHANGES b/CHANGES
index 281d059..d2fa84c 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -186,6 +186,12 @@ AMI
  * A new ContactStatus event has been added that reflects res_pjsip contact
    lifecycle changes:  Created, Removed, Reachable, Unreachable, Unknown.
 
+res_pjsip
+------------------
+* A new 'g726_non_standard' endpoint option has been added that, when set to
+  'yes' and g.726 audio is negotiated, forces the codec to be treated as if it
+  is AAL2 packed on the channel.
+
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 13.3.0 to Asterisk 13.4.0 ------------
 ------------------------------------------------------------------------------
index c872886..c32bccb 100644 (file)
@@ -518,7 +518,9 @@ static struct ast_config *realtime_directory(char *context)
                        /* Skip hidden */
                        continue;
                }
-               ast_str_set(&tmp, 0, "no-password,%s", S_OR(fullname, ""));
+
+               /* password,Full Name,email,pager,options */
+               ast_str_set(&tmp, 0, "no-password,%s,,,", S_OR(fullname, ""));
                if (ast_variable_retrieve(rtdata, mailbox, "alias")) {
                        for (alias = ast_variable_browse(rtdata, mailbox); alias; alias = alias->next) {
                                if (!strcasecmp(alias->name, "alias")) {
@@ -602,7 +604,10 @@ static int search_directory_sub(const char *context, struct ast_config *vmcfg, s
 {
        struct ast_variable *v;
        struct ast_str *buf = ast_str_thread_get(&commonbuf, 100);
-       char *pos, *bufptr, *cat, *alias;
+       char *name;
+       char *options;
+       char *alias;
+       char *cat;
        struct directory_item *item;
        int res;
 
@@ -613,33 +618,36 @@ static int search_directory_sub(const char *context, struct ast_config *vmcfg, s
        ast_debug(2, "Pattern: %s\n", ext);
 
        for (v = ast_variable_browse(vmcfg, context); v; v = v->next) {
-
-               /* Ignore hidden */
-               if (strcasestr(v->value, "hidefromdir=yes")) {
-                       continue;
-               }
-
                ast_str_set(&buf, 0, "%s", v->value);
-               bufptr = ast_str_buffer(buf);
+               options = ast_str_buffer(buf);
 
                /* password,Full Name,email,pager,options */
-               strsep(&bufptr, ",");
-               pos = strsep(&bufptr, ",");
-
-               /* No name to compare against */
-               if (ast_strlen_zero(pos)) {
+               strsep(&options, ",");          /* Skip password */
+               name = strsep(&options, ",");   /* Save full name */
+               strsep(&options, ",");          /* Skip email */
+               strsep(&options, ",");          /* Skip pager */
+               /* options is now the options field if it exists. */
+
+               if (options && strcasestr(options, "hidefromdir=yes")) {
+                       /* Ignore hidden */
+                       continue;
+               }
+               if (ast_strlen_zero(name)) {
+                       /* No name to compare against */
                        continue;
                }
 
                res = 0;
                if (ast_test_flag(&flags, OPT_LISTBYLASTNAME)) {
-                       res = check_match(&item, context, pos, v->name, ext, 0 /* use_first_name */);
+                       res = check_match(&item, context, name, v->name, ext, 0 /* use_first_name */);
                }
                if (!res && ast_test_flag(&flags, OPT_LISTBYFIRSTNAME)) {
-                       res = check_match(&item, context, pos, v->name, ext, 1 /* use_first_name */);
+                       res = check_match(&item, context, name, v->name, ext, 1 /* use_first_name */);
                }
-               if (!res && ast_test_flag(&flags, OPT_ALIAS) && (alias = strcasestr(bufptr, "alias="))) {
+               if (!res && ast_test_flag(&flags, OPT_ALIAS)
+                       && options && (alias = strcasestr(options, "alias="))) {
                        char *a;
+
                        ast_debug(1, "Found alias: %s\n", alias);
                        while ((a = strsep(&alias, "|"))) {
                                if (!strncasecmp(a, "alias=", 6)) {
@@ -683,9 +691,9 @@ static int search_directory_sub(const char *context, struct ast_config *vmcfg, s
                                res = check_match(&item, context, position, cat, ext, 1 /* use_first_name */);
                        }
                        if (!res && ast_test_flag(&flags, OPT_ALIAS)) {
-                               struct ast_variable *alias;
-                               for (alias = ast_variable_browse(ucfg, cat); alias; alias = alias->next) {
-                                       if (!strcasecmp(v->name, "alias") && (res = check_match(&item, context, v->value, cat, ext, 1))) {
+                               for (v = ast_variable_browse(ucfg, cat); v; v = v->next) {
+                                       if (!strcasecmp(v->name, "alias")
+                                               && (res = check_match(&item, context, v->value, cat, ext, 1))) {
                                                break;
                                        }
                                }
index bceaa28..4ade814 100644 (file)
@@ -20,7 +20,7 @@ AC_COMPILE_IFELSE(
 )
 else
 AC_COMPILE_IFELSE(
-       [AC_LANG_PROGRAM([$3 void __attribute__(($2)) *test(void *muffin, ...) {return (void *) 0;}],
+       [AC_LANG_PROGRAM([$3 void __attribute__(($2)) *test(void *muffin, ...) ;],
                        [])],
        AC_MSG_RESULT(yes)
        m4_ifval([$4],$4=1)
index c797b87..da6bec7 100644 (file)
@@ -395,8 +395,6 @@ static int (*iax2_regfunk)(const char *username, int onoff) = NULL;
 static struct io_context *io;
 static struct ast_sched_context *sched;
 
-#define DONT_RESCHEDULE -2
-
 static iax2_format iax2_capability = IAX_CAPABILITY_FULLBANDWIDTH;
 
 static int iaxdebug = 0;
@@ -864,6 +862,8 @@ struct chan_iax2_pvt {
        int frames_dropped;
        /*! received frame count: (just for stats) */
        int frames_received;
+       /*! Destroying this call initiated. */
+       int destroy_initiated;
        /*! num bytes used for calltoken ie, even an empty ie should contain 2 */
        unsigned char calltoken_ie_len;
        /*! hold all signaling frames from the pbx thread until we have a destination callno */
@@ -1664,23 +1664,48 @@ static int iax2_sched_add(struct ast_sched_context *con, int when,
        return ast_sched_add(con, when, callback, data);
 }
 
+/*
+ * \brief Acquire the iaxsl[callno] if call exists and not having ongoing hangup.
+ * \param callno Call number to lock.
+ * \return 0 If call disappeared or has ongoing hangup procedure. 1 If call found and mutex is locked.
+ */
+static int iax2_lock_callno_unless_destroyed(int callno)
+{
+       ast_mutex_lock(&iaxsl[callno]);
+
+       /* We acquired the lock; but the call was already destroyed (we came after full hang up procedures)
+        * or destroy initiated (in middle of hang up procedure. */
+       if (!iaxs[callno] || iaxs[callno]->destroy_initiated) {
+               ast_debug(3, "I wanted to lock callno %d, but it is dead or going to die.\n", callno);
+               ast_mutex_unlock(&iaxsl[callno]);
+               return 0;
+       }
+
+       /* Lock acquired, and callno is alive and kicking. */
+       return 1;
+}
+
 static int send_ping(const void *data);
 
 static void __send_ping(const void *data)
 {
-       int callno = (long) data;
+       int callno = PTR_TO_CALLNO(data);
 
-       ast_mutex_lock(&iaxsl[callno]);
+       if (iax2_lock_callno_unless_destroyed(callno) == 0) {
+               ast_debug(3, "Hangup initiated on call %d, aborting __send_ping\n", callno);
+               return;
+       }
 
-       if (iaxs[callno]) {
-               if (iaxs[callno]->peercallno) {
-                       send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_PING, 0, NULL, 0, -1);
-                       if (iaxs[callno]->pingid != DONT_RESCHEDULE) {
-                               iaxs[callno]->pingid = iax2_sched_add(sched, ping_time * 1000, send_ping, data);
-                       }
-               }
-       } else {
-               ast_debug(1, "I was supposed to send a PING with callno %d, but no such call exists.\n", callno);
+       /* Mark pingid as invalid scheduler id. */
+       iaxs[callno]->pingid = -1;
+
+       /* callno is now locked. */
+       if (iaxs[callno]->peercallno) {
+               /* Send PING packet. */
+               send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_PING, 0, NULL, 0, -1);
+
+               /* Schedule sending next ping. */
+               iaxs[callno]->pingid = iax2_sched_add(sched, ping_time * 1000, send_ping, data);
        }
 
        ast_mutex_unlock(&iaxsl[callno]);
@@ -1688,13 +1713,6 @@ static void __send_ping(const void *data)
 
 static int send_ping(const void *data)
 {
-       int callno = (long) data;
-       ast_mutex_lock(&iaxsl[callno]);
-       if (iaxs[callno] && iaxs[callno]->pingid != DONT_RESCHEDULE) {
-               iaxs[callno]->pingid = -1;
-       }
-       ast_mutex_unlock(&iaxsl[callno]);
-
 #ifdef SCHED_MULTITHREADED
        if (schedule_action(__send_ping, data))
 #endif
@@ -1735,19 +1753,23 @@ static int send_lagrq(const void *data);
 
 static void __send_lagrq(const void *data)
 {
-       int callno = (long) data;
+       int callno = PTR_TO_CALLNO(data);
 
-       ast_mutex_lock(&iaxsl[callno]);
+       if (iax2_lock_callno_unless_destroyed(callno) == 0) {
+               ast_debug(3, "Hangup initiated on call %d, aborting __send_lagrq\n", callno);
+               return;
+       }
 
-       if (iaxs[callno]) {
-               if (iaxs[callno]->peercallno) {
-                       send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_LAGRQ, 0, NULL, 0, -1);
-                       if (iaxs[callno]->lagid != DONT_RESCHEDULE) {
-                               iaxs[callno]->lagid = iax2_sched_add(sched, lagrq_time * 1000, send_lagrq, data);
-                       }
-               }
-       } else {
-               ast_debug(1, "I was supposed to send a LAGRQ with callno %d, but no such call exists.\n", callno);
+       /* Mark lagid as invalid scheduler id. */
+       iaxs[callno]->lagid = -1;
+
+       /* callno is now locked. */
+       if (iaxs[callno]->peercallno) {
+               /* Send LAGRQ packet. */
+               send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_LAGRQ, 0, NULL, 0, -1);
+
+               /* Schedule sending next lagrq. */
+               iaxs[callno]->lagid = iax2_sched_add(sched, lagrq_time * 1000, send_lagrq, data);
        }
 
        ast_mutex_unlock(&iaxsl[callno]);
@@ -1755,13 +1777,6 @@ static void __send_lagrq(const void *data)
 
 static int send_lagrq(const void *data)
 {
-       int callno = (long) data;
-       ast_mutex_lock(&iaxsl[callno]);
-       if (iaxs[callno] && iaxs[callno]->lagid != DONT_RESCHEDULE) {
-               iaxs[callno]->lagid = -1;
-       }
-       ast_mutex_unlock(&iaxsl[callno]);
-
 #ifdef SCHED_MULTITHREADED
        if (schedule_action(__send_lagrq, data))
 #endif
@@ -2045,6 +2060,16 @@ static int iax2_getpeername(struct ast_sockaddr addr, char *host, int len)
        return res;
 }
 
+/* Call AST_SCHED_DEL on a scheduled task if it is found in scheduler. */
+static int iax2_delete_from_sched(const void* data)
+{
+       int sched_id = (int)(long)data;
+
+       AST_SCHED_DEL(sched, sched_id);
+
+       return 0;
+}
+
 /*!\note Assumes the lock on the pvt is already held, when
  * iax2_destroy_helper() is called. */
 static void iax2_destroy_helper(struct chan_iax2_pvt *pvt)
@@ -2061,11 +2086,27 @@ static void iax2_destroy_helper(struct chan_iax2_pvt *pvt)
 
                ast_clear_flag64(pvt, IAX_MAXAUTHREQ);
        }
-       /* No more pings or lagrq's */
-       AST_SCHED_DEL_SPINLOCK(sched, pvt->pingid, &iaxsl[pvt->callno]);
-       pvt->pingid = DONT_RESCHEDULE;
-       AST_SCHED_DEL_SPINLOCK(sched, pvt->lagid, &iaxsl[pvt->callno]);
-       pvt->lagid = DONT_RESCHEDULE;
+
+
+       /* Mark call destroy initiated flag. */
+       pvt->destroy_initiated = 1;
+
+       /*
+        * Schedule deleting the scheduled (but didn't run yet) PINGs or LAGRQs.
+        * Already running tasks will be terminated because of destroy_initiated.
+        *
+        * Don't call AST_SCHED_DEL from this thread for pingid and lagid because
+        * it leads to a deadlock between the scheduler thread callback locking
+        * the callno mutex and this thread which holds the callno mutex one or
+        * more times.  It is better to have another thread delete the scheduled
+        * callbacks which doesn't lock the callno mutex.
+        */
+       iax2_sched_add(sched, 0, iax2_delete_from_sched, (void*)(long)pvt->pingid);
+       iax2_sched_add(sched, 0, iax2_delete_from_sched, (void*)(long)pvt->lagid);
+
+       pvt->pingid = -1;
+       pvt->lagid = -1;
+
        AST_SCHED_DEL(sched, pvt->autoid);
        AST_SCHED_DEL(sched, pvt->authid);
        AST_SCHED_DEL(sched, pvt->initid);
index 14ba4a2..b44d3e0 100644 (file)
@@ -388,7 +388,9 @@ static struct ast_channel *chan_pjsip_new(struct ast_sip_session *session, int s
        chan = ast_channel_alloc_with_endpoint(1, state,
                S_COR(session->id.number.valid, session->id.number.str, ""),
                S_COR(session->id.name.valid, session->id.name.str, ""),
-               session->endpoint->accountcode, "", "", assignedids, requestor, 0,
+               session->endpoint->accountcode,
+               exten, session->endpoint->context,
+               assignedids, requestor, 0,
                session->endpoint->persistent, "PJSIP/%s-%08x",
                ast_sorcery_object_get_id(session->endpoint),
                (unsigned) ast_atomic_fetchadd_int((int *) &chan_idx, +1));
@@ -445,8 +447,6 @@ static struct ast_channel *chan_pjsip_new(struct ast_sip_session *session, int s
        ast_party_id_copy(&ast_channel_caller(chan)->id, &session->id);
        ast_party_id_copy(&ast_channel_caller(chan)->ani, &session->id);
 
-       ast_channel_context_set(chan, session->endpoint->context);
-       ast_channel_exten_set(chan, S_OR(exten, "s"));
        ast_channel_priority_set(chan, 1);
 
        ast_channel_callgroup_set(chan, session->endpoint->pickup.callgroup);
index 1d58a84..4112e00 100644 (file)
@@ -9234,6 +9234,15 @@ static struct sip_pvt *__find_call(struct sip_request *req, struct ast_sockaddr
 
                        switch (found) {
                        case SIP_REQ_MATCH:
+                               sip_pvt_lock(sip_pvt_ptr);
+                               if (args.method != SIP_RESPONSE && args.authentication_present
+                                               && strcmp(args.fromtag, sip_pvt_ptr->theirtag)) {
+                                       /* If we have a request that uses athentication and the fromtag is
+                                        * different from that in the original call dialog, update the
+                                        * fromtag in the saved call dialog */
+                                       ast_string_field_set(sip_pvt_ptr, theirtag, args.fromtag);
+                               }
+                               sip_pvt_unlock(sip_pvt_ptr);
                                ao2_iterator_destroy(iterator);
                                dialog_unref(fork_pvt, "unref fork_pvt");
                                free_via(via);
index 276e214..24ff327 100644 (file)
                         ; "no")
 ;media_encryption_optimistic=no ; Use encryption if possible but don't fail the call
                                                                ; if not possible.
+;g726_non_standard=no   ; When set to "yes" and an endpoint negotiates g.726
+                        ; audio then g.726 for AAL2 packing order is used contrary
+                        ; to what is recommended in RFC3551. Note, 'g726aal2' also
+                        ; needs to be specified in the codec allow list
+                        ; (default: "no")
 ;inband_progress=no     ; Determines whether chan_pjsip will indicate ringing
                         ; using inband progress (default: "no")
 ;call_group=    ; The numeric pickup groups for a channel (default: "")
index 9f7a862..731b2f7 100755 (executable)
--- a/configure
+++ b/configure
@@ -16720,7 +16720,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -16788,7 +16788,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -16856,7 +16856,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -16924,7 +16924,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -16992,7 +16992,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -17060,7 +17060,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -17128,7 +17128,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -17196,7 +17196,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -17264,7 +17264,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -17332,7 +17332,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -17400,7 +17400,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
diff --git a/contrib/ast-db-manage/config/versions/28b8e71e541f_add_g726_non_standard.py b/contrib/ast-db-manage/config/versions/28b8e71e541f_add_g726_non_standard.py
new file mode 100644 (file)
index 0000000..ad36bd9
--- /dev/null
@@ -0,0 +1,30 @@
+"""add g726_non_standard
+
+Revision ID: 28b8e71e541f
+Revises: a541e0b5e89
+Create Date: 2015-06-12 16:07:08.609628
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '28b8e71e541f'
+down_revision = 'a541e0b5e89'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects.postgresql import ENUM
+
+YESNO_NAME = 'yesno_values'
+YESNO_VALUES = ['yes', 'no']
+
+def upgrade():
+    ############################# Enums ##############################
+
+    # yesno_values have already been created, so use postgres enum object
+    # type to get around "already created" issue - works okay with mysql
+    yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
+    op.add_column('ps_endpoints', sa.Column('g726_non_standard', yesno_values))
+
+
+def downgrade():
+    op.drop_column('ps_endpoints', 'g726_non_standard')
index 58ef05c..b0caab8 100755 (executable)
@@ -70,9 +70,10 @@ check_installed_debs() {
        do
                tocheck="${tocheck} ^${pack}$"
        done
-       aptitude -F '%c %p' search ${tocheck} 2>/dev/null \
-               | awk '/^p/{print $2}' \
-               | grep -v ':i386$'
+       pkgs=$(aptitude -F '%c %p' search ${tocheck} 2>/dev/null | awk '/^p/{print $2}')
+       if ! [ ${#pkgs} -eq 0 ]; then
+               echo $pkgs | grep -v ':i386$'
+       fi
 }
 
 # parsing the output of yum is close to impossible.
@@ -96,6 +97,9 @@ check_installed_pkgs() {
 }
 
 handle_debian() {
+       if ! [ -x "$(command -v aptitude)" ]; then
+               apt-get install aptitude
+       fi
        extra_packs=`check_installed_debs $PACKAGES_DEBIAN`
        $testcmd aptitude install -y $extra_packs
 }
index 41c15ff..835955e 100644 (file)
@@ -135,6 +135,8 @@ static int pjsip_aor_function_read(struct ast_channel *chan,
 
                        ast_str_append(buf, len, "%s", ast_sorcery_object_get_id(contact));
                        first = 0;
+
+                       ao2_ref(contact, -1);
                }
                ao2_iterator_destroy(&i);
        } else {
index be8794b..d517748 100644 (file)
@@ -161,7 +161,7 @@ struct ast_dns_query_recurring {
 struct dns_query_set_query {
        /*! \brief Whether the query started successfully or not */
        unsigned int started;
-       /*! \brief THe query itself */
+       /*! \brief The query itself */
        struct ast_dns_query *query;
 };
 
@@ -169,7 +169,7 @@ struct dns_query_set_query {
 struct ast_dns_query_set {
        /*! \brief DNS queries */
        AST_VECTOR(, struct dns_query_set_query) queries;
-       /* \brief Whether the query set is in progress or not */
+       /*! \brief Whether the query set is in progress or not */
        int in_progress;
        /*! \brief The total number of completed queries */
        int queries_completed;
index bd56c19..5267603 100644 (file)
@@ -284,21 +284,21 @@ enum ast_sip_auth_type {
 #define SIP_SORCERY_AUTH_TYPE "auth"
 
 struct ast_sip_auth {
-       /* Sorcery ID of the auth is its name */
+       /*! Sorcery ID of the auth is its name */
        SORCERY_OBJECT(details);
        AST_DECLARE_STRING_FIELDS(
-               /* Identification for these credentials */
+               /*! Identification for these credentials */
                AST_STRING_FIELD(realm);
-               /* Authentication username */
+               /*! Authentication username */
                AST_STRING_FIELD(auth_user);
-               /* Authentication password */
+               /*! Authentication password */
                AST_STRING_FIELD(auth_pass);
-               /* Authentication credentials in MD5 format (hash of user:realm:pass) */
+               /*! Authentication credentials in MD5 format (hash of user:realm:pass) */
                AST_STRING_FIELD(md5_creds);
        );
-       /* The time period (in seconds) that a nonce may be reused */
+       /*! The time period (in seconds) that a nonce may be reused */
        unsigned int nonce_lifetime;
-       /* Used to determine what to use when authenticating */
+       /*! Used to determine what to use when authenticating */
        enum ast_sip_auth_type type;
 };
 
@@ -557,6 +557,8 @@ struct ast_sip_endpoint_media_configuration {
        unsigned int tos_video;
        /*! Priority for video streams */
        unsigned int cos_video;
+       /*! Is g.726 packed in a non standard way */
+       unsigned int g726_non_standard;
 };
 
 /*!
@@ -1751,7 +1753,7 @@ const char *ast_sip_auth_type_to_str(enum ast_sip_auth_type type);
  */
 int ast_sip_auths_to_str(const struct ast_sip_auth_vector *auths, char **buf);
 
-/*
+/*!
  * \brief AMI variable container
  */
 struct ast_sip_ami {
index e1e7727..1c67058 100644 (file)
@@ -196,6 +196,22 @@ int ast_threadpool_push(struct ast_threadpool *pool, int (*task)(void *data), vo
 void ast_threadpool_shutdown(struct ast_threadpool *pool);
 
 /*!
+ * \brief Get the threadpool serializer currently associated with this thread.
+ * \since 14.0.0
+ *
+ * \note The returned pointer is valid while the serializer
+ * thread is running.
+ *
+ * \note Use ao2_ref() on serializer if you are going to keep it
+ * for another thread.  To unref it you must then use
+ * ast_taskprocessor_unreference().
+ *
+ * \retval serializer on success.
+ * \retval NULL on error or no serializer associated with the thread.
+ */
+struct ast_taskprocessor *ast_threadpool_serializer_get_current(void);
+
+/*!
  * \brief Serialized execution of tasks within a \ref ast_threadpool.
  *
  * \since 12.0.0
index 9e30068..1597226 100644 (file)
@@ -1761,6 +1761,17 @@ static void after_bridge_move_channel(struct ast_channel *chan_bridged, void *da
                return;
        }
 
+       /* The ast_channel_move function will end up updating the connected line information
+        * on chan_target to the value we have here, but will not inform it. To ensure that
+        * AST_FRAME_READ_ACTION_CONNECTED_LINE_MACRO is executed we wipe it away here. If
+        * we don't do this then the change will be considered redundant, since the connected
+        * line information is already there (despite the channel not being told).
+        */
+       ast_channel_lock(chan_target);
+       ast_party_connected_line_free(ast_channel_connected_indicated(chan_target));
+       ast_party_connected_line_init(ast_channel_connected_indicated(chan_target));
+       ast_channel_unlock(chan_target);
+
        if ((payload_size = ast_connected_line_build_data(connected_line_data,
                sizeof(connected_line_data), &connected_target, NULL)) != -1) {
                struct ast_control_read_action_payload *frame_payload;
@@ -1774,6 +1785,15 @@ static void after_bridge_move_channel(struct ast_channel *chan_bridged, void *da
                ast_queue_control_data(chan_target, AST_CONTROL_READ_ACTION, frame_payload, frame_size);
        }
 
+       /* A connected line update is queued so that if chan_target is remotely involved with
+        * anything (such as dialing a channel) the other channel(s) will be informed of the
+        * new channel they are involved with.
+        */
+       ast_channel_lock(chan_target);
+       ast_connected_line_copy_from_caller(&connected_target, ast_channel_caller(chan_target));
+       ast_channel_queue_connected_line_update(chan_target, &connected_target, NULL);
+       ast_channel_unlock(chan_target);
+
        ast_party_connected_line_free(&connected_target);
 }
 
index c6f49f1..8d7f53f 100644 (file)
@@ -912,6 +912,16 @@ static struct cdr_object *cdr_object_create_and_append(struct cdr_object *cdr)
        ast_string_field_set(new_cdr, linkedid, cdr_last->linkedid);
        ast_string_field_set(new_cdr, appl, cdr_last->appl);
        ast_string_field_set(new_cdr, data, cdr_last->data);
+       ast_string_field_set(new_cdr, context, cdr_last->context);
+       ast_string_field_set(new_cdr, exten, cdr_last->exten);
+
+       /*
+        * If the current CDR says to disable all future ones,
+        * keep the disable chain going
+        */
+       if (ast_test_flag(&cdr_last->flags, AST_CDR_FLAG_DISABLE_ALL)) {
+               ast_set_flag(&new_cdr->flags, AST_CDR_FLAG_DISABLE_ALL);
+       }
 
        /* Copy over other Party A information */
        cdr_object_snapshot_copy(&new_cdr->party_a, &cdr_last->party_a);
index 8dfc5ea..40a89e1 100644 (file)
@@ -117,12 +117,23 @@ int ast_dns_query_set_add(struct ast_dns_query_set *query_set, const char *name,
                return -1;
        }
 
+       /*
+        * We are intentionally passing NULL for the user data even
+        * though dns_query_set_callback() is not NULL tolerant.  Doing
+        * this avoids a circular reference chain until the queries are
+        * started.  ast_dns_query_set_resolve_async() will set the
+        * query user_data for us later when we actually kick off the
+        * queries.
+        */
        query.query = dns_query_alloc(name, rr_type, rr_class, dns_query_set_callback, NULL);
        if (!query.query) {
                return -1;
        }
 
-       AST_VECTOR_APPEND(&query_set->queries, query);
+       if (AST_VECTOR_APPEND(&query_set->queries, query)) {
+               ao2_ref(query.query, -1);
+               return -1;
+       }
 
        return 0;
 }
@@ -167,6 +178,11 @@ void ast_dns_query_set_resolve_async(struct ast_dns_query_set *query_set, ast_dn
        query_set->callback = callback;
        query_set->user_data = ao2_bump(data);
 
+       /*
+        * Bump the query_set ref in case all queries complete
+        * before we are done kicking them off.
+        */
+       ao2_ref(query_set, +1);
        for (idx = 0; idx < AST_VECTOR_SIZE(&query_set->queries); ++idx) {
                struct dns_query_set_query *query = AST_VECTOR_GET_ADDR(&query_set->queries, idx);
 
@@ -179,6 +195,17 @@ void ast_dns_query_set_resolve_async(struct ast_dns_query_set *query_set, ast_dn
 
                dns_query_set_callback(query->query);
        }
+       if (!idx) {
+               /*
+                * There were no queries in the set;
+                * therefore all queries are "completed".
+                * Invoke the final callback.
+                */
+               query_set->callback(query_set);
+               ao2_cleanup(query_set->user_data);
+               query_set->user_data = NULL;
+       }
+       ao2_ref(query_set, -1);
 }
 
 /*! \brief Structure used for signaling back for synchronous resolution completion */
index 911143c..d50a31e 100644 (file)
@@ -513,16 +513,8 @@ int _ast_sched_del(struct ast_sched_context *con, int id, const char *file, int
 
        if (!s && *last_id != id) {
                ast_debug(1, "Attempted to delete nonexistent schedule entry %d!\n", id);
-#ifndef AST_DEVMODE
-               ast_assert(s != NULL);
-#else
-               {
-                       char buf[100];
-
-                       snprintf(buf, sizeof(buf), "s != NULL, id=%d", id);
-                       _ast_assert(0, buf, file, line, function);
-               }
-#endif
+               /* Removing nonexistent schedule entry shouldn't trigger assert (it was enabled in DEV_MODE);
+                * because in many places entries is deleted without having valid id. */
                *last_id = id;
                return -1;
        } else if (!s) {
index 8730253..1edbaa3 100644 (file)
@@ -593,7 +593,6 @@ static struct ast_taskprocessor *__allocate_taskprocessor(const char *name, stru
                return NULL;
        }
        if (!(p->name = ast_strdup(name))) {
-               ao2_ref(p, -1);
                return NULL;
        }
 
index 597e83e..6b412d2 100644 (file)
@@ -1150,13 +1150,17 @@ static struct serializer *serializer_create(struct ast_threadpool *pool)
        return ser;
 }
 
+AST_THREADSTORAGE_RAW(current_serializer);
+
 static int execute_tasks(void *data)
 {
        struct ast_taskprocessor *tps = data;
 
+       ast_threadstorage_set_ptr(&current_serializer, tps);
        while (ast_taskprocessor_execute(tps)) {
                /* No-op */
        }
+       ast_threadstorage_set_ptr(&current_serializer, NULL);
 
        ast_taskprocessor_unreference(tps);
        return 0;
@@ -1192,6 +1196,11 @@ static struct ast_taskprocessor_listener_callbacks serializer_tps_listener_callb
        .shutdown = serializer_shutdown,
 };
 
+struct ast_taskprocessor *ast_threadpool_serializer_get_current(void)
+{
+       return ast_threadstorage_get_ptr(&current_serializer);
+}
+
 struct ast_taskprocessor *ast_threadpool_serializer(const char *name, struct ast_threadpool *pool)
 {
        RAII_VAR(struct serializer *, ser, NULL, ao2_cleanup);
index 5081371..648091e 100755 (executable)
@@ -3227,7 +3227,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -3299,7 +3299,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -3371,7 +3371,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-static void __attribute__((weakref("foo"))) *test(void *muffin, ...) {return (void *) 0;}
+static void __attribute__((weakref("foo"))) *test(void *muffin, ...) ;
 int
 main ()
 {
@@ -3445,7 +3445,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-static void __attribute__((weakref("foo"))) *test(void *muffin, ...) {return (void *) 0;}
+static void __attribute__((weakref("foo"))) *test(void *muffin, ...) ;
 int
 main ()
 {
index 6e7bd68..8d5adf6 100644 (file)
                                                set to <literal>sdes</literal> or <literal>dtls</literal>.
                                        </para></description>
                                </configOption>
+                               <configOption name="g726_non_standard" default="no">
+                                       <synopsis>Force g.726 to use AAL2 packing order when negotiating g.726 audio</synopsis>
+                                       <description><para>
+                                                When set to "yes" and an endpoint negotiates g.726 audio then use g.726 for AAL2
+                                                packing order instead of what is recommended by RFC3551. Since this essentially
+                                                replaces the underlying 'g726' codec with 'g726aal2' then 'g726aal2' needs to be
+                                                specified in the endpoint's allowed codec list.
+                                       </para></description>
+                               </configOption>
                                <configOption name="inband_progress" default="no">
                                        <synopsis>Determines whether chan_pjsip will indicate ringing using inband
                                            progress.</synopsis>
 
 #define MOD_DATA_CONTACT "contact"
 
+/*! Number of serializers in pool if one not supplied. */
+#define SERIALIZER_POOL_SIZE           8
+
+/*! Next serializer pool index to use. */
+static int serializer_pool_pos;
+
+/*! Pool of serializers to use if not supplied. */
+static struct ast_taskprocessor *serializer_pool[SERIALIZER_POOL_SIZE];
+
 static pjsip_endpoint *ast_pjsip_endpoint;
 
 static struct ast_threadpool *sip_threadpool;
@@ -3323,8 +3341,62 @@ struct ast_taskprocessor *ast_sip_create_serializer(void)
        return serializer;
 }
 
+/*!
+ * \internal
+ * \brief Shutdown the serializers in the default pool.
+ * \since 14.0.0
+ *
+ * \return Nothing
+ */
+static void serializer_pool_shutdown(void)
+{
+       int idx;
+
+       for (idx = 0; idx < SERIALIZER_POOL_SIZE; ++idx) {
+               ast_taskprocessor_unreference(serializer_pool[idx]);
+               serializer_pool[idx] = NULL;
+       }
+}
+
+/*!
+ * \internal
+ * \brief Setup the serializers in the default pool.
+ * \since 14.0.0
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int serializer_pool_setup(void)
+{
+       int idx;
+
+       for (idx = 0; idx < SERIALIZER_POOL_SIZE; ++idx) {
+               serializer_pool[idx] = ast_sip_create_serializer();
+               if (!serializer_pool[idx]) {
+                       serializer_pool_shutdown();
+                       return -1;
+               }
+       }
+       return 0;
+}
+
 int ast_sip_push_task(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data)
 {
+       if (!serializer) {
+               unsigned int pos;
+
+               /*
+                * Pick a serializer to use from the pool.
+                *
+                * Note: We don't care about any reentrancy behavior
+                * when incrementing serializer_pool_pos.  If it gets
+                * incorrectly incremented it doesn't matter.
+                */
+               pos = serializer_pool_pos++;
+               pos %= SERIALIZER_POOL_SIZE;
+               serializer = serializer_pool[pos];
+       }
+
        if (serializer) {
                return ast_taskprocessor_push(serializer, sip_task, task_data);
        } else {
@@ -3377,18 +3449,10 @@ int ast_sip_push_task_synchronous(struct ast_taskprocessor *serializer, int (*si
        std.task = sip_task;
        std.task_data = task_data;
 
-       if (serializer) {
-               if (ast_taskprocessor_push(serializer, sync_task, &std)) {
-                       ast_mutex_destroy(&std.lock);
-                       ast_cond_destroy(&std.cond);
-                       return -1;
-               }
-       } else {
-               if (ast_threadpool_push(sip_threadpool, sync_task, &std)) {
-                       ast_mutex_destroy(&std.lock);
-                       ast_cond_destroy(&std.cond);
-                       return -1;
-               }
+       if (ast_sip_push_task(serializer, sync_task, &std)) {
+               ast_mutex_destroy(&std.lock);
+               ast_cond_destroy(&std.cond);
+               return -1;
        }
 
        ast_mutex_lock(&std.lock);
@@ -3679,6 +3743,18 @@ static int load_module(void)
                return AST_MODULE_LOAD_DECLINE;
        }
 
+       if (serializer_pool_setup()) {
+               ast_log(LOG_ERROR, "Failed to create SIP serializer pool. Aborting load\n");
+               ast_threadpool_shutdown(sip_threadpool);
+               ast_sip_destroy_system();
+               pj_pool_release(memory_pool);
+               memory_pool = NULL;
+               pjsip_endpt_destroy(ast_pjsip_endpoint);
+               ast_pjsip_endpoint = NULL;
+               pj_caching_pool_destroy(&caching_pool);
+               return AST_MODULE_LOAD_DECLINE;
+       }
+
        pjsip_tsx_layer_init_module(ast_pjsip_endpoint);
        pjsip_ua_init_module(ast_pjsip_endpoint, NULL);
 
@@ -3792,6 +3868,7 @@ static int unload_module(void)
         */
        ast_sip_push_task_synchronous(NULL, unload_pjsip, NULL);
 
+       serializer_pool_shutdown();
        ast_threadpool_shutdown(sip_threadpool);
 
        ast_sip_destroy_cli();
index 4ce7735..d4fa152 100644 (file)
@@ -1923,6 +1923,7 @@ int ast_res_pjsip_initialize_configuration(void)
        ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_fingerprint", "", dtls_handler, dtlsfingerprint_to_str, NULL, 0, 0);
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "srtp_tag_32", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.srtp_tag_32));
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "media_encryption_optimistic", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.encryption_optimistic));
+       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "g726_non_standard", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.g726_non_standard));
        ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "redirect_method", "user", redirect_handler, NULL, NULL, 0, 0);
        ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "set_var", "", set_var_handler, set_var_to_str, set_var_to_vl, 0, 0);
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "message_context", "", OPT_STRINGFIELD_T, 1, STRFLDSET(struct ast_sip_endpoint, message_context));
index e32f028..9b05260 100644 (file)
 
 #include "asterisk/res_pjsip.h"
 #include "include/res_pjsip_private.h"
+#include "asterisk/taskprocessor.h"
+#include "asterisk/threadpool.h"
 
 static int distribute(void *data);
 static pj_bool_t distributor(pjsip_rx_data *rdata);
+static pj_status_t record_serializer(pjsip_tx_data *tdata);
 
 static pjsip_module distributor_mod = {
        .name = {"Request Distributor", 19},
        .priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 6,
+       .on_tx_request = record_serializer,
        .on_rx_request = distributor,
        .on_rx_response = distributor,
 };
 
+/*!
+ * \internal
+ * \brief Record the task's serializer name on the tdata structure.
+ * \since 14.0.0
+ *
+ * \param tdata The outgoing message.
+ *
+ * \retval PJ_SUCCESS.
+ */
+static pj_status_t record_serializer(pjsip_tx_data *tdata)
+{
+       struct ast_taskprocessor *serializer;
+
+       serializer = ast_threadpool_serializer_get_current();
+       if (serializer) {
+               const char *name;
+
+               name = ast_taskprocessor_name(serializer);
+               if (!ast_strlen_zero(name)
+                       && (!tdata->mod_data[distributor_mod.id]
+                               || strcmp(tdata->mod_data[distributor_mod.id], name))) {
+                       char *tdata_name;
+
+                       /* The serializer in use changed. */
+                       tdata_name = pj_pool_alloc(tdata->pool, strlen(name) + 1);
+                       strcpy(tdata_name, name);/* Safe */
+
+                       tdata->mod_data[distributor_mod.id] = tdata_name;
+               }
+       }
+
+       return PJ_SUCCESS;
+}
+
+/*!
+ * \internal
+ * \brief Find the request tdata to get the serializer it used.
+ * \since 14.0.0
+ *
+ * \param rdata The incoming message.
+ *
+ * \retval serializer on success.
+ * \retval NULL on error or could not find the serializer.
+ */
+static struct ast_taskprocessor *find_request_serializer(pjsip_rx_data *rdata)
+{
+       struct ast_taskprocessor *serializer = NULL;
+       pj_str_t tsx_key;
+       pjsip_transaction *tsx;
+
+       pjsip_tsx_create_key(rdata->tp_info.pool, &tsx_key, PJSIP_ROLE_UAC,
+               &rdata->msg_info.cseq->method, rdata);
+
+       tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE);
+       if (!tsx) {
+               ast_debug(1, "Could not find %.*s transaction for %d response.\n",
+                       (int) pj_strlen(&rdata->msg_info.cseq->method.name),
+                       pj_strbuf(&rdata->msg_info.cseq->method.name),
+                       rdata->msg_info.msg->line.status.code);
+               return NULL;
+       }
+
+       if (tsx->last_tx) {
+               const char *serializer_name;
+
+               serializer_name = tsx->last_tx->mod_data[distributor_mod.id];
+               if (!ast_strlen_zero(serializer_name)) {
+                       serializer = ast_taskprocessor_get(serializer_name, TPS_REF_IF_EXISTS);
+               }
+       }
+
+#ifdef HAVE_PJ_TRANSACTION_GRP_LOCK
+       pj_grp_lock_release(tsx->grp_lock);
+#else
+       pj_mutex_unlock(tsx->mutex);
+#endif
+
+       return serializer;
+}
+
 /*! Dialog-specific information the distributor uses */
 struct distributor_dialog_data {
-       /* Serializer to distribute tasks to for this dialog */
+       /*! Serializer to distribute tasks to for this dialog */
        struct ast_taskprocessor *serializer;
-       /* Endpoint associated with this dialog */
+       /*! Endpoint associated with this dialog */
        struct ast_sip_endpoint *endpoint;
 };
 
@@ -167,6 +251,7 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
        pjsip_dialog *dlg = find_dialog(rdata);
        struct distributor_dialog_data *dist = NULL;
        struct ast_taskprocessor *serializer = NULL;
+       struct ast_taskprocessor *req_serializer = NULL;
        pjsip_rx_data *clone;
 
        if (dlg) {
@@ -176,11 +261,16 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
                }
        }
 
-       if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG && (
-               !pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method) || 
-               !pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_bye_method)) &&
-               !serializer) {
-               pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 481, NULL, NULL, NULL);
+       if (serializer) {
+               /* We have a serializer so we know where to send the message. */
+       } else if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG) {
+               req_serializer = find_request_serializer(rdata);
+               serializer = req_serializer;
+       } else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method)
+               || !pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_bye_method)) {
+               /* We have a BYE or CANCEL request without a serializer. */
+               pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata,
+                       PJSIP_SC_CALL_TSX_DOES_NOT_EXIST, NULL, NULL, NULL);
                goto end;
        }
 
@@ -196,6 +286,7 @@ end:
        if (dlg) {
                pjsip_dlg_dec_lock(dlg);
        }
+       ast_taskprocessor_unreference(req_serializer);
 
        return PJ_TRUE;
 }
index e4cc51a..4d4d36e 100644 (file)
@@ -30,6 +30,8 @@
 #include "asterisk/dns_naptr.h"
 #include "asterisk/res_pjsip.h"
 #include "include/res_pjsip_private.h"
+#include "asterisk/taskprocessor.h"
+#include "asterisk/threadpool.h"
 
 #ifdef HAVE_PJSIP_EXTERNAL_RESOLVER
 
@@ -52,6 +54,8 @@ struct sip_resolve {
        struct ast_dns_query_set *queries;
        /*! \brief Current viable server addresses */
        pjsip_server_addresses addresses;
+       /*! \brief Serializer to run async callback into pjlib. */
+       struct ast_taskprocessor *serializer;
        /*! \brief Callback to invoke upon completion */
        pjsip_resolver_callback *callback;
        /*! \brief User provided data */
@@ -97,6 +101,7 @@ static void sip_resolve_destroy(void *data)
 
        AST_VECTOR_FREE(&resolve->resolving);
        ao2_cleanup(resolve->queries);
+       ast_taskprocessor_unreference(resolve->serializer);
 }
 
 /*!
@@ -155,10 +160,9 @@ static int sip_resolve_add(struct sip_resolve *resolve, const char *name, int rr
 
        if (!resolve->queries) {
                resolve->queries = ast_dns_query_set_create();
-       }
-
-       if (!resolve->queries) {
-               return -1;
+               if (!resolve->queries) {
+                       return -1;
+               }
        }
 
        if (!port) {
@@ -186,15 +190,18 @@ static int sip_resolve_add(struct sip_resolve *resolve, const char *name, int rr
 static int sip_resolve_invoke_user_callback(void *data)
 {
        struct sip_resolve *resolve = data;
-       int idx;
 
-       for (idx = 0; idx < resolve->addresses.count; ++idx) {
+       if (DEBUG_ATLEAST(2)) {
                /* This includes space for the IP address, [, ], :, and the port */
                char addr[PJ_INET6_ADDRSTRLEN + 10];
+               int idx;
 
-               ast_debug(2, "[%p] Address '%d' is %s with transport '%s'\n",
-                       resolve, idx, pj_sockaddr_print(&resolve->addresses.entry[idx].addr, addr, sizeof(addr), 3),
-                       pjsip_transport_get_type_name(resolve->addresses.entry[idx].type));
+               for (idx = 0; idx < resolve->addresses.count; ++idx) {
+                       pj_sockaddr_print(&resolve->addresses.entry[idx].addr, addr, sizeof(addr), 3);
+                       ast_log(LOG_DEBUG, "[%p] Address '%d' is %s with transport '%s'\n",
+                               resolve, idx, addr,
+                               pjsip_transport_get_type_name(resolve->addresses.entry[idx].type));
+               }
        }
 
        ast_debug(2, "[%p] Invoking user callback with '%d' addresses\n", resolve, resolve->addresses.count);
@@ -396,7 +403,7 @@ static void sip_resolve_callback(const struct ast_dns_query_set *query_set)
 
        /* Push a task to invoke the callback, we do this so it is guaranteed to run in a PJSIP thread */
        ao2_ref(resolve, +1);
-       if (ast_sip_push_task(NULL, sip_resolve_invoke_user_callback, resolve)) {
+       if (ast_sip_push_task(resolve->serializer, sip_resolve_invoke_user_callback, resolve)) {
                ao2_ref(resolve, -1);
        }
 
@@ -514,7 +521,7 @@ static void sip_resolve(pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip
        resolve->callback = cb;
        resolve->token = token;
 
-       if (AST_VECTOR_INIT(&resolve->resolving, 2)) {
+       if (AST_VECTOR_INIT(&resolve->resolving, 4)) {
                ao2_ref(resolve, -1);
                cb(PJ_ENOMEM, token, NULL);
                return;
@@ -563,6 +570,14 @@ static void sip_resolve(pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip
                cb(PJ_ENOMEM, token, NULL);
                return;
        }
+       if (!resolve->queries) {
+               ast_debug(2, "[%p] No resolution queries for target '%s'\n", resolve, host);
+               ao2_ref(resolve, -1);
+               cb(PJLIB_UTIL_EDNSNOANSWERREC, token, NULL);
+               return;
+       }
+
+       resolve->serializer = ao2_bump(ast_threadpool_serializer_get_current());
 
        ast_debug(2, "[%p] Starting initial resolution using parallel queries for target '%s'\n", resolve, host);
        ast_dns_query_set_resolve_async(resolve->queries, sip_resolve_callback, resolve);
index 3f48683..22c4529 100644 (file)
@@ -155,6 +155,8 @@ static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp
        char name[256];
        char media[20];
        char fmt_param[256];
+       enum ast_rtp_options options = session->endpoint->media.g726_non_standard ?
+               AST_RTP_OPT_G726_NONSTANDARD : 0;
 
        ast_rtp_codecs_payloads_initialize(codecs);
 
@@ -176,9 +178,10 @@ static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp
                 if (strcmp(name,"telephone-event") == 0) {
                         tel_event++;
                 }
+
                ast_copy_pj_str(media, (pj_str_t*)&stream->desc.media, sizeof(media));
                ast_rtp_codecs_payloads_set_rtpmap_type_rate(codecs, NULL, pj_strtoul(&stream->desc.fmt[i]),
-                                                            media, name, 0, rtpmap->clock_rate);
+                                                            media, name, options, rtpmap->clock_rate);
                /* Look for an optional associated fmtp attribute */
                if (!(attr = pjmedia_sdp_media_find_attr2(stream, "fmtp", &rtpmap->pt))) {
                        continue;
@@ -304,18 +307,20 @@ static int set_caps(struct ast_sip_session *session, struct ast_sip_session_medi
        return 0;
 }
 
-static pjmedia_sdp_attr* generate_rtpmap_attr(pjmedia_sdp_media *media, pj_pool_t *pool, int rtp_code,
-                                             int asterisk_format, struct ast_format *format, int code)
+static pjmedia_sdp_attr* generate_rtpmap_attr(struct ast_sip_session *session, pjmedia_sdp_media *media, pj_pool_t *pool,
+                                             int rtp_code, int asterisk_format, struct ast_format *format, int code)
 {
        pjmedia_sdp_rtpmap rtpmap;
        pjmedia_sdp_attr *attr = NULL;
        char tmp[64];
+       enum ast_rtp_options options = session->endpoint->media.g726_non_standard ?
+               AST_RTP_OPT_G726_NONSTANDARD : 0;
 
        snprintf(tmp, sizeof(tmp), "%d", rtp_code);
        pj_strdup2(pool, &media->desc.fmt[media->desc.fmt_count++], tmp);
        rtpmap.pt = media->desc.fmt[media->desc.fmt_count - 1];
        rtpmap.clock_rate = ast_rtp_lookup_sample_rate2(asterisk_format, format, code);
-       pj_strdup2(pool, &rtpmap.enc_name, ast_rtp_lookup_mime_subtype2(asterisk_format, format, code, 0));
+       pj_strdup2(pool, &rtpmap.enc_name, ast_rtp_lookup_mime_subtype2(asterisk_format, format, code, options));
        rtpmap.param.slen = 0;
        rtpmap.param.ptr = NULL;
 
@@ -1051,7 +1056,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
                        continue;
                }
 
-               if (!(attr = generate_rtpmap_attr(media, pool, rtp_code, 1, format, 0))) {
+               if (!(attr = generate_rtpmap_attr(session, media, pool, rtp_code, 1, format, 0))) {
                        ao2_ref(format, -1);
                        continue;
                }
@@ -1076,7 +1081,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
                                continue;
                        }
 
-                       if (!(attr = generate_rtpmap_attr(media, pool, rtp_code, 0, NULL, index))) {
+                       if (!(attr = generate_rtpmap_attr(session, media, pool, rtp_code, 0, NULL, index))) {
                                continue;
                        }
 
index 914c8b8..e98756f 100644 (file)
@@ -79,6 +79,25 @@ static pj_status_t ws_send_msg(pjsip_transport *transport,
 static pj_status_t ws_destroy(pjsip_transport *transport)
 {
        struct ws_transport *wstransport = (struct ws_transport *)transport;
+       int fd = ast_websocket_fd(wstransport->ws_session);
+
+       if (fd > 0) {
+               ast_websocket_close(wstransport->ws_session, 1000);
+               shutdown(fd, SHUT_RDWR);
+       }
+
+       ao2_ref(wstransport, -1);
+
+       return PJ_SUCCESS;
+}
+
+static void transport_dtor(void *arg)
+{
+       struct ws_transport *wstransport = arg;
+
+       if (wstransport->ws_session) {
+               ast_websocket_unref(wstransport->ws_session);
+       }
 
        if (wstransport->transport.ref_cnt) {
                pj_atomic_destroy(wstransport->transport.ref_cnt);
@@ -88,20 +107,28 @@ static pj_status_t ws_destroy(pjsip_transport *transport)
                pj_lock_destroy(wstransport->transport.lock);
        }
 
-       pjsip_endpt_release_pool(wstransport->transport.endpt, wstransport->transport.pool);
+       if (wstransport->transport.endpt && wstransport->transport.pool) {
+               pjsip_endpt_release_pool(wstransport->transport.endpt, wstransport->transport.pool);
+       }
 
        if (wstransport->rdata.tp_info.pool) {
                pjsip_endpt_release_pool(wstransport->transport.endpt, wstransport->rdata.tp_info.pool);
        }
-
-       return PJ_SUCCESS;
 }
 
 static int transport_shutdown(void *data)
 {
-       pjsip_transport *transport = data;
+       struct ws_transport *wstransport = data;
+
+       if (!wstransport->transport.is_shutdown && !wstransport->transport.is_destroying) {
+               pjsip_transport_shutdown(&wstransport->transport);
+       }
+
+       /* Note that the destructor calls PJSIP functions,
+        * therefore it must be called in a PJSIP thread.
+        */
+       ao2_ref(wstransport, -1);
 
-       pjsip_transport_shutdown(transport);
        return 0;
 }
 
@@ -116,32 +143,45 @@ struct transport_create_data {
 static int transport_create(void *data)
 {
        struct transport_create_data *create_data = data;
-       struct ws_transport *newtransport;
+       struct ws_transport *newtransport = NULL;
 
        pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
        struct pjsip_tpmgr *tpmgr = pjsip_endpt_get_tpmgr(endpt);
 
        pj_pool_t *pool;
-
        pj_str_t buf;
+       pj_status_t status;
 
-       if (!(pool = pjsip_endpt_create_pool(endpt, "ws", 512, 512))) {
-               ast_log(LOG_ERROR, "Failed to allocate WebSocket endpoint pool.\n");
-               return -1;
+       newtransport = ao2_t_alloc_options(sizeof(*newtransport), transport_dtor,
+                       AO2_ALLOC_OPT_LOCK_NOLOCK, "pjsip websocket transport");
+       if (!newtransport) {
+               ast_log(LOG_ERROR, "Failed to allocate WebSocket transport.\n");
+               goto on_error;
        }
 
-       if (!(newtransport = PJ_POOL_ZALLOC_T(pool, struct ws_transport))) {
-               ast_log(LOG_ERROR, "Failed to allocate WebSocket transport.\n");
-               pjsip_endpt_release_pool(endpt, pool);
-               return -1;
+       newtransport->transport.endpt = endpt;
+
+       if (!(pool = pjsip_endpt_create_pool(endpt, "ws", 512, 512))) {
+               ast_log(LOG_ERROR, "Failed to allocate WebSocket endpoint pool.\n");
+               goto on_error;
        }
 
+       newtransport->transport.pool = pool;
        newtransport->ws_session = create_data->ws_session;
 
-       pj_atomic_create(pool, 0, &newtransport->transport.ref_cnt);
-       pj_lock_create_recursive_mutex(pool, pool->obj_name, &newtransport->transport.lock);
+       /* Keep the session until transport dies */
+       ast_websocket_ref(newtransport->ws_session);
+
+       status = pj_atomic_create(pool, 0, &newtransport->transport.ref_cnt);
+       if (status != PJ_SUCCESS) {
+               goto on_error;
+       }
+
+       status = pj_lock_create_recursive_mutex(pool, pool->obj_name, &newtransport->transport.lock);
+       if (status != PJ_SUCCESS) {
+               goto on_error;
+       }
 
-       newtransport->transport.pool = pool;
        pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ast_sockaddr_stringify(ast_websocket_remote_address(newtransport->ws_session))), &newtransport->transport.key.rem_addr);
        newtransport->transport.key.rem_addr.addr.sa_family = pj_AF_INET();
        newtransport->transport.key.type = ast_websocket_is_secure(newtransport->ws_session) ? transport_type_wss : transport_type_ws;
@@ -159,24 +199,34 @@ static int transport_create(void *data)
        newtransport->transport.flag = pjsip_transport_get_flag_from_type((pjsip_transport_type_e)newtransport->transport.key.type);
        newtransport->transport.info = (char *)pj_pool_alloc(newtransport->transport.pool, 64);
 
-       newtransport->transport.endpt = endpt;
        newtransport->transport.tpmgr = tpmgr;
        newtransport->transport.send_msg = &ws_send_msg;
        newtransport->transport.destroy = &ws_destroy;
 
-       pjsip_transport_register(newtransport->transport.tpmgr, (pjsip_transport *)newtransport);
+       status = pjsip_transport_register(newtransport->transport.tpmgr,
+                       (pjsip_transport *)newtransport);
+       if (status != PJ_SUCCESS) {
+               goto on_error;
+       }
+
+       /* Add a reference for pjsip transport manager */
+       ao2_ref(newtransport, +1);
 
        newtransport->rdata.tp_info.transport = &newtransport->transport;
        newtransport->rdata.tp_info.pool = pjsip_endpt_create_pool(endpt, "rtd%p",
                PJSIP_POOL_RDATA_LEN, PJSIP_POOL_RDATA_INC);
        if (!newtransport->rdata.tp_info.pool) {
                ast_log(LOG_ERROR, "Failed to allocate WebSocket rdata.\n");
-               pjsip_endpt_release_pool(endpt, pool);
-               return -1;
+               pjsip_transport_destroy((pjsip_transport *)newtransport);
+               goto on_error;
        }
 
        create_data->transport = newtransport;
        return 0;
+
+on_error:
+       ao2_cleanup(newtransport);
+       return -1;
 }
 
 struct transport_read_data {
index a4e86a6..3eeb651 100644 (file)
@@ -823,7 +823,7 @@ AST_TEST_DEFINE(resolve_sync)
        case TEST_INIT:
                info->name = "resolve_sync";
                info->category = "/res/res_resolver_unbound/";
-               info->summary = "Test nominal synchronous resolution using libunbound\n";
+               info->summary = "Test nominal synchronous resolution using libunbound";
                info->description = "This test performs the following:\n"
                        "\t* Set two static A records and one static AAAA record on one domain\n"
                        "\t* Set an A record for a second domain\n"
@@ -832,7 +832,7 @@ AST_TEST_DEFINE(resolve_sync)
                        "\t* Perform an AAAA record lookup on the first domain\n"
                        "\t* Ensure that the AAAA record is returned and no A record is returned\n"
                        "\t* Perform an A record lookup on the second domain\n"
-                       "\t* Ensure that the A record from the second domain is returned\n";
+                       "\t* Ensure that the A record from the second domain is returned";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
@@ -847,7 +847,7 @@ AST_TEST_DEFINE(resolve_async)
        case TEST_INIT:
                info->name = "resolve_async";
                info->category = "/res/res_resolver_unbound/";
-               info->summary = "Test nominal asynchronous resolution using libunbound\n";
+               info->summary = "Test nominal asynchronous resolution using libunbound";
                info->description = "This test performs the following:\n"
                        "\t* Set two static A records and one static AAAA record on one domain\n"
                        "\t* Set an A record for a second domain\n"
@@ -856,7 +856,7 @@ AST_TEST_DEFINE(resolve_async)
                        "\t* Perform an AAAA record lookup on the first domain\n"
                        "\t* Ensure that the AAAA record is returned and no A record is returned\n"
                        "\t* Perform an A record lookup on the second domain\n"
-                       "\t* Ensure that the A record from the second domain is returned\n";
+                       "\t* Ensure that the A record from the second domain is returned";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
@@ -1064,11 +1064,11 @@ AST_TEST_DEFINE(resolve_sync_off_nominal)
        case TEST_INIT:
                info->name = "resolve_sync_off_nominal";
                info->category = "/res/res_resolver_unbound/";
-               info->summary = "Test off-nominal synchronous resolution using libunbound\n";
+               info->summary = "Test off-nominal synchronous resolution using libunbound";
                info->description = "This test performs the following:\n"
                        "\t* Attempt a lookup of a non-existent domain\n"
                        "\t* Attempt a lookup of a AAAA record on a domain that contains only A records\n"
-                       "\t* Attempt a lookup of an A record on Chaos-net\n";
+                       "\t* Attempt a lookup of an A record on Chaos-net";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
@@ -1083,11 +1083,11 @@ AST_TEST_DEFINE(resolve_async_off_nominal)
        case TEST_INIT:
                info->name = "resolve_async_off_nominal";
                info->category = "/res/res_resolver_unbound/";
-               info->summary = "Test off-nominal synchronous resolution using libunbound\n";
+               info->summary = "Test off-nominal synchronous resolution using libunbound";
                info->description = "This test performs the following:\n"
                        "\t* Attempt a lookup of a non-existent domain\n"
                        "\t* Attempt a lookup of a AAAA record on a domain that contains only A records\n"
-                       "\t* Attempt a lookup of an A record on Chaos-net\n";
+                       "\t* Attempt a lookup of an A record on Chaos-net";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
@@ -1152,10 +1152,10 @@ AST_TEST_DEFINE(resolve_cancel_off_nominal)
        case TEST_INIT:
                info->name = "resolve_cancel_off_nominal";
                info->category = "/res/res_resolver_unbound/";
-               info->summary = "Off nominal cancellation test using libunbound\n";
+               info->summary = "Off nominal cancellation test using libunbound";
                info->description = "This test does the following:\n"
                        "\t* Perform an asynchronous query\n"
-                       "\t* Once the query has completed, attempt to cancel it\n";
+                       "\t* Once the query has completed, attempt to cancel it";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
@@ -1220,9 +1220,9 @@ AST_TEST_DEFINE(resolve_naptr)
        case TEST_INIT:
                info->name = "resolve_naptr";
                info->category = "/res/res_resolver_unbound/";
-               info->summary = "Attempt resolution of NAPTR record\n";
+               info->summary = "Attempt resolution of NAPTR record";
                info->description = "This test performs a NAPTR lookup and ensures that\n"
-                       "the returned record has the appropriate values set\n";
+                       "the returned record has the appropriate values set";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
@@ -1319,11 +1319,11 @@ AST_TEST_DEFINE(resolve_srv)
        case TEST_INIT:
                info->name = "resolve_srv";
                info->category = "/res/res_resolver_unbound/";
-               info->summary = "Test synchronous SRV resolution using libunbound\n";
+               info->summary = "Test synchronous SRV resolution using libunbound";
                info->description = "This test performs the following:\n"
                        "\t* Set one SRV record on one domain\n"
                        "\t* Perform an SRV lookup on the domain\n"
-                       "\t* Ensure that the SRV record returned matches the expected value\n";
+                       "\t* Ensure that the SRV record returned matches the expected value";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
index 13306ad..b27281e 100644 (file)
@@ -62,7 +62,7 @@ AST_TEST_DEFINE(resolver_register_unregister)
                        "The test performs the following steps:\n"
                        "\t* Register a valid resolver.\n"
                        "\t* Unregister the resolver.\n"
-                       "If either step fails, the test fails\n";
+                       "If either step fails, the test fails";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
@@ -114,7 +114,7 @@ AST_TEST_DEFINE(resolver_register_off_nominal)
                        "\t* Register a duplicate resolver\n"
                        "\t* Register a resolver without a name\n"
                        "\t* Register a resolver without a resolve() method\n"
-                       "\t* Register a resolver without a cancel() method\n";
+                       "\t* Register a resolver without a cancel() method";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
@@ -174,7 +174,7 @@ AST_TEST_DEFINE(resolver_unregister_off_nominal)
                        "\t* Unregister a resolver that is not registered.\n"
                        "\t* Unregister a NULL pointer.\n"
                        "Because unregistering a resolver does not return an indicator of success, the best\n"
-                       "this test can do is verify that nothing blows up when this is attempted.\n";
+                       "this test can do is verify that nothing blows up when this is attempted.";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
@@ -210,7 +210,7 @@ AST_TEST_DEFINE(resolver_data)
                        "\t* Ensure that setting resolver data does not result in an error.\n"
                        "\t* Ensure that retrieving the set resolver data returns the data we expect\n"
                        "\t* Ensure that setting new resolver data on the query does not result in an error\n"
-                       "\t* Ensure that retrieving the resolver data returns the new data that we set\n";
+                       "\t* Ensure that retrieving the resolver data returns the new data that we set";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
@@ -361,13 +361,13 @@ AST_TEST_DEFINE(resolver_set_result_off_nominal)
        case TEST_INIT:
                info->name = "resolver_set_result_off_nominal";
                info->category = "/main/dns/";
-               info->summary = "Test setting off-nominal DNS results\n";
+               info->summary = "Test setting off-nominal DNS results";
                info->description =
                        "This test performs the following:\n"
                        "\t* Attempt to add a DNS result that is both bogus and secure\n"
                        "\t* Attempt to add a DNS result that has no canonical name\n"
                        "\t* Attempt to add a DNS result that has no answer\n"
-                       "\t* Attempt to add a DNS result with a zero answer size\n";
+                       "\t* Attempt to add a DNS result with a zero answer size";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
@@ -474,7 +474,7 @@ AST_TEST_DEFINE(resolver_add_record)
                        "\t* Ensure a nominal A record can be added to a query result\n"
                        "\t* Ensures that the record can be retrieved\n"
                        "\t* Ensure that a second record can be added to the query result\n"
-                       "\t* Ensures that both records can be retrieved\n";
+                       "\t* Ensures that both records can be retrieved";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
@@ -585,7 +585,7 @@ AST_TEST_DEFINE(resolver_add_record_off_nominal)
                        "\t* Ensure that an A record with invalid RR classes cannot be added to a query\n"
                        "\t* Ensure that an A record with invalid TTL cannot be added to a query\n"
                        "\t* Ensure that an A record with NULL data cannot be added to a query\n"
-                       "\t* Ensure that an A record with invalid length cannot be added to a query\n";
+                       "\t* Ensure that an A record with invalid length cannot be added to a query";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
@@ -917,7 +917,7 @@ AST_TEST_DEFINE(resolver_resolve_sync_off_nominal)
                        "\t* Attempt resolution with invalid RR type\n",
                        "\t* Attempt resolution with invalid RR class\n",
                        "\t* Attempt resolution with NULL result pointer\n",
-                       "\t* Attempt resolution with resolver that returns an error\n";
+                       "\t* Attempt resolution with resolver that returns an error";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
@@ -1174,7 +1174,7 @@ AST_TEST_DEFINE(resolver_resolve_async_off_nominal)
                        "\t* Attempt resolution with invalid RR type\n",
                        "\t* Attempt resolution with invalid RR class\n",
                        "\t* Attempt resolution with NULL callback pointer\n",
-                       "\t* Attempt resolution with resolver that returns an error\n";
+                       "\t* Attempt resolution with resolver that returns an error";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
@@ -1232,7 +1232,7 @@ AST_TEST_DEFINE(resolver_resolve_async_cancel)
                        "This test performs an asynchronous DNS resolution of a domain and then cancels\n"
                        "the resolution. The goal of this test is to ensure that the cancel() callback of\n"
                        "the resolver is called and that it properly interrupts the resolution such that no\n"
-                       "records are returned.\n";
+                       "records are returned.";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
index 9acf1bf..98a6051 100644 (file)
@@ -290,14 +290,14 @@ AST_TEST_DEFINE(query_set)
        case TEST_INIT:
                info->name = "query_set";
                info->category = "/main/dns/query_set/";
-               info->summary = "Test nominal asynchronous DNS query set\n";
+               info->summary = "Test nominal asynchronous DNS query set";
                info->description =
                        "This tests nominal query set in the following ways:\n"
                        "\t* Multiple queries are added to a query set\n"
                        "\t* The mock resolver is configured to respond to all queries\n"
                        "\t* Asynchronous resolution of the query set is started\n"
                        "\t* The mock resolver responds to all queries\n"
-                       "\t* We ensure that the query set callback is invoked upon completion\n";
+                       "\t* We ensure that the query set callback is invoked upon completion";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
@@ -306,19 +306,39 @@ AST_TEST_DEFINE(query_set)
        return query_set_test(test, 4, 0);
 }
 
+AST_TEST_DEFINE(query_set_empty)
+{
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = "query_set_empty";
+               info->category = "/main/dns/query_set/";
+               info->summary = "Test nominal asynchronous empty DNS query set";
+               info->description =
+                       "This tests nominal query set in the following ways:\n"
+                       "\t* No queries are added to a query set\n"
+                       "\t* Asynchronous resolution of the query set is started\n"
+                       "\t* We ensure that the query set callback is invoked upon completion";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       return query_set_test(test, 0, 0);
+}
+
 AST_TEST_DEFINE(query_set_nominal_cancel)
 {
        switch (cmd) {
        case TEST_INIT:
                info->name = "query_set_nominal_cancel";
                info->category = "/main/dns/query_set/";
-               info->summary = "Test nominal asynchronous DNS query set cancellation\n";
+               info->summary = "Test nominal asynchronous DNS query set cancellation";
                info->description =
                        "This tests nominal query set cancellation in the following ways:\n"
                        "\t* Multiple queries are added to a query set\n"
                        "\t* The mock resolver is configured to NOT respond to any queries\n"
                        "\t* Asynchronous resolution of the query set is started\n"
-                       "\t* The query set is canceled and is confirmed to return with success\n";
+                       "\t* The query set is canceled and is confirmed to return with success";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
@@ -333,14 +353,14 @@ AST_TEST_DEFINE(query_set_off_nominal_cancel)
        case TEST_INIT:
                info->name = "query_set_off_nominal_cancel";
                info->category = "/main/dns/query_set/";
-               info->summary = "Test off-nominal asynchronous DNS query set cancellation\n";
+               info->summary = "Test off-nominal asynchronous DNS query set cancellation";
                info->description =
                        "This tests nominal query set cancellation in the following ways:\n"
                        "\t* Multiple queries are added to a query set\n"
                        "\t* The mock resolver is configured to respond to half the queries\n"
                        "\t* Asynchronous resolution of the query set is started\n"
                        "\t* The query set is canceled and is confirmed to return failure\n"
-                       "\t* The query set callback is confirmed to run, since it could not be fully canceled\n";
+                       "\t* The query set callback is confirmed to run, since it could not be fully canceled";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
@@ -352,6 +372,7 @@ AST_TEST_DEFINE(query_set_off_nominal_cancel)
 static int unload_module(void)
 {
        AST_TEST_UNREGISTER(query_set);
+       AST_TEST_UNREGISTER(query_set_empty);
        AST_TEST_UNREGISTER(query_set_nominal_cancel);
        AST_TEST_UNREGISTER(query_set_off_nominal_cancel);
 
@@ -361,6 +382,7 @@ static int unload_module(void)
 static int load_module(void)
 {
        AST_TEST_REGISTER(query_set);
+       AST_TEST_REGISTER(query_set_empty);
        AST_TEST_REGISTER(query_set_nominal_cancel);
        AST_TEST_REGISTER(query_set_off_nominal_cancel);
 
index 3d38c64..84cd9d4 100644 (file)
@@ -278,7 +278,7 @@ AST_TEST_DEFINE(recurring_query)
        case TEST_INIT:
                info->name = "recurring_query";
                info->category = "/main/dns/recurring/";
-               info->summary = "Test nominal asynchronous recurring DNS queries\n";
+               info->summary = "Test nominal asynchronous recurring DNS queries";
                info->description =
                        "This tests nominal recurring queries in the following ways:\n"
                        "\t* An asynchronous query is sent to a mock resolver\n"
@@ -286,7 +286,7 @@ AST_TEST_DEFINE(recurring_query)
                        "\t* We ensure that the query re-occurs according to the lower of the TTLs\n"
                        "\t* The mock resolver returns two records, this time with different TTLs\n"
                        "\t  from the first time the query was resolved\n"
-                       "\t* We ensure that the query re-occurs according to the new lower TTL\n";
+                       "\t* We ensure that the query re-occurs according to the new lower TTL";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
@@ -403,8 +403,7 @@ AST_TEST_DEFINE(recurring_query_off_nominal)
                        "\t* Attempt resolution with invalid RR type\n",
                        "\t* Attempt resolution with invalid RR class\n",
                        "\t* Attempt resolution with NULL callback pointer\n",
-                       "\t* Attempt resolution with resolver that returns an error\n";
-
+                       "\t* Attempt resolution with resolver that returns an error";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
@@ -459,11 +458,11 @@ AST_TEST_DEFINE(recurring_query_cancel_between)
        case TEST_INIT:
                info->name = "recurring_query_cancel_between";
                info->category = "/main/dns/recurring/";
-               info->summary = "Test canceling a recurring DNS query during the downtime between queries\n";
+               info->summary = "Test canceling a recurring DNS query during the downtime between queries";
                info->description = "This test does the following:\n"
                        "\t* Issue a recurring DNS query.\n"
                        "\t* Once results have been returned, cancel the recurring query.\n"
-                       "\t* Wait a while to ensure that no more queries are occurring.\n";
+                       "\t* Wait a while to ensure that no more queries are occurring.";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
@@ -540,13 +539,13 @@ AST_TEST_DEFINE(recurring_query_cancel_during)
        case TEST_INIT:
                info->name = "recurring_query_cancel_during";
                info->category = "/main/dns/recurring/";
-               info->summary = "Cancel a recurring DNS query while a query is actually happening\n";
+               info->summary = "Cancel a recurring DNS query while a query is actually happening";
                info->description = "This test does the following:\n"
                        "\t* Initiate a recurring DNS query.\n"
                        "\t* Allow the initial query to complete, and a second query to start\n"
                        "\t* Cancel the recurring query while the second query is executing\n"
                        "\t* Ensure that the resolver's cancel() method was called\n"
-                       "\t* Wait a while to make sure that recurring queries are no longer occurring\n";
+                       "\t* Wait a while to make sure that recurring queries are no longer occurring";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;