Annotation of embedaddon/thttpd/libhttpd.c, revision 1.1.1.1

1.1       misho       1: /* libhttpd.c - HTTP protocol library
                      2: **
                      3: ** Copyright © 1995,1998,1999,2000,2001 by Jef Poskanzer <jef@mail.acme.com>.
                      4: ** All rights reserved.
                      5: **
                      6: ** Redistribution and use in source and binary forms, with or without
                      7: ** modification, are permitted provided that the following conditions
                      8: ** are met:
                      9: ** 1. Redistributions of source code must retain the above copyright
                     10: **    notice, this list of conditions and the following disclaimer.
                     11: ** 2. Redistributions in binary form must reproduce the above copyright
                     12: **    notice, this list of conditions and the following disclaimer in the
                     13: **    documentation and/or other materials provided with the distribution.
                     14: **
                     15: ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
                     16: ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     17: ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     18: ** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
                     19: ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     20: ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     21: ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     22: ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     23: ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     24: ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     25: ** SUCH DAMAGE.
                     26: */
                     27: 
                     28: 
                     29: #include "config.h"
                     30: #include "version.h"
                     31: 
                     32: #ifdef SHOW_SERVER_VERSION
                     33: #define EXPOSED_SERVER_SOFTWARE SERVER_SOFTWARE
                     34: #else /* SHOW_SERVER_VERSION */
                     35: #define EXPOSED_SERVER_SOFTWARE "thttpd"
                     36: #endif /* SHOW_SERVER_VERSION */
                     37: 
                     38: #include <sys/types.h>
                     39: #include <sys/param.h>
                     40: #include <sys/stat.h>
                     41: 
                     42: #include <ctype.h>
                     43: #include <errno.h>
                     44: #include <fcntl.h>
                     45: #include <time.h>
                     46: #ifdef HAVE_MEMORY_H
                     47: #include <memory.h>
                     48: #endif /* HAVE_MEMORY_H */
                     49: #include <pwd.h>
                     50: #include <signal.h>
                     51: #include <stdio.h>
                     52: #include <stdlib.h>
                     53: #include <string.h>
                     54: #include <syslog.h>
                     55: #include <unistd.h>
                     56: #include <stdarg.h>
                     57: 
                     58: #ifdef HAVE_OSRELDATE_H
                     59: #include <osreldate.h>
                     60: #endif /* HAVE_OSRELDATE_H */
                     61: 
                     62: #ifdef HAVE_DIRENT_H
                     63: # include <dirent.h>
                     64: # define NAMLEN(dirent) strlen((dirent)->d_name)
                     65: #else
                     66: # define dirent direct
                     67: # define NAMLEN(dirent) (dirent)->d_namlen
                     68: # ifdef HAVE_SYS_NDIR_H
                     69: #  include <sys/ndir.h>
                     70: # endif
                     71: # ifdef HAVE_SYS_DIR_H
                     72: #  include <sys/dir.h>
                     73: # endif
                     74: # ifdef HAVE_NDIR_H
                     75: #  include <ndir.h>
                     76: # endif
                     77: #endif
                     78: 
                     79: extern char* crypt( const char* key, const char* setting );
                     80: 
                     81: #include "libhttpd.h"
                     82: #include "mmc.h"
                     83: #include "timers.h"
                     84: #include "match.h"
                     85: #include "tdate_parse.h"
                     86: 
                     87: #ifndef STDIN_FILENO
                     88: #define STDIN_FILENO 0
                     89: #endif
                     90: #ifndef STDOUT_FILENO
                     91: #define STDOUT_FILENO 1
                     92: #endif
                     93: #ifndef STDERR_FILENO
                     94: #define STDERR_FILENO 2
                     95: #endif
                     96: 
                     97: #ifndef SHUT_WR
                     98: #define SHUT_WR 1
                     99: #endif
                    100: 
                    101: #ifndef HAVE_INT64T
                    102: typedef long long int64_t;
                    103: #endif
                    104: 
                    105: #ifndef HAVE_SOCKLENT
                    106: typedef int socklen_t;
                    107: #endif
                    108: 
                    109: #ifdef __CYGWIN__
                    110: #define timezone  _timezone
                    111: #endif
                    112: 
                    113: #ifndef MAX
                    114: #define MAX(a,b) ((a) > (b) ? (a) : (b))
                    115: #endif
                    116: #ifndef MIN
                    117: #define MIN(a,b) ((a) < (b) ? (a) : (b))
                    118: #endif
                    119: 
                    120: 
                    121: /* Forwards. */
                    122: static void check_options( void );
                    123: static void free_httpd_server( httpd_server* hs );
                    124: static int initialize_listen_socket( httpd_sockaddr* saP );
                    125: static void add_response( httpd_conn* hc, char* str );
                    126: static void send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, off_t length, time_t mod );
                    127: static void send_response( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg );
                    128: static void send_response_tail( httpd_conn* hc );
                    129: static void defang( char* str, char* dfstr, int dfsize );
                    130: #ifdef ERR_DIR
                    131: static int send_err_file( httpd_conn* hc, int status, char* title, char* extraheads, char* filename );
                    132: #endif /* ERR_DIR */
                    133: #ifdef AUTH_FILE
                    134: static void send_authenticate( httpd_conn* hc, char* realm );
                    135: static int b64_decode( const char* str, unsigned char* space, int size );
                    136: static int auth_check( httpd_conn* hc, char* dirname  );
                    137: static int auth_check2( httpd_conn* hc, char* dirname  );
                    138: #endif /* AUTH_FILE */
                    139: static void send_dirredirect( httpd_conn* hc );
                    140: static int hexit( char c );
                    141: static void strdecode( char* to, char* from );
                    142: #ifdef GENERATE_INDEXES
                    143: static void strencode( char* to, int tosize, char* from );
                    144: #endif /* GENERATE_INDEXES */
                    145: #ifdef TILDE_MAP_1
                    146: static int tilde_map_1( httpd_conn* hc );
                    147: #endif /* TILDE_MAP_1 */
                    148: #ifdef TILDE_MAP_2
                    149: static int tilde_map_2( httpd_conn* hc );
                    150: #endif /* TILDE_MAP_2 */
                    151: static int vhost_map( httpd_conn* hc );
                    152: static char* expand_symlinks( char* path, char** restP, int no_symlink_check, int tildemapped );
                    153: static char* bufgets( httpd_conn* hc );
                    154: static void de_dotdot( char* file );
                    155: static void init_mime( void );
                    156: static void figure_mime( httpd_conn* hc );
                    157: #ifdef CGI_TIMELIMIT
                    158: static void cgi_kill2( ClientData client_data, struct timeval* nowP );
                    159: static void cgi_kill( ClientData client_data, struct timeval* nowP );
                    160: #endif /* CGI_TIMELIMIT */
                    161: #ifdef GENERATE_INDEXES
                    162: static int ls( httpd_conn* hc );
                    163: #endif /* GENERATE_INDEXES */
                    164: static char* build_env( char* fmt, char* arg );
                    165: #ifdef SERVER_NAME_LIST
                    166: static char* hostname_map( char* hostname );
                    167: #endif /* SERVER_NAME_LIST */
                    168: static char** make_envp( httpd_conn* hc );
                    169: static char** make_argp( httpd_conn* hc );
                    170: static void cgi_interpose_input( httpd_conn* hc, int wfd );
                    171: static void post_post_garbage_hack( httpd_conn* hc );
                    172: static void cgi_interpose_output( httpd_conn* hc, int rfd );
                    173: static void cgi_child( httpd_conn* hc );
                    174: static int cgi( httpd_conn* hc );
                    175: static int really_start_request( httpd_conn* hc, struct timeval* nowP );
                    176: static void make_log_entry( httpd_conn* hc, struct timeval* nowP );
                    177: static int check_referer( httpd_conn* hc );
                    178: static int really_check_referer( httpd_conn* hc );
                    179: static int sockaddr_check( httpd_sockaddr* saP );
                    180: static size_t sockaddr_len( httpd_sockaddr* saP );
                    181: static int my_snprintf( char* str, size_t size, const char* format, ... );
                    182: #ifndef HAVE_ATOLL
                    183: static long long atoll( const char* str );
                    184: #endif /* HAVE_ATOLL */
                    185: 
                    186: 
                    187: /* This global keeps track of whether we are in the main process or a
                    188: ** sub-process.  The reason is that httpd_write_response() can get called
                    189: ** in either context; when it is called from the main process it must use
                    190: ** non-blocking I/O to avoid stalling the server, but when it is called
                    191: ** from a sub-process it wants to use blocking I/O so that the whole
                    192: ** response definitely gets written.  So, it checks this variable.  A bit
                    193: ** of a hack but it seems to do the right thing.
                    194: */
                    195: static int sub_process = 0;
                    196: 
                    197: 
                    198: static void
                    199: check_options( void )
                    200:     {
                    201: #if defined(TILDE_MAP_1) && defined(TILDE_MAP_2)
                    202:     syslog( LOG_CRIT, "both TILDE_MAP_1 and TILDE_MAP_2 are defined" );
                    203:     exit( 1 );
                    204: #endif /* both */
                    205:     }
                    206: 
                    207: 
                    208: static void
                    209: free_httpd_server( httpd_server* hs )
                    210:     {
                    211:     if ( hs->binding_hostname != (char*) 0 )
                    212:        free( (void*) hs->binding_hostname );
                    213:     if ( hs->cwd != (char*) 0 )
                    214:        free( (void*) hs->cwd );
                    215:     if ( hs->cgi_pattern != (char*) 0 )
                    216:        free( (void*) hs->cgi_pattern );
                    217:     if ( hs->charset != (char*) 0 )
                    218:        free( (void*) hs->charset );
                    219:     if ( hs->p3p != (char*) 0 )
                    220:        free( (void*) hs->p3p );
                    221:     if ( hs->url_pattern != (char*) 0 )
                    222:        free( (void*) hs->url_pattern );
                    223:     if ( hs->local_pattern != (char*) 0 )
                    224:        free( (void*) hs->local_pattern );
                    225:     free( (void*) hs );
                    226:     }
                    227: 
                    228: 
                    229: httpd_server*
                    230: httpd_initialize(
                    231:     char* hostname, httpd_sockaddr* sa4P, httpd_sockaddr* sa6P,
                    232:     unsigned short port, char* cgi_pattern, int cgi_limit, char* charset,
                    233:     char* p3p, int max_age, char* cwd, int no_log, FILE* logfp,
                    234:     int no_symlink_check, int vhost, int global_passwd, char* url_pattern,
                    235:     char* local_pattern, int no_empty_referers )
                    236:     {
                    237:     httpd_server* hs;
                    238:     static char ghnbuf[256];
                    239:     char* cp;
                    240: 
                    241:     check_options();
                    242: 
                    243:     hs = NEW( httpd_server, 1 );
                    244:     if ( hs == (httpd_server*) 0 )
                    245:        {
                    246:        syslog( LOG_CRIT, "out of memory allocating an httpd_server" );
                    247:        return (httpd_server*) 0;
                    248:        }
                    249: 
                    250:     if ( hostname != (char*) 0 )
                    251:        {
                    252:        hs->binding_hostname = strdup( hostname );
                    253:        if ( hs->binding_hostname == (char*) 0 )
                    254:            {
                    255:            syslog( LOG_CRIT, "out of memory copying hostname" );
                    256:            return (httpd_server*) 0;
                    257:            }
                    258:        hs->server_hostname = hs->binding_hostname;
                    259:        }
                    260:     else
                    261:        {
                    262:        hs->binding_hostname = (char*) 0;
                    263:        hs->server_hostname = (char*) 0;
                    264:        if ( gethostname( ghnbuf, sizeof(ghnbuf) ) < 0 )
                    265:            ghnbuf[0] = '\0';
                    266: #ifdef SERVER_NAME_LIST
                    267:        if ( ghnbuf[0] != '\0' )
                    268:            hs->server_hostname = hostname_map( ghnbuf );
                    269: #endif /* SERVER_NAME_LIST */
                    270:        if ( hs->server_hostname == (char*) 0 )
                    271:            {
                    272: #ifdef SERVER_NAME
                    273:            hs->server_hostname = SERVER_NAME;
                    274: #else /* SERVER_NAME */
                    275:            if ( ghnbuf[0] != '\0' )
                    276:                hs->server_hostname = ghnbuf;
                    277: #endif /* SERVER_NAME */
                    278:            }
                    279:        }
                    280: 
                    281:     hs->port = port;
                    282:     if ( cgi_pattern == (char*) 0 )
                    283:        hs->cgi_pattern = (char*) 0;
                    284:     else
                    285:        {
                    286:        /* Nuke any leading slashes. */
                    287:        if ( cgi_pattern[0] == '/' )
                    288:            ++cgi_pattern;
                    289:        hs->cgi_pattern = strdup( cgi_pattern );
                    290:        if ( hs->cgi_pattern == (char*) 0 )
                    291:            {
                    292:            syslog( LOG_CRIT, "out of memory copying cgi_pattern" );
                    293:            return (httpd_server*) 0;
                    294:            }
                    295:        /* Nuke any leading slashes in the cgi pattern. */
                    296:        while ( ( cp = strstr( hs->cgi_pattern, "|/" ) ) != (char*) 0 )
                    297:            (void) strcpy( cp + 1, cp + 2 );
                    298:        }
                    299:     hs->cgi_limit = cgi_limit;
                    300:     hs->cgi_count = 0;
                    301:     hs->charset = strdup( charset );
                    302:     hs->p3p = strdup( p3p );
                    303:     hs->max_age = max_age;
                    304:     hs->cwd = strdup( cwd );
                    305:     if ( hs->cwd == (char*) 0 )
                    306:        {
                    307:        syslog( LOG_CRIT, "out of memory copying cwd" );
                    308:        return (httpd_server*) 0;
                    309:        }
                    310:     if ( url_pattern == (char*) 0 )
                    311:        hs->url_pattern = (char*) 0;
                    312:     else
                    313:        {
                    314:        hs->url_pattern = strdup( url_pattern );
                    315:        if ( hs->url_pattern == (char*) 0 )
                    316:            {
                    317:            syslog( LOG_CRIT, "out of memory copying url_pattern" );
                    318:            return (httpd_server*) 0;
                    319:            }
                    320:        }
                    321:     if ( local_pattern == (char*) 0 )
                    322:        hs->local_pattern = (char*) 0;
                    323:     else
                    324:        {
                    325:        hs->local_pattern = strdup( local_pattern );
                    326:        if ( hs->local_pattern == (char*) 0 )
                    327:            {
                    328:            syslog( LOG_CRIT, "out of memory copying local_pattern" );
                    329:            return (httpd_server*) 0;
                    330:            }
                    331:        }
                    332:     hs->no_log = no_log;
                    333:     hs->logfp = (FILE*) 0;
                    334:     httpd_set_logfp( hs, logfp );
                    335:     hs->no_symlink_check = no_symlink_check;
                    336:     hs->vhost = vhost;
                    337:     hs->global_passwd = global_passwd;
                    338:     hs->no_empty_referers = no_empty_referers;
                    339: 
                    340:     /* Initialize listen sockets.  Try v6 first because of a Linux peculiarity;
                    341:     ** like some other systems, it has magical v6 sockets that also listen for
                    342:     ** v4, but in Linux if you bind a v4 socket first then the v6 bind fails.
                    343:     */
                    344:     if ( sa6P == (httpd_sockaddr*) 0 )
                    345:        hs->listen6_fd = -1;
                    346:     else
                    347:        hs->listen6_fd = initialize_listen_socket( sa6P );
                    348:     if ( sa4P == (httpd_sockaddr*) 0 )
                    349:        hs->listen4_fd = -1;
                    350:     else
                    351:        hs->listen4_fd = initialize_listen_socket( sa4P );
                    352:     /* If we didn't get any valid sockets, fail. */
                    353:     if ( hs->listen4_fd == -1 && hs->listen6_fd == -1 )
                    354:        {
                    355:        free_httpd_server( hs );
                    356:        return (httpd_server*) 0;
                    357:        }
                    358: 
                    359:     init_mime();
                    360: 
                    361:     /* Done initializing. */
                    362:     if ( hs->binding_hostname == (char*) 0 )
                    363:        syslog(
                    364:            LOG_NOTICE, "%.80s starting on port %d", SERVER_SOFTWARE,
                    365:            (int) hs->port );
                    366:     else
                    367:        syslog(
                    368:            LOG_NOTICE, "%.80s starting on %.80s, port %d", SERVER_SOFTWARE,
                    369:            httpd_ntoa( hs->listen4_fd != -1 ? sa4P : sa6P ),
                    370:            (int) hs->port );
                    371:     return hs;
                    372:     }
                    373: 
                    374: 
                    375: static int
                    376: initialize_listen_socket( httpd_sockaddr* saP )
                    377:     {
                    378:     int listen_fd;
                    379:     int on, flags;
                    380: 
                    381:     /* Check sockaddr. */
                    382:     if ( ! sockaddr_check( saP ) )
                    383:        {
                    384:        syslog( LOG_CRIT, "unknown sockaddr family on listen socket" );
                    385:        return -1;
                    386:        }
                    387: 
                    388:     /* Create socket. */
                    389:     listen_fd = socket( saP->sa.sa_family, SOCK_STREAM, 0 );
                    390:     if ( listen_fd < 0 )
                    391:        {
                    392:        syslog( LOG_CRIT, "socket %.80s - %m", httpd_ntoa( saP ) );
                    393:        return -1;
                    394:        }
                    395:     (void) fcntl( listen_fd, F_SETFD, 1 );
                    396: 
                    397:     /* Allow reuse of local addresses. */
                    398:     on = 1;
                    399:     if ( setsockopt(
                    400:             listen_fd, SOL_SOCKET, SO_REUSEADDR, (char*) &on,
                    401:             sizeof(on) ) < 0 )
                    402:        syslog( LOG_CRIT, "setsockopt SO_REUSEADDR - %m" );
                    403: 
                    404:     /* Bind to it. */
                    405:     if ( bind( listen_fd, &saP->sa, sockaddr_len( saP ) ) < 0 )
                    406:        {
                    407:        syslog(
                    408:            LOG_CRIT, "bind %.80s - %m", httpd_ntoa( saP ) );
                    409:        (void) close( listen_fd );
                    410:        return -1;
                    411:        }
                    412: 
                    413:     /* Set the listen file descriptor to no-delay / non-blocking mode. */
                    414:     flags = fcntl( listen_fd, F_GETFL, 0 );
                    415:     if ( flags == -1 )
                    416:        {
                    417:        syslog( LOG_CRIT, "fcntl F_GETFL - %m" );
                    418:        (void) close( listen_fd );
                    419:        return -1;
                    420:        }
                    421:     if ( fcntl( listen_fd, F_SETFL, flags | O_NDELAY ) < 0 )
                    422:        {
                    423:        syslog( LOG_CRIT, "fcntl O_NDELAY - %m" );
                    424:        (void) close( listen_fd );
                    425:        return -1;
                    426:        }
                    427: 
                    428:     /* Start a listen going. */
                    429:     if ( listen( listen_fd, LISTEN_BACKLOG ) < 0 )
                    430:        {
                    431:        syslog( LOG_CRIT, "listen - %m" );
                    432:        (void) close( listen_fd );
                    433:        return -1;
                    434:        }
                    435: 
                    436:     /* Use accept filtering, if available. */
                    437: #ifdef SO_ACCEPTFILTER
                    438:     {
                    439: #if ( __FreeBSD_version >= 411000 )
                    440: #define ACCEPT_FILTER_NAME "httpready"
                    441: #else
                    442: #define ACCEPT_FILTER_NAME "dataready"
                    443: #endif
                    444:     struct accept_filter_arg af;
                    445:     (void) bzero( &af, sizeof(af) );
                    446:     (void) strcpy( af.af_name, ACCEPT_FILTER_NAME );
                    447:     (void) setsockopt(
                    448:        listen_fd, SOL_SOCKET, SO_ACCEPTFILTER, (char*) &af, sizeof(af) );
                    449:     }
                    450: #endif /* SO_ACCEPTFILTER */
                    451: 
                    452:     return listen_fd;
                    453:     }
                    454: 
                    455: 
                    456: void
                    457: httpd_set_logfp( httpd_server* hs, FILE* logfp )
                    458:     {
                    459:     if ( hs->logfp != (FILE*) 0 )
                    460:        (void) fclose( hs->logfp );
                    461:     hs->logfp = logfp;
                    462:     }
                    463: 
                    464: 
                    465: void
                    466: httpd_terminate( httpd_server* hs )
                    467:     {
                    468:     httpd_unlisten( hs );
                    469:     if ( hs->logfp != (FILE*) 0 )
                    470:        (void) fclose( hs->logfp );
                    471:     free_httpd_server( hs );
                    472:     }
                    473: 
                    474: 
                    475: void
                    476: httpd_unlisten( httpd_server* hs )
                    477:     {
                    478:     if ( hs->listen4_fd != -1 )
                    479:        {
                    480:        (void) close( hs->listen4_fd );
                    481:        hs->listen4_fd = -1;
                    482:        }
                    483:     if ( hs->listen6_fd != -1 )
                    484:        {
                    485:        (void) close( hs->listen6_fd );
                    486:        hs->listen6_fd = -1;
                    487:        }
                    488:     }
                    489: 
                    490: 
                    491: /* Conditional macro to allow two alternate forms for use in the built-in
                    492: ** error pages.  If EXPLICIT_ERROR_PAGES is defined, the second and more
                    493: ** explicit error form is used; otherwise, the first and more generic
                    494: ** form is used.
                    495: */
                    496: #ifdef EXPLICIT_ERROR_PAGES
                    497: #define ERROR_FORM(a,b) b
                    498: #else /* EXPLICIT_ERROR_PAGES */
                    499: #define ERROR_FORM(a,b) a
                    500: #endif /* EXPLICIT_ERROR_PAGES */
                    501: 
                    502: 
                    503: static char* ok200title = "OK";
                    504: static char* ok206title = "Partial Content";
                    505: 
                    506: static char* err302title = "Found";
                    507: static char* err302form = "The actual URL is '%.80s'.\n";
                    508: 
                    509: static char* err304title = "Not Modified";
                    510: 
                    511: char* httpd_err400title = "Bad Request";
                    512: char* httpd_err400form =
                    513:     "Your request has bad syntax or is inherently impossible to satisfy.\n";
                    514: 
                    515: #ifdef AUTH_FILE
                    516: static char* err401title = "Unauthorized";
                    517: static char* err401form =
                    518:     "Authorization required for the URL '%.80s'.\n";
                    519: #endif /* AUTH_FILE */
                    520: 
                    521: static char* err403title = "Forbidden";
                    522: #ifndef EXPLICIT_ERROR_PAGES
                    523: static char* err403form =
                    524:     "You do not have permission to get URL '%.80s' from this server.\n";
                    525: #endif /* !EXPLICIT_ERROR_PAGES */
                    526: 
                    527: static char* err404title = "Not Found";
                    528: static char* err404form =
                    529:     "The requested URL '%.80s' was not found on this server.\n";
                    530: 
                    531: char* httpd_err408title = "Request Timeout";
                    532: char* httpd_err408form =
                    533:     "No request appeared within a reasonable time period.\n";
                    534: 
                    535: static char* err500title = "Internal Error";
                    536: static char* err500form =
                    537:     "There was an unusual problem serving the requested URL '%.80s'.\n";
                    538: 
                    539: static char* err501title = "Not Implemented";
                    540: static char* err501form =
                    541:     "The requested method '%.80s' is not implemented by this server.\n";
                    542: 
                    543: char* httpd_err503title = "Service Temporarily Overloaded";
                    544: char* httpd_err503form =
                    545:     "The requested URL '%.80s' is temporarily overloaded.  Please try again later.\n";
                    546: 
                    547: 
                    548: /* Append a string to the buffer waiting to be sent as response. */
                    549: static void
                    550: add_response( httpd_conn* hc, char* str )
                    551:     {
                    552:     size_t len;
                    553: 
                    554:     len = strlen( str );
                    555:     httpd_realloc_str( &hc->response, &hc->maxresponse, hc->responselen + len );
                    556:     (void) memmove( &(hc->response[hc->responselen]), str, len );
                    557:     hc->responselen += len;
                    558:     }
                    559: 
                    560: /* Send the buffered response. */
                    561: void
                    562: httpd_write_response( httpd_conn* hc )
                    563:     {
                    564:     /* If we are in a sub-process, turn off no-delay mode. */
                    565:     if ( sub_process )
                    566:        httpd_clear_ndelay( hc->conn_fd );
                    567:     /* Send the response, if necessary. */
                    568:     if ( hc->responselen > 0 )
                    569:        {
                    570:        (void) httpd_write_fully( hc->conn_fd, hc->response, hc->responselen );
                    571:        hc->responselen = 0;
                    572:        }
                    573:     }
                    574: 
                    575: 
                    576: /* Set no-delay / non-blocking mode on a socket. */
                    577: void
                    578: httpd_set_ndelay( int fd )
                    579:     {
                    580:     int flags, newflags;
                    581: 
                    582:     flags = fcntl( fd, F_GETFL, 0 );
                    583:     if ( flags != -1 )
                    584:        {
                    585:        newflags = flags | (int) O_NDELAY;
                    586:        if ( newflags != flags )
                    587:            (void) fcntl( fd, F_SETFL, newflags );
                    588:        }
                    589:     }
                    590: 
                    591: 
                    592: /* Clear no-delay / non-blocking mode on a socket. */
                    593: void
                    594: httpd_clear_ndelay( int fd )
                    595:     {
                    596:     int flags, newflags;
                    597: 
                    598:     flags = fcntl( fd, F_GETFL, 0 );
                    599:     if ( flags != -1 )
                    600:        {
                    601:        newflags = flags & ~ (int) O_NDELAY;
                    602:        if ( newflags != flags )
                    603:            (void) fcntl( fd, F_SETFL, newflags );
                    604:        }
                    605:     }
                    606: 
                    607: 
                    608: static void
                    609: send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, off_t length, time_t mod )
                    610:     {
                    611:     time_t now, expires;
                    612:     const char* rfc1123fmt = "%a, %d %b %Y %H:%M:%S GMT";
                    613:     char nowbuf[100];
                    614:     char modbuf[100];
                    615:     char expbuf[100];
                    616:     char fixed_type[500];
                    617:     char buf[1000];
                    618:     int partial_content;
                    619:     int s100;
                    620: 
                    621:     hc->status = status;
                    622:     hc->bytes_to_send = length;
                    623:     if ( hc->mime_flag )
                    624:        {
                    625:        if ( status == 200 && hc->got_range &&
                    626:             ( hc->last_byte_index >= hc->first_byte_index ) &&
                    627:             ( ( hc->last_byte_index != length - 1 ) ||
                    628:               ( hc->first_byte_index != 0 ) ) &&
                    629:             ( hc->range_if == (time_t) -1 ||
                    630:               hc->range_if == hc->sb.st_mtime ) )
                    631:            {
                    632:            partial_content = 1;
                    633:            hc->status = status = 206;
                    634:            title = ok206title;
                    635:            }
                    636:        else
                    637:            {
                    638:            partial_content = 0;
                    639:            hc->got_range = 0;
                    640:            }
                    641: 
                    642:        now = time( (time_t*) 0 );
                    643:        if ( mod == (time_t) 0 )
                    644:            mod = now;
                    645:        (void) strftime( nowbuf, sizeof(nowbuf), rfc1123fmt, gmtime( &now ) );
                    646:        (void) strftime( modbuf, sizeof(modbuf), rfc1123fmt, gmtime( &mod ) );
                    647:        (void) my_snprintf(
                    648:            fixed_type, sizeof(fixed_type), type, hc->hs->charset );
                    649:        (void) my_snprintf( buf, sizeof(buf),
                    650:            "%.20s %d %s\015\012Server: %s\015\012Content-Type: %s\015\012Date: %s\015\012Last-Modified: %s\015\012Accept-Ranges: bytes\015\012Connection: close\015\012",
                    651:            hc->protocol, status, title, EXPOSED_SERVER_SOFTWARE, fixed_type,
                    652:            nowbuf, modbuf );
                    653:        add_response( hc, buf );
                    654:        s100 = status / 100;
                    655:        if ( s100 != 2 && s100 != 3 )
                    656:            {
                    657:            (void) my_snprintf( buf, sizeof(buf),
                    658:                "Cache-Control: no-cache,no-store\015\012" );
                    659:            add_response( hc, buf );
                    660:            }
                    661:        if ( encodings[0] != '\0' )
                    662:            {
                    663:            (void) my_snprintf( buf, sizeof(buf),
                    664:                "Content-Encoding: %s\015\012", encodings );
                    665:            add_response( hc, buf );
                    666:            }
                    667:        if ( partial_content )
                    668:            {
                    669:            (void) my_snprintf( buf, sizeof(buf),
                    670:                "Content-Range: bytes %lld-%lld/%lld\015\012Content-Length: %lld\015\012",
                    671:                (int64_t) hc->first_byte_index, (int64_t) hc->last_byte_index,
                    672:                (int64_t) length,
                    673:                (int64_t) ( hc->last_byte_index - hc->first_byte_index + 1 ) );
                    674:            add_response( hc, buf );
                    675:            }
                    676:        else if ( length >= 0 )
                    677:            {
                    678:            (void) my_snprintf( buf, sizeof(buf),
                    679:                "Content-Length: %lld\015\012", (int64_t) length );
                    680:            add_response( hc, buf );
                    681:            }
                    682:        if ( hc->hs->p3p[0] != '\0' )
                    683:            {
                    684:            (void) my_snprintf( buf, sizeof(buf), "P3P: %s\015\012", hc->hs->p3p );
                    685:            add_response( hc, buf );
                    686:            }
                    687:        if ( hc->hs->max_age >= 0 )
                    688:            {
                    689:            expires = now + hc->hs->max_age;
                    690:            (void) strftime(
                    691:                expbuf, sizeof(expbuf), rfc1123fmt, gmtime( &expires ) );
                    692:            (void) my_snprintf( buf, sizeof(buf),
                    693:                "Cache-Control: max-age=%d\015\012Expires: %s\015\012",
                    694:                hc->hs->max_age, expbuf );
                    695:            add_response( hc, buf );
                    696:            }
                    697:        if ( extraheads[0] != '\0' )
                    698:            add_response( hc, extraheads );
                    699:        add_response( hc, "\015\012" );
                    700:        }
                    701:     }
                    702: 
                    703: 
                    704: static int str_alloc_count = 0;
                    705: static size_t str_alloc_size = 0;
                    706: 
                    707: void
                    708: httpd_realloc_str( char** strP, size_t* maxsizeP, size_t size )
                    709:     {
                    710:     if ( *maxsizeP == 0 )
                    711:        {
                    712:        *maxsizeP = MAX( 200, size + 100 );
                    713:        *strP = NEW( char, *maxsizeP + 1 );
                    714:        ++str_alloc_count;
                    715:        str_alloc_size += *maxsizeP;
                    716:        }
                    717:     else if ( size > *maxsizeP )
                    718:        {
                    719:        str_alloc_size -= *maxsizeP;
                    720:        *maxsizeP = MAX( *maxsizeP * 2, size * 5 / 4 );
                    721:        *strP = RENEW( *strP, char, *maxsizeP + 1 );
                    722:        str_alloc_size += *maxsizeP;
                    723:        }
                    724:     else
                    725:        return;
                    726:     if ( *strP == (char*) 0 )
                    727:        {
                    728:        syslog(
                    729:            LOG_ERR, "out of memory reallocating a string to %d bytes",
                    730:            *maxsizeP );
                    731:        exit( 1 );
                    732:        }
                    733:     }
                    734: 
                    735: 
                    736: static void
                    737: send_response( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg )
                    738:     {
                    739:     char defanged_arg[1000], buf[2000];
                    740: 
                    741:     send_mime(
                    742:        hc, status, title, "", extraheads, "text/html; charset=%s", (off_t) -1,
                    743:        (time_t) 0 );
                    744:     (void) my_snprintf( buf, sizeof(buf), "\
                    745: <HTML>\n\
                    746: <HEAD><TITLE>%d %s</TITLE></HEAD>\n\
                    747: <BODY BGCOLOR=\"#cc9999\" TEXT=\"#000000\" LINK=\"#2020ff\" VLINK=\"#4040cc\">\n\
                    748: <H2>%d %s</H2>\n",
                    749:        status, title, status, title );
                    750:     add_response( hc, buf );
                    751:     defang( arg, defanged_arg, sizeof(defanged_arg) );
                    752:     (void) my_snprintf( buf, sizeof(buf), form, defanged_arg );
                    753:     add_response( hc, buf );
                    754:     if ( match( "**MSIE**", hc->useragent ) )
                    755:        {
                    756:        int n;
                    757:        add_response( hc, "<!--\n" );
                    758:        for ( n = 0; n < 6; ++n )
                    759:            add_response( hc, "Padding so that MSIE deigns to show this error instead of its own canned one.\n");
                    760:        add_response( hc, "-->\n" );
                    761:        }
                    762:     send_response_tail( hc );
                    763:     }
                    764: 
                    765: 
                    766: static void
                    767: send_response_tail( httpd_conn* hc )
                    768:     {
                    769:     char buf[1000];
                    770: 
                    771:     (void) my_snprintf( buf, sizeof(buf), "\
                    772: <HR>\n\
                    773: <ADDRESS><A HREF=\"%s\">%s</A></ADDRESS>\n\
                    774: </BODY>\n\
                    775: </HTML>\n",
                    776:        SERVER_ADDRESS, EXPOSED_SERVER_SOFTWARE );
                    777:     add_response( hc, buf );
                    778:     }
                    779: 
                    780: 
                    781: static void
                    782: defang( char* str, char* dfstr, int dfsize )
                    783:     {
                    784:     char* cp1;
                    785:     char* cp2;
                    786: 
                    787:     for ( cp1 = str, cp2 = dfstr;
                    788:          *cp1 != '\0' && cp2 - dfstr < dfsize - 5;
                    789:          ++cp1, ++cp2 )
                    790:        {
                    791:        switch ( *cp1 )
                    792:            {
                    793:            case '<':
                    794:            *cp2++ = '&';
                    795:            *cp2++ = 'l';
                    796:            *cp2++ = 't';
                    797:            *cp2 = ';';
                    798:            break;
                    799:            case '>':
                    800:            *cp2++ = '&';
                    801:            *cp2++ = 'g';
                    802:            *cp2++ = 't';
                    803:            *cp2 = ';';
                    804:            break;
                    805:            default:
                    806:            *cp2 = *cp1;
                    807:            break;
                    808:            }
                    809:        }
                    810:     *cp2 = '\0';
                    811:     }
                    812: 
                    813: 
                    814: void
                    815: httpd_send_err( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg )
                    816:     {
                    817: #ifdef ERR_DIR
                    818: 
                    819:     char filename[1000];
                    820: 
                    821:     /* Try virtual host error page. */
                    822:     if ( hc->hs->vhost && hc->hostdir[0] != '\0' )
                    823:        {
                    824:        (void) my_snprintf( filename, sizeof(filename),
                    825:            "%s/%s/err%d.html", hc->hostdir, ERR_DIR, status );
                    826:        if ( send_err_file( hc, status, title, extraheads, filename ) )
                    827:            return;
                    828:        }
                    829: 
                    830:     /* Try server-wide error page. */
                    831:     (void) my_snprintf( filename, sizeof(filename),
                    832:        "%s/err%d.html", ERR_DIR, status );
                    833:     if ( send_err_file( hc, status, title, extraheads, filename ) )
                    834:        return;
                    835: 
                    836:     /* Fall back on built-in error page. */
                    837:     send_response( hc, status, title, extraheads, form, arg );
                    838: 
                    839: #else /* ERR_DIR */
                    840: 
                    841:     send_response( hc, status, title, extraheads, form, arg );
                    842: 
                    843: #endif /* ERR_DIR */
                    844:     }
                    845: 
                    846: 
                    847: #ifdef ERR_DIR
                    848: static int
                    849: send_err_file( httpd_conn* hc, int status, char* title, char* extraheads, char* filename )
                    850:     {
                    851:     FILE* fp;
                    852:     char buf[1000];
                    853:     size_t r;
                    854: 
                    855:     fp = fopen( filename, "r" );
                    856:     if ( fp == (FILE*) 0 )
                    857:        return 0;
                    858:     send_mime(
                    859:        hc, status, title, "", extraheads, "text/html; charset=%s", (off_t) -1,
                    860:        (time_t) 0 );
                    861:     for (;;)
                    862:        {
                    863:        r = fread( buf, 1, sizeof(buf) - 1, fp );
                    864:        if ( r == 0 )
                    865:            break;
                    866:        buf[r] = '\0';
                    867:        add_response( hc, buf );
                    868:        }
                    869:     (void) fclose( fp );
                    870: 
                    871: #ifdef ERR_APPEND_SERVER_INFO
                    872:     send_response_tail( hc );
                    873: #endif /* ERR_APPEND_SERVER_INFO */
                    874: 
                    875:     return 1;
                    876:     }
                    877: #endif /* ERR_DIR */
                    878: 
                    879: 
                    880: #ifdef AUTH_FILE
                    881: 
                    882: static void
                    883: send_authenticate( httpd_conn* hc, char* realm )
                    884:     {
                    885:     static char* header;
                    886:     static size_t maxheader = 0;
                    887:     static char headstr[] = "WWW-Authenticate: Basic realm=\"";
                    888: 
                    889:     httpd_realloc_str(
                    890:        &header, &maxheader, sizeof(headstr) + strlen( realm ) + 3 );
                    891:     (void) my_snprintf( header, maxheader, "%s%s\"\015\012", headstr, realm );
                    892:     httpd_send_err( hc, 401, err401title, header, err401form, hc->encodedurl );
                    893:     /* If the request was a POST then there might still be data to be read,
                    894:     ** so we need to do a lingering close.
                    895:     */
                    896:     if ( hc->method == METHOD_POST )
                    897:        hc->should_linger = 1;
                    898:     }
                    899: 
                    900: 
                    901: /* Base-64 decoding.  This represents binary data as printable ASCII
                    902: ** characters.  Three 8-bit binary bytes are turned into four 6-bit
                    903: ** values, like so:
                    904: **
                    905: **   [11111111]  [22222222]  [33333333]
                    906: **
                    907: **   [111111] [112222] [222233] [333333]
                    908: **
                    909: ** Then the 6-bit values are represented using the characters "A-Za-z0-9+/".
                    910: */
                    911: 
                    912: static int b64_decode_table[256] = {
                    913:     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 00-0F */
                    914:     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 10-1F */
                    915:     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,  /* 20-2F */
                    916:     52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,  /* 30-3F */
                    917:     -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,  /* 40-4F */
                    918:     15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,  /* 50-5F */
                    919:     -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,  /* 60-6F */
                    920:     41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,  /* 70-7F */
                    921:     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 80-8F */
                    922:     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 90-9F */
                    923:     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* A0-AF */
                    924:     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* B0-BF */
                    925:     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* C0-CF */
                    926:     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* D0-DF */
                    927:     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* E0-EF */
                    928:     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1   /* F0-FF */
                    929:     };
                    930: 
                    931: /* Do base-64 decoding on a string.  Ignore any non-base64 bytes.
                    932: ** Return the actual number of bytes generated.  The decoded size will
                    933: ** be at most 3/4 the size of the encoded, and may be smaller if there
                    934: ** are padding characters (blanks, newlines).
                    935: */
                    936: static int
                    937: b64_decode( const char* str, unsigned char* space, int size )
                    938:     {
                    939:     const char* cp;
                    940:     int space_idx, phase;
                    941:     int d, prev_d = 0;
                    942:     unsigned char c;
                    943: 
                    944:     space_idx = 0;
                    945:     phase = 0;
                    946:     for ( cp = str; *cp != '\0'; ++cp )
                    947:        {
                    948:        d = b64_decode_table[(int) *cp];
                    949:        if ( d != -1 )
                    950:            {
                    951:            switch ( phase )
                    952:                {
                    953:                case 0:
                    954:                ++phase;
                    955:                break;
                    956:                case 1:
                    957:                c = ( ( prev_d << 2 ) | ( ( d & 0x30 ) >> 4 ) );
                    958:                if ( space_idx < size )
                    959:                    space[space_idx++] = c;
                    960:                ++phase;
                    961:                break;
                    962:                case 2:
                    963:                c = ( ( ( prev_d & 0xf ) << 4 ) | ( ( d & 0x3c ) >> 2 ) );
                    964:                if ( space_idx < size )
                    965:                    space[space_idx++] = c;
                    966:                ++phase;
                    967:                break;
                    968:                case 3:
                    969:                c = ( ( ( prev_d & 0x03 ) << 6 ) | d );
                    970:                if ( space_idx < size )
                    971:                    space[space_idx++] = c;
                    972:                phase = 0;
                    973:                break;
                    974:                }
                    975:            prev_d = d;
                    976:            }
                    977:        }
                    978:     return space_idx;
                    979:     }
                    980: 
                    981: 
                    982: /* Returns -1 == unauthorized, 0 == no auth file, 1 = authorized. */
                    983: static int
                    984: auth_check( httpd_conn* hc, char* dirname  )
                    985:     {
                    986:     if ( hc->hs->global_passwd )
                    987:        {
                    988:        char* topdir;
                    989:        if ( hc->hs->vhost && hc->hostdir[0] != '\0' )
                    990:            topdir = hc->hostdir;
                    991:        else
                    992:            topdir = ".";
                    993:        switch ( auth_check2( hc, topdir ) )
                    994:            {
                    995:            case -1:
                    996:            return -1;
                    997:            case 1:
                    998:            return 1;
                    999:            }
                   1000:        }
                   1001:     return auth_check2( hc, dirname );
                   1002:     }
                   1003: 
                   1004: 
                   1005: /* Returns -1 == unauthorized, 0 == no auth file, 1 = authorized. */
                   1006: static int
                   1007: auth_check2( httpd_conn* hc, char* dirname  )
                   1008:     {
                   1009:     static char* authpath;
                   1010:     static size_t maxauthpath = 0;
                   1011:     struct stat sb;
                   1012:     char authinfo[500];
                   1013:     char* authpass;
                   1014:     char* colon;
                   1015:     int l;
                   1016:     FILE* fp;
                   1017:     char line[500];
                   1018:     char* cryp;
                   1019:     static char* prevauthpath;
                   1020:     static size_t maxprevauthpath = 0;
                   1021:     static time_t prevmtime;
                   1022:     static char* prevuser;
                   1023:     static size_t maxprevuser = 0;
                   1024:     static char* prevcryp;
                   1025:     static size_t maxprevcryp = 0;
                   1026: 
                   1027:     /* Construct auth filename. */
                   1028:     httpd_realloc_str(
                   1029:        &authpath, &maxauthpath, strlen( dirname ) + 1 + sizeof(AUTH_FILE) );
                   1030:     (void) my_snprintf( authpath, maxauthpath, "%s/%s", dirname, AUTH_FILE );
                   1031: 
                   1032:     /* Does this directory have an auth file? */
                   1033:     if ( stat( authpath, &sb ) < 0 )
                   1034:        /* Nope, let the request go through. */
                   1035:        return 0;
                   1036: 
                   1037:     /* Does this request contain basic authorization info? */
                   1038:     if ( hc->authorization[0] == '\0' ||
                   1039:         strncmp( hc->authorization, "Basic ", 6 ) != 0 )
                   1040:        {
                   1041:        /* Nope, return a 401 Unauthorized. */
                   1042:        send_authenticate( hc, dirname );
                   1043:        return -1;
                   1044:        }
                   1045: 
                   1046:     /* Decode it. */
                   1047:     l = b64_decode(
                   1048:        &(hc->authorization[6]), (unsigned char*) authinfo,
                   1049:        sizeof(authinfo) - 1 );
                   1050:     authinfo[l] = '\0';
                   1051:     /* Split into user and password. */
                   1052:     authpass = strchr( authinfo, ':' );
                   1053:     if ( authpass == (char*) 0 )
                   1054:        {
                   1055:        /* No colon?  Bogus auth info. */
                   1056:        send_authenticate( hc, dirname );
                   1057:        return -1;
                   1058:        }
                   1059:     *authpass++ = '\0';
                   1060:     /* If there are more fields, cut them off. */
                   1061:     colon = strchr( authpass, ':' );
                   1062:     if ( colon != (char*) 0 )
                   1063:        *colon = '\0';
                   1064: 
                   1065:     /* See if we have a cached entry and can use it. */
                   1066:     if ( maxprevauthpath != 0 &&
                   1067:         strcmp( authpath, prevauthpath ) == 0 &&
                   1068:         sb.st_mtime == prevmtime &&
                   1069:         strcmp( authinfo, prevuser ) == 0 )
                   1070:        {
                   1071:        /* Yes.  Check against the cached encrypted password. */
                   1072:        if ( strcmp( crypt( authpass, prevcryp ), prevcryp ) == 0 )
                   1073:            {
                   1074:            /* Ok! */
                   1075:            httpd_realloc_str(
                   1076:                &hc->remoteuser, &hc->maxremoteuser, strlen( authinfo ) );
                   1077:            (void) strcpy( hc->remoteuser, authinfo );
                   1078:            return 1;
                   1079:            }
                   1080:        else
                   1081:            {
                   1082:            /* No. */
                   1083:            send_authenticate( hc, dirname );
                   1084:            return -1;
                   1085:            }
                   1086:        }
                   1087: 
                   1088:     /* Open the password file. */
                   1089:     fp = fopen( authpath, "r" );
                   1090:     if ( fp == (FILE*) 0 )
                   1091:        {
                   1092:        /* The file exists but we can't open it?  Disallow access. */
                   1093:        syslog(
                   1094:            LOG_ERR, "%.80s auth file %.80s could not be opened - %m",
                   1095:            httpd_ntoa( &hc->client_addr ), authpath );
                   1096:        httpd_send_err(
                   1097:            hc, 403, err403title, "",
                   1098:            ERROR_FORM( err403form, "The requested URL '%.80s' is protected by an authentication file, but the authentication file cannot be opened.\n" ),
                   1099:            hc->encodedurl );
                   1100:        return -1;
                   1101:        }
                   1102: 
                   1103:     /* Read it. */
                   1104:     while ( fgets( line, sizeof(line), fp ) != (char*) 0 )
                   1105:        {
                   1106:        /* Nuke newline. */
                   1107:        l = strlen( line );
                   1108:        if ( line[l - 1] == '\n' )
                   1109:            line[l - 1] = '\0';
                   1110:        /* Split into user and encrypted password. */
                   1111:        cryp = strchr( line, ':' );
                   1112:        if ( cryp == (char*) 0 )
                   1113:            continue;
                   1114:        *cryp++ = '\0';
                   1115:        /* Is this the right user? */
                   1116:        if ( strcmp( line, authinfo ) == 0 )
                   1117:            {
                   1118:            /* Yes. */
                   1119:            (void) fclose( fp );
                   1120:            /* So is the password right? */
                   1121:            if ( strcmp( crypt( authpass, cryp ), cryp ) == 0 )
                   1122:                {
                   1123:                /* Ok! */
                   1124:                httpd_realloc_str(
                   1125:                    &hc->remoteuser, &hc->maxremoteuser, strlen( line ) );
                   1126:                (void) strcpy( hc->remoteuser, line );
                   1127:                /* And cache this user's info for next time. */
                   1128:                httpd_realloc_str(
                   1129:                    &prevauthpath, &maxprevauthpath, strlen( authpath ) );
                   1130:                (void) strcpy( prevauthpath, authpath );
                   1131:                prevmtime = sb.st_mtime;
                   1132:                httpd_realloc_str(
                   1133:                    &prevuser, &maxprevuser, strlen( authinfo ) );
                   1134:                (void) strcpy( prevuser, authinfo );
                   1135:                httpd_realloc_str( &prevcryp, &maxprevcryp, strlen( cryp ) );
                   1136:                (void) strcpy( prevcryp, cryp );
                   1137:                return 1;
                   1138:                }
                   1139:            else
                   1140:                {
                   1141:                /* No. */
                   1142:                send_authenticate( hc, dirname );
                   1143:                return -1;
                   1144:                }
                   1145:            }
                   1146:        }
                   1147: 
                   1148:     /* Didn't find that user.  Access denied. */
                   1149:     (void) fclose( fp );
                   1150:     send_authenticate( hc, dirname );
                   1151:     return -1;
                   1152:     }
                   1153: 
                   1154: #endif /* AUTH_FILE */
                   1155: 
                   1156: 
                   1157: static void
                   1158: send_dirredirect( httpd_conn* hc )
                   1159:     {
                   1160:     static char* location;
                   1161:     static char* header;
                   1162:     static size_t maxlocation = 0, maxheader = 0;
                   1163:     static char headstr[] = "Location: ";
                   1164: 
                   1165:     if ( hc->query[0] != '\0')
                   1166:        {
                   1167:        char* cp = strchr( hc->encodedurl, '?' );
                   1168:        if ( cp != (char*) 0 )  /* should always find it */
                   1169:            *cp = '\0';
                   1170:        httpd_realloc_str(
                   1171:            &location, &maxlocation,
                   1172:            strlen( hc->encodedurl ) + 2 + strlen( hc->query ) );
                   1173:        (void) my_snprintf( location, maxlocation,
                   1174:            "%s/?%s", hc->encodedurl, hc->query );
                   1175:        }
                   1176:     else
                   1177:        {
                   1178:        httpd_realloc_str(
                   1179:            &location, &maxlocation, strlen( hc->encodedurl ) + 1 );
                   1180:        (void) my_snprintf( location, maxlocation,
                   1181:            "%s/", hc->encodedurl );
                   1182:        }
                   1183:     httpd_realloc_str(
                   1184:        &header, &maxheader, sizeof(headstr) + strlen( location ) );
                   1185:     (void) my_snprintf( header, maxheader,
                   1186:        "%s%s\015\012", headstr, location );
                   1187:     send_response( hc, 302, err302title, header, err302form, location );
                   1188:     }
                   1189: 
                   1190: 
                   1191: char*
                   1192: httpd_method_str( int method )
                   1193:     {
                   1194:     switch ( method )
                   1195:        {
                   1196:        case METHOD_GET: return "GET";
                   1197:        case METHOD_HEAD: return "HEAD";
                   1198:        case METHOD_POST: return "POST";
                   1199:        default: return "UNKNOWN";
                   1200:        }
                   1201:     }
                   1202: 
                   1203: 
                   1204: static int
                   1205: hexit( char c )
                   1206:     {
                   1207:     if ( c >= '0' && c <= '9' )
                   1208:        return c - '0';
                   1209:     if ( c >= 'a' && c <= 'f' )
                   1210:        return c - 'a' + 10;
                   1211:     if ( c >= 'A' && c <= 'F' )
                   1212:        return c - 'A' + 10;
                   1213:     return 0;           /* shouldn't happen, we're guarded by isxdigit() */
                   1214:     }
                   1215: 
                   1216: 
                   1217: /* Copies and decodes a string.  It's ok for from and to to be the
                   1218: ** same string.
                   1219: */
                   1220: static void
                   1221: strdecode( char* to, char* from )
                   1222:     {
                   1223:     for ( ; *from != '\0'; ++to, ++from )
                   1224:        {
                   1225:        if ( from[0] == '%' && isxdigit( from[1] ) && isxdigit( from[2] ) )
                   1226:            {
                   1227:            *to = hexit( from[1] ) * 16 + hexit( from[2] );
                   1228:            from += 2;
                   1229:            }
                   1230:        else
                   1231:            *to = *from;
                   1232:        }
                   1233:     *to = '\0';
                   1234:     }
                   1235: 
                   1236: 
                   1237: #ifdef GENERATE_INDEXES
                   1238: /* Copies and encodes a string. */
                   1239: static void
                   1240: strencode( char* to, int tosize, char* from )
                   1241:     {
                   1242:     int tolen;
                   1243: 
                   1244:     for ( tolen = 0; *from != '\0' && tolen + 4 < tosize; ++from )
                   1245:        {
                   1246:        if ( isalnum(*from) || strchr( "/_.-~", *from ) != (char*) 0 )
                   1247:            {
                   1248:            *to = *from;
                   1249:            ++to;
                   1250:            ++tolen;
                   1251:            }
                   1252:        else
                   1253:            {
                   1254:            (void) sprintf( to, "%%%02x", (int) *from & 0xff );
                   1255:            to += 3;
                   1256:            tolen += 3;
                   1257:            }
                   1258:        }
                   1259:     *to = '\0';
                   1260:     }
                   1261: #endif /* GENERATE_INDEXES */
                   1262: 
                   1263: 
                   1264: #ifdef TILDE_MAP_1
                   1265: /* Map a ~username/whatever URL into <prefix>/username. */
                   1266: static int
                   1267: tilde_map_1( httpd_conn* hc )
                   1268:     {
                   1269:     static char* temp;
                   1270:     static size_t maxtemp = 0;
                   1271:     int len;
                   1272:     static char* prefix = TILDE_MAP_1;
                   1273: 
                   1274:     len = strlen( hc->expnfilename ) - 1;
                   1275:     httpd_realloc_str( &temp, &maxtemp, len );
                   1276:     (void) strcpy( temp, &hc->expnfilename[1] );
                   1277:     httpd_realloc_str(
                   1278:        &hc->expnfilename, &hc->maxexpnfilename, strlen( prefix ) + 1 + len );
                   1279:     (void) strcpy( hc->expnfilename, prefix );
                   1280:     if ( prefix[0] != '\0' )
                   1281:        (void) strcat( hc->expnfilename, "/" );
                   1282:     (void) strcat( hc->expnfilename, temp );
                   1283:     return 1;
                   1284:     }
                   1285: #endif /* TILDE_MAP_1 */
                   1286: 
                   1287: #ifdef TILDE_MAP_2
                   1288: /* Map a ~username/whatever URL into <user's homedir>/<postfix>. */
                   1289: static int
                   1290: tilde_map_2( httpd_conn* hc )
                   1291:     {
                   1292:     static char* temp;
                   1293:     static size_t maxtemp = 0;
                   1294:     static char* postfix = TILDE_MAP_2;
                   1295:     char* cp;
                   1296:     struct passwd* pw;
                   1297:     char* alt;
                   1298:     char* rest;
                   1299: 
                   1300:     /* Get the username. */
                   1301:     httpd_realloc_str( &temp, &maxtemp, strlen( hc->expnfilename ) - 1 );
                   1302:     (void) strcpy( temp, &hc->expnfilename[1] );
                   1303:     cp = strchr( temp, '/' );
                   1304:     if ( cp != (char*) 0 )
                   1305:        *cp++ = '\0';
                   1306:     else
                   1307:        cp = "";
                   1308: 
                   1309:     /* Get the passwd entry. */
                   1310:     pw = getpwnam( temp );
                   1311:     if ( pw == (struct passwd*) 0 )
                   1312:        return 0;
                   1313: 
                   1314:     /* Set up altdir. */
                   1315:     httpd_realloc_str(
                   1316:        &hc->altdir, &hc->maxaltdir,
                   1317:        strlen( pw->pw_dir ) + 1 + strlen( postfix ) );
                   1318:     (void) strcpy( hc->altdir, pw->pw_dir );
                   1319:     if ( postfix[0] != '\0' )
                   1320:        {
                   1321:        (void) strcat( hc->altdir, "/" );
                   1322:        (void) strcat( hc->altdir, postfix );
                   1323:        }
                   1324:     alt = expand_symlinks( hc->altdir, &rest, 0, 1 );
                   1325:     if ( rest[0] != '\0' )
                   1326:        return 0;
                   1327:     httpd_realloc_str( &hc->altdir, &hc->maxaltdir, strlen( alt ) );
                   1328:     (void) strcpy( hc->altdir, alt );
                   1329: 
                   1330:     /* And the filename becomes altdir plus the post-~ part of the original. */
                   1331:     httpd_realloc_str(
                   1332:        &hc->expnfilename, &hc->maxexpnfilename,
                   1333:        strlen( hc->altdir ) + 1 + strlen( cp ) );
                   1334:     (void) my_snprintf( hc->expnfilename, hc->maxexpnfilename,
                   1335:        "%s/%s", hc->altdir, cp );
                   1336: 
                   1337:     /* For this type of tilde mapping, we want to defeat vhost mapping. */
                   1338:     hc->tildemapped = 1;
                   1339: 
                   1340:     return 1;
                   1341:     }
                   1342: #endif /* TILDE_MAP_2 */
                   1343: 
                   1344: 
                   1345: /* Virtual host mapping. */
                   1346: static int
                   1347: vhost_map( httpd_conn* hc )
                   1348:     {
                   1349:     httpd_sockaddr sa;
                   1350:     socklen_t sz;
                   1351:     static char* tempfilename;
                   1352:     static size_t maxtempfilename = 0;
                   1353:     char* cp1;
                   1354:     int len;
                   1355: #ifdef VHOST_DIRLEVELS
                   1356:     int i;
                   1357:     char* cp2;
                   1358: #endif /* VHOST_DIRLEVELS */
                   1359: 
                   1360:     /* Figure out the virtual hostname. */
                   1361:     if ( hc->reqhost[0] != '\0' )
                   1362:        hc->hostname = hc->reqhost;
                   1363:     else if ( hc->hdrhost[0] != '\0' )
                   1364:        hc->hostname = hc->hdrhost;
                   1365:     else
                   1366:        {
                   1367:        sz = sizeof(sa);
                   1368:        if ( getsockname( hc->conn_fd, &sa.sa, &sz ) < 0 )
                   1369:            {
                   1370:            syslog( LOG_ERR, "getsockname - %m" );
                   1371:            return 0;
                   1372:            }
                   1373:        hc->hostname = httpd_ntoa( &sa );
                   1374:        }
                   1375:     /* Pound it to lower case. */
                   1376:     for ( cp1 = hc->hostname; *cp1 != '\0'; ++cp1 )
                   1377:        if ( isupper( *cp1 ) )
                   1378:            *cp1 = tolower( *cp1 );
                   1379: 
                   1380:     if ( hc->tildemapped )
                   1381:        return 1;
                   1382: 
                   1383:     /* Figure out the host directory. */
                   1384: #ifdef VHOST_DIRLEVELS
                   1385:     httpd_realloc_str(
                   1386:        &hc->hostdir, &hc->maxhostdir,
                   1387:        strlen( hc->hostname ) + 2 * VHOST_DIRLEVELS );
                   1388:     if ( strncmp( hc->hostname, "www.", 4 ) == 0 )
                   1389:        cp1 = &hc->hostname[4];
                   1390:     else
                   1391:        cp1 = hc->hostname;
                   1392:     for ( cp2 = hc->hostdir, i = 0; i < VHOST_DIRLEVELS; ++i )
                   1393:        {
                   1394:        /* Skip dots in the hostname.  If we don't, then we get vhost
                   1395:        ** directories in higher level of filestructure if dot gets
                   1396:        ** involved into path construction.  It's `while' used here instead
                   1397:        ** of `if' for it's possible to have a hostname formed with two
                   1398:        ** dots at the end of it.
                   1399:        */
                   1400:        while ( *cp1 == '.' )
                   1401:            ++cp1;
                   1402:        /* Copy a character from the hostname, or '_' if we ran out. */
                   1403:        if ( *cp1 != '\0' )
                   1404:            *cp2++ = *cp1++;
                   1405:        else
                   1406:            *cp2++ = '_';
                   1407:        /* Copy a slash. */
                   1408:        *cp2++ = '/';
                   1409:        }
                   1410:     (void) strcpy( cp2, hc->hostname );
                   1411: #else /* VHOST_DIRLEVELS */
                   1412:     httpd_realloc_str( &hc->hostdir, &hc->maxhostdir, strlen( hc->hostname ) );
                   1413:     (void) strcpy( hc->hostdir, hc->hostname );
                   1414: #endif /* VHOST_DIRLEVELS */
                   1415: 
                   1416:     /* Prepend hostdir to the filename. */
                   1417:     len = strlen( hc->expnfilename );
                   1418:     httpd_realloc_str( &tempfilename, &maxtempfilename, len );
                   1419:     (void) strcpy( tempfilename, hc->expnfilename );
                   1420:     httpd_realloc_str(
                   1421:        &hc->expnfilename, &hc->maxexpnfilename,
                   1422:        strlen( hc->hostdir ) + 1 + len );
                   1423:     (void) strcpy( hc->expnfilename, hc->hostdir );
                   1424:     (void) strcat( hc->expnfilename, "/" );
                   1425:     (void) strcat( hc->expnfilename, tempfilename );
                   1426:     return 1;
                   1427:     }
                   1428: 
                   1429: 
                   1430: /* Expands all symlinks in the given filename, eliding ..'s and leading /'s.
                   1431: ** Returns the expanded path (pointer to static string), or (char*) 0 on
                   1432: ** errors.  Also returns, in the string pointed to by restP, any trailing
                   1433: ** parts of the path that don't exist.
                   1434: **
                   1435: ** This is a fairly nice little routine.  It handles any size filenames
                   1436: ** without excessive mallocs.
                   1437: */
                   1438: static char*
                   1439: expand_symlinks( char* path, char** restP, int no_symlink_check, int tildemapped )
                   1440:     {
                   1441:     static char* checked;
                   1442:     static char* rest;
                   1443:     char link[5000];
                   1444:     static size_t maxchecked = 0, maxrest = 0;
                   1445:     size_t checkedlen, restlen, linklen, prevcheckedlen, prevrestlen;
                   1446:     int nlinks, i;
                   1447:     char* r;
                   1448:     char* cp1;
                   1449:     char* cp2;
                   1450: 
                   1451:     if ( no_symlink_check )
                   1452:        {
                   1453:        /* If we are chrooted, we can actually skip the symlink-expansion,
                   1454:        ** since it's impossible to get out of the tree.  However, we still
                   1455:        ** need to do the pathinfo check, and the existing symlink expansion
                   1456:        ** code is a pretty reasonable way to do this.  So, what we do is
                   1457:        ** a single stat() of the whole filename - if it exists, then we
                   1458:        ** return it as is with nothing in restP.  If it doesn't exist, we
                   1459:        ** fall through to the existing code.
                   1460:        **
                   1461:        ** One side-effect of this is that users can't symlink to central
                   1462:        ** approved CGIs any more.  The workaround is to use the central
                   1463:        ** URL for the CGI instead of a local symlinked one.
                   1464:        */
                   1465:        struct stat sb;
                   1466:        if ( stat( path, &sb ) != -1 )
                   1467:            {
                   1468:            checkedlen = strlen( path );
                   1469:            httpd_realloc_str( &checked, &maxchecked, checkedlen );
                   1470:            (void) strcpy( checked, path );
                   1471:            /* Trim trailing slashes. */
                   1472:            while ( checked[checkedlen - 1] == '/' )
                   1473:                {
                   1474:                checked[checkedlen - 1] = '\0';
                   1475:                --checkedlen;
                   1476:                }
                   1477:            httpd_realloc_str( &rest, &maxrest, 0 );
                   1478:            rest[0] = '\0';
                   1479:            *restP = rest;
                   1480:            return checked;
                   1481:            }
                   1482:        }
                   1483: 
                   1484:     /* Start out with nothing in checked and the whole filename in rest. */
                   1485:     httpd_realloc_str( &checked, &maxchecked, 1 );
                   1486:     checked[0] = '\0';
                   1487:     checkedlen = 0;
                   1488:     restlen = strlen( path );
                   1489:     httpd_realloc_str( &rest, &maxrest, restlen );
                   1490:     (void) strcpy( rest, path );
                   1491:     if ( rest[restlen - 1] == '/' )
                   1492:        rest[--restlen] = '\0';         /* trim trailing slash */
                   1493:     if ( ! tildemapped )
                   1494:        /* Remove any leading slashes. */
                   1495:        while ( rest[0] == '/' )
                   1496:            {
                   1497:            (void) strcpy( rest, &(rest[1]) );
                   1498:            --restlen;
                   1499:            }
                   1500:     r = rest;
                   1501:     nlinks = 0;
                   1502: 
                   1503:     /* While there are still components to check... */
                   1504:     while ( restlen > 0 )
                   1505:        {
                   1506:        /* Save current checkedlen in case we get a symlink.  Save current
                   1507:        ** restlen in case we get a non-existant component.
                   1508:        */
                   1509:        prevcheckedlen = checkedlen;
                   1510:        prevrestlen = restlen;
                   1511: 
                   1512:        /* Grab one component from r and transfer it to checked. */
                   1513:        cp1 = strchr( r, '/' );
                   1514:        if ( cp1 != (char*) 0 )
                   1515:            {
                   1516:            i = cp1 - r;
                   1517:            if ( i == 0 )
                   1518:                {
                   1519:                /* Special case for absolute paths. */
                   1520:                httpd_realloc_str( &checked, &maxchecked, checkedlen + 1 );
                   1521:                (void) strncpy( &checked[checkedlen], r, 1 );
                   1522:                checkedlen += 1;
                   1523:                }
                   1524:            else if ( strncmp( r, "..", MAX( i, 2 ) ) == 0 )
                   1525:                {
                   1526:                /* Ignore ..'s that go above the start of the path. */
                   1527:                if ( checkedlen != 0 )
                   1528:                    {
                   1529:                    cp2 = strrchr( checked, '/' );
                   1530:                    if ( cp2 == (char*) 0 )
                   1531:                        checkedlen = 0;
                   1532:                    else if ( cp2 == checked )
                   1533:                        checkedlen = 1;
                   1534:                    else
                   1535:                        checkedlen = cp2 - checked;
                   1536:                    }
                   1537:                }
                   1538:            else
                   1539:                {
                   1540:                httpd_realloc_str( &checked, &maxchecked, checkedlen + 1 + i );
                   1541:                if ( checkedlen > 0 && checked[checkedlen-1] != '/' )
                   1542:                    checked[checkedlen++] = '/';
                   1543:                (void) strncpy( &checked[checkedlen], r, i );
                   1544:                checkedlen += i;
                   1545:                }
                   1546:            checked[checkedlen] = '\0';
                   1547:            r += i + 1;
                   1548:            restlen -= i + 1;
                   1549:            }
                   1550:        else
                   1551:            {
                   1552:            /* No slashes remaining, r is all one component. */
                   1553:            if ( strcmp( r, ".." ) == 0 )
                   1554:                {
                   1555:                /* Ignore ..'s that go above the start of the path. */
                   1556:                if ( checkedlen != 0 )
                   1557:                    {
                   1558:                    cp2 = strrchr( checked, '/' );
                   1559:                    if ( cp2 == (char*) 0 )
                   1560:                        checkedlen = 0;
                   1561:                    else if ( cp2 == checked )
                   1562:                        checkedlen = 1;
                   1563:                    else
                   1564:                        checkedlen = cp2 - checked;
                   1565:                    checked[checkedlen] = '\0';
                   1566:                    }
                   1567:                }
                   1568:            else
                   1569:                {
                   1570:                httpd_realloc_str(
                   1571:                    &checked, &maxchecked, checkedlen + 1 + restlen );
                   1572:                if ( checkedlen > 0 && checked[checkedlen-1] != '/' )
                   1573:                    checked[checkedlen++] = '/';
                   1574:                (void) strcpy( &checked[checkedlen], r );
                   1575:                checkedlen += restlen;
                   1576:                }
                   1577:            r += restlen;
                   1578:            restlen = 0;
                   1579:            }
                   1580: 
                   1581:        /* Try reading the current filename as a symlink */
                   1582:        if ( checked[0] == '\0' )
                   1583:            continue;
                   1584:        linklen = readlink( checked, link, sizeof(link) - 1 );
                   1585:        if ( linklen == -1 )
                   1586:            {
                   1587:            if ( errno == EINVAL )
                   1588:                continue;               /* not a symlink */
                   1589:            if ( errno == EACCES || errno == ENOENT || errno == ENOTDIR )
                   1590:                {
                   1591:                /* That last component was bogus.  Restore and return. */
                   1592:                *restP = r - ( prevrestlen - restlen );
                   1593:                if ( prevcheckedlen == 0 )
                   1594:                    (void) strcpy( checked, "." );
                   1595:                else
                   1596:                    checked[prevcheckedlen] = '\0';
                   1597:                return checked;
                   1598:                }
                   1599:            syslog( LOG_ERR, "readlink %.80s - %m", checked );
                   1600:            return (char*) 0;
                   1601:            }
                   1602:        ++nlinks;
                   1603:        if ( nlinks > MAX_LINKS )
                   1604:            {
                   1605:            syslog( LOG_ERR, "too many symlinks in %.80s", path );
                   1606:            return (char*) 0;
                   1607:            }
                   1608:        link[linklen] = '\0';
                   1609:        if ( link[linklen - 1] == '/' )
                   1610:            link[--linklen] = '\0';     /* trim trailing slash */
                   1611: 
                   1612:        /* Insert the link contents in front of the rest of the filename. */
                   1613:        if ( restlen != 0 )
                   1614:            {
                   1615:            (void) strcpy( rest, r );
                   1616:            httpd_realloc_str( &rest, &maxrest, restlen + linklen + 1 );
                   1617:            for ( i = restlen; i >= 0; --i )
                   1618:                rest[i + linklen + 1] = rest[i];
                   1619:            (void) strcpy( rest, link );
                   1620:            rest[linklen] = '/';
                   1621:            restlen += linklen + 1;
                   1622:            r = rest;
                   1623:            }
                   1624:        else
                   1625:            {
                   1626:            /* There's nothing left in the filename, so the link contents
                   1627:            ** becomes the rest.
                   1628:            */
                   1629:            httpd_realloc_str( &rest, &maxrest, linklen );
                   1630:            (void) strcpy( rest, link );
                   1631:            restlen = linklen;
                   1632:            r = rest;
                   1633:            }
                   1634: 
                   1635:        if ( rest[0] == '/' )
                   1636:            {
                   1637:            /* There must have been an absolute symlink - zero out checked. */
                   1638:            checked[0] = '\0';
                   1639:            checkedlen = 0;
                   1640:            }
                   1641:        else
                   1642:            {
                   1643:            /* Re-check this component. */
                   1644:            checkedlen = prevcheckedlen;
                   1645:            checked[checkedlen] = '\0';
                   1646:            }
                   1647:        }
                   1648: 
                   1649:     /* Ok. */
                   1650:     *restP = r;
                   1651:     if ( checked[0] == '\0' )
                   1652:        (void) strcpy( checked, "." );
                   1653:     return checked;
                   1654:     }
                   1655: 
                   1656: 
                   1657: int
                   1658: httpd_get_conn( httpd_server* hs, int listen_fd, httpd_conn* hc )
                   1659:     {
                   1660:     httpd_sockaddr sa;
                   1661:     socklen_t sz;
                   1662: 
                   1663:     if ( ! hc->initialized )
                   1664:        {
                   1665:        hc->read_size = 0;
                   1666:        httpd_realloc_str( &hc->read_buf, &hc->read_size, 500 );
                   1667:        hc->maxdecodedurl =
                   1668:            hc->maxorigfilename = hc->maxexpnfilename = hc->maxencodings =
                   1669:            hc->maxpathinfo = hc->maxquery = hc->maxaccept =
                   1670:            hc->maxaccepte = hc->maxreqhost = hc->maxhostdir =
                   1671:            hc->maxremoteuser = hc->maxresponse = 0;
                   1672: #ifdef TILDE_MAP_2
                   1673:        hc->maxaltdir = 0;
                   1674: #endif /* TILDE_MAP_2 */
                   1675:        httpd_realloc_str( &hc->decodedurl, &hc->maxdecodedurl, 1 );
                   1676:        httpd_realloc_str( &hc->origfilename, &hc->maxorigfilename, 1 );
                   1677:        httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, 0 );
                   1678:        httpd_realloc_str( &hc->encodings, &hc->maxencodings, 0 );
                   1679:        httpd_realloc_str( &hc->pathinfo, &hc->maxpathinfo, 0 );
                   1680:        httpd_realloc_str( &hc->query, &hc->maxquery, 0 );
                   1681:        httpd_realloc_str( &hc->accept, &hc->maxaccept, 0 );
                   1682:        httpd_realloc_str( &hc->accepte, &hc->maxaccepte, 0 );
                   1683:        httpd_realloc_str( &hc->reqhost, &hc->maxreqhost, 0 );
                   1684:        httpd_realloc_str( &hc->hostdir, &hc->maxhostdir, 0 );
                   1685:        httpd_realloc_str( &hc->remoteuser, &hc->maxremoteuser, 0 );
                   1686:        httpd_realloc_str( &hc->response, &hc->maxresponse, 0 );
                   1687: #ifdef TILDE_MAP_2
                   1688:        httpd_realloc_str( &hc->altdir, &hc->maxaltdir, 0 );
                   1689: #endif /* TILDE_MAP_2 */
                   1690:        hc->initialized = 1;
                   1691:        }
                   1692: 
                   1693:     /* Accept the new connection. */
                   1694:     sz = sizeof(sa);
                   1695:     hc->conn_fd = accept( listen_fd, &sa.sa, &sz );
                   1696:     if ( hc->conn_fd < 0 )
                   1697:        {
                   1698:        if ( errno == EWOULDBLOCK )
                   1699:            return GC_NO_MORE;
                   1700:        syslog( LOG_ERR, "accept - %m" );
                   1701:        return GC_FAIL;
                   1702:        }
                   1703:     if ( ! sockaddr_check( &sa ) )
                   1704:        {
                   1705:        syslog( LOG_ERR, "unknown sockaddr family" );
                   1706:        close( hc->conn_fd );
                   1707:        hc->conn_fd = -1;
                   1708:        return GC_FAIL;
                   1709:        }
                   1710:     (void) fcntl( hc->conn_fd, F_SETFD, 1 );
                   1711:     hc->hs = hs;
                   1712:     (void) memset( &hc->client_addr, 0, sizeof(hc->client_addr) );
                   1713:     (void) memmove( &hc->client_addr, &sa, sockaddr_len( &sa ) );
                   1714:     hc->read_idx = 0;
                   1715:     hc->checked_idx = 0;
                   1716:     hc->checked_state = CHST_FIRSTWORD;
                   1717:     hc->method = METHOD_UNKNOWN;
                   1718:     hc->status = 0;
                   1719:     hc->bytes_to_send = 0;
                   1720:     hc->bytes_sent = 0;
                   1721:     hc->encodedurl = "";
                   1722:     hc->decodedurl[0] = '\0';
                   1723:     hc->protocol = "UNKNOWN";
                   1724:     hc->origfilename[0] = '\0';
                   1725:     hc->expnfilename[0] = '\0';
                   1726:     hc->encodings[0] = '\0';
                   1727:     hc->pathinfo[0] = '\0';
                   1728:     hc->query[0] = '\0';
                   1729:     hc->referer = "";
                   1730:     hc->useragent = "";
                   1731:     hc->accept[0] = '\0';
                   1732:     hc->accepte[0] = '\0';
                   1733:     hc->acceptl = "";
                   1734:     hc->cookie = "";
                   1735:     hc->contenttype = "";
                   1736:     hc->reqhost[0] = '\0';
                   1737:     hc->hdrhost = "";
                   1738:     hc->hostdir[0] = '\0';
                   1739:     hc->authorization = "";
                   1740:     hc->remoteuser[0] = '\0';
                   1741:     hc->response[0] = '\0';
                   1742: #ifdef TILDE_MAP_2
                   1743:     hc->altdir[0] = '\0';
                   1744: #endif /* TILDE_MAP_2 */
                   1745:     hc->responselen = 0;
                   1746:     hc->if_modified_since = (time_t) -1;
                   1747:     hc->range_if = (time_t) -1;
                   1748:     hc->contentlength = -1;
                   1749:     hc->type = "";
                   1750:     hc->hostname = (char*) 0;
                   1751:     hc->mime_flag = 1;
                   1752:     hc->one_one = 0;
                   1753:     hc->got_range = 0;
                   1754:     hc->tildemapped = 0;
                   1755:     hc->first_byte_index = 0;
                   1756:     hc->last_byte_index = -1;
                   1757:     hc->keep_alive = 0;
                   1758:     hc->should_linger = 0;
                   1759:     hc->file_address = (char*) 0;
                   1760:     return GC_OK;
                   1761:     }
                   1762: 
                   1763: 
                   1764: /* Checks hc->read_buf to see whether a complete request has been read so far;
                   1765: ** either the first line has two words (an HTTP/0.9 request), or the first
                   1766: ** line has three words and there's a blank line present.
                   1767: **
                   1768: ** hc->read_idx is how much has been read in; hc->checked_idx is how much we
                   1769: ** have checked so far; and hc->checked_state is the current state of the
                   1770: ** finite state machine.
                   1771: */
                   1772: int
                   1773: httpd_got_request( httpd_conn* hc )
                   1774:     {
                   1775:     char c;
                   1776: 
                   1777:     for ( ; hc->checked_idx < hc->read_idx; ++hc->checked_idx )
                   1778:        {
                   1779:        c = hc->read_buf[hc->checked_idx];
                   1780:        switch ( hc->checked_state )
                   1781:            {
                   1782:            case CHST_FIRSTWORD:
                   1783:            switch ( c )
                   1784:                {
                   1785:                case ' ': case '\t':
                   1786:                hc->checked_state = CHST_FIRSTWS;
                   1787:                break;
                   1788:                case '\012': case '\015':
                   1789:                hc->checked_state = CHST_BOGUS;
                   1790:                return GR_BAD_REQUEST;
                   1791:                }
                   1792:            break;
                   1793:            case CHST_FIRSTWS:
                   1794:            switch ( c )
                   1795:                {
                   1796:                case ' ': case '\t':
                   1797:                break;
                   1798:                case '\012': case '\015':
                   1799:                hc->checked_state = CHST_BOGUS;
                   1800:                return GR_BAD_REQUEST;
                   1801:                default:
                   1802:                hc->checked_state = CHST_SECONDWORD;
                   1803:                break;
                   1804:                }
                   1805:            break;
                   1806:            case CHST_SECONDWORD:
                   1807:            switch ( c )
                   1808:                {
                   1809:                case ' ': case '\t':
                   1810:                hc->checked_state = CHST_SECONDWS;
                   1811:                break;
                   1812:                case '\012': case '\015':
                   1813:                /* The first line has only two words - an HTTP/0.9 request. */
                   1814:                return GR_GOT_REQUEST;
                   1815:                }
                   1816:            break;
                   1817:            case CHST_SECONDWS:
                   1818:            switch ( c )
                   1819:                {
                   1820:                case ' ': case '\t':
                   1821:                break;
                   1822:                case '\012': case '\015':
                   1823:                hc->checked_state = CHST_BOGUS;
                   1824:                return GR_BAD_REQUEST;
                   1825:                default:
                   1826:                hc->checked_state = CHST_THIRDWORD;
                   1827:                break;
                   1828:                }
                   1829:            break;
                   1830:            case CHST_THIRDWORD:
                   1831:            switch ( c )
                   1832:                {
                   1833:                case ' ': case '\t':
                   1834:                hc->checked_state = CHST_THIRDWS;
                   1835:                break;
                   1836:                case '\012':
                   1837:                hc->checked_state = CHST_LF;
                   1838:                break;
                   1839:                case '\015':
                   1840:                hc->checked_state = CHST_CR;
                   1841:                break;
                   1842:                }
                   1843:            break;
                   1844:            case CHST_THIRDWS:
                   1845:            switch ( c )
                   1846:                {
                   1847:                case ' ': case '\t':
                   1848:                break;
                   1849:                case '\012':
                   1850:                hc->checked_state = CHST_LF;
                   1851:                break;
                   1852:                case '\015':
                   1853:                hc->checked_state = CHST_CR;
                   1854:                break;
                   1855:                default:
                   1856:                hc->checked_state = CHST_BOGUS;
                   1857:                return GR_BAD_REQUEST;
                   1858:                }
                   1859:            break;
                   1860:            case CHST_LINE:
                   1861:            switch ( c )
                   1862:                {
                   1863:                case '\012':
                   1864:                hc->checked_state = CHST_LF;
                   1865:                break;
                   1866:                case '\015':
                   1867:                hc->checked_state = CHST_CR;
                   1868:                break;
                   1869:                }
                   1870:            break;
                   1871:            case CHST_LF:
                   1872:            switch ( c )
                   1873:                {
                   1874:                case '\012':
                   1875:                /* Two newlines in a row - a blank line - end of request. */
                   1876:                return GR_GOT_REQUEST;
                   1877:                case '\015':
                   1878:                hc->checked_state = CHST_CR;
                   1879:                break;
                   1880:                default:
                   1881:                hc->checked_state = CHST_LINE;
                   1882:                break;
                   1883:                }
                   1884:            break;
                   1885:            case CHST_CR:
                   1886:            switch ( c )
                   1887:                {
                   1888:                case '\012':
                   1889:                hc->checked_state = CHST_CRLF;
                   1890:                break;
                   1891:                case '\015':
                   1892:                /* Two returns in a row - end of request. */
                   1893:                return GR_GOT_REQUEST;
                   1894:                default:
                   1895:                hc->checked_state = CHST_LINE;
                   1896:                break;
                   1897:                }
                   1898:            break;
                   1899:            case CHST_CRLF:
                   1900:            switch ( c )
                   1901:                {
                   1902:                case '\012':
                   1903:                /* Two newlines in a row - end of request. */
                   1904:                return GR_GOT_REQUEST;
                   1905:                case '\015':
                   1906:                hc->checked_state = CHST_CRLFCR;
                   1907:                break;
                   1908:                default:
                   1909:                hc->checked_state = CHST_LINE;
                   1910:                break;
                   1911:                }
                   1912:            break;
                   1913:            case CHST_CRLFCR:
                   1914:            switch ( c )
                   1915:                {
                   1916:                case '\012': case '\015':
                   1917:                /* Two CRLFs or two CRs in a row - end of request. */
                   1918:                return GR_GOT_REQUEST;
                   1919:                default:
                   1920:                hc->checked_state = CHST_LINE;
                   1921:                break;
                   1922:                }
                   1923:            break;
                   1924:            case CHST_BOGUS:
                   1925:            return GR_BAD_REQUEST;
                   1926:            }
                   1927:        }
                   1928:     return GR_NO_REQUEST;
                   1929:     }
                   1930: 
                   1931: 
                   1932: int
                   1933: httpd_parse_request( httpd_conn* hc )
                   1934:     {
                   1935:     char* buf;
                   1936:     char* method_str;
                   1937:     char* url;
                   1938:     char* protocol;
                   1939:     char* reqhost;
                   1940:     char* eol;
                   1941:     char* cp;
                   1942:     char* pi;
                   1943: 
                   1944:     hc->checked_idx = 0;       /* reset */
                   1945:     method_str = bufgets( hc );
                   1946:     url = strpbrk( method_str, " \t\012\015" );
                   1947:     if ( url == (char*) 0 )
                   1948:        {
                   1949:        httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
                   1950:        return -1;
                   1951:        }
                   1952:     *url++ = '\0';
                   1953:     url += strspn( url, " \t\012\015" );
                   1954:     protocol = strpbrk( url, " \t\012\015" );
                   1955:     if ( protocol == (char*) 0 )
                   1956:        {
                   1957:        protocol = "HTTP/0.9";
                   1958:        hc->mime_flag = 0;
                   1959:        }
                   1960:     else
                   1961:        {
                   1962:        *protocol++ = '\0';
                   1963:        protocol += strspn( protocol, " \t\012\015" );
                   1964:        if ( *protocol != '\0' )
                   1965:            {
                   1966:            eol = strpbrk( protocol, " \t\012\015" );
                   1967:            if ( eol != (char*) 0 )
                   1968:                *eol = '\0';
                   1969:            if ( strcasecmp( protocol, "HTTP/1.0" ) != 0 )
                   1970:                hc->one_one = 1;
                   1971:            }
                   1972:        }
                   1973:     hc->protocol = protocol;
                   1974: 
                   1975:     /* Check for HTTP/1.1 absolute URL. */
                   1976:     if ( strncasecmp( url, "http://", 7 ) == 0 )
                   1977:        {
                   1978:        if ( ! hc->one_one )
                   1979:            {
                   1980:            httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
                   1981:            return -1;
                   1982:            }
                   1983:        reqhost = url + 7;
                   1984:        url = strchr( reqhost, '/' );
                   1985:        if ( url == (char*) 0 )
                   1986:            {
                   1987:            httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
                   1988:            return -1;
                   1989:            }
                   1990:        *url = '\0';
                   1991:        if ( strchr( reqhost, '/' ) != (char*) 0 || reqhost[0] == '.' )
                   1992:            {
                   1993:            httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
                   1994:            return -1;
                   1995:            }
                   1996:        httpd_realloc_str( &hc->reqhost, &hc->maxreqhost, strlen( reqhost ) );
                   1997:        (void) strcpy( hc->reqhost, reqhost );
                   1998:        *url = '/';
                   1999:        }
                   2000: 
                   2001:     if ( *url != '/' )
                   2002:        {
                   2003:        httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
                   2004:        return -1;
                   2005:        }
                   2006: 
                   2007:     if ( strcasecmp( method_str, httpd_method_str( METHOD_GET ) ) == 0 )
                   2008:        hc->method = METHOD_GET;
                   2009:     else if ( strcasecmp( method_str, httpd_method_str( METHOD_HEAD ) ) == 0 )
                   2010:        hc->method = METHOD_HEAD;
                   2011:     else if ( strcasecmp( method_str, httpd_method_str( METHOD_POST ) ) == 0 )
                   2012:        hc->method = METHOD_POST;
                   2013:     else
                   2014:        {
                   2015:        httpd_send_err( hc, 501, err501title, "", err501form, method_str );
                   2016:        return -1;
                   2017:        }
                   2018: 
                   2019:     hc->encodedurl = url;
                   2020:     httpd_realloc_str(
                   2021:        &hc->decodedurl, &hc->maxdecodedurl, strlen( hc->encodedurl ) );
                   2022:     strdecode( hc->decodedurl, hc->encodedurl );
                   2023: 
                   2024:     httpd_realloc_str(
                   2025:        &hc->origfilename, &hc->maxorigfilename, strlen( hc->decodedurl ) );
                   2026:     (void) strcpy( hc->origfilename, &hc->decodedurl[1] );
                   2027:     /* Special case for top-level URL. */
                   2028:     if ( hc->origfilename[0] == '\0' )
                   2029:        (void) strcpy( hc->origfilename, "." );
                   2030: 
                   2031:     /* Extract query string from encoded URL. */
                   2032:     cp = strchr( hc->encodedurl, '?' );
                   2033:     if ( cp != (char*) 0 )
                   2034:        {
                   2035:        ++cp;
                   2036:        httpd_realloc_str( &hc->query, &hc->maxquery, strlen( cp ) );
                   2037:        (void) strcpy( hc->query, cp );
                   2038:        /* Remove query from (decoded) origfilename. */
                   2039:        cp = strchr( hc->origfilename, '?' );
                   2040:        if ( cp != (char*) 0 )
                   2041:            *cp = '\0';
                   2042:        }
                   2043: 
                   2044:     de_dotdot( hc->origfilename );
                   2045:     if ( hc->origfilename[0] == '/' ||
                   2046:         ( hc->origfilename[0] == '.' && hc->origfilename[1] == '.' &&
                   2047:           ( hc->origfilename[2] == '\0' || hc->origfilename[2] == '/' ) ) )
                   2048:        {
                   2049:        httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
                   2050:        return -1;
                   2051:        }
                   2052: 
                   2053:     if ( hc->mime_flag )
                   2054:        {
                   2055:        /* Read the MIME headers. */
                   2056:        while ( ( buf = bufgets( hc ) ) != (char*) 0 )
                   2057:            {
                   2058:            if ( buf[0] == '\0' )
                   2059:                break;
                   2060:            if ( strncasecmp( buf, "Referer:", 8 ) == 0 )
                   2061:                {
                   2062:                cp = &buf[8];
                   2063:                cp += strspn( cp, " \t" );
                   2064:                hc->referer = cp;
                   2065:                }
                   2066:            else if ( strncasecmp( buf, "User-Agent:", 11 ) == 0 )
                   2067:                {
                   2068:                cp = &buf[11];
                   2069:                cp += strspn( cp, " \t" );
                   2070:                hc->useragent = cp;
                   2071:                }
                   2072:            else if ( strncasecmp( buf, "Host:", 5 ) == 0 )
                   2073:                {
                   2074:                cp = &buf[5];
                   2075:                cp += strspn( cp, " \t" );
                   2076:                hc->hdrhost = cp;
                   2077:                cp = strchr( hc->hdrhost, ':' );
                   2078:                if ( cp != (char*) 0 )
                   2079:                    *cp = '\0';
                   2080:                if ( strchr( hc->hdrhost, '/' ) != (char*) 0 || hc->hdrhost[0] == '.' )
                   2081:                    {
                   2082:                    httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
                   2083:                    return -1;
                   2084:                    }
                   2085:                }
                   2086:            else if ( strncasecmp( buf, "Accept:", 7 ) == 0 )
                   2087:                {
                   2088:                cp = &buf[7];
                   2089:                cp += strspn( cp, " \t" );
                   2090:                if ( hc->accept[0] != '\0' )
                   2091:                    {
                   2092:                    if ( strlen( hc->accept ) > 5000 )
                   2093:                        {
                   2094:                        syslog(
                   2095:                            LOG_ERR, "%.80s way too much Accept: data",
                   2096:                            httpd_ntoa( &hc->client_addr ) );
                   2097:                        continue;
                   2098:                        }
                   2099:                    httpd_realloc_str(
                   2100:                        &hc->accept, &hc->maxaccept,
                   2101:                        strlen( hc->accept ) + 2 + strlen( cp ) );
                   2102:                    (void) strcat( hc->accept, ", " );
                   2103:                    }
                   2104:                else
                   2105:                    httpd_realloc_str(
                   2106:                        &hc->accept, &hc->maxaccept, strlen( cp ) );
                   2107:                (void) strcat( hc->accept, cp );
                   2108:                }
                   2109:            else if ( strncasecmp( buf, "Accept-Encoding:", 16 ) == 0 )
                   2110:                {
                   2111:                cp = &buf[16];
                   2112:                cp += strspn( cp, " \t" );
                   2113:                if ( hc->accepte[0] != '\0' )
                   2114:                    {
                   2115:                    if ( strlen( hc->accepte ) > 5000 )
                   2116:                        {
                   2117:                        syslog(
                   2118:                            LOG_ERR, "%.80s way too much Accept-Encoding: data",
                   2119:                            httpd_ntoa( &hc->client_addr ) );
                   2120:                        continue;
                   2121:                        }
                   2122:                    httpd_realloc_str(
                   2123:                        &hc->accepte, &hc->maxaccepte,
                   2124:                        strlen( hc->accepte ) + 2 + strlen( cp ) );
                   2125:                    (void) strcat( hc->accepte, ", " );
                   2126:                    }
                   2127:                else
                   2128:                    httpd_realloc_str(
                   2129:                        &hc->accepte, &hc->maxaccepte, strlen( cp ) );
                   2130:                (void) strcpy( hc->accepte, cp );
                   2131:                }
                   2132:            else if ( strncasecmp( buf, "Accept-Language:", 16 ) == 0 )
                   2133:                {
                   2134:                cp = &buf[16];
                   2135:                cp += strspn( cp, " \t" );
                   2136:                hc->acceptl = cp;
                   2137:                }
                   2138:            else if ( strncasecmp( buf, "If-Modified-Since:", 18 ) == 0 )
                   2139:                {
                   2140:                cp = &buf[18];
                   2141:                hc->if_modified_since = tdate_parse( cp );
                   2142:                if ( hc->if_modified_since == (time_t) -1 )
                   2143:                    syslog( LOG_DEBUG, "unparsable time: %.80s", cp );
                   2144:                }
                   2145:            else if ( strncasecmp( buf, "Cookie:", 7 ) == 0 )
                   2146:                {
                   2147:                cp = &buf[7];
                   2148:                cp += strspn( cp, " \t" );
                   2149:                hc->cookie = cp;
                   2150:                }
                   2151:            else if ( strncasecmp( buf, "Range:", 6 ) == 0 )
                   2152:                {
                   2153:                /* Only support %d- and %d-%d, not %d-%d,%d-%d or -%d. */
                   2154:                if ( strchr( buf, ',' ) == (char*) 0 )
                   2155:                    {
                   2156:                    char* cp_dash;
                   2157:                    cp = strpbrk( buf, "=" );
                   2158:                    if ( cp != (char*) 0 )
                   2159:                        {
                   2160:                        cp_dash = strchr( cp + 1, '-' );
                   2161:                        if ( cp_dash != (char*) 0 && cp_dash != cp + 1 )
                   2162:                            {
                   2163:                            *cp_dash = '\0';
                   2164:                            hc->got_range = 1;
                   2165:                            hc->first_byte_index = atoll( cp + 1 );
                   2166:                            if ( hc->first_byte_index < 0 )
                   2167:                                hc->first_byte_index = 0;
                   2168:                            if ( isdigit( (int) cp_dash[1] ) )
                   2169:                                {
                   2170:                                hc->last_byte_index = atoll( cp_dash + 1 );
                   2171:                                if ( hc->last_byte_index < 0 )
                   2172:                                    hc->last_byte_index = -1;
                   2173:                                }
                   2174:                            }
                   2175:                        }
                   2176:                    }
                   2177:                }
                   2178:            else if ( strncasecmp( buf, "Range-If:", 9 ) == 0 ||
                   2179:                      strncasecmp( buf, "If-Range:", 9 ) == 0 )
                   2180:                {
                   2181:                cp = &buf[9];
                   2182:                hc->range_if = tdate_parse( cp );
                   2183:                if ( hc->range_if == (time_t) -1 )
                   2184:                    syslog( LOG_DEBUG, "unparsable time: %.80s", cp );
                   2185:                }
                   2186:            else if ( strncasecmp( buf, "Content-Type:", 13 ) == 0 )
                   2187:                {
                   2188:                cp = &buf[13];
                   2189:                cp += strspn( cp, " \t" );
                   2190:                hc->contenttype = cp;
                   2191:                }
                   2192:            else if ( strncasecmp( buf, "Content-Length:", 15 ) == 0 )
                   2193:                {
                   2194:                cp = &buf[15];
                   2195:                hc->contentlength = atol( cp );
                   2196:                }
                   2197:            else if ( strncasecmp( buf, "Authorization:", 14 ) == 0 )
                   2198:                {
                   2199:                cp = &buf[14];
                   2200:                cp += strspn( cp, " \t" );
                   2201:                hc->authorization = cp;
                   2202:                }
                   2203:            else if ( strncasecmp( buf, "Connection:", 11 ) == 0 )
                   2204:                {
                   2205:                cp = &buf[11];
                   2206:                cp += strspn( cp, " \t" );
                   2207:                if ( strcasecmp( cp, "keep-alive" ) == 0 )
                   2208:                    hc->keep_alive = 1;
                   2209:                }
                   2210: #ifdef LOG_UNKNOWN_HEADERS
                   2211:            else if ( strncasecmp( buf, "Accept-Charset:", 15 ) == 0 ||
                   2212:                      strncasecmp( buf, "Accept-Language:", 16 ) == 0 ||
                   2213:                      strncasecmp( buf, "Agent:", 6 ) == 0 ||
                   2214:                      strncasecmp( buf, "Cache-Control:", 14 ) == 0 ||
                   2215:                      strncasecmp( buf, "Cache-Info:", 11 ) == 0 ||
                   2216:                      strncasecmp( buf, "Charge-To:", 10 ) == 0 ||
                   2217:                      strncasecmp( buf, "Client-IP:", 10 ) == 0 ||
                   2218:                      strncasecmp( buf, "Date:", 5 ) == 0 ||
                   2219:                      strncasecmp( buf, "Extension:", 10 ) == 0 ||
                   2220:                      strncasecmp( buf, "Forwarded:", 10 ) == 0 ||
                   2221:                      strncasecmp( buf, "From:", 5 ) == 0 ||
                   2222:                      strncasecmp( buf, "HTTP-Version:", 13 ) == 0 ||
                   2223:                      strncasecmp( buf, "Max-Forwards:", 13 ) == 0 ||
                   2224:                      strncasecmp( buf, "Message-Id:", 11 ) == 0 ||
                   2225:                      strncasecmp( buf, "MIME-Version:", 13 ) == 0 ||
                   2226:                      strncasecmp( buf, "Negotiate:", 10 ) == 0 ||
                   2227:                      strncasecmp( buf, "Pragma:", 7 ) == 0 ||
                   2228:                      strncasecmp( buf, "Proxy-Agent:", 12 ) == 0 ||
                   2229:                      strncasecmp( buf, "Proxy-Connection:", 17 ) == 0 ||
                   2230:                      strncasecmp( buf, "Security-Scheme:", 16 ) == 0 ||
                   2231:                      strncasecmp( buf, "Session-Id:", 11 ) == 0 ||
                   2232:                      strncasecmp( buf, "UA-Color:", 9 ) == 0 ||
                   2233:                      strncasecmp( buf, "UA-CPU:", 7 ) == 0 ||
                   2234:                      strncasecmp( buf, "UA-Disp:", 8 ) == 0 ||
                   2235:                      strncasecmp( buf, "UA-OS:", 6 ) == 0 ||
                   2236:                      strncasecmp( buf, "UA-Pixels:", 10 ) == 0 ||
                   2237:                      strncasecmp( buf, "User:", 5 ) == 0 ||
                   2238:                      strncasecmp( buf, "Via:", 4 ) == 0 ||
                   2239:                      strncasecmp( buf, "X-", 2 ) == 0 )
                   2240:                ; /* ignore */
                   2241:            else
                   2242:                syslog( LOG_DEBUG, "unknown request header: %.80s", buf );
                   2243: #endif /* LOG_UNKNOWN_HEADERS */
                   2244:            }
                   2245:        }
                   2246: 
                   2247:     if ( hc->one_one )
                   2248:        {
                   2249:        /* Check that HTTP/1.1 requests specify a host, as required. */
                   2250:        if ( hc->reqhost[0] == '\0' && hc->hdrhost[0] == '\0' )
                   2251:            {
                   2252:            httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
                   2253:            return -1;
                   2254:            }
                   2255: 
                   2256:        /* If the client wants to do keep-alives, it might also be doing
                   2257:        ** pipelining.  There's no way for us to tell.  Since we don't
                   2258:        ** implement keep-alives yet, if we close such a connection there
                   2259:        ** might be unread pipelined requests waiting.  So, we have to
                   2260:        ** do a lingering close.
                   2261:        */
                   2262:        if ( hc->keep_alive )
                   2263:            hc->should_linger = 1;
                   2264:        }
                   2265: 
                   2266:     /* Ok, the request has been parsed.  Now we resolve stuff that
                   2267:     ** may require the entire request.
                   2268:     */
                   2269: 
                   2270:     /* Copy original filename to expanded filename. */
                   2271:     httpd_realloc_str(
                   2272:        &hc->expnfilename, &hc->maxexpnfilename, strlen( hc->origfilename ) );
                   2273:     (void) strcpy( hc->expnfilename, hc->origfilename );
                   2274: 
                   2275:     /* Tilde mapping. */
                   2276:     if ( hc->expnfilename[0] == '~' )
                   2277:        {
                   2278: #ifdef TILDE_MAP_1
                   2279:        if ( ! tilde_map_1( hc ) )
                   2280:            {
                   2281:            httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl );
                   2282:            return -1;
                   2283:            }
                   2284: #endif /* TILDE_MAP_1 */
                   2285: #ifdef TILDE_MAP_2
                   2286:        if ( ! tilde_map_2( hc ) )
                   2287:            {
                   2288:            httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl );
                   2289:            return -1;
                   2290:            }
                   2291: #endif /* TILDE_MAP_2 */
                   2292:        }
                   2293: 
                   2294:     /* Virtual host mapping. */
                   2295:     if ( hc->hs->vhost )
                   2296:        if ( ! vhost_map( hc ) )
                   2297:            {
                   2298:            httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
                   2299:            return -1;
                   2300:            }
                   2301: 
                   2302:     /* Expand all symbolic links in the filename.  This also gives us
                   2303:     ** any trailing non-existing components, for pathinfo.
                   2304:     */
                   2305:     cp = expand_symlinks( hc->expnfilename, &pi, hc->hs->no_symlink_check, hc->tildemapped );
                   2306:     if ( cp == (char*) 0 )
                   2307:        {
                   2308:        httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
                   2309:        return -1;
                   2310:        }
                   2311:     httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, strlen( cp ) );
                   2312:     (void) strcpy( hc->expnfilename, cp );
                   2313:     httpd_realloc_str( &hc->pathinfo, &hc->maxpathinfo, strlen( pi ) );
                   2314:     (void) strcpy( hc->pathinfo, pi );
                   2315: 
                   2316:     /* Remove pathinfo stuff from the original filename too. */
                   2317:     if ( hc->pathinfo[0] != '\0' )
                   2318:        {
                   2319:        int i;
                   2320:        i = strlen( hc->origfilename ) - strlen( hc->pathinfo );
                   2321:        if ( i > 0 && strcmp( &hc->origfilename[i], hc->pathinfo ) == 0 )
                   2322:            hc->origfilename[i - 1] = '\0';
                   2323:        }
                   2324: 
                   2325:     /* If the expanded filename is an absolute path, check that it's still
                   2326:     ** within the current directory or the alternate directory.
                   2327:     */
                   2328:     if ( hc->expnfilename[0] == '/' )
                   2329:        {
                   2330:        if ( strncmp(
                   2331:                 hc->expnfilename, hc->hs->cwd, strlen( hc->hs->cwd ) ) == 0 )
                   2332:            {
                   2333:            /* Elide the current directory. */
                   2334:            (void) strcpy(
                   2335:                hc->expnfilename, &hc->expnfilename[strlen( hc->hs->cwd )] );
                   2336:            }
                   2337: #ifdef TILDE_MAP_2
                   2338:        else if ( hc->altdir[0] != '\0' &&
                   2339:                  ( strncmp(
                   2340:                       hc->expnfilename, hc->altdir,
                   2341:                       strlen( hc->altdir ) ) == 0 &&
                   2342:                    ( hc->expnfilename[strlen( hc->altdir )] == '\0' ||
                   2343:                      hc->expnfilename[strlen( hc->altdir )] == '/' ) ) )
                   2344:            {}
                   2345: #endif /* TILDE_MAP_2 */
                   2346:        else
                   2347:            {
                   2348:            syslog(
                   2349:                LOG_NOTICE, "%.80s URL \"%.80s\" goes outside the web tree",
                   2350:                httpd_ntoa( &hc->client_addr ), hc->encodedurl );
                   2351:            httpd_send_err(
                   2352:                hc, 403, err403title, "",
                   2353:                ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file outside the permitted web server directory tree.\n" ),
                   2354:                hc->encodedurl );
                   2355:            return -1;
                   2356:            }
                   2357:        }
                   2358: 
                   2359:     return 0;
                   2360:     }
                   2361: 
                   2362: 
                   2363: static char*
                   2364: bufgets( httpd_conn* hc )
                   2365:     {
                   2366:     int i;
                   2367:     char c;
                   2368: 
                   2369:     for ( i = hc->checked_idx; hc->checked_idx < hc->read_idx; ++hc->checked_idx )
                   2370:        {
                   2371:        c = hc->read_buf[hc->checked_idx];
                   2372:        if ( c == '\012' || c == '\015' )
                   2373:            {
                   2374:            hc->read_buf[hc->checked_idx] = '\0';
                   2375:            ++hc->checked_idx;
                   2376:            if ( c == '\015' && hc->checked_idx < hc->read_idx &&
                   2377:                 hc->read_buf[hc->checked_idx] == '\012' )
                   2378:                {
                   2379:                hc->read_buf[hc->checked_idx] = '\0';
                   2380:                ++hc->checked_idx;
                   2381:                }
                   2382:            return &(hc->read_buf[i]);
                   2383:            }
                   2384:        }
                   2385:     return (char*) 0;
                   2386:     }
                   2387: 
                   2388: 
                   2389: static void
                   2390: de_dotdot( char* file )
                   2391:     {
                   2392:     char* cp;
                   2393:     char* cp2;
                   2394:     int l;
                   2395: 
                   2396:     /* Collapse any multiple / sequences. */
                   2397:     while ( ( cp = strstr( file, "//") ) != (char*) 0 )
                   2398:        {
                   2399:        for ( cp2 = cp + 2; *cp2 == '/'; ++cp2 )
                   2400:            continue;
                   2401:        (void) strcpy( cp + 1, cp2 );
                   2402:        }
                   2403: 
                   2404:     /* Remove leading ./ and any /./ sequences. */
                   2405:     while ( strncmp( file, "./", 2 ) == 0 )
                   2406:        (void) strcpy( file, file + 2 );
                   2407:     while ( ( cp = strstr( file, "/./") ) != (char*) 0 )
                   2408:        (void) strcpy( cp, cp + 2 );
                   2409: 
                   2410:     /* Alternate between removing leading ../ and removing xxx/../ */
                   2411:     for (;;)
                   2412:        {
                   2413:        while ( strncmp( file, "../", 3 ) == 0 )
                   2414:            (void) strcpy( file, file + 3 );
                   2415:        cp = strstr( file, "/../" );
                   2416:        if ( cp == (char*) 0 )
                   2417:            break;
                   2418:        for ( cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2 )
                   2419:            continue;
                   2420:        (void) strcpy( cp2 + 1, cp + 4 );
                   2421:        }
                   2422: 
                   2423:     /* Also elide any xxx/.. at the end. */
                   2424:     while ( ( l = strlen( file ) ) > 3 &&
                   2425:            strcmp( ( cp = file + l - 3 ), "/.." ) == 0 )
                   2426:        {
                   2427:        for ( cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2 )
                   2428:            continue;
                   2429:        if ( cp2 < file )
                   2430:            break;
                   2431:        *cp2 = '\0';
                   2432:        }
                   2433:     }
                   2434: 
                   2435: 
                   2436: void
                   2437: httpd_close_conn( httpd_conn* hc, struct timeval* nowP )
                   2438:     {
                   2439:     make_log_entry( hc, nowP );
                   2440: 
                   2441:     if ( hc->file_address != (char*) 0 )
                   2442:        {
                   2443:        mmc_unmap( hc->file_address, &(hc->sb), nowP );
                   2444:        hc->file_address = (char*) 0;
                   2445:        }
                   2446:     if ( hc->conn_fd >= 0 )
                   2447:        {
                   2448:        (void) close( hc->conn_fd );
                   2449:        hc->conn_fd = -1;
                   2450:        }
                   2451:     }
                   2452: 
                   2453: void
                   2454: httpd_destroy_conn( httpd_conn* hc )
                   2455:     {
                   2456:     if ( hc->initialized )
                   2457:        {
                   2458:        free( (void*) hc->read_buf );
                   2459:        free( (void*) hc->decodedurl );
                   2460:        free( (void*) hc->origfilename );
                   2461:        free( (void*) hc->expnfilename );
                   2462:        free( (void*) hc->encodings );
                   2463:        free( (void*) hc->pathinfo );
                   2464:        free( (void*) hc->query );
                   2465:        free( (void*) hc->accept );
                   2466:        free( (void*) hc->accepte );
                   2467:        free( (void*) hc->reqhost );
                   2468:        free( (void*) hc->hostdir );
                   2469:        free( (void*) hc->remoteuser );
                   2470:        free( (void*) hc->response );
                   2471: #ifdef TILDE_MAP_2
                   2472:        free( (void*) hc->altdir );
                   2473: #endif /* TILDE_MAP_2 */
                   2474:        hc->initialized = 0;
                   2475:        }
                   2476:     }
                   2477: 
                   2478: 
                   2479: struct mime_entry {
                   2480:     char* ext;
                   2481:     size_t ext_len;
                   2482:     char* val;
                   2483:     size_t val_len;
                   2484:     };
                   2485: static struct mime_entry enc_tab[] = {
                   2486: #include "mime_encodings.h"
                   2487:     };
                   2488: static const int n_enc_tab = sizeof(enc_tab) / sizeof(*enc_tab);
                   2489: static struct mime_entry typ_tab[] = {
                   2490: #include "mime_types.h"
                   2491:     };
                   2492: static const int n_typ_tab = sizeof(typ_tab) / sizeof(*typ_tab);
                   2493: 
                   2494: 
                   2495: /* qsort comparison routine - declared old-style on purpose, for portability. */
                   2496: static int
                   2497: ext_compare( a, b )
                   2498:     struct mime_entry* a;
                   2499:     struct mime_entry* b;
                   2500:     {
                   2501:     return strcmp( a->ext, b->ext );
                   2502:     }
                   2503: 
                   2504: 
                   2505: static void
                   2506: init_mime( void )
                   2507:     {
                   2508:     int i;
                   2509: 
                   2510:     /* Sort the tables so we can do binary search. */
                   2511:     qsort( enc_tab, n_enc_tab, sizeof(*enc_tab), ext_compare );
                   2512:     qsort( typ_tab, n_typ_tab, sizeof(*typ_tab), ext_compare );
                   2513: 
                   2514:     /* Fill in the lengths. */
                   2515:     for ( i = 0; i < n_enc_tab; ++i )
                   2516:        {
                   2517:        enc_tab[i].ext_len = strlen( enc_tab[i].ext );
                   2518:        enc_tab[i].val_len = strlen( enc_tab[i].val );
                   2519:        }
                   2520:     for ( i = 0; i < n_typ_tab; ++i )
                   2521:        {
                   2522:        typ_tab[i].ext_len = strlen( typ_tab[i].ext );
                   2523:        typ_tab[i].val_len = strlen( typ_tab[i].val );
                   2524:        }
                   2525: 
                   2526:     }
                   2527: 
                   2528: 
                   2529: /* Figure out MIME encodings and type based on the filename.  Multiple
                   2530: ** encodings are separated by commas, and are listed in the order in
                   2531: ** which they were applied to the file.
                   2532: */
                   2533: static void
                   2534: figure_mime( httpd_conn* hc )
                   2535:     {
                   2536:     char* prev_dot;
                   2537:     char* dot;
                   2538:     char* ext;
                   2539:     int me_indexes[100], n_me_indexes;
                   2540:     size_t ext_len, encodings_len;
                   2541:     int i, top, bot, mid;
                   2542:     int r;
                   2543:     char* default_type = "text/plain; charset=%s";
                   2544: 
                   2545:     /* Peel off encoding extensions until there aren't any more. */
                   2546:     n_me_indexes = 0;
                   2547:     for ( prev_dot = &hc->expnfilename[strlen(hc->expnfilename)]; ; prev_dot = dot )
                   2548:        {
                   2549:        for ( dot = prev_dot - 1; dot >= hc->expnfilename && *dot != '.'; --dot )
                   2550:            ;
                   2551:        if ( dot < hc->expnfilename )
                   2552:            {
                   2553:            /* No dot found.  No more encoding extensions, and no type
                   2554:            ** extension either.
                   2555:            */
                   2556:            hc->type = default_type;
                   2557:            goto done;
                   2558:            }
                   2559:        ext = dot + 1;
                   2560:        ext_len = prev_dot - ext;
                   2561:        /* Search the encodings table.  Linear search is fine here, there
                   2562:        ** are only a few entries.
                   2563:        */
                   2564:        for ( i = 0; i < n_enc_tab; ++i )
                   2565:            {
                   2566:            if ( ext_len == enc_tab[i].ext_len && strncasecmp( ext, enc_tab[i].ext, ext_len ) == 0 )
                   2567:                {
                   2568:                if ( n_me_indexes < sizeof(me_indexes)/sizeof(*me_indexes) )
                   2569:                    {
                   2570:                    me_indexes[n_me_indexes] = i;
                   2571:                    ++n_me_indexes;
                   2572:                    }
                   2573:                goto next;
                   2574:                }
                   2575:            }
                   2576:        /* No encoding extension found.  Break and look for a type extension. */
                   2577:        break;
                   2578: 
                   2579:        next: ;
                   2580:        }
                   2581: 
                   2582:     /* Binary search for a matching type extension. */
                   2583:     top = n_typ_tab - 1;
                   2584:     bot = 0;
                   2585:     while ( top >= bot )
                   2586:        {
                   2587:        mid = ( top + bot ) / 2;
                   2588:        r = strncasecmp( ext, typ_tab[mid].ext, ext_len );
                   2589:        if ( r < 0 )
                   2590:            top = mid - 1;
                   2591:        else if ( r > 0 )
                   2592:            bot = mid + 1;
                   2593:        else
                   2594:            if ( ext_len < typ_tab[mid].ext_len )
                   2595:                top = mid - 1;
                   2596:            else if ( ext_len > typ_tab[mid].ext_len )
                   2597:                bot = mid + 1;
                   2598:            else
                   2599:                {
                   2600:                hc->type = typ_tab[mid].val;
                   2601:                goto done;
                   2602:                }
                   2603:        }
                   2604:     hc->type = default_type;
                   2605: 
                   2606:     done:
                   2607: 
                   2608:     /* The last thing we do is actually generate the mime-encoding header. */
                   2609:     hc->encodings[0] = '\0';
                   2610:     encodings_len = 0;
                   2611:     for ( i = n_me_indexes - 1; i >= 0; --i )
                   2612:        {
                   2613:        httpd_realloc_str(
                   2614:            &hc->encodings, &hc->maxencodings,
                   2615:            encodings_len + enc_tab[me_indexes[i]].val_len + 1 );
                   2616:        if ( hc->encodings[0] != '\0' )
                   2617:            {
                   2618:            (void) strcpy( &hc->encodings[encodings_len], "," );
                   2619:            ++encodings_len;
                   2620:            }
                   2621:        (void) strcpy( &hc->encodings[encodings_len], enc_tab[me_indexes[i]].val );
                   2622:        encodings_len += enc_tab[me_indexes[i]].val_len;
                   2623:        }
                   2624: 
                   2625:     }
                   2626: 
                   2627: 
                   2628: #ifdef CGI_TIMELIMIT
                   2629: static void
                   2630: cgi_kill2( ClientData client_data, struct timeval* nowP )
                   2631:     {
                   2632:     pid_t pid;
                   2633: 
                   2634:     pid = (pid_t) client_data.i;
                   2635:     if ( kill( pid, SIGKILL ) == 0 )
                   2636:        syslog( LOG_ERR, "hard-killed CGI process %d", pid );
                   2637:     }
                   2638: 
                   2639: static void
                   2640: cgi_kill( ClientData client_data, struct timeval* nowP )
                   2641:     {
                   2642:     pid_t pid;
                   2643: 
                   2644:     pid = (pid_t) client_data.i;
                   2645:     if ( kill( pid, SIGINT ) == 0 )
                   2646:        {
                   2647:        syslog( LOG_ERR, "killed CGI process %d", pid );
                   2648:        /* In case this isn't enough, schedule an uncatchable kill. */
                   2649:        if ( tmr_create( nowP, cgi_kill2, client_data, 5 * 1000L, 0 ) == (Timer*) 0 )
                   2650:            {
                   2651:            syslog( LOG_CRIT, "tmr_create(cgi_kill2) failed" );
                   2652:            exit( 1 );
                   2653:            }
                   2654:        }
                   2655:     }
                   2656: #endif /* CGI_TIMELIMIT */
                   2657: 
                   2658: 
                   2659: #ifdef GENERATE_INDEXES
                   2660: 
                   2661: /* qsort comparison routine - declared old-style on purpose, for portability. */
                   2662: static int
                   2663: name_compare( a, b )
                   2664:     char** a;
                   2665:     char** b;
                   2666:     {
                   2667:     return strcmp( *a, *b );
                   2668:     }
                   2669: 
                   2670: 
                   2671: static int
                   2672: ls( httpd_conn* hc )
                   2673:     {
                   2674:     DIR* dirp;
                   2675:     struct dirent* de;
                   2676:     int namlen;
                   2677:     static int maxnames = 0;
                   2678:     int nnames;
                   2679:     static char* names;
                   2680:     static char** nameptrs;
                   2681:     static char* name;
                   2682:     static size_t maxname = 0;
                   2683:     static char* rname;
                   2684:     static size_t maxrname = 0;
                   2685:     static char* encrname;
                   2686:     static size_t maxencrname = 0;
                   2687:     FILE* fp;
                   2688:     int i, r;
                   2689:     struct stat sb;
                   2690:     struct stat lsb;
                   2691:     char modestr[20];
                   2692:     char* linkprefix;
                   2693:     char link[MAXPATHLEN+1];
                   2694:     int linklen;
                   2695:     char* fileclass;
                   2696:     time_t now;
                   2697:     char* timestr;
                   2698:     ClientData client_data;
                   2699: 
                   2700:     dirp = opendir( hc->expnfilename );
                   2701:     if ( dirp == (DIR*) 0 )
                   2702:        {
                   2703:        syslog( LOG_ERR, "opendir %.80s - %m", hc->expnfilename );
                   2704:        httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl );
                   2705:        return -1;
                   2706:        }
                   2707: 
                   2708:     if ( hc->method == METHOD_HEAD )
                   2709:        {
                   2710:        closedir( dirp );
                   2711:        send_mime(
                   2712:            hc, 200, ok200title, "", "", "text/html; charset=%s", (off_t) -1,
                   2713:            hc->sb.st_mtime );
                   2714:        }
                   2715:     else if ( hc->method == METHOD_GET )
                   2716:        {
                   2717:        if ( hc->hs->cgi_limit != 0 && hc->hs->cgi_count >= hc->hs->cgi_limit )
                   2718:            {
                   2719:            closedir( dirp );
                   2720:            httpd_send_err(
                   2721:                hc, 503, httpd_err503title, "", httpd_err503form,
                   2722:                hc->encodedurl );
                   2723:            return -1;
                   2724:            }
                   2725:        ++hc->hs->cgi_count;
                   2726:        r = fork( );
                   2727:        if ( r < 0 )
                   2728:            {
                   2729:            syslog( LOG_ERR, "fork - %m" );
                   2730:            closedir( dirp );
                   2731:            httpd_send_err(
                   2732:                hc, 500, err500title, "", err500form, hc->encodedurl );
                   2733:            return -1;
                   2734:            }
                   2735:        if ( r == 0 )
                   2736:            {
                   2737:            /* Child process. */
                   2738:            sub_process = 1;
                   2739:            httpd_unlisten( hc->hs );
                   2740:            send_mime(
                   2741:                hc, 200, ok200title, "", "", "text/html; charset=%s",
                   2742:                (off_t) -1, hc->sb.st_mtime );
                   2743:            httpd_write_response( hc );
                   2744: 
                   2745: #ifdef CGI_NICE
                   2746:            /* Set priority. */
                   2747:            (void) nice( CGI_NICE );
                   2748: #endif /* CGI_NICE */
                   2749: 
                   2750:            /* Open a stdio stream so that we can use fprintf, which is more
                   2751:            ** efficient than a bunch of separate write()s.  We don't have
                   2752:            ** to worry about double closes or file descriptor leaks cause
                   2753:            ** we're in a subprocess.
                   2754:            */
                   2755:            fp = fdopen( hc->conn_fd, "w" );
                   2756:            if ( fp == (FILE*) 0 )
                   2757:                {
                   2758:                syslog( LOG_ERR, "fdopen - %m" );
                   2759:                httpd_send_err(
                   2760:                    hc, 500, err500title, "", err500form, hc->encodedurl );
                   2761:                httpd_write_response( hc );
                   2762:                closedir( dirp );
                   2763:                exit( 1 );
                   2764:                }
                   2765: 
                   2766:            (void) fprintf( fp, "\
                   2767: <HTML>\n\
                   2768: <HEAD><TITLE>Index of %.80s</TITLE></HEAD>\n\
                   2769: <BODY BGCOLOR=\"#99cc99\" TEXT=\"#000000\" LINK=\"#2020ff\" VLINK=\"#4040cc\">\n\
                   2770: <H2>Index of %.80s</H2>\n\
                   2771: <PRE>\n\
                   2772: mode  links  bytes  last-changed  name\n\
                   2773: <HR>",
                   2774:                hc->encodedurl, hc->encodedurl );
                   2775: 
                   2776:            /* Read in names. */
                   2777:            nnames = 0;
                   2778:            while ( ( de = readdir( dirp ) ) != 0 )     /* dirent or direct */
                   2779:                {
                   2780:                if ( nnames >= maxnames )
                   2781:                    {
                   2782:                    if ( maxnames == 0 )
                   2783:                        {
                   2784:                        maxnames = 100;
                   2785:                        names = NEW( char, maxnames * ( MAXPATHLEN + 1 ) );
                   2786:                        nameptrs = NEW( char*, maxnames );
                   2787:                        }
                   2788:                    else
                   2789:                        {
                   2790:                        maxnames *= 2;
                   2791:                        names = RENEW( names, char, maxnames * ( MAXPATHLEN + 1 ) );
                   2792:                        nameptrs = RENEW( nameptrs, char*, maxnames );
                   2793:                        }
                   2794:                    if ( names == (char*) 0 || nameptrs == (char**) 0 )
                   2795:                        {
                   2796:                        syslog( LOG_ERR, "out of memory reallocating directory names" );
                   2797:                        exit( 1 );
                   2798:                        }
                   2799:                    for ( i = 0; i < maxnames; ++i )
                   2800:                        nameptrs[i] = &names[i * ( MAXPATHLEN + 1 )];
                   2801:                    }
                   2802:                namlen = NAMLEN(de);
                   2803:                (void) strncpy( nameptrs[nnames], de->d_name, namlen );
                   2804:                nameptrs[nnames][namlen] = '\0';
                   2805:                ++nnames;
                   2806:                }
                   2807:            closedir( dirp );
                   2808: 
                   2809:            /* Sort the names. */
                   2810:            qsort( nameptrs, nnames, sizeof(*nameptrs), name_compare );
                   2811: 
                   2812:            /* Generate output. */
                   2813:            for ( i = 0; i < nnames; ++i )
                   2814:                {
                   2815:                httpd_realloc_str(
                   2816:                    &name, &maxname,
                   2817:                    strlen( hc->expnfilename ) + 1 + strlen( nameptrs[i] ) );
                   2818:                httpd_realloc_str(
                   2819:                    &rname, &maxrname,
                   2820:                    strlen( hc->origfilename ) + 1 + strlen( nameptrs[i] ) );
                   2821:                if ( hc->expnfilename[0] == '\0' ||
                   2822:                     strcmp( hc->expnfilename, "." ) == 0 )
                   2823:                    {
                   2824:                    (void) strcpy( name, nameptrs[i] );
                   2825:                    (void) strcpy( rname, nameptrs[i] );
                   2826:                    }
                   2827:                else
                   2828:                    {
                   2829:                    (void) my_snprintf( name, maxname,
                   2830:                        "%s/%s", hc->expnfilename, nameptrs[i] );
                   2831:                    if ( strcmp( hc->origfilename, "." ) == 0 )
                   2832:                        (void) my_snprintf( rname, maxrname,
                   2833:                            "%s", nameptrs[i] );
                   2834:                    else
                   2835:                        (void) my_snprintf( rname, maxrname,
                   2836:                            "%s%s", hc->origfilename, nameptrs[i] );
                   2837:                    }
                   2838:                httpd_realloc_str(
                   2839:                    &encrname, &maxencrname, 3 * strlen( rname ) + 1 );
                   2840:                strencode( encrname, maxencrname, rname );
                   2841: 
                   2842:                if ( stat( name, &sb ) < 0 || lstat( name, &lsb ) < 0 )
                   2843:                    continue;
                   2844: 
                   2845:                linkprefix = "";
                   2846:                link[0] = '\0';
                   2847:                /* Break down mode word.  First the file type. */
                   2848:                switch ( lsb.st_mode & S_IFMT )
                   2849:                    {
                   2850:                    case S_IFIFO:  modestr[0] = 'p'; break;
                   2851:                    case S_IFCHR:  modestr[0] = 'c'; break;
                   2852:                    case S_IFDIR:  modestr[0] = 'd'; break;
                   2853:                    case S_IFBLK:  modestr[0] = 'b'; break;
                   2854:                    case S_IFREG:  modestr[0] = '-'; break;
                   2855:                    case S_IFSOCK: modestr[0] = 's'; break;
                   2856:                    case S_IFLNK:  modestr[0] = 'l';
                   2857:                    linklen = readlink( name, link, sizeof(link) - 1 );
                   2858:                    if ( linklen != -1 )
                   2859:                        {
                   2860:                        link[linklen] = '\0';
                   2861:                        linkprefix = " -&gt; ";
                   2862:                        }
                   2863:                    break;
                   2864:                    default:       modestr[0] = '?'; break;
                   2865:                    }
                   2866:                /* Now the world permissions.  Owner and group permissions
                   2867:                ** are not of interest to web clients.
                   2868:                */
                   2869:                modestr[1] = ( lsb.st_mode & S_IROTH ) ? 'r' : '-';
                   2870:                modestr[2] = ( lsb.st_mode & S_IWOTH ) ? 'w' : '-';
                   2871:                modestr[3] = ( lsb.st_mode & S_IXOTH ) ? 'x' : '-';
                   2872:                modestr[4] = '\0';
                   2873: 
                   2874:                /* We also leave out the owner and group name, they are
                   2875:                ** also not of interest to web clients.  Plus if we're
                   2876:                ** running under chroot(), they would require a copy
                   2877:                ** of /etc/passwd and /etc/group, which we want to avoid.
                   2878:                */
                   2879: 
                   2880:                /* Get time string. */
                   2881:                now = time( (time_t*) 0 );
                   2882:                timestr = ctime( &lsb.st_mtime );
                   2883:                timestr[ 0] = timestr[ 4];
                   2884:                timestr[ 1] = timestr[ 5];
                   2885:                timestr[ 2] = timestr[ 6];
                   2886:                timestr[ 3] = ' ';
                   2887:                timestr[ 4] = timestr[ 8];
                   2888:                timestr[ 5] = timestr[ 9];
                   2889:                timestr[ 6] = ' ';
                   2890:                if ( now - lsb.st_mtime > 60*60*24*182 )        /* 1/2 year */
                   2891:                    {
                   2892:                    timestr[ 7] = ' ';
                   2893:                    timestr[ 8] = timestr[20];
                   2894:                    timestr[ 9] = timestr[21];
                   2895:                    timestr[10] = timestr[22];
                   2896:                    timestr[11] = timestr[23];
                   2897:                    }
                   2898:                else
                   2899:                    {
                   2900:                    timestr[ 7] = timestr[11];
                   2901:                    timestr[ 8] = timestr[12];
                   2902:                    timestr[ 9] = ':';
                   2903:                    timestr[10] = timestr[14];
                   2904:                    timestr[11] = timestr[15];
                   2905:                    }
                   2906:                timestr[12] = '\0';
                   2907: 
                   2908:                /* The ls -F file class. */
                   2909:                switch ( sb.st_mode & S_IFMT )
                   2910:                    {
                   2911:                    case S_IFDIR:  fileclass = "/"; break;
                   2912:                    case S_IFSOCK: fileclass = "="; break;
                   2913:                    case S_IFLNK:  fileclass = "@"; break;
                   2914:                    default:
                   2915:                    fileclass = ( sb.st_mode & S_IXOTH ) ? "*" : "";
                   2916:                    break;
                   2917:                    }
                   2918: 
                   2919:                /* And print. */
                   2920:                (void)  fprintf( fp,
                   2921:                   "%s %3ld  %10lld  %s  <A HREF=\"/%.500s%s\">%.80s</A>%s%s%s\n",
                   2922:                    modestr, (long) lsb.st_nlink, (int64_t) lsb.st_size,
                   2923:                    timestr, encrname, S_ISDIR(sb.st_mode) ? "/" : "",
                   2924:                    nameptrs[i], linkprefix, link, fileclass );
                   2925:                }
                   2926: 
                   2927:            (void) fprintf( fp, "</PRE></BODY>\n</HTML>\n" );
                   2928:            (void) fclose( fp );
                   2929:            exit( 0 );
                   2930:            }
                   2931: 
                   2932:        /* Parent process. */
                   2933:        closedir( dirp );
                   2934:        syslog( LOG_INFO, "spawned indexing process %d for directory '%.200s'", r, hc->expnfilename );
                   2935: #ifdef CGI_TIMELIMIT
                   2936:        /* Schedule a kill for the child process, in case it runs too long */
                   2937:        client_data.i = r;
                   2938:        if ( tmr_create( (struct timeval*) 0, cgi_kill, client_data, CGI_TIMELIMIT * 1000L, 0 ) == (Timer*) 0 )
                   2939:            {
                   2940:            syslog( LOG_CRIT, "tmr_create(cgi_kill ls) failed" );
                   2941:            exit( 1 );
                   2942:            }
                   2943: #endif /* CGI_TIMELIMIT */
                   2944:        hc->status = 200;
                   2945:        hc->bytes_sent = CGI_BYTECOUNT;
                   2946:        hc->should_linger = 0;
                   2947:        }
                   2948:     else
                   2949:        {
                   2950:        closedir( dirp );
                   2951:        httpd_send_err(
                   2952:            hc, 501, err501title, "", err501form, httpd_method_str( hc->method ) );
                   2953:        return -1;
                   2954:        }
                   2955: 
                   2956:     return 0;
                   2957:     }
                   2958: 
                   2959: #endif /* GENERATE_INDEXES */
                   2960: 
                   2961: 
                   2962: static char*
                   2963: build_env( char* fmt, char* arg )
                   2964:     {
                   2965:     char* cp;
                   2966:     size_t size;
                   2967:     static char* buf;
                   2968:     static size_t maxbuf = 0;
                   2969: 
                   2970:     size = strlen( fmt ) + strlen( arg );
                   2971:     if ( size > maxbuf )
                   2972:        httpd_realloc_str( &buf, &maxbuf, size );
                   2973:     (void) my_snprintf( buf, maxbuf, fmt, arg );
                   2974:     cp = strdup( buf );
                   2975:     if ( cp == (char*) 0 )
                   2976:        {
                   2977:        syslog( LOG_ERR, "out of memory copying environment variable" );
                   2978:        exit( 1 );
                   2979:        }
                   2980:     return cp;
                   2981:     }
                   2982: 
                   2983: 
                   2984: #ifdef SERVER_NAME_LIST
                   2985: static char*
                   2986: hostname_map( char* hostname )
                   2987:     {
                   2988:     int len, n;
                   2989:     static char* list[] = { SERVER_NAME_LIST };
                   2990: 
                   2991:     len = strlen( hostname );
                   2992:     for ( n = sizeof(list) / sizeof(*list) - 1; n >= 0; --n )
                   2993:        if ( strncasecmp( hostname, list[n], len ) == 0 )
                   2994:            if ( list[n][len] == '/' )  /* check in case of a substring match */
                   2995:                return &list[n][len + 1];
                   2996:     return (char*) 0;
                   2997:     }
                   2998: #endif /* SERVER_NAME_LIST */
                   2999: 
                   3000: 
                   3001: /* Set up environment variables. Be real careful here to avoid
                   3002: ** letting malicious clients overrun a buffer.  We don't have
                   3003: ** to worry about freeing stuff since we're a sub-process.
                   3004: */
                   3005: static char**
                   3006: make_envp( httpd_conn* hc )
                   3007:     {
                   3008:     static char* envp[50];
                   3009:     int envn;
                   3010:     char* cp;
                   3011:     char buf[256];
                   3012: 
                   3013:     envn = 0;
                   3014:     envp[envn++] = build_env( "PATH=%s", CGI_PATH );
                   3015: #ifdef CGI_LD_LIBRARY_PATH
                   3016:     envp[envn++] = build_env( "LD_LIBRARY_PATH=%s", CGI_LD_LIBRARY_PATH );
                   3017: #endif /* CGI_LD_LIBRARY_PATH */
                   3018:     envp[envn++] = build_env( "SERVER_SOFTWARE=%s", SERVER_SOFTWARE );
                   3019:     /* If vhosting, use that server-name here. */
                   3020:     if ( hc->hs->vhost && hc->hostname != (char*) 0 )
                   3021:        cp = hc->hostname;
                   3022:     else
                   3023:        cp = hc->hs->server_hostname;
                   3024:     if ( cp != (char*) 0 )
                   3025:        envp[envn++] = build_env( "SERVER_NAME=%s", cp );
                   3026:     envp[envn++] = "GATEWAY_INTERFACE=CGI/1.1";
                   3027:     envp[envn++] = build_env("SERVER_PROTOCOL=%s", hc->protocol);
                   3028:     (void) my_snprintf( buf, sizeof(buf), "%d", (int) hc->hs->port );
                   3029:     envp[envn++] = build_env( "SERVER_PORT=%s", buf );
                   3030:     envp[envn++] = build_env(
                   3031:        "REQUEST_METHOD=%s", httpd_method_str( hc->method ) );
                   3032:     if ( hc->pathinfo[0] != '\0' )
                   3033:        {
                   3034:        char* cp2;
                   3035:        size_t l;
                   3036:        envp[envn++] = build_env( "PATH_INFO=/%s", hc->pathinfo );
                   3037:        l = strlen( hc->hs->cwd ) + strlen( hc->pathinfo ) + 1;
                   3038:        cp2 = NEW( char, l );
                   3039:        if ( cp2 != (char*) 0 )
                   3040:            {
                   3041:            (void) my_snprintf( cp2, l, "%s%s", hc->hs->cwd, hc->pathinfo );
                   3042:            envp[envn++] = build_env( "PATH_TRANSLATED=%s", cp2 );
                   3043:            }
                   3044:        }
                   3045:     envp[envn++] = build_env(
                   3046:        "SCRIPT_NAME=/%s", strcmp( hc->origfilename, "." ) == 0 ?
                   3047:        "" : hc->origfilename );
                   3048:     if ( hc->query[0] != '\0')
                   3049:        envp[envn++] = build_env( "QUERY_STRING=%s", hc->query );
                   3050:     envp[envn++] = build_env(
                   3051:        "REMOTE_ADDR=%s", httpd_ntoa( &hc->client_addr ) );
                   3052:     if ( hc->referer[0] != '\0' )
                   3053:        envp[envn++] = build_env( "HTTP_REFERER=%s", hc->referer );
                   3054:     if ( hc->useragent[0] != '\0' )
                   3055:        envp[envn++] = build_env( "HTTP_USER_AGENT=%s", hc->useragent );
                   3056:     if ( hc->accept[0] != '\0' )
                   3057:        envp[envn++] = build_env( "HTTP_ACCEPT=%s", hc->accept );
                   3058:     if ( hc->accepte[0] != '\0' )
                   3059:        envp[envn++] = build_env( "HTTP_ACCEPT_ENCODING=%s", hc->accepte );
                   3060:     if ( hc->acceptl[0] != '\0' )
                   3061:        envp[envn++] = build_env( "HTTP_ACCEPT_LANGUAGE=%s", hc->acceptl );
                   3062:     if ( hc->cookie[0] != '\0' )
                   3063:        envp[envn++] = build_env( "HTTP_COOKIE=%s", hc->cookie );
                   3064:     if ( hc->contenttype[0] != '\0' )
                   3065:        envp[envn++] = build_env( "CONTENT_TYPE=%s", hc->contenttype );
                   3066:     if ( hc->hdrhost[0] != '\0' )
                   3067:        envp[envn++] = build_env( "HTTP_HOST=%s", hc->hdrhost );
                   3068:     if ( hc->contentlength != -1 )
                   3069:        {
                   3070:        (void) my_snprintf(
                   3071:            buf, sizeof(buf), "%lu", (unsigned long) hc->contentlength );
                   3072:        envp[envn++] = build_env( "CONTENT_LENGTH=%s", buf );
                   3073:        }
                   3074:     if ( hc->remoteuser[0] != '\0' )
                   3075:        envp[envn++] = build_env( "REMOTE_USER=%s", hc->remoteuser );
                   3076:     if ( hc->authorization[0] != '\0' )
                   3077:        envp[envn++] = build_env( "AUTH_TYPE=%s", "Basic" );
                   3078:        /* We only support Basic auth at the moment. */
                   3079:     if ( getenv( "TZ" ) != (char*) 0 )
                   3080:        envp[envn++] = build_env( "TZ=%s", getenv( "TZ" ) );
                   3081:     envp[envn++] = build_env( "CGI_PATTERN=%s", hc->hs->cgi_pattern );
                   3082: 
                   3083:     envp[envn] = (char*) 0;
                   3084:     return envp;
                   3085:     }
                   3086: 
                   3087: 
                   3088: /* Set up argument vector.  Again, we don't have to worry about freeing stuff
                   3089: ** since we're a sub-process.  This gets done after make_envp() because we
                   3090: ** scribble on hc->query.
                   3091: */
                   3092: static char**
                   3093: make_argp( httpd_conn* hc )
                   3094:     {
                   3095:     char** argp;
                   3096:     int argn;
                   3097:     char* cp1;
                   3098:     char* cp2;
                   3099: 
                   3100:     /* By allocating an arg slot for every character in the query, plus
                   3101:     ** one for the filename and one for the NULL, we are guaranteed to
                   3102:     ** have enough.  We could actually use strlen/2.
                   3103:     */
                   3104:     argp = NEW( char*, strlen( hc->query ) + 2 );
                   3105:     if ( argp == (char**) 0 )
                   3106:        return (char**) 0;
                   3107: 
                   3108:     argp[0] = strrchr( hc->expnfilename, '/' );
                   3109:     if ( argp[0] != (char*) 0 )
                   3110:        ++argp[0];
                   3111:     else
                   3112:        argp[0] = hc->expnfilename;
                   3113: 
                   3114:     argn = 1;
                   3115:     /* According to the CGI spec at http://hoohoo.ncsa.uiuc.edu/cgi/cl.html,
                   3116:     ** "The server should search the query information for a non-encoded =
                   3117:     ** character to determine if the command line is to be used, if it finds
                   3118:     ** one, the command line is not to be used."
                   3119:     */
                   3120:     if ( strchr( hc->query, '=' ) == (char*) 0 )
                   3121:        {
                   3122:        for ( cp1 = cp2 = hc->query; *cp2 != '\0'; ++cp2 )
                   3123:            {
                   3124:            if ( *cp2 == '+' )
                   3125:                {
                   3126:                *cp2 = '\0';
                   3127:                strdecode( cp1, cp1 );
                   3128:                argp[argn++] = cp1;
                   3129:                cp1 = cp2 + 1;
                   3130:                }
                   3131:            }
                   3132:        if ( cp2 != cp1 )
                   3133:            {
                   3134:            strdecode( cp1, cp1 );
                   3135:            argp[argn++] = cp1;
                   3136:            }
                   3137:        }
                   3138: 
                   3139:     argp[argn] = (char*) 0;
                   3140:     return argp;
                   3141:     }
                   3142: 
                   3143: 
                   3144: /* This routine is used only for POST requests.  It reads the data
                   3145: ** from the request and sends it to the child process.  The only reason
                   3146: ** we need to do it this way instead of just letting the child read
                   3147: ** directly is that we have already read part of the data into our
                   3148: ** buffer.
                   3149: */
                   3150: static void
                   3151: cgi_interpose_input( httpd_conn* hc, int wfd )
                   3152:     {
                   3153:     size_t c;
                   3154:     ssize_t r;
                   3155:     char buf[1024];
                   3156: 
                   3157:     c = hc->read_idx - hc->checked_idx;
                   3158:     if ( c > 0 )
                   3159:        {
                   3160:        if ( httpd_write_fully( wfd, &(hc->read_buf[hc->checked_idx]), c ) != c )
                   3161:            return;
                   3162:        }
                   3163:     while ( c < hc->contentlength )
                   3164:        {
                   3165:        r = read( hc->conn_fd, buf, MIN( sizeof(buf), hc->contentlength - c ) );
                   3166:        if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) )
                   3167:            {
                   3168:            sleep( 1 );
                   3169:            continue;
                   3170:            }
                   3171:        if ( r <= 0 )
                   3172:            return;
                   3173:        if ( httpd_write_fully( wfd, buf, r ) != r )
                   3174:            return;
                   3175:        c += r;
                   3176:        }
                   3177:     post_post_garbage_hack( hc );
                   3178:     }
                   3179: 
                   3180: 
                   3181: /* Special hack to deal with broken browsers that send a LF or CRLF
                   3182: ** after POST data, causing TCP resets - we just read and discard up
                   3183: ** to 2 bytes.  Unfortunately this doesn't fix the problem for CGIs
                   3184: ** which avoid the interposer process due to their POST data being
                   3185: ** short.  Creating an interposer process for all POST CGIs is
                   3186: ** unacceptably expensive.  The eventual fix will come when interposing
                   3187: ** gets integrated into the main loop as a tasklet instead of a process.
                   3188: */
                   3189: static void
                   3190: post_post_garbage_hack( httpd_conn* hc )
                   3191:     {
                   3192:     char buf[2];
                   3193: 
                   3194:     /* If we are in a sub-process, turn on no-delay mode in case we
                   3195:     ** previously cleared it.
                   3196:     */
                   3197:     if ( sub_process )
                   3198:        httpd_set_ndelay( hc->conn_fd );
                   3199:     /* And read up to 2 bytes. */
                   3200:     (void) read( hc->conn_fd, buf, sizeof(buf) );
                   3201:     }
                   3202: 
                   3203: 
                   3204: /* This routine is used for parsed-header CGIs.  The idea here is that the
                   3205: ** CGI can return special headers such as "Status:" and "Location:" which
                   3206: ** change the return status of the response.  Since the return status has to
                   3207: ** be the very first line written out, we have to accumulate all the headers
                   3208: ** and check for the special ones before writing the status.  Then we write
                   3209: ** out the saved headers and proceed to echo the rest of the response.
                   3210: */
                   3211: static void
                   3212: cgi_interpose_output( httpd_conn* hc, int rfd )
                   3213:     {
                   3214:     int r;
                   3215:     char buf[1024];
                   3216:     size_t headers_size, headers_len;
                   3217:     char* headers;
                   3218:     char* br;
                   3219:     int status;
                   3220:     char* title;
                   3221:     char* cp;
                   3222: 
                   3223:     /* Make sure the connection is in blocking mode.  It should already
                   3224:     ** be blocking, but we might as well be sure.
                   3225:     */
                   3226:     httpd_clear_ndelay( hc->conn_fd );
                   3227: 
                   3228:     /* Slurp in all headers. */
                   3229:     headers_size = 0;
                   3230:     httpd_realloc_str( &headers, &headers_size, 500 );
                   3231:     headers_len = 0;
                   3232:     for (;;)
                   3233:        {
                   3234:        r = read( rfd, buf, sizeof(buf) );
                   3235:        if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) )
                   3236:            {
                   3237:            sleep( 1 );
                   3238:            continue;
                   3239:            }
                   3240:        if ( r <= 0 )
                   3241:            {
                   3242:            br = &(headers[headers_len]);
                   3243:            break;
                   3244:            }
                   3245:        httpd_realloc_str( &headers, &headers_size, headers_len + r );
                   3246:        (void) memmove( &(headers[headers_len]), buf, r );
                   3247:        headers_len += r;
                   3248:        headers[headers_len] = '\0';
                   3249:        if ( ( br = strstr( headers, "\015\012\015\012" ) ) != (char*) 0 ||
                   3250:             ( br = strstr( headers, "\012\012" ) ) != (char*) 0 )
                   3251:            break;
                   3252:        }
                   3253: 
                   3254:     /* If there were no headers, bail. */
                   3255:     if ( headers[0] == '\0' )
                   3256:        return;
                   3257: 
                   3258:     /* Figure out the status.  Look for a Status: or Location: header;
                   3259:     ** else if there's an HTTP header line, get it from there; else
                   3260:     ** default to 200.
                   3261:     */
                   3262:     status = 200;
                   3263:     if ( strncmp( headers, "HTTP/", 5 ) == 0 )
                   3264:        {
                   3265:        cp = headers;
                   3266:        cp += strcspn( cp, " \t" );
                   3267:        status = atoi( cp );
                   3268:        }
                   3269:     if ( ( cp = strstr( headers, "Status:" ) ) != (char*) 0 &&
                   3270:         cp < br &&
                   3271:         ( cp == headers || *(cp-1) == '\012' ) )
                   3272:        {
                   3273:        cp += 7;
                   3274:        cp += strspn( cp, " \t" );
                   3275:        status = atoi( cp );
                   3276:        }
                   3277:     if ( ( cp = strstr( headers, "Location:" ) ) != (char*) 0 &&
                   3278:         cp < br &&
                   3279:         ( cp == headers || *(cp-1) == '\012' ) )
                   3280:        status = 302;
                   3281: 
                   3282:     /* Write the status line. */
                   3283:     switch ( status )
                   3284:        {
                   3285:        case 200: title = ok200title; break;
                   3286:        case 302: title = err302title; break;
                   3287:        case 304: title = err304title; break;
                   3288:        case 400: title = httpd_err400title; break;
                   3289: #ifdef AUTH_FILE
                   3290:        case 401: title = err401title; break;
                   3291: #endif /* AUTH_FILE */
                   3292:        case 403: title = err403title; break;
                   3293:        case 404: title = err404title; break;
                   3294:        case 408: title = httpd_err408title; break;
                   3295:        case 500: title = err500title; break;
                   3296:        case 501: title = err501title; break;
                   3297:        case 503: title = httpd_err503title; break;
                   3298:        default: title = "Something"; break;
                   3299:        }
                   3300:     (void) my_snprintf( buf, sizeof(buf), "HTTP/1.0 %d %s\015\012", status, title );
                   3301:     (void) httpd_write_fully( hc->conn_fd, buf, strlen( buf ) );
                   3302: 
                   3303:     /* Write the saved headers. */
                   3304:     (void) httpd_write_fully( hc->conn_fd, headers, headers_len );
                   3305: 
                   3306:     /* Echo the rest of the output. */
                   3307:     for (;;)
                   3308:        {
                   3309:        r = read( rfd, buf, sizeof(buf) );
                   3310:        if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) )
                   3311:            {
                   3312:            sleep( 1 );
                   3313:            continue;
                   3314:            }
                   3315:        if ( r <= 0 )
                   3316:            break;
                   3317:        if ( httpd_write_fully( hc->conn_fd, buf, r ) != r )
                   3318:            break;
                   3319:        }
                   3320:     shutdown( hc->conn_fd, SHUT_WR );
                   3321:     }
                   3322: 
                   3323: 
                   3324: /* CGI child process. */
                   3325: static void
                   3326: cgi_child( httpd_conn* hc )
                   3327:     {
                   3328:     int r;
                   3329:     char** argp;
                   3330:     char** envp;
                   3331:     char* binary;
                   3332:     char* directory;
                   3333: 
                   3334:     /* Unset close-on-exec flag for this socket.  This actually shouldn't
                   3335:     ** be necessary, according to POSIX a dup()'d file descriptor does
                   3336:     ** *not* inherit the close-on-exec flag, its flag is always clear.
                   3337:     ** However, Linux messes this up and does copy the flag to the
                   3338:     ** dup()'d descriptor, so we have to clear it.  This could be
                   3339:     ** ifdeffed for Linux only.
                   3340:     */
                   3341:     (void) fcntl( hc->conn_fd, F_SETFD, 0 );
                   3342: 
                   3343:     /* Close the syslog descriptor so that the CGI program can't
                   3344:     ** mess with it.  All other open descriptors should be either
                   3345:     ** the listen socket(s), sockets from accept(), or the file-logging
                   3346:     ** fd, and all of those are set to close-on-exec, so we don't
                   3347:     ** have to close anything else.
                   3348:     */
                   3349:     closelog();
                   3350: 
                   3351:     /* If the socket happens to be using one of the stdin/stdout/stderr
                   3352:     ** descriptors, move it to another descriptor so that the dup2 calls
                   3353:     ** below don't screw things up.  We arbitrarily pick fd 3 - if there
                   3354:     ** was already something on it, we clobber it, but that doesn't matter
                   3355:     ** since at this point the only fd of interest is the connection.
                   3356:     ** All others will be closed on exec.
                   3357:     */
                   3358:     if ( hc->conn_fd == STDIN_FILENO || hc->conn_fd == STDOUT_FILENO || hc->conn_fd == STDERR_FILENO )
                   3359:        {
                   3360:        int newfd = dup2( hc->conn_fd, STDERR_FILENO + 1 );
                   3361:        if ( newfd >= 0 )
                   3362:            hc->conn_fd = newfd;
                   3363:        /* If the dup2 fails, shrug.  We'll just take our chances.
                   3364:        ** Shouldn't happen though.
                   3365:        */
                   3366:        }
                   3367: 
                   3368:     /* Make the environment vector. */
                   3369:     envp = make_envp( hc );
                   3370: 
                   3371:     /* Make the argument vector. */
                   3372:     argp = make_argp( hc );
                   3373: 
                   3374:     /* Set up stdin.  For POSTs we may have to set up a pipe from an
                   3375:     ** interposer process, depending on if we've read some of the data
                   3376:     ** into our buffer.
                   3377:     */
                   3378:     if ( hc->method == METHOD_POST && hc->read_idx > hc->checked_idx )
                   3379:        {
                   3380:        int p[2];
                   3381: 
                   3382:        if ( pipe( p ) < 0 )
                   3383:            {
                   3384:            syslog( LOG_ERR, "pipe - %m" );
                   3385:            httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
                   3386:            httpd_write_response( hc );
                   3387:            exit( 1 );
                   3388:            }
                   3389:        r = fork( );
                   3390:        if ( r < 0 )
                   3391:            {
                   3392:            syslog( LOG_ERR, "fork - %m" );
                   3393:            httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
                   3394:            httpd_write_response( hc );
                   3395:            exit( 1 );
                   3396:            }
                   3397:        if ( r == 0 )
                   3398:            {
                   3399:            /* Interposer process. */
                   3400:            sub_process = 1;
                   3401:            (void) close( p[0] );
                   3402:            cgi_interpose_input( hc, p[1] );
                   3403:            exit( 0 );
                   3404:            }
                   3405:        /* Need to schedule a kill for process r; but in the main process! */
                   3406:        (void) close( p[1] );
                   3407:        if ( p[0] != STDIN_FILENO )
                   3408:            {
                   3409:            (void) dup2( p[0], STDIN_FILENO );
                   3410:            (void) close( p[0] );
                   3411:            }
                   3412:        }
                   3413:     else
                   3414:        {
                   3415:        /* Otherwise, the request socket is stdin. */
                   3416:        if ( hc->conn_fd != STDIN_FILENO )
                   3417:            (void) dup2( hc->conn_fd, STDIN_FILENO );
                   3418:        }
                   3419: 
                   3420:     /* Set up stdout/stderr.  If we're doing CGI header parsing,
                   3421:     ** we need an output interposer too.
                   3422:     */
                   3423:     if ( strncmp( argp[0], "nph-", 4 ) != 0 && hc->mime_flag )
                   3424:        {
                   3425:        int p[2];
                   3426: 
                   3427:        if ( pipe( p ) < 0 )
                   3428:            {
                   3429:            syslog( LOG_ERR, "pipe - %m" );
                   3430:            httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
                   3431:            httpd_write_response( hc );
                   3432:            exit( 1 );
                   3433:            }
                   3434:        r = fork( );
                   3435:        if ( r < 0 )
                   3436:            {
                   3437:            syslog( LOG_ERR, "fork - %m" );
                   3438:            httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
                   3439:            httpd_write_response( hc );
                   3440:            exit( 1 );
                   3441:            }
                   3442:        if ( r == 0 )
                   3443:            {
                   3444:            /* Interposer process. */
                   3445:            sub_process = 1;
                   3446:            (void) close( p[1] );
                   3447:            cgi_interpose_output( hc, p[0] );
                   3448:            exit( 0 );
                   3449:            }
                   3450:        /* Need to schedule a kill for process r; but in the main process! */
                   3451:        (void) close( p[0] );
                   3452:        if ( p[1] != STDOUT_FILENO )
                   3453:            (void) dup2( p[1], STDOUT_FILENO );
                   3454:        if ( p[1] != STDERR_FILENO )
                   3455:            (void) dup2( p[1], STDERR_FILENO );
                   3456:        if ( p[1] != STDOUT_FILENO && p[1] != STDERR_FILENO )
                   3457:            (void) close( p[1] );
                   3458:        }
                   3459:     else
                   3460:        {
                   3461:        /* Otherwise, the request socket is stdout/stderr. */
                   3462:        if ( hc->conn_fd != STDOUT_FILENO )
                   3463:            (void) dup2( hc->conn_fd, STDOUT_FILENO );
                   3464:        if ( hc->conn_fd != STDERR_FILENO )
                   3465:            (void) dup2( hc->conn_fd, STDERR_FILENO );
                   3466:        }
                   3467: 
                   3468:     /* At this point we would like to set close-on-exec again for hc->conn_fd
                   3469:     ** (see previous comments on Linux's broken behavior re: close-on-exec
                   3470:     ** and dup.)  Unfortunately there seems to be another Linux problem, or
                   3471:     ** perhaps a different aspect of the same problem - if we do this
                   3472:     ** close-on-exec in Linux, the socket stays open but stderr gets
                   3473:     ** closed - the last fd duped from the socket.  What a mess.  So we'll
                   3474:     ** just leave the socket as is, which under other OSs means an extra
                   3475:     ** file descriptor gets passed to the child process.  Since the child
                   3476:     ** probably already has that file open via stdin stdout and/or stderr,
                   3477:     ** this is not a problem.
                   3478:     */
                   3479:     /* (void) fcntl( hc->conn_fd, F_SETFD, 1 ); */
                   3480: 
                   3481: #ifdef CGI_NICE
                   3482:     /* Set priority. */
                   3483:     (void) nice( CGI_NICE );
                   3484: #endif /* CGI_NICE */
                   3485: 
                   3486:     /* Split the program into directory and binary, so we can chdir()
                   3487:     ** to the program's own directory.  This isn't in the CGI 1.1
                   3488:     ** spec, but it's what other HTTP servers do.
                   3489:     */
                   3490:     directory = strdup( hc->expnfilename );
                   3491:     if ( directory == (char*) 0 )
                   3492:        binary = hc->expnfilename;      /* ignore errors */
                   3493:     else
                   3494:        {
                   3495:        binary = strrchr( directory, '/' );
                   3496:        if ( binary == (char*) 0 )
                   3497:            binary = hc->expnfilename;
                   3498:        else
                   3499:            {
                   3500:            *binary++ = '\0';
                   3501:            (void) chdir( directory );  /* ignore errors */
                   3502:            }
                   3503:        }
                   3504: 
                   3505:     /* Default behavior for SIGPIPE. */
                   3506: #ifdef HAVE_SIGSET
                   3507:     (void) sigset( SIGPIPE, SIG_DFL );
                   3508: #else /* HAVE_SIGSET */
                   3509:     (void) signal( SIGPIPE, SIG_DFL );
                   3510: #endif /* HAVE_SIGSET */
                   3511: 
                   3512:     /* Run the program. */
                   3513:     (void) execve( binary, argp, envp );
                   3514: 
                   3515:     /* Something went wrong. */
                   3516:     syslog( LOG_ERR, "execve %.80s - %m", hc->expnfilename );
                   3517:     httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
                   3518:     httpd_write_response( hc );
                   3519:     exit( 1 );
                   3520:     }
                   3521: 
                   3522: 
                   3523: static int
                   3524: cgi( httpd_conn* hc )
                   3525:     {
                   3526:     int r;
                   3527:     ClientData client_data;
                   3528: 
                   3529:     if ( hc->method == METHOD_GET || hc->method == METHOD_POST )
                   3530:        {
                   3531:        if ( hc->hs->cgi_limit != 0 && hc->hs->cgi_count >= hc->hs->cgi_limit )
                   3532:            {
                   3533:            httpd_send_err(
                   3534:                hc, 503, httpd_err503title, "", httpd_err503form,
                   3535:                hc->encodedurl );
                   3536:            return -1;
                   3537:            }
                   3538:        ++hc->hs->cgi_count;
                   3539:        httpd_clear_ndelay( hc->conn_fd );
                   3540:        r = fork( );
                   3541:        if ( r < 0 )
                   3542:            {
                   3543:            syslog( LOG_ERR, "fork - %m" );
                   3544:            httpd_send_err(
                   3545:                hc, 500, err500title, "", err500form, hc->encodedurl );
                   3546:            return -1;
                   3547:            }
                   3548:        if ( r == 0 )
                   3549:            {
                   3550:            /* Child process. */
                   3551:            sub_process = 1;
                   3552:            httpd_unlisten( hc->hs );
                   3553:            cgi_child( hc );
                   3554:            }
                   3555: 
                   3556:        /* Parent process. */
                   3557:        syslog( LOG_INFO, "spawned CGI process %d for file '%.200s'", r, hc->expnfilename );
                   3558: #ifdef CGI_TIMELIMIT
                   3559:        /* Schedule a kill for the child process, in case it runs too long */
                   3560:        client_data.i = r;
                   3561:        if ( tmr_create( (struct timeval*) 0, cgi_kill, client_data, CGI_TIMELIMIT * 1000L, 0 ) == (Timer*) 0 )
                   3562:            {
                   3563:            syslog( LOG_CRIT, "tmr_create(cgi_kill child) failed" );
                   3564:            exit( 1 );
                   3565:            }
                   3566: #endif /* CGI_TIMELIMIT */
                   3567:        hc->status = 200;
                   3568:        hc->bytes_sent = CGI_BYTECOUNT;
                   3569:        hc->should_linger = 0;
                   3570:        }
                   3571:     else
                   3572:        {
                   3573:        httpd_send_err(
                   3574:            hc, 501, err501title, "", err501form, httpd_method_str( hc->method ) );
                   3575:        return -1;
                   3576:        }
                   3577: 
                   3578:     return 0;
                   3579:     }
                   3580: 
                   3581: 
                   3582: static int
                   3583: really_start_request( httpd_conn* hc, struct timeval* nowP )
                   3584:     {
                   3585:     static char* indexname;
                   3586:     static size_t maxindexname = 0;
                   3587:     static const char* index_names[] = { INDEX_NAMES };
                   3588:     int i;
                   3589: #ifdef AUTH_FILE
                   3590:     static char* dirname;
                   3591:     static size_t maxdirname = 0;
                   3592: #endif /* AUTH_FILE */
                   3593:     size_t expnlen, indxlen;
                   3594:     char* cp;
                   3595:     char* pi;
                   3596: 
                   3597:     expnlen = strlen( hc->expnfilename );
                   3598: 
                   3599:     if ( hc->method != METHOD_GET && hc->method != METHOD_HEAD &&
                   3600:         hc->method != METHOD_POST )
                   3601:        {
                   3602:        httpd_send_err(
                   3603:            hc, 501, err501title, "", err501form, httpd_method_str( hc->method ) );
                   3604:        return -1;
                   3605:        }
                   3606: 
                   3607:     /* Stat the file. */
                   3608:     if ( stat( hc->expnfilename, &hc->sb ) < 0 )
                   3609:        {
                   3610:        httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
                   3611:        return -1;
                   3612:        }
                   3613: 
                   3614:     /* Is it world-readable or world-executable?  We check explicitly instead
                   3615:     ** of just trying to open it, so that no one ever gets surprised by
                   3616:     ** a file that's not set world-readable and yet somehow is
                   3617:     ** readable by the HTTP server and therefore the *whole* world.
                   3618:     */
                   3619:     if ( ! ( hc->sb.st_mode & ( S_IROTH | S_IXOTH ) ) )
                   3620:        {
                   3621:        syslog(
                   3622:            LOG_INFO,
                   3623:            "%.80s URL \"%.80s\" resolves to a non world-readable file",
                   3624:            httpd_ntoa( &hc->client_addr ), hc->encodedurl );
                   3625:        httpd_send_err(
                   3626:            hc, 403, err403title, "",
                   3627:            ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file that is not world-readable.\n" ),
                   3628:            hc->encodedurl );
                   3629:        return -1;
                   3630:        }
                   3631: 
                   3632:     /* Is it a directory? */
                   3633:     if ( S_ISDIR(hc->sb.st_mode) )
                   3634:        {
                   3635:        /* If there's pathinfo, it's just a non-existent file. */
                   3636:        if ( hc->pathinfo[0] != '\0' )
                   3637:            {
                   3638:            httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl );
                   3639:            return -1;
                   3640:            }
                   3641: 
                   3642:        /* Special handling for directory URLs that don't end in a slash.
                   3643:        ** We send back an explicit redirect with the slash, because
                   3644:        ** otherwise many clients can't build relative URLs properly.
                   3645:        */
                   3646:        if ( strcmp( hc->origfilename, "" ) != 0 &&
                   3647:             strcmp( hc->origfilename, "." ) != 0 &&
                   3648:             hc->origfilename[strlen( hc->origfilename ) - 1] != '/' )
                   3649:            {
                   3650:            send_dirredirect( hc );
                   3651:            return -1;
                   3652:            }
                   3653: 
                   3654:        /* Check for an index file. */
                   3655:        for ( i = 0; i < sizeof(index_names) / sizeof(char*); ++i )
                   3656:            {
                   3657:            httpd_realloc_str(
                   3658:                &indexname, &maxindexname,
                   3659:                expnlen + 1 + strlen( index_names[i] ) );
                   3660:            (void) strcpy( indexname, hc->expnfilename );
                   3661:            indxlen = strlen( indexname );
                   3662:            if ( indxlen == 0 || indexname[indxlen - 1] != '/' )
                   3663:                (void) strcat( indexname, "/" );
                   3664:            if ( strcmp( indexname, "./" ) == 0 )
                   3665:                indexname[0] = '\0';
                   3666:            (void) strcat( indexname, index_names[i] );
                   3667:            if ( stat( indexname, &hc->sb ) >= 0 )
                   3668:                goto got_one;
                   3669:            }
                   3670: 
                   3671:        /* Nope, no index file, so it's an actual directory request. */
                   3672: #ifdef GENERATE_INDEXES
                   3673:        /* Directories must be readable for indexing. */
                   3674:        if ( ! ( hc->sb.st_mode & S_IROTH ) )
                   3675:            {
                   3676:            syslog(
                   3677:                LOG_INFO,
                   3678:                "%.80s URL \"%.80s\" tried to index a directory with indexing disabled",
                   3679:                httpd_ntoa( &hc->client_addr ), hc->encodedurl );
                   3680:            httpd_send_err(
                   3681:                hc, 403, err403title, "",
                   3682:                ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a directory that has indexing disabled.\n" ),
                   3683:                hc->encodedurl );
                   3684:            return -1;
                   3685:            }
                   3686: #ifdef AUTH_FILE
                   3687:        /* Check authorization for this directory. */
                   3688:        if ( auth_check( hc, hc->expnfilename ) == -1 )
                   3689:            return -1;
                   3690: #endif /* AUTH_FILE */
                   3691:        /* Referer check. */
                   3692:        if ( ! check_referer( hc ) )
                   3693:            return -1;
                   3694:        /* Ok, generate an index. */
                   3695:        return ls( hc );
                   3696: #else /* GENERATE_INDEXES */
                   3697:        syslog(
                   3698:            LOG_INFO, "%.80s URL \"%.80s\" tried to index a directory",
                   3699:            httpd_ntoa( &hc->client_addr ), hc->encodedurl );
                   3700:        httpd_send_err(
                   3701:            hc, 403, err403title, "",
                   3702:            ERROR_FORM( err403form, "The requested URL '%.80s' is a directory, and directory indexing is disabled on this server.\n" ),
                   3703:            hc->encodedurl );
                   3704:        return -1;
                   3705: #endif /* GENERATE_INDEXES */
                   3706: 
                   3707:        got_one: ;
                   3708:        /* Got an index file.  Expand symlinks again.  More pathinfo means
                   3709:        ** something went wrong.
                   3710:        */
                   3711:        cp = expand_symlinks( indexname, &pi, hc->hs->no_symlink_check, hc->tildemapped );
                   3712:        if ( cp == (char*) 0 || pi[0] != '\0' )
                   3713:            {
                   3714:            httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
                   3715:            return -1;
                   3716:            }
                   3717:        expnlen = strlen( cp );
                   3718:        httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, expnlen );
                   3719:        (void) strcpy( hc->expnfilename, cp );
                   3720: 
                   3721:        /* Now, is the index version world-readable or world-executable? */
                   3722:        if ( ! ( hc->sb.st_mode & ( S_IROTH | S_IXOTH ) ) )
                   3723:            {
                   3724:            syslog(
                   3725:                LOG_INFO,
                   3726:                "%.80s URL \"%.80s\" resolves to a non-world-readable index file",
                   3727:                httpd_ntoa( &hc->client_addr ), hc->encodedurl );
                   3728:            httpd_send_err(
                   3729:                hc, 403, err403title, "",
                   3730:                ERROR_FORM( err403form, "The requested URL '%.80s' resolves to an index file that is not world-readable.\n" ),
                   3731:                hc->encodedurl );
                   3732:            return -1;
                   3733:            }
                   3734:        }
                   3735: 
                   3736: #ifdef AUTH_FILE
                   3737:     /* Check authorization for this directory. */
                   3738:     httpd_realloc_str( &dirname, &maxdirname, expnlen );
                   3739:     (void) strcpy( dirname, hc->expnfilename );
                   3740:     cp = strrchr( dirname, '/' );
                   3741:     if ( cp == (char*) 0 )
                   3742:        (void) strcpy( dirname, "." );
                   3743:     else
                   3744:        *cp = '\0';
                   3745:     if ( auth_check( hc, dirname ) == -1 )
                   3746:        return -1;
                   3747: 
                   3748:     /* Check if the filename is the AUTH_FILE itself - that's verboten. */
                   3749:     if ( expnlen == sizeof(AUTH_FILE) - 1 )
                   3750:        {
                   3751:        if ( strcmp( hc->expnfilename, AUTH_FILE ) == 0 )
                   3752:            {
                   3753:            syslog(
                   3754:                LOG_NOTICE,
                   3755:                "%.80s URL \"%.80s\" tried to retrieve an auth file",
                   3756:                httpd_ntoa( &hc->client_addr ), hc->encodedurl );
                   3757:            httpd_send_err(
                   3758:                hc, 403, err403title, "",
                   3759:                ERROR_FORM( err403form, "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n" ),
                   3760:                hc->encodedurl );
                   3761:            return -1;
                   3762:            }
                   3763:        }
                   3764:     else if ( expnlen >= sizeof(AUTH_FILE) &&
                   3765:              strcmp( &(hc->expnfilename[expnlen - sizeof(AUTH_FILE) + 1]), AUTH_FILE ) == 0 &&
                   3766:              hc->expnfilename[expnlen - sizeof(AUTH_FILE)] == '/' )
                   3767:        {
                   3768:        syslog(
                   3769:            LOG_NOTICE,
                   3770:            "%.80s URL \"%.80s\" tried to retrieve an auth file",
                   3771:            httpd_ntoa( &hc->client_addr ), hc->encodedurl );
                   3772:        httpd_send_err(
                   3773:            hc, 403, err403title, "",
                   3774:            ERROR_FORM( err403form, "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n" ),
                   3775:            hc->encodedurl );
                   3776:        return -1;
                   3777:        }
                   3778: #endif /* AUTH_FILE */
                   3779: 
                   3780:     /* Referer check. */
                   3781:     if ( ! check_referer( hc ) )
                   3782:        return -1;
                   3783: 
                   3784:     /* Is it world-executable and in the CGI area? */
                   3785:     if ( hc->hs->cgi_pattern != (char*) 0 &&
                   3786:         ( hc->sb.st_mode & S_IXOTH ) &&
                   3787:         match( hc->hs->cgi_pattern, hc->expnfilename ) )
                   3788:        return cgi( hc );
                   3789: 
                   3790:     /* It's not CGI.  If it's executable or there's pathinfo, someone's
                   3791:     ** trying to either serve or run a non-CGI file as CGI.   Either case
                   3792:     ** is prohibited.
                   3793:     */
                   3794:     if ( hc->sb.st_mode & S_IXOTH )
                   3795:        {
                   3796:        syslog(
                   3797:            LOG_NOTICE, "%.80s URL \"%.80s\" is executable but isn't CGI",
                   3798:            httpd_ntoa( &hc->client_addr ), hc->encodedurl );
                   3799:        httpd_send_err(
                   3800:            hc, 403, err403title, "",
                   3801:            ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file which is marked executable but is not a CGI file; retrieving it is forbidden.\n" ),
                   3802:            hc->encodedurl );
                   3803:        return -1;
                   3804:        }
                   3805:     if ( hc->pathinfo[0] != '\0' )
                   3806:        {
                   3807:        syslog(
                   3808:            LOG_INFO, "%.80s URL \"%.80s\" has pathinfo but isn't CGI",
                   3809:            httpd_ntoa( &hc->client_addr ), hc->encodedurl );
                   3810:        httpd_send_err(
                   3811:            hc, 403, err403title, "",
                   3812:            ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file plus CGI-style pathinfo, but the file is not a valid CGI file.\n" ),
                   3813:            hc->encodedurl );
                   3814:        return -1;
                   3815:        }
                   3816: 
                   3817:     /* Fill in last_byte_index, if necessary. */
                   3818:     if ( hc->got_range &&
                   3819:         ( hc->last_byte_index == -1 || hc->last_byte_index >= hc->sb.st_size ) )
                   3820:        hc->last_byte_index = hc->sb.st_size - 1;
                   3821: 
                   3822:     figure_mime( hc );
                   3823: 
                   3824:     if ( hc->method == METHOD_HEAD )
                   3825:        {
                   3826:        send_mime(
                   3827:            hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size,
                   3828:            hc->sb.st_mtime );
                   3829:        }
                   3830:     else if ( hc->if_modified_since != (time_t) -1 &&
                   3831:         hc->if_modified_since >= hc->sb.st_mtime )
                   3832:        {
                   3833:        send_mime(
                   3834:            hc, 304, err304title, hc->encodings, "", hc->type, (off_t) -1,
                   3835:            hc->sb.st_mtime );
                   3836:        }
                   3837:     else
                   3838:        {
                   3839:        hc->file_address = mmc_map( hc->expnfilename, &(hc->sb), nowP );
                   3840:        if ( hc->file_address == (char*) 0 )
                   3841:            {
                   3842:            httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
                   3843:            return -1;
                   3844:            }
                   3845: #ifdef USE_SENDFILE
                   3846:        hc->file_fd = *((int *) hc->file_address);
                   3847: #endif
                   3848:        send_mime(
                   3849:            hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size,
                   3850:            hc->sb.st_mtime );
                   3851:        }
                   3852: 
                   3853:     return 0;
                   3854:     }
                   3855: 
                   3856: 
                   3857: int
                   3858: httpd_start_request( httpd_conn* hc, struct timeval* nowP )
                   3859:     {
                   3860:     int r;
                   3861: 
                   3862:     /* Really start the request. */
                   3863:     r = really_start_request( hc, nowP );
                   3864: 
                   3865:     /* And return the status. */
                   3866:     return r;
                   3867:     }
                   3868: 
                   3869: 
                   3870: static void
                   3871: make_log_entry( httpd_conn* hc, struct timeval* nowP )
                   3872:     {
                   3873:     char* ru;
                   3874:     char url[305];
                   3875:     char bytes[40];
                   3876: 
                   3877:     if ( hc->hs->no_log )
                   3878:        return;
                   3879: 
                   3880:     /* This is straight CERN Combined Log Format - the only tweak
                   3881:     ** being that if we're using syslog() we leave out the date, because
                   3882:     ** syslogd puts it in.  The included syslogtocern script turns the
                   3883:     ** results into true CERN format.
                   3884:     */
                   3885: 
                   3886:     /* Format remote user. */
                   3887:     if ( hc->remoteuser[0] != '\0' )
                   3888:        ru = hc->remoteuser;
                   3889:     else
                   3890:        ru = "-";
                   3891:     /* If we're vhosting, prepend the hostname to the url.  This is
                   3892:     ** a little weird, perhaps writing separate log files for
                   3893:     ** each vhost would make more sense.
                   3894:     */
                   3895:     if ( hc->hs->vhost && ! hc->tildemapped )
                   3896:        (void) my_snprintf( url, sizeof(url),
                   3897:            "/%.100s%.200s",
                   3898:            hc->hostname == (char*) 0 ? hc->hs->server_hostname : hc->hostname,
                   3899:            hc->encodedurl );
                   3900:     else
                   3901:        (void) my_snprintf( url, sizeof(url),
                   3902:            "%.200s", hc->encodedurl );
                   3903:     /* Format the bytes. */
                   3904:     if ( hc->bytes_sent >= 0 )
                   3905:        (void) my_snprintf(
                   3906:            bytes, sizeof(bytes), "%lld", (int64_t) hc->bytes_sent );
                   3907:     else
                   3908:        (void) strcpy( bytes, "-" );
                   3909: 
                   3910:     /* Logfile or syslog? */
                   3911:     if ( hc->hs->logfp != (FILE*) 0 )
                   3912:        {
                   3913:        time_t now;
                   3914:        struct tm* t;
                   3915:        const char* cernfmt_nozone = "%d/%b/%Y:%H:%M:%S";
                   3916:        char date_nozone[100];
                   3917:        int zone;
                   3918:        char sign;
                   3919:        char date[100];
                   3920: 
                   3921:        /* Get the current time, if necessary. */
                   3922:        if ( nowP != (struct timeval*) 0 )
                   3923:            now = nowP->tv_sec;
                   3924:        else
                   3925:            now = time( (time_t*) 0 );
                   3926:        /* Format the time, forcing a numeric timezone (some log analyzers
                   3927:        ** are stoooopid about this).
                   3928:        */
                   3929:        t = localtime( &now );
                   3930:        (void) strftime( date_nozone, sizeof(date_nozone), cernfmt_nozone, t );
                   3931: #ifdef HAVE_TM_GMTOFF
                   3932:        zone = t->tm_gmtoff / 60L;
                   3933: #else
                   3934:        zone = -timezone / 60L;
                   3935:        /* Probably have to add something about daylight time here. */
                   3936: #endif
                   3937:        if ( zone >= 0 )
                   3938:            sign = '+';
                   3939:        else
                   3940:            {
                   3941:            sign = '-';
                   3942:            zone = -zone;
                   3943:            }
                   3944:        zone = ( zone / 60 ) * 100 + zone % 60;
                   3945:        (void) my_snprintf( date, sizeof(date),
                   3946:            "%s %c%04d", date_nozone, sign, zone );
                   3947:        /* And write the log entry. */
                   3948:        (void) fprintf( hc->hs->logfp,
                   3949:            "%.80s - %.80s [%s] \"%.80s %.300s %.80s\" %d %s \"%.200s\" \"%.200s\"\n",
                   3950:            httpd_ntoa( &hc->client_addr ), ru, date,
                   3951:            httpd_method_str( hc->method ), url, hc->protocol,
                   3952:            hc->status, bytes, hc->referer, hc->useragent );
                   3953: #ifdef FLUSH_LOG_EVERY_TIME
                   3954:        (void) fflush( hc->hs->logfp );
                   3955: #endif
                   3956:        }
                   3957:     else
                   3958:        syslog( LOG_INFO,
                   3959:            "%.80s - %.80s \"%.80s %.200s %.80s\" %d %s \"%.200s\" \"%.200s\"",
                   3960:            httpd_ntoa( &hc->client_addr ), ru,
                   3961:            httpd_method_str( hc->method ), url, hc->protocol,
                   3962:            hc->status, bytes, hc->referer, hc->useragent );
                   3963:     }
                   3964: 
                   3965: 
                   3966: /* Returns 1 if ok to serve the url, 0 if not. */
                   3967: static int
                   3968: check_referer( httpd_conn* hc )
                   3969:     {
                   3970:     int r;
                   3971:     char* cp;
                   3972: 
                   3973:     /* Are we doing referer checking at all? */
                   3974:     if ( hc->hs->url_pattern == (char*) 0 )
                   3975:        return 1;
                   3976: 
                   3977:     r = really_check_referer( hc );
                   3978: 
                   3979:     if ( ! r )
                   3980:        {
                   3981:        if ( hc->hs->vhost && hc->hostname != (char*) 0 )
                   3982:            cp = hc->hostname;
                   3983:        else
                   3984:            cp = hc->hs->server_hostname;
                   3985:        if ( cp == (char*) 0 )
                   3986:            cp = "";
                   3987:        syslog(
                   3988:            LOG_INFO, "%.80s non-local referer \"%.80s%.80s\" \"%.80s\"",
                   3989:            httpd_ntoa( &hc->client_addr ), cp, hc->encodedurl, hc->referer );
                   3990:        httpd_send_err(
                   3991:            hc, 403, err403title, "",
                   3992:            ERROR_FORM( err403form, "You must supply a local referer to get URL '%.80s' from this server.\n" ),
                   3993:            hc->encodedurl );
                   3994:        }
                   3995:     return r;
                   3996:     }
                   3997: 
                   3998: 
                   3999: /* Returns 1 if ok to serve the url, 0 if not. */
                   4000: static int
                   4001: really_check_referer( httpd_conn* hc )
                   4002:     {
                   4003:     httpd_server* hs;
                   4004:     char* cp1;
                   4005:     char* cp2;
                   4006:     char* cp3;
                   4007:     static char* refhost = (char*) 0;
                   4008:     static size_t refhost_size = 0;
                   4009:     char *lp;
                   4010: 
                   4011:     hs = hc->hs;
                   4012: 
                   4013:     /* Check for an empty referer. */
                   4014:     if ( hc->referer == (char*) 0 || hc->referer[0] == '\0' ||
                   4015:         ( cp1 = strstr( hc->referer, "//" ) ) == (char*) 0 )
                   4016:        {
                   4017:        /* Disallow if we require a referer and the url matches. */
                   4018:        if ( hs->no_empty_referers && match( hs->url_pattern, hc->origfilename ) )
                   4019:            return 0;
                   4020:        /* Otherwise ok. */
                   4021:        return 1;
                   4022:        }
                   4023: 
                   4024:     /* Extract referer host. */
                   4025:     cp1 += 2;
                   4026:     for ( cp2 = cp1; *cp2 != '/' && *cp2 != ':' && *cp2 != '\0'; ++cp2 )
                   4027:        continue;
                   4028:     httpd_realloc_str( &refhost, &refhost_size, cp2 - cp1 );
                   4029:     for ( cp3 = refhost; cp1 < cp2; ++cp1, ++cp3 )
                   4030:        if ( isupper(*cp1) )
                   4031:            *cp3 = tolower(*cp1);
                   4032:        else
                   4033:            *cp3 = *cp1;
                   4034:     *cp3 = '\0';
                   4035: 
                   4036:     /* Local pattern? */
                   4037:     if ( hs->local_pattern != (char*) 0 )
                   4038:        lp = hs->local_pattern;
                   4039:     else
                   4040:        {
                   4041:        /* No local pattern.  What's our hostname? */
                   4042:        if ( ! hs->vhost )
                   4043:            {
                   4044:            /* Not vhosting, use the server name. */
                   4045:            lp = hs->server_hostname;
                   4046:            if ( lp == (char*) 0 )
                   4047:                /* Couldn't figure out local hostname - give up. */
                   4048:                return 1;
                   4049:            }
                   4050:        else
                   4051:            {
                   4052:            /* We are vhosting, use the hostname on this connection. */
                   4053:            lp = hc->hostname;
                   4054:            if ( lp == (char*) 0 )
                   4055:                /* Oops, no hostname.  Maybe it's an old browser that
                   4056:                ** doesn't send a Host: header.  We could figure out
                   4057:                ** the default hostname for this IP address, but it's
                   4058:                ** not worth it for the few requests like this.
                   4059:                */
                   4060:                return 1;
                   4061:            }
                   4062:        }
                   4063: 
                   4064:     /* If the referer host doesn't match the local host pattern, and
                   4065:     ** the filename does match the url pattern, it's an illegal reference.
                   4066:     */
                   4067:     if ( ! match( lp, refhost ) && match( hs->url_pattern, hc->origfilename ) )
                   4068:        return 0;
                   4069:     /* Otherwise ok. */
                   4070:     return 1;
                   4071:     }
                   4072: 
                   4073: 
                   4074: char*
                   4075: httpd_ntoa( httpd_sockaddr* saP )
                   4076:     {
                   4077: #ifdef USE_IPV6
                   4078:     static char str[200];
                   4079: 
                   4080:     if ( getnameinfo( &saP->sa, sockaddr_len( saP ), str, sizeof(str), 0, 0, NI_NUMERICHOST ) != 0 )
                   4081:        {
                   4082:        str[0] = '?';
                   4083:        str[1] = '\0';
                   4084:        }
                   4085:     else if ( IN6_IS_ADDR_V4MAPPED( &saP->sa_in6.sin6_addr ) && strncmp( str, "::ffff:", 7 ) == 0 )
                   4086:        /* Elide IPv6ish prefix for IPv4 addresses. */
                   4087:        (void) strcpy( str, &str[7] );
                   4088: 
                   4089:     return str;
                   4090: 
                   4091: #else /* USE_IPV6 */
                   4092: 
                   4093:     return inet_ntoa( saP->sa_in.sin_addr );
                   4094: 
                   4095: #endif /* USE_IPV6 */
                   4096:     }
                   4097: 
                   4098: 
                   4099: static int
                   4100: sockaddr_check( httpd_sockaddr* saP )
                   4101:     {
                   4102:     switch ( saP->sa.sa_family )
                   4103:        {
                   4104:        case AF_INET: return 1;
                   4105: #ifdef USE_IPV6
                   4106:        case AF_INET6: return 1;
                   4107: #endif /* USE_IPV6 */
                   4108:        default:
                   4109:        return 0;
                   4110:        }
                   4111:     }
                   4112: 
                   4113: 
                   4114: static size_t
                   4115: sockaddr_len( httpd_sockaddr* saP )
                   4116:     {
                   4117:     switch ( saP->sa.sa_family )
                   4118:        {
                   4119:        case AF_INET: return sizeof(struct sockaddr_in);
                   4120: #ifdef USE_IPV6
                   4121:        case AF_INET6: return sizeof(struct sockaddr_in6);
                   4122: #endif /* USE_IPV6 */
                   4123:        default:
                   4124:        return 0;       /* shouldn't happen */
                   4125:        }
                   4126:     }
                   4127: 
                   4128: 
                   4129: /* Some systems don't have snprintf(), so we make our own that uses
                   4130: ** either vsnprintf() or vsprintf().  If your system doesn't have
                   4131: ** vsnprintf(), it is probably vulnerable to buffer overruns.
                   4132: ** Upgrade!
                   4133: */
                   4134: static int
                   4135: my_snprintf( char* str, size_t size, const char* format, ... )
                   4136:     {
                   4137:     va_list ap;
                   4138:     int r;
                   4139: 
                   4140:     va_start( ap, format );
                   4141: #ifdef HAVE_VSNPRINTF
                   4142:     r = vsnprintf( str, size, format, ap );
                   4143: #else /* HAVE_VSNPRINTF */
                   4144:     r = vsprintf( str, format, ap );
                   4145: #endif /* HAVE_VSNPRINTF */
                   4146:     va_end( ap );
                   4147:     return r;
                   4148:     }
                   4149: 
                   4150: 
                   4151: #ifndef HAVE_ATOLL
                   4152: static long long
                   4153: atoll( const char* str )
                   4154:     {
                   4155:     long long value;
                   4156:     long long sign;
                   4157: 
                   4158:     while ( isspace( *str ) )
                   4159:        ++str;
                   4160:     switch ( *str )
                   4161:        {
                   4162:        case '-': sign = -1; ++str; break;
                   4163:        case '+': sign = 1; ++str; break;
                   4164:        default: sign = 1; break;
                   4165:        }
                   4166:     value = 0;
                   4167:     while ( isdigit( *str ) )
                   4168:        {
                   4169:        value = value * 10 + ( *str - '0' );
                   4170:        ++str;
                   4171:        }
                   4172:     return sign * value;
                   4173:     }
                   4174: #endif /* HAVE_ATOLL */
                   4175: 
                   4176: 
                   4177: /* Read the requested buffer completely, accounting for interruptions. */
                   4178: int
                   4179: httpd_read_fully( int fd, void* buf, size_t nbytes )
                   4180:     {
                   4181:     int nread;
                   4182: 
                   4183:     nread = 0;
                   4184:     while ( nread < nbytes )
                   4185:        {
                   4186:        int r;
                   4187: 
                   4188:        r = read( fd, (char*) buf + nread, nbytes - nread );
                   4189:        if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) )
                   4190:            {
                   4191:            sleep( 1 );
                   4192:            continue;
                   4193:            }
                   4194:        if ( r < 0 )
                   4195:            return r;
                   4196:        if ( r == 0 )
                   4197:            break;
                   4198:        nread += r;
                   4199:        }
                   4200: 
                   4201:     return nread;
                   4202:     }
                   4203: 
                   4204: 
                   4205: /* Write the requested buffer completely, accounting for interruptions. */
                   4206: int
                   4207: httpd_write_fully( int fd, const void* buf, size_t nbytes )
                   4208:     {
                   4209:     int nwritten;
                   4210: 
                   4211:     nwritten = 0;
                   4212:     while ( nwritten < nbytes )
                   4213:        {
                   4214:        int r;
                   4215: 
                   4216:        r = write( fd, (char*) buf + nwritten, nbytes - nwritten );
                   4217:        if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) )
                   4218:            {
                   4219:            sleep( 1 );
                   4220:            continue;
                   4221:            }
                   4222:        if ( r < 0 )
                   4223:            return r;
                   4224:        if ( r == 0 )
                   4225:            break;
                   4226:        nwritten += r;
                   4227:        }
                   4228: 
                   4229:     return nwritten;
                   4230:     }
                   4231: 
                   4232: 
                   4233: /* Generate debugging statistics syslog message. */
                   4234: void
                   4235: httpd_logstats( long secs )
                   4236:     {
                   4237:     if ( str_alloc_count > 0 )
                   4238:        syslog( LOG_INFO,
                   4239:            "  libhttpd - %d strings allocated, %lu bytes (%g bytes/str)",
                   4240:            str_alloc_count, (unsigned long) str_alloc_size,
                   4241:            (float) str_alloc_size / str_alloc_count );
                   4242:     }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>