telnetd: treat all 2-byte IACs in 240..249 range as NOPs.
[oweals/busybox.git] / networking / httpd.c
index cef9b8baf8de4c8e3717f2b60497308542b8cd47..abe83a458d36971570fa3ea64a2a89d81a5ec2e5 100644 (file)
 //usage:     "\n       -d STRING       URL decode STRING"
 
 #include "libbb.h"
+#include "common_bufsiz.h"
 #if ENABLE_PAM
 /* PAM may include <locale.h>. We may need to undefine bbox's stub define: */
 # undef setlocale
 # include <security/pam_appl.h>
 # include <security/pam_misc.h>
 #endif
-#if ENABLE_FEATURE_HTTPD_USE_SENDFILE
+#if ENABLE_FEATURE_USE_SENDFILE
 # include <sys/sendfile.h>
 #endif
 /* amount of buffering in a pipe */
@@ -307,7 +308,8 @@ struct globals {
        Htaccess *script_i;     /* config script interpreters */
 #endif
        char *iobuf;            /* [IOBUF_SIZE] */
-#define hdr_buf bb_common_bufsiz1
+#define        hdr_buf bb_common_bufsiz1
+#define sizeof_hdr_buf COMMON_BUFSIZE
        char *hdr_ptr;
        int hdr_cnt;
 #if ENABLE_FEATURE_HTTPD_ERROR_PAGES
@@ -368,6 +370,7 @@ enum {
 # define content_gzip     0
 #endif
 #define INIT_G() do { \
+       setup_common_bufsiz(); \
        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
        IF_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \
        IF_FEATURE_HTTPD_RANGES(range_start = -1;) \
@@ -697,7 +700,7 @@ static void parse_conf(const char *path, int flag)
                                goto config_error;
                        }
                        *host_port++ = '\0';
-                       if (strncmp(host_port, "http://", 7) == 0)
+                       if (is_prefixed_with(host_port, "http://"))
                                host_port += 7;
                        if (*host_port == '\0') {
                                goto config_error;
@@ -967,19 +970,30 @@ static void send_headers(int responseNum)
        }
 #endif
        if (responseNum == HTTP_MOVED_TEMPORARILY) {
-               len += sprintf(iobuf + len, "Location: %s/%s%s\r\n",
+               /* Responding to "GET /dir" with
+                * "HTTP/1.0 302 Found" "Location: /dir/"
+                * - IOW, asking them to repeat with a slash.
+                * Here, overflow IS possible, can't use sprintf:
+                * mkdir test
+                * python -c 'print("get /test?" + ("x" * 8192))' | busybox httpd -i -h .
+                */
+               len += snprintf(iobuf + len, IOBUF_SIZE-3 - len,
+                               "Location: %s/%s%s\r\n",
                                found_moved_temporarily,
                                (g_query ? "?" : ""),
                                (g_query ? g_query : ""));
+               if (len > IOBUF_SIZE-3)
+                       len = IOBUF_SIZE-3;
        }
 
 #if ENABLE_FEATURE_HTTPD_ERROR_PAGES
        if (error_page && access(error_page, R_OK) == 0) {
-               strcat(iobuf, "\r\n");
-               len += 2;
-
-               if (DEBUG)
+               iobuf[len++] = '\r';
+               iobuf[len++] = '\n';
+               if (DEBUG) {
+                       iobuf[len] = '\0';
                        fprintf(stderr, "headers: '%s'\n", iobuf);
+               }
                full_write(STDOUT_FILENO, iobuf, len);
                if (DEBUG)
                        fprintf(stderr, "writing error page: '%s'\n", error_page);
@@ -1021,8 +1035,10 @@ static void send_headers(int responseNum)
                                responseNum, responseString,
                                responseNum, responseString, infoString);
        }
-       if (DEBUG)
+       if (DEBUG) {
+               iobuf[len] = '\0';
                fprintf(stderr, "headers: '%s'\n", iobuf);
+       }
        if (full_write(STDOUT_FILENO, iobuf, len) != len) {
                if (verbose > 1)
                        bb_perror_msg("error");
@@ -1053,7 +1069,7 @@ static int get_line(void)
        alarm(HEADER_READ_TIMEOUT);
        while (1) {
                if (hdr_cnt <= 0) {
-                       hdr_cnt = safe_read(STDIN_FILENO, hdr_buf, sizeof(hdr_buf));
+                       hdr_cnt = safe_read(STDIN_FILENO, hdr_buf, sizeof_hdr_buf);
                        if (hdr_cnt <= 0)
                                break;
                        hdr_ptr = hdr_buf;
@@ -1104,18 +1120,31 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
 
        /* NB: breaking out of this loop jumps to log_and_exit() */
        out_cnt = 0;
+       pfd[FROM_CGI].fd = fromCgi_rd;
+       pfd[FROM_CGI].events = POLLIN;
+       pfd[TO_CGI].fd = toCgi_wr;
        while (1) {
-               memset(pfd, 0, sizeof(pfd));
-
-               pfd[FROM_CGI].fd = fromCgi_rd;
-               pfd[FROM_CGI].events = POLLIN;
-
-               if (toCgi_wr) {
-                       pfd[TO_CGI].fd = toCgi_wr;
-                       if (hdr_cnt > 0) {
-                               pfd[TO_CGI].events = POLLOUT;
-                       } else if (post_len > 0) {
-                               pfd[0].events = POLLIN;
+               /* Note: even pfd[0].events == 0 won't prevent
+                * revents == POLLHUP|POLLERR reports from closed stdin.
+                * Setting fd to -1 works: */
+               pfd[0].fd = -1;
+               pfd[0].events = POLLIN;
+               pfd[0].revents = 0; /* probably not needed, paranoia */
+
+               /* We always poll this fd, thus kernel always sets revents: */
+               /*pfd[FROM_CGI].events = POLLIN; - moved out of loop */
+               /*pfd[FROM_CGI].revents = 0; - not needed */
+
+               /* gcc-4.8.0 still doesnt fill two shorts with one insn :( */
+               /* http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47059 */
+               /* hopefully one day it will... */
+               pfd[TO_CGI].events = POLLOUT;
+               pfd[TO_CGI].revents = 0; /* needed! */
+
+               if (toCgi_wr && hdr_cnt <= 0) {
+                       if (post_len > 0) {
+                               /* Expect more POST data from network */
+                               pfd[0].fd = 0;
                        } else {
                                /* post_len <= 0 && hdr_cnt <= 0:
                                 * no more POST data to CGI,
@@ -1127,7 +1156,7 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
                }
 
                /* Now wait on the set of sockets */
-               count = safe_poll(pfd, toCgi_wr ? TO_CGI+1 : FROM_CGI+1, -1);
+               count = safe_poll(pfd, hdr_cnt > 0 ? TO_CGI+1 : FROM_CGI+1, -1);
                if (count <= 0) {
 #if 0
                        if (safe_waitpid(pid, &status, WNOHANG) <= 0) {
@@ -1144,7 +1173,7 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
                }
 
                if (pfd[TO_CGI].revents) {
-                       /* hdr_cnt > 0 here due to the way pfd[TO_CGI].events set */
+                       /* hdr_cnt > 0 here due to the way poll() called */
                        /* Have data from peer and can write to CGI */
                        count = safe_write(toCgi_wr, hdr_ptr, hdr_cnt);
                        /* Doesn't happen, we dont use nonblocking IO here
@@ -1165,9 +1194,9 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
                        /* We expect data, prev data portion is eaten by CGI
                         * and there *is* data to read from the peer
                         * (POSTDATA) */
-                       //count = post_len > (int)sizeof(hdr_buf) ? (int)sizeof(hdr_buf) : post_len;
+                       //count = post_len > (int)sizeof_hdr_buf ? (int)sizeof_hdr_buf : post_len;
                        //count = safe_read(STDIN_FILENO, hdr_buf, count);
-                       count = safe_read(STDIN_FILENO, hdr_buf, sizeof(hdr_buf));
+                       count = safe_read(STDIN_FILENO, hdr_buf, sizeof_hdr_buf);
                        if (count > 0) {
                                hdr_cnt = count;
                                hdr_ptr = hdr_buf;
@@ -1209,12 +1238,12 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
                                out_cnt += count;
                                count = 0;
                                /* "Status" header format is: "Status: 302 Redirected\r\n" */
-                               if (out_cnt >= 8 && memcmp(rbuf, "Status: ", 8) == 0) {
+                               if (out_cnt >= 7 && memcmp(rbuf, "Status:", 7) == 0) {
                                        /* send "HTTP/1.0 " */
                                        if (full_write(STDOUT_FILENO, HTTP_200, 9) != 9)
                                                break;
-                                       rbuf += 8; /* skip "Status: " */
-                                       count = out_cnt - 8;
+                                       rbuf += 7; /* skip "Status:" */
+                                       count = out_cnt - 7;
                                        out_cnt = -1; /* buffering off */
                                } else if (out_cnt >= 4) {
                                        /* Did CGI add "HTTP"? */
@@ -1611,7 +1640,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
 #endif
        if (what & SEND_HEADERS)
                send_headers(HTTP_OK);
-#if ENABLE_FEATURE_HTTPD_USE_SENDFILE
+#if ENABLE_FEATURE_USE_SENDFILE
        {
                off_t offset = range_start;
                while (1) {
@@ -1641,7 +1670,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
                        break;
        }
        if (count < 0) {
- IF_FEATURE_HTTPD_USE_SENDFILE(fin:)
+ IF_FEATURE_USE_SENDFILE(fin:)
                if (verbose > 1)
                        bb_perror_msg("error");
        }
@@ -1881,7 +1910,7 @@ static Htaccess_Proxy *find_proxy_entry(const char *url)
 {
        Htaccess_Proxy *p;
        for (p = proxy; p; p = p->next) {
-               if (strncmp(url, p->url_from, strlen(p->url_from)) == 0)
+               if (is_prefixed_with(url, p->url_from))
                        return p;
        }
        return NULL;
@@ -2170,7 +2199,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
                        if (STRNCASECMP(iobuf, "Range:") == 0) {
                                /* We know only bytes=NNN-[MMM] */
                                char *s = skip_whitespace(iobuf + sizeof("Range:")-1);
-                               if (strncmp(s, "bytes=", 6) == 0) {
+                               if (is_prefixed_with(s, "bytes=") == 0) {
                                        s += sizeof("bytes=")-1;
                                        range_start = BB_STRTOOFF(s, &s, 10);
                                        if (s[0] != '-' || range_start < 0) {
@@ -2256,7 +2285,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
        tptr = urlcopy + 1;      /* skip first '/' */
 
 #if ENABLE_FEATURE_HTTPD_CGI
-       if (strncmp(tptr, "cgi-bin/", 8) == 0) {
+       if (is_prefixed_with(tptr, "cgi-bin/")) {
                if (tptr[8] == '\0') {
                        /* protect listing "cgi-bin/" */
                        send_headers_and_exit(HTTP_FORBIDDEN);
@@ -2339,7 +2368,7 @@ static void mini_httpd(int server_socket)
                        continue;
 
                /* set the KEEPALIVE option to cull dead connections */
-               setsockopt(n, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+               setsockopt_keepalive(n);
 
                if (fork() == 0) {
                        /* child */
@@ -2382,7 +2411,7 @@ static void mini_httpd_nommu(int server_socket, int argc, char **argv)
                        continue;
 
                /* set the KEEPALIVE option to cull dead connections */
-               setsockopt(n, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+               setsockopt_keepalive(n);
 
                if (vfork() == 0) {
                        /* child */