LTP GCOV extension - code coverage report
Current view: directory - and-httpd/src - httpd_parse.c
Test: And-httpd coverage
Date: 2006-09-11 Instrumented lines: 947
Code covered: 92.7 % Executed lines: 878

       1                 : /*
       2                 :  *  Copyright (C) 2004, 2005, 2006  James Antill
       3                 :  *
       4                 :  *  This library is free software; you can redistribute it and/or
       5                 :  *  modify it under the terms of the GNU Lesser General Public
       6                 :  *  License as published by the Free Software Foundation; either
       7                 :  *  version 2 of the License, or (at your option) any later version.
       8                 :  *
       9                 :  *  This library is distributed in the hope that it will be useful,
      10                 :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      11                 :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12                 :  *  Lesser General Public License for more details.
      13                 :  *
      14                 :  *  You should have received a copy of the GNU Lesser General Public
      15                 :  *  License along with this library; if not, write to the Free Software
      16                 :  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
      17                 :  *
      18                 :  *  email: james@and.org
      19                 :  */
      20                 : /* main HTTP parsing routines, currently mainly implements server portions */
      21                 : 
      22                 : #define EX_UTILS_NO_USE_INIT  1
      23                 : #define EX_UTILS_NO_USE_EXIT  1
      24                 : #define EX_UTILS_NO_USE_LIMIT 1
      25                 : #define EX_UTILS_NO_USE_BLOCK 1
      26                 : #define EX_UTILS_NO_USE_GET   1
      27                 : #define EX_UTILS_NO_USE_PUT   1
      28                 : #define EX_UTILS_RET_FAIL     1
      29                 : #include "ex_utils.h"
      30                 : 
      31                 : #include "mk.h"
      32                 : 
      33                 : #include "vlg.h"
      34                 : 
      35                 : #define HTTPD_HAVE_GLOBAL_OPTS 1
      36                 : #include "httpd.h"
      37                 : #include "httpd_policy.h"
      38                 : 
      39                 : #if ! COMPILE_DEBUG
      40                 : # define HTTP_CONF_MMAP_LIMIT_MIN (16 * 1024) /* a couple of pages */
      41                 : # define HTTP_CONF_SAFE_PRINT_REQ TRUE
      42                 : #else
      43                 : # define HTTP_CONF_MMAP_LIMIT_MIN 8 /* debug... */
      44                 : # define HTTP_CONF_SAFE_PRINT_REQ FALSE
      45                 : #endif
      46                 : #define HTTP_CONF_MMAP_LIMIT_MAX (50 * 1024 * 1024)
      47                 : 
      48                 : #define HTTPD_CONF_ZIP_LIMIT_MIN 8
      49                 : 
      50                 : #define CLEN COMPILE_STRLEN
      51                 : 
      52                 : /* is the cstr a prefix of the vstr */
      53                 : #define VPREFIX(vstr, p, l, cstr)                                       \
      54                 :     (((l) >= CLEN(cstr)) &&                                             \
      55                 :      vstr_cmp_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
      56                 : /* is the cstr a suffix of the vstr */
      57                 : #define VSUFFIX(vstr, p, l, cstr)                                       \
      58                 :     (((l) >= CLEN(cstr)) &&                                             \
      59                 :      vstr_cmp_eod_buf_eq(vstr, p, l, cstr, CLEN(cstr)))
      60                 : 
      61                 : /* is the cstr a prefix of the vstr, no case */
      62                 : #define VIPREFIX(vstr, p, l, cstr)                                      \
      63                 :     (((l) >= CLEN(cstr)) &&                                             \
      64                 :      vstr_cmp_case_buf_eq(vstr, p, CLEN(cstr), cstr, CLEN(cstr)))
      65                 : 
      66                 : /* for simplicity */
      67                 : #define VEQ(vstr, p, l, cstr)  vstr_cmp_cstr_eq(vstr, p, l, cstr)
      68                 : #define VIEQ(vstr, p, l, cstr) vstr_cmp_case_cstr_eq(vstr, p, l, cstr)
      69                 : 
      70                 : #define HTTP__HDR_SET(req, h, p, l) do {               \
      71                 :       (req)-> http_hdrs -> hdr_ ## h ->pos = (p);          \
      72                 :       (req)-> http_hdrs -> hdr_ ## h ->len = (l);          \
      73                 :     } while (FALSE)
      74                 : #define HTTP__HDR_MULTI_SET(req, h, p, l) do {         \
      75                 :       (req)-> http_hdrs -> multi -> hdr_ ## h ->pos = (p); \
      76                 :       (req)-> http_hdrs -> multi -> hdr_ ## h ->len = (l); \
      77                 :     } while (FALSE)
      78                 : 
      79                 : #define HTTP__XTRA_HDR_INIT(x) do {             \
      80                 :       req-> x ## _vs1 = NULL;                   \
      81                 :       req-> x ## _pos = 0;                      \
      82                 :       req-> x ## _len = 0;                      \
      83                 :     } while (FALSE)
      84                 : 
      85                 : #include <syslog.h>
      86                 : 
      87                 : static Vlg *vlg = NULL;
      88                 : 
      89                 : void httpd_parse_init(Vlg *passed_vlg)
      90              64 : {
      91              32 :   ASSERT(passed_vlg && !vlg);
      92              64 :   vlg = passed_vlg;
      93              64 : }
      94                 : 
      95                 : void httpd_parse_exit(void)
      96              44 : {
      97              22 :   ASSERT(vlg);
      98              44 :   vlg = NULL;
      99              44 : }
     100                 : 
     101                 : /* HTTP crack -- Implied linear whitespace between tokens, note that it
     102                 :  * is *LWS == *([CRLF] 1*(SP | HT)) */
     103                 : void http_parse_skip_lws(const Vstr_base *s1, size_t *pos, size_t *len)
     104          111193 : {
     105          111193 :   size_t lws__len = 0;
     106                 :   
     107           55604 :   ASSERT(s1 && pos && len);
     108                 :   
     109                 :   while (TRUE)
     110                 :   {
     111          227202 :     if (VPREFIX(s1, *pos, *len, HTTP_EOL))
     112                 :     {
     113            4112 :       *len -= CLEN(HTTP_EOL); *pos += CLEN(HTTP_EOL);
     114                 :     }
     115          218978 :     else if (lws__len)
     116          111193 :       break;
     117                 :     
     118          111897 :     if (!(lws__len = vstr_spn_cstr_chrs_fwd(s1, *pos, *len, HTTP_LWS)))
     119               0 :       break;
     120          111897 :     *len -= lws__len;
     121          111897 :     *pos += lws__len;
     122          111897 :   }
     123          111193 : }
     124                 : 
     125                 : /* might have been able to do it with string matches, but getting...
     126                 :  * "HTTP/1.1" = OK
     127                 :  * "HTTP/1.10" = OK
     128                 :  * "HTTP/1.10000000000000" = BAD
     129                 :  * ...seemed not as easy. It also seems like you have to accept...
     130                 :  * "HTTP / 01 . 01" as "HTTP/1.1"
     131                 :  */
     132                 : int http_parse_version(struct Con *con, struct Httpd_req_data *req)
     133           39241 : {
     134           39241 :   Vstr_base *data = con->evnt->io_r;
     135           39241 :   size_t op_pos = VSTR_SECTS_NUM(req->sects, 3)->pos;
     136           39241 :   size_t op_len = VSTR_SECTS_NUM(req->sects, 3)->len;
     137           39241 :   unsigned int major = 0;
     138           39241 :   unsigned int minor = 0;
     139           39241 :   size_t num_len = 0;
     140                 :   unsigned int num_flags = 10 | (VSTR_FLAG_PARSE_NUM_NO_BEG_PM |
     141           39241 :                                  VSTR_FLAG_PARSE_NUM_OVERFLOW);
     142                 :   
     143           39241 :   if (!VPREFIX(data, op_pos, op_len, "HTTP"))
     144             144 :     HTTPD_ERR_MSG_RET(req, 400, "<PROTO>/Version", FALSE);
     145                 : 
     146           39097 :   op_len -= CLEN("HTTP"); op_pos += CLEN("HTTP");
     147           39097 :   HTTP_SKIP_LWS(data, op_pos, op_len);
     148                 :   
     149           39097 :   if (!VPREFIX(data, op_pos, op_len, "/"))
     150             144 :     HTTPD_ERR_MSG_RET(req, 400, "HTTP/Version", FALSE);
     151           38953 :   op_len -= CLEN("/"); op_pos += CLEN("/");
     152           38953 :   HTTP_SKIP_LWS(data, op_pos, op_len);
     153                 :   
     154           38953 :   major = vstr_parse_uint(data, op_pos, op_len, num_flags, &num_len, NULL);
     155           38953 :   op_len -= num_len; op_pos += num_len;
     156           38953 :   HTTP_SKIP_LWS(data, op_pos, op_len);
     157                 :   
     158           38953 :   if (!num_len || !VPREFIX(data, op_pos, op_len, "."))
     159             432 :     HTTPD_ERR_MSG_RET(req, 400, "HTTP/Version", FALSE);
     160                 : 
     161           38521 :   op_len -= CLEN("."); op_pos += CLEN(".");
     162           38521 :   HTTP_SKIP_LWS(data, op_pos, op_len);
     163                 :   
     164           38521 :   minor = vstr_parse_uint(data, op_pos, op_len, num_flags, &num_len, NULL);
     165           38521 :   op_len -= num_len; op_pos += num_len;
     166           38521 :   HTTP_SKIP_LWS(data, op_pos, op_len);
     167                 :   
     168           38521 :   if (!num_len || op_len)
     169             288 :     HTTPD_ERR_MSG_RET(req, 400, "HTTP/Version", FALSE);
     170                 :   
     171                 :   if (0) { } /* not allowing HTTP/0.9 here */
     172           38305 :   else if ((major == 1) && (minor >  1))
     173             144 :     req->ver_1_x = TRUE;
     174           54366 :   else if ((major == 1) && (minor == 1))
     175           32569 :     req->ver_1_1 = TRUE;
     176            5520 :   else if ((major == 1) && (minor == 0))
     177                 :   { /* do nothing */ }
     178                 :   else
     179             144 :     HTTPD_ERR_MSG_RET(req, 505, "HTTP/Version", FALSE);
     180                 :         
     181           38089 :   return (TRUE);
     182                 : }
     183                 : 
     184                 : void http_parse_clear_hdrs(struct Httpd_req_data *req)
     185           99661 : {
     186           99661 :   Vstr_base *tmp = req->http_hdrs->multi->combiner_store;
     187                 : 
     188           49167 :   ASSERT(tmp);
     189                 :   
     190           99661 :   HTTP__HDR_SET(req, ua,                  0, 0);
     191           99661 :   HTTP__HDR_SET(req, referer,             0, 0);
     192                 : 
     193           99661 :   HTTP__HDR_SET(req, authorization,       0, 0);
     194           99661 :   HTTP__HDR_SET(req, expect,              0, 0);
     195           99661 :   HTTP__HDR_SET(req, host,                0, 0);
     196           99661 :   req->http_host_port = 80;
     197           99661 :   HTTP__HDR_SET(req, if_modified_since,   0, 0);
     198           99661 :   HTTP__HDR_SET(req, if_range,            0, 0);
     199           99661 :   HTTP__HDR_SET(req, if_unmodified_since, 0, 0);
     200           99661 :   HTTP__HDR_SET(req, range,               0, 0);
     201           99661 :   HTTP__HDR_SET(req, x_moz,               0, 0);
     202                 : 
     203           99661 :   vstr_del(tmp, 1, tmp->len);
     204           99661 :   HTTP__HDR_MULTI_SET(req, accept,          0, 0);
     205           99661 :   HTTP__HDR_MULTI_SET(req, accept_charset,  0, 0);
     206           99661 :   HTTP__HDR_MULTI_SET(req, accept_encoding, 0, 0);
     207           99661 :   HTTP__HDR_MULTI_SET(req, accept_language, 0, 0);
     208           99661 :   HTTP__HDR_MULTI_SET(req, connection,      0, 0);
     209           99661 :   HTTP__HDR_MULTI_SET(req, if_match,        0, 0);
     210           99661 :   HTTP__HDR_MULTI_SET(req, if_none_match,   0, 0);
     211                 : 
     212           99661 :   if (req->xtra_content)
     213           97754 :     vstr_del(req->xtra_content, 1, req->xtra_content->len);
     214                 : 
     215           99661 :   HTTP__XTRA_HDR_INIT(content_type);
     216           99661 :   HTTP__XTRA_HDR_INIT(content_disposition);
     217           99661 :   HTTP__XTRA_HDR_INIT(content_language);
     218           99661 :   HTTP__XTRA_HDR_INIT(content_location);
     219           99661 :   HTTP__XTRA_HDR_INIT(content_md5);
     220           99661 :   HTTP__XTRA_HDR_INIT(gzip_content_md5);
     221           99661 :   HTTP__XTRA_HDR_INIT(bzip2_content_md5);
     222           99661 :   HTTP__XTRA_HDR_INIT(cache_control);
     223           99661 :   HTTP__XTRA_HDR_INIT(etag);
     224           99661 :   HTTP__XTRA_HDR_INIT(expires);
     225           99661 :   HTTP__XTRA_HDR_INIT(link);
     226           99661 :   HTTP__XTRA_HDR_INIT(p3p);
     227           99661 :   HTTP__XTRA_HDR_INIT(ext_vary_a);
     228           99661 :   HTTP__XTRA_HDR_INIT(ext_vary_ac);
     229           99661 :   HTTP__XTRA_HDR_INIT(ext_vary_al);
     230           99661 : }
     231                 : 
     232                 : static void http__multi_hdr_fixup(Vstr_sect_node *hdr_ignore,
     233                 :                                   Vstr_sect_node *hdr, size_t pos, size_t len)
     234            8400 : {
     235            8400 :   if (hdr == hdr_ignore)
     236            1200 :     return;
     237                 :   
     238            7200 :   if (hdr->pos <= pos)
     239            7200 :     return;
     240                 : 
     241               0 :   hdr->pos += len;
     242                 : }
     243                 : 
     244                 : static int http__multi_hdr_cp(Vstr_base *comb,
     245                 :                               Vstr_base *data, Vstr_sect_node *hdr)
     246            3808 : {
     247            3808 :   size_t pos = comb->len + 1;
     248                 : 
     249            3808 :   if (!hdr->len)
     250            3264 :     return (TRUE);
     251                 :   
     252             544 :   if (!vstr_add_vstr(comb, comb->len,
     253                 :                      data, hdr->pos, hdr->len, VSTR_TYPE_ADD_BUF_PTR))
     254               0 :     return (FALSE);
     255                 : 
     256             544 :   hdr->pos = pos;
     257                 :   
     258             544 :   return (TRUE);
     259                 : }
     260                 : 
     261                 : static int http__app_multi_hdr(Vstr_base *data, struct Http_hdrs *hdrs,
     262                 :                                Vstr_sect_node *hdr, size_t pos, size_t len)
     263           24256 : {
     264           24256 :   Vstr_base *comb = hdrs->multi->comb;
     265                 :   
     266           12128 :   ASSERT(comb);
     267                 :   
     268           12128 :   ASSERT((hdr == hdrs->multi->hdr_accept) ||
     269                 :          (hdr == hdrs->multi->hdr_accept_charset) ||
     270                 :          (hdr == hdrs->multi->hdr_accept_encoding) ||
     271                 :          (hdr == hdrs->multi->hdr_accept_language) ||
     272                 :          (hdr == hdrs->multi->hdr_connection) ||
     273                 :          (hdr == hdrs->multi->hdr_if_match) ||
     274                 :          (hdr == hdrs->multi->hdr_if_none_match));
     275                 : 
     276           12128 :   ASSERT((comb == data) || (comb == hdrs->multi->combiner_store));
     277                 :   
     278           24256 :   if ((data == comb) && !hdr->pos)
     279                 :   { /* Do the fast thing... */
     280           22912 :     hdr->pos = pos;
     281           22912 :     hdr->len = len;
     282           22912 :     return (TRUE);
     283                 :   }
     284                 : 
     285            1344 :   if (data == comb)
     286                 :   { /* OK, so we have a crap request and need to JOIN multiple headers... */
     287             544 :     comb = hdrs->multi->comb = hdrs->multi->combiner_store;
     288                 :   
     289             544 :     if (!http__multi_hdr_cp(comb, data, hdrs->multi->hdr_accept) ||
     290                 :         !http__multi_hdr_cp(comb, data, hdrs->multi->hdr_accept_charset) ||
     291                 :         !http__multi_hdr_cp(comb, data, hdrs->multi->hdr_accept_encoding) ||
     292                 :         !http__multi_hdr_cp(comb, data, hdrs->multi->hdr_accept_language) ||
     293                 :         !http__multi_hdr_cp(comb, data, hdrs->multi->hdr_connection) ||
     294                 :         !http__multi_hdr_cp(comb, data, hdrs->multi->hdr_if_match) ||
     295                 :         !http__multi_hdr_cp(comb, data, hdrs->multi->hdr_if_none_match) ||
     296                 :         FALSE)
     297               0 :       return (FALSE);
     298                 :   }
     299                 :   
     300            1344 :   if (!hdr->pos)
     301                 :   {
     302             144 :     hdr->pos = comb->len + 1;
     303             144 :     hdr->len = len;
     304             144 :     return (vstr_add_vstr(comb, comb->len,
     305                 :                           data, pos, len, VSTR_TYPE_ADD_BUF_PTR));
     306                 :   }
     307                 : 
     308                 :   /* reverses the order, but that doesn't matter */
     309            1200 :   if (!vstr_add_cstr_ptr(comb, hdr->pos - 1, ","))
     310               0 :     return (FALSE);
     311            1200 :   if (!vstr_add_vstr(comb, hdr->pos - 1,
     312                 :                      data, pos, len, VSTR_TYPE_ADD_BUF_PTR))
     313               0 :     return (FALSE);
     314            1200 :   hdr->len += ++len;
     315                 : 
     316                 :   /* now need to "move" any hdrs after this one */
     317            1200 :   pos = hdr->pos - 1;
     318            1200 :   http__multi_hdr_fixup(hdr, hdrs->multi->hdr_accept,          pos, len);
     319            1200 :   http__multi_hdr_fixup(hdr, hdrs->multi->hdr_accept_charset,  pos, len);
     320            1200 :   http__multi_hdr_fixup(hdr, hdrs->multi->hdr_accept_encoding, pos, len);
     321            1200 :   http__multi_hdr_fixup(hdr, hdrs->multi->hdr_accept_language, pos, len);
     322            1200 :   http__multi_hdr_fixup(hdr, hdrs->multi->hdr_connection,      pos, len);
     323            1200 :   http__multi_hdr_fixup(hdr, hdrs->multi->hdr_if_match,        pos, len);
     324            1200 :   http__multi_hdr_fixup(hdr, hdrs->multi->hdr_if_none_match,   pos, len);
     325                 :     
     326            1200 :   return (TRUE);
     327                 : }
     328                 : 
     329                 : /* viprefix, with local knowledge */
     330                 : static int http__hdr_eq(struct Con *con, size_t pos, size_t len,
     331                 :                         const char *hdr, size_t hdr_len, size_t *hdr_val_pos)
     332          641869 : {
     333          641869 :   Vstr_base *data = con->evnt->io_r;
     334                 : 
     335          320972 :   ASSERT(CLEN(hdr) == hdr_len);
     336          320972 :   ASSERT(hdr[hdr_len - 1] == ':');
     337                 :   
     338          641869 :   if (!con->policy->use_hdrs_non_spc)
     339          385816 :     --hdr_len;
     340                 :   
     341          641869 :   if ((len < hdr_len) ||
     342                 :       !vstr_cmp_case_buf_eq(data, pos, hdr_len, hdr, hdr_len))
     343          569388 :     return (FALSE);
     344           72481 :   len -= hdr_len; pos += hdr_len;
     345                 : 
     346           72481 :   if (!con->policy->use_hdrs_non_spc)
     347                 :   {
     348           44432 :     HTTP_SKIP_LWS(data, pos, len);
     349           44432 :     if (!len)
     350               0 :       return (FALSE);
     351                 :   
     352           44432 :     if (vstr_export_chr(data, pos) != ':')
     353            4752 :       return (FALSE);
     354           39680 :     --len; ++pos;
     355                 :   }
     356                 :   
     357           67729 :   *hdr_val_pos = pos;
     358                 :   
     359           67729 :   return (TRUE);
     360                 : }
     361                 : 
     362                 : /* remove LWS from front and end... what a craptastic std. */
     363                 : static void http__hdr_fixup(Vstr_base *data, size_t *pos, size_t *len,
     364                 :                             size_t hdr_val_pos)
     365           67265 : {
     366           67265 :   size_t tmp = 0;
     367                 :   
     368           67265 :   *len -= hdr_val_pos - *pos; *pos += hdr_val_pos - *pos;
     369           67265 :   HTTP_SKIP_LWS(data, *pos, *len);
     370                 : 
     371                 :   /* hand coding for a HTTP_SKIP_LWS() going backwards... */
     372          105706 :   while ((tmp = vstr_spn_cstr_chrs_rev(data, *pos, *len, HTTP_LWS)))
     373                 :   {
     374            4816 :     *len -= tmp;
     375                 :   
     376            4816 :     if (VSUFFIX(data, *pos, *len, HTTP_EOL))
     377             744 :       *len -= CLEN(HTTP_EOL);
     378                 :   }  
     379           67265 : }
     380                 : 
     381                 : /* for single headers, multiple ones aren't allowed ...
     382                 :  * we can do last one wins, or just error (erroring is more secure, see
     383                 :  * HTTP Request smuggling) */
     384                 : #define HDR__EQ(x) http__hdr_eq(con, pos, len, x ":", CLEN(x ":"), &hdr_val_pos)
     385                 : 
     386                 : #define HDR__EQ_SET(x, h)                                               \
     387                 :     else if (HDR__EQ(x)) do                                             \
     388                 :     {                                                                   \
     389                 :       if (req->policy->use_hdrs_no_x2 && http_hdrs-> hdr_ ## h ->pos)   \
     390                 :         HTTPD_ERR_MSG_RET(req, 400, "Double header " x, FALSE);         \
     391                 :       http__hdr_fixup(data, &pos, &len, hdr_val_pos);                   \
     392                 :       http_hdrs-> hdr_ ## h ->pos = pos;                                \
     393                 :       http_hdrs-> hdr_ ## h ->len = len;                                \
     394                 :     } while (FALSE)
     395                 : #define HDR__EQ_MULTI_SET(x, h)                                         \
     396                 :     else if (HDR__EQ(x)) do                                             \
     397                 :     {                                                                   \
     398                 :       http__hdr_fixup(data, &pos, &len, hdr_val_pos);                   \
     399                 :       if (!http__app_multi_hdr(data, http_hdrs,                         \
     400                 :                                http_hdrs->multi-> hdr_ ## h, pos, len)) \
     401                 :       {                                                                 \
     402                 :         req->malloc_bad = TRUE;                                         \
     403                 :         HTTPD_ERR_MSG_RET(req, 500, "Memory failure", FALSE);           \
     404                 :       }                                                                 \
     405                 :     } while (FALSE)
     406                 : #define HDR__EQ_IGNORE(x, h)                                            \
     407                 :     else if (HDR__EQ(x)) do                                             \
     408                 :     {                                                                   \
     409                 :       if (req->policy->use_hdrs_no_x2 && got_ ## h)                     \
     410                 :         HTTPD_ERR_MSG_RET(req, 400, "Double header " x, FALSE);         \
     411                 :       got_ ## h = TRUE;                                                 \
     412                 :     } while (FALSE)
     413                 : 
     414                 : int http_parse_hdrs(struct Con *con, Httpd_req_data *req)
     415           38089 : {
     416           38089 :   Vstr_base *data = con->evnt->io_r;
     417           38089 :   struct Http_hdrs *http_hdrs = req->http_hdrs;
     418           38089 :   unsigned int num = 3; /* skip "method URI version" */
     419           38089 :   int got_content_length = FALSE;
     420           38089 :   int got_content_lang = FALSE;
     421           38089 :   int got_content_type = FALSE;
     422           38089 :   int got_transfer_encoding = FALSE;
     423                 :   
     424          145379 :   while (++num <= req->sects->num)
     425                 :   {
     426           69873 :     size_t pos = VSTR_SECTS_NUM(req->sects, num)->pos;
     427           69873 :     size_t len = VSTR_SECTS_NUM(req->sects, num)->len;
     428           69873 :     size_t hdr_val_pos = 0;
     429                 : 
     430                 :     if (0) { /* nothing */ }
     431           69873 :     HDR__EQ_SET("User-Agent",                ua);
     432           69713 :     HDR__EQ_SET("Referer",                   referer);
     433           69361 :     HDR__EQ_SET("Authorization",             authorization);
     434           68113 :     HDR__EQ_SET("Expect",                    expect);
     435           67969 :     HDR__EQ_SET("Host",                      host);
     436           36232 :     HDR__EQ_SET("If-Modified-Since",         if_modified_since);
     437           35568 :     HDR__EQ_SET("If-Range",                  if_range);
     438           34424 :     HDR__EQ_SET("If-Unmodified-Since",       if_unmodified_since);
     439           33928 :     HDR__EQ_SET("Range",                     range);
     440           27728 :     HDR__EQ_SET("X-Moz",                     x_moz);
     441                 : 
     442                 :     /* allow continuations over multiple headers... *sigh* */
     443           27696 :     HDR__EQ_MULTI_SET("Accept",              accept);
     444           22704 :     HDR__EQ_MULTI_SET("Accept-Charset",      accept_charset);
     445           22704 :     HDR__EQ_MULTI_SET("Accept-Encoding",     accept_encoding);
     446           16256 :     HDR__EQ_MULTI_SET("Accept-Language",     accept_language);
     447           14336 :     HDR__EQ_MULTI_SET("Connection",          connection);
     448            7240 :     HDR__EQ_MULTI_SET("If-Match",            if_match);
     449            5560 :     HDR__EQ_MULTI_SET("If-None-Match",       if_none_match);
     450                 : 
     451                 :     /* allow a 0 (zero) length content-length, some clients do send these */
     452            3440 :     else if (HDR__EQ("Content-Length"))
     453                 :     {
     454                 :       unsigned int num_flags = 10 | (VSTR_FLAG_PARSE_NUM_NO_BEG_PM |
     455             288 :                                      VSTR_FLAG_PARSE_NUM_OVERFLOW);
     456             288 :       unsigned int num_val = 0;
     457             288 :       size_t num_len = 0;
     458                 :       
     459             288 :       if (req->policy->use_hdrs_no_x2 && got_content_length)
     460               0 :         HTTPD_ERR_MSG_RET(req, 400, "Double header Content-Length", FALSE);
     461                 : 
     462             288 :       got_content_length = TRUE;
     463             288 :       http__hdr_fixup(data, &pos, &len, hdr_val_pos);
     464                 : 
     465             288 :       num_val = vstr_parse_uint(data, pos, len, num_flags, &num_len, NULL);
     466                 : 
     467             288 :       if (num_len != len)
     468               0 :         HTTPD_ERR_MSG_RET(req, 400, "Content-Length is not a number", FALSE);
     469             288 :       if (num_val)
     470             144 :         HTTPD_ERR_MSG_RET(req, 413, "Content-Length is not zero", FALSE);
     471                 :     }
     472            3152 :     HDR__EQ_IGNORE("Content-Type",     content_type);
     473            3008 :     HDR__EQ_IGNORE("Content-Language", content_lang);
     474                 :     
     475                 :     /* in theory ,,identity;foo=bar;baz="zoom",, is ok ... who cares */
     476            2864 :     else if (HDR__EQ("Transfer-Encoding"))
     477                 :     {
     478             720 :       if (req->policy->use_hdrs_no_x2 && got_transfer_encoding)
     479             144 :         HTTPD_ERR_MSG_RET(req, 400, "Double header Transfer-Encoding", FALSE);
     480             576 :       got_transfer_encoding = TRUE;
     481             576 :       http__hdr_fixup(data, &pos, &len, hdr_val_pos);
     482                 : 
     483             576 :       if (!VEQ(data, pos, len, "identity")) /* 3.6 says 501 */
     484             144 :         HTTPD_ERR_MSG_RET(req, 501, "Transfer-Encoding is not identity", FALSE);
     485                 :     }
     486                 :     else
     487                 :     { /* we can't x2 check ... as some might be multi headers */
     488            2144 :       size_t tmp = 0;
     489                 : 
     490                 :       /* all headers _must_ contain a ':' */
     491            2144 :       tmp = vstr_srch_chr_fwd(data, pos, len, ':');
     492            2144 :       if (!tmp)
     493             208 :         HTTPD_ERR_MSG_RET(req, 400, "Header does not end with : marker", FALSE);
     494                 : 
     495                 :       /* make sure unknown header is whitespace "valid" */
     496            1936 :       tmp = vstr_sc_posdiff(pos, tmp);
     497            1936 :       if (req->policy->use_hdrs_non_spc &&
     498                 :           (vstr_cspn_cstr_chrs_fwd(data, pos, tmp, HTTP_LWS) != tmp))
     499               0 :         HTTPD_ERR_MSG_RET(req, 400, "Header has spaces before : marker", FALSE);
     500                 :     }
     501                 :   }
     502                 : 
     503           37417 :   if (!req->policy->use_hdrs_err_411)
     504               0 :     return (TRUE);
     505                 :   
     506           37417 :   if (got_content_type      && !got_content_length)
     507             144 :     HTTPD_ERR_MSG_RET(req, 411, "Type but no Length", FALSE);
     508           37273 :   if (got_content_lang      && !got_content_length)
     509             144 :     HTTPD_ERR_MSG_RET(req, 411, "Language but no Length", FALSE);
     510           37129 :   if (got_transfer_encoding && !got_content_length)
     511             144 :     HTTPD_ERR_MSG_RET(req, 411, "Transfer-Encoding but no Length", FALSE);
     512                 : 
     513           36985 :   return (TRUE);
     514                 : }
     515                 : #undef HDR__EQ
     516                 : #undef HDR__SET
     517                 : #undef HDR__MUTLI_SET
     518                 : 
     519                 : #define HDR__CON_1_0_FIXUP(name, h)                                     \
     520                 :     else if (VIEQ(data, pos, tmp, name))                                \
     521                 :       do {                                                              \
     522                 :         req -> http_hdrs -> hdr_ ## h ->pos = 0;                        \
     523                 :         req -> http_hdrs -> hdr_ ## h ->len = 0;                        \
     524                 :       } while (FALSE)
     525                 : #define HDR__CON_1_0_MULTI_FIXUP(name, h)                               \
     526                 :     else if (VIEQ(data, pos, tmp, name))                                \
     527                 :       do {                                                              \
     528                 :         req -> http_hdrs -> multi -> hdr_ ## h ->pos = 0;               \
     529                 :         req -> http_hdrs -> multi -> hdr_ ## h ->len = 0;               \
     530                 :       } while (FALSE)
     531                 : void http_parse_connection(struct Con *con, struct Httpd_req_data *req)
     532           36649 : {
     533           36649 :   Vstr_base *data = req->http_hdrs->multi->comb;
     534           36649 :   size_t pos = 0;
     535           36649 :   size_t len = 0;
     536           36649 :   unsigned int num = 0;
     537                 :   
     538           36649 :   pos = req->http_hdrs->multi->hdr_connection->pos;
     539           36649 :   len = req->http_hdrs->multi->hdr_connection->len;
     540                 : 
     541           36649 :   if (HTTPD_VER_GE_1_1(req))
     542           31417 :     con->keep_alive = HTTP_1_1_KEEP_ALIVE;
     543                 : 
     544           36649 :   if (!len)
     545           29553 :     return;
     546                 : 
     547           11140 :   while (len)
     548                 :   {
     549                 :     size_t tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len,
     550            7592 :                                          HTTP_EOL HTTP_LWS ",");
     551                 : 
     552            7592 :     ++num;
     553            9716 :     if (HTTPD_VER_GE_1_1(req))
     554                 :     { /* this is all we have to do for HTTP/1.1 ... proxies understand it */
     555            4248 :       if (VIEQ(data, pos, tmp, "close"))
     556            3872 :         con->keep_alive = HTTP_NON_KEEP_ALIVE;
     557                 :     }
     558            3344 :     else if (VIEQ(data, pos, tmp, "keep-alive"))
     559                 :     {
     560            2624 :       if (req->policy->use_keep_alive_1_0)
     561            2624 :         con->keep_alive = HTTP_1_0_KEEP_ALIVE;
     562                 :     }
     563                 :     /* now fixup connection headers for HTTP/1.0 proxies */
     564             720 :     HDR__CON_1_0_FIXUP("User-Agent",          ua);
     565             720 :     HDR__CON_1_0_FIXUP("Referer",             referer);
     566             720 :     HDR__CON_1_0_FIXUP("Authorization",       authorization);
     567             720 :     HDR__CON_1_0_FIXUP("Expect",              expect);
     568             720 :     HDR__CON_1_0_FIXUP("Host",                host);
     569             720 :     HDR__CON_1_0_FIXUP("If-Modified-Since",   if_modified_since);
     570             720 :     HDR__CON_1_0_FIXUP("If-Range",            if_range);
     571             720 :     HDR__CON_1_0_FIXUP("If-Unmodified-Since", if_unmodified_since);
     572             720 :     HDR__CON_1_0_FIXUP("Range",               range);
     573             576 :     HDR__CON_1_0_FIXUP("X-Moz",               x_moz);
     574                 :     
     575             576 :     HDR__CON_1_0_MULTI_FIXUP("Accept",          accept);
     576             432 :     HDR__CON_1_0_MULTI_FIXUP("Accept-Charset",  accept_charset);
     577             432 :     HDR__CON_1_0_MULTI_FIXUP("Accept-Encoding", accept_encoding);
     578             400 :     HDR__CON_1_0_MULTI_FIXUP("Accept-Language", accept_language);
     579             256 :     HDR__CON_1_0_MULTI_FIXUP("If-Match",        if_match);
     580             256 :     HDR__CON_1_0_MULTI_FIXUP("If-None-Match",   if_none_match);
     581                 : 
     582                 :     /* skip to end, or after next ',' */
     583            7592 :     tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len, ",");    
     584            7592 :     len -= tmp; pos += tmp;
     585            7592 :     if (!len)
     586            7096 :       break;
     587                 :     
     588             248 :     assert(VPREFIX(data, pos, len, ","));
     589             496 :     len -= 1; pos += 1;
     590             496 :     HTTP_SKIP_LWS(data, pos, len);
     591                 : 
     592             496 :     if (req->policy->max_connection_nodes &&
     593                 :         (num >= req->policy->max_connection_nodes))
     594               0 :       return;
     595                 :   }
     596                 : }
     597                 : #undef HDR__CON_1_0_FIXUP
     598                 : #undef HDR__CON_1_0_MULTI_FIXUP
     599                 :                                   
     600                 : /* parse >= 1.0 things like, version and headers */
     601                 : int http_parse_1_x(struct Con *con, struct Httpd_req_data *req)
     602           39241 : {
     603           19628 :   ASSERT(!req->ver_0_9);
     604                 :   
     605           39241 :   if (!http_parse_version(con, req))
     606            1152 :     return (FALSE);
     607                 :   
     608           38089 :   if (!http_parse_hdrs(con, req))
     609            1104 :     return (FALSE);
     610                 : 
     611           36985 :   if (req->policy->max_requests &&
     612                 :       (req->policy->max_requests <= con->evnt->acct.req_got) &&
     613                 :       HTTPD_VER_GE_1_1(req))
     614               0 :     return (TRUE);
     615                 : 
     616           36985 :   if (!req->policy->use_keep_alive && HTTPD_VER_GE_1_1(req))
     617             336 :     return (TRUE);
     618                 : 
     619           36649 :   http_parse_connection(con, req);
     620                 :   
     621           36649 :   if (req->policy->max_requests &&
     622                 :       (req->policy->max_requests <= con->evnt->acct.req_got))
     623               0 :     con->keep_alive = HTTP_NON_KEEP_ALIVE;
     624                 : 
     625           36649 :   return (TRUE);
     626                 : }
     627                 : 
     628                 : /* NOTE: allow "1  .  000" or just allow "1.000" ... LWS is craptastic, why
     629                 :  * not go all the way ? */
     630                 : #define HTTP__PARSE_CHK_RET_OK() do {                   \
     631                 :       HTTP_SKIP_LWS(data, pos, len);                    \
     632                 :                                                         \
     633                 :       if (!len ||                                       \
     634                 :           VPREFIX(data, pos, len, ",") ||               \
     635                 :           (allow_more && VPREFIX(data, pos, len, ";"))) \
     636                 :       {                                                 \
     637                 :         *passed_pos = pos;                              \
     638                 :         *passed_len = len;                              \
     639                 :                                                         \
     640                 :         ASSERT(*val <= 1000);                           \
     641                 :                                                         \
     642                 :         return (TRUE);                                  \
     643                 :       }                                                 \
     644                 :     } while (FALSE)
     645                 : 
     646                 : /* What is the quality parameter, value between 0 and 1000 inclusive.
     647                 :  * returns TRUE on success, FALSE on failure. */
     648                 : static int http_parse_quality(Vstr_base *data,
     649                 :                               size_t *passed_pos, size_t *passed_len,
     650                 :                               int allow_more, unsigned int *val)
     651           39664 : {
     652           39664 :   size_t pos = *passed_pos;
     653           39664 :   size_t len = *passed_len;
     654           39664 :   int lead_zero = FALSE;
     655           39664 :   size_t num_len = 0;
     656           39664 :   unsigned int parse_flags = VSTR_FLAG02(PARSE_NUM, NO_BEG_PM, NO_NEGATIVE);  
     657                 :   
     658           19832 :   ASSERT(val);
     659                 :   
     660           39664 :   *val = 1000;
     661                 :   
     662           39664 :   HTTP_SKIP_LWS(data, pos, len);
     663                 : 
     664           39664 :   *passed_pos = pos;
     665           39664 :   *passed_len = len;
     666                 :   
     667           39664 :   if (!len || VPREFIX(data, pos, len, ","))
     668           23760 :     return (TRUE);
     669                 : 
     670           20240 :   if (VPREFIX(data, pos, len, ";q=0.")) /* opt */
     671                 :   {
     672            4336 :     len -= strlen(";q=0."); pos += strlen(";q=0.");
     673            4336 :     lead_zero = TRUE;
     674            4336 :     *val = 0;
     675                 :   }
     676           11568 :   else if (VPREFIX(data, pos, len, ";"))
     677                 :   {
     678           11280 :     len -= 1; pos += 1;
     679           11280 :     HTTP_SKIP_LWS(data, pos, len);
     680                 :     
     681           11280 :     if (!VPREFIX(data, pos, len, "q"))
     682             704 :       return (!!allow_more);
     683                 :     
     684           10576 :     len -= 1; pos += 1;
     685           10576 :     HTTP_SKIP_LWS(data, pos, len);
     686                 :     
     687           10576 :     if (!VPREFIX(data, pos, len, "="))
     688             144 :       return (!!allow_more);
     689                 :     
     690           10432 :     len -= 1; pos += 1;
     691           10432 :     HTTP_SKIP_LWS(data, pos, len);
     692                 : 
     693                 :     /* if it's 0[.00?0?] TRUE, 0[.\d\d?\d?] or 1[.00?0?] is FALSE */
     694           10432 :     if (!(lead_zero = VPREFIX(data, pos, len, "0")) &&
     695                 :         !VPREFIX(data, pos, len, "1"))
     696             432 :       return (FALSE);
     697           10000 :     *val = (!lead_zero) * 1000;
     698                 :     
     699           10000 :     len -= 1; pos += 1;
     700                 :     
     701           10000 :     HTTP__PARSE_CHK_RET_OK();
     702                 : 
     703            5008 :     if (!VPREFIX(data, pos, len, "."))
     704             144 :       return (FALSE);
     705                 :     
     706            4864 :     len -= 1; pos += 1;
     707                 :   }
     708                 : 
     709                 :   
     710            9488 :   HTTP_SKIP_LWS(data, pos, len);
     711                 :   
     712            9488 :   *val += vstr_parse_uint(data, pos, len, 10 | parse_flags, &num_len, NULL);
     713            9488 :   if (!num_len || (num_len > 3) || (*val > 1000))
     714             864 :     return (FALSE);
     715            8624 :   if (!lead_zero)
     716             768 :     ASSERT(*val == 1000);
     717                 :   else
     718                 :   {
     719            7088 :     if (num_len < 3) *val *= 10;
     720            7088 :     if (num_len < 2) *val *= 10;
     721                 :   }
     722            8624 :   len -= num_len; pos += num_len;
     723                 :   
     724            8624 :   HTTP__PARSE_CHK_RET_OK();
     725                 :   
     726               0 :   return (FALSE);
     727                 : }
     728                 : #undef HTTP__PARSE_CHK_RET_OK
     729                 : 
     730                 : /* return the length of a quoted string (must be >= 2), or 0 on syntax error */
     731                 : static size_t http__len_quoted_string(const Vstr_base *data,
     732                 :                                       size_t pos, size_t len)
     733            5840 : {
     734            5840 :   size_t orig_pos = pos;
     735                 :   
     736            5840 :   if (!VPREFIX(data, pos, len, "\""))
     737             192 :     return (0);
     738                 :   
     739            5648 :   len -= 1; pos += 1;
     740                 : 
     741                 :   while (TRUE)
     742                 :   {
     743            5856 :     size_t tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len, "\"\\");
     744                 :     
     745            5856 :     len -= tmp; pos += tmp;
     746            5856 :     if (!len)
     747             144 :       return (0);
     748                 :     
     749            5712 :     if (vstr_export_chr(data, pos) == '"')
     750            5360 :       return (vstr_sc_posdiff(orig_pos, pos));
     751                 :     
     752             176 :     ASSERT(vstr_export_chr(data, pos) == '\\');
     753             352 :     if (len < 3) /* must be at least <\X"> */
     754             144 :       return (0);
     755             208 :     len -= 2; pos += 2;
     756             208 :   }
     757                 : 
     758                 :   assert_ret(FALSE, 0);
     759                 : }
     760                 : 
     761                 : /* skip a quoted string, or fail on syntax error */
     762                 : static int http__skip_quoted_string(const Vstr_base *data,
     763                 :                                     size_t *pos, size_t *len)
     764             528 : {
     765             528 :   size_t qlen = http__len_quoted_string(data, *pos, *len);
     766                 : 
     767             264 :   assert(VPREFIX(data, *pos, *len, "\""));
     768                 :   
     769             528 :   if (!qlen)
     770             288 :     return (FALSE);
     771                 :   
     772             240 :   *len -= qlen; *pos += qlen;
     773                 : 
     774             240 :   HTTP_SKIP_LWS(data, *pos, *len);
     775             240 :   return (TRUE);
     776                 : }
     777                 : 
     778                 : /* match non-week entity tags in both strings, return true if any match
     779                 :  * only allow non-weak entity tags if allow_weak = FALSE */
     780                 : int httpd_match_etags(struct Httpd_req_data *req,
     781                 :                       const Vstr_base *hdr, size_t hpos, size_t hlen,
     782                 :                       const Vstr_base *vs1, size_t epos, size_t elen,
     783                 :                       int allow_weak)
     784            2624 : {
     785            2624 :   int need_comma = FALSE;
     786                 : 
     787            1312 :   ASSERT(hdr);
     788                 :   
     789            2624 :   if (!vs1)
     790             640 :     return (FALSE);
     791                 :   
     792            5280 :   while (hlen)
     793                 :   {
     794            3520 :     int weak = FALSE;
     795            3520 :     size_t htlen = 0;
     796                 : 
     797            3520 :     if (vstr_export_chr(hdr, hpos) == ',')
     798                 :     {
     799             768 :       hlen -= 1; hpos += 1;
     800             768 :       HTTP_SKIP_LWS(hdr, hpos, hlen);
     801             768 :       need_comma = FALSE;
     802             768 :       continue;
     803                 :     }
     804            2752 :     else if (need_comma)
     805               0 :       return (FALSE);
     806                 :     
     807            2752 :     if (VPREFIX(hdr, hpos, hlen, "W/"))
     808                 :     {
     809             512 :       weak = TRUE;
     810             512 :       hlen -= CLEN("W/"); hpos += CLEN("W/");
     811                 :     }
     812            2752 :     if (!(htlen = http__len_quoted_string(hdr, hpos, hlen)))
     813             192 :       return (FALSE);
     814                 : 
     815            2560 :     if (allow_weak || !weak)
     816                 :     {
     817            2560 :       size_t orig_epos = epos;
     818            2560 :       size_t orig_elen = elen;
     819            2560 :       unsigned int num = 0;
     820                 :       
     821            6656 :       while (elen)
     822                 :       {
     823            2560 :         size_t etlen = 0;
     824                 : 
     825            2560 :         if (vstr_export_chr(vs1, epos) == ',')
     826                 :         {
     827               0 :           elen -= 1; epos += 1;
     828               0 :           HTTP_SKIP_LWS(vs1, epos, elen);
     829               0 :           need_comma = FALSE;
     830               0 :           continue;
     831                 :         }
     832            2560 :         else if (need_comma)
     833               0 :           return (FALSE);
     834                 : 
     835            2560 :         ++num;
     836                 :         
     837            4544 :         if (!VPREFIX(vs1, epos, elen, "W/"))
     838            1984 :           weak = FALSE;
     839                 :         else
     840                 :         {
     841             576 :           weak = TRUE;
     842             576 :           elen -= CLEN("W/"); epos += CLEN("W/");
     843                 :         }
     844            2560 :         if (!(etlen = http__len_quoted_string(vs1, epos, elen)))
     845               0 :           return (FALSE);
     846                 :         
     847            2560 :         if ((allow_weak || !weak) &&
     848                 :             vstr_cmp_eq(hdr, hpos, htlen, vs1, epos, etlen))
     849            1024 :           return (TRUE);
     850                 :         
     851            1536 :         elen -= etlen; epos += etlen;
     852            1536 :         HTTP_SKIP_LWS(vs1, epos, elen);
     853            1536 :         need_comma = TRUE;
     854                 :         
     855            1536 :         if (req->policy->max_etag_nodes && (num >= req->policy->max_etag_nodes))
     856               0 :           return (FALSE);
     857                 :       }
     858                 : 
     859            1536 :       epos = orig_epos;
     860            1536 :       elen = orig_elen;
     861                 :     }
     862                 : 
     863            1536 :     hlen -= htlen; hpos += htlen;
     864            1536 :     HTTP_SKIP_LWS(hdr, hpos, hlen);
     865            1536 :     need_comma = TRUE;
     866                 :   }
     867                 : 
     868             768 :   return (FALSE);
     869                 : }
     870                 : 
     871                 : /* skip, or fail on syntax error */
     872                 : static int http__skip_parameters(Vstr_base *data, size_t *pos, size_t *len)
     873            7536 : {
     874           16272 :   while (*len && (vstr_export_chr(data, *pos) != ','))
     875                 :   { /* skip parameters */
     876            1920 :     size_t tmp = 0;
     877                 :     
     878            1920 :     if (vstr_export_chr(data, *pos) != ';')
     879               0 :       return (FALSE); /* syntax error */
     880                 :     
     881            1920 :     *len -= 1; *pos += 1;
     882            1920 :     HTTP_SKIP_LWS(data, *pos, *len);
     883            1920 :     tmp = vstr_cspn_cstr_chrs_fwd(data, *pos, *len, ";,=");
     884            1920 :     *len -= tmp; *pos += tmp;
     885            1920 :     if (!*len)
     886             288 :       break;
     887                 :     
     888            1632 :     switch (vstr_export_chr(data, *pos))
     889                 :     {
     890             144 :       case ';': break;
     891               0 :       case ',': break;
     892                 :         
     893                 :       case '=': /* skip parameter value */
     894            1344 :         *len -= 1; *pos += 1;
     895            1344 :         HTTP_SKIP_LWS(data, *pos, *len);
     896            1344 :         if (!*len)
     897             144 :           return (FALSE); /* syntax error */
     898            1200 :         if (vstr_export_chr(data, *pos) == '"')
     899                 :         {
     900             528 :           if (!http__skip_quoted_string(data, pos, len))
     901             288 :             return (FALSE); /* syntax error */
     902                 :         }
     903                 :         else
     904                 :         {
     905             672 :           tmp = vstr_cspn_cstr_chrs_fwd(data, *pos, *len, ";,");
     906             672 :           *len -= tmp; *pos += tmp;
     907                 :         }
     908                 :         break;
     909                 :     }
     910                 :   }
     911                 : 
     912            7104 :   return (TRUE);
     913                 : }
     914                 : 
     915                 : /* returns quality of the passed content-type in the "Accept:" header,
     916                 :  * if it isn't there or we get a syntax error we return 1001 for not avilable */
     917                 : unsigned int http_parse_accept(Httpd_req_data *req,
     918                 :                                const Vstr_base *ct_vs1,
     919                 :                                size_t ct_pos, size_t ct_len)
     920           29376 : {
     921           29376 :   Vstr_base *data = req->http_hdrs->multi->comb;
     922           29376 :   size_t pos = 0;
     923           29376 :   size_t len = 0;
     924           29376 :   unsigned int num = 0;
     925           29376 :   unsigned int quality = 1001;
     926           29376 :   unsigned int dummy   = 1001;
     927           29376 :   int done_sub_type = FALSE;
     928           29376 :   size_t ct_sub_len = 0;
     929                 :   
     930           29376 :   pos = req->http_hdrs->multi->hdr_accept->pos;
     931           29376 :   len = req->http_hdrs->multi->hdr_accept->len;
     932                 :   
     933           29376 :   if (!len) /* no accept == accept all */
     934           21344 :     return (1000);
     935                 : 
     936            4016 :   ASSERT(ct_vs1);
     937                 :     
     938            8032 :   if (!(ct_sub_len = vstr_srch_chr_fwd(ct_vs1, ct_pos, ct_len, '/')))
     939                 :   { /* it's too weird, blank it */
     940               0 :     if (ct_vs1 == req->content_type_vs1)
     941               0 :       req->content_type_vs1 = NULL;
     942               0 :     return (1);
     943                 :   }
     944            8032 :   ct_sub_len = vstr_sc_posdiff(ct_pos, ct_sub_len);
     945                 :   
     946            8032 :   while (len)
     947                 :   {
     948                 :     size_t tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len,
     949           10336 :                                          HTTP_EOL HTTP_LWS ";,");
     950                 : 
     951           10336 :     ++num;
     952                 :     
     953                 :     if (0) { }
     954           10336 :     else if (vstr_cmp_eq(data, pos, tmp, ct_vs1, ct_pos, ct_len))
     955                 :     { /* full match */
     956            2368 :       len -= tmp; pos += tmp;
     957            2368 :       if (!http_parse_quality(data, &pos, &len, TRUE, &quality))
     958               0 :         return (1);
     959            2368 :       return (quality);
     960                 :     }
     961            9776 :     else if ((tmp == (ct_sub_len + 1)) &&
     962                 :              vstr_cmp_eq(data, pos, ct_sub_len, ct_vs1, ct_pos, ct_sub_len) &&
     963                 :              (vstr_export_chr(data, vstr_sc_poslast(pos, tmp)) == '*'))
     964                 :     { /* sub match */
     965            1952 :       len -= tmp; pos += tmp;
     966            1952 :       if (!http_parse_quality(data, &pos, &len, TRUE, &quality))
     967             144 :         return (1);
     968            1808 :       done_sub_type = TRUE;
     969                 :     }
     970            6616 :     else if (!done_sub_type && VEQ(data, pos, tmp, "*/*"))
     971                 :     {
     972            1344 :       len -= tmp; pos += tmp;
     973            1344 :       if (!http_parse_quality(data, &pos, &len, TRUE, &quality))
     974             144 :         return (1);
     975                 :     }
     976                 :     else
     977                 :     {
     978            4672 :       len -= tmp; pos += tmp;
     979            4672 :       if (!http_parse_quality(data, &pos, &len, TRUE, &dummy))
     980             144 :         return (1);
     981                 :     }
     982                 : 
     983            7536 :     if (!http__skip_parameters(data, &pos, &len))
     984             432 :       return (1);
     985            7104 :     if (!len)
     986            4768 :       break;
     987                 :     
     988            1168 :     assert(VPREFIX(data, pos, len, ","));
     989            2336 :     len -= 1; pos += 1;
     990            2336 :     HTTP_SKIP_LWS(data, pos, len);
     991                 :     
     992            2336 :     if (req->policy->max_A_nodes && (num >= req->policy->max_A_nodes))
     993               0 :       return (1);
     994                 :   }
     995                 : 
     996            2400 :   ASSERT(quality <= 1001);
     997            4800 :   if (quality == 1001)
     998            2704 :     return (0);
     999                 :   
    1000            2096 :   return (quality);
    1001                 : }
    1002                 : 
    1003                 : static int http__cmp_lang_eq(const Vstr_base *s1, size_t p1, size_t l1,
    1004                 :                              const Vstr_base *s2, size_t p2, size_t l2)
    1005           15520 : {
    1006           15520 :   if (l1 == l2)
    1007           10848 :     return (FALSE);
    1008                 :   
    1009            4672 :   if (l1 > l2)
    1010            1504 :     return ((vstr_export_chr(s1, p1 + l2) == '-') &&
    1011                 :             vstr_cmp_case_eq(s1, p1, l2, s2, p2, l2));
    1012                 : 
    1013            3168 :   return ((vstr_export_chr(s2, p2 + l1) == '-') &&
    1014                 :           vstr_cmp_case_eq(s1, p1, l1, s2, p2, l1));
    1015                 : }
    1016                 : 
    1017                 : unsigned int http_parse_accept_language(Httpd_req_data *req,
    1018                 :                                         const Vstr_base *ct_vs1,
    1019                 :                                         size_t ct_pos, size_t ct_len)
    1020           21760 : {
    1021           21760 :   Vstr_base *data = req->http_hdrs->multi->comb;
    1022           21760 :   size_t pos = 0;
    1023           21760 :   size_t len = 0;
    1024           21760 :   unsigned int num = 0;
    1025           21760 :   unsigned int quality = 1001;
    1026           21760 :   unsigned int dummy   = 1001;
    1027           21760 :   size_t done_sub_type = 0;
    1028                 :   
    1029           21760 :   pos = req->http_hdrs->multi->hdr_accept_language->pos;
    1030           21760 :   len = req->http_hdrs->multi->hdr_accept_language->len;
    1031                 :   
    1032           21760 :   if (!len) /* no accept-language == accept all */
    1033            7936 :     return (1000);
    1034                 : 
    1035            6912 :   ASSERT(ct_vs1);
    1036           26080 :   while (len)
    1037                 :   {
    1038                 :     size_t tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len,
    1039           19104 :                                          HTTP_EOL HTTP_LWS ";,");
    1040                 : 
    1041           19104 :     ++num;
    1042                 :     
    1043                 :     if (0) { }
    1044           19104 :     else if (vstr_cmp_case_eq(data, pos, tmp, ct_vs1, ct_pos, ct_len))
    1045                 :     { /* full match */
    1046            2976 :       len -= tmp; pos += tmp;
    1047            2976 :       if (!http_parse_quality(data, &pos, &len, FALSE, &quality))
    1048               0 :         return (1);
    1049            2976 :       return (quality);
    1050                 :     }
    1051           16928 :     else if ((tmp >= done_sub_type) &&
    1052                 :              http__cmp_lang_eq(data, pos, tmp, ct_vs1, ct_pos, ct_len))
    1053                 :     { /* sub match - can be x-y-A;q=0, x-y-B, x-y
    1054                 :          rfc2616#14.4
    1055                 :          The language quality factor assigned to a language-tag by the
    1056                 :          Accept-Language field is the quality value of the longest
    1057                 :          language-range in the field that matches the language-tag.
    1058                 :       */
    1059             800 :       unsigned int sub_type_qual = 0;
    1060                 :       
    1061             800 :       len -= tmp; pos += tmp;
    1062             800 :       if (!http_parse_quality(data, &pos, &len, FALSE, &sub_type_qual))
    1063               0 :         return (1);
    1064                 :       
    1065             400 :       ASSERT(sub_type_qual <= 1000);
    1066             800 :       if ((tmp > done_sub_type) || (sub_type_qual > quality))
    1067             800 :         quality = sub_type_qual;
    1068                 :       
    1069             800 :       done_sub_type = tmp;
    1070                 :     }
    1071           16096 :     else if (!done_sub_type && VEQ(data, pos, tmp, "*"))
    1072                 :     {
    1073            1536 :       len -= tmp; pos += tmp;
    1074            1536 :       if (!http_parse_quality(data, &pos, &len, FALSE, &quality))
    1075               0 :         return (1);
    1076                 :     }
    1077                 :     else
    1078                 :     {
    1079           13792 :       len -= tmp; pos += tmp;
    1080           13792 :       if (!http_parse_quality(data, &pos, &len, FALSE, &dummy))
    1081               0 :         return (1);
    1082                 :     }
    1083                 :     
    1084           16128 :     if (!len)
    1085           10784 :       break;
    1086                 :     
    1087            2672 :     assert(VPREFIX(data, pos, len, ","));
    1088            5344 :     len -= 1; pos += 1;
    1089            5344 :     HTTP_SKIP_LWS(data, pos, len);
    1090                 :     
    1091            5344 :     if (req->policy->max_AL_nodes && (num >= req->policy->max_AL_nodes))
    1092               0 :       return (1);
    1093                 :   }
    1094                 : 
    1095            5424 :   ASSERT(quality <= 1001);
    1096           10848 :   if (quality == 1001)
    1097            9152 :     return (0);
    1098                 :   
    1099            1696 :   return (quality);
    1100                 : }
    1101                 : 
    1102                 : int http_parse_accept_encoding(struct Httpd_req_data *req, int force)
    1103           27513 : {
    1104           27513 :   Vstr_base *data = req->http_hdrs->multi->comb;
    1105           27513 :   size_t pos = 0;
    1106           27513 :   size_t len = 0;
    1107           27513 :   unsigned int num = 0;
    1108           27513 :   unsigned int star_val     = 1001;
    1109           27513 :   unsigned int dummy_val    = 1001;
    1110                 : 
    1111           27513 :   pos = req->http_hdrs->multi->hdr_accept_encoding->pos;
    1112           27513 :   len = req->http_hdrs->multi->hdr_accept_encoding->len;
    1113                 : 
    1114           27513 :   if (!force && !req->policy->use_err_406 && !req->allow_accept_encoding)
    1115             336 :     return (FALSE);
    1116                 : 
    1117           27177 :   if (!force)
    1118           24201 :     req->vary_ae = TRUE;
    1119                 :   
    1120           27177 :   if (req->parsed_content_encoding)
    1121                 :   {
    1122            2048 :     if (!req->allow_accept_encoding)
    1123               0 :       return (FALSE);
    1124                 : 
    1125            2048 :     return (!!req->content_enc_gzip || !!req->content_enc_bzip2);
    1126                 :   }
    1127                 :   
    1128           25129 :   req->parsed_content_encoding = TRUE;
    1129                 :   
    1130           25129 :   if (!len)
    1131           19081 :     goto parse_err;
    1132                 :   
    1133            6048 :   req->content_encoding_xgzip = FALSE;
    1134            6048 :   req->content_enc_identity   = 1001;
    1135            6048 :   req->content_enc_gzip       = 1001;
    1136            6048 :   req->content_enc_bzip2      = 1001;
    1137                 :   
    1138           16560 :   while (len)
    1139                 :   {
    1140                 :     size_t tmp = vstr_cspn_cstr_chrs_fwd(data, pos, len,
    1141           10224 :                                          HTTP_EOL HTTP_LWS ";,");
    1142                 : 
    1143           10224 :     ++num;
    1144                 :     
    1145                 :     if (0) { }
    1146           10224 :     else if (VEQ(data, pos, tmp, "identity"))
    1147                 :     {
    1148             576 :       len -= tmp; pos += tmp;
    1149             576 :       if (!http_parse_quality(data, &pos, &len, FALSE,
    1150                 :                               &req->content_enc_identity))
    1151             144 :         goto parse_err;
    1152                 :     }
    1153            9648 :     else if (VEQ(data, pos, tmp, "gzip"))
    1154                 :     {
    1155            3680 :       len -= tmp; pos += tmp;
    1156            3680 :       req->content_encoding_xgzip = FALSE;
    1157            3680 :       if (!http_parse_quality(data, &pos, &len, FALSE, &req->content_enc_gzip))
    1158            1008 :         goto parse_err;
    1159                 :     }
    1160            5968 :     else if (VEQ(data, pos, tmp, "bzip2"))
    1161                 :     {
    1162             848 :       len -= tmp; pos += tmp;
    1163             848 :       if (!http_parse_quality(data, &pos, &len, FALSE, &req->content_enc_bzip2))
    1164               0 :         goto parse_err;
    1165                 :     }
    1166            5120 :     else if (VEQ(data, pos, tmp, "x-gzip"))
    1167                 :     {
    1168             144 :       len -= tmp; pos += tmp;
    1169             144 :       req->content_encoding_xgzip = TRUE;
    1170             144 :       if (!http_parse_quality(data, &pos, &len, FALSE, &req->content_enc_gzip))
    1171               0 :         goto parse_err; /* ignore quality on x-gzip - just parse for errors */
    1172             144 :       req->content_enc_gzip = 1000;
    1173                 :     }
    1174            4976 :     else if (VEQ(data, pos, tmp, "*"))
    1175                 :     { /* "*;q=0,gzip" means TRUE ... and "*;q=1.0,gzip;q=0" means FALSE */
    1176             576 :       len -= tmp; pos += tmp;
    1177             576 :       if (!http_parse_quality(data, &pos, &len, FALSE, &star_val))
    1178             144 :         goto parse_err;
    1179                 :     }
    1180                 :     else
    1181                 :     {
    1182            4400 :       len -= tmp; pos += tmp;
    1183            4400 :       if (!http_parse_quality(data, &pos, &len, FALSE, &dummy_val))
    1184               0 :         goto parse_err;
    1185                 :     }
    1186                 :     
    1187            8928 :     if (!len)
    1188            4464 :       break;
    1189            2232 :     assert(VPREFIX(data, pos, len, ","));
    1190            4464 :     len -= 1; pos += 1;
    1191            4464 :     HTTP_SKIP_LWS(data, pos, len);
    1192                 : 
    1193            4464 :     if (req->policy->max_AE_nodes && (num >= req->policy->max_AE_nodes))
    1194               0 :       goto parse_err;
    1195                 :   }
    1196                 :   
    1197            4752 :   if (req->content_enc_gzip     == 1001) req->content_enc_gzip     = star_val;
    1198            4752 :   if (req->content_enc_bzip2    == 1001) req->content_enc_bzip2    = star_val;
    1199            4752 :   if (req->content_enc_identity == 1001) req->content_enc_identity = star_val;
    1200                 : 
    1201            4752 :   if (req->content_enc_gzip     == 1001) req->content_enc_gzip     = 0;
    1202            4752 :   if (req->content_enc_bzip2    == 1001) req->content_enc_bzip2    = 0;
    1203            4752 :   if (req->content_enc_identity == 1001) req->content_enc_identity = 1;
    1204                 :   
    1205            4752 :   if (!req->allow_accept_encoding)
    1206              32 :     return (FALSE);
    1207                 : 
    1208            4720 :   if ((req->content_enc_identity > req->content_enc_gzip) &&
    1209                 :       (req->content_enc_identity > req->content_enc_bzip2))
    1210            1952 :     goto parse_err; /* this is insane, but legal */
    1211                 : 
    1212            2768 :   req->content_encoding_identity = !!req->content_enc_identity;
    1213            2768 :   req->content_encoding_gzip     = !!req->content_enc_gzip;
    1214            2768 :   req->content_encoding_bzip2    = !!req->content_enc_bzip2;
    1215                 :     
    1216            2768 :   return (!!req->content_enc_gzip || !!req->content_enc_bzip2);
    1217                 :   
    1218           22329 :  parse_err:
    1219           22329 :   req->content_enc_identity = 1;
    1220           22329 :   req->content_enc_gzip     = 0;
    1221           22329 :   req->content_enc_bzip2    = 0;
    1222                 : 
    1223           22329 :   req->content_encoding_identity = !!req->content_enc_identity;
    1224           22329 :   req->content_encoding_gzip     = !!req->content_enc_gzip;
    1225           22329 :   req->content_encoding_bzip2    = !!req->content_enc_bzip2;
    1226           22329 :   req->content_encoding_xgzip    = FALSE;
    1227                 :     
    1228           22329 :   return (FALSE);
    1229                 : }
    1230                 : 
    1231                 : /* try to use gzip content-encoding on entity */
    1232                 : static int http__try_encoded_content(struct Con *con, Httpd_req_data *req,
    1233                 :                                      const struct stat64 *req_f_stat,
    1234                 :                                      off64_t *req_f_stat_st_size,
    1235                 :                                      Vstr_base *fname,
    1236                 :                                      const char *zip_ext, size_t zip_len)
    1237            3200 : {
    1238            3200 :   const char *fname_cstr = NULL;
    1239            3200 :   int fd = -1;
    1240            3200 :   int ret = FALSE;
    1241            3200 :   int open_flags = O_NONBLOCK;
    1242                 : 
    1243            1600 :   ASSERT(con->fs && !con->fs_num && !con->fs_off);
    1244            1600 :   ASSERT(con->fs->len == (uintmax_t)req_f_stat->st_size);
    1245                 : 
    1246            3200 :   if (req_f_stat->st_size < HTTPD_CONF_ZIP_LIMIT_MIN) /* minor opt. */
    1247             128 :     return (ret);
    1248                 :   
    1249            3072 :   if (req->policy->use_noatime)
    1250               0 :     open_flags |= O_NOATIME;
    1251                 :     
    1252            3072 :   vstr_add_cstr_ptr(fname, fname->len, zip_ext);
    1253            3072 :   fname_cstr = vstr_export_cstr_ptr(fname, 1, fname->len);
    1254                 :   
    1255            3072 :   if (fname->conf->malloc_bad)
    1256               0 :     vlg_warn(vlg, "Failed to export cstr for '%s'\n", zip_ext);
    1257            3072 :   else if ((fd = io__open(fname_cstr, open_flags)) == -1)
    1258                 :   { /* no encoded version */ }
    1259                 :   else
    1260                 :   {
    1261                 :     struct stat64 f_stat[1];
    1262                 :     
    1263            2480 :     if (fstat64(fd, f_stat) == -1)
    1264               0 :       vlg_warn(vlg, "fstat: %m\n");
    1265            2480 :     else if ((req->policy->use_public_only && !(f_stat->st_mode & S_IROTH)) ||
    1266                 :              (S_ISDIR(f_stat->st_mode)) || (!S_ISREG(f_stat->st_mode)) ||
    1267                 :              (req_f_stat->st_mtime >  f_stat->st_mtime) ||
    1268                 :              !f_stat->st_size || /* zero sized compressed files aren't valid */
    1269                 :              (*req_f_stat_st_size  <= f_stat->st_size))
    1270                 :     { /* ignore the encoded version */ }
    1271                 :     else
    1272                 :     {
    1273                 :       /* swap, close the old fd (later) and use the new */
    1274            2336 :       SWAP_TYPE(con->fs->fd, fd, int);
    1275                 :       
    1276                 :       /* _only_ copy the new size over, mtime etc. is from the original file */
    1277            2336 :       con->fs->len = *req_f_stat_st_size = f_stat->st_size;
    1278            2336 :       req->encoded_mtime = f_stat->st_mtime;
    1279            2336 :       ret = TRUE;
    1280                 :     }
    1281            2480 :     close(fd);
    1282                 :   }
    1283                 : 
    1284            3072 :   if (!ret)
    1285             736 :     vstr_sc_reduce(fname, 1, fname->len, zip_len);
    1286                 : 
    1287            3072 :   return (ret);
    1288                 : }  
    1289                 : 
    1290                 : void httpd_parse_sc_try_fd_encoding(struct Con *con, Httpd_req_data *req,
    1291                 :                                     const struct stat64 *fs,
    1292                 :                                     off64_t *req_f_stat_st_size,
    1293                 :                                     Vstr_base *fname)
    1294           24537 : { /* Might normally add "!req->head_op && ..." but
    1295                 :    * http://www.w3.org/TR/chips/#gl6 says that's bad */
    1296           24537 :   if (http_parse_accept_encoding(req, FALSE))
    1297                 :   {
    1298            2768 :     if (req->content_enc_bzip2 >= req->content_enc_gzip)
    1299                 :     { /* try bzip2, then gzip */
    1300            1136 :       if (req->content_encoding_bzip2 &&
    1301                 :           !http__try_encoded_content(con, req, fs, req_f_stat_st_size,
    1302                 :                                      fname, ".bz2", CLEN(".bz2")))
    1303             640 :         req->content_encoding_bzip2 = FALSE;
    1304                 : 
    1305            1136 :       if (req->content_encoding_bzip2) req->content_encoding_gzip = FALSE;
    1306                 : 
    1307            1136 :       if (req->content_encoding_gzip &&
    1308                 :           !http__try_encoded_content(con, req, fs, req_f_stat_st_size,
    1309                 :                                      fname, ".gz", CLEN(".gz")))
    1310               0 :         req->content_encoding_gzip = FALSE;
    1311                 :     }
    1312                 :     else
    1313                 :     { /* try gzip, then bzip2 */
    1314            1632 :       if (req->content_encoding_gzip &&
    1315                 :           !http__try_encoded_content(con, req, fs, req_f_stat_st_size,
    1316                 :                                      fname, ".gz", CLEN(".gz")))
    1317             224 :         req->content_encoding_gzip = FALSE;
    1318                 : 
    1319            1632 :       if (req->content_encoding_gzip) req->content_encoding_bzip2 = FALSE;
    1320                 : 
    1321            1632 :       if (req->content_encoding_bzip2 &&
    1322                 :           !http__try_encoded_content(con, req, fs, req_f_stat_st_size,
    1323                 :                                      fname, ".bz2", CLEN(".bz2")))
    1324               0 :         req->content_encoding_bzip2 = FALSE;
    1325                 :     }
    1326                 : 
    1327                 :     /* both can't be on ... one, the other or neither */
    1328            1384 :     ASSERT(!req->content_encoding_bzip2 || !req->content_encoding_gzip);
    1329                 :   }
    1330           24537 : }
    1331                 : 
    1332                 : static int httpd__file_sect_add(struct Con *con, Httpd_req_data *req,
    1333                 :                                 uintmax_t range_beg, uintmax_t range_end)
    1334            4408 : {
    1335            4408 :   struct File_sect *fs = NULL;
    1336                 :     
    1337            2204 :   ASSERT(con->fs && (con->fs_sz >= 1));
    1338                 :   
    1339            4408 :   if (req->policy->max_range_nodes <= con->fs_num)
    1340             128 :     return (FALSE); /* done at start so we don't allocate anything needlessly */
    1341                 :   
    1342            4280 :   if (!con->fs_num)
    1343                 :   {
    1344            1852 :     ASSERT((con->fs == con->fs_store) || (con->fs_sz > 1));
    1345            1852 :     ASSERT(!con->use_mpbr);
    1346                 : 
    1347            1852 :     goto file_sect_add;
    1348                 :   }
    1349                 : 
    1350             576 :   con->use_mpbr = TRUE;
    1351                 :   
    1352             576 :   if (con->fs == con->fs_store)
    1353                 :   {
    1354              32 :     ASSERT(con->fs_num == 1);
    1355                 : 
    1356              64 :     if (!(con->mpbr_ct = vstr_make_base(con->evnt->io_w->conf)))
    1357               0 :       return (FALSE);
    1358                 :     
    1359              64 :     if (!(fs = MK(sizeof(struct File_sect) * 16)))
    1360               0 :       return (FALSE);
    1361              64 :     con->fs    = fs;
    1362              64 :     con->fs_sz = 16;
    1363                 :     
    1364              64 :     con->fs->fd  = con->fs_store->fd;
    1365              64 :     con->fs->off = con->fs_store->off;
    1366              64 :     con->fs->len = con->fs_store->len;
    1367              64 :     ++fs;
    1368                 :   }
    1369             512 :   else if (con->fs_num >= con->fs_sz)
    1370                 :   {
    1371               0 :     unsigned int num = (con->fs_sz << 1) + 1;
    1372                 :     
    1373               0 :     ASSERT(con->fs_num == con->fs_sz);
    1374                 :   
    1375               0 :     if (!MV(con->fs, fs, sizeof(struct File_sect) * num))
    1376               0 :       return (FALSE);
    1377               0 :     con->fs_sz = num;
    1378                 :   }
    1379                 : 
    1380            4280 :  file_sect_add:
    1381            4280 :   fs = con->fs + con->fs_num++;
    1382                 : 
    1383            4280 :   fs->fd  = con->fs->fd; /* copy to each one */
    1384            4280 :   fs->off = range_beg;
    1385            4280 :   fs->len = (range_end - range_beg) + 1;
    1386                 : 
    1387            4280 :   if ((req->fs_len + fs->len) < req->fs_len)
    1388               0 :     return (FALSE);
    1389                 : 
    1390            4280 :   req->fs_len += fs->len;
    1391                 :   
    1392            4280 :   return (TRUE);
    1393                 : }
    1394                 : 
    1395                 : /* Allow...
    1396                 :    bytes=NUM-NUM
    1397                 :    bytes=-NUM
    1398                 :    bytes=NUM-
    1399                 :    ...and due to LWS, http crapola parsing, even...
    1400                 :    bytes = , , NUM - NUM , ,
    1401                 :    ...allowing ability to disable multiple ranges at once, due to
    1402                 :    multipart/byteranges being too much crack, I think this is stds. compliant.
    1403                 :  */
    1404                 : int http_parse_range(struct Con *con, Httpd_req_data *req)
    1405            5288 : {
    1406            5288 :   Vstr_base *data     = con->evnt->io_r;
    1407            5288 :   Vstr_sect_node *h_r = req->http_hdrs->hdr_range;
    1408            5288 :   size_t pos = h_r->pos;
    1409            5288 :   size_t len = h_r->len;
    1410            5288 :   uintmax_t fsize = req->f_stat_st_size;
    1411                 :   unsigned int num_flags = 10 | (VSTR_FLAG_PARSE_NUM_NO_BEG_PM |
    1412            5288 :                                  VSTR_FLAG_PARSE_NUM_OVERFLOW);
    1413            5288 :   size_t num_len = 0;
    1414                 :   
    1415            5288 :   if (!VPREFIX(data, pos, len, "bytes"))
    1416             288 :     return (0);
    1417            5000 :   len -= CLEN("bytes"); pos += CLEN("bytes");
    1418                 : 
    1419            5000 :   HTTP_SKIP_LWS(data, pos, len);
    1420                 :   
    1421            5000 :   if (!VPREFIX(data, pos, len, "="))
    1422             144 :     return (0);
    1423            4856 :   len -= CLEN("="); pos += CLEN("=");
    1424                 : 
    1425            4856 :   http_parse_skip_blanks(data, &pos, &len);
    1426                 :   
    1427            4856 :   while (len)
    1428                 :   {
    1429            5704 :     uintmax_t range_beg = 0;
    1430            5704 :     uintmax_t range_end = 0;
    1431                 :     
    1432            6744 :     if (VPREFIX(data, pos, len, "-"))
    1433                 :     { /* num bytes at end */
    1434            1472 :       uintmax_t tmp = 0;
    1435                 : 
    1436            1472 :       len -= CLEN("-"); pos += CLEN("-");
    1437            1472 :       HTTP_SKIP_LWS(data, pos, len);
    1438                 : 
    1439            1472 :       tmp = vstr_parse_uintmax(data, pos, len, num_flags, &num_len, NULL);
    1440            1472 :       len -= num_len; pos += num_len;
    1441            1472 :       if (!num_len)
    1442             144 :         return (0);
    1443                 : 
    1444            1328 :       if (!tmp)
    1445             144 :         return (416);
    1446                 :     
    1447            1184 :       if (tmp >= fsize)
    1448             144 :         return (0);
    1449                 :     
    1450            1040 :       range_beg = fsize - tmp;
    1451            1040 :       range_end = fsize - 1;
    1452                 :     }
    1453                 :     else
    1454                 :     { /* offset - [end] */
    1455            4232 :       range_beg = vstr_parse_uintmax(data, pos, len, num_flags, &num_len, NULL);
    1456            4232 :       len -= num_len; pos += num_len;
    1457            4232 :       HTTP_SKIP_LWS(data, pos, len);
    1458                 :     
    1459            4232 :       if (!VPREFIX(data, pos, len, "-"))
    1460             288 :         return (0);
    1461            3944 :       len -= CLEN("-"); pos += CLEN("-");
    1462            3944 :       HTTP_SKIP_LWS(data, pos, len);
    1463                 : 
    1464            4968 :       if (!len || VPREFIX(data, pos, len, ","))
    1465            1024 :         range_end = fsize - 1;
    1466                 :       else
    1467                 :       {
    1468            2920 :         range_end = vstr_parse_uintmax(data, pos, len, num_flags, &num_len, 0);
    1469            2920 :         len -= num_len; pos += num_len;
    1470            2920 :         if (!num_len)
    1471             144 :           return (0);
    1472                 :       
    1473            2776 :         if (range_end >= fsize)
    1474             352 :           range_end = fsize - 1;
    1475                 :       }
    1476                 :     
    1477            3800 :       if ((range_beg >= fsize) || (range_beg > range_end))
    1478             288 :         return (416);
    1479                 :     
    1480            3512 :       if ((range_beg == 0) && 
    1481                 :           (range_end == (fsize - 1)))
    1482             144 :         return (0);
    1483                 :     }
    1484                 :   
    1485            4408 :     http_parse_skip_blanks(data, &pos, &len);
    1486                 : 
    1487            4408 :     if (!httpd__file_sect_add(con, req, range_beg, range_end))
    1488             128 :       return (0); /* after all that, ignore if there is more than one range */
    1489                 :   }
    1490                 : 
    1491            3432 :   return (200);
    1492                 : }
    1493                 : 
    1494                 : /* because we only parse for a combined CRLF, and some proxies/clients parse for
    1495                 :  * either ... make sure we don't have embedded singles which could cause
    1496                 :  * response splitting */
    1497                 : static int http__chk_single_crlf(Vstr_base *data, size_t pos, size_t len)
    1498           61842 : {
    1499           61842 :   if (vstr_srch_chr_fwd(data, pos, len, '\r'))
    1500               0 :     return ('\r');
    1501                 :   
    1502           61842 :   if (vstr_srch_chr_fwd(data, pos, len, '\n'))
    1503               0 :     return ('\n');
    1504                 : 
    1505           61842 :   return (0);
    1506                 : }
    1507                 : 
    1508                 : unsigned short httpd_parse_host_port(Vstr_base *s1, size_t pos, size_t len)
    1509            3456 : {
    1510                 :   unsigned int num_flags = 10 | (VSTR_FLAG_PARSE_NUM_NO_BEG_PM |
    1511            3456 :                                  VSTR_FLAG_PARSE_NUM_OVERFLOW);
    1512            3456 :   size_t num_len = 0;
    1513            3456 :   unsigned short port = 0;
    1514                 :   
    1515            1728 :   ASSERT(vstr_export_chr(s1, pos) == ':');
    1516                 :   
    1517            3456 :   len -= 1; pos += 1; /* skip the ':' */
    1518            3456 :   port = vstr_parse_ushort(s1, pos, len, num_flags, &num_len, NULL);
    1519                 :   
    1520            3456 :   if (!port || (num_len != len))
    1521               0 :     return (0);
    1522                 : 
    1523            3456 :   return (port);
    1524                 : }
    1525                 : 
    1526                 : /* convert a http://abcd/foo into /foo with host=abcd ...
    1527                 :  * also do sanity checking on the URI and host for valid characters */
    1528                 : int http_parse_host(struct Con *con, struct Httpd_req_data *req)
    1529           37657 : {
    1530           37657 :   Vstr_base *data = con->evnt->io_r;
    1531           37657 :   size_t op_pos = req->path_pos;
    1532           37657 :   size_t op_len = req->path_len;
    1533                 :   
    1534                 :   /* HTTP/1.1 requires a host -- allow blank hostnames */
    1535           37657 :   if (req->ver_1_1 && !req->http_hdrs->hdr_host->pos)
    1536            1056 :     HTTPD_ERR_MSG_RET(req, 400, "Hostname is required in 1.1", FALSE);
    1537                 :   
    1538                 :   /* check for absolute URIs */
    1539           36601 :   if (VIPREFIX(data, op_pos, op_len, "http://"))
    1540                 :   { /* ok, be forward compatible */
    1541           11329 :     size_t tmp = CLEN("http://");
    1542                 :     
    1543           11329 :     op_len -= tmp; op_pos += tmp;
    1544           11329 :     tmp = vstr_srch_chr_fwd(data, op_pos, op_len, '/');
    1545           11329 :     if (!tmp)
    1546                 :     {
    1547            1416 :       HTTP__HDR_SET(req, host, op_pos, op_len);
    1548            1416 :       op_len = 1;
    1549            1416 :       --op_pos;
    1550                 :     }
    1551                 :     else
    1552                 :     { /* found end of host ... */
    1553            9913 :       size_t host_len = tmp - op_pos;
    1554                 :       
    1555            9913 :       HTTP__HDR_SET(req, host, op_pos, host_len);
    1556            9913 :       op_len -= host_len; op_pos += host_len;
    1557                 :     }
    1558            5672 :     assert(VPREFIX(data, op_pos, op_len, "/"));
    1559                 :   }
    1560                 : 
    1561           36601 :   if (req->ver_1_x && !req->http_hdrs->hdr_host->pos)
    1562               0 :     HTTPD_ERR_MSG_RET(req, 400,
    1563                 :                       "Hostname or Absolute URL is required in 1.x", FALSE);
    1564                 :   
    1565           36601 :   if (req->http_hdrs->hdr_host->len)
    1566                 :   { /* check host looks valid ... header must exist, but can be empty */
    1567           25529 :     size_t pos = req->http_hdrs->hdr_host->pos;
    1568           25529 :     size_t len = req->http_hdrs->hdr_host->len;
    1569           25529 :     size_t tmp = 0;
    1570                 : 
    1571                 :     /* leaving out most checks for ".." or invalid chars in hostnames etc.
    1572                 :        as the default filename checks should catch them.
    1573                 :        Note: "Host: ." is also caught by default due to the check for /./
    1574                 :        Note: There are also optional checks in http__valid_hostname()
    1575                 :      */
    1576                 : 
    1577                 :     /*  Check for Host header with extra / ...
    1578                 :      * Ie. only allow a single directory name.
    1579                 :      *  We could just leave this (it's not a security check, /../ is checked
    1580                 :      * for at filepath time), but I feel like being anal and this way there
    1581                 :      * aren't multiple urls to a single path. */
    1582           25529 :     if (vstr_srch_chr_fwd(data, pos, len, '/'))
    1583             144 :       HTTPD_ERR_MSG_RET(req, 400, "Hostname contains /", FALSE);
    1584                 : 
    1585           25385 :     switch (http__chk_single_crlf(data, pos, len))
    1586                 :     {
    1587                 :       case '\r':
    1588               0 :         HTTPD_ERR_MSG_RET(req, 400, "Hostname contains \\r", FALSE);
    1589                 :       case '\n':
    1590               0 :         HTTPD_ERR_MSG_RET(req, 400, "Hostname contains \\n", FALSE);
    1591                 :       case 0:
    1592               0 :         ASSERT_NO_SWITCH_DEF();
    1593                 :     }
    1594                 : 
    1595           25385 :     if ((tmp = vstr_srch_chr_fwd(data, pos, len, ':')))
    1596                 :     { /* NOTE: not sure if we have to 400 if the port doesn't match
    1597                 :        * or if it's an "invalid" port number (Ie. == 0 || > 65535) */
    1598            2016 :       len -= tmp - pos; pos = tmp;
    1599                 : 
    1600            2016 :       if (!VEQ(data, pos, len, ":"))
    1601            1872 :         if (!(req->http_host_port = httpd_parse_host_port(data, pos, len)))
    1602               0 :           HTTPD_ERR_MSG_RET(req, 400, "Port is not a valid number", FALSE);
    1603                 :         
    1604            2016 :       req->http_hdrs->hdr_host->len -= len;
    1605                 :     }
    1606                 :   }
    1607                 : 
    1608           36457 :   switch (http__chk_single_crlf(data, op_pos, op_len))
    1609                 :   {
    1610                 :     case '\r':
    1611               0 :       HTTPD_ERR_MSG_RET(req, 400, "Path contains \\r", FALSE);
    1612                 :     case '\n':
    1613               0 :       HTTPD_ERR_MSG_RET(req, 400, "Path contains \\n", FALSE);
    1614                 :     case 0:
    1615               0 :       ASSERT_NO_SWITCH_DEF();
    1616                 :   }
    1617                 :   
    1618                 :   /* uri#fragment ... craptastic clients pass this and assume it is ignored */
    1619           36457 :   if (req->policy->remove_url_frag)
    1620           34953 :     op_len = vstr_cspn_cstr_chrs_fwd(data, op_pos, op_len, "#");
    1621                 :   /* uri?foo ... This is "ok" to pass, however if you move dynamic
    1622                 :    * resources to static ones you need to do this */
    1623           36457 :   if (req->policy->remove_url_query)
    1624            1504 :     op_len = vstr_cspn_cstr_chrs_fwd(data, op_pos, op_len, "?");
    1625                 :   
    1626           36457 :   req->path_pos = op_pos;
    1627           36457 :   req->path_len = op_len;
    1628                 :   
    1629           36457 :   return (TRUE);
    1630                 : }
    1631                 : 
    1632                 : void http_parse_skip_blanks(Vstr_base *data,
    1633                 :                             size_t *passed_pos, size_t *passed_len)
    1634            9264 : {
    1635            9264 :   size_t pos = *passed_pos;
    1636            9264 :   size_t len = *passed_len;
    1637                 :   
    1638            9264 :   HTTP_SKIP_LWS(data, pos, len);
    1639           18960 :   while (VPREFIX(data, pos, len, ",")) /* http crack */
    1640                 :   {
    1641            5064 :     len -= CLEN(","); pos += CLEN(",");
    1642            5064 :     HTTP_SKIP_LWS(data, pos, len);
    1643                 :   }
    1644                 : 
    1645            9264 :   *passed_pos = pos;
    1646            9264 :   *passed_len = len;
    1647            9264 : }
    1648                 : 
    1649                 : static int http_parse_wait_io_r(struct Con *con)
    1650           25385 : {
    1651           25385 :   if (con->evnt->io_r_shutdown)
    1652             159 :     return (!!con->evnt->io_w->len);
    1653                 : 
    1654                 :   /* so we aren't acting under the req policy anymore */
    1655           25226 :   httpd_policy_change_con(con, con->policy);
    1656                 :   
    1657           25226 :   evnt_wait_cntl_add(con->evnt, POLLIN);
    1658           25226 :   evnt_fd_set_cork(con->evnt, FALSE);
    1659                 :   
    1660           25226 :   return (TRUE);
    1661                 : }
    1662                 : 
    1663                 : static int httpd_serv__parse_no_req(struct Con *con, struct Httpd_req_data *req)
    1664           19153 : {
    1665           19153 :   if (req->policy->max_header_sz &&
    1666                 :       (con->evnt->io_r->len > req->policy->max_header_sz))
    1667                 :   {
    1668              62 :     evnt_got_pkt(con->evnt);
    1669              62 :     HTTPD_ERR_MSG_RET(req, 400, "Too big", http_fin_err_req(con, req));
    1670                 :   }
    1671                 :   
    1672           19091 :   http_req_free(req);
    1673                 :   
    1674           19091 :   return (http_parse_wait_io_r(con));
    1675                 : }
    1676                 : 
    1677                 : /* http spec says ignore leading LWS ... *sigh* */
    1678                 : static int http__parse_req_all(struct Con *con, struct Httpd_req_data *req,
    1679                 :                                const char *eol, int *ern)
    1680           70386 : {
    1681           70386 :   Vstr_base *data = con->evnt->io_r;
    1682           70386 :   size_t pos = 0;
    1683                 :   
    1684           34522 :   ASSERT(eol && ern);
    1685                 :   
    1686           70386 :   *ern = FALSE;
    1687                 : 
    1688                 :   /* should use vstr_del(data, 1, vstr_spn_cstr_buf_fwd(..., HTTP_EOL)); */
    1689                 :   /* NOTE: eol might be HTTP_END_OF_REQUEST, so need to do this first *sigh* */
    1690         4943540 :   while (VPREFIX(data, 1, data->len, HTTP_EOL))
    1691         4802768 :     vstr_del(data, 1, CLEN(HTTP_EOL));
    1692                 : 
    1693           34522 :   ASSERT(!req->len);
    1694           70386 :   if (!(pos = vstr_srch_cstr_buf_fwd(data, 1, data->len, eol)))
    1695           19153 :       goto no_req;
    1696                 : 
    1697           51233 :   req->len = pos + CLEN(eol) - 1; /* add rest of EOL */
    1698                 : 
    1699           51233 :   return (TRUE);
    1700                 : 
    1701           19153 :  no_req:
    1702           19153 :   *ern = httpd_serv__parse_no_req(con, req);
    1703           19153 :   return (FALSE);
    1704                 : }
    1705                 : 
    1706                 : int http_parse_req(struct Con *con)
    1707           65648 : {
    1708           65648 :   Vstr_base *data = con->evnt->io_r;
    1709           65648 :   struct Httpd_req_data *req = NULL;
    1710           65648 :   int ern_req_all = FALSE;
    1711                 : 
    1712           31911 :   ASSERT(con->fs && !con->fs_num);
    1713                 : 
    1714           65648 :   if (!data->len)
    1715            6294 :     return (http_parse_wait_io_r(con));
    1716                 :   
    1717           59354 :   if (!(req = http_req_make(con)))
    1718               0 :     return (FALSE);
    1719                 : 
    1720           59354 :   if (!req->policy->allow_http_0_9)
    1721               0 :     con->parsed_method_ver_1_0 = TRUE;
    1722                 : 
    1723           59354 :   if (con->parsed_method_ver_1_0)
    1724                 :   { /* wait for all the headers */
    1725           39947 :     if (!http__parse_req_all(con, req, HTTP_END_OF_REQUEST, &ern_req_all))
    1726           10178 :       return (ern_req_all);
    1727                 :   }
    1728                 :   else
    1729                 :   {
    1730           19407 :     if (!http__parse_req_all(con, req, HTTP_EOL,            &ern_req_all))
    1731            7415 :       return (ern_req_all);
    1732                 :   }
    1733                 : 
    1734           41761 :   con->keep_alive = HTTP_NON_KEEP_ALIVE;
    1735           41761 :   http_req_split_method(con, req);
    1736           41761 :   if (req->sects->malloc_bad)
    1737                 :   {
    1738               0 :     evnt_got_pkt(con->evnt);
    1739               0 :     VLG_WARNNOMEM_RET(http_fin_errmem_req(con, req), (vlg, "split: %m\n"));
    1740                 :   }
    1741           41761 :   else if ((req->sects->num < 2) ||
    1742                 :            (!req->policy->allow_http_0_9 && (req->sects->num < 3)))
    1743                 :   {
    1744             288 :     evnt_got_pkt(con->evnt);
    1745             288 :     HTTPD_ERR_MSG_RET(req, 400, "Bad request line", http_fin_err_req(con, req));
    1746                 :   }
    1747                 :   else
    1748                 :   {
    1749           41473 :     size_t op_pos = 0;
    1750           41473 :     size_t op_len = 0;
    1751                 : 
    1752           41473 :     if (req->ver_0_9)
    1753             672 :       vlg_dbg1(vlg, "Method(0.9):"
    1754                 :                " $<http-esc.vstr.sect:%p%p%u> $<http-esc.vstr.sect:%p%p%u>\n",
    1755                 :                con->evnt->io_r, req->sects, 1U,
    1756                 :                con->evnt->io_r, req->sects, 2U);
    1757                 :     else
    1758                 :     { /* need to get all headers */
    1759           40801 :       if (!con->parsed_method_ver_1_0)
    1760                 :       { /* req line isn't 0.9 ... so must continue to be */
    1761           11032 :         con->parsed_method_ver_1_0 = TRUE;
    1762           11032 :         req->len = 0;
    1763           11032 :         if (!http__parse_req_all(con, req, HTTP_END_OF_REQUEST, &ern_req_all))
    1764            1560 :           return (ern_req_all);
    1765                 :       }
    1766                 :       
    1767           39241 :       vlg_dbg1(vlg, "Method(1.x):"
    1768                 :                " $<http-esc.vstr.sect:%p%p%u> $<http-esc.vstr.sect:%p%p%u>"
    1769                 :                " $<http-esc.vstr.sect:%p%p%u>\n", data, req->sects, 1U,
    1770                 :                data, req->sects, 2U, data, req->sects, 3U);
    1771                 : 
    1772                 :       /* put this up here so errors don't get DATA */
    1773           39241 :       op_pos        = VSTR_SECTS_NUM(req->sects, 1)->pos;
    1774           39241 :       op_len        = VSTR_SECTS_NUM(req->sects, 1)->len;
    1775           39241 :       if (VEQ(data, op_pos, op_len, "HEAD"))
    1776           16216 :         req->head_op = TRUE;
    1777                 :     
    1778           39241 :       http_req_split_hdrs(con, req);
    1779                 :     }
    1780           39913 :     evnt_got_pkt(con->evnt);
    1781                 : 
    1782                 :     if (HTTP_CONF_SAFE_PRINT_REQ)
    1783           19949 :       vlg_dbg3(vlg, "REQ:\n$<vstr.hexdump:%p%zu%zu>",
    1784                 :                data, (size_t)1, req->len);
    1785                 :     else
    1786           19964 :       vlg_dbg3(vlg, "REQ:\n$<vstr:%p%zu%zu>", data, (size_t)1, req->len);
    1787                 :     
    1788           19964 :     assert(((req->sects->num >= 3) && !req->ver_0_9) || (req->sects->num == 2));
    1789                 :     
    1790           39913 :     op_pos        = VSTR_SECTS_NUM(req->sects, 1)->pos;
    1791           39913 :     op_len        = VSTR_SECTS_NUM(req->sects, 1)->len;
    1792           39913 :     req->path_pos = VSTR_SECTS_NUM(req->sects, 2)->pos;
    1793           39913 :     req->path_len = VSTR_SECTS_NUM(req->sects, 2)->len;
    1794                 : 
    1795           39913 :     if (!req->ver_0_9 && !http_parse_1_x(con, req))
    1796                 :     {
    1797            2256 :       if (req->error_code == 500)
    1798               0 :         return (http_fin_errmem_req(con, req));
    1799            2256 :       return (http_fin_err_req(con, req));
    1800                 :     }
    1801                 :     
    1802           37657 :     if (!http_parse_host(con, req))
    1803            1200 :       return (http_fin_err_req(con, req));
    1804                 : 
    1805                 :     if (0) { }
    1806           36457 :     else if (VEQ(data, op_pos, op_len, "GET"))
    1807                 :     {
    1808           18489 :       if (!VPREFIX(data, req->path_pos, req->path_len, "/"))
    1809             144 :         HTTPD_ERR_MSG_RET(req, 400, "Bad path", http_fin_err_req(con, req));
    1810                 :       
    1811           18345 :       if (!http_req_make_path(con, req))
    1812            1200 :         return (http_fin_err_req(con, req));
    1813                 : 
    1814           17145 :       return (http_req_op_get(con, req));
    1815                 :     }
    1816           17968 :     else if (req->ver_0_9) /* 400 or 501? - apache does 400 */
    1817             336 :       HTTPD_ERR_RET(req, 501, http_fin_err_req(con, req));      
    1818           17632 :     else if (VEQ(data, op_pos, op_len, "HEAD"))
    1819                 :     {
    1820            8036 :       ASSERT(req->head_op == TRUE); /* above, so errors are chopped */
    1821           16072 :       req->head_op = TRUE;
    1822                 :       
    1823           16072 :       if (!VPREFIX(data, req->path_pos, req->path_len, "/"))
    1824             192 :         HTTPD_ERR_MSG_RET(req, 400, "Bad path", http_fin_err_req(con, req));
    1825                 :       
    1826           15880 :       if (!http_req_make_path(con, req))
    1827             992 :         return (http_fin_err_req(con, req));
    1828                 : 
    1829           14888 :       return (http_req_op_get(con, req));
    1830                 :     }
    1831            1560 :     else if (VEQ(data, op_pos, op_len, "OPTIONS"))
    1832                 :     {
    1833             816 :       if (!VPREFIX(data, req->path_pos, req->path_len, "/") &&
    1834                 :           !VEQ(data, req->path_pos, req->path_len, "*"))
    1835             144 :         HTTPD_ERR_MSG_RET(req, 400, "Bad path", http_fin_err_req(con, req));
    1836                 : 
    1837                 :       /* Speed hack: Don't even call make_path if it's "OPTIONS * ..."
    1838                 :        * and we don't need to check the Host header */
    1839             672 :       if (req->policy->use_vhosts_name &&
    1840                 :           req->policy->use_host_chk && req->policy->use_host_err_400 &&
    1841                 :           !VEQ(data, req->path_pos, req->path_len, "*") &&
    1842                 :           !http_req_make_path(con, req))
    1843             144 :         return (http_fin_err_req(con, req));
    1844                 : 
    1845             528 :       return (http_req_op_opts(con, req));
    1846                 :     }
    1847             744 :     else if (req->policy->allow_trace_op && VEQ(data, op_pos, op_len, "TRACE"))
    1848             312 :       return (http_req_op_trace(con, req));
    1849             432 :     else if (VEQ(data, op_pos, op_len, "TRACE") ||
    1850                 :              VEQ(data, op_pos, op_len, "POST") ||
    1851                 :              VEQ(data, op_pos, op_len, "PUT") ||
    1852                 :              VEQ(data, op_pos, op_len, "DELETE") ||
    1853                 :              VEQ(data, op_pos, op_len, "CONNECT") ||
    1854                 :              FALSE) /* we know about these ... but don't allow them */
    1855             144 :       HTTPD_ERR_RET(req, 405, http_fin_err_req(con, req));
    1856                 :     else
    1857             288 :       HTTPD_ERR_RET(req, 501, http_fin_err_req(con, req));
    1858                 :   }
    1859                 :   ASSERT_NOT_REACHED();
    1860                 : }
    1861                 : 

Generated by: LTP GCOV extension version 1.4