[asterisk-dev] [svn-commits] mmichelson: branch 1.4 r132790 - /branches/1.4/channels/chan_sip.c

Mark Michelson mmichelson at digium.com
Tue Jul 22 17:56:41 CDT 2008


Johansson Olle E wrote:
> 
> 23 jul 2008 kl. 00.14 skrev SVN commits to the Digium repositories:
> 
>> Author: mmichelson
>> Date: Tue Jul 22 17:14:24 2008
>> New Revision: 132790
>>
>> URL: http://svn.digium.com/view/asterisk?view=rev&rev=132790
>> Log:
>> Allow Spiraled INVITEs to work correctly within Asterisk.
>>
>> Prior to this change, a spiraled INVITE would cause a 482
>> Loop Detected to be sent to the caller. With this change,
>> if a potential loop is detected, the Request-URI is inspected
>> to see if it has changed from what was originally received. If
>> pedantic mode is on, then this inspection is fully RFC 3261
>> compliant. If pedantic mode is not on, then a string comparison
>> is used to test the equality of the two R-URIs.
>>
>> This has been tested by using OpenSER to rewrite the R-URI
>> and send the INVITE back to Asterisk.
> 
> Mark,
> The important test is not to check if the request uri is changed. It might
> still be the same. But the topmost via header branch will be different.
> 
> 
> /O

Thanks for pointing this out. My goal with this was to solve the common case 
presented by users who use OpenSER or some other SIP router to spiral INVITEs. 
In fact, RFC 3261, in its definition of a SIP spiral states that "Typically, 
this means that the request's Request-URI differs from its previous arrival."

I'm curious about checking the topmost Via: header branch. Can you explain in a 
bit more detail what needs to be checked? If just the top-most Via's branch 
parameter is different than before, I'm not sure why this constitutes a 
legitimate spiral from a SIP UA's point of view.

Unfortunately, searching RFC 3261 for "spiral" and "loop" gives lots of 
guidelines for loop and spiral detection in proxies, but next to nothing for B2BUAs.

