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