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 
     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 static int word_eq(char *s1, char *s2)
    109 {
    110 	int c1;
    111 	int c2;
    112 	int end1 = 0;
    113 	int end2 = 0;
    114 	for (;;) {
    115 		c1 = *s1++;
    116 		c2 = *s2++;
    117 		if (isalpha(c1) && isupper(c1))
    118 			c1 = tolower(c1);
    119 		if (isalpha(c2) && isupper(c2))
    120 			c2 = tolower(c2);
    121 		end1 = !isgraph(c1);
    122 		end2 = !isgraph(c2);
    123 		if (end1 || end2 || c1 != c2)
    124 			break;
    125 	}
    126 	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 void httpread_destroy(struct httpread *h)
    138 {
    139 	if (httpread_debug >= 10)
    140 		wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h);
    141 	if (!h)
    142 		return;
    143 
    144 	eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
    145 	eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
    146 	os_free(h->body);
    147 	os_free(h->uri);
    148 	os_memset(h, 0, sizeof(*h));  /* aid debugging */
    149 	h->sd = -1;     /* aid debugging */
    150 	os_free(h);
    151 }
    152 
    153 
    154 /* httpread_timeout_handler -- called on excessive total duration
    155  */
    156 static void httpread_timeout_handler(void *eloop_data, void *user_ctx)
    157 {
    158 	struct httpread *h = user_ctx;
    159 	wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h);
    160 	(*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT);
    161 }
    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 static int httpread_hdr_option_analyze(
    168 	struct httpread *h,
    169 	char *hbp       /* pointer to current line in header buffer */
    170 	)
    171 {
    172 	if (word_eq(hbp, "CONTENT-LENGTH:")) {
    173 		while (isgraph(*hbp))
    174 			hbp++;
    175 		while (*hbp == ' ' || *hbp == '\t')
    176 			hbp++;
    177 		if (!isdigit(*hbp))
    178 			return -1;
    179 		h->content_length = atol(hbp);
    180 		h->got_content_length = 1;
    181 		return 0;
    182 	}
    183 	if (word_eq(hbp, "TRANSFER_ENCODING:") ||
    184 	    word_eq(hbp, "TRANSFER-ENCODING:")) {
    185 		while (isgraph(*hbp))
    186 			hbp++;
    187 		while (*hbp == ' ' || *hbp == '\t')
    188 			hbp++;
    189 		/* There should (?) be no encodings of interest
    190 		 * other than chunked...
    191 		 */
    192 		if (word_eq(hbp, "CHUNKED")) {
    193 			h->chunked = 1;
    194 			h->in_chunk_data = 0;
    195 			/* ignore possible ;<parameters> */
    196 		}
    197 		return 0;
    198 	}
    199 	/* skip anything we don't know, which is a lot */
    200 	return 0;
    201 }
    202 
    203 
    204 static int httpread_hdr_analyze(struct httpread *h)
    205 {
    206 	char *hbp = h->hdr;      /* pointer into h->hdr */
    207 	int standard_first_line = 1;
    208 
    209 	/* First line is special */
    210 	h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN;
    211 	if (!isgraph(*hbp))
    212 		goto bad;
    213 	if (os_strncmp(hbp, "HTTP/", 5) == 0) {
    214 		h->hdr_type = HTTPREAD_HDR_TYPE_REPLY;
    215 		standard_first_line = 0;
    216 		hbp += 5;
    217 		if (hbp[0] == '1' && hbp[1] == '.' &&
    218 		    isdigit(hbp[2]) && hbp[2] != '0')
    219 			h->version = 1;
    220 		while (isgraph(*hbp))
    221 			hbp++;
    222 		while (*hbp == ' ' || *hbp == '\t')
    223 			hbp++;
    224 		if (!isdigit(*hbp))
    225 			goto bad;
    226 		h->reply_code = atol(hbp);
    227 	} else if (word_eq(hbp, "GET"))
    228 		h->hdr_type = HTTPREAD_HDR_TYPE_GET;
    229 	else if (word_eq(hbp, "HEAD"))
    230 		h->hdr_type = HTTPREAD_HDR_TYPE_HEAD;
    231 	else if (word_eq(hbp, "POST"))
    232 		h->hdr_type = HTTPREAD_HDR_TYPE_POST;
    233 	else if (word_eq(hbp, "PUT"))
    234 		h->hdr_type = HTTPREAD_HDR_TYPE_PUT;
    235 	else if (word_eq(hbp, "DELETE"))
    236 		h->hdr_type = HTTPREAD_HDR_TYPE_DELETE;
    237 	else if (word_eq(hbp, "TRACE"))
    238 		h->hdr_type = HTTPREAD_HDR_TYPE_TRACE;
    239 	else if (word_eq(hbp, "CONNECT"))
    240 		h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT;
    241 	else if (word_eq(hbp, "NOTIFY"))
    242 		h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY;
    243 	else if (word_eq(hbp, "M-SEARCH"))
    244 		h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH;
    245 	else if (word_eq(hbp, "M-POST"))
    246 		h->hdr_type = HTTPREAD_HDR_TYPE_M_POST;
    247 	else if (word_eq(hbp, "SUBSCRIBE"))
    248 		h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE;
    249 	else if (word_eq(hbp, "UNSUBSCRIBE"))
    250 		h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE;
    251 	else {
    252 	}
    253 
    254 	if (standard_first_line) {
    255 		char *rawuri;
    256 		char *uri;
    257 		/* skip type */
    258 		while (isgraph(*hbp))
    259 			hbp++;
    260 		while (*hbp == ' ' || *hbp == '\t')
    261 			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 		rawuri = hbp;
    268 		while (isgraph(*hbp))
    269 			hbp++;
    270 		h->uri = os_malloc((hbp - rawuri) + 1);
    271 		if (h->uri == NULL)
    272 			goto bad;
    273 		uri = h->uri;
    274 		while (rawuri < hbp) {
    275 			int c = *rawuri;
    276 			if (c == '%' &&
    277 			    isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
    278 				*uri++ = hex2byte(rawuri + 1);
    279 				rawuri += 3;
    280 			} else {
    281 				*uri++ = c;
    282 				rawuri++;
    283 			}
    284 		}
    285 		*uri = 0;       /* null terminate */
    286 		while (isgraph(*hbp))
    287 			hbp++;
    288 		while (*hbp == ' ' || *hbp == '\t')
    289 			hbp++;
    290 		/* get version */
    291 		if (0 == strncmp(hbp, "HTTP/", 5)) {
    292 			hbp += 5;
    293 			if (hbp[0] == '1' && hbp[1] == '.' &&
    294 			    isdigit(hbp[2]) && hbp[2] != '0')
    295 				h->version = 1;
    296 		}
    297 	}
    298 	/* skip rest of line */
    299 	while (*hbp)
    300 		if (*hbp++ == '\n')
    301 			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 		if (hbp[0] == '\n' ||
    309 		    (hbp[0] == '\r' && hbp[1] == '\n'))
    310 			break;
    311 		if (!isgraph(*hbp))
    312 			goto bad;
    313 		if (httpread_hdr_option_analyze(h, hbp))
    314 			goto bad;
    315 		/* skip line */
    316 		while (*hbp)
    317 			if (*hbp++ == '\n')
    318 				break;
    319 	}
    320 
    321 	/* chunked overrides content-length always */
    322 	if (h->chunked)
    323 		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 	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 		if (h->reply_code != 200)
    336 			h->max_bytes = 0;
    337 		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 		if (h->chunked == 0 && h->got_content_length == 0)
    344 			h->max_bytes = 0;
    345 		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 		break;
    358 	}
    359 
    360 	return 0;
    361 
    362 bad:
    363 	/* Error */
    364 	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 static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
    375 {
    376 	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 	nread = read(h->sd, readbuf, sizeof(readbuf));
    390 	if (nread < 0)
    391 		goto bad;
    392 	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 		if (!h->got_hdr) {
    398 			/* Must at least have completed header */
    399 			wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h);
    400 			goto bad;
    401 		}
    402 		if (h->chunked || h->got_content_length) {
    403 			/* Premature EOF; e.g. dropped connection */
    404 			wpa_printf(MSG_DEBUG,
    405 				   "httpread premature eof(%p) %d/%d",
    406 				   h, h->body_nbytes,
    407 				   h->content_length);
    408 			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 		h->got_body = 1;
    417 		goto got_file;
    418 	}
    419 	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 	if (!h->got_hdr) {
    425 		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 			if (nread == 0)
    433 				goto get_more;
    434 			if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
    435 				goto bad;
    436 			}
    437 			*hbp++ = *rbp++;
    438 			nread--;
    439 			h->hdr_nbytes++;
    440 			if (h->hdr_nbytes >= 4 &&
    441 			    hbp[-1] == '\n' &&
    442 			    hbp[-2] == '\r' &&
    443 			    hbp[-3] == '\n' &&
    444 			    hbp[-4] == '\r' ) {
    445 				h->got_hdr = 1;
    446 				*hbp = 0;       /* null terminate */
    447 				break;
    448 			}
    449 		}
    450 		/* here we've just finished reading the header */
    451 		if (httpread_hdr_analyze(h)) {
    452 			wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h);
    453 			goto bad;
    454 		}
    455 		if (h->max_bytes == 0) {
    456 			if (httpread_debug >= 10)
    457 				wpa_printf(MSG_DEBUG,
    458 					   "httpread no body hdr end(%p)", h);
    459 			goto got_file;
    460 		}
    461 		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 			goto got_file;
    467 		}
    468 	}
    469 
    470 	/* Certain types of requests never have data and so
    471 	 * must be specially recognized.
    472 	 */
    473 	if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) ||
    474 	    !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) ||
    475 	    !os_strncasecmp(h->hdr, "HEAD", 4) ||
    476 	    !os_strncasecmp(h->hdr, "GET", 3)) {
    477 		if (!h->got_body) {
    478 			if (httpread_debug >= 10)
    479 				wpa_printf(MSG_DEBUG,
    480 					   "httpread NO BODY for sp. type");
    481 		}
    482 		h->got_body = 1;
    483 		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 	if (nread == 0)
    491 		goto get_more;
    492 	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 		if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
    498 			char *new_body;
    499 			int new_alloc_nbytes;
    500 
    501 			if (h->body_nbytes >= h->max_bytes)
    502 				goto bad;
    503 			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 			if (h->got_content_length &&
    510 			    new_alloc_nbytes < (h->content_length + 1))
    511 				new_alloc_nbytes = h->content_length + 1;
    512 			if ((new_body = os_realloc(h->body, new_alloc_nbytes))
    513 			    == NULL)
    514 				goto bad;
    515 
    516 			h->body = new_body;
    517 			h->body_alloc_nbytes = new_alloc_nbytes;
    518 		}
    519 		/* add bytes */
    520 		bbp = h->body + h->body_nbytes;
    521 		for (;;) {
    522 			int ncopy;
    523 			/* See if we need to stop */
    524 			if (h->chunked && h->in_chunk_data == 0) {
    525 				/* in chunk header */
    526 				char *cbp = h->body + h->chunk_start;
    527 				if (bbp-cbp >= 2 && bbp[-2] == '\r' &&
    528 				    bbp[-1] == '\n') {
    529 					/* end of chunk hdr line */
    530 					/* hdr line consists solely
    531 					 * of a hex numeral and CFLF
    532 					 */
    533 					if (!isxdigit(*cbp))
    534 						goto bad;
    535 					h->chunk_size = strtoul(cbp, NULL, 16);
    536 					/* throw away chunk header
    537 					 * so we have only real data
    538 					 */
    539 					h->body_nbytes = h->chunk_start;
    540 					bbp = cbp;
    541 					if (h->chunk_size == 0) {
    542 						/* end of chunking */
    543 						/* trailer follows */
    544 						h->in_trailer = 1;
    545 						if (httpread_debug >= 20)
    546 							wpa_printf(
    547 								MSG_DEBUG,
    548 								"httpread end chunks(%p)", h);
    549 						break;
    550 					}
    551 					h->in_chunk_data = 1;
    552 					/* leave chunk_start alone */
    553 				}
    554 			} else if (h->chunked) {
    555 				/* in chunk data */
    556 				if ((h->body_nbytes - h->chunk_start) ==
    557 				    (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 					if (bbp[-1] == '\n' &&
    565 					    bbp[-2] == '\r') {
    566 					} else
    567 						goto bad;
    568 					h->body_nbytes -= 2;
    569 					bbp -= 2;
    570 					h->chunk_start = h->body_nbytes;
    571 					h->in_chunk_data = 0;
    572 					h->chunk_size = 0; /* just in case */
    573 				}
    574 			} else if (h->got_content_length &&
    575 				   h->body_nbytes >= h->content_length) {
    576 				h->got_body = 1;
    577 				if (httpread_debug >= 10)
    578 					wpa_printf(
    579 						MSG_DEBUG,
    580 						"httpread got content(%p)", h);
    581 				goto got_file;
    582 			}
    583 			if (nread <= 0)
    584 				break;
    585 			/* Now transfer. Optimize using memcpy where we can. */
    586 			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 				ncopy = (h->chunk_start + h->chunk_size + 2) -
    591 					h->body_nbytes;
    592 			} else if (h->chunked) {
    593 				/*in chunk header -- don't optimize */
    594 				*bbp++ = *rbp++;
    595 				nread--;
    596 				h->body_nbytes++;
    597 				continue;
    598 			} else if (h->got_content_length) {
    599 				ncopy = h->content_length - h->body_nbytes;
    600 			} else {
    601 				ncopy = nread;
    602 			}
    603 			/* Note: should never be 0 */
    604 			if (ncopy > nread)
    605 				ncopy = nread;
    606 			os_memcpy(bbp, rbp, ncopy);
    607 			bbp += ncopy;
    608 			h->body_nbytes += ncopy;
    609 			rbp += ncopy;
    610 			nread -= ncopy;
    611 		}       /* body copy loop */
    612 	}       /* !got_body */
    613 	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 			if (nread <= 0)
    623 				break;
    624 			c = *rbp++;
    625 			nread--;
    626 			switch (h->trailer_state) {
    627 			case trailer_line_begin:
    628 				if (c == '\r')
    629 					h->trailer_state = trailer_empty_cr;
    630 				else
    631 					h->trailer_state = trailer_nonempty;
    632 				break;
    633 			case trailer_empty_cr:
    634 				/* end empty line */
    635 				if (c == '\n') {
    636 					h->trailer_state = trailer_line_begin;
    637 					h->in_trailer = 0;
    638 					if (httpread_debug >= 10)
    639 						wpa_printf(
    640 							MSG_DEBUG,
    641 							"httpread got content(%p)", h);
    642 					h->got_body = 1;
    643 					goto got_file;
    644 				}
    645 				h->trailer_state = trailer_nonempty;
    646 				break;
    647 			case trailer_nonempty:
    648 				if (c == '\r')
    649 					h->trailer_state = trailer_nonempty_cr;
    650 				break;
    651 			case trailer_nonempty_cr:
    652 				if (c == '\n')
    653 					h->trailer_state = trailer_line_begin;
    654 				else
    655 					h->trailer_state = trailer_nonempty;
    656 				break;
    657 			}
    658 		}
    659 	}
    660 	goto get_more;
    661 
    662 bad:
    663 	/* Error */
    664 	wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h);
    665 	(*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR);
    666 	return;
    667 
    668 get_more:
    669 	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 	if (h->body)
    678 		h->body[h->body_nbytes] = 0; /* null terminate */
    679 	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 	eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
    685 	/* The application can destroy us whenever they feel like...
    686 	 * cancel timeout.
    687 	 */
    688 	eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
    689 	(*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 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 	struct httpread *h = NULL;
    716 
    717 	h = os_zalloc(sizeof(*h));
    718 	if (h == NULL)
    719 		goto fail;
    720 	h->sd = sd;
    721 	h->cb = cb;
    722 	h->cookie = cookie;
    723 	h->max_bytes = max_bytes;
    724 	h->timeout_seconds = timeout_seconds;
    725 
    726 	if (timeout_seconds > 0 &&
    727 	    eloop_register_timeout(timeout_seconds, 0,
    728 				   httpread_timeout_handler, NULL, h)) {
    729 		/* No way to recover (from malloc failure) */
    730 		goto fail;
    731 	}
    732 	if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler,
    733 				NULL, h)) {
    734 		/* No way to recover (from malloc failure) */
    735 		goto fail;
    736 	}
    737 	return h;
    738 
    739 fail:
    740 
    741 	/* Error */
    742 	httpread_destroy(h);
    743 	return NULL;
    744 }
    745 
    746 
    747 /* httpread_hdr_type_get -- When file is ready, returns header type. */
    748 enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
    749 {
    750 	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 char * httpread_uri_get(struct httpread *h)
    758 {
    759 	return h->uri;
    760 }
    761 
    762 
    763 /* httpread_reply_code_get -- When reply is ready, returns reply code */
    764 int httpread_reply_code_get(struct httpread *h)
    765 {
    766 	return h->reply_code;
    767 }
    768 
    769 
    770 /* httpread_length_get -- When file is ready, returns file length. */
    771 int httpread_length_get(struct httpread *h)
    772 {
    773 	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 void * httpread_data_get(struct httpread *h)
    782 {
    783 	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 char * httpread_hdr_get(struct httpread *h)
    792 {
    793 	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 char * httpread_hdr_line_get(struct httpread *h, const char *tag)
    806 {
    807 	int tag_len = os_strlen(tag);
    808 	char *hdr = h->hdr;
    809 	hdr = os_strchr(hdr, '\n');
    810 	if (hdr == NULL)
    811 		return NULL;
    812 	hdr++;
    813 	for (;;) {
    814 		if (!os_strncasecmp(hdr, tag, tag_len)) {
    815 			hdr += tag_len;
    816 			while (*hdr == ' ' || *hdr == '\t')
    817 				hdr++;
    818 			return hdr;
    819 		}
    820 		hdr = os_strchr(hdr, '\n');
    821 		if (hdr == NULL)
    822 			return NULL;
    823 		hdr++;
    824 	}
    825 }
    826