uhttpd: added uhttpd.docroot
[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, 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                                         /* opt now contains strlen(optarg) -- no need to re-scan */
952                                         memset(port, 0, opt+1);
953                                         if (uh_urldecode(port, opt, optarg, opt) < 0)
954                                             fprintf( stderr, "uhttpd: invalid encoding\n" );
955
956                                         printf("%s", port);
957                                         free(port);
958                                         exit(0);
959                                 }
960                                 break;
961
962                         /* basic auth realm */
963                         case 'r':
964                                 conf.realm = optarg;
965                                 break;
966
967                         /* md5 crypt */
968                         case 'm':
969                                 printf("%s\n", crypt(optarg, "$1$"));
970                                 exit(0);
971                                 break;
972
973                         /* config file */
974                         case 'c':
975                                 conf.file = optarg;
976                                 break;
977
978                         default:
979                                 fprintf(stderr,
980                                         "Usage: %s -p [addr:]port [-h docroot]\n"
981                                         "       -f              Do not fork to background\n"
982                                         "       -c file         Configuration file, default is '/etc/httpd.conf'\n"
983                                         "       -p [addr:]port  Bind to specified address and port, multiple allowed\n"
984 #ifdef HAVE_TLS
985                                         "       -s [addr:]port  Like -p but provide HTTPS on this port\n"
986                                         "       -C file         ASN.1 server certificate file\n"
987                                         "       -K file         ASN.1 server private key file\n"
988 #endif
989                                         "       -h directory    Specify the document root, default is '.'\n"
990                                         "       -E string       Use given virtual URL as 404 error handler\n"
991                                         "       -I string       Use given filename as index page for directories\n"
992                                         "       -S              Do not follow symbolic links outside of the docroot\n"
993                                         "       -D              Do not allow directory listings, send 403 instead\n"
994                                         "       -R              Enable RFC1918 filter\n"
995 #ifdef HAVE_LUA
996                                         "       -l string       URL prefix for Lua handler, default is '/lua'\n"
997                                         "       -L file         Lua handler script, omit to disable Lua\n"
998 #endif
999 #ifdef HAVE_CGI
1000                                         "       -x string       URL prefix for CGI handler, default is '/cgi-bin'\n"
1001                                         "       -i .ext=path    Use interpreter at path for files with the given extension\n"
1002 #endif
1003 #if defined(HAVE_CGI) || defined(HAVE_LUA)
1004                                         "       -t seconds      CGI and Lua script timeout in seconds, default is 60\n"
1005 #endif
1006                                         "       -T seconds      Network timeout in seconds, default is 30\n"
1007                                         "       -d string       URL decode given string\n"
1008                                         "       -r string       Specify basic auth realm\n"
1009                                         "       -m string       MD5 crypt given string\n"
1010                                         "\n", argv[0]
1011                                 );
1012
1013                                 exit(1);
1014                 }
1015         }
1016
1017 #ifdef HAVE_TLS
1018         if( (tls == 1) && (keys < 2) )
1019         {
1020                 fprintf(stderr, "Error: Missing private key or certificate file\n");
1021                 exit(1);
1022         }
1023 #endif
1024
1025         if( bound < 1 )
1026         {
1027                 fprintf(stderr, "Error: No sockets bound, unable to continue\n");
1028                 exit(1);
1029         }
1030
1031         /* default docroot */
1032         if( !conf.docroot[0] && !realpath(".", conf.docroot) )
1033         {
1034                 fprintf(stderr, "Error: Can not determine default document root: %s\n",
1035                         strerror(errno));
1036                 exit(1);
1037         }
1038
1039         /* default realm */
1040         if( ! conf.realm )
1041                 conf.realm = "Protected Area";
1042
1043         /* config file */
1044         uh_config_parse(&conf);
1045
1046         /* default network timeout */
1047         if( conf.network_timeout <= 0 )
1048                 conf.network_timeout = 30;
1049
1050 #if defined(HAVE_CGI) || defined(HAVE_LUA)
1051         /* default script timeout */
1052         if( conf.script_timeout <= 0 )
1053                 conf.script_timeout = 60;
1054 #endif
1055
1056 #ifdef HAVE_CGI
1057         /* default cgi prefix */
1058         if( ! conf.cgi_prefix )
1059                 conf.cgi_prefix = "/cgi-bin";
1060 #endif
1061
1062 #ifdef HAVE_LUA
1063         /* load Lua plugin */
1064         if( ! (lib = dlopen("uhttpd_lua.so", RTLD_LAZY | RTLD_GLOBAL)) )
1065         {
1066                 fprintf(stderr,
1067                         "Notice: Unable to load Lua plugin - disabling Lua support! "
1068                         "(Reason: %s)\n", dlerror()
1069                 );
1070         }
1071         else
1072         {
1073                 /* resolve functions */
1074                 if( !(conf.lua_init    = dlsym(lib, "uh_lua_init"))    ||
1075                     !(conf.lua_close   = dlsym(lib, "uh_lua_close"))   ||
1076                     !(conf.lua_request = dlsym(lib, "uh_lua_request"))
1077                 ) {
1078                         fprintf(stderr,
1079                                 "Error: Failed to lookup required symbols "
1080                                 "in Lua plugin: %s\n", dlerror()
1081                         );
1082                         exit(1);
1083                 }
1084
1085                 /* init Lua runtime if handler is specified */
1086                 if( conf.lua_handler )
1087                 {
1088                         /* default lua prefix */
1089                         if( ! conf.lua_prefix )
1090                                 conf.lua_prefix = "/lua";
1091
1092                         conf.lua_state = conf.lua_init(&conf);
1093                 }
1094         }
1095 #endif
1096
1097         /* fork (if not disabled) */
1098         if( ! nofork )
1099         {
1100                 switch( fork() )
1101                 {
1102                         case -1:
1103                                 perror("fork()");
1104                                 exit(1);
1105
1106                         case 0:
1107                                 /* daemon setup */
1108                                 if( chdir("/") )
1109                                         perror("chdir()");
1110
1111                                 if( (cur_fd = open("/dev/null", O_WRONLY)) > -1 )
1112                                         dup2(cur_fd, 0);
1113
1114                                 if( (cur_fd = open("/dev/null", O_RDONLY)) > -1 )
1115                                         dup2(cur_fd, 1);
1116
1117                                 if( (cur_fd = open("/dev/null", O_RDONLY)) > -1 )
1118                                         dup2(cur_fd, 2);
1119
1120                                 break;
1121
1122                         default:
1123                                 exit(0);
1124                 }
1125         }
1126
1127         /* server main loop */
1128         uh_mainloop(&conf, serv_fds, max_fd);
1129
1130 #ifdef HAVE_LUA
1131         /* destroy the Lua state */
1132         if( conf.lua_state != NULL )
1133                 conf.lua_close(conf.lua_state);
1134 #endif
1135
1136         return 0;
1137 }