aboutsummaryrefslogtreecommitdiff
path: root/networking/httpd.c
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2021-05-14 08:17:12 +0100
committerRon Yorston <rmy@pobox.com>2021-05-14 08:17:12 +0100
commita3f5a1b7f4275f713acf22f534f95c0da8392e53 (patch)
tree49b65422a3e9c33f508da9ccf3ae79d324bd9e96 /networking/httpd.c
parent375cda9a88024135d630ca8990d9aff4ea414e89 (diff)
parent7de0ab21d939a5a304157f75918d0318a95261a3 (diff)
downloadbusybox-w32-a3f5a1b7f4275f713acf22f534f95c0da8392e53.tar.gz
busybox-w32-a3f5a1b7f4275f713acf22f534f95c0da8392e53.tar.bz2
busybox-w32-a3f5a1b7f4275f713acf22f534f95c0da8392e53.zip
Merge branch 'busybox' into merge
Diffstat (limited to 'networking/httpd.c')
-rw-r--r--networking/httpd.c305
1 files changed, 155 insertions, 150 deletions
diff --git a/networking/httpd.c b/networking/httpd.c
index 08313bbc7..fcc49853a 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -312,6 +312,12 @@
312 312
313#define DEBUG 0 313#define DEBUG 0
314 314
315#if DEBUG
316# define dbg(...) fprintf(stderr, __VA_ARGS__)
317#else
318# define dbg(...) ((void)0)
319#endif
320
315#define IOBUF_SIZE 8192 321#define IOBUF_SIZE 8192
316#define MAX_HTTP_HEADERS_SIZE (32*1024) 322#define MAX_HTTP_HEADERS_SIZE (32*1024)
317 323
@@ -353,13 +359,6 @@ typedef struct Htaccess_Proxy {
353 char *url_to; 359 char *url_to;
354} Htaccess_Proxy; 360} Htaccess_Proxy;
355 361
356typedef enum CGI_type {
357 CGI_NONE = 0,
358 CGI_NORMAL,
359 CGI_INDEX,
360 CGI_INTERPRETER,
361} CGI_type;
362
363enum { 362enum {
364 HTTP_OK = 200, 363 HTTP_OK = 200,
365 HTTP_PARTIAL_CONTENT = 206, 364 HTTP_PARTIAL_CONTENT = 206,
@@ -564,7 +563,6 @@ enum {
564enum { 563enum {
565 SEND_HEADERS = (1 << 0), 564 SEND_HEADERS = (1 << 0),
566 SEND_BODY = (1 << 1), 565 SEND_BODY = (1 << 1),
567 SEND_HEADERS_AND_BODY = SEND_HEADERS + SEND_BODY,
568}; 566};
569static void send_file_and_exit(const char *url, int what) NORETURN; 567static void send_file_and_exit(const char *url, int what) NORETURN;
570 568
@@ -696,7 +694,7 @@ enum {
696 SIGNALED_PARSE = 1, /* path will be "/etc" */ 694 SIGNALED_PARSE = 1, /* path will be "/etc" */
697 SUBDIR_PARSE = 2, /* path will be derived from URL */ 695 SUBDIR_PARSE = 2, /* path will be derived from URL */
698}; 696};
699static void parse_conf(const char *path, int flag) 697static int parse_conf(const char *path, int flag)
700{ 698{
701 /* internally used extra flag state */ 699 /* internally used extra flag state */
702 enum { TRY_CURDIR_PARSE = 3 }; 700 enum { TRY_CURDIR_PARSE = 3 };
@@ -737,7 +735,7 @@ static void parse_conf(const char *path, int flag)
737 while ((f = fopen_for_read(filename)) == NULL) { 735 while ((f = fopen_for_read(filename)) == NULL) {
738 if (flag >= SUBDIR_PARSE) { /* SUBDIR or TRY_CURDIR */ 736 if (flag >= SUBDIR_PARSE) { /* SUBDIR or TRY_CURDIR */
739 /* config file not found, no changes to config */ 737 /* config file not found, no changes to config */
740 return; 738 return -1;
741 } 739 }
742 if (flag == FIRST_PARSE) { 740 if (flag == FIRST_PARSE) {
743 /* -c CONFFILE given, but CONFFILE doesn't exist? */ 741 /* -c CONFFILE given, but CONFFILE doesn't exist? */
@@ -1000,6 +998,7 @@ static void parse_conf(const char *path, int flag)
1000 } /* while (fgets) */ 998 } /* while (fgets) */
1001 999
1002 fclose(f); 1000 fclose(f);
1001 return 0;
1003} 1002}
1004 1003
1005#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR 1004#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
@@ -1186,8 +1185,7 @@ static void send_headers(unsigned responseNum)
1186 fprintf(stderr, "headers: '%s'\n", iobuf); 1185 fprintf(stderr, "headers: '%s'\n", iobuf);
1187 } 1186 }
1188 full_write(STDOUT_FILENO, iobuf, len); 1187 full_write(STDOUT_FILENO, iobuf, len);
1189 if (DEBUG) 1188 dbg("writing error page: '%s'\n", error_page);
1190 fprintf(stderr, "writing error page: '%s'\n", error_page);
1191 return send_file_and_exit(error_page, SEND_BODY); 1189 return send_file_and_exit(error_page, SEND_BODY);
1192 } 1190 }
1193#endif 1191#endif
@@ -1542,8 +1540,7 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
1542 } 1540 }
1543 if (full_write(STDOUT_FILENO, rbuf, count) != count) 1541 if (full_write(STDOUT_FILENO, rbuf, count) != count)
1544 break; 1542 break;
1545 if (DEBUG) 1543 dbg("cgi read %d bytes: '%.*s'\n", count, count, rbuf);
1546 fprintf(stderr, "cgi read %d bytes: '%.*s'\n", count, count, rbuf);
1547 } /* if (pfd[FROM_CGI].revents) */ 1544 } /* if (pfd[FROM_CGI].revents) */
1548 } /* while (1) */ 1545 } /* while (1) */
1549 log_and_exit(); 1546 log_and_exit();
@@ -1789,8 +1786,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
1789 /* file_size and last_mod are already populated */ 1786 /* file_size and last_mod are already populated */
1790 } 1787 }
1791 if (fd < 0) { 1788 if (fd < 0) {
1792 if (DEBUG) 1789 dbg("can't open '%s'\n", url);
1793 bb_perror_msg("can't open '%s'", url);
1794 /* Error pages are sent by using send_file_and_exit(SEND_BODY). 1790 /* Error pages are sent by using send_file_and_exit(SEND_BODY).
1795 * IOW: it is unsafe to call send_headers_and_exit 1791 * IOW: it is unsafe to call send_headers_and_exit
1796 * if what is SEND_BODY! Can recurse! */ 1792 * if what is SEND_BODY! Can recurse! */
@@ -1803,8 +1799,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
1803 sprintf(G.etag, "\"%"LL_FMT"x-%"LL_FMT"x\"", (unsigned long long)last_mod, (unsigned long long)file_size); 1799 sprintf(G.etag, "\"%"LL_FMT"x-%"LL_FMT"x\"", (unsigned long long)last_mod, (unsigned long long)file_size);
1804 1800
1805 if (G.if_none_match) { 1801 if (G.if_none_match) {
1806 if (DEBUG) 1802 dbg("If-None-Match:'%s' file's ETag:'%s'\n", G.if_none_match, G.etag);
1807 bb_perror_msg("If-None-Match and file's ETag are: '%s' '%s'\n", G.if_none_match, G.etag);
1808 /* Weak ETag comparision. 1803 /* Weak ETag comparision.
1809 * If-None-Match may have many ETags but they are quoted so we can use simple substring search */ 1804 * If-None-Match may have many ETags but they are quoted so we can use simple substring search */
1810 if (strstr(G.if_none_match, G.etag)) 1805 if (strstr(G.if_none_match, G.etag))
@@ -1880,9 +1875,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
1880 } 1875 }
1881 } 1876 }
1882 1877
1883 if (DEBUG) 1878 dbg("sending file '%s' content-type:%s\n", url, found_mime_type);
1884 bb_error_msg("sending file '%s' content-type: %s",
1885 url, found_mime_type);
1886 1879
1887#if ENABLE_FEATURE_HTTPD_RANGES 1880#if ENABLE_FEATURE_HTTPD_RANGES
1888 if (what == SEND_BODY /* err pages and ranges don't mix */ 1881 if (what == SEND_BODY /* err pages and ranges don't mix */
@@ -1952,9 +1945,7 @@ static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(unsigned remote_ip)
1952 Htaccess_IP *cur; 1945 Htaccess_IP *cur;
1953 1946
1954 for (cur = G.ip_a_d; cur; cur = cur->next) { 1947 for (cur = G.ip_a_d; cur; cur = cur->next) {
1955#if DEBUG 1948 dbg("checkPermIP: '%s' ? '%u.%u.%u.%u/%u.%u.%u.%u'\n",
1956 fprintf(stderr,
1957 "checkPermIP: '%s' ? '%u.%u.%u.%u/%u.%u.%u.%u'\n",
1958 rmt_ip_str, 1949 rmt_ip_str,
1959 (unsigned char)(cur->ip >> 24), 1950 (unsigned char)(cur->ip >> 24),
1960 (unsigned char)(cur->ip >> 16), 1951 (unsigned char)(cur->ip >> 16),
@@ -1965,7 +1956,6 @@ static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(unsigned remote_ip)
1965 (unsigned char)(cur->mask >> 8), 1956 (unsigned char)(cur->mask >> 8),
1966 (unsigned char)(cur->mask) 1957 (unsigned char)(cur->mask)
1967 ); 1958 );
1968#endif
1969 if ((remote_ip & cur->mask) == cur->ip) { 1959 if ((remote_ip & cur->mask) == cur->ip) {
1970 if (cur->allow_deny == 'A') 1960 if (cur->allow_deny == 'A')
1971 return; 1961 return;
@@ -2061,8 +2051,7 @@ static int check_user_passwd(const char *path, char *user_and_passwd)
2061#endif 2051#endif
2062 continue; 2052 continue;
2063 2053
2064 if (DEBUG) 2054 dbg("checkPerm: '%s' ? '%s'\n", dir_prefix, user_and_passwd);
2065 fprintf(stderr, "checkPerm: '%s' ? '%s'\n", dir_prefix, user_and_passwd);
2066 2055
2067 /* If it's not a prefix match, continue searching */ 2056 /* If it's not a prefix match, continue searching */
2068 len = strlen(dir_prefix); 2057 len = strlen(dir_prefix);
@@ -2227,7 +2216,6 @@ static void send_REQUEST_TIMEOUT_and_exit(int sig UNUSED_PARAM)
2227static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) NORETURN; 2216static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) NORETURN;
2228static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) 2217static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2229{ 2218{
2230 static const char request_GET[] ALIGN1 = "GET";
2231 struct stat sb; 2219 struct stat sb;
2232 char *urlcopy; 2220 char *urlcopy;
2233 char *urlp; 2221 char *urlp;
@@ -2238,14 +2226,21 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2238#if ENABLE_FEATURE_HTTPD_CGI 2226#if ENABLE_FEATURE_HTTPD_CGI
2239 unsigned total_headers_len; 2227 unsigned total_headers_len;
2240#endif 2228#endif
2241#if ENABLE_FEATURE_HTTPD_CGI
2242 static const char request_HEAD[] ALIGN1 = "HEAD";
2243 const char *prequest; 2229 const char *prequest;
2244 unsigned long length = 0; 2230 static const char request_GET[] ALIGN1 = "GET";
2245 enum CGI_type cgi_type = CGI_NONE; 2231 static const char request_HEAD[] ALIGN1 = "HEAD";
2246#elif ENABLE_FEATURE_HTTPD_PROXY 2232#if ENABLE_FEATURE_HTTPD_CGI
2247#define prequest request_GET 2233 static const char request_POST[] ALIGN1 = "POST";
2248 unsigned long length = 0; 2234 unsigned long POST_length;
2235 enum CGI_type {
2236 CGI_NONE = 0,
2237 CGI_NORMAL,
2238 CGI_INDEX,
2239 CGI_INTERPRETER,
2240 } cgi_type = CGI_NONE;
2241#endif
2242#if ENABLE_FEATURE_HTTPD_PROXY
2243 Htaccess_Proxy *proxy_entry;
2249#endif 2244#endif
2250#if ENABLE_FEATURE_HTTPD_BASIC_AUTH 2245#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
2251 smallint authorized = -1; 2246 smallint authorized = -1;
@@ -2297,35 +2292,33 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2297 signal(SIGALRM, send_REQUEST_TIMEOUT_and_exit); 2292 signal(SIGALRM, send_REQUEST_TIMEOUT_and_exit);
2298#endif 2293#endif
2299 2294
2300 if (!get_line()) /* EOF or error or empty line */ 2295 if (!get_line()) { /* EOF or error or empty line */
2301 send_headers_and_exit(HTTP_BAD_REQUEST); 2296 /* Observed Firefox to "speculatively" open
2297 * extra connections to a new site on first access,
2298 * they are closed in ~5 seconds with nothing
2299 * being sent at all.
2300 * (Presumably it's a method to decrease latency?)
2301 */
2302 if (verbose > 2)
2303 bb_simple_error_msg("eof on read, closing");
2304 /* Don't bother generating error page in this case,
2305 * just close the socket.
2306 */
2307 //send_headers_and_exit(HTTP_BAD_REQUEST);
2308 _exit(xfunc_error_retval);
2309 }
2310 dbg("Request:'%s'\n", iobuf);
2302 2311
2303 /* Determine type of request (GET/POST) */ 2312 /* Find URL */
2304 // rfc2616: method and URI is separated by exactly one space 2313 // rfc2616: method and URI is separated by exactly one space
2305 //urlp = strpbrk(iobuf, " \t"); - no, tab isn't allowed 2314 //urlp = strpbrk(iobuf, " \t"); - no, tab isn't allowed
2306 urlp = strchr(iobuf, ' '); 2315 urlp = strchr(iobuf, ' ');
2307 if (urlp == NULL) 2316 if (urlp == NULL)
2308 send_headers_and_exit(HTTP_BAD_REQUEST); 2317 send_headers_and_exit(HTTP_BAD_REQUEST);
2309 *urlp++ = '\0'; 2318 *urlp++ = '\0';
2310#if ENABLE_FEATURE_HTTPD_CGI
2311 prequest = request_GET;
2312 if (strcasecmp(iobuf, prequest) != 0) {
2313 prequest = request_HEAD;
2314 if (strcasecmp(iobuf, prequest) != 0) {
2315 prequest = "POST";
2316 if (strcasecmp(iobuf, prequest) != 0)
2317 send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
2318 }
2319 }
2320#else
2321 if (strcasecmp(iobuf, request_GET) != 0)
2322 send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
2323#endif
2324 // rfc2616: method and URI is separated by exactly one space
2325 //urlp = skip_whitespace(urlp); - should not be necessary 2319 //urlp = skip_whitespace(urlp); - should not be necessary
2326 if (urlp[0] != '/') 2320 if (urlp[0] != '/')
2327 send_headers_and_exit(HTTP_BAD_REQUEST); 2321 send_headers_and_exit(HTTP_BAD_REQUEST);
2328
2329 /* Find end of URL */ 2322 /* Find end of URL */
2330 HTTP_slash = strchr(urlp, ' '); 2323 HTTP_slash = strchr(urlp, ' ');
2331 /* Is it " HTTP/"? */ 2324 /* Is it " HTTP/"? */
@@ -2333,48 +2326,62 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2333 send_headers_and_exit(HTTP_BAD_REQUEST); 2326 send_headers_and_exit(HTTP_BAD_REQUEST);
2334 *HTTP_slash++ = '\0'; 2327 *HTTP_slash++ = '\0';
2335 2328
2336 /* Copy URL from after "GET "/"POST " to stack-allocated char[] */
2337 urlcopy = alloca((HTTP_slash - urlp) + 2 + strlen(index_page));
2338 /*if (urlcopy == NULL)
2339 * send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);*/
2340 strcpy(urlcopy, urlp);
2341 /* NB: urlcopy ptr is never changed after this */
2342
2343#if ENABLE_FEATURE_HTTPD_PROXY 2329#if ENABLE_FEATURE_HTTPD_PROXY
2344 { 2330 proxy_entry = find_proxy_entry(urlp);
2331 if (proxy_entry) {
2345 int proxy_fd; 2332 int proxy_fd;
2346 len_and_sockaddr *lsa; 2333 len_and_sockaddr *lsa;
2347 Htaccess_Proxy *proxy_entry = find_proxy_entry(urlcopy); 2334
2348 2335 if (verbose > 1)
2349 if (proxy_entry) { 2336 bb_error_msg("proxy:%s", urlp);
2350 if (verbose > 1) 2337 lsa = host2sockaddr(proxy_entry->host_port, 80);
2351 bb_error_msg("proxy:%s", urlcopy); 2338 if (!lsa)
2352 lsa = host2sockaddr(proxy_entry->host_port, 80); 2339 send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
2353 if (!lsa) 2340 proxy_fd = socket(lsa->u.sa.sa_family, SOCK_STREAM, 0);
2354 send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR); 2341 if (proxy_fd < 0)
2355 proxy_fd = socket(lsa->u.sa.sa_family, SOCK_STREAM, 0); 2342 send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
2356 if (proxy_fd < 0) 2343 if (connect(proxy_fd, &lsa->u.sa, lsa->len) < 0)
2357 send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR); 2344 send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
2358 if (connect(proxy_fd, &lsa->u.sa, lsa->len) < 0) 2345 /* Disable peer header reading timeout */
2359 send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR); 2346 alarm(0);
2360 /* Disable peer header reading timeout */ 2347 /* Config directive was of the form:
2361 alarm(0); 2348 * P:/url:[http://]hostname[:port]/new/path
2362 /* Config directive was of the form: 2349 * When /urlSFX is requested, reverse proxy it
2363 * P:/url:[http://]hostname[:port]/new/path 2350 * to http://hostname[:port]/new/pathSFX
2364 * When /urlSFX is requested, reverse proxy it 2351 */
2365 * to http://hostname[:port]/new/pathSFX 2352 fdprintf(proxy_fd, "%s %s%s %s\r\n",
2366 */ 2353 iobuf, /* "GET" / "POST" / etc */
2367 fdprintf(proxy_fd, "%s %s%s %s\r\n", 2354 proxy_entry->url_to, /* "/new/path" */
2368 prequest, /* "GET" or "POST" */ 2355 urlp + strlen(proxy_entry->url_from), /* "SFX" */
2369 proxy_entry->url_to, /* "/new/path" */ 2356 HTTP_slash /* "HTTP/xyz" */
2370 urlcopy + strlen(proxy_entry->url_from), /* "SFX" */ 2357 );
2371 HTTP_slash /* "HTTP/xyz" */ 2358 cgi_io_loop_and_exit(proxy_fd, proxy_fd, /*max POST length:*/ INT_MAX);
2372 );
2373 cgi_io_loop_and_exit(proxy_fd, proxy_fd, /*max POST length:*/ INT_MAX);
2374 }
2375 } 2359 }
2376#endif 2360#endif
2377 2361
2362 /* Determine type of request (GET/POST/...) */
2363 prequest = request_GET;
2364 if (strcasecmp(iobuf, prequest) == 0)
2365 goto found;
2366 prequest = request_HEAD;
2367 if (strcasecmp(iobuf, prequest) == 0)
2368 goto found;
2369#if !ENABLE_FEATURE_HTTPD_CGI
2370 send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
2371#else
2372 prequest = request_POST;
2373 if (strcasecmp(iobuf, prequest) == 0)
2374 goto found;
2375 /* For CGI, allow DELETE, PUT, OPTIONS, etc too */
2376 prequest = alloca(16);
2377 safe_strncpy((char*)prequest, iobuf, 16);
2378#endif
2379 found:
2380 /* Copy URL to stack-allocated char[] */
2381 urlcopy = alloca((HTTP_slash - urlp) + 2 + strlen(index_page));
2382 strcpy(urlcopy, urlp);
2383 /* NB: urlcopy ptr is never changed after this */
2384
2378 /* Extract url args if present */ 2385 /* Extract url args if present */
2379 g_query = strchr(urlcopy, '?'); 2386 g_query = strchr(urlcopy, '?');
2380 if (g_query) 2387 if (g_query)
@@ -2393,7 +2400,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2393 /* Algorithm stolen from libbb bb_simplify_path(), 2400 /* Algorithm stolen from libbb bb_simplify_path(),
2394 * but don't strdup, retain trailing slash, protect root */ 2401 * but don't strdup, retain trailing slash, protect root */
2395 urlp = tptr = urlcopy; 2402 urlp = tptr = urlcopy;
2396 for (;;) { 2403 while (1) {
2397 if (*urlp == '/') { 2404 if (*urlp == '/') {
2398 /* skip duplicate (or initial) slash */ 2405 /* skip duplicate (or initial) slash */
2399 if (*tptr == '/') { 2406 if (*tptr == '/') {
@@ -2424,13 +2431,6 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2424 tptr++; 2431 tptr++;
2425 } 2432 }
2426 2433
2427 /* If URL is a directory, add '/' */
2428 if (urlp[-1] != '/') {
2429 if (is_directory(urlcopy + 1, /*followlinks:*/ 1)) {
2430 found_moved_temporarily = urlcopy;
2431 }
2432 }
2433
2434 /* Log it */ 2434 /* Log it */
2435 if (verbose > 1) 2435 if (verbose > 1)
2436 bb_error_msg("url:%s", urlcopy); 2436 bb_error_msg("url:%s", urlcopy);
@@ -2439,11 +2439,9 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2439 while ((tptr = strchr(tptr + 1, '/')) != NULL) { 2439 while ((tptr = strchr(tptr + 1, '/')) != NULL) {
2440 /* have path1/path2 */ 2440 /* have path1/path2 */
2441 *tptr = '\0'; 2441 *tptr = '\0';
2442 if (is_directory(urlcopy + 1, /*followlinks:*/ 1)) { 2442 /* may have subdir config */
2443 /* may have subdir config */ 2443 if (parse_conf(urlcopy + 1, SUBDIR_PARSE) == 0)
2444 parse_conf(urlcopy + 1, SUBDIR_PARSE);
2445 if_ip_denied_send_HTTP_FORBIDDEN_and_exit(remote_ip); 2444 if_ip_denied_send_HTTP_FORBIDDEN_and_exit(remote_ip);
2446 }
2447 *tptr = '/'; 2445 *tptr = '/';
2448 } 2446 }
2449 2447
@@ -2470,36 +2468,45 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2470 strcpy(urlp, index_page); 2468 strcpy(urlp, index_page);
2471 } 2469 }
2472 if (stat(tptr, &sb) == 0) { 2470 if (stat(tptr, &sb) == 0) {
2471 /* If URL is a directory with no slash, set up
2472 * "HTTP/1.1 302 Found" "Location: /dir/" reply */
2473 if (urlp[-1] != '/' && S_ISDIR(sb.st_mode)) {
2474 found_moved_temporarily = urlcopy;
2475 } else {
2473#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 2476#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
2474 char *suffix = strrchr(tptr, '.'); 2477 char *suffix = strrchr(tptr, '.');
2475 if (suffix) { 2478 if (suffix) {
2476 Htaccess *cur; 2479 Htaccess *cur;
2477 for (cur = script_i; cur; cur = cur->next) { 2480 for (cur = script_i; cur; cur = cur->next) {
2478 if (strcmp(cur->before_colon + 1, suffix) == 0) { 2481 if (strcmp(cur->before_colon + 1, suffix) == 0) {
2479 cgi_type = CGI_INTERPRETER; 2482 cgi_type = CGI_INTERPRETER;
2480 break; 2483 break;
2484 }
2481 } 2485 }
2482 } 2486 }
2483 }
2484#endif 2487#endif
2485 if (!found_moved_temporarily) {
2486 file_size = sb.st_size; 2488 file_size = sb.st_size;
2487 last_mod = sb.st_mtime; 2489 last_mod = sb.st_mtime;
2488 } 2490 }
2489 } 2491 }
2490#if ENABLE_FEATURE_HTTPD_CGI 2492#if ENABLE_FEATURE_HTTPD_CGI
2491 else if (urlp[-1] == '/') { 2493 else if (urlp[-1] == '/') {
2492 /* It's a dir URL and there is no index.html 2494 /* It's a dir URL and there is no index.html */
2493 * Try cgi-bin/index.cgi */ 2495 /* Is there cgi-bin/index.cgi? */
2494 if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) { 2496 if (access("/cgi-bin/index.cgi"+1, X_OK) != 0)
2495 cgi_type = CGI_INDEX; 2497 send_headers_and_exit(HTTP_NOT_FOUND); /* no */
2496 } 2498 cgi_type = CGI_INDEX;
2497 } 2499 }
2498#endif 2500#endif
2501
2502#if ENABLE_FEATURE_HTTPD_BASIC_AUTH || ENABLE_FEATURE_HTTPD_CGI
2503 /* check_user_passwd() would be confused by added .../index.html, truncate it */
2499 urlp[0] = '\0'; 2504 urlp[0] = '\0';
2505#endif
2500 2506
2501#if ENABLE_FEATURE_HTTPD_CGI 2507#if ENABLE_FEATURE_HTTPD_CGI
2502 total_headers_len = 0; 2508 total_headers_len = 0;
2509 POST_length = 0;
2503#endif 2510#endif
2504 2511
2505 /* Read until blank line */ 2512 /* Read until blank line */
@@ -2513,26 +2520,18 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2513 if (total_headers_len >= MAX_HTTP_HEADERS_SIZE) 2520 if (total_headers_len >= MAX_HTTP_HEADERS_SIZE)
2514 send_headers_and_exit(HTTP_ENTITY_TOO_LARGE); 2521 send_headers_and_exit(HTTP_ENTITY_TOO_LARGE);
2515#endif 2522#endif
2516 if (DEBUG) 2523 dbg("header:'%s'\n", iobuf);
2517 bb_error_msg("header: '%s'", iobuf); 2524#if ENABLE_FEATURE_HTTPD_CGI
2518#if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY 2525 /* Only POST needs to know POST_length */
2519 /* Try and do our best to parse more lines */ 2526 if (prequest == request_POST && STRNCASECMP(iobuf, "Content-Length:") == 0) {
2520 if (STRNCASECMP(iobuf, "Content-Length:") == 0) { 2527 tptr = skip_whitespace(iobuf + sizeof("Content-Length:") - 1);
2521 /* extra read only for POST */ 2528 if (!tptr[0])
2522 if (prequest != request_GET 2529 send_headers_and_exit(HTTP_BAD_REQUEST);
2523# if ENABLE_FEATURE_HTTPD_CGI 2530 /* not using strtoul: it ignores leading minus! */
2524 && prequest != request_HEAD 2531 POST_length = bb_strtou(tptr, NULL, 10);
2525# endif 2532 /* length is "ulong", but we need to pass it to int later */
2526 ) { 2533 if (errno || POST_length > INT_MAX)
2527 tptr = skip_whitespace(iobuf + sizeof("Content-Length:") - 1); 2534 send_headers_and_exit(HTTP_BAD_REQUEST);
2528 if (!tptr[0])
2529 send_headers_and_exit(HTTP_BAD_REQUEST);
2530 /* not using strtoul: it ignores leading minus! */
2531 length = bb_strtou(tptr, NULL, 10);
2532 /* length is "ulong", but we need to pass it to int later */
2533 if (errno || length > INT_MAX)
2534 send_headers_and_exit(HTTP_BAD_REQUEST);
2535 }
2536 continue; 2535 continue;
2537 } 2536 }
2538#endif 2537#endif
@@ -2646,37 +2645,36 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2646 send_headers_and_exit(HTTP_UNAUTHORIZED); 2645 send_headers_and_exit(HTTP_UNAUTHORIZED);
2647#endif 2646#endif
2648 2647
2649 if (found_moved_temporarily) { 2648 if (found_moved_temporarily)
2650 send_headers_and_exit(HTTP_MOVED_TEMPORARILY); 2649 send_headers_and_exit(HTTP_MOVED_TEMPORARILY);
2651 }
2652
2653 tptr = urlcopy + 1; /* skip first '/' */
2654 2650
2655#if ENABLE_FEATURE_HTTPD_CGI 2651#if ENABLE_FEATURE_HTTPD_CGI
2656 if (cgi_type != CGI_NONE) { 2652 if (cgi_type != CGI_NONE) {
2657 send_cgi_and_exit( 2653 send_cgi_and_exit(
2658 (cgi_type == CGI_INDEX) ? "/cgi-bin/index.cgi" 2654 (cgi_type == CGI_INDEX) ? "/cgi-bin/index.cgi"
2659 /*CGI_NORMAL or CGI_INTERPRETER*/ : urlcopy, 2655 /*CGI_NORMAL or CGI_INTERPRETER*/ : urlcopy,
2660 urlcopy, prequest, length 2656 urlcopy, prequest, POST_length
2661 ); 2657 );
2662 } 2658 }
2663#endif 2659#endif
2664 2660
2665 if (urlp[-1] == '/') {
2666 strcpy(urlp, index_page);
2667 }
2668
2669#if ENABLE_FEATURE_HTTPD_CGI 2661#if ENABLE_FEATURE_HTTPD_CGI
2670 if (prequest != request_GET && prequest != request_HEAD) { 2662 if (prequest != request_GET && prequest != request_HEAD) {
2671 /* POST for files does not make sense */ 2663 /* POST / DELETE / PUT / OPTIONS for files do not make sense */
2672 send_headers_and_exit(HTTP_NOT_IMPLEMENTED); 2664 send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
2673 } 2665 }
2674 send_file_and_exit(tptr,
2675 (prequest != request_HEAD ? SEND_HEADERS_AND_BODY : SEND_HEADERS)
2676 );
2677#else 2666#else
2678 send_file_and_exit(tptr, SEND_HEADERS_AND_BODY); 2667 /* !CGI: it can be only GET or HEAD */
2679#endif 2668#endif
2669
2670#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
2671 /* Restore truncated .../index.html */
2672 if (urlp[-1] == '/')
2673 urlp[0] = index_page[0];
2674#endif
2675 send_file_and_exit(urlcopy + 1,
2676 (prequest != request_HEAD ? (SEND_HEADERS + SEND_BODY) : SEND_HEADERS)
2677 );
2680} 2678}
2681 2679
2682 2680
@@ -2705,6 +2703,13 @@ static void mini_httpd(int server_socket)
2705 n = accept(server_socket, &fromAddr.u.sa, &fromAddr.len); 2703 n = accept(server_socket, &fromAddr.u.sa, &fromAddr.len);
2706 if (n < 0) 2704 if (n < 0)
2707 continue; 2705 continue;
2706//TODO: we can reject connects from denied IPs right away;
2707//also, we might want to do one MSG_DONTWAIT'ed recv() here
2708//to detect immediate EOF,
2709//to avoid forking a whole new process for attackers
2710//who open and close lots of connections.
2711//(OTOH, the real mitigtion for this sort of thing is
2712//to ratelimit connects in iptables)
2708 2713
2709 /* set the KEEPALIVE option to cull dead connections */ 2714 /* set the KEEPALIVE option to cull dead connections */
2710 setsockopt_keepalive(n); 2715 setsockopt_keepalive(n);