9b9608628d4d393dc654bfbe966d5bdc17cb48a7
[openwrt/svn-archive/archive.git] / package / uhttpd / src / uhttpd.c
1 /*
2 * uhttpd - Tiny single-threaded httpd - Main component
3 *
4 * Copyright (C) 2010 Jo-Philipp Wich <xm@subsignal.org>
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 #define _XOPEN_SOURCE 500 /* crypt() */
20
21 #include "uhttpd.h"
22 #include "uhttpd-utils.h"
23 #include "uhttpd-file.h"
24
25 #ifdef HAVE_CGI
26 #include "uhttpd-cgi.h"
27 #endif
28
29 #ifdef HAVE_LUA
30 #include "uhttpd-lua.h"
31 #endif
32
33 #ifdef HAVE_TLS
34 #include "uhttpd-tls.h"
35 #endif
36
37
38 static int run = 1;
39
40 static void uh_sigterm(int sig)
41 {
42 run = 0;
43 }
44
45 static void uh_sigchld(int sig)
46 {
47 while( waitpid(-1, NULL, WNOHANG) > 0 ) { }
48 }
49
50 static void uh_config_parse(struct config *conf)
51 {
52 FILE *c;
53 char line[512];
54 char *col1 = NULL;
55 char *col2 = NULL;
56 char *eol = NULL;
57
58 const char *path = conf->file ? conf->file : "/etc/httpd.conf";
59
60
61 if( (c = fopen(path, "r")) != NULL )
62 {
63 memset(line, 0, sizeof(line));
64
65 while( fgets(line, sizeof(line) - 1, c) )
66 {
67 if( (line[0] == '/') && (strchr(line, ':') != NULL) )
68 {
69 if( !(col1 = strchr(line, ':')) || (*col1++ = 0) ||
70 !(col2 = strchr(col1, ':')) || (*col2++ = 0) ||
71 !(eol = strchr(col2, '\n')) || (*eol++ = 0) )
72 continue;
73
74 if( !uh_auth_add(line, col1, col2) )
75 {
76 fprintf(stderr,
77 "Notice: No password set for user %s, ignoring "
78 "authentication on %s\n", col1, line
79 );
80 }
81 }
82 else if( !strncmp(line, "I:", 2) )
83 {
84 if( !(col1 = strchr(line, ':')) || (*col1++ = 0) ||
85 !(eol = strchr(col1, '\n')) || (*eol++ = 0) )
86 continue;
87
88 conf->index_file = strdup(col1);
89 }
90 else if( !strncmp(line, "E404:", 5) )
91 {
92 if( !(col1 = strchr(line, ':')) || (*col1++ = 0) ||
93 !(eol = strchr(col1, '\n')) || (*eol++ = 0) )
94 continue;
95
96 conf->error_handler = strdup(col1);
97 }
98 #ifdef HAVE_CGI
99 else if( (line[0] == '*') && (strchr(line, ':') != NULL) )
100 {
101 if( !(col1 = strchr(line, '*')) || (*col1++ = 0) ||
102 !(col2 = strchr(col1, ':')) || (*col2++ = 0) ||
103 !(eol = strchr(col2, '\n')) || (*eol++ = 0) )
104 continue;
105
106 if( !uh_interpreter_add(col1, col2) )
107 {
108 fprintf(stderr,
109 "Unable to add interpreter %s for extension %s: "
110 "Out of memory\n", col2, col1
111 );
112 }
113 }
114 #endif
115 }
116
117 fclose(c);
118 }
119 }
120
121 static int uh_socket_bind(
122 fd_set *serv_fds, int *max_fd, const char *host, const char *port,
123 struct addrinfo *hints, int do_tls, struct config *conf
124 ) {
125 int sock = -1;
126 int yes = 1;
127 int status;
128 int bound = 0;
129
130 int tcp_ka_idl, tcp_ka_int, tcp_ka_cnt;
131
132 struct listener *l = NULL;
133 struct addrinfo *addrs = NULL, *p = NULL;
134
135 if( (status = getaddrinfo(host, port, hints, &addrs)) != 0 )
136 {
137 fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(status));
138 }
139
140 /* try to bind a new socket to each found address */
141 for( p = addrs; p; p = p->ai_next )
142 {
143 /* get the socket */
144 if( (sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1 )
145 {
146 perror("socket()");
147 goto error;
148 }
149
150 /* "address already in use" */
151 if( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) )
152 {
153 perror("setsockopt()");
154 goto error;
155 }
156
157 /* TCP keep-alive */
158 if( conf->tcp_keepalive > 0 )
159 {
160 tcp_ka_idl = 1;
161 tcp_ka_cnt = 3;
162 tcp_ka_int = conf->tcp_keepalive;
163
164 if( setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) ||
165 setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, &tcp_ka_idl, sizeof(tcp_ka_idl)) ||
166 setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, &tcp_ka_int, sizeof(tcp_ka_int)) ||
167 setsockopt(sock, SOL_TCP, TCP_KEEPCNT, &tcp_ka_cnt, sizeof(tcp_ka_cnt)) )
168 {
169 fprintf(stderr, "Notice: Unable to enable TCP keep-alive: %s\n",
170 strerror(errno));
171 }
172 }
173
174 /* required to get parallel v4 + v6 working */
175 if( p->ai_family == AF_INET6 )
176 {
177 if( setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) == -1 )
178 {
179 perror("setsockopt()");
180 goto error;
181 }
182 }
183
184 /* bind */
185 if( bind(sock, p->ai_addr, p->ai_addrlen) == -1 )
186 {
187 perror("bind()");
188 goto error;
189 }
190
191 /* listen */
192 if( listen(sock, UH_LIMIT_CLIENTS) == -1 )
193 {
194 perror("listen()");
195 goto error;
196 }
197
198 /* add listener to global list */
199 if( ! (l = uh_listener_add(sock, conf)) )
200 {
201 fprintf(stderr, "uh_listener_add(): Failed to allocate memory\n");
202 goto error;
203 }
204
205 #ifdef HAVE_TLS
206 /* init TLS */
207 l->tls = do_tls ? conf->tls : NULL;
208 #endif
209
210 /* add socket to server fd set */
211 FD_SET(sock, serv_fds);
212 fd_cloexec(sock);
213 *max_fd = max(*max_fd, sock);
214
215 bound++;
216 continue;
217
218 error:
219 if( sock > 0 )
220 close(sock);
221 }
222
223 freeaddrinfo(addrs);
224
225 return bound;
226 }
227
228 static struct http_request * uh_http_header_parse(struct client *cl, char *buffer, int buflen)
229 {
230 char *method = &buffer[0];
231 char *path = NULL;
232 char *version = NULL;
233
234 char *headers = NULL;
235 char *hdrname = NULL;
236 char *hdrdata = NULL;
237
238 int i;
239 int hdrcount = 0;
240
241 static struct http_request req;
242
243 memset(&req, 0, sizeof(req));
244
245
246 /* terminate initial header line */
247 if( (headers = strfind(buffer, buflen, "\r\n", 2)) != NULL )
248 {
249 buffer[buflen-1] = 0;
250
251 *headers++ = 0;
252 *headers++ = 0;
253
254 /* find request path */
255 if( (path = strchr(buffer, ' ')) != NULL )
256 *path++ = 0;
257
258 /* find http version */
259 if( (path != NULL) && ((version = strchr(path, ' ')) != NULL) )
260 *version++ = 0;
261
262
263 /* check method */
264 if( strcmp(method, "GET") && strcmp(method, "HEAD") && strcmp(method, "POST") )
265 {
266 /* invalid method */
267 uh_http_response(cl, 405, "Method Not Allowed");
268 return NULL;
269 }
270 else
271 {
272 switch(method[0])
273 {
274 case 'G':
275 req.method = UH_HTTP_MSG_GET;
276 break;
277
278 case 'H':
279 req.method = UH_HTTP_MSG_HEAD;
280 break;
281
282 case 'P':
283 req.method = UH_HTTP_MSG_POST;
284 break;
285 }
286 }
287
288 /* check path */
289 if( !path || !strlen(path) )
290 {
291 /* malformed request */
292 uh_http_response(cl, 400, "Bad Request");
293 return NULL;
294 }
295 else
296 {
297 req.url = path;
298 }
299
300 /* check version */
301 if( (version == NULL) || (strcmp(version, "HTTP/0.9") &&
302 strcmp(version, "HTTP/1.0") && strcmp(version, "HTTP/1.1")) )
303 {
304 /* unsupported version */
305 uh_http_response(cl, 400, "Bad Request");
306 return NULL;
307 }
308 else
309 {
310 req.version = strtof(&version[5], NULL);
311 }
312
313
314 /* process header fields */
315 for( i = (int)(headers - buffer); i < buflen; i++ )
316 {
317 /* found eol and have name + value, push out header tuple */
318 if( hdrname && hdrdata && (buffer[i] == '\r' || buffer[i] == '\n') )
319 {
320 buffer[i] = 0;
321
322 /* store */
323 if( (hdrcount + 1) < array_size(req.headers) )
324 {
325 req.headers[hdrcount++] = hdrname;
326 req.headers[hdrcount++] = hdrdata;
327
328 hdrname = hdrdata = NULL;
329 }
330
331 /* too large */
332 else
333 {
334 uh_http_response(cl, 413, "Request Entity Too Large");
335 return NULL;
336 }
337 }
338
339 /* have name but no value and found a colon, start of value */
340 else if( hdrname && !hdrdata &&
341 ((i+1) < buflen) && (buffer[i] == ':')
342 ) {
343 buffer[i] = 0;
344 hdrdata = &buffer[i+1];
345
346 while ((hdrdata + 1) < (buffer + buflen) && *hdrdata == ' ')
347 hdrdata++;
348 }
349
350 /* have no name and found [A-Za-z], start of name */
351 else if( !hdrname && isalpha(buffer[i]) )
352 {
353 hdrname = &buffer[i];
354 }
355 }
356
357 /* valid enough */
358 req.redirect_status = 200;
359 return &req;
360 }
361
362 /* Malformed request */
363 uh_http_response(cl, 400, "Bad Request");
364 return NULL;
365 }
366
367
368 static struct http_request * uh_http_header_recv(struct client *cl)
369 {
370 static char buffer[UH_LIMIT_MSGHEAD];
371 char *bufptr = &buffer[0];
372 char *idxptr = NULL;
373
374 struct timeval timeout;
375
376 fd_set reader;
377
378 ssize_t blen = sizeof(buffer)-1;
379 ssize_t rlen = 0;
380
381 memset(buffer, 0, sizeof(buffer));
382
383 while( blen > 0 )
384 {
385 FD_ZERO(&reader);
386 FD_SET(cl->socket, &reader);
387
388 /* fail after 0.1s */
389 timeout.tv_sec = 0;
390 timeout.tv_usec = 100000;
391
392 /* check whether fd is readable */
393 if( select(cl->socket + 1, &reader, NULL, NULL, &timeout) > 0 )
394 {
395 /* receive data */
396 ensure_out(rlen = uh_tcp_peek(cl, bufptr, blen));
397
398 if( (idxptr = strfind(buffer, sizeof(buffer), "\r\n\r\n", 4)) )
399 {
400 ensure_out(rlen = uh_tcp_recv(cl, bufptr,
401 (int)(idxptr - bufptr) + 4));
402
403 /* header read complete ... */
404 blen -= rlen;
405 return uh_http_header_parse(cl, buffer,
406 sizeof(buffer) - blen - 1);
407 }
408 else
409 {
410 ensure_out(rlen = uh_tcp_recv(cl, bufptr, rlen));
411
412 /* unexpected eof - #7904 */
413 if( rlen == 0 )
414 return NULL;
415
416 blen -= rlen;
417 bufptr += rlen;
418 }
419 }
420 else
421 {
422 /* invalid request (unexpected eof/timeout) */
423 return NULL;
424 }
425 }
426
427 /* request entity too large */
428 uh_http_response(cl, 413, "Request Entity Too Large");
429
430 out:
431 return NULL;
432 }
433
434 #if defined(HAVE_LUA) || defined(HAVE_CGI)
435 static int uh_path_match(const char *prefix, const char *url)
436 {
437 if( (strstr(url, prefix) == url) &&
438 ((prefix[strlen(prefix)-1] == '/') ||
439 (strlen(url) == strlen(prefix)) ||
440 (url[strlen(prefix)] == '/'))
441 ) {
442 return 1;
443 }
444
445 return 0;
446 }
447 #endif
448
449 static void uh_dispatch_request(
450 struct client *cl, struct http_request *req, struct path_info *pin
451 ) {
452 #ifdef HAVE_CGI
453 struct interpreter *ipr = NULL;
454
455 if( uh_path_match(cl->server->conf->cgi_prefix, pin->name) ||
456 (ipr = uh_interpreter_lookup(pin->phys)) )
457 {
458 uh_cgi_request(cl, req, pin, ipr);
459 }
460 else
461 #endif
462 {
463 uh_file_request(cl, req, pin);
464 }
465 }
466
467 static void uh_mainloop(struct config *conf, fd_set serv_fds, int max_fd)
468 {
469 /* master file descriptor list */
470 fd_set used_fds, read_fds;
471
472 /* working structs */
473 struct http_request *req;
474 struct path_info *pin;
475 struct client *cl;
476
477 /* maximum file descriptor number */
478 int new_fd, cur_fd = 0;
479
480 /* clear the master and temp sets */
481 FD_ZERO(&used_fds);
482 FD_ZERO(&read_fds);
483
484 /* backup server descriptor set */
485 used_fds = serv_fds;
486
487 /* loop */
488 while(run)
489 {
490 /* create a working copy of the used fd set */
491 read_fds = used_fds;
492
493 /* sleep until socket activity */
494 if( select(max_fd + 1, &read_fds, NULL, NULL, NULL) == -1 )
495 {
496 perror("select()");
497 exit(1);
498 }
499
500 /* run through the existing connections looking for data to be read */
501 for( cur_fd = 0; cur_fd <= max_fd; cur_fd++ )
502 {
503 /* is a socket managed by us */
504 if( FD_ISSET(cur_fd, &read_fds) )
505 {
506 /* is one of our listen sockets */
507 if( FD_ISSET(cur_fd, &serv_fds) )
508 {
509 /* handle new connections */
510 if( (new_fd = accept(cur_fd, NULL, 0)) != -1 )
511 {
512 /* add to global client list */
513 if( (cl = uh_client_add(new_fd, uh_listener_lookup(cur_fd))) != NULL )
514 {
515 #ifdef HAVE_TLS
516 /* setup client tls context */
517 if( conf->tls )
518 {
519 if( conf->tls_accept(cl) < 1 )
520 {
521 fprintf(stderr,
522 "tls_accept failed, "
523 "connection dropped\n");
524
525 /* close client socket */
526 close(new_fd);
527
528 /* remove from global client list */
529 uh_client_remove(new_fd);
530
531 continue;
532 }
533 }
534 #endif
535
536 /* add client socket to global fdset */
537 FD_SET(new_fd, &used_fds);
538 fd_cloexec(new_fd);
539 max_fd = max(max_fd, new_fd);
540 }
541
542 /* insufficient resources */
543 else
544 {
545 fprintf(stderr,
546 "uh_client_add(): Cannot allocate memory\n");
547
548 close(new_fd);
549 }
550 }
551 }
552
553 /* is a client socket */
554 else
555 {
556 if( ! (cl = uh_client_lookup(cur_fd)) )
557 {
558 /* this should not happen! */
559 fprintf(stderr,
560 "uh_client_lookup(): No entry for fd %i!\n",
561 cur_fd);
562
563 goto cleanup;
564 }
565
566 /* parse message header */
567 if( (req = uh_http_header_recv(cl)) != NULL )
568 {
569 /* RFC1918 filtering required? */
570 if( conf->rfc1918_filter &&
571 sa_rfc1918(&cl->peeraddr) &&
572 !sa_rfc1918(&cl->servaddr) )
573 {
574 uh_http_sendhf(cl, 403, "Forbidden",
575 "Rejected request from RFC1918 IP "
576 "to public server address");
577 }
578 else
579 #ifdef HAVE_LUA
580 /* Lua request? */
581 if( conf->lua_state &&
582 uh_path_match(conf->lua_prefix, req->url) )
583 {
584 conf->lua_request(cl, req, conf->lua_state);
585 }
586 else
587 #endif
588 /* dispatch request */
589 if( (pin = uh_path_lookup(cl, req->url)) != NULL )
590 {
591 /* auth ok? */
592 if( !pin->redirected && uh_auth_check(cl, req, pin) )
593 uh_dispatch_request(cl, req, pin);
594 }
595
596 /* 404 */
597 else
598 {
599 /* Try to invoke an error handler */
600 pin = uh_path_lookup(cl, conf->error_handler);
601
602 if( pin && uh_auth_check(cl, req, pin) )
603 {
604 req->redirect_status = 404;
605 uh_dispatch_request(cl, req, pin);
606 }
607 else
608 {
609 uh_http_sendhf(cl, 404, "Not Found",
610 "No such file or directory");
611 }
612 }
613 }
614
615 #ifdef HAVE_TLS
616 /* free client tls context */
617 if( conf->tls )
618 conf->tls_close(cl);
619 #endif
620
621 cleanup:
622
623 /* close client socket */
624 close(cur_fd);
625 FD_CLR(cur_fd, &used_fds);
626
627 /* remove from global client list */
628 uh_client_remove(cur_fd);
629 }
630 }
631 }
632 }
633
634 #ifdef HAVE_LUA
635 /* destroy the Lua state */
636 if( conf->lua_state != NULL )
637 conf->lua_close(conf->lua_state);
638 #endif
639 }
640
641 #ifdef HAVE_TLS
642 static inline int uh_inittls(struct config *conf)
643 {
644 /* library handle */
645 void *lib;
646
647 /* already loaded */
648 if( conf->tls != NULL )
649 return 0;
650
651 /* load TLS plugin */
652 if( ! (lib = dlopen("uhttpd_tls.so", RTLD_LAZY | RTLD_GLOBAL)) )
653 {
654 fprintf(stderr,
655 "Notice: Unable to load TLS plugin - disabling SSL support! "
656 "(Reason: %s)\n", dlerror()
657 );
658
659 return 1;
660 }
661 else
662 {
663 /* resolve functions */
664 if( !(conf->tls_init = dlsym(lib, "uh_tls_ctx_init")) ||
665 !(conf->tls_cert = dlsym(lib, "uh_tls_ctx_cert")) ||
666 !(conf->tls_key = dlsym(lib, "uh_tls_ctx_key")) ||
667 !(conf->tls_free = dlsym(lib, "uh_tls_ctx_free")) ||
668 !(conf->tls_accept = dlsym(lib, "uh_tls_client_accept")) ||
669 !(conf->tls_close = dlsym(lib, "uh_tls_client_close")) ||
670 !(conf->tls_recv = dlsym(lib, "uh_tls_client_recv")) ||
671 !(conf->tls_send = dlsym(lib, "uh_tls_client_send"))
672 ) {
673 fprintf(stderr,
674 "Error: Failed to lookup required symbols "
675 "in TLS plugin: %s\n", dlerror()
676 );
677 exit(1);
678 }
679
680 /* init SSL context */
681 if( ! (conf->tls = conf->tls_init()) )
682 {
683 fprintf(stderr, "Error: Failed to initalize SSL context\n");
684 exit(1);
685 }
686 }
687
688 return 0;
689 }
690 #endif
691
692 int main (int argc, char **argv)
693 {
694 /* master file descriptor list */
695 fd_set serv_fds;
696
697 /* working structs */
698 struct addrinfo hints;
699 struct sigaction sa;
700 struct config conf;
701
702 /* signal mask */
703 sigset_t ss;
704
705 /* maximum file descriptor number */
706 int cur_fd, max_fd = 0;
707
708 #ifdef HAVE_TLS
709 int tls = 0;
710 int keys = 0;
711 #endif
712
713 int bound = 0;
714 int nofork = 0;
715
716 /* args */
717 int opt;
718 char bind[128];
719 char *port = NULL;
720
721 #ifdef HAVE_LUA
722 /* library handle */
723 void *lib;
724 #endif
725
726 FD_ZERO(&serv_fds);
727
728 /* handle SIGPIPE, SIGINT, SIGTERM, SIGCHLD */
729 sa.sa_flags = 0;
730 sigemptyset(&sa.sa_mask);
731
732 sa.sa_handler = SIG_IGN;
733 sigaction(SIGPIPE, &sa, NULL);
734
735 sa.sa_handler = uh_sigchld;
736 sigaction(SIGCHLD, &sa, NULL);
737
738 sa.sa_handler = uh_sigterm;
739 sigaction(SIGINT, &sa, NULL);
740 sigaction(SIGTERM, &sa, NULL);
741
742 /* defer SIGCHLD */
743 sigemptyset(&ss);
744 sigaddset(&ss, SIGCHLD);
745 sigprocmask(SIG_BLOCK, &ss, NULL);
746
747 /* prepare addrinfo hints */
748 memset(&hints, 0, sizeof(hints));
749 hints.ai_family = AF_UNSPEC;
750 hints.ai_socktype = SOCK_STREAM;
751 hints.ai_flags = AI_PASSIVE;
752
753 /* parse args */
754 memset(&conf, 0, sizeof(conf));
755 memset(bind, 0, sizeof(bind));
756
757
758 while( (opt = getopt(argc, argv,
759 "fSDRC:K:E:I:p:s:h:c:l:L:d:r:m:x:i:t:T:A:")) > 0
760 ) {
761 switch(opt)
762 {
763 /* [addr:]port */
764 case 'p':
765 case 's':
766 if( (port = strrchr(optarg, ':')) != NULL )
767 {
768 if( (optarg[0] == '[') && (port > optarg) && (port[-1] == ']') )
769 memcpy(bind, optarg + 1,
770 min(sizeof(bind), (int)(port - optarg) - 2));
771 else
772 memcpy(bind, optarg,
773 min(sizeof(bind), (int)(port - optarg)));
774
775 port++;
776 }
777 else
778 {
779 port = optarg;
780 }
781
782 #ifdef HAVE_TLS
783 if( opt == 's' )
784 {
785 if( uh_inittls(&conf) )
786 {
787 fprintf(stderr,
788 "Notice: TLS support is disabled, "
789 "ignoring '-s %s'\n", optarg
790 );
791 continue;
792 }
793
794 tls = 1;
795 }
796 #endif
797
798 /* bind sockets */
799 bound += uh_socket_bind(
800 &serv_fds, &max_fd, bind[0] ? bind : NULL, port,
801 &hints, (opt == 's'), &conf
802 );
803
804 memset(bind, 0, sizeof(bind));
805 break;
806
807 #ifdef HAVE_TLS
808 /* certificate */
809 case 'C':
810 if( !uh_inittls(&conf) )
811 {
812 if( conf.tls_cert(conf.tls, optarg) < 1 )
813 {
814 fprintf(stderr,
815 "Error: Invalid certificate file given\n");
816 exit(1);
817 }
818
819 keys++;
820 }
821
822 break;
823
824 /* key */
825 case 'K':
826 if( !uh_inittls(&conf) )
827 {
828 if( conf.tls_key(conf.tls, optarg) < 1 )
829 {
830 fprintf(stderr,
831 "Error: Invalid private key file given\n");
832 exit(1);
833 }
834
835 keys++;
836 }
837
838 break;
839 #endif
840
841 /* docroot */
842 case 'h':
843 if( ! realpath(optarg, conf.docroot) )
844 {
845 fprintf(stderr, "Error: Invalid directory %s: %s\n",
846 optarg, strerror(errno));
847 exit(1);
848 }
849 break;
850
851 /* error handler */
852 case 'E':
853 if( (strlen(optarg) == 0) || (optarg[0] != '/') )
854 {
855 fprintf(stderr, "Error: Invalid error handler: %s\n",
856 optarg);
857 exit(1);
858 }
859 conf.error_handler = optarg;
860 break;
861
862 /* index file */
863 case 'I':
864 if( (strlen(optarg) == 0) || (optarg[0] == '/') )
865 {
866 fprintf(stderr, "Error: Invalid index page: %s\n",
867 optarg);
868 exit(1);
869 }
870 conf.index_file = optarg;
871 break;
872
873 /* don't follow symlinks */
874 case 'S':
875 conf.no_symlinks = 1;
876 break;
877
878 /* don't list directories */
879 case 'D':
880 conf.no_dirlists = 1;
881 break;
882
883 case 'R':
884 conf.rfc1918_filter = 1;
885 break;
886
887 #ifdef HAVE_CGI
888 /* cgi prefix */
889 case 'x':
890 conf.cgi_prefix = optarg;
891 break;
892
893 /* interpreter */
894 case 'i':
895 if( (optarg[0] == '.') && (port = strchr(optarg, '=')) )
896 {
897 *port++ = 0;
898 uh_interpreter_add(optarg, port);
899 }
900 else
901 {
902 fprintf(stderr, "Error: Invalid interpreter: %s\n",
903 optarg);
904 exit(1);
905 }
906 break;
907 #endif
908
909 #ifdef HAVE_LUA
910 /* lua prefix */
911 case 'l':
912 conf.lua_prefix = optarg;
913 break;
914
915 /* lua handler */
916 case 'L':
917 conf.lua_handler = optarg;
918 break;
919 #endif
920
921 #if defined(HAVE_CGI) || defined(HAVE_LUA)
922 /* script timeout */
923 case 't':
924 conf.script_timeout = atoi(optarg);
925 break;
926 #endif
927
928 /* network timeout */
929 case 'T':
930 conf.network_timeout = atoi(optarg);
931 break;
932
933 /* tcp keep-alive */
934 case 'A':
935 conf.tcp_keepalive = atoi(optarg);
936 break;
937
938 /* no fork */
939 case 'f':
940 nofork = 1;
941 break;
942
943 /* urldecode */
944 case 'd':
945 if( (port = malloc(strlen(optarg)+1)) != NULL )
946 {
947 /* "decode" plus to space to retain compat */
948 for (opt = 0; optarg[opt]; opt++)
949 if (optarg[opt] == '+')
950 optarg[opt] = ' ';
951
952 memset(port, 0, strlen(optarg)+1);
953 uh_urldecode(port, strlen(optarg), optarg, strlen(optarg));
954
955 printf("%s", port);
956 free(port);
957 exit(0);
958 }
959 break;
960
961 /* basic auth realm */
962 case 'r':
963 conf.realm = optarg;
964 break;
965
966 /* md5 crypt */
967 case 'm':
968 printf("%s\n", crypt(optarg, "$1$"));
969 exit(0);
970 break;
971
972 /* config file */
973 case 'c':
974 conf.file = optarg;
975 break;
976
977 default:
978 fprintf(stderr,
979 "Usage: %s -p [addr:]port [-h docroot]\n"
980 " -f Do not fork to background\n"
981 " -c file Configuration file, default is '/etc/httpd.conf'\n"
982 " -p [addr:]port Bind to specified address and port, multiple allowed\n"
983 #ifdef HAVE_TLS
984 " -s [addr:]port Like -p but provide HTTPS on this port\n"
985 " -C file ASN.1 server certificate file\n"
986 " -K file ASN.1 server private key file\n"
987 #endif
988 " -h directory Specify the document root, default is '.'\n"
989 " -E string Use given virtual URL as 404 error handler\n"
990 " -I string Use given filename as index page for directories\n"
991 " -S Do not follow symbolic links outside of the docroot\n"
992 " -D Do not allow directory listings, send 403 instead\n"
993 " -R Enable RFC1918 filter\n"
994 #ifdef HAVE_LUA
995 " -l string URL prefix for Lua handler, default is '/lua'\n"
996 " -L file Lua handler script, omit to disable Lua\n"
997 #endif
998 #ifdef HAVE_CGI
999 " -x string URL prefix for CGI handler, default is '/cgi-bin'\n"
1000 " -i .ext=path Use interpreter at path for files with the given extension\n"
1001 #endif
1002 #if defined(HAVE_CGI) || defined(HAVE_LUA)
1003 " -t seconds CGI and Lua script timeout in seconds, default is 60\n"
1004 #endif
1005 " -T seconds Network timeout in seconds, default is 30\n"
1006 " -d string URL decode given string\n"
1007 " -r string Specify basic auth realm\n"
1008 " -m string MD5 crypt given string\n"
1009 "\n", argv[0]
1010 );
1011
1012 exit(1);
1013 }
1014 }
1015
1016 #ifdef HAVE_TLS
1017 if( (tls == 1) && (keys < 2) )
1018 {
1019 fprintf(stderr, "Error: Missing private key or certificate file\n");
1020 exit(1);
1021 }
1022 #endif
1023
1024 if( bound < 1 )
1025 {
1026 fprintf(stderr, "Error: No sockets bound, unable to continue\n");
1027 exit(1);
1028 }
1029
1030 /* default docroot */
1031 if( !conf.docroot[0] && !realpath(".", conf.docroot) )
1032 {
1033 fprintf(stderr, "Error: Can not determine default document root: %s\n",
1034 strerror(errno));
1035 exit(1);
1036 }
1037
1038 /* default realm */
1039 if( ! conf.realm )
1040 conf.realm = "Protected Area";
1041
1042 /* config file */
1043 uh_config_parse(&conf);
1044
1045 /* default network timeout */
1046 if( conf.network_timeout <= 0 )
1047 conf.network_timeout = 30;
1048
1049 #if defined(HAVE_CGI) || defined(HAVE_LUA)
1050 /* default script timeout */
1051 if( conf.script_timeout <= 0 )
1052 conf.script_timeout = 60;
1053 #endif
1054
1055 #ifdef HAVE_CGI
1056 /* default cgi prefix */
1057 if( ! conf.cgi_prefix )
1058 conf.cgi_prefix = "/cgi-bin";
1059 #endif
1060
1061 #ifdef HAVE_LUA
1062 /* load Lua plugin */
1063 if( ! (lib = dlopen("uhttpd_lua.so", RTLD_LAZY | RTLD_GLOBAL)) )
1064 {
1065 fprintf(stderr,
1066 "Notice: Unable to load Lua plugin - disabling Lua support! "
1067 "(Reason: %s)\n", dlerror()
1068 );
1069 }
1070 else
1071 {
1072 /* resolve functions */
1073 if( !(conf.lua_init = dlsym(lib, "uh_lua_init")) ||
1074 !(conf.lua_close = dlsym(lib, "uh_lua_close")) ||
1075 !(conf.lua_request = dlsym(lib, "uh_lua_request"))
1076 ) {
1077 fprintf(stderr,
1078 "Error: Failed to lookup required symbols "
1079 "in Lua plugin: %s\n", dlerror()
1080 );
1081 exit(1);
1082 }
1083
1084 /* init Lua runtime if handler is specified */
1085 if( conf.lua_handler )
1086 {
1087 /* default lua prefix */
1088 if( ! conf.lua_prefix )
1089 conf.lua_prefix = "/lua";
1090
1091 conf.lua_state = conf.lua_init(conf.lua_handler);
1092 }
1093 }
1094 #endif
1095
1096 /* fork (if not disabled) */
1097 if( ! nofork )
1098 {
1099 switch( fork() )
1100 {
1101 case -1:
1102 perror("fork()");
1103 exit(1);
1104
1105 case 0:
1106 /* daemon setup */
1107 if( chdir("/") )
1108 perror("chdir()");
1109
1110 if( (cur_fd = open("/dev/null", O_WRONLY)) > -1 )
1111 dup2(cur_fd, 0);
1112
1113 if( (cur_fd = open("/dev/null", O_RDONLY)) > -1 )
1114 dup2(cur_fd, 1);
1115
1116 if( (cur_fd = open("/dev/null", O_RDONLY)) > -1 )
1117 dup2(cur_fd, 2);
1118
1119 break;
1120
1121 default:
1122 exit(0);
1123 }
1124 }
1125
1126 /* server main loop */
1127 uh_mainloop(&conf, serv_fds, max_fd);
1128
1129 #ifdef HAVE_LUA
1130 /* destroy the Lua state */
1131 if( conf.lua_state != NULL )
1132 conf.lua_close(conf.lua_state);
1133 #endif
1134
1135 return 0;
1136 }