20 |
* httpd -p 80 -u 80 -h /www -c /etc/httpd.conf -r "Web Server Authentication" |
* httpd -p 80 -u 80 -h /www -c /etc/httpd.conf -r "Web Server Authentication" |
21 |
* |
* |
22 |
* |
* |
23 |
* When a url starts by "/cgi-bin/" it is assumed to be a cgi script. The |
* When an url starts by "/cgi-bin/" it is assumed to be a cgi script. The |
24 |
* server changes directory to the location of the script and executes it |
* server changes directory to the location of the script and executes it |
25 |
* after setting QUERY_STRING and other environment variables. |
* after setting QUERY_STRING and other environment variables. |
26 |
* |
* |
27 |
* Doc: |
* Doc: |
28 |
* "CGI Environment Variables": http://hoohoo.ncsa.uiuc.edu/cgi/env.html |
* "CGI Environment Variables": http://hoohoo.ncsa.uiuc.edu/cgi/env.html |
29 |
* |
* |
30 |
* The applet can also be invoked as a url arg decoder and html text encoder |
* The applet can also be invoked as an url arg decoder and html text encoder |
31 |
* as follows: |
* as follows: |
32 |
* foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World" |
* foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World" |
33 |
* bar=`httpd -e "<Hello World>"` # encode as "<Hello World>" |
* bar=`httpd -e "<Hello World>"` # encode as "<Hello World>" |
34 |
* Note that url encoding for arguments is not the same as html encoding for |
* Note that url encoding for arguments is not the same as html encoding for |
35 |
* presentation. -d decodes a url-encoded argument while -e encodes in html |
* presentation. -d decodes an url-encoded argument while -e encodes in html |
36 |
* for page display. |
* for page display. |
37 |
* |
* |
38 |
* httpd.conf has the following format: |
* httpd.conf has the following format: |
54 |
* /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/ |
* /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/ |
55 |
* /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/ |
* /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/ |
56 |
* .au:audio/basic # additional mime type for audio.au files |
* .au:audio/basic # additional mime type for audio.au files |
57 |
* *.php:/path/php # running cgi.php scripts through an interpreter |
* *.php:/path/php # run xxx.php through an interpreter |
58 |
* |
* |
59 |
* A/D may be as a/d or allow/deny - only first char matters. |
* A/D may be as a/d or allow/deny - only first char matters. |
60 |
* Deny/Allow IP logic: |
* Deny/Allow IP logic: |
94 |
* server exits with an error. |
* server exits with an error. |
95 |
* |
* |
96 |
*/ |
*/ |
97 |
|
/* TODO: use TCP_CORK, parse_config() */ |
98 |
|
|
99 |
#include "libbb.h" |
#include "libbb.h" |
100 |
#if ENABLE_FEATURE_HTTPD_USE_SENDFILE |
#if ENABLE_FEATURE_HTTPD_USE_SENDFILE |
101 |
#include <sys/sendfile.h> |
# include <sys/sendfile.h> |
102 |
#endif |
#endif |
103 |
|
|
|
//#define DEBUG 1 |
|
104 |
#define DEBUG 0 |
#define DEBUG 0 |
105 |
|
|
106 |
#define IOBUF_SIZE 8192 /* IO buffer */ |
#define IOBUF_SIZE 8192 /* IO buffer */ |
115 |
|
|
116 |
#define HEADER_READ_TIMEOUT 60 |
#define HEADER_READ_TIMEOUT 60 |
117 |
|
|
118 |
static const char default_path_httpd_conf[] ALIGN1 = "/etc"; |
static const char DEFAULT_PATH_HTTPD_CONF[] ALIGN1 = "/etc"; |
119 |
static const char httpd_conf[] ALIGN1 = "httpd.conf"; |
static const char HTTPD_CONF[] ALIGN1 = "httpd.conf"; |
120 |
static const char HTTP_200[] ALIGN1 = "HTTP/1.0 200 OK\r\n"; |
static const char HTTP_200[] ALIGN1 = "HTTP/1.0 200 OK\r\n"; |
121 |
|
|
122 |
typedef struct has_next_ptr { |
typedef struct has_next_ptr { |
231 |
#endif |
#endif |
232 |
}; |
}; |
233 |
|
|
234 |
|
static const char index_html[] ALIGN1 = "index.html"; |
235 |
|
|
236 |
|
|
237 |
struct globals { |
struct globals { |
238 |
int verbose; /* must be int (used by getopt32) */ |
int verbose; /* must be int (used by getopt32) */ |
244 |
const char *bind_addr_or_port; |
const char *bind_addr_or_port; |
245 |
|
|
246 |
const char *g_query; |
const char *g_query; |
247 |
const char *configFile; |
const char *opt_c_configFile; |
248 |
const char *home_httpd; |
const char *home_httpd; |
249 |
const char *index_page; |
const char *index_page; |
250 |
|
|
252 |
const char *found_moved_temporarily; |
const char *found_moved_temporarily; |
253 |
Htaccess_IP *ip_a_d; /* config allow/deny lines */ |
Htaccess_IP *ip_a_d; /* config allow/deny lines */ |
254 |
|
|
255 |
USE_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;) |
IF_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;) |
256 |
USE_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;) |
IF_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;) |
257 |
USE_FEATURE_HTTPD_CGI(char *referer;) |
IF_FEATURE_HTTPD_CGI(char *referer;) |
258 |
USE_FEATURE_HTTPD_CGI(char *user_agent;) |
IF_FEATURE_HTTPD_CGI(char *user_agent;) |
259 |
USE_FEATURE_HTTPD_CGI(char *http_accept;) |
IF_FEATURE_HTTPD_CGI(char *host;) |
260 |
USE_FEATURE_HTTPD_CGI(char *http_accept_language;) |
IF_FEATURE_HTTPD_CGI(char *http_accept;) |
261 |
|
IF_FEATURE_HTTPD_CGI(char *http_accept_language;) |
262 |
|
|
263 |
off_t file_size; /* -1 - unknown */ |
off_t file_size; /* -1 - unknown */ |
264 |
#if ENABLE_FEATURE_HTTPD_RANGES |
#if ENABLE_FEATURE_HTTPD_RANGES |
270 |
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH |
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH |
271 |
Htaccess *g_auth; /* config user:password lines */ |
Htaccess *g_auth; /* config user:password lines */ |
272 |
#endif |
#endif |
|
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES |
|
273 |
Htaccess *mime_a; /* config mime types */ |
Htaccess *mime_a; /* config mime types */ |
|
#endif |
|
274 |
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR |
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR |
275 |
Htaccess *script_i; /* config script interpreters */ |
Htaccess *script_i; /* config script interpreters */ |
276 |
#endif |
#endif |
291 |
#define rmt_ip (G.rmt_ip ) |
#define rmt_ip (G.rmt_ip ) |
292 |
#define bind_addr_or_port (G.bind_addr_or_port) |
#define bind_addr_or_port (G.bind_addr_or_port) |
293 |
#define g_query (G.g_query ) |
#define g_query (G.g_query ) |
294 |
#define configFile (G.configFile ) |
#define opt_c_configFile (G.opt_c_configFile ) |
295 |
#define home_httpd (G.home_httpd ) |
#define home_httpd (G.home_httpd ) |
296 |
#define index_page (G.index_page ) |
#define index_page (G.index_page ) |
297 |
#define found_mime_type (G.found_mime_type ) |
#define found_mime_type (G.found_mime_type ) |
302 |
#define remoteuser (G.remoteuser ) |
#define remoteuser (G.remoteuser ) |
303 |
#define referer (G.referer ) |
#define referer (G.referer ) |
304 |
#define user_agent (G.user_agent ) |
#define user_agent (G.user_agent ) |
305 |
|
#define host (G.host ) |
306 |
#define http_accept (G.http_accept ) |
#define http_accept (G.http_accept ) |
307 |
#define http_accept_language (G.http_accept_language) |
#define http_accept_language (G.http_accept_language) |
308 |
#define file_size (G.file_size ) |
#define file_size (G.file_size ) |
310 |
#define range_start (G.range_start ) |
#define range_start (G.range_start ) |
311 |
#define range_end (G.range_end ) |
#define range_end (G.range_end ) |
312 |
#define range_len (G.range_len ) |
#define range_len (G.range_len ) |
313 |
|
#else |
314 |
|
enum { |
315 |
|
range_start = 0, |
316 |
|
range_end = MAXINT(off_t) - 1, |
317 |
|
range_len = MAXINT(off_t), |
318 |
|
}; |
319 |
#endif |
#endif |
320 |
#define rmt_ip_str (G.rmt_ip_str ) |
#define rmt_ip_str (G.rmt_ip_str ) |
321 |
#define g_auth (G.g_auth ) |
#define g_auth (G.g_auth ) |
328 |
#define proxy (G.proxy ) |
#define proxy (G.proxy ) |
329 |
#define INIT_G() do { \ |
#define INIT_G() do { \ |
330 |
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ |
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ |
331 |
USE_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \ |
IF_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \ |
332 |
bind_addr_or_port = "80"; \ |
bind_addr_or_port = "80"; \ |
333 |
index_page = "index.html"; \ |
index_page = index_html; \ |
334 |
file_size = -1; \ |
file_size = -1; \ |
335 |
} while (0) |
} while (0) |
336 |
|
|
|
#if !ENABLE_FEATURE_HTTPD_RANGES |
|
|
enum { |
|
|
range_start = 0, |
|
|
range_end = MAXINT(off_t) - 1, |
|
|
range_len = MAXINT(off_t), |
|
|
}; |
|
|
#endif |
|
|
|
|
337 |
|
|
338 |
#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1) |
#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1) |
339 |
|
|
356 |
*pptr = NULL; |
*pptr = NULL; |
357 |
} |
} |
358 |
|
|
|
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \ |
|
|
|| ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \ |
|
|
|| ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR |
|
359 |
static ALWAYS_INLINE void free_Htaccess_list(Htaccess **pptr) |
static ALWAYS_INLINE void free_Htaccess_list(Htaccess **pptr) |
360 |
{ |
{ |
361 |
free_llist((has_next_ptr**)pptr); |
free_llist((has_next_ptr**)pptr); |
362 |
} |
} |
|
#endif |
|
363 |
|
|
364 |
static ALWAYS_INLINE void free_Htaccess_IP_list(Htaccess_IP **pptr) |
static ALWAYS_INLINE void free_Htaccess_IP_list(Htaccess_IP **pptr) |
365 |
{ |
{ |
454 |
/* |
/* |
455 |
* Parse configuration file into in-memory linked list. |
* Parse configuration file into in-memory linked list. |
456 |
* |
* |
|
* The first non-white character is examined to determine if the config line |
|
|
* is one of the following: |
|
|
* .ext:mime/type # new mime type not compiled into httpd |
|
|
* [adAD]:from # ip address allow/deny, * for wildcard |
|
|
* /path:user:pass # username/password |
|
|
* Ennn:error.html # error page for status nnn |
|
|
* P:/url:[http://]hostname[:port]/new/path # reverse proxy |
|
|
* |
|
457 |
* Any previous IP rules are discarded. |
* Any previous IP rules are discarded. |
458 |
* If the flag argument is not SUBDIR_PARSE then all /path and mime rules |
* If the flag argument is not SUBDIR_PARSE then all /path and mime rules |
459 |
* are also discarded. That is, previous settings are retained if flag is |
* are also discarded. That is, previous settings are retained if flag is |
463 |
* path Path where to look for httpd.conf (without filename). |
* path Path where to look for httpd.conf (without filename). |
464 |
* flag Type of the parse request. |
* flag Type of the parse request. |
465 |
*/ |
*/ |
466 |
/* flag */ |
/* flag param: */ |
467 |
#define FIRST_PARSE 0 |
enum { |
468 |
#define SUBDIR_PARSE 1 |
FIRST_PARSE = 0, /* path will be "/etc" */ |
469 |
#define SIGNALED_PARSE 2 |
SIGNALED_PARSE = 1, /* path will be "/etc" */ |
470 |
#define FIND_FROM_HTTPD_ROOT 3 |
SUBDIR_PARSE = 2, /* path will be derived from URL */ |
471 |
|
}; |
472 |
static void parse_conf(const char *path, int flag) |
static void parse_conf(const char *path, int flag) |
473 |
{ |
{ |
474 |
|
/* internally used extra flag state */ |
475 |
|
enum { TRY_CURDIR_PARSE = 3 }; |
476 |
|
|
477 |
FILE *f; |
FILE *f; |
478 |
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH |
const char *filename; |
|
Htaccess *prev; |
|
|
#endif |
|
|
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \ |
|
|
|| ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \ |
|
|
|| ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR |
|
|
Htaccess *cur; |
|
|
#endif |
|
|
const char *filename = configFile; |
|
479 |
char buf[160]; |
char buf[160]; |
|
char *p, *p0; |
|
|
char *after_colon; |
|
|
Htaccess_IP *pip; |
|
480 |
|
|
481 |
/* discard old rules */ |
/* discard old rules */ |
482 |
free_Htaccess_IP_list(&ip_a_d); |
free_Htaccess_IP_list(&ip_a_d); |
483 |
flg_deny_all = 0; |
flg_deny_all = 0; |
|
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \ |
|
|
|| ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \ |
|
|
|| ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR |
|
484 |
/* retain previous auth and mime config only for subdir parse */ |
/* retain previous auth and mime config only for subdir parse */ |
485 |
if (flag != SUBDIR_PARSE) { |
if (flag != SUBDIR_PARSE) { |
486 |
|
free_Htaccess_list(&mime_a); |
487 |
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH |
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH |
488 |
free_Htaccess_list(&g_auth); |
free_Htaccess_list(&g_auth); |
489 |
#endif |
#endif |
|
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES |
|
|
free_Htaccess_list(&mime_a); |
|
|
#endif |
|
490 |
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR |
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR |
491 |
free_Htaccess_list(&script_i); |
free_Htaccess_list(&script_i); |
492 |
#endif |
#endif |
493 |
} |
} |
|
#endif |
|
494 |
|
|
495 |
|
filename = opt_c_configFile; |
496 |
if (flag == SUBDIR_PARSE || filename == NULL) { |
if (flag == SUBDIR_PARSE || filename == NULL) { |
497 |
filename = alloca(strlen(path) + sizeof(httpd_conf) + 2); |
filename = alloca(strlen(path) + sizeof(HTTPD_CONF) + 2); |
498 |
sprintf((char *)filename, "%s/%s", path, httpd_conf); |
sprintf((char *)filename, "%s/%s", path, HTTPD_CONF); |
499 |
} |
} |
500 |
|
|
501 |
while ((f = fopen_for_read(filename)) == NULL) { |
while ((f = fopen_for_read(filename)) == NULL) { |
502 |
if (flag == SUBDIR_PARSE || flag == FIND_FROM_HTTPD_ROOT) { |
if (flag >= SUBDIR_PARSE) { /* SUBDIR or TRY_CURDIR */ |
503 |
/* config file not found, no changes to config */ |
/* config file not found, no changes to config */ |
504 |
return; |
return; |
505 |
} |
} |
506 |
if (configFile && flag == FIRST_PARSE) /* if -c option given */ |
if (flag == FIRST_PARSE) { |
507 |
bb_simple_perror_msg_and_die(filename); |
/* -c CONFFILE given, but CONFFILE doesn't exist? */ |
508 |
flag = FIND_FROM_HTTPD_ROOT; |
if (opt_c_configFile) |
509 |
filename = httpd_conf; |
bb_simple_perror_msg_and_die(opt_c_configFile); |
510 |
|
/* else: no -c, thus we looked at /etc/httpd.conf, |
511 |
|
* and it's not there. try ./httpd.conf: */ |
512 |
|
} |
513 |
|
flag = TRY_CURDIR_PARSE; |
514 |
|
filename = HTTPD_CONF; |
515 |
} |
} |
516 |
|
|
517 |
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH |
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH |
518 |
prev = g_auth; |
/* in "/file:user:pass" lines, we prepend path in subdirs */ |
519 |
#endif |
if (flag != SUBDIR_PARSE) |
520 |
/* This could stand some work */ |
path = ""; |
521 |
while ((p0 = fgets(buf, sizeof(buf), f)) != NULL) { |
#endif |
522 |
after_colon = NULL; |
/* The lines can be: |
523 |
for (p = p0; *p0 != '\0' && *p0 != '#'; p0++) { |
* |
524 |
if (!isspace(*p0)) { |
* I:default_index_file |
525 |
*p++ = *p0; |
* H:http_home |
526 |
if (*p0 == ':' && after_colon == NULL) |
* [AD]:IP[/mask] # allow/deny, * for wildcard |
527 |
after_colon = p; |
* Ennn:error.html # error page for status nnn |
528 |
|
* P:/url:[http://]hostname[:port]/new/path # reverse proxy |
529 |
|
* .ext:mime/type # mime type |
530 |
|
* *.php:/path/php # run xxx.php through an interpreter |
531 |
|
* /file:user:pass # username and password |
532 |
|
*/ |
533 |
|
while (fgets(buf, sizeof(buf), f) != NULL) { |
534 |
|
unsigned strlen_buf; |
535 |
|
unsigned char ch; |
536 |
|
char *after_colon; |
537 |
|
|
538 |
|
{ /* remove all whitespace, and # comments */ |
539 |
|
char *p, *p0; |
540 |
|
|
541 |
|
p0 = buf; |
542 |
|
/* skip non-whitespace beginning. Often the whole line |
543 |
|
* is non-whitespace. We want this case to work fast, |
544 |
|
* without needless copying, therefore we don't merge |
545 |
|
* this operation into next while loop. */ |
546 |
|
while ((ch = *p0) != '\0' && ch != '\n' && ch != '#' |
547 |
|
&& ch != ' ' && ch != '\t' |
548 |
|
) { |
549 |
|
p0++; |
550 |
} |
} |
551 |
|
p = p0; |
552 |
|
/* if we enter this loop, we have some whitespace. |
553 |
|
* discard it */ |
554 |
|
while (ch != '\0' && ch != '\n' && ch != '#') { |
555 |
|
if (ch != ' ' && ch != '\t') { |
556 |
|
*p++ = ch; |
557 |
|
} |
558 |
|
ch = *++p0; |
559 |
|
} |
560 |
|
*p = '\0'; |
561 |
|
strlen_buf = p - buf; |
562 |
|
if (strlen_buf == 0) |
563 |
|
continue; /* empty line */ |
564 |
} |
} |
|
*p = '\0'; |
|
565 |
|
|
566 |
/* test for empty or strange line */ |
after_colon = strchr(buf, ':'); |
567 |
if (after_colon == NULL || *after_colon == '\0') |
/* strange line? */ |
568 |
|
if (after_colon == NULL || *++after_colon == '\0') |
569 |
|
goto config_error; |
570 |
|
|
571 |
|
ch = (buf[0] & ~0x20); /* toupper if it's a letter */ |
572 |
|
|
573 |
|
if (ch == 'I') { |
574 |
|
if (index_page != index_html) |
575 |
|
free((char*)index_page); |
576 |
|
index_page = xstrdup(after_colon); |
577 |
continue; |
continue; |
578 |
p0 = buf; |
} |
579 |
if (*p0 == 'd' || *p0 == 'a') |
|
580 |
*p0 -= 0x20; /* a/d -> A/D */ |
/* do not allow jumping around using H in subdir's configs */ |
581 |
if (*after_colon == '*') { |
if (flag == FIRST_PARSE && ch == 'H') { |
582 |
if (*p0 == 'D') { |
home_httpd = xstrdup(after_colon); |
583 |
/* memorize "deny all" */ |
xchdir(home_httpd); |
|
flg_deny_all = 1; |
|
|
} |
|
|
/* skip assumed "A:*", it is a default anyway */ |
|
584 |
continue; |
continue; |
585 |
} |
} |
586 |
|
|
587 |
if (*p0 == 'A' || *p0 == 'D') { |
if (ch == 'A' || ch == 'D') { |
588 |
/* storing current config IP line */ |
Htaccess_IP *pip; |
589 |
pip = xzalloc(sizeof(Htaccess_IP)); |
|
590 |
if (scan_ip_mask(after_colon, &(pip->ip), &(pip->mask))) { |
if (*after_colon == '*') { |
591 |
|
if (ch == 'D') { |
592 |
|
/* memorize "deny all" */ |
593 |
|
flg_deny_all = 1; |
594 |
|
} |
595 |
|
/* skip assumed "A:*", it is a default anyway */ |
596 |
|
continue; |
597 |
|
} |
598 |
|
/* store "allow/deny IP/mask" line */ |
599 |
|
pip = xzalloc(sizeof(*pip)); |
600 |
|
if (scan_ip_mask(after_colon, &pip->ip, &pip->mask)) { |
601 |
/* IP{/mask} syntax error detected, protect all */ |
/* IP{/mask} syntax error detected, protect all */ |
602 |
*p0 = 'D'; |
ch = 'D'; |
603 |
pip->mask = 0; |
pip->mask = 0; |
604 |
} |
} |
605 |
pip->allow_deny = *p0; |
pip->allow_deny = ch; |
606 |
if (*p0 == 'D') { |
if (ch == 'D') { |
607 |
/* Deny:from_IP - prepend */ |
/* Deny:from_IP - prepend */ |
608 |
pip->next = ip_a_d; |
pip->next = ip_a_d; |
609 |
ip_a_d = pip; |
ip_a_d = pip; |
610 |
} else { |
} else { |
611 |
/* A:from_IP - append (thus D precedes A) */ |
/* A:from_IP - append (thus all D's precedes A's) */ |
612 |
Htaccess_IP *prev_IP = ip_a_d; |
Htaccess_IP *prev_IP = ip_a_d; |
613 |
if (prev_IP == NULL) { |
if (prev_IP == NULL) { |
614 |
ip_a_d = pip; |
ip_a_d = pip; |
622 |
} |
} |
623 |
|
|
624 |
#if ENABLE_FEATURE_HTTPD_ERROR_PAGES |
#if ENABLE_FEATURE_HTTPD_ERROR_PAGES |
625 |
if (flag == FIRST_PARSE && *p0 == 'E') { |
if (flag == FIRST_PARSE && ch == 'E') { |
626 |
unsigned i; |
unsigned i; |
627 |
int status = atoi(++p0); /* error status code */ |
int status = atoi(buf + 1); /* error status code */ |
628 |
|
|
629 |
if (status < HTTP_CONTINUE) { |
if (status < HTTP_CONTINUE) { |
630 |
bb_error_msg("config error '%s' in '%s'", buf, filename); |
goto config_error; |
|
continue; |
|
631 |
} |
} |
632 |
/* then error page; find matching status */ |
/* then error page; find matching status */ |
633 |
for (i = 0; i < ARRAY_SIZE(http_response_type); i++) { |
for (i = 0; i < ARRAY_SIZE(http_response_type); i++) { |
644 |
#endif |
#endif |
645 |
|
|
646 |
#if ENABLE_FEATURE_HTTPD_PROXY |
#if ENABLE_FEATURE_HTTPD_PROXY |
647 |
if (flag == FIRST_PARSE && *p0 == 'P') { |
if (flag == FIRST_PARSE && ch == 'P') { |
648 |
/* P:/url:[http://]hostname[:port]/new/path */ |
/* P:/url:[http://]hostname[:port]/new/path */ |
649 |
char *url_from, *host_port, *url_to; |
char *url_from, *host_port, *url_to; |
650 |
Htaccess_Proxy *proxy_entry; |
Htaccess_Proxy *proxy_entry; |
652 |
url_from = after_colon; |
url_from = after_colon; |
653 |
host_port = strchr(after_colon, ':'); |
host_port = strchr(after_colon, ':'); |
654 |
if (host_port == NULL) { |
if (host_port == NULL) { |
655 |
bb_error_msg("config error '%s' in '%s'", buf, filename); |
goto config_error; |
|
continue; |
|
656 |
} |
} |
657 |
*host_port++ = '\0'; |
*host_port++ = '\0'; |
658 |
if (strncmp(host_port, "http://", 7) == 0) |
if (strncmp(host_port, "http://", 7) == 0) |
659 |
host_port += 7; |
host_port += 7; |
660 |
if (*host_port == '\0') { |
if (*host_port == '\0') { |
661 |
bb_error_msg("config error '%s' in '%s'", buf, filename); |
goto config_error; |
|
continue; |
|
662 |
} |
} |
663 |
url_to = strchr(host_port, '/'); |
url_to = strchr(host_port, '/'); |
664 |
if (url_to == NULL) { |
if (url_to == NULL) { |
665 |
bb_error_msg("config error '%s' in '%s'", buf, filename); |
goto config_error; |
|
continue; |
|
666 |
} |
} |
667 |
*url_to = '\0'; |
*url_to = '\0'; |
668 |
proxy_entry = xzalloc(sizeof(Htaccess_Proxy)); |
proxy_entry = xzalloc(sizeof(*proxy_entry)); |
669 |
proxy_entry->url_from = xstrdup(url_from); |
proxy_entry->url_from = xstrdup(url_from); |
670 |
proxy_entry->host_port = xstrdup(host_port); |
proxy_entry->host_port = xstrdup(host_port); |
671 |
*url_to = '/'; |
*url_to = '/'; |
675 |
continue; |
continue; |
676 |
} |
} |
677 |
#endif |
#endif |
678 |
|
/* the rest of directives are non-alphabetic, |
679 |
|
* must avoid using "toupper'ed" ch */ |
680 |
|
ch = buf[0]; |
681 |
|
|
682 |
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH |
if (ch == '.' /* ".ext:mime/type" */ |
683 |
if (*p0 == '/') { |
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR |
684 |
/* make full path from httpd root / current_path / config_line_path */ |
|| (ch == '*' && buf[1] == '.') /* "*.php:/path/php" */ |
685 |
const char *tp = (flag == SUBDIR_PARSE ? path : ""); |
#endif |
686 |
p0 = xmalloc(strlen(tp) + (after_colon - buf) + 2 + strlen(after_colon)); |
) { |
687 |
after_colon[-1] = '\0'; |
char *p; |
688 |
sprintf(p0, "/%s%s", tp, buf); |
Htaccess *cur; |
|
|
|
|
/* looks like bb_simplify_path... */ |
|
|
tp = p = p0; |
|
|
do { |
|
|
if (*p == '/') { |
|
|
if (*tp == '/') { /* skip duplicate (or initial) slash */ |
|
|
continue; |
|
|
} |
|
|
if (*tp == '.') { |
|
|
if (tp[1] == '/' || tp[1] == '\0') { /* remove extra '.' */ |
|
|
continue; |
|
|
} |
|
|
if ((tp[1] == '.') && (tp[2] == '/' || tp[2] == '\0')) { |
|
|
++tp; |
|
|
if (p > p0) { |
|
|
while (*--p != '/') /* omit previous dir */ |
|
|
continue; |
|
|
} |
|
|
continue; |
|
|
} |
|
|
} |
|
|
} |
|
|
*++p = *tp; |
|
|
} while (*++tp); |
|
689 |
|
|
690 |
if ((p == p0) || (*p != '/')) { /* not a trailing slash */ |
cur = xzalloc(sizeof(*cur) /* includes space for NUL */ + strlen_buf); |
691 |
++p; /* so keep last character */ |
strcpy(cur->before_colon, buf); |
692 |
|
p = cur->before_colon + (after_colon - buf); |
693 |
|
p[-1] = '\0'; |
694 |
|
cur->after_colon = p; |
695 |
|
if (ch == '.') { |
696 |
|
/* .mime line: prepend to mime_a list */ |
697 |
|
cur->next = mime_a; |
698 |
|
mime_a = cur; |
699 |
|
} |
700 |
|
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR |
701 |
|
else { |
702 |
|
/* script interpreter line: prepend to script_i list */ |
703 |
|
cur->next = script_i; |
704 |
|
script_i = cur; |
705 |
} |
} |
|
*p = ':'; |
|
|
strcpy(p + 1, after_colon); |
|
|
} |
|
706 |
#endif |
#endif |
|
if (*p0 == 'I') { |
|
|
index_page = xstrdup(after_colon); |
|
|
continue; |
|
|
} |
|
|
|
|
|
/* Do not allow jumping around using H in subdir's configs */ |
|
|
if (flag == FIRST_PARSE && *p0 == 'H') { |
|
|
home_httpd = xstrdup(after_colon); |
|
|
xchdir(home_httpd); |
|
707 |
continue; |
continue; |
708 |
} |
} |
709 |
|
|
|
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH \ |
|
|
|| ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \ |
|
|
|| ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR |
|
|
/* storing current config line */ |
|
|
cur = xzalloc(sizeof(Htaccess) + strlen(p0)); |
|
|
strcpy(cur->before_colon, p0); |
|
710 |
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH |
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH |
711 |
if (*p0 == '/') /* was malloced - see above */ |
if (ch == '/') { /* "/file:user:pass" */ |
712 |
free(p0); |
char *p; |
713 |
#endif |
Htaccess *cur; |
714 |
cur->after_colon = strchr(cur->before_colon, ':'); |
unsigned file_len; |
715 |
*cur->after_colon++ = '\0'; |
|
716 |
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES |
/* note: path is "" unless we are in SUBDIR parse, |
717 |
if (cur->before_colon[0] == '.') { |
* otherwise it does NOT start with "/" */ |
718 |
/* .mime line: prepend to mime_a list */ |
cur = xzalloc(sizeof(*cur) /* includes space for NUL */ |
719 |
cur->next = mime_a; |
+ 1 + strlen(path) |
720 |
mime_a = cur; |
+ strlen_buf |
721 |
continue; |
); |
722 |
} |
/* form "/path/file" */ |
723 |
#endif |
sprintf(cur->before_colon, "/%s%.*s", |
724 |
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR |
path, |
725 |
if (cur->before_colon[0] == '*' && cur->before_colon[1] == '.') { |
(int) (after_colon - buf - 1), /* includes "/", but not ":" */ |
726 |
/* script interpreter line: prepend to script_i list */ |
buf); |
727 |
cur->next = script_i; |
/* canonicalize it */ |
728 |
script_i = cur; |
p = bb_simplify_abs_path_inplace(cur->before_colon); |
729 |
continue; |
file_len = p - cur->before_colon; |
730 |
} |
/* add "user:pass" after NUL */ |
731 |
#endif |
strcpy(++p, after_colon); |
732 |
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH |
cur->after_colon = p; |
733 |
//TODO: we do not test for leading "/"?? |
|
734 |
//also, do we leak cur if BASIC_AUTH is off? |
/* insert cur into g_auth */ |
735 |
if (prev == NULL) { |
/* g_auth is sorted by decreased filename length */ |
736 |
/* first line */ |
{ |
737 |
g_auth = prev = cur; |
Htaccess *auth, **authp; |
738 |
} else { |
|
739 |
/* sort path, if current length eq or bigger then move up */ |
authp = &g_auth; |
740 |
Htaccess *prev_hti = g_auth; |
while ((auth = *authp) != NULL) { |
741 |
size_t l = strlen(cur->before_colon); |
if (file_len >= strlen(auth->before_colon)) { |
742 |
Htaccess *hti; |
/* insert cur before auth */ |
743 |
|
cur->next = auth; |
744 |
for (hti = prev_hti; hti; hti = hti->next) { |
break; |
|
if (l >= strlen(hti->before_colon)) { |
|
|
/* insert before hti */ |
|
|
cur->next = hti; |
|
|
if (prev_hti != hti) { |
|
|
prev_hti->next = cur; |
|
|
} else { |
|
|
/* insert as top */ |
|
|
g_auth = cur; |
|
745 |
} |
} |
746 |
break; |
authp = &auth->next; |
747 |
} |
} |
748 |
if (prev_hti != hti) |
*authp = cur; |
|
prev_hti = prev_hti->next; |
|
|
} |
|
|
if (!hti) { /* not inserted, add to bottom */ |
|
|
prev->next = cur; |
|
|
prev = cur; |
|
749 |
} |
} |
750 |
|
continue; |
751 |
} |
} |
752 |
#endif /* BASIC_AUTH */ |
#endif /* BASIC_AUTH */ |
753 |
#endif /* BASIC_AUTH || MIME_TYPES || SCRIPT_INTERPR */ |
|
754 |
|
/* the line is not recognized */ |
755 |
|
config_error: |
756 |
|
bb_error_msg("config error '%s' in '%s'", buf, filename); |
757 |
} /* while (fgets) */ |
} /* while (fgets) */ |
758 |
|
|
759 |
fclose(f); |
fclose(f); |
760 |
} |
} |
761 |
|
|
961 |
const char *error_page = NULL; |
const char *error_page = NULL; |
962 |
#endif |
#endif |
963 |
unsigned i; |
unsigned i; |
964 |
time_t timer = time(0); |
time_t timer = time(NULL); |
965 |
char tmp_str[80]; |
char tmp_str[80]; |
966 |
int len; |
int len; |
967 |
|
|
1021 |
strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&last_mod)); |
strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&last_mod)); |
1022 |
#if ENABLE_FEATURE_HTTPD_RANGES |
#if ENABLE_FEATURE_HTTPD_RANGES |
1023 |
if (responseNum == HTTP_PARTIAL_CONTENT) { |
if (responseNum == HTTP_PARTIAL_CONTENT) { |
1024 |
len += sprintf(iobuf + len, "Content-Range: bytes %"OFF_FMT"d-%"OFF_FMT"d/%"OFF_FMT"d\r\n", |
len += sprintf(iobuf + len, "Content-Range: bytes %"OFF_FMT"u-%"OFF_FMT"u/%"OFF_FMT"u\r\n", |
1025 |
range_start, |
range_start, |
1026 |
range_end, |
range_end, |
1027 |
file_size); |
file_size); |
1032 |
#if ENABLE_FEATURE_HTTPD_RANGES |
#if ENABLE_FEATURE_HTTPD_RANGES |
1033 |
"Accept-Ranges: bytes\r\n" |
"Accept-Ranges: bytes\r\n" |
1034 |
#endif |
#endif |
1035 |
"Last-Modified: %s\r\n%s %"OFF_FMT"d\r\n", |
"Last-Modified: %s\r\n%s %"OFF_FMT"u\r\n", |
1036 |
tmp_str, |
tmp_str, |
1037 |
"Content-length:", |
"Content-length:", |
1038 |
file_size |
file_size |
1075 |
int count = 0; |
int count = 0; |
1076 |
char c; |
char c; |
1077 |
|
|
1078 |
|
alarm(HEADER_READ_TIMEOUT); |
1079 |
while (1) { |
while (1) { |
1080 |
if (hdr_cnt <= 0) { |
if (hdr_cnt <= 0) { |
1081 |
hdr_cnt = safe_read(STDIN_FILENO, hdr_buf, sizeof(hdr_buf)); |
hdr_cnt = safe_read(STDIN_FILENO, hdr_buf, sizeof(hdr_buf)); |
1090 |
continue; |
continue; |
1091 |
if (c == '\n') { |
if (c == '\n') { |
1092 |
iobuf[count] = '\0'; |
iobuf[count] = '\0'; |
1093 |
return count; |
break; |
1094 |
} |
} |
1095 |
if (count < (IOBUF_SIZE - 1)) /* check overflow */ |
if (count < (IOBUF_SIZE - 1)) /* check overflow */ |
1096 |
count++; |
count++; |
1325 |
/* Check for [dirs/]script.cgi/PATH_INFO */ |
/* Check for [dirs/]script.cgi/PATH_INFO */ |
1326 |
script = (char*)url; |
script = (char*)url; |
1327 |
while ((script = strchr(script + 1, '/')) != NULL) { |
while ((script = strchr(script + 1, '/')) != NULL) { |
|
struct stat sb; |
|
|
|
|
1328 |
*script = '\0'; |
*script = '\0'; |
1329 |
if (!is_directory(url + 1, 1, &sb)) { |
if (!is_directory(url + 1, 1, NULL)) { |
1330 |
/* not directory, found script.cgi/PATH_INFO */ |
/* not directory, found script.cgi/PATH_INFO */ |
1331 |
*script = '/'; |
*script = '/'; |
1332 |
break; |
break; |
1399 |
#endif |
#endif |
1400 |
if (referer) |
if (referer) |
1401 |
setenv1("HTTP_REFERER", referer); |
setenv1("HTTP_REFERER", referer); |
1402 |
|
setenv1("HTTP_HOST", host); /* set to "" if NULL */ |
1403 |
|
/* setenv1("SERVER_NAME", safe_gethostname()); - don't do this, |
1404 |
|
* just run "env SERVER_NAME=xyz httpd ..." instead */ |
1405 |
|
|
1406 |
xpiped_pair(fromCgi); |
xpiped_pair(fromCgi); |
1407 |
xpiped_pair(toCgi); |
xpiped_pair(toCgi); |
1500 |
* const char *url The requested URL (with leading /). |
* const char *url The requested URL (with leading /). |
1501 |
* what What to send (headers/body/both). |
* what What to send (headers/body/both). |
1502 |
*/ |
*/ |
1503 |
static void send_file_and_exit(const char *url, int what) |
static NOINLINE void send_file_and_exit(const char *url, int what) |
1504 |
{ |
{ |
|
static const char *const suffixTable[] = { |
|
|
/* Warning: shorter equivalent suffix in one line must be first */ |
|
|
".htm.html", "text/html", |
|
|
".jpg.jpeg", "image/jpeg", |
|
|
".gif", "image/gif", |
|
|
".png", "image/png", |
|
|
".txt.h.c.cc.cpp", "text/plain", |
|
|
".css", "text/css", |
|
|
".wav", "audio/wav", |
|
|
".avi", "video/x-msvideo", |
|
|
".qt.mov", "video/quicktime", |
|
|
".mpe.mpeg", "video/mpeg", |
|
|
".mid.midi", "audio/midi", |
|
|
".mp3", "audio/mpeg", |
|
|
#if 0 /* unpopular */ |
|
|
".au", "audio/basic", |
|
|
".pac", "application/x-ns-proxy-autoconfig", |
|
|
".vrml.wrl", "model/vrml", |
|
|
#endif |
|
|
NULL |
|
|
}; |
|
|
|
|
1505 |
char *suffix; |
char *suffix; |
1506 |
int f; |
int fd; |
|
const char *const *table; |
|
|
const char *try_suffix; |
|
1507 |
ssize_t count; |
ssize_t count; |
|
#if ENABLE_FEATURE_HTTPD_USE_SENDFILE |
|
|
off_t offset; |
|
|
#endif |
|
1508 |
|
|
1509 |
|
fd = open(url, O_RDONLY); |
1510 |
|
if (fd < 0) { |
1511 |
|
if (DEBUG) |
1512 |
|
bb_perror_msg("can't open '%s'", url); |
1513 |
|
/* Error pages are sent by using send_file_and_exit(SEND_BODY). |
1514 |
|
* IOW: it is unsafe to call send_headers_and_exit |
1515 |
|
* if what is SEND_BODY! Can recurse! */ |
1516 |
|
if (what != SEND_BODY) |
1517 |
|
send_headers_and_exit(HTTP_NOT_FOUND); |
1518 |
|
log_and_exit(); |
1519 |
|
} |
1520 |
/* If you want to know about EPIPE below |
/* If you want to know about EPIPE below |
1521 |
* (happens if you abort downloads from local httpd): */ |
* (happens if you abort downloads from local httpd): */ |
1522 |
signal(SIGPIPE, SIG_IGN); |
signal(SIGPIPE, SIG_IGN); |
1523 |
|
|
1524 |
suffix = strrchr(url, '.'); |
/* If not found, default is "application/octet-stream" */ |
|
|
|
|
/* If not found, set default as "application/octet-stream"; */ |
|
1525 |
found_mime_type = "application/octet-stream"; |
found_mime_type = "application/octet-stream"; |
1526 |
|
suffix = strrchr(url, '.'); |
1527 |
if (suffix) { |
if (suffix) { |
1528 |
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES |
static const char suffixTable[] ALIGN1 = |
1529 |
Htaccess *cur; |
/* Shorter suffix must be first: |
1530 |
|
* ".html.htm" will fail for ".htm" |
1531 |
|
*/ |
1532 |
|
".txt.h.c.cc.cpp\0" "text/plain\0" |
1533 |
|
/* .htm line must be after .h line */ |
1534 |
|
".htm.html\0" "text/html\0" |
1535 |
|
".jpg.jpeg\0" "image/jpeg\0" |
1536 |
|
".gif\0" "image/gif\0" |
1537 |
|
".png\0" "image/png\0" |
1538 |
|
/* .css line must be after .c line */ |
1539 |
|
".css\0" "text/css\0" |
1540 |
|
".wav\0" "audio/wav\0" |
1541 |
|
".avi\0" "video/x-msvideo\0" |
1542 |
|
".qt.mov\0" "video/quicktime\0" |
1543 |
|
".mpe.mpeg\0" "video/mpeg\0" |
1544 |
|
".mid.midi\0" "audio/midi\0" |
1545 |
|
".mp3\0" "audio/mpeg\0" |
1546 |
|
#if 0 /* unpopular */ |
1547 |
|
".au\0" "audio/basic\0" |
1548 |
|
".pac\0" "application/x-ns-proxy-autoconfig\0" |
1549 |
|
".vrml.wrl\0" "model/vrml\0" |
1550 |
#endif |
#endif |
1551 |
for (table = suffixTable; *table; table += 2) { |
/* compiler adds another "\0" here */ |
1552 |
try_suffix = strstr(table[0], suffix); |
; |
1553 |
if (try_suffix) { |
Htaccess *cur; |
1554 |
try_suffix += strlen(suffix); |
|
1555 |
if (*try_suffix == '\0' || *try_suffix == '.') { |
/* Examine built-in table */ |
1556 |
found_mime_type = table[1]; |
const char *table = suffixTable; |
1557 |
break; |
const char *table_next; |
1558 |
} |
for (; *table; table = table_next) { |
1559 |
|
const char *try_suffix; |
1560 |
|
const char *mime_type; |
1561 |
|
mime_type = table + strlen(table) + 1; |
1562 |
|
table_next = mime_type + strlen(mime_type) + 1; |
1563 |
|
try_suffix = strstr(table, suffix); |
1564 |
|
if (!try_suffix) |
1565 |
|
continue; |
1566 |
|
try_suffix += strlen(suffix); |
1567 |
|
if (*try_suffix == '\0' || *try_suffix == '.') { |
1568 |
|
found_mime_type = mime_type; |
1569 |
|
break; |
1570 |
} |
} |
1571 |
|
/* Example: strstr(table, ".av") != NULL, but it |
1572 |
|
* does not match ".avi" after all and we end up here. |
1573 |
|
* The table is arranged so that in this case we know |
1574 |
|
* that it can't match anything in the following lines, |
1575 |
|
* and we stop the search: */ |
1576 |
|
break; |
1577 |
} |
} |
1578 |
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES |
/* ...then user's table */ |
1579 |
for (cur = mime_a; cur; cur = cur->next) { |
for (cur = mime_a; cur; cur = cur->next) { |
1580 |
if (strcmp(cur->before_colon, suffix) == 0) { |
if (strcmp(cur->before_colon, suffix) == 0) { |
1581 |
found_mime_type = cur->after_colon; |
found_mime_type = cur->after_colon; |
1582 |
break; |
break; |
1583 |
} |
} |
1584 |
} |
} |
|
#endif |
|
1585 |
} |
} |
1586 |
|
|
1587 |
if (DEBUG) |
if (DEBUG) |
1588 |
bb_error_msg("sending file '%s' content-type: %s", |
bb_error_msg("sending file '%s' content-type: %s", |
1589 |
url, found_mime_type); |
url, found_mime_type); |
1590 |
|
|
|
f = open(url, O_RDONLY); |
|
|
if (f < 0) { |
|
|
if (DEBUG) |
|
|
bb_perror_msg("can't open '%s'", url); |
|
|
/* Error pages are sent by using send_file_and_exit(SEND_BODY). |
|
|
* IOW: it is unsafe to call send_headers_and_exit |
|
|
* if what is SEND_BODY! Can recurse! */ |
|
|
if (what != SEND_BODY) |
|
|
send_headers_and_exit(HTTP_NOT_FOUND); |
|
|
log_and_exit(); |
|
|
} |
|
1591 |
#if ENABLE_FEATURE_HTTPD_RANGES |
#if ENABLE_FEATURE_HTTPD_RANGES |
1592 |
if (what == SEND_BODY) |
if (what == SEND_BODY) |
1593 |
range_start = 0; /* err pages and ranges don't mix */ |
range_start = 0; /* err pages and ranges don't mix */ |
1597 |
range_end = file_size - 1; |
range_end = file_size - 1; |
1598 |
} |
} |
1599 |
if (range_end < range_start |
if (range_end < range_start |
1600 |
|| lseek(f, range_start, SEEK_SET) != range_start |
|| lseek(fd, range_start, SEEK_SET) != range_start |
1601 |
) { |
) { |
1602 |
lseek(f, 0, SEEK_SET); |
lseek(fd, 0, SEEK_SET); |
1603 |
range_start = 0; |
range_start = 0; |
1604 |
} else { |
} else { |
1605 |
range_len = range_end - range_start + 1; |
range_len = range_end - range_start + 1; |
1608 |
} |
} |
1609 |
} |
} |
1610 |
#endif |
#endif |
|
|
|
1611 |
if (what & SEND_HEADERS) |
if (what & SEND_HEADERS) |
1612 |
send_headers(HTTP_OK); |
send_headers(HTTP_OK); |
|
|
|
1613 |
#if ENABLE_FEATURE_HTTPD_USE_SENDFILE |
#if ENABLE_FEATURE_HTTPD_USE_SENDFILE |
1614 |
offset = range_start; |
{ |
1615 |
do { |
off_t offset = range_start; |
1616 |
/* sz is rounded down to 64k */ |
while (1) { |
1617 |
ssize_t sz = MAXINT(ssize_t) - 0xffff; |
/* sz is rounded down to 64k */ |
1618 |
USE_FEATURE_HTTPD_RANGES(if (sz > range_len) sz = range_len;) |
ssize_t sz = MAXINT(ssize_t) - 0xffff; |
1619 |
count = sendfile(1, f, &offset, sz); |
IF_FEATURE_HTTPD_RANGES(if (sz > range_len) sz = range_len;) |
1620 |
if (count < 0) { |
count = sendfile(STDOUT_FILENO, fd, &offset, sz); |
1621 |
if (offset == range_start) |
if (count < 0) { |
1622 |
goto fallback; |
if (offset == range_start) |
1623 |
goto fin; |
break; /* fall back to read/write loop */ |
1624 |
|
goto fin; |
1625 |
|
} |
1626 |
|
IF_FEATURE_HTTPD_RANGES(range_len -= sz;) |
1627 |
|
if (count == 0 || range_len == 0) |
1628 |
|
log_and_exit(); |
1629 |
} |
} |
1630 |
USE_FEATURE_HTTPD_RANGES(range_len -= sz;) |
} |
|
} while (count > 0 && range_len); |
|
|
log_and_exit(); |
|
|
|
|
|
fallback: |
|
1631 |
#endif |
#endif |
1632 |
while ((count = safe_read(f, iobuf, IOBUF_SIZE)) > 0) { |
while ((count = safe_read(fd, iobuf, IOBUF_SIZE)) > 0) { |
1633 |
ssize_t n; |
ssize_t n; |
1634 |
USE_FEATURE_HTTPD_RANGES(if (count > range_len) count = range_len;) |
IF_FEATURE_HTTPD_RANGES(if (count > range_len) count = range_len;) |
1635 |
n = full_write(STDOUT_FILENO, iobuf, count); |
n = full_write(STDOUT_FILENO, iobuf, count); |
1636 |
if (count != n) |
if (count != n) |
1637 |
break; |
break; |
1638 |
USE_FEATURE_HTTPD_RANGES(range_len -= count;) |
IF_FEATURE_HTTPD_RANGES(range_len -= count;) |
1639 |
if (!range_len) |
if (range_len == 0) |
1640 |
break; |
break; |
1641 |
} |
} |
1642 |
#if ENABLE_FEATURE_HTTPD_USE_SENDFILE |
if (count < 0) { |
1643 |
fin: |
IF_FEATURE_HTTPD_USE_SENDFILE(fin:) |
1644 |
#endif |
if (verbose > 1) |
1645 |
if (count < 0 && verbose > 1) |
bb_perror_msg("error"); |
1646 |
bb_perror_msg("error"); |
} |
1647 |
log_and_exit(); |
log_and_exit(); |
1648 |
} |
} |
1649 |
|
|
1771 |
/* |
/* |
1772 |
* Handle timeouts |
* Handle timeouts |
1773 |
*/ |
*/ |
1774 |
static void exit_on_signal(int sig) NORETURN; |
static void send_REQUEST_TIMEOUT_and_exit(int sig) NORETURN; |
1775 |
static void exit_on_signal(int sig UNUSED_PARAM) |
static void send_REQUEST_TIMEOUT_and_exit(int sig UNUSED_PARAM) |
1776 |
{ |
{ |
1777 |
send_headers_and_exit(HTTP_REQUEST_TIMEOUT); |
send_headers_and_exit(HTTP_REQUEST_TIMEOUT); |
1778 |
} |
} |
1837 |
bb_error_msg("connected"); |
bb_error_msg("connected"); |
1838 |
} |
} |
1839 |
|
|
1840 |
/* Install timeout handler */ |
/* Install timeout handler. get_line() needs it. */ |
1841 |
signal_no_SA_RESTART_empty_mask(SIGALRM, exit_on_signal); |
signal(SIGALRM, send_REQUEST_TIMEOUT_and_exit); |
|
alarm(HEADER_READ_TIMEOUT); |
|
1842 |
|
|
1843 |
if (!get_line()) /* EOF or error or empty line */ |
if (!get_line()) /* EOF or error or empty line */ |
1844 |
send_headers_and_exit(HTTP_BAD_REQUEST); |
send_headers_and_exit(HTTP_BAD_REQUEST); |
1868 |
|
|
1869 |
/* Find end of URL and parse HTTP version, if any */ |
/* Find end of URL and parse HTTP version, if any */ |
1870 |
http_major_version = '0'; |
http_major_version = '0'; |
1871 |
USE_FEATURE_HTTPD_PROXY(http_minor_version = '0';) |
IF_FEATURE_HTTPD_PROXY(http_minor_version = '0';) |
1872 |
tptr = strchrnul(urlp, ' '); |
tptr = strchrnul(urlp, ' '); |
1873 |
/* Is it " HTTP/"? */ |
/* Is it " HTTP/"? */ |
1874 |
if (tptr[0] && strncmp(tptr + 1, HTTP_200, 5) == 0) { |
if (tptr[0] && strncmp(tptr + 1, HTTP_200, 5) == 0) { |
1875 |
http_major_version = tptr[6]; |
http_major_version = tptr[6]; |
1876 |
USE_FEATURE_HTTPD_PROXY(http_minor_version = tptr[8];) |
IF_FEATURE_HTTPD_PROXY(http_minor_version = tptr[8];) |
1877 |
} |
} |
1878 |
*tptr = '\0'; |
*tptr = '\0'; |
1879 |
|
|
1932 |
|
|
1933 |
/* If URL is a directory, add '/' */ |
/* If URL is a directory, add '/' */ |
1934 |
if (urlp[-1] != '/') { |
if (urlp[-1] != '/') { |
1935 |
if (is_directory(urlcopy + 1, 1, &sb)) { |
if (is_directory(urlcopy + 1, 1, NULL)) { |
1936 |
found_moved_temporarily = urlcopy; |
found_moved_temporarily = urlcopy; |
1937 |
} |
} |
1938 |
} |
} |
1946 |
while (ip_allowed && (tptr = strchr(tptr + 1, '/')) != NULL) { |
while (ip_allowed && (tptr = strchr(tptr + 1, '/')) != NULL) { |
1947 |
/* have path1/path2 */ |
/* have path1/path2 */ |
1948 |
*tptr = '\0'; |
*tptr = '\0'; |
1949 |
if (is_directory(urlcopy + 1, 1, &sb)) { |
if (is_directory(urlcopy + 1, 1, NULL)) { |
1950 |
/* may have subdir config */ |
/* may have subdir config */ |
1951 |
parse_conf(urlcopy + 1, SUBDIR_PARSE); |
parse_conf(urlcopy + 1, SUBDIR_PARSE); |
1952 |
ip_allowed = checkPermIP(); |
ip_allowed = checkPermIP(); |
1965 |
|
|
1966 |
/* Read until blank line for HTTP version specified, else parse immediate */ |
/* Read until blank line for HTTP version specified, else parse immediate */ |
1967 |
while (1) { |
while (1) { |
|
alarm(HEADER_READ_TIMEOUT); |
|
1968 |
if (!get_line()) |
if (!get_line()) |
1969 |
break; /* EOF or error or empty line */ |
break; /* EOF or error or empty line */ |
1970 |
if (DEBUG) |
if (DEBUG) |
2014 |
referer = xstrdup(skip_whitespace(iobuf + sizeof("Referer:")-1)); |
referer = xstrdup(skip_whitespace(iobuf + sizeof("Referer:")-1)); |
2015 |
} else if (STRNCASECMP(iobuf, "User-Agent:") == 0) { |
} else if (STRNCASECMP(iobuf, "User-Agent:") == 0) { |
2016 |
user_agent = xstrdup(skip_whitespace(iobuf + sizeof("User-Agent:")-1)); |
user_agent = xstrdup(skip_whitespace(iobuf + sizeof("User-Agent:")-1)); |
2017 |
|
} else if (STRNCASECMP(iobuf, "Host:") == 0) { |
2018 |
|
host = xstrdup(skip_whitespace(iobuf + sizeof("Host:")-1)); |
2019 |
} else if (STRNCASECMP(iobuf, "Accept:") == 0) { |
} else if (STRNCASECMP(iobuf, "Accept:") == 0) { |
2020 |
http_accept = xstrdup(skip_whitespace(iobuf + sizeof("Accept:")-1)); |
http_accept = xstrdup(skip_whitespace(iobuf + sizeof("Accept:")-1)); |
2021 |
} else if (STRNCASECMP(iobuf, "Accept-Language:") == 0) { |
} else if (STRNCASECMP(iobuf, "Accept-Language:") == 0) { |
2060 |
/* We are done reading headers, disable peer timeout */ |
/* We are done reading headers, disable peer timeout */ |
2061 |
alarm(0); |
alarm(0); |
2062 |
|
|
2063 |
if (strcmp(bb_basename(urlcopy), httpd_conf) == 0 || !ip_allowed) { |
if (strcmp(bb_basename(urlcopy), HTTPD_CONF) == 0 || !ip_allowed) { |
2064 |
/* protect listing [/path]/httpd_conf or IP deny */ |
/* protect listing [/path]/httpd.conf or IP deny */ |
2065 |
send_headers_and_exit(HTTP_FORBIDDEN); |
send_headers_and_exit(HTTP_FORBIDDEN); |
2066 |
} |
} |
2067 |
|
|
2103 |
header_ptr += 2; |
header_ptr += 2; |
2104 |
write(proxy_fd, header_buf, header_ptr - header_buf); |
write(proxy_fd, header_buf, header_ptr - header_buf); |
2105 |
free(header_buf); /* on the order of 8k, free it */ |
free(header_buf); /* on the order of 8k, free it */ |
2106 |
/* cgi_io_loop_and_exit needs to have two disctinct fds */ |
/* cgi_io_loop_and_exit needs to have two distinct fds */ |
2107 |
cgi_io_loop_and_exit(proxy_fd, dup(proxy_fd), length); |
cgi_io_loop_and_exit(proxy_fd, dup(proxy_fd), length); |
2108 |
} |
} |
2109 |
#endif |
#endif |
2118 |
} |
} |
2119 |
send_cgi_and_exit(urlcopy, prequest, length, cookie, content_type); |
send_cgi_and_exit(urlcopy, prequest, length, cookie, content_type); |
2120 |
} |
} |
2121 |
|
#endif |
2122 |
|
|
2123 |
|
if (urlp[-1] == '/') |
2124 |
|
strcpy(urlp, index_page); |
2125 |
|
if (stat(tptr, &sb) == 0) { |
2126 |
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR |
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR |
|
{ |
|
2127 |
char *suffix = strrchr(tptr, '.'); |
char *suffix = strrchr(tptr, '.'); |
2128 |
if (suffix) { |
if (suffix) { |
2129 |
Htaccess *cur; |
Htaccess *cur; |
2133 |
} |
} |
2134 |
} |
} |
2135 |
} |
} |
|
} |
|
2136 |
#endif |
#endif |
|
if (prequest != request_GET && prequest != request_HEAD) { |
|
|
send_headers_and_exit(HTTP_NOT_IMPLEMENTED); |
|
|
} |
|
|
#endif /* FEATURE_HTTPD_CGI */ |
|
|
|
|
|
if (urlp[-1] == '/') |
|
|
strcpy(urlp, index_page); |
|
|
if (stat(tptr, &sb) == 0) { |
|
2137 |
file_size = sb.st_size; |
file_size = sb.st_size; |
2138 |
last_mod = sb.st_mtime; |
last_mod = sb.st_mtime; |
2139 |
} |
} |
2147 |
send_cgi_and_exit("/cgi-bin/index.cgi", prequest, length, cookie, content_type); |
send_cgi_and_exit("/cgi-bin/index.cgi", prequest, length, cookie, content_type); |
2148 |
} |
} |
2149 |
} |
} |
2150 |
#endif |
/* else fall through to send_file, it errors out if open fails: */ |
|
/* else { |
|
|
* fall through to send_file, it errors out if open fails |
|
|
* } |
|
|
*/ |
|
2151 |
|
|
2152 |
|
if (prequest != request_GET && prequest != request_HEAD) { |
2153 |
|
/* POST for files does not make sense */ |
2154 |
|
send_headers_and_exit(HTTP_NOT_IMPLEMENTED); |
2155 |
|
} |
2156 |
send_file_and_exit(tptr, |
send_file_and_exit(tptr, |
|
#if ENABLE_FEATURE_HTTPD_CGI |
|
2157 |
(prequest != request_HEAD ? SEND_HEADERS_AND_BODY : SEND_HEADERS) |
(prequest != request_HEAD ? SEND_HEADERS_AND_BODY : SEND_HEADERS) |
2158 |
|
); |
2159 |
#else |
#else |
2160 |
SEND_HEADERS_AND_BODY |
send_file_and_exit(tptr, SEND_HEADERS_AND_BODY); |
2161 |
#endif |
#endif |
|
); |
|
2162 |
} |
} |
2163 |
|
|
2164 |
/* |
/* |
2191 |
|
|
2192 |
if (fork() == 0) { |
if (fork() == 0) { |
2193 |
/* child */ |
/* child */ |
|
#if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP |
|
2194 |
/* Do not reload config on HUP */ |
/* Do not reload config on HUP */ |
2195 |
signal(SIGHUP, SIG_IGN); |
signal(SIGHUP, SIG_IGN); |
|
#endif |
|
2196 |
close(server_socket); |
close(server_socket); |
2197 |
xmove_fd(n, 0); |
xmove_fd(n, 0); |
2198 |
xdup2(0, 1); |
xdup2(0, 1); |
2234 |
|
|
2235 |
if (vfork() == 0) { |
if (vfork() == 0) { |
2236 |
/* child */ |
/* child */ |
|
#if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP |
|
2237 |
/* Do not reload config on HUP */ |
/* Do not reload config on HUP */ |
2238 |
signal(SIGHUP, SIG_IGN); |
signal(SIGHUP, SIG_IGN); |
|
#endif |
|
2239 |
close(server_socket); |
close(server_socket); |
2240 |
xmove_fd(n, 0); |
xmove_fd(n, 0); |
2241 |
xdup2(0, 1); |
xdup2(0, 1); |
2266 |
handle_incoming_and_exit(&fromAddr); |
handle_incoming_and_exit(&fromAddr); |
2267 |
} |
} |
2268 |
|
|
2269 |
#if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP |
static void sighup_handler(int sig UNUSED_PARAM) |
|
static void sighup_handler(int sig) |
|
2270 |
{ |
{ |
2271 |
parse_conf(default_path_httpd_conf, sig ? SIGNALED_PARSE : FIRST_PARSE); |
parse_conf(DEFAULT_PATH_HTTPD_CONF, SIGNALED_PARSE); |
|
signal_SA_RESTART_empty_mask(SIGHUP, sighup_handler); |
|
2272 |
} |
} |
|
#endif |
|
2273 |
|
|
2274 |
enum { |
enum { |
2275 |
c_opt_config_file = 0, |
c_opt_config_file = 0, |
2276 |
d_opt_decode_url, |
d_opt_decode_url, |
2277 |
h_opt_home_httpd, |
h_opt_home_httpd, |
2278 |
USE_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,) |
IF_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,) |
2279 |
USE_FEATURE_HTTPD_BASIC_AUTH( r_opt_realm ,) |
IF_FEATURE_HTTPD_BASIC_AUTH( r_opt_realm ,) |
2280 |
USE_FEATURE_HTTPD_AUTH_MD5( m_opt_md5 ,) |
IF_FEATURE_HTTPD_AUTH_MD5( m_opt_md5 ,) |
2281 |
USE_FEATURE_HTTPD_SETUID( u_opt_setuid ,) |
IF_FEATURE_HTTPD_SETUID( u_opt_setuid ,) |
2282 |
p_opt_port , |
p_opt_port , |
2283 |
p_opt_inetd , |
p_opt_inetd , |
2284 |
p_opt_foreground, |
p_opt_foreground, |
2286 |
OPT_CONFIG_FILE = 1 << c_opt_config_file, |
OPT_CONFIG_FILE = 1 << c_opt_config_file, |
2287 |
OPT_DECODE_URL = 1 << d_opt_decode_url, |
OPT_DECODE_URL = 1 << d_opt_decode_url, |
2288 |
OPT_HOME_HTTPD = 1 << h_opt_home_httpd, |
OPT_HOME_HTTPD = 1 << h_opt_home_httpd, |
2289 |
OPT_ENCODE_URL = USE_FEATURE_HTTPD_ENCODE_URL_STR((1 << e_opt_encode_url)) + 0, |
OPT_ENCODE_URL = IF_FEATURE_HTTPD_ENCODE_URL_STR((1 << e_opt_encode_url)) + 0, |
2290 |
OPT_REALM = USE_FEATURE_HTTPD_BASIC_AUTH( (1 << r_opt_realm )) + 0, |
OPT_REALM = IF_FEATURE_HTTPD_BASIC_AUTH( (1 << r_opt_realm )) + 0, |
2291 |
OPT_MD5 = USE_FEATURE_HTTPD_AUTH_MD5( (1 << m_opt_md5 )) + 0, |
OPT_MD5 = IF_FEATURE_HTTPD_AUTH_MD5( (1 << m_opt_md5 )) + 0, |
2292 |
OPT_SETUID = USE_FEATURE_HTTPD_SETUID( (1 << u_opt_setuid )) + 0, |
OPT_SETUID = IF_FEATURE_HTTPD_SETUID( (1 << u_opt_setuid )) + 0, |
2293 |
OPT_PORT = 1 << p_opt_port, |
OPT_PORT = 1 << p_opt_port, |
2294 |
OPT_INETD = 1 << p_opt_inetd, |
OPT_INETD = 1 << p_opt_inetd, |
2295 |
OPT_FOREGROUND = 1 << p_opt_foreground, |
OPT_FOREGROUND = 1 << p_opt_foreground, |
2303 |
int server_socket = server_socket; /* for gcc */ |
int server_socket = server_socket; /* for gcc */ |
2304 |
unsigned opt; |
unsigned opt; |
2305 |
char *url_for_decode; |
char *url_for_decode; |
2306 |
USE_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;) |
IF_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;) |
2307 |
USE_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;) |
IF_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;) |
2308 |
USE_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;) |
IF_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;) |
2309 |
USE_FEATURE_HTTPD_AUTH_MD5(const char *pass;) |
IF_FEATURE_HTTPD_AUTH_MD5(const char *pass;) |
2310 |
|
|
2311 |
INIT_G(); |
INIT_G(); |
2312 |
|
|
2322 |
* If user gives relative path in -h, |
* If user gives relative path in -h, |
2323 |
* $SCRIPT_FILENAME will not be set. */ |
* $SCRIPT_FILENAME will not be set. */ |
2324 |
opt = getopt32(argv, "c:d:h:" |
opt = getopt32(argv, "c:d:h:" |
2325 |
USE_FEATURE_HTTPD_ENCODE_URL_STR("e:") |
IF_FEATURE_HTTPD_ENCODE_URL_STR("e:") |
2326 |
USE_FEATURE_HTTPD_BASIC_AUTH("r:") |
IF_FEATURE_HTTPD_BASIC_AUTH("r:") |
2327 |
USE_FEATURE_HTTPD_AUTH_MD5("m:") |
IF_FEATURE_HTTPD_AUTH_MD5("m:") |
2328 |
USE_FEATURE_HTTPD_SETUID("u:") |
IF_FEATURE_HTTPD_SETUID("u:") |
2329 |
"p:ifv", |
"p:ifv", |
2330 |
&configFile, &url_for_decode, &home_httpd |
&opt_c_configFile, &url_for_decode, &home_httpd |
2331 |
USE_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode) |
IF_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode) |
2332 |
USE_FEATURE_HTTPD_BASIC_AUTH(, &g_realm) |
IF_FEATURE_HTTPD_BASIC_AUTH(, &g_realm) |
2333 |
USE_FEATURE_HTTPD_AUTH_MD5(, &pass) |
IF_FEATURE_HTTPD_AUTH_MD5(, &pass) |
2334 |
USE_FEATURE_HTTPD_SETUID(, &s_ugid) |
IF_FEATURE_HTTPD_SETUID(, &s_ugid) |
2335 |
, &bind_addr_or_port |
, &bind_addr_or_port |
2336 |
, &verbose |
, &verbose |
2337 |
); |
); |
2347 |
#endif |
#endif |
2348 |
#if ENABLE_FEATURE_HTTPD_AUTH_MD5 |
#if ENABLE_FEATURE_HTTPD_AUTH_MD5 |
2349 |
if (opt & OPT_MD5) { |
if (opt & OPT_MD5) { |
2350 |
puts(pw_encrypt(pass, "$1$", 1)); |
char salt[sizeof("$1$XXXXXXXX")]; |
2351 |
|
salt[0] = '$'; |
2352 |
|
salt[1] = '1'; |
2353 |
|
salt[2] = '$'; |
2354 |
|
crypt_make_salt(salt + 3, 4, 0); |
2355 |
|
puts(pw_encrypt(pass, salt, 1)); |
2356 |
return 0; |
return 0; |
2357 |
} |
} |
2358 |
#endif |
#endif |
2385 |
#endif |
#endif |
2386 |
} |
} |
2387 |
|
|
2388 |
#if 0 /*was #if ENABLE_FEATURE_HTTPD_CGI*/ |
#if 0 |
2389 |
/* User can do it himself: 'env - PATH="$PATH" httpd' |
/* User can do it himself: 'env - PATH="$PATH" httpd' |
2390 |
* We don't do it because we don't want to screw users |
* We don't do it because we don't want to screw users |
2391 |
* which want to do |
* which want to do |
2403 |
} |
} |
2404 |
#endif |
#endif |
2405 |
|
|
2406 |
#if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP |
parse_conf(DEFAULT_PATH_HTTPD_CONF, FIRST_PARSE); |
2407 |
if (!(opt & OPT_INETD)) { |
if (!(opt & OPT_INETD)) |
2408 |
/* runs parse_conf() inside */ |
signal(SIGHUP, sighup_handler); |
|
sighup_handler(0); |
|
|
} else |
|
|
#endif |
|
|
{ |
|
|
parse_conf(default_path_httpd_conf, FIRST_PARSE); |
|
|
} |
|
2409 |
|
|
2410 |
xfunc_error_retval = 0; |
xfunc_error_retval = 0; |
2411 |
if (opt & OPT_INETD) |
if (opt & OPT_INETD) |