[BACK]Return to bozohttpd.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / libexec / httpd

Annotation of src/libexec/httpd/bozohttpd.c, Revision 1.46

1.46    ! mrg         1: /*     $NetBSD: bozohttpd.c,v 1.45 2014/01/02 08:21:38 mrg Exp $       */
1.3       tls         2:
1.30      mrg         3: /*     $eterna: bozohttpd.c,v 1.178 2011/11/18 09:21:15 mrg Exp $      */
1.1       tls         4:
                      5: /*
1.45      mrg         6:  * Copyright (c) 1997-2014 Matthew R. Green
1.1       tls         7:  * All rights reserved.
                      8:  *
                      9:  * Redistribution and use in source and binary forms, with or without
                     10:  * modification, are permitted provided that the following conditions
                     11:  * are met:
                     12:  * 1. Redistributions of source code must retain the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer.
                     14:  * 2. Redistributions in binary form must reproduce the above copyright
                     15:  *    notice, this list of conditions and the following disclaimer and
                     16:  *    dedication in the documentation and/or other materials provided
                     17:  *    with the distribution.
                     18:  *
                     19:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
                     20:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
                     21:  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
                     22:  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
                     23:  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
                     24:  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
                     25:  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
                     26:  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
                     27:  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     28:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     29:  * SUCH DAMAGE.
                     30:  *
                     31:  */
                     32:
                     33: /* this program is dedicated to the Great God of Processed Cheese */
                     34:
                     35: /*
                     36:  * bozohttpd.c:  minimal httpd; provides only these features:
                     37:  *     - HTTP/0.9 (by virtue of ..)
                     38:  *     - HTTP/1.0
                     39:  *     - HTTP/1.1
                     40:  *     - CGI/1.1 this will only be provided for "system" scripts
                     41:  *     - automatic "missing trailing slash" redirections
                     42:  *     - configurable translation of /~user/ to ~user/public_html,
                     43:  *       however, this does not include cgi-bin support
                     44:  *     - access lists via libwrap via inetd/tcpd
                     45:  *     - virtual hosting
                     46:  *     - not that we do not even pretend to understand MIME, but
                     47:  *       rely only on the HTTP specification
                     48:  *     - ipv6 support
                     49:  *     - automatic `index.html' generation
                     50:  *     - configurable server name
                     51:  *     - directory index generation
                     52:  *     - daemon mode (lacks libwrap support)
                     53:  *     - .htpasswd support
                     54:  */
                     55:
                     56: /*
                     57:  * requirements for minimal http/1.1 (at least, as documented in
                     58:  * <draft-ietf-http-v11-spec-rev-06> which expired may 18, 1999):
                     59:  *
                     60:  *     - 14.15: content-encoding handling. [1]
                     61:  *
                     62:  *     - 14.16: content-length handling.  this is only a SHOULD header
                     63:  *       thus we could just not send it ever.  [1]
                     64:  *
                     65:  *     - 14.17: content-type handling. [1]
                     66:  *
                     67:  *     - 14.25/28: if-{,un}modified-since handling.  maybe do this, but
                     68:  *       i really don't want to have to parse 3 differnet date formats
                     69:  *
                     70:  * [1] need to revisit to ensure proper behaviour
                     71:  *
                     72:  * and the following is a list of features that we do not need
                     73:  * to have due to other limits, or are too lazy.  there are more
                     74:  * of these than are listed, but these are of particular note,
                     75:  * and could perhaps be implemented.
                     76:  *
                     77:  *     - 3.5/3.6: content/transfer codings.  probably can ignore
                     78:  *       this?  we "SHOULD"n't.  but 4.4 says we should ignore a
                     79:  *       `content-length' header upon reciept of a `transfer-encoding'
                     80:  *       header.
                     81:  *
                     82:  *     - 5.1.1: request methods.  only MUST support GET and HEAD,
                     83:  *       but there are new ones besides POST that are currently
                     84:  *       supported: OPTIONS PUT DELETE TRACE and CONNECT, plus
                     85:  *       extensions not yet known?
                     86:  *
                     87:  *     - 10.1: we can ignore informational status codes
                     88:  *
                     89:  *     - 10.3.3/10.3.4/10.3.8:  just use '302' codes always.
                     90:  *
                     91:  *     - 14.1/14.2/14.3/14.27: we do not support Accept: headers..
                     92:  *       just ignore them and send the request anyway.  they are
                     93:  *       only SHOULD.
                     94:  *
                     95:  *     - 14.5/14.16/14.35: we don't do ranges.  from section 14.35.2
                     96:  *       `A server MAY ignore the Range header'.  but it might be nice.
1.6       mrg        97:  *       since 20080301 we support simple range headers.
1.1       tls        98:  *
                     99:  *     - 14.9: we aren't a cache.
                    100:  *
                    101:  *     - 14.15: content-md5 would be nice...
1.42      mbalmer   102:  *
1.1       tls       103:  *     - 14.24/14.26/14.27: be nice to support this...
                    104:  *
                    105:  *     - 14.44: not sure about this Vary: header.  ignore it for now.
                    106:  */
                    107:
                    108: #ifndef INDEX_HTML
                    109: #define INDEX_HTML             "index.html"
                    110: #endif
                    111: #ifndef SERVER_SOFTWARE
1.45      mrg       112: #define SERVER_SOFTWARE                "bozohttpd/20140102"
1.1       tls       113: #endif
                    114: #ifndef DIRECT_ACCESS_FILE
                    115: #define DIRECT_ACCESS_FILE     ".bzdirect"
                    116: #endif
                    117: #ifndef REDIRECT_FILE
                    118: #define REDIRECT_FILE          ".bzredirect"
                    119: #endif
                    120: #ifndef ABSREDIRECT_FILE
                    121: #define ABSREDIRECT_FILE       ".bzabsredirect"
                    122: #endif
1.16      mrg       123: #ifndef PUBLIC_HTML
                    124: #define PUBLIC_HTML            "public_html"
                    125: #endif
                    126:
                    127: #ifndef USE_ARG
                    128: #define USE_ARG(x)     /*LINTED*/(void)&(x)
                    129: #endif
1.1       tls       130:
                    131: /*
                    132:  * And so it begins ..
                    133:  */
                    134:
                    135: #include <sys/param.h>
                    136: #include <sys/socket.h>
                    137: #include <sys/time.h>
                    138: #include <sys/mman.h>
                    139:
                    140: #include <arpa/inet.h>
                    141:
                    142: #include <ctype.h>
                    143: #include <dirent.h>
                    144: #include <errno.h>
                    145: #include <fcntl.h>
                    146: #include <netdb.h>
                    147: #include <pwd.h>
                    148: #include <grp.h>
                    149: #include <signal.h>
                    150: #include <stdarg.h>
                    151: #include <stdlib.h>
                    152: #include <string.h>
                    153: #include <syslog.h>
                    154: #include <time.h>
                    155: #include <unistd.h>
                    156:
                    157: #include "bozohttpd.h"
                    158:
                    159: #ifndef MAX_WAIT_TIME
                    160: #define        MAX_WAIT_TIME   60      /* hang around for 60 seconds max */
                    161: #endif
                    162:
                    163: /* variables and functions */
                    164: #ifndef LOG_FTP
                    165: #define LOG_FTP LOG_DAEMON
                    166: #endif
                    167:
                    168: volatile sig_atomic_t  alarmhit;
                    169:
1.16      mrg       170: /*
                    171:  * check there's enough space in the prefs and names arrays.
                    172:  */
                    173: static int
                    174: size_arrays(bozoprefs_t *bozoprefs, unsigned needed)
                    175: {
                    176:        char    **temp;
1.1       tls       177:
1.16      mrg       178:        if (bozoprefs->size == 0) {
                    179:                /* only get here first time around */
                    180:                bozoprefs->size = needed;
                    181:                if ((bozoprefs->name = calloc(sizeof(char *), needed)) == NULL) {
                    182:                        (void) fprintf(stderr, "size_arrays: bad alloc\n");
                    183:                        return 0;
                    184:                }
                    185:                if ((bozoprefs->value = calloc(sizeof(char *), needed)) == NULL) {
                    186:                        free(bozoprefs->name);
                    187:                        (void) fprintf(stderr, "size_arrays: bad alloc\n");
                    188:                        return 0;
                    189:                }
                    190:        } else if (bozoprefs->c == bozoprefs->size) {
                    191:                /* only uses 'needed' when filled array */
                    192:                bozoprefs->size += needed;
                    193:                temp = realloc(bozoprefs->name, sizeof(char *) * needed);
                    194:                if (temp == NULL) {
                    195:                        (void) fprintf(stderr, "size_arrays: bad alloc\n");
                    196:                        return 0;
                    197:                }
                    198:                bozoprefs->name = temp;
                    199:                temp = realloc(bozoprefs->value, sizeof(char *) * needed);
                    200:                if (temp == NULL) {
                    201:                        (void) fprintf(stderr, "size_arrays: bad alloc\n");
                    202:                        return 0;
                    203:                }
                    204:                bozoprefs->value = temp;
                    205:        }
                    206:        return 1;
                    207: }
1.1       tls       208:
1.16      mrg       209: static int
                    210: findvar(bozoprefs_t *bozoprefs, const char *name)
                    211: {
                    212:        unsigned        i;
1.1       tls       213:
1.16      mrg       214:        for (i = 0 ; i < bozoprefs->c && strcmp(bozoprefs->name[i], name) != 0; i++)
                    215:                ;
                    216:        return (i == bozoprefs->c) ? -1 : (int)i;
1.1       tls       217: }
                    218:
                    219: int
1.16      mrg       220: bozo_set_pref(bozoprefs_t *bozoprefs, const char *name, const char *value)
1.1       tls       221: {
1.16      mrg       222:        int     i;
1.1       tls       223:
1.16      mrg       224:        if ((i = findvar(bozoprefs, name)) < 0) {
                    225:                /* add the element to the array */
                    226:                if (size_arrays(bozoprefs, bozoprefs->size + 15)) {
                    227:                        bozoprefs->name[i = bozoprefs->c++] = strdup(name);
                    228:                }
                    229:        } else {
                    230:                /* replace the element in the array */
                    231:                if (bozoprefs->value[i]) {
                    232:                        free(bozoprefs->value[i]);
                    233:                        bozoprefs->value[i] = NULL;
1.1       tls       234:                }
                    235:        }
1.16      mrg       236:        /* sanity checks for range of values go here */
                    237:        bozoprefs->value[i] = strdup(value);
                    238:        return 1;
                    239: }
1.1       tls       240:
1.16      mrg       241: /*
                    242:  * get a variable's value, or NULL
                    243:  */
                    244: char *
                    245: bozo_get_pref(bozoprefs_t *bozoprefs, const char *name)
                    246: {
                    247:        int     i;
1.1       tls       248:
1.16      mrg       249:        return ((i = findvar(bozoprefs, name)) < 0) ? NULL :
                    250:                        bozoprefs->value[i];
1.1       tls       251: }
                    252:
                    253: char *
1.16      mrg       254: bozo_http_date(char *date, size_t datelen)
1.1       tls       255: {
                    256:        struct  tm *tm;
                    257:        time_t  now;
                    258:
                    259:        /* Sun, 06 Nov 1994 08:49:37 GMT */
                    260:        now = time(NULL);
                    261:        tm = gmtime(&now);      /* HTTP/1.1 spec rev 06 sez GMT only */
1.16      mrg       262:        strftime(date, datelen, "%a, %d %b %Y %H:%M:%S GMT", tm);
1.1       tls       263:        return date;
                    264: }
                    265:
                    266: /*
1.12      mrg       267:  * convert "in" into the three parts of a request (first line).
                    268:  * we allocate into file and query, but return pointers into
                    269:  * "in" for proto and method.
1.1       tls       270:  */
                    271: static void
1.16      mrg       272: parse_request(bozohttpd_t *httpd, char *in, char **method, char **file,
                    273:                char **query, char **proto)
1.1       tls       274: {
                    275:        ssize_t len;
                    276:        char    *val;
1.16      mrg       277:
                    278:        USE_ARG(httpd);
                    279:        debug((httpd, DEBUG_EXPLODING, "parse in: %s", in));
1.12      mrg       280:        *method = *file = *query = *proto = NULL;
1.1       tls       281:
                    282:        len = (ssize_t)strlen(in);
1.6       mrg       283:        val = bozostrnsep(&in, " \t\n\r", &len);
1.1       tls       284:        if (len < 1 || val == NULL)
                    285:                return;
                    286:        *method = val;
1.12      mrg       287:
1.1       tls       288:        while (*in == ' ' || *in == '\t')
                    289:                in++;
1.6       mrg       290:        val = bozostrnsep(&in, " \t\n\r", &len);
1.1       tls       291:        if (len < 1) {
                    292:                if (len == 0)
1.9       tls       293:                        *file = val;
1.1       tls       294:                else
1.9       tls       295:                        *file = in;
1.16      mrg       296:        } else {
                    297:                *file = val;
1.9       tls       298:
1.16      mrg       299:                *query = strchr(*file, '?');
                    300:                if (*query)
                    301:                        *(*query)++ = '\0';
                    302:
                    303:                if (in) {
                    304:                        while (*in && (*in == ' ' || *in == '\t'))
                    305:                                in++;
                    306:                        if (*in)
                    307:                                *proto = in;
                    308:                }
1.12      mrg       309:        }
                    310:
                    311:        /* allocate private copies */
1.16      mrg       312:        *file = bozostrdup(httpd, *file);
1.12      mrg       313:        if (*query)
1.16      mrg       314:                *query = bozostrdup(httpd, *query);
1.12      mrg       315:
1.16      mrg       316:        debug((httpd, DEBUG_FAT,
                    317:                "url: method: \"%s\" file: \"%s\" query: \"%s\" proto: \"%s\"",
                    318:                *method, *file, *query, *proto));
1.1       tls       319: }
                    320:
                    321: /*
1.16      mrg       322:  * cleanup a bozo_httpreq_t after use
1.1       tls       323:  */
1.16      mrg       324: void
                    325: bozo_clean_request(bozo_httpreq_t *request)
