Index: asterisk-1.4.speakup.26.1/channels/chan_sip.c =================================================================== --- asterisk-1.4.speakup.26.1.orig/channels/chan_sip.c 2009-08-13 11:40:28.000000000 +0200 +++ asterisk-1.4.speakup.26.1/channels/chan_sip.c 2009-10-05 11:51:13.000000000 +0200 @@ -948,6 +948,7 @@ AST_STRING_FIELD(peermd5secret); AST_STRING_FIELD(cid_num); /*!< Caller*ID number */ AST_STRING_FIELD(cid_name); /*!< Caller*ID name */ + AST_STRING_FIELD(via_branch); /*!< branch paramater of first Via header */ AST_STRING_FIELD(fullcontact); /*!< The Contact: that the UA registers with us */ AST_STRING_FIELD(our_contact); /*!< Our contact header */ AST_STRING_FIELD(rpid); /*!< Our RPID header */ @@ -4691,18 +4692,58 @@ return p; } +/*! \brief check Via: header for branch paramater */ +static char *get_branch(const struct sip_request *req, char *branch) +{ + char via[512]; + char *c, *b; + + ast_copy_string(via, get_header(req, "Via"), sizeof(via)); + + /* Work on the leftmost value of the topmost Via header */ + c = strchr(via, ','); + if (c) + *c = '\0'; + + if (option_debug) + ast_log(LOG_DEBUG, "First Via: %s\n", via); + + /* Check for branch */ + b = strstr(via, ";branch="); + if (!b) { + ast_log(LOG_DEBUG, "No branch in Via\n"); + *branch = '\0'; + return branch; + } + b = b + 8; + + c = strchr(b, ';'); + if (c) + *c = '\0'; + + ast_copy_string(branch, b, 512); + if (option_debug) + ast_log(LOG_DEBUG, "This is branch: %s\n", branch); + + return branch; +} + /*! \brief Connect incoming SIP message to current dialog or create new dialog structure 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 = NULL; - char *tag = ""; /* note, tag is never NULL */ char totag[128]; char fromtag[128]; const char *callid = get_header(req, "Call-ID"); const char *from = get_header(req, "From"); const char *to = get_header(req, "To"); const char *cseq = get_header(req, "Cseq"); + int seqno; + char *our_fromtag; + char *our_totag; + char branch[512]; + int found = FALSE; /* Call-ID, to, from and Cseq are required by RFC 3261. (Max-forwards and via too - ignored now) */ /* get_header always returns non-NULL so we must use ast_strlen_zero() */ @@ -4714,56 +4755,116 @@ /* 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 almost correctly, you need to turn on pedanticsipchecking in sip.conf */ if (gettag(req, "To", totag, sizeof(totag))) ast_set_flag(req, SIP_PKT_WITH_TOTAG); /* Used in handle_request/response */ gettag(req, "From", fromtag, sizeof(fromtag)); - tag = (req->method == SIP_RESPONSE) ? totag : fromtag; - - if (option_debug > 4 ) - 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); + if (option_debug > 4) + ast_log(LOG_DEBUG, "= Looking for Call-ID: %s From-tag %s To-tag %s \n", callid, fromtag, totag); } ast_mutex_lock(&iflock); for (p = iflist; p; p = p->next) { - /* In pedantic, we do not want packets with bad syntax to be connected to a PVT */ - int found = FALSE; - if (ast_strlen_zero(p->callid)) + /* try and find a channel with a matching callid */ + if (ast_strlen_zero(p->callid) || strcmp(p->callid, callid)) continue; - if (req->method == SIP_REGISTER) - found = (!strcmp(p->callid, callid)); - else { - found = !strcmp(p->callid, callid); - if (pedanticsipchecking && found) { - found = ast_strlen_zero(tag) || ast_strlen_zero(p->theirtag) || !ast_test_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED) || !strcmp(p->theirtag, tag); - } - } if (option_debug > 4) - ast_log(LOG_DEBUG, "= %s Their Call ID: %s Their Tag %s Our tag: %s\n", found ? "Found" : "No match", p->callid, p->theirtag, p->tag); + ast_log(LOG_DEBUG, "Considering channel: %p tag: %s theirtag: %s state: %d uri: %s contact: %s\n", p, p->tag, p->theirtag, p->invitestate, p->uri, p->our_contact); - /* 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] == '\0' && totag[0]) { - /* We have no to tag, but they have. Wrong dialog */ - found = FALSE; - } else if (totag[0]) { /* Both have tags, compare them */ - if (strcmp(totag, p->tag)) { - found = FALSE; /* This is not our packet */ - } + if (!pedanticsipchecking || req->method == SIP_REGISTER) { + found = TRUE; + break; + } + + /* tags depend on the direction of the request */ + if (req->method == SIP_RESPONSE) { + our_fromtag = p->tag; + our_totag = p->theirtag; + } else { + our_fromtag = p->theirtag; + our_totag = p->tag; + } + + /* from-tags must ALWAYS match */ + if (strcmp(our_fromtag, fromtag)) { + if (option_debug > 4) + ast_log(LOG_DEBUG, "From-tags do not match. local tag: %s remote tag: %s\n", our_fromtag, fromtag); + continue; + } + + /* Check initial INVITE */ + if ((req->method == SIP_INVITE) && ast_strlen_zero(totag)) { + /* Check for retransmission + * CSeq and branch must be identical. + * Skip CSeq check as we don't keep a history of CSeq's in the pvt :( */ + + get_branch(req, branch); + if (strcmp(branch, p->via_branch) == 0) { + if (option_debug > 4) + ast_log(LOG_DEBUG, "Match: Retransmission\n"); + found = TRUE; + break; + } + + /* If the dialog is already up, absorb the INVITE + * as it may be a late fork and we don't want 2 dialogs */ + if (ast_test_flag(&(p->flags[1]), SIP_PAGE2_DIALOG_ESTABLISHED)) { + if (option_debug > 4) + ast_log(LOG_DEBUG, "Match: Late initial INVITE for established dialog\n"); + found = TRUE; + break; + } + + /* We must match when authentication is used (CSEQ = previous CSEQ + 1) */ + sscanf(get_header(req, "CSeq"), "%d ", &seqno); + if (option_debug > 4) + ast_log(LOG_DEBUG, "CSeq: %s seqno: %d icseq: %d\n", cseq, seqno, p->icseq); + if (seqno == (p->icseq + 1)) { + if (option_debug > 4) + ast_log(LOG_DEBUG, "Match: Authentication attempt for previous request\n"); + found = TRUE; + break; } - if (!found && option_debug > 4) - 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); + + /* Every other initial INVITE must be a forked request and will be treated as new dialog */ + continue; } - if (found) { - /* Found the call */ - ast_mutex_lock(&p->lock); - ast_mutex_unlock(&iflock); - return p; + + /* Check to-tags */ + if (strcmp(our_totag, totag) == 0) { + /* to-tags match */ + if (option_debug > 4) + ast_log(LOG_DEBUG, "Match: both tags matched\n"); + found = TRUE; + break; + } else { + /* to-tags must match for in-dialog requests and ACK's*/ + if (ast_test_flag(&(p->flags[1]), SIP_PAGE2_DIALOG_ESTABLISHED) + || ((p->invitestate >= INV_COMPLETED) && p->invitestate != INV_CANCELLED)) { + /* to-tags differ */ + if (option_debug > 4) + ast_log(LOG_DEBUG, "To-tags do not match for in-dialog request. local tag: %s remote tag: %s\n", our_totag, totag); + continue; + } } + + /* There is not an established dialog for this pvt. Allow any to-tag */ + if (option_debug > 4) + ast_log(LOG_DEBUG, "Match: to-tags ignored in early-dialog\n"); + found = TRUE; + break; + } + + if (found) { + /* Found the call */ + ast_log(LOG_DEBUG, "%s Found channel: %p, local tag: %s remote tag: %s\n", sip_methods[req->method].text , p, p->tag, p->theirtag); + ast_mutex_lock(&p->lock); + ast_mutex_unlock(&iflock); + return p; } ast_mutex_unlock(&iflock); @@ -11519,6 +11620,7 @@ ast_cli(fd, " Audio IP: %s %s\n", ast_inet_ntoa(cur->redirip.sin_addr.s_addr ? cur->redirip.sin_addr : cur->ourip), cur->redirip.sin_addr.s_addr ? "(Outside bridge)" : "(local)" ); ast_cli(fd, " Our Tag: %s\n", cur->tag); ast_cli(fd, " Their Tag: %s\n", cur->theirtag); + ast_cli(fd, " Branch: %s\n", cur->via_branch); ast_cli(fd, " SIP User agent: %s\n", cur->useragent); if (!ast_strlen_zero(cur->username)) ast_cli(fd, " Username: %s\n", cur->username); @@ -13260,7 +13362,6 @@ res = handle_response_register(p, resp, rest, req, seqno); else if (sipmethod == SIP_BYE) { /* Ok, we're ready to go */ ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - ast_clear_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); } else if (sipmethod == SIP_SUBSCRIBE) ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); break; @@ -14500,10 +14601,15 @@ const char *p_replaces; char *replace_id = NULL; const char *required; + char branch[512]; unsigned int required_profile = 0; struct ast_channel *c = NULL; /* New channel */ int reinvite = 0; + /* Remember the branch from the Via header */ + get_branch(req, branch); + ast_string_field_set(p, via_branch, branch); + /* Find out what they support */ if (!p->sipoptions) { const char *supported = get_header(req, "Supported"); @@ -15720,7 +15826,6 @@ if (option_debug > 2) ast_log(LOG_DEBUG, "Received bye, no owner, selfdestruct soon.\n"); } - ast_clear_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); transmit_response(p, "200 OK", req); return 1;