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

Archive Download this file



interactive