aboutsummaryrefslogtreecommitdiff
path: root/networking/httpd.c
diff options
context:
space:
mode:
Diffstat (limited to 'networking/httpd.c')
-rw-r--r--networking/httpd.c192
1 files changed, 151 insertions, 41 deletions
diff --git a/networking/httpd.c b/networking/httpd.c
index 5d2d2681c..19cc08fcb 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -214,6 +214,44 @@
214//config: help 214//config: help
215//config: Makes httpd send files using GZIP content encoding if the 215//config: Makes httpd send files using GZIP content encoding if the
216//config: client supports it and a pre-compressed <file>.gz exists. 216//config: client supports it and a pre-compressed <file>.gz exists.
217//config:
218//config:config FEATURE_HTTPD_ETAG
219//config: bool "Support caching via ETag header"
220//config: default y
221//config: depends on HTTPD
222//config: help
223//config: If server responds with ETag then next time client (browser)
224//config: resend it via If-None-Match header.
225//config: Then httpd will check if file wasn't modified and if not,
226//config: return 304 Not Modified status code.
227//config: The ETag value is constructed from last modification date
228//config: in unix epoch, and size: "hex(last_mod)-hex(file_size)".
229//config: It's not completely reliable as hash functions but fair enough.
230//config:
231//config:config FEATURE_HTTPD_LAST_MODIFIED
232//config: bool "Add Last-Modified header to response"
233//config: default y
234//config: depends on HTTPD
235//config: help
236//config: The Last-Modified header is used for cache validation.
237//config: The client sends last seen mtime to server in If-Modified-Since.
238//config: Both headers MUST be an RFC 1123 formatted, which is hard to parse.
239//config: Use ETag header instead.
240//config:
241//config:config FEATURE_HTTPD_DATE
242//config: bool "Add Date header to response"
243//config: default y
244//config: depends on HTTPD
245//config: help
246//config: RFC2616 says that server MUST add Date header to response.
247//config: But it is almost useless and can be omitted.
248//config:
249//config:config FEATURE_HTTPD_ACL_IP
250//config: bool "ACL IP"
251//config: default y
252//config: depends on HTTPD
253//config: help
254//config: Support IP deny/allow rules
217 255
218//applet:IF_HTTPD(APPLET(httpd, BB_DIR_USR_SBIN, BB_SUID_DROP)) 256//applet:IF_HTTPD(APPLET(httpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
219 257
@@ -278,7 +316,7 @@ static void send_REQUEST_TIMEOUT_and_exit(int sig) NORETURN;
278 316
279static const char DEFAULT_PATH_HTTPD_CONF[] ALIGN1 = "/etc"; 317static const char DEFAULT_PATH_HTTPD_CONF[] ALIGN1 = "/etc";
280static const char HTTPD_CONF[] ALIGN1 = "httpd.conf"; 318static const char HTTPD_CONF[] ALIGN1 = "httpd.conf";
281static const char HTTP_200[] ALIGN1 = "HTTP/1.0 200 OK\r\n"; 319static const char HTTP_200[] ALIGN1 = "HTTP/1.1 200 OK\r\n";
282static const char index_html[] ALIGN1 = "index.html"; 320static const char index_html[] ALIGN1 = "index.html";
283 321
284typedef struct has_next_ptr { 322typedef struct has_next_ptr {
@@ -292,6 +330,7 @@ typedef struct Htaccess {
292 char before_colon[1]; /* really bigger, must be last */ 330 char before_colon[1]; /* really bigger, must be last */
293} Htaccess; 331} Htaccess;
294 332
333#if ENABLE_FEATURE_HTTPD_ACL_IP
295/* Must have "next" as a first member */ 334/* Must have "next" as a first member */
296typedef struct Htaccess_IP { 335typedef struct Htaccess_IP {
297 struct Htaccess_IP *next; 336 struct Htaccess_IP *next;
@@ -299,6 +338,7 @@ typedef struct Htaccess_IP {
299 unsigned mask; 338 unsigned mask;
300 int allow_deny; 339 int allow_deny;
301} Htaccess_IP; 340} Htaccess_IP;
341#endif
302 342
303/* Must have "next" as a first member */ 343/* Must have "next" as a first member */
304typedef struct Htaccess_Proxy { 344typedef struct Htaccess_Proxy {
@@ -319,6 +359,7 @@ enum {
319 HTTP_OK = 200, 359 HTTP_OK = 200,
320 HTTP_PARTIAL_CONTENT = 206, 360 HTTP_PARTIAL_CONTENT = 206,
321 HTTP_MOVED_TEMPORARILY = 302, 361 HTTP_MOVED_TEMPORARILY = 302,
362 HTTP_NOT_MODIFIED = 304,
322 HTTP_BAD_REQUEST = 400, /* malformed syntax */ 363 HTTP_BAD_REQUEST = 400, /* malformed syntax */
323 HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */ 364 HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
324 HTTP_NOT_FOUND = 404, 365 HTTP_NOT_FOUND = 404,
@@ -336,7 +377,6 @@ enum {
336 HTTP_NO_CONTENT = 204, 377 HTTP_NO_CONTENT = 204,
337 HTTP_MULTIPLE_CHOICES = 300, 378 HTTP_MULTIPLE_CHOICES = 300,
338 HTTP_MOVED_PERMANENTLY = 301, 379 HTTP_MOVED_PERMANENTLY = 301,
339 HTTP_NOT_MODIFIED = 304,
340 HTTP_PAYMENT_REQUIRED = 402, 380 HTTP_PAYMENT_REQUIRED = 402,
341 HTTP_BAD_GATEWAY = 502, 381 HTTP_BAD_GATEWAY = 502,
342 HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */ 382 HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */
@@ -349,6 +389,9 @@ static const uint16_t http_response_type[] ALIGN2 = {
349 HTTP_PARTIAL_CONTENT, 389 HTTP_PARTIAL_CONTENT,
350#endif 390#endif
351 HTTP_MOVED_TEMPORARILY, 391 HTTP_MOVED_TEMPORARILY,
392#if ENABLE_FEATURE_HTTPD_ETAG
393 HTTP_NOT_MODIFIED,
394#endif
352 HTTP_REQUEST_TIMEOUT, 395 HTTP_REQUEST_TIMEOUT,
353 HTTP_NOT_IMPLEMENTED, 396 HTTP_NOT_IMPLEMENTED,
354#if ENABLE_FEATURE_HTTPD_BASIC_AUTH 397#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
@@ -365,7 +408,6 @@ static const uint16_t http_response_type[] ALIGN2 = {
365 HTTP_NO_CONTENT, 408 HTTP_NO_CONTENT,
366 HTTP_MULTIPLE_CHOICES, 409 HTTP_MULTIPLE_CHOICES,
367 HTTP_MOVED_PERMANENTLY, 410 HTTP_MOVED_PERMANENTLY,
368 HTTP_NOT_MODIFIED,
369 HTTP_BAD_GATEWAY, 411 HTTP_BAD_GATEWAY,
370 HTTP_SERVICE_UNAVAILABLE, 412 HTTP_SERVICE_UNAVAILABLE,
371#endif 413#endif
@@ -380,6 +422,9 @@ static const struct {
380 { "Partial Content", NULL }, 422 { "Partial Content", NULL },
381#endif 423#endif
382 { "Found", NULL }, 424 { "Found", NULL },
425#if ENABLE_FEATURE_HTTPD_ETAG
426 { "Not Modified" },
427#endif
383 { "Request Timeout", "No request appeared within 60 seconds" }, 428 { "Request Timeout", "No request appeared within 60 seconds" },
384 { "Not Implemented", "The requested method is not recognized" }, 429 { "Not Implemented", "The requested method is not recognized" },
385#if ENABLE_FEATURE_HTTPD_BASIC_AUTH 430#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
@@ -396,7 +441,6 @@ static const struct {
396 { "No Content" }, 441 { "No Content" },
397 { "Multiple Choices" }, 442 { "Multiple Choices" },
398 { "Moved Permanently" }, 443 { "Moved Permanently" },
399 { "Not Modified" },
400 { "Bad Gateway", "" }, 444 { "Bad Gateway", "" },
401 { "Service Unavailable", "" }, 445 { "Service Unavailable", "" },
402#endif 446#endif
@@ -410,6 +454,9 @@ struct globals {
410 smallint content_gzip; 454 smallint content_gzip;
411#endif 455#endif
412 time_t last_mod; 456 time_t last_mod;
457#if ENABLE_FEATURE_HTTPD_ETAG
458 char *if_none_match;
459#endif
413 char *rmt_ip_str; /* for $REMOTE_ADDR and $REMOTE_PORT */ 460 char *rmt_ip_str; /* for $REMOTE_ADDR and $REMOTE_PORT */
414 const char *bind_addr_or_port; 461 const char *bind_addr_or_port;
415 462
@@ -420,7 +467,9 @@ struct globals {
420 467
421 const char *found_mime_type; 468 const char *found_mime_type;
422 const char *found_moved_temporarily; 469 const char *found_moved_temporarily;
470#if ENABLE_FEATURE_HTTPD_ACL_IP
423 Htaccess_IP *ip_a_d; /* config allow/deny lines */ 471 Htaccess_IP *ip_a_d; /* config allow/deny lines */
472#endif
424 473
425 IF_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;) 474 IF_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;)
426 IF_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;) 475 IF_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
@@ -444,6 +493,9 @@ struct globals {
444#define sizeof_hdr_buf COMMON_BUFSIZE 493#define sizeof_hdr_buf COMMON_BUFSIZE
445 char *hdr_ptr; 494 char *hdr_ptr;
446 int hdr_cnt; 495 int hdr_cnt;
496#if ENABLE_FEATURE_HTTPD_ETAG
497 char etag[sizeof("'%llx-%llx'") + 2 * sizeof(long long)*3];
498#endif
447#if ENABLE_FEATURE_HTTPD_ERROR_PAGES 499#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
448 const char *http_error_page[ARRAY_SIZE(http_response_type)]; 500 const char *http_error_page[ARRAY_SIZE(http_response_type)];
449#endif 501#endif
@@ -467,7 +519,6 @@ struct globals {
467#define found_mime_type (G.found_mime_type ) 519#define found_mime_type (G.found_mime_type )
468#define found_moved_temporarily (G.found_moved_temporarily) 520#define found_moved_temporarily (G.found_moved_temporarily)
469#define last_mod (G.last_mod ) 521#define last_mod (G.last_mod )
470#define ip_a_d (G.ip_a_d )
471#define g_realm (G.g_realm ) 522#define g_realm (G.g_realm )
472#define remoteuser (G.remoteuser ) 523#define remoteuser (G.remoteuser )
473#define file_size (G.file_size ) 524#define file_size (G.file_size )
@@ -528,11 +579,14 @@ static ALWAYS_INLINE void free_Htaccess_list(Htaccess **pptr)
528 free_llist((has_next_ptr**)pptr); 579 free_llist((has_next_ptr**)pptr);
529} 580}
530 581
582#if ENABLE_FEATURE_HTTPD_ACL_IP
531static ALWAYS_INLINE void free_Htaccess_IP_list(Htaccess_IP **pptr) 583static ALWAYS_INLINE void free_Htaccess_IP_list(Htaccess_IP **pptr)
532{ 584{
533 free_llist((has_next_ptr**)pptr); 585 free_llist((has_next_ptr**)pptr);
534} 586}
587#endif
535 588
589#if ENABLE_FEATURE_HTTPD_ACL_IP
536/* Returns presumed mask width in bits or < 0 on error. 590/* Returns presumed mask width in bits or < 0 on error.
537 * Updates strp, stores IP at provided pointer */ 591 * Updates strp, stores IP at provided pointer */
538static int scan_ip(const char **strp, unsigned *ipp, unsigned char endc) 592static int scan_ip(const char **strp, unsigned *ipp, unsigned char endc)
@@ -617,6 +671,7 @@ static int scan_ip_mask(const char *str, unsigned *ipp, unsigned *maskp)
617 *maskp = (uint32_t)(~mask); 671 *maskp = (uint32_t)(~mask);
618 return 0; 672 return 0;
619} 673}
674#endif
620 675
621/* 676/*
622 * Parse configuration file into in-memory linked list. 677 * Parse configuration file into in-memory linked list.
@@ -646,7 +701,9 @@ static void parse_conf(const char *path, int flag)
646 char buf[160]; 701 char buf[160];
647 702
648 /* discard old rules */ 703 /* discard old rules */
649 free_Htaccess_IP_list(&ip_a_d); 704#if ENABLE_FEATURE_HTTPD_ACL_IP
705 free_Htaccess_IP_list(&G.ip_a_d);
706#endif
650 flg_deny_all = 0; 707 flg_deny_all = 0;
651 /* retain previous auth and mime config only for subdir parse */ 708 /* retain previous auth and mime config only for subdir parse */
652 if (flag != SUBDIR_PARSE) { 709 if (flag != SUBDIR_PARSE) {
@@ -763,6 +820,7 @@ static void parse_conf(const char *path, int flag)
763 continue; 820 continue;
764 } 821 }
765 822
823#if ENABLE_FEATURE_HTTPD_ACL_IP
766 if (ch == 'A' || ch == 'D') { 824 if (ch == 'A' || ch == 'D') {
767 Htaccess_IP *pip; 825 Htaccess_IP *pip;
768 826
@@ -784,13 +842,13 @@ static void parse_conf(const char *path, int flag)
784 pip->allow_deny = ch; 842 pip->allow_deny = ch;
785 if (ch == 'D') { 843 if (ch == 'D') {
786 /* Deny:from_IP - prepend */ 844 /* Deny:from_IP - prepend */
787 pip->next = ip_a_d; 845 pip->next = G.ip_a_d;
788 ip_a_d = pip; 846 G.ip_a_d = pip;
789 } else { 847 } else {
790 /* A:from_IP - append (thus all D's precedes A's) */ 848 /* A:from_IP - append (thus all D's precedes A's) */
791 Htaccess_IP *prev_IP = ip_a_d; 849 Htaccess_IP *prev_IP = G.ip_a_d;
792 if (prev_IP == NULL) { 850 if (prev_IP == NULL) {
793 ip_a_d = pip; 851 G.ip_a_d = pip;
794 } else { 852 } else {
795 while (prev_IP->next) 853 while (prev_IP->next)
796 prev_IP = prev_IP->next; 854 prev_IP = prev_IP->next;
@@ -799,6 +857,7 @@ static void parse_conf(const char *path, int flag)
799 } 857 }
800 continue; 858 continue;
801 } 859 }
860#endif
802 861
803#if ENABLE_FEATURE_HTTPD_ERROR_PAGES 862#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
804 if (flag == FIRST_PARSE && ch == 'E') { 863 if (flag == FIRST_PARSE && ch == 'E') {
@@ -1059,11 +1118,12 @@ static void log_and_exit(void)
1059 */ 1118 */
1060static void send_headers(unsigned responseNum) 1119static void send_headers(unsigned responseNum)
1061{ 1120{
1121#if ENABLE_FEATURE_HTTPD_DATE || ENABLE_FEATURE_HTTPD_LAST_MODIFIED
1062 static const char RFC1123FMT[] ALIGN1 = "%a, %d %b %Y %H:%M:%S GMT"; 1122 static const char RFC1123FMT[] ALIGN1 = "%a, %d %b %Y %H:%M:%S GMT";
1063 /* Fixed size 29-byte string. Example: Sun, 06 Nov 1994 08:49:37 GMT */ 1123 /* Fixed size 29-byte string. Example: Sun, 06 Nov 1994 08:49:37 GMT */
1064 char date_str[40]; /* using a bit larger buffer to paranoia reasons */ 1124 char date_str[40]; /* using a bit larger buffer to paranoia reasons */
1065
1066 struct tm tm; 1125 struct tm tm;
1126#endif
1067 const char *responseString = ""; 1127 const char *responseString = "";
1068 const char *infoString = NULL; 1128 const char *infoString = NULL;
1069#if ENABLE_FEATURE_HTTPD_ERROR_PAGES 1129#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
@@ -1071,7 +1131,6 @@ static void send_headers(unsigned responseNum)
1071#endif 1131#endif
1072 unsigned len; 1132 unsigned len;
1073 unsigned i; 1133 unsigned i;
1074 time_t timer = time(NULL);
1075 1134
1076 for (i = 0; i < ARRAY_SIZE(http_response_type); i++) { 1135 for (i = 0; i < ARRAY_SIZE(http_response_type); i++) {
1077 if (http_response_type[i] == responseNum) { 1136 if (http_response_type[i] == responseNum) {
@@ -1092,15 +1151,24 @@ static void send_headers(unsigned responseNum)
1092 * always fit into those kbytes. 1151 * always fit into those kbytes.
1093 */ 1152 */
1094 1153
1095 strftime(date_str, sizeof(date_str), RFC1123FMT, gmtime_r(&timer, &tm)); 1154 {
1096 /* ^^^ using gmtime_r() instead of gmtime() to not use static data */ 1155#if ENABLE_FEATURE_HTTPD_DATE
1097 len = sprintf(iobuf, 1156 time_t timer = time(NULL);
1098 "HTTP/1.0 %u %s\r\n" 1157 strftime(date_str, sizeof(date_str), RFC1123FMT, gmtime_r(&timer, &tm));
1158 /* ^^^ using gmtime_r() instead of gmtime() to not use static data */
1159#endif
1160 len = sprintf(iobuf,
1161 "HTTP/1.1 %u %s\r\n"
1162#if ENABLE_FEATURE_HTTPD_DATE
1099 "Date: %s\r\n" 1163 "Date: %s\r\n"
1164#endif
1100 "Connection: close\r\n", 1165 "Connection: close\r\n",
1101 responseNum, responseString, 1166 responseNum, responseString
1102 date_str 1167#if ENABLE_FEATURE_HTTPD_DATE
1103 ); 1168 ,date_str
1169#endif
1170 );
1171 }
1104 1172
1105 if (responseNum != HTTP_OK || found_mime_type) { 1173 if (responseNum != HTTP_OK || found_mime_type) {
1106 len += sprintf(iobuf + len, 1174 len += sprintf(iobuf + len,
@@ -1120,7 +1188,7 @@ static void send_headers(unsigned responseNum)
1120#endif 1188#endif
1121 if (responseNum == HTTP_MOVED_TEMPORARILY) { 1189 if (responseNum == HTTP_MOVED_TEMPORARILY) {
1122 /* Responding to "GET /dir" with 1190 /* Responding to "GET /dir" with
1123 * "HTTP/1.0 302 Found" "Location: /dir/" 1191 * "HTTP/1.1 302 Found" "Location: /dir/"
1124 * - IOW, asking them to repeat with a slash. 1192 * - IOW, asking them to repeat with a slash.
1125 * Here, overflow IS possible, can't use sprintf: 1193 * Here, overflow IS possible, can't use sprintf:
1126 * mkdir test 1194 * mkdir test
@@ -1152,7 +1220,9 @@ static void send_headers(unsigned responseNum)
1152#endif 1220#endif
1153 1221
1154 if (file_size != -1) { /* file */ 1222 if (file_size != -1) { /* file */
1223#if ENABLE_FEATURE_HTTPD_LAST_MODIFIED
1155 strftime(date_str, sizeof(date_str), RFC1123FMT, gmtime_r(&last_mod, &tm)); 1224 strftime(date_str, sizeof(date_str), RFC1123FMT, gmtime_r(&last_mod, &tm));
1225#endif
1156#if ENABLE_FEATURE_HTTPD_RANGES 1226#if ENABLE_FEATURE_HTTPD_RANGES
1157 if (responseNum == HTTP_PARTIAL_CONTENT) { 1227 if (responseNum == HTTP_PARTIAL_CONTENT) {
1158 len += sprintf(iobuf + len, 1228 len += sprintf(iobuf + len,
@@ -1197,7 +1267,13 @@ static void send_headers(unsigned responseNum)
1197#if ENABLE_FEATURE_HTTPD_RANGES 1267#if ENABLE_FEATURE_HTTPD_RANGES
1198 "Accept-Ranges: bytes\r\n" 1268 "Accept-Ranges: bytes\r\n"
1199#endif 1269#endif
1270#if ENABLE_FEATURE_HTTPD_LAST_MODIFIED
1200 "Last-Modified: %s\r\n" 1271 "Last-Modified: %s\r\n"
1272#endif
1273#if ENABLE_FEATURE_HTTPD_ETAG
1274 "ETag: %s\r\n"
1275#endif
1276
1201 /* Because of 4.4 (5), we can forgo sending of "Content-Length" 1277 /* Because of 4.4 (5), we can forgo sending of "Content-Length"
1202 * since we close connection afterwards, but it helps clients 1278 * since we close connection afterwards, but it helps clients
1203 * to e.g. estimate download times, show progress bars etc. 1279 * to e.g. estimate download times, show progress bars etc.
@@ -1205,7 +1281,12 @@ static void send_headers(unsigned responseNum)
1205 * but de-facto standard is to send it (see comment below). 1281 * but de-facto standard is to send it (see comment below).
1206 */ 1282 */
1207 "Content-Length: %"OFF_FMT"u\r\n", 1283 "Content-Length: %"OFF_FMT"u\r\n",
1284#if ENABLE_FEATURE_HTTPD_LAST_MODIFIED
1208 date_str, 1285 date_str,
1286#endif
1287#if ENABLE_FEATURE_HTTPD_ETAG
1288 G.etag,
1289#endif
1209 file_size 1290 file_size
1210 ); 1291 );
1211 } 1292 }
@@ -1444,7 +1525,7 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
1444 count = safe_read(fromCgi_rd, rbuf + out_cnt, IOBUF_SIZE - 8); 1525 count = safe_read(fromCgi_rd, rbuf + out_cnt, IOBUF_SIZE - 8);
1445 if (count <= 0) { 1526 if (count <= 0) {
1446 /* eof (or error) and there was no "HTTP", 1527 /* eof (or error) and there was no "HTTP",
1447 * send "HTTP/1.0 200 OK\r\n", then send received data */ 1528 * send "HTTP/1.1 200 OK\r\n", then send received data */
1448 if (out_cnt) { 1529 if (out_cnt) {
1449 full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1); 1530 full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1);
1450 full_write(STDOUT_FILENO, rbuf, out_cnt); 1531 full_write(STDOUT_FILENO, rbuf, out_cnt);
@@ -1455,10 +1536,10 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
1455 count = 0; 1536 count = 0;
1456 /* "Status" header format is: "Status: 302 Redirected\r\n" */ 1537 /* "Status" header format is: "Status: 302 Redirected\r\n" */
1457 if (out_cnt >= 8 && memcmp(rbuf, "Status: ", 8) == 0) { 1538 if (out_cnt >= 8 && memcmp(rbuf, "Status: ", 8) == 0) {
1458 /* send "HTTP/1.0 " */ 1539 /* send "HTTP/1.1 " */
1459 if (full_write(STDOUT_FILENO, HTTP_200, 9) != 9) 1540 if (full_write(STDOUT_FILENO, HTTP_200, 9) != 9)
1460 break; 1541 break;
1461 /* skip "Status: " (including space, sending "HTTP/1.0 NNN" is wrong) */ 1542 /* skip "Status: " (including space, sending "HTTP/1.1 NNN" is wrong) */
1462 rbuf += 8; 1543 rbuf += 8;
1463 count = out_cnt - 8; 1544 count = out_cnt - 8;
1464 out_cnt = -1; /* buffering off */ 1545 out_cnt = -1; /* buffering off */
@@ -1474,7 +1555,7 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
1474 full_write(s, "Content-type: text/plain\r\n\r\n", 28); 1555 full_write(s, "Content-type: text/plain\r\n\r\n", 28);
1475 } 1556 }
1476 * Counter-example of valid CGI without Content-type: 1557 * Counter-example of valid CGI without Content-type:
1477 * echo -en "HTTP/1.0 302 Found\r\n" 1558 * echo -en "HTTP/1.1 302 Found\r\n"
1478 * echo -en "Location: http://www.busybox.net\r\n" 1559 * echo -en "Location: http://www.busybox.net\r\n"
1479 * echo -en "\r\n" 1560 * echo -en "\r\n"
1480 */ 1561 */
@@ -1581,7 +1662,7 @@ static void send_cgi_and_exit(
1581 /* (Older versions of bbox seem to do some decoding) */ 1662 /* (Older versions of bbox seem to do some decoding) */
1582 setenv1("QUERY_STRING", g_query); 1663 setenv1("QUERY_STRING", g_query);
1583 putenv((char*)"SERVER_SOFTWARE=busybox httpd/"BB_VER); 1664 putenv((char*)"SERVER_SOFTWARE=busybox httpd/"BB_VER);
1584 putenv((char*)"SERVER_PROTOCOL=HTTP/1.0"); 1665 putenv((char*)"SERVER_PROTOCOL=HTTP/1.1");
1585 putenv((char*)"GATEWAY_INTERFACE=CGI/1.1"); 1666 putenv((char*)"GATEWAY_INTERFACE=CGI/1.1");
1586 /* Having _separate_ variables for IP and port defeats 1667 /* Having _separate_ variables for IP and port defeats
1587 * the purpose of having socket abstraction. Which "port" 1668 * the purpose of having socket abstraction. Which "port"
@@ -1732,6 +1813,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
1732 } 1813 }
1733 } else { 1814 } else {
1734 fd = open(url, O_RDONLY); 1815 fd = open(url, O_RDONLY);
1816 /* file_size and last_mod are already populated */
1735 } 1817 }
1736 if (fd < 0) { 1818 if (fd < 0) {
1737 if (DEBUG) 1819 if (DEBUG)
@@ -1743,6 +1825,19 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
1743 send_headers_and_exit(HTTP_NOT_FOUND); 1825 send_headers_and_exit(HTTP_NOT_FOUND);
1744 log_and_exit(); 1826 log_and_exit();
1745 } 1827 }
1828#if ENABLE_FEATURE_HTTPD_ETAG
1829 /* ETag is "hex(last_mod)-hex(file_size)" e.g. "5e132e20-417" */
1830 sprintf(G.etag, "\"%llx-%llx\"", (unsigned long long)last_mod, (unsigned long long)file_size);
1831
1832 if (G.if_none_match) {
1833 if (DEBUG)
1834 bb_perror_msg("If-None-Match and file's ETag are: '%s' '%s'\n", G.if_none_match, G.etag);
1835 /* Weak ETag comparision.
1836 * If-None-Match may have many ETags but they are quoted so we can use simple substring search */
1837 if (strstr(G.if_none_match, G.etag))
1838 send_headers_and_exit(HTTP_NOT_MODIFIED);
1839 }
1840#endif
1746 /* If you want to know about EPIPE below 1841 /* If you want to know about EPIPE below
1747 * (happens if you abort downloads from local httpd): */ 1842 * (happens if you abort downloads from local httpd): */
1748 signal(SIGPIPE, SIG_IGN); 1843 signal(SIGPIPE, SIG_IGN);
@@ -1878,11 +1973,12 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
1878 log_and_exit(); 1973 log_and_exit();
1879} 1974}
1880 1975
1976#if ENABLE_FEATURE_HTTPD_ACL_IP
1881static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(unsigned remote_ip) 1977static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(unsigned remote_ip)
1882{ 1978{
1883 Htaccess_IP *cur; 1979 Htaccess_IP *cur;
1884 1980
1885 for (cur = ip_a_d; cur; cur = cur->next) { 1981 for (cur = G.ip_a_d; cur; cur = cur->next) {
1886#if DEBUG 1982#if DEBUG
1887 fprintf(stderr, 1983 fprintf(stderr,
1888 "checkPermIP: '%s' ? '%u.%u.%u.%u/%u.%u.%u.%u'\n", 1984 "checkPermIP: '%s' ? '%u.%u.%u.%u/%u.%u.%u.%u'\n",
@@ -1907,6 +2003,9 @@ static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(unsigned remote_ip)
1907 if (flg_deny_all) /* depends on whether we saw "D:*" */ 2003 if (flg_deny_all) /* depends on whether we saw "D:*" */
1908 send_headers_and_exit(HTTP_FORBIDDEN); 2004 send_headers_and_exit(HTTP_FORBIDDEN);
1909} 2005}
2006#else
2007# define if_ip_denied_send_HTTP_FORBIDDEN_and_exit(arg) ((void)0)
2008#endif
1910 2009
1911#if ENABLE_FEATURE_HTTPD_BASIC_AUTH 2010#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
1912 2011
@@ -2160,7 +2259,9 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2160 char *urlcopy; 2259 char *urlcopy;
2161 char *urlp; 2260 char *urlp;
2162 char *tptr; 2261 char *tptr;
2262#if ENABLE_FEATURE_HTTPD_ACL_IP
2163 unsigned remote_ip; 2263 unsigned remote_ip;
2264#endif
2164#if ENABLE_FEATURE_HTTPD_CGI 2265#if ENABLE_FEATURE_HTTPD_CGI
2165 unsigned total_headers_len; 2266 unsigned total_headers_len;
2166#endif 2267#endif
@@ -2182,18 +2283,30 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2182 * (IOW, server process doesn't need to waste 8k) */ 2283 * (IOW, server process doesn't need to waste 8k) */
2183 iobuf = xmalloc(IOBUF_SIZE); 2284 iobuf = xmalloc(IOBUF_SIZE);
2184 2285
2286 if (ENABLE_FEATURE_HTTPD_CGI || DEBUG || verbose) {
2287 /* NB: can be NULL (user runs httpd -i by hand?) */
2288 rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr->u.sa);
2289 }
2290 if (verbose) {
2291 /* this trick makes -v logging much simpler */
2292 if (rmt_ip_str)
2293 applet_name = rmt_ip_str;
2294 if (verbose > 2)
2295 bb_simple_error_msg("connected");
2296 }
2297#if ENABLE_FEATURE_HTTPD_ACL_IP
2185 remote_ip = 0; 2298 remote_ip = 0;
2186 if (fromAddr->u.sa.sa_family == AF_INET) { 2299 if (fromAddr->u.sa.sa_family == AF_INET) {
2187 remote_ip = ntohl(fromAddr->u.sin.sin_addr.s_addr); 2300 remote_ip = ntohl(fromAddr->u.sin.sin_addr.s_addr);
2188 } 2301 }
2189#if ENABLE_FEATURE_IPV6 2302# if ENABLE_FEATURE_IPV6
2190# if !ENABLE_PLATFORM_MINGW32 2303# if !ENABLE_PLATFORM_MINGW32
2191 if (fromAddr->u.sa.sa_family == AF_INET6 2304 if (fromAddr->u.sa.sa_family == AF_INET6
2192 && fromAddr->u.sin6.sin6_addr.s6_addr32[0] == 0 2305 && fromAddr->u.sin6.sin6_addr.s6_addr32[0] == 0
2193 && fromAddr->u.sin6.sin6_addr.s6_addr32[1] == 0 2306 && fromAddr->u.sin6.sin6_addr.s6_addr32[1] == 0
2194 && ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[2]) == 0xffff) 2307 && ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[2]) == 0xffff)
2195 remote_ip = ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[3]); 2308 remote_ip = ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[3]);
2196# else 2309# else
2197 if (fromAddr->u.sa.sa_family == AF_INET6 2310 if (fromAddr->u.sa.sa_family == AF_INET6
2198 && fromAddr->u.sin6.sin6_addr.s6_words[0] == 0 2311 && fromAddr->u.sin6.sin6_addr.s6_words[0] == 0
2199 && fromAddr->u.sin6.sin6_addr.s6_words[1] == 0 2312 && fromAddr->u.sin6.sin6_addr.s6_words[1] == 0
@@ -2201,20 +2314,10 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2201 && fromAddr->u.sin6.sin6_addr.s6_words[3] == 0 2314 && fromAddr->u.sin6.sin6_addr.s6_words[3] == 0
2202 && ntohl(*(uint32_t *)(fromAddr->u.sin6.sin6_addr.s6_words+4)) == 0xffff) 2315 && ntohl(*(uint32_t *)(fromAddr->u.sin6.sin6_addr.s6_words+4)) == 0xffff)
2203 remote_ip = ntohl(*(uint32_t *)(fromAddr->u.sin6.sin6_addr.s6_words+6)); 2316 remote_ip = ntohl(*(uint32_t *)(fromAddr->u.sin6.sin6_addr.s6_words+6));
2317# endif
2204# endif 2318# endif
2205#endif
2206 if (ENABLE_FEATURE_HTTPD_CGI || DEBUG || verbose) {
2207 /* NB: can be NULL (user runs httpd -i by hand?) */
2208 rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr->u.sa);
2209 }
2210 if (verbose) {
2211 /* this trick makes -v logging much simpler */
2212 if (rmt_ip_str)
2213 applet_name = rmt_ip_str;
2214 if (verbose > 2)
2215 bb_simple_error_msg("connected");
2216 }
2217 if_ip_denied_send_HTTP_FORBIDDEN_and_exit(remote_ip); 2319 if_ip_denied_send_HTTP_FORBIDDEN_and_exit(remote_ip);
2320#endif
2218 2321
2219#ifdef SIGALRM 2322#ifdef SIGALRM
2220 /* Install timeout handler. get_line() needs it. */ 2323 /* Install timeout handler. get_line() needs it. */
@@ -2512,6 +2615,13 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2512 continue; 2615 continue;
2513 } 2616 }
2514#endif 2617#endif
2618#if ENABLE_FEATURE_HTTPD_ETAG
2619 if (STRNCASECMP(iobuf, "If-None-Match:") == 0) {
2620 free(G.if_none_match);
2621 G.if_none_match = xstrdup(skip_whitespace(iobuf + sizeof("If-None-Match:") - 1));
2622 continue;
2623 }
2624#endif
2515#if ENABLE_FEATURE_HTTPD_CGI 2625#if ENABLE_FEATURE_HTTPD_CGI
2516 if (cgi_type != CGI_NONE) { 2626 if (cgi_type != CGI_NONE) {
2517 bool ct = (STRNCASECMP(iobuf, "Content-Type:") == 0); 2627 bool ct = (STRNCASECMP(iobuf, "Content-Type:") == 0);