LCOV - code coverage report
Current view: top level - src/wps - httpread.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1443382998 Lines: 374 377 99.2 %
Date: 2015-09-27 Functions: 14 14 100.0 %

          Line data    Source code
       1             : /*
       2             :  * httpread - Manage reading file(s) from HTTP/TCP socket
       3             :  * Author: Ted Merrill
       4             :  * Copyright 2008 Atheros Communications
       5             :  *
       6             :  * This software may be distributed under the terms of the BSD license.
       7             :  * See README for more details.
       8             :  *
       9             :  * The files are buffered via internal callbacks from eloop, then presented to
      10             :  * an application callback routine when completely read into memory. May also
      11             :  * be used if no file is expected but just to get the header, including HTTP
      12             :  * replies (e.g. HTTP/1.1 200 OK etc.).
      13             :  *
      14             :  * This does not attempt to be an optimally efficient implementation, but does
      15             :  * attempt to be of reasonably small size and memory consumption; assuming that
      16             :  * only small files are to be read. A maximum file size is provided by
      17             :  * application and enforced.
      18             :  *
      19             :  * It is assumed that the application does not expect any of the following:
      20             :  * -- transfer encoding other than chunked
      21             :  * -- trailer fields
      22             :  * It is assumed that, even if the other side requested that the connection be
      23             :  * kept open, that we will close it (thus HTTP messages sent by application
      24             :  * should have the connection closed field); this is allowed by HTTP/1.1 and
      25             :  * simplifies things for us.
      26             :  *
      27             :  * Other limitations:
      28             :  * -- HTTP header may not exceed a hard-coded size.
      29             :  *
      30             :  * Notes:
      31             :  * This code would be massively simpler without some of the new features of
      32             :  * HTTP/1.1, especially chunked data.
      33             :  */
      34             : 
      35             : #include "includes.h"
      36             : 
      37             : #include "common.h"
      38             : #include "eloop.h"
      39             : #include "httpread.h"
      40             : 
      41             : 
      42             : /* Tunable parameters */
      43             : #define HTTPREAD_READBUF_SIZE 1024      /* read in chunks of this size */
      44             : #define HTTPREAD_HEADER_MAX_SIZE 4096   /* max allowed for headers */
      45             : #define HTTPREAD_BODYBUF_DELTA 4096     /* increase allocation by this */
      46             : 
      47             : 
      48             : /* control instance -- actual definition (opaque to application)
      49             :  */
      50             : struct httpread {
      51             :         /* information from creation */
      52             :         int sd;         /* descriptor of TCP socket to read from */
      53             :         void (*cb)(struct httpread *handle, void *cookie,
      54             :                     enum httpread_event e);  /* call on event */
      55             :         void *cookie;   /* pass to callback */
      56             :         int max_bytes;          /* maximum file size else abort it */
      57             :         int timeout_seconds;            /* 0 or total duration timeout period */
      58             : 
      59             :         /* dynamically used information follows */
      60             : 
      61             :         int got_hdr;            /* nonzero when header is finalized */
      62             :         char hdr[HTTPREAD_HEADER_MAX_SIZE+1];   /* headers stored here */
      63             :         int hdr_nbytes;
      64             : 
      65             :         enum httpread_hdr_type hdr_type;
      66             :         int version;            /* 1 if we've seen 1.1 */
      67             :         int reply_code;         /* for type REPLY, e.g. 200 for HTTP/1.1 200 OK */
      68             :         int got_content_length; /* true if we know content length for sure */
      69             :         int content_length;     /* body length,  iff got_content_length */
      70             :         int chunked;            /* nonzero for chunked data */
      71             :         char *uri;
      72             : 
      73             :         int got_body;           /* nonzero when body is finalized */
      74             :         char *body;
      75             :         int body_nbytes;
      76             :         int body_alloc_nbytes;  /* amount allocated */
      77             : 
      78             :         int got_file;           /* here when we are done */
      79             : 
      80             :         /* The following apply if data is chunked: */
      81             :         int in_chunk_data;      /* 0=in/at header, 1=in the data or tail*/
      82             :         int chunk_start;        /* offset in body of chunk hdr or data */
      83             :         int chunk_size;         /* data of chunk (not hdr or ending CRLF)*/
      84             :         int in_trailer;         /* in header fields after data (chunked only)*/
      85             :         enum trailer_state {
      86             :                 trailer_line_begin = 0,
      87             :                 trailer_empty_cr,       /* empty line + CR */
      88             :                 trailer_nonempty,
      89             :                 trailer_nonempty_cr,
      90             :         } trailer_state;
      91             : };
      92             : 
      93             : 
      94             : /* Check words for equality, where words consist of graphical characters
      95             :  * delimited by whitespace
      96             :  * Returns nonzero if "equal" doing case insensitive comparison.
      97             :  */
      98       24219 : static int word_eq(char *s1, char *s2)
      99             : {
     100             :         int c1;
     101             :         int c2;
     102       24219 :         int end1 = 0;
     103       24219 :         int end2 = 0;
     104             :         for (;;) {
     105       59558 :                 c1 = *s1++;
     106       59558 :                 c2 = *s2++;
     107       59558 :                 if (isalpha(c1) && isupper(c1))
     108       30403 :                         c1 = tolower(c1);
     109       59558 :                 if (isalpha(c2) && isupper(c2))
     110       54031 :                         c2 = tolower(c2);
     111       59558 :                 end1 = !isgraph(c1);
     112       59558 :                 end2 = !isgraph(c2);
     113       59558 :                 if (end1 || end2 || c1 != c2)
     114             :                         break;
     115       35339 :         }
     116       24219 :         return end1 && end2;  /* reached end of both words? */
     117             : }
     118             : 
     119             : 
     120             : static void httpread_timeout_handler(void *eloop_data, void *user_ctx);
     121             : 
     122             : /* httpread_destroy -- if h is non-NULL, clean up
     123             :  * This must eventually be called by the application following
     124             :  * call of the application's callback and may be called
     125             :  * earlier if desired.
     126             :  */
     127        1554 : void httpread_destroy(struct httpread *h)
     128             : {
     129        1554 :         wpa_printf(MSG_DEBUG, "httpread_destroy(%p)", h);
     130        1554 :         if (!h)
     131        1691 :                 return;
     132             : 
     133        1417 :         eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
     134        1417 :         eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
     135        1417 :         os_free(h->body);
     136        1417 :         os_free(h->uri);
     137        1417 :         os_memset(h, 0, sizeof(*h));  /* aid debugging */
     138        1417 :         h->sd = -1;     /* aid debugging */
     139        1417 :         os_free(h);
     140             : }
     141             : 
     142             : 
     143             : /* httpread_timeout_handler -- called on excessive total duration
     144             :  */
     145           1 : static void httpread_timeout_handler(void *eloop_data, void *user_ctx)
     146             : {
     147           1 :         struct httpread *h = user_ctx;
     148           1 :         wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h);
     149           1 :         (*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT);
     150           1 : }
     151             : 
     152             : 
     153             : /* Analyze options only so far as is needed to correctly obtain the file.
     154             :  * The application can look at the raw header to find other options.
     155             :  */
     156        7956 : static int httpread_hdr_option_analyze(
     157             :         struct httpread *h,
     158             :         char *hbp       /* pointer to current line in header buffer */
     159             :         )
     160             : {
     161        7956 :         if (word_eq(hbp, "CONTENT-LENGTH:")) {
     162       22049 :                 while (isgraph(*hbp))
     163       19455 :                         hbp++;
     164        3891 :                 while (*hbp == ' ' || *hbp == '\t')
     165        1297 :                         hbp++;
     166        1297 :                 if (!isdigit(*hbp))
     167           2 :                         return -1;
     168        1295 :                 h->content_length = atol(hbp);
     169        1295 :                 if (h->content_length < 0 || h->content_length > h->max_bytes) {
     170           2 :                         wpa_printf(MSG_DEBUG,
     171             :                                    "httpread: Unacceptable Content-Length %d",
     172             :                                    h->content_length);
     173           2 :                         return -1;
     174             :                 }
     175        1293 :                 h->got_content_length = 1;
     176        1293 :                 return 0;
     177             :         }
     178       13318 :         if (word_eq(hbp, "TRANSFER_ENCODING:") ||
     179        6659 :             word_eq(hbp, "TRANSFER-ENCODING:")) {
     180         220 :                 while (isgraph(*hbp))
     181         198 :                         hbp++;
     182          33 :                 while (*hbp == ' ' || *hbp == '\t')
     183          11 :                         hbp++;
     184             :                 /* There should (?) be no encodings of interest
     185             :                  * other than chunked...
     186             :                  */
     187          11 :                 if (word_eq(hbp, "CHUNKED")) {
     188          10 :                         h->chunked = 1;
     189          10 :                         h->in_chunk_data = 0;
     190             :                         /* ignore possible ;<parameters> */
     191             :                 }
     192          11 :                 return 0;
     193             :         }
     194             :         /* skip anything we don't know, which is a lot */
     195        6648 :         return 0;
     196             : }
     197             : 
     198             : 
     199        1406 : static int httpread_hdr_analyze(struct httpread *h)
     200             : {
     201        1406 :         char *hbp = h->hdr;      /* pointer into h->hdr */
     202        1406 :         int standard_first_line = 1;
     203             : 
     204             :         /* First line is special */
     205        1406 :         h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN;
     206        1406 :         if (!isgraph(*hbp))
     207           1 :                 goto bad;
     208        1405 :         if (os_strncmp(hbp, "HTTP/", 5) == 0) {
     209         913 :                 h->hdr_type = HTTPREAD_HDR_TYPE_REPLY;
     210         913 :                 standard_first_line = 0;
     211         913 :                 hbp += 5;
     212        1825 :                 if (hbp[0] == '1' && hbp[1] == '.' &&
     213        1824 :                     isdigit(hbp[2]) && hbp[2] != '0')
     214         912 :                         h->version = 1;
     215        4562 :                 while (isgraph(*hbp))
     216        2736 :                         hbp++;
     217        2738 :                 while (*hbp == ' ' || *hbp == '\t')
     218         912 :                         hbp++;
     219         913 :                 if (!isdigit(*hbp))
     220           1 :                         goto bad;
     221         912 :                 h->reply_code = atol(hbp);
     222         492 :         } else if (word_eq(hbp, "GET"))
     223          48 :                 h->hdr_type = HTTPREAD_HDR_TYPE_GET;
     224         444 :         else if (word_eq(hbp, "HEAD"))
     225           7 :                 h->hdr_type = HTTPREAD_HDR_TYPE_HEAD;
     226         437 :         else if (word_eq(hbp, "POST"))
     227         182 :                 h->hdr_type = HTTPREAD_HDR_TYPE_POST;
     228         255 :         else if (word_eq(hbp, "PUT"))
     229           1 :                 h->hdr_type = HTTPREAD_HDR_TYPE_PUT;
     230         254 :         else if (word_eq(hbp, "DELETE"))
     231           1 :                 h->hdr_type = HTTPREAD_HDR_TYPE_DELETE;
     232         253 :         else if (word_eq(hbp, "TRACE"))
     233           1 :                 h->hdr_type = HTTPREAD_HDR_TYPE_TRACE;
     234         252 :         else if (word_eq(hbp, "CONNECT"))
     235           1 :                 h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT;
     236         251 :         else if (word_eq(hbp, "NOTIFY"))
     237         162 :                 h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY;
     238          89 :         else if (word_eq(hbp, "M-SEARCH"))
     239           1 :                 h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH;
     240          88 :         else if (word_eq(hbp, "M-POST"))
     241           1 :                 h->hdr_type = HTTPREAD_HDR_TYPE_M_POST;
     242          87 :         else if (word_eq(hbp, "SUBSCRIBE"))
     243          55 :                 h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE;
     244          32 :         else if (word_eq(hbp, "UNSUBSCRIBE"))
     245          29 :                 h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE;
     246             :         else {
     247             :         }
     248             : 
     249        1404 :         if (standard_first_line) {
     250             :                 char *rawuri;
     251             :                 char *uri;
     252             :                 /* skip type */
     253        3724 :                 while (isgraph(*hbp))
     254        2740 :                         hbp++;
     255        1476 :                 while (*hbp == ' ' || *hbp == '\t')
     256         492 :                         hbp++;
     257             :                 /* parse uri.
     258             :                  * Find length, allocate memory for translated
     259             :                  * copy, then translate by changing %<hex><hex>
     260             :                  * into represented value.
     261             :                  */
     262         492 :                 rawuri = hbp;
     263       10345 :                 while (isgraph(*hbp))
     264        9361 :                         hbp++;
     265         492 :                 h->uri = os_malloc((hbp - rawuri) + 1);
     266         492 :                 if (h->uri == NULL)
     267           1 :                         goto bad;
     268         491 :                 uri = h->uri;
     269       10340 :                 while (rawuri < hbp) {
     270        9358 :                         int c = *rawuri;
     271        9362 :                         if (c == '%' &&
     272           6 :                             isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
     273           1 :                                 *uri++ = hex2byte(rawuri + 1);
     274           1 :                                 rawuri += 3;
     275             :                         } else {
     276        9357 :                                 *uri++ = c;
     277        9357 :                                 rawuri++;
     278             :                         }
     279             :                 }
     280         491 :                 *uri = 0;       /* null terminate */
     281        1473 :                 while (*hbp == ' ' || *hbp == '\t')
     282         491 :                         hbp++;
     283             :                 /* get version */
     284         491 :                 if (0 == strncmp(hbp, "HTTP/", 5)) {
     285         491 :                         hbp += 5;
     286         982 :                         if (hbp[0] == '1' && hbp[1] == '.' &&
     287         982 :                             isdigit(hbp[2]) && hbp[2] != '0')
     288         484 :                                 h->version = 1;
     289             :                 }
     290             :         }
     291             :         /* skip rest of line */
     292       11185 :         while (*hbp)
     293        9782 :                 if (*hbp++ == '\n')
     294        1403 :                         break;
     295             : 
     296             :         /* Remainder of lines are options, in any order;
     297             :          * or empty line to terminate
     298             :          */
     299             :         for (;;) {
     300             :                 /* Empty line to terminate */
     301       18710 :                 if (hbp[0] == '\n' ||
     302       10753 :                     (hbp[0] == '\r' && hbp[1] == '\n'))
     303             :                         break;
     304        7957 :                 if (!isgraph(*hbp))
     305           1 :                         goto bad;
     306        7956 :                 if (httpread_hdr_option_analyze(h, hbp))
     307           4 :                         goto bad;
     308             :                 /* skip line */
     309      263753 :                 while (*hbp)
     310      255801 :                         if (*hbp++ == '\n')
     311        7952 :                                 break;
     312        7952 :         }
     313             : 
     314             :         /* chunked overrides content-length always */
     315        1398 :         if (h->chunked)
     316          10 :                 h->got_content_length = 0;
     317             : 
     318             :         /* For some types, we should not try to read a body
     319             :          * This is in addition to the application determining
     320             :          * that we should not read a body.
     321             :          */
     322        1398 :         switch (h->hdr_type) {
     323             :         case HTTPREAD_HDR_TYPE_REPLY:
     324             :                 /* Some codes can have a body and some not.
     325             :                  * For now, just assume that any other than 200
     326             :                  * do not...
     327             :                  */
     328         912 :                 if (h->reply_code != 200)
     329           9 :                         h->max_bytes = 0;
     330         912 :                 break;
     331             :         case HTTPREAD_HDR_TYPE_GET:
     332             :         case HTTPREAD_HDR_TYPE_HEAD:
     333             :                 /* in practice it appears that it is assumed
     334             :                  * that GETs have a body length of 0... ?
     335             :                  */
     336          50 :                 if (h->chunked == 0 && h->got_content_length == 0)
     337          48 :                         h->max_bytes = 0;
     338          50 :                 break;
     339             :         case HTTPREAD_HDR_TYPE_POST:
     340             :         case HTTPREAD_HDR_TYPE_PUT:
     341             :         case HTTPREAD_HDR_TYPE_DELETE:
     342             :         case HTTPREAD_HDR_TYPE_TRACE:
     343             :         case HTTPREAD_HDR_TYPE_CONNECT:
     344             :         case HTTPREAD_HDR_TYPE_NOTIFY:
     345             :         case HTTPREAD_HDR_TYPE_M_SEARCH:
     346             :         case HTTPREAD_HDR_TYPE_M_POST:
     347             :         case HTTPREAD_HDR_TYPE_SUBSCRIBE:
     348             :         case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
     349             :         default:
     350         436 :                 break;
     351             :         }
     352             : 
     353        1398 :         return 0;
     354             : 
     355             : bad:
     356             :         /* Error */
     357           8 :         return -1;
     358             : }
     359             : 
     360             : 
     361             : /* httpread_read_handler -- called when socket ready to read
     362             :  *
     363             :  * Note: any extra data we read past end of transmitted file is ignored;
     364             :  * if we were to support keeping connections open for multiple files then
     365             :  * this would have to be addressed.
     366             :  */
     367        1526 : static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
     368             : {
     369        1526 :         struct httpread *h = sock_ctx;
     370             :         int nread;
     371             :         char *rbp;      /* pointer into read buffer */
     372             :         char *hbp;      /* pointer into header buffer */
     373             :         char *bbp;      /* pointer into body buffer */
     374             :         char readbuf[HTTPREAD_READBUF_SIZE];  /* temp use to read into */
     375             : 
     376             :         /* read some at a time, then search for the interal
     377             :          * boundaries between header and data and etc.
     378             :          */
     379        1526 :         wpa_printf(MSG_DEBUG, "httpread: Trying to read more data(%p)", h);
     380        1526 :         nread = read(h->sd, readbuf, sizeof(readbuf));
     381        1526 :         if (nread < 0) {
     382           1 :                 wpa_printf(MSG_DEBUG, "httpread failed: %s", strerror(errno));
     383           1 :                 goto bad;
     384             :         }
     385        1525 :         wpa_hexdump_ascii(MSG_MSGDUMP, "httpread - read", readbuf, nread);
     386        1525 :         if (nread == 0) {
     387             :                 /* end of transmission... this may be normal
     388             :                  * or may be an error... in some cases we can't
     389             :                  * tell which so we must assume it is normal then.
     390             :                  */
     391          13 :                 if (!h->got_hdr) {
     392             :                         /* Must at least have completed header */
     393           3 :                         wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h);
     394           3 :                         goto bad;
     395             :                 }
     396          10 :                 if (h->chunked || h->got_content_length) {
     397             :                         /* Premature EOF; e.g. dropped connection */
     398           3 :                         wpa_printf(MSG_DEBUG,
     399             :                                    "httpread premature eof(%p) %d/%d",
     400             :                                    h, h->body_nbytes,
     401             :                                    h->content_length);
     402           3 :                         goto bad;
     403             :                 }
     404             :                 /* No explicit length, hopefully we have all the data
     405             :                  * although dropped connections can cause false
     406             :                  * end
     407             :                  */
     408           7 :                 wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
     409           7 :                 h->got_body = 1;
     410           7 :                 goto got_file;
     411             :         }
     412        1512 :         rbp = readbuf;
     413             : 
     414             :         /* Header consists of text lines (terminated by both CR and LF)
     415             :          * and an empty line (CR LF only).
     416             :          */
     417        1512 :         if (!h->got_hdr) {
     418        1419 :                 hbp = h->hdr + h->hdr_nbytes;
     419             :                 /* add to headers until:
     420             :                  *      -- we run out of data in read buffer
     421             :                  *      -- or, we run out of header buffer room
     422             :                  *      -- or, we get double CRLF in headers
     423             :                  */
     424             :                 for (;;) {
     425      300486 :                         if (nread == 0)
     426          11 :                                 goto get_more;
     427      300475 :                         if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
     428           2 :                                 wpa_printf(MSG_DEBUG,
     429             :                                            "httpread: Too long header");
     430           2 :                                 goto bad;
     431             :                         }
     432      300473 :                         *hbp++ = *rbp++;
     433      300473 :                         nread--;
     434      300473 :                         h->hdr_nbytes++;
     435      596721 :                         if (h->hdr_nbytes >= 4 &&
     436      307018 :                             hbp[-1] == '\n' &&
     437       21540 :                             hbp[-2] == '\r' &&
     438       12176 :                             hbp[-3] == '\n' &&
     439        1406 :                             hbp[-4] == '\r' ) {
     440        1406 :                                 h->got_hdr = 1;
     441        1406 :                                 *hbp = 0;       /* null terminate */
     442        1406 :                                 break;
     443             :                         }
     444      299067 :                 }
     445             :                 /* here we've just finished reading the header */
     446        1406 :                 if (httpread_hdr_analyze(h)) {
     447           8 :                         wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h);
     448           8 :                         goto bad;
     449             :                 }
     450        1398 :                 if (h->max_bytes == 0) {
     451         711 :                         wpa_printf(MSG_DEBUG, "httpread no body hdr end(%p)",
     452             :                                    h);
     453         711 :                         goto got_file;
     454             :                 }
     455         687 :                 if (h->got_content_length && h->content_length == 0) {
     456          51 :                         wpa_printf(MSG_DEBUG,
     457             :                                    "httpread zero content length(%p)", h);
     458          51 :                         goto got_file;
     459             :                 }
     460             :         }
     461             : 
     462             :         /* Certain types of requests never have data and so
     463             :          * must be specially recognized.
     464             :          */
     465        1403 :         if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) ||
     466        1320 :             !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) ||
     467        1290 :             !os_strncasecmp(h->hdr, "HEAD", 4) ||
     468         644 :             !os_strncasecmp(h->hdr, "GET", 3)) {
     469          85 :                 if (!h->got_body) {
     470          85 :                         wpa_printf(MSG_DEBUG, "httpread NO BODY for sp. type");
     471             :                 }
     472          85 :                 h->got_body = 1;
     473          85 :                 goto got_file;
     474             :         }
     475             : 
     476             :         /* Data can be just plain binary data, or if "chunked"
     477             :          * consists of chunks each with a header, ending with
     478             :          * an ending header.
     479             :          */
     480         644 :         if (nread == 0)
     481           7 :                 goto get_more;
     482         637 :         if (!h->got_body) {
     483             :                 /* Here to get (more of) body */
     484             :                 /* ensure we have enough room for worst case for body
     485             :                  * plus a null termination character
     486             :                  */
     487         637 :                 if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
     488             :                         char *new_body;
     489             :                         int new_alloc_nbytes;
     490             : 
     491         548 :                         if (h->body_nbytes >= h->max_bytes) {
     492           1 :                                 wpa_printf(MSG_DEBUG,
     493             :                                            "httpread: body_nbytes=%d >= max_bytes=%d",
     494             :                                            h->body_nbytes, h->max_bytes);
     495           1 :                                 goto bad;
     496             :                         }
     497         547 :                         new_alloc_nbytes = h->body_alloc_nbytes +
     498             :                                 HTTPREAD_BODYBUF_DELTA;
     499             :                         /* For content-length case, the first time
     500             :                          * through we allocate the whole amount
     501             :                          * we need.
     502             :                          */
     503        1080 :                         if (h->got_content_length &&
     504         533 :                             new_alloc_nbytes < (h->content_length + 1))
     505           1 :                                 new_alloc_nbytes = h->content_length + 1;
     506        1094 :                         if (new_alloc_nbytes < h->body_alloc_nbytes ||
     507         547 :                             new_alloc_nbytes > h->max_bytes +
     508             :                             HTTPREAD_BODYBUF_DELTA) {
     509           1 :                                 wpa_printf(MSG_DEBUG,
     510             :                                            "httpread: Unacceptable body length %d (body_alloc_nbytes=%u max_bytes=%u)",
     511             :                                            new_alloc_nbytes,
     512             :                                            h->body_alloc_nbytes,
     513             :                                            h->max_bytes);
     514           1 :                                 goto bad;
     515             :                         }
     516         546 :                         if ((new_body = os_realloc(h->body, new_alloc_nbytes))
     517             :                             == NULL) {
     518           1 :                                 wpa_printf(MSG_DEBUG,
     519             :                                            "httpread: Failed to reallocate buffer (len=%d)",
     520             :                                            new_alloc_nbytes);
     521           1 :                                 goto bad;
     522             :                         }
     523             : 
     524         545 :                         h->body = new_body;
     525         545 :                         h->body_alloc_nbytes = new_alloc_nbytes;
     526             :                 }
     527             :                 /* add bytes */
     528         634 :                 bbp = h->body + h->body_nbytes;
     529             :                 for (;;) {
     530             :                         int ncopy;
     531             :                         /* See if we need to stop */
     532       51660 :                         if (h->chunked && h->in_chunk_data == 0) {
     533             :                                 /* in chunk header */
     534       21643 :                                 char *cbp = h->body + h->chunk_start;
     535       28839 :                                 if (bbp-cbp >= 2 && bbp[-2] == '\r' &&
     536        7196 :                                     bbp[-1] == '\n') {
     537             :                                         /* end of chunk hdr line */
     538             :                                         /* hdr line consists solely
     539             :                                          * of a hex numeral and CFLF
     540             :                                          */
     541        7196 :                                         if (!isxdigit(*cbp)) {
     542           1 :                                                 wpa_printf(MSG_DEBUG,
     543             :                                                            "httpread: Unexpected chunk header value (not a hex digit)");
     544           1 :                                                 goto bad;
     545             :                                         }
     546        7195 :                                         h->chunk_size = strtoul(cbp, NULL, 16);
     547       14389 :                                         if (h->chunk_size < 0 ||
     548        7194 :                                             h->chunk_size > h->max_bytes) {
     549           2 :                                                 wpa_printf(MSG_DEBUG,
     550             :                                                            "httpread: Invalid chunk size %d",
     551             :                                                            h->chunk_size);
     552           2 :                                                 goto bad;
     553             :                                         }
     554             :                                         /* throw away chunk header
     555             :                                          * so we have only real data
     556             :                                          */
     557        7193 :                                         h->body_nbytes = h->chunk_start;
     558        7193 :                                         bbp = cbp;
     559        7193 :                                         if (h->chunk_size == 0) {
     560             :                                                 /* end of chunking */
     561             :                                                 /* trailer follows */
     562           4 :                                                 h->in_trailer = 1;
     563           4 :                                                 wpa_printf(MSG_DEBUG,
     564             :                                                            "httpread end chunks(%p)",
     565             :                                                            h);
     566           4 :                                                 break;
     567             :                                         }
     568        7189 :                                         h->in_chunk_data = 1;
     569             :                                         /* leave chunk_start alone */
     570             :                                 }
     571        8381 :                         } else if (h->chunked) {
     572             :                                 /* in chunk data */
     573       14434 :                                 if ((h->body_nbytes - h->chunk_start) ==
     574        7217 :                                     (h->chunk_size + 2)) {
     575             :                                         /* end of chunk reached,
     576             :                                          * new chunk starts
     577             :                                          */
     578             :                                         /* check chunk ended w/ CRLF
     579             :                                          * which we'll throw away
     580             :                                          */
     581       14377 :                                         if (bbp[-1] == '\n' &&
     582        7188 :                                             bbp[-2] == '\r') {
     583             :                                         } else {
     584           1 :                                                 wpa_printf(MSG_DEBUG,
     585             :                                                            "httpread: Invalid chunk end");
     586           1 :                                                 goto bad;
     587             :                                         }
     588        7188 :                                         h->body_nbytes -= 2;
     589        7188 :                                         bbp -= 2;
     590        7188 :                                         h->chunk_start = h->body_nbytes;
     591        7188 :                                         h->in_chunk_data = 0;
     592        7188 :                                         h->chunk_size = 0; /* just in case */
     593             :                                 }
     594        2312 :                         } else if (h->got_content_length &&
     595        1148 :                                    h->body_nbytes >= h->content_length) {
     596         532 :                                 h->got_body = 1;
     597         532 :                                 wpa_printf(MSG_DEBUG,
     598             :                                            "httpread got content(%p)", h);
     599         532 :                                 goto got_file;
     600             :                         }
     601       29484 :                         if (nread <= 0)
     602          94 :                                 break;
     603             :                         /* Now transfer. Optimize using memcpy where we can. */
     604       29390 :                         if (h->chunked && h->in_chunk_data) {
     605             :                                 /* copy up to remainder of chunk data
     606             :                                  * plus the required CR+LF at end
     607             :                                  */
     608       14406 :                                 ncopy = (h->chunk_start + h->chunk_size + 2) -
     609        7203 :                                         h->body_nbytes;
     610       22187 :                         } else if (h->chunked) {
     611             :                                 /*in chunk header -- don't optimize */
     612       21605 :                                 *bbp++ = *rbp++;
     613       21605 :                                 nread--;
     614       21605 :                                 h->body_nbytes++;
     615       21605 :                                 continue;
     616         582 :                         } else if (h->got_content_length) {
     617         574 :                                 ncopy = h->content_length - h->body_nbytes;
     618             :                         } else {
     619           8 :                                 ncopy = nread;
     620             :                         }
     621             :                         /* Note: should never be 0 */
     622        7785 :                         if (ncopy < 0) {
     623           0 :                                 wpa_printf(MSG_DEBUG,
     624             :                                            "httpread: Invalid ncopy=%d", ncopy);
     625           0 :                                 goto bad;
     626             :                         }
     627        7785 :                         if (ncopy > nread)
     628          56 :                                 ncopy = nread;
     629        7785 :                         os_memcpy(bbp, rbp, ncopy);
     630        7785 :                         bbp += ncopy;
     631        7785 :                         h->body_nbytes += ncopy;
     632        7785 :                         rbp += ncopy;
     633        7785 :                         nread -= ncopy;
     634       29390 :                 }       /* body copy loop */
     635             :         }       /* !got_body */
     636          98 :         if (h->chunked && h->in_trailer) {
     637             :                 /* If "chunked" then there is always a trailer,
     638             :                  * consisting of zero or more non-empty lines
     639             :                  * ending with CR LF and then an empty line w/ CR LF.
     640             :                  * We do NOT support trailers except to skip them --
     641             :                  * this is supported (generally) by the http spec.
     642             :                  */
     643             :                 for (;;) {
     644             :                         int c;
     645          14 :                         if (nread <= 0)
     646           2 :                                 break;
     647          12 :                         c = *rbp++;
     648          12 :                         nread--;
     649          12 :                         switch (h->trailer_state) {
     650             :                         case trailer_line_begin:
     651           4 :                                 if (c == '\r')
     652           3 :                                         h->trailer_state = trailer_empty_cr;
     653             :                                 else
     654           1 :                                         h->trailer_state = trailer_nonempty;
     655           4 :                                 break;
     656             :                         case trailer_empty_cr:
     657             :                                 /* end empty line */
     658           3 :                                 if (c == '\n') {
     659           2 :                                         h->trailer_state = trailer_line_begin;
     660           2 :                                         h->in_trailer = 0;
     661           2 :                                         wpa_printf(MSG_DEBUG,
     662             :                                                    "httpread got content(%p)",
     663             :                                                    h);
     664           2 :                                         h->got_body = 1;
     665           2 :                                         goto got_file;
     666             :                                 }
     667           1 :                                 h->trailer_state = trailer_nonempty;
     668           1 :                                 break;
     669             :                         case trailer_nonempty:
     670           3 :                                 if (c == '\r')
     671           2 :                                         h->trailer_state = trailer_nonempty_cr;
     672           3 :                                 break;
     673             :                         case trailer_nonempty_cr:
     674           2 :                                 if (c == '\n')
     675           1 :                                         h->trailer_state = trailer_line_begin;
     676             :                                 else
     677           1 :                                         h->trailer_state = trailer_nonempty;
     678           2 :                                 break;
     679             :                         }
     680          10 :                 }
     681             :         }
     682          96 :         goto get_more;
     683             : 
     684             : bad:
     685             :         /* Error */
     686          24 :         wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h);
     687          24 :         (*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR);
     688          24 :         return;
     689             : 
     690             : get_more:
     691         114 :         wpa_printf(MSG_DEBUG, "httpread: get more (%p)", h);
     692         114 :         return;
     693             : 
     694             : got_file:
     695        1388 :         wpa_printf(MSG_DEBUG, "httpread got file %d bytes type %d",
     696        1388 :                    h->body_nbytes, h->hdr_type);
     697        2776 :         wpa_hexdump_ascii(MSG_MSGDUMP, "httpread: body",
     698        2776 :                           h->body, h->body_nbytes);
     699             :         /* Null terminate for convenience of some applications */
     700        1388 :         if (h->body)
     701         534 :                 h->body[h->body_nbytes] = 0; /* null terminate */
     702        1388 :         h->got_file = 1;
     703             :         /* Assume that we do NOT support keeping connection alive,
     704             :          * and just in case somehow we don't get destroyed right away,
     705             :          * unregister now.
     706             :          */
     707        1388 :         eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
     708             :         /* The application can destroy us whenever they feel like...
     709             :          * cancel timeout.
     710             :          */
     711        1388 :         eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
     712        1388 :         (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY);
     713             : }
     714             : 
     715             : 
     716             : /* httpread_create -- start a new reading session making use of eloop.
     717             :  * The new instance will use the socket descriptor for reading (until
     718             :  * it gets a file and not after) but will not close the socket, even
     719             :  * when the instance is destroyed (the application must do that).
     720             :  * Return NULL on error.
     721             :  *
     722             :  * Provided that httpread_create successfully returns a handle,
     723             :  * the callback fnc is called to handle httpread_event events.
     724             :  * The caller should do destroy on any errors or unknown events.
     725             :  *
     726             :  * Pass max_bytes == 0 to not read body at all (required for e.g.
     727             :  * reply to HEAD request).
     728             :  */
     729        1419 : struct httpread * httpread_create(
     730             :         int sd,  /* descriptor of TCP socket to read from */
     731             :         void (*cb)(struct httpread *handle, void *cookie,
     732             :                    enum httpread_event e),  /* call on event */
     733             :         void *cookie,    /* pass to callback */
     734             :         int max_bytes,    /* maximum body size else abort it */
     735             :         int timeout_seconds     /* 0; or total duration timeout period */
     736             :         )
     737             : {
     738        1419 :         struct httpread *h = NULL;
     739             : 
     740        1419 :         h = os_zalloc(sizeof(*h));
     741        1419 :         if (h == NULL)
     742           2 :                 goto fail;
     743        1417 :         h->sd = sd;
     744        1417 :         h->cb = cb;
     745        1417 :         h->cookie = cookie;
     746        1417 :         h->max_bytes = max_bytes;
     747        1417 :         h->timeout_seconds = timeout_seconds;
     748             : 
     749        2834 :         if (timeout_seconds > 0 &&
     750        1417 :             eloop_register_timeout(timeout_seconds, 0,
     751             :                                    httpread_timeout_handler, NULL, h)) {
     752             :                 /* No way to recover (from malloc failure) */
     753           1 :                 goto fail;
     754             :         }
     755        1416 :         if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler,
     756             :                                 NULL, h)) {
     757             :                 /* No way to recover (from malloc failure) */
     758           1 :                 goto fail;
     759             :         }
     760        1415 :         return h;
     761             : 
     762             : fail:
     763             : 
     764             :         /* Error */
     765           4 :         httpread_destroy(h);
     766           4 :         return NULL;
     767             : }
     768             : 
     769             : 
     770             : /* httpread_hdr_type_get -- When file is ready, returns header type. */
     771        1388 : enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
     772             : {
     773        1388 :         return h->hdr_type;
     774             : }
     775             : 
     776             : 
     777             : /* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
     778             :  * or possibly NULL (which would be an error).
     779             :  */
     780         636 : char * httpread_uri_get(struct httpread *h)
     781             : {
     782         636 :         return h->uri;
     783             : }
     784             : 
     785             : 
     786             : /* httpread_reply_code_get -- When reply is ready, returns reply code */
     787         911 : int httpread_reply_code_get(struct httpread *h)
     788             : {
     789         911 :         return h->reply_code;
     790             : }
     791             : 
     792             : 
     793             : /* httpread_length_get -- When file is ready, returns file length. */
     794         128 : int httpread_length_get(struct httpread *h)
     795             : {
     796         128 :         return h->body_nbytes;
     797             : }
     798             : 
     799             : 
     800             : /* httpread_data_get -- When file is ready, returns file content
     801             :  * with null byte appened.
     802             :  * Might return NULL in some error condition.
     803             :  */
     804         611 : void * httpread_data_get(struct httpread *h)
     805             : {
     806         611 :         return h->body ? h->body : "";
     807             : }
     808             : 
     809             : 
     810             : /* httpread_hdr_get -- When file is ready, returns header content
     811             :  * with null byte appended.
     812             :  * Might return NULL in some error condition.
     813             :  */
     814          84 : char * httpread_hdr_get(struct httpread *h)
     815             : {
     816          84 :         return h->hdr;
     817             : }
     818             : 
     819             : 
     820             : /* httpread_hdr_line_get -- When file is ready, returns pointer
     821             :  * to line within header content matching the given tag
     822             :  * (after the tag itself and any spaces/tabs).
     823             :  *
     824             :  * The tag should end with a colon for reliable matching.
     825             :  *
     826             :  * If not found, returns NULL;
     827             :  */
     828         200 : char * httpread_hdr_line_get(struct httpread *h, const char *tag)
     829             : {
     830         200 :         int tag_len = os_strlen(tag);
     831         200 :         char *hdr = h->hdr;
     832         200 :         hdr = os_strchr(hdr, '\n');
     833         200 :         if (hdr == NULL)
     834           0 :                 return NULL;
     835         200 :         hdr++;
     836             :         for (;;) {
     837         813 :                 if (!os_strncasecmp(hdr, tag, tag_len)) {
     838         198 :                         hdr += tag_len;
     839         564 :                         while (*hdr == ' ' || *hdr == '\t')
     840         168 :                                 hdr++;
     841         198 :                         return hdr;
     842             :                 }
     843         615 :                 hdr = os_strchr(hdr, '\n');
     844         615 :                 if (hdr == NULL)
     845           2 :                         return NULL;
     846         613 :                 hdr++;
     847         613 :         }
     848             : }

Generated by: LCOV version 1.10