1.1       tls       326: {
1.16      mrg       327:        struct bozoheaders *hdr, *ohdr = NULL;
1.12      mrg       328:
                    329:        if (request == NULL)
                    330:                return;
                    331:
1.21      mrg       332:        /* If SSL enabled cleanup SSL structure. */
1.22      mrg       333:        bozo_ssl_destroy(request->hr_httpd);
1.21      mrg       334:
1.12      mrg       335:        /* clean up request */
                    336: #define MF(x)  if (request->x) free(request->x)
                    337:        MF(hr_remotehost);
                    338:        MF(hr_remoteaddr);
                    339:        MF(hr_serverport);
1.35      martin    340:        MF(hr_virthostname);
1.12      mrg       341:        MF(hr_file);
1.20      mrg       342:        MF(hr_oldfile);
1.12      mrg       343:        MF(hr_query);
                    344: #undef MF
1.16      mrg       345:        bozo_auth_cleanup(request);
1.12      mrg       346:        for (hdr = SIMPLEQ_FIRST(&request->hr_headers); hdr;
                    347:            hdr = SIMPLEQ_NEXT(hdr, h_next)) {
                    348:                free(hdr->h_value);
                    349:                free(hdr->h_header);
1.44      mbalmer   350:                free(ohdr);
1.12      mrg       351:                ohdr = hdr;
                    352:        }
1.44      mbalmer   353:        free(ohdr);
1.12      mrg       354:
                    355:        free(request);
                    356: }
                    357:
                    358: /*
1.16      mrg       359:  * send a HTTP/1.1 408 response if we timeout.
                    360:  */
                    361: /* ARGSUSED */
                    362: static void
                    363: alarmer(int sig)
                    364: {
                    365:        alarmhit = 1;
                    366: }
                    367:
                    368: /*
                    369:  * add or merge this header (val: str) into the requests list
                    370:  */
                    371: static bozoheaders_t *
                    372: addmerge_header(bozo_httpreq_t *request, char *val,
                    373:                char *str, ssize_t len)
                    374: {
                    375:        struct  bozoheaders *hdr;
                    376:
                    377:        USE_ARG(len);
                    378:        /* do we exist already? */
                    379:        SIMPLEQ_FOREACH(hdr, &request->hr_headers, h_next) {
                    380:                if (strcasecmp(val, hdr->h_header) == 0)
                    381:                        break;
                    382:        }
                    383:
                    384:        if (hdr) {
                    385:                /* yup, merge it in */
                    386:                char *nval;
                    387:
                    388:                if (asprintf(&nval, "%s, %s", hdr->h_value, str) == -1) {
                    389:                        (void)bozo_http_error(request->hr_httpd, 500, NULL,
                    390:                             "memory allocation failure");
                    391:                        return NULL;
                    392:                }
                    393:                free(hdr->h_value);
                    394:                hdr->h_value = nval;
                    395:        } else {
                    396:                /* nope, create a new one */
                    397:
                    398:                hdr = bozomalloc(request->hr_httpd, sizeof *hdr);
                    399:                hdr->h_header = bozostrdup(request->hr_httpd, val);
                    400:                if (str && *str)
                    401:                        hdr->h_value = bozostrdup(request->hr_httpd, str);
                    402:                else
                    403:                        hdr->h_value = bozostrdup(request->hr_httpd, " ");
                    404:
                    405:                SIMPLEQ_INSERT_TAIL(&request->hr_headers, hdr, h_next);
                    406:                request->hr_nheaders++;
                    407:        }
                    408:
                    409:        return hdr;
                    410: }
                    411:
                    412: /*
                    413:  * as the prototype string is not constant (eg, "HTTP/1.1" is equivalent
                    414:  * to "HTTP/001.01"), we MUST parse this.
                    415:  */
                    416: static int
                    417: process_proto(bozo_httpreq_t *request, const char *proto)
                    418: {
                    419:        char    majorstr[16], *minorstr;
                    420:        int     majorint, minorint;
                    421:
                    422:        if (proto == NULL) {
                    423: got_proto_09:
                    424:                request->hr_proto = request->hr_httpd->consts.http_09;
                    425:                debug((request->hr_httpd, DEBUG_FAT, "request %s is http/0.9",
                    426:                        request->hr_file));
                    427:                return 0;
                    428:        }
                    429:
                    430:        if (strncasecmp(proto, "HTTP/", 5) != 0)
                    431:                goto bad;
                    432:        strncpy(majorstr, proto + 5, sizeof majorstr);
                    433:        majorstr[sizeof(majorstr)-1] = 0;
                    434:        minorstr = strchr(majorstr, '.');
                    435:        if (minorstr == NULL)
                    436:                goto bad;
                    437:        *minorstr++ = 0;
                    438:
                    439:        majorint = atoi(majorstr);
                    440:        minorint = atoi(minorstr);
                    441:
                    442:        switch (majorint) {
                    443:        case 0:
                    444:                if (minorint != 9)
                    445:                        break;
                    446:                goto got_proto_09;
                    447:        case 1:
                    448:                if (minorint == 0)
                    449:                        request->hr_proto = request->hr_httpd->consts.http_10;
                    450:                else if (minorint == 1)
                    451:                        request->hr_proto = request->hr_httpd->consts.http_11;
                    452:                else
                    453:                        break;
                    454:
                    455:                debug((request->hr_httpd, DEBUG_FAT, "request %s is %s",
                    456:                    request->hr_file, request->hr_proto));
                    457:                SIMPLEQ_INIT(&request->hr_headers);
                    458:                request->hr_nheaders = 0;
                    459:                return 0;
                    460:        }
                    461: bad:
                    462:        return bozo_http_error(request->hr_httpd, 404, NULL, "unknown prototype");
                    463: }
                    464:
                    465: /*
                    466:  * process each type of HTTP method, setting this HTTP requests
                    467:  # method type.
                    468:  */
                    469: static struct method_map {
                    470:        const char *name;
                    471:        int     type;
                    472: } method_map[] = {
                    473:        { "GET",        HTTP_GET, },
                    474:        { "POST",       HTTP_POST, },
                    475:        { "HEAD",       HTTP_HEAD, },
                    476: #if 0  /* other non-required http/1.1 methods */
                    477:        { "OPTIONS",    HTTP_OPTIONS, },
                    478:        { "PUT",        HTTP_PUT, },
                    479:        { "DELETE",     HTTP_DELETE, },
                    480:        { "TRACE",      HTTP_TRACE, },
                    481:        { "CONNECT",    HTTP_CONNECT, },
                    482: #endif
                    483:        { NULL,         0, },
                    484: };
                    485:
                    486: static int
                    487: process_method(bozo_httpreq_t *request, const char *method)
                    488: {
                    489:        struct  method_map *mmp;
                    490:
                    491:        if (request->hr_proto == request->hr_httpd->consts.http_11)
                    492:                request->hr_allow = "GET, HEAD, POST";
                    493:
                    494:        for (mmp = method_map; mmp->name; mmp++)
                    495:                if (strcasecmp(method, mmp->name) == 0) {
                    496:                        request->hr_method = mmp->type;
                    497:                        request->hr_methodstr = mmp->name;
                    498:                        return 0;
                    499:                }
                    500:
                    501:        return bozo_http_error(request->hr_httpd, 404, request, "unknown method");
                    502: }
                    503:
                    504: /*
1.1       tls       505:  * This function reads a http request from stdin, returning a pointer to a
1.16      mrg       506:  * bozo_httpreq_t structure, describing the request.
1.1       tls       507:  */
1.16      mrg       508: bozo_httpreq_t *
                    509: bozo_read_request(bozohttpd_t *httpd)
