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

1.8     ! lukem       1: /*     $NetBSD: bozohttpd.c,v 1.7 2008/03/07 18:20:20 mrg Exp $        */
1.3       tls         2:
1.6       mrg         3: /*     $eterna: bozohttpd.c,v 1.142 2008/03/03 03:36:11 mrg Exp $      */
1.1       tls         4:
                      5: /*
1.6       mrg         6:  * Copyright (c) 1997-2008 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:  * 3. The name of the author may not be used to endorse or promote products
                     19:  *    derived from this software without specific prior written permission.
                     20:  *
                     21:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
                     22:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
                     23:  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
                     24:  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
                     25:  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
                     26:  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
                     27:  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
                     28:  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
                     29:  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     30:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     31:  * SUCH DAMAGE.
                     32:  *
                     33:  */
                     34:
                     35: /* this program is dedicated to the Great God of Processed Cheese */
                     36:
                     37: /*
                     38:  * bozohttpd.c:  minimal httpd; provides only these features:
                     39:  *     - HTTP/0.9 (by virtue of ..)
                     40:  *     - HTTP/1.0
                     41:  *     - HTTP/1.1
                     42:  *     - CGI/1.1 this will only be provided for "system" scripts
                     43:  *     - automatic "missing trailing slash" redirections
                     44:  *     - configurable translation of /~user/ to ~user/public_html,
                     45:  *       however, this does not include cgi-bin support
                     46:  *     - access lists via libwrap via inetd/tcpd
                     47:  *     - virtual hosting
                     48:  *     - not that we do not even pretend to understand MIME, but
                     49:  *       rely only on the HTTP specification
                     50:  *     - ipv6 support
                     51:  *     - automatic `index.html' generation
                     52:  *     - configurable server name
                     53:  *     - directory index generation
                     54:  *     - daemon mode (lacks libwrap support)
                     55:  *     - .htpasswd support
                     56:  */
                     57:
                     58: /*
                     59:  * requirements for minimal http/1.1 (at least, as documented in
                     60:  * <draft-ietf-http-v11-spec-rev-06> which expired may 18, 1999):
                     61:  *
                     62:  *     - 14.15: content-encoding handling. [1]
                     63:  *
                     64:  *     - 14.16: content-length handling.  this is only a SHOULD header
                     65:  *       thus we could just not send it ever.  [1]
                     66:  *
                     67:  *     - 14.17: content-type handling. [1]
                     68:  *
                     69:  *     - 14.25/28: if-{,un}modified-since handling.  maybe do this, but
                     70:  *       i really don't want to have to parse 3 differnet date formats
                     71:  *
                     72:  * [1] need to revisit to ensure proper behaviour
                     73:  *
                     74:  * and the following is a list of features that we do not need
                     75:  * to have due to other limits, or are too lazy.  there are more
                     76:  * of these than are listed, but these are of particular note,
                     77:  * and could perhaps be implemented.
                     78:  *
                     79:  *     - 3.5/3.6: content/transfer codings.  probably can ignore
                     80:  *       this?  we "SHOULD"n't.  but 4.4 says we should ignore a
                     81:  *       `content-length' header upon reciept of a `transfer-encoding'
                     82:  *       header.
                     83:  *
                     84:  *     - 5.1.1: request methods.  only MUST support GET and HEAD,
                     85:  *       but there are new ones besides POST that are currently
                     86:  *       supported: OPTIONS PUT DELETE TRACE and CONNECT, plus
                     87:  *       extensions not yet known?
                     88:  *
                     89:  *     - 10.1: we can ignore informational status codes
                     90:  *
                     91:  *     - 10.3.3/10.3.4/10.3.8:  just use '302' codes always.
                     92:  *
                     93:  *     - 14.1/14.2/14.3/14.27: we do not support Accept: headers..
                     94:  *       just ignore them and send the request anyway.  they are
                     95:  *       only SHOULD.
                     96:  *
                     97:  *     - 14.5/14.16/14.35: we don't do ranges.  from section 14.35.2
                     98:  *       `A server MAY ignore the Range header'.  but it might be nice.
1.6       mrg        99:  *       since 20080301 we support simple range headers.
1.1       tls       100:  *
                    101:  *     - 14.9: we aren't a cache.
                    102:  *
                    103:  *     - 14.15: content-md5 would be nice...
                    104:  *
                    105:  *     - 14.24/14.26/14.27: be nice to support this...
                    106:  *
                    107:  *     - 14.44: not sure about this Vary: header.  ignore it for now.
                    108:  */
                    109:
                    110: #ifndef INDEX_HTML
                    111: #define INDEX_HTML             "index.html"
                    112: #endif
                    113: #ifndef SERVER_SOFTWARE
