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 1401264779 Lines: 239 343 69.7 %
Date: 2014-05-28 Functions: 13 14 92.9 %

          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        5116 : static int word_eq(char *s1, char *s2)
     109             : {
     110             :         int c1;
     111             :         int c2;
     112        5116 :         int end1 = 0;
     113        5116 :         int end2 = 0;
     114             :         for (;;) {
     115       12533 :                 c1 = *s1++;
     116       12533 :                 c2 = *s2++;
     117       12533 :                 if (isalpha(c1) && isupper(c1))
     118        7284 :                         c1 = tolower(c1);
     119       12533 :                 if (isalpha(c2) && isupper(c2))
     120       11297 :                         c2 = tolower(c2);
     121       12533 :                 end1 = !isgraph(c1);
     122       12533 :                 end2 = !isgraph(c2);
     123       12533 :                 if (end1 || end2 || c1 != c2)
     124             :                         break;
     125        7417 :         }
     126        5116 :         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         330 : void httpread_destroy(struct httpread *h)
     138             : {
     139             :         if (httpread_debug >= 10)
     140             :                 wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h);
     141         330 :         if (!h)
     142         340 :                 return;
     143             : 
     144         320 :         eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
     145         320 :         eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
     146         320 :         os_free(h->body);
     147         320 :         os_free(h->uri);
     148         320 :         os_memset(h, 0, sizeof(*h));  /* aid debugging */
     149         320 :         h->sd = -1;     /* aid debugging */
     150         320 :         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        1528 : static int httpread_hdr_option_analyze(
     168             :         struct httpread *h,
     169             :         char *hbp       /* pointer to current line in header buffer */
     170             :         )
     171             : {
     172        1528 :         if (word_eq(hbp, "CONTENT-LENGTH:")) {
     173        4896 :                 while (isgraph(*hbp))
     174        4320 :                         hbp++;
     175         864 :                 while (*hbp == ' ' || *hbp == '\t')
     176         288 :                         hbp++;
     177         288 :                 if (!isdigit(*hbp))
     178           0 :                         return -1;
     179         288 :                 h->content_length = atol(hbp);
     180         288 :                 h->got_content_length = 1;
     181         288 :                 return 0;
     182             :         }
     183        2480 :         if (word_eq(hbp, "TRANSFER_ENCODING:") ||
     184        1240 :             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        1240 :         return 0;
     201             : }
     202             : 
     203             : 
     204         319 : static int httpread_hdr_analyze(struct httpread *h)
     205             : {
     206         319 :         char *hbp = h->hdr;      /* pointer into h->hdr */
     207         319 :         int standard_first_line = 1;
     208             : 
     209             :         /* First line is special */
     210         319 :         h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN;
     211         319 :         if (!isgraph(*hbp))
     212           0 :                 goto bad;
     213         319 :         if (os_strncmp(hbp, "HTTP/", 5) == 0) {
     214         141 :                 h->hdr_type = HTTPREAD_HDR_TYPE_REPLY;
     215         141 :                 standard_first_line = 0;
     216         141 :                 hbp += 5;
     217         282 :                 if (hbp[0] == '1' && hbp[1] == '.' &&
     218         282 :                     isdigit(hbp[2]) && hbp[2] != '0')
     219         141 :                         h->version = 1;
     220         705 :                 while (isgraph(*hbp))
     221         423 :                         hbp++;
     222         423 :                 while (*hbp == ' ' || *hbp == '\t')
     223         141 :                         hbp++;
     224         141 :                 if (!isdigit(*hbp))
     225           0 :                         goto bad;
     226         141 :                 h->reply_code = atol(hbp);
     227         178 :         } else if (word_eq(hbp, "GET"))
     228          13 :                 h->hdr_type = HTTPREAD_HDR_TYPE_GET;
     229         165 :         else if (word_eq(hbp, "HEAD"))
     230           0 :                 h->hdr_type = HTTPREAD_HDR_TYPE_HEAD;
     231         165 :         else if (word_eq(hbp, "POST"))
     232          72 :                 h->hdr_type = HTTPREAD_HDR_TYPE_POST;
     233          93 :         else if (word_eq(hbp, "PUT"))
     234           0 :                 h->hdr_type = HTTPREAD_HDR_TYPE_PUT;
     235          93 :         else if (word_eq(hbp, "DELETE"))
     236           0 :                 h->hdr_type = HTTPREAD_HDR_TYPE_DELETE;
     237          93 :         else if (word_eq(hbp, "TRACE"))
     238           0 :                 h->hdr_type = HTTPREAD_HDR_TYPE_TRACE;
     239          93 :         else if (word_eq(hbp, "CONNECT"))
     240           0 :                 h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT;
     241          93 :         else if (word_eq(hbp, "NOTIFY"))
     242          54 :                 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         319 :         if (standard_first_line) {
     255             :                 char *rawuri;
     256             :                 char *uri;
     257             :                 /* skip type */
     258        1390 :                 while (isgraph(*hbp))
     259        1034 :                         hbp++;
     260         534 :                 while (*hbp == ' ' || *hbp == '\t')
     261         178 :                         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         178 :                 rawuri = hbp;
     268        2714 :                 while (isgraph(*hbp))
     269        2358 :                         hbp++;
     270         178 :                 h->uri = os_malloc((hbp - rawuri) + 1);
     271         178 :                 if (h->uri == NULL)
     272           0 :                         goto bad;
     273         178 :                 uri = h->uri;
     274        2714 :                 while (rawuri < hbp) {
     275        2358 :                         int c = *rawuri;
     276        2358 :                         if (c == '%' &&
     277           0 :                             isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
     278           0 :                                 *uri++ = hex2byte(rawuri + 1);
     279           0 :                                 rawuri += 3;
     280             :                         } else {
     281        2358 :                                 *uri++ = c;
     282        2358 :                                 rawuri++;
     283             :                         }
     284             :                 }
     285         178 :                 *uri = 0;       /* null terminate */
     286         356 :                 while (isgraph(*hbp))
     287           0 :                         hbp++;
     288         534 :                 while (*hbp == ' ' || *hbp == '\t')
     289         178 :                         hbp++;
     290             :                 /* get version */
     291         178 :                 if (0 == strncmp(hbp, "HTTP/", 5)) {
     292         178 :                         hbp += 5;
     293         356 :                         if (hbp[0] == '1' && hbp[1] == '.' &&
     294         356 :                             isdigit(hbp[2]) && hbp[2] != '0')
     295         174 :                                 h->version = 1;
     296             :                 }
     297             :         }
     298             :         /* skip rest of line */
     299        2340 :         while (*hbp)
     300        2021 :                 if (*hbp++ == '\n')
     301         319 :                         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        3694 :                 if (hbp[0] == '\n' ||
     309        2166 :                     (hbp[0] == '\r' && hbp[1] == '\n'))
     310             :                         break;
     311        1528 :                 if (!isgraph(*hbp))
     312           0 :                         goto bad;
     313        1528 :                 if (httpread_hdr_option_analyze(h, hbp))
     314           0 :                         goto bad;
     315             :                 /* skip line */
     316       50455 :                 while (*hbp)
     317       48927 :                         if (*hbp++ == '\n')
     318        1528 :                                 break;
     319        1528 :         }
     320             : 
     321             :         /* chunked overrides content-length always */
     322         319 :         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         319 :         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         141 :                 if (h->reply_code != 200)
     336           1 :                         h->max_bytes = 0;
     337         141 :                 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         165 :                 break;
     358             :         }
     359             : 
     360         319 :         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         329 : static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
     375             : {
     376         329 :         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         329 :         nread = read(h->sd, readbuf, sizeof(readbuf));
     390         329 :         if (nread < 0)
     391           0 :                 goto bad;
     392         329 :         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         328 :         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         328 :         if (!h->got_hdr) {
     425         319 :                 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       57493 :                         if (nread == 0)
     433           0 :                                 goto get_more;
     434       57493 :                         if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
     435           0 :                                 goto bad;
     436             :                         }
     437       57493 :                         *hbp++ = *rbp++;
     438       57493 :                         nread--;
     439       57493 :                         h->hdr_nbytes++;
     440      114029 :                         if (h->hdr_nbytes >= 4 &&
     441       58702 :                             hbp[-1] == '\n' &&
     442        4332 :                             hbp[-2] == '\r' &&
     443        2485 :                             hbp[-3] == '\n' &&
     444         319 :                             hbp[-4] == '\r' ) {
     445         319 :                                 h->got_hdr = 1;
     446         319 :                                 *hbp = 0;       /* null terminate */
     447         319 :                                 break;
     448             :                         }
     449       57174 :                 }
     450             :                 /* here we've just finished reading the header */
     451         319 :                 if (httpread_hdr_analyze(h)) {
     452           0 :                         wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h);
     453           0 :                         goto bad;
     454             :                 }
     455         319 :                 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         251 :                 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          18 :                         goto got_file;
     467             :                 }
     468             :         }
     469             : 
     470             :         /* Certain types of requests never have data and so
     471             :          * must be specially recognized.
     472             :          */
     473         463 :         if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) ||
     474         425 :             !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) ||
     475         408 :             !os_strncasecmp(h->hdr, "HEAD", 4) ||
     476         204 :             !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         204 :         if (nread == 0)
     491           0 :                 goto get_more;
     492         204 :         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         204 :                 if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
     498             :                         char *new_body;
     499             :                         int new_alloc_nbytes;
     500             : 
     501         195 :                         if (h->body_nbytes >= h->max_bytes)
     502           0 :                                 goto bad;
     503         195 :                         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         390 :                         if (h->got_content_length &&
     510         195 :                             new_alloc_nbytes < (h->content_length + 1))
     511           0 :                                 new_alloc_nbytes = h->content_length + 1;
     512         195 :                         if ((new_body = os_realloc(h->body, new_alloc_nbytes))
     513             :                             == NULL)
     514           0 :                                 goto bad;
     515             : 
     516         195 :                         h->body = new_body;
     517         195 :                         h->body_alloc_nbytes = new_alloc_nbytes;
     518             :                 }
     519             :                 /* add bytes */
     520         204 :                 bbp = h->body + h->body_nbytes;
     521             :                 for (;;) {
     522             :                         int ncopy;
     523             :                         /* See if we need to stop */
     524         408 :                         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         408 :                         } 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         816 :                         } else if (h->got_content_length &&
     575         408 :                                    h->body_nbytes >= h->content_length) {
     576         195 :                                 h->got_body = 1;
     577             :                                 if (httpread_debug >= 10)
     578             :                                         wpa_printf(
     579             :                                                 MSG_DEBUG,
     580             :                                                 "httpread got content(%p)", h);
     581         195 :                                 goto got_file;
     582             :                         }
     583         213 :                         if (nread <= 0)
     584           9 :                                 break;
     585             :                         /* Now transfer. Optimize using memcpy where we can. */
     586         204 :                         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         204 :                         } 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         204 :                         } else if (h->got_content_length) {
     599         204 :                                 ncopy = h->content_length - h->body_nbytes;
     600             :                         } else {
     601           0 :                                 ncopy = nread;
     602             :                         }
     603             :                         /* Note: should never be 0 */
     604         204 :                         if (ncopy > nread)
     605           9 :                                 ncopy = nread;
     606         204 :                         os_memcpy(bbp, rbp, ncopy);
     607         204 :                         bbp += ncopy;
     608         204 :                         h->body_nbytes += ncopy;
     609         204 :                         rbp += ncopy;
     610         204 :                         nread -= ncopy;
     611         204 :                 }       /* 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         319 :         if (h->body)
     678         195 :                 h->body[h->body_nbytes] = 0; /* null terminate */
     679         319 :         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         319 :         eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
     685             :         /* The application can destroy us whenever they feel like...
     686             :          * cancel timeout.
     687             :          */
     688         319 :         eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
     689         319 :         (*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         320 : 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         320 :         struct httpread *h = NULL;
     716             : 
     717         320 :         h = os_zalloc(sizeof(*h));
     718         320 :         if (h == NULL)
     719           0 :                 goto fail;
     720         320 :         h->sd = sd;
     721         320 :         h->cb = cb;
     722         320 :         h->cookie = cookie;
     723         320 :         h->max_bytes = max_bytes;
     724         320 :         h->timeout_seconds = timeout_seconds;
     725             : 
     726         640 :         if (timeout_seconds > 0 &&
     727         320 :             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         320 :         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         320 :         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         319 : enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
     749             : {
     750         319 :         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         232 : char * httpread_uri_get(struct httpread *h)
     758             : {
     759         232 :         return h->uri;
     760             : }
     761             : 
     762             : 
     763             : /* httpread_reply_code_get -- When reply is ready, returns reply code */
     764         141 : int httpread_reply_code_get(struct httpread *h)
     765             : {
     766         141 :         return h->reply_code;
     767             : }
     768             : 
     769             : 
     770             : /* httpread_length_get -- When file is ready, returns file length. */
     771          27 : int httpread_length_get(struct httpread *h)
     772             : {
     773          27 :         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         207 : void * httpread_data_get(struct httpread *h)
     782             : {
     783         207 :         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          80 : char * httpread_hdr_line_get(struct httpread *h, const char *tag)
     806             : {
     807          80 :         int tag_len = os_strlen(tag);
     808          80 :         char *hdr = h->hdr;
     809          80 :         hdr = os_strchr(hdr, '\n');
     810          80 :         if (hdr == NULL)
     811           0 :                 return NULL;
     812          80 :         hdr++;
     813             :         for (;;) {
     814         322 :                 if (!os_strncasecmp(hdr, tag, tag_len)) {
     815          79 :                         hdr += tag_len;
     816         228 :                         while (*hdr == ' ' || *hdr == '\t')
     817          70 :                                 hdr++;
     818          79 :                         return hdr;
     819             :                 }
     820         243 :                 hdr = os_strchr(hdr, '\n');
     821         243 :                 if (hdr == NULL)
     822           1 :                         return NULL;
     823         242 :                 hdr++;
     824         242 :         }
     825             : }

Generated by: LCOV version 1.10