• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
word_eq(char * s1,char * s2)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  */
httpread_destroy(struct httpread * h)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  */
httpread_timeout_handler(void * eloop_data,void * user_ctx)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  */
httpread_hdr_option_analyze(struct httpread * h,char * hbp)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 
httpread_hdr_analyze(struct httpread * h)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  */
httpread_read_handler(int sd,void * eloop_ctx,void * sock_ctx)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  */
httpread_create(int sd,void (* cb)(struct httpread * handle,void * cookie,enum httpread_event e),void * cookie,int max_bytes,int timeout_seconds)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. */
httpread_hdr_type_get(struct httpread * h)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  */
httpread_uri_get(struct httpread * h)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 */
httpread_reply_code_get(struct httpread * h)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. */
httpread_length_get(struct httpread * h)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  */
httpread_data_get(struct httpread * h)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  */
httpread_hdr_get(struct httpread * h)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  */
httpread_hdr_line_get(struct httpread * h,const char * tag)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