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