~ubuntu-branches/ubuntu/oneiric/squid3/oneiric-security

« back to all changes in this revision

Viewing changes to src/HttpMsg.cc

  • Committer: Bazaar Package Importer
  • Author(s): Mahyuddin Susanto
  • Date: 2011-02-15 18:46:13 UTC
  • mfrom: (21.2.4 sid)
  • Revision ID: james.westby@ubuntu.com-20110215184613-1u3dh5sz4i055flk
Tags: 3.1.10-1ubuntu1
* Merge from debian unstable. (LP: #719283)  Remaining changes:
  - debian/patches/18-fix-ftbfs-binutils-gold.dpatch: Add library linker into
    LIBS instead to LDFLAGS to fixing FTBFS binutils-gold.
* Drop Ubuntu configuration for ufw which landed in Debian and sync it: 
  - debian/squid3.ufw.profile.

Show diffs side-by-side

added added

removed removed

Lines of Context:
321
321
int
322
322
httpMsgIsPersistent(HttpVersion const &http_ver, const HttpHeader * hdr)
323
323
{
324
 
    if ((http_ver.major >= 1) && (http_ver.minor >= 1)) {
 
324
    if (http_ver > HttpVersion(1, 0)) {
325
325
        /*
326
326
         * for modern versions of HTTP: persistent unless there is
327
327
         * a "Connection: close" header.
402
402
    hdr->req_start = hdr->req_end = -1;
403
403
    hdr->hdr_start = hdr->hdr_end = -1;
404
404
    debugs(74, 5, "httpParseInit: Request buffer is " << buf);
 
405
    hdr->m_start = hdr->m_end = -1;
 
406
    hdr->u_start = hdr->u_end = -1;
 
407
    hdr->v_start = hdr->v_end = -1;
 
408
    hdr->v_maj = hdr->v_min = 0;
405
409
}
406
410
 
407
411
#if MSGDODEBUG
446
450
}
447
451
#endif
448
452
 
449
 
/**
450
 
 * Attempt to parse the request line.
451
 
 *
452
 
 * This will set the values in hmsg that it determines. One may end up
453
 
 * with a partially-parsed buffer; the return value tells you whether
454
 
 * the values are valid or not.
455
 
 *
456
 
 * \retval      1 if parsed correctly
457
 
 * \retval      0 if more is needed
458
 
 * \retval      -1 if error
459
 
 *
460
 
 * TODO:
461
 
 *   * have it indicate "error" and "not enough" as two separate conditions!
462
 
 *   * audit this code as off-by-one errors are probably everywhere!
463
 
 */
 
453
int
 
454
HttpParser::parseRequestFirstLine()
 
455
{
 
456
    int second_word = -1; // track the suspected URI start
 
457
    int first_whitespace = -1, last_whitespace = -1; // track the first and last SP byte
 
458
    int line_end = -1; // tracks the last byte BEFORE terminal \r\n or \n sequence
 
459
 
 
460
    debugs(74, 5, HERE << "parsing possible request: " << buf);
 
461
 
 
462
    // Single-pass parse: (provided we have the whole line anyways)
 
463
 
 
464
    req_start = 0;
 
465
    if (Config.onoff.relaxed_header_parser) {
 
466
        if (Config.onoff.relaxed_header_parser < 0 && buf[req_start] == ' ')
 
467
            debugs(74, DBG_IMPORTANT, "WARNING: Invalid HTTP Request: " <<
 
468
                   "Whitespace bytes received ahead of method. " <<
 
469
                   "Ignored due to relaxed_header_parser.");
 
470
        // Be tolerant of prefix spaces (other bytes are valid method values)
 
471
        for (; req_start < bufsiz && buf[req_start] == ' '; req_start++);
 
472
    }
 
473
    req_end = -1;
 
474
    for (int i = 0; i < bufsiz; i++) {
 
475
        // track first and last whitespace (SP only)
 
476
        if (buf[i] == ' ') {
 
477
            last_whitespace = i;
 
478
            if (first_whitespace < req_start)
 
479
                first_whitespace = i;
 
480
        }
 
481
 
 
482
        // track next non-SP/non-HT byte after first_whitespace
 
483
        if (second_word < first_whitespace && buf[i] != ' ' && buf[i] != '\t') {
 
484
            second_word = i;
 
485
        }
 
486
 
 
487
        // locate line terminator
 
488
        if (buf[i] == '\n') {
 
489
            req_end = i;
 
490
            line_end = i - 1;
 
491
            break;
 
492
        }
 
493
        if (i < bufsiz - 1 && buf[i] == '\r') {
 
494
            if (Config.onoff.relaxed_header_parser) {
 
495
                if (Config.onoff.relaxed_header_parser < 0 && buf[i + 1] == '\r')
 
496
                    debugs(74, DBG_IMPORTANT, "WARNING: Invalid HTTP Request: " <<
 
497
                           "Series of carriage-return bytes received prior to line terminator. " <<
 
498
                           "Ignored due to relaxed_header_parser.");
 
499
 
 
500
                // Be tolerant of invalid multiple \r prior to terminal \n
 
501
                if (buf[i + 1] == '\n' || buf[i + 1] == '\r')
 
502
                    line_end = i - 1;
 
503
                while (i < bufsiz - 1 && buf[i + 1] == '\r')
 
504
                    i++;
 
505
 
 
506
                if (buf[i + 1] == '\n') {
 
507
                    req_end = i + 1;
 
508
                    break;
 
509
                }
 
510
            } else {
 
511
                if (buf[i + 1] == '\n') {
 
512
                    req_end = i + 1;
 
513
                    line_end = i - 1;
 
514
                    break;
 
515
                }
 
516
            }
 
517
 
 
518
            // RFC 2616 section 5.1
 
519
            // "No CR or LF is allowed except in the final CRLF sequence"
 
520
            return -1;
 
521
        }
 
522
    }
 
523
    if (req_end == -1) {
 
524
        debugs(74, 5, "Parser: retval 0: from " << req_start <<
 
525
               "->" << req_end << ": needs more data to complete first line.");
 
526
        return 0;
 
527
    }
 
528
 
 
529
    // NP: we have now seen EOL, more-data (0) cannot occur.
 
530
    //     From here on any failure is -1, success is 1
 
531
 
 
532
 
 
533
    // Input Validation:
 
534
 
 
535
    // Process what we now know about the line structure into field offsets
 
536
    // generating HTTP status for any aborts as we go.
 
537
 
 
538
    // First non-whitespace = beginning of method
 
539
    if (req_start > line_end) {
 
540
        return -1;
 
541
    }
 
542
    m_start = req_start;
 
543
 
 
544
    // First whitespace = end of method
 
545
    if (first_whitespace > line_end || first_whitespace < req_start) {
 
546
        return -1;
 
547
    }
 
548
    m_end = first_whitespace - 1;
 
549
    if (m_end < m_start) {
 
550
        return -1;
 
551
    }
 
552
 
 
553
    // First non-whitespace after first SP = beginning of URL+Version
 
554
    if (second_word > line_end || second_word < req_start) {
 
555
        return -1;
 
556
    }
 
557
    u_start = second_word;
 
558
 
 
559
    // RFC 1945: SP and version following URI are optional, marking version 0.9
 
560
    // we identify this by the last whitespace being earlier than URI start
 
561
    if (last_whitespace < second_word && last_whitespace >= req_start) {
 
562
        v_maj = 0;
 
563
        v_min = 9;
 
564
        u_end = line_end;
 
565
        return 1;
 
566
    } else {
 
567
        // otherwise last whitespace is somewhere after end of URI.
 
568
        u_end = last_whitespace;
 
569
        // crop any trailing whitespace in the area we think of as URI
 
570
        for (; u_end >= u_start && xisspace(buf[u_end]); u_end--);
 
571
    }
 
572
    if (u_end < u_start) {
 
573
        return -1;
 
574
    }
 
575
 
 
576
    // Last whitespace SP = before start of protocol/version
 
577
    if (last_whitespace >= line_end) {
 
578
        return -1;
 
579
    }
 
580
    v_start = last_whitespace + 1;
 
581
    v_end = line_end;
 
582
 
 
583
    // We only accept HTTP protocol requests right now.
 
584
    // TODO: accept other protocols; RFC 2326 (RTSP protocol) etc
 
585
    if ((v_end - v_start +1) < 5 || strncasecmp(&buf[v_start], "HTTP/", 5) != 0) {
 
586
#if USE_HTTP_VIOLATIONS
 
587
        // being lax; old parser accepted strange versions
 
588
        // there is a LOT of cases which are ambiguous, therefore we cannot use relaxed_header_parser here.
 
589
        v_maj = 0;
 
590
        v_min = 9;
 
591
        u_end = line_end;
 
592
        return 1;
 
593
#else
 
594
        return -1;
 
595
#endif
 
596
    }
 
597
 
 
598
    int i = v_start + sizeof("HTTP/") -1;
 
599
 
 
600
    /* next should be 1 or more digits */
 
601
    if (!isdigit(buf[i])) {
 
602
        return -1;
 
603
    }
 
604
    int maj = 0;
 
605
    for (; i <= line_end && (isdigit(buf[i])) && maj < 65536; i++) {
 
606
        maj = maj * 10;
 
607
        maj = maj + (buf[i]) - '0';
 
608
    }
 
609
    // catch too-big values or missing remainders
 
610
    if (maj >= 65536 || i > line_end) {
 
611
        return -1;
 
612
    }
 
613
    v_maj = maj;
 
614
 
 
615
    /* next should be .; we -have- to have this as we have a whole line.. */
 
616
    if (buf[i] != '.') {
 
617
        return -1;
 
618
    }
 
619
    // catch missing minor part
 
620
    if (++i > line_end) {
 
621
        return -1;
 
622
    }
 
623
 
 
624
    /* next should be one or more digits */
 
625
    if (!isdigit(buf[i])) {
 
626
        return -1;
 
627
    }
 
628
    int min = 0;
 
629
    for (; i <= line_end && (isdigit(buf[i])) && min < 65536; i++) {
 
630
        min = min * 10;
 
631
        min = min + (buf[i]) - '0';
 
632
    }
 
633
    // catch too-big values or trailing garbage
 
634
    if (min >= 65536 || i < line_end) {
 
635
        return -1;
 
636
    }
 
637
    v_min = min;
 
638
 
 
639
    /*
 
640
     * Rightio - we have all the schtuff. Return true; we've got enough.
 
641
     */
 
642
    return 1;
 
643
}
 
644
 
464
645
int
465
646
HttpParserParseReqLine(HttpParser *hmsg)
466
647
{
467
 
    int i = 0;
468
 
    int retcode = 0;
469
 
    unsigned int maj = 0, min = 0;
470
 
    int last_whitespace = -1, line_end = -1;
471
 
 
472
 
    debugs(74, 5, "httpParserParseReqLine: parsing " << hmsg->buf);
473
 
 
474
648
    PROF_start(HttpParserParseReqLine);
475
 
    /* Find \r\n - end of URL+Version (and the request) */
476
 
    hmsg->req_end = -1;
477
 
    for (i = 0; i < hmsg->bufsiz; i++) {
478
 
        if (hmsg->buf[i] == '\n') {
479
 
            hmsg->req_end = i;
480
 
            break;
481
 
        }
482
 
        if (i < hmsg->bufsiz - 1 && hmsg->buf[i] == '\r' && hmsg->buf[i + 1] == '\n') {
483
 
            hmsg->req_end = i + 1;
484
 
            break;
485
 
        }
486
 
    }
487
 
    if (hmsg->req_end == -1) {
488
 
        retcode = 0;
489
 
        goto finish;
490
 
    }
491
 
    assert(hmsg->buf[hmsg->req_end] == '\n');
492
 
    /* Start at the beginning again */
493
 
    i = 0;
494
 
 
495
 
    /* Find first non-whitespace - beginning of method */
496
 
    for (; i < hmsg->req_end && (xisspace(hmsg->buf[i])); i++);
497
 
    if (i >= hmsg->req_end) {
498
 
        retcode = 0;
499
 
        goto finish;
500
 
    }
501
 
    hmsg->m_start = i;
502
 
    hmsg->req_start = i;
503
 
 
504
 
    /* Find first whitespace - end of method */
505
 
    for (; i < hmsg->req_end && (! xisspace(hmsg->buf[i])); i++);
506
 
    if (i >= hmsg->req_end) {
507
 
        retcode = 0;
508
 
        goto finish;
509
 
    }
510
 
    hmsg->m_end = i - 1;
511
 
 
512
 
    /* Find first non-whitespace - beginning of URL+Version */
513
 
    for (; i < hmsg->req_end && (xisspace(hmsg->buf[i])); i++);
514
 
    if (i >= hmsg->req_end) {
515
 
        retcode = 0;
516
 
        goto finish;
517
 
    }
518
 
    hmsg->u_start = i;
519
 
 
520
 
    /* Find \r\n or \n - thats the end of the line. Keep track of the last whitespace! */
521
 
    for (; i <= hmsg->req_end; i++) {
522
 
        /* If \n - its end of line */
523
 
        if (hmsg->buf[i] == '\n') {
524
 
            line_end = i;
525
 
            break;
526
 
        }
527
 
        /* XXX could be off-by-one wrong! */
528
 
        if (hmsg->buf[i] == '\r' && (i + 1) <= hmsg->req_end && hmsg->buf[i+1] == '\n') {
529
 
            line_end = i;
530
 
            break;
531
 
        }
532
 
        /* If its a whitespace, note it as it'll delimit our version */
533
 
        if (hmsg->buf[i] == ' ' || hmsg->buf[i] == '\t') {
534
 
            last_whitespace = i;
535
 
        }
536
 
    }
537
 
    if (i > hmsg->req_end) {
538
 
        retcode = 0;
539
 
        goto finish;
540
 
    }
541
 
 
542
 
    /* At this point we don't need the 'i' value; so we'll recycle it for version parsing */
543
 
 
544
 
    /*
545
 
     * At this point: line_end points to the first eol char (\r or \n);
546
 
     * last_whitespace points to the last whitespace char in the URL.
547
 
     * We know we have a full buffer here!
548
 
     */
549
 
    if (last_whitespace == -1) {
550
 
        maj = 0;
551
 
        min = 9;
552
 
        hmsg->u_end = line_end - 1;
553
 
        assert(hmsg->u_end >= hmsg->u_start);
554
 
    } else {
555
 
        /* Find the first non-whitespace after last_whitespace */
556
 
        /* XXX why <= vs < ? I do need to really re-audit all of this ..*/
557
 
        for (i = last_whitespace; i <= hmsg->req_end && xisspace(hmsg->buf[i]); i++);
558
 
        if (i > hmsg->req_end) {
559
 
            retcode = 0;
560
 
            goto finish;
561
 
        }
562
 
 
563
 
        /* is it http/ ? if so, we try parsing. If not, the URL is the whole line; version is 0.9 */
564
 
        if (i + 5 >= hmsg->req_end || (strncasecmp(&hmsg->buf[i], "HTTP/", 5) != 0)) {
565
 
            maj = 0;
566
 
            min = 9;
567
 
            hmsg->u_end = line_end - 1;
568
 
            assert(hmsg->u_end >= hmsg->u_start);
569
 
        } else {
570
 
            /* Ok, lets try parsing! Yes, this needs refactoring! */
571
 
            hmsg->v_start = i;
572
 
            i += 5;
573
 
 
574
 
            /* next should be 1 or more digits */
575
 
            maj = 0;
576
 
            for (; i < hmsg->req_end && (isdigit(hmsg->buf[i])) && maj < 65536; i++) {
577
 
                maj = maj * 10;
578
 
                maj = maj + (hmsg->buf[i]) - '0';
579
 
            }
580
 
            if (maj >= 65536) {
581
 
                retcode = -1;
582
 
                goto finish;
583
 
            }
584
 
            if (i >= hmsg->req_end) {
585
 
                retcode = 0;
586
 
                goto finish;
587
 
            }
588
 
 
589
 
            /* next should be .; we -have- to have this as we have a whole line.. */
590
 
            if (hmsg->buf[i] != '.') {
591
 
                retcode = 0;
592
 
                goto finish;
593
 
            }
594
 
            if (i + 1 >= hmsg->req_end) {
595
 
                retcode = 0;
596
 
                goto finish;
597
 
            }
598
 
 
599
 
            /* next should be one or more digits */
600
 
            i++;
601
 
            min = 0;
602
 
            for (; i < hmsg->req_end && (isdigit(hmsg->buf[i])) && min < 65536; i++) {
603
 
                min = min * 10;
604
 
                min = min + (hmsg->buf[i]) - '0';
605
 
            }
606
 
 
607
 
            if (min >= 65536) {
608
 
                retcode = -1;
609
 
                goto finish;
610
 
            }
611
 
 
612
 
            /* Find whitespace, end of version */
613
 
            hmsg->v_end = i;
614
 
            hmsg->u_end = last_whitespace - 1;
615
 
        }
616
 
    }
617
 
 
618
 
    /*
619
 
     * Rightio - we have all the schtuff. Return true; we've got enough.
620
 
     */
621
 
    retcode = 1;
622
 
 
623
 
finish:
624
 
    hmsg->v_maj = maj;
625
 
    hmsg->v_min = min;
626
 
    PROF_stop(HttpParserParseReqLine);
 
649
    int retcode = hmsg->parseRequestFirstLine();
627
650
    debugs(74, 5, "Parser: retval " << retcode << ": from " << hmsg->req_start <<
628
651
           "->" << hmsg->req_end << ": method " << hmsg->m_start << "->" <<
629
652
           hmsg->m_end << "; url " << hmsg->u_start << "->" << hmsg->u_end <<
630
 
           "; version " << hmsg->v_start << "->" << hmsg->v_end << " (" << maj <<
631
 
           "/" << min << ")");
632
 
 
 
653
           "; version " << hmsg->v_start << "->" << hmsg->v_end << " (" << hmsg->v_maj <<
 
654
           "/" << hmsg->v_min << ")");
 
655
    PROF_stop(HttpParserParseReqLine);
633
656
    return retcode;
634
657
}
635