]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Merged revisions 215758 via svnmerge from
authorTerry Wilson <twilson@digium.com>
Thu, 3 Sep 2009 00:23:13 +0000 (00:23 +0000)
committerTerry Wilson <twilson@digium.com>
Thu, 3 Sep 2009 00:23:13 +0000 (00:23 +0000)
https://origsvn.digium.com/svn/asterisk/trunk

................
  r215758 | twilson | 2009-09-02 18:31:04 -0500 (Wed, 02 Sep 2009) | 25 lines

  Merged revisions 215682 via svnmerge from
  https://origsvn.digium.com/svn/asterisk/branches/1.4

  ........
    r215682 | twilson | 2009-09-02 16:41:22 -0500 (Wed, 02 Sep 2009) | 18 lines

    Re-send non-100 provisional responses to prevent cancellation

    From section 13.3.1.1 of RFC 3261:

       If the UAS desires an extended period of time to answer the INVITE,
       it will need to ask for an "extension" in order to prevent proxies
       from canceling the transaction. A proxy has the option of canceling
       a transaction when there is a gap of 3 minutes between responses in a
       transaction. To prevent cancellation, the UAS MUST send a non-100
       provisional response at every minute, to handle the possibility of
       lost provisional responses.

    (closes issue #11157)
    Reported by: rjain
    Tested by: twilson

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

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

channels/chan_sip.c

index ab2e93307c30e1c50d50cf4a885b2da70415014b..362019c609c3789bc6977b9d1d9812d3e4aea9d7 100644 (file)
@@ -261,6 +261,7 @@ static int default_expiry = DEFAULT_DEFAULT_EXPIRY;
                                                       \todo Use known T1 for timeout (peerpoke)
                                                       */
 #define DEFAULT_TRANS_TIMEOUT        -1               /* Use default SIP transaction timeout */
+#define PROVIS_KEEPALIVE_TIMEOUT     60000            /*!< How long to wait before retransmitting a provisional response (rfc 3261 13.3.1.1) */
 #define MAX_AUTHTRIES                3                /*!< Try authentication three times, then fail */
 
 #define SIP_MAX_HEADERS              64               /*!< Max amount of SIP headers to read */
@@ -1366,6 +1367,8 @@ struct sip_pvt {
        struct ast_variable *chanvars;          /*!< Channel variables to set for inbound call */
        AST_LIST_HEAD_NOLOCK(request_queue, sip_request) request_queue; /*!< Requests that arrived but could not be processed immediately */
        int request_queue_sched_id;             /*!< Scheduler ID of any scheduled action to process queued requests */
+       int provisional_keepalive_sched_id; /*!< Scheduler ID for provisional responses that need to be sent out to avoid cancellation */
+       const char *last_provisional;   /*!< The last successfully transmitted provisonal response message */
        struct sip_invite_param *options;       /*!< Options for INVITE */
        int autoframing;                        /*!< The number of Asters we group in a Pyroflax
                                                        before strolling to the Grokyzpå
@@ -1878,6 +1881,7 @@ static int transmit_response_with_date(struct sip_pvt *p, const char *msg, const
 static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable, int oldsdp);
 static int transmit_response_with_unsupported(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *unsupported);
 static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *rand, enum xmittype reliable, const char *header, int stale);
+static int transmit_provisional_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, int with_sdp);
 static int transmit_response_with_allow(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable);
 static void transmit_fake_auth_response(struct sip_pvt *p, int sipmethod, struct sip_request *req, enum xmittype reliable);
 static int transmit_request(struct sip_pvt *p, int sipmethod, int inc, enum xmittype reliable, int newbranch);
@@ -2574,6 +2578,8 @@ static void *dialog_unlink_all(struct sip_pvt *dialog, int lockowner, int lockdi
                AST_SCHED_DEL_UNREF(sched, dialog->request_queue_sched_id, dialog_unref(dialog, "when you delete the request_queue_sched_id sched, you should dec the refcount for the stored dialog ptr"));
        }
 
+       AST_SCHED_DEL_UNREF(sched, dialog->provisional_keepalive_sched_id, dialog_unref(dialog, "when you delete the provisional_keepalive_sched_id, you should dec the refcount for the stored dialog ptr"));
+
        if (dialog->t38id > -1) {
                AST_SCHED_DEL_UNREF(sched, dialog->t38id, dialog_unref(dialog, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr"));
        }
@@ -3506,6 +3512,46 @@ static void add_blank(struct sip_request *req)
        }
 }
 
+static int send_provisional_keepalive_full(struct sip_pvt *pvt, int with_sdp)
+{
+       const char *msg = NULL;
+
+       if (!pvt->last_provisional || !strncasecmp(pvt->last_provisional, "100", 3)) {
+               msg = "183 Session Progress";
+       }
+
+       if (pvt->invitestate < INV_COMPLETED) {
+               if (with_sdp) {
+                       transmit_response_with_sdp(pvt, S_OR(msg, pvt->last_provisional), &pvt->initreq, XMIT_UNRELIABLE, FALSE);
+               } else {
+                       transmit_response(pvt, S_OR(msg, pvt->last_provisional), &pvt->initreq);
+               }
+               return PROVIS_KEEPALIVE_TIMEOUT;
+       }
+
+       return 0;
+}
+
+static int send_provisional_keepalive(const void *data) {
+       struct sip_pvt *pvt = (struct sip_pvt *) data;
+
+       return send_provisional_keepalive_full(pvt, 0);
+}
+
+static int send_provisional_keepalive_with_sdp(const void *data) {
+       struct sip_pvt *pvt = (void *)data;
+
+       return send_provisional_keepalive_full(pvt, 1);
+}
+
+static void update_provisional_keepalive(struct sip_pvt *pvt, int with_sdp)
+{
+       AST_SCHED_DEL_UNREF(sched, pvt->provisional_keepalive_sched_id, dialog_unref(pvt, "when you delete the provisional_keepalive_sched_id, you should dec the refcount for the stored dialog ptr"));
+
+       pvt->provisional_keepalive_sched_id = ast_sched_add(sched, PROVIS_KEEPALIVE_TIMEOUT,
+               with_sdp ? send_provisional_keepalive_with_sdp : send_provisional_keepalive, dialog_ref(pvt, "Increment refcount to pass dialog pointer to sched callback"));
+}
+
 /*! \brief Transmit response on SIP request*/
 static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno)
 {
@@ -3527,6 +3573,12 @@ static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmitty
                        (tmp.method == SIP_RESPONSE || tmp.method == SIP_UNKNOWN) ? REQ_OFFSET_TO_STR(&tmp, rlPart2) : sip_methods[tmp.method].text);
                ast_free(tmp.data);
        }
