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