← Commit list

df1ef312a

Denys Vlasenko
Denys Vlasenko
2026-01-28
SUCCESS Duration: 142s

Subject

httpd: handle bare "Location: URL" redirects from CGIs

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0
gcc -Wp,-MD,applets/.usage.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer      -Iinclude -Iinclude -o applets/usage applets/usage.c

Build Log

(log yok)

Commit Diff GitHub →

networking/httpd.c+29 -14
@@ -1409,6 +1409,13 @@ static unsigned get_line(void)
}
#if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY
+static void log_cgi_status(const char *pfx, const char *st, unsigned len)
+{
+ unsigned char *end = (void*)st;
+ while (*end >= ' ' && *end < 0x7f && len > 0)
+ end++, len--;
+ bb_error_msg("cgi %s:'%.*s'", pfx, (int)((char *)end - st), st);
+}
/* gcc 4.2.1 fares better with NOINLINE */
static 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)
{
enum { FROM_CGI = 1, TO_CGI = 2 }; /* indexes in pfd[] */
struct pollfd pfd[3];
- int out_cnt; /* we buffer a bit of initial CGI output */
+ int out_cnt;
int count;
/* 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)
G.POST_len -= hdr_cnt;
//bb_error_msg("G.POST_len:%d", G.POST_len);
- /* NB: breaking out of this loop jumps to log_and_exit() */
+ /* If it's really CGI, we buffer a bit of initial CGI output and handle "Status:" etc */
+ /* If it's proxying, then no buffering/conversion is needed (out_cnt set to -1)*/
+#if ENABLE_FEATURE_HTTPD_PROXY
+ out_cnt = - (fromCgi_rd == toCgi_wr);
+#else
out_cnt = 0;
+#endif
+
+ /* NB: breaking out of this loop jumps to log_and_exit() */
pfd[FROM_CGI].fd = fromCgi_rd;
pfd[FROM_CGI].events = POLLIN;
pfd[TO_CGI].fd = toCgi_wr;
@@ -1552,8 +1566,8 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr)
* CGI may output a few first bytes and then wait
* for POSTDATA without closing stdout.
* With full_read we may wait here forever. */
- count = safe_read(fromCgi_rd, iobuf + out_cnt, IOBUF_SIZE - 8);
-// "- 8" is important, out_cnt can be up to 7
+ count = safe_read(fromCgi_rd, iobuf + out_cnt, IOBUF_SIZE - 16);
+// "- 16" is important, out_cnt can be up to 9
if (count <= 0) {
/* EOF (or error, and out_cnt=0..7
* 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)
}
out_cnt += count;
count = 0;
- if (out_cnt >= 8) {
+ if (out_cnt >= 10) {
//FIXME: "Status: " is not required to be the first header! It can be anywhere!
-//FIXME: many servers also check "Location: ". If it exists but "Status: " does _not_, "302 Found" is assumed instead of "200 OK".
uint64_t str8 = *(uint64_t*)iobuf;
//bb_error_msg("from cgi:'%.*s'", out_cnt, iobuf);
@@ -1577,18 +1590,20 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr)
memmove(iobuf + 1, iobuf, out_cnt);
out_cnt += 1;
memcpy(iobuf, HTTP_200, 9);
- if (verbose) {
- char *end = iobuf + 9;
- int cnt = out_cnt - 9;
- while ((unsigned char)*end >= ' ' && (unsigned char)*end < 0x7f && cnt > 0)
- end++, cnt--;
- bb_error_msg("cgi response:'%.*s'", (int)(end - (iobuf + 9)), iobuf + 9);
- }
+ if (verbose)
+ log_cgi_status("response", iobuf + 9, out_cnt - 9);
+ }
+ else
+ if (str8 == PACK64_LITERAL_STR("Location") && iobuf[8] == ':' && iobuf[9] == ' ') {
+#define HTTP_302 "HTTP/1.1 302 Found\r\n"
+ if (full_write(STDOUT_FILENO, HTTP_302, sizeof(HTTP_302)-1) != sizeof(HTTP_302)-1)
+ break;
+ if (verbose)
+ log_cgi_status("redirect", iobuf + 10, out_cnt - 10);
}
//NB: Apache has no such autodetection. It always adds its own HTTP/1.x header,
//unless the CGI name starts with "nph-", in which case it passes its output verbatim to network.
else /* Did CGI send "HTTP/1.1"? */
- //if (memcmp(iobuf, HTTP_200, 8) != 0)
if (str8 != PACK64_LITERAL_STR(HTTP_200)) {
write_HTTP_200_OK:
/* no, send "HTTP/1.1 200 OK\r\n" ourself */