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