Thanks for your feedback.
Mark Michelson
> 
> 
>>
>>
>> +            } else {
>> +                goto fail;
>> +            }
>> +        } else if (!strcasecmp(name1, "method")) {
>> +            if (matched) {
>> +                methodmatch = 1;
>> +            } else {
>> +                goto fail;
>> +            }
>> +        }
>> +        pos1 = semicolon1;
>> +    }
>> +
>> +    /* We've made it out of that horrible O(m*n) construct and there 
>> are no
>> +     * failures yet. We're not done yet, though, because params2 
>> could have
>> +     * an maddr, ttl, user, or method header and params1 did not.
>> +     */
>> +    pos2 = params2;
>> +    while (!ast_strlen_zero(pos2)) {
>> +        char *name2 = pos2;
>> +        char *value2 = strchr(pos2, '=');
>> +        char *semicolon2 = strchr(pos2, ';');
>> +        if (semicolon2) {
>> +            *semicolon2++ = '\0';
>> +        }
>> +        if (!value2) {
>> +            goto fail;
>> +        }
>> +        if ((!strcasecmp(name2, "maddr") && !maddrmatch) ||
>> +                (!strcasecmp(name2, "ttl") && !ttlmatch) ||
>> +                (!strcasecmp(name2, "user") && !usermatch) ||
>> +                (!strcasecmp(name2, "method") && !methodmatch)) {
>> +            goto fail;
>> +        }
>> +    }
>> +    return 0;
>> +
>> +fail:
>> +    return 1;
>> +}
>> +
>> +/*! \brief helper routine for sip_uri_cmp
>> + *
>> + * This takes the "headers" from two SIP URIs and determines
>> + * if the URIs match. The rules for headers is simple. If a header
>> + * appears in one URI, then it must also appear in the other URI. The
>> + * order in which the headers appear does not matter.
>> + *
>> + * \param input1 Headers from URI 1
>> + * \param input2 Headers from URI 2
>> + * \return Return 0 if the URIs' headers match, 1 if they do not
>> + */
>> +static int sip_uri_headers_cmp(const char *input1, const char *input2)
>> +{
>> +    char *headers1 = ast_strdupa(input1);
>> +    char *headers2 = ast_strdupa(input2);
>> +    int zerolength1 = ast_strlen_zero(headers1);
>> +    int zerolength2 = ast_strlen_zero(headers2);
>> +    int different = 0;
>> +    char *header1;
>> +
>> +    if ((zerolength1 && !zerolength2) ||
>> +            (zerolength2 && !zerolength1))
>> +        return 1;
>> +
>> +    if (zerolength1 && zerolength2)
>> +        return 0;
>> +
>> +    /* At this point, we can definitively state that both inputs are
>> +     * not zero-length. First, one more optimization. If the length
>> +     * of the headers is not equal, then we definitely have no match
>> +     */
>> +    if (strlen(headers1) != strlen(headers2)) {
>> +        return 1;
>> +    }
>> +
>> +    for (header1 = strsep(&headers1, "&"); header1; header1 = 
>> strsep(&headers1, "&")) {
>> +        if (!strcasestr(headers2, header1)) {
>> +            different = 1;
>> +            break;
>> +        }
>> +    }
>> +
>> +    return different;
>> +}
>> +
>> +static int sip_uri_cmp(const char *input1, const char *input2)
>> +{
>> +    char *uri1 = ast_strdupa(input1);
>> +    char *uri2 = ast_strdupa(input2);
>> +    char *host1;
>> +    char *host2;
>> +    char *params1;
>> +    char *params2;
>> +    char *headers1;
>> +    char *headers2;
>> +
>> +    /* Strip off "sip:" from the URI. We know this is present
>> +     * because it was checked back in parse_request()
>> +     */
>> +    strsep(&uri1, ":");
>> +    strsep(&uri2, ":");
>> +
>> +    if ((host1 = strchr(uri1, '@'))) {
>> +        *host1++ = '\0';
>> +    }
>> +    if ((host2 = strchr(uri2, '@'))) {
>> +        *host2++ = '\0';
>> +    }
>> +
>> +    /* Check for mismatched username and passwords. This is the
>> +     * only case-sensitive comparison of a SIP URI
>> +     */
>> +    if ((host1 && !host2) ||
>> +            (host2 && !host1) ||
>> +            (host1 && host2 && strcmp(uri1, uri2))) {
>> +        return 1;
>> +    }
>> +
>> +    if (!host1)
>> +        host1 = uri1;
>> +    if (!host2)
>> +        host2 = uri2;
>> +
>> +    /* Strip off the parameters and headers so we can compare
>> +     * host and port
>> +     */
>> +
>> +    if ((params1 = strchr(host1, ';'))) {
>> +        *params1++ = '\0';
>> +    }
>> +    if ((params2 = strchr(host2, ';'))) {
>> +        *params2++ = '\0';
>> +    }
>> +
>> +    /* Headers come after parameters, but there may be headers without
>> +     * parameters, thus the S_OR
>> +     */
>> +    if ((headers1 = strchr(S_OR(params1, host1), '?'))) {
>> +        *headers1++ = '\0';
>> +    }
>> +    if ((headers2 = strchr(S_OR(params2, host2), '?'))) {
>> +        *headers2++ = '\0';
>> +    }
>> +
>> +    /* Now the host/port are properly isolated. We can get by with a 
>> string comparison
>> +     * because the SIP URI checking rules have some interesting 
>> exceptions that make
>> +     * this possible. I will note 2 in particular
>> +     * 1. hostnames which resolve to the same IP address as well as a 
>> hostname and its
>> +     *    IP address are not considered a match with SIP URI's.
>> +     * 2. If one URI specifies a port and the other does not, then 
>> the URIs do not match.
>> +     *    This includes if one URI explicitly contains port 5060 and 
>> the other implies it
>> +     *    by not having a port specified.
>> +     */
>> +
>> +    if (strcasecmp(host1, host2)) {
>> +        return 1;
>> +    }
>> +
>> +    /* Headers have easier rules to follow, so do those first */
>> +    if (sip_uri_headers_cmp(headers1, headers2)) {
>> +        return 1;
>> +    }
>> +
>> +    /* And now the parameters. Ugh */
>> +    return sip_uri_params_cmp(params1, params2);
>> +}
>> +
>>
>> /*! \brief Handle incoming INVITE request
>> \note     If the INVITE has a Replaces header, it is part of an
>> @@ -13814,10 +14069,44 @@
>>            being able to call yourself */
>>         /* If pedantic is on, we need to check the tags. If they're 
>> different, this is
>>            in fact a forked call through a SIP proxy somewhere. */
>> -        transmit_response_reliable(p, "482 Loop Detected", req);
>> -        p->invitestate = INV_COMPLETED;
>> -        sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
>> -        return 0;
>> +        int different;
>> +        if (pedanticsipchecking)
>> +            different = sip_uri_cmp(p->initreq.rlPart2, req->rlPart2);
>> +        else
>> +            different = strcmp(p->initreq.rlPart2, req->rlPart2);
>> +        if (!different) {
>> +            transmit_response(p, "482 Loop Detected", req);
>> +            p->invitestate = INV_COMPLETED;
>> +            sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
>> +            return 0;
>> +        } else {
>> +            /* This is a spiral. What we need to do is to just change 
>> the outgoing INVITE
>> +             * so that it now routes to the new Request URI. Since we 
>> created the INVITE ourselves
>> +             * that should be all we need to do.
>> +             */
>> +            char *uri = ast_strdupa(req->rlPart2);
>> +            char *at = strchr(uri, '@');
>> +            char *peerorhost;
>> +            struct sip_pkt *pkt = NULL;
>> +            if (option_debug > 2) {
>> +                ast_log(LOG_DEBUG, "Potential spiral detected. 
>> Original RURI was %s, new RURI is %s\n", p->initreq.rlPart2, 
>> req->rlPart2);
>> +            }
>> +            if (at) {
>> +                *at = '\0';
>> +            }
>> +            /* Parse out "sip:" */
>> +            if ((peerorhost = strchr(uri, ':'))) {
>> +                *peerorhost++ = '\0';
>> +            }
>> +            create_addr(p, peerorhost);
>> +            ast_string_field_free(p, theirtag);
>> +            for (pkt = p->packets; pkt; pkt = pkt->next) {
>> +                if (pkt->seqno == p->icseq && pkt->method == 
>> SIP_INVITE) {
>> +                    AST_SCHED_DEL(sched, pkt->retransid);
>> +                }
>> +            }
>> +            return transmit_invite(p, SIP_INVITE, 1, 3);
>> +        }
>>     }
>>     
>>     if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->pendinginvite) {
>> @@ -15332,7 +15621,7 @@
>>         if (!p->initreq.headers) {
>>             if (option_debug)
>>                 ast_log(LOG_DEBUG, "That's odd...  Got a response on a 
>> call we dont know about. Cseq %d Cmd %s\n", seqno, cmd);
>> -            ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);   
>> +            ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
>>             return 0;
>>         } else if (p->ocseq && (p->ocseq < seqno) && (seqno != 
>> p->lastnoninvite)) {
>>             if (option_debug)
>>
>>
>> _______________________________________________
>> --Bandwidth and Colocation Provided by http://www.api-digital.com--
>>
>> svn-commits mailing list
>> To UNSUBSCRIBE or update options visit:
>>   http://lists.digium.com/mailman/listinfo/svn-commits
> 
> ---
> * Olle E Johansson - oej at edvina.net
> * Cell phone +46 70 593 68 51, Office +46 8 96 40 20, Sweden
> 
> 
> 




More information about the asterisk-dev mailing list