aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--networking/httpd.c62
-rw-r--r--networking/httpd_ratelimit_cgi.c4
2 files changed, 37 insertions, 29 deletions
diff --git a/networking/httpd.c b/networking/httpd.c
index ed6b0682e..5101f86f7 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -309,6 +309,12 @@
309 * 309 *
310 * TODO: 310 * TODO:
311 * - do not allow all MAXCONN connections be taken up by the same remote IP. 311 * - do not allow all MAXCONN connections be taken up by the same remote IP.
312 * - CGI I/O loop lacks full timeout protection (may be abused by slow POST client?),
313 * see cgi_io_loop_and_exit()
314 * - sanity: add a limit how big POSTDATA can be? (-P 1024: "megabyte+ of POSTDATA is insanity")
315 * currently we only do: if (POST_length > INT_MAX) HTTP_BAD_REQUEST
316 * - sanity: kill CGIs which are obviously stuck? (-K 60: "if CGI isn't done in 1 minute, it's fishy. SIGTERM+SIGKILL")
317 * - sanity: measure CGI memory consumption (how?), kill when way too big?
312 */ 318 */
313#define HEADER_READ_TIMEOUT 30 319#define HEADER_READ_TIMEOUT 30
314#define DATA_WRITE_TIMEOUT 60 320#define DATA_WRITE_TIMEOUT 60
@@ -1091,7 +1097,7 @@ static void log_and_exit(void)
1091{ 1097{
1092 if (VERBOSE_3) 1098 if (VERBOSE_3)
1093 bb_simple_error_msg("closed"); 1099 bb_simple_error_msg("closed");
1094 _exit(xfunc_error_retval); 1100 _exit_SUCCESS();
1095} 1101}
1096static void send_EOF_and_exit(void) NORETURN; 1102static void send_EOF_and_exit(void) NORETURN;
1097static void send_EOF_and_exit(void) 1103static void send_EOF_and_exit(void)
@@ -1440,7 +1446,12 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
1440 } 1446 }
1441 1447
1442 /* Now wait on the set of sockets */ 1448 /* Now wait on the set of sockets */
1449 /* Poll whether TO_CGI is ready to accept writes *only* if we have some POSTDATA to give to it */
1443 count = safe_poll(pfd, hdr_cnt > 0 ? TO_CGI+1 : FROM_CGI+1, -1); 1450 count = safe_poll(pfd, hdr_cnt > 0 ? TO_CGI+1 : FROM_CGI+1, -1);
1451//FIXME:do not allow the client to stall POSTDATA:
1452//the timeout must not be infinite, should be similar to DATA_WRITE_TIMEOUT
1453//("give some data at least once a minute", better "give >=N bytes once minute")
1454
1444 if (count <= 0) { 1455 if (count <= 0) {
1445#if 0 /* This doesn't work since we SIG_IGNed SIGCHLD (which means kernel auto-reaps our children) */ 1456#if 0 /* This doesn't work since we SIG_IGNed SIGCHLD (which means kernel auto-reaps our children) */
1446 if (VERBOSE_3) { 1457 if (VERBOSE_3) {
@@ -1479,15 +1490,15 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
1479 if (pfd[0].revents) { 1490 if (pfd[0].revents) {
1480 /* post_len > 0 && hdr_cnt == 0 here */ 1491 /* post_len > 0 && hdr_cnt == 0 here */
1481 /* We expect data, prev data portion is eaten by CGI 1492 /* We expect data, prev data portion is eaten by CGI
1482 * and there *is* data to read from the peer 1493 * and there *is* POSTDATA to read from the peer
1483 * (POSTDATA) */ 1494 */
1484 //count = post_len > (int)sizeof_hdr_buf ? (int)sizeof_hdr_buf : post_len; 1495 //count = post_len > (int)sizeof_hdr_buf ? (int)sizeof_hdr_buf : post_len;
1485 //count = safe_read(STDIN_FILENO, hdr_buf, count); 1496 //count = safe_read(STDIN_FILENO, hdr_buf, count);
1486 count = safe_read(STDIN_FILENO, hdr_buf, sizeof_hdr_buf); 1497 count = safe_read(STDIN_FILENO, hdr_buf, sizeof_hdr_buf);
1487 if (count > 0) { 1498 if (count > 0) {
1488 hdr_cnt = count; 1499 hdr_cnt = count;
1489 hdr_ptr = hdr_buf; 1500 hdr_ptr = hdr_buf;
1490 post_len -= count; 1501 post_len -= count; /* can go negative (peer wrote more than expected POSTDATA) */
1491 } else { 1502 } else {
1492 /* no more POST data can be read */ 1503 /* no more POST data can be read */
1493 post_len = 0; 1504 post_len = 0;
@@ -1498,34 +1509,34 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
1498 /* There is something to read from CGI */ 1509 /* There is something to read from CGI */
1499 char *rbuf = iobuf; 1510 char *rbuf = iobuf;
1500 1511
1501 /* Are we still buffering CGI output? */ 1512 /* Still buffering CGI output? */
1502 if (out_cnt >= 0) { 1513 if (out_cnt >= 0) {
1503 /* HTTP_200[] has single "\r\n" at the end. 1514 /* HTTP_200[] has single "\r\n" at the end.
1504 * According to http://hoohoo.ncsa.uiuc.edu/cgi/out.html, 1515 * According to http://hoohoo.ncsa.uiuc.edu/cgi/out.html,
1505 * CGI scripts MUST send their own header terminated by 1516 * CGI scripts MUST send their own header terminated by
1506 * empty line, then data. That's why we have only one 1517 * empty line, then data. That's why we have only one
1507 * <cr><lf> pair here. We will output "200 OK" line 1518 * <cr><lf> pair here. We will output "200 OK" line
1508 * if needed, but CGI still has to provide blank line 1519 * if needed, but CGI still has to provide the blank line
1509 * between header and body */ 1520 * between header and body */
1510 1521
1511 /* Must use safe_read, not full_read, because 1522 /* Must use safe_read, not full_read, because
1512 * CGI may output a few first bytes and then wait 1523 * CGI may output a few first bytes and then wait
1513 * for POSTDATA without closing stdout. 1524 * for POSTDATA without closing stdout.
1514 * With full_read we may wait here forever. */ 1525 * With full_read we may wait here forever. */
1515 count = safe_read(fromCgi_rd, rbuf + out_cnt, IOBUF_SIZE - 8); 1526 count = safe_read(fromCgi_rd, rbuf + out_cnt, IOBUF_SIZE);
1516 if (count <= 0) { 1527 if (count <= 0) {
1517 /* eof (or error) and there was no "HTTP", 1528 /* eof (or error) and there was no "HTTP" (tiny 0..3 bytes output),
1518 * send "HTTP/1.1 200 OK\r\n", then send received data */ 1529 * send "HTTP/1.1 200 OK\r\n", then send received 0..3 bytes */
1519 if (out_cnt) { 1530 goto write_HTTP_200_OK;
1520 full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1); 1531 /* we'll read once more later, in "no longer buffering"
1521 full_write(STDOUT_FILENO, rbuf, out_cnt); 1532 * code path, get another EOF there and exit */
1522 }
1523 send_EOF_and_exit(); /* CGI stdout is closed, exiting */
1524 } 1533 }
1525 out_cnt += count; 1534 out_cnt += count;
1526 count = 0; 1535 count = 0;
1527 /* "Status" header format is: "Status: 302 Redirected\r\n" */ 1536 /* "Status" header format is: "Status: 302 Redirected\r\n" */
1528 if (out_cnt >= 8 && memcmp(rbuf, "Status: ", 8) == 0) { 1537 if (out_cnt >= 8 && memcmp(rbuf, "Status: ", 8) == 0) {
1538//FIXME: "Status: " is not required to be the first header! It can be anywhere!
1539//FIXME: many servers also check "Location: ". If it exists but "Status: " does _not_, "302 Found" is assumed instead of "200 OK".
1529 /* send "HTTP/1.1 " */ 1540 /* send "HTTP/1.1 " */
1530 if (full_write(STDOUT_FILENO, HTTP_200, 9) != 9) 1541 if (full_write(STDOUT_FILENO, HTTP_200, 9) != 9)
1531 break; 1542 break;
@@ -1534,29 +1545,31 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
1534 count = out_cnt - 8; 1545 count = out_cnt - 8;
1535 out_cnt = -1; /* buffering off */ 1546 out_cnt = -1; /* buffering off */
1536 } else if (out_cnt >= 4) { 1547 } else if (out_cnt >= 4) {
1548//NB: Apache has no such autodetection. It always adds its own HTTP/ header,
1549//unless the CGI name starts with "nph-", in which case it passes its output verbatim to network.
1537 /* Did CGI add "HTTP"? */ 1550 /* Did CGI add "HTTP"? */
1538 if (memcmp(rbuf, HTTP_200, 4) != 0) { 1551 if (memcmp(rbuf, HTTP_200, 4) != 0) {
1539 /* there is no "HTTP", do it ourself */ 1552 write_HTTP_200_OK:
1553 /* there is no "HTTP", send "HTTP/1.1 200 OK\r\n" ourself */
1540 if (full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1) 1554 if (full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1)
1541 break; 1555 break;
1542 } 1556 }
1543 /* Commented out: 1557 /* Used to add "Content-type: text/plain\r\n\r\n" here if CGI didn't,
1544 if (!strstr(rbuf, "ontent-")) { 1558 * but it's wrong. Counter-example of valid CGI without Content-type:
1545 full_write(s, "Content-type: text/plain\r\n\r\n", 28);
1546 }
1547 * Counter-example of valid CGI without Content-type:
1548 * echo -en "HTTP/1.1 302 Found\r\n" 1559 * echo -en "HTTP/1.1 302 Found\r\n"
1549 * echo -en "Location: http://www.busybox.net\r\n" 1560 * echo -en "Location: http://www.busybox.net\r\n"
1550 * echo -en "\r\n" 1561 * echo -en "\r\n"
1551 */ 1562 */
1552 count = out_cnt; 1563 count = out_cnt;
1553 out_cnt = -1; /* buffering off */ 1564 out_cnt = -1; /* buffering off */
1565 /* NB: we mishandle CGI writing "Stat","us..." in two separate writes */
1554 } 1566 }
1555 } else { 1567 } else {
1556 count = safe_read(fromCgi_rd, rbuf, IOBUF_SIZE); 1568 count = safe_read(fromCgi_rd, rbuf, IOBUF_SIZE);
1557 if (count <= 0) 1569 if (count <= 0)
1558 send_EOF_and_exit(); /* eof (or error) */ 1570 send_EOF_and_exit(); /* eof (or error) */
1559 } 1571 }
1572//FIXME: many (most?) servers translate bare "\n" to "\r\n", often only in the headers, not body (the part after empty line)
1560 if (full_write(STDOUT_FILENO, rbuf, count) != count) 1573 if (full_write(STDOUT_FILENO, rbuf, count) != count)
1561 break; 1574 break;
1562 dbg("cgi read %d bytes: '%.*s'\n", count, count, rbuf); 1575 dbg("cgi read %d bytes: '%.*s'\n", count, count, rbuf);
@@ -1697,8 +1710,6 @@ static void send_cgi_and_exit(
1697 /* Child process */ 1710 /* Child process */
1698 char *argv[3]; 1711 char *argv[3];
1699 1712
1700 xfunc_error_retval = 242;
1701
1702 /* NB: close _first_, then move fds! */ 1713 /* NB: close _first_, then move fds! */
1703 close(toCgi.wr); 1714 close(toCgi.wr);
1704 close(fromCgi.rd); 1715 close(fromCgi.rd);
@@ -1768,9 +1779,6 @@ static void send_cgi_and_exit(
1768 1779
1769 /* Parent process */ 1780 /* Parent process */
1770 1781
1771 /* Restore variables possibly changed by child */
1772 xfunc_error_retval = 0;
1773
1774 /* Pump data */ 1782 /* Pump data */
1775 close(fromCgi.wr); 1783 close(fromCgi.wr);
1776 close(toCgi.rd); 1784 close(toCgi.rd);
@@ -2239,7 +2247,7 @@ static void send_REQUEST_TIMEOUT_and_exit(int sig UNUSED_PARAM)
2239// /* writing timed out: exit without writing anything */ 2247// /* writing timed out: exit without writing anything */
2240// if (VERBOSE_3) 2248// if (VERBOSE_3)
2241// bb_simple_error_msg("write timeout"); 2249// bb_simple_error_msg("write timeout");
2242// _exit(xfunc_error_retval); 2250// _exit_SUCCESS();
2243} 2251}
2244 2252
2245static void prepare_write_timeout(void) 2253static void prepare_write_timeout(void)
@@ -2334,7 +2342,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2334 * just close the socket. 2342 * just close the socket.
2335 */ 2343 */
2336 //send_headers_and_exit(HTTP_BAD_REQUEST); 2344 //send_headers_and_exit(HTTP_BAD_REQUEST);
2337 _exit(xfunc_error_retval); 2345 _exit_SUCCESS();
2338 } 2346 }
2339 dbg("Request:'%s'\n", iobuf); 2347 dbg("Request:'%s'\n", iobuf);
2340 2348
diff --git a/networking/httpd_ratelimit_cgi.c b/networking/httpd_ratelimit_cgi.c
index 713274873..96702131e 100644
--- a/networking/httpd_ratelimit_cgi.c
+++ b/networking/httpd_ratelimit_cgi.c
@@ -223,7 +223,7 @@ int main(int argc, char **argv)
223 } 223 }
224 224
225 /* No slot available, return 429 */ 225 /* No slot available, return 429 */
226 write_and_die(1, "HTTP/1.1 429 Too Many Requests\r\n" 226 write_and_die(1, "Status: 429 Too Many Requests\r\n"
227 "Content-Type: text/plain\r\n" 227 "Content-Type: text/plain\r\n"
228 "Retry-After: 60\r\n" 228 "Retry-After: 60\r\n"
229 "Connection: close\r\n\r\n" 229 "Connection: close\r\n\r\n"
@@ -235,7 +235,7 @@ int main(int argc, char **argv)
235exec: 235exec:
236 execv(argv[0], argv); 236 execv(argv[0], argv);
237 full_write2(2, "can't execute", argv[0]); 237 full_write2(2, "can't execute", argv[0]);
238 write_and_die(1, "HTTP/1.1 500 Internal Server Error\r\n" 238 write_and_die(1, "Status: 500 Internal Server Error\r\n"
239 "Content-Type: text/plain\r\n\r\n" 239 "Content-Type: text/plain\r\n\r\n"
240 "Failed to execute binary\n"); 240 "Failed to execute binary\n");
241 return 1; 241 return 1;