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