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