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

Archive Download this file



interactive