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(url + 1) != 0) {
1671 bb_perror_msg("can't change directory to '%s'", url + 1);
1672 goto error_execing_cgi;
1673 }
1674
1675 }
1676 script++;
1677
1678
1679 argv[0] = script;
1680 argv[1] = NULL;
1681
1682#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
1683 {
1684 char *suffix = strrchr(script, '.');
1685
1686 if (suffix) {
1687 Htaccess *cur;
1688 for (cur = script_i; cur; cur = cur->next) {
1689 if (strcmp(cur->before_colon + 1, suffix) == 0) {
1690
1691 argv[0] = cur->after_colon;
1692 argv[1] = script;
1693 argv[2] = NULL;
1694 break;
1695 }
1696 }
1697 }
1698 }
1699#endif
1700
1701 bb_signals(0
1702 | (1 << SIGCHLD)
1703 | (1 << SIGPIPE)
1704 | (1 << SIGHUP)
1705 , SIG_DFL);
1706
1707
1708
1709
1710 execv(argv[0], argv);
1711 if (verbose)
1712 bb_perror_msg("can't execute '%s'", argv[0]);
1713 error_execing_cgi:
1714
1715
1716 send_headers_and_exit(HTTP_NOT_FOUND);
1717 }
1718
1719
1720
1721
1722 xfunc_error_retval = 0;
1723
1724
1725 close(fromCgi.wr);
1726 close(toCgi.rd);
1727 cgi_io_loop_and_exit(fromCgi.rd, toCgi.wr, post_len);
1728}
1729
1730#endif
1731
1732
1733
1734
1735
1736
1737
1738
1739static NOINLINE void send_file_and_exit(const char *url, int what)
1740{
1741 char *suffix;
1742 int fd;
1743 ssize_t count;
1744
1745 if (content_gzip) {
1746
1747 char *gzurl = xasprintf("%s.gz", url);
1748 fd = open(gzurl, O_RDONLY);
1749 free(gzurl);
1750 if (fd != -1) {
1751 struct stat sb;
1752 fstat(fd, &sb);
1753 file_size = sb.st_size;
1754 last_mod = sb.st_mtime;
1755 } else {
1756 IF_FEATURE_HTTPD_GZIP(content_gzip = 0;)
1757 fd = open(url, O_RDONLY);
1758 }
1759 } else {
1760 fd = open(url, O_RDONLY);
1761
1762 }
1763 if (fd < 0) {
1764 dbg("can't open '%s'\n", url);
1765
1766
1767
1768 if (what != SEND_BODY)
1769 send_headers_and_exit(HTTP_NOT_FOUND);
1770 log_and_exit();
1771 }
1772#if ENABLE_FEATURE_HTTPD_ETAG
1773
1774 sprintf(G.etag, "\"%llx-%llx\"", (unsigned long long)last_mod, (unsigned long long)file_size);
1775
1776 if (G.if_none_match) {
1777 dbg("If-None-Match:'%s' file's ETag:'%s'\n", G.if_none_match, G.etag);
1778
1779
1780 if (strstr(G.if_none_match, G.etag))
1781 send_headers_and_exit(HTTP_NOT_MODIFIED);
1782 }
1783#endif
1784
1785
1786 signal(SIGPIPE, SIG_IGN);
1787
1788
1789
1790 suffix = strrchr(url, '.');
1791 if (suffix) {
1792 static const char suffixTable[] ALIGN1 =
1793
1794
1795
1796 ".txt.h.c.cc.cpp\0" "text/plain\0"
1797
1798 ".htm.html\0" "text/html\0"
1799 ".jpg.jpeg\0" "image/jpeg\0"
1800 ".gif\0" "image/gif\0"
1801 ".png\0" "image/png\0"
1802 ".svg\0" "image/svg+xml\0"
1803
1804 ".css\0" "text/css\0"
1805 ".js\0" "application/javascript\0"
1806 ".wav\0" "audio/wav\0"
1807 ".avi\0" "video/x-msvideo\0"
1808 ".qt.mov\0" "video/quicktime\0"
1809 ".mpe.mpeg\0" "video/mpeg\0"
1810 ".mid.midi\0" "audio/midi\0"
1811 ".mp3\0" "audio/mpeg\0"
1812#if 0
1813 ".au\0" "audio/basic\0"
1814 ".pac\0" "application/x-ns-proxy-autoconfig\0"
1815 ".vrml.wrl\0" "model/vrml\0"
1816#endif
1817
1818 ;
1819 Htaccess *cur;
1820
1821
1822 const char *table = suffixTable;
1823 const char *table_next;
1824 for (; *table; table = table_next) {
1825 const char *try_suffix;
1826 const char *mime_type;
1827 mime_type = table + strlen(table) + 1;
1828 table_next = mime_type + strlen(mime_type) + 1;
1829 try_suffix = strstr(table, suffix);
1830 if (!try_suffix)
1831 continue;
1832 try_suffix += strlen(suffix);
1833 if (*try_suffix == '\0' || *try_suffix == '.') {
1834 found_mime_type = mime_type;
1835 break;
1836 }
1837
1838
1839
1840
1841
1842 break;
1843 }
1844
1845 for (cur = mime_a; cur; cur = cur->next) {
1846 if (strcmp(cur->before_colon, suffix) == 0) {
1847 found_mime_type = cur->after_colon;
1848 break;
1849 }
1850 }
1851 }
1852
1853 dbg("sending file '%s' content-type:%s\n", url, found_mime_type);
1854
1855#if ENABLE_FEATURE_HTTPD_RANGES
1856 if (what == SEND_BODY
1857 || content_gzip
1858 ) {
1859 range_start = -1;
1860 }
1861 range_len = MAXINT(off_t);
1862 if (range_start >= 0) {
1863 if (!range_end || range_end > file_size - 1) {
1864 range_end = file_size - 1;
1865 }
1866 if (range_end < range_start
1867 || lseek(fd, range_start, SEEK_SET) != range_start
1868 ) {
1869 lseek(fd, 0, SEEK_SET);
1870 range_start = -1;
1871 } else {
1872 range_len = range_end - range_start + 1;
1873 send_headers(HTTP_PARTIAL_CONTENT);
1874 what = SEND_BODY;
1875 }
1876 }
1877#endif
1878 if (what & SEND_HEADERS)
1879 send_headers(HTTP_OK);
1880#if ENABLE_FEATURE_USE_SENDFILE
1881 {
1882 off_t offset;
1883# if ENABLE_FEATURE_HTTPD_RANGES
1884 if (range_start < 0)
1885 range_start = 0;
1886 offset = range_start;
1887# else
1888 offset = 0;
1889# endif
1890 while (1) {
1891
1892 ssize_t sz = MAXINT(ssize_t) - 0xffff;
1893 IF_FEATURE_HTTPD_RANGES(if (sz > range_len) sz = range_len;)
1894 count = sendfile(STDOUT_FILENO, fd, &offset, sz);
1895 if (count < 0) {
1896 if (offset == range_start)
1897 break;
1898 goto fin;
1899 }
1900 IF_FEATURE_HTTPD_RANGES(range_len -= count;)
1901 if (count == 0 || range_len == 0)
1902 log_and_exit();
1903 }
1904 }
1905#endif
1906 while ((count = safe_read(fd, iobuf, IOBUF_SIZE)) > 0) {
1907 ssize_t n;
1908 IF_FEATURE_HTTPD_RANGES(if (count > range_len) count = range_len;)
1909 n = full_write(STDOUT_FILENO, iobuf, count);
1910 if (count != n)
1911 break;
1912 IF_FEATURE_HTTPD_RANGES(range_len -= count;)
1913 if (range_len == 0)
1914 break;
1915 }
1916 if (count < 0) {
1917 IF_FEATURE_USE_SENDFILE(fin:)
1918 if (verbose > 1)
1919 bb_simple_perror_msg("error");
1920 }
1921 log_and_exit();
1922}
1923
1924#if ENABLE_FEATURE_HTTPD_ACL_IP
1925static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(unsigned remote_ip)
1926{
1927 Htaccess_IP *cur;
1928
1929 for (cur = G.ip_a_d; cur; cur = cur->next) {
1930 dbg("checkPermIP: '%s' ? '%u.%u.%u.%u/%u.%u.%u.%u'\n",
1931 rmt_ip_str,
1932 (unsigned char)(cur->ip >> 24),
1933 (unsigned char)(cur->ip >> 16),
1934 (unsigned char)(cur->ip >> 8),
1935 (unsigned char)(cur->ip),
1936 (unsigned char)(cur->mask >> 24),
1937 (unsigned char)(cur->mask >> 16),
1938 (unsigned char)(cur->mask >> 8),
1939 (unsigned char)(cur->mask)
1940 );
1941 if ((remote_ip & cur->mask) == cur->ip) {
1942 if (cur->allow_deny == 'A')
1943 return;
1944 send_headers_and_exit(HTTP_FORBIDDEN);
1945 }
1946 }
1947
1948 if (flg_deny_all)
1949 send_headers_and_exit(HTTP_FORBIDDEN);
1950}
1951#else
1952# define if_ip_denied_send_HTTP_FORBIDDEN_and_exit(arg) ((void)0)
1953#endif
1954
1955#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
1956
1957# if ENABLE_PAM
1958struct pam_userinfo {
1959 const char *name;
1960 const char *pw;
1961};
1962
1963static int pam_talker(int num_msg,
1964 const struct pam_message **msg,
1965 struct pam_response **resp,
1966 void *appdata_ptr)
1967{
1968 int i;
1969 struct pam_userinfo *userinfo = (struct pam_userinfo *) appdata_ptr;
1970 struct pam_response *response;
1971
1972 if (!resp || !msg || !userinfo)
1973 return PAM_CONV_ERR;
1974
1975
1976 response = xzalloc(num_msg * sizeof(*response));
1977
1978
1979 for (i = 0; i < num_msg; i++) {
1980 const char *s;
1981
1982 switch (msg[i]->msg_style) {
1983 case PAM_PROMPT_ECHO_ON:
1984 s = userinfo->name;
1985 break;
1986 case PAM_PROMPT_ECHO_OFF:
1987 s = userinfo->pw;
1988 break;
1989 case PAM_ERROR_MSG:
1990 case PAM_TEXT_INFO:
1991 s = "";
1992 break;
1993 default:
1994 free(response);
1995 return PAM_CONV_ERR;
1996 }
1997 response[i].resp = xstrdup(s);
1998 if (PAM_SUCCESS != 0)
1999 response[i].resp_retcode = PAM_SUCCESS;
2000 }
2001 *resp = response;
2002 return PAM_SUCCESS;
2003}
2004# endif
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015static int check_user_passwd(const char *path, char *user_and_passwd)
2016{
2017 Htaccess *cur;
2018 const char *prev = NULL;
2019
2020 for (cur = g_auth; cur; cur = cur->next) {
2021 const char *dir_prefix;
2022 size_t len;
2023 int r;
2024
2025 dir_prefix = cur->before_colon;
2026
2027
2028
2029 if (prev && strcmp(prev, dir_prefix) != 0)
2030 continue;
2031
2032 dbg("checkPerm: '%s' ? '%s'\n", dir_prefix, user_and_passwd);
2033
2034
2035 len = strlen(dir_prefix);
2036 if (len != 1
2037 && (strncmp(dir_prefix, path, len) != 0
2038 || (path[len] != '/' && path[len] != '\0')
2039 )
2040 ) {
2041 continue;
2042 }
2043
2044
2045 prev = dir_prefix;
2046
2047 if (ENABLE_FEATURE_HTTPD_AUTH_MD5) {
2048 char *colon_after_user;
2049 const char *passwd;
2050# if ENABLE_FEATURE_SHADOWPASSWDS && !ENABLE_PAM
2051 char sp_buf[256];
2052# endif
2053
2054 colon_after_user = strchr(user_and_passwd, ':');
2055 if (!colon_after_user)
2056 goto bad_input;
2057
2058
2059 if (cur->after_colon[0] != '*'
2060 && strncmp(cur->after_colon, user_and_passwd,
2061 colon_after_user - user_and_passwd + 1) != 0
2062 ) {
2063 continue;
2064 }
2065
2066
2067 passwd = strchr(cur->after_colon, ':');
2068 if (!passwd)
2069 goto bad_input;
2070 passwd++;
2071 if (passwd[0] == '*') {
2072# if ENABLE_PAM
2073 struct pam_userinfo userinfo;
2074 struct pam_conv conv_info = { &pam_talker, (void *) &userinfo };
2075 pam_handle_t *pamh;
2076
2077 *colon_after_user = '\0';
2078 userinfo.name = user_and_passwd;
2079 userinfo.pw = colon_after_user + 1;
2080 r = pam_start("httpd", user_and_passwd, &conv_info, &pamh) != PAM_SUCCESS;
2081 if (r == 0) {
2082 r = pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK) != PAM_SUCCESS
2083 || pam_acct_mgmt(pamh, PAM_DISALLOW_NULL_AUTHTOK) != PAM_SUCCESS
2084 ;
2085 pam_end(pamh, PAM_SUCCESS);
2086 }
2087 *colon_after_user = ':';
2088 goto end_check_passwd;
2089# else
2090# if ENABLE_FEATURE_SHADOWPASSWDS
2091
2092 struct spwd spw;
2093# endif
2094 struct passwd *pw;
2095
2096 *colon_after_user = '\0';
2097 pw = getpwnam(user_and_passwd);
2098 *colon_after_user = ':';
2099 if (!pw || !pw->pw_passwd)
2100 continue;
2101 passwd = pw->pw_passwd;
2102# if ENABLE_FEATURE_SHADOWPASSWDS
2103 if ((passwd[0] == 'x' || passwd[0] == '*') && !passwd[1]) {
2104
2105
2106 struct spwd *result = NULL;
2107 r = getspnam_r(pw->pw_name, &spw, sp_buf, sizeof(sp_buf), &result);
2108 if (r == 0 && result)
2109 passwd = result->sp_pwdp;
2110 }
2111# endif
2112
2113
2114
2115 goto check_encrypted;
2116# endif
2117 }
2118
2119
2120 if (passwd[0] == '$' && isdigit(passwd[1])) {
2121 char *encrypted;
2122# if !ENABLE_PAM
2123 check_encrypted:
2124# endif
2125
2126 encrypted = pw_encrypt(
2127 colon_after_user + 1,
2128 passwd,
2129 0
2130 );
2131 r = strcmp(encrypted, passwd);
2132 free(encrypted);
2133 } else {
2134
2135 r = strcmp(colon_after_user + 1, passwd);
2136 }
2137 goto end_check_passwd;
2138 }
2139 bad_input:
2140
2141 r = strcmp(cur->after_colon, user_and_passwd);
2142 end_check_passwd:
2143 if (r == 0) {
2144 remoteuser = xstrndup(user_and_passwd,
2145 strchrnul(user_and_passwd, ':') - user_and_passwd
2146 );
2147 return 1;
2148 }
2149 }
2150
2151
2152 return (prev == NULL);
2153}
2154#endif
2155
2156#if ENABLE_FEATURE_HTTPD_PROXY
2157static Htaccess_Proxy *find_proxy_entry(const char *url)
2158{
2159 Htaccess_Proxy *p;
2160 for (p = proxy; p; p = p->next) {
2161 if (is_prefixed_with(url, p->url_from))
2162 return p;
2163 }
2164 return NULL;
2165}
2166#endif
2167
2168
2169
2170
2171static void send_REQUEST_TIMEOUT_and_exit(int sig) NORETURN;
2172static void send_REQUEST_TIMEOUT_and_exit(int sig UNUSED_PARAM)
2173{
2174 send_headers_and_exit(HTTP_REQUEST_TIMEOUT);
2175}
2176
2177
2178
2179
2180static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) NORETURN;
2181static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2182{
2183 struct stat sb;
2184 char *urlcopy;
2185 char *urlp;
2186 char *tptr;
2187#if ENABLE_FEATURE_HTTPD_ACL_IP
2188 unsigned remote_ip;
2189#endif
2190#if ENABLE_FEATURE_HTTPD_CGI
2191 unsigned total_headers_len;
2192#endif
2193 const char *prequest;
2194 static const char request_GET[] ALIGN1 = "GET";
2195 static const char request_HEAD[] ALIGN1 = "HEAD";
2196#if ENABLE_FEATURE_HTTPD_CGI
2197 static const char request_POST[] ALIGN1 = "POST";
2198 unsigned long POST_length;
2199 enum CGI_type {
2200 CGI_NONE = 0,
2201 CGI_NORMAL,
2202 CGI_INDEX,
2203 CGI_INTERPRETER,
2204 } cgi_type = CGI_NONE;
2205#endif
2206#if ENABLE_FEATURE_HTTPD_PROXY
2207 Htaccess_Proxy *proxy_entry;
2208#endif
2209#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
2210 smallint authorized = -1;
2211#endif
2212 char *HTTP_slash;
2213
2214
2215
2216 iobuf = xmalloc(IOBUF_SIZE);
2217
2218 if (ENABLE_FEATURE_HTTPD_CGI || DEBUG || verbose) {
2219
2220 rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr->u.sa);
2221 }
2222 if (verbose) {
2223
2224 if (rmt_ip_str)
2225 applet_name = rmt_ip_str;
2226 if (verbose > 2)
2227 bb_simple_error_msg("connected");
2228 }
2229#if ENABLE_FEATURE_HTTPD_ACL_IP
2230 remote_ip = 0;
2231 if (fromAddr->u.sa.sa_family == AF_INET) {
2232 remote_ip = ntohl(fromAddr->u.sin.sin_addr.s_addr);
2233 }
2234# if ENABLE_FEATURE_IPV6
2235 if (fromAddr->u.sa.sa_family == AF_INET6
2236 && fromAddr->u.sin6.sin6_addr.s6_addr32[0] == 0
2237 && fromAddr->u.sin6.sin6_addr.s6_addr32[1] == 0
2238 && ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[2]) == 0xffff)
2239 remote_ip = ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[3]);
2240# endif
2241 if_ip_denied_send_HTTP_FORBIDDEN_and_exit(remote_ip);
2242#endif
2243
2244
2245 signal(SIGALRM, send_REQUEST_TIMEOUT_and_exit);
2246
2247 if (!get_line()) {
2248
2249
2250
2251
2252
2253
2254 if (verbose > 2)
2255 bb_simple_error_msg("eof on read, closing");
2256
2257
2258
2259
2260 _exit(xfunc_error_retval);
2261 }
2262 dbg("Request:'%s'\n", iobuf);
2263
2264
2265
2266
2267 urlp = strchr(iobuf, ' ');
2268 if (urlp == NULL)
2269 send_headers_and_exit(HTTP_BAD_REQUEST);
2270 *urlp++ = '\0';
2271
2272 if (urlp[0] != '/')
2273 send_headers_and_exit(HTTP_BAD_REQUEST);
2274
2275 HTTP_slash = strchr(urlp, ' ');
2276
2277 if (!HTTP_slash || strncmp(HTTP_slash + 1, HTTP_200, 5) != 0)
2278 send_headers_and_exit(HTTP_BAD_REQUEST);
2279 *HTTP_slash++ = '\0';
2280
2281#if ENABLE_FEATURE_HTTPD_PROXY
2282 proxy_entry = find_proxy_entry(urlp);
2283 if (proxy_entry) {
2284 int proxy_fd;
2285 len_and_sockaddr *lsa;
2286
2287 if (verbose > 1)
2288 bb_error_msg("proxy:%s", urlp);
2289 lsa = host2sockaddr(proxy_entry->host_port, 80);
2290 if (!lsa)
2291 send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
2292 proxy_fd = socket(lsa->u.sa.sa_family, SOCK_STREAM, 0);
2293 if (proxy_fd < 0)
2294 send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
2295 if (connect(proxy_fd, &lsa->u.sa, lsa->len) < 0)
2296 send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
2297
2298 alarm(0);
2299
2300
2301
2302
2303
2304 fdprintf(proxy_fd, "%s %s%s %s\r\n",
2305 iobuf,
2306 proxy_entry->url_to,
2307 urlp + strlen(proxy_entry->url_from),
2308 HTTP_slash
2309 );
2310 cgi_io_loop_and_exit(proxy_fd, proxy_fd, INT_MAX);
2311 }
2312#endif
2313
2314
2315 prequest = request_GET;
2316 if (strcasecmp(iobuf, prequest) == 0)
2317 goto found;
2318 prequest = request_HEAD;
2319 if (strcasecmp(iobuf, prequest) == 0)
2320 goto found;
2321#if !ENABLE_FEATURE_HTTPD_CGI
2322 send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
2323#else
2324 prequest = request_POST;
2325 if (strcasecmp(iobuf, prequest) == 0)
2326 goto found;
2327
2328 prequest = alloca(16);
2329 safe_strncpy((char*)prequest, iobuf, 16);
2330#endif
2331 found:
2332
2333 urlcopy = alloca((HTTP_slash - urlp) + 2 + strlen(index_page));
2334 strcpy(urlcopy, urlp);
2335
2336
2337
2338 g_query = strchr(urlcopy, '?');
2339 if (g_query)
2340 *g_query++ = '\0';
2341
2342
2343 tptr = percent_decode_in_place(urlcopy, 1);
2344 if (tptr == NULL)
2345 send_headers_and_exit(HTTP_BAD_REQUEST);
2346 if (tptr == urlcopy + 1) {
2347
2348 send_headers_and_exit(HTTP_NOT_FOUND);
2349 }
2350
2351
2352
2353
2354 urlp = tptr = urlcopy;
2355 while (1) {
2356 if (*urlp == '/') {
2357
2358 if (*tptr == '/') {
2359 goto next_char;
2360 }
2361 if (*tptr == '.') {
2362 if (tptr[1] == '.' && (tptr[2] == '/' || tptr[2] == '\0')) {
2363
2364
2365 if (urlp == urlcopy)
2366 send_headers_and_exit(HTTP_BAD_REQUEST);
2367
2368 while (*--urlp != '/')
2369 continue;
2370
2371 tptr++;
2372 }
2373 if (tptr[1] == '/' || tptr[1] == '\0') {
2374
2375 goto next_char;
2376 }
2377 }
2378 }
2379 *++urlp = *tptr;
2380 if (*tptr == '\0')
2381 break;
2382 next_char:
2383 tptr++;
2384 }
2385
2386
2387 if (verbose > 1)
2388 bb_error_msg("url:%s", urlcopy);
2389
2390 tptr = urlcopy;
2391 while ((tptr = strchr(tptr + 1, '/')) != NULL) {
2392
2393 *tptr = '\0';
2394
2395 if (parse_conf(urlcopy + 1, SUBDIR_PARSE) == 0)
2396 if_ip_denied_send_HTTP_FORBIDDEN_and_exit(remote_ip);
2397 *tptr = '/';
2398 }
2399
2400 tptr = urlcopy + 1;
2401
2402#if ENABLE_FEATURE_HTTPD_CGI
2403 if (is_prefixed_with(tptr, "cgi-bin/")) {
2404 if (tptr[8] == '\0') {
2405
2406 send_headers_and_exit(HTTP_FORBIDDEN);
2407 }
2408 cgi_type = CGI_NORMAL;
2409 }
2410#endif
2411
2412 if (urlp[-1] == '/') {
2413
2414
2415
2416
2417
2418 if (ENABLE_FEATURE_HTTPD_CGI)
2419 g_query = xstrdup(g_query);
2420 strcpy(urlp, index_page);
2421 }
2422 if (stat(tptr, &sb) == 0) {
2423
2424
2425 if (urlp[-1] != '/' && S_ISDIR(sb.st_mode)) {
2426 found_moved_temporarily = urlcopy;
2427 } else {
2428#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
2429 char *suffix = strrchr(tptr, '.');
2430 if (suffix) {
2431 Htaccess *cur;
2432 for (cur = script_i; cur; cur = cur->next) {
2433 if (strcmp(cur->before_colon + 1, suffix) == 0) {
2434 cgi_type = CGI_INTERPRETER;
2435 break;
2436 }
2437 }
2438 }
2439#endif
2440 file_size = sb.st_size;
2441 last_mod = sb.st_mtime;
2442 }
2443 }
2444#if ENABLE_FEATURE_HTTPD_CGI
2445 else if (urlp[-1] == '/') {
2446
2447
2448 if (access("/cgi-bin/index.cgi"+1, X_OK) != 0)
2449 send_headers_and_exit(HTTP_NOT_FOUND);
2450 cgi_type = CGI_INDEX;
2451 }
2452#endif
2453
2454#if ENABLE_FEATURE_HTTPD_BASIC_AUTH || ENABLE_FEATURE_HTTPD_CGI
2455
2456 urlp[0] = '\0';
2457#endif
2458
2459#if ENABLE_FEATURE_HTTPD_CGI
2460 total_headers_len = 0;
2461 POST_length = 0;
2462#endif
2463
2464
2465 while (1) {
2466 unsigned iobuf_len = get_line();
2467 if (!iobuf_len)
2468 break;
2469#if ENABLE_FEATURE_HTTPD_CGI
2470
2471 total_headers_len += iobuf_len;
2472 if (total_headers_len >= MAX_HTTP_HEADERS_SIZE)
2473 send_headers_and_exit(HTTP_ENTITY_TOO_LARGE);
2474#endif
2475 dbg("header:'%s'\n", iobuf);
2476#if ENABLE_FEATURE_HTTPD_CGI
2477
2478 if (prequest == request_POST && STRNCASECMP(iobuf, "Content-Length:") == 0) {
2479 tptr = skip_whitespace(iobuf + sizeof("Content-Length:") - 1);
2480 if (!tptr[0])
2481 send_headers_and_exit(HTTP_BAD_REQUEST);
2482
2483 POST_length = bb_strtou(tptr, NULL, 10);
2484
2485 if (errno || POST_length > INT_MAX)
2486 send_headers_and_exit(HTTP_BAD_REQUEST);
2487 continue;
2488 }
2489#endif
2490#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
2491 if (STRNCASECMP(iobuf, "Authorization:") == 0) {
2492
2493
2494
2495
2496 tptr = skip_whitespace(iobuf + sizeof("Authorization:")-1);
2497 if (STRNCASECMP(tptr, "Basic") == 0) {
2498 tptr += sizeof("Basic")-1;
2499
2500 decodeBase64(tptr);
2501 authorized = check_user_passwd(urlcopy, tptr);
2502 continue;
2503 }
2504 }
2505#endif
2506#if ENABLE_FEATURE_HTTPD_RANGES
2507 if (STRNCASECMP(iobuf, "Range:") == 0) {
2508
2509 char *s = skip_whitespace(iobuf + sizeof("Range:")-1);
2510 s = is_prefixed_with(s, "bytes=");
2511 if (s) {
2512 range_start = BB_STRTOOFF(s, &s, 10);
2513 if (s[0] != '-' || range_start < 0) {
2514 range_start = -1;
2515 } else if (s[1]) {
2516 range_end = BB_STRTOOFF(s+1, NULL, 10);
2517 if (errno || range_end < range_start)
2518 range_start = -1;
2519 }
2520 }
2521 continue;
2522 }
2523#endif
2524#if ENABLE_FEATURE_HTTPD_GZIP
2525 if (STRNCASECMP(iobuf, "Accept-Encoding:") == 0) {
2526
2527
2528
2529 const char *s = strstr(iobuf, "gzip");
2530 if (s) {
2531
2532
2533
2534
2535
2536 content_gzip = 1;
2537
2538 }
2539 continue;
2540 }
2541#endif
2542#if ENABLE_FEATURE_HTTPD_ETAG
2543 if (STRNCASECMP(iobuf, "If-None-Match:") == 0) {
2544 free(G.if_none_match);
2545 G.if_none_match = xstrdup(skip_whitespace(iobuf + sizeof("If-None-Match:") - 1));
2546 continue;
2547 }
2548#endif
2549#if ENABLE_FEATURE_HTTPD_CGI
2550 if (cgi_type != CGI_NONE) {
2551 bool ct = (STRNCASECMP(iobuf, "Content-Type:") == 0);
2552 char *cp;
2553 char *colon = strchr(iobuf, ':');
2554
2555 if (!colon)
2556 continue;
2557 cp = iobuf;
2558 while (cp < colon) {
2559
2560 char c = (*cp & ~0x20);
2561 if ((unsigned)(c - 'A') <= ('Z' - 'A')) {
2562 *cp++ = c;
2563 continue;
2564 }
2565 if (!isdigit(*cp))
2566 *cp = '_';
2567 cp++;
2568 }
2569
2570 cp = xasprintf(ct ? "HTTP_%.*s=%s" + 5 : "HTTP_%.*s=%s",
2571 (int)(colon - iobuf), iobuf,
2572 skip_whitespace(colon + 1)
2573 );
2574 putenv(cp);
2575 }
2576#endif
2577 }
2578
2579
2580 alarm(0);
2581
2582 if (strcmp(bb_basename(urlcopy), HTTPD_CONF) == 0) {
2583
2584 send_headers_and_exit(HTTP_FORBIDDEN);
2585 }
2586
2587#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
2588
2589
2590 if (authorized < 0)
2591 authorized = check_user_passwd(urlcopy, (char *) "");
2592 if (!authorized)
2593 send_headers_and_exit(HTTP_UNAUTHORIZED);
2594#endif
2595
2596 if (found_moved_temporarily)
2597 send_headers_and_exit(HTTP_MOVED_TEMPORARILY);
2598
2599#if ENABLE_FEATURE_HTTPD_CGI
2600 if (cgi_type != CGI_NONE) {
2601 send_cgi_and_exit(
2602 (cgi_type == CGI_INDEX) ? "/cgi-bin/index.cgi"
2603 : urlcopy,
2604 urlcopy, prequest, POST_length
2605 );
2606 }
2607#endif
2608
2609#if ENABLE_FEATURE_HTTPD_CGI
2610 if (prequest != request_GET && prequest != request_HEAD) {
2611
2612 send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
2613 }
2614#else
2615
2616#endif
2617
2618#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
2619
2620 if (urlp[-1] == '/')
2621 urlp[0] = index_page[0];
2622#endif
2623 send_file_and_exit(urlcopy + 1,
2624 (prequest != request_HEAD ? (SEND_HEADERS + SEND_BODY) : SEND_HEADERS)
2625 );
2626}
2627
2628
2629
2630
2631
2632
2633
2634#if BB_MMU
2635static void mini_httpd(int server_socket) NORETURN;
2636static void mini_httpd(int server_socket)
2637{
2638
2639
2640
2641
2642
2643 while (1) {
2644 int n;
2645 len_and_sockaddr fromAddr;
2646
2647
2648 fromAddr.len = LSA_SIZEOF_SA;
2649 n = accept(server_socket, &fromAddr.u.sa, &fromAddr.len);
2650 if (n < 0)
2651 continue;
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661 setsockopt_keepalive(n);
2662
2663 if (fork() == 0) {
2664
2665
2666 signal(SIGHUP, SIG_IGN);
2667 close(server_socket);
2668 xmove_fd(n, 0);
2669 xdup2(0, 1);
2670
2671 handle_incoming_and_exit(&fromAddr);
2672 }
2673
2674 close(n);
2675 }
2676
2677}
2678#else
2679static void mini_httpd_nommu(int server_socket, int argc, char **argv) NORETURN;
2680static void mini_httpd_nommu(int server_socket, int argc, char **argv)
2681{
2682 char *argv_copy[argc + 2];
2683
2684 argv_copy[0] = argv[0];
2685 argv_copy[1] = (char*)"-i";
2686 memcpy(&argv_copy[2], &argv[1], argc * sizeof(argv[0]));
2687
2688
2689
2690
2691
2692
2693 while (1) {
2694 int n;
2695
2696
2697 n = accept(server_socket, NULL, NULL);
2698 if (n < 0)
2699 continue;
2700
2701
2702 setsockopt_keepalive(n);
2703
2704 if (vfork() == 0) {
2705
2706
2707 signal(SIGHUP, SIG_IGN);
2708 close(server_socket);
2709 xmove_fd(n, 0);
2710 xdup2(0, 1);
2711
2712
2713 re_exec(argv_copy);
2714 }
2715 argv_copy[0][0] &= 0x7f;
2716
2717 close(n);
2718 }
2719
2720}
2721#endif
2722
2723
2724
2725
2726
2727static void mini_httpd_inetd(void) NORETURN;
2728static void mini_httpd_inetd(void)
2729{
2730 len_and_sockaddr fromAddr;
2731
2732 memset(&fromAddr, 0, sizeof(fromAddr));
2733 fromAddr.len = LSA_SIZEOF_SA;
2734
2735 getpeername(0, &fromAddr.u.sa, &fromAddr.len);
2736 handle_incoming_and_exit(&fromAddr);
2737}
2738
2739static void sighup_handler(int sig UNUSED_PARAM)
2740{
2741 int sv = errno;
2742 parse_conf(DEFAULT_PATH_HTTPD_CONF, SIGNALED_PARSE);
2743 errno = sv;
2744}
2745
2746enum {
2747 c_opt_config_file = 0,
2748 d_opt_decode_url,
2749 h_opt_home_httpd,
2750 IF_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,)
2751 IF_FEATURE_HTTPD_BASIC_AUTH( r_opt_realm ,)
2752 IF_FEATURE_HTTPD_AUTH_MD5( m_opt_md5 ,)
2753 IF_FEATURE_HTTPD_SETUID( u_opt_setuid ,)
2754 p_opt_port ,
2755 p_opt_inetd ,
2756 p_opt_foreground,
2757 p_opt_verbose ,
2758 OPT_CONFIG_FILE = 1 << c_opt_config_file,
2759 OPT_DECODE_URL = 1 << d_opt_decode_url,
2760 OPT_HOME_HTTPD = 1 << h_opt_home_httpd,
2761 OPT_ENCODE_URL = IF_FEATURE_HTTPD_ENCODE_URL_STR((1 << e_opt_encode_url)) + 0,
2762 OPT_REALM = IF_FEATURE_HTTPD_BASIC_AUTH( (1 << r_opt_realm )) + 0,
2763 OPT_MD5 = IF_FEATURE_HTTPD_AUTH_MD5( (1 << m_opt_md5 )) + 0,
2764 OPT_SETUID = IF_FEATURE_HTTPD_SETUID( (1 << u_opt_setuid )) + 0,
2765 OPT_PORT = 1 << p_opt_port,
2766 OPT_INETD = 1 << p_opt_inetd,
2767 OPT_FOREGROUND = 1 << p_opt_foreground,
2768 OPT_VERBOSE = 1 << p_opt_verbose,
2769};
2770
2771
2772int httpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
2773int httpd_main(int argc UNUSED_PARAM, char **argv)
2774{
2775 int server_socket = server_socket;
2776 unsigned opt;
2777 char *url_for_decode;
2778 IF_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;)
2779 IF_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;)
2780 IF_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;)
2781 IF_FEATURE_HTTPD_AUTH_MD5(const char *pass;)
2782
2783 INIT_G();
2784
2785#if ENABLE_LOCALE_SUPPORT
2786
2787 setlocale(LC_TIME, "C");
2788#endif
2789
2790 home_httpd = xrealloc_getcwd_or_warn(NULL);
2791
2792
2793
2794 opt = getopt32(argv, "^"
2795 "c:d:h:"
2796 IF_FEATURE_HTTPD_ENCODE_URL_STR("e:")
2797 IF_FEATURE_HTTPD_BASIC_AUTH("r:")
2798 IF_FEATURE_HTTPD_AUTH_MD5("m:")
2799 IF_FEATURE_HTTPD_SETUID("u:")
2800 "p:ifv"
2801 "\0"
2802
2803 "vv:if",
2804 &opt_c_configFile, &url_for_decode, &home_httpd
2805 IF_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode)
2806 IF_FEATURE_HTTPD_BASIC_AUTH(, &g_realm)
2807 IF_FEATURE_HTTPD_AUTH_MD5(, &pass)
2808 IF_FEATURE_HTTPD_SETUID(, &s_ugid)
2809 , &bind_addr_or_port
2810 , &verbose
2811 );
2812 if (opt & OPT_DECODE_URL) {
2813 fputs_stdout(percent_decode_in_place(url_for_decode, 0));
2814 return 0;
2815 }
2816#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
2817 if (opt & OPT_ENCODE_URL) {
2818 fputs_stdout(encodeString(url_for_encode));
2819 return 0;
2820 }
2821#endif
2822#if ENABLE_FEATURE_HTTPD_AUTH_MD5
2823 if (opt & OPT_MD5) {
2824 char salt[sizeof("$1$XXXXXXXX")];
2825 salt[0] = '$';
2826 salt[1] = '1';
2827 salt[2] = '$';
2828 crypt_make_salt(salt + 3, 4);
2829 puts(pw_encrypt(pass, salt, 0));
2830 return 0;
2831 }
2832#endif
2833#if ENABLE_FEATURE_HTTPD_SETUID
2834 if (opt & OPT_SETUID) {
2835 xget_uidgid(&ugid, s_ugid);
2836 }
2837#endif
2838
2839#if !BB_MMU
2840 if (!(opt & OPT_FOREGROUND)) {
2841 bb_daemonize_or_rexec(0, argv);
2842 re_execed = 0;
2843 }
2844#endif
2845
2846
2847
2848 if (!re_execed)
2849 xchdir(home_httpd);
2850
2851 if (!(opt & OPT_INETD)) {
2852 signal(SIGCHLD, SIG_IGN);
2853 server_socket = openServer();
2854#if ENABLE_FEATURE_HTTPD_SETUID
2855
2856 if (opt & OPT_SETUID) {
2857 if (ugid.gid != (gid_t)-1) {
2858 if (setgroups(1, &ugid.gid) == -1)
2859 bb_simple_perror_msg_and_die("setgroups");
2860 xsetgid(ugid.gid);
2861 }
2862 xsetuid(ugid.uid);
2863 }
2864#endif
2865 }
2866
2867#if 0
2868
2869
2870
2871
2872
2873
2874 {
2875 char *p = getenv("PATH");
2876
2877 clearenv();
2878 if (p)
2879 putenv(p - 5);
2880
2881
2882 }
2883#endif
2884
2885 parse_conf(DEFAULT_PATH_HTTPD_CONF, FIRST_PARSE);
2886 if (!(opt & OPT_INETD))
2887 signal(SIGHUP, sighup_handler);
2888
2889 xfunc_error_retval = 0;
2890 if (opt & OPT_INETD)
2891 mini_httpd_inetd();
2892#if BB_MMU
2893 if (!(opt & OPT_FOREGROUND))
2894 bb_daemonize(0);
2895 mini_httpd(server_socket);
2896#else
2897 mini_httpd_nommu(server_socket, argc, argv);
2898#endif
2899
2900}
2901