#define DEBUG 0
#define IOBUF_SIZE 8192
+#define MAX_HTTP_HEADERS_SIZE ((8*1024) - 16)
#if PIPE_BUF >= IOBUF_SIZE
# error "PIPE_BUF >= IOBUF_SIZE"
#endif
char *url_to;
} Htaccess_Proxy;
+typedef enum CGI_type {
+ CGI_NONE = 0,
+ CGI_NORMAL,
+ CGI_INDEX,
+ CGI_INTERPRETER,
+} CGI_type;
+
enum {
HTTP_OK = 200,
HTTP_PARTIAL_CONTENT = 206,
HTTP_REQUEST_TIMEOUT = 408,
HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */
HTTP_INTERNAL_SERVER_ERROR = 500,
+ HTTP_ENTITY_TOO_LARGE = 413,
HTTP_CONTINUE = 100,
#if 0 /* future use */
HTTP_SWITCHING_PROTOCOLS = 101,
HTTP_BAD_REQUEST,
HTTP_FORBIDDEN,
HTTP_INTERNAL_SERVER_ERROR,
+ HTTP_ENTITY_TOO_LARGE,
#if 0 /* not implemented */
HTTP_CREATED,
HTTP_ACCEPTED,
{ "Bad Request", "Unsupported method" },
{ "Forbidden", "" },
{ "Internal Server Error", "Internal Server Error" },
+ { "Entity Too Large", "Entity Too Large" },
#if 0 /* not implemented */
{ "Created" },
{ "Accepted" },
IF_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;)
IF_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
- IF_FEATURE_HTTPD_CGI(char *referer;)
- IF_FEATURE_HTTPD_CGI(char *user_agent;)
- IF_FEATURE_HTTPD_CGI(char *host;)
- IF_FEATURE_HTTPD_CGI(char *http_accept;)
- IF_FEATURE_HTTPD_CGI(char *http_accept_language;)
off_t file_size; /* -1 - unknown */
#if ENABLE_FEATURE_HTTPD_RANGES
* const char *url The requested URL (with leading /).
* const char *orig_uri The original URI before rewriting (if any)
* int post_len Length of the POST body.
- * const char *cookie For set HTTP_COOKIE.
- * const char *content_type For set CONTENT_TYPE.
*/
static void send_cgi_and_exit(
const char *url,
const char *orig_uri,
const char *request,
- int post_len,
- const char *cookie,
- const char *content_type) NORETURN;
+ int post_len) NORETURN;
static void send_cgi_and_exit(
const char *url,
const char *orig_uri,
const char *request,
- int post_len,
- const char *cookie,
- const char *content_type)
+ int post_len)
{
struct fd_pair fromCgi; /* CGI -> httpd pipe */
struct fd_pair toCgi; /* httpd -> CGI pipe */
#endif
}
}
- setenv1("HTTP_USER_AGENT", G.user_agent);
- if (G.http_accept)
- setenv1("HTTP_ACCEPT", G.http_accept);
- if (G.http_accept_language)
- setenv1("HTTP_ACCEPT_LANGUAGE", G.http_accept_language);
if (post_len)
putenv(xasprintf("CONTENT_LENGTH=%u", post_len));
- if (cookie)
- setenv1("HTTP_COOKIE", cookie);
- if (content_type)
- setenv1("CONTENT_TYPE", content_type);
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
if (remoteuser) {
setenv1("REMOTE_USER", remoteuser);
putenv((char*)"AUTH_TYPE=Basic");
}
#endif
- if (G.referer)
- setenv1("HTTP_REFERER", G.referer);
- setenv1("HTTP_HOST", G.host); /* set to "" if NULL */
/* setenv1("SERVER_NAME", safe_gethostname()); - don't do this,
* just run "env SERVER_NAME=xyz httpd ..." instead */
char *urlcopy;
char *urlp;
char *tptr;
+ unsigned header_len;
#if ENABLE_FEATURE_HTTPD_CGI
static const char request_HEAD[] ALIGN1 = "HEAD";
const char *prequest;
- char *cookie = NULL;
- char *content_type = NULL;
unsigned long length = 0;
+ enum CGI_type cgi_type = CGI_NONE;
#elif ENABLE_FEATURE_HTTPD_PROXY
#define prequest request_GET
unsigned long length = 0;
#if ENABLE_FEATURE_HTTPD_PROXY
proxy_entry = find_proxy_entry(urlcopy);
if (proxy_entry)
- header_buf = header_ptr = xmalloc(IOBUF_SIZE);
+ header_buf = header_ptr = xmalloc(MAX_HTTP_HEADERS_SIZE + 2);
+//TODO: why we don't just start pumping data to proxy here,
+//without decoding URL, saving headers and such???
else
#endif
{
*tptr = '/';
}
+ tptr = urlcopy + 1; /* skip first '/' */
+
+#if ENABLE_FEATURE_HTTPD_CGI
+ if (is_prefixed_with(tptr, "cgi-bin/")) {
+ if (tptr[8] == '\0') {
+ /* protect listing "cgi-bin/" */
+ send_headers_and_exit(HTTP_FORBIDDEN);
+ }
+ cgi_type = CGI_NORMAL;
+ }
+#endif
+
+ if (urlp[-1] == '/') {
+ /* When index_page string is appended to <dir>/ URL, it overwrites
+ * the query string. If we fall back to call /cgi-bin/index.cgi,
+ * query string would be lost and not available to the CGI.
+ * Work around it by making a deep copy.
+ */
+ if (ENABLE_FEATURE_HTTPD_CGI)
+ g_query = xstrdup(g_query); /* ok for NULL too */
+ strcpy(urlp, index_page);
+ }
+ if (stat(tptr, &sb) == 0) {
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ char *suffix = strrchr(tptr, '.');
+ if (suffix) {
+ Htaccess *cur;
+ for (cur = script_i; cur; cur = cur->next) {
+ if (strcmp(cur->before_colon + 1, suffix) == 0) {
+ cgi_type = CGI_INTERPRETER;
+ break;
+ }
+ }
+ }
+#endif
+ if (!found_moved_temporarily) {
+ file_size = sb.st_size;
+ last_mod = sb.st_mtime;
+ }
+ }
+#if ENABLE_FEATURE_HTTPD_CGI
+ else if (urlp[-1] == '/') {
+ /* It's a dir URL and there is no index.html
+ * Try cgi-bin/index.cgi */
+ if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) {
+ cgi_type = CGI_INDEX;
+ }
+ }
+#endif
+ urlp[0] = '\0';
+
+ header_len = 0;
if (http_major_version >= '0') {
/* Request was with "... HTTP/nXXX", and n >= 0 */
/* Read until blank line */
while (1) {
- if (!get_line())
+ int iobuf_len = get_line();
+ if (!iobuf_len)
break; /* EOF or error or empty line */
+ header_len += iobuf_len + 2;
+ if (header_len >= MAX_HTTP_HEADERS_SIZE)
+ send_headers_and_exit(HTTP_ENTITY_TOO_LARGE);
if (DEBUG)
bb_error_msg("header: '%s'", iobuf);
-
#if ENABLE_FEATURE_HTTPD_PROXY
if (proxy_entry) {
- /* Why 4, not 2?
- * We need 2 more bytes for yet another "\r\n" -
- * see near fdprintf(proxy_fd...) further below.
- */
- int maxlen = (IOBUF_SIZE-4) - (int)(header_ptr - header_buf);
- if (maxlen > 0) {
- int len = strnlen(iobuf, maxlen);
- memcpy(header_ptr, iobuf, len);
- header_ptr += len;
- header_ptr[0] = '\r';
- header_ptr[1] = '\n';
- header_ptr += 2;
- }
+ /* protected from overflow by header_len check above */
+ memcpy(header_ptr, iobuf, iobuf_len);
+ header_ptr += iobuf_len;
+ header_ptr[0] = '\r';
+ header_ptr[1] = '\n';
+ header_ptr += 2;
}
#endif
if (errno || length > INT_MAX)
send_headers_and_exit(HTTP_BAD_REQUEST);
}
- }
-#endif
-#if ENABLE_FEATURE_HTTPD_CGI
- else if (STRNCASECMP(iobuf, "Cookie:") == 0) {
- if (!cookie) /* in case they send millions of these, do not OOM */
- cookie = xstrdup(skip_whitespace(iobuf + sizeof("Cookie:")-1));
- } else if (STRNCASECMP(iobuf, "Content-Type:") == 0) {
- if (!content_type)
- content_type = xstrdup(skip_whitespace(iobuf + sizeof("Content-Type:")-1));
- } else if (STRNCASECMP(iobuf, "Referer:") == 0) {
- if (!G.referer)
- G.referer = xstrdup(skip_whitespace(iobuf + sizeof("Referer:")-1));
- } else if (STRNCASECMP(iobuf, "User-Agent:") == 0) {
- if (!G.user_agent)
- G.user_agent = xstrdup(skip_whitespace(iobuf + sizeof("User-Agent:")-1));
- } else if (STRNCASECMP(iobuf, "Host:") == 0) {
- if (!G.host)
- G.host = xstrdup(skip_whitespace(iobuf + sizeof("Host:")-1));
- } else if (STRNCASECMP(iobuf, "Accept:") == 0) {
- if (!G.http_accept)
- G.http_accept = xstrdup(skip_whitespace(iobuf + sizeof("Accept:")-1));
- } else if (STRNCASECMP(iobuf, "Accept-Language:") == 0) {
- if (!G.http_accept_language)
- G.http_accept_language = xstrdup(skip_whitespace(iobuf + sizeof("Accept-Language:")-1));
+ continue;
}
#endif
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
/* decodeBase64() skips whitespace itself */
decodeBase64(tptr);
authorized = check_user_passwd(urlcopy, tptr);
+ continue;
}
#endif
#if ENABLE_FEATURE_HTTPD_RANGES
range_start = -1;
}
}
+ continue;
}
#endif
#if ENABLE_FEATURE_HTTPD_GZIP
content_gzip = 1;
//}
}
+ continue;
+ }
+#endif
+#if ENABLE_FEATURE_HTTPD_CGI
+ if (cgi_type != CGI_NONE) {
+ bool ct = (STRNCASECMP(iobuf, "Content-Type:") == 0);
+ char *cp;
+ char *colon = strchr(iobuf, ':');
+
+ if (!colon)
+ continue;
+ cp = iobuf;
+ while (cp < colon) {
+ /* a-z => A-Z, not-alnum => _ */
+ char c = (*cp & ~0x20); /* toupper for A-Za-z, undef for others */
+ if ((unsigned)(c - 'A') <= ('Z' - 'A')) {
+ *cp++ = c;
+ continue;
+ }
+ if (!isdigit(*cp))
+ *cp = '_';
+ cp++;
+ }
+ /* "Content-Type:" gets no HTTP_ prefix, all others do */
+ cp = xasprintf(ct ? "HTTP_%.*s=%s" + 5 : "HTTP_%.*s=%s",
+ (int)(colon - iobuf), iobuf,
+ skip_whitespace(colon + 1)
+ );
+ putenv(cp);
}
#endif
} /* while extra header reading */
tptr = urlcopy + 1; /* skip first '/' */
#if ENABLE_FEATURE_HTTPD_CGI
- if (is_prefixed_with(tptr, "cgi-bin/")) {
- if (tptr[8] == '\0') {
- /* protect listing "cgi-bin/" */
- send_headers_and_exit(HTTP_FORBIDDEN);
- }
- send_cgi_and_exit(urlcopy, urlcopy, prequest, length, cookie, content_type);
+ if (cgi_type != CGI_NONE) {
+ send_cgi_and_exit(
+ (cgi_type == CGI_INDEX) ? "/cgi-bin/index.cgi"
+ /*CGI_NORMAL or CGI_INTERPRETER*/ : urlcopy,
+ urlcopy, prequest, length
+ );
}
#endif
if (urlp[-1] == '/') {
- /* When index_page string is appended to <dir>/ URL, it overwrites
- * the query string. If we fall back to call /cgi-bin/index.cgi,
- * query string would be lost and not available to the CGI.
- * Work around it by making a deep copy.
- */
- if (ENABLE_FEATURE_HTTPD_CGI)
- g_query = xstrdup(g_query); /* ok for NULL too */
strcpy(urlp, index_page);
}
- if (stat(tptr, &sb) == 0) {
-#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
- char *suffix = strrchr(tptr, '.');
- if (suffix) {
- Htaccess *cur;
- for (cur = script_i; cur; cur = cur->next) {
- if (strcmp(cur->before_colon + 1, suffix) == 0) {
- send_cgi_and_exit(urlcopy, urlcopy, prequest, length, cookie, content_type);
- }
- }
- }
-#endif
- file_size = sb.st_size;
- last_mod = sb.st_mtime;
- }
-#if ENABLE_FEATURE_HTTPD_CGI
- else if (urlp[-1] == '/') {
- /* It's a dir URL and there is no index.html
- * Try cgi-bin/index.cgi */
- if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) {
- urlp[0] = '\0'; /* remove index_page */
- send_cgi_and_exit("/cgi-bin/index.cgi", urlcopy, prequest, length, cookie, content_type);
- }
- }
- /* else fall through to send_file, it errors out if open fails: */
+#if ENABLE_FEATURE_HTTPD_CGI
if (prequest != request_GET && prequest != request_HEAD) {
/* POST for files does not make sense */
send_headers_and_exit(HTTP_NOT_IMPLEMENTED);