aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2019-04-16 11:37:02 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2019-04-16 11:37:02 +0200
commitfba665a8889ad7ec20f926bf2719be5c688ed829 (patch)
tree531b305e8b570f42fb0c4f7a1d3844f165b51669
parentd0ae4103ddca21b7b765347611a9cf33f0cccd74 (diff)
downloadbusybox-w32-fba665a8889ad7ec20f926bf2719be5c688ed829.tar.gz
busybox-w32-fba665a8889ad7ec20f926bf2719be5c688ed829.tar.bz2
busybox-w32-fba665a8889ad7ec20f926bf2719be5c688ed829.zip
httpd: put all headers into environment as HTTP_UPPERCASED_HEADER=val
Set up environment variables before running the CGI script. 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 '_'. function old new delta http_response 80 88 +8 http_response_type 20 22 +2 send_headers 718 715 -3 parse_conf 1481 1478 -3 get_line 128 110 -18 cgi_io_loop_and_exit 599 569 -30 send_cgi_and_exit 882 738 -144 handle_incoming_and_exit 2793 2592 -201 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/6 up/down: 10/-399) Total: -389 bytes text data bss dec hex filename 982178 485 7296 989959 f1b07 busybox_old 981675 485 7296 989456 f1910 busybox_unstripped Signed-off-by: Alexander Vickberg <wickbergster@gmail.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--networking/httpd.c225
1 files changed, 120 insertions, 105 deletions
diff --git a/networking/httpd.c b/networking/httpd.c
index 53be500d3..11c26a891 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -267,6 +267,7 @@
267#define DEBUG 0 267#define DEBUG 0
268 268
269#define IOBUF_SIZE 8192 269#define IOBUF_SIZE 8192
270#define MAX_HTTP_HEADERS_SIZE ((8*1024) - 16)
270#if PIPE_BUF >= IOBUF_SIZE 271#if PIPE_BUF >= IOBUF_SIZE
271# error "PIPE_BUF >= IOBUF_SIZE" 272# error "PIPE_BUF >= IOBUF_SIZE"
272#endif 273#endif
@@ -305,6 +306,13 @@ typedef struct Htaccess_Proxy {
305 char *url_to; 306 char *url_to;
306} Htaccess_Proxy; 307} Htaccess_Proxy;
307 308
309typedef enum CGI_type {
310 CGI_NONE = 0,
311 CGI_NORMAL,
312 CGI_INDEX,
313 CGI_INTERPRETER,
314} CGI_type;
315
308enum { 316enum {
309 HTTP_OK = 200, 317 HTTP_OK = 200,
310 HTTP_PARTIAL_CONTENT = 206, 318 HTTP_PARTIAL_CONTENT = 206,
@@ -316,6 +324,7 @@ enum {
316 HTTP_REQUEST_TIMEOUT = 408, 324 HTTP_REQUEST_TIMEOUT = 408,
317 HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */ 325 HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */
318 HTTP_INTERNAL_SERVER_ERROR = 500, 326 HTTP_INTERNAL_SERVER_ERROR = 500,
327 HTTP_ENTITY_TOO_LARGE = 413,
319 HTTP_CONTINUE = 100, 328 HTTP_CONTINUE = 100,
320#if 0 /* future use */ 329#if 0 /* future use */
321 HTTP_SWITCHING_PROTOCOLS = 101, 330 HTTP_SWITCHING_PROTOCOLS = 101,
@@ -347,6 +356,7 @@ static const uint16_t http_response_type[] ALIGN2 = {
347 HTTP_BAD_REQUEST, 356 HTTP_BAD_REQUEST,
348 HTTP_FORBIDDEN, 357 HTTP_FORBIDDEN,
349 HTTP_INTERNAL_SERVER_ERROR, 358 HTTP_INTERNAL_SERVER_ERROR,
359 HTTP_ENTITY_TOO_LARGE,
350#if 0 /* not implemented */ 360#if 0 /* not implemented */
351 HTTP_CREATED, 361 HTTP_CREATED,
352 HTTP_ACCEPTED, 362 HTTP_ACCEPTED,
@@ -377,6 +387,7 @@ static const struct {
377 { "Bad Request", "Unsupported method" }, 387 { "Bad Request", "Unsupported method" },
378 { "Forbidden", "" }, 388 { "Forbidden", "" },
379 { "Internal Server Error", "Internal Server Error" }, 389 { "Internal Server Error", "Internal Server Error" },
390 { "Entity Too Large", "Entity Too Large" },
380#if 0 /* not implemented */ 391#if 0 /* not implemented */
381 { "Created" }, 392 { "Created" },
382 { "Accepted" }, 393 { "Accepted" },
@@ -412,11 +423,6 @@ struct globals {
412 423
413 IF_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;) 424 IF_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;)
414 IF_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;) 425 IF_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
415 IF_FEATURE_HTTPD_CGI(char *referer;)
416 IF_FEATURE_HTTPD_CGI(char *user_agent;)
417 IF_FEATURE_HTTPD_CGI(char *host;)
418 IF_FEATURE_HTTPD_CGI(char *http_accept;)
419 IF_FEATURE_HTTPD_CGI(char *http_accept_language;)
420 426
421 off_t file_size; /* -1 - unknown */ 427 off_t file_size; /* -1 - unknown */
422#if ENABLE_FEATURE_HTTPD_RANGES 428#if ENABLE_FEATURE_HTTPD_RANGES
@@ -1439,23 +1445,17 @@ static void setenv1(const char *name, const char *value)
1439 * const char *url The requested URL (with leading /). 1445 * const char *url The requested URL (with leading /).
1440 * const char *orig_uri The original URI before rewriting (if any) 1446 * const char *orig_uri The original URI before rewriting (if any)
1441 * int post_len Length of the POST body. 1447 * int post_len Length of the POST body.
1442 * const char *cookie For set HTTP_COOKIE.
1443 * const char *content_type For set CONTENT_TYPE.
1444 */ 1448 */
1445static void send_cgi_and_exit( 1449static void send_cgi_and_exit(
1446 const char *url, 1450 const char *url,
1447 const char *orig_uri, 1451 const char *orig_uri,
1448 const char *request, 1452 const char *request,
1449 int post_len, 1453 int post_len) NORETURN;
1450 const char *cookie,
1451 const char *content_type) NORETURN;
1452static void send_cgi_and_exit( 1454static void send_cgi_and_exit(
1453 const char *url, 1455 const char *url,
1454 const char *orig_uri, 1456 const char *orig_uri,
1455 const char *request, 1457 const char *request,
1456 int post_len, 1458 int post_len)
1457 const char *cookie,
1458 const char *content_type)
1459{ 1459{
1460 struct fd_pair fromCgi; /* CGI -> httpd pipe */ 1460 struct fd_pair fromCgi; /* CGI -> httpd pipe */
1461 struct fd_pair toCgi; /* httpd -> CGI pipe */ 1461 struct fd_pair toCgi; /* httpd -> CGI pipe */
@@ -1533,26 +1533,14 @@ static void send_cgi_and_exit(
1533#endif 1533#endif
1534 } 1534 }
1535 } 1535 }
1536 setenv1("HTTP_USER_AGENT", G.user_agent);
1537 if (G.http_accept)
1538 setenv1("HTTP_ACCEPT", G.http_accept);
1539 if (G.http_accept_language)
1540 setenv1("HTTP_ACCEPT_LANGUAGE", G.http_accept_language);
1541 if (post_len) 1536 if (post_len)
1542 putenv(xasprintf("CONTENT_LENGTH=%u", post_len)); 1537 putenv(xasprintf("CONTENT_LENGTH=%u", post_len));
1543 if (cookie)
1544 setenv1("HTTP_COOKIE", cookie);
1545 if (content_type)
1546 setenv1("CONTENT_TYPE", content_type);
1547#if ENABLE_FEATURE_HTTPD_BASIC_AUTH 1538#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
1548 if (remoteuser) { 1539 if (remoteuser) {
1549 setenv1("REMOTE_USER", remoteuser); 1540 setenv1("REMOTE_USER", remoteuser);
1550 putenv((char*)"AUTH_TYPE=Basic"); 1541 putenv((char*)"AUTH_TYPE=Basic");
1551 } 1542 }
1552#endif 1543#endif
1553 if (G.referer)
1554 setenv1("HTTP_REFERER", G.referer);
1555 setenv1("HTTP_HOST", G.host); /* set to "" if NULL */
1556 /* setenv1("SERVER_NAME", safe_gethostname()); - don't do this, 1544 /* setenv1("SERVER_NAME", safe_gethostname()); - don't do this,
1557 * just run "env SERVER_NAME=xyz httpd ..." instead */ 1545 * just run "env SERVER_NAME=xyz httpd ..." instead */
1558 1546
@@ -2083,12 +2071,12 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2083 char *urlcopy; 2071 char *urlcopy;
2084 char *urlp; 2072 char *urlp;
2085 char *tptr; 2073 char *tptr;
2074 unsigned header_len;
2086#if ENABLE_FEATURE_HTTPD_CGI 2075#if ENABLE_FEATURE_HTTPD_CGI
2087 static const char request_HEAD[] ALIGN1 = "HEAD"; 2076 static const char request_HEAD[] ALIGN1 = "HEAD";
2088 const char *prequest; 2077 const char *prequest;
2089 char *cookie = NULL;
2090 char *content_type = NULL;
2091 unsigned long length = 0; 2078 unsigned long length = 0;
2079 enum CGI_type cgi_type = CGI_NONE;
2092#elif ENABLE_FEATURE_HTTPD_PROXY 2080#elif ENABLE_FEATURE_HTTPD_PROXY
2093#define prequest request_GET 2081#define prequest request_GET
2094 unsigned long length = 0; 2082 unsigned long length = 0;
@@ -2201,7 +2189,9 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2201#if ENABLE_FEATURE_HTTPD_PROXY 2189#if ENABLE_FEATURE_HTTPD_PROXY
2202 proxy_entry = find_proxy_entry(urlcopy); 2190 proxy_entry = find_proxy_entry(urlcopy);
2203 if (proxy_entry) 2191 if (proxy_entry)
2204 header_buf = header_ptr = xmalloc(IOBUF_SIZE); 2192 header_buf = header_ptr = xmalloc(MAX_HTTP_HEADERS_SIZE + 2);
2193//TODO: why we don't just start pumping data to proxy here,
2194//without decoding URL, saving headers and such???
2205 else 2195 else
2206#endif 2196#endif
2207 { 2197 {
@@ -2275,31 +2265,79 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2275 *tptr = '/'; 2265 *tptr = '/';
2276 } 2266 }
2277 2267
2268 tptr = urlcopy + 1; /* skip first '/' */
2269
2270#if ENABLE_FEATURE_HTTPD_CGI
2271 if (is_prefixed_with(tptr, "cgi-bin/")) {
2272 if (tptr[8] == '\0') {
2273 /* protect listing "cgi-bin/" */
2274 send_headers_and_exit(HTTP_FORBIDDEN);
2275 }
2276 cgi_type = CGI_NORMAL;
2277 }
2278#endif
2279
2280 if (urlp[-1] == '/') {
2281 /* When index_page string is appended to <dir>/ URL, it overwrites
2282 * the query string. If we fall back to call /cgi-bin/index.cgi,
2283 * query string would be lost and not available to the CGI.
2284 * Work around it by making a deep copy.
2285 */
2286 if (ENABLE_FEATURE_HTTPD_CGI)
2287 g_query = xstrdup(g_query); /* ok for NULL too */
2288 strcpy(urlp, index_page);
2289 }
2290 if (stat(tptr, &sb) == 0) {
2291#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
2292 char *suffix = strrchr(tptr, '.');
2293 if (suffix) {
2294 Htaccess *cur;
2295 for (cur = script_i; cur; cur = cur->next) {
2296 if (strcmp(cur->before_colon + 1, suffix) == 0) {
2297 cgi_type = CGI_INTERPRETER;
2298 break;
2299 }
2300 }
2301 }
2302#endif
2303 if (!found_moved_temporarily) {
2304 file_size = sb.st_size;
2305 last_mod = sb.st_mtime;
2306 }
2307 }
2308#if ENABLE_FEATURE_HTTPD_CGI
2309 else if (urlp[-1] == '/') {
2310 /* It's a dir URL and there is no index.html
2311 * Try cgi-bin/index.cgi */
2312 if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) {
2313 cgi_type = CGI_INDEX;
2314 }
2315 }
2316#endif
2317 urlp[0] = '\0';
2318
2319 header_len = 0;
2278 if (http_major_version >= '0') { 2320 if (http_major_version >= '0') {
2279 /* Request was with "... HTTP/nXXX", and n >= 0 */ 2321 /* Request was with "... HTTP/nXXX", and n >= 0 */
2280 2322
2281 /* Read until blank line */ 2323 /* Read until blank line */
2282 while (1) { 2324 while (1) {
2283 if (!get_line()) 2325 int iobuf_len = get_line();
2326 if (!iobuf_len)
2284 break; /* EOF or error or empty line */ 2327 break; /* EOF or error or empty line */
2328 header_len += iobuf_len + 2;
2329 if (header_len >= MAX_HTTP_HEADERS_SIZE)
2330 send_headers_and_exit(HTTP_ENTITY_TOO_LARGE);
2285 if (DEBUG) 2331 if (DEBUG)
2286 bb_error_msg("header: '%s'", iobuf); 2332 bb_error_msg("header: '%s'", iobuf);
2287
2288#if ENABLE_FEATURE_HTTPD_PROXY 2333#if ENABLE_FEATURE_HTTPD_PROXY
2289 if (proxy_entry) { 2334 if (proxy_entry) {
2290 /* Why 4, not 2? 2335 /* protected from overflow by header_len check above */
2291 * We need 2 more bytes for yet another "\r\n" - 2336 memcpy(header_ptr, iobuf, iobuf_len);
2292 * see near fdprintf(proxy_fd...) further below. 2337 header_ptr += iobuf_len;
2293 */ 2338 header_ptr[0] = '\r';
2294 int maxlen = (IOBUF_SIZE-4) - (int)(header_ptr - header_buf); 2339 header_ptr[1] = '\n';
2295 if (maxlen > 0) { 2340 header_ptr += 2;
2296 int len = strnlen(iobuf, maxlen);
2297 memcpy(header_ptr, iobuf, len);
2298 header_ptr += len;
2299 header_ptr[0] = '\r';
2300 header_ptr[1] = '\n';
2301 header_ptr += 2;
2302 }
2303 } 2341 }
2304#endif 2342#endif
2305 2343
@@ -2321,30 +2359,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2321 if (errno || length > INT_MAX) 2359 if (errno || length > INT_MAX)
2322 send_headers_and_exit(HTTP_BAD_REQUEST); 2360 send_headers_and_exit(HTTP_BAD_REQUEST);
2323 } 2361 }
2324 } 2362 continue;
2325#endif
2326#if ENABLE_FEATURE_HTTPD_CGI
2327 else if (STRNCASECMP(iobuf, "Cookie:") == 0) {
2328 if (!cookie) /* in case they send millions of these, do not OOM */
2329 cookie = xstrdup(skip_whitespace(iobuf + sizeof("Cookie:")-1));
2330 } else if (STRNCASECMP(iobuf, "Content-Type:") == 0) {
2331 if (!content_type)
2332 content_type = xstrdup(skip_whitespace(iobuf + sizeof("Content-Type:")-1));
2333 } else if (STRNCASECMP(iobuf, "Referer:") == 0) {
2334 if (!G.referer)
2335 G.referer = xstrdup(skip_whitespace(iobuf + sizeof("Referer:")-1));
2336 } else if (STRNCASECMP(iobuf, "User-Agent:") == 0) {
2337 if (!G.user_agent)
2338 G.user_agent = xstrdup(skip_whitespace(iobuf + sizeof("User-Agent:")-1));
2339 } else if (STRNCASECMP(iobuf, "Host:") == 0) {
2340 if (!G.host)
2341 G.host = xstrdup(skip_whitespace(iobuf + sizeof("Host:")-1));
2342 } else if (STRNCASECMP(iobuf, "Accept:") == 0) {
2343 if (!G.http_accept)
2344 G.http_accept = xstrdup(skip_whitespace(iobuf + sizeof("Accept:")-1));
2345 } else if (STRNCASECMP(iobuf, "Accept-Language:") == 0) {
2346 if (!G.http_accept_language)
2347 G.http_accept_language = xstrdup(skip_whitespace(iobuf + sizeof("Accept-Language:")-1));
2348 } 2363 }
2349#endif 2364#endif
2350#if ENABLE_FEATURE_HTTPD_BASIC_AUTH 2365#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
@@ -2360,6 +2375,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2360 /* decodeBase64() skips whitespace itself */ 2375 /* decodeBase64() skips whitespace itself */
2361 decodeBase64(tptr); 2376 decodeBase64(tptr);
2362 authorized = check_user_passwd(urlcopy, tptr); 2377 authorized = check_user_passwd(urlcopy, tptr);
2378 continue;
2363 } 2379 }
2364#endif 2380#endif
2365#if ENABLE_FEATURE_HTTPD_RANGES 2381#if ENABLE_FEATURE_HTTPD_RANGES
@@ -2377,6 +2393,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2377 range_start = -1; 2393 range_start = -1;
2378 } 2394 }
2379 } 2395 }
2396 continue;
2380 } 2397 }
2381#endif 2398#endif
2382#if ENABLE_FEATURE_HTTPD_GZIP 2399#if ENABLE_FEATURE_HTTPD_GZIP
@@ -2394,6 +2411,35 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2394 content_gzip = 1; 2411 content_gzip = 1;
2395 //} 2412 //}
2396 } 2413 }
2414 continue;
2415 }
2416#endif
2417#if ENABLE_FEATURE_HTTPD_CGI
2418 if (cgi_type != CGI_NONE) {
2419 bool ct = (STRNCASECMP(iobuf, "Content-Type:") == 0);
2420 char *cp;
2421 char *colon = strchr(iobuf, ':');
2422
2423 if (!colon)
2424 continue;
2425 cp = iobuf;
2426 while (cp < colon) {
2427 /* a-z => A-Z, not-alnum => _ */
2428 char c = (*cp & ~0x20); /* toupper for A-Za-z, undef for others */
2429 if ((unsigned)(c - 'A') <= ('Z' - 'A')) {
2430 *cp++ = c;
2431 continue;
2432 }
2433 if (!isdigit(*cp))
2434 *cp = '_';
2435 cp++;
2436 }
2437 /* "Content-Type:" gets no HTTP_ prefix, all others do */
2438 cp = xasprintf(ct ? "HTTP_%.*s=%s" + 5 : "HTTP_%.*s=%s",
2439 (int)(colon - iobuf), iobuf,
2440 skip_whitespace(colon + 1)
2441 );
2442 putenv(cp);
2397 } 2443 }
2398#endif 2444#endif
2399 } /* while extra header reading */ 2445 } /* while extra header reading */
@@ -2452,51 +2498,20 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2452 tptr = urlcopy + 1; /* skip first '/' */ 2498 tptr = urlcopy + 1; /* skip first '/' */
2453 2499
2454#if ENABLE_FEATURE_HTTPD_CGI 2500#if ENABLE_FEATURE_HTTPD_CGI
2455 if (is_prefixed_with(tptr, "cgi-bin/")) { 2501 if (cgi_type != CGI_NONE) {
2456 if (tptr[8] == '\0') { 2502 send_cgi_and_exit(
2457 /* protect listing "cgi-bin/" */ 2503 (cgi_type == CGI_INDEX) ? "/cgi-bin/index.cgi"
2458 send_headers_and_exit(HTTP_FORBIDDEN); 2504 /*CGI_NORMAL or CGI_INTERPRETER*/ : urlcopy,
2459 } 2505 urlcopy, prequest, length
2460 send_cgi_and_exit(urlcopy, urlcopy, prequest, length, cookie, content_type); 2506 );
2461 } 2507 }
2462#endif 2508#endif
2463 2509
2464 if (urlp[-1] == '/') { 2510 if (urlp[-1] == '/') {
2465 /* When index_page string is appended to <dir>/ URL, it overwrites
2466 * the query string. If we fall back to call /cgi-bin/index.cgi,
2467 * query string would be lost and not available to the CGI.
2468 * Work around it by making a deep copy.
2469 */
2470 if (ENABLE_FEATURE_HTTPD_CGI)
2471 g_query = xstrdup(g_query); /* ok for NULL too */
2472 strcpy(urlp, index_page); 2511 strcpy(urlp, index_page);
2473 } 2512 }
2474 if (stat(tptr, &sb) == 0) {
2475#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
2476 char *suffix = strrchr(tptr, '.');
2477 if (suffix) {
2478 Htaccess *cur;
2479 for (cur = script_i; cur; cur = cur->next) {
2480 if (strcmp(cur->before_colon + 1, suffix) == 0) {
2481 send_cgi_and_exit(urlcopy, urlcopy, prequest, length, cookie, content_type);
2482 }
2483 }
2484 }
2485#endif
2486 file_size = sb.st_size;
2487 last_mod = sb.st_mtime;
2488 }
2489#if ENABLE_FEATURE_HTTPD_CGI
2490 else if (urlp[-1] == '/') {
2491 /* It's a dir URL and there is no index.html
2492 * Try cgi-bin/index.cgi */
2493 if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) {
2494 urlp[0] = '\0'; /* remove index_page */
2495 send_cgi_and_exit("/cgi-bin/index.cgi", urlcopy, prequest, length, cookie, content_type);
2496 }
2497 }
2498 /* else fall through to send_file, it errors out if open fails: */
2499 2513
2514#if ENABLE_FEATURE_HTTPD_CGI
2500 if (prequest != request_GET && prequest != request_HEAD) { 2515 if (prequest != request_GET && prequest != request_HEAD) {
2501 /* POST for files does not make sense */ 2516 /* POST for files does not make sense */
2502 send_headers_and_exit(HTTP_NOT_IMPLEMENTED); 2517 send_headers_and_exit(HTTP_NOT_IMPLEMENTED);