1.1       tls       510: {
                    511:        struct  sigaction       sa;
1.9       tls       512:        char    *str, *val, *method, *file, *proto, *query;
1.1       tls       513:        char    *host, *addr, *port;
                    514:        char    bufport[10];
                    515:        char    hbuf[NI_MAXHOST], abuf[NI_MAXHOST];
                    516:        struct  sockaddr_storage ss;
                    517:        ssize_t len;
                    518:        int     line = 0;
                    519:        socklen_t slen;
1.16      mrg       520:        bozo_httpreq_t *request;
1.1       tls       521:
                    522:        /*
1.20      mrg       523:         * if we're in daemon mode, bozo_daemon_fork() will return here twice
                    524:         * for each call.  once in the child, returning 0, and once in the
                    525:         * parent, returning 1.  for each child, then we can setup SSL, and
                    526:         * the parent can signal the caller there was no request to process
                    527:         * and it will wait for another.
1.1       tls       528:         */
1.20      mrg       529:        if (bozo_daemon_fork(httpd))
                    530:                return NULL;
1.16      mrg       531:        bozo_ssl_accept(httpd);
1.1       tls       532:
1.16      mrg       533:        request = bozomalloc(httpd, sizeof(*request));
                    534:        memset(request, 0, sizeof(*request));
                    535:        request->hr_httpd = httpd;
1.1       tls       536:        request->hr_allow = request->hr_host = NULL;
                    537:        request->hr_content_type = request->hr_content_length = NULL;
1.6       mrg       538:        request->hr_range = NULL;
1.12      mrg       539:        request->hr_last_byte_pos = -1;
1.10      joerg     540:        request->hr_if_modified_since = NULL;
1.35      martin    541:        request->hr_virthostname = NULL;
1.12      mrg       542:        request->hr_file = NULL;
1.20      mrg       543:        request->hr_oldfile = NULL;
1.1       tls       544:
                    545:        slen = sizeof(ss);
1.16      mrg       546:        if (getpeername(0, (struct sockaddr *)(void *)&ss, &slen) < 0)
1.1       tls       547:                host = addr = NULL;
                    548:        else {
1.16      mrg       549:                if (getnameinfo((struct sockaddr *)(void *)&ss, slen,
1.1       tls       550:                    abuf, sizeof abuf, NULL, 0, NI_NUMERICHOST) == 0)
                    551:                        addr = abuf;
                    552:                else
                    553:                        addr = NULL;
1.16      mrg       554:                if (httpd->numeric == 0 &&
                    555:                    getnameinfo((struct sockaddr *)(void *)&ss, slen,
                    556:                                hbuf, sizeof hbuf, NULL, 0, 0) == 0)
1.1       tls       557:                        host = hbuf;
                    558:                else
                    559:                        host = NULL;
                    560:        }
                    561:        if (host != NULL)
1.16      mrg       562:                request->hr_remotehost = bozostrdup(request->hr_httpd, host);
1.1       tls       563:        if (addr != NULL)
1.16      mrg       564:                request->hr_remoteaddr = bozostrdup(request->hr_httpd, addr);
1.1       tls       565:        slen = sizeof(ss);
1.29      mrg       566:
                    567:        /*
                    568:         * Override the bound port from the request value, so it works even
                    569:         * if passed through a proxy that doesn't rewrite the port.
                    570:         */
                    571:        if (httpd->bindport) {
                    572:                if (strcmp(httpd->bindport, "80") != 0)
                    573:                        port = httpd->bindport;
1.1       tls       574:                else
                    575:                        port = NULL;
1.29      mrg       576:        } else {
                    577:                if (getsockname(0, (struct sockaddr *)(void *)&ss, &slen) < 0)
                    578:                        port = NULL;
                    579:                else {
                    580:                        if (getnameinfo((struct sockaddr *)(void *)&ss, slen, NULL, 0,
                    581:                                        bufport, sizeof bufport, NI_NUMERICSERV) == 0)
                    582:                                port = bufport;
                    583:                        else
                    584:                                port = NULL;
                    585:                }
1.1       tls       586:        }
                    587:        if (port != NULL)
1.16      mrg       588:                request->hr_serverport = bozostrdup(request->hr_httpd, port);
1.1       tls       589:
                    590:        /*
                    591:         * setup a timer to make sure the request is not hung
                    592:         */
                    593:        sa.sa_handler = alarmer;
                    594:        sigemptyset(&sa.sa_mask);
                    595:        sigaddset(&sa.sa_mask, SIGALRM);
                    596:        sa.sa_flags = 0;
                    597:        sigaction(SIGALRM, &sa, NULL);  /* XXX */
                    598:
                    599:        alarm(MAX_WAIT_TIME);
1.16      mrg       600:        while ((str = bozodgetln(httpd, STDIN_FILENO, &len, bozo_read)) != NULL) {
1.1       tls       601:                alarm(0);
1.12      mrg       602:                if (alarmhit) {
1.16      mrg       603:                        (void)bozo_http_error(httpd, 408, NULL,
                    604:                                        "request timed out");
1.12      mrg       605:                        goto cleanup;
                    606:                }
1.1       tls       607:                line++;
                    608:
                    609:                if (line == 1) {
                    610:
1.12      mrg       611:                        if (len < 1) {
1.16      mrg       612:                                (void)bozo_http_error(httpd, 404, NULL,
                    613:                                                "null method");
1.12      mrg       614:                                goto cleanup;
                    615:                        }
                    616:
1.16      mrg       617:                        bozo_warn(httpd, "got request ``%s'' from host %s to port %s",
                    618:                                str,
                    619:                                host ? host : addr ? addr : "<local>",
                    620:                                port ? port : "<stdin>");
1.1       tls       621:
1.12      mrg       622:                        /* we allocate return space in file and query only */
1.16      mrg       623:                        parse_request(httpd, str, &method, &file, &query, &proto);
1.12      mrg       624:                        request->hr_file = file;
                    625:                        request->hr_query = query;
                    626:                        if (method == NULL) {
1.16      mrg       627:                                (void)bozo_http_error(httpd, 404, NULL,
                    628:                                                "null method");
1.12      mrg       629:                                goto cleanup;
                    630:                        }
                    631:                        if (file == NULL) {
1.16      mrg       632:                                (void)bozo_http_error(httpd, 404, NULL,
                    633:                                                "null file");
1.12      mrg       634:                                goto cleanup;
                    635:                        }
1.1       tls       636:
                    637:                        /*
                    638:                         * note that we parse the proto first, so that we
                    639:                         * can more properly parse the method and the url.
                    640:                         */
1.9       tls       641:
1.12      mrg       642:                        if (process_proto(request, proto) ||
                    643:                            process_method(request, method)) {
                    644:                                goto cleanup;
                    645:                        }
                    646:
1.16      mrg       647:                        debug((httpd, DEBUG_FAT, "got file \"%s\" query \"%s\"",
1.12      mrg       648:                            request->hr_file,
                    649:                            request->hr_query ? request->hr_query : "<none>"));
1.1       tls       650:
                    651:                        /* http/0.9 has no header processing */
1.16      mrg       652:                        if (request->hr_proto == httpd->consts.http_09)
1.1       tls       653:                                break;
                    654:                } else {                /* incoming headers */
1.16      mrg       655:                        bozoheaders_t *hdr;
1.1       tls       656:
                    657:                        if (*str == '\0')
                    658:                                break;
                    659:
1.6       mrg       660:                        val = bozostrnsep(&str, ":", &len);
1.16      mrg       661:                        debug((httpd, DEBUG_EXPLODING,
1.6       mrg       662:                            "read_req2: after bozostrnsep: str ``%s'' val ``%s''",
1.1       tls       663:                            str, val));
1.12      mrg       664:                        if (val == NULL || len == -1) {
1.16      mrg       665:                                (void)bozo_http_error(httpd, 404, request,
                    666:                                                "no header");
1.12      mrg       667:                                goto cleanup;
                    668:                        }
1.1       tls       669:                        while (*str == ' ' || *str == '\t')
                    670:                                len--, str++;
1.6       mrg       671:                        while (*val == ' ' || *val == '\t')
                    672:                                val++;
1.1       tls       673:
1.17      mrg       674:                        if (bozo_auth_check_headers(request, val, str, len))
1.1       tls       675:                                goto next_header;
                    676:
                    677:                        hdr = addmerge_header(request, val, str, len);
                    678:
                    679:                        if (strcasecmp(hdr->h_header, "content-type") == 0)
                    680:                                request->hr_content_type = hdr->h_value;
                    681:                        else if (strcasecmp(hdr->h_header, "content-length") == 0)
                    682:                                request->hr_content_length = hdr->h_value;
                    683:                        else if (strcasecmp(hdr->h_header, "host") == 0)
                    684:                                request->hr_host = hdr->h_value;
                    685:                        /* HTTP/1.1 rev06 draft spec: 14.20 */
1.12      mrg       686:                        else if (strcasecmp(hdr->h_header, "expect") == 0) {
1.16      mrg       687:                                (void)bozo_http_error(httpd, 417, request,
                    688:                                                "we don't support Expect:");
1.12      mrg       689:                                goto cleanup;
                    690:                        }
1.1       tls       691:                        else if (strcasecmp(hdr->h_header, "referrer") == 0 ||
                    692:                                 strcasecmp(hdr->h_header, "referer") == 0)
                    693:                                request->hr_referrer = hdr->h_value;
1.6       mrg       694:                        else if (strcasecmp(hdr->h_header, "range") == 0)
                    695:                                request->hr_range = hdr->h_value;
1.16      mrg       696:                        else if (strcasecmp(hdr->h_header,
                    697:                                        "if-modified-since") == 0)
1.10      joerg     698:                                request->hr_if_modified_since = hdr->h_value;
1.31      elric     699:                        else if (strcasecmp(hdr->h_header,
                    700:                                        "accept-encoding") == 0)
                    701:                                request->hr_accept_encoding = hdr->h_value;
1.1       tls       702:
1.16      mrg       703:                        debug((httpd, DEBUG_FAT, "adding header %s: %s",
1.1       tls       704:                            hdr->h_header, hdr->h_value));
                    705:                }
                    706: next_header:
                    707:                alarm(MAX_WAIT_TIME);
                    708:        }
                    709:
                    710:        /* now, clear it all out */
                    711:        alarm(0);
                    712:        signal(SIGALRM, SIG_DFL);
                    713:
                    714:        /* RFC1945, 8.3 */
1.16      mrg       715:        if (request->hr_method == HTTP_POST &&
                    716:            request->hr_content_length == NULL) {
                    717:                (void)bozo_http_error(httpd, 400, request,
                    718:                                "missing content length");
1.12      mrg       719:                goto cleanup;
                    720:        }
1.1       tls       721:
                    722:        /* HTTP/1.1 draft rev-06, 14.23 & 19.6.1.1 */
1.16      mrg       723:        if (request->hr_proto == httpd->consts.http_11 &&
                    724:            request->hr_host == NULL) {
                    725:                (void)bozo_http_error(httpd, 400, request,
                    726:                                "missing Host header");
1.12      mrg       727:                goto cleanup;
                    728:        }
                    729:
                    730:        if (request->hr_range != NULL) {
1.16      mrg       731:                debug((httpd, DEBUG_FAT, "hr_range: %s", request->hr_range));
1.12      mrg       732:                /* support only simple ranges %d- and %d-%d */
                    733:                if (strchr(request->hr_range, ',') == NULL) {
                    734:                        const char *rstart, *dash;
                    735:
                    736:                        rstart = strchr(request->hr_range, '=');
                    737:                        if (rstart != NULL) {
                    738:                                rstart++;
                    739:                                dash = strchr(rstart, '-');
                    740:                                if (dash != NULL && dash != rstart) {
                    741:                                        dash++;
                    742:                                        request->hr_have_range = 1;
                    743:                                        request->hr_first_byte_pos =
                    744:                                            strtoll(rstart, NULL, 10);
                    745:                                        if (request->hr_first_byte_pos < 0)
                    746:                                                request->hr_first_byte_pos = 0;
                    747:                                        if (*dash != '\0') {
                    748:                                                request->hr_last_byte_pos =
                    749:                                                    strtoll(dash, NULL, 10);
                    750:                                                if (request->hr_last_byte_pos < 0)
                    751:                                                        request->hr_last_byte_pos = -1;
                    752:                                        }
                    753:                                }
                    754:                        }
                    755:                }
                    756:        }
1.1       tls       757:
1.16      mrg       758:        debug((httpd, DEBUG_FAT, "bozo_read_request returns url %s in request",
                    759:               request->hr_file));
                    760:        return request;
1.1       tls       761:
1.16      mrg       762: cleanup:
                    763:        bozo_clean_request(request);
1.1       tls       764:
1.16      mrg       765:        return NULL;
1.1       tls       766: }
                    767:
1.10      joerg     768: static int
1.16      mrg       769: mmap_and_write_part(bozohttpd_t *httpd, int fd, off_t first_byte_pos, size_t sz)
1.12      mrg       770: {
1.14      mrg       771:        size_t mappedsz, wroffset;
                    772:        off_t mappedoffset;
1.12      mrg       773:        char *addr;
1.14      mrg       774:        void *mappedaddr;
                    775:
                    776:        /*
                    777:         * we need to ensure that both the size *and* offset arguments to
                    778:         * mmap() are page-aligned.  our formala for this is:
                    779:         *
                    780:         *    input offset: first_byte_pos
                    781:         *    input size: sz
                    782:         *
                    783:         *    mapped offset = page align truncate (input offset)
                    784:         *    mapped size   =
                    785:         *        page align extend (input offset - mapped offset + input size)
                    786:         *    write offset  = input offset - mapped offset
                    787:         *
                    788:         * we use the write offset in all writes
                    789:         */
1.16      mrg       790:        mappedoffset = first_byte_pos & ~(httpd->page_size - 1);
                    791:        mappedsz = (size_t)
                    792:                (first_byte_pos - mappedoffset + sz + httpd->page_size - 1) &
                    793:                ~(httpd->page_size - 1);
                    794:        wroffset = (size_t)(first_byte_pos - mappedoffset);
1.12      mrg       795:
1.14      mrg       796:        addr = mmap(0, mappedsz, PROT_READ, MAP_SHARED, fd, mappedoffset);
1.12      mrg       797:        if (addr == (char *)-1) {
1.16      mrg       798:                bozo_warn(httpd, "mmap failed: %s", strerror(errno));
1.12      mrg       799:                return -1;
                    800:        }
1.14      mrg       801:        mappedaddr = addr;
1.12      mrg       802:
                    803: #ifdef MADV_SEQUENTIAL
                    804:        (void)madvise(addr, sz, MADV_SEQUENTIAL);
                    805: #endif
1.16      mrg       806:        while (sz > BOZO_WRSZ) {
                    807:                if (bozo_write(httpd, STDOUT_FILENO, addr + wroffset,
                    808:                                BOZO_WRSZ) != BOZO_WRSZ) {
                    809:                        bozo_warn(httpd, "write failed: %s", strerror(errno));
1.12      mrg       810:                        goto out;
                    811:                }
1.16      mrg       812:                debug((httpd, DEBUG_OBESE, "wrote %d bytes", BOZO_WRSZ));
                    813:                sz -= BOZO_WRSZ;
                    814:                addr += BOZO_WRSZ;
                    815:        }
                    816:        if (sz && (size_t)bozo_write(httpd, STDOUT_FILENO, addr + wroffset,
                    817:                                sz) != sz) {
                    818:                bozo_warn(httpd, "final write failed: %s", strerror(errno));
1.12      mrg       819:                goto out;
                    820:        }
1.16      mrg       821:        debug((httpd, DEBUG_OBESE, "wrote %d bytes", (int)sz));
1.12      mrg       822:  out:
1.14      mrg       823:        if (munmap(mappedaddr, mappedsz) < 0) {
1.16      mrg       824:                bozo_warn(httpd, "munmap failed");
1.12      mrg       825:                return -1;
                    826:        }
                    827:
                    828:        return 0;
                    829: }
                    830:
                    831: static int
1.10      joerg     832: parse_http_date(const char *val, time_t *timestamp)
                    833: {
                    834:        char *remainder;
                    835:        struct tm tm;
                    836:
                    837:        if ((remainder = strptime(val, "%a, %d %b %Y %T GMT", &tm)) == NULL &&
                    838:            (remainder = strptime(val, "%a, %d-%b-%y %T GMT", &tm)) == NULL &&
                    839:            (remainder = strptime(val, "%a %b %d %T %Y", &tm)) == NULL)
                    840:                return 0; /* Invalid HTTP date format */
                    841:
                    842:        if (*remainder)
                    843:                return 0; /* No trailing garbage */
                    844:
                    845:        *timestamp = timegm(&tm);
                    846:        return 1;
                    847: }
                    848:
1.1       tls       849: /*
1.32      mrg       850:  * given an url, encode it ala rfc 3986.  ie, escape ? and friends.
                    851:  * note that this function returns a static buffer, and thus needs
                    852:  * to be updated for any sort of parallel processing.
                    853:  */
                    854: char *
1.40      mrg       855: bozo_escape_rfc3986(bozohttpd_t *httpd, const char *url)
1.32      mrg       856: {
                    857:        static char *buf;
                    858:        static size_t buflen = 0;
                    859:        size_t len;
                    860:        const char *s;
                    861:        char *d;
                    862:
                    863:        len = strlen(url);
                    864:        if (buflen < len * 3 + 1) {
                    865:                buflen = len * 3 + 1;
                    866:                buf = bozorealloc(httpd, buf, buflen);
                    867:        }
1.42      mbalmer   868:
1.32      mrg       869:        if (url == NULL) {
                    870:                buf[0] = 0;
                    871:                return buf;
                    872:        }
                    873:
1.33      mrg       874:        for (len = 0, s = url, d = buf; *s;) {
1.32      mrg       875:                if (*s & 0x80)
                    876:                        goto encode_it;
                    877:                switch (*s) {
                    878:                case ':':
                    879:                case '/':
                    880:                case '?':
                    881:                case '#':
                    882:                case '[':
                    883:                case ']':
                    884:                case '@':
                    885:                case '!':
                    886:                case '$':
                    887:                case '&':
                    888:                case '\'':
                    889:                case '(':
                    890:                case ')':
                    891:                case '*':
                    892:                case '+':
                    893:                case ',':
                    894:                case ';':
                    895:                case '=':
1.33      mrg       896:                case '%':
1.32      mrg       897:                encode_it:
                    898:                        snprintf(d, 4, "%%%2X", *s++);
                    899:                        d += 3;
                    900:                        len += 3;
1.33      mrg       901:                        break;
1.32      mrg       902:                default:
                    903:                        *d++ = *s++;
                    904:                        len++;
1.33      mrg       905:                        break;
1.32      mrg       906:                }
                    907:        }
                    908:        buf[len] = 0;
                    909:
                    910:        return buf;
                    911: }
                    912:
                    913: /*
1.16      mrg       914:  * checks to see if this request has a valid .bzdirect file.  returns
                    915:  * 0 on failure and 1 on success.
1.1       tls       916:  */
1.16      mrg       917: static int
                    918: check_direct_access(bozo_httpreq_t *request)
