Annotation of embedaddon/thttpd/libhttpd.c, revision 1.1
1.1 ! misho 1: /* libhttpd.c - HTTP protocol library
! 2: **
! 3: ** Copyright © 1995,1998,1999,2000,2001 by Jef Poskanzer <jef@mail.acme.com>.
! 4: ** All rights reserved.
! 5: **
! 6: ** Redistribution and use in source and binary forms, with or without
! 7: ** modification, are permitted provided that the following conditions
! 8: ** are met:
! 9: ** 1. Redistributions of source code must retain the above copyright
! 10: ** notice, this list of conditions and the following disclaimer.
! 11: ** 2. Redistributions in binary form must reproduce the above copyright
! 12: ** notice, this list of conditions and the following disclaimer in the
! 13: ** documentation and/or other materials provided with the distribution.
! 14: **
! 15: ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
! 16: ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 17: ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 18: ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
! 19: ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 20: ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 21: ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 22: ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 23: ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 24: ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 25: ** SUCH DAMAGE.
! 26: */
! 27:
! 28:
! 29: #include "config.h"
! 30: #include "version.h"
! 31:
! 32: #ifdef SHOW_SERVER_VERSION
! 33: #define EXPOSED_SERVER_SOFTWARE SERVER_SOFTWARE
! 34: #else /* SHOW_SERVER_VERSION */
! 35: #define EXPOSED_SERVER_SOFTWARE "thttpd"
! 36: #endif /* SHOW_SERVER_VERSION */
! 37:
! 38: #include <sys/types.h>
! 39: #include <sys/param.h>
! 40: #include <sys/stat.h>
! 41:
! 42: #include <ctype.h>
! 43: #include <errno.h>
! 44: #include <fcntl.h>
! 45: #include <time.h>
! 46: #ifdef HAVE_MEMORY_H
! 47: #include <memory.h>
! 48: #endif /* HAVE_MEMORY_H */
! 49: #include <pwd.h>
! 50: #include <signal.h>
! 51: #include <stdio.h>
! 52: #include <stdlib.h>
! 53: #include <string.h>
! 54: #include <syslog.h>
! 55: #include <unistd.h>
! 56: #include <stdarg.h>
! 57:
! 58: #ifdef HAVE_OSRELDATE_H
! 59: #include <osreldate.h>
! 60: #endif /* HAVE_OSRELDATE_H */
! 61:
! 62: #ifdef HAVE_DIRENT_H
! 63: # include <dirent.h>
! 64: # define NAMLEN(dirent) strlen((dirent)->d_name)
! 65: #else
! 66: # define dirent direct
! 67: # define NAMLEN(dirent) (dirent)->d_namlen
! 68: # ifdef HAVE_SYS_NDIR_H
! 69: # include <sys/ndir.h>
! 70: # endif
! 71: # ifdef HAVE_SYS_DIR_H
! 72: # include <sys/dir.h>
! 73: # endif
! 74: # ifdef HAVE_NDIR_H
! 75: # include <ndir.h>
! 76: # endif
! 77: #endif
! 78:
! 79: extern char* crypt( const char* key, const char* setting );
! 80:
! 81: #include "libhttpd.h"
! 82: #include "mmc.h"
! 83: #include "timers.h"
! 84: #include "match.h"
! 85: #include "tdate_parse.h"
! 86:
! 87: #ifndef STDIN_FILENO
! 88: #define STDIN_FILENO 0
! 89: #endif
! 90: #ifndef STDOUT_FILENO
! 91: #define STDOUT_FILENO 1
! 92: #endif
! 93: #ifndef STDERR_FILENO
! 94: #define STDERR_FILENO 2
! 95: #endif
! 96:
! 97: #ifndef SHUT_WR
! 98: #define SHUT_WR 1
! 99: #endif
! 100:
! 101: #ifndef HAVE_INT64T
! 102: typedef long long int64_t;
! 103: #endif
! 104:
! 105: #ifndef HAVE_SOCKLENT
! 106: typedef int socklen_t;
! 107: #endif
! 108:
! 109: #ifdef __CYGWIN__
! 110: #define timezone _timezone
! 111: #endif
! 112:
! 113: #ifndef MAX
! 114: #define MAX(a,b) ((a) > (b) ? (a) : (b))
! 115: #endif
! 116: #ifndef MIN
! 117: #define MIN(a,b) ((a) < (b) ? (a) : (b))
! 118: #endif
! 119:
! 120:
! 121: /* Forwards. */
! 122: static void check_options( void );
! 123: static void free_httpd_server( httpd_server* hs );
! 124: static int initialize_listen_socket( httpd_sockaddr* saP );
! 125: static void add_response( httpd_conn* hc, char* str );
! 126: static void send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, off_t length, time_t mod );
! 127: static void send_response( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg );
! 128: static void send_response_tail( httpd_conn* hc );
! 129: static void defang( char* str, char* dfstr, int dfsize );
! 130: #ifdef ERR_DIR
! 131: static int send_err_file( httpd_conn* hc, int status, char* title, char* extraheads, char* filename );
! 132: #endif /* ERR_DIR */
! 133: #ifdef AUTH_FILE
! 134: static void send_authenticate( httpd_conn* hc, char* realm );
! 135: static int b64_decode( const char* str, unsigned char* space, int size );
! 136: static int auth_check( httpd_conn* hc, char* dirname );
! 137: static int auth_check2( httpd_conn* hc, char* dirname );
! 138: #endif /* AUTH_FILE */
! 139: static void send_dirredirect( httpd_conn* hc );
! 140: static int hexit( char c );
! 141: static void strdecode( char* to, char* from );
! 142: #ifdef GENERATE_INDEXES
! 143: static void strencode( char* to, int tosize, char* from );
! 144: #endif /* GENERATE_INDEXES */
! 145: #ifdef TILDE_MAP_1
! 146: static int tilde_map_1( httpd_conn* hc );
! 147: #endif /* TILDE_MAP_1 */
! 148: #ifdef TILDE_MAP_2
! 149: static int tilde_map_2( httpd_conn* hc );
! 150: #endif /* TILDE_MAP_2 */
! 151: static int vhost_map( httpd_conn* hc );
! 152: static char* expand_symlinks( char* path, char** restP, int no_symlink_check, int tildemapped );
! 153: static char* bufgets( httpd_conn* hc );
! 154: static void de_dotdot( char* file );
! 155: static void init_mime( void );
! 156: static void figure_mime( httpd_conn* hc );
! 157: #ifdef CGI_TIMELIMIT
! 158: static void cgi_kill2( ClientData client_data, struct timeval* nowP );
! 159: static void cgi_kill( ClientData client_data, struct timeval* nowP );
! 160: #endif /* CGI_TIMELIMIT */
! 161: #ifdef GENERATE_INDEXES
! 162: static int ls( httpd_conn* hc );
! 163: #endif /* GENERATE_INDEXES */
! 164: static char* build_env( char* fmt, char* arg );
! 165: #ifdef SERVER_NAME_LIST
! 166: static char* hostname_map( char* hostname );
! 167: #endif /* SERVER_NAME_LIST */
! 168: static char** make_envp( httpd_conn* hc );
! 169: static char** make_argp( httpd_conn* hc );
! 170: static void cgi_interpose_input( httpd_conn* hc, int wfd );
! 171: static void post_post_garbage_hack( httpd_conn* hc );
! 172: static void cgi_interpose_output( httpd_conn* hc, int rfd );
! 173: static void cgi_child( httpd_conn* hc );
! 174: static int cgi( httpd_conn* hc );
! 175: static int really_start_request( httpd_conn* hc, struct timeval* nowP );
! 176: static void make_log_entry( httpd_conn* hc, struct timeval* nowP );
! 177: static int check_referer( httpd_conn* hc );
! 178: static int really_check_referer( httpd_conn* hc );
! 179: static int sockaddr_check( httpd_sockaddr* saP );
! 180: static size_t sockaddr_len( httpd_sockaddr* saP );
! 181: static int my_snprintf( char* str, size_t size, const char* format, ... );
! 182: #ifndef HAVE_ATOLL
! 183: static long long atoll( const char* str );
! 184: #endif /* HAVE_ATOLL */
! 185:
! 186:
! 187: /* This global keeps track of whether we are in the main process or a
! 188: ** sub-process. The reason is that httpd_write_response() can get called
! 189: ** in either context; when it is called from the main process it must use
! 190: ** non-blocking I/O to avoid stalling the server, but when it is called
! 191: ** from a sub-process it wants to use blocking I/O so that the whole
! 192: ** response definitely gets written. So, it checks this variable. A bit
! 193: ** of a hack but it seems to do the right thing.
! 194: */
! 195: static int sub_process = 0;
! 196:
! 197:
! 198: static void
! 199: check_options( void )
! 200: {
! 201: #if defined(TILDE_MAP_1) && defined(TILDE_MAP_2)
! 202: syslog( LOG_CRIT, "both TILDE_MAP_1 and TILDE_MAP_2 are defined" );
! 203: exit( 1 );
! 204: #endif /* both */
! 205: }
! 206:
! 207:
! 208: static void
! 209: free_httpd_server( httpd_server* hs )
! 210: {
! 211: if ( hs->binding_hostname != (char*) 0 )
! 212: free( (void*) hs->binding_hostname );
! 213: if ( hs->cwd != (char*) 0 )
! 214: free( (void*) hs->cwd );
! 215: if ( hs->cgi_pattern != (char*) 0 )
! 216: free( (void*) hs->cgi_pattern );
! 217: if ( hs->charset != (char*) 0 )
! 218: free( (void*) hs->charset );
! 219: if ( hs->p3p != (char*) 0 )
! 220: free( (void*) hs->p3p );
! 221: if ( hs->url_pattern != (char*) 0 )
! 222: free( (void*) hs->url_pattern );
! 223: if ( hs->local_pattern != (char*) 0 )
! 224: free( (void*) hs->local_pattern );
! 225: free( (void*) hs );
! 226: }
! 227:
! 228:
! 229: httpd_server*
! 230: httpd_initialize(
! 231: char* hostname, httpd_sockaddr* sa4P, httpd_sockaddr* sa6P,
! 232: unsigned short port, char* cgi_pattern, int cgi_limit, char* charset,
! 233: char* p3p, int max_age, char* cwd, int no_log, FILE* logfp,
! 234: int no_symlink_check, int vhost, int global_passwd, char* url_pattern,
! 235: char* local_pattern, int no_empty_referers )
! 236: {
! 237: httpd_server* hs;
! 238: static char ghnbuf[256];
! 239: char* cp;
! 240:
! 241: check_options();
! 242:
! 243: hs = NEW( httpd_server, 1 );
! 244: if ( hs == (httpd_server*) 0 )
! 245: {
! 246: syslog( LOG_CRIT, "out of memory allocating an httpd_server" );
! 247: return (httpd_server*) 0;
! 248: }
! 249:
! 250: if ( hostname != (char*) 0 )
! 251: {
! 252: hs->binding_hostname = strdup( hostname );
! 253: if ( hs->binding_hostname == (char*) 0 )
! 254: {
! 255: syslog( LOG_CRIT, "out of memory copying hostname" );
! 256: return (httpd_server*) 0;
! 257: }
! 258: hs->server_hostname = hs->binding_hostname;
! 259: }
! 260: else
! 261: {
! 262: hs->binding_hostname = (char*) 0;
! 263: hs->server_hostname = (char*) 0;
! 264: if ( gethostname( ghnbuf, sizeof(ghnbuf) ) < 0 )
! 265: ghnbuf[0] = '\0';
! 266: #ifdef SERVER_NAME_LIST
! 267: if ( ghnbuf[0] != '\0' )
! 268: hs->server_hostname = hostname_map( ghnbuf );
! 269: #endif /* SERVER_NAME_LIST */
! 270: if ( hs->server_hostname == (char*) 0 )
! 271: {
! 272: #ifdef SERVER_NAME
! 273: hs->server_hostname = SERVER_NAME;
! 274: #else /* SERVER_NAME */
! 275: if ( ghnbuf[0] != '\0' )
! 276: hs->server_hostname = ghnbuf;
! 277: #endif /* SERVER_NAME */
! 278: }
! 279: }
! 280:
! 281: hs->port = port;
! 282: if ( cgi_pattern == (char*) 0 )
! 283: hs->cgi_pattern = (char*) 0;
! 284: else
! 285: {
! 286: /* Nuke any leading slashes. */
! 287: if ( cgi_pattern[0] == '/' )
! 288: ++cgi_pattern;
! 289: hs->cgi_pattern = strdup( cgi_pattern );
! 290: if ( hs->cgi_pattern == (char*) 0 )
! 291: {
! 292: syslog( LOG_CRIT, "out of memory copying cgi_pattern" );
! 293: return (httpd_server*) 0;
! 294: }
! 295: /* Nuke any leading slashes in the cgi pattern. */
! 296: while ( ( cp = strstr( hs->cgi_pattern, "|/" ) ) != (char*) 0 )
! 297: (void) strcpy( cp + 1, cp + 2 );
! 298: }
! 299: hs->cgi_limit = cgi_limit;
! 300: hs->cgi_count = 0;
! 301: hs->charset = strdup( charset );
! 302: hs->p3p = strdup( p3p );
! 303: hs->max_age = max_age;
! 304: hs->cwd = strdup( cwd );
! 305: if ( hs->cwd == (char*) 0 )
! 306: {
! 307: syslog( LOG_CRIT, "out of memory copying cwd" );
! 308: return (httpd_server*) 0;
! 309: }
! 310: if ( url_pattern == (char*) 0 )
! 311: hs->url_pattern = (char*) 0;
! 312: else
! 313: {
! 314: hs->url_pattern = strdup( url_pattern );
! 315: if ( hs->url_pattern == (char*) 0 )
! 316: {
! 317: syslog( LOG_CRIT, "out of memory copying url_pattern" );
! 318: return (httpd_server*) 0;
! 319: }
! 320: }
! 321: if ( local_pattern == (char*) 0 )
! 322: hs->local_pattern = (char*) 0;
! 323: else
! 324: {
! 325: hs->local_pattern = strdup( local_pattern );
! 326: if ( hs->local_pattern == (char*) 0 )
! 327: {
! 328: syslog( LOG_CRIT, "out of memory copying local_pattern" );
! 329: return (httpd_server*) 0;
! 330: }
! 331: }
! 332: hs->no_log = no_log;
! 333: hs->logfp = (FILE*) 0;
! 334: httpd_set_logfp( hs, logfp );
! 335: hs->no_symlink_check = no_symlink_check;
! 336: hs->vhost = vhost;
! 337: hs->global_passwd = global_passwd;
! 338: hs->no_empty_referers = no_empty_referers;
! 339:
! 340: /* Initialize listen sockets. Try v6 first because of a Linux peculiarity;
! 341: ** like some other systems, it has magical v6 sockets that also listen for
! 342: ** v4, but in Linux if you bind a v4 socket first then the v6 bind fails.
! 343: */
! 344: if ( sa6P == (httpd_sockaddr*) 0 )
! 345: hs->listen6_fd = -1;
! 346: else
! 347: hs->listen6_fd = initialize_listen_socket( sa6P );
! 348: if ( sa4P == (httpd_sockaddr*) 0 )
! 349: hs->listen4_fd = -1;
! 350: else
! 351: hs->listen4_fd = initialize_listen_socket( sa4P );
! 352: /* If we didn't get any valid sockets, fail. */
! 353: if ( hs->listen4_fd == -1 && hs->listen6_fd == -1 )
! 354: {
! 355: free_httpd_server( hs );
! 356: return (httpd_server*) 0;
! 357: }
! 358:
! 359: init_mime();
! 360:
! 361: /* Done initializing. */
! 362: if ( hs->binding_hostname == (char*) 0 )
! 363: syslog(
! 364: LOG_NOTICE, "%.80s starting on port %d", SERVER_SOFTWARE,
! 365: (int) hs->port );
! 366: else
! 367: syslog(
! 368: LOG_NOTICE, "%.80s starting on %.80s, port %d", SERVER_SOFTWARE,
! 369: httpd_ntoa( hs->listen4_fd != -1 ? sa4P : sa6P ),
! 370: (int) hs->port );
! 371: return hs;
! 372: }
! 373:
! 374:
! 375: static int
! 376: initialize_listen_socket( httpd_sockaddr* saP )
! 377: {
! 378: int listen_fd;
! 379: int on, flags;
! 380:
! 381: /* Check sockaddr. */
! 382: if ( ! sockaddr_check( saP ) )
! 383: {
! 384: syslog( LOG_CRIT, "unknown sockaddr family on listen socket" );
! 385: return -1;
! 386: }
! 387:
! 388: /* Create socket. */
! 389: listen_fd = socket( saP->sa.sa_family, SOCK_STREAM, 0 );
! 390: if ( listen_fd < 0 )
! 391: {
! 392: syslog( LOG_CRIT, "socket %.80s - %m", httpd_ntoa( saP ) );
! 393: return -1;
! 394: }
! 395: (void) fcntl( listen_fd, F_SETFD, 1 );
! 396:
! 397: /* Allow reuse of local addresses. */
! 398: on = 1;
! 399: if ( setsockopt(
! 400: listen_fd, SOL_SOCKET, SO_REUSEADDR, (char*) &on,
! 401: sizeof(on) ) < 0 )
! 402: syslog( LOG_CRIT, "setsockopt SO_REUSEADDR - %m" );
! 403:
! 404: /* Bind to it. */
! 405: if ( bind( listen_fd, &saP->sa, sockaddr_len( saP ) ) < 0 )
! 406: {
! 407: syslog(
! 408: LOG_CRIT, "bind %.80s - %m", httpd_ntoa( saP ) );
! 409: (void) close( listen_fd );
! 410: return -1;
! 411: }
! 412:
! 413: /* Set the listen file descriptor to no-delay / non-blocking mode. */
! 414: flags = fcntl( listen_fd, F_GETFL, 0 );
! 415: if ( flags == -1 )
! 416: {
! 417: syslog( LOG_CRIT, "fcntl F_GETFL - %m" );
! 418: (void) close( listen_fd );
! 419: return -1;
! 420: }
! 421: if ( fcntl( listen_fd, F_SETFL, flags | O_NDELAY ) < 0 )
! 422: {
! 423: syslog( LOG_CRIT, "fcntl O_NDELAY - %m" );
! 424: (void) close( listen_fd );
! 425: return -1;
! 426: }
! 427:
! 428: /* Start a listen going. */
! 429: if ( listen( listen_fd, LISTEN_BACKLOG ) < 0 )
! 430: {
! 431: syslog( LOG_CRIT, "listen - %m" );
! 432: (void) close( listen_fd );
! 433: return -1;
! 434: }
! 435:
! 436: /* Use accept filtering, if available. */
! 437: #ifdef SO_ACCEPTFILTER
! 438: {
! 439: #if ( __FreeBSD_version >= 411000 )
! 440: #define ACCEPT_FILTER_NAME "httpready"
! 441: #else
! 442: #define ACCEPT_FILTER_NAME "dataready"
! 443: #endif
! 444: struct accept_filter_arg af;
! 445: (void) bzero( &af, sizeof(af) );
! 446: (void) strcpy( af.af_name, ACCEPT_FILTER_NAME );
! 447: (void) setsockopt(
! 448: listen_fd, SOL_SOCKET, SO_ACCEPTFILTER, (char*) &af, sizeof(af) );
! 449: }
! 450: #endif /* SO_ACCEPTFILTER */
! 451:
! 452: return listen_fd;
! 453: }
! 454:
! 455:
! 456: void
! 457: httpd_set_logfp( httpd_server* hs, FILE* logfp )
! 458: {
! 459: if ( hs->logfp != (FILE*) 0 )
! 460: (void) fclose( hs->logfp );
! 461: hs->logfp = logfp;
! 462: }
! 463:
! 464:
! 465: void
! 466: httpd_terminate( httpd_server* hs )
! 467: {
! 468: httpd_unlisten( hs );
! 469: if ( hs->logfp != (FILE*) 0 )
! 470: (void) fclose( hs->logfp );
! 471: free_httpd_server( hs );
! 472: }
! 473:
! 474:
! 475: void
! 476: httpd_unlisten( httpd_server* hs )
! 477: {
! 478: if ( hs->listen4_fd != -1 )
! 479: {
! 480: (void) close( hs->listen4_fd );
! 481: hs->listen4_fd = -1;
! 482: }
! 483: if ( hs->listen6_fd != -1 )
! 484: {
! 485: (void) close( hs->listen6_fd );
! 486: hs->listen6_fd = -1;
! 487: }
! 488: }
! 489:
! 490:
! 491: /* Conditional macro to allow two alternate forms for use in the built-in
! 492: ** error pages. If EXPLICIT_ERROR_PAGES is defined, the second and more
! 493: ** explicit error form is used; otherwise, the first and more generic
! 494: ** form is used.
! 495: */
! 496: #ifdef EXPLICIT_ERROR_PAGES
! 497: #define ERROR_FORM(a,b) b
! 498: #else /* EXPLICIT_ERROR_PAGES */
! 499: #define ERROR_FORM(a,b) a
! 500: #endif /* EXPLICIT_ERROR_PAGES */
! 501:
! 502:
! 503: static char* ok200title = "OK";
! 504: static char* ok206title = "Partial Content";
! 505:
! 506: static char* err302title = "Found";
! 507: static char* err302form = "The actual URL is '%.80s'.\n";
! 508:
! 509: static char* err304title = "Not Modified";
! 510:
! 511: char* httpd_err400title = "Bad Request";
! 512: char* httpd_err400form =
! 513: "Your request has bad syntax or is inherently impossible to satisfy.\n";
! 514:
! 515: #ifdef AUTH_FILE
! 516: static char* err401title = "Unauthorized";
! 517: static char* err401form =
! 518: "Authorization required for the URL '%.80s'.\n";
! 519: #endif /* AUTH_FILE */
! 520:
! 521: static char* err403title = "Forbidden";
! 522: #ifndef EXPLICIT_ERROR_PAGES
! 523: static char* err403form =
! 524: "You do not have permission to get URL '%.80s' from this server.\n";
! 525: #endif /* !EXPLICIT_ERROR_PAGES */
! 526:
! 527: static char* err404title = "Not Found";
! 528: static char* err404form =
! 529: "The requested URL '%.80s' was not found on this server.\n";
! 530:
! 531: char* httpd_err408title = "Request Timeout";
! 532: char* httpd_err408form =
! 533: "No request appeared within a reasonable time period.\n";
! 534:
! 535: static char* err500title = "Internal Error";
! 536: static char* err500form =
! 537: "There was an unusual problem serving the requested URL '%.80s'.\n";
! 538:
! 539: static char* err501title = "Not Implemented";
! 540: static char* err501form =
! 541: "The requested method '%.80s' is not implemented by this server.\n";
! 542:
! 543: char* httpd_err503title = "Service Temporarily Overloaded";
! 544: char* httpd_err503form =
! 545: "The requested URL '%.80s' is temporarily overloaded. Please try again later.\n";
! 546:
! 547:
! 548: /* Append a string to the buffer waiting to be sent as response. */
! 549: static void
! 550: add_response( httpd_conn* hc, char* str )
! 551: {
! 552: size_t len;
! 553:
! 554: len = strlen( str );
! 555: httpd_realloc_str( &hc->response, &hc->maxresponse, hc->responselen + len );
! 556: (void) memmove( &(hc->response[hc->responselen]), str, len );
! 557: hc->responselen += len;
! 558: }
! 559:
! 560: /* Send the buffered response. */
! 561: void
! 562: httpd_write_response( httpd_conn* hc )
! 563: {
! 564: /* If we are in a sub-process, turn off no-delay mode. */
! 565: if ( sub_process )
! 566: httpd_clear_ndelay( hc->conn_fd );
! 567: /* Send the response, if necessary. */
! 568: if ( hc->responselen > 0 )
! 569: {
! 570: (void) httpd_write_fully( hc->conn_fd, hc->response, hc->responselen );
! 571: hc->responselen = 0;
! 572: }
! 573: }
! 574:
! 575:
! 576: /* Set no-delay / non-blocking mode on a socket. */
! 577: void
! 578: httpd_set_ndelay( int fd )
! 579: {
! 580: int flags, newflags;
! 581:
! 582: flags = fcntl( fd, F_GETFL, 0 );
! 583: if ( flags != -1 )
! 584: {
! 585: newflags = flags | (int) O_NDELAY;
! 586: if ( newflags != flags )
! 587: (void) fcntl( fd, F_SETFL, newflags );
! 588: }
! 589: }
! 590:
! 591:
! 592: /* Clear no-delay / non-blocking mode on a socket. */
! 593: void
! 594: httpd_clear_ndelay( int fd )
! 595: {
! 596: int flags, newflags;
! 597:
! 598: flags = fcntl( fd, F_GETFL, 0 );
! 599: if ( flags != -1 )
! 600: {
! 601: newflags = flags & ~ (int) O_NDELAY;
! 602: if ( newflags != flags )
! 603: (void) fcntl( fd, F_SETFL, newflags );
! 604: }
! 605: }
! 606:
! 607:
! 608: static void
! 609: send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, off_t length, time_t mod )
! 610: {
! 611: time_t now, expires;
! 612: const char* rfc1123fmt = "%a, %d %b %Y %H:%M:%S GMT";
! 613: char nowbuf[100];
! 614: char modbuf[100];
! 615: char expbuf[100];
! 616: char fixed_type[500];
! 617: char buf[1000];
! 618: int partial_content;
! 619: int s100;
! 620:
! 621: hc->status = status;
! 622: hc->bytes_to_send = length;
! 623: if ( hc->mime_flag )
! 624: {
! 625: if ( status == 200 && hc->got_range &&
! 626: ( hc->last_byte_index >= hc->first_byte_index ) &&
! 627: ( ( hc->last_byte_index != length - 1 ) ||
! 628: ( hc->first_byte_index != 0 ) ) &&
! 629: ( hc->range_if == (time_t) -1 ||
! 630: hc->range_if == hc->sb.st_mtime ) )
! 631: {
! 632: partial_content = 1;
! 633: hc->status = status = 206;
! 634: title = ok206title;
! 635: }
! 636: else
! 637: {
! 638: partial_content = 0;
! 639: hc->got_range = 0;
! 640: }
! 641:
! 642: now = time( (time_t*) 0 );
! 643: if ( mod == (time_t) 0 )
! 644: mod = now;
! 645: (void) strftime( nowbuf, sizeof(nowbuf), rfc1123fmt, gmtime( &now ) );
! 646: (void) strftime( modbuf, sizeof(modbuf), rfc1123fmt, gmtime( &mod ) );
! 647: (void) my_snprintf(
! 648: fixed_type, sizeof(fixed_type), type, hc->hs->charset );
! 649: (void) my_snprintf( buf, sizeof(buf),
! 650: "%.20s %d %s\015\012Server: %s\015\012Content-Type: %s\015\012Date: %s\015\012Last-Modified: %s\015\012Accept-Ranges: bytes\015\012Connection: close\015\012",
! 651: hc->protocol, status, title, EXPOSED_SERVER_SOFTWARE, fixed_type,
! 652: nowbuf, modbuf );
! 653: add_response( hc, buf );
! 654: s100 = status / 100;
! 655: if ( s100 != 2 && s100 != 3 )
! 656: {
! 657: (void) my_snprintf( buf, sizeof(buf),
! 658: "Cache-Control: no-cache,no-store\015\012" );
! 659: add_response( hc, buf );
! 660: }
! 661: if ( encodings[0] != '\0' )
! 662: {
! 663: (void) my_snprintf( buf, sizeof(buf),
! 664: "Content-Encoding: %s\015\012", encodings );
! 665: add_response( hc, buf );
! 666: }
! 667: if ( partial_content )
! 668: {
! 669: (void) my_snprintf( buf, sizeof(buf),
! 670: "Content-Range: bytes %lld-%lld/%lld\015\012Content-Length: %lld\015\012",
! 671: (int64_t) hc->first_byte_index, (int64_t) hc->last_byte_index,
! 672: (int64_t) length,
! 673: (int64_t) ( hc->last_byte_index - hc->first_byte_index + 1 ) );
! 674: add_response( hc, buf );
! 675: }
! 676: else if ( length >= 0 )
! 677: {
! 678: (void) my_snprintf( buf, sizeof(buf),
! 679: "Content-Length: %lld\015\012", (int64_t) length );
! 680: add_response( hc, buf );
! 681: }
! 682: if ( hc->hs->p3p[0] != '\0' )
! 683: {
! 684: (void) my_snprintf( buf, sizeof(buf), "P3P: %s\015\012", hc->hs->p3p );
! 685: add_response( hc, buf );
! 686: }
! 687: if ( hc->hs->max_age >= 0 )
! 688: {
! 689: expires = now + hc->hs->max_age;
! 690: (void) strftime(
! 691: expbuf, sizeof(expbuf), rfc1123fmt, gmtime( &expires ) );
! 692: (void) my_snprintf( buf, sizeof(buf),
! 693: "Cache-Control: max-age=%d\015\012Expires: %s\015\012",
! 694: hc->hs->max_age, expbuf );
! 695: add_response( hc, buf );
! 696: }
! 697: if ( extraheads[0] != '\0' )
! 698: add_response( hc, extraheads );
! 699: add_response( hc, "\015\012" );
! 700: }
! 701: }
! 702:
! 703:
! 704: static int str_alloc_count = 0;
! 705: static size_t str_alloc_size = 0;
! 706:
! 707: void
! 708: httpd_realloc_str( char** strP, size_t* maxsizeP, size_t size )
! 709: {
! 710: if ( *maxsizeP == 0 )
! 711: {
! 712: *maxsizeP = MAX( 200, size + 100 );
! 713: *strP = NEW( char, *maxsizeP + 1 );
! 714: ++str_alloc_count;
! 715: str_alloc_size += *maxsizeP;
! 716: }
! 717: else if ( size > *maxsizeP )
! 718: {
! 719: str_alloc_size -= *maxsizeP;
! 720: *maxsizeP = MAX( *maxsizeP * 2, size * 5 / 4 );
! 721: *strP = RENEW( *strP, char, *maxsizeP + 1 );
! 722: str_alloc_size += *maxsizeP;
! 723: }
! 724: else
! 725: return;
! 726: if ( *strP == (char*) 0 )
! 727: {
! 728: syslog(
! 729: LOG_ERR, "out of memory reallocating a string to %d bytes",
! 730: *maxsizeP );
! 731: exit( 1 );
! 732: }
! 733: }
! 734:
! 735:
! 736: static void
! 737: send_response( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg )
! 738: {
! 739: char defanged_arg[1000], buf[2000];
! 740:
! 741: send_mime(
! 742: hc, status, title, "", extraheads, "text/html; charset=%s", (off_t) -1,
! 743: (time_t) 0 );
! 744: (void) my_snprintf( buf, sizeof(buf), "\
! 745: <HTML>\n\
! 746: <HEAD><TITLE>%d %s</TITLE></HEAD>\n\
! 747: <BODY BGCOLOR=\"#cc9999\" TEXT=\"#000000\" LINK=\"#2020ff\" VLINK=\"#4040cc\">\n\
! 748: <H2>%d %s</H2>\n",
! 749: status, title, status, title );
! 750: add_response( hc, buf );
! 751: defang( arg, defanged_arg, sizeof(defanged_arg) );
! 752: (void) my_snprintf( buf, sizeof(buf), form, defanged_arg );
! 753: add_response( hc, buf );
! 754: if ( match( "**MSIE**", hc->useragent ) )
! 755: {
! 756: int n;
! 757: add_response( hc, "<!--\n" );
! 758: for ( n = 0; n < 6; ++n )
! 759: add_response( hc, "Padding so that MSIE deigns to show this error instead of its own canned one.\n");
! 760: add_response( hc, "-->\n" );
! 761: }
! 762: send_response_tail( hc );
! 763: }
! 764:
! 765:
! 766: static void
! 767: send_response_tail( httpd_conn* hc )
! 768: {
! 769: char buf[1000];
! 770:
! 771: (void) my_snprintf( buf, sizeof(buf), "\
! 772: <HR>\n\
! 773: <ADDRESS><A HREF=\"%s\">%s</A></ADDRESS>\n\
! 774: </BODY>\n\
! 775: </HTML>\n",
! 776: SERVER_ADDRESS, EXPOSED_SERVER_SOFTWARE );
! 777: add_response( hc, buf );
! 778: }
! 779:
! 780:
! 781: static void
! 782: defang( char* str, char* dfstr, int dfsize )
! 783: {
! 784: char* cp1;
! 785: char* cp2;
! 786:
! 787: for ( cp1 = str, cp2 = dfstr;
! 788: *cp1 != '\0' && cp2 - dfstr < dfsize - 5;
! 789: ++cp1, ++cp2 )
! 790: {
! 791: switch ( *cp1 )
! 792: {
! 793: case '<':
! 794: *cp2++ = '&';
! 795: *cp2++ = 'l';
! 796: *cp2++ = 't';
! 797: *cp2 = ';';
! 798: break;
! 799: case '>':
! 800: *cp2++ = '&';
! 801: *cp2++ = 'g';
! 802: *cp2++ = 't';
! 803: *cp2 = ';';
! 804: break;
! 805: default:
! 806: *cp2 = *cp1;
! 807: break;
! 808: }
! 809: }
! 810: *cp2 = '\0';
! 811: }
! 812:
! 813:
! 814: void
! 815: httpd_send_err( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg )
! 816: {
! 817: #ifdef ERR_DIR
! 818:
! 819: char filename[1000];
! 820:
! 821: /* Try virtual host error page. */
! 822: if ( hc->hs->vhost && hc->hostdir[0] != '\0' )
! 823: {
! 824: (void) my_snprintf( filename, sizeof(filename),
! 825: "%s/%s/err%d.html", hc->hostdir, ERR_DIR, status );
! 826: if ( send_err_file( hc, status, title, extraheads, filename ) )
! 827: return;
! 828: }
! 829:
! 830: /* Try server-wide error page. */
! 831: (void) my_snprintf( filename, sizeof(filename),
! 832: "%s/err%d.html", ERR_DIR, status );
! 833: if ( send_err_file( hc, status, title, extraheads, filename ) )
! 834: return;
! 835:
! 836: /* Fall back on built-in error page. */
! 837: send_response( hc, status, title, extraheads, form, arg );
! 838:
! 839: #else /* ERR_DIR */
! 840:
! 841: send_response( hc, status, title, extraheads, form, arg );
! 842:
! 843: #endif /* ERR_DIR */
! 844: }
! 845:
! 846:
! 847: #ifdef ERR_DIR
! 848: static int
! 849: send_err_file( httpd_conn* hc, int status, char* title, char* extraheads, char* filename )
! 850: {
! 851: FILE* fp;
! 852: char buf[1000];
! 853: size_t r;
! 854:
! 855: fp = fopen( filename, "r" );
! 856: if ( fp == (FILE*) 0 )
! 857: return 0;
! 858: send_mime(
! 859: hc, status, title, "", extraheads, "text/html; charset=%s", (off_t) -1,
! 860: (time_t) 0 );
! 861: for (;;)
! 862: {
! 863: r = fread( buf, 1, sizeof(buf) - 1, fp );
! 864: if ( r == 0 )
! 865: break;
! 866: buf[r] = '\0';
! 867: add_response( hc, buf );
! 868: }
! 869: (void) fclose( fp );
! 870:
! 871: #ifdef ERR_APPEND_SERVER_INFO
! 872: send_response_tail( hc );
! 873: #endif /* ERR_APPEND_SERVER_INFO */
! 874:
! 875: return 1;
! 876: }
! 877: #endif /* ERR_DIR */
! 878:
! 879:
! 880: #ifdef AUTH_FILE
! 881:
! 882: static void
! 883: send_authenticate( httpd_conn* hc, char* realm )
! 884: {
! 885: static char* header;
! 886: static size_t maxheader = 0;
! 887: static char headstr[] = "WWW-Authenticate: Basic realm=\"";
! 888:
! 889: httpd_realloc_str(
! 890: &header, &maxheader, sizeof(headstr) + strlen( realm ) + 3 );
! 891: (void) my_snprintf( header, maxheader, "%s%s\"\015\012", headstr, realm );
! 892: httpd_send_err( hc, 401, err401title, header, err401form, hc->encodedurl );
! 893: /* If the request was a POST then there might still be data to be read,
! 894: ** so we need to do a lingering close.
! 895: */
! 896: if ( hc->method == METHOD_POST )
! 897: hc->should_linger = 1;
! 898: }
! 899:
! 900:
! 901: /* Base-64 decoding. This represents binary data as printable ASCII
! 902: ** characters. Three 8-bit binary bytes are turned into four 6-bit
! 903: ** values, like so:
! 904: **
! 905: ** [11111111] [22222222] [33333333]
! 906: **
! 907: ** [111111] [112222] [222233] [333333]
! 908: **
! 909: ** Then the 6-bit values are represented using the characters "A-Za-z0-9+/".
! 910: */
! 911:
! 912: static int b64_decode_table[256] = {
! 913: -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
! 914: -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
! 915: -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
! 916: 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
! 917: -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
! 918: 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
! 919: -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
! 920: 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
! 921: -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
! 922: -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
! 923: -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
! 924: -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
! 925: -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
! 926: -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
! 927: -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
! 928: -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
! 929: };
! 930:
! 931: /* Do base-64 decoding on a string. Ignore any non-base64 bytes.
! 932: ** Return the actual number of bytes generated. The decoded size will
! 933: ** be at most 3/4 the size of the encoded, and may be smaller if there
! 934: ** are padding characters (blanks, newlines).
! 935: */
! 936: static int
! 937: b64_decode( const char* str, unsigned char* space, int size )
! 938: {
! 939: const char* cp;
! 940: int space_idx, phase;
! 941: int d, prev_d = 0;
! 942: unsigned char c;
! 943:
! 944: space_idx = 0;
! 945: phase = 0;
! 946: for ( cp = str; *cp != '\0'; ++cp )
! 947: {
! 948: d = b64_decode_table[(int) *cp];
! 949: if ( d != -1 )
! 950: {
! 951: switch ( phase )
! 952: {
! 953: case 0:
! 954: ++phase;
! 955: break;
! 956: case 1:
! 957: c = ( ( prev_d << 2 ) | ( ( d & 0x30 ) >> 4 ) );
! 958: if ( space_idx < size )
! 959: space[space_idx++] = c;
! 960: ++phase;
! 961: break;
! 962: case 2:
! 963: c = ( ( ( prev_d & 0xf ) << 4 ) | ( ( d & 0x3c ) >> 2 ) );
! 964: if ( space_idx < size )
! 965: space[space_idx++] = c;
! 966: ++phase;
! 967: break;
! 968: case 3:
! 969: c = ( ( ( prev_d & 0x03 ) << 6 ) | d );
! 970: if ( space_idx < size )
! 971: space[space_idx++] = c;
! 972: phase = 0;
! 973: break;
! 974: }
! 975: prev_d = d;
! 976: }
! 977: }
! 978: return space_idx;
! 979: }
! 980:
! 981:
! 982: /* Returns -1 == unauthorized, 0 == no auth file, 1 = authorized. */
! 983: static int
! 984: auth_check( httpd_conn* hc, char* dirname )
! 985: {
! 986: if ( hc->hs->global_passwd )
! 987: {
! 988: char* topdir;
! 989: if ( hc->hs->vhost && hc->hostdir[0] != '\0' )
! 990: topdir = hc->hostdir;
! 991: else
! 992: topdir = ".";
! 993: switch ( auth_check2( hc, topdir ) )
! 994: {
! 995: case -1:
! 996: return -1;
! 997: case 1:
! 998: return 1;
! 999: }
! 1000: }
! 1001: return auth_check2( hc, dirname );
! 1002: }
! 1003:
! 1004:
! 1005: /* Returns -1 == unauthorized, 0 == no auth file, 1 = authorized. */
! 1006: static int
! 1007: auth_check2( httpd_conn* hc, char* dirname )
! 1008: {
! 1009: static char* authpath;
! 1010: static size_t maxauthpath = 0;
! 1011: struct stat sb;
! 1012: char authinfo[500];
! 1013: char* authpass;
! 1014: char* colon;
! 1015: int l;
! 1016: FILE* fp;
! 1017: char line[500];
! 1018: char* cryp;
! 1019: static char* prevauthpath;
! 1020: static size_t maxprevauthpath = 0;
! 1021: static time_t prevmtime;
! 1022: static char* prevuser;
! 1023: static size_t maxprevuser = 0;
! 1024: static char* prevcryp;
! 1025: static size_t maxprevcryp = 0;
! 1026:
! 1027: /* Construct auth filename. */
! 1028: httpd_realloc_str(
! 1029: &authpath, &maxauthpath, strlen( dirname ) + 1 + sizeof(AUTH_FILE) );
! 1030: (void) my_snprintf( authpath, maxauthpath, "%s/%s", dirname, AUTH_FILE );
! 1031:
! 1032: /* Does this directory have an auth file? */
! 1033: if ( stat( authpath, &sb ) < 0 )
! 1034: /* Nope, let the request go through. */
! 1035: return 0;
! 1036:
! 1037: /* Does this request contain basic authorization info? */
! 1038: if ( hc->authorization[0] == '\0' ||
! 1039: strncmp( hc->authorization, "Basic ", 6 ) != 0 )
! 1040: {
! 1041: /* Nope, return a 401 Unauthorized. */
! 1042: send_authenticate( hc, dirname );
! 1043: return -1;
! 1044: }
! 1045:
! 1046: /* Decode it. */
! 1047: l = b64_decode(
! 1048: &(hc->authorization[6]), (unsigned char*) authinfo,
! 1049: sizeof(authinfo) - 1 );
! 1050: authinfo[l] = '\0';
! 1051: /* Split into user and password. */
! 1052: authpass = strchr( authinfo, ':' );
! 1053: if ( authpass == (char*) 0 )
! 1054: {
! 1055: /* No colon? Bogus auth info. */
! 1056: send_authenticate( hc, dirname );
! 1057: return -1;
! 1058: }
! 1059: *authpass++ = '\0';
! 1060: /* If there are more fields, cut them off. */
! 1061: colon = strchr( authpass, ':' );
! 1062: if ( colon != (char*) 0 )
! 1063: *colon = '\0';
! 1064:
! 1065: /* See if we have a cached entry and can use it. */
! 1066: if ( maxprevauthpath != 0 &&
! 1067: strcmp( authpath, prevauthpath ) == 0 &&
! 1068: sb.st_mtime == prevmtime &&
! 1069: strcmp( authinfo, prevuser ) == 0 )
! 1070: {
! 1071: /* Yes. Check against the cached encrypted password. */
! 1072: if ( strcmp( crypt( authpass, prevcryp ), prevcryp ) == 0 )
! 1073: {
! 1074: /* Ok! */
! 1075: httpd_realloc_str(
! 1076: &hc->remoteuser, &hc->maxremoteuser, strlen( authinfo ) );
! 1077: (void) strcpy( hc->remoteuser, authinfo );
! 1078: return 1;
! 1079: }
! 1080: else
! 1081: {
! 1082: /* No. */
! 1083: send_authenticate( hc, dirname );
! 1084: return -1;
! 1085: }
! 1086: }
! 1087:
! 1088: /* Open the password file. */
! 1089: fp = fopen( authpath, "r" );
! 1090: if ( fp == (FILE*) 0 )
! 1091: {
! 1092: /* The file exists but we can't open it? Disallow access. */
! 1093: syslog(
! 1094: LOG_ERR, "%.80s auth file %.80s could not be opened - %m",
! 1095: httpd_ntoa( &hc->client_addr ), authpath );
! 1096: httpd_send_err(
! 1097: hc, 403, err403title, "",
! 1098: ERROR_FORM( err403form, "The requested URL '%.80s' is protected by an authentication file, but the authentication file cannot be opened.\n" ),
! 1099: hc->encodedurl );
! 1100: return -1;
! 1101: }
! 1102:
! 1103: /* Read it. */
! 1104: while ( fgets( line, sizeof(line), fp ) != (char*) 0 )
! 1105: {
! 1106: /* Nuke newline. */
! 1107: l = strlen( line );
! 1108: if ( line[l - 1] == '\n' )
! 1109: line[l - 1] = '\0';
! 1110: /* Split into user and encrypted password. */
! 1111: cryp = strchr( line, ':' );
! 1112: if ( cryp == (char*) 0 )
! 1113: continue;
! 1114: *cryp++ = '\0';
! 1115: /* Is this the right user? */
! 1116: if ( strcmp( line, authinfo ) == 0 )
! 1117: {
! 1118: /* Yes. */
! 1119: (void) fclose( fp );
! 1120: /* So is the password right? */
! 1121: if ( strcmp( crypt( authpass, cryp ), cryp ) == 0 )
! 1122: {
! 1123: /* Ok! */
! 1124: httpd_realloc_str(
! 1125: &hc->remoteuser, &hc->maxremoteuser, strlen( line ) );
! 1126: (void) strcpy( hc->remoteuser, line );
! 1127: /* And cache this user's info for next time. */
! 1128: httpd_realloc_str(
! 1129: &prevauthpath, &maxprevauthpath, strlen( authpath ) );
! 1130: (void) strcpy( prevauthpath, authpath );
! 1131: prevmtime = sb.st_mtime;
! 1132: httpd_realloc_str(
! 1133: &prevuser, &maxprevuser, strlen( authinfo ) );
! 1134: (void) strcpy( prevuser, authinfo );
! 1135: httpd_realloc_str( &prevcryp, &maxprevcryp, strlen( cryp ) );
! 1136: (void) strcpy( prevcryp, cryp );
! 1137: return 1;
! 1138: }
! 1139: else
! 1140: {
! 1141: /* No. */
! 1142: send_authenticate( hc, dirname );
! 1143: return -1;
! 1144: }
! 1145: }
! 1146: }
! 1147:
! 1148: /* Didn't find that user. Access denied. */
! 1149: (void) fclose( fp );
! 1150: send_authenticate( hc, dirname );
! 1151: return -1;
! 1152: }
! 1153:
! 1154: #endif /* AUTH_FILE */
! 1155:
! 1156:
! 1157: static void
! 1158: send_dirredirect( httpd_conn* hc )
! 1159: {
! 1160: static char* location;
! 1161: static char* header;
! 1162: static size_t maxlocation = 0, maxheader = 0;
! 1163: static char headstr[] = "Location: ";
! 1164:
! 1165: if ( hc->query[0] != '\0')
! 1166: {
! 1167: char* cp = strchr( hc->encodedurl, '?' );
! 1168: if ( cp != (char*) 0 ) /* should always find it */
! 1169: *cp = '\0';
! 1170: httpd_realloc_str(
! 1171: &location, &maxlocation,
! 1172: strlen( hc->encodedurl ) + 2 + strlen( hc->query ) );
! 1173: (void) my_snprintf( location, maxlocation,
! 1174: "%s/?%s", hc->encodedurl, hc->query );
! 1175: }
! 1176: else
! 1177: {
! 1178: httpd_realloc_str(
! 1179: &location, &maxlocation, strlen( hc->encodedurl ) + 1 );
! 1180: (void) my_snprintf( location, maxlocation,
! 1181: "%s/", hc->encodedurl );
! 1182: }
! 1183: httpd_realloc_str(
! 1184: &header, &maxheader, sizeof(headstr) + strlen( location ) );
! 1185: (void) my_snprintf( header, maxheader,
! 1186: "%s%s\015\012", headstr, location );
! 1187: send_response( hc, 302, err302title, header, err302form, location );
! 1188: }
! 1189:
! 1190:
! 1191: char*
! 1192: httpd_method_str( int method )
! 1193: {
! 1194: switch ( method )
! 1195: {
! 1196: case METHOD_GET: return "GET";
! 1197: case METHOD_HEAD: return "HEAD";
! 1198: case METHOD_POST: return "POST";
! 1199: default: return "UNKNOWN";
! 1200: }
! 1201: }
! 1202:
! 1203:
! 1204: static int
! 1205: hexit( char c )
! 1206: {
! 1207: if ( c >= '0' && c <= '9' )
! 1208: return c - '0';
! 1209: if ( c >= 'a' && c <= 'f' )
! 1210: return c - 'a' + 10;
! 1211: if ( c >= 'A' && c <= 'F' )
! 1212: return c - 'A' + 10;
! 1213: return 0; /* shouldn't happen, we're guarded by isxdigit() */
! 1214: }
! 1215:
! 1216:
! 1217: /* Copies and decodes a string. It's ok for from and to to be the
! 1218: ** same string.
! 1219: */
! 1220: static void
! 1221: strdecode( char* to, char* from )
! 1222: {
! 1223: for ( ; *from != '\0'; ++to, ++from )
! 1224: {
! 1225: if ( from[0] == '%' && isxdigit( from[1] ) && isxdigit( from[2] ) )
! 1226: {
! 1227: *to = hexit( from[1] ) * 16 + hexit( from[2] );
! 1228: from += 2;
! 1229: }
! 1230: else
! 1231: *to = *from;
! 1232: }
! 1233: *to = '\0';
! 1234: }
! 1235:
! 1236:
! 1237: #ifdef GENERATE_INDEXES
! 1238: /* Copies and encodes a string. */
! 1239: static void
! 1240: strencode( char* to, int tosize, char* from )
! 1241: {
! 1242: int tolen;
! 1243:
! 1244: for ( tolen = 0; *from != '\0' && tolen + 4 < tosize; ++from )
! 1245: {
! 1246: if ( isalnum(*from) || strchr( "/_.-~", *from ) != (char*) 0 )
! 1247: {
! 1248: *to = *from;
! 1249: ++to;
! 1250: ++tolen;
! 1251: }
! 1252: else
! 1253: {
! 1254: (void) sprintf( to, "%%%02x", (int) *from & 0xff );
! 1255: to += 3;
! 1256: tolen += 3;
! 1257: }
! 1258: }
! 1259: *to = '\0';
! 1260: }
! 1261: #endif /* GENERATE_INDEXES */
! 1262:
! 1263:
! 1264: #ifdef TILDE_MAP_1
! 1265: /* Map a ~username/whatever URL into <prefix>/username. */
! 1266: static int
! 1267: tilde_map_1( httpd_conn* hc )
! 1268: {
! 1269: static char* temp;
! 1270: static size_t maxtemp = 0;
! 1271: int len;
! 1272: static char* prefix = TILDE_MAP_1;
! 1273:
! 1274: len = strlen( hc->expnfilename ) - 1;
! 1275: httpd_realloc_str( &temp, &maxtemp, len );
! 1276: (void) strcpy( temp, &hc->expnfilename[1] );
! 1277: httpd_realloc_str(
! 1278: &hc->expnfilename, &hc->maxexpnfilename, strlen( prefix ) + 1 + len );
! 1279: (void) strcpy( hc->expnfilename, prefix );
! 1280: if ( prefix[0] != '\0' )
! 1281: (void) strcat( hc->expnfilename, "/" );
! 1282: (void) strcat( hc->expnfilename, temp );
! 1283: return 1;
! 1284: }
! 1285: #endif /* TILDE_MAP_1 */
! 1286:
! 1287: #ifdef TILDE_MAP_2
! 1288: /* Map a ~username/whatever URL into <user's homedir>/<postfix>. */
! 1289: static int
! 1290: tilde_map_2( httpd_conn* hc )
! 1291: {
! 1292: static char* temp;
! 1293: static size_t maxtemp = 0;
! 1294: static char* postfix = TILDE_MAP_2;
! 1295: char* cp;
! 1296: struct passwd* pw;
! 1297: char* alt;
! 1298: char* rest;
! 1299:
! 1300: /* Get the username. */
! 1301: httpd_realloc_str( &temp, &maxtemp, strlen( hc->expnfilename ) - 1 );
! 1302: (void) strcpy( temp, &hc->expnfilename[1] );
! 1303: cp = strchr( temp, '/' );
! 1304: if ( cp != (char*) 0 )
! 1305: *cp++ = '\0';
! 1306: else
! 1307: cp = "";
! 1308:
! 1309: /* Get the passwd entry. */
! 1310: pw = getpwnam( temp );
! 1311: if ( pw == (struct passwd*) 0 )
! 1312: return 0;
! 1313:
! 1314: /* Set up altdir. */
! 1315: httpd_realloc_str(
! 1316: &hc->altdir, &hc->maxaltdir,
! 1317: strlen( pw->pw_dir ) + 1 + strlen( postfix ) );
! 1318: (void) strcpy( hc->altdir, pw->pw_dir );
! 1319: if ( postfix[0] != '\0' )
! 1320: {
! 1321: (void) strcat( hc->altdir, "/" );
! 1322: (void) strcat( hc->altdir, postfix );
! 1323: }
! 1324: alt = expand_symlinks( hc->altdir, &rest, 0, 1 );
! 1325: if ( rest[0] != '\0' )
! 1326: return 0;
! 1327: httpd_realloc_str( &hc->altdir, &hc->maxaltdir, strlen( alt ) );
! 1328: (void) strcpy( hc->altdir, alt );
! 1329:
! 1330: /* And the filename becomes altdir plus the post-~ part of the original. */
! 1331: httpd_realloc_str(
! 1332: &hc->expnfilename, &hc->maxexpnfilename,
! 1333: strlen( hc->altdir ) + 1 + strlen( cp ) );
! 1334: (void) my_snprintf( hc->expnfilename, hc->maxexpnfilename,
! 1335: "%s/%s", hc->altdir, cp );
! 1336:
! 1337: /* For this type of tilde mapping, we want to defeat vhost mapping. */
! 1338: hc->tildemapped = 1;
! 1339:
! 1340: return 1;
! 1341: }
! 1342: #endif /* TILDE_MAP_2 */
! 1343:
! 1344:
! 1345: /* Virtual host mapping. */
! 1346: static int
! 1347: vhost_map( httpd_conn* hc )
! 1348: {
! 1349: httpd_sockaddr sa;
! 1350: socklen_t sz;
! 1351: static char* tempfilename;
! 1352: static size_t maxtempfilename = 0;
! 1353: char* cp1;
! 1354: int len;
! 1355: #ifdef VHOST_DIRLEVELS
! 1356: int i;
! 1357: char* cp2;
! 1358: #endif /* VHOST_DIRLEVELS */
! 1359:
! 1360: /* Figure out the virtual hostname. */
! 1361: if ( hc->reqhost[0] != '\0' )
! 1362: hc->hostname = hc->reqhost;
! 1363: else if ( hc->hdrhost[0] != '\0' )
! 1364: hc->hostname = hc->hdrhost;
! 1365: else
! 1366: {
! 1367: sz = sizeof(sa);
! 1368: if ( getsockname( hc->conn_fd, &sa.sa, &sz ) < 0 )
! 1369: {
! 1370: syslog( LOG_ERR, "getsockname - %m" );
! 1371: return 0;
! 1372: }
! 1373: hc->hostname = httpd_ntoa( &sa );
! 1374: }
! 1375: /* Pound it to lower case. */
! 1376: for ( cp1 = hc->hostname; *cp1 != '\0'; ++cp1 )
! 1377: if ( isupper( *cp1 ) )
! 1378: *cp1 = tolower( *cp1 );
! 1379:
! 1380: if ( hc->tildemapped )
! 1381: return 1;
! 1382:
! 1383: /* Figure out the host directory. */
! 1384: #ifdef VHOST_DIRLEVELS
! 1385: httpd_realloc_str(
! 1386: &hc->hostdir, &hc->maxhostdir,
! 1387: strlen( hc->hostname ) + 2 * VHOST_DIRLEVELS );
! 1388: if ( strncmp( hc->hostname, "www.", 4 ) == 0 )
! 1389: cp1 = &hc->hostname[4];
! 1390: else
! 1391: cp1 = hc->hostname;
! 1392: for ( cp2 = hc->hostdir, i = 0; i < VHOST_DIRLEVELS; ++i )
! 1393: {
! 1394: /* Skip dots in the hostname. If we don't, then we get vhost
! 1395: ** directories in higher level of filestructure if dot gets
! 1396: ** involved into path construction. It's `while' used here instead
! 1397: ** of `if' for it's possible to have a hostname formed with two
! 1398: ** dots at the end of it.
! 1399: */
! 1400: while ( *cp1 == '.' )
! 1401: ++cp1;
! 1402: /* Copy a character from the hostname, or '_' if we ran out. */
! 1403: if ( *cp1 != '\0' )
! 1404: *cp2++ = *cp1++;
! 1405: else
! 1406: *cp2++ = '_';
! 1407: /* Copy a slash. */
! 1408: *cp2++ = '/';
! 1409: }
! 1410: (void) strcpy( cp2, hc->hostname );
! 1411: #else /* VHOST_DIRLEVELS */
! 1412: httpd_realloc_str( &hc->hostdir, &hc->maxhostdir, strlen( hc->hostname ) );
! 1413: (void) strcpy( hc->hostdir, hc->hostname );
! 1414: #endif /* VHOST_DIRLEVELS */
! 1415:
! 1416: /* Prepend hostdir to the filename. */
! 1417: len = strlen( hc->expnfilename );
! 1418: httpd_realloc_str( &tempfilename, &maxtempfilename, len );
! 1419: (void) strcpy( tempfilename, hc->expnfilename );
! 1420: httpd_realloc_str(
! 1421: &hc->expnfilename, &hc->maxexpnfilename,
! 1422: strlen( hc->hostdir ) + 1 + len );
! 1423: (void) strcpy( hc->expnfilename, hc->hostdir );
! 1424: (void) strcat( hc->expnfilename, "/" );
! 1425: (void) strcat( hc->expnfilename, tempfilename );
! 1426: return 1;
! 1427: }
! 1428:
! 1429:
! 1430: /* Expands all symlinks in the given filename, eliding ..'s and leading /'s.
! 1431: ** Returns the expanded path (pointer to static string), or (char*) 0 on
! 1432: ** errors. Also returns, in the string pointed to by restP, any trailing
! 1433: ** parts of the path that don't exist.
! 1434: **
! 1435: ** This is a fairly nice little routine. It handles any size filenames
! 1436: ** without excessive mallocs.
! 1437: */
! 1438: static char*
! 1439: expand_symlinks( char* path, char** restP, int no_symlink_check, int tildemapped )
! 1440: {
! 1441: static char* checked;
! 1442: static char* rest;
! 1443: char link[5000];
! 1444: static size_t maxchecked = 0, maxrest = 0;
! 1445: size_t checkedlen, restlen, linklen, prevcheckedlen, prevrestlen;
! 1446: int nlinks, i;
! 1447: char* r;
! 1448: char* cp1;
! 1449: char* cp2;
! 1450:
! 1451: if ( no_symlink_check )
! 1452: {
! 1453: /* If we are chrooted, we can actually skip the symlink-expansion,
! 1454: ** since it's impossible to get out of the tree. However, we still
! 1455: ** need to do the pathinfo check, and the existing symlink expansion
! 1456: ** code is a pretty reasonable way to do this. So, what we do is
! 1457: ** a single stat() of the whole filename - if it exists, then we
! 1458: ** return it as is with nothing in restP. If it doesn't exist, we
! 1459: ** fall through to the existing code.
! 1460: **
! 1461: ** One side-effect of this is that users can't symlink to central
! 1462: ** approved CGIs any more. The workaround is to use the central
! 1463: ** URL for the CGI instead of a local symlinked one.
! 1464: */
! 1465: struct stat sb;
! 1466: if ( stat( path, &sb ) != -1 )
! 1467: {
! 1468: checkedlen = strlen( path );
! 1469: httpd_realloc_str( &checked, &maxchecked, checkedlen );
! 1470: (void) strcpy( checked, path );
! 1471: /* Trim trailing slashes. */
! 1472: while ( checked[checkedlen - 1] == '/' )
! 1473: {
! 1474: checked[checkedlen - 1] = '\0';
! 1475: --checkedlen;
! 1476: }
! 1477: httpd_realloc_str( &rest, &maxrest, 0 );
! 1478: rest[0] = '\0';
! 1479: *restP = rest;
! 1480: return checked;
! 1481: }
! 1482: }
! 1483:
! 1484: /* Start out with nothing in checked and the whole filename in rest. */
! 1485: httpd_realloc_str( &checked, &maxchecked, 1 );
! 1486: checked[0] = '\0';
! 1487: checkedlen = 0;
! 1488: restlen = strlen( path );
! 1489: httpd_realloc_str( &rest, &maxrest, restlen );
! 1490: (void) strcpy( rest, path );
! 1491: if ( rest[restlen - 1] == '/' )
! 1492: rest[--restlen] = '\0'; /* trim trailing slash */
! 1493: if ( ! tildemapped )
! 1494: /* Remove any leading slashes. */
! 1495: while ( rest[0] == '/' )
! 1496: {
! 1497: (void) strcpy( rest, &(rest[1]) );
! 1498: --restlen;
! 1499: }
! 1500: r = rest;
! 1501: nlinks = 0;
! 1502:
! 1503: /* While there are still components to check... */
! 1504: while ( restlen > 0 )
! 1505: {
! 1506: /* Save current checkedlen in case we get a symlink. Save current
! 1507: ** restlen in case we get a non-existant component.
! 1508: */
! 1509: prevcheckedlen = checkedlen;
! 1510: prevrestlen = restlen;
! 1511:
! 1512: /* Grab one component from r and transfer it to checked. */
! 1513: cp1 = strchr( r, '/' );
! 1514: if ( cp1 != (char*) 0 )
! 1515: {
! 1516: i = cp1 - r;
! 1517: if ( i == 0 )
! 1518: {
! 1519: /* Special case for absolute paths. */
! 1520: httpd_realloc_str( &checked, &maxchecked, checkedlen + 1 );
! 1521: (void) strncpy( &checked[checkedlen], r, 1 );
! 1522: checkedlen += 1;
! 1523: }
! 1524: else if ( strncmp( r, "..", MAX( i, 2 ) ) == 0 )
! 1525: {
! 1526: /* Ignore ..'s that go above the start of the path. */
! 1527: if ( checkedlen != 0 )
! 1528: {
! 1529: cp2 = strrchr( checked, '/' );
! 1530: if ( cp2 == (char*) 0 )
! 1531: checkedlen = 0;
! 1532: else if ( cp2 == checked )
! 1533: checkedlen = 1;
! 1534: else
! 1535: checkedlen = cp2 - checked;
! 1536: }
! 1537: }
! 1538: else
! 1539: {
! 1540: httpd_realloc_str( &checked, &maxchecked, checkedlen + 1 + i );
! 1541: if ( checkedlen > 0 && checked[checkedlen-1] != '/' )
! 1542: checked[checkedlen++] = '/';
! 1543: (void) strncpy( &checked[checkedlen], r, i );
! 1544: checkedlen += i;
! 1545: }
! 1546: checked[checkedlen] = '\0';
! 1547: r += i + 1;
! 1548: restlen -= i + 1;
! 1549: }
! 1550: else
! 1551: {
! 1552: /* No slashes remaining, r is all one component. */
! 1553: if ( strcmp( r, ".." ) == 0 )
! 1554: {
! 1555: /* Ignore ..'s that go above the start of the path. */
! 1556: if ( checkedlen != 0 )
! 1557: {
! 1558: cp2 = strrchr( checked, '/' );
! 1559: if ( cp2 == (char*) 0 )
! 1560: checkedlen = 0;
! 1561: else if ( cp2 == checked )
! 1562: checkedlen = 1;
! 1563: else
! 1564: checkedlen = cp2 - checked;
! 1565: checked[checkedlen] = '\0';
! 1566: }
! 1567: }
! 1568: else
! 1569: {
! 1570: httpd_realloc_str(
! 1571: &checked, &maxchecked, checkedlen + 1 + restlen );
! 1572: if ( checkedlen > 0 && checked[checkedlen-1] != '/' )
! 1573: checked[checkedlen++] = '/';
! 1574: (void) strcpy( &checked[checkedlen], r );
! 1575: checkedlen += restlen;
! 1576: }
! 1577: r += restlen;
! 1578: restlen = 0;
! 1579: }
! 1580:
! 1581: /* Try reading the current filename as a symlink */
! 1582: if ( checked[0] == '\0' )
! 1583: continue;
! 1584: linklen = readlink( checked, link, sizeof(link) - 1 );
! 1585: if ( linklen == -1 )
! 1586: {
! 1587: if ( errno == EINVAL )
! 1588: continue; /* not a symlink */
! 1589: if ( errno == EACCES || errno == ENOENT || errno == ENOTDIR )
! 1590: {
! 1591: /* That last component was bogus. Restore and return. */
! 1592: *restP = r - ( prevrestlen - restlen );
! 1593: if ( prevcheckedlen == 0 )
! 1594: (void) strcpy( checked, "." );
! 1595: else
! 1596: checked[prevcheckedlen] = '\0';
! 1597: return checked;
! 1598: }
! 1599: syslog( LOG_ERR, "readlink %.80s - %m", checked );
! 1600: return (char*) 0;
! 1601: }
! 1602: ++nlinks;
! 1603: if ( nlinks > MAX_LINKS )
! 1604: {
! 1605: syslog( LOG_ERR, "too many symlinks in %.80s", path );
! 1606: return (char*) 0;
! 1607: }
! 1608: link[linklen] = '\0';
! 1609: if ( link[linklen - 1] == '/' )
! 1610: link[--linklen] = '\0'; /* trim trailing slash */
! 1611:
! 1612: /* Insert the link contents in front of the rest of the filename. */
! 1613: if ( restlen != 0 )
! 1614: {
! 1615: (void) strcpy( rest, r );
! 1616: httpd_realloc_str( &rest, &maxrest, restlen + linklen + 1 );
! 1617: for ( i = restlen; i >= 0; --i )
! 1618: rest[i + linklen + 1] = rest[i];
! 1619: (void) strcpy( rest, link );
! 1620: rest[linklen] = '/';
! 1621: restlen += linklen + 1;
! 1622: r = rest;
! 1623: }
! 1624: else
! 1625: {
! 1626: /* There's nothing left in the filename, so the link contents
! 1627: ** becomes the rest.
! 1628: */
! 1629: httpd_realloc_str( &rest, &maxrest, linklen );
! 1630: (void) strcpy( rest, link );
! 1631: restlen = linklen;
! 1632: r = rest;
! 1633: }
! 1634:
! 1635: if ( rest[0] == '/' )
! 1636: {
! 1637: /* There must have been an absolute symlink - zero out checked. */
! 1638: checked[0] = '\0';
! 1639: checkedlen = 0;
! 1640: }
! 1641: else
! 1642: {
! 1643: /* Re-check this component. */
! 1644: checkedlen = prevcheckedlen;
! 1645: checked[checkedlen] = '\0';
! 1646: }
! 1647: }
! 1648:
! 1649: /* Ok. */
! 1650: *restP = r;
! 1651: if ( checked[0] == '\0' )
! 1652: (void) strcpy( checked, "." );
! 1653: return checked;
! 1654: }
! 1655:
! 1656:
! 1657: int
! 1658: httpd_get_conn( httpd_server* hs, int listen_fd, httpd_conn* hc )
! 1659: {
! 1660: httpd_sockaddr sa;
! 1661: socklen_t sz;
! 1662:
! 1663: if ( ! hc->initialized )
! 1664: {
! 1665: hc->read_size = 0;
! 1666: httpd_realloc_str( &hc->read_buf, &hc->read_size, 500 );
! 1667: hc->maxdecodedurl =
! 1668: hc->maxorigfilename = hc->maxexpnfilename = hc->maxencodings =
! 1669: hc->maxpathinfo = hc->maxquery = hc->maxaccept =
! 1670: hc->maxaccepte = hc->maxreqhost = hc->maxhostdir =
! 1671: hc->maxremoteuser = hc->maxresponse = 0;
! 1672: #ifdef TILDE_MAP_2
! 1673: hc->maxaltdir = 0;
! 1674: #endif /* TILDE_MAP_2 */
! 1675: httpd_realloc_str( &hc->decodedurl, &hc->maxdecodedurl, 1 );
! 1676: httpd_realloc_str( &hc->origfilename, &hc->maxorigfilename, 1 );
! 1677: httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, 0 );
! 1678: httpd_realloc_str( &hc->encodings, &hc->maxencodings, 0 );
! 1679: httpd_realloc_str( &hc->pathinfo, &hc->maxpathinfo, 0 );
! 1680: httpd_realloc_str( &hc->query, &hc->maxquery, 0 );
! 1681: httpd_realloc_str( &hc->accept, &hc->maxaccept, 0 );
! 1682: httpd_realloc_str( &hc->accepte, &hc->maxaccepte, 0 );
! 1683: httpd_realloc_str( &hc->reqhost, &hc->maxreqhost, 0 );
! 1684: httpd_realloc_str( &hc->hostdir, &hc->maxhostdir, 0 );
! 1685: httpd_realloc_str( &hc->remoteuser, &hc->maxremoteuser, 0 );
! 1686: httpd_realloc_str( &hc->response, &hc->maxresponse, 0 );
! 1687: #ifdef TILDE_MAP_2
! 1688: httpd_realloc_str( &hc->altdir, &hc->maxaltdir, 0 );
! 1689: #endif /* TILDE_MAP_2 */
! 1690: hc->initialized = 1;
! 1691: }
! 1692:
! 1693: /* Accept the new connection. */
! 1694: sz = sizeof(sa);
! 1695: hc->conn_fd = accept( listen_fd, &sa.sa, &sz );
! 1696: if ( hc->conn_fd < 0 )
! 1697: {
! 1698: if ( errno == EWOULDBLOCK )
! 1699: return GC_NO_MORE;
! 1700: syslog( LOG_ERR, "accept - %m" );
! 1701: return GC_FAIL;
! 1702: }
! 1703: if ( ! sockaddr_check( &sa ) )
! 1704: {
! 1705: syslog( LOG_ERR, "unknown sockaddr family" );
! 1706: close( hc->conn_fd );
! 1707: hc->conn_fd = -1;
! 1708: return GC_FAIL;
! 1709: }
! 1710: (void) fcntl( hc->conn_fd, F_SETFD, 1 );
! 1711: hc->hs = hs;
! 1712: (void) memset( &hc->client_addr, 0, sizeof(hc->client_addr) );
! 1713: (void) memmove( &hc->client_addr, &sa, sockaddr_len( &sa ) );
! 1714: hc->read_idx = 0;
! 1715: hc->checked_idx = 0;
! 1716: hc->checked_state = CHST_FIRSTWORD;
! 1717: hc->method = METHOD_UNKNOWN;
! 1718: hc->status = 0;
! 1719: hc->bytes_to_send = 0;
! 1720: hc->bytes_sent = 0;
! 1721: hc->encodedurl = "";
! 1722: hc->decodedurl[0] = '\0';
! 1723: hc->protocol = "UNKNOWN";
! 1724: hc->origfilename[0] = '\0';
! 1725: hc->expnfilename[0] = '\0';
! 1726: hc->encodings[0] = '\0';
! 1727: hc->pathinfo[0] = '\0';
! 1728: hc->query[0] = '\0';
! 1729: hc->referer = "";
! 1730: hc->useragent = "";
! 1731: hc->accept[0] = '\0';
! 1732: hc->accepte[0] = '\0';
! 1733: hc->acceptl = "";
! 1734: hc->cookie = "";
! 1735: hc->contenttype = "";
! 1736: hc->reqhost[0] = '\0';
! 1737: hc->hdrhost = "";
! 1738: hc->hostdir[0] = '\0';
! 1739: hc->authorization = "";
! 1740: hc->remoteuser[0] = '\0';
! 1741: hc->response[0] = '\0';
! 1742: #ifdef TILDE_MAP_2
! 1743: hc->altdir[0] = '\0';
! 1744: #endif /* TILDE_MAP_2 */
! 1745: hc->responselen = 0;
! 1746: hc->if_modified_since = (time_t) -1;
! 1747: hc->range_if = (time_t) -1;
! 1748: hc->contentlength = -1;
! 1749: hc->type = "";
! 1750: hc->hostname = (char*) 0;
! 1751: hc->mime_flag = 1;
! 1752: hc->one_one = 0;
! 1753: hc->got_range = 0;
! 1754: hc->tildemapped = 0;
! 1755: hc->first_byte_index = 0;
! 1756: hc->last_byte_index = -1;
! 1757: hc->keep_alive = 0;
! 1758: hc->should_linger = 0;
! 1759: hc->file_address = (char*) 0;
! 1760: return GC_OK;
! 1761: }
! 1762:
! 1763:
! 1764: /* Checks hc->read_buf to see whether a complete request has been read so far;
! 1765: ** either the first line has two words (an HTTP/0.9 request), or the first
! 1766: ** line has three words and there's a blank line present.
! 1767: **
! 1768: ** hc->read_idx is how much has been read in; hc->checked_idx is how much we
! 1769: ** have checked so far; and hc->checked_state is the current state of the
! 1770: ** finite state machine.
! 1771: */
! 1772: int
! 1773: httpd_got_request( httpd_conn* hc )
! 1774: {
! 1775: char c;
! 1776:
! 1777: for ( ; hc->checked_idx < hc->read_idx; ++hc->checked_idx )
! 1778: {
! 1779: c = hc->read_buf[hc->checked_idx];
! 1780: switch ( hc->checked_state )
! 1781: {
! 1782: case CHST_FIRSTWORD:
! 1783: switch ( c )
! 1784: {
! 1785: case ' ': case '\t':
! 1786: hc->checked_state = CHST_FIRSTWS;
! 1787: break;
! 1788: case '\012': case '\015':
! 1789: hc->checked_state = CHST_BOGUS;
! 1790: return GR_BAD_REQUEST;
! 1791: }
! 1792: break;
! 1793: case CHST_FIRSTWS:
! 1794: switch ( c )
! 1795: {
! 1796: case ' ': case '\t':
! 1797: break;
! 1798: case '\012': case '\015':
! 1799: hc->checked_state = CHST_BOGUS;
! 1800: return GR_BAD_REQUEST;
! 1801: default:
! 1802: hc->checked_state = CHST_SECONDWORD;
! 1803: break;
! 1804: }
! 1805: break;
! 1806: case CHST_SECONDWORD:
! 1807: switch ( c )
! 1808: {
! 1809: case ' ': case '\t':
! 1810: hc->checked_state = CHST_SECONDWS;
! 1811: break;
! 1812: case '\012': case '\015':
! 1813: /* The first line has only two words - an HTTP/0.9 request. */
! 1814: return GR_GOT_REQUEST;
! 1815: }
! 1816: break;
! 1817: case CHST_SECONDWS:
! 1818: switch ( c )
! 1819: {
! 1820: case ' ': case '\t':
! 1821: break;
! 1822: case '\012': case '\015':
! 1823: hc->checked_state = CHST_BOGUS;
! 1824: return GR_BAD_REQUEST;
! 1825: default:
! 1826: hc->checked_state = CHST_THIRDWORD;
! 1827: break;
! 1828: }
! 1829: break;
! 1830: case CHST_THIRDWORD:
! 1831: switch ( c )
! 1832: {
! 1833: case ' ': case '\t':
! 1834: hc->checked_state = CHST_THIRDWS;
! 1835: break;
! 1836: case '\012':
! 1837: hc->checked_state = CHST_LF;
! 1838: break;
! 1839: case '\015':
! 1840: hc->checked_state = CHST_CR;
! 1841: break;
! 1842: }
! 1843: break;
! 1844: case CHST_THIRDWS:
! 1845: switch ( c )
! 1846: {
! 1847: case ' ': case '\t':
! 1848: break;
! 1849: case '\012':
! 1850: hc->checked_state = CHST_LF;
! 1851: break;
! 1852: case '\015':
! 1853: hc->checked_state = CHST_CR;
! 1854: break;
! 1855: default:
! 1856: hc->checked_state = CHST_BOGUS;
! 1857: return GR_BAD_REQUEST;
! 1858: }
! 1859: break;
! 1860: case CHST_LINE:
! 1861: switch ( c )
! 1862: {
! 1863: case '\012':
! 1864: hc->checked_state = CHST_LF;
! 1865: break;
! 1866: case '\015':
! 1867: hc->checked_state = CHST_CR;
! 1868: break;
! 1869: }
! 1870: break;
! 1871: case CHST_LF:
! 1872: switch ( c )
! 1873: {
! 1874: case '\012':
! 1875: /* Two newlines in a row - a blank line - end of request. */
! 1876: return GR_GOT_REQUEST;
! 1877: case '\015':
! 1878: hc->checked_state = CHST_CR;
! 1879: break;
! 1880: default:
! 1881: hc->checked_state = CHST_LINE;
! 1882: break;
! 1883: }
! 1884: break;
! 1885: case CHST_CR:
! 1886: switch ( c )
! 1887: {
! 1888: case '\012':
! 1889: hc->checked_state = CHST_CRLF;
! 1890: break;
! 1891: case '\015':
! 1892: /* Two returns in a row - end of request. */
! 1893: return GR_GOT_REQUEST;
! 1894: default:
! 1895: hc->checked_state = CHST_LINE;
! 1896: break;
! 1897: }
! 1898: break;
! 1899: case CHST_CRLF:
! 1900: switch ( c )
! 1901: {
! 1902: case '\012':
! 1903: /* Two newlines in a row - end of request. */
! 1904: return GR_GOT_REQUEST;
! 1905: case '\015':
! 1906: hc->checked_state = CHST_CRLFCR;
! 1907: break;
! 1908: default:
! 1909: hc->checked_state = CHST_LINE;
! 1910: break;
! 1911: }
! 1912: break;
! 1913: case CHST_CRLFCR:
! 1914: switch ( c )
! 1915: {
! 1916: case '\012': case '\015':
! 1917: /* Two CRLFs or two CRs in a row - end of request. */
! 1918: return GR_GOT_REQUEST;
! 1919: default:
! 1920: hc->checked_state = CHST_LINE;
! 1921: break;
! 1922: }
! 1923: break;
! 1924: case CHST_BOGUS:
! 1925: return GR_BAD_REQUEST;
! 1926: }
! 1927: }
! 1928: return GR_NO_REQUEST;
! 1929: }
! 1930:
! 1931:
! 1932: int
! 1933: httpd_parse_request( httpd_conn* hc )
! 1934: {
! 1935: char* buf;
! 1936: char* method_str;
! 1937: char* url;
! 1938: char* protocol;
! 1939: char* reqhost;
! 1940: char* eol;
! 1941: char* cp;
! 1942: char* pi;
! 1943:
! 1944: hc->checked_idx = 0; /* reset */
! 1945: method_str = bufgets( hc );
! 1946: url = strpbrk( method_str, " \t\012\015" );
! 1947: if ( url == (char*) 0 )
! 1948: {
! 1949: httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
! 1950: return -1;
! 1951: }
! 1952: *url++ = '\0';
! 1953: url += strspn( url, " \t\012\015" );
! 1954: protocol = strpbrk( url, " \t\012\015" );
! 1955: if ( protocol == (char*) 0 )
! 1956: {
! 1957: protocol = "HTTP/0.9";
! 1958: hc->mime_flag = 0;
! 1959: }
! 1960: else
! 1961: {
! 1962: *protocol++ = '\0';
! 1963: protocol += strspn( protocol, " \t\012\015" );
! 1964: if ( *protocol != '\0' )
! 1965: {
! 1966: eol = strpbrk( protocol, " \t\012\015" );
! 1967: if ( eol != (char*) 0 )
! 1968: *eol = '\0';
! 1969: if ( strcasecmp( protocol, "HTTP/1.0" ) != 0 )
! 1970: hc->one_one = 1;
! 1971: }
! 1972: }
! 1973: hc->protocol = protocol;
! 1974:
! 1975: /* Check for HTTP/1.1 absolute URL. */
! 1976: if ( strncasecmp( url, "http://", 7 ) == 0 )
! 1977: {
! 1978: if ( ! hc->one_one )
! 1979: {
! 1980: httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
! 1981: return -1;
! 1982: }
! 1983: reqhost = url + 7;
! 1984: url = strchr( reqhost, '/' );
! 1985: if ( url == (char*) 0 )
! 1986: {
! 1987: httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
! 1988: return -1;
! 1989: }
! 1990: *url = '\0';
! 1991: if ( strchr( reqhost, '/' ) != (char*) 0 || reqhost[0] == '.' )
! 1992: {
! 1993: httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
! 1994: return -1;
! 1995: }
! 1996: httpd_realloc_str( &hc->reqhost, &hc->maxreqhost, strlen( reqhost ) );
! 1997: (void) strcpy( hc->reqhost, reqhost );
! 1998: *url = '/';
! 1999: }
! 2000:
! 2001: if ( *url != '/' )
! 2002: {
! 2003: httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
! 2004: return -1;
! 2005: }
! 2006:
! 2007: if ( strcasecmp( method_str, httpd_method_str( METHOD_GET ) ) == 0 )
! 2008: hc->method = METHOD_GET;
! 2009: else if ( strcasecmp( method_str, httpd_method_str( METHOD_HEAD ) ) == 0 )
! 2010: hc->method = METHOD_HEAD;
! 2011: else if ( strcasecmp( method_str, httpd_method_str( METHOD_POST ) ) == 0 )
! 2012: hc->method = METHOD_POST;
! 2013: else
! 2014: {
! 2015: httpd_send_err( hc, 501, err501title, "", err501form, method_str );
! 2016: return -1;
! 2017: }
! 2018:
! 2019: hc->encodedurl = url;
! 2020: httpd_realloc_str(
! 2021: &hc->decodedurl, &hc->maxdecodedurl, strlen( hc->encodedurl ) );
! 2022: strdecode( hc->decodedurl, hc->encodedurl );
! 2023:
! 2024: httpd_realloc_str(
! 2025: &hc->origfilename, &hc->maxorigfilename, strlen( hc->decodedurl ) );
! 2026: (void) strcpy( hc->origfilename, &hc->decodedurl[1] );
! 2027: /* Special case for top-level URL. */
! 2028: if ( hc->origfilename[0] == '\0' )
! 2029: (void) strcpy( hc->origfilename, "." );
! 2030:
! 2031: /* Extract query string from encoded URL. */
! 2032: cp = strchr( hc->encodedurl, '?' );
! 2033: if ( cp != (char*) 0 )
! 2034: {
! 2035: ++cp;
! 2036: httpd_realloc_str( &hc->query, &hc->maxquery, strlen( cp ) );
! 2037: (void) strcpy( hc->query, cp );
! 2038: /* Remove query from (decoded) origfilename. */
! 2039: cp = strchr( hc->origfilename, '?' );
! 2040: if ( cp != (char*) 0 )
! 2041: *cp = '\0';
! 2042: }
! 2043:
! 2044: de_dotdot( hc->origfilename );
! 2045: if ( hc->origfilename[0] == '/' ||
! 2046: ( hc->origfilename[0] == '.' && hc->origfilename[1] == '.' &&
! 2047: ( hc->origfilename[2] == '\0' || hc->origfilename[2] == '/' ) ) )
! 2048: {
! 2049: httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
! 2050: return -1;
! 2051: }
! 2052:
! 2053: if ( hc->mime_flag )
! 2054: {
! 2055: /* Read the MIME headers. */
! 2056: while ( ( buf = bufgets( hc ) ) != (char*) 0 )
! 2057: {
! 2058: if ( buf[0] == '\0' )
! 2059: break;
! 2060: if ( strncasecmp( buf, "Referer:", 8 ) == 0 )
! 2061: {
! 2062: cp = &buf[8];
! 2063: cp += strspn( cp, " \t" );
! 2064: hc->referer = cp;
! 2065: }
! 2066: else if ( strncasecmp( buf, "User-Agent:", 11 ) == 0 )
! 2067: {
! 2068: cp = &buf[11];
! 2069: cp += strspn( cp, " \t" );
! 2070: hc->useragent = cp;
! 2071: }
! 2072: else if ( strncasecmp( buf, "Host:", 5 ) == 0 )
! 2073: {
! 2074: cp = &buf[5];
! 2075: cp += strspn( cp, " \t" );
! 2076: hc->hdrhost = cp;
! 2077: cp = strchr( hc->hdrhost, ':' );
! 2078: if ( cp != (char*) 0 )
! 2079: *cp = '\0';
! 2080: if ( strchr( hc->hdrhost, '/' ) != (char*) 0 || hc->hdrhost[0] == '.' )
! 2081: {
! 2082: httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
! 2083: return -1;
! 2084: }
! 2085: }
! 2086: else if ( strncasecmp( buf, "Accept:", 7 ) == 0 )
! 2087: {
! 2088: cp = &buf[7];
! 2089: cp += strspn( cp, " \t" );
! 2090: if ( hc->accept[0] != '\0' )
! 2091: {
! 2092: if ( strlen( hc->accept ) > 5000 )
! 2093: {
! 2094: syslog(
! 2095: LOG_ERR, "%.80s way too much Accept: data",
! 2096: httpd_ntoa( &hc->client_addr ) );
! 2097: continue;
! 2098: }
! 2099: httpd_realloc_str(
! 2100: &hc->accept, &hc->maxaccept,
! 2101: strlen( hc->accept ) + 2 + strlen( cp ) );
! 2102: (void) strcat( hc->accept, ", " );
! 2103: }
! 2104: else
! 2105: httpd_realloc_str(
! 2106: &hc->accept, &hc->maxaccept, strlen( cp ) );
! 2107: (void) strcat( hc->accept, cp );
! 2108: }
! 2109: else if ( strncasecmp( buf, "Accept-Encoding:", 16 ) == 0 )
! 2110: {
! 2111: cp = &buf[16];
! 2112: cp += strspn( cp, " \t" );
! 2113: if ( hc->accepte[0] != '\0' )
! 2114: {
! 2115: if ( strlen( hc->accepte ) > 5000 )
! 2116: {
! 2117: syslog(
! 2118: LOG_ERR, "%.80s way too much Accept-Encoding: data",
! 2119: httpd_ntoa( &hc->client_addr ) );
! 2120: continue;
! 2121: }
! 2122: httpd_realloc_str(
! 2123: &hc->accepte, &hc->maxaccepte,
! 2124: strlen( hc->accepte ) + 2 + strlen( cp ) );
! 2125: (void) strcat( hc->accepte, ", " );
! 2126: }
! 2127: else
! 2128: httpd_realloc_str(
! 2129: &hc->accepte, &hc->maxaccepte, strlen( cp ) );
! 2130: (void) strcpy( hc->accepte, cp );
! 2131: }
! 2132: else if ( strncasecmp( buf, "Accept-Language:", 16 ) == 0 )
! 2133: {
! 2134: cp = &buf[16];
! 2135: cp += strspn( cp, " \t" );
! 2136: hc->acceptl = cp;
! 2137: }
! 2138: else if ( strncasecmp( buf, "If-Modified-Since:", 18 ) == 0 )
! 2139: {
! 2140: cp = &buf[18];
! 2141: hc->if_modified_since = tdate_parse( cp );
! 2142: if ( hc->if_modified_since == (time_t) -1 )
! 2143: syslog( LOG_DEBUG, "unparsable time: %.80s", cp );
! 2144: }
! 2145: else if ( strncasecmp( buf, "Cookie:", 7 ) == 0 )
! 2146: {
! 2147: cp = &buf[7];
! 2148: cp += strspn( cp, " \t" );
! 2149: hc->cookie = cp;
! 2150: }
! 2151: else if ( strncasecmp( buf, "Range:", 6 ) == 0 )
! 2152: {
! 2153: /* Only support %d- and %d-%d, not %d-%d,%d-%d or -%d. */
! 2154: if ( strchr( buf, ',' ) == (char*) 0 )
! 2155: {
! 2156: char* cp_dash;
! 2157: cp = strpbrk( buf, "=" );
! 2158: if ( cp != (char*) 0 )
! 2159: {
! 2160: cp_dash = strchr( cp + 1, '-' );
! 2161: if ( cp_dash != (char*) 0 && cp_dash != cp + 1 )
! 2162: {
! 2163: *cp_dash = '\0';
! 2164: hc->got_range = 1;
! 2165: hc->first_byte_index = atoll( cp + 1 );
! 2166: if ( hc->first_byte_index < 0 )
! 2167: hc->first_byte_index = 0;
! 2168: if ( isdigit( (int) cp_dash[1] ) )
! 2169: {
! 2170: hc->last_byte_index = atoll( cp_dash + 1 );
! 2171: if ( hc->last_byte_index < 0 )
! 2172: hc->last_byte_index = -1;
! 2173: }
! 2174: }
! 2175: }
! 2176: }
! 2177: }
! 2178: else if ( strncasecmp( buf, "Range-If:", 9 ) == 0 ||
! 2179: strncasecmp( buf, "If-Range:", 9 ) == 0 )
! 2180: {
! 2181: cp = &buf[9];
! 2182: hc->range_if = tdate_parse( cp );
! 2183: if ( hc->range_if == (time_t) -1 )
! 2184: syslog( LOG_DEBUG, "unparsable time: %.80s", cp );
! 2185: }
! 2186: else if ( strncasecmp( buf, "Content-Type:", 13 ) == 0 )
! 2187: {
! 2188: cp = &buf[13];
! 2189: cp += strspn( cp, " \t" );
! 2190: hc->contenttype = cp;
! 2191: }
! 2192: else if ( strncasecmp( buf, "Content-Length:", 15 ) == 0 )
! 2193: {
! 2194: cp = &buf[15];
! 2195: hc->contentlength = atol( cp );
! 2196: }
! 2197: else if ( strncasecmp( buf, "Authorization:", 14 ) == 0 )
! 2198: {
! 2199: cp = &buf[14];
! 2200: cp += strspn( cp, " \t" );
! 2201: hc->authorization = cp;
! 2202: }
! 2203: else if ( strncasecmp( buf, "Connection:", 11 ) == 0 )
! 2204: {
! 2205: cp = &buf[11];
! 2206: cp += strspn( cp, " \t" );
! 2207: if ( strcasecmp( cp, "keep-alive" ) == 0 )
! 2208: hc->keep_alive = 1;
! 2209: }
! 2210: #ifdef LOG_UNKNOWN_HEADERS
! 2211: else if ( strncasecmp( buf, "Accept-Charset:", 15 ) == 0 ||
! 2212: strncasecmp( buf, "Accept-Language:", 16 ) == 0 ||
! 2213: strncasecmp( buf, "Agent:", 6 ) == 0 ||
! 2214: strncasecmp( buf, "Cache-Control:", 14 ) == 0 ||
! 2215: strncasecmp( buf, "Cache-Info:", 11 ) == 0 ||
! 2216: strncasecmp( buf, "Charge-To:", 10 ) == 0 ||
! 2217: strncasecmp( buf, "Client-IP:", 10 ) == 0 ||
! 2218: strncasecmp( buf, "Date:", 5 ) == 0 ||
! 2219: strncasecmp( buf, "Extension:", 10 ) == 0 ||
! 2220: strncasecmp( buf, "Forwarded:", 10 ) == 0 ||
! 2221: strncasecmp( buf, "From:", 5 ) == 0 ||
! 2222: strncasecmp( buf, "HTTP-Version:", 13 ) == 0 ||
! 2223: strncasecmp( buf, "Max-Forwards:", 13 ) == 0 ||
! 2224: strncasecmp( buf, "Message-Id:", 11 ) == 0 ||
! 2225: strncasecmp( buf, "MIME-Version:", 13 ) == 0 ||
! 2226: strncasecmp( buf, "Negotiate:", 10 ) == 0 ||
! 2227: strncasecmp( buf, "Pragma:", 7 ) == 0 ||
! 2228: strncasecmp( buf, "Proxy-Agent:", 12 ) == 0 ||
! 2229: strncasecmp( buf, "Proxy-Connection:", 17 ) == 0 ||
! 2230: strncasecmp( buf, "Security-Scheme:", 16 ) == 0 ||
! 2231: strncasecmp( buf, "Session-Id:", 11 ) == 0 ||
! 2232: strncasecmp( buf, "UA-Color:", 9 ) == 0 ||
! 2233: strncasecmp( buf, "UA-CPU:", 7 ) == 0 ||
! 2234: strncasecmp( buf, "UA-Disp:", 8 ) == 0 ||
! 2235: strncasecmp( buf, "UA-OS:", 6 ) == 0 ||
! 2236: strncasecmp( buf, "UA-Pixels:", 10 ) == 0 ||
! 2237: strncasecmp( buf, "User:", 5 ) == 0 ||
! 2238: strncasecmp( buf, "Via:", 4 ) == 0 ||
! 2239: strncasecmp( buf, "X-", 2 ) == 0 )
! 2240: ; /* ignore */
! 2241: else
! 2242: syslog( LOG_DEBUG, "unknown request header: %.80s", buf );
! 2243: #endif /* LOG_UNKNOWN_HEADERS */
! 2244: }
! 2245: }
! 2246:
! 2247: if ( hc->one_one )
! 2248: {
! 2249: /* Check that HTTP/1.1 requests specify a host, as required. */
! 2250: if ( hc->reqhost[0] == '\0' && hc->hdrhost[0] == '\0' )
! 2251: {
! 2252: httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
! 2253: return -1;
! 2254: }
! 2255:
! 2256: /* If the client wants to do keep-alives, it might also be doing
! 2257: ** pipelining. There's no way for us to tell. Since we don't
! 2258: ** implement keep-alives yet, if we close such a connection there
! 2259: ** might be unread pipelined requests waiting. So, we have to
! 2260: ** do a lingering close.
! 2261: */
! 2262: if ( hc->keep_alive )
! 2263: hc->should_linger = 1;
! 2264: }
! 2265:
! 2266: /* Ok, the request has been parsed. Now we resolve stuff that
! 2267: ** may require the entire request.
! 2268: */
! 2269:
! 2270: /* Copy original filename to expanded filename. */
! 2271: httpd_realloc_str(
! 2272: &hc->expnfilename, &hc->maxexpnfilename, strlen( hc->origfilename ) );
! 2273: (void) strcpy( hc->expnfilename, hc->origfilename );
! 2274:
! 2275: /* Tilde mapping. */
! 2276: if ( hc->expnfilename[0] == '~' )
! 2277: {
! 2278: #ifdef TILDE_MAP_1
! 2279: if ( ! tilde_map_1( hc ) )
! 2280: {
! 2281: httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl );
! 2282: return -1;
! 2283: }
! 2284: #endif /* TILDE_MAP_1 */
! 2285: #ifdef TILDE_MAP_2
! 2286: if ( ! tilde_map_2( hc ) )
! 2287: {
! 2288: httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl );
! 2289: return -1;
! 2290: }
! 2291: #endif /* TILDE_MAP_2 */
! 2292: }
! 2293:
! 2294: /* Virtual host mapping. */
! 2295: if ( hc->hs->vhost )
! 2296: if ( ! vhost_map( hc ) )
! 2297: {
! 2298: httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
! 2299: return -1;
! 2300: }
! 2301:
! 2302: /* Expand all symbolic links in the filename. This also gives us
! 2303: ** any trailing non-existing components, for pathinfo.
! 2304: */
! 2305: cp = expand_symlinks( hc->expnfilename, &pi, hc->hs->no_symlink_check, hc->tildemapped );
! 2306: if ( cp == (char*) 0 )
! 2307: {
! 2308: httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
! 2309: return -1;
! 2310: }
! 2311: httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, strlen( cp ) );
! 2312: (void) strcpy( hc->expnfilename, cp );
! 2313: httpd_realloc_str( &hc->pathinfo, &hc->maxpathinfo, strlen( pi ) );
! 2314: (void) strcpy( hc->pathinfo, pi );
! 2315:
! 2316: /* Remove pathinfo stuff from the original filename too. */
! 2317: if ( hc->pathinfo[0] != '\0' )
! 2318: {
! 2319: int i;
! 2320: i = strlen( hc->origfilename ) - strlen( hc->pathinfo );
! 2321: if ( i > 0 && strcmp( &hc->origfilename[i], hc->pathinfo ) == 0 )
! 2322: hc->origfilename[i - 1] = '\0';
! 2323: }
! 2324:
! 2325: /* If the expanded filename is an absolute path, check that it's still
! 2326: ** within the current directory or the alternate directory.
! 2327: */
! 2328: if ( hc->expnfilename[0] == '/' )
! 2329: {
! 2330: if ( strncmp(
! 2331: hc->expnfilename, hc->hs->cwd, strlen( hc->hs->cwd ) ) == 0 )
! 2332: {
! 2333: /* Elide the current directory. */
! 2334: (void) strcpy(
! 2335: hc->expnfilename, &hc->expnfilename[strlen( hc->hs->cwd )] );
! 2336: }
! 2337: #ifdef TILDE_MAP_2
! 2338: else if ( hc->altdir[0] != '\0' &&
! 2339: ( strncmp(
! 2340: hc->expnfilename, hc->altdir,
! 2341: strlen( hc->altdir ) ) == 0 &&
! 2342: ( hc->expnfilename[strlen( hc->altdir )] == '\0' ||
! 2343: hc->expnfilename[strlen( hc->altdir )] == '/' ) ) )
! 2344: {}
! 2345: #endif /* TILDE_MAP_2 */
! 2346: else
! 2347: {
! 2348: syslog(
! 2349: LOG_NOTICE, "%.80s URL \"%.80s\" goes outside the web tree",
! 2350: httpd_ntoa( &hc->client_addr ), hc->encodedurl );
! 2351: httpd_send_err(
! 2352: hc, 403, err403title, "",
! 2353: ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file outside the permitted web server directory tree.\n" ),
! 2354: hc->encodedurl );
! 2355: return -1;
! 2356: }
! 2357: }
! 2358:
! 2359: return 0;
! 2360: }
! 2361:
! 2362:
! 2363: static char*
! 2364: bufgets( httpd_conn* hc )
! 2365: {
! 2366: int i;
! 2367: char c;
! 2368:
! 2369: for ( i = hc->checked_idx; hc->checked_idx < hc->read_idx; ++hc->checked_idx )
! 2370: {
! 2371: c = hc->read_buf[hc->checked_idx];
! 2372: if ( c == '\012' || c == '\015' )
! 2373: {
! 2374: hc->read_buf[hc->checked_idx] = '\0';
! 2375: ++hc->checked_idx;
! 2376: if ( c == '\015' && hc->checked_idx < hc->read_idx &&
! 2377: hc->read_buf[hc->checked_idx] == '\012' )
! 2378: {
! 2379: hc->read_buf[hc->checked_idx] = '\0';
! 2380: ++hc->checked_idx;
! 2381: }
! 2382: return &(hc->read_buf[i]);
! 2383: }
! 2384: }
! 2385: return (char*) 0;
! 2386: }
! 2387:
! 2388:
! 2389: static void
! 2390: de_dotdot( char* file )
! 2391: {
! 2392: char* cp;
! 2393: char* cp2;
! 2394: int l;
! 2395:
! 2396: /* Collapse any multiple / sequences. */
! 2397: while ( ( cp = strstr( file, "//") ) != (char*) 0 )
! 2398: {
! 2399: for ( cp2 = cp + 2; *cp2 == '/'; ++cp2 )
! 2400: continue;
! 2401: (void) strcpy( cp + 1, cp2 );
! 2402: }
! 2403:
! 2404: /* Remove leading ./ and any /./ sequences. */
! 2405: while ( strncmp( file, "./", 2 ) == 0 )
! 2406: (void) strcpy( file, file + 2 );
! 2407: while ( ( cp = strstr( file, "/./") ) != (char*) 0 )
! 2408: (void) strcpy( cp, cp + 2 );
! 2409:
! 2410: /* Alternate between removing leading ../ and removing xxx/../ */
! 2411: for (;;)
! 2412: {
! 2413: while ( strncmp( file, "../", 3 ) == 0 )
! 2414: (void) strcpy( file, file + 3 );
! 2415: cp = strstr( file, "/../" );
! 2416: if ( cp == (char*) 0 )
! 2417: break;
! 2418: for ( cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2 )
! 2419: continue;
! 2420: (void) strcpy( cp2 + 1, cp + 4 );
! 2421: }
! 2422:
! 2423: /* Also elide any xxx/.. at the end. */
! 2424: while ( ( l = strlen( file ) ) > 3 &&
! 2425: strcmp( ( cp = file + l - 3 ), "/.." ) == 0 )
! 2426: {
! 2427: for ( cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2 )
! 2428: continue;
! 2429: if ( cp2 < file )
! 2430: break;
! 2431: *cp2 = '\0';
! 2432: }
! 2433: }
! 2434:
! 2435:
! 2436: void
! 2437: httpd_close_conn( httpd_conn* hc, struct timeval* nowP )
! 2438: {
! 2439: make_log_entry( hc, nowP );
! 2440:
! 2441: if ( hc->file_address != (char*) 0 )
! 2442: {
! 2443: mmc_unmap( hc->file_address, &(hc->sb), nowP );
! 2444: hc->file_address = (char*) 0;
! 2445: }
! 2446: if ( hc->conn_fd >= 0 )
! 2447: {
! 2448: (void) close( hc->conn_fd );
! 2449: hc->conn_fd = -1;
! 2450: }
! 2451: }
! 2452:
! 2453: void
! 2454: httpd_destroy_conn( httpd_conn* hc )
! 2455: {
! 2456: if ( hc->initialized )
! 2457: {
! 2458: free( (void*) hc->read_buf );
! 2459: free( (void*) hc->decodedurl );
! 2460: free( (void*) hc->origfilename );
! 2461: free( (void*) hc->expnfilename );
! 2462: free( (void*) hc->encodings );
! 2463: free( (void*) hc->pathinfo );
! 2464: free( (void*) hc->query );
! 2465: free( (void*) hc->accept );
! 2466: free( (void*) hc->accepte );
! 2467: free( (void*) hc->reqhost );
! 2468: free( (void*) hc->hostdir );
! 2469: free( (void*) hc->remoteuser );
! 2470: free( (void*) hc->response );
! 2471: #ifdef TILDE_MAP_2
! 2472: free( (void*) hc->altdir );
! 2473: #endif /* TILDE_MAP_2 */
! 2474: hc->initialized = 0;
! 2475: }
! 2476: }
! 2477:
! 2478:
! 2479: struct mime_entry {
! 2480: char* ext;
! 2481: size_t ext_len;
! 2482: char* val;
! 2483: size_t val_len;
! 2484: };
! 2485: static struct mime_entry enc_tab[] = {
! 2486: #include "mime_encodings.h"
! 2487: };
! 2488: static const int n_enc_tab = sizeof(enc_tab) / sizeof(*enc_tab);
! 2489: static struct mime_entry typ_tab[] = {
! 2490: #include "mime_types.h"
! 2491: };
! 2492: static const int n_typ_tab = sizeof(typ_tab) / sizeof(*typ_tab);
! 2493:
! 2494:
! 2495: /* qsort comparison routine - declared old-style on purpose, for portability. */
! 2496: static int
! 2497: ext_compare( a, b )
! 2498: struct mime_entry* a;
! 2499: struct mime_entry* b;
! 2500: {
! 2501: return strcmp( a->ext, b->ext );
! 2502: }
! 2503:
! 2504:
! 2505: static void
! 2506: init_mime( void )
! 2507: {
! 2508: int i;
! 2509:
! 2510: /* Sort the tables so we can do binary search. */
! 2511: qsort( enc_tab, n_enc_tab, sizeof(*enc_tab), ext_compare );
! 2512: qsort( typ_tab, n_typ_tab, sizeof(*typ_tab), ext_compare );
! 2513:
! 2514: /* Fill in the lengths. */
! 2515: for ( i = 0; i < n_enc_tab; ++i )
! 2516: {
! 2517: enc_tab[i].ext_len = strlen( enc_tab[i].ext );
! 2518: enc_tab[i].val_len = strlen( enc_tab[i].val );
! 2519: }
! 2520: for ( i = 0; i < n_typ_tab; ++i )
! 2521: {
! 2522: typ_tab[i].ext_len = strlen( typ_tab[i].ext );
! 2523: typ_tab[i].val_len = strlen( typ_tab[i].val );
! 2524: }
! 2525:
! 2526: }
! 2527:
! 2528:
! 2529: /* Figure out MIME encodings and type based on the filename. Multiple
! 2530: ** encodings are separated by commas, and are listed in the order in
! 2531: ** which they were applied to the file.
! 2532: */
! 2533: static void
! 2534: figure_mime( httpd_conn* hc )
! 2535: {
! 2536: char* prev_dot;
! 2537: char* dot;
! 2538: char* ext;
! 2539: int me_indexes[100], n_me_indexes;
! 2540: size_t ext_len, encodings_len;
! 2541: int i, top, bot, mid;
! 2542: int r;
! 2543: char* default_type = "text/plain; charset=%s";
! 2544:
! 2545: /* Peel off encoding extensions until there aren't any more. */
! 2546: n_me_indexes = 0;
! 2547: for ( prev_dot = &hc->expnfilename[strlen(hc->expnfilename)]; ; prev_dot = dot )
! 2548: {
! 2549: for ( dot = prev_dot - 1; dot >= hc->expnfilename && *dot != '.'; --dot )
! 2550: ;
! 2551: if ( dot < hc->expnfilename )
! 2552: {
! 2553: /* No dot found. No more encoding extensions, and no type
! 2554: ** extension either.
! 2555: */
! 2556: hc->type = default_type;
! 2557: goto done;
! 2558: }
! 2559: ext = dot + 1;
! 2560: ext_len = prev_dot - ext;
! 2561: /* Search the encodings table. Linear search is fine here, there
! 2562: ** are only a few entries.
! 2563: */
! 2564: for ( i = 0; i < n_enc_tab; ++i )
! 2565: {
! 2566: if ( ext_len == enc_tab[i].ext_len && strncasecmp( ext, enc_tab[i].ext, ext_len ) == 0 )
! 2567: {
! 2568: if ( n_me_indexes < sizeof(me_indexes)/sizeof(*me_indexes) )
! 2569: {
! 2570: me_indexes[n_me_indexes] = i;
! 2571: ++n_me_indexes;
! 2572: }
! 2573: goto next;
! 2574: }
! 2575: }
! 2576: /* No encoding extension found. Break and look for a type extension. */
! 2577: break;
! 2578:
! 2579: next: ;
! 2580: }
! 2581:
! 2582: /* Binary search for a matching type extension. */
! 2583: top = n_typ_tab - 1;
! 2584: bot = 0;
! 2585: while ( top >= bot )
! 2586: {
! 2587: mid = ( top + bot ) / 2;
! 2588: r = strncasecmp( ext, typ_tab[mid].ext, ext_len );
! 2589: if ( r < 0 )
! 2590: top = mid - 1;
! 2591: else if ( r > 0 )
! 2592: bot = mid + 1;
! 2593: else
! 2594: if ( ext_len < typ_tab[mid].ext_len )
! 2595: top = mid - 1;
! 2596: else if ( ext_len > typ_tab[mid].ext_len )
! 2597: bot = mid + 1;
! 2598: else
! 2599: {
! 2600: hc->type = typ_tab[mid].val;
! 2601: goto done;
! 2602: }
! 2603: }
! 2604: hc->type = default_type;
! 2605:
! 2606: done:
! 2607:
! 2608: /* The last thing we do is actually generate the mime-encoding header. */
! 2609: hc->encodings[0] = '\0';
! 2610: encodings_len = 0;
! 2611: for ( i = n_me_indexes - 1; i >= 0; --i )
! 2612: {
! 2613: httpd_realloc_str(
! 2614: &hc->encodings, &hc->maxencodings,
! 2615: encodings_len + enc_tab[me_indexes[i]].val_len + 1 );
! 2616: if ( hc->encodings[0] != '\0' )
! 2617: {
! 2618: (void) strcpy( &hc->encodings[encodings_len], "," );
! 2619: ++encodings_len;
! 2620: }
! 2621: (void) strcpy( &hc->encodings[encodings_len], enc_tab[me_indexes[i]].val );
! 2622: encodings_len += enc_tab[me_indexes[i]].val_len;
! 2623: }
! 2624:
! 2625: }
! 2626:
! 2627:
! 2628: #ifdef CGI_TIMELIMIT
! 2629: static void
! 2630: cgi_kill2( ClientData client_data, struct timeval* nowP )
! 2631: {
! 2632: pid_t pid;
! 2633:
! 2634: pid = (pid_t) client_data.i;
! 2635: if ( kill( pid, SIGKILL ) == 0 )
! 2636: syslog( LOG_ERR, "hard-killed CGI process %d", pid );
! 2637: }
! 2638:
! 2639: static void
! 2640: cgi_kill( ClientData client_data, struct timeval* nowP )
! 2641: {
! 2642: pid_t pid;
! 2643:
! 2644: pid = (pid_t) client_data.i;
! 2645: if ( kill( pid, SIGINT ) == 0 )
! 2646: {
! 2647: syslog( LOG_ERR, "killed CGI process %d", pid );
! 2648: /* In case this isn't enough, schedule an uncatchable kill. */
! 2649: if ( tmr_create( nowP, cgi_kill2, client_data, 5 * 1000L, 0 ) == (Timer*) 0 )
! 2650: {
! 2651: syslog( LOG_CRIT, "tmr_create(cgi_kill2) failed" );
! 2652: exit( 1 );
! 2653: }
! 2654: }
! 2655: }
! 2656: #endif /* CGI_TIMELIMIT */
! 2657:
! 2658:
! 2659: #ifdef GENERATE_INDEXES
! 2660:
! 2661: /* qsort comparison routine - declared old-style on purpose, for portability. */
! 2662: static int
! 2663: name_compare( a, b )
! 2664: char** a;
! 2665: char** b;
! 2666: {
! 2667: return strcmp( *a, *b );
! 2668: }
! 2669:
! 2670:
! 2671: static int
! 2672: ls( httpd_conn* hc )
! 2673: {
! 2674: DIR* dirp;
! 2675: struct dirent* de;
! 2676: int namlen;
! 2677: static int maxnames = 0;
! 2678: int nnames;
! 2679: static char* names;
! 2680: static char** nameptrs;
! 2681: static char* name;
! 2682: static size_t maxname = 0;
! 2683: static char* rname;
! 2684: static size_t maxrname = 0;
! 2685: static char* encrname;
! 2686: static size_t maxencrname = 0;
! 2687: FILE* fp;
! 2688: int i, r;
! 2689: struct stat sb;
! 2690: struct stat lsb;
! 2691: char modestr[20];
! 2692: char* linkprefix;
! 2693: char link[MAXPATHLEN+1];
! 2694: int linklen;
! 2695: char* fileclass;
! 2696: time_t now;
! 2697: char* timestr;
! 2698: ClientData client_data;
! 2699:
! 2700: dirp = opendir( hc->expnfilename );
! 2701: if ( dirp == (DIR*) 0 )
! 2702: {
! 2703: syslog( LOG_ERR, "opendir %.80s - %m", hc->expnfilename );
! 2704: httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl );
! 2705: return -1;
! 2706: }
! 2707:
! 2708: if ( hc->method == METHOD_HEAD )
! 2709: {
! 2710: closedir( dirp );
! 2711: send_mime(
! 2712: hc, 200, ok200title, "", "", "text/html; charset=%s", (off_t) -1,
! 2713: hc->sb.st_mtime );
! 2714: }
! 2715: else if ( hc->method == METHOD_GET )
! 2716: {
! 2717: if ( hc->hs->cgi_limit != 0 && hc->hs->cgi_count >= hc->hs->cgi_limit )
! 2718: {
! 2719: closedir( dirp );
! 2720: httpd_send_err(
! 2721: hc, 503, httpd_err503title, "", httpd_err503form,
! 2722: hc->encodedurl );
! 2723: return -1;
! 2724: }
! 2725: ++hc->hs->cgi_count;
! 2726: r = fork( );
! 2727: if ( r < 0 )
! 2728: {
! 2729: syslog( LOG_ERR, "fork - %m" );
! 2730: closedir( dirp );
! 2731: httpd_send_err(
! 2732: hc, 500, err500title, "", err500form, hc->encodedurl );
! 2733: return -1;
! 2734: }
! 2735: if ( r == 0 )
! 2736: {
! 2737: /* Child process. */
! 2738: sub_process = 1;
! 2739: httpd_unlisten( hc->hs );
! 2740: send_mime(
! 2741: hc, 200, ok200title, "", "", "text/html; charset=%s",
! 2742: (off_t) -1, hc->sb.st_mtime );
! 2743: httpd_write_response( hc );
! 2744:
! 2745: #ifdef CGI_NICE
! 2746: /* Set priority. */
! 2747: (void) nice( CGI_NICE );
! 2748: #endif /* CGI_NICE */
! 2749:
! 2750: /* Open a stdio stream so that we can use fprintf, which is more
! 2751: ** efficient than a bunch of separate write()s. We don't have
! 2752: ** to worry about double closes or file descriptor leaks cause
! 2753: ** we're in a subprocess.
! 2754: */
! 2755: fp = fdopen( hc->conn_fd, "w" );
! 2756: if ( fp == (FILE*) 0 )
! 2757: {
! 2758: syslog( LOG_ERR, "fdopen - %m" );
! 2759: httpd_send_err(
! 2760: hc, 500, err500title, "", err500form, hc->encodedurl );
! 2761: httpd_write_response( hc );
! 2762: closedir( dirp );
! 2763: exit( 1 );
! 2764: }
! 2765:
! 2766: (void) fprintf( fp, "\
! 2767: <HTML>\n\
! 2768: <HEAD><TITLE>Index of %.80s</TITLE></HEAD>\n\
! 2769: <BODY BGCOLOR=\"#99cc99\" TEXT=\"#000000\" LINK=\"#2020ff\" VLINK=\"#4040cc\">\n\
! 2770: <H2>Index of %.80s</H2>\n\
! 2771: <PRE>\n\
! 2772: mode links bytes last-changed name\n\
! 2773: <HR>",
! 2774: hc->encodedurl, hc->encodedurl );
! 2775:
! 2776: /* Read in names. */
! 2777: nnames = 0;
! 2778: while ( ( de = readdir( dirp ) ) != 0 ) /* dirent or direct */
! 2779: {
! 2780: if ( nnames >= maxnames )
! 2781: {
! 2782: if ( maxnames == 0 )
! 2783: {
! 2784: maxnames = 100;
! 2785: names = NEW( char, maxnames * ( MAXPATHLEN + 1 ) );
! 2786: nameptrs = NEW( char*, maxnames );
! 2787: }
! 2788: else
! 2789: {
! 2790: maxnames *= 2;
! 2791: names = RENEW( names, char, maxnames * ( MAXPATHLEN + 1 ) );
! 2792: nameptrs = RENEW( nameptrs, char*, maxnames );
! 2793: }
! 2794: if ( names == (char*) 0 || nameptrs == (char**) 0 )
! 2795: {
! 2796: syslog( LOG_ERR, "out of memory reallocating directory names" );
! 2797: exit( 1 );
! 2798: }
! 2799: for ( i = 0; i < maxnames; ++i )
! 2800: nameptrs[i] = &names[i * ( MAXPATHLEN + 1 )];
! 2801: }
! 2802: namlen = NAMLEN(de);
! 2803: (void) strncpy( nameptrs[nnames], de->d_name, namlen );
! 2804: nameptrs[nnames][namlen] = '\0';
! 2805: ++nnames;
! 2806: }
! 2807: closedir( dirp );
! 2808:
! 2809: /* Sort the names. */
! 2810: qsort( nameptrs, nnames, sizeof(*nameptrs), name_compare );
! 2811:
! 2812: /* Generate output. */
! 2813: for ( i = 0; i < nnames; ++i )
! 2814: {
! 2815: httpd_realloc_str(
! 2816: &name, &maxname,
! 2817: strlen( hc->expnfilename ) + 1 + strlen( nameptrs[i] ) );
! 2818: httpd_realloc_str(
! 2819: &rname, &maxrname,
! 2820: strlen( hc->origfilename ) + 1 + strlen( nameptrs[i] ) );
! 2821: if ( hc->expnfilename[0] == '\0' ||
! 2822: strcmp( hc->expnfilename, "." ) == 0 )
! 2823: {
! 2824: (void) strcpy( name, nameptrs[i] );
! 2825: (void) strcpy( rname, nameptrs[i] );
! 2826: }
! 2827: else
! 2828: {
! 2829: (void) my_snprintf( name, maxname,
! 2830: "%s/%s", hc->expnfilename, nameptrs[i] );
! 2831: if ( strcmp( hc->origfilename, "." ) == 0 )
! 2832: (void) my_snprintf( rname, maxrname,
! 2833: "%s", nameptrs[i] );
! 2834: else
! 2835: (void) my_snprintf( rname, maxrname,
! 2836: "%s%s", hc->origfilename, nameptrs[i] );
! 2837: }
! 2838: httpd_realloc_str(
! 2839: &encrname, &maxencrname, 3 * strlen( rname ) + 1 );
! 2840: strencode( encrname, maxencrname, rname );
! 2841:
! 2842: if ( stat( name, &sb ) < 0 || lstat( name, &lsb ) < 0 )
! 2843: continue;
! 2844:
! 2845: linkprefix = "";
! 2846: link[0] = '\0';
! 2847: /* Break down mode word. First the file type. */
! 2848: switch ( lsb.st_mode & S_IFMT )
! 2849: {
! 2850: case S_IFIFO: modestr[0] = 'p'; break;
! 2851: case S_IFCHR: modestr[0] = 'c'; break;
! 2852: case S_IFDIR: modestr[0] = 'd'; break;
! 2853: case S_IFBLK: modestr[0] = 'b'; break;
! 2854: case S_IFREG: modestr[0] = '-'; break;
! 2855: case S_IFSOCK: modestr[0] = 's'; break;
! 2856: case S_IFLNK: modestr[0] = 'l';
! 2857: linklen = readlink( name, link, sizeof(link) - 1 );
! 2858: if ( linklen != -1 )
! 2859: {
! 2860: link[linklen] = '\0';
! 2861: linkprefix = " -> ";
! 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>