1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249#include "libbb.h"
250#include "common_bufsiz.h"
251#if ENABLE_PAM
252
253# undef setlocale
254
255
256# include <security/pam_appl.h>
257# include <security/pam_misc.h>
258#endif
259#if ENABLE_FEATURE_USE_SENDFILE
260# include <sys/sendfile.h>
261#endif
262
263#define DEBUG 0
264
265#define IOBUF_SIZE 8192
266#define MAX_HTTP_HEADERS_SIZE (32*1024)
267
268#define HEADER_READ_TIMEOUT 60
269
270static const char DEFAULT_PATH_HTTPD_CONF[] ALIGN1 = "/etc";
271static const char HTTPD_CONF[] ALIGN1 = "httpd.conf";
272static const char HTTP_200[] ALIGN1 = "HTTP/1.0 200 OK\r\n";
273static const char index_html[] ALIGN1 = "index.html";
274
275typedef struct has_next_ptr {
276 struct has_next_ptr *next;
277} has_next_ptr;
278
279
280typedef struct Htaccess {
281 struct Htaccess *next;
282 char *after_colon;
283 char before_colon[1];
284} Htaccess;
285
286
287typedef struct Htaccess_IP {
288 struct Htaccess_IP *next;
289 unsigned ip;
290 unsigned mask;
291 int allow_deny;
292} Htaccess_IP;
293
294
295typedef struct Htaccess_Proxy {
296 struct Htaccess_Proxy *next;
297 char *url_from;
298 char *host_port;
299 char *url_to;
300} Htaccess_Proxy;
301
302typedef enum CGI_type {
303 CGI_NONE = 0,
304 CGI_NORMAL,
305 CGI_INDEX,
306 CGI_INTERPRETER,
307} CGI_type;
308
309enum {
310 HTTP_OK = 200,
311 HTTP_PARTIAL_CONTENT = 206,
312 HTTP_MOVED_TEMPORARILY = 302,
313 HTTP_BAD_REQUEST = 400,
314 HTTP_UNAUTHORIZED = 401,
315 HTTP_NOT_FOUND = 404,
316 HTTP_FORBIDDEN = 403,
317 HTTP_REQUEST_TIMEOUT = 408,
318 HTTP_NOT_IMPLEMENTED = 501,
319 HTTP_INTERNAL_SERVER_ERROR = 500,
320 HTTP_ENTITY_TOO_LARGE = 413,
321 HTTP_CONTINUE = 100,
322#if 0
323 HTTP_SWITCHING_PROTOCOLS = 101,
324 HTTP_CREATED = 201,
325 HTTP_ACCEPTED = 202,
326 HTTP_NON_AUTHORITATIVE_INFO = 203,
327 HTTP_NO_CONTENT = 204,
328 HTTP_MULTIPLE_CHOICES = 300,
329 HTTP_MOVED_PERMANENTLY = 301,
330 HTTP_NOT_MODIFIED = 304,
331 HTTP_PAYMENT_REQUIRED = 402,
332 HTTP_BAD_GATEWAY = 502,
333 HTTP_SERVICE_UNAVAILABLE = 503,
334#endif
335};
336
337static const uint16_t http_response_type[] ALIGN2 = {
338 HTTP_OK,
339#if ENABLE_FEATURE_HTTPD_RANGES
340 HTTP_PARTIAL_CONTENT,
341#endif
342 HTTP_MOVED_TEMPORARILY,
343 HTTP_REQUEST_TIMEOUT,
344 HTTP_NOT_IMPLEMENTED,
345#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
346 HTTP_UNAUTHORIZED,
347#endif
348 HTTP_NOT_FOUND,
349 HTTP_BAD_REQUEST,
350 HTTP_FORBIDDEN,
351 HTTP_INTERNAL_SERVER_ERROR,
352 HTTP_ENTITY_TOO_LARGE,
353#if 0
354 HTTP_CREATED,
355 HTTP_ACCEPTED,
356 HTTP_NO_CONTENT,
357 HTTP_MULTIPLE_CHOICES,
358 HTTP_MOVED_PERMANENTLY,
359 HTTP_NOT_MODIFIED,
360 HTTP_BAD_GATEWAY,
361 HTTP_SERVICE_UNAVAILABLE,
362#endif
363};
364
365static const struct {
366 const char *name;
367 const char *info;
368} http_response[ARRAY_SIZE(http_response_type)] = {
369 { "OK", NULL },
370#if ENABLE_FEATURE_HTTPD_RANGES
371 { "Partial Content", NULL },
372#endif
373 { "Found", NULL },
374 { "Request Timeout", "No request appeared within 60 seconds" },
375 { "Not Implemented", "The requested method is not recognized" },
376#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
377 { "Unauthorized", "" },
378#endif
379 { "Not Found", "The requested URL was not found" },
380 { "Bad Request", "Unsupported method" },
381 { "Forbidden", "" },
382 { "Internal Server Error", "Internal Server Error" },
383 { "Entity Too Large", "Entity Too Large" },
384#if 0
385 { "Created" },
386 { "Accepted" },
387 { "No Content" },
388 { "Multiple Choices" },
389 { "Moved Permanently" },
390 { "Not Modified" },
391 { "Bad Gateway", "" },
392 { "Service Unavailable", "" },
393#endif
394};
395
396struct globals {
397 int verbose;
398 smallint flg_deny_all;
399#if ENABLE_FEATURE_HTTPD_GZIP
400
401 smallint content_gzip;
402#endif
403 time_t last_mod;
404 char *rmt_ip_str;
405 const char *bind_addr_or_port;
406
407 char *g_query;
408 const char *opt_c_configFile;
409 const char *home_httpd;
410 const char *index_page;
411
412 const char *found_mime_type;
413 const char *found_moved_temporarily;
414 Htaccess_IP *ip_a_d;
415
416 IF_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;)
417 IF_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
418
419 off_t file_size;
420#if ENABLE_FEATURE_HTTPD_RANGES
421 off_t range_start;
422 off_t range_end;
423 off_t range_len;
424#endif
425
426#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
427 Htaccess *g_auth;
428#endif
429 Htaccess *mime_a;
430#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
431 Htaccess *script_i;
432#endif
433 char *iobuf;
434#define hdr_buf bb_common_bufsiz1
435#define sizeof_hdr_buf COMMON_BUFSIZE
436 char *hdr_ptr;
437 int hdr_cnt;
438#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
439 const char *http_error_page[ARRAY_SIZE(http_response_type)];
440#endif
441#if ENABLE_FEATURE_HTTPD_PROXY
442 Htaccess_Proxy *proxy;
443#endif
444};
445#define G (*ptr_to_globals)
446#define verbose (G.verbose )
447#define flg_deny_all (G.flg_deny_all )
448#if ENABLE_FEATURE_HTTPD_GZIP
449# define content_gzip (G.content_gzip )
450#else
451# define content_gzip 0
452#endif
453#define bind_addr_or_port (G.bind_addr_or_port)
454#define g_query (G.g_query )
455#define opt_c_configFile (G.opt_c_configFile )
456#define home_httpd (G.home_httpd )
457#define index_page (G.index_page )
458#define found_mime_type (G.found_mime_type )
459#define found_moved_temporarily (G.found_moved_temporarily)
460#define last_mod (G.last_mod )
461#define ip_a_d (G.ip_a_d )
462#define g_realm (G.g_realm )
463#define remoteuser (G.remoteuser )
464#define file_size (G.file_size )
465#if ENABLE_FEATURE_HTTPD_RANGES
466#define range_start (G.range_start )
467#define range_end (G.range_end )
468#define range_len (G.range_len )
469#else
470enum {
471 range_start = -1,
472 range_end = MAXINT(off_t) - 1,
473 range_len = MAXINT(off_t),
474};
475#endif
476#define rmt_ip_str (G.rmt_ip_str )
477#define g_auth (G.g_auth )
478#define mime_a (G.mime_a )
479#define script_i (G.script_i )
480#define iobuf (G.iobuf )
481#define hdr_ptr (G.hdr_ptr )
482#define hdr_cnt (G.hdr_cnt )
483#define http_error_page (G.http_error_page )
484#define proxy (G.proxy )
485#define INIT_G() do { \
486 setup_common_bufsiz(); \
487 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
488 IF_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \
489 IF_FEATURE_HTTPD_RANGES(range_start = -1;) \
490 bind_addr_or_port = "80"; \
491 index_page = index_html; \
492 file_size = -1; \
493} while (0)
494
495
496#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1)
497
498
499enum {
500 SEND_HEADERS = (1 << 0),
501 SEND_BODY = (1 << 1),
502 SEND_HEADERS_AND_BODY = SEND_HEADERS + SEND_BODY,
503};
504static void send_file_and_exit(const char *url, int what) NORETURN;
505
506static void free_llist(has_next_ptr **pptr)
507{
508 has_next_ptr *cur = *pptr;
509 while (cur) {
510 has_next_ptr *t = cur;
511 cur = cur->next;
512 free(t);
513 }
514 *pptr = NULL;
515}
516
517static ALWAYS_INLINE void free_Htaccess_list(Htaccess **pptr)
518{
519 free_llist((has_next_ptr**)pptr);
520}
521
522static ALWAYS_INLINE void free_Htaccess_IP_list(Htaccess_IP **pptr)
523{
524 free_llist((has_next_ptr**)pptr);
525}
526
527
528
529static int scan_ip(const char **strp, unsigned *ipp, unsigned char endc)
530{
531 const char *p = *strp;
532 int auto_mask = 8;
533 unsigned ip = 0;
534 int j;
535
536 if (*p == '/')
537 return -auto_mask;
538
539 for (j = 0; j < 4; j++) {
540 unsigned octet;
541
542 if ((*p < '0' || *p > '9') && *p != '/' && *p)
543 return -auto_mask;
544 octet = 0;
545 while (*p >= '0' && *p <= '9') {
546 octet *= 10;
547 octet += *p - '0';
548 if (octet > 255)
549 return -auto_mask;
550 p++;
551 }
552 if (*p == '.')
553 p++;
554 if (*p != '/' && *p)
555 auto_mask += 8;
556 ip = (ip << 8) | octet;
557 }
558 if (*p) {
559 if (*p != endc)
560 return -auto_mask;
561 p++;
562 if (*p == '\0')
563 return -auto_mask;
564 }
565 *ipp = ip;
566 *strp = p;
567 return auto_mask;
568}
569
570
571static int scan_ip_mask(const char *str, unsigned *ipp, unsigned *maskp)
572{
573 int i;
574 unsigned mask;
575 char *p;
576
577 i = scan_ip(&str, ipp, '/');
578 if (i < 0)
579 return i;
580
581 if (*str) {
582
583 i = bb_strtou(str, &p, 10);
584 if (*p == '.') {
585
586
587 return scan_ip(&str, maskp, '\0') - 32;
588 }
589 if (*p)
590 return -1;
591 }
592
593 if (i > 32)
594 return -1;
595
596 if (sizeof(unsigned) == 4 && i == 32) {
597
598 mask = 0;
599 } else {
600 mask = 0xffffffff;
601 mask >>= i;
602 }
603
604
605
606
607
608 *maskp = (uint32_t)(~mask);
609 return 0;
610}
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625enum {
626 FIRST_PARSE = 0,
627 SIGNALED_PARSE = 1,
628 SUBDIR_PARSE = 2,
629};
630static void parse_conf(const char *path, int flag)
631{
632
633 enum { TRY_CURDIR_PARSE = 3 };
634
635 FILE *f;
636 const char *filename;
637 char buf[160];
638
639
640 free_Htaccess_IP_list(&ip_a_d);
641 flg_deny_all = 0;
642
643 if (flag != SUBDIR_PARSE) {
644 free_Htaccess_list(&mime_a);
645#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
646 free_Htaccess_list(&g_auth);
647#endif
648#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
649 free_Htaccess_list(&script_i);
650#endif
651 }
652
653 filename = opt_c_configFile;
654 if (flag == SUBDIR_PARSE || filename == NULL) {
655 filename = alloca(strlen(path) + sizeof(HTTPD_CONF) + 2);
656 sprintf((char *)filename, "%s/%s", path, HTTPD_CONF);
657 }
658
659 while ((f = fopen_for_read(filename)) == NULL) {
660 if (flag >= SUBDIR_PARSE) {
661
662 return;
663 }
664 if (flag == FIRST_PARSE) {
665
666 if (opt_c_configFile)
667 bb_simple_perror_msg_and_die(opt_c_configFile);
668
669
670 }
671 flag = TRY_CURDIR_PARSE;
672 filename = HTTPD_CONF;
673 }
674
675#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
676
677 if (flag != SUBDIR_PARSE)
678 path = "";
679#endif
680
681
682
683
684
685
686
687
688
689
690
691 while (fgets(buf, sizeof(buf), f) != NULL) {
692 unsigned strlen_buf;
693 unsigned char ch;
694 char *after_colon;
695
696 {
697 char *p, *p0;
698
699 p0 = buf;
700
701
702
703
704 while ((ch = *p0) != '\0' && ch != '\n' && ch != '#'
705 && ch != ' ' && ch != '\t'
706 ) {
707 p0++;
708 }
709 p = p0;
710
711
712 while (ch != '\0' && ch != '\n' && ch != '#') {
713 if (ch != ' ' && ch != '\t') {
714 *p++ = ch;
715 }
716 ch = *++p0;
717 }
718 *p = '\0';
719 strlen_buf = p - buf;
720 if (strlen_buf == 0)
721 continue;
722 }
723
724 after_colon = strchr(buf, ':');
725
726 if (after_colon == NULL || *++after_colon == '\0')
727 goto config_error;
728
729 ch = (buf[0] & ~0x20);
730
731 if (ch == 'I') {
732 if (index_page != index_html)
733 free((char*)index_page);
734 index_page = xstrdup(after_colon);
735 continue;
736 }
737
738
739 if (flag == FIRST_PARSE && ch == 'H') {
740 home_httpd = xstrdup(after_colon);
741 xchdir(home_httpd);
742 continue;
743 }
744
745 if (ch == 'A' || ch == 'D') {
746 Htaccess_IP *pip;
747
748 if (*after_colon == '*') {
749 if (ch == 'D') {
750
751 flg_deny_all = 1;
752 }
753
754 continue;
755 }
756
757 pip = xzalloc(sizeof(*pip));
758 if (scan_ip_mask(after_colon, &pip->ip, &pip->mask)) {
759
760 ch = 'D';
761 pip->mask = 0;
762 }
763 pip->allow_deny = ch;
764 if (ch == 'D') {
765
766 pip->next = ip_a_d;
767 ip_a_d = pip;
768 } else {
769
770 Htaccess_IP *prev_IP = ip_a_d;
771 if (prev_IP == NULL) {
772 ip_a_d = pip;
773 } else {
774 while (prev_IP->next)
775 prev_IP = prev_IP->next;
776 prev_IP->next = pip;
777 }
778 }
779 continue;
780 }
781
782#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
783 if (flag == FIRST_PARSE && ch == 'E') {
784 unsigned i;
785 int status = atoi(buf + 1);
786
787 if (status < HTTP_CONTINUE) {
788 goto config_error;
789 }
790
791 for (i = 0; i < ARRAY_SIZE(http_response_type); i++) {
792 if (http_response_type[i] == status) {
793
794
795
796 http_error_page[i] = xstrdup(after_colon);
797 break;
798 }
799 }
800 continue;
801 }
802#endif
803
804#if ENABLE_FEATURE_HTTPD_PROXY
805 if (flag == FIRST_PARSE && ch == 'P') {
806
807 char *url_from, *host_port, *url_to;
808 Htaccess_Proxy *proxy_entry;
809
810 url_from = after_colon;
811 host_port = strchr(after_colon, ':');
812 if (host_port == NULL) {
813 goto config_error;
814 }
815 *host_port++ = '\0';
816 if (is_prefixed_with(host_port, "http://"))
817 host_port += 7;
818 if (*host_port == '\0') {
819 goto config_error;
820 }
821 url_to = strchr(host_port, '/');
822 if (url_to == NULL) {
823 goto config_error;
824 }
825 *url_to = '\0';
826 proxy_entry = xzalloc(sizeof(*proxy_entry));
827 proxy_entry->url_from = xstrdup(url_from);
828 proxy_entry->host_port = xstrdup(host_port);
829 *url_to = '/';
830 proxy_entry->url_to = xstrdup(url_to);
831 proxy_entry->next = proxy;
832 proxy = proxy_entry;
833 continue;
834 }
835#endif
836
837
838 ch = buf[0];
839
840 if (ch == '.'
841#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
842 || (ch == '*' && buf[1] == '.')
843#endif
844 ) {
845 char *p;
846 Htaccess *cur;
847
848 cur = xzalloc(sizeof(*cur) + strlen_buf);
849 strcpy(cur->before_colon, buf);
850 p = cur->before_colon + (after_colon - buf);
851 p[-1] = '\0';
852 cur->after_colon = p;
853 if (ch == '.') {
854
855 cur->next = mime_a;
856 mime_a = cur;
857 }
858#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
859 else {
860
861 cur->next = script_i;
862 script_i = cur;
863 }
864#endif
865 continue;
866 }
867
868#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
869 if (ch == '/') {
870 char *p;
871 Htaccess *cur;
872 unsigned file_len;
873
874
875
876 cur = xzalloc(sizeof(*cur)
877 + 1 + strlen(path)
878 + strlen_buf
879 );
880
881 sprintf(cur->before_colon, "/%s%.*s",
882 path,
883 (int) (after_colon - buf - 1),
884 buf);
885
886 p = bb_simplify_abs_path_inplace(cur->before_colon);
887 file_len = p - cur->before_colon;
888
889 strcpy(++p, after_colon);
890 cur->after_colon = p;
891
892
893
894 {
895 Htaccess *auth, **authp;
896
897 authp = &g_auth;
898 while ((auth = *authp) != NULL) {
899 if (file_len >= strlen(auth->before_colon)) {
900
901 cur->next = auth;
902 break;
903 }
904 authp = &auth->next;
905 }
906 *authp = cur;
907 }
908 continue;
909 }
910#endif
911
912
913 config_error:
914 bb_error_msg("config error '%s' in '%s'", buf, filename);
915 }
916
917 fclose(f);
918}
919
920#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
921
922
923
924
925
926
927
928
929static char *encodeString(const char *string)
930{
931
932
933 int len = strlen(string);
934 char *out = xmalloc(len * 6 + 1);
935 char *p = out;
936 char ch;
937
938 while ((ch = *string++) != '\0') {
939
940 if (isalnum(ch))
941 *p++ = ch;
942 else
943 p += sprintf(p, "&#%u;", (unsigned char) ch);
944 }
945 *p = '\0';
946 return out;
947}
948#endif
949
950#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
951
952
953
954
955
956
957
958
959static void decodeBase64(char *Data)
960{
961 const unsigned char *in = (const unsigned char *)Data;
962
963 unsigned ch = 0;
964 int i = 0;
965
966 while (*in) {
967 int t = *in++;
968
969 if (t >= '0' && t <= '9')
970 t = t - '0' + 52;
971 else if (t >= 'A' && t <= 'Z')
972 t = t - 'A';
973 else if (t >= 'a' && t <= 'z')
974 t = t - 'a' + 26;
975 else if (t == '+')
976 t = 62;
977 else if (t == '/')
978 t = 63;
979 else if (t == '=')
980 t = 0;
981 else
982 continue;
983
984 ch = (ch << 6) | t;
985 i++;
986 if (i == 4) {
987 *Data++ = (char) (ch >> 16);
988 *Data++ = (char) (ch >> 8);
989 *Data++ = (char) ch;
990 i = 0;
991 }
992 }
993 *Data = '\0';
994}
995#endif
996
997
998
999
1000static int openServer(void)
1001{
1002 unsigned n = bb_strtou(bind_addr_or_port, NULL, 10);
1003 if (!errno && n && n <= 0xffff)
1004 n = create_and_bind_stream_or_die(NULL, n);
1005 else
1006 n = create_and_bind_stream_or_die(bind_addr_or_port, 80);
1007 xlisten(n, 9);
1008 return n;
1009}
1010
1011
1012
1013
1014static void log_and_exit(void) NORETURN;
1015static void log_and_exit(void)
1016{
1017
1018
1019 shutdown(1, SHUT_WR);
1020
1021
1022
1023
1024
1025
1026
1027 if (verbose > 2)
1028 bb_error_msg("closed");
1029 _exit(xfunc_error_retval);
1030}
1031
1032
1033
1034
1035
1036
1037
1038
1039static void send_headers(unsigned responseNum)
1040{
1041 static const char RFC1123FMT[] ALIGN1 = "%a, %d %b %Y %H:%M:%S GMT";
1042
1043 char date_str[40];
1044
1045 struct tm tm;
1046 const char *responseString = "";
1047 const char *infoString = NULL;
1048#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
1049 const char *error_page = NULL;
1050#endif
1051 unsigned len;
1052 unsigned i;
1053 time_t timer = time(NULL);
1054
1055 for (i = 0; i < ARRAY_SIZE(http_response_type); i++) {
1056 if (http_response_type[i] == responseNum) {
1057 responseString = http_response[i].name;
1058 infoString = http_response[i].info;
1059#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
1060 error_page = http_error_page[i];
1061#endif
1062 break;
1063 }
1064 }
1065
1066 if (verbose)
1067 bb_error_msg("response:%u", responseNum);
1068
1069
1070
1071
1072
1073
1074 strftime(date_str, sizeof(date_str), RFC1123FMT, gmtime_r(&timer, &tm));
1075
1076 len = sprintf(iobuf,
1077 "HTTP/1.0 %u %s\r\n"
1078 "Date: %s\r\n"
1079 "Connection: close\r\n",
1080 responseNum, responseString,
1081 date_str
1082 );
1083
1084 if (responseNum != HTTP_OK || found_mime_type) {
1085 len += sprintf(iobuf + len,
1086 "Content-type: %s\r\n",
1087
1088 (responseNum != HTTP_OK ? "text/html" : found_mime_type)
1089 );
1090 }
1091
1092#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
1093 if (responseNum == HTTP_UNAUTHORIZED) {
1094 len += sprintf(iobuf + len,
1095 "WWW-Authenticate: Basic realm=\"%.999s\"\r\n",
1096 g_realm
1097 );
1098 }
1099#endif
1100 if (responseNum == HTTP_MOVED_TEMPORARILY) {
1101
1102
1103
1104
1105
1106
1107
1108 len += snprintf(iobuf + len, IOBUF_SIZE-3 - len,
1109 "Location: %s/%s%s\r\n",
1110 found_moved_temporarily,
1111 (g_query ? "?" : ""),
1112 (g_query ? g_query : "")
1113 );
1114 if (len > IOBUF_SIZE-3)
1115 len = IOBUF_SIZE-3;
1116 }
1117
1118#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
1119 if (error_page && access(error_page, R_OK) == 0) {
1120 iobuf[len++] = '\r';
1121 iobuf[len++] = '\n';
1122 if (DEBUG) {
1123 iobuf[len] = '\0';
1124 fprintf(stderr, "headers: '%s'\n", iobuf);
1125 }
1126 full_write(STDOUT_FILENO, iobuf, len);
1127 if (DEBUG)
1128 fprintf(stderr, "writing error page: '%s'\n", error_page);
1129 return send_file_and_exit(error_page, SEND_BODY);
1130 }
1131#endif
1132
1133 if (file_size != -1) {
1134 strftime(date_str, sizeof(date_str), RFC1123FMT, gmtime_r(&last_mod, &tm));
1135#if ENABLE_FEATURE_HTTPD_RANGES
1136 if (responseNum == HTTP_PARTIAL_CONTENT) {
1137 len += sprintf(iobuf + len,
1138 "Content-Range: bytes %"OFF_FMT"u-%"OFF_FMT"u/%"OFF_FMT"u\r\n",
1139 range_start,
1140 range_end,
1141 file_size
1142 );
1143 file_size = range_end - range_start + 1;
1144 }
1145#endif
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175 len += sprintf(iobuf + len,
1176#if ENABLE_FEATURE_HTTPD_RANGES
1177 "Accept-Ranges: bytes\r\n"
1178#endif
1179 "Last-Modified: %s\r\n"
1180
1181
1182
1183
1184
1185
1186 "Content-Length: %"OFF_FMT"u\r\n",
1187 date_str,
1188 file_size
1189 );
1190 }
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201 if (content_gzip)
1202 len += sprintf(iobuf + len, "Content-Encoding: gzip\r\n");
1203
1204 iobuf[len++] = '\r';
1205 iobuf[len++] = '\n';
1206 if (infoString) {
1207 len += sprintf(iobuf + len,
1208 "<HTML><HEAD><TITLE>%u %s</TITLE></HEAD>\n"
1209 "<BODY><H1>%u %s</H1>\n"
1210 "%s\n"
1211 "</BODY></HTML>\n",
1212 responseNum, responseString,
1213 responseNum, responseString,
1214 infoString
1215 );
1216 }
1217 if (DEBUG) {
1218 iobuf[len] = '\0';
1219 fprintf(stderr, "headers: '%s'\n", iobuf);
1220 }
1221 if (full_write(STDOUT_FILENO, iobuf, len) != len) {
1222 if (verbose > 1)
1223 bb_perror_msg("error");
1224 log_and_exit();
1225 }
1226}
1227
1228static void send_headers_and_exit(int responseNum) NORETURN;
1229static void send_headers_and_exit(int responseNum)
1230{
1231 IF_FEATURE_HTTPD_GZIP(content_gzip = 0;)
1232 send_headers(responseNum);
1233 log_and_exit();
1234}
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244static unsigned get_line(void)
1245{
1246 unsigned count;
1247 char c;
1248
1249 count = 0;
1250 while (1) {
1251 if (hdr_cnt <= 0) {
1252 alarm(HEADER_READ_TIMEOUT);
1253 hdr_cnt = safe_read(STDIN_FILENO, hdr_buf, sizeof_hdr_buf);
1254 if (hdr_cnt <= 0)
1255 goto ret;
1256 hdr_ptr = hdr_buf;
1257 }
1258 hdr_cnt--;
1259 c = *hdr_ptr++;
1260 if (c == '\r')
1261 continue;
1262 if (c == '\n')
1263 break;
1264 iobuf[count] = c;
1265 if (count < (IOBUF_SIZE - 1))
1266 count++;
1267 }
1268 ret:
1269 iobuf[count] = '\0';
1270 return count;
1271}
1272
1273#if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY
1274
1275
1276static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post_len) NORETURN;
1277static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post_len)
1278{
1279 enum { FROM_CGI = 1, TO_CGI = 2 };
1280 struct pollfd pfd[3];
1281 int out_cnt;
1282 int count;
1283
1284
1285
1286
1287
1288
1289 signal(SIGPIPE, SIG_IGN);
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299 post_len -= hdr_cnt;
1300
1301
1302
1303 out_cnt = 0;
1304 pfd[FROM_CGI].fd = fromCgi_rd;
1305 pfd[FROM_CGI].events = POLLIN;
1306 pfd[TO_CGI].fd = toCgi_wr;
1307 while (1) {
1308
1309
1310
1311 pfd[0].fd = -1;
1312 pfd[0].events = POLLIN;
1313 pfd[0].revents = 0;
1314
1315
1316
1317
1318
1319
1320
1321
1322 pfd[TO_CGI].events = POLLOUT;
1323 pfd[TO_CGI].revents = 0;
1324
1325 if (toCgi_wr && hdr_cnt <= 0) {
1326 if (post_len > 0) {
1327
1328 pfd[0].fd = 0;
1329 } else {
1330
1331
1332
1333 if (toCgi_wr != fromCgi_rd)
1334 close(toCgi_wr);
1335 toCgi_wr = 0;
1336 }
1337 }
1338
1339
1340 count = safe_poll(pfd, hdr_cnt > 0 ? TO_CGI+1 : FROM_CGI+1, -1);
1341 if (count <= 0) {
1342#if 0
1343 if (safe_waitpid(pid, &status, WNOHANG) <= 0) {
1344
1345
1346 continue;
1347 }
1348 if (DEBUG && WIFEXITED(status))
1349 bb_error_msg("CGI exited, status=%u", WEXITSTATUS(status));
1350 if (DEBUG && WIFSIGNALED(status))
1351 bb_error_msg("CGI killed, signal=%u", WTERMSIG(status));
1352#endif
1353 break;
1354 }
1355
1356 if (pfd[TO_CGI].revents) {
1357
1358
1359 count = safe_write(toCgi_wr, hdr_ptr, hdr_cnt);
1360
1361
1362
1363
1364 if (count > 0) {
1365 hdr_ptr += count;
1366 hdr_cnt -= count;
1367 } else {
1368
1369 hdr_cnt = post_len = 0;
1370 }
1371 }
1372
1373 if (pfd[0].revents) {
1374
1375
1376
1377
1378
1379
1380 count = safe_read(STDIN_FILENO, hdr_buf, sizeof_hdr_buf);
1381 if (count > 0) {
1382 hdr_cnt = count;
1383 hdr_ptr = hdr_buf;
1384 post_len -= count;
1385 } else {
1386
1387 post_len = 0;
1388 }
1389 }
1390
1391 if (pfd[FROM_CGI].revents) {
1392
1393 char *rbuf = iobuf;
1394
1395
1396 if (out_cnt >= 0) {
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409 count = safe_read(fromCgi_rd, rbuf + out_cnt, IOBUF_SIZE - 8);
1410 if (count <= 0) {
1411
1412
1413 if (out_cnt) {
1414 full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1);
1415 full_write(STDOUT_FILENO, rbuf, out_cnt);
1416 }
1417 break;
1418 }
1419 out_cnt += count;
1420 count = 0;
1421
1422 if (out_cnt >= 8 && memcmp(rbuf, "Status: ", 8) == 0) {
1423
1424 if (full_write(STDOUT_FILENO, HTTP_200, 9) != 9)
1425 break;
1426
1427 rbuf += 8;
1428 count = out_cnt - 8;
1429 out_cnt = -1;
1430 } else if (out_cnt >= 4) {
1431
1432 if (memcmp(rbuf, HTTP_200, 4) != 0) {
1433
1434 if (full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1)
1435 break;
1436 }
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446 count = out_cnt;
1447 out_cnt = -1;
1448 }
1449 } else {
1450 count = safe_read(fromCgi_rd, rbuf, IOBUF_SIZE);
1451 if (count <= 0)
1452 break;
1453 }
1454 if (full_write(STDOUT_FILENO, rbuf, count) != count)
1455 break;
1456 if (DEBUG)
1457 fprintf(stderr, "cgi read %d bytes: '%.*s'\n", count, count, rbuf);
1458 }
1459 }
1460 log_and_exit();
1461}
1462#endif
1463
1464#if ENABLE_FEATURE_HTTPD_CGI
1465
1466static void setenv1(const char *name, const char *value)
1467{
1468 setenv(name, value ? value : "", 1);
1469}
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483static void send_cgi_and_exit(
1484 const char *url,
1485 const char *orig_uri,
1486 const char *request,
1487 int post_len) NORETURN;
1488static void send_cgi_and_exit(
1489 const char *url,
1490 const char *orig_uri,
1491 const char *request,
1492 int post_len)
1493{
1494 struct fd_pair fromCgi;
1495 struct fd_pair toCgi;
1496 char *script, *last_slash;
1497 int pid;
1498
1499
1500
1501 url = xstrdup(url);
1502
1503
1504
1505
1506
1507
1508
1509
1510 last_slash = script = (char*)url;
1511 while ((script = strchr(script + 1, '/')) != NULL) {
1512 int dir;
1513 *script = '\0';
1514 dir = is_directory(url + 1, 1);
1515 *script = '/';
1516 if (!dir) {
1517
1518 break;
1519 }
1520
1521 last_slash = script;
1522 }
1523 setenv1("PATH_INFO", script);
1524 setenv1("REQUEST_METHOD", request);
1525 if (g_query) {
1526 putenv(xasprintf("%s=%s?%s", "REQUEST_URI", orig_uri, g_query));
1527 } else {
1528 setenv1("REQUEST_URI", orig_uri);
1529 }
1530 if (script != NULL)
1531 *script = '\0';
1532
1533
1534 if (home_httpd[0] == '/') {
1535 char *fullpath = concat_path_file(home_httpd, url);
1536 setenv1("SCRIPT_FILENAME", fullpath);
1537 }
1538
1539 setenv1("SCRIPT_NAME", url);
1540
1541
1542
1543
1544
1545
1546
1547 setenv1("QUERY_STRING", g_query);
1548 putenv((char*)"SERVER_SOFTWARE=busybox httpd/"BB_VER);
1549 putenv((char*)"SERVER_PROTOCOL=HTTP/1.0");
1550 putenv((char*)"GATEWAY_INTERFACE=CGI/1.1");
1551
1552
1553
1554
1555
1556 {
1557 char *p = rmt_ip_str ? rmt_ip_str : (char*)"";
1558 char *cp = strrchr(p, ':');
1559 if (ENABLE_FEATURE_IPV6 && cp && strchr(cp, ']'))
1560 cp = NULL;
1561 if (cp) *cp = '\0';
1562 setenv1("REMOTE_ADDR", p);
1563 if (cp) {
1564 *cp = ':';
1565#if ENABLE_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
1566 setenv1("REMOTE_PORT", cp + 1);
1567#endif
1568 }
1569 }
1570 if (post_len)
1571 putenv(xasprintf("CONTENT_LENGTH=%u", post_len));
1572#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
1573 if (remoteuser) {
1574 setenv1("REMOTE_USER", remoteuser);
1575 putenv((char*)"AUTH_TYPE=Basic");
1576 }
1577#endif
1578
1579
1580
1581 xpiped_pair(fromCgi);
1582 xpiped_pair(toCgi);
1583
1584 pid = vfork();
1585 if (pid < 0) {
1586
1587 log_and_exit();
1588 }
1589
1590 if (pid == 0) {
1591
1592 char *argv[3];
1593
1594 xfunc_error_retval = 242;
1595
1596
1597 close(toCgi.wr);
1598 close(fromCgi.rd);
1599 xmove_fd(toCgi.rd, 0);
1600 xmove_fd(fromCgi.wr, 1);
1601
1602
1603
1604
1605
1606 script = last_slash;
1607 if (script != url) {
1608 *script = '\0';
1609 if (chdir(url + 1) != 0) {
1610 bb_perror_msg("can't change directory to '%s'", url + 1);
1611 goto error_execing_cgi;
1612 }
1613
1614 }
1615 script++;
1616
1617
1618 argv[0] = script;
1619 argv[1] = NULL;
1620
1621#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
1622 {
1623 char *suffix = strrchr(script, '.');
1624
1625 if (suffix) {
1626 Htaccess *cur;
1627 for (cur = script_i; cur; cur = cur->next) {
1628 if (strcmp(cur->before_colon + 1, suffix) == 0) {
1629
1630 argv[0] = cur->after_colon;
1631 argv[1] = script;
1632 argv[2] = NULL;
1633 break;
1634 }
1635 }
1636 }
1637 }
1638#endif
1639
1640 bb_signals(0
1641 | (1 << SIGCHLD)
1642 | (1 << SIGPIPE)
1643 | (1 << SIGHUP)
1644 , SIG_DFL);
1645
1646
1647
1648
1649 execv(argv[0], argv);
1650 if (verbose)
1651 bb_perror_msg("can't execute '%s'", argv[0]);
1652 error_execing_cgi:
1653
1654
1655 send_headers_and_exit(HTTP_NOT_FOUND);
1656 }
1657
1658
1659
1660
1661 xfunc_error_retval = 0;
1662
1663
1664 close(fromCgi.wr);
1665 close(toCgi.rd);
1666 cgi_io_loop_and_exit(fromCgi.rd, toCgi.wr, post_len);
1667}
1668
1669#endif
1670
1671
1672
1673
1674
1675
1676
1677
1678static NOINLINE void send_file_and_exit(const char *url, int what)
1679{
1680 char *suffix;
1681 int fd;
1682 ssize_t count;
1683
1684 if (content_gzip) {
1685
1686 char *gzurl = xasprintf("%s.gz", url);
1687 fd = open(gzurl, O_RDONLY);
1688 free(gzurl);
1689 if (fd != -1) {
1690 struct stat sb;
1691 fstat(fd, &sb);
1692 file_size = sb.st_size;
1693 last_mod = sb.st_mtime;
1694 } else {
1695 IF_FEATURE_HTTPD_GZIP(content_gzip = 0;)
1696 fd = open(url, O_RDONLY);
1697 }
1698 } else {
1699 fd = open(url, O_RDONLY);
1700 }
1701 if (fd < 0) {
1702 if (DEBUG)
1703 bb_perror_msg("can't open '%s'", url);
1704
1705
1706
1707 if (what != SEND_BODY)
1708 send_headers_and_exit(HTTP_NOT_FOUND);
1709 log_and_exit();
1710 }
1711
1712
1713 signal(SIGPIPE, SIG_IGN);
1714
1715
1716
1717 suffix = strrchr(url, '.');
1718 if (suffix) {
1719 static const char suffixTable[] ALIGN1 =
1720
1721
1722
1723 ".txt.h.c.cc.cpp\0" "text/plain\0"
1724
1725 ".htm.html\0" "text/html\0"
1726 ".jpg.jpeg\0" "image/jpeg\0"
1727 ".gif\0" "image/gif\0"
1728 ".png\0" "image/png\0"
1729 ".svg\0" "image/svg+xml\0"
1730
1731 ".css\0" "text/css\0"
1732 ".js\0" "application/javascript\0"
1733 ".wav\0" "audio/wav\0"
1734 ".avi\0" "video/x-msvideo\0"
1735 ".qt.mov\0" "video/quicktime\0"
1736 ".mpe.mpeg\0" "video/mpeg\0"
1737 ".mid.midi\0" "audio/midi\0"
1738 ".mp3\0" "audio/mpeg\0"
1739#if 0
1740 ".au\0" "audio/basic\0"
1741 ".pac\0" "application/x-ns-proxy-autoconfig\0"
1742 ".vrml.wrl\0" "model/vrml\0"
1743#endif
1744
1745 ;
1746 Htaccess *cur;
1747
1748
1749 const char *table = suffixTable;
1750 const char *table_next;
1751 for (; *table; table = table_next) {
1752 const char *try_suffix;
1753 const char *mime_type;
1754 mime_type = table + strlen(table) + 1;
1755 table_next = mime_type + strlen(mime_type) + 1;
1756 try_suffix = strstr(table, suffix);
1757 if (!try_suffix)
1758 continue;
1759 try_suffix += strlen(suffix);
1760 if (*try_suffix == '\0' || *try_suffix == '.') {
1761 found_mime_type = mime_type;
1762 break;
1763 }
1764
1765
1766
1767
1768
1769 break;
1770 }
1771
1772 for (cur = mime_a; cur; cur = cur->next) {
1773 if (strcmp(cur->before_colon, suffix) == 0) {
1774 found_mime_type = cur->after_colon;
1775 break;
1776 }
1777 }
1778 }
1779
1780 if (DEBUG)
1781 bb_error_msg("sending file '%s' content-type: %s",
1782 url, found_mime_type);
1783
1784#if ENABLE_FEATURE_HTTPD_RANGES
1785 if (what == SEND_BODY
1786 || content_gzip
1787 ) {
1788 range_start = -1;
1789 }
1790 range_len = MAXINT(off_t);
1791 if (range_start >= 0) {
1792 if (!range_end || range_end > file_size - 1) {
1793 range_end = file_size - 1;
1794 }
1795 if (range_end < range_start
1796 || lseek(fd, range_start, SEEK_SET) != range_start
1797 ) {
1798 lseek(fd, 0, SEEK_SET);
1799 range_start = -1;
1800 } else {
1801 range_len = range_end - range_start + 1;
1802 send_headers(HTTP_PARTIAL_CONTENT);
1803 what = SEND_BODY;
1804 }
1805 }
1806#endif
1807 if (what & SEND_HEADERS)
1808 send_headers(HTTP_OK);
1809#if ENABLE_FEATURE_USE_SENDFILE
1810 {
1811 off_t offset = range_start;
1812 while (1) {
1813
1814 ssize_t sz = MAXINT(ssize_t) - 0xffff;
1815 IF_FEATURE_HTTPD_RANGES(if (sz > range_len) sz = range_len;)
1816 count = sendfile(STDOUT_FILENO, fd, &offset, sz);
1817 if (count < 0) {
1818 if (offset == range_start)
1819 break;
1820 goto fin;
1821 }
1822 IF_FEATURE_HTTPD_RANGES(range_len -= count;)
1823 if (count == 0 || range_len == 0)
1824 log_and_exit();
1825 }
1826 }
1827#endif
1828 while ((count = safe_read(fd, iobuf, IOBUF_SIZE)) > 0) {
1829 ssize_t n;
1830 IF_FEATURE_HTTPD_RANGES(if (count > range_len) count = range_len;)
1831 n = full_write(STDOUT_FILENO, iobuf, count);
1832 if (count != n)
1833 break;
1834 IF_FEATURE_HTTPD_RANGES(range_len -= count;)
1835 if (range_len == 0)
1836 break;
1837 }
1838 if (count < 0) {
1839 IF_FEATURE_USE_SENDFILE(fin:)
1840 if (verbose > 1)
1841 bb_perror_msg("error");
1842 }
1843 log_and_exit();
1844}
1845
1846static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(unsigned remote_ip)
1847{
1848 Htaccess_IP *cur;
1849
1850 for (cur = ip_a_d; cur; cur = cur->next) {
1851#if DEBUG
1852 fprintf(stderr,
1853 "checkPermIP: '%s' ? '%u.%u.%u.%u/%u.%u.%u.%u'\n",
1854 rmt_ip_str,
1855 (unsigned char)(cur->ip >> 24),
1856 (unsigned char)(cur->ip >> 16),
1857 (unsigned char)(cur->ip >> 8),
1858 (unsigned char)(cur->ip),
1859 (unsigned char)(cur->mask >> 24),
1860 (unsigned char)(cur->mask >> 16),
1861 (unsigned char)(cur->mask >> 8),
1862 (unsigned char)(cur->mask)
1863 );
1864#endif
1865 if ((remote_ip & cur->mask) == cur->ip) {
1866 if (cur->allow_deny == 'A')
1867 return;
1868 send_headers_and_exit(HTTP_FORBIDDEN);
1869 }
1870 }
1871
1872 if (flg_deny_all)
1873 send_headers_and_exit(HTTP_FORBIDDEN);
1874}
1875
1876#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
1877
1878# if ENABLE_PAM
1879struct pam_userinfo {
1880 const char *name;
1881 const char *pw;
1882};
1883
1884static int pam_talker(int num_msg,
1885 const struct pam_message **msg,
1886 struct pam_response **resp,
1887 void *appdata_ptr)
1888{
1889 int i;
1890 struct pam_userinfo *userinfo = (struct pam_userinfo *) appdata_ptr;
1891 struct pam_response *response;
1892
1893 if (!resp || !msg || !userinfo)
1894 return PAM_CONV_ERR;
1895
1896
1897 response = xzalloc(num_msg * sizeof(*response));
1898
1899
1900 for (i = 0; i < num_msg; i++) {
1901 const char *s;
1902
1903 switch (msg[i]->msg_style) {
1904 case PAM_PROMPT_ECHO_ON:
1905 s = userinfo->name;
1906 break;
1907 case PAM_PROMPT_ECHO_OFF:
1908 s = userinfo->pw;
1909 break;
1910 case PAM_ERROR_MSG:
1911 case PAM_TEXT_INFO:
1912 s = "";
1913 break;
1914 default:
1915 free(response);
1916 return PAM_CONV_ERR;
1917 }
1918 response[i].resp = xstrdup(s);
1919 if (PAM_SUCCESS != 0)
1920 response[i].resp_retcode = PAM_SUCCESS;
1921 }
1922 *resp = response;
1923 return PAM_SUCCESS;
1924}
1925# endif
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936static int check_user_passwd(const char *path, char *user_and_passwd)
1937{
1938 Htaccess *cur;
1939 const char *prev = NULL;
1940
1941 for (cur = g_auth; cur; cur = cur->next) {
1942 const char *dir_prefix;
1943 size_t len;
1944 int r;
1945
1946 dir_prefix = cur->before_colon;
1947
1948
1949
1950 if (prev && strcmp(prev, dir_prefix) != 0)
1951 continue;
1952
1953 if (DEBUG)
1954 fprintf(stderr, "checkPerm: '%s' ? '%s'\n", dir_prefix, user_and_passwd);
1955
1956
1957 len = strlen(dir_prefix);
1958 if (len != 1
1959 && (strncmp(dir_prefix, path, len) != 0
1960 || (path[len] != '/' && path[len] != '\0')
1961 )
1962 ) {
1963 continue;
1964 }
1965
1966
1967 prev = dir_prefix;
1968
1969 if (ENABLE_FEATURE_HTTPD_AUTH_MD5) {
1970 char *colon_after_user;
1971 const char *passwd;
1972# if ENABLE_FEATURE_SHADOWPASSWDS && !ENABLE_PAM
1973 char sp_buf[256];
1974# endif
1975
1976 colon_after_user = strchr(user_and_passwd, ':');
1977 if (!colon_after_user)
1978 goto bad_input;
1979
1980
1981 if (cur->after_colon[0] != '*'
1982 && strncmp(cur->after_colon, user_and_passwd,
1983 colon_after_user - user_and_passwd + 1) != 0
1984 ) {
1985 continue;
1986 }
1987
1988
1989 passwd = strchr(cur->after_colon, ':');
1990 if (!passwd)
1991 goto bad_input;
1992 passwd++;
1993 if (passwd[0] == '*') {
1994# if ENABLE_PAM
1995 struct pam_userinfo userinfo;
1996 struct pam_conv conv_info = { &pam_talker, (void *) &userinfo };
1997 pam_handle_t *pamh;
1998
1999 *colon_after_user = '\0';
2000 userinfo.name = user_and_passwd;
2001 userinfo.pw = colon_after_user + 1;
2002 r = pam_start("httpd", user_and_passwd, &conv_info, &pamh) != PAM_SUCCESS;
2003 if (r == 0) {
2004 r = pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK) != PAM_SUCCESS
2005 || pam_acct_mgmt(pamh, PAM_DISALLOW_NULL_AUTHTOK) != PAM_SUCCESS
2006 ;
2007 pam_end(pamh, PAM_SUCCESS);
2008 }
2009 *colon_after_user = ':';
2010 goto end_check_passwd;
2011# else
2012# if ENABLE_FEATURE_SHADOWPASSWDS
2013
2014 struct spwd spw;
2015# endif
2016 struct passwd *pw;
2017
2018 *colon_after_user = '\0';
2019 pw = getpwnam(user_and_passwd);
2020 *colon_after_user = ':';
2021 if (!pw || !pw->pw_passwd)
2022 continue;
2023 passwd = pw->pw_passwd;
2024# if ENABLE_FEATURE_SHADOWPASSWDS
2025 if ((passwd[0] == 'x' || passwd[0] == '*') && !passwd[1]) {
2026
2027
2028 struct spwd *result = NULL;
2029 r = getspnam_r(pw->pw_name, &spw, sp_buf, sizeof(sp_buf), &result);
2030 if (r == 0 && result)
2031 passwd = result->sp_pwdp;
2032 }
2033# endif
2034
2035
2036
2037 goto check_encrypted;
2038# endif
2039 }
2040
2041
2042 if (passwd[0] == '$' && isdigit(passwd[1])) {
2043 char *encrypted;
2044# if !ENABLE_PAM
2045 check_encrypted:
2046# endif
2047
2048 encrypted = pw_encrypt(
2049 colon_after_user + 1,
2050 passwd,
2051 0
2052 );
2053 r = strcmp(encrypted, passwd);
2054 free(encrypted);
2055 } else {
2056
2057 r = strcmp(colon_after_user + 1, passwd);
2058 }
2059 goto end_check_passwd;
2060 }
2061 bad_input:
2062
2063 r = strcmp(cur->after_colon, user_and_passwd);
2064 end_check_passwd:
2065 if (r == 0) {
2066 remoteuser = xstrndup(user_and_passwd,
2067 strchrnul(user_and_passwd, ':') - user_and_passwd
2068 );
2069 return 1;
2070 }
2071 }
2072
2073
2074 return (prev == NULL);
2075}
2076#endif
2077
2078#if ENABLE_FEATURE_HTTPD_PROXY
2079static Htaccess_Proxy *find_proxy_entry(const char *url)
2080{
2081 Htaccess_Proxy *p;
2082 for (p = proxy; p; p = p->next) {
2083 if (is_prefixed_with(url, p->url_from))
2084 return p;
2085 }
2086 return NULL;
2087}
2088#endif
2089
2090
2091
2092
2093static void send_REQUEST_TIMEOUT_and_exit(int sig) NORETURN;
2094static void send_REQUEST_TIMEOUT_and_exit(int sig UNUSED_PARAM)
2095{
2096 send_headers_and_exit(HTTP_REQUEST_TIMEOUT);
2097}
2098
2099
2100
2101
2102static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) NORETURN;
2103static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2104{
2105 static const char request_GET[] ALIGN1 = "GET";
2106 struct stat sb;
2107 char *urlcopy;
2108 char *urlp;
2109 char *tptr;
2110 unsigned remote_ip;
2111#if ENABLE_FEATURE_HTTPD_CGI
2112 unsigned total_headers_len;
2113#endif
2114#if ENABLE_FEATURE_HTTPD_CGI
2115 static const char request_HEAD[] ALIGN1 = "HEAD";
2116 const char *prequest;
2117 unsigned long length = 0;
2118 enum CGI_type cgi_type = CGI_NONE;
2119#elif ENABLE_FEATURE_HTTPD_PROXY
2120#define prequest request_GET
2121 unsigned long length = 0;
2122#endif
2123#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
2124 smallint authorized = -1;
2125#endif
2126 char *HTTP_slash;
2127
2128
2129
2130 iobuf = xmalloc(IOBUF_SIZE);
2131
2132 remote_ip = 0;
2133 if (fromAddr->u.sa.sa_family == AF_INET) {
2134 remote_ip = ntohl(fromAddr->u.sin.sin_addr.s_addr);
2135 }
2136#if ENABLE_FEATURE_IPV6
2137 if (fromAddr->u.sa.sa_family == AF_INET6
2138 && fromAddr->u.sin6.sin6_addr.s6_addr32[0] == 0
2139 && fromAddr->u.sin6.sin6_addr.s6_addr32[1] == 0
2140 && ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[2]) == 0xffff)
2141 remote_ip = ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[3]);
2142#endif
2143 if (ENABLE_FEATURE_HTTPD_CGI || DEBUG || verbose) {
2144
2145 rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr->u.sa);
2146 }
2147 if (verbose) {
2148
2149 if (rmt_ip_str)
2150 applet_name = rmt_ip_str;
2151 if (verbose > 2)
2152 bb_error_msg("connected");
2153 }
2154 if_ip_denied_send_HTTP_FORBIDDEN_and_exit(remote_ip);
2155
2156
2157 signal(SIGALRM, send_REQUEST_TIMEOUT_and_exit);
2158
2159 if (!get_line())
2160 send_headers_and_exit(HTTP_BAD_REQUEST);
2161
2162
2163
2164
2165 urlp = strchr(iobuf, ' ');
2166 if (urlp == NULL)
2167 send_headers_and_exit(HTTP_BAD_REQUEST);
2168 *urlp++ = '\0';
2169#if ENABLE_FEATURE_HTTPD_CGI
2170 prequest = request_GET;
2171 if (strcasecmp(iobuf, prequest) != 0) {
2172 prequest = request_HEAD;
2173 if (strcasecmp(iobuf, prequest) != 0) {
2174 prequest = "POST";
2175 if (strcasecmp(iobuf, prequest) != 0)
2176 send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
2177 }
2178 }
2179#else
2180 if (strcasecmp(iobuf, request_GET) != 0)
2181 send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
2182#endif
2183
2184
2185 if (urlp[0] != '/')
2186 send_headers_and_exit(HTTP_BAD_REQUEST);
2187
2188
2189 HTTP_slash = strchr(urlp, ' ');
2190
2191 if (!HTTP_slash || strncmp(HTTP_slash + 1, HTTP_200, 5) != 0)
2192 send_headers_and_exit(HTTP_BAD_REQUEST);
2193 *HTTP_slash++ = '\0';
2194
2195
2196 urlcopy = alloca((HTTP_slash - urlp) + 2 + strlen(index_page));
2197
2198
2199 strcpy(urlcopy, urlp);
2200
2201
2202#if ENABLE_FEATURE_HTTPD_PROXY
2203 {
2204 int proxy_fd;
2205 len_and_sockaddr *lsa;
2206 Htaccess_Proxy *proxy_entry = find_proxy_entry(urlcopy);
2207
2208 if (proxy_entry) {
2209 if (verbose > 1)
2210 bb_error_msg("proxy:%s", urlcopy);
2211 lsa = host2sockaddr(proxy_entry->host_port, 80);
2212 if (!lsa)
2213 send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
2214 proxy_fd = socket(lsa->u.sa.sa_family, SOCK_STREAM, 0);
2215 if (proxy_fd < 0)
2216 send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
2217 if (connect(proxy_fd, &lsa->u.sa, lsa->len) < 0)
2218 send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
2219
2220 alarm(0);
2221
2222
2223
2224
2225
2226 fdprintf(proxy_fd, "%s %s%s %s\r\n",
2227 prequest,
2228 proxy_entry->url_to,
2229 urlcopy + strlen(proxy_entry->url_from),
2230 HTTP_slash
2231 );
2232 cgi_io_loop_and_exit(proxy_fd, proxy_fd, INT_MAX);
2233 }
2234 }
2235#endif
2236
2237
2238 g_query = strchr(urlcopy, '?');
2239 if (g_query)
2240 *g_query++ = '\0';
2241
2242
2243 tptr = percent_decode_in_place(urlcopy, 1);
2244 if (tptr == NULL)
2245 send_headers_and_exit(HTTP_BAD_REQUEST);
2246 if (tptr == urlcopy + 1) {
2247
2248 send_headers_and_exit(HTTP_NOT_FOUND);
2249 }
2250
2251
2252
2253
2254 urlp = tptr = urlcopy;
2255 for (;;) {
2256 if (*urlp == '/') {
2257
2258 if (*tptr == '/') {
2259 goto next_char;
2260 }
2261 if (*tptr == '.') {
2262 if (tptr[1] == '.' && (tptr[2] == '/' || tptr[2] == '\0')) {
2263
2264
2265 if (urlp == urlcopy)
2266 send_headers_and_exit(HTTP_BAD_REQUEST);
2267
2268 while (*--urlp != '/')
2269 continue;
2270
2271 tptr++;
2272 }
2273 if (tptr[1] == '/' || tptr[1] == '\0') {
2274
2275 goto next_char;
2276 }
2277 }
2278 }
2279 *++urlp = *tptr;
2280 if (*tptr == '\0')
2281 break;
2282 next_char:
2283 tptr++;
2284 }
2285
2286
2287 if (urlp[-1] != '/') {
2288 if (is_directory(urlcopy + 1, 1)) {
2289 found_moved_temporarily = urlcopy;
2290 }
2291 }
2292
2293
2294 if (verbose > 1)
2295 bb_error_msg("url:%s", urlcopy);
2296
2297 tptr = urlcopy;
2298 while ((tptr = strchr(tptr + 1, '/')) != NULL) {
2299
2300 *tptr = '\0';
2301 if (is_directory(urlcopy + 1, 1)) {
2302
2303 parse_conf(urlcopy + 1, SUBDIR_PARSE);
2304 if_ip_denied_send_HTTP_FORBIDDEN_and_exit(remote_ip);
2305 }
2306 *tptr = '/';
2307 }
2308
2309 tptr = urlcopy + 1;
2310
2311#if ENABLE_FEATURE_HTTPD_CGI
2312 if (is_prefixed_with(tptr, "cgi-bin/")) {
2313 if (tptr[8] == '\0') {
2314
2315 send_headers_and_exit(HTTP_FORBIDDEN);
2316 }
2317 cgi_type = CGI_NORMAL;
2318 }
2319#endif
2320
2321 if (urlp[-1] == '/') {
2322
2323
2324
2325
2326
2327 if (ENABLE_FEATURE_HTTPD_CGI)
2328 g_query = xstrdup(g_query);
2329 strcpy(urlp, index_page);
2330 }
2331 if (stat(tptr, &sb) == 0) {
2332#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
2333 char *suffix = strrchr(tptr, '.');
2334 if (suffix) {
2335 Htaccess *cur;
2336 for (cur = script_i; cur; cur = cur->next) {
2337 if (strcmp(cur->before_colon + 1, suffix) == 0) {
2338 cgi_type = CGI_INTERPRETER;
2339 break;
2340 }
2341 }
2342 }
2343#endif
2344 if (!found_moved_temporarily) {
2345 file_size = sb.st_size;
2346 last_mod = sb.st_mtime;
2347 }
2348 }
2349#if ENABLE_FEATURE_HTTPD_CGI
2350 else if (urlp[-1] == '/') {
2351
2352
2353 if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) {
2354 cgi_type = CGI_INDEX;
2355 }
2356 }
2357#endif
2358 urlp[0] = '\0';
2359
2360#if ENABLE_FEATURE_HTTPD_CGI
2361 total_headers_len = 0;
2362#endif
2363
2364
2365 while (1) {
2366 unsigned iobuf_len = get_line();
2367 if (!iobuf_len)
2368 break;
2369#if ENABLE_FEATURE_HTTPD_CGI
2370
2371 total_headers_len += iobuf_len;
2372 if (total_headers_len >= MAX_HTTP_HEADERS_SIZE)
2373 send_headers_and_exit(HTTP_ENTITY_TOO_LARGE);
2374#endif
2375 if (DEBUG)
2376 bb_error_msg("header: '%s'", iobuf);
2377#if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY
2378
2379 if (STRNCASECMP(iobuf, "Content-Length:") == 0) {
2380
2381 if (prequest != request_GET
2382# if ENABLE_FEATURE_HTTPD_CGI
2383 && prequest != request_HEAD
2384# endif
2385 ) {
2386 tptr = skip_whitespace(iobuf + sizeof("Content-Length:") - 1);
2387 if (!tptr[0])
2388 send_headers_and_exit(HTTP_BAD_REQUEST);
2389
2390 length = bb_strtou(tptr, NULL, 10);
2391
2392 if (errno || length > INT_MAX)
2393 send_headers_and_exit(HTTP_BAD_REQUEST);
2394 }
2395 continue;
2396 }
2397#endif
2398#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
2399 if (STRNCASECMP(iobuf, "Authorization:") == 0) {
2400
2401
2402
2403
2404 tptr = skip_whitespace(iobuf + sizeof("Authorization:")-1);
2405 if (STRNCASECMP(tptr, "Basic") == 0) {
2406 tptr += sizeof("Basic")-1;
2407
2408 decodeBase64(tptr);
2409 authorized = check_user_passwd(urlcopy, tptr);
2410 continue;
2411 }
2412 }
2413#endif
2414#if ENABLE_FEATURE_HTTPD_RANGES
2415 if (STRNCASECMP(iobuf, "Range:") == 0) {
2416
2417 char *s = skip_whitespace(iobuf + sizeof("Range:")-1);
2418 if (is_prefixed_with(s, "bytes=")) {
2419 s += sizeof("bytes=")-1;
2420 range_start = BB_STRTOOFF(s, &s, 10);
2421 if (s[0] != '-' || range_start < 0) {
2422 range_start = -1;
2423 } else if (s[1]) {
2424 range_end = BB_STRTOOFF(s+1, NULL, 10);
2425 if (errno || range_end < range_start)
2426 range_start = -1;
2427 }
2428 }
2429 continue;
2430 }
2431#endif
2432#if ENABLE_FEATURE_HTTPD_GZIP
2433 if (STRNCASECMP(iobuf, "Accept-Encoding:") == 0) {
2434
2435
2436
2437 const char *s = strstr(iobuf, "gzip");
2438 if (s) {
2439
2440
2441
2442
2443
2444 content_gzip = 1;
2445
2446 }
2447 continue;
2448 }
2449#endif
2450#if ENABLE_FEATURE_HTTPD_CGI
2451 if (cgi_type != CGI_NONE) {
2452 bool ct = (STRNCASECMP(iobuf, "Content-Type:") == 0);
2453 char *cp;
2454 char *colon = strchr(iobuf, ':');
2455
2456 if (!colon)
2457 continue;
2458 cp = iobuf;
2459 while (cp < colon) {
2460
2461 char c = (*cp & ~0x20);
2462 if ((unsigned)(c - 'A') <= ('Z' - 'A')) {
2463 *cp++ = c;
2464 continue;
2465 }
2466 if (!isdigit(*cp))
2467 *cp = '_';
2468 cp++;
2469 }
2470
2471 cp = xasprintf(ct ? "HTTP_%.*s=%s" + 5 : "HTTP_%.*s=%s",
2472 (int)(colon - iobuf), iobuf,
2473 skip_whitespace(colon + 1)
2474 );
2475 putenv(cp);
2476 }
2477#endif
2478 }
2479
2480
2481 alarm(0);
2482
2483 if (strcmp(bb_basename(urlcopy), HTTPD_CONF) == 0) {
2484
2485 send_headers_and_exit(HTTP_FORBIDDEN);
2486 }
2487
2488#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
2489
2490
2491 if (authorized < 0)
2492 authorized = check_user_passwd(urlcopy, (char *) "");
2493 if (!authorized)
2494 send_headers_and_exit(HTTP_UNAUTHORIZED);
2495#endif
2496
2497 if (found_moved_temporarily) {
2498 send_headers_and_exit(HTTP_MOVED_TEMPORARILY);
2499 }
2500
2501 tptr = urlcopy + 1;
2502
2503#if ENABLE_FEATURE_HTTPD_CGI
2504 if (cgi_type != CGI_NONE) {
2505 send_cgi_and_exit(
2506 (cgi_type == CGI_INDEX) ? "/cgi-bin/index.cgi"
2507 : urlcopy,
2508 urlcopy, prequest, length
2509 );
2510 }
2511#endif
2512
2513 if (urlp[-1] == '/') {
2514 strcpy(urlp, index_page);
2515 }
2516
2517#if ENABLE_FEATURE_HTTPD_CGI
2518 if (prequest != request_GET && prequest != request_HEAD) {
2519
2520 send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
2521 }
2522 send_file_and_exit(tptr,
2523 (prequest != request_HEAD ? SEND_HEADERS_AND_BODY : SEND_HEADERS)
2524 );
2525#else
2526 send_file_and_exit(tptr, SEND_HEADERS_AND_BODY);
2527#endif
2528}
2529
2530
2531
2532
2533
2534
2535
2536#if BB_MMU
2537static void mini_httpd(int server_socket) NORETURN;
2538static void mini_httpd(int server_socket)
2539{
2540
2541
2542
2543
2544
2545 while (1) {
2546 int n;
2547 len_and_sockaddr fromAddr;
2548
2549
2550 fromAddr.len = LSA_SIZEOF_SA;
2551 n = accept(server_socket, &fromAddr.u.sa, &fromAddr.len);
2552 if (n < 0)
2553 continue;
2554
2555
2556 setsockopt_keepalive(n);
2557
2558 if (fork() == 0) {
2559
2560
2561 signal(SIGHUP, SIG_IGN);
2562 close(server_socket);
2563 xmove_fd(n, 0);
2564 xdup2(0, 1);
2565
2566 handle_incoming_and_exit(&fromAddr);
2567 }
2568
2569 close(n);
2570 }
2571
2572}
2573#else
2574static void mini_httpd_nommu(int server_socket, int argc, char **argv) NORETURN;
2575static void mini_httpd_nommu(int server_socket, int argc, char **argv)
2576{
2577 char *argv_copy[argc + 2];
2578
2579 argv_copy[0] = argv[0];
2580 argv_copy[1] = (char*)"-i";
2581 memcpy(&argv_copy[2], &argv[1], argc * sizeof(argv[0]));
2582
2583
2584
2585
2586
2587
2588 while (1) {
2589 int n;
2590
2591
2592 n = accept(server_socket, NULL, NULL);
2593 if (n < 0)
2594 continue;
2595
2596
2597 setsockopt_keepalive(n);
2598
2599 if (vfork() == 0) {
2600
2601
2602 signal(SIGHUP, SIG_IGN);
2603 close(server_socket);
2604 xmove_fd(n, 0);
2605 xdup2(0, 1);
2606
2607
2608 re_exec(argv_copy);
2609 }
2610 argv_copy[0][0] &= 0x7f;
2611
2612 close(n);
2613 }
2614
2615}
2616#endif
2617
2618
2619
2620
2621
2622static void mini_httpd_inetd(void) NORETURN;
2623static void mini_httpd_inetd(void)
2624{
2625 len_and_sockaddr fromAddr;
2626
2627 memset(&fromAddr, 0, sizeof(fromAddr));
2628 fromAddr.len = LSA_SIZEOF_SA;
2629
2630 getpeername(0, &fromAddr.u.sa, &fromAddr.len);
2631 handle_incoming_and_exit(&fromAddr);
2632}
2633
2634static void sighup_handler(int sig UNUSED_PARAM)
2635{
2636 int sv = errno;
2637 parse_conf(DEFAULT_PATH_HTTPD_CONF, SIGNALED_PARSE);
2638 errno = sv;
2639}
2640
2641enum {
2642 c_opt_config_file = 0,
2643 d_opt_decode_url,
2644 h_opt_home_httpd,
2645 IF_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,)
2646 IF_FEATURE_HTTPD_BASIC_AUTH( r_opt_realm ,)
2647 IF_FEATURE_HTTPD_AUTH_MD5( m_opt_md5 ,)
2648 IF_FEATURE_HTTPD_SETUID( u_opt_setuid ,)
2649 p_opt_port ,
2650 p_opt_inetd ,
2651 p_opt_foreground,
2652 p_opt_verbose ,
2653 OPT_CONFIG_FILE = 1 << c_opt_config_file,
2654 OPT_DECODE_URL = 1 << d_opt_decode_url,
2655 OPT_HOME_HTTPD = 1 << h_opt_home_httpd,
2656 OPT_ENCODE_URL = IF_FEATURE_HTTPD_ENCODE_URL_STR((1 << e_opt_encode_url)) + 0,
2657 OPT_REALM = IF_FEATURE_HTTPD_BASIC_AUTH( (1 << r_opt_realm )) + 0,
2658 OPT_MD5 = IF_FEATURE_HTTPD_AUTH_MD5( (1 << m_opt_md5 )) + 0,
2659 OPT_SETUID = IF_FEATURE_HTTPD_SETUID( (1 << u_opt_setuid )) + 0,
2660 OPT_PORT = 1 << p_opt_port,
2661 OPT_INETD = 1 << p_opt_inetd,
2662 OPT_FOREGROUND = 1 << p_opt_foreground,
2663 OPT_VERBOSE = 1 << p_opt_verbose,
2664};
2665
2666
2667int httpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
2668int httpd_main(int argc UNUSED_PARAM, char **argv)
2669{
2670 int server_socket = server_socket;
2671 unsigned opt;
2672 char *url_for_decode;
2673 IF_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;)
2674 IF_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;)
2675 IF_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;)
2676 IF_FEATURE_HTTPD_AUTH_MD5(const char *pass;)
2677
2678 INIT_G();
2679
2680#if ENABLE_LOCALE_SUPPORT
2681
2682 setlocale(LC_TIME, "C");
2683#endif
2684
2685 home_httpd = xrealloc_getcwd_or_warn(NULL);
2686
2687
2688
2689 opt = getopt32(argv, "^"
2690 "c:d:h:"
2691 IF_FEATURE_HTTPD_ENCODE_URL_STR("e:")
2692 IF_FEATURE_HTTPD_BASIC_AUTH("r:")
2693 IF_FEATURE_HTTPD_AUTH_MD5("m:")
2694 IF_FEATURE_HTTPD_SETUID("u:")
2695 "p:ifv"
2696 "\0"
2697
2698 "vv:if",
2699 &opt_c_configFile, &url_for_decode, &home_httpd
2700 IF_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode)
2701 IF_FEATURE_HTTPD_BASIC_AUTH(, &g_realm)
2702 IF_FEATURE_HTTPD_AUTH_MD5(, &pass)
2703 IF_FEATURE_HTTPD_SETUID(, &s_ugid)
2704 , &bind_addr_or_port
2705 , &verbose
2706 );
2707 if (opt & OPT_DECODE_URL) {
2708 fputs(percent_decode_in_place(url_for_decode, 0), stdout);
2709 return 0;
2710 }
2711#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
2712 if (opt & OPT_ENCODE_URL) {
2713 fputs(encodeString(url_for_encode), stdout);
2714 return 0;
2715 }
2716#endif
2717#if ENABLE_FEATURE_HTTPD_AUTH_MD5
2718 if (opt & OPT_MD5) {
2719 char salt[sizeof("$1$XXXXXXXX")];
2720 salt[0] = '$';
2721 salt[1] = '1';
2722 salt[2] = '$';
2723 crypt_make_salt(salt + 3, 4);
2724 puts(pw_encrypt(pass, salt, 0));
2725 return 0;
2726 }
2727#endif
2728#if ENABLE_FEATURE_HTTPD_SETUID
2729 if (opt & OPT_SETUID) {
2730 xget_uidgid(&ugid, s_ugid);
2731 }
2732#endif
2733
2734#if !BB_MMU
2735 if (!(opt & OPT_FOREGROUND)) {
2736 bb_daemonize_or_rexec(0, argv);
2737 }
2738#endif
2739
2740 xchdir(home_httpd);
2741 if (!(opt & OPT_INETD)) {
2742 signal(SIGCHLD, SIG_IGN);
2743 server_socket = openServer();
2744#if ENABLE_FEATURE_HTTPD_SETUID
2745
2746 if (opt & OPT_SETUID) {
2747 if (ugid.gid != (gid_t)-1) {
2748 if (setgroups(1, &ugid.gid) == -1)
2749 bb_perror_msg_and_die("setgroups");
2750 xsetgid(ugid.gid);
2751 }
2752 xsetuid(ugid.uid);
2753 }
2754#endif
2755 }
2756
2757#if 0
2758
2759
2760
2761
2762
2763
2764 {
2765 char *p = getenv("PATH");
2766
2767 clearenv();
2768 if (p)
2769 putenv(p - 5);
2770
2771
2772 }
2773#endif
2774
2775 parse_conf(DEFAULT_PATH_HTTPD_CONF, FIRST_PARSE);
2776 if (!(opt & OPT_INETD))
2777 signal(SIGHUP, sighup_handler);
2778
2779 xfunc_error_retval = 0;
2780 if (opt & OPT_INETD)
2781 mini_httpd_inetd();
2782#if BB_MMU
2783 if (!(opt & OPT_FOREGROUND))
2784 bb_daemonize(0);
2785 mini_httpd(server_socket);
2786#else
2787 mini_httpd_nommu(server_socket, argc, argv);
2788#endif
2789
2790}
2791