1.1       tls       919: {
1.16      mrg       920:        FILE *fp;
                    921:        struct stat sb;
                    922:        char dir[MAXPATHLEN], dirfile[MAXPATHLEN], *basename;
1.1       tls       923:
1.16      mrg       924:        snprintf(dir, sizeof(dir), "%s", request->hr_file + 1);
1.37      martin    925:        debug((request->hr_httpd, DEBUG_FAT, "check_direct_access: dir %s", dir));
1.16      mrg       926:        basename = strrchr(dir, '/');
1.12      mrg       927:
1.16      mrg       928:        if ((!basename || basename[1] != '\0') &&
                    929:            lstat(dir, &sb) == 0 && S_ISDIR(sb.st_mode))
                    930:                /* nothing */;
                    931:        else if (basename == NULL)
                    932:                strcpy(dir, ".");
                    933:        else {
                    934:                *basename++ = '\0';
                    935:                bozo_check_special_files(request, basename);
1.12      mrg       936:        }
                    937:
1.16      mrg       938:        snprintf(dirfile, sizeof(dirfile), "%s/%s", dir, DIRECT_ACCESS_FILE);
                    939:        if (stat(dirfile, &sb) < 0 ||
                    940:            (fp = fopen(dirfile, "r")) == NULL)
                    941:                return 0;
                    942:        fclose(fp);
                    943:        return 1;
                    944: }
1.1       tls       945:
1.16      mrg       946: /*
                    947:  * do automatic redirection -- if there are query parameters for the URL
                    948:  * we will tack these on to the new (redirected) URL.
                    949:  */
                    950: static void
                    951: handle_redirect(bozo_httpreq_t *request,
                    952:                const char *url, int absolute)
                    953: {
                    954:        bozohttpd_t *httpd = request->hr_httpd;
                    955:        char *urlbuf;
                    956:        char portbuf[20];
1.35      martin    957:        const char *hostname = BOZOHOST(httpd, request);
1.16      mrg       958:        int query = 0;
1.42      mbalmer   959:
1.16      mrg       960:        if (url == NULL) {
                    961:                if (asprintf(&urlbuf, "/%s/", request->hr_file) < 0)
                    962:                        bozo_err(httpd, 1, "asprintf");
                    963:                url = urlbuf;
                    964:        } else
                    965:                urlbuf = NULL;
1.40      mrg       966:        url = bozo_escape_rfc3986(request->hr_httpd, url);
1.1       tls       967:
1.32      mrg       968:        if (request->hr_query && strlen(request->hr_query))
1.16      mrg       969:                query = 1;
1.1       tls       970:
1.16      mrg       971:        if (request->hr_serverport && strcmp(request->hr_serverport, "80") != 0)
                    972:                snprintf(portbuf, sizeof(portbuf), ":%s",
                    973:                    request->hr_serverport);
                    974:        else
                    975:                portbuf[0] = '\0';
1.38      martin    976:        if (absolute)
                    977:                bozo_warn(httpd, "redirecting %s", url);
                    978:        else
                    979:                bozo_warn(httpd, "redirecting %s%s%s", hostname, portbuf, url);
1.16      mrg       980:        debug((httpd, DEBUG_FAT, "redirecting %s", url));
                    981:        bozo_printf(httpd, "%s 301 Document Moved\r\n", request->hr_proto);
1.42      mbalmer   982:        if (request->hr_proto != httpd->consts.http_09)
1.16      mrg       983:                bozo_print_header(request, NULL, "text/html", NULL);
                    984:        if (request->hr_proto != httpd->consts.http_09) {
                    985:                bozo_printf(httpd, "Location: http://");
                    986:                if (absolute == 0)
1.35      martin    987:                        bozo_printf(httpd, "%s%s", hostname, portbuf);
1.16      mrg       988:                if (query) {
1.32      mrg       989:                        bozo_printf(httpd, "%s?%s\r\n", url, request->hr_query);
1.16      mrg       990:                } else {
1.32      mrg       991:                        bozo_printf(httpd, "%s\r\n", url);
1.12      mrg       992:                }
1.1       tls       993:        }
1.16      mrg       994:        bozo_printf(httpd, "\r\n");
                    995:        if (request->hr_method == HTTP_HEAD)
                    996:                goto head;
                    997:        bozo_printf(httpd, "<html><head><title>Document Moved</title></head>\n");
                    998:        bozo_printf(httpd, "<body><h1>Document Moved</h1>\n");
                    999:        bozo_printf(httpd, "This document had moved <a href=\"http://");
                   1000:        if (query) {
1.32      mrg      1001:                if (absolute)
                   1002:                        bozo_printf(httpd, "%s?%s", url, request->hr_query);
                   1003:                else
1.35      martin   1004:                        bozo_printf(httpd, "%s%s%s?%s", hostname,
1.32      mrg      1005:                                    portbuf, url, request->hr_query);
                   1006:        } else {
                   1007:                if (absolute)
                   1008:                        bozo_printf(httpd, "%s", url);
                   1009:                else
1.35      martin   1010:                        bozo_printf(httpd, "%s%s%s", hostname,
1.32      mrg      1011:                                    portbuf, url);
1.16      mrg      1012:        }
                   1013:        bozo_printf(httpd, "\">here</a>\n");
                   1014:        bozo_printf(httpd, "</body></html>\n");
                   1015: head:
                   1016:        bozo_flush(httpd, stdout);
1.44      mbalmer  1017:        free(urlbuf);
1.1       tls      1018: }
                   1019:
                   1020: /*
                   1021:  * deal with virtual host names; we do this:
1.16      mrg      1022:  *     if we have a virtual path root (httpd->virtbase), and we are given a
1.1       tls      1023:  *     virtual host spec (Host: ho.st or http://ho.st/), see if this
1.16      mrg      1024:  *     directory exists under httpd->virtbase.  if it does, use this as the
1.1       tls      1025:  #     new slashdir.
                   1026:  */
1.12      mrg      1027: static int
1.16      mrg      1028: check_virtual(bozo_httpreq_t *request)
1.1       tls      1029: {
1.16      mrg      1030:        bozohttpd_t *httpd = request->hr_httpd;
1.9       tls      1031:        char *file = request->hr_file, *s;
1.1       tls      1032:        size_t len;
                   1033:
1.16      mrg      1034:        if (!httpd->virtbase)
1.1       tls      1035:                goto use_slashdir;
                   1036:
                   1037:        /*
                   1038:         * convert http://virtual.host/ to request->hr_host
                   1039:         */
1.16      mrg      1040:        debug((httpd, DEBUG_OBESE, "checking for http:// virtual host in ``%s''",
                   1041:                        file));
1.9       tls      1042:        if (strncasecmp(file, "http://", 7) == 0) {
1.1       tls      1043:                /* we would do virtual hosting here? */
1.9       tls      1044:                file += 7;
                   1045:                s = strchr(file, '/');
1.1       tls      1046:                /* HTTP/1.1 draft rev-06, 5.2: URI takes precedence over Host: */
1.9       tls      1047:                request->hr_host = file;
1.16      mrg      1048:                request->hr_file = bozostrdup(request->hr_httpd, s ? s : "/");
                   1049:                debug((httpd, DEBUG_OBESE, "got host ``%s'' file is now ``%s''",
1.9       tls      1050:                    request->hr_host, request->hr_file));
1.1       tls      1051:        } else if (!request->hr_host)
                   1052:                goto use_slashdir;
                   1053:
                   1054:        /*
                   1055:         * ok, we have a virtual host, use scandir(3) to find a case
                   1056:         * insensitive match for the virtual host we are asked for.
                   1057:         * note that if the virtual host is the same as the master,
                   1058:         * we don't need to do anything special.
                   1059:         */
                   1060:        len = strlen(request->hr_host);
1.16      mrg      1061:        debug((httpd, DEBUG_OBESE,
                   1062:            "check_virtual: checking host `%s' under httpd->virtbase `%s' "
                   1063:            "for file `%s'",
                   1064:            request->hr_host, httpd->virtbase, request->hr_file));
                   1065:        if (strncasecmp(httpd->virthostname, request->hr_host, len) != 0) {
1.1       tls      1066:                s = 0;
1.24      mrg      1067:                DIR *dirp;
                   1068:                struct dirent *d;
                   1069:
1.23      mrg      1070:                if ((dirp = opendir(httpd->virtbase)) != NULL) {
                   1071:                        while ((d = readdir(dirp)) != NULL) {
                   1072:                                if (strcmp(d->d_name, ".") == 0 ||
                   1073:                                    strcmp(d->d_name, "..") == 0) {
                   1074:                                        continue;
                   1075:                                }
                   1076:                                debug((httpd, DEBUG_OBESE, "looking at dir``%s''",
                   1077:                                   d->d_name));
                   1078:                                if (strncasecmp(d->d_name, request->hr_host,
                   1079:                                    len) == 0) {
                   1080:                                        /* found it, punch it */
                   1081:                                        debug((httpd, DEBUG_OBESE, "found it punch it"));
1.35      martin   1082:                                        request->hr_virthostname =
1.40      mrg      1083:                                            bozostrdup(httpd, d->d_name);
1.23      mrg      1084:                                        if (asprintf(&s, "%s/%s", httpd->virtbase,
1.35      martin   1085:                                            request->hr_virthostname) < 0)
1.23      mrg      1086:                                                bozo_err(httpd, 1, "asprintf");
                   1087:                                        break;
                   1088:                                }
1.1       tls      1089:                        }
1.23      mrg      1090:                        closedir(dirp);
                   1091:                }
                   1092:                else {
                   1093:                        debug((httpd, DEBUG_FAT, "opendir %s failed: %s",
                   1094:                            httpd->virtbase, strerror(errno)));
1.1       tls      1095:                }
                   1096:                if (s == 0) {
1.16      mrg      1097:                        if (httpd->unknown_slash)
1.1       tls      1098:                                goto use_slashdir;
1.16      mrg      1099:                        return bozo_http_error(httpd, 404, request,
                   1100:                                                "unknown URL");
1.1       tls      1101:                }
                   1102:        } else
                   1103: use_slashdir:
1.16      mrg      1104:                s = httpd->slashdir;
1.1       tls      1105:
                   1106:        /*
                   1107:         * ok, nailed the correct slashdir, chdir to it
                   1108:         */
                   1109:        if (chdir(s) < 0)
1.16      mrg      1110:                return bozo_http_error(httpd, 404, request,
                   1111:                                        "can't chdir to slashdir");
1.12      mrg      1112:        return 0;
1.1       tls      1113: }
                   1114:
                   1115: /*
                   1116:  * checks to see if this request has a valid .bzredirect file.  returns
1.36      martin   1117:  * 0 when no redirection happend, or 1 when handle_redirect() has been
                   1118:  * called.
1.1       tls      1119:  */
1.36      martin   1120: static int
1.16      mrg      1121: check_bzredirect(bozo_httpreq_t *request)
1.1       tls      1122: {
                   1123:        struct stat sb;
1.39      martin   1124:        char dir[MAXPATHLEN], redir[MAXPATHLEN], redirpath[MAXPATHLEN + 1],
                   1125:            path[MAXPATHLEN];
1.1       tls      1126:        char *basename, *finalredir;
                   1127:        int rv, absolute;
                   1128:
                   1129:        /*
                   1130:         * if this pathname is really a directory, but doesn't end in /,
                   1131:         * use it as the directory to look for the redir file.
                   1132:         */
1.9       tls      1133:        snprintf(dir, sizeof(dir), "%s", request->hr_file + 1);
1.17      mrg      1134:        debug((request->hr_httpd, DEBUG_FAT, "check_bzredirect: dir %s", dir));
1.1       tls      1135:        basename = strrchr(dir, '/');
                   1136:
                   1137:        if ((!basename || basename[1] != '\0') &&
                   1138:            lstat(dir, &sb) == 0 && S_ISDIR(sb.st_mode))
                   1139:                /* nothing */;
                   1140:        else if (basename == NULL)
                   1141:                strcpy(dir, ".");
                   1142:        else {
                   1143:                *basename++ = '\0';
1.16      mrg      1144:                bozo_check_special_files(request, basename);
1.1       tls      1145:        }
                   1146:
                   1147:        snprintf(redir, sizeof(redir), "%s/%s", dir, REDIRECT_FILE);
                   1148:        if (lstat(redir, &sb) == 0) {
1.16      mrg      1149:                if (!S_ISLNK(sb.st_mode))
1.36      martin   1150:                        return 0;
1.1       tls      1151:                absolute = 0;
                   1152:        } else {
                   1153:                snprintf(redir, sizeof(redir), "%s/%s", dir, ABSREDIRECT_FILE);
1.16      mrg      1154:                if (lstat(redir, &sb) < 0 || !S_ISLNK(sb.st_mode))
1.36      martin   1155:                        return 0;
1.1       tls      1156:                absolute = 1;
                   1157:        }
1.17      mrg      1158:        debug((request->hr_httpd, DEBUG_FAT,
                   1159:               "check_bzredirect: calling readlink"));
1.1       tls      1160:        rv = readlink(redir, redirpath, sizeof redirpath - 1);
                   1161:        if (rv == -1 || rv == 0) {
1.17      mrg      1162:                debug((request->hr_httpd, DEBUG_FAT, "readlink failed"));
1.36      martin   1163:                return 0;
1.1       tls      1164:        }
                   1165:        redirpath[rv] = '\0';
1.17      mrg      1166:        debug((request->hr_httpd, DEBUG_FAT,
                   1167:               "readlink returned \"%s\"", redirpath));
1.39      martin   1168:
                   1169:        /* check if we need authentication */
                   1170:        snprintf(path, sizeof(path), "%s/", dir);
                   1171:        if (bozo_auth_check(request, path))
                   1172:                return 1;
                   1173:
1.1       tls      1174:        /* now we have the link pointer, redirect to the real place */
                   1175:        if (absolute)
                   1176:                finalredir = redirpath;
                   1177:        else
                   1178:                snprintf(finalredir = redir, sizeof(redir), "/%s/%s", dir,
                   1179:                         redirpath);
                   1180:
1.17      mrg      1181:        debug((request->hr_httpd, DEBUG_FAT,
                   1182:               "check_bzredirect: new redir %s", finalredir));
1.1       tls      1183:        handle_redirect(request, finalredir, absolute);
1.36      martin   1184:        return 1;
1.1       tls      1185: }
                   1186:
