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