aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2026-01-23 10:14:04 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2026-01-23 10:14:04 +0100
commita5f120b6205c4e22792c818350b33bf57644ed1c (patch)
treeed04cab0817886ddb89afa8aebd7a3a3282d0341
parent01ea35e81d7f3dcc9d5032ac53b794a5e9d7cedd (diff)
downloadbusybox-w32-a5f120b6205c4e22792c818350b33bf57644ed1c.tar.gz
busybox-w32-a5f120b6205c4e22792c818350b33bf57644ed1c.tar.bz2
busybox-w32-a5f120b6205c4e22792c818350b33bf57644ed1c.zip
httpd: simplify CGI headers handling, check "HTTP/1.1" prefix, not just "HTTP"
function old new delta cgi_io_loop_and_exit 477 498 +21 .rodata 106830 106821 -9 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/1 up/down: 21/-9) Total: 12 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--include/platform.h2
-rw-r--r--networking/httpd.c58
2 files changed, 32 insertions, 28 deletions
diff --git a/include/platform.h b/include/platform.h
index a5b61757f..c449075b9 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -211,6 +211,7 @@
211# define IF_LITTLE_ENDIAN(...) 211# define IF_LITTLE_ENDIAN(...)
212/* How do bytes a,b,c,d (sequential in memory) look if fetched into uint32_t? */ 212/* How do bytes a,b,c,d (sequential in memory) look if fetched into uint32_t? */
213# define PACK32_BYTES(a,b,c,d) (uint32_t)((d)+((c)<<8)+((b)<<16)+((a)<<24)) 213# define PACK32_BYTES(a,b,c,d) (uint32_t)((d)+((c)<<8)+((b)<<16)+((a)<<24))
214# define PACK64_LITERAL_STR(s) (((uint64_t)PACK32_BYTES((s)[0],(s)[1],(s)[2],(s)[3])<<32) + PACK32_BYTES((s)[4],(s)[5],(s)[6],(s)[7]))
214#else 215#else
215# define SWAP_BE16(x) bswap_16(x) 216# define SWAP_BE16(x) bswap_16(x)
216# define SWAP_BE32(x) bswap_32(x) 217# define SWAP_BE32(x) bswap_32(x)
@@ -221,6 +222,7 @@
221# define IF_BIG_ENDIAN(...) 222# define IF_BIG_ENDIAN(...)
222# define IF_LITTLE_ENDIAN(...) __VA_ARGS__ 223# define IF_LITTLE_ENDIAN(...) __VA_ARGS__
223# define PACK32_BYTES(a,b,c,d) (uint32_t)((a)+((b)<<8)+((c)<<16)+((d)<<24)) 224# define PACK32_BYTES(a,b,c,d) (uint32_t)((a)+((b)<<8)+((c)<<16)+((d)<<24))
225# define PACK64_LITERAL_STR(s) (((uint64_t)PACK32_BYTES((s)[4],(s)[5],(s)[6],(s)[7])<<32) + PACK32_BYTES((s)[0],(s)[1],(s)[2],(s)[3]))
224#endif 226#endif
225 227
226 228
diff --git a/networking/httpd.c b/networking/httpd.c
index 5101f86f7..e98da9c57 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -537,7 +537,7 @@ struct globals {
537#if ENABLE_FEATURE_HTTPD_PROXY 537#if ENABLE_FEATURE_HTTPD_PROXY
538 Htaccess_Proxy *proxy; 538 Htaccess_Proxy *proxy;
539#endif 539#endif
540 char iobuf[IOBUF_SIZE]; 540 char iobuf[IOBUF_SIZE] ALIGN8;
541}; 541};
542#define G (*ptr_to_globals) 542#define G (*ptr_to_globals)
543#define verbose (G.verbose ) 543#define verbose (G.verbose )
@@ -1425,7 +1425,7 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
1425 /*pfd[FROM_CGI].events = POLLIN; - moved out of loop */ 1425 /*pfd[FROM_CGI].events = POLLIN; - moved out of loop */
1426 /*pfd[FROM_CGI].revents = 0; - not needed */ 1426 /*pfd[FROM_CGI].revents = 0; - not needed */
1427 1427
1428 /* gcc-4.8.0 still doesnt fill two shorts with one insn :( */ 1428 /* gcc-4.8.0 still doesn't fill two shorts with one insn :( */
1429 /* http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47059 */ 1429 /* http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47059 */
1430 /* hopefully one day it will... */ 1430 /* hopefully one day it will... */
1431 pfd[TO_CGI].events = POLLOUT; 1431 pfd[TO_CGI].events = POLLOUT;
@@ -1507,8 +1507,6 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
1507 1507
1508 if (pfd[FROM_CGI].revents) { 1508 if (pfd[FROM_CGI].revents) {
1509 /* There is something to read from CGI */ 1509 /* There is something to read from CGI */
1510 char *rbuf = iobuf;
1511
1512 /* Still buffering CGI output? */ 1510 /* Still buffering CGI output? */
1513 if (out_cnt >= 0) { 1511 if (out_cnt >= 0) {
1514 /* HTTP_200[] has single "\r\n" at the end. 1512 /* HTTP_200[] has single "\r\n" at the end.
@@ -1523,34 +1521,34 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
1523 * CGI may output a few first bytes and then wait 1521 * CGI may output a few first bytes and then wait
1524 * for POSTDATA without closing stdout. 1522 * for POSTDATA without closing stdout.
1525 * With full_read we may wait here forever. */ 1523 * With full_read we may wait here forever. */
1526 count = safe_read(fromCgi_rd, rbuf + out_cnt, IOBUF_SIZE); 1524 count = safe_read(fromCgi_rd, iobuf + out_cnt, IOBUF_SIZE);
1527 if (count <= 0) { 1525 if (count <= 0) {
1528 /* eof (or error) and there was no "HTTP" (tiny 0..3 bytes output), 1526 /* EOF (or error, and out_cnt=0..7
1529 * send "HTTP/1.1 200 OK\r\n", then send received 0..3 bytes */ 1527 * send "HTTP/1.1 200 OK\r\n", then send received 0..7 bytes */
1530 goto write_HTTP_200_OK; 1528 goto write_HTTP_200_OK;
1531 /* we'll read once more later, in "no longer buffering" 1529 /* we'll read once more later, in "no longer buffering"
1532 * code path, get another EOF there and exit */ 1530 * code path, get another EOF there and exit */
1533 } 1531 }
1534 out_cnt += count; 1532 out_cnt += count;
1535 count = 0; 1533 count = 0;
1536 /* "Status" header format is: "Status: 302 Redirected\r\n" */ 1534 if (out_cnt >= 8) {
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! 1535//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". 1536//FIXME: many servers also check "Location: ". If it exists but "Status: " does _not_, "302 Found" is assumed instead of "200 OK".
1540 /* send "HTTP/1.1 " */ 1537 /* "Status" header format is: "Status: 302 Redirected\r\n" */
1541 if (full_write(STDOUT_FILENO, HTTP_200, 9) != 9) 1538 //if (memcmp(iobuf, "Status: ", 8) == 0)
1542 break; 1539 if (*(uint64_t*)iobuf == PACK64_LITERAL_STR("Status: ")) {
1543 /* skip "Status: " (including space, sending "HTTP/1.1 NNN" is wrong) */ 1540 /* send "HTTP/1.1 " */
1544 rbuf += 8; 1541 if (full_write(STDOUT_FILENO, HTTP_200, 9) != 9)
1545 count = out_cnt - 8; 1542 break;
1546 out_cnt = -1; /* buffering off */ 1543 /* skip "Status: " (including space, sending "HTTP/1.1 NNN" is wrong) */
1547 } else if (out_cnt >= 4) { 1544 out_cnt -= 8;
1548//NB: Apache has no such autodetection. It always adds its own HTTP/ header, 1545 memmove(iobuf, iobuf + 8, out_cnt);
1546 }
1547//NB: Apache has no such autodetection. It always adds its own HTTP/1.x header,
1549//unless the CGI name starts with "nph-", in which case it passes its output verbatim to network. 1548//unless the CGI name starts with "nph-", in which case it passes its output verbatim to network.
1550 /* Did CGI add "HTTP"? */ 1549 else if (memcmp(iobuf, HTTP_200, 8) != 0) { /* Did CGI send "HTTP/1.1"? */
1551 if (memcmp(rbuf, HTTP_200, 4) != 0) {
1552 write_HTTP_200_OK: 1550 write_HTTP_200_OK:
1553 /* there is no "HTTP", send "HTTP/1.1 200 OK\r\n" ourself */ 1551 /* no, send "HTTP/1.1 200 OK\r\n" ourself */
1554 if (full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1) 1552 if (full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1)
1555 break; 1553 break;
1556 } 1554 }
@@ -1562,17 +1560,16 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
1562 */ 1560 */
1563 count = out_cnt; 1561 count = out_cnt;
1564 out_cnt = -1; /* buffering off */ 1562 out_cnt = -1; /* buffering off */
1565 /* NB: we mishandle CGI writing "Stat","us..." in two separate writes */
1566 } 1563 }
1567 } else { 1564 } else {
1568 count = safe_read(fromCgi_rd, rbuf, IOBUF_SIZE); 1565 count = safe_read(fromCgi_rd, iobuf, IOBUF_SIZE);
1569 if (count <= 0) 1566 if (count <= 0)
1570 send_EOF_and_exit(); /* eof (or error) */ 1567 send_EOF_and_exit(); /* EOF (or error) */
1571 } 1568 }
1572//FIXME: many (most?) servers translate bare "\n" to "\r\n", often only in the headers, not body (the part after empty line) 1569//FIXME: many (most?) servers translate bare "\n" to "\r\n", only in the headers, not body (the part after empty line)
1573 if (full_write(STDOUT_FILENO, rbuf, count) != count) 1570 if (full_write(STDOUT_FILENO, iobuf, count) != count)
1574 break; 1571 break;
1575 dbg("cgi read %d bytes: '%.*s'\n", count, count, rbuf); 1572 dbg("cgi read %d bytes: '%.*s'\n", count, count, iobuf);
1576 } /* if (pfd[FROM_CGI].revents) */ 1573 } /* if (pfd[FROM_CGI].revents) */
1577 } /* while (1) */ 1574 } /* while (1) */
1578 log_and_exit(); 1575 log_and_exit();
@@ -2948,7 +2945,12 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
2948 IF_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;) 2945 IF_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;)
2949 IF_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;) 2946 IF_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;)
2950 IF_FEATURE_HTTPD_AUTH_MD5(const char *pass;) 2947 IF_FEATURE_HTTPD_AUTH_MD5(const char *pass;)
2951 2948#if 0 // PACK64_LITERAL_STR test
2949 char testing[16] = "Status: ";
2950 printf("0x%08llx\n", PACK64_LITERAL_STR("Status: "));
2951 printf("0x%08llx\n", *(uint64_t*)testing);
2952 exit(1);
2953#endif
2952 INIT_G(); 2954 INIT_G();
2953 2955
2954#if ENABLE_LOCALE_SUPPORT 2956#if ENABLE_LOCALE_SUPPORT