xref: /freebsd/contrib/wpa/src/wps/httpread.c (revision f05cddf9)
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