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