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

Archive Download this file



interactive