18d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/*
28d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * httpread - Manage reading file(s) from HTTP/TCP socket
38d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Author: Ted Merrill
48d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Copyright 2008 Atheros Communications
58d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
6c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt * This software may be distributed under the terms of the BSD license.
7c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt * See README for more details.
88d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
98d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * The files are buffered via internal callbacks from eloop, then presented to
108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * an application callback routine when completely read into memory. May also
118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * be used if no file is expected but just to get the header, including HTTP
128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * replies (e.g. HTTP/1.1 200 OK etc.).
138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * This does not attempt to be an optimally efficient implementation, but does
158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * attempt to be of reasonably small size and memory consumption; assuming that
168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * only small files are to be read. A maximum file size is provided by
178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * application and enforced.
188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * It is assumed that the application does not expect any of the following:
208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * -- transfer encoding other than chunked
218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * -- trailer fields
228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * It is assumed that, even if the other side requested that the connection be
238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * kept open, that we will close it (thus HTTP messages sent by application
248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * should have the connection closed field); this is allowed by HTTP/1.1 and
258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * simplifies things for us.
268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Other limitations:
288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * -- HTTP header may not exceed a hard-coded size.
298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Notes:
318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * This code would be massively simpler without some of the new features of
328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * HTTP/1.1, especially chunked data.
338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */
348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "includes.h"
368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "common.h"
388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "eloop.h"
398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "httpread.h"
408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* Tunable parameters */
438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define HTTPREAD_READBUF_SIZE 1024      /* read in chunks of this size */
448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define HTTPREAD_HEADER_MAX_SIZE 4096   /* max allowed for headers */
458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define HTTPREAD_BODYBUF_DELTA 4096     /* increase allocation by this */
468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* control instance -- actual definition (opaque to application)
498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */
508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstruct httpread {
518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* information from creation */
528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int sd;         /* descriptor of TCP socket to read from */
538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	void (*cb)(struct httpread *handle, void *cookie,
548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		    enum httpread_event e);  /* call on event */
558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	void *cookie;   /* pass to callback */
568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int max_bytes;          /* maximum file size else abort it */
578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int timeout_seconds;            /* 0 or total duration timeout period */
588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* dynamically used information follows */
608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int got_hdr;            /* nonzero when header is finalized */
628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	char hdr[HTTPREAD_HEADER_MAX_SIZE+1];   /* headers stored here */
638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int hdr_nbytes;
648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	enum httpread_hdr_type hdr_type;
668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int version;            /* 1 if we've seen 1.1 */
678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int reply_code;         /* for type REPLY, e.g. 200 for HTTP/1.1 200 OK */
688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int got_content_length; /* true if we know content length for sure */
698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int content_length;     /* body length,  iff got_content_length */
708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int chunked;            /* nonzero for chunked data */
718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	char *uri;
728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int got_body;           /* nonzero when body is finalized */
748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	char *body;
758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int body_nbytes;
768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int body_alloc_nbytes;  /* amount allocated */
778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int got_file;           /* here when we are done */
798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* The following apply if data is chunked: */
818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int in_chunk_data;      /* 0=in/at header, 1=in the data or tail*/
828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int chunk_start;        /* offset in body of chunk hdr or data */
838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int chunk_size;         /* data of chunk (not hdr or ending CRLF)*/
848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int in_trailer;         /* in header fields after data (chunked only)*/
858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	enum trailer_state {
868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		trailer_line_begin = 0,
878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		trailer_empty_cr,       /* empty line + CR */
888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		trailer_nonempty,
898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		trailer_nonempty_cr,
908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	} trailer_state;
918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt};
928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* Check words for equality, where words consist of graphical characters
958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * delimited by whitespace
968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Returns nonzero if "equal" doing case insensitive comparison.
978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */
988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic int word_eq(char *s1, char *s2)
998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int c1;
1018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int c2;
1028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int end1 = 0;
1038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int end2 = 0;
1048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	for (;;) {
1058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		c1 = *s1++;
1068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		c2 = *s2++;
1078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (isalpha(c1) && isupper(c1))
1088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			c1 = tolower(c1);
1098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (isalpha(c2) && isupper(c2))
1108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			c2 = tolower(c2);
1118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		end1 = !isgraph(c1);
1128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		end2 = !isgraph(c2);
1138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (end1 || end2 || c1 != c2)
1148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			break;
1158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return end1 && end2;  /* reached end of both words? */
1178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic void httpread_timeout_handler(void *eloop_data, void *user_ctx);
1218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* httpread_destroy -- if h is non-NULL, clean up
1238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * This must eventually be called by the application following
1248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * call of the application's callback and may be called
1258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * earlier if desired.
1268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */
1278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid httpread_destroy(struct httpread *h)
1288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
129cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt	wpa_printf(MSG_DEBUG, "httpread_destroy(%p)", h);
1308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (!h)
1318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return;
1328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
133fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt	eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
134fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt	eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
1358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	os_free(h->body);
1368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	os_free(h->uri);
1378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	os_memset(h, 0, sizeof(*h));  /* aid debugging */
1388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	h->sd = -1;     /* aid debugging */
1398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	os_free(h);
1408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* httpread_timeout_handler -- called on excessive total duration
1448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */
1458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic void httpread_timeout_handler(void *eloop_data, void *user_ctx)
1468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct httpread *h = user_ctx;
1488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h);
1498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	(*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT);
1508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* Analyze options only so far as is needed to correctly obtain the file.
1548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * The application can look at the raw header to find other options.
1558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */
1568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic int httpread_hdr_option_analyze(
1578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct httpread *h,
1588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	char *hbp       /* pointer to current line in header buffer */
1598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	)
1608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (word_eq(hbp, "CONTENT-LENGTH:")) {
1628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		while (isgraph(*hbp))
1638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			hbp++;
1648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		while (*hbp == ' ' || *hbp == '\t')
1658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			hbp++;
1668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (!isdigit(*hbp))
1678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			return -1;
1688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h->content_length = atol(hbp);
169cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt		if (h->content_length < 0 || h->content_length > h->max_bytes) {
170cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt			wpa_printf(MSG_DEBUG,
171cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt				   "httpread: Unacceptable Content-Length %d",
172cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt				   h->content_length);
173cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt			return -1;
174cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt		}
1758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h->got_content_length = 1;
1768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return 0;
1778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (word_eq(hbp, "TRANSFER_ENCODING:") ||
1798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	    word_eq(hbp, "TRANSFER-ENCODING:")) {
1808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		while (isgraph(*hbp))
1818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			hbp++;
1828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		while (*hbp == ' ' || *hbp == '\t')
1838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			hbp++;
1848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/* There should (?) be no encodings of interest
1858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * other than chunked...
1868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 */
1878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (word_eq(hbp, "CHUNKED")) {
1888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			h->chunked = 1;
1898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			h->in_chunk_data = 0;
1908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			/* ignore possible ;<parameters> */
1918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
1928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return 0;
1938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* skip anything we don't know, which is a lot */
1958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return 0;
1968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic int httpread_hdr_analyze(struct httpread *h)
2008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
2018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	char *hbp = h->hdr;      /* pointer into h->hdr */
2028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int standard_first_line = 1;
2038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* First line is special */
2058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN;
2068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (!isgraph(*hbp))
2078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		goto bad;
2088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (os_strncmp(hbp, "HTTP/", 5) == 0) {
2098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h->hdr_type = HTTPREAD_HDR_TYPE_REPLY;
2108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		standard_first_line = 0;
2118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		hbp += 5;
2128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (hbp[0] == '1' && hbp[1] == '.' &&
2138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		    isdigit(hbp[2]) && hbp[2] != '0')
2148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			h->version = 1;
2158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		while (isgraph(*hbp))
2168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			hbp++;
2178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		while (*hbp == ' ' || *hbp == '\t')
2188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			hbp++;
2198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (!isdigit(*hbp))
2208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			goto bad;
2218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h->reply_code = atol(hbp);
2228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	} else if (word_eq(hbp, "GET"))
2238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h->hdr_type = HTTPREAD_HDR_TYPE_GET;
2248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	else if (word_eq(hbp, "HEAD"))
2258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h->hdr_type = HTTPREAD_HDR_TYPE_HEAD;
2268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	else if (word_eq(hbp, "POST"))
2278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h->hdr_type = HTTPREAD_HDR_TYPE_POST;
2288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	else if (word_eq(hbp, "PUT"))
2298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h->hdr_type = HTTPREAD_HDR_TYPE_PUT;
2308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	else if (word_eq(hbp, "DELETE"))
2318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h->hdr_type = HTTPREAD_HDR_TYPE_DELETE;
2328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	else if (word_eq(hbp, "TRACE"))
2338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h->hdr_type = HTTPREAD_HDR_TYPE_TRACE;
2348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	else if (word_eq(hbp, "CONNECT"))
2358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT;
2368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	else if (word_eq(hbp, "NOTIFY"))
2378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY;
2388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	else if (word_eq(hbp, "M-SEARCH"))
2398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH;
2408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	else if (word_eq(hbp, "M-POST"))
2418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h->hdr_type = HTTPREAD_HDR_TYPE_M_POST;
2428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	else if (word_eq(hbp, "SUBSCRIBE"))
2438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE;
2448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	else if (word_eq(hbp, "UNSUBSCRIBE"))
2458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE;
2468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	else {
2478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
2488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (standard_first_line) {
2508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		char *rawuri;
2518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		char *uri;
2528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/* skip type */
2538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		while (isgraph(*hbp))
2548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			hbp++;
2558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		while (*hbp == ' ' || *hbp == '\t')
2568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			hbp++;
2578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/* parse uri.
2588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * Find length, allocate memory for translated
2598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * copy, then translate by changing %<hex><hex>
2608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * into represented value.
2618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 */
2628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		rawuri = hbp;
2638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		while (isgraph(*hbp))
2648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			hbp++;
2658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h->uri = os_malloc((hbp - rawuri) + 1);
2668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (h->uri == NULL)
2678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			goto bad;
2688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		uri = h->uri;
2698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		while (rawuri < hbp) {
2708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			int c = *rawuri;
2718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			if (c == '%' &&
2728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			    isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
273fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt				*uri++ = hex2byte(rawuri + 1);
2748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				rawuri += 3;
2758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			} else {
2768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				*uri++ = c;
2778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				rawuri++;
2788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			}
2798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
2808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		*uri = 0;       /* null terminate */
2818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		while (*hbp == ' ' || *hbp == '\t')
2828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			hbp++;
2838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/* get version */
2848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (0 == strncmp(hbp, "HTTP/", 5)) {
2858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			hbp += 5;
2868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			if (hbp[0] == '1' && hbp[1] == '.' &&
2878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			    isdigit(hbp[2]) && hbp[2] != '0')
2888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				h->version = 1;
2898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
2908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
2918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* skip rest of line */
2928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	while (*hbp)
2938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (*hbp++ == '\n')
2948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			break;
2958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* Remainder of lines are options, in any order;
2978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 * or empty line to terminate
2988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 */
2998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	for (;;) {
3008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/* Empty line to terminate */
3018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (hbp[0] == '\n' ||
3028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		    (hbp[0] == '\r' && hbp[1] == '\n'))
3038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			break;
3048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (!isgraph(*hbp))
3058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			goto bad;
3068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (httpread_hdr_option_analyze(h, hbp))
3078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			goto bad;
3088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/* skip line */
3098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		while (*hbp)
3108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			if (*hbp++ == '\n')
3118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				break;
3128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
3138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* chunked overrides content-length always */
3158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (h->chunked)
3168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h->got_content_length = 0;
3178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* For some types, we should not try to read a body
3198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 * This is in addition to the application determining
3208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 * that we should not read a body.
3218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 */
3228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	switch (h->hdr_type) {
3238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	case HTTPREAD_HDR_TYPE_REPLY:
3248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/* Some codes can have a body and some not.
3258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * For now, just assume that any other than 200
3268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * do not...
3278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 */
3288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (h->reply_code != 200)
3298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			h->max_bytes = 0;
3308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		break;
3318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	case HTTPREAD_HDR_TYPE_GET:
3328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	case HTTPREAD_HDR_TYPE_HEAD:
3338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/* in practice it appears that it is assumed
3348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * that GETs have a body length of 0... ?
3358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 */
3368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (h->chunked == 0 && h->got_content_length == 0)
3378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			h->max_bytes = 0;
3388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		break;
3398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	case HTTPREAD_HDR_TYPE_POST:
3408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	case HTTPREAD_HDR_TYPE_PUT:
3418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	case HTTPREAD_HDR_TYPE_DELETE:
3428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	case HTTPREAD_HDR_TYPE_TRACE:
3438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	case HTTPREAD_HDR_TYPE_CONNECT:
3448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	case HTTPREAD_HDR_TYPE_NOTIFY:
3458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	case HTTPREAD_HDR_TYPE_M_SEARCH:
3468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	case HTTPREAD_HDR_TYPE_M_POST:
3478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	case HTTPREAD_HDR_TYPE_SUBSCRIBE:
3488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
3498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	default:
3508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		break;
3518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
3528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return 0;
3548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtbad:
3568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* Error */
3578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return -1;
3588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
3598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* httpread_read_handler -- called when socket ready to read
3628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
3638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Note: any extra data we read past end of transmitted file is ignored;
3648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * if we were to support keeping connections open for multiple files then
3658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * this would have to be addressed.
3668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */
3678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
3688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
3698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct httpread *h = sock_ctx;
3708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int nread;
3718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	char *rbp;      /* pointer into read buffer */
3728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	char *hbp;      /* pointer into header buffer */
3738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	char *bbp;      /* pointer into body buffer */
3748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	char readbuf[HTTPREAD_READBUF_SIZE];  /* temp use to read into */
3758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* read some at a time, then search for the interal
3778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 * boundaries between header and data and etc.
3788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 */
379cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt	wpa_printf(MSG_DEBUG, "httpread: Trying to read more data(%p)", h);
3808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	nread = read(h->sd, readbuf, sizeof(readbuf));
381cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt	if (nread < 0) {
382cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt		wpa_printf(MSG_DEBUG, "httpread failed: %s", strerror(errno));
3838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		goto bad;
384cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt	}
385cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt	wpa_hexdump_ascii(MSG_MSGDUMP, "httpread - read", readbuf, nread);
3868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (nread == 0) {
3878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/* end of transmission... this may be normal
3888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * or may be an error... in some cases we can't
3898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * tell which so we must assume it is normal then.
3908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 */
3918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (!h->got_hdr) {
3928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			/* Must at least have completed header */
3938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h);
3948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			goto bad;
3958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
3968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (h->chunked || h->got_content_length) {
3978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			/* Premature EOF; e.g. dropped connection */
3988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			wpa_printf(MSG_DEBUG,
3998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				   "httpread premature eof(%p) %d/%d",
4008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				   h, h->body_nbytes,
4018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				   h->content_length);
4028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			goto bad;
4038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
4048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/* No explicit length, hopefully we have all the data
4058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * although dropped connections can cause false
4068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * end
4078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 */
408cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt		wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
409623d63a3a443027e50efdaaec027befcc3882527Dmitry Shmidt		h->got_body = 1;
410623d63a3a443027e50efdaaec027befcc3882527Dmitry Shmidt		goto got_file;
4118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
4128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	rbp = readbuf;
4138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
4148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* Header consists of text lines (terminated by both CR and LF)
4158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 * and an empty line (CR LF only).
4168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 */
4178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (!h->got_hdr) {
4188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		hbp = h->hdr + h->hdr_nbytes;
4198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/* add to headers until:
4208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 *      -- we run out of data in read buffer
4218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 *      -- or, we run out of header buffer room
4228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 *      -- or, we get double CRLF in headers
4238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 */
4248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		for (;;) {
4258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			if (nread == 0)
4268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				goto get_more;
4278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
428cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt				wpa_printf(MSG_DEBUG,
429cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt					   "httpread: Too long header");
4308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				goto bad;
4318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			}
4328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			*hbp++ = *rbp++;
4338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			nread--;
4348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			h->hdr_nbytes++;
4358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			if (h->hdr_nbytes >= 4 &&
4368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			    hbp[-1] == '\n' &&
4378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			    hbp[-2] == '\r' &&
4388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			    hbp[-3] == '\n' &&
4398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			    hbp[-4] == '\r' ) {
4408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				h->got_hdr = 1;
4418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				*hbp = 0;       /* null terminate */
4428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				break;
4438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			}
4448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
4458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/* here we've just finished reading the header */
4468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (httpread_hdr_analyze(h)) {
4478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h);
4488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			goto bad;
4498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
4508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (h->max_bytes == 0) {
451cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt			wpa_printf(MSG_DEBUG, "httpread no body hdr end(%p)",
452cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt				   h);
4538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			goto got_file;
4548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
4558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (h->got_content_length && h->content_length == 0) {
456cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt			wpa_printf(MSG_DEBUG,
457cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt				   "httpread zero content length(%p)", h);
4588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			goto got_file;
4598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
4608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
4618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
4628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* Certain types of requests never have data and so
4638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 * must be specially recognized.
4648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 */
4658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) ||
4668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	    !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) ||
4678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	    !os_strncasecmp(h->hdr, "HEAD", 4) ||
4688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	    !os_strncasecmp(h->hdr, "GET", 3)) {
4698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (!h->got_body) {
470cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt			wpa_printf(MSG_DEBUG, "httpread NO BODY for sp. type");
4718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
4728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h->got_body = 1;
4738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		goto got_file;
4748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
4758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
4768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* Data can be just plain binary data, or if "chunked"
4778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 * consists of chunks each with a header, ending with
4788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 * an ending header.
4798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 */
4808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (nread == 0)
4818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		goto get_more;
4828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (!h->got_body) {
4838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/* Here to get (more of) body */
4848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/* ensure we have enough room for worst case for body
4858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * plus a null termination character
4868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 */
4878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
4888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			char *new_body;
4898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			int new_alloc_nbytes;
4908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
491cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt			if (h->body_nbytes >= h->max_bytes) {
492cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt				wpa_printf(MSG_DEBUG,
493cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt					   "httpread: body_nbytes=%d >= max_bytes=%d",
494cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt					   h->body_nbytes, h->max_bytes);
4958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				goto bad;
496cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt			}
4978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			new_alloc_nbytes = h->body_alloc_nbytes +
4988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				HTTPREAD_BODYBUF_DELTA;
4998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			/* For content-length case, the first time
5008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			 * through we allocate the whole amount
5018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			 * we need.
5028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			 */
5038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			if (h->got_content_length &&
5048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			    new_alloc_nbytes < (h->content_length + 1))
5058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				new_alloc_nbytes = h->content_length + 1;
506cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt			if (new_alloc_nbytes < h->body_alloc_nbytes ||
507d80a401aed31d06f261efd19223cf55d1a2a8228Dmitry Shmidt			    new_alloc_nbytes > h->max_bytes +
508d80a401aed31d06f261efd19223cf55d1a2a8228Dmitry Shmidt			    HTTPREAD_BODYBUF_DELTA) {
509cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt				wpa_printf(MSG_DEBUG,
510d80a401aed31d06f261efd19223cf55d1a2a8228Dmitry Shmidt					   "httpread: Unacceptable body length %d (body_alloc_nbytes=%u max_bytes=%u)",
511d80a401aed31d06f261efd19223cf55d1a2a8228Dmitry Shmidt					   new_alloc_nbytes,
512d80a401aed31d06f261efd19223cf55d1a2a8228Dmitry Shmidt					   h->body_alloc_nbytes,
513d80a401aed31d06f261efd19223cf55d1a2a8228Dmitry Shmidt					   h->max_bytes);
514cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt				goto bad;
515cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt			}
5168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			if ((new_body = os_realloc(h->body, new_alloc_nbytes))
517cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt			    == NULL) {
518cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt				wpa_printf(MSG_DEBUG,
519cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt					   "httpread: Failed to reallocate buffer (len=%d)",
520cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt					   new_alloc_nbytes);
5218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				goto bad;
522cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt			}
5238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
5248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			h->body = new_body;
5258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			h->body_alloc_nbytes = new_alloc_nbytes;
5268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
5278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/* add bytes */
5288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		bbp = h->body + h->body_nbytes;
5298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		for (;;) {
5308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			int ncopy;
5318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			/* See if we need to stop */
5328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			if (h->chunked && h->in_chunk_data == 0) {
5338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				/* in chunk header */
5348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				char *cbp = h->body + h->chunk_start;
5358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				if (bbp-cbp >= 2 && bbp[-2] == '\r' &&
5368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				    bbp[-1] == '\n') {
5378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					/* end of chunk hdr line */
5388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					/* hdr line consists solely
5398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					 * of a hex numeral and CFLF
5408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					 */
541cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt					if (!isxdigit(*cbp)) {
542cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt						wpa_printf(MSG_DEBUG,
543cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt							   "httpread: Unexpected chunk header value (not a hex digit)");
5448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt						goto bad;
545cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt					}
5468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					h->chunk_size = strtoul(cbp, NULL, 16);
547cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt					if (h->chunk_size < 0 ||
548cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt					    h->chunk_size > h->max_bytes) {
549cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt						wpa_printf(MSG_DEBUG,
550cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt							   "httpread: Invalid chunk size %d",
551cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt							   h->chunk_size);
552cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt						goto bad;
553cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt					}
5548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					/* throw away chunk header
5558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					 * so we have only real data
5568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					 */
5578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					h->body_nbytes = h->chunk_start;
5588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					bbp = cbp;
5598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					if (h->chunk_size == 0) {
5608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt						/* end of chunking */
5618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt						/* trailer follows */
5628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt						h->in_trailer = 1;
563cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt						wpa_printf(MSG_DEBUG,
564cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt							   "httpread end chunks(%p)",
565cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt							   h);
5668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt						break;
5678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					}
5688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					h->in_chunk_data = 1;
5698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					/* leave chunk_start alone */
5708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				}
5718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			} else if (h->chunked) {
5728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				/* in chunk data */
5738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				if ((h->body_nbytes - h->chunk_start) ==
5748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				    (h->chunk_size + 2)) {
5758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					/* end of chunk reached,
5768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					 * new chunk starts
5778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					 */
5788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					/* check chunk ended w/ CRLF
5798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					 * which we'll throw away
5808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					 */
5818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					if (bbp[-1] == '\n' &&
5828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					    bbp[-2] == '\r') {
583cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt					} else {
584cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt						wpa_printf(MSG_DEBUG,
585cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt							   "httpread: Invalid chunk end");
5868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt						goto bad;
587cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt					}
5888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					h->body_nbytes -= 2;
5898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					bbp -= 2;
5908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					h->chunk_start = h->body_nbytes;
5918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					h->in_chunk_data = 0;
5928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					h->chunk_size = 0; /* just in case */
5938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				}
5948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			} else if (h->got_content_length &&
5958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				   h->body_nbytes >= h->content_length) {
5968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				h->got_body = 1;
597cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt				wpa_printf(MSG_DEBUG,
598cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt					   "httpread got content(%p)", h);
5998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				goto got_file;
6008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			}
6018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			if (nread <= 0)
6028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				break;
6038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			/* Now transfer. Optimize using memcpy where we can. */
6048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			if (h->chunked && h->in_chunk_data) {
6058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				/* copy up to remainder of chunk data
6068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				 * plus the required CR+LF at end
6078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				 */
6088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				ncopy = (h->chunk_start + h->chunk_size + 2) -
6098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					h->body_nbytes;
6108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			} else if (h->chunked) {
6118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				/*in chunk header -- don't optimize */
6128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				*bbp++ = *rbp++;
6138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				nread--;
6148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				h->body_nbytes++;
6158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				continue;
6168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			} else if (h->got_content_length) {
6178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				ncopy = h->content_length - h->body_nbytes;
6188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			} else {
6198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				ncopy = nread;
6208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			}
6218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			/* Note: should never be 0 */
622cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt			if (ncopy < 0) {
623cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt				wpa_printf(MSG_DEBUG,
624cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt					   "httpread: Invalid ncopy=%d", ncopy);
625cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt				goto bad;
626cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt			}
6278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			if (ncopy > nread)
6288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				ncopy = nread;
6298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			os_memcpy(bbp, rbp, ncopy);
6308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			bbp += ncopy;
6318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			h->body_nbytes += ncopy;
6328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			rbp += ncopy;
6338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			nread -= ncopy;
6348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}       /* body copy loop */
6358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}       /* !got_body */
6368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (h->chunked && h->in_trailer) {
6378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/* If "chunked" then there is always a trailer,
6388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * consisting of zero or more non-empty lines
6398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * ending with CR LF and then an empty line w/ CR LF.
6408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * We do NOT support trailers except to skip them --
6418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 * this is supported (generally) by the http spec.
6428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		 */
6438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		for (;;) {
6448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			int c;
6458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			if (nread <= 0)
6468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				break;
6478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			c = *rbp++;
6488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			nread--;
6498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			switch (h->trailer_state) {
6508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			case trailer_line_begin:
6518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				if (c == '\r')
6528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					h->trailer_state = trailer_empty_cr;
6538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				else
6548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					h->trailer_state = trailer_nonempty;
6558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				break;
6568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			case trailer_empty_cr:
6578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				/* end empty line */
6588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				if (c == '\n') {
6598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					h->trailer_state = trailer_line_begin;
6608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					h->in_trailer = 0;
661cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt					wpa_printf(MSG_DEBUG,
662cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt						   "httpread got content(%p)",
663cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt						   h);
6648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					h->got_body = 1;
6658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					goto got_file;
6668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				}
6678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				h->trailer_state = trailer_nonempty;
6688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				break;
6698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			case trailer_nonempty:
6708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				if (c == '\r')
6718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					h->trailer_state = trailer_nonempty_cr;
6728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				break;
6738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			case trailer_nonempty_cr:
6748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				if (c == '\n')
6758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					h->trailer_state = trailer_line_begin;
6768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				else
6778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt					h->trailer_state = trailer_nonempty;
6788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				break;
6798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			}
6808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
6818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
6828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	goto get_more;
6838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
6848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtbad:
6858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* Error */
6868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h);
6878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	(*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR);
6888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return;
6898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
6908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtget_more:
691cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt	wpa_printf(MSG_DEBUG, "httpread: get more (%p)", h);
6928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return;
6938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
6948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtgot_file:
695cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt	wpa_printf(MSG_DEBUG, "httpread got file %d bytes type %d",
696cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt		   h->body_nbytes, h->hdr_type);
697cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt	wpa_hexdump_ascii(MSG_MSGDUMP, "httpread: body",
698cc00d5dc8483e32158b2ba61ea44b0c38d790ed7Dmitry Shmidt			  h->body, h->body_nbytes);
6998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* Null terminate for convenience of some applications */
7008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (h->body)
7018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		h->body[h->body_nbytes] = 0; /* null terminate */
7028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	h->got_file = 1;
7038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* Assume that we do NOT support keeping connection alive,
7048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 * and just in case somehow we don't get destroyed right away,
7058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 * unregister now.
7068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 */
707fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt	eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
7088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* The application can destroy us whenever they feel like...
7098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 * cancel timeout.
7108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	 */
711fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt	eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
7128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	(*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY);
7138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
7148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* httpread_create -- start a new reading session making use of eloop.
7178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * The new instance will use the socket descriptor for reading (until
7188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * it gets a file and not after) but will not close the socket, even
7198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * when the instance is destroyed (the application must do that).
7208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Return NULL on error.
7218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
7228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Provided that httpread_create successfully returns a handle,
7238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * the callback fnc is called to handle httpread_event events.
7248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * The caller should do destroy on any errors or unknown events.
7258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
7268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Pass max_bytes == 0 to not read body at all (required for e.g.
7278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * reply to HEAD request).
7288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */
7298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstruct httpread * httpread_create(
7308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int sd,	 /* descriptor of TCP socket to read from */
7318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	void (*cb)(struct httpread *handle, void *cookie,
7328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		   enum httpread_event e),  /* call on event */
7338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	void *cookie,    /* pass to callback */
7348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int max_bytes,	  /* maximum body size else abort it */
7358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int timeout_seconds     /* 0; or total duration timeout period */
7368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	)
7378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
7388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct httpread *h = NULL;
7398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	h = os_zalloc(sizeof(*h));
7418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (h == NULL)
7428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		goto fail;
7438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	h->sd = sd;
7448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	h->cb = cb;
7458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	h->cookie = cookie;
7468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	h->max_bytes = max_bytes;
7478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	h->timeout_seconds = timeout_seconds;
7488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
749fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt	if (timeout_seconds > 0 &&
750fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt	    eloop_register_timeout(timeout_seconds, 0,
751fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt				   httpread_timeout_handler, NULL, h)) {
752fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt		/* No way to recover (from malloc failure) */
753fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt		goto fail;
7548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
7558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler,
7568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				NULL, h)) {
7578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		/* No way to recover (from malloc failure) */
7588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		goto fail;
7598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
7608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return h;
7618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtfail:
7638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	/* Error */
7658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	httpread_destroy(h);
7668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return NULL;
7678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
7688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* httpread_hdr_type_get -- When file is ready, returns header type. */
7718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtenum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
7728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
7738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return h->hdr_type;
7748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
7758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
7788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * or possibly NULL (which would be an error).
7798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */
7808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtchar * httpread_uri_get(struct httpread *h)
7818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
7828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return h->uri;
7838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
7848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* httpread_reply_code_get -- When reply is ready, returns reply code */
7878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint httpread_reply_code_get(struct httpread *h)
7888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
7898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return h->reply_code;
7908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
7918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* httpread_length_get -- When file is ready, returns file length. */
7948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint httpread_length_get(struct httpread *h)
7958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
7968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return h->body_nbytes;
7978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
7988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
8008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* httpread_data_get -- When file is ready, returns file content
8018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * with null byte appened.
8028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Might return NULL in some error condition.
8038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */
8048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid * httpread_data_get(struct httpread *h)
8058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
8068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return h->body ? h->body : "";
8078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
8088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
8098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
8108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* httpread_hdr_get -- When file is ready, returns header content
8118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * with null byte appended.
8128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Might return NULL in some error condition.
8138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */
8148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtchar * httpread_hdr_get(struct httpread *h)
8158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
8168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return h->hdr;
8178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
8188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
8198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
8208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/* httpread_hdr_line_get -- When file is ready, returns pointer
8218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * to line within header content matching the given tag
8228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * (after the tag itself and any spaces/tabs).
8238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
8248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * The tag should end with a colon for reliable matching.
8258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
8268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * If not found, returns NULL;
8278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */
8288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtchar * httpread_hdr_line_get(struct httpread *h, const char *tag)
8298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
8308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int tag_len = os_strlen(tag);
8318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	char *hdr = h->hdr;
8328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	hdr = os_strchr(hdr, '\n');
8338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (hdr == NULL)
8348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return NULL;
8358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	hdr++;
8368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	for (;;) {
8378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (!os_strncasecmp(hdr, tag, tag_len)) {
8388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			hdr += tag_len;
8398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			while (*hdr == ' ' || *hdr == '\t')
8408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				hdr++;
8418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			return hdr;
8428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
8438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		hdr = os_strchr(hdr, '\n');
8448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (hdr == NULL)
8458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			return NULL;
8468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		hdr++;
8478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
8488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
849