+
+       /* If we are sending a final response to an INVITE, stop retransmitting provisional responses */
+       if (p->initreq.method == SIP_INVITE && reliable == XMIT_CRITICAL) {
+               AST_SCHED_DEL_UNREF(sched, p->provisional_keepalive_sched_id, dialog_unref(p, "when you delete the provisional_keepalive_sched_id, you should dec the refcount for the stored dialog ptr"));
+       }
+
        res = (reliable) ?
                 __sip_reliable_xmit(p, seqno, 1, req->data, req->len, (reliable == XMIT_CRITICAL), req->method) :
                __sip_xmit(p, req->data, req->len);
@@ -5501,8 +5553,8 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame)
                                    !ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
                                        ast_rtp_new_source(p->rtp);
                                        p->invitestate = INV_EARLY_MEDIA;
-                                       transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE);
-                                       ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);  
+                                       transmit_provisional_response(p, "183 Session Progress", &p->initreq, TRUE);
+                                       ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
                                } else if (p->t38.state == T38_ENABLED) {
                                        change_t38_state(p, T38_DISABLED);
                                        transmit_reinvite_with_sdp(p, FALSE, FALSE);
@@ -5523,8 +5575,8 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame)
                                    !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) &&
                                    !ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
                                        p->invitestate = INV_EARLY_MEDIA;
