LCOV - code coverage report
Current view: top level - wps - httpread.c (source / functions) Hit Total Coverage
Test: hostapd hwsim test run 1401872338 Lines: 234 343 68.2 %
Date: 2014-06-04 Functions: 12 14 85.7 %

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

Generated by: LCOV version 1.10