Implement SIP retransmissions

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@710 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.0
Mark Spencer 22 years ago
parent 33fbc73fc5
commit 235fb19b74

@ -65,6 +65,9 @@ static int default_expirey = DEFAULT_DEFAULT_EXPIREY;
#define DEFAULT_FREQ_OK 60 * 1000 /* How often to check for the host to be up */
#define DEFAULT_FREQ_NOTOK 10 * 1000 /* How often to check, if the host is down... */
#define DEFAULT_RETRANS 1000 /* How frequently to retransmit */
#define MAX_RETRANS 5 /* Try only 5 times for retransmissions */
static char *desc = "Session Initiation Protocol (SIP)";
static char *type = "sip";
static char *tdesc = "Session Initiation Protocol (SIP)";
@ -185,6 +188,7 @@ static struct sip_pvt {
int maxtime; /* Max time for first response */
int initid; /* Auto-congest ID if appropriate */
int autokillid; /* Auto-kill ID */
int dtmfmode;
struct ast_dsp *vad;
@ -200,6 +204,7 @@ struct sip_pkt {
struct sip_pkt *next; /* Next packet */
int retrans; /* Retransmission number */
int seqno; /* Sequence number */
int resp; /* non-zero if this is a response packet (e.g. 200 OK) */
struct sip_pvt *owner; /* Owner call */
int retransid; /* Retransmission ID */
int packetlen; /* Length of packet */
@ -306,8 +311,8 @@ static struct sockaddr_in bindaddr;
static struct ast_frame *sip_read(struct ast_channel *ast);
static int transmit_response(struct sip_pvt *p, char *msg, struct sip_request *req);
static int transmit_response_with_sdp(struct sip_pvt *p, char *msg, struct sip_request *req);
static int transmit_response_with_auth(struct sip_pvt *p, char *msg, struct sip_request *req, char *rand);
static int transmit_request(struct sip_pvt *p, char *msg, int inc);
static int transmit_response_with_auth(struct sip_pvt *p, char *msg, struct sip_request *req, char *rand, int reliable);
static int transmit_request(struct sip_pvt *p, char *msg, int inc, int reliable);
static int transmit_invite(struct sip_pvt *p, char *msg, int sendsdp, char *auth, char *vxml_url);
static int transmit_reinvite_with_sdp(struct sip_pvt *p, struct ast_rtp *rtp);
static int transmit_info_with_digit(struct sip_pvt *p, char digit);
@ -328,30 +333,135 @@ static int __sip_xmit(struct sip_pvt *p, char *data, int len)
return res;
}
static int send_response(struct sip_pvt *p, struct sip_request *req)
static void sip_destroy(struct sip_pvt *p);
static int retrans_pkt(void *data)
{
struct sip_pkt *pkt=data;
int res = 0;
ast_pthread_mutex_lock(&pkt->owner->lock);
if (pkt->retrans < MAX_RETRANS) {
pkt->retrans++;
if (sipdebug) {
if (pkt->owner->nat)
ast_verbose("Retransmitting #%d (NAT):\n%s\n to %s:%d\n", pkt->retrans, pkt->data, inet_ntoa(pkt->owner->recv.sin_addr), ntohs(pkt->owner->recv.sin_port));
else
ast_verbose("Retransmitting #%d (no NAT):\n%s\n to %s:%d\n", pkt->retrans, pkt->data, inet_ntoa(pkt->owner->sa.sin_addr), ntohs(pkt->owner->sa.sin_port));
}
__sip_xmit(pkt->owner, pkt->data, pkt->packetlen);
res = 1;
} else {
ast_log(LOG_WARNING, "Maximum retries exceeded on call %s for seqno %d (%s)\n", pkt->owner->callid, pkt->seqno, pkt->resp ? "Response" : "Request");
pkt->retransid = -1;
if (pkt->owner->owner) {
/* XXX Potential deadlocK?? XXX */
ast_queue_hangup(pkt->owner->owner, 1);
} else {
/* If no owner, destroy now */
sip_destroy(pkt->owner);
}
}
ast_pthread_mutex_unlock(&pkt->owner->lock);
return res;
}
static int __sip_reliable_xmit(struct sip_pvt *p, int seqno, int resp, char *data, int len)
{
struct sip_pkt *pkt;
pkt = malloc(sizeof(struct sip_pkt) + len);
if (!pkt)
return -1;
memset(pkt, 0, sizeof(struct sip_pkt));
memcpy(pkt->data, data, len);
pkt->packetlen = len;
pkt->next = p->packets;
pkt->owner = p;
pkt->seqno = seqno;
pkt->resp = resp;
/* Schedule retransmission */
pkt->retransid = ast_sched_add(sched, 1000, retrans_pkt, pkt);
pkt->next = p->packets;
p->packets = pkt;
__sip_xmit(pkt->owner, pkt->data, pkt->packetlen);
return 0;
}
static int __sip_autodestruct(void *data)
{
struct sip_pvt *p = data;
p->autokillid = -1;
ast_log(LOG_DEBUG, "Auto destroying call '%s'\n", p->callid);
if (p->owner) {
ast_log(LOG_WARNING, "Autodestruct on call '%s' with owner in place\n", p->callid);
ast_queue_hangup(p->owner, 0);
} else {
sip_destroy(p);
}
return 0;
}
static int sip_scheddestroy(struct sip_pvt *p, int ms)
{
if (p->autokillid > -1)
ast_sched_del(sched, p->autokillid);
p->autokillid = ast_sched_add(sched, ms, __sip_autodestruct, p);
return 0;
}
static int __sip_ack(struct sip_pvt *p, int seqno, int resp)
{
struct sip_pkt *cur, *prev = NULL;
int res = -1;
cur = p->packets;
while(cur) {
if ((cur->seqno == seqno) && (cur->resp == resp)) {
/* this is our baby */
if (prev)
prev->next = cur->next;
else
p->packets = cur->next;
if (cur->retransid > -1)
ast_sched_del(sched, cur->retransid);
free(cur);
res = 0;
break;
}
prev = cur;
cur = cur->next;
}
return res;
}
static int send_response(struct sip_pvt *p, struct sip_request *req, int reliable, int seqno)
{
int res;
if (sipdebug) {
if (p->nat)
ast_verbose("Transmitting (NAT):\n%s\n to %s:%d\n", req->data, inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port));
ast_verbose("%sTransmitting (NAT):\n%s\n to %s:%d\n", reliable ? "Reliably " : "", req->data, inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port));
else
ast_verbose("Transmitting (no NAT):\n%s\n to %s:%d\n", req->data, inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port));
ast_verbose("%sTransmitting (no NAT):\n%s\n to %s:%d\n", reliable ? "Reliably " : "", req->data, inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port));
}
if (reliable)
res = __sip_reliable_xmit(p, seqno, 1, req->data, req->len);
else
res = __sip_xmit(p, req->data, req->len);
if (res > 0)
res = 0;
return res;
}
static int send_request(struct sip_pvt *p, struct sip_request *req)
static int send_request(struct sip_pvt *p, struct sip_request *req, int reliable, int seqno)
{
int res;
if (sipdebug) {
if (p->nat)
ast_verbose("XXX Need to handle Retransmitting XXX:\n%s (NAT) to %s:%d\n", req->data, inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port));
ast_verbose("%sTransmitting:\n%s (NAT) to %s:%d\n", reliable ? "Reliably " : "", req->data, inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port));
else
ast_verbose("XXX Need to handle Retransmitting XXX:\n%s (no NAT) to %s:%d\n", req->data, inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port));
ast_verbose("%sTransmitting:\n%s (no NAT) to %s:%d\n", reliable ? "Reliably " : "", req->data, inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port));
}
if (reliable)
res = __sip_reliable_xmit(p, seqno, 0, req->data, req->len);
else
res = __sip_xmit(p, req->data, req->len);
return res;
}
@ -568,6 +678,10 @@ static void __sip_destroy(struct sip_pvt *p, int lockowner)
{
struct sip_pvt *cur, *prev = NULL;
struct sip_pkt *cp;
if (p->initid > -1)
ast_sched_del(sched, p->initid);
if (p->autokillid > -1)
ast_sched_del(sched, p->autokillid);
if (p->rtp) {
ast_rtp_destroy(p->rtp);
}
@ -741,10 +855,10 @@ static int sip_hangup(struct ast_channel *ast)
/* Start the process if it's not already started */
if (!p->alreadygone && strlen(p->initreq.data)) {
if (needcancel) {
transmit_request(p, "CANCEL", 0);
transmit_request(p, "CANCEL", 0, 1);
} else {
/* Send a hangup */
transmit_request(p, "BYE", 1);
transmit_request(p, "BYE", 1, 1);
}
}
ast_pthread_mutex_unlock(&p->lock);
@ -1157,6 +1271,7 @@ static struct sip_pvt *sip_alloc(char *callid, struct sockaddr_in *sin, int useg
/* Keep track of stuff */
memset(p, 0, sizeof(struct sip_pvt));
p->initid = -1;
p->autokillid = -1;
p->rtp = ast_rtp_new(NULL, NULL);
p->branch = rand();
p->tag = rand();
@ -1770,7 +1885,7 @@ static int transmit_response(struct sip_pvt *p, char *msg, struct sip_request *r
respprep(&resp, p, msg, req);
add_header(&resp, "Content-Length", "0");
add_blank_header(&resp);
return send_response(p, &resp);
return send_response(p, &resp, 0, 0);
}
static int transmit_response_with_allow(struct sip_pvt *p, char *msg, struct sip_request *req)
@ -1781,19 +1896,24 @@ static int transmit_response_with_allow(struct sip_pvt *p, char *msg, struct sip
add_header(&resp, "Accept", "application/sdp");
add_header(&resp, "Content-Length", "0");
add_blank_header(&resp);
return send_response(p, &resp);
return send_response(p, &resp, 0, 0);
}
static int transmit_response_with_auth(struct sip_pvt *p, char *msg, struct sip_request *req, char *randdata)
static int transmit_response_with_auth(struct sip_pvt *p, char *msg, struct sip_request *req, char *randdata, int reliable)
{
struct sip_request resp;
char tmp[256];
int seqno = 0;
if (reliable && (sscanf(get_header(req, "CSeq"), "%i ", &seqno) != 1)) {
ast_log(LOG_WARNING, "Unable to determine sequence number from '%s'\n", get_header(req, "CSeq"));
return -1;
}
snprintf(tmp, sizeof(tmp), "Digest realm=\"asterisk\", nonce=\"%s\"", randdata);
respprep(&resp, p, msg, req);
add_header(&resp, "Proxy-Authenticate", tmp);
add_header(&resp, "Content-Length", "0");
add_blank_header(&resp);
return send_response(p, &resp);
return send_response(p, &resp, reliable, seqno);
}
static int add_text(struct sip_request *req, char *text)
@ -1943,9 +2063,14 @@ static void copy_request(struct sip_request *dst,struct sip_request *src)
static int transmit_response_with_sdp(struct sip_pvt *p, char *msg, struct sip_request *req)
{
struct sip_request resp;
int seqno;
if (sscanf(get_header(req, "CSeq"), "%i ", &seqno) != 1) {
ast_log(LOG_WARNING, "Unable to get seqno from '%s'\n", get_header(req, "CSeq"));
return -1;
}
respprep(&resp, p, msg, req);
add_sdp(&resp, p, NULL);
return send_response(p, &resp);
return send_response(p, &resp, 1, seqno);
}
static int transmit_reinvite_with_sdp(struct sip_pvt *p, struct ast_rtp *rtp)
@ -1953,7 +2078,7 @@ static int transmit_reinvite_with_sdp(struct sip_pvt *p, struct ast_rtp *rtp)
struct sip_request resp;
reqprep(&resp, p, "INVITE", 1);
add_sdp(&resp, p, rtp);
return send_response(p, &resp);
return send_response(p, &resp, 1, p->ocseq);
}
static void initreqprep(struct sip_request *req, struct sip_pvt *p, char *cmd, char *vxml_url)
@ -2034,7 +2159,7 @@ static int transmit_invite(struct sip_pvt *p, char *cmd, int sdp, char *auth, ch
parse(&p->initreq);
}
p->lastinvite = p->ocseq;
return send_request(p, &req);
return send_request(p, &req, 1, p->ocseq);
}
static int transmit_notify(struct sip_pvt *p, int newmsgs, int oldmsgs)
@ -2061,7 +2186,7 @@ static int transmit_notify(struct sip_pvt *p, int newmsgs, int oldmsgs)
}
p->lastinvite = p->ocseq;
return send_request(p, &req);
return send_request(p, &req, 1, p->ocseq);
}
static int transmit_register(struct sip_registry *r, char *cmd, char *auth);
@ -2165,7 +2290,7 @@ static int transmit_register(struct sip_registry *r, char *cmd, char *auth)
add_header(&req, "Event", "registration");
copy_request(&p->initreq, &req);
r->regstate=auth?REG_STATE_AUTHSENT:REG_STATE_REGSENT;
return send_request(p, &req);
return send_request(p, &req, 1, p->ocseq);
}
static int transmit_message_with_text(struct sip_pvt *p, char *text)
@ -2173,7 +2298,7 @@ static int transmit_message_with_text(struct sip_pvt *p, char *text)
struct sip_request req;
reqprep(&req, p, "MESSAGE", 1);
add_text(&req, text);
return send_request(p, &req);
return send_request(p, &req, 1, p->ocseq);
}
static int transmit_info_with_digit(struct sip_pvt *p, char digit)
@ -2181,16 +2306,16 @@ static int transmit_info_with_digit(struct sip_pvt *p, char digit)
struct sip_request req;
reqprep(&req, p, "INFO", 1);
add_digit(&req, digit);
return send_request(p, &req);
return send_request(p, &req, 1, p->ocseq);
}
static int transmit_request(struct sip_pvt *p, char *msg, int inc)
static int transmit_request(struct sip_pvt *p, char *msg, int inc, int reliable)
{
struct sip_request resp;
reqprep(&resp, p, msg, inc);
add_header(&resp, "Content-Length", "0");
add_blank_header(&resp);
return send_request(p, &resp);
return send_request(p, &resp, reliable, p->ocseq);
}
static int expire_register(void *data)
@ -2312,7 +2437,7 @@ static void md5_hash(char *output, char *input)
ptr += sprintf(ptr, "%2.2x", digest[x]);
}
static int check_auth(struct sip_pvt *p, struct sip_request *req, char *randdata, int randlen, char *username, char *secret, char *method, char *uri)
static int check_auth(struct sip_pvt *p, struct sip_request *req, char *randdata, int randlen, char *username, char *secret, char *method, char *uri, int reliable)
{
int res = -1;
/* Always OK if no secret */
@ -2320,7 +2445,9 @@ static int check_auth(struct sip_pvt *p, struct sip_request *req, char *randdata
return 0;
if (!strlen(randdata) || !strlen(get_header(req, "Proxy-Authorization"))) {
snprintf(randdata, randlen, "%08x", rand());
transmit_response_with_auth(p, "407 Proxy Authentication Required", req, randdata);
transmit_response_with_auth(p, "407 Proxy Authentication Required", req, randdata, reliable);
/* Schedule auto destroy in 15 seconds */
sip_scheddestroy(p, 15000);
res = 1;
} else {
/* Whoever came up with the authentication section of SIP can suck my %&#$&* for not putting
@ -2427,7 +2554,7 @@ static int register_verify(struct sip_pvt *p, struct sockaddr_in *sin, struct si
if (!strcasecmp(peer->name, name) && peer->dynamic) {
p->nat = peer->nat;
transmit_response(p, "100 Trying", req);
if (!(res = check_auth(p, req, p->randdata, sizeof(p->randdata), peer->name, peer->secret, "REGISTER", uri))) {
if (!(res = check_auth(p, req, p->randdata, sizeof(p->randdata), peer->name, peer->secret, "REGISTER", uri, 0))) {
if (parse_contact(p, peer, req)) {
ast_log(LOG_WARNING, "Failed to parse contact info\n");
} else {
@ -2672,7 +2799,7 @@ static int check_user(struct sip_pvt *p, struct sip_request *req, char *cmd, cha
ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", p->nat);
ast_rtp_setnat(p->rtp, p->nat);
}
if (!(res = check_auth(p, req, p->randdata, sizeof(p->randdata), user->name, user->secret, cmd, uri))) {
if (!(res = check_auth(p, req, p->randdata, sizeof(p->randdata), user->name, user->secret, cmd, uri, 1))) {
strncpy(p->context, user->context, sizeof(p->context) - 1);
if (strlen(user->callerid) && strlen(p->callerid))
strncpy(p->callerid, user->callerid, sizeof(p->callerid) - 1);
@ -3134,7 +3261,11 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_
struct sip_peer *peer;
int pingtime;
struct timeval tv;
int seqno=0;
c = get_header(req, "Cseq");
if (sscanf(c, "%d ", &seqno) != 1) {
ast_log(LOG_WARNING, "Unable to determine sequence number\n");
}
msg = strchr(c, ' ');
if (!msg) msg = ""; else msg++;
retrylock:
@ -3171,7 +3302,7 @@ retrylock:
if (peer->pokeexpire > -1)
ast_sched_del(sched, peer->pokeexpire);
if (!strcasecmp(msg, "INVITE"))
transmit_request(p, "ACK", 0);
transmit_request(p, "ACK", 0, 0);
sip_destroy(p);
p = NULL;
/* Try again eventually */
@ -3181,6 +3312,8 @@ retrylock:
peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_OK, sip_poke_peer_s, peer);
}
} else if (p->outgoing) {
/* Acknowledge sequence number */
__sip_ack(p, seqno, 0);
if (p->initid > -1) {
/* Don't auto congest anymore since we've gotten something useful back */
ast_sched_del(sched, p->initid);
@ -3215,6 +3348,16 @@ retrylock:
}
break;
case 200:
if (!strcasecmp(msg, "NOTIFY")) {
/* They got the notify, this is the end */
if (p->owner) {
ast_log(LOG_WARNING, "Notify answer on an owned channel?\n");
ast_queue_hangup(p->owner, 0);
} else {
sip_destroy(p);
p = NULL;
}
} else if (!strcasecmp(msg, "INVITE")) {
if (strlen(get_header(req, "Content-Type")))
process_sdp(p, req);
if (p->owner) {
@ -3223,10 +3366,8 @@ retrylock:
ast_queue_control(p->owner, AST_CONTROL_ANSWER, 0);
}
}
if (!strcasecmp(msg, "INVITE"))
transmit_request(p, "ACK", 0);
else if (!strcasecmp(msg, "REGISTER"))
{
transmit_request(p, "ACK", 0, 0);
} else if (!strcasecmp(msg, "REGISTER")) {
/* char *exp; */
int expires;
struct sip_registry *r;
@ -3255,7 +3396,7 @@ retrylock:
break;
case 407:
/* First we ACK */
transmit_request(p, "ACK", 0);
transmit_request(p, "ACK", 0, 0);
/* Then we AUTH */
do_proxy_auth(p, req);
/* This is just a hack to kill the channel while testing */
@ -3303,7 +3444,7 @@ retrylock:
ast_queue_hangup(p->owner, 0);
break;
}
transmit_request(p, "ACK", 0);
transmit_request(p, "ACK", 0, 0);
p->alreadygone = 1;
} else
ast_log(LOG_NOTICE, "Dunno anything about a %d %s response from %s\n", resp, rest, p->owner ? p->owner->name : inet_ntoa(p->sa.sin_addr));
@ -3314,7 +3455,7 @@ retrylock:
switch(resp) {
case 200:
if (!strcasecmp(msg, "INVITE") || !strcasecmp(msg, "REGISTER") )
transmit_request(p, "ACK", 0);
transmit_request(p, "ACK", 0, 0);
break;
}
}
@ -3610,7 +3751,7 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc
}
}
/* Always increment on a BYE */
transmit_request(p, "BYE", 1);
transmit_request(p, "BYE", 1, 1);
p->alreadygone = 1;
}
} else if (!strcasecmp(cmd, "CANCEL") || !strcasecmp(cmd, "BYE")) {
@ -3648,6 +3789,7 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc
/* Uhm, I haven't figured out the point of the ACK yet. Are we
supposed to retransmit responses until we get an ack?
Make sure this is on a valid call */
__sip_ack(p, seqno, 1);
if (strlen(get_header(req, "Content-Type"))) {
if (process_sdp(p, req))
return -1;
@ -3740,9 +3882,9 @@ static int sip_send_mwi_to_peer(struct sip_peer *peer)
snprintf(p->via, sizeof(p->via), "SIP/2.0/UDP %s:%d;branch=%08x", inet_ntoa(p->ourip), ourport, p->branch);
build_callid(p->callid, sizeof(p->callid), p->ourip);
/* Send MWI */
p->outgoing = 1;
transmit_notify(p, newmsgs, oldmsgs);
/* Destroy channel */
sip_destroy(p);
sip_scheddestroy(p, 15000);
return 0;
}

Loading…
Cancel
Save