[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.38

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

CVSweb <webmaster@jp.NetBSD.org>