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