1.6       mrg       114: #define SERVER_SOFTWARE                "bozohttpd/20080303"
1.1       tls       115: #endif
                    116: #ifndef DIRECT_ACCESS_FILE
                    117: #define DIRECT_ACCESS_FILE     ".bzdirect"
                    118: #endif
                    119: #ifndef REDIRECT_FILE
                    120: #define REDIRECT_FILE          ".bzredirect"
                    121: #endif
                    122: #ifndef ABSREDIRECT_FILE
                    123: #define ABSREDIRECT_FILE       ".bzabsredirect"
                    124: #endif
                    125:
                    126: /*
                    127:  * And so it begins ..
                    128:  */
                    129:
                    130: #include <sys/param.h>
                    131: #include <sys/socket.h>
                    132: #include <sys/time.h>
                    133: #include <sys/mman.h>
                    134:
                    135: #include <arpa/inet.h>
                    136:
                    137: #include <ctype.h>
                    138: #include <dirent.h>
                    139: #include <errno.h>
                    140: #include <fcntl.h>
                    141: #include <netdb.h>
                    142: #include <pwd.h>
                    143: #include <grp.h>
                    144: #include <signal.h>
                    145: #include <stdarg.h>
                    146: #include <stdlib.h>
                    147: #include <string.h>
                    148: #include <syslog.h>
                    149: #include <time.h>
                    150: #include <unistd.h>
                    151:
                    152: #ifndef __attribute__
                    153: #define __attribute__(x)
                    154: #endif /* __attribute__ */
                    155:
                    156: #include "bozohttpd.h"
                    157:
                    158: #ifndef MAX_WAIT_TIME
                    159: #define        MAX_WAIT_TIME   60      /* hang around for 60 seconds max */
                    160: #endif
                    161:
                    162: /* variables and functions */
                    163:
                    164:        int     bflag;          /* background; drop into daemon mode */
                    165:        int     fflag;          /* keep daemon mode in foreground */
                    166: static int     eflag;          /* don't clean environ; -t/-U only */
                    167:        const char *Iflag = "http";/* bind port; default "http" */
                    168:        int     Iflag_set;
                    169:        int     dflag = 0;      /* debugging level */
                    170:        char    *myname;        /* my name */
                    171:
                    172: #ifndef LOG_FTP
                    173: #define LOG_FTP LOG_DAEMON
                    174: #endif
                    175:
                    176: static char    *tflag;         /* root directory */
                    177: static char    *Uflag;         /* user name to switch to */
                    178: static int     Vflag;          /* unknown vhosts go to normal slashdir */
                    179: static int     nflag;          /* avoid gethostby*() */
                    180: static int     rflag;          /* make sure referrer = me unless url = / */
                    181: static int     sflag;          /* log to stderr even if it is not a TTY */
                    182: static char    *vpath;         /* virtual directory base */
                    183:
                    184:        char    *slashdir;      /* www slash directory */
                    185:
                    186:        const char *server_software = SERVER_SOFTWARE;
                    187:        const char *index_html = INDEX_HTML;
                    188:        const char http_09[] = "HTTP/0.9";
                    189:        const char http_10[] = "HTTP/1.0";
                    190:        const char http_11[] = "HTTP/1.1";
                    191:        const char text_plain[] = "text/plain";
                    192:
                    193: static void    usage(void);
                    194: static void    alarmer(int);
                    195: volatile sig_atomic_t  alarmhit;
                    196:
                    197: static void    parse_request(char *, char **, char **, char **);
                    198: static http_req *read_request(void);
                    199: static struct headers *addmerge_header(http_req *request, char *val,
                    200:                                        char *str, ssize_t len);
                    201: static void    process_request(http_req *);
                    202: static int     check_direct_access(http_req *request);
                    203: static char    *transform_request(http_req *, int *);
                    204: static void    handle_redirect(http_req *, const char *, int);
                    205:
                    206: static void    check_virtual(http_req *);
                    207: static void    check_bzredirect(http_req *);
                    208: static void    fix_url_percent(http_req *);
                    209: static void    process_method(http_req *, const char *);
                    210: static void    process_proto(http_req *, const char *);
                    211: static void    escape_html(http_req *);
                    212:
                    213: static const char *http_errors_short(int);
                    214: static const char *http_errors_long(int);
                    215:
                    216:
                    217: void   *bozomalloc(size_t);
                    218: void   *bozorealloc(void *, size_t);
                    219: char   *bozostrdup(const char *);
                    220:
                    221: /* bozotic io */
                    222: int    (*bozoprintf)(const char *, ...) = printf;
                    223: ssize_t        (*bozoread)(int, void *, size_t) = read;
                    224: ssize_t        (*bozowrite)(int, const void *, size_t) = write;
                    225: int    (*bozoflush)(FILE *) = fflush;
                    226:
                    227:        char    *progname;
                    228:
                    229:        int     main(int, char **);
                    230:
                    231: static void
                    232: usage(void)
                    233: {
                    234:        warning("usage: %s [options] slashdir [myname]", progname);
                    235:        warning("options:");
                    236: #ifdef DEBUG
                    237:        warning("   -d\t\t\tenable debug support");
                    238: #endif
                    239:        warning("   -s\t\t\talways log to stderr");
                    240: #ifndef NO_USER_SUPPORT
                    241:        warning("   -u\t\t\tenable ~user/public_html support");
                    242:        warning("   -p dir\t\tchange `public_html' directory name]");
                    243: #endif
                    244: #ifndef NO_DYNAMIC_CONTENT
                    245:        warning("   -M arg t c c11\tadd this mime extenstion");
                    246: #endif
                    247: #ifndef NO_CGIBIN_SUPPORT
                    248: #ifndef NO_DYNAMIC_CONTENT
                    249:        warning("   -C arg prog\t\tadd this CGI handler");
                    250: #endif
                    251:        warning("   -c cgibin\t\tenable cgi-bin support in this directory");
                    252: #endif
                    253: #ifndef NO_DAEMON_MODE
                    254:        warning("   -b\t\t\tbackground and go into daemon mode");
                    255:        warning("   -f\t\t\tkeep daemon mode in the foreground");
                    256:        warning("   -i address\t\tbind on this address (daemon mode only)");
                    257:        warning("   -I port\t\tbind on this port (daemon mode only)");
                    258: #endif
                    259:        warning("   -S version\t\tset server version string");
                    260:        warning("   -t dir\t\tchroot to `dir'");
                    261:        warning("   -U username\t\tchange user to `user'");
                    262:        warning("   -e\t\t\tdon't clean the environment (-t and -U only)");
                    263:        warning("   -v virtualroot\tenable virtual host support in this directory");
                    264:        warning("   -r\t\t\tmake sure sub-pages come from this host via referrer");
                    265: #ifndef NO_DIRINDEX_SUPPORT
                    266:        warning("   -X\t\t\tenable automatic directory index support");
                    267:        warning("   -H\t\t\thide files starting with a period (.) in index mode");
                    268: #endif
                    269:        warning("   -x index\t\tchange default `index.html' file name");
                    270: #ifndef NO_SSL_SUPPORT
                    271:        warning("   -Z cert privkey\tspecify path to server certificate and private key file\n"
                    272:                "\t\t\tin pem format and enable bozohttpd in SSL mode");
                    273: #endif /* NO_SSL_SUPPORT */
                    274:        error(1, "%s failed to start", progname);
                    275: }
                    276:
                    277: int
                    278: main(int argc, char **argv)
                    279: {
                    280:        http_req *http_request;
                    281:        extern  char **environ;
                    282:        char    *cleanenv[1];
                    283:        uid_t   uid;
                    284:        int     c;
                    285:
                    286:        uid = 0;        /* XXX gcc */
                    287:
                    288:        if ((progname = strrchr(argv[0], '/')) != NULL)
                    289:                progname++;
                    290:        else
                    291:                progname = argv[0];
                    292:
                    293:        openlog(progname, LOG_PID|LOG_NDELAY, LOG_FTP);
                    294:
                    295:        while ((c = getopt(argc, argv,
                    296:                           "C:HI:M:S:U:VXZ:bc:defhi:np:rst:uv:x:z:")) != -1) {
                    297:                switch(c) {
                    298:
                    299:                case 'M':
                    300: #ifndef NO_DYNAMIC_CONTENT
                    301:                        /* make sure there's four arguments */
                    302:                        if (argc - optind < 3)
                    303:                                usage();
                    304:                        add_content_map_mime(optarg, argv[optind],
                    305:                            argv[optind+1], argv[optind+2]);
                    306:                        optind += 3;
                    307:                        break;
                    308: #else
                    309:                        error(1, "dynmic mime content support is not enabled");
                    310:                        /* NOTREACHED */
                    311: #endif /* NO_DYNAMIC_CONTENT */
                    312:
                    313:                case 'n':
                    314:                        nflag = 1;
                    315:                        break;
                    316:
                    317:                case 'r':
                    318:                        rflag = 1;
                    319:                        break;
                    320:
                    321:                case 's':
                    322:                        sflag = 1;
                    323:                        break;
                    324:
                    325:                case 'S':
                    326:                        server_software = optarg;
                    327:                        break;
                    328:                case 'Z':
                    329: #ifndef NO_SSL_SUPPORT
                    330:                        /* make sure there's two arguments */
                    331:                        if (argc - optind < 1)
                    332:                                usage();
                    333:                        ssl_set_opts(optarg, argv[optind++]);
                    334:                        break;
                    335: #else
                    336:                        error(1, "ssl support is not enabled");
                    337:                        /* NOT REACHED */
                    338: #endif /* NO_SSL_SUPPORT */
                    339:                case 'U':
                    340:                        Uflag = optarg;
                    341:                        break;
                    342:
                    343:                case 'V':
                    344:                        Vflag = 1;
                    345:                        break;
                    346:
                    347:                case 'v':
                    348:                        vpath = optarg;
                    349:                        break;
                    350:
                    351:                case 'x':
                    352:                        index_html = optarg;
                    353:                        break;
                    354:
                    355: #ifndef NO_DAEMON_MODE
                    356:                case 'b':
                    357:                        bflag = 1;
                    358:                        break;
                    359:
                    360:                case 'e':
                    361:                        eflag = 1;
                    362:                        break;
                    363:
                    364:                case 'f':
                    365:                        fflag = 1;
                    366:                        break;
                    367:
                    368:                case 'i':
                    369:                        iflag = optarg;
                    370:                        break;
                    371:
                    372:                case 'I':
                    373:                        Iflag_set = 1;
                    374:                        Iflag = optarg;
                    375:                        break;
                    376: #else /* NO_DAEMON_MODE */
                    377:                case 'b':
1.6       mrg       378:                case 'e':
                    379:                case 'f':
1.1       tls       380:                case 'i':
                    381:                case 'I':
                    382:                        error(1, "Daemon mode is not enabled");
                    383:                        /* NOTREACHED */
                    384: #endif /* NO_DAEMON_MODE */
                    385:
                    386: #ifndef NO_CGIBIN_SUPPORT
                    387:                case 'c':
                    388:                        set_cgibin(optarg);
                    389:                        break;
                    390:
                    391:                case 'C':
                    392: #ifndef NO_DYNAMIC_CONTENT
                    393:                        /* make sure there's two arguments */
                    394:                        if (argc - optind < 1)
                    395:                                usage();
                    396:                        add_content_map_cgi(optarg, argv[optind++]);
                    397:                        break;
                    398: #else
                    399:                        error(1, "dynmic CGI handler support is not enabled");
                    400:                        /* NOTREACHED */
                    401: #endif /* NO_DYNAMIC_CONTENT */
                    402:
                    403: #else
                    404:                case 'c':
                    405:                case 'C':
                    406:                        error(1, "CGI is not enabled");
                    407:                        /* NOTREACHED */
                    408: #endif /* NO_CGIBIN_SUPPORT */
                    409:
                    410:                case 'd':
                    411:                        dflag++;
                    412: #ifndef DEBUG
                    413:                        if (dflag == 1)
                    414:                                warning("Debugging is not enabled");
                    415: #endif /* !DEBUG */
                    416:                        break;
                    417:
                    418: #ifndef NO_USER_SUPPORT
                    419:                case 'p':
                    420:                        public_html = optarg;
                    421:                        break;
                    422:
                    423:                case 't':
                    424:                        tflag = optarg;
                    425:                        break;
                    426:
                    427:                case 'u':
                    428:                        uflag = 1;
                    429:                        break;
                    430: #else
1.6       mrg       431:                case 'p':
                    432:                case 't':
1.1       tls       433:                case 'u':
                    434:                        error(1, "User support is not enabled");
                    435:                        /* NOTREACHED */
                    436: #endif /* NO_USER_SUPPORT */
                    437:
                    438: #ifndef NO_DIRINDEX_SUPPORT
                    439:                case 'H':
                    440:                        Hflag = 1;
                    441:                        break;
                    442:
                    443:                case 'X':
                    444:                        Xflag = 1;
                    445:                        break;
                    446:
                    447: #else
                    448:                case 'H':
                    449:                case 'X':
                    450:                        error(1, "directory indexing is not enabled");
                    451:                        /* NOTREACHED */
                    452: #endif /* NO_DIRINDEX_SUPPORT */
                    453:
                    454:                default:
                    455:                        usage();
                    456:                        /* NOTREACHED */
                    457:                }
                    458:        }
                    459:        argc -= optind;
                    460:        argv += optind;
                    461:
                    462:        if (argc == 1) {
                    463:                myname = bozomalloc(MAXHOSTNAMELEN+1);
                    464:                /* XXX we do not check for FQDN here */
                    465:                if (gethostname(myname, MAXHOSTNAMELEN+1) < 0)
                    466:                        error(1, "gethostname");
                    467:                myname[MAXHOSTNAMELEN] = '\0';
                    468:        } else if (argc == 2)
                    469:                myname = argv[1];
                    470:        else
                    471:                usage();
                    472:
                    473:        slashdir = argv[0];
                    474:        debug((DEBUG_OBESE, "myname is %s, slashdir is %s", myname, slashdir));
                    475:
                    476:        /*
                    477:         * initialise ssl and daemon mode if necessary.
                    478:         */
                    479:        ssl_init();
                    480:        daemon_init();
                    481:
                    482:        /*
                    483:         * prevent info leakage between different compartments.
                    484:         * some PATH values in the environment would be invalided
                    485:         * by chroot. cross-user settings might result in undesirable
                    486:         * effects.
                    487:         */
                    488:        if ((tflag != NULL || Uflag != NULL) && !eflag) {
                    489:                cleanenv[0] = NULL;
                    490:                environ = cleanenv;
                    491:        }
                    492:
                    493:        /*
                    494:         * look up user/group information.
                    495:         */
                    496:        if (Uflag != NULL) {
                    497:                struct passwd *pw;
                    498:
                    499:                if ((pw = getpwnam(Uflag)) == NULL)
                    500:                        error(1, "getpwnam(%s): %s", Uflag, strerror(errno));
                    501:                if (initgroups(pw->pw_name, pw->pw_gid) == -1)
                    502:                        error(1, "initgroups: %s", strerror(errno));
                    503:                if (setgid(pw->pw_gid) == -1)
                    504:                        error(1, "setgid(%u): %s", pw->pw_gid, strerror(errno));
                    505:                uid = pw->pw_uid;
                    506:        }
                    507:
                    508:        /*
                    509:         * handle chroot.
                    510:         */
                    511:        if (tflag != NULL) {
                    512:                if (chdir(tflag) == -1)
                    513:                        error(1, "chdir(%s): %s", tflag, strerror(errno));
                    514:                if (chroot(tflag) == -1)
                    515:                        error(1, "chroot(%s): %s", tflag, strerror(errno));
                    516:        }
                    517:
                    518:        if (Uflag != NULL)
                    519:                if (setuid(uid) == -1)
                    520:                        error(1, "setuid(%d): %s", uid, strerror(errno));
                    521:
                    522:        /*
                    523:         * be sane, don't start serving up files from a
                    524:         * hierarchy we don't have permission to get to.
                    525:         */
                    526:        if (tflag != NULL)
                    527:                if (chdir("/") == -1)
                    528:                        error(1, "chdir /: %s", strerror(errno));
                    529:
                    530:        /*
                    531:         * read and process the HTTP request.
                    532:         */
                    533:        do {
                    534:                http_request = read_request();
                    535:                if (http_request) {
                    536:                        process_request(http_request);
                    537:                        return (0);
                    538:                }
                    539:        } while (bflag);
                    540:
                    541:        return (0);
                    542: }
                    543:
                    544: char *
                    545: http_date(void)
                    546: {
                    547:        static  char date[40];
                    548:        struct  tm *tm;
                    549:        time_t  now;
                    550:
                    551:        /* Sun, 06 Nov 1994 08:49:37 GMT */
                    552:        now = time(NULL);
                    553:        tm = gmtime(&now);      /* HTTP/1.1 spec rev 06 sez GMT only */
                    554:        strftime(date, sizeof date, "%a, %d %b %Y %H:%M:%S GMT", tm);
                    555:        return date;
                    556: }
                    557:
                    558: /*
                    559:  * convert "in" into the three parts of a request (first line)
                    560:  */
                    561: static void
                    562: parse_request(char *in, char **method, char **url, char **proto)
                    563: {
                    564:        ssize_t len;
                    565:        char    *val;
                    566:
                    567:        *method = *url = *proto = NULL;         /* set them up */
                    568:
                    569:        len = (ssize_t)strlen(in);
1.6       mrg       570:        val = bozostrnsep(&in, " \t\n\r", &len);
1.1       tls       571:        if (len < 1 || val == NULL)
                    572:                return;
                    573:        *method = val;
                    574:        while (*in == ' ' || *in == '\t')
                    575:                in++;
1.6       mrg       576:        val = bozostrnsep(&in, " \t\n\r", &len);
1.1       tls       577:        if (len < 1) {
                    578:                if (len == 0)
                    579:                        *url = val;
                    580:                else
                    581:                        *url = in;
                    582:                return;
                    583:        }
                    584:        *url = val;
                    585:        if (in) {
                    586:                while (*in && (*in == ' ' || *in == '\t'))
                    587:                        in++;
                    588:                if (*in)
                    589:                        *proto = in;
                    590:        }
                    591: }
                    592:
                    593: /*
                    594:  * send a HTTP/1.1 408 response if we timeout.
                    595:  */
                    596: /* ARGSUSED */
                    597: static void
                    598: alarmer(int sig)
                    599: {
                    600:        alarmhit = 1;
                    601: }
                    602:
                    603: /*
                    604:  * This function reads a http request from stdin, returning a pointer to a
                    605:  * http_req structure, describing the request.
                    606:  */
                    607: static http_req *
                    608: read_request(void)
                    609: {
                    610:        struct  sigaction       sa;
                    611:        char    *str, *val, *method, *url, *proto;
                    612:        char    *host, *addr, *port;
                    613:        char    bufport[10];
                    614:        char    hbuf[NI_MAXHOST], abuf[NI_MAXHOST];
                    615:        struct  sockaddr_storage ss;
                    616:        ssize_t len;
                    617:        int     line = 0;
                    618:        socklen_t slen;
                    619:        http_req *request;
                    620:
                    621:        /*
                    622:         * if we're in daemon mode, daemon_fork() will return here once
                    623:         * for each child, then we can setup SSL.
                    624:         */
                    625:        daemon_fork();
                    626:        ssl_accept();
                    627:
                    628:        request = bozomalloc(sizeof *request);
                    629:        memset(request, 0, sizeof *request);
                    630:        request->hr_allow = request->hr_host = NULL;
                    631:        request->hr_content_type = request->hr_content_length = NULL;
1.6       mrg       632:        request->hr_range = NULL;
                    633:        request->hr_last_byte_pos = -1;
1.1       tls       634:
                    635:        slen = sizeof(ss);
                    636:        if (getpeername(0, (struct sockaddr *)&ss, &slen) < 0)
                    637:                host = addr = NULL;
                    638:        else {
                    639:                if (getnameinfo((struct sockaddr *)&ss, slen,
                    640:                    abuf, sizeof abuf, NULL, 0, NI_NUMERICHOST) == 0)
                    641:                        addr = abuf;
                    642:                else
                    643:                        addr = NULL;
                    644:                if (nflag == 0 && getnameinfo((struct sockaddr *)&ss, slen,
                    645:                    hbuf, sizeof hbuf, NULL, 0, 0) == 0)
                    646:                        host = hbuf;
                    647:                else
                    648:                        host = NULL;
                    649:        }
                    650:        if (host != NULL)
                    651:                request->hr_remotehost = bozostrdup(host);
                    652:        if (addr != NULL)
                    653:                request->hr_remoteaddr = bozostrdup(addr);
                    654:        slen = sizeof(ss);
                    655:        if (getsockname(0, (struct sockaddr *)&ss, &slen) < 0)
                    656:                port = NULL;
                    657:        else {
                    658:                if (getnameinfo((struct sockaddr *)&ss, slen, NULL, 0,
                    659:                    bufport, sizeof bufport, NI_NUMERICSERV) == 0)
                    660:                        port = bufport;
                    661:                else
                    662:                        port = NULL;
                    663:        }
                    664:        if (port != NULL)
                    665:                request->hr_serverport = bozostrdup(port);
                    666:
                    667:        /*
                    668:         * setup a timer to make sure the request is not hung
                    669:         */
                    670:        sa.sa_handler = alarmer;
                    671:        sigemptyset(&sa.sa_mask);
                    672:        sigaddset(&sa.sa_mask, SIGALRM);
                    673:        sa.sa_flags = 0;
                    674:        sigaction(SIGALRM, &sa, NULL);  /* XXX */
                    675:
                    676:        alarm(MAX_WAIT_TIME);
1.6       mrg       677:        while ((str = bozodgetln(STDIN_FILENO, &len, bozoread)) != NULL) {
1.1       tls       678:                alarm(0);
                    679:                if (alarmhit)
                    680:                        http_error(408, NULL, "request timed out");
                    681:                line++;
                    682:
                    683:                if (line == 1) {
                    684:                        str = bozostrdup(str);  /* we use this copy */
                    685:
                    686:                        if (len < 1)
                    687:                                http_error(404, NULL, "null method");
                    688:                        warning("got request ``%s'' from host %s to port %s",
                    689:                            str,
                    690:                            host ? host : addr ? addr : "<local>",
                    691:                            port ? port : "<stdin>");
                    692:                        debug((DEBUG_FAT, "read_req, getting request: ``%s''",
                    693:                            str));
                    694:
                    695:                        parse_request(str, &method, &url, &proto);
                    696:                        if (method == NULL)
                    697:                                http_error(404, NULL, "null method");
                    698:                        if (url == NULL)
                    699:                                http_error(404, NULL, "null url");
                    700:
                    701:                        /*
                    702:                         * note that we parse the proto first, so that we
                    703:                         * can more properly parse the method and the url.
                    704:                         */
                    705:                        request->hr_url = url;
                    706:                        process_proto(request, proto);
                    707:                        process_method(request, method);
                    708:
                    709:                        /* http/0.9 has no header processing */
                    710:                        if (request->hr_proto == http_09)
                    711:                                break;
                    712:                } else {                /* incoming headers */
                    713:                        struct  headers *hdr;
                    714:
                    715:                        if (*str == '\0')
                    716:                                break;
                    717:
1.6       mrg       718:                        val = bozostrnsep(&str, ":", &len);
1.1       tls       719:                        debug((DEBUG_EXPLODING,
1.6       mrg       720:                            "read_req2: after bozostrnsep: str ``%s'' val ``%s''",
1.1       tls       721:                            str, val));
                    722:                        if (val == NULL || len == -1)
                    723:                                http_error(404, request, "no header");
                    724:                        while (*str == ' ' || *str == '\t')
                    725:                                len--, str++;
1.6       mrg       726:                        while (*val == ' ' || *val == '\t')
                    727:                                val++;
1.1       tls       728:
                    729:                        if (auth_check_headers(request, val, str, len))
                    730:                                goto next_header;
                    731:
                    732:                        hdr = addmerge_header(request, val, str, len);
                    733:
                    734:                        if (strcasecmp(hdr->h_header, "content-type") == 0)
                    735:                                request->hr_content_type = hdr->h_value;
                    736:                        else if (strcasecmp(hdr->h_header, "content-length") == 0)
                    737:                                request->hr_content_length = hdr->h_value;
                    738:                        else if (strcasecmp(hdr->h_header, "host") == 0)
                    739:                                request->hr_host = hdr->h_value;
                    740:                        /* HTTP/1.1 rev06 draft spec: 14.20 */
                    741:                        else if (strcasecmp(hdr->h_header, "expect") == 0)
                    742:                                http_error(417, request, "we don't support Expect:");
                    743:                        else if (strcasecmp(hdr->h_header, "referrer") == 0 ||
                    744:                                 strcasecmp(hdr->h_header, "referer") == 0)
                    745:                                request->hr_referrer = hdr->h_value;
1.6       mrg       746:                        else if (strcasecmp(hdr->h_header, "range") == 0)
                    747:                                request->hr_range = hdr->h_value;
1.1       tls       748:
                    749:                        debug((DEBUG_FAT, "adding header %s: %s",
                    750:                            hdr->h_header, hdr->h_value));
                    751:                }
                    752: next_header:
                    753:                alarm(MAX_WAIT_TIME);
                    754:        }
                    755:
                    756:        /* now, clear it all out */
                    757:        alarm(0);
                    758:        signal(SIGALRM, SIG_DFL);
                    759:
                    760:        /* RFC1945, 8.3 */
                    761:        if (request->hr_method == HTTP_POST && request->hr_content_length == NULL)
                    762:                http_error(400, request, "missing content length");
                    763:
                    764:        /* HTTP/1.1 draft rev-06, 14.23 & 19.6.1.1 */
                    765:        if (request->hr_proto == http_11 && request->hr_host == NULL)
                    766:                http_error(400, request, "missing Host header");
                    767:
1.6       mrg       768:        if (request->hr_range != NULL) {
                    769:                debug((DEBUG_FAT, "hr_range: %s", request->hr_range));
                    770:                /* support only simple ranges %d- and %d-%d */
                    771:                if (strchr(request->hr_range, ',') == NULL) {
                    772:                        const char *rstart, *dash;
                    773:
                    774:                        rstart = strchr(request->hr_range, '=');
                    775:                        if (rstart != NULL) {
                    776:                                rstart++;
                    777:                                dash = strchr(rstart, '-');
                    778:                                if (dash != NULL && dash != rstart) {
                    779:                                        dash++;
                    780:                                        request->hr_have_range = 1;
                    781:                                        request->hr_first_byte_pos =
                    782:                                            strtoll(rstart, NULL, 10);
                    783:                                        if (request->hr_first_byte_pos < 0)
                    784:                                                request->hr_first_byte_pos = 0;
                    785:                                        if (*dash != '\0') {
                    786:                                                request->hr_last_byte_pos =
                    787:                                                    strtoll(dash, NULL, 10);
                    788:                                                if (request->hr_last_byte_pos < 0)
                    789:                                                        request->hr_last_byte_pos = -1;
                    790:                                        }
                    791:                                }
                    792:                        }
                    793:                }
                    794:        }
                    795:
1.1       tls       796:        debug((DEBUG_FAT, "read_request returns url %s in request", request->hr_url));
                    797:        return (request);
                    798: }
                    799:
                    800: /*
                    801:  * add or merge this header (val: str) into the requests list
                    802:  */
                    803: static struct headers *
                    804: addmerge_header(http_req *request, char *val, char *str, ssize_t len)
                    805: {
                    806:        struct  headers *hdr;
                    807:        static char space[2] = { ' ', 0 };
                    808:
                    809:        /* do we exist already? */
1.2       tls       810:        SIMPLEQ_FOREACH(hdr, &request->hr_headers, h_next) {
1.1       tls       811:                if (strcasecmp(val, hdr->h_header) == 0)
                    812:                        break;
1.2       tls       813:        }
1.1       tls       814:
                    815:        if (hdr) {
                    816:                /* yup, merge it in */
                    817:                if (hdr->h_value == space)
                    818:                        hdr->h_value = bozostrdup(str);
                    819:                else  {
                    820:                        char *nval;
                    821:
                    822:                        if (asprintf(&nval, "%s, %s", hdr->h_value, str) == -1)
                    823:                                http_error(500, NULL,
                    824:                                     "memory allocation failure");
                    825:                        free(hdr->h_value);
                    826:                        hdr->h_value = nval;
                    827:                }
                    828:        } else {
                    829:                /* nope, create a new one */
                    830:
                    831:                hdr = bozomalloc(sizeof *hdr);
                    832:                hdr->h_header = bozostrdup(val);
                    833:                if (str && *str)
                    834:                        hdr->h_value = bozostrdup(str);
                    835:                else
                    836:                        hdr->h_value = space;
                    837:
                    838:                SIMPLEQ_INSERT_TAIL(&request->hr_headers, hdr, h_next);
                    839:                request->hr_nheaders++;
                    840:        }
                    841:
                    842:        return hdr;
                    843: }
                    844:
                    845: /*
                    846:  * process_request does the following:
                    847:  *     - check the request is valid
                    848:  *     - process cgi-bin if necessary
                    849:  *     - transform a filename if necesarry
                    850:  *     - return the HTTP request
                    851:  */
                    852: static void
                    853: process_request(http_req *request)
                    854: {
                    855:        struct  stat sb;
                    856:        char    *file;
                    857:        const char *type, *encoding;
                    858:        int     fd, isindex;
                    859:
                    860:        /*
                    861:         * note that transform_request chdir()'s if required.  also note
                    862:         * that cgi is handed here, and a cgi request will never return
                    863:         * back here.
                    864:         */
                    865:        file = transform_request(request, &isindex);
                    866:        if (file == NULL)
                    867:                http_error(404, request, "empty file after transform");
                    868:
                    869:        fd = open(file, O_RDONLY);
                    870:        if (fd < 0) {
                    871:                debug((DEBUG_FAT, "open failed: %s", strerror(errno)));
                    872:                if (errno == EPERM)
                    873:                        http_error(403, request, "no permission to open file");
                    874:                else if (errno == ENOENT) {
                    875:                        if (directory_index(request, file, isindex))
                    876:                                return;
                    877:                        http_error(404, request, "no file");
                    878:                } else
                    879:                        http_error(500, request, "open file");
                    880:        }
                    881:        if (fstat(fd, &sb) < 0)
                    882:                http_error(500, request, "can't fstat");
                    883:        if (S_ISDIR(sb.st_mode))
                    884:                handle_redirect(request, NULL, 0);
                    885:                /* NOTREACHED */
                    886:        /* XXX RFC1945 10.9 If-Modified-Since (http code 304) */
                    887:
1.6       mrg       888:        /* validate requested range */
                    889:        if (request->hr_last_byte_pos == -1 ||
                    890:            request->hr_last_byte_pos >= sb.st_size)
                    891:                request->hr_last_byte_pos = sb.st_size - 1;
                    892:        if (request->hr_have_range &&
                    893:            request->hr_first_byte_pos > request->hr_last_byte_pos) {
                    894:                request->hr_have_range = 0;     /* punt */
                    895:                request->hr_first_byte_pos = 0;
                    896:                request->hr_last_byte_pos = sb.st_size - 1;
                    897:        }
                    898:        debug((DEBUG_FAT, "have_range %d first_pos %qd last_pos %qd",
                    899:            request->hr_have_range,
                    900:            request->hr_first_byte_pos, request->hr_last_byte_pos));
                    901:        if (request->hr_have_range)
                    902:                bozoprintf("%s 206 Partial Content\r\n", request->hr_proto);
                    903:        else
                    904:                bozoprintf("%s 200 OK\r\n", request->hr_proto);
1.1       tls       905:
                    906:        if (request->hr_proto != http_09) {
                    907:                type = content_type(request, file);
                    908:                encoding = content_encoding(request, file);
                    909:
                    910:                print_header(request, &sb, type, encoding);
                    911:                bozoprintf("\r\n");
                    912:        }
                    913:        bozoflush(stdout);
                    914:
                    915:        if (request->hr_method != HTTP_HEAD) {
                    916:                char *addr;
                    917:                void *oaddr;
1.6       mrg       918:                size_t mappedsz;
                    919:                size_t sz;
1.1       tls       920:
1.6       mrg       921:                sz = mappedsz = request->hr_last_byte_pos - request->hr_first_byte_pos + 1;
                    922:                oaddr = addr = mmap(0, mappedsz, PROT_READ,
                    923:                    MAP_SHARED, fd, request->hr_first_byte_pos);
1.1       tls       924:                if (addr == (char *)-1)
                    925:                        error(1, "mmap failed: %s", strerror(errno));
                    926:
                    927: #ifdef MADV_SEQUENTIAL
                    928:                madvise(addr, sz, MADV_SEQUENTIAL);
                    929: #endif
                    930:                while (sz > WRSZ) {
                    931:                        if (bozowrite(STDOUT_FILENO, addr, WRSZ) != WRSZ)
                    932:                                error(1, "write failed: %s", strerror(errno));
1.6       mrg       933:                        debug((DEBUG_OBESE, "wrote %d bytes", WRSZ));
1.1       tls       934:                        sz -= WRSZ;
                    935:                        addr += WRSZ;
                    936:                }
1.8     ! lukem     937:                if (sz && (size_t)bozowrite(STDOUT_FILENO, addr, sz) != sz)
1.1       tls       938:                        error(1, "final write failed: %s", strerror(errno));
1.6       mrg       939:                debug((DEBUG_OBESE, "wrote %d bytes", (int)sz));
                    940:                if (munmap(oaddr, mappedsz) < 0)
1.1       tls       941:                        warning("munmap failed");
                    942:        }
                    943:        /* If SSL enabled cleanup SSL structure. */
                    944:        ssl_destroy();
                    945:        close(fd);
1.2       tls       946:        free(file);
1.1       tls       947: }
                    948:
                    949: /*
                    950:  * deal with virtual host names; we do this:
                    951:  *     if we have a virtual path root (vpath), and we are given a
                    952:  *     virtual host spec (Host: ho.st or http://ho.st/), see if this
                    953:  *     directory exists under vpath.  if it does, use this as the
                    954:  #     new slashdir.
                    955:  */
                    956: static void
                    957: check_virtual(http_req *request)
                    958: {
                    959:        char *url = request->hr_url, *s;
                    960:        struct dirent **list;
                    961:        size_t len;
                    962:        int i;
                    963:
                    964:        if (!vpath)
                    965:                goto use_slashdir;
                    966:
                    967:        /*
                    968:         * convert http://virtual.host/ to request->hr_host
                    969:         */
                    970:        debug((DEBUG_OBESE, "checking for http:// virtual host in ``%s''", url));
                    971:        if (strncasecmp(url, "http://", 7) == 0) {
                    972:                /* we would do virtual hosting here? */
                    973:                url += 7;
                    974:                s = strchr(url, '/');
                    975:                /* HTTP/1.1 draft rev-06, 5.2: URI takes precedence over Host: */
                    976:                request->hr_host = url;
                    977:                request->hr_url = bozostrdup(s ? s : "/");
                    978:                debug((DEBUG_OBESE, "got host ``%s'' url is now ``%s''",
                    979:                    request->hr_host, request->hr_url));
                    980:        } else if (!request->hr_host)
                    981:                goto use_slashdir;
                    982:
                    983:
                    984:        /*
                    985:         * ok, we have a virtual host, use scandir(3) to find a case
                    986:         * insensitive match for the virtual host we are asked for.
                    987:         * note that if the virtual host is the same as the master,
                    988:         * we don't need to do anything special.
                    989:         */
                    990:        len = strlen(request->hr_host);
                    991:        debug((DEBUG_OBESE,
                    992:            "check_virtual: checking host `%s' under vpath `%s' for url `%s'",
                    993:            request->hr_host, vpath, request->hr_url));
                    994:        if (strncasecmp(myname, request->hr_host, len) != 0) {
                    995:                s = 0;
                    996:                for (i = scandir(vpath, &list, 0, 0); i--; list++) {
                    997:                        debug((DEBUG_OBESE, "looking at dir``%s''",
                    998:                            (*list)->d_name));
                    999:                        if (strncasecmp((*list)->d_name, request->hr_host,
                   1000:                            len) == 0) {
                   1001:                                /* found it, punch it */
                   1002:                                myname = (*list)->d_name;
                   1003:                                if (asprintf(&s, "%s/%s", vpath, myname) < 0)
                   1004:                                        error(1, "asprintf");
                   1005:                                break;
                   1006:                        }
                   1007:                }
                   1008:                if (s == 0) {
                   1009:                        if (Vflag)
                   1010:                                goto use_slashdir;
                   1011:                        http_error(404, request, "unknown URL");
                   1012:                }
                   1013:        } else
                   1014: use_slashdir:
                   1015:                s = slashdir;
                   1016:
                   1017:        /*
                   1018:         * ok, nailed the correct slashdir, chdir to it
                   1019:         */
                   1020:        if (chdir(s) < 0)
                   1021:                error(1, "can't chdir %s: %s", s, strerror(errno));
                   1022: }
                   1023:
                   1024: /* make sure we're not trying to access special files */
                   1025: void
                   1026: check_special_files(http_req *request, const char *name)
                   1027: {
                   1028:        /* ensure basename(name) != special files */
                   1029:        if (strcmp(name, DIRECT_ACCESS_FILE) == 0)
                   1030:                http_error(403, request,
                   1031:                    "no permission to open direct access file");
                   1032:        if (strcmp(name, REDIRECT_FILE) == 0)
                   1033:                http_error(403, request,
                   1034:                    "no permission to open redirect file");
                   1035:        if (strcmp(name, ABSREDIRECT_FILE) == 0)
                   1036:                http_error(403, request,
                   1037:                    "no permission to open redirect file");
                   1038:        auth_check_special_files(request, name);
                   1039: }
                   1040:
                   1041: /*
                   1042:  * checks to see if this request has a valid .bzredirect file.  returns
                   1043:  * 0 on failure and 1 on success.
                   1044:  */
                   1045: static void
                   1046: check_bzredirect(http_req *request)
                   1047: {
                   1048:        struct stat sb;
                   1049:        char dir[MAXPATHLEN], redir[MAXPATHLEN], redirpath[MAXPATHLEN + 1];
                   1050:        char *basename, *finalredir;
                   1051:        int rv, absolute;
                   1052:
                   1053:        /*
                   1054:         * if this pathname is really a directory, but doesn't end in /,
                   1055:         * use it as the directory to look for the redir file.
                   1056:         */
                   1057:        snprintf(dir, sizeof(dir), "%s", request->hr_url + 1);
                   1058:        debug((DEBUG_FAT, "check_bzredirect: dir %s", dir));
                   1059:        basename = strrchr(dir, '/');
                   1060:
                   1061:        if ((!basename || basename[1] != '\0') &&
                   1062:            lstat(dir, &sb) == 0 && S_ISDIR(sb.st_mode))
                   1063:                /* nothing */;
                   1064:        else if (basename == NULL)
                   1065:                strcpy(dir, ".");
                   1066:        else {
                   1067:                *basename++ = '\0';
                   1068:                check_special_files(request, basename);
                   1069:        }
                   1070:
                   1071:        snprintf(redir, sizeof(redir), "%s/%s", dir, REDIRECT_FILE);
                   1072:        if (lstat(redir, &sb) == 0) {
                   1073:                if (S_ISLNK(sb.st_mode) == 0)
                   1074:                        return;
                   1075:                absolute = 0;
                   1076:        } else {
                   1077:                snprintf(redir, sizeof(redir), "%s/%s", dir, ABSREDIRECT_FILE);
                   1078:                if (lstat(redir, &sb) < 0 || S_ISLNK(sb.st_mode) == 0)
                   1079:                        return;
                   1080:                absolute = 1;
                   1081:        }
                   1082:        debug((DEBUG_FAT, "check_bzredirect: calling readlink"));
                   1083:        rv = readlink(redir, redirpath, sizeof redirpath - 1);
                   1084:        if (rv == -1 || rv == 0) {
                   1085:                debug((DEBUG_FAT, "readlink failed"));
                   1086:                return;
                   1087:        }
                   1088:        redirpath[rv] = '\0';
                   1089:        debug((DEBUG_FAT, "readlink returned \"%s\"", redirpath));
                   1090:
                   1091:        /* now we have the link pointer, redirect to the real place */
                   1092:        if (absolute)
                   1093:                finalredir = redirpath;
                   1094:        else
                   1095:                snprintf(finalredir = redir, sizeof(redir), "/%s/%s", dir,
                   1096:                         redirpath);
                   1097:
                   1098:        debug((DEBUG_FAT, "check_bzredirect: new redir %s", finalredir));
                   1099:        handle_redirect(request, finalredir, absolute);
                   1100: }
                   1101:
                   1102: /*
                   1103:  * checks to see if this request has a valid .bzdirect file.  returns
                   1104:  * 0 on failure and 1 on success.
                   1105:  */
                   1106: static int
                   1107: check_direct_access(http_req *request)
                   1108: {
                   1109:        FILE *fp;
                   1110:        struct stat sb;
                   1111:        char dir[MAXPATHLEN], dirfile[MAXPATHLEN], *basename;
                   1112:
                   1113:        snprintf(dir, sizeof(dir), "%s", request->hr_url + 1);
                   1114:        debug((DEBUG_FAT, "check_bzredirect: dir %s", dir));
                   1115:        basename = strrchr(dir, '/');
                   1116:
                   1117:        if ((!basename || basename[1] != '\0') &&
                   1118:            lstat(dir, &sb) == 0 && S_ISDIR(sb.st_mode))
                   1119:                /* nothing */;
                   1120:        else if (basename == NULL)
                   1121:                strcpy(dir, ".");
                   1122:        else {
                   1123:                *basename++ = '\0';
                   1124:                check_special_files(request, basename);
                   1125:        }
                   1126:
                   1127:        snprintf(dirfile, sizeof(dirfile), "%s/%s", dir, DIRECT_ACCESS_FILE);
                   1128:        if (stat(dirfile, &sb) < 0 ||
                   1129:            (fp = fopen(dirfile, "r")) == NULL)
                   1130:                return 0;
                   1131:        fclose(fp);
                   1132:        return 1;
                   1133: }
                   1134:
                   1135: /*
                   1136:  * transform_request does this:
                   1137:  *     - ``expand'' %20 crapola
                   1138:  *     - punt if it doesn't start with /
                   1139:  *     - check rflag / referrer
                   1140:  *     - look for "http://myname/" and deal with it.
                   1141:  *     - maybe call process_cgi()
                   1142:  *     - check for ~user and call user_transform() if so
                   1143:  *     - if the length > 1, check for trailing slash.  if so,
                   1144:  *       add the index.html file
                   1145:  *     - if the length is 1, return the index.html file
                   1146:  *     - disallow anything ending up with a file starting
                   1147:  *       at "/" or having ".." in it.
                   1148:  *     - anything else is a really weird internal error
                   1149:  */
                   1150: static char *
                   1151: transform_request(http_req *request, int *isindex)
                   1152: {
                   1153:        char    *file;
                   1154:        char    *url;
                   1155:        size_t  len;
                   1156:
                   1157:        file = NULL;
                   1158:        *isindex = 0;
                   1159:        debug((DEBUG_FAT, "tf_req: url %s", request->hr_url));
                   1160:        fix_url_percent(request);
                   1161:        check_virtual(request);
                   1162:        url = request->hr_url;
                   1163:
                   1164:        if (url[0] != '/')
                   1165:                http_error(404, request, "unknown URL");
                   1166:
                   1167:        check_bzredirect(request);
                   1168:
                   1169:        if (rflag) {
                   1170:                int to_indexhtml = 0;
                   1171:
                   1172: #define TOP_PAGE(x)    (strcmp((x), "/") == 0 || \
                   1173:                         strcmp((x) + 1, index_html) == 0 || \
                   1174:                         strcmp((x) + 1, "favicon.ico") == 0)
                   1175:
                   1176:                debug((DEBUG_EXPLODING, "checking rflag"));
                   1177:                /*
                   1178:                 * first check that this path isn't allowed via .bzdirect file,
                   1179:                 * and then check referrer; make sure that people come via the
                   1180:                 * real name... otherwise if we aren't looking at / or
                   1181:                 * /index.html, redirect...  we also special case favicon.ico.
                   1182:                 */
                   1183:                if (check_direct_access(request))
                   1184:                        /* nothing */;
                   1185:                else if (request->hr_referrer) {
                   1186:                        const char *r = request->hr_referrer;
                   1187:
                   1188:                        debug((DEBUG_FAT,
                   1189:                           "checking referrer \"%s\" vs myname %s", r, myname));
                   1190:                        if (strncmp(r, "http://", 7) != 0 ||
                   1191:                            (strncasecmp(r + 7, myname, strlen(myname)) != 0 &&
                   1192:                             !TOP_PAGE(url)))
                   1193:                                to_indexhtml = 1;
                   1194:                } else {
                   1195:                        const char *h = request->hr_host;
                   1196:
                   1197:                        debug((DEBUG_FAT, "url has no referrer at all"));
                   1198:                        /* if there's no referrer, let / or /index.html past */
                   1199:                        if (!TOP_PAGE(url) ||
                   1200:                            (h && strncasecmp(h, myname, strlen(myname)) != 0))
                   1201:                                to_indexhtml = 1;
                   1202:                }
                   1203:
                   1204:                if (to_indexhtml) {
                   1205:                        char *slashindexhtml;
                   1206:
                   1207:                        if (asprintf(&slashindexhtml, "/%s", index_html) < 0)
                   1208:                                error(1, "asprintf");
                   1209:                        debug((DEBUG_FAT, "rflag: redirecting %s to %s", url, slashindexhtml));
                   1210:                        handle_redirect(request, slashindexhtml, 0);
                   1211:                        /* NOTREACHED */
                   1212:                }
                   1213:        }
                   1214:
                   1215:        process_cgi(request);
                   1216:
                   1217:        len = strlen(url);
                   1218:        if (0) {
                   1219: #ifndef NO_USER_SUPPORT
                   1220:        } else if (len > 1 && uflag && url[1] == '~') {
                   1221:                if (url[2] == '\0')
                   1222:                        http_error(404, request, "missing username");
                   1223:                if (strchr(url + 2, '/') == NULL)
                   1224:                        handle_redirect(request, NULL, 0);
                   1225:                        /* NOTREACHED */
                   1226:                debug((DEBUG_FAT, "calling user_transform"));
                   1227:                return (user_transform(request, isindex));
                   1228: #endif /* NO_USER_SUPPORT */
                   1229:        } else if (len > 1) {
                   1230:                debug((DEBUG_FAT, "url[len-1] == %c", url[len-1]));
                   1231:                if (url[len-1] == '/') {        /* append index.html */
                   1232:                        *isindex = 1;
                   1233:                        debug((DEBUG_FAT, "appending index.html"));
                   1234:                        file = bozomalloc(len + strlen(index_html) + 1);
                   1235:                        strcpy(file, url + 1);
                   1236:                        strcat(file, index_html);
                   1237:                } else
                   1238:                        file = bozostrdup(url + 1);
                   1239:        } else if (len == 1) {
                   1240:                debug((DEBUG_EXPLODING, "tf_req: len == 1"));
                   1241:                file = bozostrdup(index_html);
                   1242:                *isindex = 1;
                   1243:        } else          /* len == 0 ? */
                   1244:                http_error(500, request, "request->hr_url is nul?");
                   1245:
                   1246:        if (file == NULL)
                   1247:                http_error(500, request, "internal failure");
                   1248:
                   1249:        /*
                   1250:         * look for "http://myname/" and deal with it as necessary.
                   1251:         */
                   1252:
                   1253:        /*
                   1254:         * stop traversing outside our domain
                   1255:         *
                   1256:         * XXX true security only comes from our parent using chroot(2)
                   1257:         * before execve(2)'ing us.  or our own built in chroot(2) support.
                   1258:         */
                   1259:        if (*file == '/' || strcmp(file, "..") == 0 ||
                   1260:            strstr(file, "/..") || strstr(file, "../"))
                   1261:                http_error(403, request, "illegal request");
                   1262:
                   1263:        auth_check(request, file);
                   1264:
                   1265:        debug((DEBUG_FAT, "transform_request returned: %s", file));
                   1266:        return (file);
                   1267: }
                   1268:
                   1269: /*
                   1270:  * do automatic redirection
                   1271:  */
                   1272: static void
                   1273: handle_redirect(http_req *request, const char *url, int absolute)
                   1274: {
                   1275:        char *urlbuf;
                   1276:        char portbuf[20];
                   1277:
                   1278:        if (url == NULL) {
                   1279:                if (asprintf(&urlbuf, "%s/", request->hr_url) < 0)
                   1280:                        error(1, "asprintf");
                   1281:                url = urlbuf;
                   1282:        }
                   1283:        if (request->hr_serverport && strcmp(request->hr_serverport, "80") != 0)
                   1284:                snprintf(portbuf, sizeof(portbuf), ":%s",
                   1285:                    request->hr_serverport);
                   1286:        else
                   1287:                portbuf[0] = '\0';
                   1288:        warning("redirecting %s%s%s", myname, portbuf, url);
                   1289:        debug((DEBUG_FAT, "redirecting %s", url));
                   1290:        bozoprintf("%s 301 Document Moved\r\n", request->hr_proto);
                   1291:        if (request->hr_proto != http_09)
                   1292:                print_header(request, NULL, "text/html", NULL);
                   1293:        if (request->hr_proto != http_09) {
                   1294:                bozoprintf("Location: http://");
                   1295:                if (absolute == 0)
                   1296:                        bozoprintf("%s%s", myname, portbuf);
                   1297:                bozoprintf("%s\r\n", url);
                   1298:        }
                   1299:        bozoprintf("\r\n");
                   1300:        if (request->hr_method == HTTP_HEAD)
                   1301:                goto head;
                   1302:        bozoprintf("<html><head><title>Document Moved</title></head>\n");
                   1303:        bozoprintf("<body><h1>Document Moved</h1>\n");
                   1304:        bozoprintf("This document had moved <a href=\"http://");
                   1305:        if (absolute)
                   1306:                bozoprintf("%s", url);
                   1307:        else
                   1308:                bozoprintf("%s%s%s", myname, portbuf, url);
                   1309:        bozoprintf("\">here</a>\n");
                   1310:        bozoprintf("</body></html>\n");
                   1311: head:
                   1312:        bozoflush(stdout);
                   1313:        exit(0);
                   1314: }
                   1315:
                   1316: /* generic header printing routine */
                   1317: void
                   1318: print_header(http_req *request, struct stat *sbp, const char *type,
                   1319:             const char *encoding)
                   1320: {
1.6       mrg      1321:        off_t len;
                   1322:
1.1       tls      1323:        bozoprintf("Date: %s\r\n", http_date());
                   1324:        bozoprintf("Server: %s\r\n", server_software);
1.6       mrg      1325:        bozoprintf("Accept-Ranges: bytes\r\n");
1.1       tls      1326:        if (sbp) {
                   1327:                char filedate[40];
                   1328:                struct  tm *tm;
                   1329:
                   1330:                tm = gmtime(&sbp->st_mtime);
                   1331:                strftime(filedate, sizeof filedate,
                   1332:                    "%a, %d %b %Y %H:%M:%S GMT", tm);
                   1333:                bozoprintf("Last-Modified: %s\r\n", filedate);
                   1334:        }
                   1335:        if (type && *type)
                   1336:                bozoprintf("Content-Type: %s\r\n", type);
                   1337:        if (encoding && *encoding)
                   1338:                bozoprintf("Content-Encoding: %s\r\n", encoding);
1.6       mrg      1339:        if (sbp) {
                   1340:                if (request->hr_have_range) {
                   1341:                        len = request->hr_last_byte_pos - request->hr_first_byte_pos +1;
                   1342:                        bozoprintf("Content-Range: bytes %qd-%qd/%qd\r\n",
                   1343:                            (long long) request->hr_first_byte_pos,
                   1344:                            (long long) request->hr_last_byte_pos,
                   1345:                            (long long) sbp->st_size);
                   1346:                }
                   1347:                else
                   1348:                        len = sbp->st_size;
                   1349:                bozoprintf("Content-Length: %qd\r\n", (long long)len);
                   1350:        }
1.1       tls      1351:        if (request && request->hr_proto == http_11)
                   1352:                bozoprintf("Connection: close\r\n");
                   1353:        bozoflush(stdout);
                   1354: }
                   1355:
                   1356: /* this escape HTML tags */
                   1357: static void
                   1358: escape_html(http_req *request)
                   1359: {
                   1360:        int     i, j;
                   1361:        char    *url = request->hr_url, *tmp;
                   1362:
                   1363:        for (i = 0, j = 0; url[i]; i++) {
                   1364:                switch (url[i]) {
                   1365:                case '<':
                   1366:                case '>':
                   1367:                        j += 4;
                   1368:                        break;
                   1369:                case '&':
                   1370:                        j += 5;
                   1371:                        break;
                   1372:                }
                   1373:        }
                   1374:
                   1375:        if (j == 0)
                   1376:                return;
                   1377:
                   1378:        if ((tmp = (char *) malloc(strlen(url) + j)) == 0)
                   1379:                /*
                   1380:                 * ouch, but we are only called from an error context, and
                   1381:                 * most paths here come from malloc(3) failures anyway...
                   1382:                 * we could completely punt and just exit, but isn't returning
                   1383:                 * an not-quite-correct error better than nothing at all?
                   1384:                 */
                   1385:                return;
                   1386:
                   1387:        for (i = 0, j = 0; url[i]; i++) {
                   1388:                switch (url[i]) {
                   1389:                case '<':
                   1390:                        memcpy(tmp + j, "&lt;", 4);
                   1391:                        j += 4;
                   1392:                        break;
                   1393:                case '>':
                   1394:                        memcpy(tmp + j, "&gt;", 4);
                   1395:                        j += 4;
                   1396:                        break;
                   1397:                case '&':
                   1398:                        memcpy(tmp + j, "&amp;", 5);
                   1399:                        j += 5;
                   1400:                        break;
                   1401:                default:
                   1402:                        tmp[j++] = url[i];
                   1403:                }
                   1404:        }
                   1405:        tmp[j] = 0;
                   1406:
                   1407:        /*
1.6       mrg      1408:         * original "url" is a substring of an allocation, so we
1.1       tls      1409:         * can't touch it.  so, ignore it and replace the request.
                   1410:         */
                   1411:        request->hr_url = tmp;
                   1412: }
                   1413:
                   1414: /* this fixes the %HH hack that RFC2396 requires.  */
                   1415: static void
                   1416: fix_url_percent(http_req *request)
                   1417: {
                   1418:        char    *s, *t, buf[3], *url;
                   1419:        char    *end;   /* if end is not-zero, we don't translate beyond that */
                   1420:
                   1421:        url = request->hr_url;
                   1422:
                   1423:        /* make sure we don't translate *too* much */
                   1424:        end = strchr(request->hr_url, '?');
                   1425:
                   1426:        /* fast forward to the first % */
                   1427:        if ((s = strchr(url, '%')) == NULL)
                   1428:                return;
                   1429:
                   1430:        t = s;
                   1431:        do {
                   1432:                if (end && s >= end) {
                   1433:                        debug((DEBUG_EXPLODING, "fu_%%: past end, filling out.."));
                   1434:                        while (*s)
                   1435:                                *t++ = *s++;
                   1436:                        break;
                   1437:                }
                   1438:                debug((DEBUG_EXPLODING, "fu_%%: got s == %%, s[1]s[2] == %c%c",
                   1439:                    s[1], s[2]));
                   1440:                if (s[1] == '\0' || s[2] == '\0')
                   1441:                        http_error(400, request,
                   1442:                            "percent hack missing two chars afterwards");
                   1443:                if (s[1] == '0' && s[2] == '0')
                   1444:                        http_error(404, request, "percent hack was %00");
                   1445:                if (s[1] == '2' && s[2] == 'f')
                   1446:                        http_error(404, request, "percent hack was %2f (/)");
                   1447:
                   1448:                buf[0] = *++s;
                   1449:                buf[1] = *++s;
                   1450:                buf[2] = '\0';
                   1451:                s++;
                   1452:                *t = (char)strtol(buf, NULL, 16);
1.7       mrg      1453:                debug((DEBUG_EXPLODING, "fu_%%: strtol put '%c' into *t", *t));
1.1       tls      1454:                if (*t++ == '\0')
                   1455:                        http_error(400, request, "percent hack got a 0 back");
                   1456:
                   1457:                while (*s && *s != '%') {
1.7       mrg      1458:                        if (end && s >= end)
1.1       tls      1459:                                break;
                   1460:                        *t++ = *s++;
                   1461:                }
                   1462:        } while (*s);
                   1463:        *t = '\0';
                   1464:        debug((DEBUG_FAT, "fix_url_percent returns %s in url", request->hr_url));
                   1465: }
                   1466:
                   1467: /*
                   1468:  * process each type of HTTP method, setting this HTTP requests
                   1469:  # method type.
                   1470:  */
                   1471: static struct method_map {
                   1472:        const char *name;
                   1473:        int     type;
                   1474: } method_map[] = {
                   1475:        { "GET",        HTTP_GET, },
                   1476:        { "POST",       HTTP_POST, },
                   1477:        { "HEAD",       HTTP_HEAD, },
                   1478: #if 0  /* other non-required http/1.1 methods */
                   1479:        { "OPTIONS",    HTTP_OPTIONS, },
                   1480:        { "PUT",        HTTP_PUT, },
                   1481:        { "DELETE",     HTTP_DELETE, },
                   1482:        { "TRACE",      HTTP_TRACE, },
                   1483:        { "CONNECT",    HTTP_CONNECT, },
                   1484: #endif
                   1485:        { NULL,         0, },
                   1486: };
                   1487:
                   1488: static void
                   1489: process_method(http_req *request, const char *method)
                   1490: {
                   1491:        struct  method_map *mmp;
                   1492:
                   1493:        for (mmp = method_map; mmp->name; mmp++)
                   1494:                if (strcasecmp(method, mmp->name) == 0) {
                   1495:                        request->hr_method = mmp->type;
                   1496:                        request->hr_methodstr = mmp->name;
                   1497:                        return;
                   1498:                }
                   1499:
                   1500:        if (request->hr_proto == http_11)
                   1501:                request->hr_allow = "GET, HEAD, POST";
                   1502:        http_error(404, request, "unknown method");
                   1503: }
                   1504:
                   1505: /*
                   1506:  * as the prototype string is not constant (eg, "HTTP/1.1" is equivalent
                   1507:  * to "HTTP/001.01"), we MUST parse this.
                   1508:  */
                   1509: static void
                   1510: process_proto(http_req *request, const char *proto)
                   1511: {
                   1512:        char    majorstr[16], *minorstr;
                   1513:        int     majorint, minorint;
                   1514:
                   1515:        if (proto == NULL) {
                   1516: got_proto_09:
                   1517:                request->hr_proto = http_09;
                   1518:                debug((DEBUG_FAT, "request %s is http/0.9", request->hr_url));
                   1519:                return;
                   1520:        }
                   1521:
                   1522:        if (strncasecmp(proto, "HTTP/", 5) != 0)
                   1523:                goto bad;
                   1524:        strncpy(majorstr, proto + 5, sizeof majorstr);
                   1525:        majorstr[sizeof(majorstr)-1] = 0;
                   1526:        minorstr = strchr(majorstr, '.');
                   1527:        if (minorstr == NULL)
                   1528:                goto bad;
                   1529:        *minorstr++ = 0;
                   1530:
                   1531:        majorint = atoi(majorstr);
                   1532:        minorint = atoi(minorstr);
                   1533:
                   1534:        switch (majorint) {
                   1535:        case 0:
                   1536:                if (minorint != 9)
                   1537:                        break;
                   1538:                goto got_proto_09;
                   1539:        case 1:
                   1540:                if (minorint == 0)
                   1541:                        request->hr_proto = http_10;
                   1542:                else if (minorint == 1)
                   1543:                        request->hr_proto = http_11;
                   1544:                else
                   1545:                        break;
                   1546:
                   1547:                debug((DEBUG_FAT, "request %s is %s", request->hr_url,
                   1548:                    request->hr_proto));
                   1549:                SIMPLEQ_INIT(&request->hr_headers);
                   1550:                request->hr_nheaders = 0;
                   1551:                return;
                   1552:        }
                   1553: bad:
                   1554:        http_error(404, NULL, "unknown prototype");
                   1555: }
                   1556:
                   1557: #ifdef DEBUG
                   1558: void
                   1559: debug__(int level, const char *fmt, ...)
                   1560: {
                   1561:        va_list ap;
                   1562:        int savederrno;
                   1563:
                   1564:        /* only log if the level is low enough */
                   1565:        if (dflag < level)
                   1566:                return;
                   1567:
                   1568:        savederrno = errno;
                   1569:        va_start(ap, fmt);
                   1570:        if (sflag) {
                   1571:                vfprintf(stderr, fmt, ap);
                   1572:                fputs("\n", stderr);
                   1573:        } else
                   1574:                vsyslog(LOG_DEBUG, fmt, ap);
                   1575:        va_end(ap);
                   1576:        errno = savederrno;
                   1577: }
                   1578: #endif /* DEBUG */
                   1579:
                   1580: /* these are like warn() and err(), except for syslog not stderr */
                   1581: void
                   1582: warning(const char *fmt, ...)
                   1583: {
                   1584:        va_list ap;
                   1585:
                   1586:        va_start(ap, fmt);
                   1587:        if (sflag || isatty(STDERR_FILENO)) {
                   1588:                vfprintf(stderr, fmt, ap);
                   1589:                fputs("\n", stderr);
                   1590:        } else
                   1591:                vsyslog(LOG_INFO, fmt, ap);
                   1592:        va_end(ap);
                   1593: }
                   1594:
                   1595: void
                   1596: error(int code, const char *fmt, ...)
                   1597: {
                   1598:        va_list ap;
                   1599:
                   1600:        va_start(ap, fmt);
                   1601:        if (sflag || isatty(STDERR_FILENO)) {
                   1602:                vfprintf(stderr, fmt, ap);
                   1603:                fputs("\n", stderr);
                   1604:        } else
                   1605:                vsyslog(LOG_ERR, fmt, ap);
                   1606:        va_end(ap);
                   1607:        exit(code);
                   1608: }
                   1609:
                   1610: /* the follow functions and variables are used in handling HTTP errors */
                   1611: /* ARGSUSED */
                   1612: void
                   1613: http_error(int code, http_req *request, const char *msg)
                   1614: {
                   1615:        static  char buf[BUFSIZ];
                   1616:        char portbuf[20];
                   1617:        const char *header = http_errors_short(code);
                   1618:        const char *reason = http_errors_long(code);
                   1619:        const char *proto = (request && request->hr_proto) ? request->hr_proto : http_11;
                   1620:        int     size;
                   1621:
                   1622:        debug((DEBUG_FAT, "http_error %d: %s", code, msg));
                   1623:        if (header == NULL || reason == NULL)
                   1624:                error(1, "http_error() failed (short = %p, long = %p)",
                   1625:                    header, reason);
                   1626:
1.6       mrg      1627:        if (request && request->hr_serverport &&
                   1628:            strcmp(request->hr_serverport, "80") != 0)
1.1       tls      1629:                snprintf(portbuf, sizeof(portbuf), ":%s", request->hr_serverport);
                   1630:        else
                   1631:                portbuf[0] = '\0';
                   1632:
                   1633:        if (request && request->hr_url) {
                   1634:                escape_html(request);
                   1635:                size = snprintf(buf, sizeof buf,
                   1636:                    "<html><head><title>%s</title></head>\n"
                   1637:                    "<body><h1>%s</h1>\n"
                   1638:                    "%s: <pre>%s</pre>\n"
                   1639:                    "<hr><address><a href=\"http://%s%s/\">%s%s</a></address>\n"
                   1640:                    "</body></html>\n",
                   1641:                    header, header, request->hr_url, reason,
                   1642:                    myname, portbuf, myname, portbuf);
1.8     ! lukem    1643:                if (size >= (int)sizeof buf)
1.1       tls      1644:                        warning("http_error buffer too small, truncated");
                   1645:        } else
                   1646:                size = 0;
                   1647:
                   1648:        bozoprintf("%s %s\r\n", proto, header);
                   1649:        auth_check_401(request, code);
                   1650:
                   1651:        bozoprintf("Content-Type: text/html\r\n");
                   1652:        bozoprintf("Content-Length: %d\r\n", size);
                   1653:        bozoprintf("Server: %s\r\n", server_software);
                   1654:        if (request && request->hr_allow)
                   1655:                bozoprintf("Allow: %s\r\n", request->hr_allow);
                   1656:        bozoprintf("\r\n");
                   1657:        if (size)
                   1658:                bozoprintf("%s", buf);
                   1659:        bozoflush(stdout);
                   1660:
                   1661:        exit(1);
                   1662: }
                   1663:
                   1664: /* short map between error code, and short/long messages */
                   1665: static struct errors_map {
                   1666:        int     code;                   /* HTTP return code */
                   1667:        const char *shortmsg;           /* short version of message */
                   1668:        const char *longmsg;            /* long version of message */
                   1669: } errors_map[] = {
                   1670:        { 400,  "400 Bad Request",      "The request was not valid", },
                   1671:        { 401,  "401 Unauthorized",     "No authorization", },
1.6       mrg      1672:        { 403,  "403 Forbidden",        "Access to this item has been denied",},
1.1       tls      1673:        { 404,  "404 Not Found",        "This item has not been found", },
                   1674:        { 408,  "408 Request Timeout",  "This request took too long", },
                   1675:        { 417,  "417 Expectation Failed","Expectations not available", },
                   1676:        { 500,  "500 Internal Error",   "An error occured on the server", },
                   1677:        { 501,  "501 Not Implemented",  "This request is not available", },
                   1678:        { 0,    NULL,                   NULL, },
                   1679: };
                   1680:
                   1681: static const char *help = "DANGER! WILL ROBINSON! DANGER!";
                   1682:
                   1683: static const char *
                   1684: http_errors_short(int code)
                   1685: {
                   1686:        struct errors_map *ep;
                   1687:
                   1688:        for (ep = errors_map; ep->code; ep++)
                   1689:                if (ep->code == code)
                   1690:                        return (ep->shortmsg);
                   1691:        return (help);
                   1692: }
                   1693:
                   1694: static const char *
                   1695: http_errors_long(int code)
                   1696: {
                   1697:        struct errors_map *ep;
                   1698:
                   1699:        for (ep = errors_map; ep->code; ep++)
                   1700:                if (ep->code == code)
                   1701:                        return (ep->longmsg);
                   1702:        return (help);
                   1703: }
                   1704:
                   1705: /* Below are various modified libc functions */
                   1706:
                   1707: /*
                   1708:  * returns -1 in lenp if the string ran out before finding a delimiter,
                   1709:  * but is otherwise the same as strsep.  Note that the length must be
                   1710:  * correctly passed in.
                   1711:  */
                   1712: char *
