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 <string.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->file_address = (char*) 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->file_address != (char*) 0 ) 2463 { 2464 //mmc_unmap( hc->file_address, &(hc->sb), nowP ); 2465 hc->file_address = (char*) 0; 2466 } 2467 if ( hc->conn_fd >= 0 ) 2468 { 2469 (void) close( hc->conn_fd ); 2470 hc->conn_fd = -1; 2471 } 2472 } 2473 2474 void 2475 httpd_destroy_conn( httpd_conn* hc ) 2476 { 2477 if ( hc->initialized ) 2478 { 2479 free( (void*) hc->read_buf ); 2480 free( (void*) hc->decodedurl ); 2481 free( (void*) hc->origfilename ); 2482 free( (void*) hc->expnfilename ); 2483 free( (void*) hc->encodings ); 2484 free( (void*) hc->pathinfo ); 2485 free( (void*) hc->query ); 2486 free( (void*) hc->accept ); 2487 free( (void*) hc->accepte ); 2488 free( (void*) hc->reqhost ); 2489 free( (void*) hc->hostdir ); 2490 free( (void*) hc->remoteuser ); 2491 free( (void*) hc->response ); 2492 #ifdef TILDE_MAP_2 2493 free( (void*) hc->altdir ); 2494 #endif /* TILDE_MAP_2 */ 2495 hc->initialized = 0; 2496 } 2497 } 2498 2499 2500 struct mime_entry { 2501 char* ext; 2502 size_t ext_len; 2503 char* val; 2504 size_t val_len; 2505 }; 2506 static struct mime_entry enc_tab[] = { 2507 #include "mime_encodings.h" 2508 }; 2509 static const int n_enc_tab = sizeof(enc_tab) / sizeof(*enc_tab); 2510 static struct mime_entry typ_tab[] = { 2511 #include "mime_types.h" 2512 }; 2513 static const int n_typ_tab = sizeof(typ_tab) / sizeof(*typ_tab); 2514 2515 2516 /* qsort comparison routine - declared old-style on purpose, for portability. */ 2517 static int 2518 ext_compare( a, b ) 2519 struct mime_entry* a; 2520 struct mime_entry* b; 2521 { 2522 return strcmp( a->ext, b->ext ); 2523 } 2524 2525 2526 static void 2527 init_mime( void ) 2528 { 2529 int i; 2530 2531 /* Sort the tables so we can do binary search. */ 2532 qsort( enc_tab, n_enc_tab, sizeof(*enc_tab), ext_compare ); 2533 qsort( typ_tab, n_typ_tab, sizeof(*typ_tab), ext_compare ); 2534 2535 /* Fill in the lengths. */ 2536 for ( i = 0; i < n_enc_tab; ++i ) 2537 { 2538 enc_tab[i].ext_len = strlen( enc_tab[i].ext ); 2539 enc_tab[i].val_len = strlen( enc_tab[i].val ); 2540 } 2541 for ( i = 0; i < n_typ_tab; ++i ) 2542 { 2543 typ_tab[i].ext_len = strlen( typ_tab[i].ext ); 2544 typ_tab[i].val_len = strlen( typ_tab[i].val ); 2545 } 2546 2547 } 2548 2549 2550 /* Figure out MIME encodings and type based on the filename. Multiple 2551 ** encodings are separated by commas, and are listed in the order in 2552 ** which they were applied to the file. 2553 */ 2554 static void 2555 figure_mime( httpd_conn* hc ) 2556 { 2557 char* prev_dot; 2558 char* dot; 2559 char* ext; 2560 int me_indexes[100]; 2561 unsigned int n_me_indexes; 2562 size_t ext_len, encodings_len; 2563 int i, top, bot, mid; 2564 int r; 2565 char* default_type = "text/plain"; 2566 2567 /* Peel off encoding extensions until there aren't any more. */ 2568 n_me_indexes = 0; 2569 for ( prev_dot = &hc->expnfilename[strlen(hc->expnfilename)]; ; prev_dot = dot ) 2570 { 2571 for ( dot = prev_dot - 1; dot >= hc->expnfilename && *dot != '.'; --dot ) 2572 ; 2573 if ( dot < hc->expnfilename ) 2574 { 2575 /* No dot found. No more encoding extensions, and no type 2576 ** extension either. 2577 */ 2578 hc->type = default_type; 2579 goto done; 2580 } 2581 ext = dot + 1; 2582 ext_len = prev_dot - ext; 2583 /* Search the encodings table. Linear search is fine here, there 2584 ** are only a few entries. 2585 */ 2586 for ( i = 0; i < n_enc_tab; ++i ) 2587 { 2588 if ( ext_len == enc_tab[i].ext_len && strncasecmp( ext, enc_tab[i].ext, ext_len ) == 0 ) 2589 { 2590 if ( n_me_indexes < sizeof(me_indexes)/sizeof(*me_indexes) ) 2591 { 2592 me_indexes[n_me_indexes] = i; 2593 ++n_me_indexes; 2594 } 2595 goto next; 2596 } 2597 } 2598 /* No encoding extension found. Break and look for a type extension. */ 2599 break; 2600 2601 next: ; 2602 } 2603 2604 /* Binary search for a matching type extension. */ 2605 top = n_typ_tab - 1; 2606 bot = 0; 2607 while ( top >= bot ) 2608 { 2609 mid = ( top + bot ) / 2; 2610 r = strncasecmp( ext, typ_tab[mid].ext, ext_len ); 2611 if ( r < 0 ) 2612 top = mid - 1; 2613 else if ( r > 0 ) 2614 bot = mid + 1; 2615 else 2616 if ( ext_len < typ_tab[mid].ext_len ) 2617 top = mid - 1; 2618 else if ( ext_len > typ_tab[mid].ext_len ) 2619 bot = mid + 1; 2620 else 2621 { 2622 hc->type = typ_tab[mid].val; 2623 goto done; 2624 } 2625 } 2626 hc->type = default_type; 2627 2628 done: 2629 2630 /* The last thing we do is actually generate the mime-encoding header. */ 2631 hc->encodings[0] = '\0'; 2632 encodings_len = 0; 2633 for ( i = n_me_indexes - 1; i >= 0; --i ) 2634 { 2635 httpd_realloc_str( 2636 &hc->encodings, &hc->maxencodings, 2637 encodings_len + enc_tab[me_indexes[i]].val_len + 1 ); 2638 if ( hc->encodings[0] != '\0' ) 2639 { 2640 (void) strcpy( &hc->encodings[encodings_len], "," ); 2641 ++encodings_len; 2642 } 2643 (void) strcpy( &hc->encodings[encodings_len], enc_tab[me_indexes[i]].val ); 2644 encodings_len += enc_tab[me_indexes[i]].val_len; 2645 } 2646 2647 } 2648 2649 #ifdef GENERATE_INDEXES 2650 2651 /* qsort comparison routine - declared old-style on purpose, for portability. */ 2652 static int 2653 name_compare( a, b ) 2654 char** a; 2655 char** b; 2656 { 2657 return strcmp( *a, *b ); 2658 } 2659 2660 2661 static int 2662 ls( httpd_conn* hc ) 2663 { 2664 DIR* dirp; 2665 struct dirent* de; 2666 struct dirent* dep; 2667 int namlen; 2668 /*static*/int maxnames = 0; 2669 int nnames; 2670 /*static*/char* names = NULL; 2671 /*static*/char** nameptrs = NULL; 2672 /*static*/char* name; 2673 /*static*/size_t maxname = 0; 2674 /*static*/char* rname; 2675 /*static*/size_t maxrname = 0; 2676 /*static*/char* encrname; 2677 /*static*/size_t maxencrname = 0; 2678 FILE* fp; 2679 int i/*, r*/; 2680 struct stat sb; 2681 struct stat lsb; 2682 //char modestr[20]; 2683 char* linkprefix; 2684 char link[MAXPATHLEN+1]; 2685 int linklen; 2686 char* fileclass; 2687 time_t now; 2688 char timestr[26]; 2689 //ClientData client_data; 2690 2691 de = dep = malloc(sizeof(struct dirent)+B_FILE_NAME_LENGTH+1); 2692 if(de == 0) 2693 { 2694 httpd_send_err( hc, 501, err501title, "", err501form, hc->encodedurl ); 2695 return -1; 2696 } 2697 2698 dirp = opendir( hc->expnfilename ); 2699 if ( dirp == (DIR*) 0 ) 2700 { 2701 char logString[27+B_PATH_NAME_LENGTH+1]; 2702 sprintf(logString, "Error 404 File not found: %s\n", hc->decodedurl+1); 2703 poorman_log(logString, true, hc->client_addr.sa_in.sin_addr.s_addr, RED); 2704 // syslog( LOG_ERR, "opendir %.80s - %m", hc->expnfilename ); 2705 httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl ); 2706 free(de); 2707 return -1; 2708 } 2709 2710 if ( hc->method == METHOD_HEAD ) 2711 { 2712 closedir( dirp ); 2713 send_mime( 2714 hc, 200, ok200title, "", "", "text/html; charset=%s", (off_t) -1, 2715 hc->sb.st_mtime ); 2716 free(de); 2717 } 2718 else if ( hc->method == METHOD_GET ) 2719 { 2720 { 2721 char logString[B_FILE_NAME_LENGTH+B_PATH_NAME_LENGTH+51]; 2722 if(pthread_rwlock_rdlock(get_web_dir_lock()) == 0){ 2723 sprintf( 2724 logString, 2725 "Directory %s/%s/ has no ", 2726 hc->hs->cwd, 2727 hc->expnfilename 2728 ); 2729 pthread_rwlock_unlock(get_web_dir_lock()); 2730 } 2731 else 2732 strcpy(logString, "A web directory has no "); 2733 2734 if(pthread_rwlock_rdlock(get_index_name_lock()) == 0){ 2735 strcat(logString, hc->hs->index_name); 2736 pthread_rwlock_unlock(get_index_name_lock()); 2737 } 2738 else 2739 strcat(logString, "index file"); 2740 2741 strcat(logString, ". Sending directory listing.\n"); 2742 poorman_log(logString, true, hc->client_addr.sa_in.sin_addr.s_addr, BLACK); 2743 } 2744 2745 send_mime( 2746 hc, 200, ok200title, "", "", "text/html; charset=%s", 2747 (off_t) -1, hc->sb.st_mtime ); 2748 httpd_write_response( hc ); 2749 2750 /* Open a stdio stream so that we can use fprintf, which is more 2751 ** efficient than a bunch of separate write()s. We don't have 2752 ** to worry about double closes or file descriptor leaks cause 2753 ** we're in a subprocess. 2754 */ 2755 fp = fdopen( hc->conn_fd, "w" ); 2756 if ( fp == (FILE*) 0 ) 2757 { 2758 // syslog( LOG_ERR, "fdopen - %m" ); 2759 httpd_send_err( 2760 hc, 500, err500title, "", err500form, hc->encodedurl ); 2761 httpd_write_response( hc ); 2762 closedir( dirp ); 2763 free(de); 2764 return -1; 2765 } 2766 2767 (void) fprintf( fp, "\ 2768 <HTML>\n\ 2769 <HEAD><TITLE>Index of %.80s</TITLE></HEAD>\n\ 2770 <BODY BGCOLOR=\"#99cc99\" TEXT=\"#000000\" LINK=\"#2020ff\" VLINK=\"#4040cc\">\n\ 2771 <H2>Index of %.80s</H2>\n\ 2772 <PRE>\n\ 2773 bytes last-changed name\n\ 2774 <HR>", 2775 hc->decodedurl, hc->decodedurl ); 2776 2777 /* Read in names. */ 2778 nnames = 0; 2779 while ( readdir_r( dirp, de, &dep ) == 0 && dep != NULL) /* dirent or direct */ 2780 { 2781 if ( nnames >= maxnames ) 2782 { 2783 if ( maxnames == 0 ) 2784 { 2785 maxnames = 100; 2786 names = NEW( char, maxnames * ( MAXPATHLEN + 1 ) ); 2787 nameptrs = NEW( char*, maxnames ); 2788 } 2789 else 2790 { 2791 maxnames *= 2; 2792 names = RENEW( names, char, maxnames * ( MAXPATHLEN + 1 ) ); 2793 nameptrs = RENEW( nameptrs, char*, maxnames ); 2794 } 2795 if ( names == (char*) 0 || nameptrs == (char**) 0 ) 2796 { 2797 // syslog( LOG_ERR, "out of memory reallocating directory names" ); 2798 closedir( dirp ); 2799 return -1; 2800 } 2801 for ( i = 0; i < maxnames; ++i ) 2802 nameptrs[i] = &names[i * ( MAXPATHLEN + 1 )]; 2803 } 2804 namlen = NAMLEN(de); 2805 (void) strncpy( nameptrs[nnames], de->d_name, namlen ); 2806 nameptrs[nnames][namlen] = '\0'; 2807 ++nnames; 2808 }//while loop 2809 closedir( dirp ); 2810 free(de); 2811 2812 /* Sort the names. */ 2813 qsort( nameptrs, nnames, sizeof(*nameptrs), name_compare ); 2814 2815 /* Generate output. */ 2816 for ( i = 0; i < nnames; ++i ) 2817 { 2818 httpd_realloc_str( 2819 &name, &maxname, 2820 strlen( hc->expnfilename ) + 1 + strlen( nameptrs[i] ) ); 2821 httpd_realloc_str( 2822 &rname, &maxrname, 2823 strlen( hc->origfilename ) + 1 + strlen( nameptrs[i] ) ); 2824 if ( hc->expnfilename[0] == '\0' || 2825 strcmp( hc->expnfilename, "." ) == 0 ) 2826 { 2827 (void) strcpy( name, nameptrs[i] ); 2828 (void) strcpy( rname, nameptrs[i] ); 2829 } 2830 else 2831 { 2832 (void) my_snprintf( name, maxname, 2833 "%s/%s", hc->expnfilename, nameptrs[i] ); 2834 if ( strcmp( hc->origfilename, "." ) == 0 ) 2835 (void) my_snprintf( rname, maxrname, 2836 "%s", nameptrs[i] ); 2837 else 2838 (void) my_snprintf( rname, maxrname, 2839 "%s%s", hc->origfilename, nameptrs[i] ); 2840 } 2841 httpd_realloc_str( 2842 &encrname, &maxencrname, 3 * strlen( rname ) + 1 ); 2843 strencode( encrname, maxencrname, rname ); 2844 2845 if ( stat( name, &sb ) < 0 || lstat( name, &lsb ) < 0 ) 2846 continue; 2847 2848 linkprefix = ""; 2849 link[0] = '\0'; 2850 /* Break down mode word. First the file type. */ 2851 switch ( lsb.st_mode & S_IFMT ) 2852 { 2853 /*case S_IFIFO: modestr[0] = 'p'; break; 2854 case S_IFCHR: modestr[0] = 'c'; break; 2855 case S_IFDIR: modestr[0] = 'd'; break; 2856 case S_IFBLK: modestr[0] = 'b'; break; 2857 case S_IFREG: modestr[0] = '-'; break; 2858 case S_IFSOCK: modestr[0] = 's'; break;*/ 2859 case S_IFLNK: //modestr[0] = 'l'; 2860 linklen = readlink( name, link, sizeof(link) - 1 ); 2861 if ( linklen != -1 ) 2862 { 2863 link[linklen] = '\0'; 2864 linkprefix = " -> "; 2865 } 2866 break; 2867 //default: modestr[0] = '?'; break; 2868 } 2869 /* Now the world permissions. Owner and group permissions 2870 ** are not of interest to web clients. 2871 */ 2872 /*modestr[1] = ( lsb.st_mode & S_IROTH ) ? 'r' : '-'; 2873 modestr[2] = ( lsb.st_mode & S_IWOTH ) ? 'w' : '-'; 2874 modestr[3] = ( lsb.st_mode & S_IXOTH ) ? 'x' : '-'; 2875 modestr[4] = '\0';*/ 2876 2877 /* We also leave out the owner and group name, they are 2878 ** also not of interest to web clients. Plus if we're 2879 ** running under chroot(), they would require a copy 2880 ** of /etc/passwd and /etc/group, which we want to avoid. 2881 */ 2882 2883 /* Get time string. */ 2884 now = time( (time_t*) 0 ); 2885 ctime_r( &lsb.st_mtime, timestr ); 2886 timestr[ 0] = timestr[ 4]; 2887 timestr[ 1] = timestr[ 5]; 2888 timestr[ 2] = timestr[ 6]; 2889 timestr[ 3] = ' '; 2890 timestr[ 4] = timestr[ 8]; 2891 timestr[ 5] = timestr[ 9]; 2892 timestr[ 6] = ' '; 2893 if ( now - lsb.st_mtime > 60*60*24*182 ) /* 1/2 year */ 2894 { 2895 timestr[ 7] = ' '; 2896 timestr[ 8] = timestr[20]; 2897 timestr[ 9] = timestr[21]; 2898 timestr[10] = timestr[22]; 2899 timestr[11] = timestr[23]; 2900 } 2901 else 2902 { 2903 timestr[ 7] = timestr[11]; 2904 timestr[ 8] = timestr[12]; 2905 timestr[ 9] = ':'; 2906 timestr[10] = timestr[14]; 2907 timestr[11] = timestr[15]; 2908 } 2909 timestr[12] = '\0'; 2910 2911 /* The ls -F file class. */ 2912 switch ( sb.st_mode & S_IFMT ) 2913 { 2914 case S_IFDIR: fileclass = "/"; break; 2915 //case S_IFSOCK: fileclass = "="; break; 2916 //case S_IFLNK: fileclass = "@"; break; 2917 default: 2918 fileclass = "";//( sb.st_mode & S_IXOTH ) ? "*" : ""; 2919 break; 2920 } 2921 2922 /* And print. */ 2923 (void) fprintf( fp, 2924 "%10" B_PRId64 " %s <A HREF=\"/%.500s%s\">%.80s</A>%s%s%s\n", 2925 (int64_t) lsb.st_size, 2926 timestr, encrname, S_ISDIR(sb.st_mode) ? "/" : "", 2927 nameptrs[i], linkprefix, link, fileclass ); 2928 }//for loop 2929 2930 (void) fprintf( fp, "</PRE></BODY>\n</HTML>\n" ); 2931 (void) fclose( fp ); 2932 2933 hc->status = 200; 2934 hc->bytes_sent = CGI_BYTECOUNT; 2935 hc->should_linger = 0; 2936 2937 free(names); 2938 free(nameptrs); 2939 free(name); 2940 free(rname); 2941 free(encrname); 2942 }//else if ( hc->method == METHOD_GET ) 2943 else 2944 { 2945 closedir( dirp ); 2946 httpd_send_err( 2947 hc, 501, err501title, "", err501form, httpd_method_str( hc->method ) ); 2948 free(de); 2949 return -1; 2950 } 2951 return 0; 2952 } 2953 2954 #endif /* GENERATE_INDEXES */ 2955 2956 2957 //static char* 2958 //build_env( char* fmt, char* arg ) 2959 // { 2960 // char* cp; 2961 // size_t size; 2962 // static char* buf; 2963 // static size_t maxbuf = 0; 2964 // 2965 // size = strlen( fmt ) + strlen( arg ); 2966 // if ( size > maxbuf ) 2967 // httpd_realloc_str( &buf, &maxbuf, size ); 2968 // (void) my_snprintf( buf, maxbuf, fmt, arg ); 2969 // cp = strdup( buf ); 2970 // if ( cp == (char*) 0 ) 2971 // { 2972 // syslog( LOG_ERR, "out of memory copying environment variable" ); 2973 // exit( 1 ); 2974 // } 2975 // return cp; 2976 // } 2977 2978 2979 #ifdef SERVER_NAME_LIST 2980 static char* 2981 hostname_map( char* hostname ) 2982 { 2983 int len, n; 2984 static char* list[] = { SERVER_NAME_LIST }; 2985 2986 len = strlen( hostname ); 2987 for ( n = sizeof(list) / sizeof(*list) - 1; n >= 0; --n ) 2988 if ( strncasecmp( hostname, list[n], len ) == 0 ) 2989 if ( list[n][len] == '/' ) /* check in case of a substring match */ 2990 return &list[n][len + 1]; 2991 return (char*) 0; 2992 } 2993 #endif /* SERVER_NAME_LIST */ 2994 2995 2996 static int 2997 really_start_request( httpd_conn* hc, struct timeval* nowP ) 2998 { 2999 /*static*/ char* indexname; 3000 /*static*/ size_t maxindexname = 0; 3001 //static const char* index_names[]; 3002 //int i; 3003 #ifdef AUTH_FILE 3004 static char* dirname; 3005 static size_t maxdirname = 0; 3006 #endif /* AUTH_FILE */ 3007 size_t expnlen, indxlen; 3008 char* cp; 3009 char* pi; 3010 char* freethis; 3011 3012 expnlen = strlen( hc->expnfilename ); 3013 3014 if ( hc->method != METHOD_GET && hc->method != METHOD_HEAD /*&& 3015 hc->method != METHOD_POST*/ ) 3016 { 3017 httpd_send_err( 3018 hc, 501, err501title, "", err501form, httpd_method_str( hc->method ) ); 3019 return -1; 3020 } 3021 3022 /* Stat the file. */ 3023 if ( stat( hc->expnfilename, &hc->sb ) < 0 ) 3024 { 3025 httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); 3026 return -1; 3027 } 3028 3029 /* Is it world-readable or world-executable? We check explicitly instead 3030 ** of just trying to open it, so that no one ever gets surprised by 3031 ** a file that's not set world-readable and yet somehow is 3032 ** readable by the HTTP server and therefore the *whole* world. 3033 */ 3034 if ( ! ( hc->sb.st_mode & ( S_IROTH /*| S_IXOTH*/ ) ) ) 3035 { 3036 // syslog( 3037 // LOG_INFO, 3038 // "%.80s URL \"%.80s\" resolves to a non world-readable file", 3039 // httpd_ntoa( &hc->client_addr ), hc->encodedurl ); 3040 httpd_send_err( 3041 hc, 403, err403title, "", 3042 ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file that is not world-readable.\n" ), 3043 hc->encodedurl ); 3044 return -1; 3045 } 3046 3047 /* Is it a directory? */ 3048 if ( S_ISDIR(hc->sb.st_mode) ) 3049 { 3050 /* If there's pathinfo, it's just a non-existent file. */ 3051 if ( hc->pathinfo[0] != '\0' ) 3052 { 3053 char logString[27+B_PATH_NAME_LENGTH+1]; 3054 sprintf(logString, "Error 404 File not found: %s\n", hc->decodedurl+1); 3055 poorman_log(logString, true, hc->client_addr.sa_in.sin_addr.s_addr, RED); 3056 httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl ); 3057 return -1; 3058 } 3059 3060 /* Special handling for directory URLs that don't end in a slash. 3061 ** We send back an explicit redirect with the slash, because 3062 ** otherwise many clients can't build relative URLs properly. 3063 */ 3064 if ( strcmp( hc->origfilename, "" ) != 0 && 3065 strcmp( hc->origfilename, "." ) != 0 && 3066 hc->origfilename[strlen( hc->origfilename ) - 1] != '/' ) 3067 { 3068 send_dirredirect( hc ); 3069 return -1; 3070 } 3071 3072 /* Check for an index file. */ 3073 if(pthread_rwlock_rdlock(get_index_name_lock()) == 0){ 3074 httpd_realloc_str( 3075 &indexname, &maxindexname, 3076 expnlen + 1 + strlen( /*index_names[i]*/hc->hs->index_name ) ); 3077 (void) strcpy( indexname, hc->expnfilename ); 3078 indxlen = strlen( indexname ); 3079 if ( indxlen == 0 || indexname[indxlen - 1] != '/' ) 3080 (void) strcat( indexname, "/" ); 3081 if ( strcmp( indexname, "./" ) == 0 ) 3082 indexname[0] = '\0'; 3083 (void) strcat( indexname, /*index_names[i]*/hc->hs->index_name ); 3084 pthread_rwlock_unlock(get_index_name_lock()); 3085 } 3086 else{ 3087 httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); 3088 return -1; 3089 } 3090 if ( stat( indexname, &hc->sb ) >= 0 ) 3091 goto got_one; 3092 3093 free(indexname); 3094 /* Nope, no index file, so it's an actual directory request. */ 3095 #ifdef GENERATE_INDEXES 3096 if(hc->hs->do_list_dir){ 3097 /* Directories must be readable for indexing. */ 3098 if ( ! ( hc->sb.st_mode & S_IROTH ) ) 3099 { 3100 // syslog( 3101 // LOG_INFO, 3102 // "%.80s URL \"%.80s\" tried to index a directory with indexing disabled", 3103 // httpd_ntoa( &hc->client_addr ), hc->encodedurl ); 3104 httpd_send_err( 3105 hc, 403, err403title, "", 3106 ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a directory that has indexing disabled.\n" ), 3107 hc->encodedurl ); 3108 return -1; 3109 } 3110 #ifdef AUTH_FILE 3111 /* Check authorization for this directory. */ 3112 if ( auth_check( hc, hc->expnfilename ) == -1 ) 3113 return -1; 3114 #endif /* AUTH_FILE */ 3115 /* Referer check. */ 3116 if ( ! check_referer( hc ) ) 3117 return -1; 3118 /* Ok, generate an index. */ 3119 return ls( hc ); 3120 //#else /* GENERATE_INDEXES */ 3121 } else { 3122 // syslog( 3123 // LOG_INFO, "%.80s URL \"%.80s\" tried to index a directory", 3124 // httpd_ntoa( &hc->client_addr ), hc->encodedurl ); 3125 httpd_send_err( 3126 hc, 404, err404title, "", 3127 err404form, 3128 hc->encodedurl ); 3129 return -1; 3130 } 3131 #endif /* GENERATE_INDEXES */ 3132 3133 got_one: ; 3134 /* Got an index file. Expand symlinks again. More pathinfo means 3135 ** something went wrong. 3136 */ 3137 cp = expand_symlinks( indexname, &freethis, &pi, hc->hs->no_symlink_check, hc->tildemapped ); 3138 if ( cp == (char*) 0 || pi[0] != '\0' ) 3139 { 3140 httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); 3141 return -1; 3142 } 3143 free(indexname); 3144 free(freethis); 3145 expnlen = strlen( cp ); 3146 httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, expnlen ); 3147 (void) strcpy( hc->expnfilename, cp ); 3148 free(cp); 3149 3150 /* Now, is the index version world-readable or world-executable? */ 3151 if ( ! ( hc->sb.st_mode & ( S_IROTH | S_IXOTH ) ) ) 3152 { 3153 // syslog( 3154 // LOG_INFO, 3155 // "%.80s URL \"%.80s\" resolves to a non-world-readable index file", 3156 // httpd_ntoa( &hc->client_addr ), hc->encodedurl ); 3157 httpd_send_err( 3158 hc, 403, err403title, "", 3159 ERROR_FORM( err403form, "The requested URL '%.80s' resolves to an index file that is not world-readable.\n" ), 3160 hc->encodedurl ); 3161 return -1; 3162 } 3163 }/* Is it a directory? */ 3164 3165 #ifdef AUTH_FILE 3166 /* Check authorization for this directory. */ 3167 httpd_realloc_str( &dirname, &maxdirname, expnlen ); 3168 (void) strcpy( dirname, hc->expnfilename ); 3169 cp = strrchr( dirname, '/' ); 3170 if ( cp == (char*) 0 ) 3171 (void) strcpy( dirname, "." ); 3172 else 3173 *cp = '\0'; 3174 if ( auth_check( hc, dirname ) == -1 ) 3175 return -1; 3176 3177 /* Check if the filename is the AUTH_FILE itself - that's verboten. */ 3178 if ( expnlen == sizeof(AUTH_FILE) - 1 ) 3179 { 3180 if ( strcmp( hc->expnfilename, AUTH_FILE ) == 0 ) 3181 { 3182 // syslog( 3183 // LOG_NOTICE, 3184 // "%.80s URL \"%.80s\" tried to retrieve an auth file", 3185 // httpd_ntoa( &hc->client_addr ), hc->encodedurl ); 3186 httpd_send_err( 3187 hc, 403, err403title, "", 3188 ERROR_FORM( err403form, "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n" ), 3189 hc->encodedurl ); 3190 return -1; 3191 } 3192 } 3193 else if ( expnlen >= sizeof(AUTH_FILE) && 3194 strcmp( &(hc->expnfilename[expnlen - sizeof(AUTH_FILE) + 1]), AUTH_FILE ) == 0 && 3195 hc->expnfilename[expnlen - sizeof(AUTH_FILE)] == '/' ) 3196 { 3197 // syslog( 3198 // LOG_NOTICE, 3199 // "%.80s URL \"%.80s\" tried to retrieve an auth file", 3200 // httpd_ntoa( &hc->client_addr ), hc->encodedurl ); 3201 httpd_send_err( 3202 hc, 403, err403title, "", 3203 ERROR_FORM( err403form, "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n" ), 3204 hc->encodedurl ); 3205 return -1; 3206 } 3207 #endif /* AUTH_FILE */ 3208 3209 /* Referer check. */ 3210 if ( ! check_referer( hc ) ) 3211 return -1; 3212 3213 /* Is it world-executable and in the CGI area? */ 3214 /*if ( hc->hs->cgi_pattern != (char*) 0 && 3215 ( hc->sb.st_mode & S_IXOTH ) && 3216 match( hc->hs->cgi_pattern, hc->expnfilename ) ) 3217 return cgi( hc );*/ 3218 3219 /* It's not CGI. If it's executable or there's pathinfo, someone's 3220 ** trying to either serve or run a non-CGI file as CGI. Either case 3221 ** is prohibited. 3222 */ 3223 /*if ( hc->sb.st_mode & S_IXOTH ) 3224 { 3225 syslog( 3226 LOG_NOTICE, "%.80s URL \"%.80s\" is executable but isn't CGI", 3227 httpd_ntoa( &hc->client_addr ), hc->encodedurl ); 3228 httpd_send_err( 3229 hc, 403, err403title, "", 3230 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" ), 3231 hc->encodedurl ); 3232 return -1; 3233 }*/ 3234 if ( hc->pathinfo[0] != '\0' ) 3235 { 3236 // syslog( 3237 // LOG_INFO, "%.80s URL \"%.80s\" has pathinfo but isn't CGI", 3238 // httpd_ntoa( &hc->client_addr ), hc->encodedurl ); 3239 httpd_send_err( 3240 hc, 403, err403title, "", 3241 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" ), 3242 hc->encodedurl ); 3243 return -1; 3244 } 3245 3246 /* Fill in last_byte_index, if necessary. */ 3247 if ( hc->got_range && 3248 ( hc->last_byte_index == -1 || hc->last_byte_index >= hc->sb.st_size ) ) 3249 hc->last_byte_index = hc->sb.st_size - 1; 3250 3251 figure_mime( hc ); 3252 3253 if ( hc->method == METHOD_HEAD ) 3254 { 3255 send_mime( 3256 hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size, 3257 hc->sb.st_mtime ); 3258 } 3259 else if ( hc->if_modified_since != (time_t) -1 && 3260 hc->if_modified_since >= hc->sb.st_mtime ) 3261 { 3262 send_mime( 3263 hc, 304, err304title, hc->encodings, "", hc->type, (off_t) -1, 3264 hc->sb.st_mtime ); 3265 } 3266 else 3267 { 3268 hc->file_address = (char*)1; 3269 send_mime( 3270 hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size, 3271 hc->sb.st_mtime ); 3272 } 3273 3274 return 0; 3275 } 3276 3277 3278 int 3279 httpd_start_request( httpd_conn* hc, struct timeval* nowP ) 3280 { 3281 int r; 3282 3283 /* Really start the request. */ 3284 r = really_start_request( hc, nowP ); 3285 3286 /* And return the status. */ 3287 return r; 3288 } 3289 3290 3291 static void 3292 make_log_entry( httpd_conn* hc, struct timeval* nowP ) 3293 { 3294 char* ru; 3295 char url[305]; 3296 char bytes[40]; 3297 3298 if ( hc->hs->no_log ) 3299 return; 3300 3301 /* This is straight CERN Combined Log Format - the only tweak 3302 ** being that if we're using syslog() we leave out the date, because 3303 ** syslogd puts it in. The included syslogtocern script turns the 3304 ** results into true CERN format. 3305 */ 3306 3307 /* Format remote user. */ 3308 if ( hc->remoteuser[0] != '\0' ) 3309 ru = hc->remoteuser; 3310 else 3311 ru = "-"; 3312 /* If we're vhosting, prepend the hostname to the url. This is 3313 ** a little weird, perhaps writing separate log files for 3314 ** each vhost would make more sense. 3315 */ 3316 if ( hc->hs->vhost && ! hc->tildemapped ) 3317 (void) my_snprintf( url, sizeof(url), 3318 "/%.100s%.200s", 3319 hc->hostname == (char*) 0 ? hc->hs->server_hostname : hc->hostname, 3320 hc->encodedurl ); 3321 else 3322 (void) my_snprintf( url, sizeof(url), 3323 "%.200s", hc->encodedurl ); 3324 /* Format the bytes. */ 3325 if ( hc->bytes_sent >= 0 ) 3326 (void) my_snprintf( 3327 bytes, sizeof(bytes), "%lld", (int64_t) hc->bytes_sent ); 3328 else 3329 (void) strcpy( bytes, "-" ); 3330 3331 /* Logfile or syslog? */ 3332 if ( hc->hs->logfp != (FILE*) 0 ) 3333 { 3334 time_t now; 3335 struct tm* t; 3336 const char* cernfmt_nozone = "%d/%b/%Y:%H:%M:%S"; 3337 char date_nozone[100]; 3338 int zone; 3339 char sign; 3340 char date[100]; 3341 3342 /* Get the current time, if necessary. */ 3343 if ( nowP != (struct timeval*) 0 ) 3344 now = nowP->tv_sec; 3345 else 3346 now = time( (time_t*) 0 ); 3347 /* Format the time, forcing a numeric timezone (some log analyzers 3348 ** are stoooopid about this). 3349 */ 3350 t = localtime( &now ); 3351 (void) strftime( date_nozone, sizeof(date_nozone), cernfmt_nozone, t ); 3352 #ifdef HAVE_TM_GMTOFF 3353 zone = t->tm_gmtoff / 60L; 3354 #else 3355 zone = -timezone / 60L; 3356 /* Probably have to add something about daylight time here. */ 3357 #endif 3358 if ( zone >= 0 ) 3359 sign = '+'; 3360 else 3361 { 3362 sign = '-'; 3363 zone = -zone; 3364 } 3365 zone = ( zone / 60 ) * 100 + zone % 60; 3366 (void) my_snprintf( date, sizeof(date), 3367 "%s %c%04d", date_nozone, sign, zone ); 3368 /* And write the log entry. */ 3369 (void) fprintf( hc->hs->logfp, 3370 "%.80s - %.80s [%s] \"%.80s %.300s %.80s\" %d %s \"%.200s\" \"%.200s\"\n", 3371 httpd_ntoa( &hc->client_addr ), ru, date, 3372 httpd_method_str( hc->method ), url, hc->protocol, 3373 hc->status, bytes, hc->referer, hc->useragent ); 3374 #ifdef FLUSH_LOG_EVERY_TIME 3375 (void) fflush( hc->hs->logfp ); 3376 #endif 3377 } 3378 else 3379 /*syslog( LOG_INFO, 3380 "%.80s - %.80s \"%.80s %.200s %.80s\" %d %s \"%.200s\" \"%.200s\"", 3381 httpd_ntoa( &hc->client_addr ), ru, 3382 httpd_method_str( hc->method ), url, hc->protocol, 3383 hc->status, bytes, hc->referer, hc->useragent )*/; 3384 } 3385 3386 3387 /* Returns 1 if ok to serve the url, 0 if not. */ 3388 static int 3389 check_referer( httpd_conn* hc ) 3390 { 3391 int r; 3392 char* cp; 3393 3394 /* Are we doing referer checking at all? */ 3395 if ( hc->hs->url_pattern == (char*) 0 ) 3396 return 1; 3397 3398 r = really_check_referer( hc ); 3399 3400 if ( ! r ) 3401 { 3402 if ( hc->hs->vhost && hc->hostname != (char*) 0 ) 3403 cp = hc->hostname; 3404 else 3405 cp = hc->hs->server_hostname; 3406 if ( cp == (char*) 0 ) 3407 cp = ""; 3408 // syslog( 3409 // LOG_INFO, "%.80s non-local referer \"%.80s%.80s\" \"%.80s\"", 3410 // httpd_ntoa( &hc->client_addr ), cp, hc->encodedurl, hc->referer ); 3411 httpd_send_err( 3412 hc, 403, err403title, "", 3413 ERROR_FORM( err403form, "You must supply a local referer to get URL '%.80s' from this server.\n" ), 3414 hc->encodedurl ); 3415 } 3416 return r; 3417 } 3418 3419 3420 /* Returns 1 if ok to serve the url, 0 if not. */ 3421 static int 3422 really_check_referer( httpd_conn* hc ) 3423 { 3424 httpd_server* hs; 3425 char* cp1; 3426 char* cp2; 3427 char* cp3; 3428 /*static*/char* refhost = (char*) 0; 3429 /*static*/size_t refhost_size = 0; 3430 char *lp; 3431 3432 hs = hc->hs; 3433 3434 /* Check for an empty referer. */ 3435 if ( hc->referer == (char*) 0 || hc->referer[0] == '\0' || 3436 ( cp1 = strstr( hc->referer, "//" ) ) == (char*) 0 ) 3437 { 3438 /* Disallow if we require a referer and the url matches. */ 3439 if ( hs->no_empty_referers && match( hs->url_pattern, hc->origfilename ) ) 3440 return 0; 3441 /* Otherwise ok. */ 3442 return 1; 3443 } 3444 3445 /* Extract referer host. */ 3446 cp1 += 2; 3447 for ( cp2 = cp1; *cp2 != '/' && *cp2 != ':' && *cp2 != '\0'; ++cp2 ) 3448 continue; 3449 httpd_realloc_str( &refhost, &refhost_size, cp2 - cp1 ); 3450 for ( cp3 = refhost; cp1 < cp2; ++cp1, ++cp3 ) 3451 if ( isupper(*cp1) ) 3452 *cp3 = tolower(*cp1); 3453 else 3454 *cp3 = *cp1; 3455 *cp3 = '\0'; 3456 3457 /* Local pattern? */ 3458 if ( hs->local_pattern != (char*) 0 ) 3459 lp = hs->local_pattern; 3460 else 3461 { 3462 /* No local pattern. What's our hostname? */ 3463 if ( ! hs->vhost ) 3464 { 3465 /* Not vhosting, use the server name. */ 3466 lp = hs->server_hostname; 3467 if ( lp == (char*) 0 ) 3468 { 3469 /* Couldn't figure out local hostname - give up. */ 3470 free(refhost); 3471 return 1; 3472 } 3473 } 3474 else 3475 { 3476 /* We are vhosting, use the hostname on this connection. */ 3477 lp = hc->hostname; 3478 if ( lp == (char*) 0 ) 3479 { 3480 /* Oops, no hostname. Maybe it's an old browser that 3481 ** doesn't send a Host: header. We could figure out 3482 ** the default hostname for this IP address, but it's 3483 ** not worth it for the few requests like this. 3484 */ 3485 free(refhost); 3486 return 1; 3487 } 3488 } 3489 } 3490 3491 /* If the referer host doesn't match the local host pattern, and 3492 ** the filename does match the url pattern, it's an illegal reference. 3493 */ 3494 if ( ! match( lp, refhost ) && match( hs->url_pattern, hc->origfilename ) ) 3495 { 3496 free(refhost); 3497 return 0; 3498 } 3499 /* Otherwise ok. */ 3500 free(refhost); 3501 return 1; 3502 } 3503 3504 3505 char* 3506 httpd_ntoa( httpd_sockaddr* saP ) 3507 { 3508 #ifdef USE_IPV6 3509 static char str[200]; 3510 3511 if ( getnameinfo( &saP->sa, sockaddr_len( saP ), str, sizeof(str), 0, 0, NI_NUMERICHOST ) != 0 ) 3512 { 3513 str[0] = '?'; 3514 str[1] = '\0'; 3515 } 3516 else if ( IN6_IS_ADDR_V4MAPPED( &saP->sa_in6.sin6_addr ) && strncmp( str, "::ffff:", 7 ) == 0 ) 3517 /* Elide IPv6ish prefix for IPv4 addresses. */ 3518 (void) strcpy( str, &str[7] ); 3519 3520 return str; 3521 3522 #else /* USE_IPV6 */ 3523 3524 return inet_ntoa( saP->sa_in.sin_addr ); 3525 3526 #endif /* USE_IPV6 */ 3527 } 3528 3529 3530 static int 3531 sockaddr_check( httpd_sockaddr* saP ) 3532 { 3533 switch ( saP->sa.sa_family ) 3534 { 3535 case AF_INET: return 1; 3536 #ifdef USE_IPV6 3537 case AF_INET6: return 1; 3538 #endif /* USE_IPV6 */ 3539 default: 3540 return 0; 3541 } 3542 } 3543 3544 3545 static size_t 3546 sockaddr_len( httpd_sockaddr* saP ) 3547 { 3548 switch ( saP->sa.sa_family ) 3549 { 3550 case AF_INET: return sizeof(struct sockaddr_in); 3551 #ifdef USE_IPV6 3552 case AF_INET6: return sizeof(struct sockaddr_in6); 3553 #endif /* USE_IPV6 */ 3554 default: 3555 return 0; /* shouldn't happen */ 3556 } 3557 } 3558 3559 3560 /* Some systems don't have snprintf(), so we make our own that uses 3561 ** either vsnprintf() or vsprintf(). If your system doesn't have 3562 ** vsnprintf(), it is probably vulnerable to buffer overruns. 3563 ** Upgrade! 3564 */ 3565 static int 3566 my_snprintf( char* str, size_t size, const char* format, ... ) 3567 { 3568 va_list ap; 3569 int r; 3570 3571 va_start( ap, format ); 3572 #ifdef HAVE_VSNPRINTF 3573 r = vsnprintf( str, size, format, ap ); 3574 #else /* HAVE_VSNPRINTF */ 3575 r = vsprintf( str, format, ap ); 3576 #endif /* HAVE_VSNPRINTF */ 3577 va_end( ap ); 3578 return r; 3579 } 3580 3581 3582 #ifndef HAVE_ATOLL 3583 static long long 3584 atoll( const char* str ) 3585 { 3586 long long value; 3587 long long sign; 3588 3589 while ( isspace( *str ) ) 3590 ++str; 3591 switch ( *str ) 3592 { 3593 case '-': sign = -1; ++str; break; 3594 case '+': sign = 1; ++str; break; 3595 default: sign = 1; break; 3596 } 3597 value = 0; 3598 while ( isdigit( *str ) ) 3599 { 3600 value = value * 10 + ( *str - '0' ); 3601 ++str; 3602 } 3603 return sign * value; 3604 } 3605 #endif /* HAVE_ATOLL */ 3606 3607 3608 /* Read the requested buffer completely, accounting for interruptions. */ 3609 int 3610 httpd_read_fully( int fd, void* buf, size_t nbytes ) 3611 { 3612 size_t nread; 3613 3614 nread = 0; 3615 while ( nread < nbytes ) 3616 { 3617 int r; 3618 3619 r = read( fd, (char*) buf + nread, nbytes - nread ); 3620 if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) ) 3621 { 3622 sleep( 1 ); 3623 continue; 3624 } 3625 if ( r < 0 ) 3626 return r; 3627 if ( r == 0 ) 3628 break; 3629 nread += r; 3630 } 3631 3632 return nread; 3633 } 3634 3635 3636 /* Write the requested buffer completely, accounting for interruptions. */ 3637 int 3638 httpd_write_fully( int fd, const void* buf, size_t nbytes ) 3639 { 3640 size_t nwritten; 3641 3642 nwritten = 0; 3643 while ( nwritten < nbytes ) 3644 { 3645 int r; 3646 3647 r = write( fd, (char*) buf + nwritten, nbytes - nwritten ); 3648 if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) ) 3649 { 3650 sleep( 1 ); 3651 continue; 3652 } 3653 if ( r < 0 ) 3654 return r; 3655 if ( r == 0 ) 3656 break; 3657 nwritten += r; 3658 } 3659 3660 return nwritten; 3661 } 3662 3663 3664 /* Generate debugging statistics syslog message. */ 3665 void 3666 httpd_logstats( long secs ) 3667 { 3668 if ( str_alloc_count > 0 ) 3669 /*syslog( LOG_INFO, 3670 " libhttpd - %d strings allocated, %lu bytes (%g bytes/str)", 3671 str_alloc_count, (unsigned long) str_alloc_size, 3672 (float) str_alloc_size / str_alloc_count )*/; 3673 } 3674