INFO/Record request configurable to use dynamic features
[asterisk/asterisk.git] / channels / chan_sip.c
index 0c57d45..99fb9b0 100644 (file)
@@ -1078,21 +1078,24 @@ static void destroy_escs(void)
        }
 }
 
-/*! \brief
+/*!
+ * \details
  * Here we implement the container for dialogs which are in the
  * dialog_needdestroy state to iterate only through the dialogs
  * unlink them instead of iterate through all dialogs
  */
 struct ao2_container *dialogs_needdestroy;
 
-/*! \brief
+/*!
+ * \details
  * Here we implement the container for dialogs which have rtp
  * traffic and rtptimeout, rtpholdtimeout or rtpkeepalive
  * set. We use this container instead the whole dialog list.
  */
 struct ao2_container *dialogs_rtpcheck;
 
-/*! \brief
+/*!
+ * \details
  * Here we implement the container for dialogs (sip_pvt), defining
  * generic wrapper functions to ease the transition from the current
  * implementation (a single linked list) to a different container.
@@ -1289,7 +1292,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
 static struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *totag, const char *fromtag);
 static void check_pendings(struct sip_pvt *p);
 static void *sip_park_thread(void *stuff);
-static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, int seqno, char *parkexten);
+static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, int seqno, const char *park_exten, const char *park_context);
 
 static void *sip_pickup_thread(void *stuff);
 static int sip_pickup(struct ast_channel *chan);
@@ -1341,7 +1344,7 @@ static void add_realm_authentication(struct sip_auth_container **credentials, co
 static struct sip_auth *find_realm_authentication(struct sip_auth_container *credentials, const char *realm);
 
 /*--- Misc functions */
-static void check_rtp_timeout(struct sip_pvt *dialog, time_t t);
+static int check_rtp_timeout(struct sip_pvt *dialog, time_t t);
 static int reload_config(enum channelreloadreason reason);
 static void add_diversion_header(struct sip_request *req, struct sip_pvt *pvt);
 static int expire_register(const void *data);
@@ -1350,6 +1353,7 @@ static int restart_monitor(void);
 static void peer_mailboxes_to_str(struct ast_str **mailbox_str, struct sip_peer *peer);
 static struct ast_variable *copy_vars(struct ast_variable *src);
 static int dialog_find_multiple(void *obj, void *arg, int flags);
+static struct ast_channel *sip_pvt_lock_full(struct sip_pvt *pvt);
 /* static int sip_addrcmp(char *name, struct sockaddr_in *sin);        Support for peer matching */
 static int sip_refer_allocate(struct sip_pvt *p);
 static int sip_notify_allocate(struct sip_pvt *p);
@@ -1380,6 +1384,7 @@ static void  print_group(int fd, ast_group_t group, int crlf);
 static const char *dtmfmode2str(int mode) attribute_const;
 static int str2dtmfmode(const char *str) attribute_unused;
 static const char *insecure2str(int mode) attribute_const;
+static const char *allowoverlap2str(int mode) attribute_const;
 static void cleanup_stale_contexts(char *new, char *old);
 static void print_codec_to_cli(int fd, struct ast_codec_pref *pref);
 static const char *domain_mode_to_text(const enum domain_mode mode);
@@ -1447,7 +1452,7 @@ static void set_socket_transport(struct sip_socket *socket, int transport);
 static void realtime_update_peer(const char *peername, struct ast_sockaddr *addr, const char *username, const char *fullcontact, const char *useragent, int expirey, unsigned short deprecated_username, int lastms);
 static void update_peer(struct sip_peer *p, int expire);
 static struct ast_variable *get_insecure_variable_from_config(struct ast_config *config);
-static const char *get_name_from_variable(struct ast_variable *var, const char *newpeername);
+static const char *get_name_from_variable(const struct ast_variable *var);
 static struct sip_peer *realtime_peer(const char *peername, struct ast_sockaddr *sin, int devstate_only, int which_objects);
 static char *sip_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
 
@@ -1487,7 +1492,7 @@ static void check_via(struct sip_pvt *p, struct sip_request *req);
 static int get_rpid(struct sip_pvt *p, struct sip_request *oreq);
 static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, char **number, int *reason);
 static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_request *oreq, int *cc_recall_core_id);
-static int get_msg_text(char *buf, int len, struct sip_request *req, int addnewline);
+static int get_msg_text(char *buf, int len, struct sip_request *req);
 static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout);
 static void update_connectedline(struct sip_pvt *p, const void *data, size_t datalen);
 static void update_redirecting(struct sip_pvt *p, const void *data, size_t datalen);
@@ -1513,6 +1518,7 @@ static int create_addr_from_peer(struct sip_pvt *r, struct sip_peer *peer);
 static int create_addr(struct sip_pvt *dialog, const char *opeer, struct ast_sockaddr *addr, int newdialog, struct ast_sockaddr *remote_address);
 static char *generate_random_string(char *buf, size_t size);
 static void build_callid_pvt(struct sip_pvt *pvt);
+static void change_callid_pvt(struct sip_pvt *pvt, const char *callid);
 static void build_callid_registry(struct sip_registry *reg, const struct ast_sockaddr *ourip, const char *fromdomain);
 static void make_our_tag(char *tagbuf, size_t len);
 static int add_header(struct sip_request *req, const char *var, const char *value);
@@ -2807,14 +2813,7 @@ cleanup:
 
        if (tcptls_session) {
                ast_mutex_lock(&tcptls_session->lock);
-               if (tcptls_session->f) {
-                       fclose(tcptls_session->f);
-                       tcptls_session->f = NULL;
-               }
-               if (tcptls_session->fd != -1) {
-                       close(tcptls_session->fd);
-                       tcptls_session->fd = -1;
-               }
+               ast_tcptls_close_session_file(tcptls_session);
                tcptls_session->parent = NULL;
                ast_mutex_unlock(&tcptls_session->lock);
 
@@ -2946,15 +2945,6 @@ static void ref_proxy(struct sip_pvt *pvt, struct sip_proxy *proxy)
        }
 }
 
- /*!
- * \brief Unlink a dialog from the dialogs_checkrtp container
- */
-static void *dialog_unlink_rtpcheck(struct sip_pvt *dialog)
-{
-       ao2_t_unlink(dialogs_rtpcheck, dialog, "unlinking dialog_rtpcheck via ao2_unlink");
-       return NULL;
-}
-
 /*!
  * \brief Unlink a dialog from the dialogs container, as well as any other places
  * that it may be currently stored.
@@ -2962,9 +2952,10 @@ static void *dialog_unlink_rtpcheck(struct sip_pvt *dialog)
  * \note A reference to the dialog must be held before calling this function, and this
  * function does not release that reference.
  */