1.16      mrg      1187: /* this fixes the %HH hack that RFC2396 requires.  */
                   1188: static void
                   1189: fix_url_percent(bozo_httpreq_t *request)
1.1       tls      1190: {
1.16      mrg      1191:        bozohttpd_t *httpd = request->hr_httpd;
                   1192:        char    *s, *t, buf[3], *url;
                   1193:        char    *end;   /* if end is not-zero, we don't translate beyond that */
                   1194:
                   1195:        url = request->hr_file;
                   1196:
                   1197:        end = url + strlen(url);
                   1198:
                   1199:        /* fast forward to the first % */
                   1200:        if ((s = strchr(url, '%')) == NULL)
                   1201:                return;
1.1       tls      1202:
1.16      mrg      1203:        t = s;
                   1204:        do {
                   1205:                if (end && s >= end) {
                   1206:                        debug((httpd, DEBUG_EXPLODING,
                   1207:                                "fu_%%: past end, filling out.."));
                   1208:                        while (*s)
                   1209:                                *t++ = *s++;
                   1210:                        break;
                   1211:                }
                   1212:                debug((httpd, DEBUG_EXPLODING,
                   1213:                        "fu_%%: got s == %%, s[1]s[2] == %c%c",
                   1214:                        s[1], s[2]));
                   1215:                if (s[1] == '\0' || s[2] == '\0') {
                   1216:                        (void)bozo_http_error(httpd, 400, request,
                   1217:                            "percent hack missing two chars afterwards");
                   1218:                        goto copy_rest;
                   1219:                }
                   1220:                if (s[1] == '0' && s[2] == '0') {
                   1221:                        (void)bozo_http_error(httpd, 404, request,
                   1222:                                        "percent hack was %00");
                   1223:                        goto copy_rest;
                   1224:                }
                   1225:                if (s[1] == '2' && s[2] == 'f') {
                   1226:                        (void)bozo_http_error(httpd, 404, request,
                   1227:                                        "percent hack was %2f (/)");
                   1228:                        goto copy_rest;
                   1229:                }
1.42      mbalmer  1230:
1.16      mrg      1231:                buf[0] = *++s;
                   1232:                buf[1] = *++s;
                   1233:                buf[2] = '\0';
                   1234:                s++;
                   1235:                *t = (char)strtol(buf, NULL, 16);
                   1236:                debug((httpd, DEBUG_EXPLODING,
                   1237:                                "fu_%%: strtol put '%02x' into *t", *t));
                   1238:                if (*t++ == '\0') {
                   1239:                        (void)bozo_http_error(httpd, 400, request,
                   1240:                                        "percent hack got a 0 back");
                   1241:                        goto copy_rest;
                   1242:                }
1.1       tls      1243:
1.16      mrg      1244:                while (*s && *s != '%') {
                   1245:                        if (end && s >= end)
                   1246:                                break;
                   1247:                        *t++ = *s++;
                   1248:                }
                   1249:        } while (*s);
                   1250: copy_rest:
                   1251:        while (*s) {
                   1252:                if (s >= end)
                   1253:                        break;
                   1254:                *t++ = *s++;
1.1       tls      1255:        }
1.16      mrg      1256:        *t = '\0';
                   1257:        debug((httpd, DEBUG_FAT, "fix_url_percent returns %s in url",
                   1258:                        request->hr_file));
1.1       tls      1259: }
                   1260:
                   1261: /*
                   1262:  * transform_request does this:
                   1263:  *     - ``expand'' %20 crapola
                   1264:  *     - punt if it doesn't start with /
1.16      mrg      1265:  *     - check httpd->untrustedref / referrer
1.1       tls      1266:  *     - look for "http://myname/" and deal with it.
1.42      mbalmer  1267:  *     - maybe call bozo_process_cgi()
1.16      mrg      1268:  *     - check for ~user and call bozo_user_transform() if so
1.1       tls      1269:  *     - if the length > 1, check for trailing slash.  if so,
                   1270:  *       add the index.html file
                   1271:  *     - if the length is 1, return the index.html file
                   1272:  *     - disallow anything ending up with a file starting
                   1273:  *       at "/" or having ".." in it.
                   1274:  *     - anything else is a really weird internal error
1.12      mrg      1275:  *     - returns malloced file to serve, if unhandled
1.1       tls      1276:  */
1.16      mrg      1277: static int
                   1278: transform_request(bozo_httpreq_t *request, int *isindex)
1.1       tls      1279: {
1.16      mrg      1280:        bozohttpd_t *httpd = request->hr_httpd;
1.12      mrg      1281:        char    *file, *newfile = NULL;
1.1       tls      1282:        size_t  len;
1.35      martin   1283:        const char *hostname = BOZOHOST(httpd, request);
1.1       tls      1284:
1.12      mrg      1285:        file = NULL;
1.1       tls      1286:        *isindex = 0;
1.16      mrg      1287:        debug((httpd, DEBUG_FAT, "tf_req: file %s", request->hr_file));
1.1       tls      1288:        fix_url_percent(request);
1.12      mrg      1289:        if (check_virtual(request)) {
                   1290:                goto bad_done;
                   1291:        }
                   1292:        file = request->hr_file;
1.1       tls      1293:
1.12      mrg      1294:        if (file[0] != '/') {
1.16      mrg      1295:                (void)bozo_http_error(httpd, 404, request, "unknown URL");
1.12      mrg      1296:                goto bad_done;
                   1297:        }
1.1       tls      1298:
1.36      martin   1299:        if (check_bzredirect(request))
                   1300:                return 0;
1.1       tls      1301:
1.16      mrg      1302:        if (httpd->untrustedref) {
1.1       tls      1303:                int to_indexhtml = 0;
                   1304:
                   1305: #define TOP_PAGE(x)    (strcmp((x), "/") == 0 || \
1.16      mrg      1306:                         strcmp((x) + 1, httpd->index_html) == 0 || \
1.42      mbalmer  1307:                         strcmp((x) + 1, "favicon.ico") == 0)
                   1308:
1.16      mrg      1309:                debug((httpd, DEBUG_EXPLODING, "checking httpd->untrustedref"));
1.1       tls      1310:                /*
                   1311:                 * first check that this path isn't allowed via .bzdirect file,
                   1312:                 * and then check referrer; make sure that people come via the
                   1313:                 * real name... otherwise if we aren't looking at / or
                   1314:                 * /index.html, redirect...  we also special case favicon.ico.
                   1315:                 */
                   1316:                if (check_direct_access(request))
                   1317:                        /* nothing */;
                   1318:                else if (request->hr_referrer) {
                   1319:                        const char *r = request->hr_referrer;
                   1320:
1.16      mrg      1321:                        debug((httpd, DEBUG_FAT,
                   1322:                                "checking referrer \"%s\" vs virthostname %s",
1.35      martin   1323:                                r, hostname));
1.1       tls      1324:                        if (strncmp(r, "http://", 7) != 0 ||
1.35      martin   1325:                            (strncasecmp(r + 7, hostname,
                   1326:                                         strlen(hostname)) != 0 &&
1.12      mrg      1327:                             !TOP_PAGE(file)))
1.1       tls      1328:                                to_indexhtml = 1;
                   1329:                } else {
                   1330:                        const char *h = request->hr_host;
                   1331:
1.16      mrg      1332:                        debug((httpd, DEBUG_FAT, "url has no referrer at all"));
1.1       tls      1333:                        /* if there's no referrer, let / or /index.html past */
1.12      mrg      1334:                        if (!TOP_PAGE(file) ||
1.35      martin   1335:                            (h && strncasecmp(h, hostname,
                   1336:                                        strlen(hostname)) != 0))
1.1       tls      1337:                                to_indexhtml = 1;
                   1338:                }
                   1339:
                   1340:                if (to_indexhtml) {
                   1341:                        char *slashindexhtml;
                   1342:
1.16      mrg      1343:                        if (asprintf(&slashindexhtml, "/%s",
                   1344:                                        httpd->index_html) < 0)
                   1345:                                bozo_err(httpd, 1, "asprintf");
                   1346:                        debug((httpd, DEBUG_FAT,
                   1347:                                "httpd->untrustedref: redirecting %s to %s",
                   1348:                                file, slashindexhtml));
1.1       tls      1349:                        handle_redirect(request, slashindexhtml, 0);
1.12      mrg      1350:                        free(slashindexhtml);
                   1351:                        return 0;
1.1       tls      1352:                }
                   1353:        }
                   1354:
1.12      mrg      1355:        len = strlen(file);
1.16      mrg      1356:        if (/*CONSTCOND*/0) {
1.1       tls      1357: #ifndef NO_USER_SUPPORT
1.16      mrg      1358:        } else if (len > 1 && httpd->enable_users && file[1] == '~') {
1.12      mrg      1359:                if (file[2] == '\0') {
1.16      mrg      1360:                        (void)bozo_http_error(httpd, 404, request,
                   1361:                                                "missing username");
1.12      mrg      1362:                        goto bad_done;
                   1363:                }
                   1364:                if (strchr(file + 2, '/') == NULL) {
1.1       tls      1365:                        handle_redirect(request, NULL, 0);
1.12      mrg      1366:                        return 0;
                   1367:                }
1.16      mrg      1368:                debug((httpd, DEBUG_FAT, "calling bozo_user_transform"));
1.12      mrg      1369:
1.16      mrg      1370:                return bozo_user_transform(request, isindex);
1.1       tls      1371: #endif /* NO_USER_SUPPORT */
                   1372:        } else if (len > 1) {
1.16      mrg      1373:                debug((httpd, DEBUG_FAT, "file[len-1] == %c", file[len-1]));
1.12      mrg      1374:                if (file[len-1] == '/') {       /* append index.html */
1.1       tls      1375:                        *isindex = 1;
1.16      mrg      1376:                        debug((httpd, DEBUG_FAT, "appending index.html"));
                   1377:                        newfile = bozomalloc(httpd,
                   1378:                                        len + strlen(httpd->index_html) + 1);
1.12      mrg      1379:                        strcpy(newfile, file + 1);
1.16      mrg      1380:                        strcat(newfile, httpd->index_html);
1.1       tls      1381:                } else
1.16      mrg      1382:                        newfile = bozostrdup(request->hr_httpd, file + 1);
1.1       tls      1383:        } else if (len == 1) {
1.16      mrg      1384:                debug((httpd, DEBUG_EXPLODING, "tf_req: len == 1"));
                   1385:                newfile = bozostrdup(request->hr_httpd, httpd->index_html);
1.1       tls      1386:                *isindex = 1;
1.12      mrg      1387:        } else {        /* len == 0 ? */
1.16      mrg      1388:                (void)bozo_http_error(httpd, 500, request,
                   1389:                                        "request->hr_file is nul?");
1.12      mrg      1390:                goto bad_done;
                   1391:        }
1.1       tls      1392:
1.12      mrg      1393:        if (newfile == NULL) {
1.16      mrg      1394:                (void)bozo_http_error(httpd, 500, request, "internal failure");
1.12      mrg      1395:                goto bad_done;
                   1396:        }
1.1       tls      1397:
                   1398:        /*
                   1399:         * look for "http://myname/" and deal with it as necessary.
                   1400:         */
                   1401:
                   1402:        /*
1.42      mbalmer  1403:         * stop traversing outside our domain
1.1       tls      1404:         *
                   1405:         * XXX true security only comes from our parent using chroot(2)
                   1406:         * before execve(2)'ing us.  or our own built in chroot(2) support.
                   1407:         */
1.12      mrg      1408:        if (*newfile == '/' || strcmp(newfile, "..") == 0 ||
                   1409:            strstr(newfile, "/..") || strstr(newfile, "../")) {
1.16      mrg      1410:                (void)bozo_http_error(httpd, 403, request, "illegal request");
1.12      mrg      1411:                goto bad_done;
                   1412:        }
                   1413:
