diff options
author | Ron Yorston <rmy@pobox.com> | 2021-05-14 08:17:12 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2021-05-14 08:17:12 +0100 |
commit | a3f5a1b7f4275f713acf22f534f95c0da8392e53 (patch) | |
tree | 49b65422a3e9c33f508da9ccf3ae79d324bd9e96 /networking/httpd.c | |
parent | 375cda9a88024135d630ca8990d9aff4ea414e89 (diff) | |
parent | 7de0ab21d939a5a304157f75918d0318a95261a3 (diff) | |
download | busybox-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.c | 305 |
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 | ||
356 | typedef enum CGI_type { | ||
357 | CGI_NONE = 0, | ||
358 | CGI_NORMAL, | ||
359 | CGI_INDEX, | ||
360 | CGI_INTERPRETER, | ||
361 | } CGI_type; | ||
362 | |||
363 | enum { | 362 | enum { |
364 | HTTP_OK = 200, | 363 | HTTP_OK = 200, |
365 | HTTP_PARTIAL_CONTENT = 206, | 364 | HTTP_PARTIAL_CONTENT = 206, |
@@ -564,7 +563,6 @@ enum { | |||
564 | enum { | 563 | enum { |
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 | }; |
569 | static void send_file_and_exit(const char *url, int what) NORETURN; | 567 | static 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 | }; |
699 | static void parse_conf(const char *path, int flag) | 697 | static 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) | |||
2227 | static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) NORETURN; | 2216 | static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) NORETURN; |
2228 | static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) | 2217 | static 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); |