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 */
50struct 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 */
98static 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
120static 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 */
127void 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 */
145static 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 */
156static 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
199static 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
355bad:
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 */
367static 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
684bad:
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
690get_more:
691	wpa_printf(MSG_DEBUG, "httpread: get more (%p)", h);
692	return;
693
694got_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 */
729struct 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
762fail:
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. */
771enum 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 */
780char * 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 */
787int 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. */
794int 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 */
804void * 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 */
814char * 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 */
828char * 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