1.17      mrg      1414:        if (bozo_auth_check(request, newfile))
1.12      mrg      1415:                goto bad_done;
1.1       tls      1416:
1.13      mrg      1417:        if (strlen(newfile)) {
1.20      mrg      1418:                request->hr_oldfile = request->hr_file;
1.12      mrg      1419:                request->hr_file = newfile;
1.13      mrg      1420:        }
1.9       tls      1421:
1.16      mrg      1422:        if (bozo_process_cgi(request))
1.12      mrg      1423:                return 0;
1.1       tls      1424:
1.43      mbalmer  1425:        if (bozo_process_lua(request))
                   1426:                return 0;
                   1427:
1.16      mrg      1428:        debug((httpd, DEBUG_FAT, "transform_request set: %s", newfile));
1.12      mrg      1429:        return 1;
                   1430: bad_done:
1.16      mrg      1431:        debug((httpd, DEBUG_FAT, "transform_request returning: 0"));
1.44      mbalmer  1432:        free(newfile);
1.12      mrg      1433:        return 0;
1.1       tls      1434: }
                   1435:
                   1436: /*
1.31      elric    1437:  * can_gzip checks if the request supports and prefers gzip encoding.
                   1438:  *
                   1439:  * XXX: we do not consider the associated q with gzip in making our
                   1440:  *      decision which is broken.
                   1441:  */
                   1442:
                   1443: static int
                   1444: can_gzip(bozo_httpreq_t *request)
                   1445: {
                   1446:        const char      *pos;
                   1447:        const char      *tmp;
                   1448:        size_t           len;
                   1449:
                   1450:        /* First we decide if the request can be gzipped at all. */
                   1451:
                   1452:        /* not if we already are encoded... */
                   1453:        tmp = bozo_content_encoding(request, request->hr_file);
                   1454:        if (tmp && *tmp)
                   1455:                return 0;
                   1456:
                   1457:        /* not if we are not asking for the whole file... */
                   1458:        if (request->hr_last_byte_pos != -1 || request->hr_have_range)
                   1459:                return 0;
                   1460:
                   1461:        /* Then we determine if gzip is on the cards. */
                   1462:
                   1463:        for (pos = request->hr_accept_encoding; pos && *pos; pos += len) {
                   1464:                while (*pos == ' ')
                   1465:                        pos++;
                   1466:
                   1467:                len = strcspn(pos, ";,");
                   1468:
                   1469:                if ((len == 4 && strncasecmp("gzip", pos, 4) == 0) ||
                   1470:                    (len == 6 && strncasecmp("x-gzip", pos, 6) == 0))
                   1471:                        return 1;
                   1472:
                   1473:                if (pos[len] == ';')
                   1474:                        len += strcspn(&pos[len], ",");
                   1475:
                   1476:                if (pos[len])
                   1477:                        len++;
                   1478:        }
                   1479:
                   1480:        return 0;
                   1481: }
                   1482:
                   1483: /*
1.16      mrg      1484:  * bozo_process_request does the following:
                   1485:  *     - check the request is valid
                   1486:  *     - process cgi-bin if necessary
                   1487:  *     - transform a filename if necesarry
                   1488:  *     - return the HTTP request
1.1       tls      1489:  */
1.16      mrg      1490: void
                   1491: bozo_process_request(bozo_httpreq_t *request)
1.1       tls      1492: {
1.16      mrg      1493:        bozohttpd_t *httpd = request->hr_httpd;
                   1494:        struct  stat sb;
                   1495:        time_t timestamp;
                   1496:        char    *file;
                   1497:        const char *type, *encoding;
                   1498:        int     fd, isindex;
                   1499:
                   1500:        /*
                   1501:         * note that transform_request chdir()'s if required.  also note
                   1502:         * that cgi is handed here.  if transform_request() returns 0
                   1503:         * then the request has been handled already.
                   1504:         */
                   1505:        if (transform_request(request, &isindex) == 0)
                   1506:                return;
1.12      mrg      1507:
1.31      elric    1508:        fd = -1;
                   1509:        encoding = NULL;
                   1510:        if (can_gzip(request)) {
                   1511:                asprintf(&file, "%s.gz", request->hr_file);
                   1512:                fd = open(file, O_RDONLY);
                   1513:                if (fd >= 0)
                   1514:                        encoding = "gzip";
                   1515:                free(file);
                   1516:        }
                   1517:
1.16      mrg      1518:        file = request->hr_file;
1.9       tls      1519:
1.31      elric    1520:        if (fd < 0)
                   1521:                fd = open(file, O_RDONLY);
                   1522:
1.16      mrg      1523:        if (fd < 0) {
                   1524:                debug((httpd, DEBUG_FAT, "open failed: %s", strerror(errno)));
                   1525:                if (errno == EPERM)
                   1526:                        (void)bozo_http_error(httpd, 403, request,
                   1527:                                                "no permission to open file");
                   1528:                else if (errno == ENOENT) {
1.42      mbalmer  1529:                        if (!bozo_dir_index(request, file, isindex))
1.16      mrg      1530:                                (void)bozo_http_error(httpd, 404, request,
                   1531:                                                        "no file");
                   1532:                } else
                   1533:                        (void)bozo_http_error(httpd, 500, request, "open file");
                   1534:                goto cleanup_nofd;
1.1       tls      1535:        }
1.16      mrg      1536:        if (fstat(fd, &sb) < 0) {
                   1537:                (void)bozo_http_error(httpd, 500, request, "can't fstat");
                   1538:                goto cleanup;
1.9       tls      1539:        }
1.16      mrg      1540:        if (S_ISDIR(sb.st_mode)) {
                   1541:                handle_redirect(request, NULL, 0);
                   1542:                goto cleanup;
1.6       mrg      1543:        }
1.1       tls      1544:
1.16      mrg      1545:        if (request->hr_if_modified_since &&
                   1546:            parse_http_date(request->hr_if_modified_since, &timestamp) &&
                   1547:            timestamp >= sb.st_mtime) {
                   1548:                /* XXX ignore subsecond of timestamp */
                   1549:                bozo_printf(httpd, "%s 304 Not Modified\r\n",
                   1550:                                request->hr_proto);
                   1551:                bozo_printf(httpd, "\r\n");
                   1552:                bozo_flush(httpd, stdout);
                   1553:                goto cleanup;
1.1       tls      1554:        }
                   1555:
1.16      mrg      1556:        /* validate requested range */
                   1557:        if (request->hr_last_byte_pos == -1 ||
                   1558:            request->hr_last_byte_pos >= sb.st_size)
                   1559:                request->hr_last_byte_pos = sb.st_size - 1;
                   1560:        if (request->hr_have_range &&
                   1561:            request->hr_first_byte_pos > request->hr_last_byte_pos) {
                   1562:                request->hr_have_range = 0;     /* punt */
                   1563:                request->hr_first_byte_pos = 0;
                   1564:                request->hr_last_byte_pos = sb.st_size - 1;
1.1       tls      1565:        }
1.28      joerg    1566:        debug((httpd, DEBUG_FAT, "have_range %d first_pos %lld last_pos %lld",
1.16      mrg      1567:            request->hr_have_range,
1.28      joerg    1568:            (long long)request->hr_first_byte_pos,
                   1569:            (long long)request->hr_last_byte_pos));
1.16      mrg      1570:        if (request->hr_have_range)
                   1571:                bozo_printf(httpd, "%s 206 Partial Content\r\n",
                   1572:                                request->hr_proto);
                   1573:        else
                   1574:                bozo_printf(httpd, "%s 200 OK\r\n", request->hr_proto);
1.1       tls      1575:
1.16      mrg      1576:        if (request->hr_proto != httpd->consts.http_09) {
                   1577:                type = bozo_content_type(request, file);
1.31      elric    1578:                if (!encoding)
                   1579:                        encoding = bozo_content_encoding(request, file);
1.1       tls      1580:
1.16      mrg      1581:                bozo_print_header(request, &sb, type, encoding);
                   1582:                bozo_printf(httpd, "\r\n");
1.12      mrg      1583:        }
1.16      mrg      1584:        bozo_flush(httpd, stdout);
1.1       tls      1585:
1.16      mrg      1586:        if (request->hr_method != HTTP_HEAD) {
                   1587:                off_t szleft, cur_byte_pos;
1.1       tls      1588:
1.16      mrg      1589:                szleft =
                   1590:                     request->hr_last_byte_pos - request->hr_first_byte_pos + 1;
                   1591:                cur_byte_pos = request->hr_first_byte_pos;
1.1       tls      1592:
1.16      mrg      1593:  retry:
                   1594:                while (szleft) {
                   1595:                        size_t sz;
1.12      mrg      1596:
1.16      mrg      1597:                        /* This should take care of the first unaligned chunk */
                   1598:                        if ((cur_byte_pos & (httpd->page_size - 1)) != 0)
                   1599:                                sz = (size_t)(cur_byte_pos & ~httpd->page_size);
                   1600:                        if ((off_t)httpd->mmapsz < szleft)
                   1601:                                sz = httpd->mmapsz;
                   1602:                        else
                   1603:                                sz = (size_t)szleft;
                   1604:                        if (mmap_and_write_part(httpd, fd, cur_byte_pos, sz)) {
                   1605:                                if (errno == ENOMEM) {
                   1606:                                        httpd->mmapsz /= 2;
                   1607:                                        if (httpd->mmapsz >= httpd->page_size)
                   1608:                                                goto retry;
                   1609:                                }
                   1610:                                goto cleanup;
                   1611:                        }
                   1612:                        cur_byte_pos += sz;
                   1613:                        szleft -= sz;
1.1       tls      1614:                }
1.16      mrg      1615:        }
                   1616:  cleanup:
                   1617:        close(fd);
                   1618:  cleanup_nofd:
                   1619:        close(STDIN_FILENO);
                   1620:        close(STDOUT_FILENO);
                   1621:        /*close(STDERR_FILENO);*/
1.1       tls      1622: }
                   1623:
1.16      mrg      1624: /* make sure we're not trying to access special files */
                   1625: int
                   1626: bozo_check_special_files(bozo_httpreq_t *request, const char *name)
1.1       tls      1627: {
1.16      mrg      1628:        bozohttpd_t *httpd = request->hr_httpd;
1.1       tls      1629:
1.16      mrg      1630:        /* ensure basename(name) != special files */
                   1631:        if (strcmp(name, DIRECT_ACCESS_FILE) == 0)
                   1632:                return bozo_http_error(httpd, 403, request,
                   1633:                    "no permission to open direct access file");
                   1634:        if (strcmp(name, REDIRECT_FILE) == 0)
                   1635:                return bozo_http_error(httpd, 403, request,
                   1636:                    "no permission to open redirect file");
                   1637:        if (strcmp(name, ABSREDIRECT_FILE) == 0)
                   1638:                return bozo_http_error(httpd, 403, request,
                   1639:                    "no permission to open redirect file");
1.17      mrg      1640:        return bozo_auth_check_special_files(request, name);
1.16      mrg      1641: }
1.1       tls      1642:
1.16      mrg      1643: /* generic header printing routine */
                   1644: void
                   1645: bozo_print_header(bozo_httpreq_t *request,
                   1646:                struct stat *sbp, const char *type, const char *encoding)
                   1647: {
                   1648:        bozohttpd_t *httpd = request->hr_httpd;
                   1649:        off_t len;
                   1650:        char    date[40];
1.1       tls      1651:
1.16      mrg      1652:        bozo_printf(httpd, "Date: %s\r\n", bozo_http_date(date, sizeof(date)));
                   1653:        bozo_printf(httpd, "Server: %s\r\n", httpd->server_software);
                   1654:        bozo_printf(httpd, "Accept-Ranges: bytes\r\n");
                   1655:        if (sbp) {
                   1656:                char filedate[40];
                   1657:                struct  tm *tm;
1.1       tls      1658:
1.16      mrg      1659:                tm = gmtime(&sbp->st_mtime);
                   1660:                strftime(filedate, sizeof filedate,
                   1661:                    "%a, %d %b %Y %H:%M:%S GMT", tm);
                   1662:                bozo_printf(httpd, "Last-Modified: %s\r\n", filedate);
                   1663:        }
                   1664:        if (type && *type)
                   1665:                bozo_printf(httpd, "Content-Type: %s\r\n", type);
                   1666:        if (encoding && *encoding)
                   1667:                bozo_printf(httpd, "Content-Encoding: %s\r\n", encoding);
                   1668:        if (sbp) {
                   1669:                if (request->hr_have_range) {
                   1670:                        len = request->hr_last_byte_pos -
                   1671:                                        request->hr_first_byte_pos +1;
                   1672:                        bozo_printf(httpd,
                   1673:                                "Content-Range: bytes %qd-%qd/%qd\r\n",
                   1674:                                (long long) request->hr_first_byte_pos,
                   1675:                                (long long) request->hr_last_byte_pos,
                   1676:                                (long long) sbp->st_size);
                   1677:                } else
                   1678:                        len = sbp->st_size;
                   1679:                bozo_printf(httpd, "Content-Length: %qd\r\n", (long long)len);
1.1       tls      1680:        }
1.16      mrg      1681:        if (request && request->hr_proto == httpd->consts.http_11)
                   1682:                bozo_printf(httpd, "Connection: close\r\n");
                   1683:        bozo_flush(httpd, stdout);
1.1       tls      1684: }
                   1685:
