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