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