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