1.25      mrg      1686: #ifndef NO_DEBUG
1.1       tls      1687: void
1.16      mrg      1688: debug__(bozohttpd_t *httpd, int level, const char *fmt, ...)
1.1       tls      1689: {
                   1690:        va_list ap;
                   1691:        int savederrno;
1.42      mbalmer  1692:
1.1       tls      1693:        /* only log if the level is low enough */
1.16      mrg      1694:        if (httpd->debug < level)
1.1       tls      1695:                return;
                   1696:
                   1697:        savederrno = errno;
                   1698:        va_start(ap, fmt);
1.16      mrg      1699:        if (httpd->logstderr) {
1.1       tls      1700:                vfprintf(stderr, fmt, ap);
                   1701:                fputs("\n", stderr);
                   1702:        } else
                   1703:                vsyslog(LOG_DEBUG, fmt, ap);
                   1704:        va_end(ap);
                   1705:        errno = savederrno;
                   1706: }
1.25      mrg      1707: #endif /* NO_DEBUG */
1.1       tls      1708:
                   1709: /* these are like warn() and err(), except for syslog not stderr */
                   1710: void
1.16      mrg      1711: bozo_warn(bozohttpd_t *httpd, const char *fmt, ...)
1.1       tls      1712: {
                   1713:        va_list ap;
                   1714:
                   1715:        va_start(ap, fmt);
1.16      mrg      1716:        if (httpd->logstderr || isatty(STDERR_FILENO)) {
1.14      mrg      1717:                //fputs("warning: ", stderr);
1.1       tls      1718:                vfprintf(stderr, fmt, ap);
                   1719:                fputs("\n", stderr);
                   1720:        } else
                   1721:                vsyslog(LOG_INFO, fmt, ap);
                   1722:        va_end(ap);
                   1723: }
                   1724:
                   1725: void
1.16      mrg      1726: bozo_err(bozohttpd_t *httpd, int code, const char *fmt, ...)
1.1       tls      1727: {
                   1728:        va_list ap;
                   1729:
                   1730:        va_start(ap, fmt);
1.16      mrg      1731:        if (httpd->logstderr || isatty(STDERR_FILENO)) {
1.14      mrg      1732:                //fputs("error: ", stderr);
1.1       tls      1733:                vfprintf(stderr, fmt, ap);
                   1734:                fputs("\n", stderr);
                   1735:        } else
                   1736:                vsyslog(LOG_ERR, fmt, ap);
                   1737:        va_end(ap);
                   1738:        exit(code);
                   1739: }
                   1740:
1.40      mrg      1741: /*
                   1742:  * this escapes HTML tags.  returns allocated escaped
                   1743:  * string if needed, or NULL on allocation failure or
                   1744:  * lack of escape need.
                   1745:  * call with NULL httpd in error paths, to avoid recursive
                   1746:  * malloc failure.  call with valid httpd in normal paths
                   1747:  * to get automatic allocation failure handling.
                   1748:  */
                   1749: char *
                   1750: bozo_escape_html(bozohttpd_t *httpd, const char *url)
1.1       tls      1751: {
1.16      mrg      1752:        int     i, j;
1.40      mrg      1753:        char    *tmp;
                   1754:        size_t  len;
1.1       tls      1755:
1.16      mrg      1756:        for (i = 0, j = 0; url[i]; i++) {
                   1757:                switch (url[i]) {
                   1758:                case '<':
                   1759:                case '>':
                   1760:                        j += 4;
                   1761:                        break;
                   1762:                case '&':
                   1763:                        j += 5;
                   1764:                        break;
                   1765:                }
1.12      mrg      1766:        }
1.1       tls      1767:
1.16      mrg      1768:        if (j == 0)
1.40      mrg      1769:                return NULL;
1.16      mrg      1770:
1.40      mrg      1771:        /*
                   1772:         * we need to handle being called from different
                   1773:         * pathnames.
                   1774:         */
                   1775:        len = strlen(url) + j;
                   1776:        if (httpd)
                   1777:                tmp = bozomalloc(httpd, len);
                   1778:        else if ((tmp = malloc(len)) == 0)
                   1779:                        return NULL;
1.1       tls      1780:
1.16      mrg      1781:        for (i = 0, j = 0; url[i]; i++) {
                   1782:                switch (url[i]) {
                   1783:                case '<':
                   1784:                        memcpy(tmp + j, "&lt;", 4);
                   1785:                        j += 4;
                   1786:                        break;
                   1787:                case '>':
                   1788:                        memcpy(tmp + j, "&gt;", 4);
                   1789:                        j += 4;
                   1790:                        break;
                   1791:                case '&':
                   1792:                        memcpy(tmp + j, "&amp;", 5);
                   1793:                        j += 5;
                   1794:                        break;
                   1795:                default:
                   1796:                        tmp[j++] = url[i];
1.12      mrg      1797:                }
1.16      mrg      1798:        }
                   1799:        tmp[j] = 0;
1.1       tls      1800:
1.40      mrg      1801:        return tmp;
1.1       tls      1802: }
                   1803:
                   1804: /* short map between error code, and short/long messages */
                   1805: static struct errors_map {
                   1806:        int     code;                   /* HTTP return code */
                   1807:        const char *shortmsg;           /* short version of message */
                   1808:        const char *longmsg;            /* long version of message */
                   1809: } errors_map[] = {
                   1810:        { 400,  "400 Bad Request",      "The request was not valid", },
                   1811:        { 401,  "401 Unauthorized",     "No authorization", },
1.6       mrg      1812:        { 403,  "403 Forbidden",        "Access to this item has been denied",},
1.1       tls      1813:        { 404,  "404 Not Found",        "This item has not been found", },
                   1814:        { 408,  "408 Request Timeout",  "This request took too long", },
                   1815:        { 417,  "417 Expectation Failed","Expectations not available", },
                   1816:        { 500,  "500 Internal Error",   "An error occured on the server", },
                   1817:        { 501,  "501 Not Implemented",  "This request is not available", },
                   1818:        { 0,    NULL,                   NULL, },
                   1819: };
                   1820:
                   1821: static const char *help = "DANGER! WILL ROBINSON! DANGER!";
                   1822:
                   1823: static const char *
                   1824: http_errors_short(int code)
                   1825: {
                   1826:        struct errors_map *ep;
                   1827:
                   1828:        for (ep = errors_map; ep->code; ep++)
                   1829:                if (ep->code == code)
                   1830:                        return (ep->shortmsg);
                   1831:        return (help);
                   1832: }
                   1833:
                   1834: static const char *
                   1835: http_errors_long(int code)
                   1836: {
                   1837:        struct errors_map *ep;
                   1838:
                   1839:        for (ep = errors_map; ep->code; ep++)
                   1840:                if (ep->code == code)
                   1841:                        return (ep->longmsg);
                   1842:        return (help);
                   1843: }
                   1844:
1.16      mrg      1845: /* the follow functions and variables are used in handling HTTP errors */
                   1846: /* ARGSUSED */
                   1847: int
                   1848: bozo_http_error(bozohttpd_t *httpd, int code, bozo_httpreq_t *request,
                   1849:                const char *msg)
                   1850: {
                   1851:        char portbuf[20];
                   1852:        const char *header = http_errors_short(code);
                   1853:        const char *reason = http_errors_long(code);
                   1854:        const char *proto = (request && request->hr_proto) ?
                   1855:                                request->hr_proto : httpd->consts.http_11;
                   1856:        int     size;
                   1857:
                   1858:        debug((httpd, DEBUG_FAT, "bozo_http_error %d: %s", code, msg));
                   1859:        if (header == NULL || reason == NULL) {
                   1860:                bozo_err(httpd, 1,
                   1861:                        "bozo_http_error() failed (short = %p, long = %p)",
                   1862:                        header, reason);
                   1863:                return code;
                   1864:        }
                   1865:
                   1866:        if (request && request->hr_serverport &&
                   1867:            strcmp(request->hr_serverport, "80") != 0)
                   1868:                snprintf(portbuf, sizeof(portbuf), ":%s",
                   1869:                                request->hr_serverport);
                   1870:        else
                   1871:                portbuf[0] = '\0';
                   1872:
                   1873:        if (request && request->hr_file) {
1.40      mrg      1874:                char *file = NULL;
1.46    ! mrg      1875:                const char *hostname = BOZOHOST(httpd, request);
1.40      mrg      1876:
                   1877:                /* bozo_escape_html() failure here is just too bad. */
                   1878:                file = bozo_escape_html(NULL, request->hr_file);
                   1879:                if (file == NULL)
                   1880:                        file = request->hr_file;
1.16      mrg      1881:                size = snprintf(httpd->errorbuf, BUFSIZ,
                   1882:                    "<html><head><title>%s</title></head>\n"
                   1883:                    "<body><h1>%s</h1>\n"
                   1884:                    "%s: <pre>%s</pre>\n"
                   1885:                    "<hr><address><a href=\"http://%s%s/\">%s%s</a></address>\n"
                   1886:                    "</body></html>\n",
1.40      mrg      1887:                    header, header, file, reason,
1.35      martin   1888:                    hostname, portbuf, hostname, portbuf);
1.16      mrg      1889:                if (size >= (int)BUFSIZ) {
                   1890:                        bozo_warn(httpd,
                   1891:                                "bozo_http_error buffer too small, truncated");
                   1892:                        size = (int)BUFSIZ;
                   1893:                }
                   1894:        } else
                   1895:                size = 0;
                   1896:
                   1897:        bozo_printf(httpd, "%s %s\r\n", proto, header);
1.26      pooka    1898:        if (request)
                   1899:                bozo_auth_check_401(request, code);
1.16      mrg      1900:
                   1901:        bozo_printf(httpd, "Content-Type: text/html\r\n");
                   1902:        bozo_printf(httpd, "Content-Length: %d\r\n", size);
                   1903:        bozo_printf(httpd, "Server: %s\r\n", httpd->server_software);
                   1904:        if (request && request->hr_allow)
                   1905:                bozo_printf(httpd, "Allow: %s\r\n", request->hr_allow);
                   1906:        bozo_printf(httpd, "\r\n");
                   1907:        if (size)
                   1908:                bozo_printf(httpd, "%s", httpd->errorbuf);
                   1909:        bozo_flush(httpd, stdout);
                   1910:
                   1911:        return code;
                   1912: }
                   1913:
1.1       tls      1914: /* Below are various modified libc functions */
                   1915:
                   1916: /*
                   1917:  * returns -1 in lenp if the string ran out before finding a delimiter,
                   1918:  * but is otherwise the same as strsep.  Note that the length must be
                   1919:  * correctly passed in.
                   1920:  */
                   1921: char *
1.6       mrg      1922: bozostrnsep(char **strp, const char *delim, ssize_t    *lenp)
1.1       tls      1923: {
                   1924:        char    *s;
                   1925:        const   char *spanp;
                   1926:        int     c, sc;
                   1927:        char    *tok;
                   1928:
                   1929:        if ((s = *strp) == NULL)
                   1930:                return (NULL);
                   1931:        for (tok = s;;) {
                   1932:                if (lenp && --(*lenp) == -1)
                   1933:                        return (NULL);
                   1934:                c = *s++;
                   1935:                spanp = delim;
                   1936:                do {
                   1937:                        if ((sc = *spanp++) == c) {
                   1938:                                if (c == 0)
                   1939:                                        s = NULL;
                   1940:                                else
                   1941:                                        s[-1] = '\0';
                   1942:                                *strp = s;
                   1943:                                return (tok);
                   1944:                        }
                   1945:                } while (sc != 0);
                   1946:        }
                   1947:        /* NOTREACHED */
                   1948: }
                   1949:
                   1950: /*
                   1951:  * inspired by fgetln(3), but works for fd's.  should work identically
                   1952:  * except it, however, does *not* return the newline, and it does nul
                   1953:  * terminate the string.
                   1954:  */
                   1955: char *
1.16      mrg      1956: bozodgetln(bozohttpd_t *httpd, int fd, ssize_t *lenp,
                   1957:        ssize_t (*readfn)(bozohttpd_t *, int, void *, size_t))
