root/libhttpd.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. my_realloc
  2. check_options
  3. free_httpd_server
  4. httpd_initialize
  5. initialize_listen_socket
  6. httpd_set_logfp
  7. httpd_terminate
  8. httpd_unlisten
  9. add_response
  10. httpd_write_response
  11. httpd_set_ndelay
  12. httpd_clear_ndelay
  13. send_mime
  14. httpd_realloc_str
  15. send_response
  16. send_response_tail
  17. defang
  18. httpd_send_err
  19. send_err_file
  20. send_authenticate
  21. b64_decode
  22. auth_check
  23. auth_check2
  24. send_dirredirect
  25. httpd_method_str
  26. hexit
  27. strdecode
  28. strencode
  29. tilde_map_1
  30. tilde_map_2
  31. vhost_map
  32. expand_symlinks
  33. httpd_init_conn_mem
  34. httpd_get_conn
  35. httpd_init_conn_content
  36. httpd_got_request
  37. httpd_parse_request
  38. bufgets
  39. de_dotdot
  40. httpd_close_conn
  41. httpd_destroy_conn
  42. ext_compare
  43. load_table
  44. init_mime
  45. figure_mime
  46. cgi_kill2
  47. cgi_kill
  48. name_compare
  49. ls_readable_fs
  50. ls
  51. build_env
  52. hostname_map
  53. make_envp
  54. make_argp
  55. cgi_interpose_input
  56. post_post_garbage_hack
  57. cgi_interpose_output
  58. cgi_child
  59. cgi
  60. httpd_start_request
  61. make_log_entry
  62. check_referrer
  63. really_check_referrer
  64. httpd_ntoa
  65. sockaddr_check
  66. sockaddr_len
  67. my_snprintf
  68. atoll
  69. httpd_read_fully
  70. httpd_write_fully
  71. httpd_logstats

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

/* [previous][next][first][last][top][bottom][index][help] */