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