chan_sip: Send CANCEL via original INVITE destination even after UPDATE request
authorMatthew Jordan <mjordan@digium.com>
Mon, 22 Dec 2014 15:40:27 +0000 (15:40 +0000)
committerMatthew Jordan <mjordan@digium.com>
Mon, 22 Dec 2014 15:40:27 +0000 (15:40 +0000)
Given the following scenario:
* Three SIP phones (A, B, C), all communicating via a proxy with Asterisk
* A call is established between A and B. B performs a SIP attended transfer of
  A to C. B sets the call on hold (A is hearing MOH) and dials the extension of
  C. While phone C is ringing, B transfers the call (that is, what we typically
  call a 'blond transfer').
* When the transfer completes, A hears the ringing of phone C, while B is idle.

In the SIP messaging for the above scenario, a REFER request is sent to
transfer the call. When "sendrpid=yes" is set in sip.conf, Asterisk may send an
UPDATE request to phone C to update party information. This update is sent
directly to phone C, not through the intervening proxy. This has the unfortunate
side effect of providing route information, which is then set on the sip_pvt
structure for C. If someone (e.g. B) is trying to get the call back (through a
directed pickup), Asterisk will send a CANCEL request to C. However, since we
have now updated the route set, the CANCEL request will be sent directly to C
and not through the proxy. The phone ignores this CANCEL according to RFC3261
(Section 9.1).

This patch updates reqprep such that the route is not updated if an UPDATE
request is being sent while the INVITE state is INV_PROCEEDING or
INV_EARLY_MEDIA. This ensures that a subsequent CANCEL request is still sent
to the correct location.

Review: https://reviewboard.asterisk.org/r/4279

ASTERISK-24628 #close
Reported by: Karsten Wemheuer
patches:
  issue.patch uploaded by Karsten Wemheuer (License 5930)
........

Merged revisions 429982 from http://svn.asterisk.org/svn/asterisk/branches/11
........

Merged revisions 429983 from http://svn.asterisk.org/svn/asterisk/branches/13

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

channels/chan_sip.c

index 4789c55..dd3f272 100644 (file)
@@ -11985,13 +11985,15 @@ static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, ui
 
        add_header(req, "Via", p->via);
        /*
-        * Use the learned route set unless this is a CANCEL on an ACK for a non-2xx
+        * Use the learned route set unless this is a CANCEL or an ACK for a non-2xx
         * final response. For a CANCEL or ACK, we have to send to the same destination
         * as the original INVITE.
+        * Send UPDATE to the same destination as CANCEL, if call is not in final state.
         */
        if (!sip_route_empty(&p->route) &&
                        !(sipmethod == SIP_CANCEL ||
-                               (sipmethod == SIP_ACK && (p->invitestate == INV_COMPLETED || p->invitestate == INV_CANCELLED)))) {
+                               (sipmethod == SIP_ACK && (p->invitestate == INV_COMPLETED || p->invitestate == INV_CANCELLED)) ||
+                               (sipmethod == SIP_UPDATE && (p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA)))) {
                if (p->socket.type != AST_TRANSPORT_UDP && p->socket.tcptls_session) {
                        /* For TCP/TLS sockets that are connected we won't need
                         * to do any hostname/IP lookups */