-void *dialog_unlink_all(struct sip_pvt *dialog, int lockowner, int lockdialoglist)
+void dialog_unlink_all(struct sip_pvt *dialog)
 {
        struct sip_pkt *cp;
+       struct ast_channel *owner;
 
        dialog_ref(dialog, "Let's bump the count in the unlink so it doesn't accidentally become dead before we are done");
 
@@ -2973,26 +2964,25 @@ void *dialog_unlink_all(struct sip_pvt *dialog, int lockowner, int lockdialoglis
        ao2_t_unlink(dialogs_rtpcheck, dialog, "unlinking dialog_rtpcheck via ao2_unlink");
 
        /* Unlink us from the owner (channel) if we have one */
-       if (dialog->owner) {
-               if (lockowner) {
-                       ast_channel_lock(dialog->owner);
-               }
-               ast_debug(1, "Detaching from channel %s\n", dialog->owner->name);
-               dialog->owner->tech_pvt = dialog_unref(dialog->owner->tech_pvt, "resetting channel dialog ptr in unlink_all");
-               if (lockowner) {
-                       ast_channel_unlock(dialog->owner);
-               }
+       owner = sip_pvt_lock_full(dialog);
+       if (owner) {
+               ast_debug(1, "Detaching from channel %s\n", owner->name);
+               owner->tech_pvt = dialog_unref(owner->tech_pvt, "resetting channel dialog ptr in unlink_all");
+               ast_channel_unlock(owner);
+               ast_channel_unref(owner);
+               dialog->owner = NULL;
        }
+       sip_pvt_unlock(dialog);
+
        if (dialog->registry) {
                if (dialog->registry->call == dialog) {
                        dialog->registry->call = dialog_unref(dialog->registry->call, "nulling out the registry's call dialog field in unlink_all");
                }
                dialog->registry = registry_unref(dialog->registry, "delete dialog->registry");
        }
-       if (dialog->stateid > -1) {
-               ast_extension_state_del(dialog->stateid, NULL);
-               dialog_unref(dialog, "removing extension_state, should unref the associated dialog ptr that was stored there.");
-               dialog->stateid = -1; /* shouldn't we 'zero' this out? */
+       if (dialog->stateid != -1) {
+               ast_extension_state_del(dialog->stateid, cb_extensionstate);
+               dialog->stateid = -1;
        }
        /* Remove link from peer to subscription of MWI */
        if (dialog->relatedpeer && dialog->relatedpeer->mwipvt == dialog) {
@@ -3036,7 +3026,6 @@ void *dialog_unlink_all(struct sip_pvt *dialog, int lockowner, int lockdialoglis
        }
 
        dialog_unref(dialog, "Let's unbump the count in the unlink so the poor pvt can disappear if it is time");
-       return NULL;
 }
 
 void *registry_unref(struct sip_registry *reg, char *tag)
@@ -3075,11 +3064,11 @@ static inline void pvt_set_needdestroy(struct sip_pvt *pvt, const char *reason)
        if (pvt->final_destruction_scheduled) {
                return; /* This is already scheduled for final destruction, let the scheduler take care of it. */
        }
-       if(pvt->needdestroy != 1) {
+       append_history(pvt, "NeedDestroy", "Setting needdestroy because %s", reason);
+       if (!pvt->needdestroy) {
+               pvt->needdestroy = 1;
                ao2_t_link(dialogs_needdestroy, pvt, "link pvt into dialogs_needdestroy container");
        }
-       append_history(pvt, "NeedDestroy", "Setting needdestroy because %s", reason);
-       pvt->needdestroy = 1;
 }
 
 /*! \brief Initialize the initital request packet in the pvt structure.
@@ -3852,6 +3841,7 @@ static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int res
 static int __sip_autodestruct(const void *data)
 {
        struct sip_pvt *p = (struct sip_pvt *)data;
+       struct ast_channel *owner;
 
        /* If this is a subscription, tell the phone that we got a timeout */
        if (p->subscribed && p->subscribed != MWI_NOTIFICATION && p->subscribed != CALL_COMPLETION) {
@@ -3887,17 +3877,12 @@ static int __sip_autodestruct(const void *data)
        /*
         * Lock both the pvt and the channel safely so that we can queue up a frame.
         */
-       sip_pvt_lock(p);
-       while (p->owner && ast_channel_trylock(p->owner)) {
-               sip_pvt_unlock(p);
-               sched_yield();
-               sip_pvt_lock(p);
-       }
-
-       if (p->owner) {
+       owner = sip_pvt_lock_full(p);
+       if (owner) {
                ast_log(LOG_WARNING, "Autodestruct on dialog '%s' with owner in place (Method: %s)\n", p->callid, sip_methods[p->method].text);
-               ast_queue_hangup_with_cause(p->owner, AST_CAUSE_PROTOCOL_ERROR);
-               ast_channel_unlock(p->owner);
+               ast_queue_hangup_with_cause(owner, AST_CAUSE_PROTOCOL_ERROR);
+               ast_channel_unlock(owner);
+               ast_channel_unref(owner);
        } else if (p->refer && !p->alreadygone) {
                ast_debug(3, "Finally hanging up channel after transfer: %s\n", p->callid);
                transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1);
@@ -3906,7 +3891,9 @@ static int __sip_autodestruct(const void *data)
        } else {
                append_history(p, "AutoDestroy", "%s", p->callid);
                ast_debug(3, "Auto destroying SIP dialog '%s'\n", p->callid);
-               dialog_unlink_all(p, TRUE, TRUE); /* once it's unlinked and unrefd everywhere, it'll be freed automagically */
+               sip_pvt_unlock(p);
+               dialog_unlink_all(p); /* once it's unlinked and unrefd everywhere, it'll be freed automagically */
+               sip_pvt_lock(p);
                /* dialog_unref(p, "unref dialog-- no other matching conditions"); -- unlink all now should finish off the dialog's references and free it. */
                /* sip_destroy(p); */           /* Go ahead and destroy dialog. All attempts to recover is done */
                /* sip_destroy also absorbs the reference */
@@ -4597,18 +4584,27 @@ static void sip_destroy_peer_fn(void *peer)
 static void sip_destroy_peer(struct sip_peer *peer)
 {
        ast_debug(3, "Destroying SIP peer %s\n", peer->name);
-       if (peer->outboundproxy)
+
+       /*
+        * Remove any mailbox event subscriptions for this peer before
+        * we destroy anything.  An event subscription callback may be
+        * happening right now.
+        */
+       clear_peer_mailboxes(peer);
+
+       if (peer->outboundproxy) {
                ao2_ref(peer->outboundproxy, -1);
-       peer->outboundproxy = NULL;
+               peer->outboundproxy = NULL;
+       }
 
        /* Delete it, it needs to disappear */
        if (peer->call) {
-               dialog_unlink_all(peer->call, TRUE, TRUE);
+               dialog_unlink_all(peer->call);
                peer->call = dialog_unref(peer->call, "peer->call is being unset");
        }
 
        if (peer->mwipvt) {     /* We have an active subscription, delete it */
-               dialog_unlink_all(peer->mwipvt, TRUE, TRUE);
+               dialog_unlink_all(peer->mwipvt);
                peer->mwipvt = dialog_unref(peer->mwipvt, "unreffing peer->mwipvt");
        }
 
@@ -4633,7 +4629,6 @@ static void sip_destroy_peer(struct sip_peer *peer)
        }
        if (peer->dnsmgr)
                ast_dnsmgr_release(peer->dnsmgr);
-       clear_peer_mailboxes(peer);
 
        if (peer->socket.tcptls_session) {
                ao2_ref(peer->socket.tcptls_session, -1);
@@ -4653,7 +4648,7 @@ static void update_peer(struct sip_peer *p, int expire)
        int rtcachefriends = ast_test_flag(&p->flags[1], SIP_PAGE2_RTCACHEFRIENDS);
        if (sip_cfg.peer_rtupdate &&
            (p->is_realtime || rtcachefriends)) {
-               realtime_update_peer(p->name, &p->addr, p->username, rtcachefriends ? p->fullcontact : NULL, p->useragent, expire, p->deprecated_username, p->lastms);
+               realtime_update_peer(p->name, &p->addr, p->username, p->fullcontact, p->useragent, expire, p->deprecated_username, p->lastms);
        }
 }
 
@@ -4674,177 +4669,280 @@ static struct ast_variable *get_insecure_variable_from_config(struct ast_config
        return var;
 }
 
-static const char *get_name_from_variable(struct ast_variable *var, const char *newpeername)
+static struct ast_variable *get_insecure_variable_from_sippeers(const char *column, const char *value)
 {
-       struct ast_variable *tmp;
-       for (tmp = var; tmp; tmp = tmp->next) {
-               if (!newpeername && !strcasecmp(tmp->name, "name"))
-                       newpeername = tmp->value;
+       struct ast_config *peerlist;
+       struct ast_variable *var = NULL;
+       if ((peerlist = ast_load_realtime_multientry("sippeers", column, value, "insecure LIKE", "%port%", SENTINEL))) {
+               if ((var = get_insecure_variable_from_config(peerlist))) {
+                       /* Must clone, because var will get freed along with
+                        * peerlist. */
+                       var = ast_variables_dup(var);
+               }
+               ast_config_destroy(peerlist);
        }
-       return newpeername;
+       return var;
 }
 
-/*! \brief  realtime_peer: Get peer from realtime storage
- * Checks the "sippeers" realtime family from extconfig.conf
- * Checks the "sipregs" realtime family from extconfig.conf if it's configured.
- * This returns a pointer to a peer and because we use build_peer, we can rest
- * assured that the refcount is bumped.
-*/
-static struct sip_peer *realtime_peer(const char *newpeername, struct ast_sockaddr *addr, int devstate_only, int which_objects)
+/* Yes.. the only column that makes sense to pass is "ipaddr", but for
+ * consistency's sake, we require the column name to be passed. As extra
+ * argument, we take a pointer to var. We already got the info, so we better
+ * return it and save the caller a query. If return value is nonzero, then *var
+ * is nonzero too (and the other way around). */
+static struct ast_variable *get_insecure_variable_from_sipregs(const char *column, const char *value, struct ast_variable **var)
 {
-       struct sip_peer *peer;
-       struct ast_variable *var = NULL;
        struct ast_variable *varregs = NULL;
-       struct ast_variable *tmp;
-       struct ast_config *peerlist = NULL;
-       char ipaddr[INET6_ADDRSTRLEN];
-       char portstring[6]; /*up to 5 digits plus null terminator*/
-       int realtimeregs = ast_check_realtime("sipregs");
+       struct ast_config *regs, *peers;
+       char *regscat;
+       const char *regname;
 
-       /* First check on peer name */
-       if (newpeername) {
-               if (realtimeregs)
-                       varregs = ast_load_realtime("sipregs", "name", newpeername, SENTINEL);
-
-               var = ast_load_realtime("sippeers", "name", newpeername, "host", "dynamic", SENTINEL);
-               if (!var && addr) {
-                       var = ast_load_realtime("sippeers", "name", newpeername, "host", ast_sockaddr_stringify_addr(addr), SENTINEL);
-               }
-               if (!var) {
-                       var = ast_load_realtime("sippeers", "name", newpeername, SENTINEL);
-                       /*!\note
-                        * If this one loaded something, then we need to ensure that the host
-                        * field matched.  The only reason why we can't have this as a criteria
-                        * is because we only have the IP address and the host field might be
-                        * set as a name (and the reverse PTR might not match).
-                        */
-                       if (var && addr) {
-                               for (tmp = var; tmp; tmp = tmp->next) {
-                                       if (!strcasecmp(tmp->name, "host")) {
-                                               struct ast_sockaddr *addrs = NULL;
-
-                                               if (ast_sockaddr_resolve(&addrs,
-                                                                        tmp->value,
-                                                                        PARSE_PORT_FORBID,
-                                                                        get_address_family_filter(&bindaddr)) <= 0 ||
-                                                   ast_sockaddr_cmp(&addrs[0], addr)) {
-                                                       /* No match */
-                                                       ast_variables_destroy(var);
-                                                       var = NULL;
+       if (!(regs = ast_load_realtime_multientry("sipregs", column, value, SENTINEL))) {
+               return NULL;
+       }
+
+       /* Load *all* peers that are probably insecure=port */
+       if (!(peers = ast_load_realtime_multientry("sippeers", "insecure LIKE", "%port%", SENTINEL))) {
+               ast_config_destroy(regs);
+               return NULL;
+       }
+
+       /* Loop over the sipregs that match IP address and attempt to find an
+        * insecure=port match to it in sippeers. */
+       regscat = NULL;
+       while ((regscat = ast_category_browse(regs, regscat)) && (regname = ast_variable_retrieve(regs, regscat, "name"))) {
+               char *peerscat;
+               const char *peername;
+
+               peerscat = NULL;
+               while ((peerscat = ast_category_browse(peers, peerscat)) && (peername = ast_variable_retrieve(peers, peerscat, "name"))) {
+                       if (!strcasecmp(regname, peername)) {
+                               /* Ensure that it really is insecure=port and
+                                * not something else. */
+                               const char *insecure = ast_variable_retrieve(peers, peerscat, "insecure");
+                               struct ast_flags flags = {0};
+                               set_insecure_flags(&flags, insecure, -1);
+                               if (ast_test_flag(&flags, SIP_INSECURE_PORT)) {
+                                       /* ENOMEM checks till the bitter end. */
+                                       if ((varregs = ast_variables_dup(ast_category_root(regs, regscat)))) {
+                                               if (!(*var = ast_variables_dup(ast_category_root(peers, peerscat)))) {
+                                                       ast_variables_destroy(varregs);
+                                                       varregs = NULL;
                                                }
-                                               ast_free(addrs);
-                                               break;
                                        }
+                                       goto done;
                                }
                        }
                }
        }
 
-       if (!var && addr) {     /* Then check on IP address for dynamic peers */
-               ast_copy_string(ipaddr, ast_sockaddr_stringify_addr(addr), sizeof(ipaddr));
-               ast_copy_string(portstring, ast_sockaddr_stringify_port(addr), sizeof(portstring));
-               var = ast_load_realtime("sippeers", "host", ipaddr, "port", portstring, SENTINEL);      /* First check for fixed IP hosts */
-               if (var) {
-                       if (realtimeregs) {
-                               newpeername = get_name_from_variable(var, newpeername);
-                               varregs = ast_load_realtime("sipregs", "name", newpeername, SENTINEL);
+done:
+       ast_config_destroy(regs);
+       ast_config_destroy(peers);
+       return varregs;
+}
+
+static const char *get_name_from_variable(const struct ast_variable *var)
+{
+       /* Don't expect this to return non-NULL. Both NULL and empty
+        * values can cause the option to get removed from the variable
+        * list. This is called on ast_variables gotten from both
+        * ast_load_realtime and ast_load_realtime_multientry.
+        * - ast_load_realtime removes options with empty values
+        * - ast_load_realtime_multientry does not!
+        * For consistent behaviour, we check for the empty name and
+        * return NULL instead. */
+       const struct ast_variable *tmp;
+       for (tmp = var; tmp; tmp = tmp->next) {
+               if (!strcasecmp(tmp->name, "name")) {
+                       if (!ast_strlen_zero(tmp->value)) {
+                               return tmp->value;
                        }
-               } else {
-                       if (realtimeregs)
-                               varregs = ast_load_realtime("sipregs", "ipaddr", ipaddr, "port", portstring, SENTINEL); /* Then check for registered hosts */
-                       else
-                               var = ast_load_realtime("sippeers", "ipaddr", ipaddr, "port", portstring, SENTINEL); /* Then check for registered hosts */
-                       if (varregs) {
-                               newpeername = get_name_from_variable(varregs, newpeername);
-                               var = ast_load_realtime("sippeers", "name", newpeername, SENTINEL);
-                       }
-               }
-               if (!var) { /*We couldn't match on ipaddress and port, so we need to check if port is insecure*/
-                       peerlist = ast_load_realtime_multientry("sippeers", "host", ipaddr, SENTINEL);
-                       if (peerlist) {
-                               var = get_insecure_variable_from_config(peerlist);
-                               if(var) {
-                                       if (realtimeregs) {
-                                               newpeername = get_name_from_variable(var, newpeername);
-                                               varregs = ast_load_realtime("sipregs", "name", newpeername, SENTINEL);
-                                       }
-                               } else { /*var wasn't found in the list of "hosts", so try "ipaddr"*/
-                                       peerlist = NULL;
-                                       peerlist = ast_load_realtime_multientry("sippeers", "ipaddr", ipaddr, SENTINEL);
-                                       if(peerlist) {
-                                               var = get_insecure_variable_from_config(peerlist);
-                                               if(var) {
-                                                       if (realtimeregs) {
-                                                               newpeername = get_name_from_variable(var, newpeername);
-                                                               varregs = ast_load_realtime("sipregs", "name", newpeername, SENTINEL);
-                                                       }
-                                               }
-                                       }
-                               }
-                       } else {
-                               if (realtimeregs) {
-                                       peerlist = ast_load_realtime_multientry("sipregs", "ipaddr", ipaddr, SENTINEL);
-                                       if (peerlist) {
-                                               varregs = get_insecure_variable_from_config(peerlist);
-                                               if (varregs) {
-                                                       newpeername = get_name_from_variable(varregs, newpeername);
-                                                       var = ast_load_realtime("sippeers", "name", newpeername, SENTINEL);
-                                               }
-                                       }
-                               } else {
-                                       peerlist = ast_load_realtime_multientry("sippeers", "ipaddr", ipaddr, SENTINEL);
-                                       if (peerlist) {
-                                               var = get_insecure_variable_from_config(peerlist);
-                                               if (var) {
-                                                       newpeername = get_name_from_variable(var, newpeername);
-                                                       varregs = ast_load_realtime("sipregs", "name", newpeername, SENTINEL);
-                                               }
+                       break;
+               }
+       }
+       return NULL;
+}
+
+/* If varregs is NULL, we don't use sipregs.
+ * Using empty if-bodies instead of goto's while avoiding unnecessary indents */
+static int realtime_peer_by_name(const char *const *name, struct ast_sockaddr *addr, const char *ipaddr, struct ast_variable **var, struct ast_variable **varregs)
+{
+       /* Peer by name and host=dynamic */
+       if ((*var = ast_load_realtime("sippeers", "name", *name, "host", "dynamic", SENTINEL))) {
+               ;
+       /* Peer by name and host=IP */
+       } else if (addr && !(*var = ast_load_realtime("sippeers", "name", *name, "host", ipaddr, SENTINEL))) {
+               ;
+       /* Peer by name and host=HOSTNAME */
+       } else if ((*var = ast_load_realtime("sippeers", "name", *name, SENTINEL))) {
+               /*!\note
+                * If this one loaded something, then we need to ensure that the host
+                * field matched.  The only reason why we can't have this as a criteria
+                * is because we only have the IP address and the host field might be
+                * set as a name (and the reverse PTR might not match).
+                */
+               if (addr) {
+                       struct ast_variable *tmp;
+                       for (tmp = *var; tmp; tmp = tmp->next) {
+                               if (!strcasecmp(tmp->name, "host")) {
+                                       struct ast_sockaddr *addrs = NULL;
+
+                                       if (ast_sockaddr_resolve(&addrs,
+                                                                tmp->value,
+                                                                PARSE_PORT_FORBID,
+                                                                get_address_family_filter(&bindaddr)) <= 0 ||
+                                                                ast_sockaddr_cmp(&addrs[0], addr)) {
+                                               /* No match */
+                                               ast_variables_destroy(*var);
+                                               *var = NULL;
                                        }
+                                       ast_free(addrs);
+                                       break;
                                }
                        }
                }
        }
 
-       if (!var) {
-               if (peerlist)
-                       ast_config_destroy(peerlist);
-               return NULL;
+       /* Did we find anything? */
+       if (*var) {
+               if (varregs) {
+                       *varregs = ast_load_realtime("sipregs", "name", *name, SENTINEL);
+               }
+               return 1;
        }
+       return 0;
+}
 
-       for (tmp = var; tmp; tmp = tmp->next) {
-               if (!strcasecmp(tmp->name, "type") && (!strcasecmp(tmp->value, "peer") && which_objects == FINDUSERS)) {
-                       if (peerlist) {
-                               ast_config_destroy(peerlist);
-                       } else {
-                               ast_variables_destroy(var);
-                               ast_variables_destroy(varregs);
-                       }
-                       return NULL;
-               } else if (!newpeername && !strcasecmp(tmp->name, "name")) {
-                       newpeername = tmp->value;
+/* Another little helper function for backwards compatibility: this
+ * checks/fetches the sippeer that belongs to the sipreg. If none is
+ * found, we free the sipreg and return false. This way we can do the
+ * check inside the if-condition below. In the old code, not finding
+ * the sippeer also had it continue look for another match, so we do
+ * the same. */
+static struct ast_variable *realtime_peer_get_sippeer_helper(const char **name, struct ast_variable **varregs) {
+       struct ast_variable *var = NULL;
+       const char *old_name = *name;
+       *name = get_name_from_variable(*varregs);
+       if (!*name || !(var = ast_load_realtime("sippeers", "name", *name, SENTINEL))) {
+               if (!*name) {
+                       ast_log(LOG_WARNING, "Found sipreg but it has no name\n");
                }
+               ast_variables_destroy(*varregs);
+               *varregs = NULL;
+               *name = old_name;
        }
+       return var;
+}
 
-       if (!newpeername) {     /* Did not find peer in realtime */
-               ast_log(LOG_WARNING, "Cannot Determine peer name ip=%s\n", ipaddr);
-               if(peerlist)
-                       ast_config_destroy(peerlist);
-               else
-                       ast_variables_destroy(var);
+/* If varregs is NULL, we don't use sipregs. If we return true, then *name is
+ * set. Using empty if-bodies instead of goto's while avoiding unnecessary
+ * indents. */
+static int realtime_peer_by_addr(const char **name, struct ast_sockaddr *addr, const char *ipaddr, struct ast_variable **var, struct ast_variable **varregs)
+{
+       char portstring[6]; /* up to 5 digits plus null terminator */
+       ast_copy_string(portstring, ast_sockaddr_stringify_port(addr), sizeof(portstring));
+
+       /* We're not finding this peer by this name anymore. Reset it. */
+       *name = NULL;
+
+       /* First check for fixed IP hosts */
+       if ((*var = ast_load_realtime("sippeers", "host", ipaddr, "port", portstring, SENTINEL))) {
+               ;
+       /* Check for registered hosts (in sipregs) */
+       } else if (varregs && (*varregs = ast_load_realtime("sipregs", "ipaddr", ipaddr, "port", portstring, SENTINEL)) &&
+                       (*var = realtime_peer_get_sippeer_helper(name, varregs))) {
+               ;
+       /* Check for registered hosts (in sippeers) */
+       } else if (!varregs && (*var = ast_load_realtime("sippeers", "ipaddr", ipaddr, "port", portstring, SENTINEL))) {
+               ;
+       /* We couldn't match on ipaddress and port, so we need to check if port is insecure */
+       } else if ((*var = get_insecure_variable_from_sippeers("host", ipaddr))) {
+               ;
+       /* Same as above, but try the IP address field (in sipregs)
+        * Observe that it fetches the name/var at the same time, without the
+        * realtime_peer_get_sippeer_helper. Also note that it is quite inefficient.
+        * Avoid sipregs if possible. */
+       } else if (varregs && (*varregs = get_insecure_variable_from_sipregs("ipaddr", ipaddr, var))) {
+               ;
+       /* Same as above, but try the IP address field (in sippeers) */
+       } else if (!varregs && (*var = get_insecure_variable_from_sippeers("ipaddr", ipaddr))) {
+               ;
+       }
+
+       /* Nothing found? */
+       if (!*var) {
+               return 0;
+       }
+
+       /* Check peer name. It must not be empty. There may exist a
+        * different match that does have a name, but it's too late for
+        * that now. */
+       if (!*name && !(*name = get_name_from_variable(*var))) {
+               ast_log(LOG_WARNING, "Found peer for IP %s but it has no name\n", ipaddr);
+               ast_variables_destroy(*var);
+               *var = NULL;
+               if (varregs && *varregs) {
+                       ast_variables_destroy(*varregs);
+                       *varregs = NULL;
+               }
+               return 0;
+       }
+
+       /* Make sure varregs is populated if var is. The inverse,
+        * ensuring that var is set when varregs is, is taken
+        * care of by realtime_peer_get_sippeer_helper(). */
+       if (varregs && !*varregs) {
+               *varregs = ast_load_realtime("sipregs", "name", *name, SENTINEL);
+       }
+       return 1;
+}
+
+/*! \brief  realtime_peer: Get peer from realtime storage
+ * Checks the "sippeers" realtime family from extconfig.conf
+ * Checks the "sipregs" realtime family from extconfig.conf if it's configured.
+ * This returns a pointer to a peer and because we use build_peer, we can rest
+ * assured that the refcount is bumped.
+ * 
+ * \note This is never called with both newpeername and addr at the same time.
+ * If you do, be prepared to get a peer with a different name than newpeername.
+ */
+static struct sip_peer *realtime_peer(const char *newpeername, struct ast_sockaddr *addr, int devstate_only, int which_objects)
+{
+       struct sip_peer *peer = NULL;
+       struct ast_variable *var = NULL;
+       struct ast_variable *varregs = NULL;
+       char ipaddr[INET6_ADDRSTRLEN];
+       int realtimeregs = ast_check_realtime("sipregs");
+
+       if (addr) {
+               ast_copy_string(ipaddr, ast_sockaddr_stringify_addr(addr), sizeof(ipaddr));
+       } else {
+               ipaddr[0] = '\0';
+       }
+
+       if (newpeername && realtime_peer_by_name(&newpeername, addr, ipaddr, &var, realtimeregs ? &varregs : NULL)) {
+               ;
+       } else if (addr && realtime_peer_by_addr(&newpeername, addr, ipaddr, &var, realtimeregs ? &varregs : NULL)) {
+               ;
+       } else {
                return NULL;
        }
 
+       /* If we're looking for users, don't return peers (although this check
+        * should probably be done in realtime_peer_by_* instead...) */
+       if (which_objects == FINDUSERS) {
+               struct ast_variable *tmp;
+               for (tmp = var; tmp; tmp = tmp->next) {
+                       if (!strcasecmp(tmp->name, "type") && (!strcasecmp(tmp->value, "peer"))) {
+                               goto cleanup;
+                       }
+               }
+       }
 
        /* Peer found in realtime, now build it in memory */
        peer = build_peer(newpeername, var, varregs, TRUE, devstate_only);
        if (!peer) {
-               if(peerlist)
-                       ast_config_destroy(peerlist);
-               else {
-                       ast_variables_destroy(var);
-                       ast_variables_destroy(varregs);
-               }
-               return NULL;
+               goto cleanup;
        }
 
        ast_debug(3, "-REALTIME- loading peer from database to memory. Name: %s. Peer objects: %d\n", peer->name, rpeerobjs);
@@ -4864,13 +4962,10 @@ static struct sip_peer *realtime_peer(const char *newpeername, struct ast_sockad
                }
        }
        peer->is_realtime = 1;
-       if (peerlist)
-               ast_config_destroy(peerlist);
-       else {
-               ast_variables_destroy(var);
-               ast_variables_destroy(varregs);
-       }
 
+cleanup:
+       ast_variables_destroy(var);
+       ast_variables_destroy(varregs);
        return peer;
 }
 
@@ -5233,15 +5328,20 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
        if (!ast_strlen_zero(peer->fromdomain)) {
                ast_string_field_set(dialog, fromdomain, peer->fromdomain);
                if (!dialog->initreq.headers) {
-                       char *c;
+                       char *new_callid;
                        char *tmpcall = ast_strdupa(dialog->callid);
                        /* this sure looks to me like we are going to change the callid on this dialog!! */
-                       c = strchr(tmpcall, '@');
-                       if (c) {
-                               *c = '\0';
-                               ao2_t_unlink(dialogs, dialog, "About to change the callid -- remove the old name");
-                               ast_string_field_build(dialog, callid, "%s@%s", tmpcall, peer->fromdomain);
-                               ao2_t_link(dialogs, dialog, "New dialog callid -- inserted back into table");
+                       new_callid = strchr(tmpcall, '@');
+                       if (new_callid) {
+                               int callid_size;
+
+                               *new_callid = '\0';
+
+                               /* Change the dialog callid. */
+                               callid_size = strlen(tmpcall) + strlen(peer->fromdomain) + 2;
+                               new_callid = alloca(callid_size);
+                               snprintf(new_callid, callid_size, "%s@%s", tmpcall, peer->fromdomain);
+                               change_callid_pvt(dialog, new_callid);
                        }
                }
        }
@@ -5322,6 +5422,12 @@ static int create_addr(struct sip_pvt *dialog, const char *opeer, struct ast_soc
                dialog->relatedpeer = sip_ref_peer(peer, "create_addr: setting dialog's relatedpeer pointer");
                sip_unref_peer(peer, "create_addr: unref peer from sip_find_peer hashtab lookup");
                return res;
+       } else if (ast_check_digits(peername)) {
+               /* Although an IPv4 hostname *could* be represented as a 32-bit integer, it is uncommon and
+                * it makes dialing SIP/${EXTEN} for a peer that isn't defined resolve to an IP that is
+                * almost certainly not intended. It is much better to just reject purely numeric hostnames */
+               ast_log(LOG_WARNING, "Purely numeric hostname (%s), and not a peer--rejecting!\n", peername);
+               return -1;
        } else {
                dialog->rtptimeout = global_rtptimeout;
                dialog->rtpholdtimeout = global_rtpholdtimeout;
@@ -5363,6 +5469,7 @@ static int create_addr(struct sip_pvt *dialog, const char *opeer, struct ast_soc
 
                if (ast_sockaddr_resolve_first(&dialog->sa, hostn, 0)) {
                        ast_log(LOG_WARNING, "No such host: %s\n", peername);
+                       return -1;
                }
 
                if (srv_ret > 0) {
@@ -5590,7 +5697,7 @@ static void sip_registry_destroy(struct sip_registry *reg)
                   we don't get reentered trying to grab the registry lock */
                reg->call->registry = registry_unref(reg->call->registry, "destroy reg->call->registry");
                ast_debug(3, "Destroying active SIP dialog for registry %s@%s\n", reg->username, reg->hostname);
-               dialog_unlink_all(reg->call, TRUE, TRUE);
+               dialog_unlink_all(reg->call);
                reg->call = dialog_unref(reg->call, "unref reg->call");
                /* reg->call = sip_destroy(reg->call); */
        }
@@ -6148,7 +6255,6 @@ static int sip_hangup(struct ast_channel *ast)
                ast_debug(4, "SIP Transfer: Not hanging up right now... Rescheduling hangup for %s.\n", p->callid);
                sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
                ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER);        /* Really hang up next time */
-               p->needdestroy = 0;
                p->owner->tech_pvt = dialog_unref(p->owner->tech_pvt, "unref p->owner->tech_pvt");
                sip_pvt_lock(p);
                p->owner = NULL;  /* Owner will be gone after we return, so take it away */
@@ -6724,17 +6830,25 @@ static int sip_indicate(struct ast_channel *ast, int condition, const void *data
                break;
        case AST_CONTROL_INCOMPLETE:
                if (ast->_state != AST_STATE_UP) {
-                       if (ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP)) {
+                       switch (ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP)) {
+                       case SIP_PAGE2_ALLOWOVERLAP_YES:
                                transmit_response_reliable(p, "484 Address Incomplete", &p->initreq);
-                       } else {
+                               p->invitestate = INV_COMPLETED;
+                               sip_alreadygone(p);
+                               ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
+                               break;
+                       case SIP_PAGE2_ALLOWOVERLAP_DTMF:
+                               /* Just wait for inband DTMF digits */
+                               break;
+                       default:
+                               /* it actually means no support for overlap */
                                transmit_response_reliable(p, "404 Not Found", &p->initreq);
+                               p->invitestate = INV_COMPLETED;
+                               sip_alreadygone(p);
+                               ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
+                               break;
                        }
-                       p->invitestate = INV_COMPLETED;
-                       sip_alreadygone(p);
-                       ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
-                       break;
                }
-               res = 0;
                break;
        case AST_CONTROL_PROCEEDING:
                if ((ast->_state != AST_STATE_UP) &&
@@ -6842,11 +6956,17 @@ static int sip_indicate(struct ast_channel *ast, int condition, const void *data
        return res;
 }
 
-/*! \brief Initiate a call in the SIP channel
-       called from sip_request_call (calls from the pbx ) for outbound channels
-       and from handle_request_invite for inbound channels
-       
-*/
+/*!
+ * \brief Initiate a call in the SIP channel
+ *
+ * \note called from sip_request_call (calls from the pbx ) for
+ * outbound channels and from handle_request_invite for inbound
+ * channels
+ *
+ * \pre i is locked
+ *
+ * \return New ast_channel locked.
+ */
 static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *title, const char *linkedid)
 {
        struct ast_channel *tmp;
@@ -6856,7 +6976,7 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit
        int needvideo = 0;
        int needtext = 0;
        char buf[SIPBUFSIZE];
-       char *decoded_exten;
+       char *exten;
 
        {
                const char *my_name;    /* pick a good name */
@@ -7006,14 +7126,15 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit
         * we should decode the uri before storing it in the channel, but leave it encoded in the sip_pvt
         * structure so that there aren't issues when forming URI's
         */
-       if (ast_exists_extension(NULL, i->context, i->exten, 1, i->cid_num)) {
-               /* encoded in dialplan, so keep extension encoded */
-               ast_copy_string(tmp->exten, i->exten, sizeof(tmp->exten));
-       } else {
-               decoded_exten = ast_strdupa(i->exten);
-               ast_uri_decode(decoded_exten, ast_uri_sip_user);
-               ast_copy_string(tmp->exten, decoded_exten, sizeof(tmp->exten));
+       exten = ast_strdupa(i->exten);
+       sip_pvt_unlock(i);
+       ast_channel_unlock(tmp);
+       if (!ast_exists_extension(NULL, i->context, i->exten, 1, i->cid_num)) {
+               ast_uri_decode(exten, ast_uri_sip_user);
        }
+       ast_channel_lock(tmp);
+       sip_pvt_lock(i);
+       ast_copy_string(tmp->exten, exten, sizeof(tmp->exten));
 
        /* Don't use ast_set_callerid() here because it will
         * generate an unnecessary NewCallerID event  */
@@ -7050,15 +7171,6 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit
                pbx_builtin_setvar_helper(tmp, v->name, ast_get_encoded_str(v->value, valuebuf, sizeof(valuebuf)));
        }
 
-       ast_channel_unlock(tmp); /* ast_hangup requires the channel to be unlocked */
-
-       if (state != AST_STATE_DOWN && ast_pbx_start(tmp)) {
-               ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
-               tmp->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
-               ast_hangup(tmp);
-               tmp = NULL;
-       }
-
        if (i->do_history) {
                append_history(i, "NewChan", "Channel %s - from %s", tmp->name, i->callid);
        }
@@ -7259,16 +7371,23 @@ static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p
        case 4:
                f = ast_rtp_instance_read(p->trtp, 0);  /* RTP Text */
                if (sipdebug_text) {
+                       struct ast_str *out = ast_str_create(f->datalen * 4 + 6);
                        int i;
                        unsigned char* arr = f->data.ptr;
-                       for (i=0; i < f->datalen; i++) {
-                               ast_verbose("%c", (arr[i] > ' ' && arr[i] < '}') ? arr[i] : '.');
-                       }
-                       ast_verbose(" -> ");
-                       for (i=0; i < f->datalen; i++) {
-                               ast_verbose("%02X ", arr[i]);
-                       }
-                       ast_verbose("\n");
+                       do {
+                               if (!out) {
+                                       break;
+                               }
+                               for (i = 0; i < f->datalen; i++) {
+                                       ast_str_append(&out, 0, "%c", (arr[i] > ' ' && arr[i] < '}') ? arr[i] : '.');
+                               }
+                               ast_str_append(&out, 0, " -> ");
+                               for (i = 0; i < f->datalen; i++) {
+                                       ast_str_append(&out, 0, "%02X ", arr[i]);
+                               }
+                               ast_verb(0, "%s\n", ast_str_buffer(out));
+                               ast_free(out);
+                       } while (0);
                }
                break;
        case 5:
@@ -7351,7 +7470,7 @@ static struct ast_frame *sip_read(struct ast_channel *ast)
                                S_COR(ast->caller.id.number.valid, ast->caller.id.number.str, NULL))) {
                                ast_channel_lock(ast);
                                sip_pvt_lock(p);
-                               ast_verbose(VERBOSE_PREFIX_2 "Redirecting '%s' to fax extension due to CNG detection\n", ast->name);
+                               ast_verb(2, "Redirecting '%s' to fax extension due to CNG detection\n", ast->name);
                                pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast->exten);
                                if (ast_async_goto(ast, target_context, "fax", 1)) {
                                        ast_log(LOG_NOTICE, "Failed to async goto '%s' into fax of '%s'\n", ast->name, target_context);
@@ -7402,15 +7521,68 @@ static char *generate_uri(struct sip_pvt *pvt, char *buf, size_t size)
        return buf;
 }
 
-/*! \brief Build SIP Call-ID value for a non-REGISTER transaction */
+/*!
+ * \brief Build SIP Call-ID value for a non-REGISTER transaction
+ *
+ * \note The passed in pvt must not be in a dialogs container
+ * since this function changes the hash key used by the
+ * container.
+ */
 static void build_callid_pvt(struct sip_pvt *pvt)
 {
        char buf[33];
-
        const char *host = S_OR(pvt->fromdomain, ast_sockaddr_stringify_remote(&pvt->ourip));
-       
+
        ast_string_field_build(pvt, callid, "%s@%s", generate_random_string(buf, sizeof(buf)), host);
+}
 
+/*! \brief Unlink the given object from the container and return TRUE if it was in the container. */
+#define CONTAINER_UNLINK(container, obj, tag)                                                          \
+       ({                                                                                                                                              \
+               int found = 0;                                                                                                          \
+               typeof((obj)) __removed_obj;                                                                            \
+               __removed_obj = ao2_t_callback((container),                                                     \
+                       OBJ_UNLINK | OBJ_POINTER, ao2_match_by_addr, (obj), (tag));             \
+               if (__removed_obj) {                                                                                            \
+                       ao2_ref(__removed_obj, -1);                                                                             \
+                       found = 1;                                                                                                              \
+               }                                                                                                                                       \
+               found;                                                                                                                          \
+       })
+
+/*!
+ * \internal
+ * \brief Safely change the callid of the given SIP dialog.
+ *
+ * \param pvt SIP private structure to change callid
+ * \param callid Specified new callid to use.  NULL if generate new callid.
+ *
+ * \return Nothing
+ */
+static void change_callid_pvt(struct sip_pvt *pvt, const char *callid)
+{
+       int in_dialog_container;
+       int in_rtp_container;
+
+       ao2_lock(dialogs);
+       ao2_lock(dialogs_rtpcheck);
+       in_dialog_container = CONTAINER_UNLINK(dialogs, pvt,
+               "About to change the callid -- remove the old name");
+       in_rtp_container = CONTAINER_UNLINK(dialogs_rtpcheck, pvt,
+               "About to change the callid -- remove the old name");
+       if (callid) {
+               ast_string_field_set(pvt, callid, callid);
+       } else {
+               build_callid_pvt(pvt);
+       }
+       if (in_dialog_container) {
+               ao2_t_link(dialogs, pvt, "New dialog callid -- inserted back into table");
+       }
+       if (in_rtp_container) {
+               ao2_t_link(dialogs_rtpcheck, pvt, "New dialog callid -- inserted back into table");
+       }
+       ao2_unlock(dialogs_rtpcheck);
+       ao2_unlock(dialogs);
 }
 
 /*! \brief Build SIP Call-ID value for a REGISTER transaction */
@@ -7886,7 +8058,7 @@ static void forked_invite_init(struct sip_request *req, const char *new_theirtag
  * \note This function will never let you down.
  * \note This function will run around and desert you.
  *
- * \pre vpt is not locked
+ * \pre pvt is not locked
  * \post pvt is locked
  * \post pvt->owner is locked and its reference count is increased (if pvt->owner is not NULL)
  *
@@ -8666,6 +8838,17 @@ static int get_ip_and_port_from_sdp(struct sip_request *req, const enum media_ty
        return 0;
 }
 
+/*! \internal
+ * \brief Returns whether or not the address is null or ANY / unspecified (0.0.0.0 or ::)
+ * \retval TRUE if the address is null or any
+ * \retval FALSE if the address it not null or any
+ * \note In some circumstances, calls should be placed on hold if either of these conditions exist.
+ */
+static int sockaddr_is_null_or_any(const struct ast_sockaddr *addr)
+{
+       return ast_sockaddr_isnull(addr) || ast_sockaddr_is_any(addr);
+}
+
 /*! \brief Process SIP SDP offer, select formats and activate RTP channels
        If offer is rejected, we will not change any properties of the call
        Return 0 on success, a negative value on errors.
@@ -8844,9 +9027,13 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                                ast_log(LOG_WARNING, "unknown SDP media protocol in offer: %s\n", protocol);
                                continue;
                        }
+                       if (p->offered_media[SDP_AUDIO].order_offered) {
+                               ast_log(LOG_WARNING, "Multiple audio streams are not supported\n");
+                               res = -3;
+                               goto process_sdp_cleanup;
+                       }
                        audio = TRUE;
-                       p->offered_media[SDP_AUDIO].offered = TRUE;
-                       numberofmediastreams++;
+                       p->offered_media[SDP_AUDIO].order_offered = ++numberofmediastreams;
                        portno = x;
 
                        /* Scan through the RTP payload types specified in a "m=" line: */
@@ -8872,10 +9059,14 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                                ast_log(LOG_WARNING, "unknown SDP media protocol in offer: %s\n", protocol);
                                continue;
                        }
+                       if (p->offered_media[SDP_VIDEO].order_offered) {
+                               ast_log(LOG_WARNING, "Multiple video streams are not supported\n");
+                               res = -3;
+                               goto process_sdp_cleanup;
+                       }
                        video = TRUE;
                        p->novideo = FALSE;
-                       p->offered_media[SDP_VIDEO].offered = TRUE;
-                       numberofmediastreams++;
+                       p->offered_media[SDP_VIDEO].order_offered = ++numberofmediastreams;
                        vportno = x;
 
                        /* Scan through the RTP payload types specified in a "m=" line: */
@@ -8894,10 +9085,14 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                /* Search for text media definition */
                } else if ((sscanf(m, "text %30u/%30u RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0 && x) ||
                           (sscanf(m, "text %30u RTP/AVP %n", &x, &len) == 1 && len > 0 && x)) {
+                       if (p->offered_media[SDP_TEXT].order_offered) {
+                               ast_log(LOG_WARNING, "Multiple text streams are not supported\n");
+                               res = -3;
+                               goto process_sdp_cleanup;
+                       }
                        text = TRUE;
                        p->notext = FALSE;
-                       p->offered_media[SDP_TEXT].offered = TRUE;
-                       numberofmediastreams++;
+                       p->offered_media[SDP_TEXT].order_offered = ++numberofmediastreams;
                        tportno = x;
 
                        /* Scan through the RTP payload types specified in a "m=" line: */
@@ -8916,12 +9111,16 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                /* Search for image media definition */
                } else if (p->udptl && ((sscanf(m, "image %30u udptl t38%n", &x, &len) == 1 && len > 0 && x) ||
                                        (sscanf(m, "image %30u UDPTL t38%n", &x, &len) == 1 && len > 0 && x) )) {
+                       if (p->offered_media[SDP_IMAGE].order_offered) {
+                               ast_log(LOG_WARNING, "Multiple T.38 streams are not supported\n");
+                               res = -3;
+                               goto process_sdp_cleanup;
+                       }
                        image = TRUE;
                        if (debug)
                                ast_verbose("Got T.38 offer in SDP in dialog %s\n", p->callid);
-                       p->offered_media[SDP_IMAGE].offered = TRUE;
+                       p->offered_media[SDP_IMAGE].order_offered = ++numberofmediastreams;
                        udptlportno = x;
-                       numberofmediastreams++;
 
                        if (p->t38.state != T38_ENABLED) {
                                memset(&p->t38.their_parms, 0, sizeof(p->t38.their_parms));
@@ -9024,13 +9223,6 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                goto process_sdp_cleanup;
        }
 
-       if (numberofmediastreams > 3) {
-               /* We have too many fax, audio and/or video and/or text media streams, fail this offer */
-               ast_log(LOG_WARNING, "Faling due to too many media streams\n");
-               res = -3;
-               goto process_sdp_cleanup;
-       }
-
        if (secure_audio && !(p->srtp && (ast_test_flag(p->srtp, SRTP_CRYPTO_OFFER_OK)))) {
                ast_log(LOG_WARNING, "Can't provide secure audio requested in SDP offer\n");
                res = -4;
@@ -9075,7 +9267,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
        ast_format_cap_append(newpeercapability, tpeercapability);
 
        ast_format_cap_joint_copy(p->caps, newpeercapability, newjointcapability);
-       if (ast_format_cap_is_empty(newjointcapability) && (portno != -1)) {
+       if (ast_format_cap_is_empty(newjointcapability) && udptlportno == -1) {
                ast_log(LOG_NOTICE, "No compatible codecs, not accepting this offer!\n");
                /* Do NOT Change current setting */
                res = -1;
@@ -9106,6 +9298,20 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                            ast_rtp_lookup_mime_multiple2(s3, NULL, newnoncodeccapability, 0, 0));
        }
 
+       if (portno != -1 || vportno != -1 || tportno != -1) {
+               /* We are now ready to change the sip session and p->rtp and p->vrtp with the offered codecs, since
+                  they are acceptable */
+               ast_format_cap_copy(p->jointcaps, newjointcapability);                /* Our joint codec profile for this call */
+               ast_format_cap_copy(p->peercaps, newpeercapability);                  /* The other sides capability in latest offer */
+               p->jointnoncodeccapability = newnoncodeccapability;     /* DTMF capabilities */
+       
+               /* respond with single most preferred joint codec, limiting the other side's choice */
+               if (ast_test_flag(&p->flags[1], SIP_PAGE2_PREFERRED_CODEC)) {
+                       ast_codec_choose(&p->prefs, p->jointcaps, 1, &tmp_fmt);
+                       ast_format_cap_set(p->jointcaps, &tmp_fmt);
+               }
+       }
+
        /* Setup audio address and port */
        if (p->rtp) {
                if (portno > 0) {
@@ -9115,18 +9321,11 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                                ast_verbose("Peer audio RTP is at port %s\n",
                                            ast_sockaddr_stringify(sa));
                        }
-                       /* We are now ready to change the sip session and p->rtp and p->vrtp with the offered codecs, since
-                          they are acceptable */
-                       ast_format_cap_copy(p->jointcaps, newjointcapability);                /* Our joint codec profile for this call */
-                       ast_format_cap_copy(p->peercaps, newpeercapability);                  /* The other sides capability in latest offer */
-                       p->jointnoncodeccapability = newnoncodeccapability;     /* DTMF capabilities */
-
-                       if (ast_test_flag(&p->flags[1], SIP_PAGE2_PREFERRED_CODEC)) { /* respond with single most preferred joint codec, limiting the other side's choice */
-                               ast_codec_choose(&p->prefs, p->jointcaps, 1, &tmp_fmt);
-                               ast_format_cap_set(p->jointcaps, &tmp_fmt);
-                       }
 
                        ast_rtp_codecs_payloads_copy(&newaudiortp, ast_rtp_instance_get_codecs(p->rtp), p->rtp);
+                       /* Ensure RTCP is enabled since it may be inactive
+                          if we're coming back from a T.38 session */
+                       ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_RTCP, 1);
 
                        if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO) {
                                ast_clear_flag(&p->flags[0], SIP_DTMF);
@@ -9143,6 +9342,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                } else if (udptlportno > 0) {
                        if (debug)
                                ast_verbose("Got T.38 Re-invite without audio. Keeping RTP active during T.38 session.\n");
+                       /* Silence RTCP while audio RTP is inactive */
+                       ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_RTCP, 0);
                } else {
                        ast_rtp_instance_stop(p->rtp);
                        if (debug)
@@ -9224,7 +9425,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                                                ast_channel_unlock(p->owner);
                                                if (ast_exists_extension(p->owner, target_context, "fax", 1,
                                                        S_COR(p->owner->caller.id.number.valid, p->owner->caller.id.number.str, NULL))) {
-                                                       ast_verbose(VERBOSE_PREFIX_2 "Redirecting '%s' to fax extension due to peer T.38 re-INVITE\n", p->owner->name);
+                                                       ast_verb(2, "Redirecting '%s' to fax extension due to peer T.38 re-INVITE\n", p->owner->name);
                                                        pbx_builtin_setvar_helper(p->owner, "FAXEXTEN", p->owner->exten);
                                                        if (ast_async_goto(p->owner, target_context, "fax", 1)) {
                                                                ast_log(LOG_NOTICE, "Failed to async goto '%s' into fax of '%s'\n", p->owner->name, target_context);
@@ -9283,7 +9484,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                /* Activate a re-invite */
                ast_queue_frame(p->owner, &ast_null_frame);
                change_hold_state(p, req, FALSE, sendonly);
-       } else if ((ast_sockaddr_isnull(sa) && ast_sockaddr_isnull(vsa) && ast_sockaddr_isnull(tsa) && ast_sockaddr_isnull(isa)) || (sendonly && sendonly != -1)) {
+       } else if ((sockaddr_is_null_or_any(sa) && sockaddr_is_null_or_any(vsa) && sockaddr_is_null_or_any(tsa) && sockaddr_is_null_or_any(isa)) || (sendonly && sendonly != -1)) {
                ast_queue_control_data(p->owner, AST_CONTROL_HOLD,
                                       S_OR(p->mohsuggest, NULL),
                                       !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
@@ -10716,14 +10917,20 @@ static int add_digit(struct sip_request *req, char digit, unsigned int duration,
        int event;
        if (mode) {
                /* Application/dtmf short version used by some implementations */
-               if (digit == '*')
+               if ('0' <= digit && digit <= '9') {
+                       event = digit - '0';
+               } else if (digit == '*') {
                        event = 10;
-               else if (digit == '#')
+               } else if (digit == '#') {
                        event = 11;
-               else if ((digit >= 'A') && (digit <= 'D'))
+               } else if ('A' <= digit && digit <= 'D') {
                        event = 12 + digit - 'A';
-               else
-                       event = atoi(&digit);
+               } else if ('a' <= digit && digit <= 'd') {
+                       event = 12 + digit - 'a';
+               } else {
+                       /* Unknown digit */
+                       event = 0;
+               }
                snprintf(tmp, sizeof(tmp), "%d\r\n", event);
                add_header(req, "Content-Type", "application/dtmf");
                add_content(req, tmp);
@@ -11299,7 +11506,7 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
                        needaudio = TRUE;
 
                if (debug) {
-                       ast_verbose("Audio is at %s\n", ast_sockaddr_stringify_port(&p->ourip));
+                       ast_verbose("Audio is at %s\n", ast_sockaddr_stringify_port(&addr));
                }
 
                /* Ok, we need video. Let's add what we need for video and set codecs.
@@ -11313,7 +11520,7 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
                        if (p->maxcallbitrate)
                                snprintf(bandwidth, sizeof(bandwidth), "b=CT:%d\r\n", p->maxcallbitrate);
                        if (debug) {
-                               ast_verbose("Video is at %s\n", ast_sockaddr_stringify(&p->ourip));
+                               ast_verbose("Video is at %s\n", ast_sockaddr_stringify(&vdest));
                        }
                }
 
@@ -11326,7 +11533,7 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
                        ast_str_append(&m_text, 0, "m=text %d RTP/%s", ast_sockaddr_port(&tdest),
                                t_a_crypto ? "SAVP" : "AVP");
                        if (debug) {  /* XXX should I use tdest below ? */
-                               ast_verbose("Text is at %s\n", ast_sockaddr_stringify(&p->ourip));
+                               ast_verbose("Text is at %s\n", ast_sockaddr_stringify(&taddr));
                        }
                }
 
@@ -11499,55 +11706,107 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
        add_content(resp, owner);
        add_content(resp, subject);
        add_content(resp, connection);
-       if (needvideo)          /* only if video response is appropriate */
+       /* only if video response is appropriate */
+       if (needvideo) {
                add_content(resp, bandwidth);
-       add_content(resp, session_time);
-       if (needaudio) {
-               add_content(resp, m_audio->str);
-               add_content(resp, a_audio->str);
-               add_content(resp, hold);
-               if (a_crypto) {
-                       add_content(resp, a_crypto);
-               }
-       } else if (p->offered_media[SDP_AUDIO].offered) {
-               snprintf(dummy_answer, sizeof(dummy_answer), "m=audio 0 RTP/AVP %s\r\n", p->offered_media[SDP_AUDIO].codecs);
-               add_content(resp, dummy_answer);
-       }
-       if (needvideo) { /* only if video response is appropriate */
-               add_content(resp, m_video->str);
-               add_content(resp, a_video->str);
-               add_content(resp, hold);        /* Repeat hold for the video stream */
-               if (v_a_crypto) {
-                       add_content(resp, v_a_crypto);
-               }
-       } else if (p->offered_media[SDP_VIDEO].offered) {
-               snprintf(dummy_answer, sizeof(dummy_answer), "m=video 0 RTP/AVP %s\r\n", p->offered_media[SDP_VIDEO].codecs);
-               add_content(resp, dummy_answer);
-       }
-       if (needtext) { /* only if text response is appropriate */
-               add_content(resp, m_text->str);
-               add_content(resp, a_text->str);
-               add_content(resp, hold);        /* Repeat hold for the text stream */
-               if (t_a_crypto) {
-                       add_content(resp, t_a_crypto);
-               }
-       } else if (p->offered_media[SDP_TEXT].offered) {
-               snprintf(dummy_answer, sizeof(dummy_answer), "m=text 0 RTP/AVP %s\r\n", p->offered_media[SDP_TEXT].codecs);
-               add_content(resp, dummy_answer);
        }
-       if (add_t38) {
-               add_content(resp, m_modem->str);
-               add_content(resp, a_modem->str);
-       } else if (p->offered_media[SDP_IMAGE].offered) {
-               add_content(resp, "m=image 0 udptl t38\r\n");
+       add_content(resp, session_time);
+       /* if this is a response to an invite, order our offers properly */
+       if (p->offered_media[SDP_AUDIO].order_offered ||
+               p->offered_media[SDP_VIDEO].order_offered ||
+               p->offered_media[SDP_TEXT].order_offered ||
+               p->offered_media[SDP_IMAGE].order_offered) {
+               int i;
+               /* we have up to 3 streams as limited by process_sdp */
+               for (i = 1; i <= 3; i++) {
+                       if (p->offered_media[SDP_AUDIO].order_offered == i) {
+                               if (needaudio) {
+                                       add_content(resp, m_audio->str);
+                                       add_content(resp, a_audio->str);
+                                       add_content(resp, hold);
+                                       if (a_crypto) {
+                                               add_content(resp, a_crypto);
+                                       }
+                               } else {
+                                       snprintf(dummy_answer, sizeof(dummy_answer), "m=audio 0 RTP/AVP %s\r\n", p->offered_media[SDP_AUDIO].codecs);
+                                       add_content(resp, dummy_answer);
+                               }
+                       } else if (p->offered_media[SDP_VIDEO].order_offered == i) {
+                               if (needvideo) { /* only if video response is appropriate */
+                                       add_content(resp, m_video->str);
+                                       add_content(resp, a_video->str);
+                                       add_content(resp, hold);        /* Repeat hold for the video stream */
+                                       if (v_a_crypto) {
+                                               add_content(resp, v_a_crypto);
+                                       }
+                               } else {
+                                       snprintf(dummy_answer, sizeof(dummy_answer), "m=video 0 RTP/AVP %s\r\n", p->offered_media[SDP_VIDEO].codecs);
+                                       add_content(resp, dummy_answer);
+                               }
+                       } else if (p->offered_media[SDP_TEXT].order_offered == i) {
+                               if (needtext) { /* only if text response is appropriate */
+                                       add_content(resp, m_text->str);
+                                       add_content(resp, a_text->str);
+                                       add_content(resp, hold);        /* Repeat hold for the text stream */
+                                       if (t_a_crypto) {
+                                               add_content(resp, t_a_crypto);
+                                       }
+                               } else {
+                                       snprintf(dummy_answer, sizeof(dummy_answer), "m=text 0 RTP/AVP %s\r\n", p->offered_media[SDP_TEXT].codecs);
+                                       add_content(resp, dummy_answer);
+                               }
+                       } else if (p->offered_media[SDP_IMAGE].order_offered == i) {
+                               if (add_t38) {
+                                       add_content(resp, m_modem->str);
+                                       add_content(resp, a_modem->str);
+                               } else {
+                                       add_content(resp, "m=image 0 udptl t38\r\n");
+                               }
+                       }
+               }
+       } else {
+               /* generate new SDP from scratch, no offers */
+               if (needaudio) {
+                       add_content(resp, m_audio->str);
+                       add_content(resp, a_audio->str);
+                       add_content(resp, hold);
+                       if (a_crypto) {
+                               add_content(resp, a_crypto);
+                       }
+               }
+               if (needvideo) { /* only if video response is appropriate */
+                       add_content(resp, m_video->str);
+                       add_content(resp, a_video->str);
+                       add_content(resp, hold);        /* Repeat hold for the video stream */
+                       if (v_a_crypto) {
+                               add_content(resp, v_a_crypto);
+                       }
+               }
+               if (needtext) { /* only if text response is appropriate */
+                       add_content(resp, m_text->str);
+                       add_content(resp, a_text->str);
+                       add_content(resp, hold);        /* Repeat hold for the text stream */
+                       if (t_a_crypto) {
+                               add_content(resp, t_a_crypto);
+                       }
+               }
+               if (add_t38) {
+                       add_content(resp, m_modem->str);
+                       add_content(resp, a_modem->str);
+               }
        }
 
        /* Update lastrtprx when we send our SDP */
        p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */
 
-       /* we unlink this dialog and link again into the dialogs_rtpcheck container to doesnt add it twice */
+       /*
+        * We unlink this dialog and link again into the
+        * dialogs_rtpcheck container so its not in there twice.
+        */
+       ao2_lock(dialogs_rtpcheck);
        ao2_t_unlink(dialogs_rtpcheck, p, "unlink pvt into dialogs_rtpcheck container");
        ao2_t_link(dialogs_rtpcheck, p, "link pvt into dialogs_rtpcheck container");
+       ao2_unlock(dialogs_rtpcheck);
 
        ast_debug(3, "Done building SDP. Settling with this capability: %s\n", ast_getformatname_multiple(buf, SIPBUFSIZE, tmpcap));
 
@@ -12057,7 +12316,7 @@ static int transmit_publish(struct sip_epa_entry *epa_entry, enum sip_publish_ty
 
        if (create_addr(pvt, epa_entry->destination, NULL, TRUE, NULL)) {
                sip_pvt_unlock(pvt);
-               dialog_unlink_all(pvt, TRUE, TRUE);
+               dialog_unlink_all(pvt);
                dialog_unref(pvt, "create_addr failed in transmit_publish. Unref dialog");
                return -1;
        }
@@ -12299,7 +12558,7 @@ static int __sip_subscribe_mwi_do(struct sip_subscription_mwi *mwi)
        
        /* Setup the destination of our subscription */
        if (create_addr(mwi->call, mwi->hostname, &mwi->us, 0, NULL)) {
-               dialog_unlink_all(mwi->call, TRUE, TRUE);
+               dialog_unlink_all(mwi->call);
                mwi->call = dialog_unref(mwi->call, "unref dialog after unlink_all");
                return 0;
        }
@@ -12332,7 +12591,10 @@ static int __sip_subscribe_mwi_do(struct sip_subscription_mwi *mwi)
        ast_sip_ouraddrfor(&mwi->call->sa, &mwi->call->ourip, mwi->call);
        build_contact(mwi->call);
        build_via(mwi->call);
-       build_callid_pvt(mwi->call);
+
+       /* Change the dialog callid. */
+       change_callid_pvt(mwi->call, NULL);
+
        ast_set_flag(&mwi->call->flags[0], SIP_OUTGOING);
        
        /* Associate the call with us */
@@ -12754,7 +13016,7 @@ static int manager_sipnotify(struct mansession *s, const struct message *m)
 
        if (create_addr(p, channame, NULL, 0, NULL)) {
                /* Maybe they're not registered, etc. */
-               dialog_unlink_all(p, TRUE, TRUE);
+               dialog_unlink_all(p);
                dialog_unref(p, "unref dialog inside for loop" );
                /* sip_destroy(p); */
                astman_send_error(s, m, "Could not create address");
@@ -12837,7 +13099,7 @@ static void update_connectedline(struct sip_pvt *p, const void *data, size_t dat
                        ast_set_flag(&p->flags[0], SIP_OUTGOING);
                        p->invitestate = INV_CALLING;
                        send_request(p, &req, XMIT_CRITICAL, p->ocseq);
-               } else if (is_method_allowed(&p->allowed_methods, SIP_UPDATE)) {
+               } else if ((is_method_allowed(&p->allowed_methods, SIP_UPDATE)) && (!ast_strlen_zero(p->okcontacturi))) { 
                        reqprep(&req, p, SIP_UPDATE, 0, 1);
                        add_rpid(&req, p);
                        add_header(&req, "X-Asterisk-rpid-update", "Yes");
@@ -13093,7 +13355,7 @@ static int transmit_register(struct sip_registry *r, int sipmethod, const char *
                if (create_addr(p, S_OR(r->peername, r->hostname), &r->us, 0, NULL)) {
                        /* we have what we hope is a temporary network error,
                         * probably DNS.  We need to reschedule a registration try */
-                       dialog_unlink_all(p, TRUE, TRUE);
+                       dialog_unlink_all(p);
                        p = dialog_unref(p, "unref dialog after unlink_all");
                        if (r->timeout > -1) {
                                AST_SCHED_REPLACE_UNREF(r->timeout, sched, global_reg_timeout * 1000, sip_reg_timeout, r,
@@ -13727,10 +13989,15 @@ static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req)
        return TRUE;            
 }
 
-/*! \brief parse uri in a way that allows semicolon stripping if legacy mode is enabled */
-static int parse_uri_legacy_check(char *uri, const char *scheme, char **user, char **pass, char **domain, char **transport)
+/*! \brief parse uri in a way that allows semicolon stripping if legacy mode is enabled
+ *
+ * \note This calls parse_uri which has the unexpected property that passing more
+ *       arguments results in more splitting. Most common is to leave out the pass
+ *       argument, causing user to contain user:pass if available.
+ */
+static int parse_uri_legacy_check(char *uri, const char *scheme, char **user, char **pass, char **hostport, char **transport)
 {
-       int ret = parse_uri(uri, scheme, user, pass, domain, transport);
+       int ret = parse_uri(uri, scheme, user, pass, hostport, transport);
        if (sip_cfg.legacy_useroption_parsing) { /* if legacy mode is active, strip semis from the user field */
                char *p;
                if ((p = strchr(uri, (int)';'))) {
@@ -13742,7 +14009,7 @@ static int parse_uri_legacy_check(char *uri, const char *scheme, char **user, ch
 
 static int __set_address_from_contact(const char *fullcontact, struct ast_sockaddr *addr, int tcp)
 {
-       char *domain, *transport;
+       char *hostport, *transport;
        char contact_buf[256];
        char *contact;
 
@@ -13757,7 +14024,7 @@ static int __set_address_from_contact(const char *fullcontact, struct ast_sockad
         * We still need to be able to send to the remote agent through the proxy.
         */
 
-       if (parse_uri_legacy_check(contact, "sip:,sips:", &contact, NULL, &domain,
+       if (parse_uri_legacy_check(contact, "sip:,sips:", &contact, NULL, &hostport,
                      &transport)) {
                ast_log(LOG_WARNING, "Invalid contact uri %s (missing sip: or sips:), attempting to use anyway\n", fullcontact);
        }
@@ -13766,19 +14033,19 @@ static int __set_address_from_contact(const char *fullcontact, struct ast_sockad
        /* We should only do this if it's a name, not an IP */
        /* \todo - if there's no PORT number in contact - we are required to check NAPTR/SRV records
                to find transport, port address and hostname. If there's a port number, we have to
-               assume that the domain part is a host name and only look for an A/AAAA record in DNS.
+               assume that the hostport part is a host name and only look for an A/AAAA record in DNS.
        */
 
-       /* If we took in an invalid URI, domain may not have been initialized */
-       /* ast_sockaddr_resolve requires an initialized domain string. */
-       if (ast_strlen_zero(domain)) {
-               ast_log(LOG_WARNING, "Invalid URI: parse_uri failed to acquire domain\n");
+       /* If we took in an invalid URI, hostport may not have been initialized */
+       /* ast_sockaddr_resolve requires an initialized hostport string. */
+       if (ast_strlen_zero(hostport)) {
+               ast_log(LOG_WARNING, "Invalid URI: parse_uri failed to acquire hostport\n");
                return -1;
        }
 
-       if (ast_sockaddr_resolve_first(addr, domain, 0)) {
+       if (ast_sockaddr_resolve_first(addr, hostport, 0)) {
                ast_log(LOG_WARNING, "Invalid host name in Contact: (can't "
-                       "resolve in DNS) : '%s'\n", domain);
+                       "resolve in DNS) : '%s'\n", hostport);
                return -1;
        }
 
@@ -13815,7 +14082,7 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st
        char data[SIPBUFSIZE];
        const char *expires = sip_get_header(req, "Expires");
        int expire = atoi(expires);
-       char *curi, *domain, *transport;
+       char *curi = NULL, *hostport = NULL, *transport = NULL;
        int transport_type;
        const char *useragent;
        struct ast_sockaddr oldsin, testsa;
@@ -13893,7 +14160,7 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st
        ast_string_field_build(pvt, our_contact, "<%s>", curi);
 
        /* Make sure it's a SIP URL */
-       if (parse_uri_legacy_check(curi, "sip:,sips:", &curi, NULL, &domain, &transport)) {
+       if (ast_strlen_zero(curi) || parse_uri_legacy_check(curi, "sip:,sips:", &curi, NULL, &hostport, &transport)) {
                ast_log(LOG_NOTICE, "Not a valid SIP contact (missing sip:/sips:) trying to use anyway\n");
        }
 
@@ -13921,15 +14188,15 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st
                ast_debug(1, "Store REGISTER's Contact header for call routing.\n");
                /* XXX This could block for a long time XXX */
                /*! \todo Check NAPTR/SRV if we have not got a port in the URI */
-               if (ast_sockaddr_resolve_first(&testsa, domain, 0)) {
-                       ast_log(LOG_WARNING, "Invalid domain '%s'\n", domain);
+               if (ast_sockaddr_resolve_first(&testsa, hostport, 0)) {
+                       ast_log(LOG_WARNING, "Invalid hostport '%s'\n", hostport);
                        ast_string_field_set(peer, fullcontact, "");
                        ast_string_field_set(pvt, our_contact, "");
                        return PARSE_REGISTER_FAILED;
                }
 
                /* If we have a port number in the given URI, make sure we do remember to not check for NAPTR/SRV records.
-                  The domain part is actually a host. */
+                  The hostport part is actually a host. */
                peer->portinuri = ast_sockaddr_port(&testsa) ? TRUE : FALSE;
 
                if (!ast_sockaddr_port(&testsa)) {
@@ -13949,7 +14216,7 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st
        /* Check that they're allowed to register at this IP */
        if (ast_apply_ha(sip_cfg.contact_ha, &peer->addr) != AST_SENSE_ALLOW ||
                        ast_apply_ha(peer->contactha, &peer->addr) != AST_SENSE_ALLOW) {
-               ast_log(LOG_WARNING, "Domain '%s' disallowed by contact ACL (violating IP %s)\n", domain,
+               ast_log(LOG_WARNING, "Domain '%s' disallowed by contact ACL (violating IP %s)\n", hostport,
                        ast_sockaddr_stringify_addr(&testsa));
                ast_string_field_set(peer, fullcontact, "");
                ast_string_field_set(pvt, our_contact, "");
@@ -14003,13 +14270,13 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st
        manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Registered\r\nAddress: %s\r\n", peer->name,  ast_sockaddr_stringify(&peer->addr));
 
        /* Is this a new IP address for us? */
-       if (VERBOSITY_ATLEAST(2) && ast_sockaddr_cmp(&peer->addr, &oldsin)) {
-               ast_verbose(VERBOSE_PREFIX_3 "Registered SIP '%s' at %s\n", peer->name,
-                               ast_sockaddr_stringify(&peer->addr));
+       if (ast_sockaddr_cmp(&peer->addr, &oldsin)) {
+               ast_verb(3, "Registered SIP '%s' at %s\n", peer->name,
+                       ast_sockaddr_stringify(&peer->addr));
        }
        sip_poke_peer(peer, 0);
        register_peer_exten(peer, 1);
-       
+
        /* Save User agent */
        useragent = sip_get_header(req, "User-Agent");
        if (strcasecmp(useragent, peer->useragent)) {
@@ -14413,6 +14680,13 @@ static void network_change_event_cb(const struct ast_event *event, void *userdat
        }
 }
 
+static void cb_extensionstate_destroy(int id, void *data)
+{
+       struct sip_pvt *p = data;
+
+       dialog_unref(p, "the extensionstate containing this dialog ptr was destroyed");
+}
+
 /*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem
 \note  If you add an "hint" priority to the extension in the dial plan,
        you will get notifications on device state changes */
@@ -14427,7 +14701,6 @@ static int cb_extensionstate(const char *context, const char *exten, enum ast_ex
        case AST_EXTENSION_REMOVED:     /* Extension is gone */
                sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);     /* Delete subscription in 32 secs */
                ast_verb(2, "Extension state: Watcher for hint %s %s. Notify User %s\n", exten, state == AST_EXTENSION_DEACTIVATED ? "deactivated" : "removed", p->username);
-               p->stateid = -1;
                p->subscribed = NONE;
                append_history(p, "Subscribestatus", "%s", state == AST_EXTENSION_REMOVED ? "HintRemoved" : "Deactivated");
                break;
@@ -14571,6 +14844,18 @@ static char *terminate_uri(char *uri)
        return uri;
 }
 
+/*! \brief Terminate a host:port at the ':'
+ * \param hostport The address of the hostport string
+ *
+ * \note In the case of a bracket-enclosed IPv6 address, the hostport variable
+ * will contain the non-bracketed host as a result of calling this function.
+ */
+static void extract_host_from_hostport(char **hostport)
+{
+       char *dont_care;
+       ast_sockaddr_split_hostport(*hostport, hostport, &dont_care, PARSE_PORT_IGNORE);
+}
+
 /*! \brief Verify registration of user
        - Registration is done in several steps, first a REGISTER without auth
          to get a challenge (nonce) then a second one with auth
@@ -14582,7 +14867,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
        enum check_auth_result res = AUTH_NOT_FOUND;
        struct sip_peer *peer;
        char tmp[256];
-       char *name = NULL, *c, *domain = NULL, *dummy = NULL;
+       char *c, *name, *unused_password, *domain;
        char *uri2 = ast_strdupa(uri);
 
        terminate_uri(uri2);
@@ -14592,7 +14877,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
        c = get_in_brackets(tmp);
        c = remove_uri_parameters(c);
 
-       if (parse_uri_legacy_check(c, "sip:,sips:", &name, &dummy, &domain, NULL)) {
+       if (parse_uri_legacy_check(c, "sip:,sips:", &name, &unused_password, &domain, NULL)) {
                ast_log(LOG_NOTICE, "Invalid to address: '%s' from %s (missing sip:) trying to use anyway...\n", c, ast_sockaddr_stringify_addr(addr));
                return -1;
        }
@@ -14600,12 +14885,36 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
        SIP_PEDANTIC_DECODE(name);
        SIP_PEDANTIC_DECODE(domain);
 
-       /*! \todo XXX here too we interpret a missing @domain as a name-only
-        * URI, whereas the RFC says this is a domain-only uri.
-        */
-       if (!ast_strlen_zero(domain) && !AST_LIST_EMPTY(&domain_list)) {
+       extract_host_from_hostport(&domain);
+
+       if (ast_strlen_zero(domain)) {
+               /* <sip:name@[EMPTY]>, never good */
+               transmit_response(p, "404 Not found", &p->initreq);
+               return AUTH_UNKNOWN_DOMAIN;
+       }
+
+       if (ast_strlen_zero(name)) {
+               /* <sip:[EMPTY][@]hostport>, unsure whether valid for
+                * registration. RFC 3261, 10.2 states:
+                * "The To header field and the Request-URI field typically
+                * differ, as the former contains a user name."
+                * But, Asterisk has always treated the domain-only uri as a
+                * username: we allow admins to create accounts described by
+                * domain name. */
+               name = domain;
+       }
+
+       /* This here differs from 1.4 and 1.6: the domain matching ACLs were
+        * skipped if it was a domain-only URI (used as username). Here we treat
+        * <sip:hostport> as <sip:host@hostport> and won't forget to test the
+        * domain ACLs against host. */
+       if (!AST_LIST_EMPTY(&domain_list)) {
                if (!check_sip_domain(domain, NULL, 0)) {
-                       transmit_response(p, "404 Not found (unknown domain)", &p->initreq);
+                       if (sip_cfg.alwaysauthreject) {
+                               transmit_fake_auth_response(p, SIP_REGISTER, &p->initreq, XMIT_UNRELIABLE);
+                       } else {
+                               transmit_response(p, "404 Not found (unknown domain)", &p->initreq);
+                       }
                        return AUTH_UNKNOWN_DOMAIN;
                }
        }
@@ -14647,8 +14956,6 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
                        res = AUTH_PEER_NOT_DYNAMIC;
                } else {
                        ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_NAT_FORCE_RPORT);
-                       if (ast_test_flag(&p->flags[1], SIP_PAGE2_REGISTERTRYING))
-                               transmit_response(p, "100 Trying", req);
                        if (!(res = check_auth(p, req, peer->name, peer->secret, peer->md5secret, SIP_REGISTER, uri2, XMIT_UNRELIABLE, req->ignore))) {
                                if (sip_cancel_destroy(p))
                                        ast_log(LOG_WARNING, "Unable to cancel SIP destruction.  Expect bad things.\n");
@@ -14691,7 +14998,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
                }
                ao2_unlock(peer);
        }
-       if (!peer && sip_cfg.autocreatepeer) {
+       if (!peer && sip_cfg.autocreatepeer != AUTOPEERS_DISABLED) {
                /* Create peer if we have autocreate mode enabled */
                peer = temp_peer(name);
                if (peer) {
@@ -14729,16 +15036,10 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
                        ao2_unlock(peer);
                }
        }
-       if (!peer && sip_cfg.alwaysauthreject) {
-               /* If we found a peer, we transmit a 100 Trying.  Therefore, if we're
-                * trying to avoid leaking information, we MUST also transmit the same
-                * response when we DON'T find a peer. */
-               transmit_response(p, "100 Trying", req);
-               /* Insert a fake delay between the 100 and the subsequent failure. */
-               sched_yield();
-       }
        if (!res) {
+               ao2_unlock(p);
                sip_send_mwi_to_peer(peer, 0);
+               ao2_lock(p);
                ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name);
        }
        if (res < 0) {
@@ -15149,7 +15450,7 @@ static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, c
  */
 static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_request *oreq, int *cc_recall_core_id)
 {
-       char tmp[256] = "", *uri, *domain, *dummy = NULL;
+       char tmp[256] = "", *uri, *unused_password, *domain;
        char tmpf[256] = "", *from = NULL;
        struct sip_request *req;
        char *decoded_uri;
@@ -15165,13 +15466,16 @@ static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_re
        
        uri = ast_strdupa(get_in_brackets(tmp));
 
-       if (parse_uri_legacy_check(uri, "sip:,sips:", &uri, &dummy, &domain, NULL)) {
+       if (parse_uri_legacy_check(uri, "sip:,sips:", &uri, &unused_password, &domain, NULL)) {
                ast_log(LOG_WARNING, "Not a SIP header (%s)?\n", uri);
                return SIP_GET_DEST_INVALID_URI;
        }
 
        SIP_PEDANTIC_DECODE(domain);
        SIP_PEDANTIC_DECODE(uri);
+
+       extract_host_from_hostport(&domain);
+
        if (ast_strlen_zero(uri)) {
                /*
                 * Either there really was no extension found or the request
@@ -15198,6 +15502,8 @@ static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_re
                SIP_PEDANTIC_DECODE(from);
                SIP_PEDANTIC_DECODE(domain);
 
+               extract_host_from_hostport(&domain);
+
                ast_string_field_set(p, fromdomain, domain);
        }
 
@@ -15247,18 +15553,23 @@ static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_re
                }
        } else {
                struct ast_cc_agent *agent;
-               int which = 0;
                /* Check the dialplan for the username part of the request URI,
                   the domain will be stored in the SIPDOMAIN variable
                   Return 0 if we have a matching extension */
-               if (ast_exists_extension(NULL, p->context, uri, 1, S_OR(p->cid_num, from)) ||
-                   (ast_exists_extension(NULL, p->context, decoded_uri, 1, S_OR(p->cid_num, from)) && (which = 1)) ||
-                   !strcmp(decoded_uri, ast_pickup_ext())) {
+               if (ast_exists_extension(NULL, p->context, uri, 1, S_OR(p->cid_num, from))) {
                        if (!oreq) {
-                               ast_string_field_set(p, exten, which ? decoded_uri : uri);
+                               ast_string_field_set(p, exten, uri);
+                       }
+                       return SIP_GET_DEST_EXTEN_FOUND;
+               }
+               if (ast_exists_extension(NULL, p->context, decoded_uri, 1, S_OR(p->cid_num, from))
+                       || !strcmp(decoded_uri, ast_pickup_ext())) {
+                       if (!oreq) {
+                               ast_string_field_set(p, exten, decoded_uri);
                        }
                        return SIP_GET_DEST_EXTEN_FOUND;
-               } else if ((agent = find_sip_cc_agent_by_notify_uri(tmp))) {
+               }
+               if ((agent = find_sip_cc_agent_by_notify_uri(tmp))) {
                        struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
                        /* This is a CC recall. We can set p's extension to the exten from
                         * the original INVITE
@@ -15277,11 +15588,12 @@ static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_re
                }
        }
 
-       /* Return 1 for pickup extension or overlap dialling support (if we support it) */
-       if((ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP) &&
-           ast_canmatch_extension(NULL, p->context, decoded_uri, 1, S_OR(p->cid_num, from))) ||
-           !strncmp(decoded_uri, ast_pickup_ext(), strlen(decoded_uri))) {
-               return SIP_GET_DEST_PICKUP_EXTEN_FOUND;
+       if (ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP)
+               && (ast_canmatch_extension(NULL, p->context, uri, 1, S_OR(p->cid_num, from))
+                       || ast_canmatch_extension(NULL, p->context, decoded_uri, 1, S_OR(p->cid_num, from))
+                       || !strncmp(decoded_uri, ast_pickup_ext(), strlen(decoded_uri)))) {
+               /* Overlap dialing is enabled and we need more digits to match an extension. */
+               return SIP_GET_DEST_EXTEN_MATCHMORE;
        }
 
        return SIP_GET_DEST_EXTEN_NOT_FOUND;
@@ -15685,7 +15997,7 @@ static void check_via(struct sip_pvt *p, struct sip_request *req)
 {
        char via[512];
        char *c, *maddr;
-       struct ast_sockaddr tmp;
+       struct ast_sockaddr tmp = { { 0, } };
        uint16_t port;
 
        ast_copy_string(via, sip_get_header(req, "Via"), sizeof(via));
@@ -15941,10 +16253,7 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ
                                              int sipmethod, const char *uri, enum xmittype reliable,
                                              struct ast_sockaddr *addr, struct sip_peer **authpeer)
 {
-       char from[256] = { 0, };
-       char *dummy = NULL;     /* dummy return value for parse_uri */
-       char *domain = NULL;    /* dummy return value for parse_uri */
-       char *of;
+       char from[256] = "", *of, *name, *unused_password, *domain;
        enum check_auth_result res = AUTH_DONT_KNOW;
        char calleridname[50];
        char *uri2 = ast_strdupa(uri);
@@ -15961,8 +16270,9 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ
                return res;
        }
 
-       if (calleridname[0])
+       if (calleridname[0]) {
                ast_string_field_set(p, cid_name, calleridname);
+       }
 
        if (ast_strlen_zero(p->exten)) {
                char *t = uri2;
@@ -15984,30 +16294,36 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ
        /* save the URI part of the From header */
        ast_string_field_set(p, from, of);
 
-       /* ignore all fields but name */
-       if (parse_uri_legacy_check(of, "sip:,sips:", &of, &dummy, &domain, NULL)) {
+       if (parse_uri_legacy_check(of, "sip:,sips:", &name, &unused_password, &domain, NULL)) {
                ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n");
        }
 
-       SIP_PEDANTIC_DECODE(of);
+       SIP_PEDANTIC_DECODE(name);
        SIP_PEDANTIC_DECODE(domain);
 
-       if (ast_strlen_zero(of)) {
-               /* XXX note: the original code considered a missing @host
-                * as a username-only URI. The SIP RFC (19.1.1) says that
-                * this is wrong, and it should be considered as a domain-only URI.
-                * For backward compatibility, we keep this block, but it is
-                * really a mistake and should go away.
-                */
-               of = domain;
+       extract_host_from_hostport(&domain);
+
+       if (ast_strlen_zero(domain)) {
+               /* <sip:name@[EMPTY]>, never good */
+               ast_log(LOG_ERROR, "Empty domain name in FROM header\n");
+               return res;
+       }
+
+       if (ast_strlen_zero(name)) {
+               /* <sip:[EMPTY][@]hostport>. Asterisk 1.4 and 1.6 have always
+                * treated that as a username, so we continue the tradition:
+                * uri is now <sip:host@hostport>. */
+               name = domain;
        } else {
-               char *tmp = ast_strdupa(of);
-               /* We need to be able to handle auth-headers looking like
+               /* Non-empty name, try to get caller id from it */
+               char *tmp = ast_strdupa(name);
+               /* We need to be able to handle from-headers looking like
                        <sip:8164444422;phone-context=+1@1.2.3.4:5060;user=phone;tag=SDadkoa01-gK0c3bdb43>
                */
                tmp = strsep(&tmp, ";");
-               if (global_shrinkcallerid && ast_is_shrinkable_phonenumber(tmp))
+               if (global_shrinkcallerid && ast_is_shrinkable_phonenumber(tmp)) {
                        ast_shrink_phone_number(tmp);
+               }
                ast_string_field_set(p, cid_num, tmp);
        }
 
@@ -16021,20 +16337,22 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ
                 * pick one or another depending on the request ? XXX
                 */
                const char *hdr = sip_get_header(req, "Authorization");
-               if (ast_strlen_zero(hdr))
+               if (ast_strlen_zero(hdr)) {
                        hdr = sip_get_header(req, "Proxy-Authorization");
+               }
 
-               if ( !ast_strlen_zero(hdr) && (hdr = strstr(hdr, "username=\"")) ) {
+               if (!ast_strlen_zero(hdr) && (hdr = strstr(hdr, "username=\""))) {
                        ast_copy_string(from, hdr + strlen("username=\""), sizeof(from));
-                       of = from;
-                       of = strsep(&of, "\"");
+                       name = from;
+                       name = strsep(&name, "\"");
                }
        }
 
-       res = check_peer_ok(p, of, req, sipmethod, addr,
+       res = check_peer_ok(p, name, req, sipmethod, addr,
                        authpeer, reliable, calleridname, uri2);
-       if (res != AUTH_DONT_KNOW)
+       if (res != AUTH_DONT_KNOW) {
                return res;
+       }
 
        /* Finally, apply the guest policy */
        if (sip_cfg.allowguest) {
@@ -16047,11 +16365,11 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ
                } else {
                        res = AUTH_RTP_FAILED;
                }
-       } else if (sip_cfg.alwaysauthreject)
+       } else if (sip_cfg.alwaysauthreject) {
                res = AUTH_FAKE_AUTH; /* reject with fake authorization request */
-       else
+       } else {
                res = AUTH_SECRET_FAILED; /* we don't want any guests, authentication will fail */
-
+       }
 
        if (ast_test_flag(&p->flags[1], SIP_PAGE2_RPORT_PRESENT)) {
                ast_set_flag(&p->flags[0], SIP_NAT_RPORT_PRESENT);
@@ -16068,30 +16386,37 @@ static int check_user(struct sip_pvt *p, struct sip_request *req, int sipmethod,
        return check_user_full(p, req, sipmethod, uri, reliable, addr, NULL);
 }
 
-/*! \brief  Get text out of a SIP MESSAGE packet */
-static int get_msg_text(char *buf, int len, struct sip_request *req, int addnewline)
+/*! \brief Get message body from a SIP request
+ * \param buf Destination buffer
+ * \param len Destination buffer size
+ * \param req The SIP request
+ *
+ * When parsing the request originally, the lines are split by LF or CRLF.
+ * This function adds a single LF after every line.
+ */
+static int get_msg_text(char *buf, int len, struct sip_request *req)
 {
        int x;
-       int y;
+       int linelen;
 
        buf[0] = '\0';
-       /*XXX isn't strlen(buf) going to always be 0? */
-       y = len - strlen(buf) - 5;
-       if (y < 0)
-               y = 0;
-       for (x = 0; x < req->lines; x++) {
+       --len; /* reserve strncat null */
+       for (x = 0; len && x < req->lines; ++x) {
                const char *line = REQ_OFFSET_TO_STR(req, line[x]);
-               strncat(buf, line, y); /* safe */
-               y -= strlen(line) + 1;
-               if (y < 0)
-                       y = 0;
-               if (y != 0 && addnewline)
+               strncat(buf, line, len); /* safe */
+               linelen = strlen(buf);
+               buf += linelen;
+               len -= linelen;
+               if (len) {
                        strcat(buf, "\n"); /* safe */
+                       ++buf;
+                       --len;
+               }
        }
        return 0;
 }
 
-static int get_msg_text2(struct ast_str **buf, struct sip_request *req, int addnewline)
+static int get_msg_text2(struct ast_str **buf, struct sip_request *req)
 {
        int i, res = 0;
 
@@ -16100,7 +16425,7 @@ static int get_msg_text2(struct ast_str **buf, struct sip_request *req, int addn
        for (i = 0; res >= 0 && i < req->lines; i++) {
                const char *line = REQ_OFFSET_TO_STR(req, line[i]);
 
-               res = ast_str_append(buf, 0, "%s%s", line, addnewline ? "\n" : "");
+               res = ast_str_append(buf, 0, "%s\n", line);
        }
 
        return res < 0 ? -1 : 0;
@@ -16132,6 +16457,8 @@ AST_THREADSTORAGE(sip_msg_buf);
 static void receive_message(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e)
 {
        struct ast_str *buf;
+       char *cbuf;
+       size_t len;
        struct ast_frame f;
        const char *content_type = sip_get_header(req, "Content-Type");
        struct ast_msg *msg;
@@ -16153,11 +16480,7 @@ static void receive_message(struct sip_pvt *p, struct sip_request *req, struct a
                return;
        }
 
-       /* If this is an out of dialog msg, add back newlines, otherwise strip the new lines.
-        * In dialog msg's newlines are stripped to preserve the behavior of how Asterisk has worked
-        * in the past.  If it is found later that new lines can be added into in dialog msgs as well,
-        * then change this. */
-       if (get_msg_text2(&buf, req, p->owner ? FALSE : TRUE)) {
+       if (get_msg_text2(&buf, req)) {
                ast_log(LOG_WARNING, "Unable to retrieve text from %s\n", p->callid);
                transmit_response(p, "202 Accepted", req);
                if (!p->owner)
@@ -16165,6 +16488,18 @@ static void receive_message(struct sip_pvt *p, struct sip_request *req, struct a
                return;
        }
 
+       /* Strip trailing line feeds from message body. (get_msg_text2 may add
+        * a trailing linefeed and we don't need any at the end) */
+       cbuf = ast_str_buffer(buf);
+       len = ast_str_strlen(buf);
+       while (len > 0) {
+               if (cbuf[--len] != '\n') {
+                       ++len;
+                       break;
+               }
+       }
+       ast_str_truncate(buf, len);
+
        if (p->owner) {
                if (sip_debug_test_pvt(p))
                        ast_verbose("SIP Text message received: '%s'\n", ast_str_buffer(buf));
@@ -16380,6 +16715,18 @@ static enum st_refresher str2strefresher(const char *s)
        return map_s_x(strefreshers, s, -1);
 }
 
+/* Autocreatepeer modes */
+static struct _map_x_s autopeermodes[] = {
+        { AUTOPEERS_DISABLED, "Off"},
+        { AUTOPEERS_VOLATILE, "Volatile"},
+        { AUTOPEERS_PERSIST,  "Persisted"},
+        { -1, NULL},
+};
+
+static const char *autocreatepeer2str(enum autocreatepeer_mode r)
+{
+       return map_x_s(autopeermodes, r, "Unknown");
+}
 
 static int peer_status(struct sip_peer *peer, char *status, int statuslen)
 {
@@ -16889,6 +17236,19 @@ static const char *insecure2str(int mode)
        return map_x_s(insecurestr, mode, "<error>");
 }
 
+static const struct _map_x_s allowoverlapstr[] = {
+       { SIP_PAGE2_ALLOWOVERLAP_YES,   "Yes" },
+       { SIP_PAGE2_ALLOWOVERLAP_DTMF,  "DTMF" },
+       { SIP_PAGE2_ALLOWOVERLAP_NO,    "No" },
+       { -1,                           NULL }, /* terminator */
+};
+
+/*! \brief Convert AllowOverlap setting to printable string */
+static const char *allowoverlap2str(int mode)
+{
+       return map_x_s(allowoverlapstr, mode, "<error>");
+}
+
 /*! \brief Destroy disused contexts between reloads
        Only used in reload_config so the code for regcontext doesn't get ugly
 */
@@ -16915,42 +17275,45 @@ static void cleanup_stale_contexts(char *new, char *old)
        }
 }
 
-/*! \brief Check RTP Timeout on dialogs
+/*!
+ * \brief Check RTP Timeout on dialogs
+ *
  * \details This is used with ao2_callback to check rtptimeout
- * rtponholdtimeout and send rtpkeepalive packets
+ * rtponholdtimeout and send rtpkeepalive packets.
+ *
+ * \return CMP_MATCH for items to be unlinked from dialogs_rtpcheck.
  */
 static int dialog_checkrtp_cb(void *dialogobj, void *arg, int flags)
 {
        struct sip_pvt *dialog = dialogobj;
        time_t *t = arg;
+       int match_status;
 
        if (sip_pvt_trylock(dialog)) {
                return 0;
        }
 
        if (dialog->rtp || dialog->vrtp) {
-               check_rtp_timeout(dialog, *t);
+               match_status = check_rtp_timeout(dialog, *t);
        } else {
-               /* Dialog has no active RTP or VRTP. unlink it from the checkrtp container */
-               dialog_unlink_rtpcheck(dialog);
+               /* Dialog has no active RTP or VRTP. unlink it from dialogs_rtpcheck. */
+               match_status = CMP_MATCH;
        }
        sip_pvt_unlock(dialog);
 
-       return 0;
+       return match_status;
 }
 
 /*!
  * \brief Match dialogs that need to be destroyed
  *
  * \details This is used with ao2_callback to unlink/delete all dialogs that
- * are marked needdestroy. It will return CMP_MATCH for candidates, and they
- * will be unlinked.
+ * are marked needdestroy.
  *
  * \todo Re-work this to improve efficiency.  Currently, this function is called
  * on _every_ dialog after processing _every_ incoming SIP/UDP packet, or
  * potentially even more often when the scheduler has entries to run.
  */
-
 static int dialog_needdestroy(void *dialogobj, void *arg, int flags)
 {
        struct sip_pvt *dialog = dialogobj;
@@ -16961,7 +17324,6 @@ static int dialog_needdestroy(void *dialogobj, void *arg, int flags)
                return 0;
        }
 
-
        /* If we have sessions that needs to be destroyed, do it now */
        /* Check if we have outstanding requests not responsed to or an active call
           - if that's the case, wait with destruction */
@@ -16982,7 +17344,7 @@ static int dialog_needdestroy(void *dialogobj, void *arg, int flags)
                sip_pvt_unlock(dialog);
                /* no, the unlink should handle this: dialog_unref(dialog, "needdestroy: one more refcount decrement to allow dialog to be destroyed"); */
                /* the CMP_MATCH will unlink this dialog from the dialog hash table */
-               dialog_unlink_all(dialog, TRUE, FALSE);
+               dialog_unlink_all(dialog);
                return 0; /* the unlink_all should unlink this from the table, so.... no need to return a match */
        }
 
@@ -17389,6 +17751,8 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
                        ao2_t_ref(credentials, -1, "Unref peer auth for show");
                }
                ast_cli(fd, "  Context      : %s\n", peer->context);
+               ast_cli(fd, "  Record On feature : %s\n", peer->record_on_feature);
+               ast_cli(fd, "  Record Off feature : %s\n", peer->record_off_feature);
                ast_cli(fd, "  Subscr.Cont. : %s\n", S_OR(peer->subscribecontext, "<Not set>") );
                ast_cli(fd, "  Language     : %s\n", peer->language);
                ast_cli(fd, "  Tonezone     : %s\n", peer->zone[0] != '\0' ? peer->zone : "<Not set>");
@@ -17433,7 +17797,7 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
                ast_cli(fd, "  Trust RPID   : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[0], SIP_TRUSTRPID)));
                ast_cli(fd, "  Send RPID    : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[0], SIP_SENDRPID)));
                ast_cli(fd, "  Subscriptions: %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)));
-               ast_cli(fd, "  Overlap dial : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWOVERLAP)));
+               ast_cli(fd, "  Overlap dial : %s\n", allowoverlap2str(ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWOVERLAP)));
                if (peer->outboundproxy)
                        ast_cli(fd, "  Outb. proxy  : %s %s\n", ast_strlen_zero(peer->outboundproxy->name) ? "<not set>" : peer->outboundproxy->name,
                                                        peer->outboundproxy->force ? "(forced)" : "");
@@ -17472,7 +17836,6 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
                ast_cli(fd, ")\n");
 
                ast_cli(fd, "  Auto-Framing :  %s \n", AST_CLI_YESNO(peer->autoframing));
-               ast_cli(fd, "  100 on REG   : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_REGISTERTRYING)));
                ast_cli(fd, "  Status       : ");
                peer_status(peer, status, sizeof(status));
                ast_cli(fd, "%s\n", status);
@@ -17987,11 +18350,11 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_
        ast_cli(a->fd, "  Videosupport:           %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_VIDEOSUPPORT)));
        ast_cli(a->fd, "  Textsupport:            %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_TEXTSUPPORT)));
        ast_cli(a->fd, "  Ignore SDP sess. ver.:  %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_IGNORESDPVERSION)));
-       ast_cli(a->fd, "  AutoCreate Peer:        %s\n", AST_CLI_YESNO(sip_cfg.autocreatepeer));
+       ast_cli(a->fd, "  AutoCreate Peer:        %s\n", autocreatepeer2str(sip_cfg.autocreatepeer));
        ast_cli(a->fd, "  Match Auth Username:    %s\n", AST_CLI_YESNO(global_match_auth_username));
        ast_cli(a->fd, "  Allow unknown access:   %s\n", AST_CLI_YESNO(sip_cfg.allowguest));
        ast_cli(a->fd, "  Allow subscriptions:    %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)));
-       ast_cli(a->fd, "  Allow overlap dialing:  %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP)));
+       ast_cli(a->fd, "  Allow overlap dialing:  %s\n", allowoverlap2str(ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP)));
        ast_cli(a->fd, "  Allow promisc. redir:   %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_PROMISCREDIR)));
        ast_cli(a->fd, "  Enable call counters:   %s\n", AST_CLI_YESNO(global_callcounter));
        ast_cli(a->fd, "  SIP domain support:     %s\n", AST_CLI_YESNO(!AST_LIST_EMPTY(&domain_list)));
@@ -18021,8 +18384,8 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_
        ast_cli(a->fd, "  SDP Owner Name:         %s\n", ast_strlen_zero(global_sdpowner) ? "-" : global_sdpowner);
        ast_cli(a->fd, "  Reg. context:           %s\n", S_OR(sip_cfg.regcontext, "(not set)"));
        ast_cli(a->fd, "  Regexten on Qualify:    %s\n", AST_CLI_YESNO(sip_cfg.regextenonqualify));
-       ast_cli(a->fd, "  Trust RPID:             %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_TRUSTRPID)));
-       ast_cli(a->fd, "  Send RPID:              %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_SENDRPID)));
+       ast_cli(a->fd, "  Trust RPID:             %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_TRUSTRPID)));
+       ast_cli(a->fd, "  Send RPID:              %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_SENDRPID)));
        ast_cli(a->fd, "  Legacy userfield parse: %s\n", AST_CLI_YESNO(sip_cfg.legacy_useroption_parsing));
        ast_cli(a->fd, "  Caller ID:              %s\n", default_callerid);
        if ((default_fromdomainport) && (default_fromdomainport != STANDARD_SIP_PORT)) {
@@ -18141,6 +18504,8 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_
        ast_cli(a->fd, "  Allowed transports:     %s\n", get_transport_list(default_transports));
        ast_cli(a->fd, "  Outbound transport:     %s\n", sip_get_transport(default_primary_transport));
        ast_cli(a->fd, "  Context:                %s\n", sip_cfg.default_context);
+       ast_cli(a->fd, "  Record on feature:      %s\n", sip_cfg.default_record_on_feature);
+       ast_cli(a->fd, "  Record off feature:     %s\n", sip_cfg.default_record_off_feature);
        ast_cli(a->fd, "  Force rport:            %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_NAT_FORCE_RPORT)));
        ast_cli(a->fd, "  DTMF:                   %s\n", dtmfmode2str(ast_test_flag(&global_flags[0], SIP_DTMF)));
        ast_cli(a->fd, "  Qualify:                %d\n", default_qualify);
@@ -18728,16 +19093,21 @@ static void handle_request_info(struct sip_pvt *p, struct sip_request *req)
                        return;
                }
 
-               if (buf[0] == '*') {
+               if ('0' <= buf[0] && buf[0] <= '9') {
+                       event = buf[0] - '0';
+               } else if (buf[0] == '*') {
                        event = 10;
                } else if (buf[0] == '#') {
                        event = 11;
-               } else if ((buf[0] >= 'A') && (buf[0] <= 'D')) {
+               } else if ('A' <= buf[0] && buf[0] <= 'D') {
                        event = 12 + buf[0] - 'A';
+               } else if ('a' <= buf[0] && buf[0] <= 'd') {
+                       event = 12 + buf[0] - 'a';
                } else if (buf[0] == '!') {
                        event = 16;
                } else {
-                       event = atoi(buf);
+                       /* Unknown digit */
+                       event = 0;
                }
                if (event == 16) {
                        /* send a FLASH event */
@@ -18776,7 +19146,7 @@ static void handle_request_info(struct sip_pvt *p, struct sip_request *req)
                        return;
                }
 
-               get_msg_text(buf, sizeof(buf), req, TRUE);
+               get_msg_text(buf, sizeof(buf), req);
                duration = 100; /* 100 ms */
 
                if (ast_strlen_zero(buf)) {
@@ -18802,6 +19172,9 @@ static void handle_request_info(struct sip_pvt *p, struct sip_request *req)
                                f.subclass.integer = '#';
                        } else if (event < 16) {
                                f.subclass.integer = 'A' + (event - 12);
+                       } else {
+                               /* Unknown digit. */
+                               f.subclass.integer = '0';
                        }
                        f.len = duration;
                        ast_queue_frame(p->owner, &f);
@@ -18835,20 +19208,43 @@ static void handle_request_info(struct sip_pvt *p, struct sip_request *req)
                return;
        } else if (!ast_strlen_zero(c = sip_get_header(req, "Record"))) {
                /* INFO messages generated by some phones to start/stop recording
-                       on phone calls.
-                       OEJ: I think this should be something that is enabled/disabled
-                       per device. I don't want incoming callers to record calls in my
-                       pbx.
-               */
-               /* first, get the feature string, if it exists */
-               struct ast_call_feature *feat;
+                * on phone calls.
+                */
+
+               struct ast_call_feature *feat = NULL;
                int j;
                struct ast_frame f = { AST_FRAME_DTMF, };
+               int suppress_warning = 0; /* Supress warning if the feature is blank */
 
+               if (!p->owner) {        /* not a PBX call */
+                       transmit_response(p, "481 Call leg/transaction does not exist", req);
+                       sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+                       return;
+               }
+
+               /* first, get the feature string, if it exists */
                ast_rdlock_call_features();
-               feat = ast_find_call_feature("automon");
+               if (p->relatedpeer) {
+                       if (!strcasecmp(c, "on")) {
+                               if (ast_strlen_zero(p->relatedpeer->record_on_feature)) {
+                                       suppress_warning = 1;
+                               } else {
+                                       feat = ast_find_call_feature(p->relatedpeer->record_on_feature);
+                               }
+                       } else if (!strcasecmp(c, "off")) {
+                               if (ast_strlen_zero(p->relatedpeer->record_on_feature)) {
+                                       suppress_warning = 1;
+                               } else {
+                                       feat = ast_find_call_feature(p->relatedpeer->record_off_feature);
+                               }
+                       } else {
+                               ast_log(LOG_ERROR, "Received INFO requesting to record with invalid value: %s\n", c);
+                       }
+               }
                if (!feat || ast_strlen_zero(feat->exten)) {
-                       ast_log(LOG_WARNING, "Recording requested, but no One Touch Monitor registered. (See features.conf)\n");
+                       if (!suppress_warning) {
+                               ast_log(LOG_WARNING, "Recording requested, but no One Touch Monitor registered. (See features.conf)\n");
+                       }
                        /* 403 means that we don't support this feature, so don't request it again */
                        transmit_response(p, "403 Forbidden", req);
                        ast_unlock_call_features();
@@ -19002,7 +19398,7 @@ static char *sip_cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
 
                if (create_addr(p, a->argv[i], NULL, 1, NULL)) {
                        /* Maybe they're not registered, etc. */
-                       dialog_unlink_all(p, TRUE, TRUE);
+                       dialog_unlink_all(p);
                        dialog_unref(p, "unref dialog inside for loop" );
                        /* sip_destroy(p); */
                        ast_cli(a->fd, "Could not create address for '%s'\n", a->argv[i]);
@@ -19985,6 +20381,13 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest
                if (!req->ignore && p->invitestate != INV_CANCELLED && sip_cancel_destroy(p)) {
                        ast_log(LOG_WARNING, "Unable to cancel SIP destruction.  Expect bad things.\n");
                }
+               /* Store Route-set from provisional SIP responses so
+                * early-dialog request can be routed properly
+                * */
+               parse_ok_contact(p, req);
+               if (!reinvite) {
+                       build_route(p, req, 1);
+               }
                if (!req->ignore && p->owner) {
                        if (get_rpid(p, req)) {
                                /* Queue a connected line update */
@@ -20029,6 +20432,13 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest
        case 181:       /* Call Is Being Forwarded */
                if (!req->ignore && (p->invitestate != INV_CANCELLED) && sip_cancel_destroy(p))
                        ast_log(LOG_WARNING, "Unable to cancel SIP destruction.  Expect bad things.\n");
+               /* Store Route-set from provisional SIP responses so
+                * early-dialog request can be routed properly
+                * */
+               parse_ok_contact(p, req);
+               if (!reinvite) {
+                       build_route(p, req, 1);
+               }
                if (!req->ignore && p->owner) {
                        struct ast_party_redirecting redirecting;
                        struct ast_set_party_redirecting update_redirecting;
@@ -20049,6 +20459,13 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest
                if (!req->ignore && (p->invitestate != INV_CANCELLED) && sip_cancel_destroy(p)) {
                        ast_log(LOG_WARNING, "Unable to cancel SIP destruction.  Expect bad things.\n");
                }
+               /* Store Route-set from provisional SIP responses so
+                * early-dialog request can be routed properly
+                * */
+               parse_ok_contact(p, req);
+               if (!reinvite) {
+                       build_route(p, req, 1);
+               }
                if (!req->ignore && p->owner) {
                        if (get_rpid(p, req)) {
                                /* Queue a connected line update */
@@ -20760,8 +21177,14 @@ static void handle_response_peerpoke(struct sip_pvt *p, int resp, struct sip_req
         * -1 means did not respond, 0 means unknown,
         * 1..maxms is a valid response, >maxms means late response.
         */
-       if (pingtime < 1)       /* zero = unknown, so round up to 1 */
+       if (pingtime < 1) {     /* zero = unknown, so round up to 1 */
                pingtime = 1;
+       }
+
+       if (!peer->maxms) { /* this should never happens */
+               pvt_set_needdestroy(p, "got OPTIONS response but qualify is not enabled");
+               return;
+       }
 
        /* Now determine new state and whether it has changed.
         * Use some helper variables to simplify the writing
@@ -21035,7 +21458,7 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
                return;
        }
 
-       if (p->relatedpeer && p->method == SIP_OPTIONS) {
+       if (p->relatedpeer && sipmethod == SIP_OPTIONS) {
                /* We don't really care what the response is, just that it replied back.
                   Well, as long as it's not a 100 response...  since we might
                   need to hang around for something more "definitive" */
@@ -21263,10 +21686,13 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
                                        break;
                                case 484: /* Address Incomplete */
                                        if (owner && sipmethod != SIP_BYE) {
-                                               if (ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP)) {
+                                               switch (ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP)) {
+                                               case SIP_PAGE2_ALLOWOVERLAP_YES:
                                                        ast_queue_hangup_with_cause(p->owner, hangup_sip2cause(resp));
-                                               } else {
+                                                       break;
+                                               default:
                                                        ast_queue_hangup_with_cause(p->owner, hangup_sip2cause(404));
+                                                       break;
                                                }
                                        }
                                        break;
@@ -21412,32 +21838,16 @@ static void *sip_park_thread(void *stuff)
 {
        struct ast_channel *transferee, *transferer;    /* Chan1: The transferee, Chan2: The transferer */
        struct sip_dual *d;
-       struct sip_request req = {0,};
        int ext;
        int res;
 
        d = stuff;
        transferee = d->chan1;
        transferer = d->chan2;
-       copy_request(&req, &d->req);
 
-       if (!transferee || !transferer) {
-               ast_log(LOG_ERROR, "Missing channels for parking! Transferer %s Transferee %s\n", transferer ? "<available>" : "<missing>", transferee ? "<available>" : "<missing>" );
-               deinit_req(&d->req);
-               ast_free(d);
-               return NULL;
-       }
        ast_debug(4, "SIP Park: Transferer channel %s, Transferee %s\n", transferer->name, transferee->name);
 
-       if (ast_do_masquerade(transferee)) {
-               ast_log(LOG_WARNING, "Masquerade failed.\n");
-               transmit_response(transferer->tech_pvt, "503 Internal error", &req);
-               deinit_req(&d->req);
-               ast_free(d);
-               return NULL;
-       }
-
-       res = ast_park_call(transferee, transferer, 0, d->parkexten, &ext);
+       res = ast_park_call_exten(transferee, transferer, d->park_exten, d->park_context, 0, &ext);
 
 #ifdef WHEN_WE_KNOW_THAT_THE_CLIENT_SUPPORTS_MESSAGE
        if (res) {
@@ -21465,31 +21875,40 @@ static void *sip_park_thread(void *stuff)
                /* Do not hangup call */
        }
        deinit_req(&d->req);
+       ast_free(d->park_exten);
+       ast_free(d->park_context);
        ast_free(d);
        return NULL;
 }
 
-/*! \brief Park a call using the subsystem in res_features.c
-       This is executed in a separate thread
-*/
-static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, int seqno, char *parkexten)
+/*! DO NOT hold any locks while calling sip_park */
+static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, int seqno, const char *park_exten, const char *park_context)
 {
        struct sip_dual *d;
        struct ast_channel *transferee, *transferer;
-               /* Chan2m: The transferer, chan1m: The transferee */
        pthread_t th;
 
        transferee = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan1->accountcode, chan1->exten, chan1->context, chan1->linkedid, chan1->amaflags, "Parking/%s", chan1->name);
        transferer = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan2->exten, chan2->context, chan2->linkedid, chan2->amaflags, "SIPPeer/%s", chan2->name);
-       if ((!transferer) || (!transferee)) {
+       d = ast_calloc(1, sizeof(*d));
+       if (!transferee || !transferer || !d) {
                if (transferee) {
-                       transferee->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
                        ast_hangup(transferee);
                }
                if (transferer) {
-                       transferer->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
                        ast_hangup(transferer);
                }
+               ast_free(d);
+               return -1;
+       }
+       d->park_exten = ast_strdup(park_exten);
+       d->park_context = ast_strdup(park_context);
+       if (!d->park_exten || !d->park_context) {
+               ast_hangup(transferee);
+               ast_hangup(transferer);
+               ast_free(d->park_exten);
+               ast_free(d->park_context);
+               ast_free(d);
                return -1;
        }
 
@@ -21498,67 +21917,56 @@ static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct
        transferee->writeformat = chan1->writeformat;
 
        /* Prepare for taking over the channel */
-       ast_channel_masquerade(transferee, chan1);
+       if (ast_channel_masquerade(transferee, chan1)) {
+               ast_hangup(transferee);
+               ast_hangup(transferer);
+               ast_free(d->park_exten);
+               ast_free(d->park_context);
+               ast_free(d);
+               return -1;
+       }
 
        /* Setup the extensions and such */
        ast_copy_string(transferee->context, chan1->context, sizeof(transferee->context));
        ast_copy_string(transferee->exten, chan1->exten, sizeof(transferee->exten));
        transferee->priority = chan1->priority;
-               
+
+       ast_do_masquerade(transferee);
+
        /* We make a clone of the peer channel too, so we can play
           back the announcement */
 
        /* Make formats okay */
        transferer->readformat = chan2->readformat;
        transferer->writeformat = chan2->writeformat;
-       if (!ast_strlen_zero(chan2->parkinglot))
-               ast_string_field_set(transferer, parkinglot, chan2->parkinglot);
-
-       /* Prepare for taking over the channel.  Go ahead and grab this channel
-        * lock here to avoid a deadlock with callbacks into the channel driver
-        * that hold the channel lock and want the pvt lock.  */
-       while (ast_channel_trylock(chan2)) {
-               struct sip_pvt *pvt = chan2->tech_pvt;
-               sip_pvt_unlock(pvt);
-               usleep(1);
-               sip_pvt_lock(pvt);
+       ast_string_field_set(transferer, parkinglot, chan2->parkinglot);
+
+       /* Prepare for taking over the channel */
+       if (ast_channel_masquerade(transferer, chan2)) {
+               ast_hangup(transferer);
+               ast_free(d->park_exten);
+               ast_free(d->park_context);
+               ast_free(d);
+               return -1;
        }
-       ast_channel_masquerade(transferer, chan2);
-       ast_channel_unlock(chan2);
 
        /* Setup the extensions and such */
        ast_copy_string(transferer->context, chan2->context, sizeof(transferer->context));
        ast_copy_string(transferer->exten, chan2->exten, sizeof(transferer->exten));
        transferer->priority = chan2->priority;
 
-       if (ast_do_masquerade(transferer)) {
-               ast_log(LOG_WARNING, "Masquerade failed :(\n");
-               transferer->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
-               ast_hangup(transferer);
-               return -1;
-       }
-       if (!transferer || !transferee) {
-               if (!transferer) {
-                       ast_debug(1, "No transferer channel, giving up parking\n");
-               }
-               if (!transferee) {
-                       ast_debug(1, "No transferee channel, giving up parking\n");
-               }
-               return -1;
-       }
-       if (!(d = ast_calloc(1, sizeof(*d)))) {
-               return -1;
-       }
+       ast_do_masquerade(transferer);
 
        /* Save original request for followup */
        copy_request(&d->req, req);
        d->chan1 = transferee;  /* Transferee */
        d->chan2 = transferer;  /* Transferer */
        d->seqno = seqno;
-       d->parkexten = parkexten;
        if (ast_pthread_create_detached_background(&th, NULL, sip_park_thread, d) < 0) {
                /* Could not start thread */
                deinit_req(&d->req);
+               ast_free(d->park_exten);
+               ast_free(d->park_context);
                ast_free(d);    /* We don't need it anymore. If thread is created, d will be free'd
                                   by sip_park_thread() */
                return -1;
@@ -21812,7 +22220,7 @@ static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, str
                }
 
                /* Get the text of the attachment */
-               if (get_msg_text(buf, sizeof(buf), req, TRUE)) {
+               if (get_msg_text(buf, sizeof(buf), req)) {
                        ast_log(LOG_WARNING, "Unable to retrieve attachment from NOTIFY %s\n", p->callid);
                        transmit_response(p, "400 Bad request", req);
                        sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
@@ -21997,7 +22405,7 @@ static int handle_request_options(struct sip_pvt *p, struct sip_request *req, st
                case SIP_GET_DEST_INVALID_URI:
                        msg = "416 Unsupported URI scheme";
                        break;
-               case SIP_GET_DEST_PICKUP_EXTEN_FOUND:
+               case SIP_GET_DEST_EXTEN_MATCHMORE:
                case SIP_GET_DEST_REFUSED:
                case SIP_GET_DEST_EXTEN_NOT_FOUND:
                        //msg = "404 Not Found";
@@ -22718,12 +23126,21 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
                        case SIP_GET_DEST_INVALID_URI:
                                transmit_response_reliable(p, "416 Unsupported URI scheme", req);
                                break;
-                       case SIP_GET_DEST_PICKUP_EXTEN_FOUND:
-                               if (ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP)) {
+                       case SIP_GET_DEST_EXTEN_MATCHMORE:
+                               if (ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP)
+                                       == SIP_PAGE2_ALLOWOVERLAP_YES) {
                                        transmit_response_reliable(p, "484 Address Incomplete", req);
                                        break;
                                }
-                       /* INTENTIONAL FALL THROUGH */
+                               /*
+                                * XXX We would have to implement collecting more digits in
+                                * chan_sip for any other schemes of overlap dialing.
+                                *
+                                * For SIP_PAGE2_ALLOWOVERLAP_DTMF it is better to do this in
+                                * the dialplan using the Incomplete application rather than
+                                * having the channel driver do it.
+                                */
+                               /* Fall through */
                        case SIP_GET_DEST_EXTEN_NOT_FOUND:
                        case SIP_GET_DEST_REFUSED:
                        default:
@@ -22766,8 +23183,6 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
                        if (c) {
                                ast_party_redirecting_init(&redirecting);
                                memset(&update_redirecting, 0, sizeof(update_redirecting));
-                               /* Pre-lock the call */
-                               ast_channel_lock(c);
                                change_redirecting_information(p, req, &redirecting, &update_redirecting,
                                        FALSE); /*Will return immediately if no Diversion header is present */
                                ast_channel_set_redirecting(c, &redirecting, &update_redirecting);
@@ -23587,6 +24002,7 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int
        callid = ast_strdupa(p->callid);
        localtransfer = p->refer->localtransfer;
        attendedtransfer = p->refer->attendedtransfer;
+
        if (!*nounlock) {
                ast_channel_unlock(p->owner);
                *nounlock = 1;
@@ -23595,9 +24011,6 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int
 
        /* Parking a call.  DO NOT hold any locks while calling ast_parking_ext_valid() */
        if (localtransfer && ast_parking_ext_valid(refer_to, current.chan1, current.chan1->context)) {
-
-               copy_request(&current.req, req);
-
                sip_pvt_lock(p);
                ast_clear_flag(&p->flags[0], SIP_GOTREFER);
                p->refer->status = REFER_200OK;
@@ -23626,7 +24039,7 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int
                }
 
                /* DO NOT hold any locks while calling sip_park */
-               if (sip_park(current.chan2, current.chan1, req, seqno, refer_to)) {
+               if (sip_park(current.chan2, current.chan1, req, seqno, refer_to, current.chan1->context)) {
                        sip_pvt_lock(p);
                        transmit_notify_with_sipfrag(p, seqno, "500 Internal Server Error", TRUE);
                } else {
@@ -23676,7 +24089,7 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int
 
        /* FAKE ringing if not attended transfer */
        if (!p->refer->attendedtransfer) {
-               transmit_notify_with_sipfrag(p, seqno, "183 Ringing", FALSE);
+               transmit_notify_with_sipfrag(p, seqno, "180 Ringing", FALSE);
        }
 
        /* For blind transfer, this will lead to a new call */
@@ -24014,22 +24427,19 @@ static int sip_msg_send(const struct ast_msg *msg, const char *to, const char *f
 {
        struct sip_pvt *pvt;
        int res;
-       char *peer;
+       char *to_uri, *to_host, *to_user;
        struct sip_peer *peer_ptr;
 
        if (!(pvt = sip_alloc(NULL, NULL, 0, SIP_MESSAGE, NULL))) {
                return -1;
        }
 
-       peer = ast_strdupa(to);
-       if (strchr(peer, '@')) {
-               strsep(&peer, "@");
-       } else {
-               strsep(&peer, ":");
-       }
-       if (ast_strlen_zero(peer)) {
+       to_uri = ast_strdupa(to);
+       parse_uri(to_uri, "sip:,sips:", &to_user, NULL, &to_host, NULL);
+
+       if (ast_strlen_zero(to_host)) {
                ast_log(LOG_WARNING, "MESSAGE(to) is invalid for SIP - '%s'\n", to);
-               dialog_unlink_all(pvt, TRUE, TRUE);
+               dialog_unlink_all(pvt);
                dialog_unref(pvt, "MESSAGE(to) is invalid for SIP");
                return -1;
        }
@@ -24048,6 +24458,9 @@ static int sip_msg_send(const struct ast_msg *msg, const char *to, const char *f
                        ast_string_field_set(pvt, fromname, name);
                        if (strchr(location, ':')) { /* Must be a URI */
                                parse_uri(location, "sip:,sips:", &user, NULL, &domain, NULL);
+                               SIP_PEDANTIC_DECODE(user);
+                               SIP_PEDANTIC_DECODE(domain);
+                               extract_host_from_hostport(&domain);
                                ast_string_field_set(pvt, fromuser, user);
                                ast_string_field_set(pvt, fromdomain, domain);
                        } else { /* Treat it as an exten/user */
@@ -24060,12 +24473,17 @@ static int sip_msg_send(const struct ast_msg *msg, const char *to, const char *f
 
        sip_pvt_lock(pvt);
 
-       if (create_addr(pvt, peer, NULL, TRUE, NULL)) {
+       /* Look up the host to contact */
+       if (create_addr(pvt, to_host, NULL, TRUE, NULL)) {
                sip_pvt_unlock(pvt);
-               dialog_unlink_all(pvt, TRUE, TRUE);
+               dialog_unlink_all(pvt);
                dialog_unref(pvt, "create_addr failed sending a MESSAGE");
                return -1;
        }
+
+       if (!ast_strlen_zero(to_user)) {
+               ast_string_field_set(pvt, username, to_user);
+       }
        ast_sip_ouraddrfor(&pvt->sa, &pvt->ourip, pvt);
        ast_set_flag(&pvt->flags[0], SIP_OUTGOING);
 
@@ -24586,11 +25004,21 @@ static int handle_request_publish(struct sip_pvt *p, struct sip_request *req, st
        return handler_result;
 }
 
+/*! \internal \brief Subscribe to MWI events for the specified peer
+ * \note The peer cannot be locked during this method.  sip_send_mwi_peer will
+ * attempt to lock the peer after the event subscription lock is held; if the peer is locked during
+ * this method then we will attempt to lock the event subscription lock but after the peer, creating
+ * a locking inversion.
+ */
 static void add_peer_mwi_subs(struct sip_peer *peer)
 {
        struct sip_mailbox *mailbox;
 
        AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) {
+               if (mailbox->event_sub) {
+                       ast_event_unsubscribe(mailbox->event_sub);
+               }
+
                mailbox->event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, "SIP mbox event", peer,
                        AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox->mailbox,
                        AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, S_OR(mailbox->context, "default"),
@@ -24661,7 +25089,7 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
 {
        int gotdest = 0;
        int res = 0;
-       int firststate = AST_EXTENSION_REMOVED;
+       int firststate;
        struct sip_peer *authpeer = NULL;
        const char *eventheader = sip_get_header(req, "Event"); /* Get Event package name */
        int resubscribe = (p->subscribed != NONE) && !req->ignore;
@@ -24744,7 +25172,7 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                /* if an authentication response was sent, we are done here */
                if (res == AUTH_CHALLENGE_SENT) /* authpeer = NULL here */
                        return 0;
-               if (res < 0) {
+               if (res != AUTH_SUCCESSFUL) {
                        if (res == AUTH_FAKE_AUTH) {
                                ast_log(LOG_NOTICE, "Sending fake auth rejection for device %s\n", sip_get_header(req, "From"));
                                transmit_fake_auth_response(p, SIP_SUBSCRIBE, req, XMIT_UNRELIABLE);
@@ -24758,17 +25186,17 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                }
        }
 
-       /* At this point, authpeer cannot be NULL. Remember we hold a reference,
-        * so we must release it when done.
-        * XXX must remove all the checks for authpeer == NULL.
+       /* At this point, we hold a reference to authpeer (if not NULL).  It
+        * must be released when done.
         */
 
        /* Check if this device  is allowed to subscribe at all */
        if (!ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)) {
                transmit_response(p, "403 Forbidden (policy)", req);
                pvt_set_needdestroy(p, "subscription not allowed");
-               if (authpeer)
+               if (authpeer) {
                        sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 1)");
+               }
                return 0;
        }
 
@@ -24788,8 +25216,9 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                        transmit_response(p, "404 Not Found", req);
                }
                pvt_set_needdestroy(p, "subscription target not found");
-               if (authpeer)
+               if (authpeer) {
                        sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 2)");
+               }
                return 0;
        }
 
@@ -24804,9 +25233,6 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                enum subscriptiontype subscribed = NONE;
                const char *unknown_acceptheader = NULL;
 
-               if (authpeer)   /* We do not need the authpeer any more */
-                       authpeer = sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 2)");
-
                /* Header from Xten Eye-beam Accept: multipart/related, application/rlmi+xml, application/pidf+xml, application/xpidf+xml */
                accept = __get_header(req, "Accept", &start);
                while ((subscribed == NONE) && !ast_strlen_zero(accept)) {
@@ -24844,6 +25270,9 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                                        p->subscribecontext,
                                        p->subscribeuri);
                                pvt_set_needdestroy(p, "no Accept header");
+                               if (authpeer) {
+                                       sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 2)");
+                               }
                                return 0;
                        }
                        /* if p->subscribed is non-zero, then accept is not obligatory; according to rfc 3265 section 3.1.3, at least.
@@ -24868,6 +25297,9 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                                p->subscribecontext,
                                p->subscribeuri);
                        pvt_set_needdestroy(p, "unrecognized format");
+                       if (authpeer) {
+                               sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 2)");
+                       }
                        return 0;
                } else {
                        p->subscribed = subscribed;
@@ -24890,8 +25322,9 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                        transmit_response(p, "406 Not Acceptable", req);
                        ast_debug(2, "Received SIP mailbox subscription for unknown format: %s\n", acceptheader);
                        pvt_set_needdestroy(p, "unknown format");
-                       if (authpeer)
+                       if (authpeer) {
                                sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 3)");
+                       }
                        return 0;
                }
                /* Looks like they actually want a mailbox status
@@ -24900,30 +25333,41 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                  In most devices, this is configurable to the voicemailmain extension you use
                */
                if (!authpeer || AST_LIST_EMPTY(&authpeer->mailboxes)) {
-                       transmit_response(p, "404 Not found (no mailbox)", req);
+                       if (!authpeer) {
+                               transmit_response(p, "404 Not found", req);
+                       } else {
+                               transmit_response(p, "404 Not found (no mailbox)", req);
+                               ast_log(LOG_NOTICE, "Received SIP subscribe for peer without mailbox: %s\n", S_OR(authpeer->name, ""));
+                       }
                        pvt_set_needdestroy(p, "received 404 response");
-                       ast_log(LOG_NOTICE, "Received SIP subscribe for peer without mailbox: %s\n", S_OR(authpeer->name, ""));
-                       if (authpeer)
-                               sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 4)");
+
+                       if (authpeer) {
+                               sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 3)");
+                       }
                        return 0;
                }
 
                p->subscribed = MWI_NOTIFICATION;
                if (ast_test_flag(&authpeer->flags[1], SIP_PAGE2_SUBSCRIBEMWIONLY)) {
+                       ao2_unlock(p);
                        add_peer_mwi_subs(authpeer);
+                       ao2_lock(p);
                }
-               if (authpeer->mwipvt && authpeer->mwipvt != p) {        /* Destroy old PVT if this is a new one */
+               if (authpeer->mwipvt != p) {    /* Destroy old PVT if this is a new one */
                        /* We only allow one subscription per peer */
-                       dialog_unlink_all(authpeer->mwipvt, TRUE, TRUE);
-                       authpeer->mwipvt = dialog_unref(authpeer->mwipvt, "unref dialog authpeer->mwipvt");
-                       /* sip_destroy(authpeer->mwipvt); */
-               }
-               if (authpeer->mwipvt)
-                       dialog_unref(authpeer->mwipvt, "Unref previously stored mwipvt dialog pointer");
-               authpeer->mwipvt = dialog_ref(p, "setting peers' mwipvt to p");         /* Link from peer to pvt UH- should this be dialog_ref()? */
-               if (p->relatedpeer)
-                       sip_unref_peer(p->relatedpeer, "Unref previously stored relatedpeer ptr");
-               p->relatedpeer = sip_ref_peer(authpeer, "setting dialog's relatedpeer pointer");        /* already refcounted...Link from pvt to peer UH- should this be dialog_ref()? */
+                       if (authpeer->mwipvt) {
+                               dialog_unlink_all(authpeer->mwipvt);
+                               authpeer->mwipvt = dialog_unref(authpeer->mwipvt, "unref dialog authpeer->mwipvt");
+                       }
+                       authpeer->mwipvt = dialog_ref(p, "setting peers' mwipvt to p");
+               }
+
+               if (p->relatedpeer != authpeer) {
+                       if (p->relatedpeer) {
+                               sip_unref_peer(p->relatedpeer, "Unref previously stored relatedpeer ptr");
+                       }
+                       p->relatedpeer = sip_ref_peer(authpeer, "setting dialog's relatedpeer pointer");
+               }
                /* Do not release authpeer here */
        } else if (!strcmp(event, "call-completion")) {
                handle_cc_subscribe(p, req);
@@ -24931,24 +25375,23 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                transmit_response(p, "489 Bad Event", req);
                ast_debug(2, "Received SIP subscribe for unknown event package: %s\n", event);
                pvt_set_needdestroy(p, "unknown event package");
-               if (authpeer)
+               if (authpeer) {
                        sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 5)");
+               }
                return 0;
        }
 
-       /* At this point, if we have an authpeer we should unref it. */
-       if (authpeer) {
-               authpeer = sip_unref_peer(authpeer, "unref pointer into (*authpeer)");
-       }
-
        /* Add subscription for extension state from the PBX core */
        if (p->subscribed != MWI_NOTIFICATION  && p->subscribed != CALL_COMPLETION && !resubscribe) {
-               if (p->stateid > -1) {
+               if (p->stateid != -1) {
                        ast_extension_state_del(p->stateid, cb_extensionstate);
-                       /* we need to dec the refcount, now that the extensionstate is removed */
-                       dialog_unref(p, "the extensionstate containing this dialog ptr was deleted");
                }
-               p->stateid = ast_extension_state_add(p->context, p->exten, cb_extensionstate, dialog_ref(p,"copying dialog ptr into extension state struct"));
+               dialog_ref(p, "copying dialog ptr into extension state struct");
+               p->stateid = ast_extension_state_add_destroy(p->context, p->exten,
+                       cb_extensionstate, cb_extensionstate_destroy, p);
+               if (p->stateid == -1) {
+                       dialog_unref(p, "copying dialog ptr into extension state struct failed");
+               }
        }
 
        if (!req->ignore && p)
@@ -24965,6 +25408,9 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                                "with Expire header less that 'minexpire' limit. Received \"Expire: %d\" min is %d\n",
                                p->exten, p->context, p->expiry, min_expiry);
                        pvt_set_needdestroy(p, "Expires is less that the min expires allowed.");
+                       if (authpeer) {
+                               sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 6)");
+                       }
                        return 0;
                }
 
@@ -24986,7 +25432,12 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                        ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
                        transmit_response(p, "200 OK", req);
                        if (p->relatedpeer) {   /* Send first notification */
-                               sip_send_mwi_to_peer(p->relatedpeer, 0);
+                               struct sip_peer *peer = p->relatedpeer;
+                               sip_ref_peer(peer, "ensure a peer ref is held during MWI sending");
+                               ao2_unlock(p);
+                               sip_send_mwi_to_peer(peer, 0);
+                               ao2_lock(p);
+                               sip_unref_peer(peer, "release a peer ref now that MWI is sent");
                        }
                } else if (p->subscribed != CALL_COMPLETION) {
 
@@ -24995,6 +25446,9 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                                ast_log(LOG_NOTICE, "Got SUBSCRIBE for extension %s@%s from %s, but there is no hint for that extension.\n", p->exten, p->context, ast_sockaddr_stringify(&p->sa));
                                transmit_response(p, "404 Not found", req);
                                pvt_set_needdestroy(p, "no extension for SUBSCRIBE");
+                               if (authpeer) {
+                                       sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 6)");
+                               }
                                return 0;
                        }
                        ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
@@ -25010,6 +25464,10 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                        pvt_set_needdestroy(p, "forcing expiration");
                }
        }
+
+       if (authpeer) {
+               sip_unref_peer(authpeer, "unref pointer into (*authpeer)");
+       }
        return 1;
 }
 
@@ -25682,8 +26140,8 @@ create_tcptls_session_fail:
                ao2_t_ref(ca, -1, "failed to create client, getting rid of client tcptls_session arguments");
        }
        if (s->tcptls_session) {
-               close(tcptls_session->fd);
-               s->fd = tcptls_session->fd = -1;
+               ast_tcptls_close_session_file(tcptls_session);
+               s->fd = -1;
                ao2_ref(s->tcptls_session, -1);
                s->tcptls_session = NULL;
        }
@@ -25722,20 +26180,30 @@ static int get_cached_mwi(struct sip_peer *peer, int *new, int *old)
 }
 
 /*! \brief Send message waiting indication to alert peer that they've got voicemail
+ *  \note Both peer and associated sip_pvt must be unlocked prior to calling this function
  *  \returns -1 on failure, 0 on success
  */
 static int sip_send_mwi_to_peer(struct sip_peer *peer, int cache_only)
 {
-       /* Called with peerl lock, but releases it */
+       /* Called with peer lock, but releases it */
        struct sip_pvt *p;
        int newmsgs = 0, oldmsgs = 0;
+       const char *vmexten = NULL;
+
+       ao2_lock(peer);
+
+       if (peer->vmexten) {
+               vmexten = ast_strdupa(peer->vmexten);
+       }
 
        if (ast_test_flag((&peer->flags[1]), SIP_PAGE2_SUBSCRIBEMWIONLY) && !peer->mwipvt) {
+               ao2_unlock(peer);
                return -1;
        }
 
        /* Do we have an IP address? If not, skip this peer */
        if (ast_sockaddr_isnull(&peer->addr) && ast_sockaddr_isnull(&peer->defaddr)) {
+               ao2_unlock(peer);
                return -1;
        }
 
@@ -25746,19 +26214,22 @@ static int sip_send_mwi_to_peer(struct sip_peer *peer, int cache_only)
                peer_mailboxes_to_str(&mailbox_str, peer);
                /* if there is no mailbox do nothing */
                if (ast_strlen_zero(mailbox_str->str)) {
+                       ao2_unlock(peer);
                        return -1;
                }
+               ao2_unlock(peer);
                ast_app_inboxcount(mailbox_str->str, &newmsgs, &oldmsgs);
+               ao2_lock(peer);
        }
-       ao2_lock(peer);
 
        if (peer->mwipvt) {
                /* Base message on subscription */
-               p = dialog_ref(peer->mwipvt, "sip_send_mwi_to_peer: Setting dialog ptr p from peer->mwipvt-- should this be done?");
+               p = dialog_ref(peer->mwipvt, "sip_send_mwi_to_peer: Setting dialog ptr p from peer->mwipvt");
+               ao2_unlock(peer);
        } else {
+               ao2_unlock(peer);
                /* Build temporary dialog for this message */
                if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY, NULL))) {
-                       ao2_unlock(peer);
                        return -1;
                }
 
@@ -25769,23 +26240,26 @@ static int sip_send_mwi_to_peer(struct sip_peer *peer, int cache_only)
                set_socket_transport(&p->socket, 0);
                if (create_addr_from_peer(p, peer)) {
                        /* Maybe they're not registered, etc. */
-                       dialog_unlink_all(p, TRUE, TRUE);
+                       dialog_unlink_all(p);
                        dialog_unref(p, "unref dialog p just created via sip_alloc");
                        /* sip_destroy(p); */
-                       ao2_unlock(peer);
                        return -1;
                }
                /* Recalculate our side, and recalculate Call ID */
                ast_sip_ouraddrfor(&p->sa, &p->ourip, p);
                build_via(p);
-               ao2_t_unlink(dialogs, p, "About to change the callid -- remove the old name");
-               build_callid_pvt(p);
+
+               ao2_lock(peer);
                if (!ast_strlen_zero(peer->mwi_from)) {
                        ast_string_field_set(p, mwi_from, peer->mwi_from);
                } else if (!ast_strlen_zero(default_mwi_from)) {
                        ast_string_field_set(p, mwi_from, default_mwi_from);
                }
-               ao2_t_link(dialogs, p, "Linking in under new name");
+               ao2_unlock(peer);
+
+               /* Change the dialog callid. */
+               change_callid_pvt(p, NULL);
+
                /* Destroy this session after 32 secs */
                sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
        }
@@ -25797,40 +26271,49 @@ static int sip_send_mwi_to_peer(struct sip_peer *peer, int cache_only)
        /* Send MWI */
        ast_set_flag(&p->flags[0], SIP_OUTGOING);
        /* the following will decrement the refcount on p as it finishes */
-       transmit_notify_with_mwi(p, newmsgs, oldmsgs, peer->vmexten);
+       transmit_notify_with_mwi(p, newmsgs, oldmsgs, vmexten);
        sip_pvt_unlock(p);
        dialog_unref(p, "unref dialog ptr p just before it goes out of scope at the end of sip_send_mwi_to_peer.");
-       ao2_unlock(peer);
+
        return 0;
 }
 
-/*! \brief helper function for the monitoring thread -- seems to be called with the assumption that the dialog is locked */
-static void check_rtp_timeout(struct sip_pvt *dialog, time_t t)
+/*!
+ * \brief helper function for the monitoring thread -- seems to be called with the assumption that the dialog is locked
+ *
+ * \return CMP_MATCH for items to be unlinked from dialogs_rtpcheck.
+ */
+static int check_rtp_timeout(struct sip_pvt *dialog, time_t t)
 {
        int timeout;
        int hold_timeout;
        int keepalive;
 
+       if (!dialog->rtp) {
+               /*
+                * We have no RTP.  Since we don't do much with video RTP for
+                * now, stop checking this dialog.
+                */
+               return CMP_MATCH;
+       }
+
        /* If we have no active owner, no need to check timers */
        if (!dialog->owner) {
-               dialog_unlink_rtpcheck(dialog);
-               return;
+               return CMP_MATCH;
        }
-       /* If the call is redirected outside Asterisk, no need to check timers */
 
+       /* If the call is redirected outside Asterisk, no need to check timers */
        if (!ast_sockaddr_isnull(&dialog->redirip)) {
-               dialog_unlink_rtpcheck(dialog);
-               return;
+               return CMP_MATCH;
        }
 
        /* If the call is involved in a T38 fax session do not check RTP timeout */
        if (dialog->t38.state == T38_ENABLED) {
-               dialog_unlink_rtpcheck(dialog);
-               return;
+               return CMP_MATCH;
        }
        /* If the call is not in UP state return for later check. */
        if (dialog->owner->_state != AST_STATE_UP) {
-               return;
+               return 0;
        }
 
        /* Store these values locally to avoid multiple function calls */
@@ -25840,8 +26323,7 @@ static void check_rtp_timeout(struct sip_pvt *dialog, time_t t)
 
        /* If we have no timers set, return now */
        if (!keepalive && !timeout && !hold_timeout) {
-               dialog_unlink_rtpcheck(dialog);
-               return;
+               return CMP_MATCH;
        }
 
        /* Check AUDIO RTP keepalives */
@@ -25867,7 +26349,7 @@ static void check_rtp_timeout(struct sip_pvt *dialog, time_t t)
                                         * Don't block, just try again later.
                                         * If there was no owner, the call is dead already.
                                         */
-                                       return;
+                                       return 0;
                                }
                                ast_log(LOG_NOTICE, "Disconnecting call '%s' for lack of RTP activity in %ld seconds\n",
                                        dialog->owner->name, (long) (t - dialog->lastrtprx));
@@ -25884,10 +26366,12 @@ static void check_rtp_timeout(struct sip_pvt *dialog, time_t t)
                                        ast_rtp_instance_set_timeout(dialog->vrtp, 0);
                                        ast_rtp_instance_set_hold_timeout(dialog->vrtp, 0);
                                }
-                               dialog_unlink_rtpcheck(dialog); /* finally unlink the dialog from the checkrtp container */
+                               /* finally unlink the dialog from dialogs_rtpcheck. */
+                               return CMP_MATCH;
                        }
                }
        }
+       return 0;
 }
 
 /*! \brief The SIP monitoring thread
@@ -25931,15 +26415,20 @@ static void *do_monitor(void *data)
 
                /* Check for dialogs needing to be killed */
                t = time(NULL);
-               /* Check for dialogs with rtp and rtptimeout
-                * All Dialogs which have rtp are in dialogs_rtpcheck container*/
-               ao2_t_callback(dialogs_rtpcheck, OBJ_NODATA | OBJ_MULTIPLE, dialog_checkrtp_cb, &t,
-                               "callback to check rtptimeout and hangup calls if necessary");
 
-               /* Check for dialogs marked to be destroyed
-                * All Dialogs which need Destroy are in dialogs_needdestroy container*/
-               ao2_t_callback(dialogs_needdestroy, OBJ_NODATA | OBJ_MULTIPLE, dialog_needdestroy, &t,
-                               "callback to check rtptimeout and hangup calls if necessary");
+               /*
+                * Check dialogs with rtp and rtptimeout.
+                * All dialogs which have rtp are in dialogs_rtpcheck.
+                */
+               ao2_t_callback(dialogs_rtpcheck, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
+                       dialog_checkrtp_cb, &t,
+                       "callback to check rtptimeout and hangup calls if necessary");
+               /*
+                * Check dialogs marked to be destroyed.
+                * All dialogs with needdestroy set are in dialogs_needdestroy.
+                */
+               ao2_t_callback(dialogs_needdestroy, OBJ_NODATA | OBJ_MULTIPLE, dialog_needdestroy,
+                       NULL, "callback to check dialogs which need to be destroyed");
 
                /* XXX TODO The scheduler usage in this module does not have sufficient
                 * synchronization being done between running the scheduler and places
@@ -26332,7 +26821,7 @@ static int sip_poke_noanswer(const void *data)
        }
 
        if (peer->call) {
-               dialog_unlink_all(peer->call, TRUE, TRUE);
+               dialog_unlink_all(peer->call);
                peer->call = dialog_unref(peer->call, "unref dialog peer->call");
                /* peer->call = sip_destroy(peer->call);*/
        }
@@ -26382,7 +26871,7 @@ static int sip_poke_peer(struct sip_peer *peer, int force)
                if (sipdebug) {
                        ast_log(LOG_NOTICE, "Still have a QUALIFY dialog active, deleting\n");
                }
-               dialog_unlink_all(peer->call, TRUE, TRUE);
+               dialog_unlink_all(peer->call);
                peer->call = dialog_unref(peer->call, "unref dialog peer->call");
                /* peer->call = sip_destroy(peer->call); */
        }
@@ -26390,7 +26879,7 @@ static int sip_poke_peer(struct sip_peer *peer, int force)
                return -1;
        }
        peer->call = dialog_ref(p, "copy sip alloc from p to peer->call");
-       
+
        p->sa = peer->addr;
        p->recv = peer->addr;
        copy_socket_data(&p->socket, &peer->socket);
@@ -26399,20 +26888,26 @@ static int sip_poke_peer(struct sip_peer *peer, int force)
        ast_copy_flags(&p->flags[2], &peer->flags[2], SIP_PAGE3_FLAGS_TO_COPY);
 
        /* Send OPTIONs to peer's fullcontact */
-       if (!ast_strlen_zero(peer->fullcontact))
+       if (!ast_strlen_zero(peer->fullcontact)) {
                ast_string_field_set(p, fullcontact, peer->fullcontact);
+       }
+
+       if (!ast_strlen_zero(peer->fromuser)) {
+               ast_string_field_set(p, fromuser, peer->fromuser);
+       }
 
-       if (!ast_strlen_zero(peer->tohost))
+       if (!ast_strlen_zero(peer->tohost)) {
                ast_string_field_set(p, tohost, peer->tohost);
-       else
+       } else {
                ast_string_field_set(p, tohost, ast_sockaddr_stringify_host_remote(&peer->addr));
+       }
 
        /* Recalculate our side, and recalculate Call ID */
        ast_sip_ouraddrfor(&p->sa, &p->ourip, p);
        build_via(p);
-       ao2_t_unlink(dialogs, p, "About to change the callid -- remove the old name");
-       build_callid_pvt(p);
-       ao2_t_link(dialogs, p, "Linking in under new name");
+
+       /* Change the dialog callid. */
+       change_callid_pvt(p, NULL);
 
        AST_SCHED_DEL_UNREF(sched, peer->pokeexpire,
                        sip_unref_peer(peer, "removing poke peer ref"));
@@ -26610,7 +27105,7 @@ static struct ast_channel *sip_request_call(const char *type, struct ast_format_
        ast_string_field_set(p, dialstring, dialstring);
 
        if (!(p->options = ast_calloc(1, sizeof(*p->options)))) {
-               dialog_unlink_all(p, TRUE, TRUE);
+               dialog_unlink_all(p);
                dialog_unref(p, "unref dialog p from mem fail");
                /* sip_destroy(p); */
                ast_log(LOG_ERROR, "Unable to build option SIP data structure - Out of memory\n");
@@ -26699,7 +27194,7 @@ static struct ast_channel *sip_request_call(const char *type, struct ast_format_
        if (create_addr(p, host, NULL, 1, &remote_address_sa)) {
                *cause = AST_CAUSE_UNREGISTERED;
                ast_debug(3, "Cant create SIP call - target device not registered\n");
-               dialog_unlink_all(p, TRUE, TRUE);
+               dialog_unlink_all(p);
                dialog_unref(p, "unref dialog p UNREGISTERED");
                /* sip_destroy(p); */
                return NULL;
@@ -26709,9 +27204,9 @@ static struct ast_channel *sip_request_call(const char *type, struct ast_format_
        /* Recalculate our side, and recalculate Call ID */
        ast_sip_ouraddrfor(&p->sa, &p->ourip, p);
        build_via(p);
-       ao2_t_unlink(dialogs, p, "About to change the callid -- remove the old name");
-       build_callid_pvt(p);
-       ao2_t_link(dialogs, p, "Linking in under new name");
+
+       /* Change the dialog callid. */
+       change_callid_pvt(p, NULL);
 
        /* We have an extension to call, don't use the full contact here */
        /* This to enable dialing registered peers with extension dialling,
@@ -26744,8 +27239,10 @@ static struct ast_channel *sip_request_call(const char *type, struct ast_format_
                        p->owner? p->owner->name : "", "SIP", p->callid, p->fullcontact, p->peername);
        sip_pvt_unlock(p);
        if (!tmpc) {
-               dialog_unlink_all(p, TRUE, TRUE);
+               dialog_unlink_all(p);
                /* sip_destroy(p); */
+       } else {
+               ast_channel_unlock(tmpc);
        }
        dialog_unref(p, "toss pvt ptr at end of sip_request_call");
        ast_update_use_count();
@@ -26872,12 +27369,11 @@ static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask
                }
        } else if (!strcasecmp(v->name, "nat")) {
                ast_set_flag(&mask[0], SIP_NAT_FORCE_RPORT);
+               ast_set_flag(&flags[0], SIP_NAT_FORCE_RPORT); /* Default to "force_rport" */
                if (!strcasecmp(v->value, "no")) {
                        ast_clear_flag(&flags[0], SIP_NAT_FORCE_RPORT);
-               } else if (!strcasecmp(v->value, "force_rport")) {
-                       ast_set_flag(&flags[0], SIP_NAT_FORCE_RPORT);
                } else if (!strcasecmp(v->value, "yes")) {
-                       ast_set_flag(&flags[0], SIP_NAT_FORCE_RPORT);
+                       /* We've already defaulted to force_rport */
                        ast_set_flag(&mask[1], SIP_PAGE2_SYMMETRICRTP);
                        ast_set_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP);
                } else if (!strcasecmp(v->value, "comedia")) {
@@ -26934,7 +27430,12 @@ static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask
                res = 1;
        } else if (!strcasecmp(v->name, "allowoverlap")) {
                ast_set_flag(&mask[1], SIP_PAGE2_ALLOWOVERLAP);
-               ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_ALLOWOVERLAP);
+               ast_clear_flag(&flags[1], SIP_PAGE2_ALLOWOVERLAP);
+               if (ast_true(v->value)) {
+                       ast_set_flag(&flags[1], SIP_PAGE2_ALLOWOVERLAP_YES);
+               } else if (!strcasecmp(v->value, "dtmf")){
+                       ast_set_flag(&flags[1], SIP_PAGE2_ALLOWOVERLAP_DTMF);
+               }
        } else if (!strcasecmp(v->name, "allowsubscribe")) {
                ast_set_flag(&mask[1], SIP_PAGE2_ALLOWSUBSCRIBE);
                ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_ALLOWSUBSCRIBE);
@@ -27011,8 +27512,9 @@ static int check_sip_domain(const char *domain, char *context, size_t len)
 
        AST_LIST_LOCK(&domain_list);
        AST_LIST_TRAVERSE(&domain_list, d, list) {
-               if (strcasecmp(d->domain, domain))
+               if (strcasecmp(d->domain, domain)) {
                        continue;
+               }
 
                if (len && !ast_strlen_zero(d->context))
                        ast_copy_string(context, d->context, len);
@@ -27184,6 +27686,8 @@ static void set_peer_defaults(struct sip_peer *peer)
        ast_copy_flags(&peer->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY);
        ast_copy_flags(&peer->flags[2], &global_flags[2], SIP_PAGE3_FLAGS_TO_COPY);
        ast_string_field_set(peer, context, sip_cfg.default_context);
+       ast_string_field_set(peer, record_on_feature, sip_cfg.default_record_on_feature);
+       ast_string_field_set(peer, record_off_feature, sip_cfg.default_record_off_feature);
        ast_string_field_set(peer, messagecontext, sip_cfg.messagecontext);
        ast_string_field_set(peer, subscribecontext, sip_cfg.default_subscribecontext);
        ast_string_field_set(peer, language, default_language);
@@ -27346,7 +27850,9 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
                found++;
                /* we've unlinked the peer from the peers container but not unlinked from the peers_by_ip container yet
                  this leads to a wrong refcounter and the peer object is never destroyed */
-               ao2_t_unlink(peers_by_ip, peer, "ao2_unlink peer from peers_by_ip table");
+               if (!ast_sockaddr_isnull(&peer->addr)) {
+                       ao2_t_unlink(peers_by_ip, peer, "ao2_unlink peer from peers_by_ip table");
+               }
                if (!(peer->the_mark))
                        firstpass = 0;
        } else {
@@ -27383,8 +27889,9 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
                set_peer_defaults(peer);        /* Set peer defaults */
                peer->type = 0;
        }
-       if (!found && name)
-               ast_copy_string(peer->name, name, sizeof(peer->name));
+
+       /* in case the case of the peer name has changed, update the name */
+       ast_copy_string(peer->name, name, sizeof(peer->name));
 
        /* If we have channel variables, remove them (reload) */
        if (peer->chanvars) {
@@ -27432,12 +27939,14 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
 
                                        if (!strncasecmp(trans, "udp", 3)) {
                                                peer->transports |= SIP_TRANSPORT_UDP;
-                                       } else if (!strncasecmp(trans, "tcp", 3)) {
+                                       } else if (sip_cfg.tcp_enabled && !strncasecmp(trans, "tcp", 3)) {
                                                peer->transports |= SIP_TRANSPORT_TCP;
-                                       } else if (!strncasecmp(trans, "tls", 3)) {
+                                       } else if (default_tls_cfg.enabled && !strncasecmp(trans, "tls", 3)) {
                                                peer->transports |= SIP_TRANSPORT_TLS;
+                                       } else if (!strncasecmp(trans, "tcp", 3) || !strncasecmp(trans, "tls", 3)) {
+                                               ast_log(LOG_WARNING, "'%.3s' is not a valid transport type when %.3senabled=no. If no other is specified, the defaults from general will be used.\n", trans, trans);
                                        } else {
-                                               ast_log(LOG_NOTICE, "'%s' is not a valid transport type. if no other is specified, udp will be used.\n", trans);
+                                               ast_log(LOG_NOTICE, "'%s' is not a valid transport type. if no other is specified, the defaults from general will be used.\n", trans);
                                        }
 
                                        if (!peer->default_outbound_transport) { /*!< The first transport listed should be default outbound */
@@ -27488,6 +27997,10 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
                        } else if (!strcasecmp(v->name, "context")) {
                                ast_string_field_set(peer, context, v->value);
                                ast_set_flag(&peer->flags[1], SIP_PAGE2_HAVEPEERCONTEXT);
+                       } else if (!strcasecmp(v->name, "recordonfeature")) {
+                               ast_string_field_set(peer, record_on_feature, v->value);
+                       } else if (!strcasecmp(v->name, "recordofffeature")) {
+                               ast_string_field_set(peer, record_off_feature, v->value);
                        } else if (!strcasecmp(v->name, "outofcall_message_context")) {
                                ast_string_field_set(peer, messagecontext, v->value);
                        } else if (!strcasecmp(v->name, "subscribecontext")) {
@@ -27630,7 +28143,8 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
                                        peer->amaflags = format;
                                }
                        } else if (!strcasecmp(v->name, "maxforwards")) {
-                               if ((sscanf(v->value, "%30d", &peer->maxforwards) != 1) || (peer->maxforwards < 1)) {
+                               if (sscanf(v->value, "%30d", &peer->maxforwards) != 1
+                                       || peer->maxforwards < 1 || 255 < peer->maxforwards) {
                                        ast_log(LOG_WARNING, "'%s' is not a valid maxforwards value at line %d.  Using default.\n", v->value, v->lineno);
                                        peer->maxforwards = sip_cfg.default_max_forwards;
                                }
@@ -27674,8 +28188,6 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
                                }
                        } else if (!strcasecmp(v->name, "preferred_codec_only")) {
                                ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_PREFERRED_CODEC);
-                       } else if (!strcasecmp(v->name, "registertrying")) {
-                               ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_REGISTERTRYING);
                        } else if (!strcasecmp(v->name, "autoframing")) {
                                peer->autoframing = ast_true(v->value);
                        } else if (!strcasecmp(v->name, "rtptimeout")) {
@@ -27913,11 +28425,11 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
                ast_string_field_set(peer, tohost, srvlookup);
 
                if (global_dynamic_exclude_static) {
-                       int err = 0;
+                       int ha_error = 0;
                        sip_cfg.contact_ha = ast_append_ha("deny", ast_sockaddr_stringify_addr(&peer->addr), 
-                                                       sip_cfg.contact_ha, &err);
-                       if (err) {
-                               ast_log(LOG_ERROR, "Bad ACL entry in configuration line %d : %s\n", v->lineno, v->value);
+                                                       sip_cfg.contact_ha, &ha_error);
+                       if (ha_error) {
+                               ast_log(LOG_ERROR, "Bad or unresolved host/IP entry in configuration for peer %s, cannot add to contact ACL\n", peer->name);
                        }
                }
        } else if (peer->dnsmgr && !peer->host_dynamic) {
@@ -28003,7 +28515,9 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
 static int peer_markall_func(void *device, void *arg, int flags)
 {
        struct sip_peer *peer = device;
-       peer->the_mark = 1;
+       if (!peer->selfdestruct || sip_cfg.autocreatepeer != AUTOPEERS_PERSIST) {
+               peer->the_mark = 1;
+       }
        return 0;
 }
 
@@ -28023,6 +28537,18 @@ static void sip_set_default_format_capabilities(struct ast_format_cap *cap)
        ast_format_cap_add(cap, ast_format_set(&tmp_fmt, AST_FORMAT_H263, 0));
 }
 
+static void display_nat_warning(const char *cat, int reason, struct ast_flags *flags) {
+       int global_nat, specific_nat;
+
+       if (reason == CHANNEL_MODULE_LOAD && (specific_nat = ast_test_flag(&flags[0], SIP_NAT_FORCE_RPORT)) != (global_nat = ast_test_flag(&global_flags[0], SIP_NAT_FORCE_RPORT))) {
+               ast_log(LOG_WARNING, "!!! PLEASE NOTE: Setting 'nat' for a peer/user that differs from the  global setting can make\n");
+               ast_log(LOG_WARNING, "!!! the name of that peer/user discoverable by an attacker. Replies for non-existent peers/users\n");
+               ast_log(LOG_WARNING, "!!! will be sent to a different port than replies for an existing peer/user. If at all possible,\n");
+               ast_log(LOG_WARNING, "!!! use the global 'nat' setting and do not set 'nat' per peer/user.\n");
+               ast_log(LOG_WARNING, "!!! (config category='%s' global force_rport='%s' peer/user force_rport='%s')\n", cat, AST_CLI_YESNO(global_nat), AST_CLI_YESNO(specific_nat));
+       }
+}
+
 /*! \brief Re-read SIP.conf config file
 \note  This function reloads all config data, except for
        active peers (with registrations). They will only
@@ -28105,7 +28631,7 @@ static int reload_config(enum channelreloadreason reason)
                                if (iterator->call) {
                                        ast_debug(3, "Destroying active SIP dialog for registry %s@%s\n", iterator->username, iterator->hostname);
                                        /* This will also remove references to the registry */
-                                       dialog_unlink_all(iterator->call, TRUE, TRUE);
+                                       dialog_unlink_all(iterator->call);
                                        iterator->call = dialog_unref(iterator->call, "remove iterator->call from registry traversal");
                                }
                                if (iterator->expire > -1) {
@@ -28116,11 +28642,6 @@ static int reload_config(enum channelreloadreason reason)
                                }
                                ASTOBJ_UNLOCK(iterator);
                } while(0));
-
-               /* Then, actually destroy users and registry */
-               ASTOBJ_CONTAINER_DESTROYALL(&regl, sip_registry_destroy);
-               ast_debug(4, "--------------- Done destroying registry list\n");
-               ao2_t_callback(peers, OBJ_NODATA, peer_markall_func, NULL, "callback to mark all peers");
        }
 
        /* Reset certificate handling for TLS sessions */
@@ -28146,6 +28667,7 @@ static int reload_config(enum channelreloadreason reason)
        sipdebug &= sip_debug_console;
        ast_clear_flag(&global_flags[0], AST_FLAGS_ALL);
        ast_clear_flag(&global_flags[1], AST_FLAGS_ALL);
+       ast_clear_flag(&global_flags[2], AST_FLAGS_ALL);
 
        /* Reset IP addresses  */
        ast_sockaddr_parse(&bindaddr, "0.0.0.0:0", 0);
@@ -28221,7 +28743,7 @@ static int reload_config(enum channelreloadreason reason)
        sip_cfg.allowtransfer = TRANSFER_OPENFORALL;    /* Merrily accept all transfers by default */
        sip_cfg.rtautoclear = 120;
        ast_set_flag(&global_flags[1], SIP_PAGE2_ALLOWSUBSCRIBE);       /* Default for all devices: TRUE */
-       ast_set_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP);         /* Default for all devices: TRUE */
+       ast_set_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP_YES);     /* Default for all devices: Yes */
        sip_cfg.peer_rtupdate = TRUE;
        global_dynamic_exclude_static = 0;      /* Exclude static peers */
        sip_cfg.tcp_enabled = FALSE;
@@ -28238,6 +28760,8 @@ static int reload_config(enum channelreloadreason reason)
 
        /* Initialize some reasonable defaults at SIP reload (used both for channel and as default for devices */
        ast_copy_string(sip_cfg.default_context, DEFAULT_CONTEXT, sizeof(sip_cfg.default_context));
+       ast_copy_string(sip_cfg.default_record_on_feature, DEFAULT_RECORD_FEATURE, sizeof(sip_cfg.default_record_on_feature));
+       ast_copy_string(sip_cfg.default_record_off_feature, DEFAULT_RECORD_FEATURE, sizeof(sip_cfg.default_record_off_feature));
        sip_cfg.default_subscribecontext[0] = '\0';
        sip_cfg.default_max_forwards = DEFAULT_MAX_FORWARDS;
        default_language[0] = '\0';
@@ -28249,8 +28773,9 @@ static int reload_config(enum channelreloadreason reason)
        ast_copy_string(default_mohinterpret, DEFAULT_MOHINTERPRET, sizeof(default_mohinterpret));
        ast_copy_string(default_mohsuggest, DEFAULT_MOHSUGGEST, sizeof(default_mohsuggest));
        ast_copy_string(default_vmexten, DEFAULT_VMEXTEN, sizeof(default_vmexten));
-       ast_set_flag(&global_flags[0], SIP_DTMF_RFC2833);                       /*!< Default DTMF setting: RFC2833 */
-       ast_set_flag(&global_flags[0], SIP_DIRECT_MEDIA);                       /*!< Allow re-invites */
+       ast_set_flag(&global_flags[0], SIP_DTMF_RFC2833);    /*!< Default DTMF setting: RFC2833 */
+       ast_set_flag(&global_flags[0], SIP_DIRECT_MEDIA);    /*!< Allow re-invites */
+       ast_set_flag(&global_flags[0], SIP_NAT_FORCE_RPORT); /*!< Default to nat=force_rport */
        ast_copy_string(default_engine, DEFAULT_ENGINE, sizeof(default_engine));
        ast_copy_string(default_parkinglot, DEFAULT_PARKINGLOT, sizeof(default_parkinglot));
 
@@ -28304,6 +28829,10 @@ static int reload_config(enum channelreloadreason reason)
 
                if (!strcasecmp(v->name, "context")) {
                        ast_copy_string(sip_cfg.default_context, v->value, sizeof(sip_cfg.default_context));
+               } else if (!strcasecmp(v->name, "recordonfeature")) {
+                       ast_copy_string(sip_cfg.default_record_on_feature, v->value, sizeof(sip_cfg.default_record_on_feature));
+               } else if (!strcasecmp(v->name, "recordofffeature")) {
+                       ast_copy_string(sip_cfg.default_record_off_feature, v->value, sizeof(sip_cfg.default_record_off_feature));
                } else if (!strcasecmp(v->name, "subscribecontext")) {
                        ast_copy_string(sip_cfg.default_subscribecontext, v->value, sizeof(sip_cfg.default_subscribecontext));
                } else if (!strcasecmp(v->name, "callcounter")) {
@@ -28524,7 +29053,11 @@ static int reload_config(enum channelreloadreason reason)
 
                        proxy_update(&sip_cfg.outboundproxy);
                } else if (!strcasecmp(v->name, "autocreatepeer")) {
-                       sip_cfg.autocreatepeer = ast_true(v->value);
+                       if (!strcasecmp(v->value, "persist")) {
+                               sip_cfg.autocreatepeer = AUTOPEERS_PERSIST;
+                       } else {
+                               sip_cfg.autocreatepeer = ast_true(v->value) ? AUTOPEERS_VOLATILE : AUTOPEERS_DISABLED;
+                       }
                } else if (!strcasecmp(v->name, "match_auth_username")) {
                        global_match_auth_username = ast_true(v->value);
                } else if (!strcasecmp(v->name, "srvlookup")) {
@@ -28802,6 +29335,13 @@ static int reload_config(enum channelreloadreason reason)
                }
        }
 
+       if (reason != CHANNEL_MODULE_LOAD) {
+               /* Then, actually destroy users and registry */
+               ASTOBJ_CONTAINER_DESTROYALL(&regl, sip_registry_destroy);
+               ast_debug(4, "--------------- Done destroying registry list\n");
+               ao2_t_callback(peers, OBJ_NODATA, peer_markall_func, NULL, "callback to mark all peers");
+       }
+
        if (subscribe_network_change) {
                network_change_event_subscribe();
        } else {
@@ -28830,9 +29370,23 @@ static int reload_config(enum channelreloadreason reason)
                ast_log(LOG_WARNING, "To disallow external domains, you need to configure local SIP domains.\n");
                sip_cfg.allow_external_domains = 1;
        }
-       /* If not configured, set default transports */
-       if (default_transports == 0) {
+       /* If not or badly configured, set default transports */
+       if (!sip_cfg.tcp_enabled && (default_transports & SIP_TRANSPORT_TCP)) {
+               ast_log(LOG_WARNING, "Cannot use 'tcp' transport with tcpenable=no. Removing from available transports.\n");
+               default_primary_transport &= ~SIP_TRANSPORT_TCP;
+               default_transports &= ~SIP_TRANSPORT_TCP;
+       }
+       if (!default_tls_cfg.enabled && (default_transports & SIP_TRANSPORT_TLS)) {
+               ast_log(LOG_WARNING, "Cannot use 'tls' transport with tlsenable=no. Removing from available transports.\n");
+               default_primary_transport &= ~SIP_TRANSPORT_TLS;
+               default_transports &= ~SIP_TRANSPORT_TLS;
+       }
+       if (!default_transports) {
+               ast_log(LOG_WARNING, "No valid transports available, falling back to 'udp'.\n");
                default_transports = default_primary_transport = SIP_TRANSPORT_UDP;
+       } else if (!default_primary_transport) {
+               ast_log(LOG_WARNING, "No valid default transport. Selecting 'udp' as default.\n");
+               default_primary_transport = SIP_TRANSPORT_UDP;
        }
 
        /* Build list of authentication to various SIP realms, i.e. service providers */
@@ -29035,6 +29589,7 @@ static int reload_config(enum channelreloadreason reason)
                        }
                        peer = build_peer(cat, ast_variable_browse(cfg, cat), NULL, 0, 0);
                        if (peer) {
+                               display_nat_warning(cat, reason, &peer->flags[0]);
                                ao2_t_link(peers, peer, "link peer into peers table");
                                if ((peer->type & SIP_TYPE_PEER) && !ast_sockaddr_isnull(&peer->addr)) {
                                        ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table");
@@ -30390,7 +30945,7 @@ static int load_module(void)
        peers = ao2_t_container_alloc(HASH_PEER_SIZE, peer_hash_cb, peer_cmp_cb, "allocate peers");
        peers_by_ip = ao2_t_container_alloc(HASH_PEER_SIZE, peer_iphash_cb, peer_ipcmp_cb, "allocate peers_by_ip");
        dialogs = ao2_t_container_alloc(HASH_DIALOG_SIZE, dialog_hash_cb, dialog_cmp_cb, "allocate dialogs");
-       dialogs_needdestroy = ao2_t_container_alloc(HASH_DIALOG_SIZE, dialog_hash_cb, dialog_cmp_cb, "allocate dialogs_needdestroy");
+       dialogs_needdestroy = ao2_t_container_alloc(1, NULL, NULL, "allocate dialogs_needdestroy");
        dialogs_rtpcheck = ao2_t_container_alloc(HASH_DIALOG_SIZE, dialog_hash_cb, dialog_cmp_cb, "allocate dialogs for rtpchecks");
        threadt = ao2_t_container_alloc(HASH_DIALOG_SIZE, threadt_hash_cb, threadt_cmp_cb, "allocate threadt table");
        if (!peers || !peers_by_ip || !dialogs || !dialogs_needdestroy || !dialogs_rtpcheck
@@ -30623,7 +31178,7 @@ static int unload_module(void)
        /* Destroy all the dialogs and free their memory */
        i = ao2_iterator_init(dialogs, 0);
        while ((p = ao2_t_iterator_next(&i, "iterate thru dialogs"))) {
-               dialog_unlink_all(p, TRUE, TRUE);
+               dialog_unlink_all(p);
                ao2_t_ref(p, -1, "throw away iterator result");
        }
        ao2_iterator_destroy(&i);
@@ -30664,8 +31219,8 @@ static int unload_module(void)
        ao2_t_ref(peers, -1, "unref the peers table");
        ao2_t_ref(peers_by_ip, -1, "unref the peers_by_ip table");
        ao2_t_ref(dialogs, -1, "unref the dialogs table");
-       ao2_t_ref(dialogs_needdestroy, -1, "unref the dialogs table");
-       ao2_t_ref(dialogs_rtpcheck, -1, "unref the dialogs table");
+       ao2_t_ref(dialogs_needdestroy, -1, "unref dialogs_needdestroy");
+       ao2_t_ref(dialogs_rtpcheck, -1, "unref dialogs_rtpcheck");
        ao2_t_ref(threadt, -1, "unref the thread table");
        ao2_t_ref(sip_monitor_instances, -1, "unref the sip_monitor_instances table");