1.6       mrg      1713: bozostrnsep(char **strp, const char *delim, ssize_t    *lenp)
1.1       tls      1714: {
                   1715:        char    *s;
                   1716:        const   char *spanp;
                   1717:        int     c, sc;
                   1718:        char    *tok;
                   1719:
                   1720:        if ((s = *strp) == NULL)
                   1721:                return (NULL);
                   1722:        for (tok = s;;) {
                   1723:                if (lenp && --(*lenp) == -1)
                   1724:                        return (NULL);
                   1725:                c = *s++;
                   1726:                spanp = delim;
                   1727:                do {
                   1728:                        if ((sc = *spanp++) == c) {
                   1729:                                if (c == 0)
                   1730:                                        s = NULL;
                   1731:                                else
                   1732:                                        s[-1] = '\0';
                   1733:                                *strp = s;
                   1734:                                return (tok);
                   1735:                        }
                   1736:                } while (sc != 0);
                   1737:        }
                   1738:        /* NOTREACHED */
                   1739: }
                   1740:
                   1741: /*
                   1742:  * inspired by fgetln(3), but works for fd's.  should work identically
                   1743:  * except it, however, does *not* return the newline, and it does nul
                   1744:  * terminate the string.
                   1745:  */
                   1746: char *
1.6       mrg      1747: bozodgetln(int fd, ssize_t *lenp, ssize_t (*readfn)(int, void *, size_t))
1.1       tls      1748: {
                   1749:        static  char *buffer;
                   1750:        static  ssize_t buflen = 0;
                   1751:        ssize_t len;
                   1752:        int     got_cr = 0;
                   1753:        char    c, *nbuffer;
                   1754:
                   1755:        /* initialise */
                   1756:        if (buflen == 0) {
                   1757:                buflen = 128;   /* should be plenty for most requests */
                   1758:                buffer = malloc(buflen);
                   1759:                if (buffer == NULL) {
                   1760:                        buflen = 0;
                   1761:                        return NULL;
                   1762:                }
                   1763:        }
                   1764:        len = 0;
                   1765:
                   1766:        /*
                   1767:         * we *have* to read one byte at a time, to not break cgi
                   1768:         * programs (for we pass stdin off to them).  could fix this
                   1769:         * by becoming a fd-passing program instead of just exec'ing
                   1770:         * the program
                   1771:         */
                   1772:        for (; readfn(fd, &c, 1) == 1; ) {
1.6       mrg      1773:                debug((DEBUG_EXPLODING, "bozodgetln read %c", c));
1.1       tls      1774:
                   1775:                if (len >= buflen - 1) {
                   1776:                        buflen *= 2;
1.6       mrg      1777:                        debug((DEBUG_EXPLODING, "bozodgetln: "
                   1778:                            "reallocating buffer to buflen %zu", buflen));
1.1       tls      1779:                        nbuffer = realloc(buffer, buflen);
                   1780:                        if (nbuffer == NULL) {
                   1781:                                free(buffer);
                   1782:                                buflen = 0;
                   1783:                                buffer = NULL;
                   1784:                                return NULL;
                   1785:                        }
                   1786:                        buffer = nbuffer;
                   1787:                }
                   1788:
                   1789:                buffer[len++] = c;
                   1790:                if (c == '\r') {
                   1791:                        got_cr = 1;
                   1792:                        continue;
                   1793:                } else if (c == '\n') {
                   1794:                        /*
                   1795:                         * HTTP/1.1 spec says to ignore CR and treat
                   1796:                         * LF as the real line terminator.  even though
                   1797:                         * the same spec defines CRLF as the line
                   1798:                         * terminator, it is recommended in section 19.3
                   1799:                         * to do the LF trick for tolerance.
                   1800:                         */
                   1801:                        if (got_cr)
                   1802:                                len -= 2;
                   1803:                        else
                   1804:                                len -= 1;
                   1805:                        break;
                   1806:                }
                   1807:
                   1808:        }
                   1809:        buffer[len] = '\0';
1.6       mrg      1810:        debug((DEBUG_OBESE, "bozodgetln returns: ``%s'' with len %d",
                   1811:               buffer, len));
1.1       tls      1812:        *lenp = len;
                   1813:        return (buffer);
                   1814: }
                   1815:
                   1816: void *
                   1817: bozorealloc(void *ptr, size_t size)
                   1818: {
                   1819:        void    *p;
                   1820:
                   1821:        p = realloc(ptr, size);
                   1822:        if (p == NULL)
                   1823:                http_error(500, NULL, "memory allocation failure");
                   1824:        return (p);
                   1825: }
                   1826:
                   1827: void *
                   1828: bozomalloc(size_t size)
                   1829: {
                   1830:        void    *p;
                   1831:
                   1832:        p = malloc(size);
                   1833:        if (p == NULL)
                   1834:                http_error(500, NULL, "memory allocation failure");
                   1835:        return (p);
                   1836: }
                   1837:
                   1838: char *
                   1839: bozostrdup(const char *str)
                   1840: {
                   1841:        char    *p;
                   1842:
                   1843:        p = strdup(str);
                   1844:        if (p == NULL)
                   1845:                http_error(500, NULL, "memory allocation failure");
                   1846:        return (p);
                   1847: }

CVSweb <webmaster@jp.NetBSD.org>