Hi Denys, What about something like this?
Enable passing of more HTTP headers to CGI applications. Even custom ones. The variables will be named HTTP_<filtered name> where the filtered name is the header name capitalized and all characters not matching [a-z] | [A-Z] | [0-9] replaced with '_'. This patch also introduces a size limit of 8kB for the incoming HTTP headers. If larger a 413 Entity too large will be issued. Signed-off-by: Alexander Vickberg <[email protected]> function old new delta http_response 160 176 +16 http_response_type 20 22 +2 httpd_main 874 868 -6 send_headers 1004 986 -18 send_file_and_exit 802 778 -24 .rodata 158036 157938 -98 handle_incoming_and_exit 2809 2693 -116 send_cgi_and_exit 1016 854 -162 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/6 up/down: 18/-424) Total: -406 bytes diff --git a/networking/httpd.c b/networking/httpd.c index c486197b8..03b69fb99 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -267,6 +267,7 @@ #define DEBUG 0 #define IOBUF_SIZE 8192 +#define MAX_HTTP_HEADER_SIZE (8*1024) #if PIPE_BUF >= IOBUF_SIZE # error "PIPE_BUF >= IOBUF_SIZE" #endif @@ -305,6 +306,13 @@ typedef struct Htaccess_Proxy { char *url_to; } Htaccess_Proxy; +typedef enum CGI_type { + CGI_NONE = 0, + CGI_NORMAL, + CGI_INDEX, + CGI_INTERPRETOR, +} CGI_type; + enum { HTTP_OK = 200, HTTP_PARTIAL_CONTENT = 206, @@ -316,6 +324,7 @@ enum { 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, @@ -347,6 +356,7 @@ static const uint16_t http_response_type[] ALIGN2 = { HTTP_BAD_REQUEST, HTTP_FORBIDDEN, HTTP_INTERNAL_SERVER_ERROR, + HTTP_ENTITY_TOO_LARGE, #if 0 /* not implemented */ HTTP_CREATED, HTTP_ACCEPTED, @@ -377,6 +387,7 @@ static const struct { { "Bad Request", "Unsupported method" }, { "Forbidden", "" }, { "Internal Server Error", "Internal Server Error" }, + { "Entity Too Large", "Entity Too Large" }, #if 0 /* not implemented */ { "Created" }, { "Accepted" }, @@ -412,11 +423,6 @@ struct globals { 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 @@ -1437,23 +1443,17 @@ static void setenv1(const char *name, const char *value) * 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 */ @@ -1531,26 +1531,14 @@ static void send_cgi_and_exit( #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 */ @@ -1836,10 +1824,11 @@ static void send_HTTP_FORBIDDEN_and_exit_if_denied_ip(void) (unsigned char)(cur->mask) ); #endif - if ((rmt_ip & cur->mask) == cur->ip) + if ((rmt_ip & cur->mask) == cur->ip) { if (cur->allow_deny == 'A') return; send_headers_and_exit(HTTP_FORBIDDEN); + } } if (flg_deny_all) /* depends on whether we saw "D:*" */ @@ -2080,12 +2069,12 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) char *urlcopy; char *urlp; char *tptr; + int hdr_len = 0; #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; @@ -2260,13 +2249,68 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) header_buf = header_ptr = xmalloc(IOBUF_SIZE); #endif + 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_INTERPRETOR; + 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'; + 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 */ + hdr_len += iobuf_len; + if (hdr_len > MAX_HTTP_HEADER_SIZE) + send_headers_and_exit(HTTP_ENTITY_TOO_LARGE); if (DEBUG) bb_error_msg("header: '%s'", iobuf); @@ -2306,30 +2350,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) 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 @@ -2345,6 +2366,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) /* decodeBase64() skips whitespace itself */ decodeBase64(tptr); authorized = check_user_passwd(urlcopy, tptr); + continue; } #endif #if ENABLE_FEATURE_HTTPD_RANGES @@ -2362,6 +2384,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) range_start = -1; } } + continue; } #endif #if ENABLE_FEATURE_HTTPD_GZIP @@ -2379,6 +2402,36 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) content_gzip = 1; //} } + continue; + } +#endif +#if ENABLE_FEATURE_HTTPD_CGI + if (cgi_type == CGI_NONE) + continue; + if (STRNCASECMP(iobuf, "Content-Type:") == 0) { + putenv(xasprintf("CONTENT_TYPE=%s", skip_whitespace(iobuf + sizeof("Content-Type:")-1))); + } else { + char *after_colon = strchr(iobuf, ':'); + char *ch = iobuf; + char *val, *varval; + + if (!after_colon) + continue; + + *after_colon++ = '\0'; + + while (*ch) { + if (isalpha(*ch)) + *ch &= ~0x20; + else if (!isdigit(*ch)) + *ch = '_'; + ch++; + } + + val = skip_whitespace(after_colon); + varval = xmalloc(strlen(iobuf) + strlen(val) + 7); + sprintf(varval, "HTTP_%s=%s", iobuf, val); + putenv(varval); } #endif } /* while extra header reading */ @@ -2437,51 +2490,18 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) 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_NORMAL || cgi_type == CGI_INTERPRETOR) { + send_cgi_and_exit(urlcopy, urlcopy, prequest, length); + } else if (cgi_type == CGI_INDEX) { + send_cgi_and_exit("/cgi-bin/index.cgi", 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);
_______________________________________________ busybox mailing list [email protected] http://lists.busybox.net/mailman/listinfo/busybox
