• 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 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  */
word_eq(char * s1,char * s2)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  */
hex_value(int c)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  */
httpread_destroy(struct httpread * h)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  */
httpread_timeout_handler(void * eloop_data,void * user_ctx)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  */
httpread_hdr_option_analyze(struct httpread * h,char * hbp)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 	    word_eq(hbp, "TRANSFER-ENCODING:")) {
211 		while (isgraph(*hbp))
212 			hbp++;
213 		while (*hbp == ' ' || *hbp == '\t')
214 			hbp++;
215 		/* There should (?) be no encodings of interest
216 		 * other than chunked...
217 		 */
218 		if (word_eq(hbp, "CHUNKED")) {
219 			h->chunked = 1;
220 			h->in_chunk_data = 0;
221 			/* ignore possible ;<parameters> */
222 		}
223 		return 0;
224 	}
225 	/* skip anything we don't know, which is a lot */
226 	return 0;
227 }
228 
229 
httpread_hdr_analyze(struct httpread * h)230 static int httpread_hdr_analyze(struct httpread *h)
231 {
232 	char *hbp = h->hdr;      /* pointer into h->hdr */
233 	int standard_first_line = 1;
234 
235 	/* First line is special */
236 	h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN;
237 	if (!isgraph(*hbp))
238 		goto bad;
239 	if (os_strncmp(hbp, "HTTP/", 5) == 0) {
240 		h->hdr_type = HTTPREAD_HDR_TYPE_REPLY;
241 		standard_first_line = 0;
242 		hbp += 5;
243 		if (hbp[0] == '1' && hbp[1] == '.' &&
244 		    isdigit(hbp[2]) && hbp[2] != '0')
245 			h->version = 1;
246 		while (isgraph(*hbp))
247 			hbp++;
248 		while (*hbp == ' ' || *hbp == '\t')
249 			hbp++;
250 		if (!isdigit(*hbp))
251 			goto bad;
252 		h->reply_code = atol(hbp);
253 	} else if (word_eq(hbp, "GET"))
254 		h->hdr_type = HTTPREAD_HDR_TYPE_GET;
255 	else if (word_eq(hbp, "HEAD"))
256 		h->hdr_type = HTTPREAD_HDR_TYPE_HEAD;
257 	else if (word_eq(hbp, "POST"))
258 		h->hdr_type = HTTPREAD_HDR_TYPE_POST;
259 	else if (word_eq(hbp, "PUT"))
260 		h->hdr_type = HTTPREAD_HDR_TYPE_PUT;
261 	else if (word_eq(hbp, "DELETE"))
262 		h->hdr_type = HTTPREAD_HDR_TYPE_DELETE;
263 	else if (word_eq(hbp, "TRACE"))
264 		h->hdr_type = HTTPREAD_HDR_TYPE_TRACE;
265 	else if (word_eq(hbp, "CONNECT"))
266 		h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT;
267 	else if (word_eq(hbp, "NOTIFY"))
268 		h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY;
269 	else if (word_eq(hbp, "M-SEARCH"))
270 		h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH;
271 	else if (word_eq(hbp, "M-POST"))
272 		h->hdr_type = HTTPREAD_HDR_TYPE_M_POST;
273 	else if (word_eq(hbp, "SUBSCRIBE"))
274 		h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE;
275 	else if (word_eq(hbp, "UNSUBSCRIBE"))
276 		h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE;
277 	else {
278 	}
279 
280 	if (standard_first_line) {
281 		char *rawuri;
282 		char *uri;
283 		/* skip type */
284 		while (isgraph(*hbp))
285 			hbp++;
286 		while (*hbp == ' ' || *hbp == '\t')
287 			hbp++;
288 		/* parse uri.
289 		 * Find length, allocate memory for translated
290 		 * copy, then translate by changing %<hex><hex>
291 		 * into represented value.
292 		 */
293 		rawuri = hbp;
294 		while (isgraph(*hbp))
295 			hbp++;
296 		h->uri = os_malloc((hbp - rawuri) + 1);
297 		if (h->uri == NULL)
298 			goto bad;
299 		uri = h->uri;
300 		while (rawuri < hbp) {
301 			int c = *rawuri;
302 			if (c == '%' &&
303 			    isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
304 				*uri++ = (hex_value(rawuri[1]) << 4) |
305 					hex_value(rawuri[2]);
306 				rawuri += 3;
307 			} else {
308 				*uri++ = c;
309 				rawuri++;
310 			}
311 		}
312 		*uri = 0;       /* null terminate */
313 		while (isgraph(*hbp))
314 			hbp++;
315 		while (*hbp == ' ' || *hbp == '\t')
316 			hbp++;
317 		/* get version */
318 		if (0 == strncmp(hbp, "HTTP/", 5)) {
319 			hbp += 5;
320 			if (hbp[0] == '1' && hbp[1] == '.' &&
321 			    isdigit(hbp[2]) && hbp[2] != '0')
322 				h->version = 1;
323 		}
324 	}
325 	/* skip rest of line */
326 	while (*hbp)
327 		if (*hbp++ == '\n')
328 			break;
329 
330 	/* Remainder of lines are options, in any order;
331 	 * or empty line to terminate
332 	 */
333 	for (;;) {
334 		/* Empty line to terminate */
335 		if (hbp[0] == '\n' ||
336 		    (hbp[0] == '\r' && hbp[1] == '\n'))
337 			break;
338 		if (!isgraph(*hbp))
339 			goto bad;
340 		if (httpread_hdr_option_analyze(h, hbp))
341 			goto bad;
342 		/* skip line */
343 		while (*hbp)
344 			if (*hbp++ == '\n')
345 				break;
346 	}
347 
348 	/* chunked overrides content-length always */
349 	if (h->chunked)
350 		h->got_content_length = 0;
351 
352 	/* For some types, we should not try to read a body
353 	 * This is in addition to the application determining
354 	 * that we should not read a body.
355 	 */
356 	switch (h->hdr_type) {
357 	case HTTPREAD_HDR_TYPE_REPLY:
358 		/* Some codes can have a body and some not.
359 		 * For now, just assume that any other than 200
360 		 * do not...
361 		 */
362 		if (h->reply_code != 200)
363 			h->max_bytes = 0;
364 		break;
365 	case HTTPREAD_HDR_TYPE_GET:
366 	case HTTPREAD_HDR_TYPE_HEAD:
367 		/* in practice it appears that it is assumed
368 		 * that GETs have a body length of 0... ?
369 		 */
370 		if (h->chunked == 0 && h->got_content_length == 0)
371 			h->max_bytes = 0;
372 		break;
373 	case HTTPREAD_HDR_TYPE_POST:
374 	case HTTPREAD_HDR_TYPE_PUT:
375 	case HTTPREAD_HDR_TYPE_DELETE:
376 	case HTTPREAD_HDR_TYPE_TRACE:
377 	case HTTPREAD_HDR_TYPE_CONNECT:
378 	case HTTPREAD_HDR_TYPE_NOTIFY:
379 	case HTTPREAD_HDR_TYPE_M_SEARCH:
380 	case HTTPREAD_HDR_TYPE_M_POST:
381 	case HTTPREAD_HDR_TYPE_SUBSCRIBE:
382 	case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
383 	default:
384 		break;
385 	}
386 
387 	return 0;
388 
389 bad:
390 	/* Error */
391 	return -1;
392 }
393 
394 
395 /* httpread_read_handler -- called when socket ready to read
396  *
397  * Note: any extra data we read past end of transmitted file is ignored;
398  * if we were to support keeping connections open for multiple files then
399  * this would have to be addressed.
400  */
httpread_read_handler(int sd,void * eloop_ctx,void * sock_ctx)401 static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
402 {
403 	struct httpread *h = sock_ctx;
404 	int nread;
405 	char *rbp;      /* pointer into read buffer */
406 	char *hbp;      /* pointer into header buffer */
407 	char *bbp;      /* pointer into body buffer */
408 	char readbuf[HTTPREAD_READBUF_SIZE];  /* temp use to read into */
409 
410 	if (httpread_debug >= 20)
411 		wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h);
412 
413 	/* read some at a time, then search for the interal
414 	 * boundaries between header and data and etc.
415 	 */
416 	nread = read(h->sd, readbuf, sizeof(readbuf));
417 	if (nread < 0)
418 		goto bad;
419 	if (nread == 0) {
420 		/* end of transmission... this may be normal
421 		 * or may be an error... in some cases we can't
422 		 * tell which so we must assume it is normal then.
423 		 */
424 		if (!h->got_hdr) {
425 			/* Must at least have completed header */
426 			wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h);
427 			goto bad;
428 		}
429 		if (h->chunked || h->got_content_length) {
430 			/* Premature EOF; e.g. dropped connection */
431 			wpa_printf(MSG_DEBUG,
432 				   "httpread premature eof(%p) %d/%d",
433 				   h, h->body_nbytes,
434 				   h->content_length);
435 			goto bad;
436 		}
437 		/* No explicit length, hopefully we have all the data
438 		 * although dropped connections can cause false
439 		 * end
440 		 */
441 		if (httpread_debug >= 10)
442 			wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
443 			h->got_body = 1;
444 			goto got_file;
445 	}
446 	rbp = readbuf;
447 
448 	/* Header consists of text lines (terminated by both CR and LF)
449 	 * and an empty line (CR LF only).
450 	 */
451 	if (!h->got_hdr) {
452 		hbp = h->hdr + h->hdr_nbytes;
453 		/* add to headers until:
454 		 *      -- we run out of data in read buffer
455 		 *      -- or, we run out of header buffer room
456 		 *      -- or, we get double CRLF in headers
457 		 */
458 		for (;;) {
459 			if (nread == 0)
460 				goto get_more;
461 			if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
462 				goto bad;
463 			}
464 			*hbp++ = *rbp++;
465 			nread--;
466 			h->hdr_nbytes++;
467 			if (h->hdr_nbytes >= 4 &&
468 			    hbp[-1] == '\n' &&
469 			    hbp[-2] == '\r' &&
470 			    hbp[-3] == '\n' &&
471 			    hbp[-4] == '\r' ) {
472 				h->got_hdr = 1;
473 				*hbp = 0;       /* null terminate */
474 				break;
475 			}
476 		}
477 		/* here we've just finished reading the header */
478 		if (httpread_hdr_analyze(h)) {
479 			wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h);
480 			goto bad;
481 		}
482 		if (h->max_bytes == 0) {
483 			if (httpread_debug >= 10)
484 				wpa_printf(MSG_DEBUG,
485 					   "httpread no body hdr end(%p)", h);
486 			goto got_file;
487 		}
488 		if (h->got_content_length && h->content_length == 0) {
489 			if (httpread_debug >= 10)
490 				wpa_printf(MSG_DEBUG,
491 					   "httpread zero content length(%p)",
492 					   h);
493 			goto got_file;
494 		}
495 	}
496 
497 	/* Certain types of requests never have data and so
498 	 * must be specially recognized.
499 	 */
500 	if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) ||
501 	    !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) ||
502 	    !os_strncasecmp(h->hdr, "HEAD", 4) ||
503 	    !os_strncasecmp(h->hdr, "GET", 3)) {
504 		if (!h->got_body) {
505 			if (httpread_debug >= 10)
506 				wpa_printf(MSG_DEBUG,
507 					   "httpread NO BODY for sp. type");
508 		}
509 		h->got_body = 1;
510 		goto got_file;
511 	}
512 
513 	/* Data can be just plain binary data, or if "chunked"
514 	 * consists of chunks each with a header, ending with
515 	 * an ending header.
516 	 */
517 	if (nread == 0)
518 		goto get_more;
519 	if (!h->got_body) {
520 		/* Here to get (more of) body */
521 		/* ensure we have enough room for worst case for body
522 		 * plus a null termination character
523 		 */
524 		if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
525 			char *new_body;
526 			int new_alloc_nbytes;
527 
528 			if (h->body_nbytes >= h->max_bytes)
529 				goto bad;
530 			new_alloc_nbytes = h->body_alloc_nbytes +
531 				HTTPREAD_BODYBUF_DELTA;
532 			/* For content-length case, the first time
533 			 * through we allocate the whole amount
534 			 * we need.
535 			 */
536 			if (h->got_content_length &&
537 			    new_alloc_nbytes < (h->content_length + 1))
538 				new_alloc_nbytes = h->content_length + 1;
539 			if ((new_body = os_realloc(h->body, new_alloc_nbytes))
540 			    == NULL)
541 				goto bad;
542 
543 			h->body = new_body;
544 			h->body_alloc_nbytes = new_alloc_nbytes;
545 		}
546 		/* add bytes */
547 		bbp = h->body + h->body_nbytes;
548 		for (;;) {
549 			int ncopy;
550 			/* See if we need to stop */
551 			if (h->chunked && h->in_chunk_data == 0) {
552 				/* in chunk header */
553 				char *cbp = h->body + h->chunk_start;
554 				if (bbp-cbp >= 2 && bbp[-2] == '\r' &&
555 				    bbp[-1] == '\n') {
556 					/* end of chunk hdr line */
557 					/* hdr line consists solely
558 					 * of a hex numeral and CFLF
559 					 */
560 					if (!isxdigit(*cbp))
561 						goto bad;
562 					h->chunk_size = strtoul(cbp, NULL, 16);
563 					/* throw away chunk header
564 					 * so we have only real data
565 					 */
566 					h->body_nbytes = h->chunk_start;
567 					bbp = cbp;
568 					if (h->chunk_size == 0) {
569 						/* end of chunking */
570 						/* trailer follows */
571 						h->in_trailer = 1;
572 						if (httpread_debug >= 20)
573 							wpa_printf(
574 								MSG_DEBUG,
575 								"httpread end chunks(%p)", h);
576 						break;
577 					}
578 					h->in_chunk_data = 1;
579 					/* leave chunk_start alone */
580 				}
581 			} else if (h->chunked) {
582 				/* in chunk data */
583 				if ((h->body_nbytes - h->chunk_start) ==
584 				    (h->chunk_size + 2)) {
585 					/* end of chunk reached,
586 					 * new chunk starts
587 					 */
588 					/* check chunk ended w/ CRLF
589 					 * which we'll throw away
590 					 */
591 					if (bbp[-1] == '\n' &&
592 					    bbp[-2] == '\r') {
593 					} else
594 						goto bad;
595 					h->body_nbytes -= 2;
596 					bbp -= 2;
597 					h->chunk_start = h->body_nbytes;
598 					h->in_chunk_data = 0;
599 					h->chunk_size = 0; /* just in case */
600 				}
601 			} else if (h->got_content_length &&
602 				   h->body_nbytes >= h->content_length) {
603 				h->got_body = 1;
604 				if (httpread_debug >= 10)
605 					wpa_printf(
606 						MSG_DEBUG,
607 						"httpread got content(%p)", h);
608 				goto got_file;
609 			}
610 			if (nread <= 0)
611 				break;
612 			/* Now transfer. Optimize using memcpy where we can. */
613 			if (h->chunked && h->in_chunk_data) {
614 				/* copy up to remainder of chunk data
615 				 * plus the required CR+LF at end
616 				 */
617 				ncopy = (h->chunk_start + h->chunk_size + 2) -
618 					h->body_nbytes;
619 			} else if (h->chunked) {
620 				/*in chunk header -- don't optimize */
621 				*bbp++ = *rbp++;
622 				nread--;
623 				h->body_nbytes++;
624 				continue;
625 			} else if (h->got_content_length) {
626 				ncopy = h->content_length - h->body_nbytes;
627 			} else {
628 				ncopy = nread;
629 			}
630 			/* Note: should never be 0 */
631 			if (ncopy > nread)
632 				ncopy = nread;
633 			os_memcpy(bbp, rbp, ncopy);
634 			bbp += ncopy;
635 			h->body_nbytes += ncopy;
636 			rbp += ncopy;
637 			nread -= ncopy;
638 		}       /* body copy loop */
639 	}       /* !got_body */
640 	if (h->chunked && h->in_trailer) {
641 		/* If "chunked" then there is always a trailer,
642 		 * consisting of zero or more non-empty lines
643 		 * ending with CR LF and then an empty line w/ CR LF.
644 		 * We do NOT support trailers except to skip them --
645 		 * this is supported (generally) by the http spec.
646 		 */
647 		bbp = h->body + h->body_nbytes;
648 		for (;;) {
649 			int c;
650 			if (nread <= 0)
651 				break;
652 			c = *rbp++;
653 			nread--;
654 			switch (h->trailer_state) {
655 			case trailer_line_begin:
656 				if (c == '\r')
657 					h->trailer_state = trailer_empty_cr;
658 				else
659 					h->trailer_state = trailer_nonempty;
660 				break;
661 			case trailer_empty_cr:
662 				/* end empty line */
663 				if (c == '\n') {
664 					h->trailer_state = trailer_line_begin;
665 					h->in_trailer = 0;
666 					if (httpread_debug >= 10)
667 						wpa_printf(
668 							MSG_DEBUG,
669 							"httpread got content(%p)", h);
670 					h->got_body = 1;
671 					goto got_file;
672 				}
673 				h->trailer_state = trailer_nonempty;
674 				break;
675 			case trailer_nonempty:
676 				if (c == '\r')
677 					h->trailer_state = trailer_nonempty_cr;
678 				break;
679 			case trailer_nonempty_cr:
680 				if (c == '\n')
681 					h->trailer_state = trailer_line_begin;
682 				else
683 					h->trailer_state = trailer_nonempty;
684 				break;
685 			}
686 		}
687 	}
688 	goto get_more;
689 
690 bad:
691 	/* Error */
692 	wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h);
693 	(*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR);
694 	return;
695 
696 get_more:
697 	return;
698 
699 got_file:
700 	if (httpread_debug >= 10)
701 		wpa_printf(MSG_DEBUG,
702 			   "httpread got file %d bytes type %d",
703 			   h->body_nbytes, h->hdr_type);
704 	/* Null terminate for convenience of some applications */
705 	if (h->body)
706 		h->body[h->body_nbytes] = 0; /* null terminate */
707 	h->got_file = 1;
708 	/* Assume that we do NOT support keeping connection alive,
709 	 * and just in case somehow we don't get destroyed right away,
710 	 * unregister now.
711 	 */
712 	if (h->sd_registered)
713 		eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
714 	h->sd_registered = 0;
715 	/* The application can destroy us whenever they feel like...
716 	 * cancel timeout.
717 	 */
718 	if (h->to_registered)
719 		eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
720 	h->to_registered = 0;
721 	(*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY);
722 }
723 
724 
725 /* httpread_create -- start a new reading session making use of eloop.
726  * The new instance will use the socket descriptor for reading (until
727  * it gets a file and not after) but will not close the socket, even
728  * when the instance is destroyed (the application must do that).
729  * Return NULL on error.
730  *
731  * Provided that httpread_create successfully returns a handle,
732  * the callback fnc is called to handle httpread_event events.
733  * The caller should do destroy on any errors or unknown events.
734  *
735  * Pass max_bytes == 0 to not read body at all (required for e.g.
736  * reply to HEAD request).
737  */
httpread_create(int sd,void (* cb)(struct httpread * handle,void * cookie,enum httpread_event e),void * cookie,int max_bytes,int timeout_seconds)738 struct httpread * httpread_create(
739 	int sd,	 /* descriptor of TCP socket to read from */
740 	void (*cb)(struct httpread *handle, void *cookie,
741 		   enum httpread_event e),  /* call on event */
742 	void *cookie,    /* pass to callback */
743 	int max_bytes,	  /* maximum body size else abort it */
744 	int timeout_seconds     /* 0; or total duration timeout period */
745 	)
746 {
747 	struct httpread *h = NULL;
748 
749 	h = os_zalloc(sizeof(*h));
750 	if (h == NULL)
751 		goto fail;
752 	h->sd = sd;
753 	h->cb = cb;
754 	h->cookie = cookie;
755 	h->max_bytes = max_bytes;
756 	h->timeout_seconds = timeout_seconds;
757 
758 	if (timeout_seconds > 0) {
759 		if (eloop_register_timeout(timeout_seconds, 0,
760 					   httpread_timeout_handler,
761 					   NULL, h)) {
762 			/* No way to recover (from malloc failure) */
763 			goto fail;
764 		}
765 		h->to_registered = 1;
766 	}
767 	if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler,
768 				NULL, h)) {
769 		/* No way to recover (from malloc failure) */
770 		goto fail;
771 	}
772 	h->sd_registered = 1;
773 	return h;
774 
775 fail:
776 
777 	/* Error */
778 	httpread_destroy(h);
779 	return NULL;
780 }
781 
782 
783 /* httpread_hdr_type_get -- When file is ready, returns header type. */
httpread_hdr_type_get(struct httpread * h)784 enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
785 {
786 	return h->hdr_type;
787 }
788 
789 
790 /* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
791  * or possibly NULL (which would be an error).
792  */
httpread_uri_get(struct httpread * h)793 char * httpread_uri_get(struct httpread *h)
794 {
795 	return h->uri;
796 }
797 
798 
799 /* httpread_reply_code_get -- When reply is ready, returns reply code */
httpread_reply_code_get(struct httpread * h)800 int httpread_reply_code_get(struct httpread *h)
801 {
802 	return h->reply_code;
803 }
804 
805 
806 /* httpread_length_get -- When file is ready, returns file length. */
httpread_length_get(struct httpread * h)807 int httpread_length_get(struct httpread *h)
808 {
809 	return h->body_nbytes;
810 }
811 
812 
813 /* httpread_data_get -- When file is ready, returns file content
814  * with null byte appened.
815  * Might return NULL in some error condition.
816  */
httpread_data_get(struct httpread * h)817 void * httpread_data_get(struct httpread *h)
818 {
819 	return h->body ? h->body : "";
820 }
821 
822 
823 /* httpread_hdr_get -- When file is ready, returns header content
824  * with null byte appended.
825  * Might return NULL in some error condition.
826  */
httpread_hdr_get(struct httpread * h)827 char * httpread_hdr_get(struct httpread *h)
828 {
829 	return h->hdr;
830 }
831 
832 
833 /* httpread_hdr_line_get -- When file is ready, returns pointer
834  * to line within header content matching the given tag
835  * (after the tag itself and any spaces/tabs).
836  *
837  * The tag should end with a colon for reliable matching.
838  *
839  * If not found, returns NULL;
840  */
httpread_hdr_line_get(struct httpread * h,const char * tag)841 char * httpread_hdr_line_get(struct httpread *h, const char *tag)
842 {
843 	int tag_len = os_strlen(tag);
844 	char *hdr = h->hdr;
845 	hdr = os_strchr(hdr, '\n');
846 	if (hdr == NULL)
847 		return NULL;
848 	hdr++;
849 	for (;;) {
850 		if (!os_strncasecmp(hdr, tag, tag_len)) {
851 			hdr += tag_len;
852 			while (*hdr == ' ' || *hdr == '\t')
853 				hdr++;
854 			return hdr;
855 		}
856 		hdr = os_strchr(hdr, '\n');
857 		if (hdr == NULL)
858 			return NULL;
859 		hdr++;
860 	}
861 }
862