mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-03 03:02:15 +00:00
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.4@215682 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -211,6 +211,7 @@ static char seen_lastms = 0;
|
||||
\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 */
|
||||
@@ -1034,6 +1035,8 @@ static 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_pvt *next; /*!< Next dialog in chain */
|
||||
struct sip_invite_param *options; /*!< Options for INVITE */
|
||||
int autoframing;
|
||||
@@ -1290,6 +1293,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);
|
||||
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);
|
||||
@@ -2321,6 +2325,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);
|
||||
} 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(sched, pvt->provisional_keepalive_sched_id);
|
||||
|
||||
pvt->provisional_keepalive_sched_id = ast_sched_add(sched, PROVIS_KEEPALIVE_TIMEOUT,
|
||||
with_sdp ? send_provisional_keepalive_with_sdp : send_provisional_keepalive, pvt);
|
||||
}
|
||||
|
||||
/*! \brief Transmit response on SIP request*/
|
||||
static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno)
|
||||
{
|
||||
@@ -2341,6 +2385,12 @@ static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmitty
|
||||
append_history(p, reliable ? "TxRespRel" : "TxResp", "%s / %s - %s", tmp.data, get_header(&tmp, "CSeq"),
|
||||
(tmp.method == SIP_RESPONSE || tmp.method == SIP_UNKNOWN) ? tmp.rlPart2 : sip_methods[tmp.method].text);
|
||||
}
|
||||
|
||||
/* 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(sched, p->provisional_keepalive_sched_id);
|
||||
}
|
||||
|
||||
res = (reliable) ?
|
||||
__sip_reliable_xmit(p, seqno, 1, req->data, req->len, (reliable == XMIT_CRITICAL), req->method) :
|
||||
__sip_xmit(p, req->data, req->len);
|
||||
@@ -3228,6 +3278,7 @@ static int __sip_destroy(struct sip_pvt *p, int lockowner)
|
||||
AST_SCHED_DEL(sched, p->waitid);
|
||||
AST_SCHED_DEL(sched, p->autokillid);
|
||||
AST_SCHED_DEL(sched, p->request_queue_sched_id);
|
||||
AST_SCHED_DEL(sched, p->provisional_keepalive_sched_id);
|
||||
|
||||
if (p->rtp) {
|
||||
ast_rtp_destroy(p->rtp);
|
||||
@@ -3844,7 +3895,7 @@ 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);
|
||||
transmit_provisional_response(p, "183 Session Progress", &p->initreq, 1);
|
||||
ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
|
||||
} else if (p->t38.state == T38_ENABLED && !p->t38.direct) {
|
||||
p->t38.state = T38_DISABLED;
|
||||
@@ -3866,7 +3917,7 @@ 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);
|
||||
transmit_provisional_response(p, "183 Session Progress", &p->initreq, 1);
|
||||
ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
|
||||
}
|
||||
p->lastrtptx = time(NULL);
|
||||
@@ -4037,7 +4088,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;
|
||||
@@ -4082,7 +4133,7 @@ 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);
|
||||
transmit_provisional_response(p, "183 Session Progress", &p->initreq, 1);
|
||||
ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT);
|
||||
break;
|
||||
}
|
||||
@@ -4594,6 +4645,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->subscribed = NONE;
|
||||
p->stateid = -1;
|
||||
p->prefs = default_prefs; /* Set default codecs for this call */
|
||||
@@ -6519,6 +6571,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) : 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)
|
||||
{
|
||||
@@ -14975,7 +15040,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
|
||||
case AST_STATE_DOWN:
|
||||
if (option_debug > 1)
|
||||
ast_log(LOG_DEBUG, "%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 */
|
||||
@@ -15039,11 +15104,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:
|
||||
|
Reference in New Issue
Block a user