-                                       transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE);
-                                       ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);  
+                                       transmit_provisional_response(p, "183 Session Progress", &p->initreq, TRUE);
+                                       ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
                                }
                                p->lastrtptx = time(NULL);
                                res = ast_rtp_write(p->vrtp, frame);
@@ -5544,8 +5596,8 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame)
                                            !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) &&
                                            !ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
                                                p->invitestate = INV_EARLY_MEDIA;
-                                               transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE);
-                                               ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);  
+                                               transmit_provisional_response(p, "183 Session Progress", &p->initreq, TRUE);
+                                               ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
                                        }
                                        p->lastrtptx = time(NULL);
                                        res = ast_rtp_write(p->trtp, frame);
@@ -5758,7 +5810,7 @@ static int sip_indicate(struct ast_channel *ast, int condition, const void *data
                        if (!ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) ||
                            (ast_test_flag(&p->flags[0], SIP_PROG_INBAND) == SIP_PROG_INBAND_NEVER)) {                          
                                /* Send 180 ringing if out-of-band seems reasonable */
-                               transmit_response(p, "180 Ringing", &p->initreq);
+                               transmit_provisional_response(p, "180 Ringing", &p->initreq, 0);
                                ast_set_flag(&p->flags[0], SIP_RINGING);
                                if (ast_test_flag(&p->flags[0], SIP_PROG_INBAND) != SIP_PROG_INBAND_YES)
                                        break;
@@ -5803,8 +5855,8 @@ static int sip_indicate(struct ast_channel *ast, int condition, const void *data
                    !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) &&
                    !ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
                        p->invitestate = INV_EARLY_MEDIA;
-                       transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE, FALSE);
-                       ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);  
+                       transmit_provisional_response(p, "183 Session Progress", &p->initreq, TRUE);
+                       ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
                        break;
                }
                res = -1;
@@ -6379,6 +6431,7 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si
        p->waitid = -1;
        p->autokillid = -1;
        p->request_queue_sched_id = -1;
+       p->provisional_keepalive_sched_id = -1;
        p->t38id = -1;
        p->subscribed = NONE;
        p->stateid = -1;
@@ -8588,6 +8641,19 @@ static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const
        return send_response(p, &resp, reliable, seqno);
 }
 
+/* Only use a static string for the msg, here! */
+static int transmit_provisional_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, int with_sdp)
+{
+       int res;
+
+       if (!(res = with_sdp ? transmit_response_with_sdp(p, msg, req, XMIT_UNRELIABLE, FALSE) : transmit_response(p, msg, req))) {
+               p->last_provisional = msg;
+               update_provisional_keepalive(p, with_sdp);
+       }
+
+       return res;
+}
+
 /*! \brief Add text body to SIP message */
 static int add_text(struct sip_request *req, const char *text)
 {
@@ -18729,7 +18795,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
                switch(c_state) {
                case AST_STATE_DOWN:
                        ast_debug(2, "%s: New call is still down.... Trying... \n", c->name);
-                       transmit_response(p, "100 Trying", req);
+                       transmit_provisional_response(p, "100 Trying", req, 0);
                        p->invitestate = INV_PROCEEDING;
                        ast_setstate(c, AST_STATE_RING);
                        if (strcmp(p->exten, ast_pickup_ext())) {       /* Call to extension -start pbx on this call */
@@ -18784,11 +18850,11 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
                        }
                        break;
                case AST_STATE_RING:
-                       transmit_response(p, "100 Trying", req);
+                       transmit_provisional_response(p, "100 Trying", req, 0);
                        p->invitestate = INV_PROCEEDING;
                        break;
                case AST_STATE_RINGING:
-                       transmit_response(p, "180 Ringing", req);
+                       transmit_provisional_response(p, "180 Ringing", req, 0);
                        p->invitestate = INV_PROCEEDING;
                        break;
                case AST_STATE_UP: