aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--networking/httpd.c611
1 files changed, 284 insertions, 327 deletions
diff --git a/networking/httpd.c b/networking/httpd.c
index e62168d38..9411117ea 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -42,7 +42,7 @@
42 * The server can also be invoked as a url arg decoder and html text encoder 42 * The server can also be invoked as a url arg decoder and html text encoder
43 * as follows: 43 * as follows:
44 * foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World" 44 * foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World"
45 * bar=`httpd -e "<Hello World>"` # encode as "%3CHello%20World%3E" 45 * bar=`httpd -e "<Hello World>"` # encode as "&#60Hello&#32World&#62"
46 * 46 *
47 * httpd.conf has the following format: 47 * httpd.conf has the following format:
48 48
@@ -56,51 +56,17 @@ D:* # Deny from other IP connections
56/adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/ 56/adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/
57.au:audio/basic # additional mime type for audio.au files 57.au:audio/basic # additional mime type for audio.au files
58 58
59A shortes path and D:from[^*] automaticaly sorting to top.
60All longest path can`t reset user:password if shorted protect setted.
61
62A/D may be as a/d or allow/deny - first char case unsensitive parsed only. 59A/D may be as a/d or allow/deny - first char case unsensitive parsed only.
63 60
64Each subdir can have config file. 61Each subdir can have config file.
62You can set less IP allow from subdir config.
63Password protection from subdir config can rewriten previous sets for
64current or/and next subpathes.
65For protect as user:pass current subdir and subpathes set from subdir config: 65For protect as user:pass current subdir and subpathes set from subdir config:
66/:user:pass 66/:user:pass
67if not, other subpathes for give effect must have path from httpd root 67/subpath:user2:pass2
68/current_subdir_path_from_httpd_root/subpath:user:pass
69
70The Deny/Allow IP logic:
71
72 1. Allow all:
73The config don`t set D: lines
74
75 2. Allow from setted only:
76see the begin format example
77
78 3. Set deny, allow from other:
79D:1.2.3. # deny from 1.2.3.0 - 1.2.3.255
80D:2.3.4. # deny from 2.3.4.0 - 2.3.4.255
81A:* # allow from other, this line not strongly require
82
83 A global and subdirs config merging logic:
84allow rules reducing, deny lines strongled.
85 The algorithm combinations:
86
87 4. If current config have
88A:from
89D:*
90 subdir config A: lines skiping, D:from - moving top
91
92 5. If current config have
93D:from
94A:*
95 result config:
96D:from current
97D:from subdir
98A:from subdir
99 and seting D:* if subdir config have this
100 68
101 If -c don`t setted, used httpd root config, else httpd root config skiped. 69 If -c don`t setted, used httpd root config, else httpd root config skiped.
102 Exited with fault if can`t open start config.
103 For set wide open server, use -c /dev/null ;=)
104 */ 70 */
105 71
106#include <stdio.h> 72#include <stdio.h>
@@ -120,30 +86,29 @@ A:from subdir
120#include "busybox.h" 86#include "busybox.h"
121 87
122 88
123static const char httpdVersion[] = "busybox httpd/1.20 31-Jan-2003"; 89static const char httpdVersion[] = "busybox httpd/1.25 10-May-2003";
124static const char default_patch_httpd_conf[] = "/etc"; 90static const char default_path_httpd_conf[] = "/etc";
125static const char httpd_conf[] = "httpd.conf"; 91static const char httpd_conf[] = "httpd.conf";
126static const char home[] = "/www"; 92static const char home[] = "/www";
127 93
128// Note: xfuncs are not used because we want the server to keep running 94// Note: bussybox xfuncs are not used because we want the server to keep running
129// if something bad happens due to a malformed user request. 95// if something bad happens due to a malformed user request.
130// As a result, all memory allocation after daemonize 96// As a result, all memory allocation after daemonize
131// is checked rigorously 97// is checked rigorously
132 98
133//#define DEBUG 1 99//#define DEBUG 1
134 100
135/* Configure options, disabled by default as nonstandart httpd feature */ 101/* Configure options, disabled by default as custom httpd feature */
102
103/* disabled as optional features */
136//#define CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV 104//#define CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
137//#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR 105//#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
138//#define CONFIG_FEATURE_HTTPD_DECODE_URL_STR
139
140/* disabled as not necessary feature */
141//#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV 106//#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
142//#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES 107//#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
143//#define CONFIG_FEATURE_HTTPD_SETUID 108//#define CONFIG_FEATURE_HTTPD_SETUID
144//#define CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP 109//#define CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
145 110
146/* If seted this you can use this server from internet superserver only */ 111/* If set, use this server from internet superserver only */
147//#define CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY 112//#define CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
148 113
149/* You can use this server as standalone, require libbb.a for linking */ 114/* You can use this server as standalone, require libbb.a for linking */
@@ -160,7 +125,6 @@ static const char home[] = "/www";
160#undef CONFIG_FEATURE_HTTPD_BASIC_AUTH 125#undef CONFIG_FEATURE_HTTPD_BASIC_AUTH
161#undef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV 126#undef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
162#undef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR 127#undef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
163#undef CONFIG_FEATURE_HTTPD_DECODE_URL_STR
164#undef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV 128#undef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
165#undef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES 129#undef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
166#undef CONFIG_FEATURE_HTTPD_CGI 130#undef CONFIG_FEATURE_HTTPD_CGI
@@ -170,7 +134,6 @@ static const char home[] = "/www";
170#define CONFIG_FEATURE_HTTPD_BASIC_AUTH 134#define CONFIG_FEATURE_HTTPD_BASIC_AUTH
171#define CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV 135#define CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
172#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR 136#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
173#define CONFIG_FEATURE_HTTPD_DECODE_URL_STR
174#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV 137#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
175#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES 138#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
176#define CONFIG_FEATURE_HTTPD_CGI 139#define CONFIG_FEATURE_HTTPD_CGI
@@ -183,7 +146,7 @@ const char *bb_applet_name = "httpd";
183void bb_show_usage(void) 146void bb_show_usage(void)
184{ 147{
185 fprintf(stderr, "Usage: %s [-p <port>] [-c configFile] [-d/-e <string>] " 148 fprintf(stderr, "Usage: %s [-p <port>] [-c configFile] [-d/-e <string>] "
186 "[-r realm] [-u user]\n", bb_applet_name); 149 "[-r realm] [-u user] [-h homedir]\n", bb_applet_name);
187 exit(1); 150 exit(1);
188} 151}
189#endif 152#endif
@@ -232,6 +195,15 @@ typedef struct
232 const char *found_mime_type; 195 const char *found_mime_type;
233 off_t ContentLength; /* -1 - unknown */ 196 off_t ContentLength; /* -1 - unknown */
234 time_t last_mod; 197 time_t last_mod;
198
199 Htaccess *ip_a_d; /* config allow/deny lines */
200#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
201 Htaccess *auth; /* config user:password lines */
202#endif
203#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
204 Htaccess *mime_a; /* config mime types */
205#endif
206
235#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY 207#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
236 int accepted_socket; 208 int accepted_socket;
237#define a_c_r config->accepted_socket 209#define a_c_r config->accepted_socket
@@ -241,7 +213,6 @@ typedef struct
241#define a_c_r 0 213#define a_c_r 0
242#define a_c_w 1 214#define a_c_w 1
243#endif 215#endif
244 Htaccess *Httpd_conf_parsed;
245} HttpdConfig; 216} HttpdConfig;
246 217
247static HttpdConfig *config; 218static HttpdConfig *config;
@@ -308,15 +279,16 @@ static const HttpEnumString httpResponseNames[] = {
308 { HTTP_OK, "OK" }, 279 { HTTP_OK, "OK" },
309 { HTTP_NOT_IMPLEMENTED, "Not Implemented", 280 { HTTP_NOT_IMPLEMENTED, "Not Implemented",
310 "The requested method is not recognized by this server." }, 281 "The requested method is not recognized by this server." },
282#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
311 { HTTP_UNAUTHORIZED, "Unauthorized", "" }, 283 { HTTP_UNAUTHORIZED, "Unauthorized", "" },
284#endif
312 { HTTP_NOT_FOUND, "Not Found", 285 { HTTP_NOT_FOUND, "Not Found",
313 "The requested URL was not found on this server." }, 286 "The requested URL was not found on this server." },
314 { HTTP_BAD_REQUEST, "Bad Request" , 287 { HTTP_BAD_REQUEST, "Bad Request", "Unsupported method." },
315 "Unsupported method." },
316 { HTTP_FORBIDDEN, "Forbidden", "" }, 288 { HTTP_FORBIDDEN, "Forbidden", "" },
317 { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error" 289 { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error",
318 "Internal Server Error" }, 290 "Internal Server Error" },
319#if 0 291#if 0 /* not implemented */
320 { HTTP_CREATED, "Created" }, 292 { HTTP_CREATED, "Created" },
321 { HTTP_ACCEPTED, "Accepted" }, 293 { HTTP_ACCEPTED, "Accepted" },
322 { HTTP_NO_CONTENT, "No Content" }, 294 { HTTP_NO_CONTENT, "No Content" },
@@ -334,157 +306,106 @@ static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT";
334static const char Content_length[] = "Content-length:"; 306static const char Content_length[] = "Content-length:";
335 307
336 308
337/* 309
338 * sotring to: 310static void free_config_lines(Htaccess **pprev)
339 * .ext:mime/type
340 * /path:user:pass
341 * /path/subdir:user:pass
342 * D:from
343 * A:from
344 * D:*
345 */
346static int conf_sort(const void *p1, const void *p2)
347{ 311{
348 const Htaccess *cl1 = *(const Htaccess **)p1; 312 Htaccess *prev = *pprev;
349 const Htaccess *cl2 = *(const Htaccess **)p2;
350 char c1 = cl1->before_colon[0];
351 char c2 = cl2->before_colon[0];
352 int test;
353 313
354#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES 314 while( prev ) {
355 /* .ext line up before other lines for simlify algorithm */ 315 Htaccess *cur = prev;
356 test = c2 == '.';
357 if(c1 == '.')
358 return -(!test);
359 if(test)
360 return test;
361#endif
362 316
363#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 317 prev = cur->next;
364 test = c1 == '/'; 318 free(cur);
365 /* /path line up before A/D lines for simlify algorithm */
366 if(test) {
367 if(c2 != '/')
368 return -test;
369 /* a shortes path with user:pass must be first */
370 return strlen(cl1->before_colon) - strlen(cl2->before_colon);
371 } else if(c2 == '/')
372 return !test;
373#endif
374
375 /* D:from must move top */
376 test = c2 == 'D' && cl2->after_colon[0] != 0;
377 if(c1 == 'D' && cl1->after_colon[0] != 0) {
378 return -(!test);
379 } 319 }
380 if(test) 320 *pprev = NULL;
381 return test; 321}
382 322
383 /* next lines - A:from */ 323static void add_config_line(Htaccess **pprev, Htaccess *cur)
384 test = c2 == 'A' && cl2->after_colon[0] != 0; 324{
385 if(c1 == 'A' && cl1->after_colon[0] != 0) { 325 if(*pprev == NULL) {
386 return -(!test); 326 *pprev = cur;
387 } 327 } else {
388 if(test) 328 Htaccess *prev;
389 return test;
390 329
391 /* end lines - D:* */ 330 for(prev = *pprev; prev->next; prev = prev->next)
392 test = c2 == 'D' && cl2->after_colon[0] == 0; 331 ;
393 if(c1 == 'D' && cl1->after_colon[0] == 0) { 332 prev->next = cur;
394 return -(!test);
395 } 333 }
396#ifdef DEBUG
397 if(!test)
398 bb_error_msg_and_die("sort: can`t found compares!");
399#endif
400 return test;
401} 334}
402 335
403
404/* flag */ 336/* flag */
405#define FIRST_PARSE 0 337#define FIRST_PARSE 0
406#define SUBDIR_PARSE 1 338#define SUBDIR_PARSE 1
407#define SIGNALED_PARSE 2 339#define SIGNALED_PARSE 2
340#define FIND_FROM_HTTPD_ROOT 3
408 341
409static void parse_conf(const char *path, int flag) 342static void parse_conf(const char *path, int flag)
410{ 343{
411#define bc cur->before_colon[0]
412#define ac cur->after_colon[0]
413 FILE *f; 344 FILE *f;
414 Htaccess *prev;
415 Htaccess *cur; 345 Htaccess *cur;
346#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
347 Htaccess *prev;
348#endif
349
416 const char *cf = config->configFile; 350 const char *cf = config->configFile;
417 char buf[80]; 351 char buf[80];
418 char *p0 = NULL; 352 char *p0 = NULL;
419 int deny_all = 0; /* default A:* */ 353 char *c, *p;
420 int n = 0; /* count config lines */ 354
355 /* free previous setuped */
356 free_config_lines(&config->ip_a_d);
357 if(flag != SUBDIR_PARSE) {
358#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
359 free_config_lines(&config->auth)
360#endif
361 ; /* syntax confuse */
362#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
363 free_config_lines(&config->mime_a);
364#endif
365 }
421 366
422 if(flag == SUBDIR_PARSE || cf == NULL) { 367 if(flag == SUBDIR_PARSE || cf == NULL) {
423 cf = p0 = alloca(strlen(path) + sizeof(httpd_conf) + 2); 368 cf = alloca(strlen(path) + sizeof(httpd_conf) + 2);
424 if(p0 == NULL) { 369 if(cf == NULL) {
425 if(flag == FIRST_PARSE) 370 if(flag == FIRST_PARSE)
426 bb_error_msg_and_die(bb_msg_memory_exhausted); 371 bb_error_msg_and_die(bb_msg_memory_exhausted);
427 return; 372 return;
428 } 373 }
429 sprintf(p0, "%s/%s", path, httpd_conf); 374 sprintf((char *)cf, "%s/%s", path, httpd_conf);
430 } 375 }
431 376
432 while((f = fopen(cf, "r")) == NULL) { 377 while((f = fopen(cf, "r")) == NULL) {
433 if(flag != FIRST_PARSE) 378 if(flag != FIRST_PARSE) {
434 return; /* subdir config not found */ 379 /* config file not found */
435 if(p0 == NULL) /* if -c option gived */ 380 return;
436 bb_perror_msg_and_die("%s", cf);
437 p0 = NULL;
438 cf = httpd_conf; /* set -c ./httpd_conf */
439 }
440
441 prev = config->Httpd_conf_parsed;
442 if(flag != SUBDIR_PARSE) {
443 /* free previous setuped */
444 while( prev ) {
445 cur = prev;
446 prev = cur->next;
447 free(cur);
448 }
449 config->Httpd_conf_parsed = prev; /* eq NULL */
450 } else {
451 /* parse previous IP logic for merge */
452 for(cur = prev; cur; cur = cur->next) {
453 if(bc == 'D' && ac == 0)
454 deny_all++;
455 n++;
456 /* find last for set prev->next of merging */
457 if(cur != prev)
458 prev = prev->next;
459 } 381 }
382 if(config->configFile) /* if -c option given */
383 bb_perror_msg_and_die("%s", cf);
384 flag = FIND_FROM_HTTPD_ROOT;
385 cf = httpd_conf;
460 } 386 }
461 387
462 /* This could stand some work */ 388#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
389 prev = config->auth;
390#endif
391 /* This could stand some work */
463 while ( (p0 = fgets(buf, 80, f)) != NULL) { 392 while ( (p0 = fgets(buf, 80, f)) != NULL) {
464 char *p; 393 c = NULL;
465 char *colon; 394 for(p = p0; *p0 != 0 && *p0 != '#'; p0++) {
466 395 if(!isspace(*p0)) {
467 for(p = colon = p0; *p; p++) { 396 *p++ = *p0;
468 if(*p == '#') { 397 if(*p0 == ':' && c == NULL)
469 *p = 0; 398 c = p;
470 break;
471 } 399 }
472 if(isspace(*p)) {
473 if(p != p0) {
474 *p = 0;
475 break;
476 }
477 p0++;
478 }
479 else if(*p == ':' && colon <= p0)
480 colon = p;
481 } 400 }
401 *p = 0;
482 402
483 /* test for empty or strange line */ 403 /* test for empty or strange line */
484 if (colon <= p0 || colon[1] == 0) 404 if (c == NULL || *c == 0)
485 continue; 405 continue;
486 if(colon[1] == '*') 406 if(*c == '*')
487 colon[1] = 0; /* Allow all */ 407 *c = 0; /* Allow all */
408 p0 = buf;
488 if(*p0 == 'a') 409 if(*p0 == 'a')
489 *p0 = 'A'; 410 *p0 = 'A';
490 if(*p0 == 'd') 411 if(*p0 == 'd')
@@ -498,70 +419,109 @@ static void parse_conf(const char *path, int flag)
498#endif 419#endif
499 ) 420 )
500 continue; 421 continue;
501#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 422
502 if(*p0 == '/' && colon[1] == 0) { 423 if(*p0 == 'A' && *c == 0) {
503 /* skip /path:* */ 424 /* skip default A:* */
504 continue; 425 continue;
505 } 426 }
506#endif 427 p0 = buf;
428#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
429 if(*p0 == '/') {
430 if(*c == 0) {
431 /* skip /path:* */
432 continue;
433 }
434 /* make full path from httpd root / curent_path / config_line_path */
435 cf = flag == SUBDIR_PARSE ? path : "";
436 p0 = malloc(strlen(cf) + (c - buf) + 2 + strlen(c));
437 if(p0 == NULL)
438 continue;
439 c[-1] = 0;
440 sprintf(p0, "/%s%s", cf, buf);
441
442 /* another call bb_simplify_path */
443 cf = p = p0;
444
445 do {
446 if (*p == '/') {
447 if (*cf == '/') { /* skip duplicate (or initial) slash */
448 continue;
449 } else if (*cf == '.') {
450 if (cf[1] == '/' || cf[1] == 0) { /* remove extra '.' */
451 continue;
452 } else if ((cf[1] == '.') && (cf[2] == '/' || cf[2] == 0)) {
453 ++cf;
454 if (p > p0) {
455 while (*--p != '/'); /* omit previous dir */
456 }
457 continue;
458 }
459 }
460 }
461 *++p = *cf;
462 } while (*++cf);
507 463
508 if(*p0 == 'A' || *p0 == 'D') { 464 if ((p == p0) || (*p != '/')) { /* not a trailing slash */
509 if(colon[1] == 0) { 465 ++p; /* so keep last character */
510 if(*p0 == 'A' || deny_all != 0)
511 continue; /* skip default A:* or double D:* */
512 } 466 }
513 if(deny_all != 0 && *p0 == 'A') 467 *p = 0;
514 continue; // if previous setted rule D:* skip all subdir A: 468 sprintf(p0, "%s:%s", p0, c);
515 } 469 }
516 470#endif
517 /* storing current config line */ 471 /* storing current config line */
518 cur = calloc(1, sizeof(Htaccess) + (p-p0)); 472
473 cur = calloc(1, sizeof(Htaccess) + strlen(p0));
519 if(cur) { 474 if(cur) {
520 if(*(colon-1) == '/' && (colon-1) != p0) 475 cf = strcpy(cur->before_colon, p0);
521 colon[-1] = 0; // remove last / from /path/ 476 c = strchr(cf, ':');
522 cur->after_colon = strcpy(cur->before_colon, p0); 477 *c++ = 0;
523 cur->after_colon += (colon-p0); 478 cur->after_colon = c;
524 *cur->after_colon++ = 0; 479#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
480 if(*cf == '/')
481 free(p0);
482#endif
483 if(*cf == 'A' || *cf == 'D')
484 add_config_line(&config->ip_a_d, cur);
485#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
486 else if(*cf == '.')
487 add_config_line(&config->mime_a, cur);
488#endif
525 489
526 if(prev == NULL) { 490#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
491 else if(prev == NULL) {
527 /* first line */ 492 /* first line */
528 config->Httpd_conf_parsed = prev = cur; 493 config->auth = prev = cur;
529 } else { 494 } else {
530 prev->next = cur; 495 /* sort path, if current lenght eq or bigger then move up */
531 prev = cur; 496 Htaccess *prev_hti = config->auth;
497 int l = strlen(cf);
498 Htaccess *hti;
499
500 for(hti = prev_hti; hti; hti = hti->next) {
501 if(l >= strlen(hti->before_colon)) {
502 /* insert before hti */
503 cur->next = hti;
504 if(prev_hti != hti) {
505 prev_hti->next = cur;
506 break;
507 } else {
508 /* insert as top */
509 config->auth = cur;
510 break;
511 }
512 }
513 if(prev_hti != hti)
514 prev_hti = prev_hti->next;
515 }
516 if(!hti) { /* not inserted, add to bottom */
517 prev->next = cur;
518 prev = cur;
519 }
532 } 520 }
533 n++; 521#endif
534 } 522 }
535 } 523 }
536 fclose(f); 524 fclose(f);
537
538 if(n > 1) {
539 /* sorting conf lines */
540 Htaccess ** pcur; /* array for qsort */
541
542 prev = config->Httpd_conf_parsed;
543 pcur = alloca((n + 1) * sizeof(Htaccess *));
544 if(pcur == NULL) {
545 if(flag == FIRST_PARSE)
546 bb_error_msg_and_die(bb_msg_memory_exhausted);
547 return;
548 }
549 n = 0;
550 for(cur = prev; cur; cur = cur->next)
551 pcur[n++] = cur;
552 pcur[n] = NULL;
553
554 qsort(pcur, n, sizeof(Htaccess *), conf_sort);
555
556 /* storing sorted config */
557 config->Httpd_conf_parsed = *pcur;
558 for(cur = *pcur; cur; cur = cur->next) {
559#ifdef DEBUG
560 bb_error_msg("%s: %s:%s", cf, cur->before_colon, cur->after_colon);
561#endif
562 cur->next = *++pcur;
563 }
564 }
565} 525}
566 526
567#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR 527#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
@@ -602,7 +562,6 @@ static char *encodeString(const char *string)
602} 562}
603#endif /* CONFIG_FEATURE_HTTPD_ENCODE_URL_STR */ 563#endif /* CONFIG_FEATURE_HTTPD_ENCODE_URL_STR */
604 564
605#ifdef CONFIG_FEATURE_HTTPD_DECODE_URL_STR
606/**************************************************************************** 565/****************************************************************************
607 * 566 *
608 > $Function: decodeString() 567 > $Function: decodeString()
@@ -615,20 +574,21 @@ static char *encodeString(const char *string)
615 * 574 *
616 * $Parameters: 575 * $Parameters:
617 * (char *) string . . . The first string to decode. 576 * (char *) string . . . The first string to decode.
577 * (int) flag . . . 1 if require decode '+' as ' ' for CGI
618 * 578 *
619 * $Return: (char *) . . . . A pointer to the decoded string (same as input). 579 * $Return: (char *) . . . . A pointer to the decoded string (same as input).
620 * 580 *
621 * $Errors: None 581 * $Errors: None
622 * 582 *
623 ****************************************************************************/ 583 ****************************************************************************/
624static char *decodeString(char *string) 584static char *decodeString(char *string, int flag_plus_to_space)
625{ 585{
626 /* note that decoded string is always shorter than original */ 586 /* note that decoded string is always shorter than original */
627 char *orig = string; 587 char *orig = string;
628 char *ptr = string; 588 char *ptr = string;
629 while (*ptr) 589 while (*ptr)
630 { 590 {
631 if (*ptr == '+') { *string++ = ' '; ptr++; } 591 if (*ptr == '+' && flag_plus_to_space) { *string++ = ' '; ptr++; }
632 else if (*ptr != '%') *string++ = *ptr++; 592 else if (*ptr != '%') *string++ = *ptr++;
633 else { 593 else {
634 unsigned int value; 594 unsigned int value;
@@ -640,7 +600,6 @@ static char *decodeString(char *string)
640 *string = '\0'; 600 *string = '\0';
641 return orig; 601 return orig;
642} 602}
643#endif /* CONFIG_FEATURE_HTTPD_DECODE_URL_STR */
644 603
645 604
646#ifdef CONFIG_FEATURE_HTTPD_CGI 605#ifdef CONFIG_FEATURE_HTTPD_CGI
@@ -730,7 +689,7 @@ static void addEnvCgi(const char *pargs)
730 args = strchr(value, '&'); 689 args = strchr(value, '&');
731 if (args) 690 if (args)
732 *args++ = 0; 691 *args++ = 0;
733 addEnv("CGI", name, decodeString(value)); 692 addEnv("CGI", name, decodeString(value, 1));
734 } 693 }
735 free(memargs); 694 free(memargs);
736} 695}
@@ -758,33 +717,44 @@ static void addEnvCgi(const char *pargs)
758 ****************************************************************************/ 717 ****************************************************************************/
759static void decodeBase64(char *Data) 718static void decodeBase64(char *Data)
760{ 719{
761 int i = 0;
762 static const char base64ToBin[] =
763 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
764 720
765 const unsigned char *in = Data; 721 const unsigned char *in = Data;
766 // The decoded size will be at most 3/4 the size of the encoded 722 // The decoded size will be at most 3/4 the size of the encoded
767 unsigned long ch = 0; 723 unsigned long ch = 0;
724 int i = 0;
768 725
769 while (*in) { 726 while (*in) {
770 unsigned char conv = 0; 727 int t = *in++;
771
772 while (*in) {
773 const char *p64;
774 728
775 p64 = strchr(base64ToBin, *in++); 729 switch(t) {
776 if(p64 == NULL) 730 case '+':
777 continue; 731 t = 62;
778 conv = (p64 - base64ToBin); 732 break;
779 break; 733 case '/':
734 t = 63;
735 break;
736 case '=':
737 t = 0;
738 break;
739 case 'A' ... 'Z':
740 t = t - 'A';
741 break;
742 case 'a' ... 'z':
743 t = t - 'a' + 26;
744 break;
745 case '0' ... '9':
746 t = t - '0' + 52;
747 break;
748 default:
749 continue;
780 } 750 }
781 ch = (ch << 6) | conv; 751 ch = (ch << 6) | t;
782 i++; 752 i++;
783 if (i== 4) { 753 if (i == 4) {
784 *Data++ = (char) (ch >> 16); 754 *Data++ = (char) (ch >> 16);
785 *Data++ = (char) (ch >> 8); 755 *Data++ = (char) (ch >> 8);
786 *Data++ = (char) ch; 756 *Data++ = (char) ch;
787 i = 0; 757 i = 0;
788 } 758 }
789 } 759 }
790 *Data = 0; 760 *Data = 0;
@@ -1203,7 +1173,7 @@ static int sendFile(const char *url, char *buf)
1203 if (suffix) { 1173 if (suffix) {
1204 Htaccess * cur; 1174 Htaccess * cur;
1205 1175
1206 for (cur = config->Httpd_conf_parsed; cur; cur = cur->next) { 1176 for (cur = config->mime_a; cur; cur = cur->next) {
1207 if(strcmp(cur->before_colon, suffix) == 0) { 1177 if(strcmp(cur->before_colon, suffix) == 0) {
1208 config->found_mime_type = cur->after_colon; 1178 config->found_mime_type = cur->after_colon;
1209 break; 1179 break;
@@ -1255,74 +1225,71 @@ static int sendFile(const char *url, char *buf)
1255 * 1225 *
1256 ****************************************************************************/ 1226 ****************************************************************************/
1257 1227
1258 1228#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1259static int checkPerm(const char *path, const char *request) 1229static int checkPerm(const char *path, const char *request)
1260{ 1230{
1261 Htaccess * cur; 1231 Htaccess * cur;
1262 const char *p; 1232 const char *p;
1233 const char *p0;
1263 1234
1264#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1265 int ipaddr = path == NULL; 1235 int ipaddr = path == NULL;
1266 const char *prev = NULL; 1236 const char *prev = NULL;
1267#else
1268# define ipaddr 1
1269#endif
1270 1237
1271 /* This could stand some work */ 1238 /* This could stand some work */
1272 for (cur = config->Httpd_conf_parsed; cur; cur = cur->next) { 1239 for (cur = ipaddr ? config->ip_a_d : config->auth; cur; cur = cur->next) {
1273 const char *p0 = cur->before_colon; 1240 p0 = cur->before_colon;
1274 1241 if(prev != NULL && strcmp(prev, p0) != 0)
1275 if(*p0 == 'A' || *p0 == 'D') { 1242 continue; /* find next identical */
1276 if(!ipaddr)
1277 continue;
1278 } else {
1279 if(ipaddr)
1280 continue;
1281#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
1282 if(*p0 == '.')
1283 continue;
1284#endif
1285#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1286 if(prev != NULL && strcmp(prev, p0) != 0)
1287 continue; /* find next identical */
1288#endif
1289 }
1290
1291 p = cur->after_colon; 1243 p = cur->after_colon;
1292#ifdef DEBUG 1244#ifdef DEBUG
1293 if (config->debugHttpd) 1245 if (config->debugHttpd)
1294 fprintf(stderr,"checkPerm: '%s' ? '%s'\n", 1246 fprintf(stderr,"checkPerm: '%s' ? '%s'\n",
1295 (ipaddr ? p : p0), request); 1247 (ipaddr ? (*p ? p : "*") : p0), request);
1296#endif 1248#endif
1297 if(ipaddr) { 1249 if(ipaddr) {
1298 if(strncmp(p, request, strlen(p)) != 0) 1250 if(strncmp(p, request, strlen(p)) != 0)
1299 continue; 1251 continue;
1300 return *p0 == 'A'; /* Allow/Deny */ 1252 return *p0 == 'A'; /* Allow/Deny */
1301 1253 } else {
1254 int l = strlen(p0);
1255
1256 if(strncmp(p0, path, l) == 0 &&
1257 (l == 1 || path[l] == '/' || path[l] == 0)) {
1258 /* path match found. Check request */
1259 if (strcmp(p, request) == 0)
1260 return 1; /* Ok */
1261 /* unauthorized, but check next /path:user:password */
1262 prev = p0;
1263 }
1302 } 1264 }
1303#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1304 else {
1305 int l = strlen(p0);
1306
1307 if(strncmp(p0, path, l) == 0 &&
1308 (l == 1 || path[l] == '/' || path[l] == 0)) {
1309 /* path match found. Check request */
1310 if (strcmp(p, request) == 0)
1311 return 1; /* Ok */
1312 /* unauthorized, but check next /path:user:password */
1313 prev = p0;
1314 }
1315 }
1316#endif
1317 } /* for */ 1265 } /* for */
1318 1266
1319#ifndef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1320 /* if uncofigured, return 1 - access from all */
1321 return 1;
1322#else
1323 return prev == NULL; 1267 return prev == NULL;
1268}
1269
1270#else /* ifndef CONFIG_FEATURE_HTTPD_BASIC_AUTH */
1271static int checkPermIP(const char *request)
1272{
1273 Htaccess * cur;
1274 const char *p;
1275
1276 /* This could stand some work */
1277 for (cur = config->ip_a_d; cur; cur = cur->next) {
1278 p = cur->after_colon;
1279#ifdef DEBUG
1280 if (config->debugHttpd)
1281 fprintf(stderr, "checkPerm: '%s' ? '%s'\n",
1282 (*p ? p : "*"), request);
1324#endif 1283#endif
1284 if(strncmp(p, request, strlen(p)) == 0)
1285 return *cur->before_colon == 'A'; /* Allow/Deny */
1286 }
1287
1288 /* if uncofigured, return 1 - access from all */
1289 return 1;
1325} 1290}
1291#define checkPerm(null, request) checkPermIP(request)
1292#endif /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */
1326 1293
1327 1294
1328/**************************************************************************** 1295/****************************************************************************
@@ -1347,6 +1314,7 @@ static void handleIncoming(void)
1347#endif 1314#endif
1348 char *test; 1315 char *test;
1349 struct stat sb; 1316 struct stat sb;
1317 int ip_allowed;
1350 1318
1351#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 1319#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1352 int credentials = -1; /* if not requred this is Ok */ 1320 int credentials = -1; /* if not requred this is Ok */
@@ -1382,6 +1350,7 @@ BAD_REQUEST:
1382 *purl = ' '; 1350 *purl = ' ';
1383 count = sscanf(purl, " %[^ ] HTTP/%d.%*d", buf, &blank); 1351 count = sscanf(purl, " %[^ ] HTTP/%d.%*d", buf, &blank);
1384 1352
1353 decodeString(buf, 0);
1385 if (count < 1 || buf[0] != '/') { 1354 if (count < 1 || buf[0] != '/') {
1386 /* Garbled request/URL */ 1355 /* Garbled request/URL */
1387 goto BAD_REQUEST; 1356 goto BAD_REQUEST;
@@ -1394,13 +1363,11 @@ BAD_REQUEST:
1394 strcpy(url, buf); 1363 strcpy(url, buf);
1395 /* extract url args if present */ 1364 /* extract url args if present */
1396 urlArgs = strchr(url, '?'); 1365 urlArgs = strchr(url, '?');
1397 if (urlArgs) { 1366 if (urlArgs)
1398 *urlArgs++ = 0; /* next code can set '/' to this pointer, 1367 *urlArgs++ = 0;
1399 but CGI script can`t be a directory */
1400 }
1401 1368
1402 /* algorithm stolen from libbb bb_simplify_path(), 1369 /* algorithm stolen from libbb bb_simplify_path(),
1403 but don`t strdup and reducing trailing slash */ 1370 but don`t strdup and reducing trailing slash and protect out root */
1404 purl = test = url; 1371 purl = test = url;
1405 1372
1406 do { 1373 do {
@@ -1441,12 +1408,14 @@ BAD_REQUEST:
1441#endif 1408#endif
1442 1409
1443 test = url; 1410 test = url;
1444 while((test = strchr( test + 1, '/' )) != NULL) { 1411 ip_allowed = checkPerm(NULL, config->rmt_ip);
1412 while(ip_allowed && (test = strchr( test + 1, '/' )) != NULL) {
1445 /* have path1/path2 */ 1413 /* have path1/path2 */
1446 *test = '\0'; 1414 *test = '\0';
1447 if( is_directory(url + 1, 1, &sb) ) { 1415 if( is_directory(url + 1, 1, &sb) ) {
1448 /* may be having subdir config */ 1416 /* may be having subdir config */
1449 parse_conf(url + 1, SUBDIR_PARSE); 1417 parse_conf(url + 1, SUBDIR_PARSE);
1418 ip_allowed = checkPerm(NULL, config->rmt_ip);
1450 } 1419 }
1451 *test = '/'; 1420 *test = '/';
1452 } 1421 }
@@ -1490,8 +1459,7 @@ BAD_REQUEST:
1490 } /* while extra header reading */ 1459 } /* while extra header reading */
1491 1460
1492 1461
1493 if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 || 1462 if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 || ip_allowed == 0) {
1494 checkPerm(NULL, config->rmt_ip) == 0) {
1495 /* protect listing [/path]/httpd_conf or IP deny */ 1463 /* protect listing [/path]/httpd_conf or IP deny */
1496#ifdef CONFIG_FEATURE_HTTPD_CGI 1464#ifdef CONFIG_FEATURE_HTTPD_CGI
1497FORBIDDEN: /* protect listing /cgi-bin */ 1465FORBIDDEN: /* protect listing /cgi-bin */
@@ -1525,8 +1493,8 @@ FORBIDDEN: /* protect listing /cgi-bin */
1525 } 1493 }
1526 1494
1527 if (strncmp(test, "cgi-bin", 7) == 0) { 1495 if (strncmp(test, "cgi-bin", 7) == 0) {
1528 if(test[7] == 0 || (test[7] == '/' && test[8] == 0)) 1496 if(test[7] == '/' && test[8] == 0)
1529 goto FORBIDDEN; // protect listing cgi-bin 1497 goto FORBIDDEN; // protect listing cgi-bin/
1530 sendCgi(url, prequest, urlArgs, body, length, cookie); 1498 sendCgi(url, prequest, urlArgs, body, length, cookie);
1531 } else { 1499 } else {
1532 if (prequest != request_GET) 1500 if (prequest != request_GET)
@@ -1587,7 +1555,6 @@ FORBIDDEN: /* protect listing /cgi-bin */
1587static int miniHttpd(int server) 1555static int miniHttpd(int server)
1588{ 1556{
1589 fd_set readfd, portfd; 1557 fd_set readfd, portfd;
1590 int nfound;
1591 1558
1592 FD_ZERO(&portfd); 1559 FD_ZERO(&portfd);
1593 FD_SET(server, &portfd); 1560 FD_SET(server, &portfd);
@@ -1597,16 +1564,7 @@ static int miniHttpd(int server)
1597 readfd = portfd; 1564 readfd = portfd;
1598 1565
1599 /* Now wait INDEFINATELY on the set of sockets! */ 1566 /* Now wait INDEFINATELY on the set of sockets! */
1600 nfound = select(server + 1, &readfd, 0, 0, 0); 1567 if (select(server + 1, &readfd, 0, 0, 0) > 0) {
1601
1602 switch (nfound) {
1603 case 0:
1604 /* select timeout error! */
1605 break ;
1606 case -1:
1607 /* select error */
1608 break;
1609 default:
1610 if (FD_ISSET(server, &readfd)) { 1568 if (FD_ISSET(server, &readfd)) {
1611 int on; 1569 int on;
1612 struct sockaddr_in fromAddr; 1570 struct sockaddr_in fromAddr;
@@ -1617,7 +1575,7 @@ static int miniHttpd(int server)
1617 (struct sockaddr *)&fromAddr, &fromAddrLen); 1575 (struct sockaddr *)&fromAddr, &fromAddrLen);
1618 1576
1619 if (s < 0) { 1577 if (s < 0) {
1620 continue; 1578 continue;
1621 } 1579 }
1622 config->accepted_socket = s; 1580 config->accepted_socket = s;
1623 addr = ntohl(fromAddr.sin_addr.s_addr); 1581 addr = ntohl(fromAddr.sin_addr.s_addr);
@@ -1629,7 +1587,7 @@ static int miniHttpd(int server)
1629 config->port = ntohs(fromAddr.sin_port); 1587 config->port = ntohs(fromAddr.sin_port);
1630#ifdef DEBUG 1588#ifdef DEBUG
1631 if (config->debugHttpd) { 1589 if (config->debugHttpd) {
1632 bb_error_msg("connection from IP=%s, port %u\n", 1590 bb_error_msg("connection from IP=%s, port %u\n",
1633 config->rmt_ip, config->port); 1591 config->rmt_ip, config->port);
1634 } 1592 }
1635#endif 1593#endif
@@ -1638,9 +1596,13 @@ static int miniHttpd(int server)
1638 setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof (on)); 1596 setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof (on));
1639 1597
1640 if (config->debugHttpd || fork() == 0) { 1598 if (config->debugHttpd || fork() == 0) {
1641 /* This is the spawned thread */ 1599 /* This is the spawned thread */
1642 handleIncoming(); 1600#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
1643 if(!config->debugHttpd) 1601 /* protect reload config, may be confuse checking */
1602 signal(SIGHUP, SIG_IGN);
1603#endif
1604 handleIncoming();
1605 if(!config->debugHttpd)
1644 exit(0); 1606 exit(0);
1645 } 1607 }
1646 close(s); 1608 close(s);
@@ -1682,7 +1644,7 @@ static void sighup_handler(int sig)
1682 sigemptyset(&sa.sa_mask); 1644 sigemptyset(&sa.sa_mask);
1683 sa.sa_flags = SA_RESTART; 1645 sa.sa_flags = SA_RESTART;
1684 sigaction(SIGHUP, &sa, NULL); 1646 sigaction(SIGHUP, &sa, NULL);
1685 parse_conf(default_patch_httpd_conf, 1647 parse_conf(default_path_httpd_conf,
1686 sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE); 1648 sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE);
1687} 1649}
1688#endif 1650#endif
@@ -1716,16 +1678,13 @@ int httpd_main(int argc, char *argv[])
1716 1678
1717 /* check if user supplied a port number */ 1679 /* check if user supplied a port number */
1718 for (;;) { 1680 for (;;) {
1719 int c = getopt( argc, argv, "c:h:" 1681 int c = getopt( argc, argv, "c:d:h:"
1720#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY 1682#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1721 "p:v" 1683 "p:v"
1722#endif 1684#endif
1723#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR 1685#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
1724 "e:" 1686 "e:"
1725#endif 1687#endif
1726#ifdef CONFIG_FEATURE_HTTPD_DECODE_URL_STR
1727 "d:"
1728#endif
1729#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH 1688#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1730 "r:" 1689 "r:"
1731#endif 1690#endif
@@ -1751,11 +1710,9 @@ int httpd_main(int argc, char *argv[])
1751 bb_error_msg_and_die("invalid %s for -p", optarg); 1710 bb_error_msg_and_die("invalid %s for -p", optarg);
1752 break; 1711 break;
1753#endif 1712#endif
1754#ifdef CONFIG_FEATURE_HTTPD_DECODE_URL_STR
1755 case 'd': 1713 case 'd':
1756 printf("%s", decodeString(optarg)); 1714 printf("%s", decodeString(optarg, 1));
1757 return 0; 1715 return 0;
1758#endif
1759#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR 1716#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
1760 case 'e': 1717 case 'e':
1761 printf("%s", encodeString(optarg)); 1718 printf("%s", encodeString(optarg));
@@ -1803,7 +1760,7 @@ int httpd_main(int argc, char *argv[])
1803#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP 1760#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
1804 sighup_handler(0); 1761 sighup_handler(0);
1805#else 1762#else
1806 parse_conf(default_patch_httpd_conf, FIRST_PARSE); 1763 parse_conf(default_path_httpd_conf, FIRST_PARSE);
1807#endif 1764#endif
1808 1765
1809#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY 1766#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY