[PATCH v4] httpd: Pass custom HTTP headers to CGI scripts
Alexander Vickberg
wickbergster at gmail.com
Tue Apr 16 06:30:12 UTC 2019
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 <wickbergster at gmail.com>
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);
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.busybox.net/pipermail/busybox/attachments/20190416/eb58bf57/attachment-0001.html>
More information about the busybox
mailing list