* Author: Ted Merrill
* Copyright 2008 Atheros Communications
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* The files are buffered via internal callbacks from eloop, then presented to
* an application callback routine when completely read into memory. May also
#define HTTPREAD_HEADER_MAX_SIZE 4096 /* max allowed for headers */
#define HTTPREAD_BODYBUF_DELTA 4096 /* increase allocation by this */
-#if 0
-/* httpread_debug -- set this global variable > 0 e.g. from debugger
- * to enable debugs (larger numbers for more debugs)
- * Make this a #define of 0 to eliminate the debugging code.
- */
-int httpread_debug = 99;
-#else
-#define httpread_debug 0 /* eliminates even the debugging code */
-#endif
-
/* control instance -- actual definition (opaque to application)
*/
int timeout_seconds; /* 0 or total duration timeout period */
/* dynamically used information follows */
- int sd_registered; /* nonzero if we need to unregister socket */
- int to_registered; /* nonzero if we need to unregister timeout */
int got_hdr; /* nonzero when header is finalized */
char hdr[HTTPREAD_HEADER_MAX_SIZE+1]; /* headers stored here */
}
-/* convert hex to binary
- * Requires that c have been previously tested true with isxdigit().
- */
-static int hex_value(int c)
-{
- if (isdigit(c))
- return c - '0';
- if (islower(c))
- return 10 + c - 'a';
- return 10 + c - 'A';
-}
-
-
static void httpread_timeout_handler(void *eloop_data, void *user_ctx);
/* httpread_destroy -- if h is non-NULL, clean up
*/
void httpread_destroy(struct httpread *h)
{
- if (httpread_debug >= 10)
- wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h);
+ wpa_printf(MSG_DEBUG, "httpread_destroy(%p)", h);
if (!h)
return;
- if (h->to_registered)
- eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
- h->to_registered = 0;
- if (h->sd_registered)
- eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
- h->sd_registered = 0;
+ eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
+ eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
os_free(h->body);
os_free(h->uri);
os_memset(h, 0, sizeof(*h)); /* aid debugging */
{
struct httpread *h = user_ctx;
wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h);
- h->to_registered = 0; /* is self-cancelling */
(*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT);
}
if (!isdigit(*hbp))
return -1;
h->content_length = atol(hbp);
+ if (h->content_length < 0 || h->content_length > h->max_bytes) {
+ wpa_printf(MSG_DEBUG,
+ "httpread: Unacceptable Content-Length %d",
+ h->content_length);
+ return -1;
+ }
h->got_content_length = 1;
return 0;
}
int c = *rawuri;
if (c == '%' &&
isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
- *uri++ = (hex_value(rawuri[1]) << 4) |
- hex_value(rawuri[2]);
+ *uri++ = hex2byte(rawuri + 1);
rawuri += 3;
} else {
*uri++ = c;
}
}
*uri = 0; /* null terminate */
- while (isgraph(*hbp))
- hbp++;
while (*hbp == ' ' || *hbp == '\t')
hbp++;
/* get version */
char *bbp; /* pointer into body buffer */
char readbuf[HTTPREAD_READBUF_SIZE]; /* temp use to read into */
- if (httpread_debug >= 20)
- wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h);
-
/* read some at a time, then search for the interal
* boundaries between header and data and etc.
*/
+ wpa_printf(MSG_DEBUG, "httpread: Trying to read more data(%p)", h);
nread = read(h->sd, readbuf, sizeof(readbuf));
- if (nread < 0)
+ if (nread < 0) {
+ wpa_printf(MSG_DEBUG, "httpread failed: %s", strerror(errno));
goto bad;
+ }
+ wpa_hexdump_ascii(MSG_MSGDUMP, "httpread - read", readbuf, nread);
if (nread == 0) {
/* end of transmission... this may be normal
* or may be an error... in some cases we can't
* although dropped connections can cause false
* end
*/
- if (httpread_debug >= 10)
- wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
- h->got_body = 1;
- goto got_file;
+ wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
+ h->got_body = 1;
+ goto got_file;
}
rbp = readbuf;
if (nread == 0)
goto get_more;
if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
+ wpa_printf(MSG_DEBUG,
+ "httpread: Too long header");
goto bad;
}
*hbp++ = *rbp++;
goto bad;
}
if (h->max_bytes == 0) {
- if (httpread_debug >= 10)
- wpa_printf(MSG_DEBUG,
- "httpread no body hdr end(%p)", h);
+ wpa_printf(MSG_DEBUG, "httpread no body hdr end(%p)",
+ h);
goto got_file;
}
if (h->got_content_length && h->content_length == 0) {
- if (httpread_debug >= 10)
- wpa_printf(MSG_DEBUG,
- "httpread zero content length(%p)",
- h);
+ wpa_printf(MSG_DEBUG,
+ "httpread zero content length(%p)", h);
goto got_file;
}
}
!os_strncasecmp(h->hdr, "HEAD", 4) ||
!os_strncasecmp(h->hdr, "GET", 3)) {
if (!h->got_body) {
- if (httpread_debug >= 10)
- wpa_printf(MSG_DEBUG,
- "httpread NO BODY for sp. type");
+ wpa_printf(MSG_DEBUG, "httpread NO BODY for sp. type");
}
h->got_body = 1;
goto got_file;
char *new_body;
int new_alloc_nbytes;
- if (h->body_nbytes >= h->max_bytes)
+ if (h->body_nbytes >= h->max_bytes) {
+ wpa_printf(MSG_DEBUG,
+ "httpread: body_nbytes=%d >= max_bytes=%d",
+ h->body_nbytes, h->max_bytes);
goto bad;
+ }
new_alloc_nbytes = h->body_alloc_nbytes +
HTTPREAD_BODYBUF_DELTA;
/* For content-length case, the first time
if (h->got_content_length &&
new_alloc_nbytes < (h->content_length + 1))
new_alloc_nbytes = h->content_length + 1;
+ if (new_alloc_nbytes < h->body_alloc_nbytes ||
+ new_alloc_nbytes > h->max_bytes +
+ HTTPREAD_BODYBUF_DELTA) {
+ wpa_printf(MSG_DEBUG,
+ "httpread: Unacceptable body length %d (body_alloc_nbytes=%u max_bytes=%u)",
+ new_alloc_nbytes,
+ h->body_alloc_nbytes,
+ h->max_bytes);
+ goto bad;
+ }
if ((new_body = os_realloc(h->body, new_alloc_nbytes))
- == NULL)
+ == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "httpread: Failed to reallocate buffer (len=%d)",
+ new_alloc_nbytes);
goto bad;
+ }
h->body = new_body;
h->body_alloc_nbytes = new_alloc_nbytes;
/* hdr line consists solely
* of a hex numeral and CFLF
*/
- if (!isxdigit(*cbp))
+ if (!isxdigit(*cbp)) {
+ wpa_printf(MSG_DEBUG,
+ "httpread: Unexpected chunk header value (not a hex digit)");
goto bad;
+ }
h->chunk_size = strtoul(cbp, NULL, 16);
+ if (h->chunk_size < 0 ||
+ h->chunk_size > h->max_bytes) {
+ wpa_printf(MSG_DEBUG,
+ "httpread: Invalid chunk size %d",
+ h->chunk_size);
+ goto bad;
+ }
/* throw away chunk header
* so we have only real data
*/
/* end of chunking */
/* trailer follows */
h->in_trailer = 1;
- if (httpread_debug >= 20)
- wpa_printf(
- MSG_DEBUG,
- "httpread end chunks(%p)", h);
+ wpa_printf(MSG_DEBUG,
+ "httpread end chunks(%p)",
+ h);
break;
}
h->in_chunk_data = 1;
*/
if (bbp[-1] == '\n' &&
bbp[-2] == '\r') {
- } else
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "httpread: Invalid chunk end");
goto bad;
+ }
h->body_nbytes -= 2;
bbp -= 2;
h->chunk_start = h->body_nbytes;
} else if (h->got_content_length &&
h->body_nbytes >= h->content_length) {
h->got_body = 1;
- if (httpread_debug >= 10)
- wpa_printf(
- MSG_DEBUG,
- "httpread got content(%p)", h);
+ wpa_printf(MSG_DEBUG,
+ "httpread got content(%p)", h);
goto got_file;
}
if (nread <= 0)
ncopy = nread;
}
/* Note: should never be 0 */
+ if (ncopy < 0) {
+ wpa_printf(MSG_DEBUG,
+ "httpread: Invalid ncopy=%d", ncopy);
+ goto bad;
+ }
if (ncopy > nread)
ncopy = nread;
os_memcpy(bbp, rbp, ncopy);
* We do NOT support trailers except to skip them --
* this is supported (generally) by the http spec.
*/
- bbp = h->body + h->body_nbytes;
for (;;) {
int c;
if (nread <= 0)
if (c == '\n') {
h->trailer_state = trailer_line_begin;
h->in_trailer = 0;
- if (httpread_debug >= 10)
- wpa_printf(
- MSG_DEBUG,
- "httpread got content(%p)", h);
+ wpa_printf(MSG_DEBUG,
+ "httpread got content(%p)",
+ h);
h->got_body = 1;
goto got_file;
}
return;
get_more:
+ wpa_printf(MSG_DEBUG, "httpread: get more (%p)", h);
return;
got_file:
- if (httpread_debug >= 10)
- wpa_printf(MSG_DEBUG,
- "httpread got file %d bytes type %d",
- h->body_nbytes, h->hdr_type);
+ wpa_printf(MSG_DEBUG, "httpread got file %d bytes type %d",
+ h->body_nbytes, h->hdr_type);
+ wpa_hexdump_ascii(MSG_MSGDUMP, "httpread: body",
+ h->body, h->body_nbytes);
/* Null terminate for convenience of some applications */
if (h->body)
h->body[h->body_nbytes] = 0; /* null terminate */
* and just in case somehow we don't get destroyed right away,
* unregister now.
*/
- if (h->sd_registered)
- eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
- h->sd_registered = 0;
+ eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
/* The application can destroy us whenever they feel like...
* cancel timeout.
*/
- if (h->to_registered)
- eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
- h->to_registered = 0;
+ eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
(*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY);
}
h->max_bytes = max_bytes;
h->timeout_seconds = timeout_seconds;
- if (timeout_seconds > 0) {
- if (eloop_register_timeout(timeout_seconds, 0,
- httpread_timeout_handler,
- NULL, h)) {
- /* No way to recover (from malloc failure) */
- goto fail;
- }
- h->to_registered = 1;
+ if (timeout_seconds > 0 &&
+ eloop_register_timeout(timeout_seconds, 0,
+ httpread_timeout_handler, NULL, h)) {
+ /* No way to recover (from malloc failure) */
+ goto fail;
}
if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler,
NULL, h)) {
/* No way to recover (from malloc failure) */
goto fail;
}
- h->sd_registered = 1;
return h;
fail: