diff options
author | twilson <twilson@f38db490-d61c-443f-a65b-d21fe96a405b> | 2009-09-03 00:23:13 +0000 |
---|---|---|
committer | twilson <twilson@f38db490-d61c-443f-a65b-d21fe96a405b> | 2009-09-03 00:23:13 +0000 |
commit | b23fa4609d83fa2ff55526b9b5fb4976b95fa491 (patch) | |
tree | 03b6d1d9fafa27f9cc1300557ae9aa51859b1fd2 /channels | |
parent | 9c9c56a26becbff1b0cee708b783fc4734159ea6 (diff) |
Merged revisions 215758 via svnmerge from
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: http://svn.digium.com/svn/asterisk/branches/1.6.1@215774 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'channels')
-rw-r--r-- | channels/chan_sip.c | 90 |
1 files changed, 78 insertions, 12 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c index ab2e93307..362019c60 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -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: |