source: trunk/package/uhttpd/src/uhttpd.c @ 22988

Last change on this file since 22988 was 22988, checked in by jow, 6 years ago

[package] uhttpd: break tight loop when receiving eof during header reading (#7904)

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