Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/networking/httpd.c
- Timestamp:
- Feb 25, 2011, 9:26:54 PM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2.2.9/mindi-busybox/networking/httpd.c
r1772 r2725 6 6 * Copyright (C) 2003-2006 Vladimir Oleynik <dzo@simtreas.ru> 7 7 * 8 * simplify patch stolen from libbb without using strdup 9 * 10 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. 8 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 11 9 * 12 10 ***************************************************************************** 13 11 * 14 12 * Typical usage: 15 * for non root user 16 * httpd -p 8080 -h $HOME/public_html 17 * or for daemon start from rc script with uid=0: 18 * httpd -u www 19 * This is equivalent if www user have uid=80 to 20 * 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 24 * server changes directory to the location of the script and executes it 13 * For non root user: 14 * httpd -p 8080 -h $HOME/public_html 15 * For daemon start from rc script with uid=0: 16 * httpd -u www 17 * which is equivalent to (assuming user www has uid 80): 18 * httpd -p 80 -u 80 -h $PWD -c /etc/httpd.conf -r "Web Server Authentication" 19 * 20 * When an url starts with "/cgi-bin/" it is assumed to be a cgi script. 21 * The server changes directory to the location of the script and executes it 25 22 * after setting QUERY_STRING and other environment variables. 26 23 * … … 28 25 * "CGI Environment Variables": http://hoohoo.ncsa.uiuc.edu/cgi/env.html 29 26 * 30 * The server can also be invoked as aurl arg decoder and html text encoder27 * The applet can also be invoked as an url arg decoder and html text encoder 31 28 * as follows: 32 * foo=`httpd -d $foo`# decode "Hello%20World" as "Hello World"33 * bar=`httpd -e "<Hello World>"` # encode as "<Hello World>"29 * foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World" 30 * bar=`httpd -e "<Hello World>"` # encode as "<Hello World>" 34 31 * 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 html32 * presentation. -d decodes an url-encoded argument while -e encodes in html 36 33 * for page display. 37 34 * 38 35 * httpd.conf has the following format: 39 36 * 37 * H:/serverroot # define the server root. It will override -h 40 38 * A:172.20. # Allow address from 172.20.0.0/16 41 39 * A:10.0.0.0/25 # Allow any address from 10.0.0.0-10.0.0.127 … … 44 42 * D:* # Deny from other IP connections 45 43 * E404:/path/e404.html # /path/e404.html is the 404 (not found) error page 44 * I:index.html # Show index.html when a directory is requested 45 * 46 * P:/url:[http://]hostname[:port]/new/path 47 * # When /urlXXXXXX is requested, reverse proxy 48 * # it to http://hostname[:port]/new/pathXXXXXX 49 * 46 50 * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/ 47 51 * /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/ 48 52 * /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/ 49 53 * .au:audio/basic # additional mime type for audio.au files 50 * *.php:/path/php # running cgi.php scripts through an interpreter 51 * 52 * A/D may be as a/d or allow/deny - first char case insensitive 53 * Deny IP rules take precedence over allow rules. 54 * 55 * 56 * The Deny/Allow IP logic: 57 * 58 * - Default is to allow all. No addresses are denied unless 59 * denied with a D: rule. 60 * - Order of Deny/Allow rules is significant 54 * *.php:/path/php # run xxx.php through an interpreter 55 * 56 * A/D may be as a/d or allow/deny - only first char matters. 57 * Deny/Allow IP logic: 58 * - Default is to allow all (Allow all (A:*) is a no-op). 61 59 * - Deny rules take precedence over allow rules. 62 * - If a deny all rule (D:*) is used it acts as a catch-all for unmatched 63 * addresses. 64 * - Specification of Allow all (A:*) is a no-op 60 * - "Deny all" rule (D:*) is applied last. 65 61 * 66 62 * Example: … … 96 92 * 97 93 */ 94 /* TODO: use TCP_CORK, parse_config() */ 98 95 99 96 #include "libbb.h" 100 97 #if ENABLE_FEATURE_HTTPD_USE_SENDFILE 101 #include <sys/sendfile.h> 102 #endif 103 104 //#define DEBUG 1 105 #define DEBUG 0 106 98 # include <sys/sendfile.h> 99 #endif 107 100 /* amount of buffering in a pipe */ 108 101 #ifndef PIPE_BUF … … 110 103 #endif 111 104 112 #define IOBUF_SIZE 8192 /* IO buffer */ 105 #define DEBUG 0 106 107 #define IOBUF_SIZE 8192 108 #if PIPE_BUF >= IOBUF_SIZE 109 # error "PIPE_BUF >= IOBUF_SIZE" 110 #endif 113 111 114 112 #define HEADER_READ_TIMEOUT 60 115 113 116 static const char default_path_httpd_conf[] ALIGN1 = "/etc";117 static const char httpd_conf[] ALIGN1 = "httpd.conf";114 static const char DEFAULT_PATH_HTTPD_CONF[] ALIGN1 = "/etc"; 115 static const char HTTPD_CONF[] ALIGN1 = "httpd.conf"; 118 116 static const char HTTP_200[] ALIGN1 = "HTTP/1.0 200 OK\r\n"; 117 static const char index_html[] ALIGN1 = "index.html"; 119 118 120 119 typedef struct has_next_ptr { … … 137 136 } Htaccess_IP; 138 137 138 /* Must have "next" as a first member */ 139 typedef struct Htaccess_Proxy { 140 struct Htaccess_Proxy *next; 141 char *url_from; 142 char *host_port; 143 char *url_to; 144 } Htaccess_Proxy; 145 139 146 enum { 140 147 HTTP_OK = 200, 148 HTTP_PARTIAL_CONTENT = 206, 141 149 HTTP_MOVED_TEMPORARILY = 302, 142 150 HTTP_BAD_REQUEST = 400, /* malformed syntax */ … … 160 168 HTTP_BAD_GATEWAY = 502, 161 169 HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */ 162 HTTP_RESPONSE_SETSIZE = 0xffffffff163 170 #endif 164 171 }; … … 166 173 static const uint16_t http_response_type[] ALIGN2 = { 167 174 HTTP_OK, 175 #if ENABLE_FEATURE_HTTPD_RANGES 176 HTTP_PARTIAL_CONTENT, 177 #endif 168 178 HTTP_MOVED_TEMPORARILY, 169 179 HTTP_REQUEST_TIMEOUT, 170 180 HTTP_NOT_IMPLEMENTED, 171 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 181 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 172 182 HTTP_UNAUTHORIZED, 173 183 #endif … … 193 203 } http_response[ARRAY_SIZE(http_response_type)] = { 194 204 { "OK", NULL }, 195 { "Found", "Directories must end with a slash" }, /* ?? */ 205 #if ENABLE_FEATURE_HTTPD_RANGES 206 { "Partial Content", NULL }, 207 #endif 208 { "Found", NULL }, 196 209 { "Request Timeout", "No request appeared within 60 seconds" }, 197 210 { "Not Implemented", "The requested method is not recognized" }, … … 219 232 smallint flg_deny_all; 220 233 221 unsigned rmt_ip; 234 unsigned rmt_ip; /* used for IP-based allow/deny rules */ 222 235 time_t last_mod; 223 off_t ContentLength; /* -1 - unknown */224 236 char *rmt_ip_str; /* for $REMOTE_ADDR and $REMOTE_PORT */ 225 237 const char *bind_addr_or_port; 226 238 227 239 const char *g_query; 228 const char * configFile;240 const char *opt_c_configFile; 229 241 const char *home_httpd; 242 const char *index_page; 230 243 231 244 const char *found_mime_type; … … 233 246 Htaccess_IP *ip_a_d; /* config allow/deny lines */ 234 247 235 USE_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;) 236 USE_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;) 237 USE_FEATURE_HTTPD_CGI(char *referer;) 238 USE_FEATURE_HTTPD_CGI(char *user_agent;) 248 IF_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;) 249 IF_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;) 250 IF_FEATURE_HTTPD_CGI(char *referer;) 251 IF_FEATURE_HTTPD_CGI(char *user_agent;) 252 IF_FEATURE_HTTPD_CGI(char *host;) 253 IF_FEATURE_HTTPD_CGI(char *http_accept;) 254 IF_FEATURE_HTTPD_CGI(char *http_accept_language;) 255 256 off_t file_size; /* -1 - unknown */ 257 #if ENABLE_FEATURE_HTTPD_RANGES 258 off_t range_start; 259 off_t range_end; 260 off_t range_len; 261 #endif 239 262 240 263 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 241 264 Htaccess *g_auth; /* config user:password lines */ 242 265 #endif 243 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES244 266 Htaccess *mime_a; /* config mime types */ 245 #endif246 267 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 247 268 Htaccess *script_i; /* config script interpreters */ 248 269 #endif 249 char *iobuf; 270 char *iobuf; /* [IOBUF_SIZE] */ 250 271 #define hdr_buf bb_common_bufsiz1 251 272 char *hdr_ptr; … … 253 274 #if ENABLE_FEATURE_HTTPD_ERROR_PAGES 254 275 const char *http_error_page[ARRAY_SIZE(http_response_type)]; 276 #endif 277 #if ENABLE_FEATURE_HTTPD_PROXY 278 Htaccess_Proxy *proxy; 279 #endif 280 #if ENABLE_FEATURE_HTTPD_GZIP 281 /* client can handle gzip / we are going to send gzip */ 282 smallint content_gzip; 255 283 #endif 256 284 }; … … 261 289 #define bind_addr_or_port (G.bind_addr_or_port) 262 290 #define g_query (G.g_query ) 263 #define configFile (G.configFile)291 #define opt_c_configFile (G.opt_c_configFile ) 264 292 #define home_httpd (G.home_httpd ) 293 #define index_page (G.index_page ) 265 294 #define found_mime_type (G.found_mime_type ) 266 295 #define found_moved_temporarily (G.found_moved_temporarily) 267 #define ContentLength (G.ContentLength )268 296 #define last_mod (G.last_mod ) 269 297 #define ip_a_d (G.ip_a_d ) … … 272 300 #define referer (G.referer ) 273 301 #define user_agent (G.user_agent ) 302 #define host (G.host ) 303 #define http_accept (G.http_accept ) 304 #define http_accept_language (G.http_accept_language) 305 #define file_size (G.file_size ) 306 #if ENABLE_FEATURE_HTTPD_RANGES 307 #define range_start (G.range_start ) 308 #define range_end (G.range_end ) 309 #define range_len (G.range_len ) 310 #else 311 enum { 312 range_start = 0, 313 range_end = MAXINT(off_t) - 1, 314 range_len = MAXINT(off_t), 315 }; 316 #endif 274 317 #define rmt_ip_str (G.rmt_ip_str ) 275 318 #define g_auth (G.g_auth ) … … 280 323 #define hdr_cnt (G.hdr_cnt ) 281 324 #define http_error_page (G.http_error_page ) 325 #define proxy (G.proxy ) 326 #if ENABLE_FEATURE_HTTPD_GZIP 327 # define content_gzip (G.content_gzip ) 328 #else 329 # define content_gzip 0 330 #endif 282 331 #define INIT_G() do { \ 283 PTR_TO_GLOBALS = xzalloc(sizeof(G)); \284 USE_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \332 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 333 IF_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \ 285 334 bind_addr_or_port = "80"; \ 286 ContentLength = -1; \ 335 index_page = index_html; \ 336 file_size = -1; \ 287 337 } while (0) 288 338 … … 291 341 292 342 /* Prototypes */ 293 static void send_file_and_exit(const char *url, int headers) ATTRIBUTE_NORETURN; 343 enum { 344 SEND_HEADERS = (1 << 0), 345 SEND_BODY = (1 << 1), 346 SEND_HEADERS_AND_BODY = SEND_HEADERS + SEND_BODY, 347 }; 348 static void send_file_and_exit(const char *url, int what) NORETURN; 294 349 295 350 static void free_llist(has_next_ptr **pptr) … … 304 359 } 305 360 306 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH \307 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \308 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR309 361 static ALWAYS_INLINE void free_Htaccess_list(Htaccess **pptr) 310 362 { 311 363 free_llist((has_next_ptr**)pptr); 312 364 } 313 #endif314 365 315 366 static ALWAYS_INLINE void free_Htaccess_IP_list(Htaccess_IP **pptr) … … 406 457 * Parse configuration file into in-memory linked list. 407 458 * 408 * The first non-white character is examined to determine if the config line409 * is one of the following:410 * .ext:mime/type # new mime type not compiled into httpd411 * [adAD]:from # ip address allow/deny, * for wildcard412 * /path:user:pass # username/password413 * Ennn:error.html # error page for status nnn414 *415 459 * Any previous IP rules are discarded. 416 460 * If the flag argument is not SUBDIR_PARSE then all /path and mime rules … … 422 466 * flag Type of the parse request. 423 467 */ 424 /* flag */ 425 #define FIRST_PARSE 0 426 #define SUBDIR_PARSE 1 427 #define SIGNALED_PARSE 2 428 #define FIND_FROM_HTTPD_ROOT 3 468 /* flag param: */ 469 enum { 470 FIRST_PARSE = 0, /* path will be "/etc" */ 471 SIGNALED_PARSE = 1, /* path will be "/etc" */ 472 SUBDIR_PARSE = 2, /* path will be derived from URL */ 473 }; 429 474 static void parse_conf(const char *path, int flag) 430 475 { 476 /* internally used extra flag state */ 477 enum { TRY_CURDIR_PARSE = 3 }; 478 431 479 FILE *f; 432 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 433 Htaccess *prev; 434 #endif 435 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH \ 436 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \ 437 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 438 Htaccess *cur; 439 #endif 440 const char *cf = configFile; 480 const char *filename; 441 481 char buf[160]; 442 char *p0 = NULL;443 char *c, *p;444 Htaccess_IP *pip;445 482 446 483 /* discard old rules */ 447 484 free_Htaccess_IP_list(&ip_a_d); 448 485 flg_deny_all = 0; 449 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH \450 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \451 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR452 486 /* retain previous auth and mime config only for subdir parse */ 453 487 if (flag != SUBDIR_PARSE) { 488 free_Htaccess_list(&mime_a); 454 489 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 455 490 free_Htaccess_list(&g_auth); 456 491 #endif 457 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES458 free_Htaccess_list(&mime_a);459 #endif460 492 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 461 493 free_Htaccess_list(&script_i); 462 494 #endif 463 495 } 464 #endif 465 466 if (flag == SUBDIR_PARSE || cf== NULL) {467 cf = alloca(strlen(path) + sizeof(httpd_conf) + 2);468 sprintf((char *) cf, "%s/%s", path, httpd_conf);469 } 470 471 while ((f = fopen (cf, "r")) == NULL) {472 if (flag == SUBDIR_PARSE || flag == FIND_FROM_HTTPD_ROOT) {496 497 filename = opt_c_configFile; 498 if (flag == SUBDIR_PARSE || filename == NULL) { 499 filename = alloca(strlen(path) + sizeof(HTTPD_CONF) + 2); 500 sprintf((char *)filename, "%s/%s", path, HTTPD_CONF); 501 } 502 503 while ((f = fopen_for_read(filename)) == NULL) { 504 if (flag >= SUBDIR_PARSE) { /* SUBDIR or TRY_CURDIR */ 473 505 /* config file not found, no changes to config */ 474 506 return; 475 507 } 476 if (configFile && flag == FIRST_PARSE) /* if -c option given */ 477 bb_perror_msg_and_die("%s", cf); 478 flag = FIND_FROM_HTTPD_ROOT; 479 cf = httpd_conf; 508 if (flag == FIRST_PARSE) { 509 /* -c CONFFILE given, but CONFFILE doesn't exist? */ 510 if (opt_c_configFile) 511 bb_simple_perror_msg_and_die(opt_c_configFile); 512 /* else: no -c, thus we looked at /etc/httpd.conf, 513 * and it's not there. try ./httpd.conf: */ 514 } 515 flag = TRY_CURDIR_PARSE; 516 filename = HTTPD_CONF; 480 517 } 481 518 482 519 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 483 prev = g_auth; 484 #endif 485 /* This could stand some work */ 486 while ((p0 = fgets(buf, sizeof(buf), f)) != NULL) { 487 c = NULL; 488 for (p = p0; *p0 != '\0' && *p0 != '#'; p0++) { 489 if (!isspace(*p0)) { 490 *p++ = *p0; 491 if (*p0 == ':' && c == NULL) 492 c = p; 493 } 494 } 495 *p = '\0'; 496 497 /* test for empty or strange line */ 498 if (c == NULL || *c == '\0') 520 /* in "/file:user:pass" lines, we prepend path in subdirs */ 521 if (flag != SUBDIR_PARSE) 522 path = ""; 523 #endif 524 /* The lines can be: 525 * 526 * I:default_index_file 527 * H:http_home 528 * [AD]:IP[/mask] # allow/deny, * for wildcard 529 * Ennn:error.html # error page for status nnn 530 * P:/url:[http://]hostname[:port]/new/path # reverse proxy 531 * .ext:mime/type # mime type 532 * *.php:/path/php # run xxx.php through an interpreter 533 * /file:user:pass # username and password 534 */ 535 while (fgets(buf, sizeof(buf), f) != NULL) { 536 unsigned strlen_buf; 537 unsigned char ch; 538 char *after_colon; 539 540 { /* remove all whitespace, and # comments */ 541 char *p, *p0; 542 543 p0 = buf; 544 /* skip non-whitespace beginning. Often the whole line 545 * is non-whitespace. We want this case to work fast, 546 * without needless copying, therefore we don't merge 547 * this operation into next while loop. */ 548 while ((ch = *p0) != '\0' && ch != '\n' && ch != '#' 549 && ch != ' ' && ch != '\t' 550 ) { 551 p0++; 552 } 553 p = p0; 554 /* if we enter this loop, we have some whitespace. 555 * discard it */ 556 while (ch != '\0' && ch != '\n' && ch != '#') { 557 if (ch != ' ' && ch != '\t') { 558 *p++ = ch; 559 } 560 ch = *++p0; 561 } 562 *p = '\0'; 563 strlen_buf = p - buf; 564 if (strlen_buf == 0) 565 continue; /* empty line */ 566 } 567 568 after_colon = strchr(buf, ':'); 569 /* strange line? */ 570 if (after_colon == NULL || *++after_colon == '\0') 571 goto config_error; 572 573 ch = (buf[0] & ~0x20); /* toupper if it's a letter */ 574 575 if (ch == 'I') { 576 if (index_page != index_html) 577 free((char*)index_page); 578 index_page = xstrdup(after_colon); 499 579 continue; 500 p0 = buf; 501 if (*p0 == 'd') 502 *p0 = 'D'; 503 if (*c == '*') { 504 if (*p0 == 'D') { 505 /* memorize deny all */ 506 flg_deny_all = 1; 507 } 508 /* skip default other "word:*" config lines */ 580 } 581 582 /* do not allow jumping around using H in subdir's configs */ 583 if (flag == FIRST_PARSE && ch == 'H') { 584 home_httpd = xstrdup(after_colon); 585 xchdir(home_httpd); 509 586 continue; 510 587 } 511 588 512 if (*p0 == 'a') 513 *p0 = 'A'; 514 if (*p0 == 'A' || *p0 == 'D') { 515 /* storing current config IP line */ 516 pip = xzalloc(sizeof(Htaccess_IP)); 517 if (pip) { 518 if (scan_ip_mask(c, &(pip->ip), &(pip->mask))) { 519 /* syntax IP{/mask} error detected, protect all */ 520 *p0 = 'D'; 521 pip->mask = 0; 589 if (ch == 'A' || ch == 'D') { 590 Htaccess_IP *pip; 591 592 if (*after_colon == '*') { 593 if (ch == 'D') { 594 /* memorize "deny all" */ 595 flg_deny_all = 1; 522 596 } 523 pip->allow_deny = *p0; 524 if (*p0 == 'D') { 525 /* Deny:from_IP move top */ 526 pip->next = ip_a_d; 597 /* skip assumed "A:*", it is a default anyway */ 598 continue; 599 } 600 /* store "allow/deny IP/mask" line */ 601 pip = xzalloc(sizeof(*pip)); 602 if (scan_ip_mask(after_colon, &pip->ip, &pip->mask)) { 603 /* IP{/mask} syntax error detected, protect all */ 604 ch = 'D'; 605 pip->mask = 0; 606 } 607 pip->allow_deny = ch; 608 if (ch == 'D') { 609 /* Deny:from_IP - prepend */ 610 pip->next = ip_a_d; 611 ip_a_d = pip; 612 } else { 613 /* A:from_IP - append (thus all D's precedes A's) */ 614 Htaccess_IP *prev_IP = ip_a_d; 615 if (prev_IP == NULL) { 527 616 ip_a_d = pip; 528 617 } else { 529 /* add to bottom A:form_IP config line */ 530 Htaccess_IP *prev_IP = ip_a_d; 531 532 if (prev_IP == NULL) { 533 ip_a_d = pip; 534 } else { 535 while (prev_IP->next) 536 prev_IP = prev_IP->next; 537 prev_IP->next = pip; 538 } 618 while (prev_IP->next) 619 prev_IP = prev_IP->next; 620 prev_IP->next = pip; 539 621 } 540 622 } … … 543 625 544 626 #if ENABLE_FEATURE_HTTPD_ERROR_PAGES 545 if (flag == FIRST_PARSE && *p0 == 'E') { 546 int i; 547 /* error status code */ 548 int status = atoi(++p0); 549 /* c already points at the character following ':' in parse loop */ 550 /* c = strchr(p0, ':'); c++; */ 627 if (flag == FIRST_PARSE && ch == 'E') { 628 unsigned i; 629 int status = atoi(buf + 1); /* error status code */ 630 551 631 if (status < HTTP_CONTINUE) { 552 bb_error_msg("config error '%s' in '%s'", buf, cf); 553 continue; 554 } 555 632 goto config_error; 633 } 556 634 /* then error page; find matching status */ 557 635 for (i = 0; i < ARRAY_SIZE(http_response_type); i++) { 558 636 if (http_response_type[i] == status) { 559 http_error_page[i] = concat_path_file((*c == '/') ? NULL : home_httpd, c); 637 /* We chdir to home_httpd, thus no need to 638 * concat_path_file(home_httpd, after_colon) 639 * here */ 640 http_error_page[i] = xstrdup(after_colon); 560 641 break; 561 642 } … … 565 646 #endif 566 647 567 #if ENABLE_FEATURE_HTTPD_ BASIC_AUTH568 if ( *p0 == '/') {569 /* make full path from httpd root / current_path / config_line_path */570 c f = (flag == SUBDIR_PARSE ? path : "");571 p0 = xmalloc(strlen(cf) + (c - buf) + 2 + strlen(c));572 c[-1] = '\0'; 573 sprintf(p0, "/%s%s", cf, buf);574 575 /* another call bb_simplify_path */576 cf = p = p0;577 578 do {579 if (*p == '/') {580 if (*cf == '/') { /* skip duplicate (or initial) slash */581 continue;582 } else if (*cf == '.') {583 if (cf[1] == '/' || cf[1] == '\0') { /* remove extra '.' */584 continue;585 } else if ((cf[1] == '.') && (cf[2] == '/' || cf[2] == '\0')) {586 ++cf;587 if (p > p0) {588 while (*--p != '/') /* omit previous dir */;589 }590 continue;591 }592 }593 }594 *++p = *cf;595 } while (*++cf);596 597 if ((p == p0) || (*p != '/')) { /* not a trailing slash */598 ++p; /* so keep last character */ 599 }600 *p = '\0';601 sprintf(p0 + strlen(p0), ":%s", c);602 } 603 #endif 604 605 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH \ 606 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES \ 607 || ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 608 /* storing current config line */609 cur = xzalloc(sizeof(Htaccess) + strlen(p0));610 if (cur) { 611 c f = strcpy(cur->before_colon, p0);612 c = strchr(cf, ':');613 *c++ = 0;614 cur->after_colon = c;615 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES 616 if ( *cf== '.') {617 /* config .mime line move top for overwrite previous*/648 #if ENABLE_FEATURE_HTTPD_PROXY 649 if (flag == FIRST_PARSE && ch == 'P') { 650 /* P:/url:[http://]hostname[:port]/new/path */ 651 char *url_from, *host_port, *url_to; 652 Htaccess_Proxy *proxy_entry; 653 654 url_from = after_colon; 655 host_port = strchr(after_colon, ':'); 656 if (host_port == NULL) { 657 goto config_error; 658 } 659 *host_port++ = '\0'; 660 if (strncmp(host_port, "http://", 7) == 0) 661 host_port += 7; 662 if (*host_port == '\0') { 663 goto config_error; 664 } 665 url_to = strchr(host_port, '/'); 666 if (url_to == NULL) { 667 goto config_error; 668 } 669 *url_to = '\0'; 670 proxy_entry = xzalloc(sizeof(*proxy_entry)); 671 proxy_entry->url_from = xstrdup(url_from); 672 proxy_entry->host_port = xstrdup(host_port); 673 *url_to = '/'; 674 proxy_entry->url_to = xstrdup(url_to); 675 proxy_entry->next = proxy; 676 proxy = proxy_entry; 677 continue; 678 } 679 #endif 680 /* the rest of directives are non-alphabetic, 681 * must avoid using "toupper'ed" ch */ 682 ch = buf[0]; 683 684 if (ch == '.' /* ".ext:mime/type" */ 685 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 686 || (ch == '*' && buf[1] == '.') /* "*.php:/path/php" */ 687 #endif 688 ) { 689 char *p; 690 Htaccess *cur; 691 692 cur = xzalloc(sizeof(*cur) /* includes space for NUL */ + strlen_buf); 693 strcpy(cur->before_colon, buf); 694 p = cur->before_colon + (after_colon - buf); 695 p[-1] = '\0'; 696 cur->after_colon = p; 697 if (ch == '.') { 698 /* .mime line: prepend to mime_a list */ 618 699 cur->next = mime_a; 619 700 mime_a = cur; 620 continue; 621 } 622 #endif 701 } 623 702 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 624 if (*cf == '*' && cf[1] == '.'){625 /* config script interpreter line move top for overwrite previous*/703 else { 704 /* script interpreter line: prepend to script_i list */ 626 705 cur->next = script_i; 627 706 script_i = cur; 628 continue; 629 } 630 #endif 707 } 708 #endif 709 continue; 710 } 711 631 712 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 632 free(p0); 633 if (prev == NULL) { 634 /* first line */ 635 g_auth = prev = cur; 636 } else { 637 /* sort path, if current lenght eq or bigger then move up */ 638 Htaccess *prev_hti = g_auth; 639 size_t l = strlen(cf); 640 Htaccess *hti; 641 642 for (hti = prev_hti; hti; hti = hti->next) { 643 if (l >= strlen(hti->before_colon)) { 644 /* insert before hti */ 645 cur->next = hti; 646 if (prev_hti != hti) { 647 prev_hti->next = cur; 648 } else { 649 /* insert as top */ 650 g_auth = cur; 651 } 713 if (ch == '/') { /* "/file:user:pass" */ 714 char *p; 715 Htaccess *cur; 716 unsigned file_len; 717 718 /* note: path is "" unless we are in SUBDIR parse, 719 * otherwise it does NOT start with "/" */ 720 cur = xzalloc(sizeof(*cur) /* includes space for NUL */ 721 + 1 + strlen(path) 722 + strlen_buf 723 ); 724 /* form "/path/file" */ 725 sprintf(cur->before_colon, "/%s%.*s", 726 path, 727 (int) (after_colon - buf - 1), /* includes "/", but not ":" */ 728 buf); 729 /* canonicalize it */ 730 p = bb_simplify_abs_path_inplace(cur->before_colon); 731 file_len = p - cur->before_colon; 732 /* add "user:pass" after NUL */ 733 strcpy(++p, after_colon); 734 cur->after_colon = p; 735 736 /* insert cur into g_auth */ 737 /* g_auth is sorted by decreased filename length */ 738 { 739 Htaccess *auth, **authp; 740 741 authp = &g_auth; 742 while ((auth = *authp) != NULL) { 743 if (file_len >= strlen(auth->before_colon)) { 744 /* insert cur before auth */ 745 cur->next = auth; 652 746 break; 653 747 } 654 if (prev_hti != hti) 655 prev_hti = prev_hti->next; 748 authp = &auth->next; 656 749 } 657 if (!hti) { /* not inserted, add to bottom */ 658 prev->next = cur; 659 prev = cur; 660 } 661 } 662 #endif 663 } 664 #endif 665 } 750 *authp = cur; 751 } 752 continue; 753 } 754 #endif /* BASIC_AUTH */ 755 756 /* the line is not recognized */ 757 config_error: 758 bb_error_msg("config error '%s' in '%s'", buf, filename); 759 } /* while (fgets) */ 760 666 761 fclose(f); 667 762 } … … 685 780 char ch; 686 781 687 while ((ch = *string++) ) {782 while ((ch = *string++) != '\0') { 688 783 /* very simple check for what to encode */ 689 784 if (isalnum(ch)) … … 695 790 return out; 696 791 } 697 #endif /* FEATURE_HTTPD_ENCODE_URL_STR */792 #endif 698 793 699 794 /* 700 795 * Given a URL encoded string, convert it to plain ascii. 701 796 * Since decoding always makes strings smaller, the decode is done in-place. 702 * Thus, callers should strdup() the argument if they do not want the797 * Thus, callers should xstrdup() the argument if they do not want the 703 798 * argument modified. The return is the original pointer, allowing this 704 799 * function to be easily used as arguments to other functions. … … 722 817 return v + 10; 723 818 return ~0; 724 }725 819 /* For testing: 726 820 void t(char c) { printf("'%c'(%u) %u\n", c, c, hex_to_bin(c)); } … … 728 822 t('0'-1); t('9'+1); t('A'-1); t('F'+1); t('a'-1); t('f'+1); return 0; } 729 823 */ 824 } 730 825 static char *decodeString(char *orig, int option_d) 731 826 { … … 821 916 static int openServer(void) 822 917 { 823 intn = bb_strtou(bind_addr_or_port, NULL, 10);918 unsigned n = bb_strtou(bind_addr_or_port, NULL, 10); 824 919 if (!errno && n && n <= 0xffff) 825 920 n = create_and_bind_stream_or_die(NULL, n); … … 833 928 * Log the connection closure and exit. 834 929 */ 835 static void log_and_exit(void) ATTRIBUTE_NORETURN;930 static void log_and_exit(void) NORETURN; 836 931 static void log_and_exit(void) 837 932 { … … 839 934 * or be confused by us just exiting without SHUT_WR. Oh well. */ 840 935 shutdown(1, SHUT_WR); 936 /* Why?? 937 (this also messes up stdin when user runs httpd -i from terminal) 841 938 ndelay_on(0); 842 while (read( 0, iobuf, IOBUF_SIZE) > 0)939 while (read(STDIN_FILENO, iobuf, IOBUF_SIZE) > 0) 843 940 continue; 941 */ 844 942 845 943 if (verbose > 2) … … 863 961 const char *mime_type; 864 962 #if ENABLE_FEATURE_HTTPD_ERROR_PAGES 865 const char *error_page = 0;963 const char *error_page = NULL; 866 964 #endif 867 965 unsigned i; 868 time_t timer = time( 0);966 time_t timer = time(NULL); 869 967 char tmp_str[80]; 870 968 int len; … … 909 1007 910 1008 #if ENABLE_FEATURE_HTTPD_ERROR_PAGES 911 if (error_page && !access(error_page, R_OK)) {1009 if (error_page && access(error_page, R_OK) == 0) { 912 1010 strcat(iobuf, "\r\n"); 913 1011 len += 2; … … 915 1013 if (DEBUG) 916 1014 fprintf(stderr, "headers: '%s'\n", iobuf); 917 full_write( 1, iobuf, len);1015 full_write(STDOUT_FILENO, iobuf, len); 918 1016 if (DEBUG) 919 1017 fprintf(stderr, "writing error page: '%s'\n", error_page); 920 return send_file_and_exit(error_page, FALSE);921 } 922 #endif 923 924 if ( ContentLength!= -1) { /* file */1018 return send_file_and_exit(error_page, SEND_BODY); 1019 } 1020 #endif 1021 1022 if (file_size != -1) { /* file */ 925 1023 strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&last_mod)); 926 len += sprintf(iobuf + len, "Last-Modified: %s\r\n%s %"OFF_FMT"d\r\n", 927 tmp_str, "Content-length:", ContentLength); 928 } 1024 #if ENABLE_FEATURE_HTTPD_RANGES 1025 if (responseNum == HTTP_PARTIAL_CONTENT) { 1026 len += sprintf(iobuf + len, "Content-Range: bytes %"OFF_FMT"u-%"OFF_FMT"u/%"OFF_FMT"u\r\n", 1027 range_start, 1028 range_end, 1029 file_size); 1030 file_size = range_end - range_start + 1; 1031 } 1032 #endif 1033 len += sprintf(iobuf + len, 1034 #if ENABLE_FEATURE_HTTPD_RANGES 1035 "Accept-Ranges: bytes\r\n" 1036 #endif 1037 "Last-Modified: %s\r\n%s %"OFF_FMT"u\r\n", 1038 tmp_str, 1039 content_gzip ? "Transfer-length:" : "Content-length:", 1040 file_size 1041 ); 1042 } 1043 1044 if (content_gzip) 1045 len += sprintf(iobuf + len, "Content-Encoding: gzip\r\n"); 1046 929 1047 iobuf[len++] = '\r'; 930 1048 iobuf[len++] = '\n'; … … 938 1056 if (DEBUG) 939 1057 fprintf(stderr, "headers: '%s'\n", iobuf); 940 if (full_write( 1, iobuf, len) != len) {1058 if (full_write(STDOUT_FILENO, iobuf, len) != len) { 941 1059 if (verbose > 1) 942 1060 bb_perror_msg("error"); … … 945 1063 } 946 1064 947 static void send_headers_and_exit(int responseNum) ATTRIBUTE_NORETURN;1065 static void send_headers_and_exit(int responseNum) NORETURN; 948 1066 static void send_headers_and_exit(int responseNum) 949 1067 { … … 964 1082 char c; 965 1083 1084 alarm(HEADER_READ_TIMEOUT); 966 1085 while (1) { 967 1086 if (hdr_cnt <= 0) { 968 hdr_cnt = safe_read( 0, hdr_buf, sizeof(hdr_buf));1087 hdr_cnt = safe_read(STDIN_FILENO, hdr_buf, sizeof(hdr_buf)); 969 1088 if (hdr_cnt <= 0) 970 1089 break; … … 978 1097 if (c == '\n') { 979 1098 iobuf[count] = '\0'; 980 return count;1099 break; 981 1100 } 982 1101 if (count < (IOBUF_SIZE - 1)) /* check overflow */ … … 986 1105 } 987 1106 1107 #if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY 1108 1109 /* gcc 4.2.1 fares better with NOINLINE */ 1110 static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post_len) NORETURN; 1111 static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post_len) 1112 { 1113 enum { FROM_CGI = 1, TO_CGI = 2 }; /* indexes in pfd[] */ 1114 struct pollfd pfd[3]; 1115 int out_cnt; /* we buffer a bit of initial CGI output */ 1116 int count; 1117 1118 /* iobuf is used for CGI -> network data, 1119 * hdr_buf is for network -> CGI data (POSTDATA) */ 1120 1121 /* If CGI dies, we still want to correctly finish reading its output 1122 * and send it to the peer. So please no SIGPIPEs! */ 1123 signal(SIGPIPE, SIG_IGN); 1124 1125 // We inconsistently handle a case when more POSTDATA from network 1126 // is coming than we expected. We may give *some part* of that 1127 // extra data to CGI. 1128 1129 //if (hdr_cnt > post_len) { 1130 // /* We got more POSTDATA from network than we expected */ 1131 // hdr_cnt = post_len; 1132 //} 1133 post_len -= hdr_cnt; 1134 /* post_len - number of POST bytes not yet read from network */ 1135 1136 /* NB: breaking out of this loop jumps to log_and_exit() */ 1137 out_cnt = 0; 1138 while (1) { 1139 memset(pfd, 0, sizeof(pfd)); 1140 1141 pfd[FROM_CGI].fd = fromCgi_rd; 1142 pfd[FROM_CGI].events = POLLIN; 1143 1144 if (toCgi_wr) { 1145 pfd[TO_CGI].fd = toCgi_wr; 1146 if (hdr_cnt > 0) { 1147 pfd[TO_CGI].events = POLLOUT; 1148 } else if (post_len > 0) { 1149 pfd[0].events = POLLIN; 1150 } else { 1151 /* post_len <= 0 && hdr_cnt <= 0: 1152 * no more POST data to CGI, 1153 * let CGI see EOF on CGI's stdin */ 1154 if (toCgi_wr != fromCgi_rd) 1155 close(toCgi_wr); 1156 toCgi_wr = 0; 1157 } 1158 } 1159 1160 /* Now wait on the set of sockets */ 1161 count = safe_poll(pfd, toCgi_wr ? TO_CGI+1 : FROM_CGI+1, -1); 1162 if (count <= 0) { 1163 #if 0 1164 if (safe_waitpid(pid, &status, WNOHANG) <= 0) { 1165 /* Weird. CGI didn't exit and no fd's 1166 * are ready, yet poll returned?! */ 1167 continue; 1168 } 1169 if (DEBUG && WIFEXITED(status)) 1170 bb_error_msg("CGI exited, status=%d", WEXITSTATUS(status)); 1171 if (DEBUG && WIFSIGNALED(status)) 1172 bb_error_msg("CGI killed, signal=%d", WTERMSIG(status)); 1173 #endif 1174 break; 1175 } 1176 1177 if (pfd[TO_CGI].revents) { 1178 /* hdr_cnt > 0 here due to the way pfd[TO_CGI].events set */ 1179 /* Have data from peer and can write to CGI */ 1180 count = safe_write(toCgi_wr, hdr_ptr, hdr_cnt); 1181 /* Doesn't happen, we dont use nonblocking IO here 1182 *if (count < 0 && errno == EAGAIN) { 1183 * ... 1184 *} else */ 1185 if (count > 0) { 1186 hdr_ptr += count; 1187 hdr_cnt -= count; 1188 } else { 1189 /* EOF/broken pipe to CGI, stop piping POST data */ 1190 hdr_cnt = post_len = 0; 1191 } 1192 } 1193 1194 if (pfd[0].revents) { 1195 /* post_len > 0 && hdr_cnt == 0 here */ 1196 /* We expect data, prev data portion is eaten by CGI 1197 * and there *is* data to read from the peer 1198 * (POSTDATA) */ 1199 //count = post_len > (int)sizeof(hdr_buf) ? (int)sizeof(hdr_buf) : post_len; 1200 //count = safe_read(STDIN_FILENO, hdr_buf, count); 1201 count = safe_read(STDIN_FILENO, hdr_buf, sizeof(hdr_buf)); 1202 if (count > 0) { 1203 hdr_cnt = count; 1204 hdr_ptr = hdr_buf; 1205 post_len -= count; 1206 } else { 1207 /* no more POST data can be read */ 1208 post_len = 0; 1209 } 1210 } 1211 1212 if (pfd[FROM_CGI].revents) { 1213 /* There is something to read from CGI */ 1214 char *rbuf = iobuf; 1215 1216 /* Are we still buffering CGI output? */ 1217 if (out_cnt >= 0) { 1218 /* HTTP_200[] has single "\r\n" at the end. 1219 * According to http://hoohoo.ncsa.uiuc.edu/cgi/out.html, 1220 * CGI scripts MUST send their own header terminated by 1221 * empty line, then data. That's why we have only one 1222 * <cr><lf> pair here. We will output "200 OK" line 1223 * if needed, but CGI still has to provide blank line 1224 * between header and body */ 1225 1226 /* Must use safe_read, not full_read, because 1227 * CGI may output a few first bytes and then wait 1228 * for POSTDATA without closing stdout. 1229 * With full_read we may wait here forever. */ 1230 count = safe_read(fromCgi_rd, rbuf + out_cnt, PIPE_BUF - 8); 1231 if (count <= 0) { 1232 /* eof (or error) and there was no "HTTP", 1233 * so write it, then write received data */ 1234 if (out_cnt) { 1235 full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1); 1236 full_write(STDOUT_FILENO, rbuf, out_cnt); 1237 } 1238 break; /* CGI stdout is closed, exiting */ 1239 } 1240 out_cnt += count; 1241 count = 0; 1242 /* "Status" header format is: "Status: 302 Redirected\r\n" */ 1243 if (out_cnt >= 8 && memcmp(rbuf, "Status: ", 8) == 0) { 1244 /* send "HTTP/1.0 " */ 1245 if (full_write(STDOUT_FILENO, HTTP_200, 9) != 9) 1246 break; 1247 rbuf += 8; /* skip "Status: " */ 1248 count = out_cnt - 8; 1249 out_cnt = -1; /* buffering off */ 1250 } else if (out_cnt >= 4) { 1251 /* Did CGI add "HTTP"? */ 1252 if (memcmp(rbuf, HTTP_200, 4) != 0) { 1253 /* there is no "HTTP", do it ourself */ 1254 if (full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1) 1255 break; 1256 } 1257 /* Commented out: 1258 if (!strstr(rbuf, "ontent-")) { 1259 full_write(s, "Content-type: text/plain\r\n\r\n", 28); 1260 } 1261 * Counter-example of valid CGI without Content-type: 1262 * echo -en "HTTP/1.0 302 Found\r\n" 1263 * echo -en "Location: http://www.busybox.net\r\n" 1264 * echo -en "\r\n" 1265 */ 1266 count = out_cnt; 1267 out_cnt = -1; /* buffering off */ 1268 } 1269 } else { 1270 count = safe_read(fromCgi_rd, rbuf, PIPE_BUF); 1271 if (count <= 0) 1272 break; /* eof (or error) */ 1273 } 1274 if (full_write(STDOUT_FILENO, rbuf, count) != count) 1275 break; 1276 if (DEBUG) 1277 fprintf(stderr, "cgi read %d bytes: '%.*s'\n", count, count, rbuf); 1278 } /* if (pfd[FROM_CGI].revents) */ 1279 } /* while (1) */ 1280 log_and_exit(); 1281 } 1282 #endif 1283 988 1284 #if ENABLE_FEATURE_HTTPD_CGI 1285 989 1286 static void setenv1(const char *name, const char *value) 990 1287 { … … 996 1293 * 997 1294 * Environment variables are set up and the script is invoked with pipes 998 * for stdin/stdout. If a postis being done the script is fed the POST1295 * for stdin/stdout. If a POST is being done the script is fed the POST 999 1296 * data in addition to setting the QUERY_STRING variable (for GETs or POSTs). 1000 1297 * 1001 1298 * Parameters: 1002 1299 * const char *url The requested URL (with leading /). 1003 * int bodyLen Length of the postbody.1300 * int post_len Length of the POST body. 1004 1301 * const char *cookie For set HTTP_COOKIE. 1005 1302 * const char *content_type For set CONTENT_TYPE. … … 1008 1305 const char *url, 1009 1306 const char *request, 1010 int bodyLen,1307 int post_len, 1011 1308 const char *cookie, 1012 const char *content_type) ATTRIBUTE_NORETURN;1309 const char *content_type) NORETURN; 1013 1310 static void send_cgi_and_exit( 1014 1311 const char *url, 1015 1312 const char *request, 1016 int bodyLen,1313 int post_len, 1017 1314 const char *cookie, 1018 1315 const char *content_type) 1019 1316 { 1020 struct { int rd; int wr; } fromCgi; /* CGI -> httpd pipe */ 1021 struct { int rd; int wr; } toCgi; /* httpd -> CGI pipe */ 1022 char *fullpath; 1317 struct fd_pair fromCgi; /* CGI -> httpd pipe */ 1318 struct fd_pair toCgi; /* httpd -> CGI pipe */ 1023 1319 char *script; 1024 char *purl; 1025 int buf_count; 1026 int status; 1027 int pid = 0; 1320 int pid; 1321 1322 /* Make a copy. NB: caller guarantees: 1323 * url[0] == '/', url[1] != '/' */ 1324 url = xstrdup(url); 1028 1325 1029 1326 /* 1030 1327 * We are mucking with environment _first_ and then vfork/exec, 1031 * this allows us to use vfork safely. Parent do n't care about1328 * this allows us to use vfork safely. Parent doesn't care about 1032 1329 * these environment changes anyway. 1033 1330 */ 1034 1331 1035 /* 1036 * Find PATH_INFO. 1037 */ 1038 purl = xstrdup(url); 1039 script = purl; 1332 /* Check for [dirs/]script.cgi/PATH_INFO */ 1333 script = (char*)url; 1040 1334 while ((script = strchr(script + 1, '/')) != NULL) { 1041 /* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */1042 struct stat sb;1043 1044 1335 *script = '\0'; 1045 if (!is_directory( purl + 1, 1, &sb)) {1336 if (!is_directory(url + 1, 1, NULL)) { 1046 1337 /* not directory, found script.cgi/PATH_INFO */ 1047 1338 *script = '/'; 1048 1339 break; 1049 1340 } 1050 *script = '/'; 1051 } 1052 setenv1("PATH_INFO", script); /* set /PATH_INFO or "" */1341 *script = '/'; /* is directory, find next '/' */ 1342 } 1343 setenv1("PATH_INFO", script); /* set to /PATH_INFO or "" */ 1053 1344 setenv1("REQUEST_METHOD", request); 1054 1345 if (g_query) { 1055 putenv(xasprintf("%s=%s?%s", "REQUEST_URI", purl, g_query));1346 putenv(xasprintf("%s=%s?%s", "REQUEST_URI", url, g_query)); 1056 1347 } else { 1057 setenv1("REQUEST_URI", purl);1348 setenv1("REQUEST_URI", url); 1058 1349 } 1059 1350 if (script != NULL) 1060 1351 *script = '\0'; /* cut off /PATH_INFO */ 1061 1352 1062 /* SCRIPT_FILENAME required by PHP in CGI mode */ 1063 fullpath = concat_path_file(home_httpd, purl); 1064 setenv1("SCRIPT_FILENAME", fullpath); 1353 /* SCRIPT_FILENAME is required by PHP in CGI mode */ 1354 if (home_httpd[0] == '/') { 1355 char *fullpath = concat_path_file(home_httpd, url); 1356 setenv1("SCRIPT_FILENAME", fullpath); 1357 } 1065 1358 /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */ 1066 setenv1("SCRIPT_NAME", purl);1359 setenv1("SCRIPT_NAME", url); 1067 1360 /* http://hoohoo.ncsa.uiuc.edu/cgi/env.html: 1068 1361 * QUERY_STRING: The information which follows the ? in the URL … … 1096 1389 } 1097 1390 setenv1("HTTP_USER_AGENT", user_agent); 1098 if (bodyLen) 1099 putenv(xasprintf("CONTENT_LENGTH=%d", bodyLen)); 1391 if (http_accept) 1392 setenv1("HTTP_ACCEPT", http_accept); 1393 if (http_accept_language) 1394 setenv1("HTTP_ACCEPT_LANGUAGE", http_accept_language); 1395 if (post_len) 1396 putenv(xasprintf("CONTENT_LENGTH=%d", post_len)); 1100 1397 if (cookie) 1101 1398 setenv1("HTTP_COOKIE", cookie); … … 1110 1407 if (referer) 1111 1408 setenv1("HTTP_REFERER", referer); 1112 1113 xpipe(&fromCgi.rd); 1114 xpipe(&toCgi.rd); 1409 setenv1("HTTP_HOST", host); /* set to "" if NULL */ 1410 /* setenv1("SERVER_NAME", safe_gethostname()); - don't do this, 1411 * just run "env SERVER_NAME=xyz httpd ..." instead */ 1412 1413 xpiped_pair(fromCgi); 1414 xpiped_pair(toCgi); 1115 1415 1116 1416 pid = vfork(); … … 1122 1422 if (!pid) { 1123 1423 /* Child process */ 1424 char *argv[3]; 1425 1124 1426 xfunc_error_retval = 242; 1125 1427 1428 /* NB: close _first_, then move fds! */ 1429 close(toCgi.wr); 1430 close(fromCgi.rd); 1126 1431 xmove_fd(toCgi.rd, 0); /* replace stdin with the pipe */ 1127 1432 xmove_fd(fromCgi.wr, 1); /* replace stdout with the pipe */ 1128 close(fromCgi.rd);1129 close(toCgi.wr);1130 1433 /* User seeing stderr output can be a security problem. 1131 1434 * If CGI really wants that, it can always do dup itself. */ 1132 1435 /* dup2(1, 2); */ 1133 1436 1134 /* script must have absolute path */ 1135 script = strrchr(fullpath, '/'); 1136 if (!script) 1137 goto error_execing_cgi; 1138 *script = '\0'; 1139 /* chdiring to script's dir */ 1140 if (chdir(fullpath) == 0) { 1141 char *argv[2]; 1437 /* Chdiring to script's dir */ 1438 script = strrchr(url, '/'); 1439 if (script != url) { /* paranoia */ 1440 *script = '\0'; 1441 if (chdir(url + 1) != 0) { 1442 bb_perror_msg("chdir(%s)", url + 1); 1443 goto error_execing_cgi; 1444 } 1445 // not needed: *script = '/'; 1446 } 1447 script++; 1448 1449 /* set argv[0] to name without path */ 1450 argv[0] = script; 1451 argv[1] = NULL; 1452 1142 1453 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 1143 char *interpr = NULL;1144 char *suffix = strrchr( purl, '.');1454 { 1455 char *suffix = strrchr(script, '.'); 1145 1456 1146 1457 if (suffix) { … … 1148 1459 for (cur = script_i; cur; cur = cur->next) { 1149 1460 if (strcmp(cur->before_colon + 1, suffix) == 0) { 1150 interpr = cur->after_colon; 1461 /* found interpreter name */ 1462 argv[0] = cur->after_colon; 1463 argv[1] = script; 1464 argv[2] = NULL; 1151 1465 break; 1152 1466 } 1153 1467 } 1154 1468 } 1155 #endif 1156 *script = '/'; 1157 /* set argv[0] to name without path */ 1158 argv[0] = (char*)bb_basename(purl); 1159 argv[1] = NULL; 1160 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 1161 if (interpr) 1162 execv(interpr, argv); 1163 else 1164 #endif 1165 execv(fullpath, argv); 1166 } 1469 } 1470 #endif 1471 /* restore default signal dispositions for CGI process */ 1472 bb_signals(0 1473 | (1 << SIGCHLD) 1474 | (1 << SIGPIPE) 1475 | (1 << SIGHUP) 1476 , SIG_DFL); 1477 1478 /* _NOT_ execvp. We do not search PATH. argv[0] is a filename 1479 * without any dir components and will only match a file 1480 * in the current directory */ 1481 execv(argv[0], argv); 1482 if (verbose) 1483 bb_perror_msg("can't execute '%s'", argv[0]); 1167 1484 error_execing_cgi: 1168 1485 /* send to stdout … … 1173 1490 /* Parent process */ 1174 1491 1175 /* First, restore variables possibly changed by child */1492 /* Restore variables possibly changed by child */ 1176 1493 xfunc_error_retval = 0; 1177 1494 1178 /* Prepare for pumping data. 1179 * iobuf is used for CGI -> network data, 1180 * hdr_buf is for network -> CGI data (POSTDATA) */ 1181 buf_count = 0; 1495 /* Pump data */ 1182 1496 close(fromCgi.wr); 1183 1497 close(toCgi.rd); 1184 1185 /* If CGI dies, we still want to correctly finish reading its output 1186 * and send it to the peer. So please no SIGPIPEs! */ 1187 signal(SIGPIPE, SIG_IGN); 1188 1189 /* Accound for POSTDATA already in hdr_buf */ 1190 bodyLen -= hdr_cnt; 1191 1192 /* This loop still looks messy. What is an exit criteria? 1193 * "CGI's output closed"? Or "CGI has exited"? 1194 * What to do if CGI has closed both input and output, but 1195 * didn't exit? etc... */ 1196 1197 /* NB: breaking out of this loop jumps to log_and_exit() */ 1198 while (1) { 1199 fd_set readSet; 1200 fd_set writeSet; 1201 int nfound; 1202 int count; 1203 1204 FD_ZERO(&readSet); 1205 FD_ZERO(&writeSet); 1206 FD_SET(fromCgi.rd, &readSet); 1207 if (bodyLen > 0 || hdr_cnt > 0) { 1208 FD_SET(toCgi.wr, &writeSet); 1209 nfound = toCgi.wr > fromCgi.rd ? toCgi.wr : fromCgi.rd; 1210 if (hdr_cnt <= 0) 1211 FD_SET(0, &readSet); 1212 /* Now wait on the set of sockets! */ 1213 nfound = select(nfound + 1, &readSet, &writeSet, NULL, NULL); 1214 } else { 1215 if (!bodyLen) { 1216 close(toCgi.wr); /* no more POST data to CGI */ 1217 bodyLen = -1; 1218 } 1219 nfound = select(fromCgi.rd + 1, &readSet, NULL, NULL, NULL); 1220 } 1221 1222 if (nfound <= 0) { 1223 if (waitpid(pid, &status, WNOHANG) <= 0) { 1224 /* Weird. CGI didn't exit and no fd's 1225 * are ready, yet select returned?! */ 1226 continue; 1227 } 1228 close(fromCgi.rd); 1229 if (DEBUG && WIFEXITED(status)) 1230 bb_error_msg("CGI exited, status=%d", WEXITSTATUS(status)); 1231 if (DEBUG && WIFSIGNALED(status)) 1232 bb_error_msg("CGI killed, signal=%d", WTERMSIG(status)); 1233 break; 1234 } 1235 1236 if (hdr_cnt > 0 && FD_ISSET(toCgi.wr, &writeSet)) { 1237 /* Have data from peer and can write to CGI */ 1238 count = safe_write(toCgi.wr, hdr_ptr, hdr_cnt); 1239 /* Doesn't happen, we dont use nonblocking IO here 1240 *if (count < 0 && errno == EAGAIN) { 1241 * ... 1242 *} else */ 1243 if (count > 0) { 1244 hdr_ptr += count; 1245 hdr_cnt -= count; 1246 } else { 1247 hdr_cnt = bodyLen = 0; /* EOF/broken pipe to CGI */ 1248 } 1249 } else if (bodyLen > 0 && hdr_cnt == 0 1250 && FD_ISSET(0, &readSet) 1251 ) { 1252 /* We expect data, prev data portion is eaten by CGI 1253 * and there *is* data to read from the peer 1254 * (POSTDATA?) */ 1255 count = bodyLen > (int)sizeof(hdr_buf) ? (int)sizeof(hdr_buf) : bodyLen; 1256 count = safe_read(0, hdr_buf, count); 1257 if (count > 0) { 1258 hdr_cnt = count; 1259 hdr_ptr = hdr_buf; 1260 bodyLen -= count; 1261 } else { 1262 bodyLen = 0; /* closed */ 1263 } 1264 } 1265 1266 #define PIPESIZE PIPE_BUF 1267 #if PIPESIZE >= IOBUF_SIZE 1268 # error "PIPESIZE >= IOBUF_SIZE" 1269 #endif 1270 if (FD_ISSET(fromCgi.rd, &readSet)) { 1271 /* There is something to read from CGI */ 1272 char *rbuf = iobuf; 1273 1274 /* Are we still buffering CGI output? */ 1275 if (buf_count >= 0) { 1276 /* HTTP_200[] has single "\r\n" at the end. 1277 * According to http://hoohoo.ncsa.uiuc.edu/cgi/out.html, 1278 * CGI scripts MUST send their own header terminated by 1279 * empty line, then data. That's why we have only one 1280 * <cr><lf> pair here. We will output "200 OK" line 1281 * if needed, but CGI still has to provide blank line 1282 * between header and body */ 1283 1284 /* Must use safe_read, not full_read, because 1285 * CGI may output a few first bytes and then wait 1286 * for POSTDATA without closing stdout. 1287 * With full_read we may wait here forever. */ 1288 count = safe_read(fromCgi.rd, rbuf + buf_count, PIPESIZE - 8); 1289 if (count <= 0) { 1290 /* eof (or error) and there was no "HTTP", 1291 * so write it, then write received data */ 1292 if (buf_count) { 1293 full_write(1, HTTP_200, sizeof(HTTP_200)-1); 1294 full_write(1, rbuf, buf_count); 1295 } 1296 break; /* CGI stdout is closed, exiting */ 1297 } 1298 buf_count += count; 1299 count = 0; 1300 /* "Status" header format is: "Status: 302 Redirected\r\n" */ 1301 if (buf_count >= 8 && memcmp(rbuf, "Status: ", 8) == 0) { 1302 /* send "HTTP/1.0 " */ 1303 if (full_write(1, HTTP_200, 9) != 9) 1304 break; 1305 rbuf += 8; /* skip "Status: " */ 1306 count = buf_count - 8; 1307 buf_count = -1; /* buffering off */ 1308 } else if (buf_count >= 4) { 1309 /* Did CGI add "HTTP"? */ 1310 if (memcmp(rbuf, HTTP_200, 4) != 0) { 1311 /* there is no "HTTP", do it ourself */ 1312 if (full_write(1, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1) 1313 break; 1314 } 1315 /* Commented out: 1316 if (!strstr(rbuf, "ontent-")) { 1317 full_write(s, "Content-type: text/plain\r\n\r\n", 28); 1318 } 1319 * Counter-example of valid CGI without Content-type: 1320 * echo -en "HTTP/1.0 302 Found\r\n" 1321 * echo -en "Location: http://www.busybox.net\r\n" 1322 * echo -en "\r\n" 1323 */ 1324 count = buf_count; 1325 buf_count = -1; /* buffering off */ 1326 } 1327 } else { 1328 count = safe_read(fromCgi.rd, rbuf, PIPESIZE); 1329 if (count <= 0) 1330 break; /* eof (or error) */ 1331 } 1332 if (full_write(1, rbuf, count) != count) 1333 break; 1334 if (DEBUG) 1335 fprintf(stderr, "cgi read %d bytes: '%.*s'\n", count, count, rbuf); 1336 } /* if (FD_ISSET(fromCgi.rd)) */ 1337 } /* while (1) */ 1338 log_and_exit(); 1339 } 1498 cgi_io_loop_and_exit(fromCgi.rd, toCgi.wr, post_len); 1499 } 1500 1340 1501 #endif /* FEATURE_HTTPD_CGI */ 1341 1502 1342 1503 /* 1343 1504 * Send a file response to a HTTP request, and exit 1344 * 1505 * 1345 1506 * Parameters: 1346 * const char *url 1347 * headers Don't send headers before if FALSE.1507 * const char *url The requested URL (with leading /). 1508 * what What to send (headers/body/both). 1348 1509 */ 1349 static void send_file_and_exit(const char *url, int headers) 1350 { 1351 static const char *const suffixTable[] = { 1352 /* Warning: shorter equivalent suffix in one line must be first */ 1353 ".htm.html", "text/html", 1354 ".jpg.jpeg", "image/jpeg", 1355 ".gif", "image/gif", 1356 ".png", "image/png", 1357 ".txt.h.c.cc.cpp", "text/plain", 1358 ".css", "text/css", 1359 ".wav", "audio/wav", 1360 ".avi", "video/x-msvideo", 1361 ".qt.mov", "video/quicktime", 1362 ".mpe.mpeg", "video/mpeg", 1363 ".mid.midi", "audio/midi", 1364 ".mp3", "audio/mpeg", 1365 #if 0 /* unpopular */ 1366 ".au", "audio/basic", 1367 ".pac", "application/x-ns-proxy-autoconfig", 1368 ".vrml.wrl", "model/vrml", 1369 #endif 1370 NULL 1371 }; 1372 1510 static NOINLINE void send_file_and_exit(const char *url, int what) 1511 { 1373 1512 char *suffix; 1374 int f; 1375 const char *const *table; 1376 const char *try_suffix; 1513 int fd; 1377 1514 ssize_t count; 1378 #if ENABLE_FEATURE_HTTPD_USE_SENDFILE 1379 off_t offset = 0; 1380 #endif 1381 1515 1516 if (content_gzip) { 1517 /* does <url>.gz exist? Then use it instead */ 1518 char *gzurl = xasprintf("%s.gz", url); 1519 fd = open(gzurl, O_RDONLY); 1520 free(gzurl); 1521 if (fd != -1) { 1522 struct stat sb; 1523 fstat(fd, &sb); 1524 file_size = sb.st_size; 1525 last_mod = sb.st_mtime; 1526 } else { 1527 IF_FEATURE_HTTPD_GZIP(content_gzip = 0;) 1528 fd = open(url, O_RDONLY); 1529 } 1530 } else { 1531 fd = open(url, O_RDONLY); 1532 } 1533 if (fd < 0) { 1534 if (DEBUG) 1535 bb_perror_msg("can't open '%s'", url); 1536 /* Error pages are sent by using send_file_and_exit(SEND_BODY). 1537 * IOW: it is unsafe to call send_headers_and_exit 1538 * if what is SEND_BODY! Can recurse! */ 1539 if (what != SEND_BODY) 1540 send_headers_and_exit(HTTP_NOT_FOUND); 1541 log_and_exit(); 1542 } 1543 /* If you want to know about EPIPE below 1544 * (happens if you abort downloads from local httpd): */ 1545 signal(SIGPIPE, SIG_IGN); 1546 1547 /* If not found, default is "application/octet-stream" */ 1548 found_mime_type = "application/octet-stream"; 1382 1549 suffix = strrchr(url, '.'); 1383 1384 /* If not found, set default as "application/octet-stream"; */1385 found_mime_type = "application/octet-stream";1386 1550 if (suffix) { 1387 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES 1551 static const char suffixTable[] ALIGN1 = 1552 /* Shorter suffix must be first: 1553 * ".html.htm" will fail for ".htm" 1554 */ 1555 ".txt.h.c.cc.cpp\0" "text/plain\0" 1556 /* .htm line must be after .h line */ 1557 ".htm.html\0" "text/html\0" 1558 ".jpg.jpeg\0" "image/jpeg\0" 1559 ".gif\0" "image/gif\0" 1560 ".png\0" "image/png\0" 1561 /* .css line must be after .c line */ 1562 ".css\0" "text/css\0" 1563 ".wav\0" "audio/wav\0" 1564 ".avi\0" "video/x-msvideo\0" 1565 ".qt.mov\0" "video/quicktime\0" 1566 ".mpe.mpeg\0" "video/mpeg\0" 1567 ".mid.midi\0" "audio/midi\0" 1568 ".mp3\0" "audio/mpeg\0" 1569 #if 0 /* unpopular */ 1570 ".au\0" "audio/basic\0" 1571 ".pac\0" "application/x-ns-proxy-autoconfig\0" 1572 ".vrml.wrl\0" "model/vrml\0" 1573 #endif 1574 /* compiler adds another "\0" here */ 1575 ; 1388 1576 Htaccess *cur; 1389 #endif 1390 for (table = suffixTable; *table; table += 2) { 1391 try_suffix = strstr(table[0], suffix); 1392 if (try_suffix) { 1393 try_suffix += strlen(suffix); 1394 if (*try_suffix == '\0' || *try_suffix == '.') { 1395 found_mime_type = table[1]; 1396 break; 1397 } 1398 } 1399 } 1400 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES 1577 1578 /* Examine built-in table */ 1579 const char *table = suffixTable; 1580 const char *table_next; 1581 for (; *table; table = table_next) { 1582 const char *try_suffix; 1583 const char *mime_type; 1584 mime_type = table + strlen(table) + 1; 1585 table_next = mime_type + strlen(mime_type) + 1; 1586 try_suffix = strstr(table, suffix); 1587 if (!try_suffix) 1588 continue; 1589 try_suffix += strlen(suffix); 1590 if (*try_suffix == '\0' || *try_suffix == '.') { 1591 found_mime_type = mime_type; 1592 break; 1593 } 1594 /* Example: strstr(table, ".av") != NULL, but it 1595 * does not match ".avi" after all and we end up here. 1596 * The table is arranged so that in this case we know 1597 * that it can't match anything in the following lines, 1598 * and we stop the search: */ 1599 break; 1600 } 1601 /* ...then user's table */ 1401 1602 for (cur = mime_a; cur; cur = cur->next) { 1402 1603 if (strcmp(cur->before_colon, suffix) == 0) { … … 1405 1606 } 1406 1607 } 1407 #endif1408 1608 } 1409 1609 … … 1412 1612 url, found_mime_type); 1413 1613 1414 f = open(url, O_RDONLY); 1415 if (f < 0) { 1416 if (DEBUG) 1417 bb_perror_msg("cannot open '%s'", url); 1418 if (headers) 1419 send_headers_and_exit(HTTP_NOT_FOUND); 1420 } 1421 1422 if (headers) 1614 #if ENABLE_FEATURE_HTTPD_RANGES 1615 if (what == SEND_BODY /* err pages and ranges don't mix */ 1616 || content_gzip /* we are sending compressed page: can't do ranges */ ///why? 1617 ) { 1618 range_start = 0; 1619 } 1620 range_len = MAXINT(off_t); 1621 if (range_start) { 1622 if (!range_end) { 1623 range_end = file_size - 1; 1624 } 1625 if (range_end < range_start 1626 || lseek(fd, range_start, SEEK_SET) != range_start 1627 ) { 1628 lseek(fd, 0, SEEK_SET); 1629 range_start = 0; 1630 } else { 1631 range_len = range_end - range_start + 1; 1632 send_headers(HTTP_PARTIAL_CONTENT); 1633 what = SEND_BODY; 1634 } 1635 } 1636 #endif 1637 if (what & SEND_HEADERS) 1423 1638 send_headers(HTTP_OK); 1424 1425 /* If you want to know about EPIPE below1426 * (happens if you abort downloads from local httpd): */1427 signal(SIGPIPE, SIG_IGN);1428 1429 1639 #if ENABLE_FEATURE_HTTPD_USE_SENDFILE 1430 do { 1431 /* byte count (3rd arg) is rounded down to 64k */ 1432 count = sendfile(1, f, &offset, MAXINT(ssize_t) - 0xffff); 1433 if (count < 0) { 1434 if (offset == 0) 1435 goto fallback; 1436 goto fin; 1437 } 1438 } while (count > 0); 1439 log_and_exit(); 1440 1441 fallback: 1442 #endif 1443 while ((count = safe_read(f, iobuf, IOBUF_SIZE)) > 0) { 1444 ssize_t n = count; 1445 count = full_write(1, iobuf, count); 1640 { 1641 off_t offset = range_start; 1642 while (1) { 1643 /* sz is rounded down to 64k */ 1644 ssize_t sz = MAXINT(ssize_t) - 0xffff; 1645 IF_FEATURE_HTTPD_RANGES(if (sz > range_len) sz = range_len;) 1646 count = sendfile(STDOUT_FILENO, fd, &offset, sz); 1647 if (count < 0) { 1648 if (offset == range_start) 1649 break; /* fall back to read/write loop */ 1650 goto fin; 1651 } 1652 IF_FEATURE_HTTPD_RANGES(range_len -= sz;) 1653 if (count == 0 || range_len == 0) 1654 log_and_exit(); 1655 } 1656 } 1657 #endif 1658 while ((count = safe_read(fd, iobuf, IOBUF_SIZE)) > 0) { 1659 ssize_t n; 1660 IF_FEATURE_HTTPD_RANGES(if (count > range_len) count = range_len;) 1661 n = full_write(STDOUT_FILENO, iobuf, count); 1446 1662 if (count != n) 1447 1663 break; 1448 } 1449 #if ENABLE_FEATURE_HTTPD_USE_SENDFILE 1450 fin: 1451 #endif 1452 if (count < 0 && verbose > 1) 1453 bb_perror_msg("error"); 1664 IF_FEATURE_HTTPD_RANGES(range_len -= count;) 1665 if (range_len == 0) 1666 break; 1667 } 1668 if (count < 0) { 1669 IF_FEATURE_HTTPD_USE_SENDFILE(fin:) 1670 if (verbose > 1) 1671 bb_perror_msg("error"); 1672 } 1454 1673 log_and_exit(); 1455 1674 } … … 1459 1678 Htaccess_IP *cur; 1460 1679 1461 /* This could stand some work */1462 1680 for (cur = ip_a_d; cur; cur = cur->next) { 1463 1681 #if DEBUG … … 1476 1694 #endif 1477 1695 if ((rmt_ip & cur->mask) == cur->ip) 1478 return cur->allow_deny == 'A'; /* Allow/Deny */ 1479 } 1480 1481 /* if unconfigured, return 1 - access from all */ 1482 return !flg_deny_all; 1696 return (cur->allow_deny == 'A'); /* A -> 1 */ 1697 } 1698 1699 return !flg_deny_all; /* depends on whether we saw "D:*" */ 1483 1700 } 1484 1701 1485 1702 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 1486 1703 /* 1487 * Check the permission file for access password protected. 1488 * 1489 * If config file isn't present, everything is allowed. 1490 * Entries are of the form you can see example from header source 1491 * 1492 * path The file path. 1493 * request User information to validate. 1494 * 1495 * Returns 1 if request is OK. 1704 * Config file entries are of the form "/<path>:<user>:<passwd>". 1705 * If config file has no prefix match for path, access is allowed. 1706 * 1707 * path The file path 1708 * user_and_passwd "user:passwd" to validate 1709 * 1710 * Returns 1 if user_and_passwd is OK. 1496 1711 */ 1497 static int check Perm(const char *path, const char *request)1712 static int check_user_passwd(const char *path, const char *user_and_passwd) 1498 1713 { 1499 1714 Htaccess *cur; 1500 const char *p;1501 const char *p0;1502 1503 1715 const char *prev = NULL; 1504 1716 1505 /* This could stand some work */1506 1717 for (cur = g_auth; cur; cur = cur->next) { 1507 size_t l; 1508 1509 p0 = cur->before_colon; 1510 if (prev != NULL && strcmp(prev, p0) != 0) 1511 continue; /* find next identical */ 1512 p = cur->after_colon; 1718 const char *dir_prefix; 1719 size_t len; 1720 1721 dir_prefix = cur->before_colon; 1722 1723 /* WHY? */ 1724 /* If already saw a match, don't accept other different matches */ 1725 if (prev && strcmp(prev, dir_prefix) != 0) 1726 continue; 1727 1513 1728 if (DEBUG) 1514 fprintf(stderr, "checkPerm: '%s' ? '%s'\n", p0, request); 1515 1516 l = strlen(p0); 1517 if (strncmp(p0, path, l) == 0 1518 && (l == 1 || path[l] == '/' || path[l] == '\0') 1729 fprintf(stderr, "checkPerm: '%s' ? '%s'\n", dir_prefix, user_and_passwd); 1730 1731 /* If it's not a prefix match, continue searching */ 1732 len = strlen(dir_prefix); 1733 if (len != 1 /* dir_prefix "/" matches all, don't need to check */ 1734 && (strncmp(dir_prefix, path, len) != 0 1735 || (path[len] != '/' && path[len] != '\0')) 1519 1736 ) { 1520 char *u; 1521 /* path match found. Check request */ 1522 /* for check next /path:user:password */ 1523 prev = p0; 1524 u = strchr(request, ':'); 1525 if (u == NULL) { 1526 /* bad request, ':' required */ 1527 break; 1528 } 1529 1530 if (ENABLE_FEATURE_HTTPD_AUTH_MD5) { 1531 char *cipher; 1532 char *pp; 1533 1534 if (strncmp(p, request, u - request) != 0) { 1535 /* user doesn't match */ 1737 continue; 1738 } 1739 1740 /* Path match found */ 1741 prev = dir_prefix; 1742 1743 if (ENABLE_FEATURE_HTTPD_AUTH_MD5) { 1744 char *md5_passwd; 1745 1746 md5_passwd = strchr(cur->after_colon, ':'); 1747 if (md5_passwd && md5_passwd[1] == '$' && md5_passwd[2] == '1' 1748 && md5_passwd[3] == '$' && md5_passwd[4] 1749 ) { 1750 char *encrypted; 1751 int r, user_len_p1; 1752 1753 md5_passwd++; 1754 user_len_p1 = md5_passwd - cur->after_colon; 1755 /* comparing "user:" */ 1756 if (strncmp(cur->after_colon, user_and_passwd, user_len_p1) != 0) { 1536 1757 continue; 1537 1758 } 1538 pp = strchr(p, ':'); 1539 if (pp && pp[1] == '$' && pp[2] == '1'1540 && pp[3] == '$' && pp[4]1541 ) {1542 pp++;1543 cipher = pw_encrypt(u+1, pp);1544 if (strcmp(cipher, pp)== 0)1545 goto set_remoteuser_var;/* Ok */1546 /* unauthorized */1547 continue;1548 1549 } 1550 1551 if (strcmp(p, request) == 0) {1759 1760 encrypted = pw_encrypt( 1761 user_and_passwd + user_len_p1 /* cleartext pwd from user */, 1762 md5_passwd /*salt */, 1 /* cleanup */); 1763 r = strcmp(encrypted, md5_passwd); 1764 free(encrypted); 1765 if (r == 0) 1766 goto set_remoteuser_var; /* Ok */ 1767 continue; 1768 } 1769 } 1770 1771 /* Comparing plaintext "user:pass" in one go */ 1772 if (strcmp(cur->after_colon, user_and_passwd) == 0) { 1552 1773 set_remoteuser_var: 1553 remoteuser = strdup(request); 1554 if (remoteuser) 1555 remoteuser[u - request] = '\0'; 1556 return 1; /* Ok */ 1557 } 1558 /* unauthorized */ 1774 remoteuser = xstrndup(user_and_passwd, 1775 strchrnul(user_and_passwd, ':') - user_and_passwd); 1776 return 1; /* Ok */ 1559 1777 } 1560 1778 } /* for */ 1561 1779 1562 return prev == NULL; 1780 /* 0(bad) if prev is set: matches were found but passwd was wrong */ 1781 return (prev == NULL); 1563 1782 } 1564 1783 #endif /* FEATURE_HTTPD_BASIC_AUTH */ 1784 1785 #if ENABLE_FEATURE_HTTPD_PROXY 1786 static Htaccess_Proxy *find_proxy_entry(const char *url) 1787 { 1788 Htaccess_Proxy *p; 1789 for (p = proxy; p; p = p->next) { 1790 if (strncmp(url, p->url_from, strlen(p->url_from)) == 0) 1791 return p; 1792 } 1793 return NULL; 1794 } 1795 #endif 1565 1796 1566 1797 /* 1567 1798 * Handle timeouts 1568 1799 */ 1569 static void exit_on_signal(int sig) ATTRIBUTE_NORETURN;1570 static void exit_on_signal(int sig)1800 static void send_REQUEST_TIMEOUT_and_exit(int sig) NORETURN; 1801 static void send_REQUEST_TIMEOUT_and_exit(int sig UNUSED_PARAM) 1571 1802 { 1572 1803 send_headers_and_exit(HTTP_REQUEST_TIMEOUT); … … 1576 1807 * Handle an incoming http request and exit. 1577 1808 */ 1578 static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) ATTRIBUTE_NORETURN;1809 static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) NORETURN; 1579 1810 static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) 1580 1811 { 1581 1812 static const char request_GET[] ALIGN1 = "GET"; 1582 1583 1813 struct stat sb; 1584 1814 char *urlcopy; 1585 1815 char *urlp; 1586 1816 char *tptr; 1587 int http_major_version;1588 int ip_allowed;1589 1817 #if ENABLE_FEATURE_HTTPD_CGI 1818 static const char request_HEAD[] ALIGN1 = "HEAD"; 1590 1819 const char *prequest; 1820 char *cookie = NULL; 1821 char *content_type = NULL; 1591 1822 unsigned long length = 0; 1592 char *cookie = 0; 1593 char *content_type = 0; 1594 #endif 1595 struct sigaction sa; 1823 #elif ENABLE_FEATURE_HTTPD_PROXY 1824 #define prequest request_GET 1825 unsigned long length = 0; 1826 #endif 1596 1827 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 1597 int credentials = -1; /* if not required this is Ok */ 1828 smallint authorized = -1; 1829 #endif 1830 smallint ip_allowed; 1831 char http_major_version; 1832 #if ENABLE_FEATURE_HTTPD_PROXY 1833 char http_minor_version; 1834 char *header_buf = header_buf; /* for gcc */ 1835 char *header_ptr = header_ptr; 1836 Htaccess_Proxy *proxy_entry; 1598 1837 #endif 1599 1838 … … 1603 1842 1604 1843 rmt_ip = 0; 1605 if (fromAddr-> sa.sa_family == AF_INET) {1606 rmt_ip = ntohl(fromAddr-> sin.sin_addr.s_addr);1844 if (fromAddr->u.sa.sa_family == AF_INET) { 1845 rmt_ip = ntohl(fromAddr->u.sin.sin_addr.s_addr); 1607 1846 } 1608 1847 #if ENABLE_FEATURE_IPV6 1609 if (fromAddr-> sa.sa_family == AF_INET61610 && fromAddr-> sin6.sin6_addr.s6_addr32[0] == 01611 && fromAddr-> sin6.sin6_addr.s6_addr32[1] == 01612 && ntohl(fromAddr-> sin6.sin6_addr.s6_addr32[2]) == 0xffff)1613 rmt_ip = ntohl(fromAddr-> sin6.sin6_addr.s6_addr32[3]);1848 if (fromAddr->u.sa.sa_family == AF_INET6 1849 && fromAddr->u.sin6.sin6_addr.s6_addr32[0] == 0 1850 && fromAddr->u.sin6.sin6_addr.s6_addr32[1] == 0 1851 && ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[2]) == 0xffff) 1852 rmt_ip = ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[3]); 1614 1853 #endif 1615 1854 if (ENABLE_FEATURE_HTTPD_CGI || DEBUG || verbose) { 1616 rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr->sa); 1855 /* NB: can be NULL (user runs httpd -i by hand?) */ 1856 rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr->u.sa); 1617 1857 } 1618 1858 if (verbose) { 1619 1859 /* this trick makes -v logging much simpler */ 1620 applet_name = rmt_ip_str; 1860 if (rmt_ip_str) 1861 applet_name = rmt_ip_str; 1621 1862 if (verbose > 2) 1622 1863 bb_error_msg("connected"); 1623 1864 } 1624 1865 1625 /* Install timeout handler */ 1626 memset(&sa, 0, sizeof(sa)); 1627 sa.sa_handler = exit_on_signal; 1628 /* sigemptyset(&sa.sa_mask); - memset should be enough */ 1629 /*sa.sa_flags = 0; - no SA_RESTART */ 1630 sigaction(SIGALRM, &sa, NULL); 1631 alarm(HEADER_READ_TIMEOUT); 1866 /* Install timeout handler. get_line() needs it. */ 1867 signal(SIGALRM, send_REQUEST_TIMEOUT_and_exit); 1632 1868 1633 1869 if (!get_line()) /* EOF or error or empty line */ … … 1642 1878 prequest = request_GET; 1643 1879 if (strcasecmp(iobuf, prequest) != 0) { 1644 prequest = "POST"; 1645 if (strcasecmp(iobuf, prequest) != 0) 1646 send_headers_and_exit(HTTP_NOT_IMPLEMENTED); 1880 prequest = request_HEAD; 1881 if (strcasecmp(iobuf, prequest) != 0) { 1882 prequest = "POST"; 1883 if (strcasecmp(iobuf, prequest) != 0) 1884 send_headers_and_exit(HTTP_NOT_IMPLEMENTED); 1885 } 1647 1886 } 1648 1887 #else … … 1655 1894 1656 1895 /* Find end of URL and parse HTTP version, if any */ 1657 http_major_version = -1; 1896 http_major_version = '0'; 1897 IF_FEATURE_HTTPD_PROXY(http_minor_version = '0';) 1658 1898 tptr = strchrnul(urlp, ' '); 1659 1899 /* Is it " HTTP/"? */ 1660 if (tptr[0] && strncmp(tptr + 1, HTTP_200, 5) == 0) 1661 http_major_version = (tptr[6] - '0'); 1900 if (tptr[0] && strncmp(tptr + 1, HTTP_200, 5) == 0) { 1901 http_major_version = tptr[6]; 1902 IF_FEATURE_HTTPD_PROXY(http_minor_version = tptr[8];) 1903 } 1662 1904 *tptr = '\0'; 1663 1905 1664 1906 /* Copy URL from after "GET "/"POST " to stack-allocated char[] */ 1665 urlcopy = alloca((tptr - urlp) + sizeof("/index.html"));1907 urlcopy = alloca((tptr - urlp) + 2 + strlen(index_page)); 1666 1908 /*if (urlcopy == NULL) 1667 1909 * send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);*/ … … 1670 1912 1671 1913 /* Extract url args if present */ 1914 g_query = NULL; 1672 1915 tptr = strchr(urlcopy, '?'); 1673 g_query = NULL;1674 1916 if (tptr) { 1675 1917 *tptr++ = '\0'; … … 1688 1930 /* Canonicalize path */ 1689 1931 /* Algorithm stolen from libbb bb_simplify_path(), 1690 * but don't strdup and reducing trailing slash and protect out root */1932 * but don't strdup, retain trailing slash, protect root */ 1691 1933 urlp = tptr = urlcopy; 1692 1934 do { … … 1697 1939 } 1698 1940 if (*tptr == '.') { 1699 /* skip extra '.'*/1941 /* skip extra "/./" */ 1700 1942 if (tptr[1] == '/' || !tptr[1]) { 1701 1943 continue; 1702 1944 } 1703 /* '..': be careful */1945 /* "..": be careful */ 1704 1946 if (tptr[1] == '.' && (tptr[2] == '/' || !tptr[2])) { 1705 1947 ++tptr; … … 1713 1955 *++urlp = *tptr; 1714 1956 } while (*++tptr); 1715 *++urlp = '\0'; /* so keep last character */ 1716 tptr = urlp; /* end ptr */ 1957 *++urlp = '\0'; /* terminate after last character */ 1717 1958 1718 1959 /* If URL is a directory, add '/' */ 1719 if ( tptr[-1] != '/') {1720 if (is_directory(urlcopy + 1, 1, &sb)) {1960 if (urlp[-1] != '/') { 1961 if (is_directory(urlcopy + 1, 1, NULL)) { 1721 1962 found_moved_temporarily = urlcopy; 1722 1963 } … … 1732 1973 /* have path1/path2 */ 1733 1974 *tptr = '\0'; 1734 if (is_directory(urlcopy + 1, 1, &sb)) {1735 /* may be havingsubdir config */1975 if (is_directory(urlcopy + 1, 1, NULL)) { 1976 /* may have subdir config */ 1736 1977 parse_conf(urlcopy + 1, SUBDIR_PARSE); 1737 1978 ip_allowed = checkPermIP(); … … 1739 1980 *tptr = '/'; 1740 1981 } 1741 if (http_major_version >= 0) { 1982 1983 #if ENABLE_FEATURE_HTTPD_PROXY 1984 proxy_entry = find_proxy_entry(urlcopy); 1985 if (proxy_entry) 1986 header_buf = header_ptr = xmalloc(IOBUF_SIZE); 1987 #endif 1988 1989 if (http_major_version >= '0') { 1742 1990 /* Request was with "... HTTP/nXXX", and n >= 0 */ 1743 1991 1744 /* Read until blank line for HTTP version specified, else parse immediate*/1992 /* Read until blank line */ 1745 1993 while (1) { 1746 alarm(HEADER_READ_TIMEOUT);1747 1994 if (!get_line()) 1748 1995 break; /* EOF or error or empty line */ … … 1750 1997 bb_error_msg("header: '%s'", iobuf); 1751 1998 1752 #if ENABLE_FEATURE_HTTPD_CGI 1753 /* try and do our best to parse more lines */ 1999 #if ENABLE_FEATURE_HTTPD_PROXY 2000 /* We need 2 more bytes for yet another "\r\n" - 2001 * see near fdprintf(proxy_fd...) further below */ 2002 if (proxy_entry && (header_ptr - header_buf) < IOBUF_SIZE - 2) { 2003 int len = strlen(iobuf); 2004 if (len > IOBUF_SIZE - (header_ptr - header_buf) - 4) 2005 len = IOBUF_SIZE - (header_ptr - header_buf) - 4; 2006 memcpy(header_ptr, iobuf, len); 2007 header_ptr += len; 2008 header_ptr[0] = '\r'; 2009 header_ptr[1] = '\n'; 2010 header_ptr += 2; 2011 } 2012 #endif 2013 2014 #if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY 2015 /* Try and do our best to parse more lines */ 1754 2016 if ((STRNCASECMP(iobuf, "Content-length:") == 0)) { 1755 2017 /* extra read only for POST */ 1756 if (prequest != request_GET) { 1757 tptr = iobuf + sizeof("Content-length:") - 1; 2018 if (prequest != request_GET 2019 # if ENABLE_FEATURE_HTTPD_CGI 2020 && prequest != request_HEAD 2021 # endif 2022 ) { 2023 tptr = skip_whitespace(iobuf + sizeof("Content-length:") - 1); 1758 2024 if (!tptr[0]) 1759 2025 send_headers_and_exit(HTTP_BAD_REQUEST); 1760 errno = 0;1761 2026 /* not using strtoul: it ignores leading minus! */ 1762 length = strtol(tptr, &tptr, 10);2027 length = bb_strtou(tptr, NULL, 10); 1763 2028 /* length is "ulong", but we need to pass it to int later */ 1764 /* so we check for negative or too large values in one go: */ 1765 /* (long -> ulong conv caused negatives to be seen as > INT_MAX) */ 1766 if (tptr[0] || errno || length > INT_MAX) 2029 if (errno || length > INT_MAX) 1767 2030 send_headers_and_exit(HTTP_BAD_REQUEST); 1768 2031 } 1769 } else if (STRNCASECMP(iobuf, "Cookie:") == 0) { 1770 cookie = strdup(skip_whitespace(iobuf + sizeof("Cookie:")-1)); 2032 } 2033 #endif 2034 #if ENABLE_FEATURE_HTTPD_CGI 2035 else if (STRNCASECMP(iobuf, "Cookie:") == 0) { 2036 cookie = xstrdup(skip_whitespace(iobuf + sizeof("Cookie:")-1)); 1771 2037 } else if (STRNCASECMP(iobuf, "Content-Type:") == 0) { 1772 content_type = strdup(skip_whitespace(iobuf + sizeof("Content-Type:")-1));2038 content_type = xstrdup(skip_whitespace(iobuf + sizeof("Content-Type:")-1)); 1773 2039 } else if (STRNCASECMP(iobuf, "Referer:") == 0) { 1774 referer = strdup(skip_whitespace(iobuf + sizeof("Referer:")-1));2040 referer = xstrdup(skip_whitespace(iobuf + sizeof("Referer:")-1)); 1775 2041 } else if (STRNCASECMP(iobuf, "User-Agent:") == 0) { 1776 user_agent = strdup(skip_whitespace(iobuf + sizeof("User-Agent:")-1)); 2042 user_agent = xstrdup(skip_whitespace(iobuf + sizeof("User-Agent:")-1)); 2043 } else if (STRNCASECMP(iobuf, "Host:") == 0) { 2044 host = xstrdup(skip_whitespace(iobuf + sizeof("Host:")-1)); 2045 } else if (STRNCASECMP(iobuf, "Accept:") == 0) { 2046 http_accept = xstrdup(skip_whitespace(iobuf + sizeof("Accept:")-1)); 2047 } else if (STRNCASECMP(iobuf, "Accept-Language:") == 0) { 2048 http_accept_language = xstrdup(skip_whitespace(iobuf + sizeof("Accept-Language:")-1)); 1777 2049 } 1778 2050 #endif … … 1780 2052 if (STRNCASECMP(iobuf, "Authorization:") == 0) { 1781 2053 /* We only allow Basic credentials. 1782 * It shows up as "Authorization: Basic <user id:password>" where1783 * the userid:passwordis base64 encoded.2054 * It shows up as "Authorization: Basic <user>:<passwd>" where 2055 * "<user>:<passwd>" is base64 encoded. 1784 2056 */ 1785 2057 tptr = skip_whitespace(iobuf + sizeof("Authorization:")-1); … … 1789 2061 /* decodeBase64() skips whitespace itself */ 1790 2062 decodeBase64(tptr); 1791 credentials = checkPerm(urlcopy, tptr); 1792 } 1793 #endif /* FEATURE_HTTPD_BASIC_AUTH */ 2063 authorized = check_user_passwd(urlcopy, tptr); 2064 } 2065 #endif 2066 #if ENABLE_FEATURE_HTTPD_RANGES 2067 if (STRNCASECMP(iobuf, "Range:") == 0) { 2068 /* We know only bytes=NNN-[MMM] */ 2069 char *s = skip_whitespace(iobuf + sizeof("Range:")-1); 2070 if (strncmp(s, "bytes=", 6) == 0) { 2071 s += sizeof("bytes=")-1; 2072 range_start = BB_STRTOOFF(s, &s, 10); 2073 if (s[0] != '-' || range_start < 0) { 2074 range_start = 0; 2075 } else if (s[1]) { 2076 range_end = BB_STRTOOFF(s+1, NULL, 10); 2077 if (errno || range_end < range_start) 2078 range_start = 0; 2079 } 2080 } 2081 } 2082 #endif 2083 #if ENABLE_FEATURE_HTTPD_GZIP 2084 if (STRNCASECMP(iobuf, "Accept-Encoding:") == 0) { 2085 /* Note: we do not support "gzip;q=0" 2086 * method of _disabling_ gzip 2087 * delivery. No one uses that, though */ 2088 const char *s = strstr(iobuf, "gzip"); 2089 if (s) { 2090 // want more thorough checks? 2091 //if (s[-1] == ' ' 2092 // || s[-1] == ',' 2093 // || s[-1] == ':' 2094 //) { 2095 content_gzip = 1; 2096 //} 2097 } 2098 } 2099 #endif 1794 2100 } /* while extra header reading */ 1795 2101 } 1796 2102 1797 /* We readheaders, disable peer timeout */2103 /* We are done reading headers, disable peer timeout */ 1798 2104 alarm(0); 1799 2105 1800 if (strcmp(bb_basename(urlcopy), httpd_conf) == 0 || ip_allowed == 0) {1801 /* protect listing [/path]/httpd _conf or IP deny */2106 if (strcmp(bb_basename(urlcopy), HTTPD_CONF) == 0 || !ip_allowed) { 2107 /* protect listing [/path]/httpd.conf or IP deny */ 1802 2108 send_headers_and_exit(HTTP_FORBIDDEN); 1803 2109 } 1804 2110 1805 2111 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH 1806 if (credentials <= 0 && checkPerm(urlcopy, ":") == 0) { 2112 /* Case: no "Authorization:" was seen, but page does require passwd. 2113 * Check that with dummy user:pass */ 2114 if (authorized < 0) 2115 authorized = check_user_passwd(urlcopy, ":"); 2116 if (!authorized) 1807 2117 send_headers_and_exit(HTTP_UNAUTHORIZED); 1808 }1809 2118 #endif 1810 2119 … … 1812 2121 send_headers_and_exit(HTTP_MOVED_TEMPORARILY); 1813 2122 } 2123 2124 #if ENABLE_FEATURE_HTTPD_PROXY 2125 if (proxy_entry != NULL) { 2126 int proxy_fd; 2127 len_and_sockaddr *lsa; 2128 2129 proxy_fd = socket(AF_INET, SOCK_STREAM, 0); 2130 if (proxy_fd < 0) 2131 send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR); 2132 lsa = host2sockaddr(proxy_entry->host_port, 80); 2133 if (lsa == NULL) 2134 send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR); 2135 if (connect(proxy_fd, &lsa->u.sa, lsa->len) < 0) 2136 send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR); 2137 fdprintf(proxy_fd, "%s %s%s%s%s HTTP/%c.%c\r\n", 2138 prequest, /* GET or POST */ 2139 proxy_entry->url_to, /* url part 1 */ 2140 urlcopy + strlen(proxy_entry->url_from), /* url part 2 */ 2141 (g_query ? "?" : ""), /* "?" (maybe) */ 2142 (g_query ? g_query : ""), /* query string (maybe) */ 2143 http_major_version, http_minor_version); 2144 header_ptr[0] = '\r'; 2145 header_ptr[1] = '\n'; 2146 header_ptr += 2; 2147 write(proxy_fd, header_buf, header_ptr - header_buf); 2148 free(header_buf); /* on the order of 8k, free it */ 2149 cgi_io_loop_and_exit(proxy_fd, proxy_fd, length); 2150 } 2151 #endif 1814 2152 1815 2153 tptr = urlcopy + 1; /* skip first '/' */ … … 1823 2161 send_cgi_and_exit(urlcopy, prequest, length, cookie, content_type); 1824 2162 } 2163 #endif 2164 2165 if (urlp[-1] == '/') 2166 strcpy(urlp, index_page); 2167 if (stat(tptr, &sb) == 0) { 1825 2168 #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR 1826 {1827 2169 char *suffix = strrchr(tptr, '.'); 1828 2170 if (suffix) { … … 1834 2176 } 1835 2177 } 1836 } 1837 #endif 1838 if (prequest != request_GET) { 1839 send_headers_and_exit(HTTP_NOT_IMPLEMENTED); 1840 } 1841 #endif /* FEATURE_HTTPD_CGI */ 1842 1843 if (urlp[-1] == '/') 1844 strcpy(urlp, "index.html"); 1845 if (stat(tptr, &sb) == 0) { 1846 /* It's a dir URL and there is index.html */ 1847 ContentLength = sb.st_size; 2178 #endif 2179 file_size = sb.st_size; 1848 2180 last_mod = sb.st_mtime; 1849 2181 } … … 1858 2190 } 1859 2191 } 1860 #endif 1861 /* else { 1862 * fall through to send_file, it errors out if open fails 1863 * } 1864 */ 1865 1866 send_file_and_exit(tptr, TRUE); 2192 /* else fall through to send_file, it errors out if open fails: */ 2193 2194 if (prequest != request_GET && prequest != request_HEAD) { 2195 /* POST for files does not make sense */ 2196 send_headers_and_exit(HTTP_NOT_IMPLEMENTED); 2197 } 2198 send_file_and_exit(tptr, 2199 (prequest != request_HEAD ? SEND_HEADERS_AND_BODY : SEND_HEADERS) 2200 ); 2201 #else 2202 send_file_and_exit(tptr, SEND_HEADERS_AND_BODY); 2203 #endif 1867 2204 } 1868 2205 … … 1874 2211 */ 1875 2212 #if BB_MMU 1876 static void mini_httpd(int server_socket) ATTRIBUTE_NORETURN;2213 static void mini_httpd(int server_socket) NORETURN; 1877 2214 static void mini_httpd(int server_socket) 1878 2215 { … … 1885 2222 int n; 1886 2223 len_and_sockaddr fromAddr; 1887 2224 1888 2225 /* Wait for connections... */ 1889 2226 fromAddr.len = LSA_SIZEOF_SA; 1890 n = accept(server_socket, &fromAddr.sa, &fromAddr.len); 1891 2227 n = accept(server_socket, &fromAddr.u.sa, &fromAddr.len); 1892 2228 if (n < 0) 1893 2229 continue; 2230 1894 2231 /* set the KEEPALIVE option to cull dead connections */ 1895 2232 setsockopt(n, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1)); … … 1897 2234 if (fork() == 0) { 1898 2235 /* child */ 1899 #if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP1900 2236 /* Do not reload config on HUP */ 1901 2237 signal(SIGHUP, SIG_IGN); 1902 #endif1903 2238 close(server_socket); 1904 2239 xmove_fd(n, 0); … … 1913 2248 } 1914 2249 #else 1915 static void mini_httpd_nommu(int server_socket, int argc, char **argv) ATTRIBUTE_NORETURN;2250 static void mini_httpd_nommu(int server_socket, int argc, char **argv) NORETURN; 1916 2251 static void mini_httpd_nommu(int server_socket, int argc, char **argv) 1917 2252 { … … 1930 2265 int n; 1931 2266 len_and_sockaddr fromAddr; 1932 2267 1933 2268 /* Wait for connections... */ 1934 2269 fromAddr.len = LSA_SIZEOF_SA; 1935 n = accept(server_socket, &fromAddr.sa, &fromAddr.len); 1936 2270 n = accept(server_socket, &fromAddr.u.sa, &fromAddr.len); 1937 2271 if (n < 0) 1938 2272 continue; 2273 1939 2274 /* set the KEEPALIVE option to cull dead connections */ 1940 2275 setsockopt(n, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1)); … … 1942 2277 if (vfork() == 0) { 1943 2278 /* child */ 1944 #if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP1945 2279 /* Do not reload config on HUP */ 1946 2280 signal(SIGHUP, SIG_IGN); 1947 #endif1948 2281 close(server_socket); 1949 2282 xmove_fd(n, 0); … … 1964 2297 * Never returns. 1965 2298 */ 1966 static void mini_httpd_inetd(void) ATTRIBUTE_NORETURN;2299 static void mini_httpd_inetd(void) NORETURN; 1967 2300 static void mini_httpd_inetd(void) 1968 2301 { 1969 2302 len_and_sockaddr fromAddr; 1970 2303 2304 memset(&fromAddr, 0, sizeof(fromAddr)); 1971 2305 fromAddr.len = LSA_SIZEOF_SA; 1972 getpeername(0, &fromAddr.sa, &fromAddr.len); 2306 /* NB: can fail if user runs it by hand and types in http cmds */ 2307 getpeername(0, &fromAddr.u.sa, &fromAddr.len); 1973 2308 handle_incoming_and_exit(&fromAddr); 1974 2309 } 1975 2310 1976 #if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP 1977 static void sighup_handler(int sig) 1978 { 1979 struct sigaction sa; 1980 1981 parse_conf(default_path_httpd_conf, sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE); 1982 1983 memset(&sa, 0, sizeof(sa)); 1984 sa.sa_handler = sighup_handler; 1985 /*sigemptyset(&sa.sa_mask); - memset should be enough */ 1986 sa.sa_flags = SA_RESTART; 1987 sigaction(SIGHUP, &sa, NULL); 1988 } 1989 #endif 2311 static void sighup_handler(int sig UNUSED_PARAM) 2312 { 2313 parse_conf(DEFAULT_PATH_HTTPD_CONF, SIGNALED_PARSE); 2314 } 1990 2315 1991 2316 enum { … … 1993 2318 d_opt_decode_url, 1994 2319 h_opt_home_httpd, 1995 USE_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,)1996 USE_FEATURE_HTTPD_BASIC_AUTH( r_opt_realm ,)1997 USE_FEATURE_HTTPD_AUTH_MD5( m_opt_md5 ,)1998 USE_FEATURE_HTTPD_SETUID( u_opt_setuid ,)2320 IF_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,) 2321 IF_FEATURE_HTTPD_BASIC_AUTH( r_opt_realm ,) 2322 IF_FEATURE_HTTPD_AUTH_MD5( m_opt_md5 ,) 2323 IF_FEATURE_HTTPD_SETUID( u_opt_setuid ,) 1999 2324 p_opt_port , 2000 2325 p_opt_inetd , … … 2004 2329 OPT_DECODE_URL = 1 << d_opt_decode_url, 2005 2330 OPT_HOME_HTTPD = 1 << h_opt_home_httpd, 2006 OPT_ENCODE_URL = USE_FEATURE_HTTPD_ENCODE_URL_STR((1 << e_opt_encode_url)) + 0,2007 OPT_REALM = USE_FEATURE_HTTPD_BASIC_AUTH( (1 << r_opt_realm )) + 0,2008 OPT_MD5 = USE_FEATURE_HTTPD_AUTH_MD5( (1 << m_opt_md5 )) + 0,2009 OPT_SETUID = USE_FEATURE_HTTPD_SETUID( (1 << u_opt_setuid )) + 0,2331 OPT_ENCODE_URL = IF_FEATURE_HTTPD_ENCODE_URL_STR((1 << e_opt_encode_url)) + 0, 2332 OPT_REALM = IF_FEATURE_HTTPD_BASIC_AUTH( (1 << r_opt_realm )) + 0, 2333 OPT_MD5 = IF_FEATURE_HTTPD_AUTH_MD5( (1 << m_opt_md5 )) + 0, 2334 OPT_SETUID = IF_FEATURE_HTTPD_SETUID( (1 << u_opt_setuid )) + 0, 2010 2335 OPT_PORT = 1 << p_opt_port, 2011 2336 OPT_INETD = 1 << p_opt_inetd, … … 2015 2340 2016 2341 2017 int httpd_main(int argc, char **argv) ;2018 int httpd_main(int argc , char **argv)2342 int httpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 2343 int httpd_main(int argc UNUSED_PARAM, char **argv) 2019 2344 { 2020 2345 int server_socket = server_socket; /* for gcc */ 2021 2346 unsigned opt; 2022 2347 char *url_for_decode; 2023 USE_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;)2024 USE_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;)2025 USE_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;)2026 USE_FEATURE_HTTPD_AUTH_MD5(const char *pass;)2348 IF_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;) 2349 IF_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;) 2350 IF_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;) 2351 IF_FEATURE_HTTPD_AUTH_MD5(const char *pass;) 2027 2352 2028 2353 INIT_G(); … … 2037 2362 opt_complementary = "vv:if"; 2038 2363 /* We do not "absolutize" path given by -h (home) opt. 2039 * If user gives relative path in -h, $SCRIPT_FILENAME can end up2040 * relative too. */2364 * If user gives relative path in -h, 2365 * $SCRIPT_FILENAME will not be set. */ 2041 2366 opt = getopt32(argv, "c:d:h:" 2042 USE_FEATURE_HTTPD_ENCODE_URL_STR("e:")2043 USE_FEATURE_HTTPD_BASIC_AUTH("r:")2044 USE_FEATURE_HTTPD_AUTH_MD5("m:")2045 USE_FEATURE_HTTPD_SETUID("u:")2367 IF_FEATURE_HTTPD_ENCODE_URL_STR("e:") 2368 IF_FEATURE_HTTPD_BASIC_AUTH("r:") 2369 IF_FEATURE_HTTPD_AUTH_MD5("m:") 2370 IF_FEATURE_HTTPD_SETUID("u:") 2046 2371 "p:ifv", 2047 & configFile, &url_for_decode, &home_httpd2048 USE_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode)2049 USE_FEATURE_HTTPD_BASIC_AUTH(, &g_realm)2050 USE_FEATURE_HTTPD_AUTH_MD5(, &pass)2051 USE_FEATURE_HTTPD_SETUID(, &s_ugid)2372 &opt_c_configFile, &url_for_decode, &home_httpd 2373 IF_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode) 2374 IF_FEATURE_HTTPD_BASIC_AUTH(, &g_realm) 2375 IF_FEATURE_HTTPD_AUTH_MD5(, &pass) 2376 IF_FEATURE_HTTPD_SETUID(, &s_ugid) 2052 2377 , &bind_addr_or_port 2053 2378 , &verbose … … 2065 2390 #if ENABLE_FEATURE_HTTPD_AUTH_MD5 2066 2391 if (opt & OPT_MD5) { 2067 puts(pw_encrypt(pass, "$1$")); 2392 char salt[sizeof("$1$XXXXXXXX")]; 2393 salt[0] = '$'; 2394 salt[1] = '1'; 2395 salt[2] = '$'; 2396 crypt_make_salt(salt + 3, 4, 0); 2397 puts(pw_encrypt(pass, salt, 1)); 2068 2398 return 0; 2069 2399 } … … 2071 2401 #if ENABLE_FEATURE_HTTPD_SETUID 2072 2402 if (opt & OPT_SETUID) { 2073 if (!get_uidgid(&ugid, s_ugid, 1)) 2074 bb_error_msg_and_die("unrecognized user[:group] " 2075 "name '%s'", s_ugid); 2403 xget_uidgid(&ugid, s_ugid); 2076 2404 } 2077 2405 #endif … … 2100 2428 } 2101 2429 2102 #if ENABLE_FEATURE_HTTPD_CGI 2430 #if 0 2431 /* User can do it himself: 'env - PATH="$PATH" httpd' 2432 * We don't do it because we don't want to screw users 2433 * which want to do 2434 * 'env - VAR1=val1 VAR2=val2 httpd' 2435 * and have VAR1 and VAR2 values visible in their CGIs. 2436 * Besides, it is also smaller. */ 2103 2437 { 2104 2438 char *p = getenv("PATH"); 2105 /* env strings themself are not freed, no need to strdup(p): */2439 /* env strings themself are not freed, no need to xstrdup(p): */ 2106 2440 clearenv(); 2107 2441 if (p) … … 2112 2446 #endif 2113 2447 2114 #if ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP 2448 parse_conf(DEFAULT_PATH_HTTPD_CONF, FIRST_PARSE); 2115 2449 if (!(opt & OPT_INETD)) 2116 sighup_handler(0); 2117 else /* do not install HUP handler in inetd mode */ 2118 #endif 2119 parse_conf(default_path_httpd_conf, FIRST_PARSE); 2450 signal(SIGHUP, sighup_handler); 2120 2451 2121 2452 xfunc_error_retval = 0;
Note:
See TracChangeset
for help on using the changeset viewer.