LCOV - code coverage report
Current view: top level - ballet/http - fd_picohttpparser.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 229 465 49.2 %
Date: 2024-11-13 11:58:15 Functions: 9 14 64.3 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
       3             :  *                         Shigeo Mitsunari
       4             :  *
       5             :  * The software is licensed under either the MIT License (below) or the Perl
       6             :  * license.
       7             :  *
       8             :  * Permission is hereby granted, free of charge, to any person obtaining a copy
       9             :  * of this software and associated documentation files (the "Software"), to
      10             :  * deal in the Software without restriction, including without limitation the
      11             :  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
      12             :  * sell copies of the Software, and to permit persons to whom the Software is
      13             :  * furnished to do so, subject to the following conditions:
      14             :  *
      15             :  * The above copyright notice and this permission notice shall be included in
      16             :  * all copies or substantial portions of the Software.
      17             :  *
      18             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      19             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      20             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      21             :  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      22             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      23             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
      24             :  * IN THE SOFTWARE.
      25             :  */
      26             : 
      27             : #include <assert.h>
      28             : #include <stddef.h>
      29             : #include <string.h>
      30             : #ifdef __SSE4_2__
      31             : #ifdef _MSC_VER
      32             : #include <nmmintrin.h>
      33             : #else
      34             : #include <x86intrin.h>
      35             : #endif
      36             : #endif
      37             : #include "picohttpparser.h"
      38             : 
      39             : #if __GNUC__ >= 3
      40         210 : #define likely(x) __builtin_expect(!!(x), 1)
      41         129 : #define unlikely(x) __builtin_expect(!!(x), 0)
      42             : #else
      43             : #define likely(x) (x)
      44             : #define unlikely(x) (x)
      45             : #endif
      46             : 
      47             : #ifdef _MSC_VER
      48             : #define ALIGNED(n) _declspec(align(n))
      49             : #else
      50             : #define ALIGNED(n) __attribute__((aligned(n)))
      51             : #endif
      52             : 
      53             : #define IS_PRINTABLE_ASCII(c) ((unsigned char)(c)-040u < 0137u)
      54             : 
      55             : #define CHECK_EOF()                                                                                                                \
      56         336 :     if (buf == buf_end) {                                                                                                          \
      57          12 :         *ret = -2;                                                                                                                 \
      58          12 :         return NULL;                                                                                                               \
      59          12 :     }
      60             : 
      61             : #define EXPECT_CHAR_NO_CHECK(ch)                                                                                                   \
      62         207 :     if (*buf++ != ch) {                                                                                                            \
      63           0 :         *ret = -1;                                                                                                                 \
      64           0 :         return NULL;                                                                                                               \
      65           0 :     }
      66             : 
      67             : #define EXPECT_CHAR(ch)                                                                                                            \
      68          81 :     CHECK_EOF();                                                                                                                   \
      69          81 :     EXPECT_CHAR_NO_CHECK(ch);
      70             : 
      71             : #define ADVANCE_TOKEN(tok, toklen)                                                                                                 \
      72           9 :     do {                                                                                                                           \
      73           9 :         const char *tok_start = buf;                                                                                               \
      74           9 :         static const char ALIGNED(16) ranges2[16] = "\000\040\177\177";                                                            \
      75           9 :         int found2;                                                                                                                \
      76           9 :         buf = findchar_fast(buf, buf_end, ranges2, 4, &found2);                                                                    \
      77           9 :         if (!found2) {                                                                                                             \
      78           0 :             CHECK_EOF();                                                                                                           \
      79           0 :         }                                                                                                                          \
      80           9 :         while (1) {                                                                                                                \
      81           9 :             if (*buf == ' ') {                                                                                                     \
      82           9 :                 break;                                                                                                             \
      83           9 :             } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {                                                                      \
      84           0 :                 if ((unsigned char)*buf < '\040' || *buf == '\177') {                                                              \
      85           0 :                     *ret = -1;                                                                                                     \
      86           0 :                     return NULL;                                                                                                   \
      87           0 :                 }                                                                                                                  \
      88           0 :             }                                                                                                                      \
      89           9 :             ++buf;                                                                                                                 \
      90           0 :             CHECK_EOF();                                                                                                           \
      91           0 :         }                                                                                                                          \
      92           9 :         tok = tok_start;                                                                                                           \
      93           9 :         toklen = (size_t)(buf - tok_start);                                                                                        \
      94           9 :     } while (0)
      95             : 
      96             : static const char *token_char_map = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
      97             :                                     "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0"
      98             :                                     "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1"
      99             :                                     "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0"
     100             :                                     "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
     101             :                                     "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
     102             :                                     "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
     103             :                                     "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
     104             : 
     105             : static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges, size_t ranges_size, int *found)
     106         132 : {
     107         132 :     *found = 0;
     108         132 : #if __SSE4_2__
     109         132 :     if (likely(buf_end - buf >= 16)) {
     110         111 :         __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges);
     111             : 
     112         111 :         size_t left = (size_t)((buf_end - buf) & ~15);
     113         126 :         do {
     114         126 :             __m128i b16 = _mm_loadu_si128((const __m128i *)buf);
     115         126 :             int r = (int)_mm_cmpestri(ranges16, (int)ranges_size, b16, 16, _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS);
     116         126 :             if (unlikely(r != 16)) {
     117         111 :                 buf += r;
     118         111 :                 *found = 1;
     119         111 :                 break;
     120         111 :             }
     121          15 :             buf += 16;
     122          15 :             left -= 16;
     123          15 :         } while (likely(left != 0));
     124         111 :     }
     125             : #else
     126             :     /* suppress unused parameter warning */
     127             :     (void)buf_end;
     128             :     (void)ranges;
     129             :     (void)ranges_size;
     130             : #endif
     131           0 :     return buf;
     132         132 : }
     133             : 
     134             : static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token, size_t *token_len, int *ret)
     135          54 : {
     136          54 :     const char *token_start = buf;
     137             : 
     138          54 : #ifdef __SSE4_2__
     139          54 :     static const char ALIGNED(16) ranges1[16] = "\0\010"    /* allow HT */
     140          54 :                                                 "\012\037"  /* allow SP and up to but not including DEL */
     141          54 :                                                 "\177\177"; /* allow chars w. MSB set */
     142          54 :     int found;
     143          54 :     buf = findchar_fast(buf, buf_end, ranges1, 6, &found);
     144          54 :     if (found)
     145          51 :         goto FOUND_CTL;
     146             : #else
     147             :     /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined */
     148             :     while (likely(buf_end - buf >= 8)) {
     149             : #define DOIT()                                                                                                                     \
     150             :     do {                                                                                                                           \
     151             :         if (unlikely(!IS_PRINTABLE_ASCII(*buf)))                                                                                   \
     152             :             goto NonPrintable;                                                                                                     \
     153             :         ++buf;                                                                                                                     \
     154             :     } while (0)
     155             :         DOIT();
     156             :         DOIT();
     157             :         DOIT();
     158             :         DOIT();
     159             :         DOIT();
     160             :         DOIT();
     161             :         DOIT();
     162             :         DOIT();
     163             : #undef DOIT
     164             :         continue;
     165             :     NonPrintable:
     166             :         if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) {
     167             :             goto FOUND_CTL;
     168             :         }
     169             :         ++buf;
     170             :     }
     171             : #endif
     172           3 :     for (;; ++buf) {
     173           3 :         CHECK_EOF();
     174           3 :         if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {
     175           3 :             if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) {
     176           3 :                 goto FOUND_CTL;
     177           3 :             }
     178           3 :         }
     179           3 :     }
     180          54 : FOUND_CTL:
     181          54 :     if (likely(*buf == '\015')) {
     182          54 :         ++buf;
     183         108 :         EXPECT_CHAR('\012');
     184         108 :         *token_len = (size_t)(buf - 2 - token_start);
     185         108 :     } else if (*buf == '\012') {
     186           0 :         *token_len = (size_t)(buf - token_start);
     187           0 :         ++buf;
     188           0 :     } else {
     189           0 :         *ret = -1;
     190           0 :         return NULL;
     191           0 :     }
     192          54 :     *token = token_start;
     193             : 
     194          54 :     return buf;
     195          54 : }
     196             : 
     197             : static const char *is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret)
     198           0 : {
     199           0 :     int ret_cnt = 0;
     200           0 :     buf = last_len < 3 ? buf : buf + last_len - 3;
     201             : 
     202           0 :     while (1) {
     203           0 :         CHECK_EOF();
     204           0 :         if (*buf == '\015') {
     205           0 :             ++buf;
     206           0 :             CHECK_EOF();
     207           0 :             EXPECT_CHAR('\012');
     208           0 :             ++ret_cnt;
     209           0 :         } else if (*buf == '\012') {
     210           0 :             ++buf;
     211           0 :             ++ret_cnt;
     212           0 :         } else {
     213           0 :             ++buf;
     214           0 :             ret_cnt = 0;
     215           0 :         }
     216           0 :         if (ret_cnt == 2) {
     217           0 :             return buf;
     218           0 :         }
     219           0 :     }
     220             : 
     221           0 :     *ret = -2;
     222           0 :     return NULL;
     223           0 : }
     224             : 
     225             : #define PARSE_INT(valp_, mul_)                                                                                                     \
     226          45 :     if (*buf < '0' || '9' < *buf) {                                                                                                \
     227           0 :         buf++;                                                                                                                     \
     228           0 :         *ret = -1;                                                                                                                 \
     229           0 :         return NULL;                                                                                                               \
     230           0 :     }                                                                                                                              \
     231          45 :     *(valp_) = (mul_) * (*buf++ - '0');
     232             : 
     233             : #define PARSE_INT_3(valp_)                                                                                                         \
     234           9 :     do {                                                                                                                           \
     235           9 :         int res_ = 0;                                                                                                              \
     236           9 :         PARSE_INT(&res_, 100)                                                                                                      \
     237           9 :         *valp_ = res_;                                                                                                             \
     238           9 :         PARSE_INT(&res_, 10)                                                                                                       \
     239           9 :         *valp_ += res_;                                                                                                            \
     240           9 :         PARSE_INT(&res_, 1)                                                                                                        \
     241           9 :         *valp_ += res_;                                                                                                            \
     242           9 :     } while (0)
     243             : 
     244             : /* returned pointer is always within [buf, buf_end), or null */
     245             : static const char *parse_token(const char *buf, const char *buf_end, const char **token, size_t *token_len, char next_char,
     246             :                                int *ret)
     247          69 : {
     248             :     /* We use pcmpestri to detect non-token characters. This instruction can take no more than eight character ranges (8*2*8=128
     249             :      * bits that is the size of a SSE register). Due to this restriction, characters `|` and `~` are handled in the slow loop. */
     250          69 :     static const char ALIGNED(16) ranges[] = "\x00 "  /* control chars and up to SP */
     251          69 :                                              "\"\""   /* 0x22 */
     252          69 :                                              "()"     /* 0x28,0x29 */
     253          69 :                                              ",,"     /* 0x2c */
     254          69 :                                              "//"     /* 0x2f */
     255          69 :                                              ":@"     /* 0x3a-0x40 */
     256          69 :                                              "[]"     /* 0x5b-0x5d */
     257          69 :                                              "{\xff"; /* 0x7b-0xff */
     258          69 :     const char *buf_start = buf;
     259          69 :     int found;
     260          69 :     buf = findchar_fast(buf, buf_end, ranges, sizeof(ranges) - 1, &found);
     261          69 :     if (!found) {
     262          18 :         CHECK_EOF();
     263          18 :     }
     264          87 :     while (1) {
     265          87 :         if (*buf == next_char) {
     266          57 :             break;
     267          57 :         } else if (!token_char_map[(unsigned char)*buf]) {
     268           3 :             *ret = -1;
     269           3 :             return NULL;
     270           3 :         }
     271          27 :         ++buf;
     272          27 :         CHECK_EOF();
     273          18 :     }
     274          57 :     *token = buf_start;
     275          57 :     *token_len = (size_t)(buf - buf_start);
     276          57 :     return buf;
     277          69 : }
     278             : 
     279             : /* returned pointer is always within [buf, buf_end), or null */
     280             : static const char *parse_http_version(const char *buf, const char *buf_end, int *minor_version, int *ret)
     281          18 : {
     282             :     /* we want at least [HTTP/1.<two chars>] to try to parse */
     283          18 :     if (buf_end - buf < 9) {
     284           0 :         *ret = -2;
     285           0 :         return NULL;
     286           0 :     }
     287          18 :     EXPECT_CHAR_NO_CHECK('H');
     288          18 :     EXPECT_CHAR_NO_CHECK('T');
     289          18 :     EXPECT_CHAR_NO_CHECK('T');
     290          18 :     EXPECT_CHAR_NO_CHECK('P');
     291          18 :     EXPECT_CHAR_NO_CHECK('/');
     292          18 :     EXPECT_CHAR_NO_CHECK('1');
     293          18 :     EXPECT_CHAR_NO_CHECK('.');
     294          18 :     PARSE_INT(minor_version, 1);
     295          18 :     return buf;
     296          18 : }
     297             : 
     298             : static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers, size_t *num_headers,
     299             :                                  size_t max_headers, int *ret)
     300          18 : {
     301          63 :     for (;; ++*num_headers) {
     302          63 :         CHECK_EOF();
     303          63 :         if (*buf == '\015') {
     304          18 :             ++buf;
     305          18 :             EXPECT_CHAR('\012');
     306          18 :             break;
     307          45 :         } else if (*buf == '\012') {
     308           0 :             ++buf;
     309           0 :             break;
     310           0 :         }
     311          45 :         if (*num_headers == max_headers) {
     312           0 :             *ret = -1;
     313           0 :             return NULL;
     314           0 :         }
     315          45 :         if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) {
     316             :             /* parsing name, but do not discard SP before colon, see
     317             :              * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */
     318          45 :             if ((buf = parse_token(buf, buf_end, &headers[*num_headers].name, &headers[*num_headers].name_len, ':', ret)) == NULL) {
     319           0 :                 return NULL;
     320           0 :             }
     321          45 :             if (headers[*num_headers].name_len == 0) {
     322           0 :                 *ret = -1;
     323           0 :                 return NULL;
     324           0 :             }
     325          45 :             ++buf;
     326          90 :             for (;; ++buf) {
     327          90 :                 CHECK_EOF();
     328          90 :                 if (!(*buf == ' ' || *buf == '\t')) {
     329          45 :                     break;
     330          45 :                 }
     331          90 :             }
     332          45 :         } else {
     333           0 :             headers[*num_headers].name = NULL;
     334           0 :             headers[*num_headers].name_len = 0;
     335           0 :         }
     336          45 :         const char *value;
     337          45 :         size_t value_len;
     338          45 :         if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len, ret)) == NULL) {
     339           0 :             return NULL;
     340           0 :         }
     341             :         /* remove trailing SPs and HTABs */
     342          45 :         const char *value_end = value + value_len;
     343          45 :         for (; value_end != value; --value_end) {
     344          42 :             const char c = *(value_end - 1);
     345          42 :             if (!(c == ' ' || c == '\t')) {
     346          42 :                 break;
     347          42 :             }
     348          42 :         }
     349          45 :         headers[*num_headers].value = value;
     350          45 :         headers[*num_headers].value_len = (size_t)(value_end - value);
     351          45 :     }
     352          18 :     return buf;
     353          18 : }
     354             : 
     355             : static const char *parse_request(const char *buf, const char *buf_end, const char **method, size_t *method_len, const char **path,
     356             :                                  size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers,
     357             :                                  size_t max_headers, int *ret)
     358          24 : {
     359             :     /* skip first empty line (some clients add CRLF after POST content) */
     360          24 :     CHECK_EOF();
     361          24 :     if (*buf == '\015') {
     362           0 :         ++buf;
     363           0 :         EXPECT_CHAR('\012');
     364          24 :     } else if (*buf == '\012') {
     365           0 :         ++buf;
     366           0 :     }
     367             : 
     368             :     /* parse request line */
     369          24 :     if ((buf = parse_token(buf, buf_end, method, method_len, ' ', ret)) == NULL) {
     370          12 :         return NULL;
     371          12 :     }
     372          12 :     do {
     373          12 :         ++buf;
     374          12 :         CHECK_EOF();
     375           9 :     } while (*buf == ' ');
     376           9 :     ADVANCE_TOKEN(*path, *path_len);
     377           9 :     do {
     378           9 :         ++buf;
     379           9 :         CHECK_EOF();
     380           9 :     } while (*buf == ' ');
     381           9 :     if (*method_len == 0 || *path_len == 0) {
     382           0 :         *ret = -1;
     383           0 :         return NULL;
     384           0 :     }
     385           9 :     if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
     386           0 :         return NULL;
     387           0 :     }
     388           9 :     if (*buf == '\015') {
     389           9 :         ++buf;
     390          18 :         EXPECT_CHAR('\012');
     391          18 :     } else if (*buf == '\012') {
     392           0 :         ++buf;
     393           0 :     } else {
     394           0 :         *ret = -1;
     395           0 :         return NULL;
     396           0 :     }
     397             : 
     398           9 :     return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
     399           9 : }
     400             : 
     401             : int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path,
     402             :                       size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len)
     403          24 : {
     404          24 :     const char *buf = buf_start, *buf_end = buf_start + len;
     405          24 :     size_t max_headers = *num_headers;
     406          24 :     int r = 0;
     407             : 
     408          24 :     *method = NULL;
     409          24 :     *method_len = 0;
     410          24 :     *path = NULL;
     411          24 :     *path_len = 0;
     412          24 :     *minor_version = -1;
     413          24 :     *num_headers = 0;
     414             : 
     415             :     /* if last_len != 0, check if the request is complete (a fast countermeasure
     416             :        againt slowloris */
     417          24 :     if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
     418           0 :         return r;
     419           0 :     }
     420             : 
     421          24 :     if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, minor_version, headers, num_headers, max_headers,
     422          24 :                              &r)) == NULL) {
     423          15 :         return r;
     424          15 :     }
     425             : 
     426           9 :     return (int)(buf - buf_start);
     427          24 : }
     428             : 
     429             : static const char *parse_response(const char *buf, const char *buf_end, int *minor_version, int *status, const char **msg,
     430             :                                   size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t max_headers, int *ret)
     431           9 : {
     432             :     /* parse "HTTP/1.x" */
     433           9 :     if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
     434           0 :         return NULL;
     435           0 :     }
     436             :     /* skip space */
     437           9 :     if (*buf != ' ') {
     438           0 :         *ret = -1;
     439           0 :         return NULL;
     440           0 :     }
     441           9 :     do {
     442           9 :         ++buf;
     443           9 :         CHECK_EOF();
     444           9 :     } while (*buf == ' ');
     445             :     /* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse */
     446           9 :     if (buf_end - buf < 4) {
     447           0 :         *ret = -2;
     448           0 :         return NULL;
     449           0 :     }
     450           9 :     PARSE_INT_3(status);
     451             : 
     452             :     /* get message including preceding space */
     453           9 :     if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) {
     454           0 :         return NULL;
     455           0 :     }
     456           9 :     if (*msg_len == 0) {
     457             :         /* ok */
     458           9 :     } else if (**msg == ' ') {
     459             :         /* Remove preceding space. Successful return from `get_token_to_eol` guarantees that we would hit something other than SP
     460             :          * before running past the end of the given buffer. */
     461           9 :         do {
     462           9 :             ++*msg;
     463           9 :             --*msg_len;
     464           9 :         } while (**msg == ' ');
     465           9 :     } else {
     466             :         /* garbage found after status code */
     467           0 :         *ret = -1;
     468           0 :         return NULL;
     469           0 :     }
     470             : 
     471           9 :     return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
     472           9 : }
     473             : 
     474             : int phr_parse_response(const char *buf_start, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len,
     475             :                        struct phr_header *headers, size_t *num_headers, size_t last_len)
     476           9 : {
     477           9 :     const char *buf = buf_start, *buf_end = buf + len;
     478           9 :     size_t max_headers = *num_headers;
     479           9 :     int r = 0;
     480             : 
     481           9 :     *minor_version = -1;
     482           9 :     *status = 0;
     483           9 :     *msg = NULL;
     484           9 :     *msg_len = 0;
     485           9 :     *num_headers = 0;
     486             : 
     487             :     /* if last_len != 0, check if the response is complete (a fast countermeasure
     488             :        against slowloris */
     489           9 :     if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
     490           0 :         return r;
     491           0 :     }
     492             : 
     493           9 :     if ((buf = parse_response(buf, buf_end, minor_version, status, msg, msg_len, headers, num_headers, max_headers, &r)) == NULL) {
     494           0 :         return r;
     495           0 :     }
     496             : 
     497           9 :     return (int)(buf - buf_start);
     498           9 : }
     499             : 
     500             : int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len)
     501           0 : {
     502           0 :     const char *buf = buf_start, *buf_end = buf + len;
     503           0 :     size_t max_headers = *num_headers;
     504           0 :     int r = 0;
     505             : 
     506           0 :     *num_headers = 0;
     507             : 
     508             :     /* if last_len != 0, check if the response is complete (a fast countermeasure
     509             :        against slowloris */
     510           0 :     if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
     511           0 :         return r;
     512           0 :     }
     513             : 
     514           0 :     if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers, &r)) == NULL) {
     515           0 :         return r;
     516           0 :     }
     517             : 
     518           0 :     return (int)(buf - buf_start);
     519           0 : }
     520             : 
     521             : enum {
     522             :     CHUNKED_IN_CHUNK_SIZE,
     523             :     CHUNKED_IN_CHUNK_EXT,
     524             :     CHUNKED_IN_CHUNK_DATA,
     525             :     CHUNKED_IN_CHUNK_CRLF,
     526             :     CHUNKED_IN_TRAILERS_LINE_HEAD,
     527             :     CHUNKED_IN_TRAILERS_LINE_MIDDLE
     528             : };
     529             : 
     530             : static int decode_hex(int ch)
     531           0 : {
     532           0 :     if ('0' <= ch && ch <= '9') {
     533           0 :         return ch - '0';
     534           0 :     } else if ('A' <= ch && ch <= 'F') {
     535           0 :         return ch - 'A' + 0xa;
     536           0 :     } else if ('a' <= ch && ch <= 'f') {
     537           0 :         return ch - 'a' + 0xa;
     538           0 :     } else {
     539           0 :         return -1;
     540           0 :     }
     541           0 : }
     542             : 
     543             : ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *_bufsz)
     544           0 : {
     545           0 :     size_t dst = 0, src = 0, bufsz = *_bufsz;
     546           0 :     ssize_t ret = -2; /* incomplete */
     547             : 
     548           0 :     while (1) {
     549           0 :         switch (decoder->_state) {
     550           0 :         case CHUNKED_IN_CHUNK_SIZE:
     551           0 :             for (;; ++src) {
     552           0 :                 int v;
     553           0 :                 if (src == bufsz)
     554           0 :                     goto Exit;
     555           0 :                 if ((v = decode_hex(buf[src])) == -1) {
     556           0 :                     if (decoder->_hex_count == 0) {
     557           0 :                         ret = -1;
     558           0 :                         goto Exit;
     559           0 :                     }
     560           0 :                     break;
     561           0 :                 }
     562           0 :                 if (decoder->_hex_count == sizeof(size_t) * 2) {
     563           0 :                     ret = -1;
     564           0 :                     goto Exit;
     565           0 :                 }
     566           0 :                 decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + (size_t)v;
     567           0 :                 ++decoder->_hex_count;
     568           0 :             }
     569           0 :             decoder->_hex_count = 0;
     570           0 :             decoder->_state = CHUNKED_IN_CHUNK_EXT;
     571           0 :             __attribute__((fallthrough));
     572             :         /* fallthru */
     573           0 :         case CHUNKED_IN_CHUNK_EXT:
     574             :             /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */
     575           0 :             for (;; ++src) {
     576           0 :                 if (src == bufsz)
     577           0 :                     goto Exit;
     578           0 :                 if (buf[src] == '\012')
     579           0 :                     break;
     580           0 :             }
     581           0 :             ++src;
     582           0 :             if (decoder->bytes_left_in_chunk == 0) {
     583           0 :                 if (decoder->consume_trailer) {
     584           0 :                     decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
     585           0 :                     break;
     586           0 :                 } else {
     587           0 :                     goto Complete;
     588           0 :                 }
     589           0 :             }
     590           0 :             decoder->_state = CHUNKED_IN_CHUNK_DATA;
     591           0 :             __attribute__((fallthrough));
     592             :         /* fallthru */
     593           0 :         case CHUNKED_IN_CHUNK_DATA: {
     594           0 :             size_t avail = bufsz - src;
     595           0 :             if (avail < decoder->bytes_left_in_chunk) {
     596           0 :                 if (dst != src)
     597           0 :                     memmove(buf + dst, buf + src, avail);
     598           0 :                 src += avail;
     599           0 :                 dst += avail;
     600           0 :                 decoder->bytes_left_in_chunk -= avail;
     601           0 :                 goto Exit;
     602           0 :             }
     603           0 :             if (dst != src)
     604           0 :                 memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk);
     605           0 :             src += decoder->bytes_left_in_chunk;
     606           0 :             dst += decoder->bytes_left_in_chunk;
     607           0 :             decoder->bytes_left_in_chunk = 0;
     608           0 :             decoder->_state = CHUNKED_IN_CHUNK_CRLF;
     609           0 :         }
     610           0 :         __attribute__((fallthrough));
     611             :         /* fallthru */
     612           0 :         case CHUNKED_IN_CHUNK_CRLF:
     613           0 :             for (;; ++src) {
     614           0 :                 if (src == bufsz)
     615           0 :                     goto Exit;
     616           0 :                 if (buf[src] != '\015')
     617           0 :                     break;
     618           0 :             }
     619           0 :             if (buf[src] != '\012') {
     620           0 :                 ret = -1;
     621           0 :                 goto Exit;
     622           0 :             }
     623           0 :             ++src;
     624           0 :             decoder->_state = CHUNKED_IN_CHUNK_SIZE;
     625           0 :             break;
     626           0 :         case CHUNKED_IN_TRAILERS_LINE_HEAD:
     627           0 :             for (;; ++src) {
     628           0 :                 if (src == bufsz)
     629           0 :                     goto Exit;
     630           0 :                 if (buf[src] != '\015')
     631           0 :                     break;
     632           0 :             }
     633           0 :             if (buf[src++] == '\012')
     634           0 :                 goto Complete;
     635           0 :             decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE;
     636           0 :             __attribute__((fallthrough));
     637             :         /* fallthru */
     638           0 :         case CHUNKED_IN_TRAILERS_LINE_MIDDLE:
     639           0 :             for (;; ++src) {
     640           0 :                 if (src == bufsz)
     641           0 :                     goto Exit;
     642           0 :                 if (buf[src] == '\012')
     643           0 :                     break;
     644           0 :             }
     645           0 :             ++src;
     646           0 :             decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
     647           0 :             break;
     648           0 :         default:
     649           0 :             assert(0); // assert(!"decoder is corrupt");
     650           0 :         }
     651           0 :     }
     652             : 
     653           0 : Complete:
     654           0 :     ret = (ssize_t)(bufsz - src);
     655           0 : Exit:
     656           0 :     if (dst != src)
     657           0 :         memmove(buf + dst, buf + src, bufsz - src);
     658           0 :     *_bufsz = dst;
     659           0 :     return ret;
     660           0 : }
     661             : 
     662             : int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder)
     663           0 : {
     664           0 :     return decoder->_state == CHUNKED_IN_CHUNK_DATA;
     665           0 : }
     666             : 
     667             : #undef CHECK_EOF
     668             : #undef EXPECT_CHAR
     669             : #undef ADVANCE_TOKEN

Generated by: LCOV version 1.14