root/libhttpd.c
/*DEFINITIONS
This source file includes following definitions.- my_realloc
- check_options
- free_httpd_server
- httpd_initialize
- initialize_listen_socket
- httpd_set_logfp
- httpd_terminate
- httpd_unlisten
- add_response
- httpd_write_response
- httpd_set_ndelay
- httpd_clear_ndelay
- send_mime
- httpd_realloc_str
- send_response
- send_response_tail
- defang
- httpd_send_err
- send_err_file
- send_authenticate
- b64_decode
- auth_check
- auth_check2
- send_dirredirect
- httpd_method_str
- hexit
- strdecode
- strencode
- tilde_map_1
- tilde_map_2
- vhost_map
- expand_symlinks
- httpd_init_conn_mem
- httpd_get_conn
- httpd_init_conn_content
- httpd_got_request
- httpd_parse_request
- bufgets
- de_dotdot
- httpd_close_conn
- httpd_destroy_conn
- ext_compare
- load_table
- init_mime
- figure_mime
- cgi_kill2
- cgi_kill
- name_compare
- ls_readable_fs
- ls
- build_env
- hostname_map
- make_envp
- make_argp
- cgi_interpose_input
- post_post_garbage_hack
- cgi_interpose_output
- cgi_child
- cgi
- httpd_start_request
- make_log_entry
- check_referrer
- really_check_referrer
- httpd_ntoa
- sockaddr_check
- sockaddr_len
- my_snprintf
- atoll
- httpd_read_fully
- httpd_write_fully
- 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 = " -> "; 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 }
/*