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