Fix dialog matching in the SIP distributor.
authorMark Michelson <mmichelson@digium.com>
Wed, 28 Aug 2013 15:43:15 +0000 (15:43 +0000)
committerMark Michelson <mmichelson@digium.com>
Wed, 28 Aug 2013 15:43:15 +0000 (15:43 +0000)
Dialog matching is performed in the distributor for the sole
purpose of retrieving an associated serializer so the request
may be serialized.

This patch fixes two problems.

First, incoming CANCEL requests that had no to-tag (which really
should be *all* CANCEL requests) would not match with a dialog.
An earlier bug fix to deal with early CANCEL requests would result
in the CANCEL being replied to with a 481. The fix for this is to
find the matching INVITE transaction and get the dialog from that
transaction.

Second, no SIP responses were matching dialogs. This is because we
were inverting the tags that we were passing into PJSIP's dialog
finding function. This logic has been corrected by setting local
and remote tag variables based on whether the incoming message is
a request or response.
........

Merged revisions 397854 from http://svn.asterisk.org/svn/asterisk/branches/12

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@397855 65c4cc65-6c06-0410-ace0-fbb531ad65f3

res/res_pjsip/pjsip_distributor.c

index 7597ae7..d7a92ab 100644 (file)
@@ -92,9 +92,61 @@ struct ast_sip_endpoint *ast_sip_dialog_get_endpoint(pjsip_dialog *dlg)
        return dist->endpoint;
 }
 
+static pjsip_dialog *find_dialog(pjsip_rx_data *rdata)
+{
+       pj_str_t tsx_key;
+       pjsip_transaction *tsx;
+       pjsip_dialog *dlg;
+       pj_str_t *local_tag;
+       pj_str_t *remote_tag;
+
+       if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
+               local_tag = &rdata->msg_info.to->tag;
+               remote_tag = &rdata->msg_info.from->tag;
+       } else {
+               local_tag = &rdata->msg_info.from->tag;
+               remote_tag = &rdata->msg_info.to->tag;
+       }
+
+       /* We can only call the convenient method for
+        *  1) responses
+        *  2) non-CANCEL requests
+        *  3) CANCEL requests with a to-tag
+        */
+       if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG ||
+                       pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method) ||
+                       rdata->msg_info.to->tag.slen != 0) {
+               return pjsip_ua_find_dialog(&rdata->msg_info.cid->id, local_tag,
+                               remote_tag, PJ_TRUE);
+       }
+
+       /* Incoming CANCEL without a to-tag can't use same method for finding the
+        * dialog. Instead, we have to find the matching INVITE transaction and
+        * then get the dialog from the transaction
+        */
+       pjsip_tsx_create_key(rdata->tp_info.pool, &tsx_key, PJSIP_ROLE_UAS,
+                       pjsip_get_invite_method(), rdata);
+
+       tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE);
+       if (!tsx) {
+               ast_log(LOG_ERROR, "Could not find matching INVITE transaction for CANCEL request\n");
+               return NULL;
+       }
+
+       dlg = pjsip_tsx_get_dlg(tsx);
+       pj_mutex_unlock(tsx->mutex);
+
+       if (!dlg) {
+               return NULL;
+       }
+
+       pjsip_dlg_inc_lock(dlg);
+       return dlg;
+}
+
 static pj_bool_t distributor(pjsip_rx_data *rdata)
 {
-       pjsip_dialog *dlg = pjsip_ua_find_dialog(&rdata->msg_info.cid->id, &rdata->msg_info.to->tag, &rdata->msg_info.from->tag, PJ_TRUE);
+       pjsip_dialog *dlg = find_dialog(rdata);
        struct distributor_dialog_data *dist = NULL;
        struct ast_taskprocessor *serializer = NULL;
        pjsip_rx_data *clone;