File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / thttpd / libhttpd.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 17:21:13 2012 UTC (12 years, 3 months ago) by misho
Branches: thttpd, MAIN
CVS tags: v2_25b, HEAD
thttpd

    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>