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