[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 "fSDRC: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 case 'R':
652 conf.rfc1918_filter = 1;
653 break;
654
655 #ifdef HAVE_CGI
656 /* cgi prefix */
657 case 'x':
658 conf.cgi_prefix = optarg;
659 break;
660 #endif
661
662 #ifdef HAVE_LUA
663 /* lua prefix */
664 case 'l':
665 conf.lua_prefix = optarg;
666 break;
667
668 /* lua handler */
669 case 'L':
670 conf.lua_handler = optarg;
671 break;
672 #endif
673
674 #if defined(HAVE_CGI) || defined(HAVE_LUA)
675 /* script timeout */
676 case 't':
677 conf.script_timeout = atoi(optarg);
678 break;
679 #endif
680
681 /* network timeout */
682 case 'T':
683 conf.network_timeout = atoi(optarg);
684 break;
685
686 /* no fork */
687 case 'f':
688 nofork = 1;
689 break;
690
691 /* urldecode */
692 case 'd':
693 if( (port = malloc(strlen(optarg)+1)) != NULL )
694 {
695 memset(port, 0, strlen(optarg)+1);
696 uh_urldecode(port, strlen(optarg), optarg, strlen(optarg));
697 printf("%s", port);
698 free(port);
699 exit(0);
700 }
701 break;
702
703 /* basic auth realm */
704 case 'r':
705 conf.realm = optarg;
706 break;
707
708 /* md5 crypt */
709 case 'm':
710 printf("%s\n", crypt(optarg, "$1$"));
711 exit(0);
712 break;
713
714 /* config file */
715 case 'c':
716 conf.file = optarg;
717 break;
718
719 default:
720 fprintf(stderr,
721 "Usage: %s -p [addr:]port [-h docroot]\n"
722 " -f Do not fork to background\n"
723 " -c file Configuration file, default is '/etc/httpd.conf'\n"
724 " -p [addr:]port Bind to specified address and port, multiple allowed\n"
725 #ifdef HAVE_TLS
726 " -s [addr:]port Like -p but provide HTTPS on this port\n"
727 " -C file ASN.1 server certificate file\n"
728 " -K file ASN.1 server private key file\n"
729 #endif
730 " -h directory Specify the document root, default is '.'\n"
731 " -E string Use given virtual URL as 404 error handler\n"
732 " -I string Use given filename as index page for directories\n"
733 " -S Do not follow symbolic links outside of the docroot\n"
734 " -D Do not allow directory listings, send 403 instead\n"
735 " -R Enable RFC1918 filter\n"
736 #ifdef HAVE_LUA
737 " -l string URL prefix for Lua handler, default is '/lua'\n"
738 " -L file Lua handler script, omit to disable Lua\n"
739 #endif
740 #ifdef HAVE_CGI
741 " -x string URL prefix for CGI handler, default is '/cgi-bin'\n"
742 #endif
743 #if defined(HAVE_CGI) || defined(HAVE_LUA)
744 " -t seconds CGI and Lua script timeout in seconds, default is 60\n"
745 #endif
746 " -T seconds Network timeout in seconds, default is 30\n"
747 " -d string URL decode given string\n"
748 " -r string Specify basic auth realm\n"
749 " -m string MD5 crypt given string\n"
750 "\n", argv[0]
751 );
752
753 exit(1);
754 }
755 }
756
757 #ifdef HAVE_TLS
758 if( (tls == 1) && (keys < 2) )
759 {
760 fprintf(stderr, "Error: Missing private key or certificate file\n");
761 exit(1);
762 }
763 #endif
764
765 if( bound < 1 )
766 {
767 fprintf(stderr, "Error: No sockets bound, unable to continue\n");
768 exit(1);
769 }
770
771 /* default docroot */
772 if( !conf.docroot[0] && !realpath(".", conf.docroot) )
773 {
774 fprintf(stderr, "Error: Can not determine default document root: %s\n",
775 strerror(errno));
776 exit(1);
777 }
778
779 /* default realm */
780 if( ! conf.realm )
781 conf.realm = "Protected Area";
782
783 /* config file */
784 uh_config_parse(&conf);
785
786 /* default network timeout */
787 if( conf.network_timeout <= 0 )
788 conf.network_timeout = 30;
789
790 #if defined(HAVE_CGI) || defined(HAVE_LUA)
791 /* default script timeout */
792 if( conf.script_timeout <= 0 )
793 conf.script_timeout = 60;
794 #endif
795
796 #ifdef HAVE_CGI
797 /* default cgi prefix */
798 if( ! conf.cgi_prefix )
799 conf.cgi_prefix = "/cgi-bin";
800 #endif
801
802 #ifdef HAVE_LUA
803 /* load Lua plugin */
804 if( ! (lib = dlopen("uhttpd_lua.so", RTLD_LAZY | RTLD_GLOBAL)) )
805 {
806 fprintf(stderr,
807 "Notice: Unable to load Lua plugin - disabling Lua support! "
808 "(Reason: %s)\n", dlerror()
809 );
810 }
811 else
812 {
813 /* resolve functions */
814 if( !(conf.lua_init = dlsym(lib, "uh_lua_init")) ||
815 !(conf.lua_close = dlsym(lib, "uh_lua_close")) ||
816 !(conf.lua_request = dlsym(lib, "uh_lua_request"))
817 ) {
818 fprintf(stderr,
819 "Error: Failed to lookup required symbols "
820 "in Lua plugin: %s\n", dlerror()
821 );
822 exit(1);
823 }
824
825 /* init Lua runtime if handler is specified */
826 if( conf.lua_handler )
827 {
828 /* default lua prefix */
829 if( ! conf.lua_prefix )
830 conf.lua_prefix = "/lua";
831
832 L = conf.lua_init(conf.lua_handler);
833 }
834 }
835 #endif
836
837 /* fork (if not disabled) */
838 if( ! nofork )
839 {
840 switch( fork() )
841 {
842 case -1:
843 perror("fork()");
844 exit(1);
845
846 case 0:
847 /* daemon setup */
848 if( chdir("/") )
849 perror("chdir()");
850
851 if( (cur_fd = open("/dev/null", O_WRONLY)) > -1 )
852 dup2(cur_fd, 0);
853
854 if( (cur_fd = open("/dev/null", O_RDONLY)) > -1 )
855 dup2(cur_fd, 1);
856
857 if( (cur_fd = open("/dev/null", O_RDONLY)) > -1 )
858 dup2(cur_fd, 2);
859
860 break;
861
862 default:
863 exit(0);
864 }
865 }
866
867 /* backup server descriptor set */
868 used_fds = serv_fds;
869
870 /* loop */
871 while(run)
872 {
873 /* create a working copy of the used fd set */
874 read_fds = used_fds;
875
876 /* sleep until socket activity */
877 if( select(max_fd + 1, &read_fds, NULL, NULL, NULL) == -1 )
878 {
879 perror("select()");
880 exit(1);
881 }
882
883 /* run through the existing connections looking for data to be read */
884 for( cur_fd = 0; cur_fd <= max_fd; cur_fd++ )
885 {
886 /* is a socket managed by us */
887 if( FD_ISSET(cur_fd, &read_fds) )
888 {
889 /* is one of our listen sockets */
890 if( FD_ISSET(cur_fd, &serv_fds) )
891 {
892 /* handle new connections */
893 if( (new_fd = accept(cur_fd, NULL, 0)) != -1 )
894 {
895 /* add to global client list */
896 if( (cl = uh_client_add(new_fd, uh_listener_lookup(cur_fd))) != NULL )
897 {
898 #ifdef HAVE_TLS
899 /* setup client tls context */
900 if( conf.tls )
901 conf.tls_accept(cl);
902 #endif
903
904 /* add client socket to global fdset */
905 FD_SET(new_fd, &used_fds);
906 fd_cloexec(new_fd);
907 max_fd = max(max_fd, new_fd);
908 }
909
910 /* insufficient resources */
911 else
912 {
913 fprintf(stderr,
914 "uh_client_add(): Can not manage more than "
915 "%i client sockets, connection dropped\n",
916 UH_LIMIT_CLIENTS
917 );
918
919 close(new_fd);
920 }
921 }
922 }
923
924 /* is a client socket */
925 else
926 {
927 if( ! (cl = uh_client_lookup(cur_fd)) )
928 {
929 /* this should not happen! */
930 fprintf(stderr,
931 "uh_client_lookup(): No entry for fd %i!\n",
932 cur_fd);
933
934 goto cleanup;
935 }
936
937 /* parse message header */
938 if( (req = uh_http_header_recv(cl)) != NULL )
939 {
940 /* RFC1918 filtering required? */
941 if( conf.rfc1918_filter && sa_rfc1918(&cl->peeraddr) &&
942 !sa_rfc1918(&cl->servaddr) )
943 {
944 uh_http_sendhf(cl, 403, "Forbidden",
945 "Rejected request from RFC1918 IP to public server address");
946 }
947 else
948 #ifdef HAVE_LUA
949 /* Lua request? */
950 if( L && uh_path_match(conf.lua_prefix, req->url) )
951 {
952 conf.lua_request(cl, req, L);
953 }
954 else
955 #endif
956 /* dispatch request */
957 if( (pin = uh_path_lookup(cl, req->url)) != NULL )
958 {
959 /* auth ok? */
960 if( uh_auth_check(cl, req, pin) )
961 {
962 #ifdef HAVE_CGI
963 if( uh_path_match(conf.cgi_prefix, pin->name) )
964 {
965 uh_cgi_request(cl, req, pin);
966 }
967 else
968 #endif
969 {
970 uh_file_request(cl, req, pin);
971 }
972 }
973 }
974
975 /* 404 */
976 else
977 {
978 /* Try to invoke an error handler */
979 pin = uh_path_lookup(cl, conf.error_handler);
980
981 if( pin && uh_auth_check(cl, req, pin) )
982 {
983 req->redirect_status = 404;
984
985 #ifdef HAVE_CGI
986 if( uh_path_match(conf.cgi_prefix, pin->name) )
987 {
988 uh_cgi_request(cl, req, pin);
989 }
990 else
991 #endif
992 {
993 uh_file_request(cl, req, pin);
994 }
995 }
996 else
997 {
998 uh_http_sendhf(cl, 404, "Not Found",
999 "No such file or directory");
1000 }
1001 }
1002 }
1003
1004 #ifdef HAVE_TLS
1005 /* free client tls context */
1006 if( conf.tls )
1007 conf.tls_close(cl);
1008 #endif
1009
1010 cleanup:
1011
1012 /* close client socket */
1013 close(cur_fd);
1014 FD_CLR(cur_fd, &used_fds);
1015
1016 /* remove from global client list */
1017 uh_client_remove(cur_fd);
1018 }
1019 }
1020 }
1021 }
1022
1023 #ifdef HAVE_LUA
1024 /* destroy the Lua state */
1025 if( L != NULL )
1026 conf.lua_close(L);
1027 #endif
1028
1029 return 0;
1030 }
1031