1.1       tls      1958: {
                   1959:        ssize_t len;
                   1960:        int     got_cr = 0;
                   1961:        char    c, *nbuffer;
                   1962:
                   1963:        /* initialise */
1.16      mrg      1964:        if (httpd->getln_buflen == 0) {
                   1965:                /* should be plenty for most requests */
                   1966:                httpd->getln_buflen = 128;
                   1967:                httpd->getln_buffer = malloc((size_t)httpd->getln_buflen);
                   1968:                if (httpd->getln_buffer == NULL) {
                   1969:                        httpd->getln_buflen = 0;
1.1       tls      1970:                        return NULL;
                   1971:                }
                   1972:        }
                   1973:        len = 0;
                   1974:
                   1975:        /*
                   1976:         * we *have* to read one byte at a time, to not break cgi
                   1977:         * programs (for we pass stdin off to them).  could fix this
                   1978:         * by becoming a fd-passing program instead of just exec'ing
                   1979:         * the program
1.17      mrg      1980:         *
                   1981:         * the above is no longer true, we are the fd-passing
                   1982:         * program already.
1.1       tls      1983:         */
1.16      mrg      1984:        for (; readfn(httpd, fd, &c, 1) == 1; ) {
                   1985:                debug((httpd, DEBUG_EXPLODING, "bozodgetln read %c", c));
1.1       tls      1986:
1.16      mrg      1987:                if (len >= httpd->getln_buflen - 1) {
                   1988:                        httpd->getln_buflen *= 2;
                   1989:                        debug((httpd, DEBUG_EXPLODING, "bozodgetln: "
                   1990:                                "reallocating buffer to buflen %zu",
                   1991:                                httpd->getln_buflen));
                   1992:                        nbuffer = bozorealloc(httpd, httpd->getln_buffer,
                   1993:                                (size_t)httpd->getln_buflen);
                   1994:                        httpd->getln_buffer = nbuffer;
1.1       tls      1995:                }
                   1996:
1.16      mrg      1997:                httpd->getln_buffer[len++] = c;
1.1       tls      1998:                if (c == '\r') {
                   1999:                        got_cr = 1;
                   2000:                        continue;
                   2001:                } else if (c == '\n') {
                   2002:                        /*
                   2003:                         * HTTP/1.1 spec says to ignore CR and treat
                   2004:                         * LF as the real line terminator.  even though
                   2005:                         * the same spec defines CRLF as the line
                   2006:                         * terminator, it is recommended in section 19.3
                   2007:                         * to do the LF trick for tolerance.
                   2008:                         */
                   2009:                        if (got_cr)
                   2010:                                len -= 2;
                   2011:                        else
                   2012:                                len -= 1;
                   2013:                        break;
                   2014:                }
                   2015:
                   2016:        }
1.16      mrg      2017:        httpd->getln_buffer[len] = '\0';
1.28      joerg    2018:        debug((httpd, DEBUG_OBESE, "bozodgetln returns: ``%s'' with len %zd",
1.16      mrg      2019:               httpd->getln_buffer, len));
1.1       tls      2020:        *lenp = len;
1.16      mrg      2021:        return httpd->getln_buffer;
1.1       tls      2022: }
                   2023:
                   2024: void *
1.16      mrg      2025: bozorealloc(bozohttpd_t *httpd, void *ptr, size_t size)
1.1       tls      2026: {
                   2027:        void    *p;
                   2028:
                   2029:        p = realloc(ptr, size);
1.12      mrg      2030:        if (p == NULL) {
1.16      mrg      2031:                (void)bozo_http_error(httpd, 500, NULL,
                   2032:                                "memory allocation failure");
1.12      mrg      2033:                exit(1);
                   2034:        }
1.1       tls      2035:        return (p);
                   2036: }
                   2037:
                   2038: void *
1.16      mrg      2039: bozomalloc(bozohttpd_t *httpd, size_t size)
1.1       tls      2040: {
                   2041:        void    *p;
                   2042:
                   2043:        p = malloc(size);
1.12      mrg      2044:        if (p == NULL) {
1.16      mrg      2045:                (void)bozo_http_error(httpd, 500, NULL,
                   2046:                                "memory allocation failure");
1.12      mrg      2047:                exit(1);
                   2048:        }
1.1       tls      2049:        return (p);
                   2050: }
                   2051:
                   2052: char *
1.16      mrg      2053: bozostrdup(bozohttpd_t *httpd, const char *str)
1.1       tls      2054: {
                   2055:        char    *p;
                   2056:
                   2057:        p = strdup(str);
1.12      mrg      2058:        if (p == NULL) {
1.16      mrg      2059:                (void)bozo_http_error(httpd, 500, NULL,
                   2060:                                        "memory allocation failure");
1.12      mrg      2061:                exit(1);
                   2062:        }
1.1       tls      2063:        return (p);
                   2064: }
1.16      mrg      2065:
                   2066: /* set default values in bozohttpd_t struct */
                   2067: int
                   2068: bozo_init_httpd(bozohttpd_t *httpd)
                   2069: {
                   2070:        /* make sure everything is clean */
                   2071:        (void) memset(httpd, 0x0, sizeof(*httpd));
                   2072:
                   2073:        /* constants */
                   2074:        httpd->consts.http_09 = "HTTP/0.9";
                   2075:        httpd->consts.http_10 = "HTTP/1.0";
                   2076:        httpd->consts.http_11 = "HTTP/1.1";
                   2077:        httpd->consts.text_plain = "text/plain";
                   2078:
                   2079:        /* mmap region size */
                   2080:        httpd->mmapsz = BOZO_MMAPSZ;
                   2081:
                   2082:        /* error buffer for bozo_http_error() */
                   2083:        if ((httpd->errorbuf = malloc(BUFSIZ)) == NULL) {
                   2084:                (void) fprintf(stderr,
                   2085:                        "bozohttpd: memory_allocation failure\n");
                   2086:                return 0;
                   2087:        }
1.43      mbalmer  2088: #ifndef NO_LUA_SUPPORT
                   2089:        SIMPLEQ_INIT(&httpd->lua_states);
                   2090: #endif
1.16      mrg      2091:        return 1;
                   2092: }
                   2093:
                   2094: /* set default values in bozoprefs_t struct */
                   2095: int
                   2096: bozo_init_prefs(bozoprefs_t *prefs)
                   2097: {
                   2098:        /* make sure everything is clean */
                   2099:        (void) memset(prefs, 0x0, sizeof(*prefs));
                   2100:
                   2101:        /* set up default values */
                   2102:        bozo_set_pref(prefs, "server software", SERVER_SOFTWARE);
                   2103:        bozo_set_pref(prefs, "index.html", INDEX_HTML);
                   2104:        bozo_set_pref(prefs, "public_html", PUBLIC_HTML);
                   2105:
                   2106:        return 1;
                   2107: }
                   2108:
                   2109: /* set default values */
                   2110: int
                   2111: bozo_set_defaults(bozohttpd_t *httpd, bozoprefs_t *prefs)
                   2112: {
                   2113:        return bozo_init_httpd(httpd) && bozo_init_prefs(prefs);
                   2114: }
                   2115:
                   2116: /* set the virtual host name, port and root */
                   2117: int
                   2118: bozo_setup(bozohttpd_t *httpd, bozoprefs_t *prefs, const char *vhost,
                   2119:                const char *root)
                   2120: {
                   2121:        struct passwd    *pw;
                   2122:        extern char     **environ;
1.21      mrg      2123:        static char      *cleanenv[1] = { NULL };
1.16      mrg      2124:        uid_t             uid;
                   2125:        char             *chrootdir;
                   2126:        char             *username;
                   2127:        char             *portnum;
                   2128:        char             *cp;
                   2129:        int               dirtyenv;
                   2130:
                   2131:        dirtyenv = 0;
                   2132:
                   2133:        if (vhost == NULL) {
                   2134:                httpd->virthostname = bozomalloc(httpd, MAXHOSTNAMELEN+1);
                   2135:                /* XXX we do not check for FQDN here */
                   2136:                if (gethostname(httpd->virthostname, MAXHOSTNAMELEN+1) < 0)
                   2137:                        bozo_err(httpd, 1, "gethostname");
                   2138:                httpd->virthostname[MAXHOSTNAMELEN] = '\0';
                   2139:        } else {
                   2140:                httpd->virthostname = strdup(vhost);
                   2141:        }
                   2142:        httpd->slashdir = strdup(root);
                   2143:        if ((portnum = bozo_get_pref(prefs, "port number")) != NULL) {
                   2144:                httpd->bindport = strdup(portnum);
                   2145:        }
                   2146:
                   2147:        /* go over preferences now */
                   2148:        if ((cp = bozo_get_pref(prefs, "numeric")) != NULL &&
                   2149:            strcmp(cp, "true") == 0) {
                   2150:                httpd->numeric = 1;
                   2151:        }
                   2152:        if ((cp = bozo_get_pref(prefs, "trusted referal")) != NULL &&
                   2153:            strcmp(cp, "true") == 0) {
                   2154:                httpd->untrustedref = 1;
                   2155:        }
                   2156:        if ((cp = bozo_get_pref(prefs, "log to stderr")) != NULL &&
                   2157:            strcmp(cp, "true") == 0) {
                   2158:                httpd->logstderr = 1;
                   2159:        }
                   2160:        if ((cp = bozo_get_pref(prefs, "bind address")) != NULL) {
                   2161:                httpd->bindaddress = strdup(cp);
                   2162:        }
                   2163:        if ((cp = bozo_get_pref(prefs, "background")) != NULL) {
                   2164:                httpd->background = atoi(cp);
                   2165:        }
                   2166:        if ((cp = bozo_get_pref(prefs, "foreground")) != NULL &&
                   2167:            strcmp(cp, "true") == 0) {
                   2168:                httpd->foreground = 1;
                   2169:        }
1.27      jmmv     2170:        if ((cp = bozo_get_pref(prefs, "pid file")) != NULL) {
                   2171:                httpd->pidfile = strdup(cp);
                   2172:        }
1.16      mrg      2173:        if ((cp = bozo_get_pref(prefs, "unknown slash")) != NULL &&
                   2174:            strcmp(cp, "true") == 0) {
                   2175:                httpd->unknown_slash = 1;
                   2176:        }
                   2177:        if ((cp = bozo_get_pref(prefs, "virtual base")) != NULL) {
                   2178:                httpd->virtbase = strdup(cp);
                   2179:        }
                   2180:        if ((cp = bozo_get_pref(prefs, "enable users")) != NULL &&
                   2181:            strcmp(cp, "true") == 0) {
                   2182:                httpd->enable_users = 1;
                   2183:        }
                   2184:        if ((cp = bozo_get_pref(prefs, "dirty environment")) != NULL &&
                   2185:            strcmp(cp, "true") == 0) {
                   2186:                dirtyenv = 1;
                   2187:        }
                   2188:        if ((cp = bozo_get_pref(prefs, "hide dots")) != NULL &&
                   2189:            strcmp(cp, "true") == 0) {
                   2190:                httpd->hide_dots = 1;
                   2191:        }
                   2192:        if ((cp = bozo_get_pref(prefs, "directory indexing")) != NULL &&
                   2193:            strcmp(cp, "true") == 0) {
                   2194:                httpd->dir_indexing = 1;
                   2195:        }
1.20      mrg      2196:        if ((cp = bozo_get_pref(prefs, "public_html")) != NULL) {
                   2197:                httpd->public_html = strdup(cp);
                   2198:        }
1.16      mrg      2199:        httpd->server_software =
                   2200:                        strdup(bozo_get_pref(prefs, "server software"));
                   2201:        httpd->index_html = strdup(bozo_get_pref(prefs, "index.html"));
                   2202:
                   2203:        /*
                   2204:         * initialise ssl and daemon mode if necessary.
                   2205:         */
                   2206:        bozo_ssl_init(httpd);
                   2207:        bozo_daemon_init(httpd);
                   2208:
                   2209:        if ((username = bozo_get_pref(prefs, "username")) == NULL) {
                   2210:                if ((pw = getpwuid(uid = 0)) == NULL)
                   2211:                        bozo_err(httpd, 1, "getpwuid(0): %s", strerror(errno));
                   2212:                httpd->username = strdup(pw->pw_name);
                   2213:        } else {
                   2214:                httpd->username = strdup(username);
                   2215:                if ((pw = getpwnam(httpd->username)) == NULL)
                   2216:                        bozo_err(httpd, 1, "getpwnam(%s): %s", httpd->username,
                   2217:                                        strerror(errno));
                   2218:                if (initgroups(pw->pw_name, pw->pw_gid) == -1)
                   2219:                        bozo_err(httpd, 1, "initgroups: %s", strerror(errno));
                   2220:                if (setgid(pw->pw_gid) == -1)
                   2221:                        bozo_err(httpd, 1, "setgid(%u): %s", pw->pw_gid,
                   2222:                                        strerror(errno));
                   2223:                uid = pw->pw_uid;
                   2224:        }
                   2225:        /*
                   2226:         * handle chroot.
                   2227:         */
                   2228:        if ((chrootdir = bozo_get_pref(prefs, "chroot dir")) != NULL) {
                   2229:                httpd->rootdir = strdup(chrootdir);
                   2230:                if (chdir(httpd->rootdir) == -1)
                   2231:                        bozo_err(httpd, 1, "chdir(%s): %s", httpd->rootdir,
                   2232:                                strerror(errno));
                   2233:                if (chroot(httpd->rootdir) == -1)
                   2234:                        bozo_err(httpd, 1, "chroot(%s): %s", httpd->rootdir,
                   2235:                                strerror(errno));
                   2236:        }
                   2237:
                   2238:        if (username != NULL)
                   2239:                if (setuid(uid) == -1)
                   2240:                        bozo_err(httpd, 1, "setuid(%d): %s", uid,
                   2241:                                        strerror(errno));
                   2242:
                   2243:        /*
                   2244:         * prevent info leakage between different compartments.
                   2245:         * some PATH values in the environment would be invalided
                   2246:         * by chroot. cross-user settings might result in undesirable
                   2247:         * effects.
                   2248:         */
1.21      mrg      2249:        if ((chrootdir != NULL || username != NULL) && !dirtyenv)
1.16      mrg      2250:                environ = cleanenv;
1.21      mrg      2251:
1.16      mrg      2252: #ifdef _SC_PAGESIZE
                   2253:        httpd->page_size = (long)sysconf(_SC_PAGESIZE);
                   2254: #else
                   2255:        httpd->page_size = 4096;
                   2256: #endif
                   2257:        debug((httpd, DEBUG_OBESE, "myname is %s, slashdir is %s",
                   2258:                        httpd->virthostname, httpd->slashdir));
                   2259:
                   2260:        return 1;
                   2261: }

CVSweb <webmaster@jp.NetBSD.org>