aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2026-01-28 17:45:37 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2026-01-28 17:45:37 +0100
commitdf1ef312a081a7aa9e16ef21020ee895aa5ab893 (patch)
treef726ba9ed4d03b99596023bf9319d80213634556
parent03d6d1014aad3389a67b3050aa1e5ca049d241c6 (diff)
downloadbusybox-w32-df1ef312a081a7aa9e16ef21020ee895aa5ab893.tar.gz
busybox-w32-df1ef312a081a7aa9e16ef21020ee895aa5ab893.tar.bz2
busybox-w32-df1ef312a081a7aa9e16ef21020ee895aa5ab893.zip
httpd: handle bare "Location: URL" redirects from CGIs
Many sites on the Web give those as valid CGI examples - no "Status:", just "Location:". function old new delta cgi_io_loop_and_exit 620 684 +64 log_cgi_status - 49 +49 .rodata 106846 106861 +15 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 2/0 up/down: 128/0) Total: 128 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--networking/httpd.c43
1 files changed, 29 insertions, 14 deletions
diff --git a/networking/httpd.c b/networking/httpd.c
index cdcae01fb..3739fd55f 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -1409,6 +1409,13 @@ static unsigned get_line(void)
1409} 1409}
1410 1410
1411#if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY 1411#if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY
1412static void log_cgi_status(const char *pfx, const char *st, unsigned len)
1413{
1414 unsigned char *end = (void*)st;
1415 while (*end >= ' ' && *end < 0x7f && len > 0)
1416 end++, len--;
1417 bb_error_msg("cgi %s:'%.*s'", pfx, (int)((char *)end - st), st);
1418}
1412 1419
1413/* gcc 4.2.1 fares better with NOINLINE */ 1420/* gcc 4.2.1 fares better with NOINLINE */
1414static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr) NORETURN; 1421static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr) NORETURN;
@@ -1416,7 +1423,7 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr)
1416{ 1423{
1417 enum { FROM_CGI = 1, TO_CGI = 2 }; /* indexes in pfd[] */ 1424 enum { FROM_CGI = 1, TO_CGI = 2 }; /* indexes in pfd[] */
1418 struct pollfd pfd[3]; 1425 struct pollfd pfd[3];
1419 int out_cnt; /* we buffer a bit of initial CGI output */ 1426 int out_cnt;
1420 int count; 1427 int count;
1421 1428
1422 /* iobuf is used for CGI -> network data, 1429 /* iobuf is used for CGI -> network data,
@@ -1435,8 +1442,15 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr)
1435 G.POST_len -= hdr_cnt; 1442 G.POST_len -= hdr_cnt;
1436 //bb_error_msg("G.POST_len:%d", G.POST_len); 1443 //bb_error_msg("G.POST_len:%d", G.POST_len);
1437 1444
1438 /* NB: breaking out of this loop jumps to log_and_exit() */ 1445 /* If it's really CGI, we buffer a bit of initial CGI output and handle "Status:" etc */
1446 /* If it's proxying, then no buffering/conversion is needed (out_cnt set to -1)*/
1447#if ENABLE_FEATURE_HTTPD_PROXY
1448 out_cnt = - (fromCgi_rd == toCgi_wr);
1449#else
1439 out_cnt = 0; 1450 out_cnt = 0;
1451#endif
1452
1453 /* NB: breaking out of this loop jumps to log_and_exit() */
1440 pfd[FROM_CGI].fd = fromCgi_rd; 1454 pfd[FROM_CGI].fd = fromCgi_rd;
1441 pfd[FROM_CGI].events = POLLIN; 1455 pfd[FROM_CGI].events = POLLIN;
1442 pfd[TO_CGI].fd = toCgi_wr; 1456 pfd[TO_CGI].fd = toCgi_wr;
@@ -1552,8 +1566,8 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr)
1552 * CGI may output a few first bytes and then wait 1566 * CGI may output a few first bytes and then wait
1553 * for POSTDATA without closing stdout. 1567 * for POSTDATA without closing stdout.
1554 * With full_read we may wait here forever. */ 1568 * With full_read we may wait here forever. */
1555 count = safe_read(fromCgi_rd, iobuf + out_cnt, IOBUF_SIZE - 8); 1569 count = safe_read(fromCgi_rd, iobuf + out_cnt, IOBUF_SIZE - 16);
1556// "- 8" is important, out_cnt can be up to 7 1570// "- 16" is important, out_cnt can be up to 9
1557 if (count <= 0) { 1571 if (count <= 0) {
1558 /* EOF (or error, and out_cnt=0..7 1572 /* EOF (or error, and out_cnt=0..7
1559 * send "HTTP/1.1 200 OK\r\n", then send received 0..7 bytes */ 1573 * send "HTTP/1.1 200 OK\r\n", then send received 0..7 bytes */
@@ -1563,9 +1577,8 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr)
1563 } 1577 }
1564 out_cnt += count; 1578 out_cnt += count;
1565 count = 0; 1579 count = 0;
1566 if (out_cnt >= 8) { 1580 if (out_cnt >= 10) {
1567//FIXME: "Status: " is not required to be the first header! It can be anywhere! 1581//FIXME: "Status: " is not required to be the first header! It can be anywhere!
1568//FIXME: many servers also check "Location: ". If it exists but "Status: " does _not_, "302 Found" is assumed instead of "200 OK".
1569 uint64_t str8 = *(uint64_t*)iobuf; 1582 uint64_t str8 = *(uint64_t*)iobuf;
1570 //bb_error_msg("from cgi:'%.*s'", out_cnt, iobuf); 1583 //bb_error_msg("from cgi:'%.*s'", out_cnt, iobuf);
1571 1584
@@ -1577,18 +1590,20 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr)
1577 memmove(iobuf + 1, iobuf, out_cnt); 1590 memmove(iobuf + 1, iobuf, out_cnt);
1578 out_cnt += 1; 1591 out_cnt += 1;
1579 memcpy(iobuf, HTTP_200, 9); 1592 memcpy(iobuf, HTTP_200, 9);
1580 if (verbose) { 1593 if (verbose)
1581 char *end = iobuf + 9; 1594 log_cgi_status("response", iobuf + 9, out_cnt - 9);
1582 int cnt = out_cnt - 9; 1595 }
1583 while ((unsigned char)*end >= ' ' && (unsigned char)*end < 0x7f && cnt > 0) 1596 else
1584 end++, cnt--; 1597 if (str8 == PACK64_LITERAL_STR("Location") && iobuf[8] == ':' && iobuf[9] == ' ') {
1585 bb_error_msg("cgi response:'%.*s'", (int)(end - (iobuf + 9)), iobuf + 9); 1598#define HTTP_302 "HTTP/1.1 302 Found\r\n"
1586 } 1599 if (full_write(STDOUT_FILENO, HTTP_302, sizeof(HTTP_302)-1) != sizeof(HTTP_302)-1)
1600 break;
1601 if (verbose)
1602 log_cgi_status("redirect", iobuf + 10, out_cnt - 10);
1587 } 1603 }
1588//NB: Apache has no such autodetection. It always adds its own HTTP/1.x header, 1604//NB: Apache has no such autodetection. It always adds its own HTTP/1.x header,
1589//unless the CGI name starts with "nph-", in which case it passes its output verbatim to network. 1605//unless the CGI name starts with "nph-", in which case it passes its output verbatim to network.
1590 else /* Did CGI send "HTTP/1.1"? */ 1606 else /* Did CGI send "HTTP/1.1"? */
1591 //if (memcmp(iobuf, HTTP_200, 8) != 0)
1592 if (str8 != PACK64_LITERAL_STR(HTTP_200)) { 1607 if (str8 != PACK64_LITERAL_STR(HTTP_200)) {
1593 write_HTTP_200_OK: 1608 write_HTTP_200_OK:
1594 /* no, send "HTTP/1.1 200 OK\r\n" ourself */ 1609 /* no, send "HTTP/1.1 200 OK\r\n" ourself */