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

Archive Download this file



interactive