Index: channels/chan_sip.c =================================================================== RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v retrieving revision 1.839 diff -u -r1.839 chan_sip.c --- channels/chan_sip.c 7 Sep 2005 04:23:15 -0000 1.839 +++ channels/chan_sip.c 7 Sep 2005 17:11:23 -0000 @@ -433,6 +433,7 @@ char *line[SIP_MAX_LINES]; char data[SIP_MAX_PACKET]; int debug; /* Debug flag for this packet */ + unsigned int flags; /* SIP_PKT Flags for this packet */ }; struct sip_pkt; @@ -515,7 +516,8 @@ #define SIP_OSPAUTH_EXCLUSIVE (2 << 26) /* Call states */ #define SIP_CALL_ONHOLD (1 << 28) -#define SIP_CALL_LIMIT (1 << 29) +#define SIP_CALL_LIMIT (1 << 29) /* Call limit enforced on this session */ + /* a new page of flags for peer */ #define SIP_PAGE2_RTCACHEFRIENDS (1 << 0) @@ -523,6 +525,11 @@ #define SIP_PAGE2_RTAUTOCLEAR (1 << 2) #define SIP_PAGE2_RTIGNOREREGEXPIRE (1 << 3) +/* SIP packet flags */ +#define SIP_PKT_WITH_TOTAG (1 << 0) /* This packet has a to-tag */ +#define SIP_PKT_WITH_FROMTAG (1 << 1) /* This packet has a from-tag */ +#define SIP_PKT_DEBUG (1 << 2) /* Debug this packet */ + static int global_rtautoclear = 120; /* sip_pvt: PVT structures are used for each SIP conversation, ie. a call */ @@ -642,7 +649,7 @@ int retrans; /* Retransmission number */ int method; /* SIP method for this packet */ int seqno; /* Sequence number */ - unsigned int flags; /* non-zero if this is a response packet (e.g. 200 OK) */ + unsigned int flags; /* Flags */ struct sip_pvt *owner; /* Owner call */ int retransid; /* Retransmission ID */ int timer_a; /* SIP timer A, retransmission timer */ @@ -2992,13 +2999,16 @@ } /*--- find_call: Connect incoming SIP message to current dialog or create new dialog structure */ -/* Called by handle_request ,sipsock_read */ +/* Called by handle_request, sipsock_read */ static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *sin, const int intended_method) { struct sip_pvt *p; char *callid; - char tmp[256] = ""; char *tag = "", *c; + int badsyntax = 0; + char toheader[128] = ""; + char fromheader[128] = ""; + char *fromtag = "", *totag = ""; callid = get_header(req, "Call-ID"); @@ -3006,32 +3016,98 @@ /* In principle Call-ID's uniquely identify a call, but with a forking SIP proxy we need more to identify a branch - so we have to check branch, from and to tags to identify a call leg. - For Asterisk to behave correctly, you need to turn on pedanticsipchecking + For Asterisk to behave correctly, you need to turn on pedanticsipchecking in sip.conf */ + totag = get_header(req, "To"); + c = strcasestr(totag, ";tag="); + if (c) { + ast_copy_string(toheader, c+5 , sizeof(toheader)); + totag = toheader; + ast_set_flag(req, SIP_PKT_WITH_TOTAG); + } else + totag = NULL; + if (totag) { + if ((c = strchr(totag, ';'))) + *c = '\0'; + } + fromtag = get_header(req, "From"); + c = strcasestr(fromtag, ";tag="); + if (c) { + ast_copy_string(fromheader, c+5, sizeof(fromheader)); + fromtag = fromheader; + ast_set_flag(req, SIP_PKT_WITH_FROMTAG); + } else + fromtag = NULL; + + if (fromtag) { + if ((c = strchr(fromtag, ';'))) + *c = '\0'; + } if (req->method == SIP_RESPONSE) - ast_copy_string(tmp, get_header(req, "To"), sizeof(tmp)); + tag = totag; else - ast_copy_string(tmp, get_header(req, "From"), sizeof(tmp)); - tag = strcasestr(tmp, "tag="); - if (tag) { - tag += 4; - c = strchr(tag, ';'); - if (c) - *c = '\0'; + tag = fromtag; + if (option_debug > 3) { + if (req->method != SIP_RESPONSE) { /* Request */ + if (totag && fromtag) + ast_log(LOG_DEBUG, "...Request within an existing dialog\n"); + else if (totag && !fromtag) + ast_log(LOG_DEBUG, "...Request with no fromtag? Bad request.\n"); + else if (!totag && !fromtag) + ast_log(LOG_DEBUG, "...Request with no tags? Bad request.\n"); + else if (option_debug > 3) /* From tag, no to tag */ + ast_log(LOG_DEBUG, "New SIP request coming in: Call ID: %s (Checking %s) --From tag %s --To-tag %s \n", callid, req->method==SIP_RESPONSE ? "To" : "From", fromtag, totag); + } else { /* Response */ + if (totag && fromtag) + ast_log(LOG_DEBUG, "SIP response coming in: Call ID: %s (Checking %s) --From tag %s --To-tag %s \n", callid, req->method==SIP_RESPONSE ? "To" : "From", fromtag, totag); + else if (totag && !fromtag) + ast_log(LOG_DEBUG, "...Response with no fromtag? Bad response on bad request.\n"); + else if (!totag && !fromtag) + ast_log(LOG_DEBUG, "...Response with no tags at all? Bad response.\n"); + + } + } + if (!fromtag) { /* Always need from tags */ + badsyntax = 1; + ast_log(LOG_WARNING, "Bad SIP %s in dialog %s. \n", req->method==SIP_RESPONSE ? "Response" : "Request", callid); } } + + if (!badsyntax && option_debug > 3) + ast_log(LOG_DEBUG, "= Looking for Call ID: %s (Checking %s) --From tag %s --To-tag %s \n", callid, req->method==SIP_RESPONSE ? "To" : "From", fromtag, totag); ast_mutex_lock(&iflock); p = iflist; - while(p) { + while(p && !badsyntax) { /* In pedantic, we do not want packets with bad syntax to be connected to a PVT */ int found = 0; if (req->method == SIP_REGISTER) found = (!strcmp(p->callid, callid)); else found = (!strcmp(p->callid, callid) && (!pedanticsipchecking || !tag || ast_strlen_zero(p->theirtag) || !strcmp(p->theirtag, tag))) ; + + if (option_debug > 3) + ast_log(LOG_DEBUG, "= %s Their Call ID: %s Their Tag %s Our tag: as%08x\n", found ? "Found" : "No match", p->callid, p->theirtag, p->tag); + + /* If we get a new request within an existing to-tag - check the to tag as well */ + if (pedanticsipchecking && found && req->method != SIP_RESPONSE) { /* SIP Request */ + if (p->tag == 0 && totag) { + /* We have no to tag, and they have. Wrong dialog */ + found = 0; + } else if (totag) { /* Both have tags, compare them */ + char ourtag[12]; + sprintf(ourtag, "as%08x", p->tag); + if (strcmp(totag, ourtag)) { + found = 0; /* This is not our packet */ + } + } + if (!found && option_debug > 3) + ast_log(LOG_DEBUG, "= Being pedantic: This is not our match on request: Call ID: %s Ourtag Totag %s Method %s\n", p->callid, totag, sip_methods[req->method].text); + } + + if (found) { /* Found the call */ ast_mutex_lock(&p->lock); @@ -3042,8 +3118,6 @@ } ast_mutex_unlock(&iflock); p = sip_alloc(callid, sin, 1, intended_method); - if (p) - ast_mutex_lock(&p->lock); return p; } @@ -9079,13 +9153,13 @@ msg = ""; else msg++; - sipmethod = find_sip_method(msg); + sipmethod = find_sip_method(msg); /* Find method of packet we're getting response to */ owner = p->owner; if (owner) owner->hangupcause = hangup_sip2cause(resp); - /* Acknowledge whatever it is destined for */ + /* Stop retransmission of packet this is a reply to */ if ((resp >= 100) && (resp <= 199)) __sip_semi_ack(p, seqno, 0, sipmethod); else @@ -9102,7 +9176,8 @@ if (to) *to = '\0'; } - } + } + if (p->peerpoke) { /* We don't really care what the response is, just that it replied back. Well, as long as it's not a 100 response... since we might @@ -10212,12 +10287,15 @@ /* New SIP request coming in (could be new request in existing SIP dialog as well...) */ + p->method = req->method; /* Find out which SIP method they are using */ if (option_debug > 2) ast_log(LOG_DEBUG, "**** Received %s (%d) - Command in SIP %s\n", sip_methods[p->method].text, sip_methods[p->method].id, cmd); if (p->icseq && (p->icseq > seqno)) { ast_log(LOG_DEBUG, "Ignoring too old SIP packet packet %d (expecting >= %d)\n", seqno, p->icseq); + /* No from tag on request? Don't bother with it */ + transmit_response(p, "503 Server error", req); /* We must respond according to RFC 3261 sec 12.2 */ return -1; } else if (p->icseq && (p->icseq == seqno) && req->method != SIP_ACK &&(p->method != SIP_CANCEL|| ast_test_flag(p, SIP_ALREADYGONE))) { /* ignore means "don't do anything with it" but still have to @@ -10237,9 +10315,9 @@ /* Find their tag if we haven't got it */ if (ast_strlen_zero(p->theirtag)) { from = get_header(req, "From"); - from = strcasestr(from, "tag="); + from = strcasestr(from, ";tag="); if (from) { - from += 4; + from += 5; ast_copy_string(p->theirtag, from, sizeof(p->theirtag)); from = strchr(p->theirtag, ';'); if (from) @@ -10248,6 +10326,35 @@ } snprintf(p->lastmsg, sizeof(p->lastmsg), "Rx: %s", cmd); + if (pedanticsipchecking) { + /* If this is a request packet without a from tag, it's not + correct according to RFC 3261 */ + if (!ast_test_flag(req, SIP_PKT_WITH_FROMTAG)) { /* Only happens in pedantic */ + /* No from tag on request? Don't bother with it */ + if (!ignore && req->method == SIP_INVITE) { + transmit_response_reliable(p, "400 Bad Request", req, 1); + /* Will cease to exist after ACK */ + } else { + transmit_response(p, "400 Bad Request", req); + ast_set_flag(p, SIP_NEEDDESTROY); + } + } + + /* Check if this a new request in a new dialog with a totag already attached to it, + if it is, and it is not matched to a current call, forget it. + RFC 3261 - section 12.2 - and we don't want to mess with recovery */ + if (!p->initreq.headers && ast_test_flag(req, SIP_PKT_WITH_TOTAG)) { + /* If this is a first request and it got a to-tag, it is not for us */ + if (!ignore && req->method == SIP_INVITE) { + transmit_response_reliable(p, "481 Call/Transaction Does Not Exist", req, 1); + /* Will cease to exist after ACK */ + } else { + transmit_response(p, "481 Call/Transaction Does Not Exist", req); + ast_set_flag(p, SIP_NEEDDESTROY); + } + } + } + /* Handle various incoming SIP methods in requests */ switch (p->method) { case SIP_OPTIONS: @@ -10344,14 +10451,16 @@ } req.data[res] = '\0'; req.len = res; - req.debug = sip_debug_test_addr(&sin); + if(sip_debug_test_addr(&sin)) + ast_set_flag(&req, SIP_PKT_DEBUG); if (pedanticsipchecking) req.len = lws2sws(req.data, req.len); /* Fix multiline headers */ - if (req.debug) + if (ast_test_flag(&req, SIP_PKT_DEBUG)) { ast_verbose("\n<-- SIP read from %s:%d: \n%s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port), req.data); + } parse_request(&req); req.method = find_sip_method(req.rlPart1); - if (req.debug) { + if (ast_test_flag(&req, SIP_PKT_DEBUG)) { ast_verbose("--- (%d headers %d lines)", req.headers, req.lines); if (req.headers + req.lines == 0) ast_verbose(" Nat keepalive ");