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