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                            {
516                                if( conf->tls_accept(cl) < 1 )
517                                {
518                                    fprintf(stderr,
519                                        "tls_accept failed, "
520                                        "connection dropped\n");
521
522                                    /* close client socket */
523                                    close(new_fd);
524
525                                    /* remove from global client list */
526                                    uh_client_remove(new_fd);
527
528                                    continue;
529                                }
530                            }
531#endif
532
533                            /* add client socket to global fdset */
534                            FD_SET(new_fd, &used_fds);
535                            fd_cloexec(new_fd);
536                            max_fd = max(max_fd, new_fd);
537                        }
538
539                        /* insufficient resources */
540                        else
541                        {
542                            fprintf(stderr,
543                                "uh_client_add(): Cannot allocate memory\n");
544
545                            close(new_fd);
546                        }
547                    }
548                }
549
550                /* is a client socket */
551                else
552                {
553                    if( ! (cl = uh_client_lookup(cur_fd)) )
554                    {
555                        /* this should not happen! */
556                        fprintf(stderr,
557                            "uh_client_lookup(): No entry for fd %i!\n",
558                            cur_fd);
559
560                        goto cleanup;
561                    }
562
563                    /* parse message header */
564                    if( (req = uh_http_header_recv(cl)) != NULL )
565                    {
566                        /* RFC1918 filtering required? */
567                        if( conf->rfc1918_filter &&
568                            sa_rfc1918(&cl->peeraddr) &&
569                            !sa_rfc1918(&cl->servaddr) )
570                        {
571                            uh_http_sendhf(cl, 403, "Forbidden",
572                                "Rejected request from RFC1918 IP "
573                                "to public server address");
574                        }
575                        else
576#ifdef HAVE_LUA
577                        /* Lua request? */
578                        if( conf->lua_state &&
579                            uh_path_match(conf->lua_prefix, req->url) )
580                        {
581                            conf->lua_request(cl, req, conf->lua_state);
582                        }
583                        else
584#endif
585                        /* dispatch request */
586                        if( (pin = uh_path_lookup(cl, req->url)) != NULL )
587                        {
588                            /* auth ok? */
589                            if( !pin->redirected && uh_auth_check(cl, req, pin) )
590                                uh_dispatch_request(cl, req, pin);
591                        }
592
593                        /* 404 */
594                        else
595                        {
596                            /* Try to invoke an error handler */
597                            pin = uh_path_lookup(cl, conf->error_handler);
598
599                            if( pin && uh_auth_check(cl, req, pin) )
600                            {
601                                req->redirect_status = 404;
602                                uh_dispatch_request(cl, req, pin);
603                            }
604                            else
605                            {
606                                uh_http_sendhf(cl, 404, "Not Found",
607                                    "No such file or directory");
608                            }
609                        }
610                    }
611
612#ifdef HAVE_TLS
613                    /* free client tls context */
614                    if( conf->tls )
615                        conf->tls_close(cl);
616#endif
617
618                    cleanup:
619
620                    /* close client socket */
621                    close(cur_fd);
622                    FD_CLR(cur_fd, &used_fds);
623
624                    /* remove from global client list */
625                    uh_client_remove(cur_fd);
626                }
627            }
628        }
629    }
630
631#ifdef HAVE_LUA
632    /* destroy the Lua state */
633    if( conf->lua_state != NULL )
634        conf->lua_close(conf->lua_state);
635#endif
636}
637
638#ifdef HAVE_TLS
639static inline int uh_inittls(struct config *conf)
640{
641    /* library handle */
642    void *lib;
643
644    /* already loaded */
645    if( conf->tls != NULL )
646        return 0;
647
648    /* load TLS plugin */
649    if( ! (lib = dlopen("uhttpd_tls.so", RTLD_LAZY | RTLD_GLOBAL)) )
650    {
651        fprintf(stderr,
652            "Notice: Unable to load TLS plugin - disabling SSL support! "
653            "(Reason: %s)\n", dlerror()
654        );
655
656        return 1;
657    }
658    else
659    {
660        /* resolve functions */
661        if( !(conf->tls_init = dlsym(lib, "uh_tls_ctx_init")) ||
662            !(conf->tls_cert = dlsym(lib, "uh_tls_ctx_cert")) ||
663            !(conf->tls_key = dlsym(lib, "uh_tls_ctx_key")) ||
664            !(conf->tls_free = dlsym(lib, "uh_tls_ctx_free")) ||
665            !(conf->tls_accept = dlsym(lib, "uh_tls_client_accept")) ||
666            !(conf->tls_close = dlsym(lib, "uh_tls_client_close")) ||
667            !(conf->tls_recv = dlsym(lib, "uh_tls_client_recv")) ||
668            !(conf->tls_send = dlsym(lib, "uh_tls_client_send"))
669        ) {
670            fprintf(stderr,
671                "Error: Failed to lookup required symbols "
672                "in TLS plugin: %s\n", dlerror()
673            );
674            exit(1);
675        }
676
677        /* init SSL context */
678        if( ! (conf->tls = conf->tls_init()) )
679        {
680            fprintf(stderr, "Error: Failed to initalize SSL context\n");
681            exit(1);
682        }
683    }
684
685    return 0;
686}
687#endif
688
689int main (int argc, char **argv)
690{
691    /* master file descriptor list */
692    fd_set serv_fds;
693
694    /* working structs */
695    struct addrinfo hints;
696    struct sigaction sa;
697    struct config conf;
698
699    /* signal mask */
700    sigset_t ss;
701
702    /* maximum file descriptor number */
703    int cur_fd, max_fd = 0;
704
705#ifdef HAVE_TLS
706    int tls = 0;
707    int keys = 0;
708#endif
709
710    int bound = 0;
711    int nofork = 0;
712
713    /* args */
714    int opt;
715    char bind[128];
716    char *port = NULL;
717
718#ifdef HAVE_LUA
719    /* library handle */
720    void *lib;
721#endif
722
723    FD_ZERO(&serv_fds);
724
725    /* handle SIGPIPE, SIGINT, SIGTERM, SIGCHLD */
726    sa.sa_flags = 0;
727    sigemptyset(&sa.sa_mask);
728
729    sa.sa_handler = SIG_IGN;
730    sigaction(SIGPIPE, &sa, NULL);
731
732    sa.sa_handler = uh_sigchld;
733    sigaction(SIGCHLD, &sa, NULL);
734
735    sa.sa_handler = uh_sigterm;
736    sigaction(SIGINT, &sa, NULL);
737    sigaction(SIGTERM, &sa, NULL);
738
739    /* defer SIGCHLD */
740    sigemptyset(&ss);
741    sigaddset(&ss, SIGCHLD);
742    sigprocmask(SIG_BLOCK, &ss, NULL);
743
744    /* prepare addrinfo hints */
745    memset(&hints, 0, sizeof(hints));
746    hints.ai_family = AF_UNSPEC;
747    hints.ai_socktype = SOCK_STREAM;
748    hints.ai_flags = AI_PASSIVE;
749
750    /* parse args */
751    memset(&conf, 0, sizeof(conf));
752    memset(bind, 0, sizeof(bind));
753
754
755    while( (opt = getopt(argc, argv,
756        "fSDRC:K:E:I:p:s:h:c:l:L:d:r:m:x:i:t:T:A:")) > 0
757    ) {
758        switch(opt)
759        {
760            /* [addr:]port */
761            case 'p':
762            case 's':
763                if( (port = strrchr(optarg, ':')) != NULL )
764                {
765                    if( (optarg[0] == '[') && (port > optarg) && (port[-1] == ']') )
766                        memcpy(bind, optarg + 1,
767                            min(sizeof(bind), (int)(port - optarg) - 2));
768                    else
769                        memcpy(bind, optarg,
770                            min(sizeof(bind), (int)(port - optarg)));
771
772                    port++;
773                }
774                else
775                {
776                    port = optarg;
777                }
778
779#ifdef HAVE_TLS
780                if( opt == 's' )
781                {
782                    if( uh_inittls(&conf) )
783                    {
784                        fprintf(stderr,
785                            "Notice: TLS support is disabled, "
786                            "ignoring '-s %s'\n", optarg
787                        );
788                        continue;
789                    }
790
791                    tls = 1;
792                }
793#endif
794
795                /* bind sockets */
796                bound += uh_socket_bind(
797                    &serv_fds, &max_fd, bind[0] ? bind : NULL, port,
798                    &hints, (opt == 's'), &conf
799                );
800
801                memset(bind, 0, sizeof(bind));
802                break;
803
804#ifdef HAVE_TLS
805            /* certificate */
806            case 'C':
807                if( !uh_inittls(&conf) )
808                {
809                    if( conf.tls_cert(conf.tls, optarg) < 1 )
810                    {
811                        fprintf(stderr,
812                            "Error: Invalid certificate file given\n");
813                        exit(1);
814                    }
815
816                    keys++;
817                }
818
819                break;
820
821            /* key */
822            case 'K':
823                if( !uh_inittls(&conf) )
824                {
825                    if( conf.tls_key(conf.tls, optarg) < 1 )
826                    {
827                        fprintf(stderr,
828                            "Error: Invalid private key file given\n");
829                        exit(1);
830                    }
831
832                    keys++;
833                }
834
835                break;
836#endif
837
838            /* docroot */
839            case 'h':
840                if( ! realpath(optarg, conf.docroot) )
841                {
842                    fprintf(stderr, "Error: Invalid directory %s: %s\n",
843                        optarg, strerror(errno));
844                    exit(1);
845                }
846                break;
847
848            /* error handler */
849            case 'E':
850                if( (strlen(optarg) == 0) || (optarg[0] != '/') )
851                {
852                    fprintf(stderr, "Error: Invalid error handler: %s\n",
853                        optarg);
854                    exit(1);
855                }
856                conf.error_handler = optarg;
857                break;
858
859            /* index file */
860            case 'I':
861                if( (strlen(optarg) == 0) || (optarg[0] == '/') )
862                {
863                    fprintf(stderr, "Error: Invalid index page: %s\n",
864                        optarg);
865                    exit(1);
866                }
867                conf.index_file = optarg;
868                break;
869
870            /* don't follow symlinks */
871            case 'S':
872                conf.no_symlinks = 1;
873                break;
874
875            /* don't list directories */
876            case 'D':
877                conf.no_dirlists = 1;
878                break;
879
880            case 'R':
881                conf.rfc1918_filter = 1;
882                break;
883
884#ifdef HAVE_CGI
885            /* cgi prefix */
886            case 'x':
887                conf.cgi_prefix = optarg;
888                break;
889
890            /* interpreter */
891            case 'i':
892                if( (optarg[0] == '.') && (port = strchr(optarg, '=')) )
893                {
894                    *port++ = 0;
895                    uh_interpreter_add(optarg, port);
896                }
897                else
898                {
899                    fprintf(stderr, "Error: Invalid interpreter: %s\n",
900                        optarg);
901                    exit(1);
902                }
903                break;
904#endif
905
906#ifdef HAVE_LUA
907            /* lua prefix */
908            case 'l':
909                conf.lua_prefix = optarg;
910                break;
911
912            /* lua handler */
913            case 'L':
914                conf.lua_handler = optarg;
915                break;
916#endif
917
918#if defined(HAVE_CGI) || defined(HAVE_LUA)
919            /* script timeout */
920            case 't':
921                conf.script_timeout = atoi(optarg);
922                break;
923#endif
924
925            /* network timeout */
926            case 'T':
927                conf.network_timeout = atoi(optarg);
928                break;
929
930            /* tcp keep-alive */
931            case 'A':
932                conf.tcp_keepalive = atoi(optarg);
933                break;
934
935            /* no fork */
936            case 'f':
937                nofork = 1;
938                break;
939
940            /* urldecode */
941            case 'd':
942                if( (port = malloc(strlen(optarg)+1)) != NULL )
943                {
944                    /* "decode" plus to space to retain compat */
945                    for (opt = 0; optarg[opt]; opt++)
946                        if (optarg[opt] == '+')
947                            optarg[opt] = ' ';
948
949                    memset(port, 0, strlen(optarg)+1);
950                    uh_urldecode(port, strlen(optarg), optarg, strlen(optarg));
951
952                    printf("%s", port);
953                    free(port);
954                    exit(0);
955                }
956                break;
957
958            /* basic auth realm */
959            case 'r':
960                conf.realm = optarg;
961                break;
962
963            /* md5 crypt */
964            case 'm':
965                printf("%s\n", crypt(optarg, "$1$"));
966                exit(0);
967                break;
968
969            /* config file */
970            case 'c':
971                conf.file = optarg;
972                break;
973
974            default:
975                fprintf(stderr,
976                    "Usage: %s -p [addr:]port [-h docroot]\n"
977                    " -f Do not fork to background\n"
978                    " -c file Configuration file, default is '/etc/httpd.conf'\n"
979                    " -p [addr:]port Bind to specified address and port, multiple allowed\n"
980#ifdef HAVE_TLS
981                    " -s [addr:]port Like -p but provide HTTPS on this port\n"
982                    " -C file ASN.1 server certificate file\n"
983                    " -K file ASN.1 server private key file\n"
984#endif
985                    " -h directory Specify the document root, default is '.'\n"
986                    " -E string Use given virtual URL as 404 error handler\n"
987                    " -I string Use given filename as index page for directories\n"
988                    " -S Do not follow symbolic links outside of the docroot\n"
989                    " -D Do not allow directory listings, send 403 instead\n"
990                    " -R Enable RFC1918 filter\n"
991#ifdef HAVE_LUA
992                    " -l string URL prefix for Lua handler, default is '/lua'\n"
993                    " -L file Lua handler script, omit to disable Lua\n"
994#endif
995#ifdef HAVE_CGI
996                    " -x string URL prefix for CGI handler, default is '/cgi-bin'\n"
997                    " -i .ext=path Use interpreter at path for files with the given extension\n"
998#endif
999#if defined(HAVE_CGI) || defined(HAVE_LUA)
1000                    " -t seconds CGI and Lua script timeout in seconds, default is 60\n"
1001#endif
1002                    " -T seconds Network timeout in seconds, default is 30\n"
1003                    " -d string URL decode given string\n"
1004                    " -r string Specify basic auth realm\n"
1005                    " -m string MD5 crypt given string\n"
1006                    "\n", argv[0]
1007                );
1008
1009                exit(1);
1010        }
1011    }
1012
1013#ifdef HAVE_TLS
1014    if( (tls == 1) && (keys < 2) )
1015    {
1016        fprintf(stderr, "Error: Missing private key or certificate file\n");
1017        exit(1);
1018    }
1019#endif
1020
1021    if( bound < 1 )
1022    {
1023        fprintf(stderr, "Error: No sockets bound, unable to continue\n");
1024        exit(1);
1025    }
1026
1027    /* default docroot */
1028    if( !conf.docroot[0] && !realpath(".", conf.docroot) )
1029    {
1030        fprintf(stderr, "Error: Can not determine default document root: %s\n",
1031            strerror(errno));
1032        exit(1);
1033    }
1034
1035    /* default realm */
1036    if( ! conf.realm )
1037        conf.realm = "Protected Area";
1038
1039    /* config file */
1040    uh_config_parse(&conf);
1041
1042    /* default network timeout */
1043    if( conf.network_timeout <= 0 )
1044        conf.network_timeout = 30;
1045
1046#if defined(HAVE_CGI) || defined(HAVE_LUA)
1047    /* default script timeout */
1048    if( conf.script_timeout <= 0 )
1049        conf.script_timeout = 60;
1050#endif
1051
1052#ifdef HAVE_CGI
1053    /* default cgi prefix */
1054    if( ! conf.cgi_prefix )
1055        conf.cgi_prefix = "/cgi-bin";
1056#endif
1057
1058#ifdef HAVE_LUA
1059    /* load Lua plugin */
1060    if( ! (lib = dlopen("uhttpd_lua.so", RTLD_LAZY | RTLD_GLOBAL)) )
1061    {
1062        fprintf(stderr,
1063            "Notice: Unable to load Lua plugin - disabling Lua support! "
1064            "(Reason: %s)\n", dlerror()
1065        );
1066    }
1067    else
1068    {
1069        /* resolve functions */
1070        if( !(conf.lua_init = dlsym(lib, "uh_lua_init")) ||
1071            !(conf.lua_close = dlsym(lib, "uh_lua_close")) ||
1072            !(conf.lua_request = dlsym(lib, "uh_lua_request"))
1073        ) {
1074            fprintf(stderr,
1075                "Error: Failed to lookup required symbols "
1076                "in Lua plugin: %s\n", dlerror()
1077            );
1078            exit(1);
1079        }
1080
1081        /* init Lua runtime if handler is specified */
1082        if( conf.lua_handler )
1083        {
1084            /* default lua prefix */
1085            if( ! conf.lua_prefix )
1086                conf.lua_prefix = "/lua";
1087
1088            conf.lua_state = conf.lua_init(conf.lua_handler);
1089        }
1090    }
1091#endif
1092
1093    /* fork (if not disabled) */
1094    if( ! nofork )
1095    {
1096        switch( fork() )
1097        {
1098            case -1:
1099                perror("fork()");
1100                exit(1);
1101
1102            case 0:
1103                /* daemon setup */
1104                if( chdir("/") )
1105                    perror("chdir()");
1106
1107                if( (cur_fd = open("/dev/null", O_WRONLY)) > -1 )
1108                    dup2(cur_fd, 0);
1109
1110                if( (cur_fd = open("/dev/null", O_RDONLY)) > -1 )
1111                    dup2(cur_fd, 1);
1112
1113                if( (cur_fd = open("/dev/null", O_RDONLY)) > -1 )
1114                    dup2(cur_fd, 2);
1115
1116                break;
1117
1118            default:
1119                exit(0);
1120        }
1121    }
1122
1123    /* server main loop */
1124    uh_mainloop(&conf, serv_fds, max_fd);
1125
1126#ifdef HAVE_LUA
1127    /* destroy the Lua state */
1128    if( conf.lua_state != NULL )
1129        conf.lua_close(conf.lua_state);
1130#endif
1131
1132    return 0;
1133}
1134

Archive Download this file



interactive