1 /* libhttpd.c - HTTP protocol library
3 ** Copyright © 1995,1998,1999,2000,2001 by Jef Poskanzer <jef@mail.acme.com>.
4 ** All rights reserved.
6 ** Redistribution and use in source and binary forms, with or without
7 ** modification, are permitted provided that the following conditions
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.
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
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 */
38 #include <sys/types.h>
39 #include <sys/param.h>
48 #endif /* HAVE_MEMORY_H */
58 #include <StorageDefs.h>
59 #include <GraphicsDefs.h>
61 #ifdef HAVE_OSRELDATE_H
62 #include <osreldate.h>
63 #endif /* HAVE_OSRELDATE_H */
67 # define NAMLEN(dirent) strlen((dirent)->d_name)
69 # define dirent direct
70 # define NAMLEN(dirent) (dirent)->d_namlen
71 # ifdef HAVE_SYS_NDIR_H
72 # include <sys/ndir.h>
74 # ifdef HAVE_SYS_DIR_H
82 extern char* crypt( const char* key
, const char* setting
);
88 #include "tdate_parse.h"
89 #include "../PoorManServer.h"
90 #include "../PoorManLogger.h"
93 #define STDIN_FILENO 0
96 #define STDOUT_FILENO 1
99 #define STDERR_FILENO 2
107 typedef long long int64_t;
110 #ifndef HAVE_SOCKLENT
111 typedef int socklen_t
;
115 #define timezone _timezone
119 #define MAX(a,b) ((a) > (b) ? (a) : (b))
122 #define MIN(a,b) ((a) < (b) ? (a) : (b))
127 #define O_NDELAY O_NONBLOCK
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
);
143 static int send_err_file( httpd_conn
* hc
, int status
, char* title
, char* extraheads
, char* filename
);
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 */
158 static int tilde_map_1( httpd_conn
* hc
);
159 #endif /* TILDE_MAP_1 */
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
);
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
, ... );
188 static long long atoll( const char* str
);
189 #endif /* HAVE_ATOLL */
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.
200 //static int sub_process = 0;
203 check_options( void )
205 #if defined(TILDE_MAP_1) && defined(TILDE_MAP_2)
206 // syslog( LOG_CRIT, "both TILDE_MAP_1 and TILDE_MAP_2 are defined" );
213 free_httpd_server( httpd_server
* hs
)
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
);
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
)
242 static char ghnbuf
[256];
247 hs
= NEW( httpd_server
, 1 );
248 if ( hs
== (httpd_server
*) 0 )
250 // syslog( LOG_CRIT, "out of memory allocating an httpd_server" );
251 return (httpd_server
*) 0;
254 if ( hostname
!= (char*) 0 )
256 hs
->binding_hostname
= strdup( hostname
);
257 if ( hs
->binding_hostname
== (char*) 0 )
259 // syslog( LOG_CRIT, "out of memory copying hostname" );
260 return (httpd_server
*) 0;
262 hs
->server_hostname
= hs
->binding_hostname
;
266 hs
->binding_hostname
= (char*) 0;
267 hs
->server_hostname
= (char*) 0;
268 if ( gethostname( ghnbuf
, sizeof(ghnbuf
) ) < 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 )
277 hs
->server_hostname
= SERVER_NAME
;
278 #else /* SERVER_NAME */
279 if ( ghnbuf
[0] != '\0' )
280 hs
->server_hostname
= ghnbuf
;
281 #endif /* SERVER_NAME */
286 if ( cgi_pattern
== (char*) 0 )
287 hs
->cgi_pattern
= (char*) 0;
290 /* Nuke any leading slashes. */
291 if ( cgi_pattern
[0] == '/' )
293 hs
->cgi_pattern
= strdup( cgi_pattern
);
294 if ( hs
->cgi_pattern
== (char*) 0 )
296 // syslog( LOG_CRIT, "out of memory copying cgi_pattern" );
297 return (httpd_server
*) 0;
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 );
303 hs
->cgi_limit
= cgi_limit
;
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 )
311 // syslog( LOG_CRIT, "out of memory copying cwd" );
312 return (httpd_server
*) 0;
314 if ( url_pattern
== (char*) 0 )
315 hs
->url_pattern
= (char*) 0;
318 hs
->url_pattern
= strdup( url_pattern
);
319 if ( hs
->url_pattern
== (char*) 0 )
321 // syslog( LOG_CRIT, "out of memory copying url_pattern" );
322 return (httpd_server
*) 0;
325 if ( local_pattern
== (char*) 0 )
326 hs
->local_pattern
= (char*) 0;
329 hs
->local_pattern
= strdup( local_pattern
);
330 if ( hs
->local_pattern
== (char*) 0 )
332 // syslog( LOG_CRIT, "out of memory copying local_pattern" );
333 return (httpd_server
*) 0;
337 hs
->logfp
= (FILE*) 0;
338 httpd_set_logfp( hs
, logfp
);
339 hs
->no_symlink_check
= no_symlink_check
;
341 hs
->global_passwd
= global_passwd
;
342 hs
->no_empty_referers
= no_empty_referers
;
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.
348 /*if ( sa6P == (httpd_sockaddr*) 0 )
351 hs->listen6_fd = initialize_listen_socket( sa6P );
352 if ( sa4P == (httpd_sockaddr*) 0 )
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 )
359 free_httpd_server( hs );
360 return (httpd_server*) 0;
365 /* Done initializing. */
366 // if ( hs->binding_hostname == (char*) 0 )
368 // LOG_NOTICE, "%.80s starting on port %d", SERVER_SOFTWARE,
372 // LOG_NOTICE, "%.80s starting on %.80s, port %d", SERVER_SOFTWARE,
373 // httpd_ntoa( hs->listen4_fd != -1 ? sa4P : sa6P ),
380 httpd_initialize_listen_socket( httpd_sockaddr
* saP
)
385 /* Check sockaddr. */
386 if ( ! sockaddr_check( saP
) )
388 // syslog( LOG_CRIT, "unknown sockaddr family on listen socket" );
393 listen_fd
= socket( saP
->sa
.sa_family
, SOCK_STREAM
, 0 );
396 // syslog( LOG_CRIT, "socket %.80s - %m", httpd_ntoa( saP ) );
397 poorman_log("can't create socket.\n", false, INADDR_NONE
, RED
);
400 (void) fcntl( listen_fd
, F_SETFD
, 1 );
402 /* Allow reuse of local addresses. */
405 listen_fd
, SOL_SOCKET
, SO_REUSEADDR
, (char*) &on
,
407 /*syslog( LOG_CRIT, "setsockopt SO_REUSEADDR - %m" )*/;
410 if ( bind( listen_fd
, &saP
->sa
, sockaddr_len( saP
) ) < 0 )
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
);
419 /* Start a listen going. */
420 if ( listen( listen_fd
, LISTEN_BACKLOG
) < 0 )
422 // syslog( LOG_CRIT, "listen - %m" );
423 poorman_log("can't listen to socket.\n", false, INADDR_NONE
, RED
);
424 (void) close( listen_fd
);
428 /* Use accept filtering, if available. */
429 #ifdef SO_ACCEPTFILTER
431 #if ( __FreeBSD_version >= 411000 )
432 #define ACCEPT_FILTER_NAME "httpready"
434 #define ACCEPT_FILTER_NAME "dataready"
436 struct accept_filter_arg af
;
437 (void) bzero( &af
, sizeof(af
) );
438 (void) strcpy( af
.af_name
, ACCEPT_FILTER_NAME
);
440 listen_fd
, SOL_SOCKET
, SO_ACCEPTFILTER
, (char*) &af
, sizeof(af
) );
442 #endif /* SO_ACCEPTFILTER */
449 httpd_set_logfp( httpd_server
* hs
, FILE* logfp
)
451 if ( hs
->logfp
!= (FILE*) 0 )
452 (void) fclose( hs
->logfp
);
458 httpd_terminate( httpd_server
* hs
)
460 httpd_unlisten( hs
);
461 if ( hs
->logfp
!= (FILE*) 0 )
462 (void) fclose( hs
->logfp
);
463 free_httpd_server( hs
);
468 httpd_unlisten( httpd_server
* hs
)
470 if ( hs
->listen4_fd
!= -1 )
472 (void) close( hs
->listen4_fd
);
475 if ( hs
->listen6_fd
!= -1 )
477 (void) close( hs
->listen6_fd
);
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
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 */
495 static char* ok200title
= "OK";
496 static char* ok206title
= "Partial Content";
498 static char* err302title
= "Found";
499 static char* err302form
= "The actual URL is '%.80s'.\n";
501 static char* err304title
= "Not Modified";
503 char* httpd_err400title
= "Bad Request";
504 char* httpd_err400form
=
505 "Your request has bad syntax or is inherently impossible to satisfy.\n";
508 static char* err401title
= "Unauthorized";
509 static char* err401form
=
510 "Authorization required for the URL '%.80s'.\n";
511 #endif /* AUTH_FILE */
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 */
519 static char* err404title
= "Not Found";
520 static char* err404form
=
521 "The requested URL '%.80s' was not found on this server.\n";
523 char* httpd_err408title
= "Request Timeout";
524 char* httpd_err408form
=
525 "No request appeared within a reasonable time period.\n";
527 static char* err500title
= "Internal Error";
528 static char* err500form
=
529 "There was an unusual problem serving the requested URL '%.80s'.\n";
531 static char* err501title
= "Not Implemented";
532 static char* err501form
=
533 "The requested method '%.80s' is not implemented by this server.\n";
535 char* httpd_err503title
= "Service Temporarily Overloaded";
536 char* httpd_err503form
=
537 "The requested URL '%.80s' is temporarily overloaded. Please try again later.\n";
540 /* Append a string to the buffer waiting to be sent as response. */
542 add_response( httpd_conn
* hc
, char* 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
;
552 /* Send the buffered response. */
554 httpd_write_response( httpd_conn
* hc
)
556 /* If we are in a sub-process, turn off no-delay mode. */
558 httpd_clear_ndelay( hc->conn_fd );*/
559 /* Send the response, if necessary. */
560 if ( hc
->responselen
> 0 )
562 (void) httpd_write_fully( hc
->conn_fd
, hc
->response
, hc
->responselen
);
568 /* Set no-delay / non-blocking mode on a socket. */
570 httpd_set_ndelay( int fd
)
574 flags
= fcntl( fd
, F_GETFL
, 0 );
577 newflags
= flags
| (int) O_NDELAY
;
578 if ( newflags
!= flags
)
579 (void) fcntl( fd
, F_SETFL
, newflags
);
584 /* Clear no-delay / non-blocking mode on a socket. */
586 httpd_clear_ndelay( int fd
)
590 flags
= fcntl( fd
, F_GETFL
, 0 );
593 newflags
= flags
& ~ (int) O_NDELAY
;
594 if ( newflags
!= flags
)
595 (void) fcntl( fd
, F_SETFL
, newflags
);
601 send_mime( httpd_conn
* hc
, int status
, char* title
, char* encodings
, char* extraheads
, char* type
, off_t length
, time_t mod
)
604 const char* rfc1123fmt
= "%a, %d %b %Y %H:%M:%S GMT";
608 char fixed_type
[500];
614 hc
->bytes_to_send
= length
;
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
) )
625 hc
->status
= status
= 206;
634 now
= time( (time_t*) 0 );
635 if ( mod
== (time_t) 0 )
637 (void) strftime( nowbuf
, sizeof(nowbuf
), rfc1123fmt
, gmtime( &now
) );
638 (void) strftime( modbuf
, sizeof(modbuf
), rfc1123fmt
, gmtime( &mod
) );
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
,
645 add_response( hc
, buf
);
647 if ( s100
!= 2 && s100
!= 3 )
649 (void) my_snprintf( buf
, sizeof(buf
),
650 "Cache-Control: no-cache,no-store\015\012" );
651 add_response( hc
, buf
);
653 if ( encodings
[0] != '\0' )
655 (void) my_snprintf( buf
, sizeof(buf
),
656 "Content-Encoding: %s\015\012", encodings
);
657 add_response( hc
, buf
);
659 if ( partial_content
)
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
,
665 (int64_t) ( hc
->last_byte_index
- hc
->first_byte_index
+ 1 ) );
666 add_response( hc
, buf
);
668 else if ( length
>= 0 )
670 (void) my_snprintf( buf
, sizeof(buf
),
671 "Content-Length: %lld\015\012", (int64_t) length
);
672 add_response( hc
, buf
);
674 if ( hc
->hs
->p3p
[0] != '\0' )
676 (void) my_snprintf( buf
, sizeof(buf
), "P3P: %s\015\012", hc
->hs
->p3p
);
677 add_response( hc
, buf
);
679 if ( hc
->hs
->max_age
>= 0 )
681 expires
= now
+ hc
->hs
->max_age
;
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
);
689 if ( extraheads
[0] != '\0' )
690 add_response( hc
, extraheads
);
691 add_response( hc
, "\015\012" );
696 static int str_alloc_count
= 0;
697 static size_t str_alloc_size
= 0;
700 httpd_realloc_str( char** strP
, size_t* maxsizeP
, size_t size
)
702 if ( *maxsizeP
== 0 )
704 *maxsizeP
= MAX( 200, size
+ 100 );
705 *strP
= NEW( char, *maxsizeP
+ 1 );
707 str_alloc_size
+= *maxsizeP
;
709 else if ( size
> *maxsizeP
)
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
;
718 if ( *strP
== (char*) 0 )
721 // LOG_ERR, "out of memory reallocating a string to %d bytes",
729 send_response( httpd_conn
* hc
, int status
, char* title
, char* extraheads
, char* form
, char* arg
)
731 char defanged_arg
[1000], buf
[2000];
734 hc
, status
, title
, "", extraheads
, "text/html; charset=%s", (off_t
) -1,
736 (void) my_snprintf( buf
, sizeof(buf
), "\
738 <HEAD><TITLE>%d %s</TITLE></HEAD>\n\
739 <BODY BGCOLOR=\"#cc9999\" TEXT=\"#000000\" LINK=\"#2020ff\" VLINK=\"#4040cc\">\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
) )
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" );
754 send_response_tail( hc
);
759 send_response_tail( httpd_conn
* hc
)
763 (void) my_snprintf( buf
, sizeof(buf
), "\
765 <ADDRESS><A HREF=\"%s\">%s</A></ADDRESS>\n\
768 SERVER_ADDRESS
, EXPOSED_SERVER_SOFTWARE
);
769 add_response( hc
, buf
);
774 defang( char* str
, char* dfstr
, int dfsize
)
779 for ( cp1
= str
, cp2
= dfstr
;
780 *cp1
!= '\0' && cp2
- dfstr
< dfsize
- 5;
807 httpd_send_err( httpd_conn
* hc
, int status
, char* title
, char* extraheads
, char* form
, char* arg
)
813 /* Try virtual host error page. */
814 if ( hc
->hs
->vhost
&& hc
->hostdir
[0] != '\0' )
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
) )
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
) )
828 /* Fall back on built-in error page. */
829 send_response( hc
, status
, title
, extraheads
, form
, arg
);
833 send_response( hc
, status
, title
, extraheads
, form
, arg
);
841 send_err_file( httpd_conn
* hc
, int status
, char* title
, char* extraheads
, char* filename
)
847 fp
= fopen( filename
, "r" );
848 if ( fp
== (FILE*) 0 )
851 hc
, status
, title
, "", extraheads
, "text/html; charset=%s", (off_t
) -1,
855 r
= fread( buf
, 1, sizeof(buf
) - 1, fp
);
859 add_response( hc
, buf
);
863 #ifdef ERR_APPEND_SERVER_INFO
864 send_response_tail( hc
);
865 #endif /* ERR_APPEND_SERVER_INFO */
875 send_authenticate( httpd_conn
* hc
, char* realm
)
878 static size_t maxheader
= 0;
879 static char headstr
[] = "WWW-Authenticate: Basic realm=\"";
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.
888 if ( hc
->method
== METHOD_POST
)
889 hc
->should_linger
= 1;
893 /* Base-64 decoding. This represents binary data as printable ASCII
894 ** characters. Three 8-bit binary bytes are turned into four 6-bit
897 ** [11111111] [22222222] [33333333]
899 ** [111111] [112222] [222233] [333333]
901 ** Then the 6-bit values are represented using the characters "A-Za-z0-9+/".
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 */
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).
929 b64_decode( const char* str
, unsigned char* space
, int size
)
932 int space_idx
, phase
;
938 for ( cp
= str
; *cp
!= '\0'; ++cp
)
940 d
= b64_decode_table
[(int) *cp
];
949 c
= ( ( prev_d
<< 2 ) | ( ( d
& 0x30 ) >> 4 ) );
950 if ( space_idx
< size
)
951 space
[space_idx
++] = c
;
955 c
= ( ( ( prev_d
& 0xf ) << 4 ) | ( ( d
& 0x3c ) >> 2 ) );
956 if ( space_idx
< size
)
957 space
[space_idx
++] = c
;
961 c
= ( ( ( prev_d
& 0x03 ) << 6 ) | d
);
962 if ( space_idx
< size
)
963 space
[space_idx
++] = c
;
974 /* Returns -1 == unauthorized, 0 == no auth file, 1 = authorized. */
976 auth_check( httpd_conn
* hc
, char* dirname
)
978 if ( hc
->hs
->global_passwd
)
981 if ( hc
->hs
->vhost
&& hc
->hostdir
[0] != '\0' )
982 topdir
= hc
->hostdir
;
985 switch ( auth_check2( hc
, topdir
) )
993 return auth_check2( hc
, dirname
);
997 /* Returns -1 == unauthorized, 0 == no auth file, 1 = authorized. */
999 auth_check2( httpd_conn
* hc
, char* dirname
)
1001 static char* authpath
;
1002 static size_t maxauthpath
= 0;
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;
1019 /* Construct auth filename. */
1021 &authpath
, &maxauthpath
, strlen( dirname
) + 1 + sizeof(AUTH_FILE
) );
1022 (void) my_snprintf( authpath
, maxauthpath
, "%s/%s", dirname
, AUTH_FILE
);
1024 /* Does this directory have an auth file? */
1025 if ( stat( authpath
, &sb
) < 0 )
1026 /* Nope, let the request go through. */
1029 /* Does this request contain basic authorization info? */
1030 if ( hc
->authorization
[0] == '\0' ||
1031 strncmp( hc
->authorization
, "Basic ", 6 ) != 0 )
1033 /* Nope, return a 401 Unauthorized. */
1034 send_authenticate( hc
, dirname
);
1040 &(hc
->authorization
[6]), (unsigned char*) authinfo
,
1041 sizeof(authinfo
) - 1 );
1043 /* Split into user and password. */
1044 authpass
= strchr( authinfo
, ':' );
1045 if ( authpass
== (char*) 0 )
1047 /* No colon? Bogus auth info. */
1048 send_authenticate( hc
, dirname
);
1052 /* If there are more fields, cut them off. */
1053 colon
= strchr( authpass
, ':' );
1054 if ( colon
!= (char*) 0 )
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 )
1063 /* Yes. Check against the cached encrypted password. */
1064 if ( strcmp( crypt( authpass
, prevcryp
), prevcryp
) == 0 )
1068 &hc
->remoteuser
, &hc
->maxremoteuser
, strlen( authinfo
) );
1069 (void) strcpy( hc
->remoteuser
, authinfo
);
1075 send_authenticate( hc
, dirname
);
1080 /* Open the password file. */
1081 fp
= fopen( authpath
, "r" );
1082 if ( fp
== (FILE*) 0 )
1084 /* The file exists but we can't open it? Disallow access. */
1086 // LOG_ERR, "%.80s auth file %.80s could not be opened - %m",
1087 // httpd_ntoa( &hc->client_addr ), authpath );
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" ),
1096 while ( fgets( line
, sizeof(line
), fp
) != (char*) 0 )
1100 if ( line
[l
- 1] == '\n' )
1102 /* Split into user and encrypted password. */
1103 cryp
= strchr( line
, ':' );
1104 if ( cryp
== (char*) 0 )
1107 /* Is this the right user? */
1108 if ( strcmp( line
, authinfo
) == 0 )
1111 (void) fclose( fp
);
1112 /* So is the password right? */
1113 if ( strcmp( crypt( authpass
, cryp
), cryp
) == 0 )
1117 &hc
->remoteuser
, &hc
->maxremoteuser
, strlen( line
) );
1118 (void) strcpy( hc
->remoteuser
, line
);
1119 /* And cache this user's info for next time. */
1121 &prevauthpath
, &maxprevauthpath
, strlen( authpath
) );
1122 (void) strcpy( prevauthpath
, authpath
);
1123 prevmtime
= sb
.st_mtime
;
1125 &prevuser
, &maxprevuser
, strlen( authinfo
) );
1126 (void) strcpy( prevuser
, authinfo
);
1127 httpd_realloc_str( &prevcryp
, &maxprevcryp
, strlen( cryp
) );
1128 (void) strcpy( prevcryp
, cryp
);
1134 send_authenticate( hc
, dirname
);
1140 /* Didn't find that user. Access denied. */
1141 (void) fclose( fp
);
1142 send_authenticate( hc
, dirname
);
1146 #endif /* AUTH_FILE */
1150 send_dirredirect( httpd_conn
* hc
)
1152 /*static*/char* location
;
1153 /*static*/char* header
;
1154 /*static*/size_t maxlocation
= 0, maxheader
= 0;
1155 static char headstr
[] = "Location: ";
1157 if ( hc
->query
[0] != '\0')
1159 char* cp
= strchr( hc
->encodedurl
, '?' );
1160 if ( cp
!= (char*) 0 ) /* should always find it */
1163 &location
, &maxlocation
,
1164 strlen( hc
->encodedurl
) + 2 + strlen( hc
->query
) );
1165 (void) my_snprintf( location
, maxlocation
,
1166 "%s/?%s", hc
->encodedurl
, hc
->query
);
1171 &location
, &maxlocation
, strlen( hc
->encodedurl
) + 1 );
1172 (void) my_snprintf( location
, maxlocation
,
1173 "%s/", hc
->encodedurl
);
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
);
1186 httpd_method_str( int method
)
1190 case METHOD_GET
: return "GET";
1191 case METHOD_HEAD
: return "HEAD";
1192 case METHOD_POST
: return "POST";
1193 default: return "UNKNOWN";
1201 if ( c
>= '0' && c
<= '9' )
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() */
1211 /* Copies and decodes a string. It's ok for from and to to be the
1215 strdecode( char* to
, char* from
)
1217 for ( ; *from
!= '\0'; ++to
, ++from
)
1219 if ( from
[0] == '%' && isxdigit( from
[1] ) && isxdigit( from
[2] ) )
1221 *to
= hexit( from
[1] ) * 16 + hexit( from
[2] );
1231 #ifdef GENERATE_INDEXES
1232 /* Copies and encodes a string. */
1234 strencode( char* to
, int tosize
, char* from
)
1238 for ( tolen
= 0; *from
!= '\0' && tolen
+ 4 < tosize
; ++from
)
1240 if ( isalnum(*from
) || strchr( "/_.-~", *from
) != (char*) 0 )
1248 (void) sprintf( to
, "%%%02x", (int) *from
& 0xff );
1255 #endif /* GENERATE_INDEXES */
1259 /* Map a ~username/whatever URL into <prefix>/username. */
1261 tilde_map_1( httpd_conn
* hc
)
1264 static size_t maxtemp
= 0;
1266 static char* prefix
= TILDE_MAP_1
;
1268 len
= strlen( hc
->expnfilename
) - 1;
1269 httpd_realloc_str( &temp
, &maxtemp
, len
);
1270 (void) strcpy( temp
, &hc
->expnfilename
[1] );
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
);
1279 #endif /* TILDE_MAP_1 */
1282 /* Map a ~username/whatever URL into <user's homedir>/<postfix>. */
1284 tilde_map_2( httpd_conn
* hc
)
1287 static size_t maxtemp
= 0;
1288 static char* postfix
= TILDE_MAP_2
;
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 )
1303 /* Get the passwd entry. */
1304 pw
= getpwnam( temp
);
1305 if ( pw
== (struct passwd
*) 0 )
1308 /* Set up altdir. */
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' )
1315 (void) strcat( hc
->altdir
, "/" );
1316 (void) strcat( hc
->altdir
, postfix
);
1318 alt
= expand_symlinks( hc
->altdir
, &rest
, 0, 1 );
1319 if ( rest
[0] != '\0' )
1321 httpd_realloc_str( &hc
->altdir
, &hc
->maxaltdir
, strlen( alt
) );
1322 (void) strcpy( hc
->altdir
, alt
);
1324 /* And the filename becomes altdir plus the post-~ part of the original. */
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
);
1331 /* For this type of tilde mapping, we want to defeat vhost mapping. */
1332 hc
->tildemapped
= 1;
1336 #endif /* TILDE_MAP_2 */
1339 /* Virtual host mapping. */
1341 vhost_map( httpd_conn
* hc
)
1345 static char* tempfilename
;
1346 static size_t maxtempfilename
= 0;
1349 #ifdef VHOST_DIRLEVELS
1352 #endif /* VHOST_DIRLEVELS */
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
;
1362 if ( getsockname( hc
->conn_fd
, &sa
.sa
, &sz
) < 0 )
1364 // syslog( LOG_ERR, "getsockname - %m" );
1367 hc
->hostname
= httpd_ntoa( &sa
);
1369 /* Pound it to lower case. */
1370 for ( cp1
= hc
->hostname
; *cp1
!= '\0'; ++cp1
)
1371 if ( isupper( *cp1
) )
1372 *cp1
= tolower( *cp1
);
1374 if ( hc
->tildemapped
)
1377 /* Figure out the host directory. */
1378 #ifdef VHOST_DIRLEVELS
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];
1386 for ( cp2
= hc
->hostdir
, i
= 0; i
< VHOST_DIRLEVELS
; ++i
)
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.
1394 while ( *cp1
== '.' )
1396 /* Copy a character from the hostname, or '_' if we ran out. */
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 */
1410 /* Prepend hostdir to the filename. */
1411 len
= strlen( hc
->expnfilename
);
1412 httpd_realloc_str( &tempfilename
, &maxtempfilename
, len
);
1413 (void) strcpy( tempfilename
, hc
->expnfilename
);
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
);
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.
1429 ** This is a fairly nice little routine. It handles any size filenames
1430 ** without excessive mallocs.
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
1439 expand_symlinks( char* path
, char** freethis
, char** restP
, int no_symlink_check
, int tildemapped
)
1444 size_t maxchecked
= 0, maxrest
= 0;
1445 size_t checkedlen
, restlen
, prevcheckedlen
, prevrestlen
;
1452 if ( no_symlink_check
)
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.
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.
1467 if ( stat( path
, &sb
) != -1 )
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] == '/' )
1475 checked
[checkedlen
- 1] = '\0';
1478 httpd_realloc_str( &rest
, &maxrest
, 0 );
1486 /* Start out with nothing in checked and the whole filename in rest. */
1487 httpd_realloc_str( &checked
, &maxchecked
, 1 );
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] == '/' )
1499 (void) strcpy( rest
, &(rest
[1]) );
1505 /* While there are still components to check... */
1506 while ( restlen
> 0 )
1508 /* Save current checkedlen in case we get a symlink. Save current
1509 ** restlen in case we get a non-existant component.
1511 prevcheckedlen
= checkedlen
;
1512 prevrestlen
= restlen
;
1514 /* Grab one component from r and transfer it to checked. */
1515 cp1
= strchr( r
, '/' );
1516 if ( cp1
!= (char*) 0 )
1521 /* Special case for absolute paths. */
1522 httpd_realloc_str( &checked
, &maxchecked
, checkedlen
+ 1 );
1523 (void) strncpy( &checked
[checkedlen
], r
, 1 );
1526 else if ( strncmp( r
, "..", MAX( i
, 2 ) ) == 0 )
1528 /* Ignore ..'s that go above the start of the path. */
1529 if ( checkedlen
!= 0 )
1531 cp2
= strrchr( checked
, '/' );
1532 if ( cp2
== (char*) 0 )
1534 else if ( cp2
== checked
)
1537 checkedlen
= cp2
- checked
;
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
);
1548 checked
[checkedlen
] = '\0';
1554 /* No slashes remaining, r is all one component. */
1555 if ( strcmp( r
, ".." ) == 0 )
1557 /* Ignore ..'s that go above the start of the path. */
1558 if ( checkedlen
!= 0 )
1560 cp2
= strrchr( checked
, '/' );
1561 if ( cp2
== (char*) 0 )
1563 else if ( cp2
== checked
)
1566 checkedlen
= cp2
- checked
;
1567 checked
[checkedlen
] = '\0';
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
;
1583 /* Try reading the current filename as a symlink */
1584 if ( checked
[0] == '\0' )
1586 linklen
= readlink( checked
, link
, sizeof(link
) - 1 );
1587 if ( linklen
== -1 )
1589 if ( errno
== EINVAL
)
1590 continue; /* not a symlink */
1591 if ( errno
== EACCES
|| errno
== ENOENT
|| errno
== ENOTDIR
)
1593 /* That last component was bogus. Restore and return. */
1594 *restP
= r
- ( prevrestlen
- restlen
);
1595 if ( prevcheckedlen
== 0 )
1596 (void) strcpy( checked
, "." );
1598 checked
[prevcheckedlen
] = '\0';
1602 // syslog( LOG_ERR, "readlink %.80s - %m", checked );
1609 if ( nlinks
> MAX_LINKS
)
1611 // syslog( LOG_ERR, "too many symlinks in %.80s", path );
1617 link
[linklen
] = '\0';
1618 if ( link
[linklen
- 1] == '/' )
1619 link
[--linklen
] = '\0'; /* trim trailing slash */
1621 /* Insert the link contents in front of the rest of the filename. */
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;
1635 /* There's nothing left in the filename, so the link contents
1636 ** becomes the rest.
1638 httpd_realloc_str( &rest
, &maxrest
, linklen
);
1639 (void) strcpy( rest
, link
);
1644 if ( rest
[0] == '/' )
1646 /* There must have been an absolute symlink - zero out checked. */
1652 /* Re-check this component. */
1653 checkedlen
= prevcheckedlen
;
1654 checked
[checkedlen
] = '\0';
1660 if ( checked
[0] == '\0' )
1661 (void) strcpy( checked
, "." );
1668 httpd_get_conn( httpd_server
* hs
, int listen_fd
, httpd_conn
* hc
)
1674 if ( ! hc
->initialized
)
1677 httpd_realloc_str( &hc
->read_buf
, &hc
->read_size
, 500 );
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;
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 );
1699 httpd_realloc_str( &hc
->altdir
, &hc
->maxaltdir
, 0 );
1700 #endif /* TILDE_MAP_2 */
1701 hc
->initialized
= 1;
1704 /* Accept the new connection. */
1706 hc
->conn_fd
= accept( listen_fd
, &sa
.sa
, &sz
);
1707 if ( hc
->conn_fd
< 0 )
1709 if ( errno
== EWOULDBLOCK
)
1711 // syslog( LOG_ERR, "accept - %m" );
1714 if ( ! sockaddr_check( &sa
) )
1716 // syslog( LOG_ERR, "unknown sockaddr family" );
1717 close( hc
->conn_fd
);
1721 (void) fcntl( hc
->conn_fd
, F_SETFD
, 1 );
1723 (void) memset( &hc
->client_addr
, 0, sizeof(hc
->client_addr
) );
1724 (void) memmove( &hc
->client_addr
, &sa
, sockaddr_len( &sa
) );
1726 hc
->checked_idx
= 0;
1727 hc
->checked_state
= CHST_FIRSTWORD
;
1728 hc
->method
= METHOD_UNKNOWN
;
1730 hc
->bytes_to_send
= 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';
1742 hc
->accept
[0] = '\0';
1743 hc
->accepte
[0] = '\0';
1746 hc
->contenttype
= "";
1747 hc
->reqhost
[0] = '\0';
1749 hc
->hostdir
[0] = '\0';
1750 hc
->authorization
= "";
1751 hc
->remoteuser
[0] = '\0';
1752 hc
->response
[0] = '\0';
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;
1761 hc
->hostname
= (char*) 0;
1765 hc
->tildemapped
= 0;
1766 hc
->first_byte_index
= 0;
1767 hc
->last_byte_index
= -1;
1769 hc
->should_linger
= 0;
1770 hc
->processed_directory_index
= 0;
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.
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.
1784 httpd_got_request( httpd_conn
* hc
)
1788 for ( ; hc
->checked_idx
< hc
->read_idx
; ++hc
->checked_idx
)
1790 c
= hc
->read_buf
[hc
->checked_idx
];
1791 switch ( hc
->checked_state
)
1793 case CHST_FIRSTWORD
:
1796 case ' ': case '\t':
1797 hc
->checked_state
= CHST_FIRSTWS
;
1799 case '\012': case '\015':
1800 hc
->checked_state
= CHST_BOGUS
;
1801 return GR_BAD_REQUEST
;
1807 case ' ': case '\t':
1809 case '\012': case '\015':
1810 hc
->checked_state
= CHST_BOGUS
;
1811 return GR_BAD_REQUEST
;
1813 hc
->checked_state
= CHST_SECONDWORD
;
1817 case CHST_SECONDWORD
:
1820 case ' ': case '\t':
1821 hc
->checked_state
= CHST_SECONDWS
;
1823 case '\012': case '\015':
1824 /* The first line has only two words - an HTTP/0.9 request. */
1825 return GR_GOT_REQUEST
;
1831 case ' ': case '\t':
1833 case '\012': case '\015':
1834 hc
->checked_state
= CHST_BOGUS
;
1835 return GR_BAD_REQUEST
;
1837 hc
->checked_state
= CHST_THIRDWORD
;
1841 case CHST_THIRDWORD
:
1844 case ' ': case '\t':
1845 hc
->checked_state
= CHST_THIRDWS
;
1848 hc
->checked_state
= CHST_LF
;
1851 hc
->checked_state
= CHST_CR
;
1858 case ' ': case '\t':
1861 hc
->checked_state
= CHST_LF
;
1864 hc
->checked_state
= CHST_CR
;
1867 hc
->checked_state
= CHST_BOGUS
;
1868 return GR_BAD_REQUEST
;
1875 hc
->checked_state
= CHST_LF
;
1878 hc
->checked_state
= CHST_CR
;
1886 /* Two newlines in a row - a blank line - end of request. */
1887 return GR_GOT_REQUEST
;
1889 hc
->checked_state
= CHST_CR
;
1892 hc
->checked_state
= CHST_LINE
;
1900 hc
->checked_state
= CHST_CRLF
;
1903 /* Two returns in a row - end of request. */
1904 return GR_GOT_REQUEST
;
1906 hc
->checked_state
= CHST_LINE
;
1914 /* Two newlines in a row - end of request. */
1915 return GR_GOT_REQUEST
;
1917 hc
->checked_state
= CHST_CRLFCR
;
1920 hc
->checked_state
= CHST_LINE
;
1927 case '\012': case '\015':
1928 /* Two CRLFs or two CRs in a row - end of request. */
1929 return GR_GOT_REQUEST
;
1931 hc
->checked_state
= CHST_LINE
;
1936 return GR_BAD_REQUEST
;
1939 return GR_NO_REQUEST
;
1944 httpd_parse_request( httpd_conn
* hc
)
1956 hc
->checked_idx
= 0; /* reset */
1957 method_str
= bufgets( hc
);
1958 url
= strpbrk( method_str
, " \t\012\015" );
1959 if ( url
== (char*) 0 )
1961 httpd_send_err( hc
, 400, httpd_err400title
, "", httpd_err400form
, "" );
1965 url
+= strspn( url
, " \t\012\015" );
1966 protocol
= strpbrk( url
, " \t\012\015" );
1967 if ( protocol
== (char*) 0 )
1969 protocol
= "HTTP/0.9";
1975 protocol
+= strspn( protocol
, " \t\012\015" );
1976 if ( *protocol
!= '\0' )
1978 eol
= strpbrk( protocol
, " \t\012\015" );
1979 if ( eol
!= (char*) 0 )
1981 if ( strcasecmp( protocol
, "HTTP/1.0" ) != 0 )
1985 hc
->protocol
= protocol
;
1987 /* Check for HTTP/1.1 absolute URL. */
1988 if ( strncasecmp( url
, "http://", 7 ) == 0 )
1990 if ( ! hc
->one_one
)
1992 httpd_send_err( hc
, 400, httpd_err400title
, "", httpd_err400form
, "" );
1996 url
= strchr( reqhost
, '/' );
1997 if ( url
== (char*) 0 )
1999 httpd_send_err( hc
, 400, httpd_err400title
, "", httpd_err400form
, "" );
2003 if ( strchr( reqhost
, '/' ) != (char*) 0 || reqhost
[0] == '.' )
2005 httpd_send_err( hc
, 400, httpd_err400title
, "", httpd_err400form
, "" );
2008 httpd_realloc_str( &hc
->reqhost
, &hc
->maxreqhost
, strlen( reqhost
) );
2009 (void) strcpy( hc
->reqhost
, reqhost
);
2015 httpd_send_err( hc
, 400, httpd_err400title
, "", httpd_err400form
, "" );
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;*/
2027 httpd_send_err( hc
, 501, err501title
, "", err501form
, method_str
);
2031 hc
->encodedurl
= url
;
2033 &hc
->decodedurl
, &hc
->maxdecodedurl
, strlen( hc
->encodedurl
) );
2034 strdecode( hc
->decodedurl
, hc
->encodedurl
);
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
, "." );
2043 /* Extract query string from encoded URL. */
2044 cp
= strchr( hc
->encodedurl
, '?' );
2045 if ( cp
!= (char*) 0 )
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 )
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] == '/' ) ) )
2061 httpd_send_err( hc
, 400, httpd_err400title
, "", httpd_err400form
, "" );
2065 if ( hc
->mime_flag
)
2067 /* Read the MIME headers. */
2068 while ( ( buf
= bufgets( hc
) ) != (char*) 0 )
2070 if ( buf
[0] == '\0' )
2072 if ( strncasecmp( buf
, "Referer:", 8 ) == 0 )
2075 cp
+= strspn( cp
, " \t" );
2078 else if ( strncasecmp( buf
, "User-Agent:", 11 ) == 0 )
2081 cp
+= strspn( cp
, " \t" );
2084 else if ( strncasecmp( buf
, "Host:", 5 ) == 0 )
2087 cp
+= strspn( cp
, " \t" );
2089 cp
= strchr( hc
->hdrhost
, ':' );
2090 if ( cp
!= (char*) 0 )
2092 if ( strchr( hc
->hdrhost
, '/' ) != (char*) 0 || hc
->hdrhost
[0] == '.' )
2094 httpd_send_err( hc
, 400, httpd_err400title
, "", httpd_err400form
, "" );
2098 else if ( strncasecmp( buf
, "Accept:", 7 ) == 0 )
2101 cp
+= strspn( cp
, " \t" );
2102 if ( hc
->accept
[0] != '\0' )
2104 if ( strlen( hc
->accept
) > 5000 )
2107 // LOG_ERR, "%.80s way too much Accept: data",
2108 // httpd_ntoa( &hc->client_addr ) );
2112 &hc
->accept
, &hc
->maxaccept
,
2113 strlen( hc
->accept
) + 2 + strlen( cp
) );
2114 (void) strcat( hc
->accept
, ", " );
2118 &hc
->accept
, &hc
->maxaccept
, strlen( cp
) );
2119 (void) strcat( hc
->accept
, cp
);
2121 else if ( strncasecmp( buf
, "Accept-Encoding:", 16 ) == 0 )
2124 cp
+= strspn( cp
, " \t" );
2125 if ( hc
->accepte
[0] != '\0' )
2127 if ( strlen( hc
->accepte
) > 5000 )
2130 // LOG_ERR, "%.80s way too much Accept-Encoding: data",
2131 // httpd_ntoa( &hc->client_addr ) );
2135 &hc
->accepte
, &hc
->maxaccepte
,
2136 strlen( hc
->accepte
) + 2 + strlen( cp
) );
2137 (void) strcat( hc
->accepte
, ", " );
2141 &hc
->accepte
, &hc
->maxaccepte
, strlen( cp
) );
2142 (void) strcpy( hc
->accepte
, cp
);
2144 else if ( strncasecmp( buf
, "Accept-Language:", 16 ) == 0 )
2147 cp
+= strspn( cp
, " \t" );
2150 else if ( strncasecmp( buf
, "If-Modified-Since:", 18 ) == 0 )
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 )*/;
2157 else if ( strncasecmp( buf
, "Cookie:", 7 ) == 0 )
2160 cp
+= strspn( cp
, " \t" );
2163 else if ( strncasecmp( buf
, "Range:", 6 ) == 0 )
2165 /* Only support %d- and %d-%d, not %d-%d,%d-%d or -%d. */
2166 if ( strchr( buf
, ',' ) == (char*) 0 )
2169 cp
= strpbrk( buf
, "=" );
2170 if ( cp
!= (char*) 0 )
2172 cp_dash
= strchr( cp
+ 1, '-' );
2173 if ( cp_dash
!= (char*) 0 && cp_dash
!= cp
+ 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] ) )
2182 hc
->last_byte_index
= atoll( cp_dash
+ 1 );
2183 if ( hc
->last_byte_index
< 0 )
2184 hc
->last_byte_index
= -1;
2190 else if ( strncasecmp( buf
, "Range-If:", 9 ) == 0 ||
2191 strncasecmp( buf
, "If-Range:", 9 ) == 0 )
2194 hc
->range_if
= tdate_parse( cp
);
2195 if ( hc
->range_if
== (time_t) -1 )
2196 /*syslog( LOG_DEBUG, "unparsable time: %.80s", cp )*/;
2198 else if ( strncasecmp( buf
, "Content-Type:", 13 ) == 0 )
2201 cp
+= strspn( cp
, " \t" );
2202 hc
->contenttype
= cp
;
2204 else if ( strncasecmp( buf
, "Content-Length:", 15 ) == 0 )
2207 hc
->contentlength
= atol( cp
);
2209 else if ( strncasecmp( buf
, "Authorization:", 14 ) == 0 )
2212 cp
+= strspn( cp
, " \t" );
2213 hc
->authorization
= cp
;
2215 else if ( strncasecmp( buf
, "Connection:", 11 ) == 0 )
2218 cp
+= strspn( cp
, " \t" );
2219 if ( strcasecmp( cp
, "keep-alive" ) == 0 )
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 )
2254 syslog( LOG_DEBUG
, "unknown request header: %.80s", buf
);
2255 #endif /* LOG_UNKNOWN_HEADERS */
2261 /* Check that HTTP/1.1 requests specify a host, as required. */
2262 if ( hc
->reqhost
[0] == '\0' && hc
->hdrhost
[0] == '\0' )
2264 httpd_send_err( hc
, 400, httpd_err400title
, "", httpd_err400form
, "" );
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.
2274 if ( hc
->keep_alive
)
2275 hc
->should_linger
= 1;
2278 /* Ok, the request has been parsed. Now we resolve stuff that
2279 ** may require the entire request.
2282 /* Copy original filename to expanded filename. */
2284 &hc
->expnfilename
, &hc
->maxexpnfilename
, strlen( hc
->origfilename
) );
2285 (void) strcpy( hc
->expnfilename
, hc
->origfilename
);
2287 /* Tilde mapping. */
2288 if ( hc
->expnfilename
[0] == '~' )
2291 if ( ! tilde_map_1( hc
) )
2293 httpd_send_err( hc
, 404, err404title
, "", err404form
, hc
->encodedurl
);
2296 #endif /* TILDE_MAP_1 */
2298 if ( ! tilde_map_2( hc
) )
2300 httpd_send_err( hc
, 404, err404title
, "", err404form
, hc
->encodedurl
);
2303 #endif /* TILDE_MAP_2 */
2306 /* Virtual host mapping. */
2307 if ( hc
->hs
->vhost
)
2308 if ( ! vhost_map( hc
) )
2310 httpd_send_err( hc
, 500, err500title
, "", err500form
, hc
->encodedurl
);
2314 /* Expand all symbolic links in the filename. This also gives us
2315 ** any trailing non-existing components, for pathinfo.
2317 cp
= expand_symlinks( hc
->expnfilename
, &freethis
, &pi
, hc
->hs
->no_symlink_check
, hc
->tildemapped
);
2318 if ( cp
== (char*) 0 )
2320 httpd_send_err( hc
, 500, err500title
, "", err500form
, hc
->encodedurl
);
2323 httpd_realloc_str( &hc
->expnfilename
, &hc
->maxexpnfilename
, strlen( cp
) );
2324 (void) strcpy( hc
->expnfilename
, cp
);
2326 httpd_realloc_str( &hc
->pathinfo
, &hc
->maxpathinfo
, strlen( pi
) );
2327 (void) strcpy( hc
->pathinfo
, pi
);
2330 /* Remove pathinfo stuff from the original filename too. */
2331 if ( hc
->pathinfo
[0] != '\0' )
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';
2339 /* If the expanded filename is an absolute path, check that it's still
2340 ** within the current directory or the alternate directory.
2342 if ( hc
->expnfilename
[0] == '/' )
2344 if(pthread_rwlock_rdlock(get_web_dir_lock()) == 0){
2346 hc
->expnfilename
, hc
->hs
->cwd
, strlen( hc
->hs
->cwd
) ) == 0 )
2348 /* Elide the current directory. */
2350 hc
->expnfilename
, &hc
->expnfilename
[strlen( hc
->hs
->cwd
)] );
2353 else if ( hc
->altdir
[0] != '\0' &&
2355 hc
->expnfilename
, hc
->altdir
,
2356 strlen( hc
->altdir
) ) == 0 &&
2357 ( hc
->expnfilename
[strlen( hc
->altdir
)] == '\0' ||
2358 hc
->expnfilename
[strlen( hc
->altdir
)] == '/' ) ) )
2360 #endif /* TILDE_MAP_2 */
2364 // LOG_NOTICE, "%.80s URL \"%.80s\" goes outside the web tree",
2365 // httpd_ntoa( &hc->client_addr ), hc->encodedurl );
2367 hc
, 403, err403title
, "",
2368 ERROR_FORM( err403form
, "The requested URL '%.80s' resolves to a file outside the permitted web server directory tree.\n" ),
2372 pthread_rwlock_unlock(get_web_dir_lock());
2375 httpd_send_err( hc
, 500, err500title
, "", err500form
, hc
->encodedurl
);
2385 bufgets( httpd_conn
* hc
)
2390 for ( i
= hc
->checked_idx
; hc
->checked_idx
< hc
->read_idx
; ++hc
->checked_idx
)
2392 c
= hc
->read_buf
[hc
->checked_idx
];
2393 if ( c
== '\012' || c
== '\015' )
2395 hc
->read_buf
[hc
->checked_idx
] = '\0';
2397 if ( c
== '\015' && hc
->checked_idx
< hc
->read_idx
&&
2398 hc
->read_buf
[hc
->checked_idx
] == '\012' )
2400 hc
->read_buf
[hc
->checked_idx
] = '\0';
2403 return &(hc
->read_buf
[i
]);
2411 de_dotdot( char* file
)
2417 /* Collapse any multiple / sequences. */
2418 while ( ( cp
= strstr( file
, "//") ) != (char*) 0 )
2420 for ( cp2
= cp
+ 2; *cp2
== '/'; ++cp2
)
2422 (void) strcpy( cp
+ 1, cp2
);
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 );
2431 /* Alternate between removing leading ../ and removing xxx/../ */
2434 while ( strncmp( file
, "../", 3 ) == 0 )
2435 (void) strcpy( file
, file
+ 3 );
2436 cp
= strstr( file
, "/../" );
2437 if ( cp
== (char*) 0 )
2439 for ( cp2
= cp
- 1; cp2
>= file
&& *cp2
!= '/'; --cp2
)
2441 (void) strcpy( cp2
+ 1, cp
+ 4 );
2444 /* Also elide any xxx/.. at the end. */
2445 while ( ( l
= strlen( file
) ) > 3 &&
2446 strcmp( ( cp
= file
+ l
- 3 ), "/.." ) == 0 )
2448 for ( cp2
= cp
- 1; cp2
>= file
&& *cp2
!= '/'; --cp2
)
2458 httpd_close_conn( httpd_conn
* hc
, struct timeval
* nowP
)
2460 make_log_entry( hc
, nowP
);
2462 if ( hc
->conn_fd
>= 0 )
2464 (void) close( hc
->conn_fd
);
2470 httpd_destroy_conn( httpd_conn
* hc
)
2472 if ( hc
->initialized
)
2474 free( (void*) hc
->read_buf
);
2475 free( (void*) hc
->decodedurl
);
2476 free( (void*) hc
->origfilename
);
2477 free( (void*) hc
->expnfilename
);
2478 free( (void*) hc
->encodings
);
2479 free( (void*) hc
->pathinfo
);
2480 free( (void*) hc
->query
);
2481 free( (void*) hc
->accept
);
2482 free( (void*) hc
->accepte
);
2483 free( (void*) hc
->reqhost
);
2484 free( (void*) hc
->hostdir
);
2485 free( (void*) hc
->remoteuser
);
2486 free( (void*) hc
->response
);
2488 free( (void*) hc
->altdir
);
2489 #endif /* TILDE_MAP_2 */
2490 hc
->initialized
= 0;
2501 static struct mime_entry enc_tab
[] = {
2502 #include "mime_encodings.h"
2504 static const int n_enc_tab
= sizeof(enc_tab
) / sizeof(*enc_tab
);
2505 static struct mime_entry typ_tab
[] = {
2506 #include "mime_types.h"
2508 static const int n_typ_tab
= sizeof(typ_tab
) / sizeof(*typ_tab
);
2511 /* qsort comparison routine - declared old-style on purpose, for portability. */
2514 struct mime_entry
* a
;
2515 struct mime_entry
* b
;
2517 return strcmp( a
->ext
, b
->ext
);
2526 /* Sort the tables so we can do binary search. */
2527 qsort( enc_tab
, n_enc_tab
, sizeof(*enc_tab
), ext_compare
);
2528 qsort( typ_tab
, n_typ_tab
, sizeof(*typ_tab
), ext_compare
);
2530 /* Fill in the lengths. */
2531 for ( i
= 0; i
< n_enc_tab
; ++i
)
2533 enc_tab
[i
].ext_len
= strlen( enc_tab
[i
].ext
);
2534 enc_tab
[i
].val_len
= strlen( enc_tab
[i
].val
);
2536 for ( i
= 0; i
< n_typ_tab
; ++i
)
2538 typ_tab
[i
].ext_len
= strlen( typ_tab
[i
].ext
);
2539 typ_tab
[i
].val_len
= strlen( typ_tab
[i
].val
);
2545 /* Figure out MIME encodings and type based on the filename. Multiple
2546 ** encodings are separated by commas, and are listed in the order in
2547 ** which they were applied to the file.
2550 figure_mime( httpd_conn
* hc
)
2555 int me_indexes
[100];
2556 unsigned int n_me_indexes
;
2557 size_t ext_len
, encodings_len
;
2558 int i
, top
, bot
, mid
;
2560 char* default_type
= "text/plain";
2562 /* Peel off encoding extensions until there aren't any more. */
2564 for ( prev_dot
= &hc
->expnfilename
[strlen(hc
->expnfilename
)]; ; prev_dot
= dot
)
2566 for ( dot
= prev_dot
- 1; dot
>= hc
->expnfilename
&& *dot
!= '.'; --dot
)
2568 if ( dot
< hc
->expnfilename
)
2570 /* No dot found. No more encoding extensions, and no type
2571 ** extension either.
2573 hc
->type
= default_type
;
2577 ext_len
= prev_dot
- ext
;
2578 /* Search the encodings table. Linear search is fine here, there
2579 ** are only a few entries.
2581 for ( i
= 0; i
< n_enc_tab
; ++i
)
2583 if ( ext_len
== enc_tab
[i
].ext_len
&& strncasecmp( ext
, enc_tab
[i
].ext
, ext_len
) == 0 )
2585 if ( n_me_indexes
< sizeof(me_indexes
)/sizeof(*me_indexes
) )
2587 me_indexes
[n_me_indexes
] = i
;
2593 /* No encoding extension found. Break and look for a type extension. */
2599 /* Binary search for a matching type extension. */
2600 top
= n_typ_tab
- 1;
2602 while ( top
>= bot
)
2604 mid
= ( top
+ bot
) / 2;
2605 r
= strncasecmp( ext
, typ_tab
[mid
].ext
, ext_len
);
2611 if ( ext_len
< typ_tab
[mid
].ext_len
)
2613 else if ( ext_len
> typ_tab
[mid
].ext_len
)
2617 hc
->type
= typ_tab
[mid
].val
;
2621 hc
->type
= default_type
;
2625 /* The last thing we do is actually generate the mime-encoding header. */
2626 hc
->encodings
[0] = '\0';
2628 for ( i
= n_me_indexes
- 1; i
>= 0; --i
)
2631 &hc
->encodings
, &hc
->maxencodings
,
2632 encodings_len
+ enc_tab
[me_indexes
[i
]].val_len
+ 1 );
2633 if ( hc
->encodings
[0] != '\0' )
2635 (void) strcpy( &hc
->encodings
[encodings_len
], "," );
2638 (void) strcpy( &hc
->encodings
[encodings_len
], enc_tab
[me_indexes
[i
]].val
);
2639 encodings_len
+= enc_tab
[me_indexes
[i
]].val_len
;
2644 #ifdef GENERATE_INDEXES
2646 /* qsort comparison routine - declared old-style on purpose, for portability. */
2648 name_compare( a
, b
)
2652 return strcmp( *a
, *b
);
2657 ls( httpd_conn
* hc
)
2663 /*static*/int maxnames
= 0;
2665 /*static*/char* names
= NULL
;
2666 /*static*/char** nameptrs
= NULL
;
2667 /*static*/char* name
;
2668 /*static*/size_t maxname
= 0;
2669 /*static*/char* rname
;
2670 /*static*/size_t maxrname
= 0;
2671 /*static*/char* encrname
;
2672 /*static*/size_t maxencrname
= 0;
2679 char link
[MAXPATHLEN
+1];
2684 //ClientData client_data;
2686 de
= dep
= malloc(sizeof(struct dirent
)+B_FILE_NAME_LENGTH
+1);
2689 httpd_send_err( hc
, 501, err501title
, "", err501form
, hc
->encodedurl
);
2693 dirp
= opendir( hc
->expnfilename
);
2694 if ( dirp
== (DIR*) 0 )
2696 char logString
[27+B_PATH_NAME_LENGTH
+1];
2697 sprintf(logString
, "Error 404 File not found: %s\n", hc
->decodedurl
+1);
2698 poorman_log(logString
, true, hc
->client_addr
.sa_in
.sin_addr
.s_addr
, RED
);
2699 // syslog( LOG_ERR, "opendir %.80s - %m", hc->expnfilename );
2700 httpd_send_err( hc
, 404, err404title
, "", err404form
, hc
->encodedurl
);
2705 if ( hc
->method
== METHOD_HEAD
)
2709 hc
, 200, ok200title
, "", "", "text/html; charset=%s", (off_t
) -1,
2711 httpd_write_response( hc
);
2714 else if ( hc
->method
== METHOD_GET
)
2717 char logString
[B_FILE_NAME_LENGTH
+B_PATH_NAME_LENGTH
+51];
2718 if(pthread_rwlock_rdlock(get_web_dir_lock()) == 0){
2721 "Directory %s/%s/ has no ",
2725 pthread_rwlock_unlock(get_web_dir_lock());
2728 strcpy(logString
, "A web directory has no ");
2730 if(pthread_rwlock_rdlock(get_index_name_lock()) == 0){
2731 strcat(logString
, hc
->hs
->index_name
);
2732 pthread_rwlock_unlock(get_index_name_lock());
2735 strcat(logString
, "index file");
2737 strcat(logString
, ". Sending directory listing.\n");
2738 poorman_log(logString
, true, hc
->client_addr
.sa_in
.sin_addr
.s_addr
, BLACK
);
2742 hc
, 200, ok200title
, "", "", "text/html; charset=%s",
2743 (off_t
) -1, hc
->sb
.st_mtime
);
2744 httpd_write_response( hc
);
2746 /* Open a stdio stream so that we can use fprintf, which is more
2747 ** efficient than a bunch of separate write()s. We don't have
2748 ** to worry about double closes or file descriptor leaks cause
2749 ** we're in a subprocess.
2751 fp
= fdopen( hc
->conn_fd
, "w" );
2752 if ( fp
== (FILE*) 0 )
2754 // syslog( LOG_ERR, "fdopen - %m" );
2756 hc
, 500, err500title
, "", err500form
, hc
->encodedurl
);
2757 httpd_write_response( hc
);
2763 (void) fprintf( fp
, "\
2765 <HEAD><TITLE>Index of %.80s</TITLE></HEAD>\n\
2766 <BODY BGCOLOR=\"#99cc99\" TEXT=\"#000000\" LINK=\"#2020ff\" VLINK=\"#4040cc\">\n\
2767 <H2>Index of %.80s</H2>\n\
2769 bytes last-changed name\n\
2771 hc
->decodedurl
, hc
->decodedurl
);
2773 /* Read in names. */
2775 while ( readdir_r( dirp
, de
, &dep
) == 0 && dep
!= NULL
) /* dirent or direct */
2777 if ( nnames
>= maxnames
)
2779 if ( maxnames
== 0 )
2782 names
= NEW( char, maxnames
* ( MAXPATHLEN
+ 1 ) );
2783 nameptrs
= NEW( char*, maxnames
);
2788 names
= RENEW( names
, char, maxnames
* ( MAXPATHLEN
+ 1 ) );
2789 nameptrs
= RENEW( nameptrs
, char*, maxnames
);
2791 if ( names
== (char*) 0 || nameptrs
== (char**) 0 )
2793 // syslog( LOG_ERR, "out of memory reallocating directory names" );
2797 for ( i
= 0; i
< maxnames
; ++i
)
2798 nameptrs
[i
] = &names
[i
* ( MAXPATHLEN
+ 1 )];
2800 namlen
= NAMLEN(de
);
2801 (void) strncpy( nameptrs
[nnames
], de
->d_name
, namlen
);
2802 nameptrs
[nnames
][namlen
] = '\0';
2808 /* Sort the names. */
2809 qsort( nameptrs
, nnames
, sizeof(*nameptrs
), name_compare
);
2811 /* Generate output. */
2812 for ( i
= 0; i
< nnames
; ++i
)
2816 strlen( hc
->expnfilename
) + 1 + strlen( nameptrs
[i
] ) );
2819 strlen( hc
->origfilename
) + 1 + strlen( nameptrs
[i
] ) );
2820 if ( hc
->expnfilename
[0] == '\0' ||
2821 strcmp( hc
->expnfilename
, "." ) == 0 )
2823 (void) strcpy( name
, nameptrs
[i
] );
2824 (void) strcpy( rname
, nameptrs
[i
] );
2828 (void) my_snprintf( name
, maxname
,
2829 "%s/%s", hc
->expnfilename
, nameptrs
[i
] );
2830 if ( strcmp( hc
->origfilename
, "." ) == 0 )
2831 (void) my_snprintf( rname
, maxrname
,
2832 "%s", nameptrs
[i
] );
2834 (void) my_snprintf( rname
, maxrname
,
2835 "%s%s", hc
->origfilename
, nameptrs
[i
] );
2838 &encrname
, &maxencrname
, 3 * strlen( rname
) + 1 );
2839 strencode( encrname
, maxencrname
, rname
);
2841 if ( stat( name
, &sb
) < 0 || lstat( name
, &lsb
) < 0 )
2846 /* Break down mode word. First the file type. */
2847 switch ( lsb
.st_mode
& S_IFMT
)
2849 /*case S_IFIFO: modestr[0] = 'p'; break;
2850 case S_IFCHR: modestr[0] = 'c'; break;
2851 case S_IFDIR: modestr[0] = 'd'; break;
2852 case S_IFBLK: modestr[0] = 'b'; break;
2853 case S_IFREG: modestr[0] = '-'; break;
2854 case S_IFSOCK: modestr[0] = 's'; break;*/
2855 case S_IFLNK
: //modestr[0] = 'l';
2856 linklen
= readlink( name
, link
, sizeof(link
) - 1 );
2857 if ( linklen
!= -1 )
2859 link
[linklen
] = '\0';
2860 linkprefix
= " -> ";
2863 //default: modestr[0] = '?'; break;
2865 /* Now the world permissions. Owner and group permissions
2866 ** are not of interest to web clients.
2868 /*modestr[1] = ( lsb.st_mode & S_IROTH ) ? 'r' : '-';
2869 modestr[2] = ( lsb.st_mode & S_IWOTH ) ? 'w' : '-';
2870 modestr[3] = ( lsb.st_mode & S_IXOTH ) ? 'x' : '-';
2871 modestr[4] = '\0';*/
2873 /* We also leave out the owner and group name, they are
2874 ** also not of interest to web clients. Plus if we're
2875 ** running under chroot(), they would require a copy
2876 ** of /etc/passwd and /etc/group, which we want to avoid.
2879 /* Get time string. */
2880 now
= time( (time_t*) 0 );
2881 ctime_r( &lsb
.st_mtime
, timestr
);
2882 timestr
[ 0] = timestr
[ 4];
2883 timestr
[ 1] = timestr
[ 5];
2884 timestr
[ 2] = timestr
[ 6];
2886 timestr
[ 4] = timestr
[ 8];
2887 timestr
[ 5] = timestr
[ 9];
2889 if ( now
- lsb
.st_mtime
> 60*60*24*182 ) /* 1/2 year */
2892 timestr
[ 8] = timestr
[20];
2893 timestr
[ 9] = timestr
[21];
2894 timestr
[10] = timestr
[22];
2895 timestr
[11] = timestr
[23];
2899 timestr
[ 7] = timestr
[11];
2900 timestr
[ 8] = timestr
[12];
2902 timestr
[10] = timestr
[14];
2903 timestr
[11] = timestr
[15];
2907 /* The ls -F file class. */
2908 switch ( sb
.st_mode
& S_IFMT
)
2910 case S_IFDIR
: fileclass
= "/"; break;
2911 //case S_IFSOCK: fileclass = "="; break;
2912 //case S_IFLNK: fileclass = "@"; break;
2914 fileclass
= "";//( sb.st_mode & S_IXOTH ) ? "*" : "";
2920 "%10" B_PRId64
" %s <A HREF=\"/%.500s%s\">%.80s</A>%s%s%s\n",
2921 (int64_t) lsb
.st_size
,
2922 timestr
, encrname
, S_ISDIR(sb
.st_mode
) ? "/" : "",
2923 nameptrs
[i
], linkprefix
, link
, fileclass
);
2926 (void) fprintf( fp
, "</PRE></BODY>\n</HTML>\n" );
2927 (void) fclose( fp
);
2930 hc
->bytes_sent
= CGI_BYTECOUNT
;
2931 hc
->should_linger
= 0;
2938 }//else if ( hc->method == METHOD_GET )
2943 hc
, 501, err501title
, "", err501form
, httpd_method_str( hc
->method
) );
2947 hc
->processed_directory_index
= 1;
2951 #endif /* GENERATE_INDEXES */
2955 //build_env( char* fmt, char* arg )
2959 // static char* buf;
2960 // static size_t maxbuf = 0;
2962 // size = strlen( fmt ) + strlen( arg );
2963 // if ( size > maxbuf )
2964 // httpd_realloc_str( &buf, &maxbuf, size );
2965 // (void) my_snprintf( buf, maxbuf, fmt, arg );
2966 // cp = strdup( buf );
2967 // if ( cp == (char*) 0 )
2969 // syslog( LOG_ERR, "out of memory copying environment variable" );
2976 #ifdef SERVER_NAME_LIST
2978 hostname_map( char* hostname
)
2981 static char* list
[] = { SERVER_NAME_LIST
};
2983 len
= strlen( hostname
);
2984 for ( n
= sizeof(list
) / sizeof(*list
) - 1; n
>= 0; --n
)
2985 if ( strncasecmp( hostname
, list
[n
], len
) == 0 )
2986 if ( list
[n
][len
] == '/' ) /* check in case of a substring match */
2987 return &list
[n
][len
+ 1];
2990 #endif /* SERVER_NAME_LIST */
2994 really_start_request( httpd_conn
* hc
, struct timeval
* nowP
)
2996 /*static*/ char* indexname
;
2997 /*static*/ size_t maxindexname
= 0;
2998 //static const char* index_names[];
3001 static char* dirname
;
3002 static size_t maxdirname
= 0;
3003 #endif /* AUTH_FILE */
3004 size_t expnlen
, indxlen
;
3009 expnlen
= strlen( hc
->expnfilename
);
3011 if ( hc
->method
!= METHOD_GET
&& hc
->method
!= METHOD_HEAD
/*&&
3012 hc->method != METHOD_POST*/ )
3015 hc
, 501, err501title
, "", err501form
, httpd_method_str( hc
->method
) );
3019 /* Stat the file. */
3020 if ( stat( hc
->expnfilename
, &hc
->sb
) < 0 )
3022 httpd_send_err( hc
, 500, err500title
, "", err500form
, hc
->encodedurl
);
3026 /* Is it world-readable or world-executable? We check explicitly instead
3027 ** of just trying to open it, so that no one ever gets surprised by
3028 ** a file that's not set world-readable and yet somehow is
3029 ** readable by the HTTP server and therefore the *whole* world.
3031 if ( ! ( hc
->sb
.st_mode
& ( S_IROTH
/*| S_IXOTH*/ ) ) )
3035 // "%.80s URL \"%.80s\" resolves to a non world-readable file",
3036 // httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3038 hc
, 403, err403title
, "",
3039 ERROR_FORM( err403form
, "The requested URL '%.80s' resolves to a file that is not world-readable.\n" ),
3044 /* Is it a directory? */
3045 if ( S_ISDIR(hc
->sb
.st_mode
) )
3047 /* If there's pathinfo, it's just a non-existent file. */
3048 if ( hc
->pathinfo
[0] != '\0' )
3050 char logString
[27+B_PATH_NAME_LENGTH
+1];
3051 sprintf(logString
, "Error 404 File not found: %s\n", hc
->decodedurl
+1);
3052 poorman_log(logString
, true, hc
->client_addr
.sa_in
.sin_addr
.s_addr
, RED
);
3053 httpd_send_err( hc
, 404, err404title
, "", err404form
, hc
->encodedurl
);
3057 /* Special handling for directory URLs that don't end in a slash.
3058 ** We send back an explicit redirect with the slash, because
3059 ** otherwise many clients can't build relative URLs properly.
3061 if ( strcmp( hc
->origfilename
, "" ) != 0 &&
3062 strcmp( hc
->origfilename
, "." ) != 0 &&
3063 hc
->origfilename
[strlen( hc
->origfilename
) - 1] != '/' )
3065 send_dirredirect( hc
);
3069 /* Check for an index file. */
3070 if(pthread_rwlock_rdlock(get_index_name_lock()) == 0){
3072 &indexname
, &maxindexname
,
3073 expnlen
+ 1 + strlen( /*index_names[i]*/hc
->hs
->index_name
) );
3074 (void) strcpy( indexname
, hc
->expnfilename
);
3075 indxlen
= strlen( indexname
);
3076 if ( indxlen
== 0 || indexname
[indxlen
- 1] != '/' )
3077 (void) strcat( indexname
, "/" );
3078 if ( strcmp( indexname
, "./" ) == 0 )
3079 indexname
[0] = '\0';
3080 (void) strcat( indexname
, /*index_names[i]*/hc
->hs
->index_name
);
3081 pthread_rwlock_unlock(get_index_name_lock());
3084 httpd_send_err( hc
, 500, err500title
, "", err500form
, hc
->encodedurl
);
3087 if ( stat( indexname
, &hc
->sb
) >= 0 )
3091 /* Nope, no index file, so it's an actual directory request. */
3092 #ifdef GENERATE_INDEXES
3093 if(hc
->hs
->do_list_dir
){
3094 /* Directories must be readable for indexing. */
3095 if ( ! ( hc
->sb
.st_mode
& S_IROTH
) )
3099 // "%.80s URL \"%.80s\" tried to index a directory with indexing disabled",
3100 // httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3102 hc
, 403, err403title
, "",
3103 ERROR_FORM( err403form
, "The requested URL '%.80s' resolves to a directory that has indexing disabled.\n" ),
3108 /* Check authorization for this directory. */
3109 if ( auth_check( hc
, hc
->expnfilename
) == -1 )
3111 #endif /* AUTH_FILE */
3112 /* Referer check. */
3113 if ( ! check_referer( hc
) )
3115 /* Ok, generate an index. */
3117 //#else /* GENERATE_INDEXES */
3120 // LOG_INFO, "%.80s URL \"%.80s\" tried to index a directory",
3121 // httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3123 hc
, 404, err404title
, "",
3128 #endif /* GENERATE_INDEXES */
3131 /* Got an index file. Expand symlinks again. More pathinfo means
3132 ** something went wrong.
3134 cp
= expand_symlinks( indexname
, &freethis
, &pi
, hc
->hs
->no_symlink_check
, hc
->tildemapped
);
3135 if ( cp
== (char*) 0 || pi
[0] != '\0' )
3137 httpd_send_err( hc
, 500, err500title
, "", err500form
, hc
->encodedurl
);
3142 expnlen
= strlen( cp
);
3143 httpd_realloc_str( &hc
->expnfilename
, &hc
->maxexpnfilename
, expnlen
);
3144 (void) strcpy( hc
->expnfilename
, cp
);
3147 /* Now, is the index version world-readable or world-executable? */
3148 if ( ! ( hc
->sb
.st_mode
& ( S_IROTH
| S_IXOTH
) ) )
3152 // "%.80s URL \"%.80s\" resolves to a non-world-readable index file",
3153 // httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3155 hc
, 403, err403title
, "",
3156 ERROR_FORM( err403form
, "The requested URL '%.80s' resolves to an index file that is not world-readable.\n" ),
3160 }/* Is it a directory? */
3163 /* Check authorization for this directory. */
3164 httpd_realloc_str( &dirname
, &maxdirname
, expnlen
);
3165 (void) strcpy( dirname
, hc
->expnfilename
);
3166 cp
= strrchr( dirname
, '/' );
3167 if ( cp
== (char*) 0 )
3168 (void) strcpy( dirname
, "." );
3171 if ( auth_check( hc
, dirname
) == -1 )
3174 /* Check if the filename is the AUTH_FILE itself - that's verboten. */
3175 if ( expnlen
== sizeof(AUTH_FILE
) - 1 )
3177 if ( strcmp( hc
->expnfilename
, AUTH_FILE
) == 0 )
3181 // "%.80s URL \"%.80s\" tried to retrieve an auth file",
3182 // httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3184 hc
, 403, err403title
, "",
3185 ERROR_FORM( err403form
, "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n" ),
3190 else if ( expnlen
>= sizeof(AUTH_FILE
) &&
3191 strcmp( &(hc
->expnfilename
[expnlen
- sizeof(AUTH_FILE
) + 1]), AUTH_FILE
) == 0 &&
3192 hc
->expnfilename
[expnlen
- sizeof(AUTH_FILE
)] == '/' )
3196 // "%.80s URL \"%.80s\" tried to retrieve an auth file",
3197 // httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3199 hc
, 403, err403title
, "",
3200 ERROR_FORM( err403form
, "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n" ),
3204 #endif /* AUTH_FILE */
3206 /* Referer check. */
3207 if ( ! check_referer( hc
) )
3210 /* Is it world-executable and in the CGI area? */
3211 /*if ( hc->hs->cgi_pattern != (char*) 0 &&
3212 ( hc->sb.st_mode & S_IXOTH ) &&
3213 match( hc->hs->cgi_pattern, hc->expnfilename ) )
3216 /* It's not CGI. If it's executable or there's pathinfo, someone's
3217 ** trying to either serve or run a non-CGI file as CGI. Either case
3220 /*if ( hc->sb.st_mode & S_IXOTH )
3223 LOG_NOTICE, "%.80s URL \"%.80s\" is executable but isn't CGI",
3224 httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3226 hc, 403, err403title, "",
3227 ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file which is marked executable but is not a CGI file; retrieving it is forbidden.\n" ),
3231 if ( hc
->pathinfo
[0] != '\0' )
3234 // LOG_INFO, "%.80s URL \"%.80s\" has pathinfo but isn't CGI",
3235 // httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3237 hc
, 403, err403title
, "",
3238 ERROR_FORM( err403form
, "The requested URL '%.80s' resolves to a file plus CGI-style pathinfo, but the file is not a valid CGI file.\n" ),
3243 /* Fill in last_byte_index, if necessary. */
3244 if ( hc
->got_range
&&
3245 ( hc
->last_byte_index
== -1 || hc
->last_byte_index
>= hc
->sb
.st_size
) )
3246 hc
->last_byte_index
= hc
->sb
.st_size
- 1;
3250 if ( hc
->method
== METHOD_HEAD
)
3253 hc
, 200, ok200title
, hc
->encodings
, "", hc
->type
, hc
->sb
.st_size
,
3256 else if ( hc
->if_modified_since
!= (time_t) -1 &&
3257 hc
->if_modified_since
>= hc
->sb
.st_mtime
)
3260 hc
, 304, err304title
, hc
->encodings
, "", hc
->type
, (off_t
) -1,
3266 hc
, 200, ok200title
, hc
->encodings
, "", hc
->type
, hc
->sb
.st_size
,
3275 httpd_start_request( httpd_conn
* hc
, struct timeval
* nowP
)
3279 /* Really start the request. */
3280 r
= really_start_request( hc
, nowP
);
3282 /* And return the status. */
3288 make_log_entry( httpd_conn
* hc
, struct timeval
* nowP
)
3294 if ( hc
->hs
->no_log
)
3297 /* This is straight CERN Combined Log Format - the only tweak
3298 ** being that if we're using syslog() we leave out the date, because
3299 ** syslogd puts it in. The included syslogtocern script turns the
3300 ** results into true CERN format.
3303 /* Format remote user. */
3304 if ( hc
->remoteuser
[0] != '\0' )
3305 ru
= hc
->remoteuser
;
3308 /* If we're vhosting, prepend the hostname to the url. This is
3309 ** a little weird, perhaps writing separate log files for
3310 ** each vhost would make more sense.
3312 if ( hc
->hs
->vhost
&& ! hc
->tildemapped
)
3313 (void) my_snprintf( url
, sizeof(url
),
3315 hc
->hostname
== (char*) 0 ? hc
->hs
->server_hostname
: hc
->hostname
,
3318 (void) my_snprintf( url
, sizeof(url
),
3319 "%.200s", hc
->encodedurl
);
3320 /* Format the bytes. */
3321 if ( hc
->bytes_sent
>= 0 )
3323 bytes
, sizeof(bytes
), "%lld", (int64_t) hc
->bytes_sent
);
3325 (void) strcpy( bytes
, "-" );
3327 /* Logfile or syslog? */
3328 if ( hc
->hs
->logfp
!= (FILE*) 0 )
3332 const char* cernfmt_nozone
= "%d/%b/%Y:%H:%M:%S";
3333 char date_nozone
[100];
3338 /* Get the current time, if necessary. */
3339 if ( nowP
!= (struct timeval
*) 0 )
3342 now
= time( (time_t*) 0 );
3343 /* Format the time, forcing a numeric timezone (some log analyzers
3344 ** are stoooopid about this).
3346 t
= localtime( &now
);
3347 (void) strftime( date_nozone
, sizeof(date_nozone
), cernfmt_nozone
, t
);
3348 #ifdef HAVE_TM_GMTOFF
3349 zone
= t
->tm_gmtoff
/ 60L;
3351 zone
= -timezone
/ 60L;
3352 /* Probably have to add something about daylight time here. */
3361 zone
= ( zone
/ 60 ) * 100 + zone
% 60;
3362 (void) my_snprintf( date
, sizeof(date
),
3363 "%s %c%04d", date_nozone
, sign
, zone
);
3364 /* And write the log entry. */
3365 (void) fprintf( hc
->hs
->logfp
,
3366 "%.80s - %.80s [%s] \"%.80s %.300s %.80s\" %d %s \"%.200s\" \"%.200s\"\n",
3367 httpd_ntoa( &hc
->client_addr
), ru
, date
,
3368 httpd_method_str( hc
->method
), url
, hc
->protocol
,
3369 hc
->status
, bytes
, hc
->referer
, hc
->useragent
);
3370 #ifdef FLUSH_LOG_EVERY_TIME
3371 (void) fflush( hc
->hs
->logfp
);
3376 "%.80s - %.80s \"%.80s %.200s %.80s\" %d %s \"%.200s\" \"%.200s\"",
3377 httpd_ntoa( &hc->client_addr ), ru,
3378 httpd_method_str( hc->method ), url, hc->protocol,
3379 hc->status, bytes, hc->referer, hc->useragent )*/;
3383 /* Returns 1 if ok to serve the url, 0 if not. */
3385 check_referer( httpd_conn
* hc
)
3390 /* Are we doing referer checking at all? */
3391 if ( hc
->hs
->url_pattern
== (char*) 0 )
3394 r
= really_check_referer( hc
);
3398 if ( hc
->hs
->vhost
&& hc
->hostname
!= (char*) 0 )
3401 cp
= hc
->hs
->server_hostname
;
3402 if ( cp
== (char*) 0 )
3405 // LOG_INFO, "%.80s non-local referer \"%.80s%.80s\" \"%.80s\"",
3406 // httpd_ntoa( &hc->client_addr ), cp, hc->encodedurl, hc->referer );
3408 hc
, 403, err403title
, "",
3409 ERROR_FORM( err403form
, "You must supply a local referer to get URL '%.80s' from this server.\n" ),
3416 /* Returns 1 if ok to serve the url, 0 if not. */
3418 really_check_referer( httpd_conn
* hc
)
3424 /*static*/char* refhost
= (char*) 0;
3425 /*static*/size_t refhost_size
= 0;
3430 /* Check for an empty referer. */
3431 if ( hc
->referer
== (char*) 0 || hc
->referer
[0] == '\0' ||
3432 ( cp1
= strstr( hc
->referer
, "//" ) ) == (char*) 0 )
3434 /* Disallow if we require a referer and the url matches. */
3435 if ( hs
->no_empty_referers
&& match( hs
->url_pattern
, hc
->origfilename
) )
3441 /* Extract referer host. */
3443 for ( cp2
= cp1
; *cp2
!= '/' && *cp2
!= ':' && *cp2
!= '\0'; ++cp2
)
3445 httpd_realloc_str( &refhost
, &refhost_size
, cp2
- cp1
);
3446 for ( cp3
= refhost
; cp1
< cp2
; ++cp1
, ++cp3
)
3447 if ( isupper(*cp1
) )
3448 *cp3
= tolower(*cp1
);
3453 /* Local pattern? */
3454 if ( hs
->local_pattern
!= (char*) 0 )
3455 lp
= hs
->local_pattern
;
3458 /* No local pattern. What's our hostname? */
3461 /* Not vhosting, use the server name. */
3462 lp
= hs
->server_hostname
;
3463 if ( lp
== (char*) 0 )
3465 /* Couldn't figure out local hostname - give up. */
3472 /* We are vhosting, use the hostname on this connection. */
3474 if ( lp
== (char*) 0 )
3476 /* Oops, no hostname. Maybe it's an old browser that
3477 ** doesn't send a Host: header. We could figure out
3478 ** the default hostname for this IP address, but it's
3479 ** not worth it for the few requests like this.
3487 /* If the referer host doesn't match the local host pattern, and
3488 ** the filename does match the url pattern, it's an illegal reference.
3490 if ( ! match( lp
, refhost
) && match( hs
->url_pattern
, hc
->origfilename
) )
3502 httpd_ntoa( httpd_sockaddr
* saP
)
3505 static char str
[200];
3507 if ( getnameinfo( &saP
->sa
, sockaddr_len( saP
), str
, sizeof(str
), 0, 0, NI_NUMERICHOST
) != 0 )
3512 else if ( IN6_IS_ADDR_V4MAPPED( &saP
->sa_in6
.sin6_addr
) && strncmp( str
, "::ffff:", 7 ) == 0 )
3513 /* Elide IPv6ish prefix for IPv4 addresses. */
3514 (void) strcpy( str
, &str
[7] );
3518 #else /* USE_IPV6 */
3520 return inet_ntoa( saP
->sa_in
.sin_addr
);
3522 #endif /* USE_IPV6 */
3527 sockaddr_check( httpd_sockaddr
* saP
)
3529 switch ( saP
->sa
.sa_family
)
3531 case AF_INET
: return 1;
3533 case AF_INET6
: return 1;
3534 #endif /* USE_IPV6 */
3542 sockaddr_len( httpd_sockaddr
* saP
)
3544 switch ( saP
->sa
.sa_family
)
3546 case AF_INET
: return sizeof(struct sockaddr_in
);
3548 case AF_INET6
: return sizeof(struct sockaddr_in6
);
3549 #endif /* USE_IPV6 */
3551 return 0; /* shouldn't happen */
3556 /* Some systems don't have snprintf(), so we make our own that uses
3557 ** either vsnprintf() or vsprintf(). If your system doesn't have
3558 ** vsnprintf(), it is probably vulnerable to buffer overruns.
3562 my_snprintf( char* str
, size_t size
, const char* format
, ... )
3567 va_start( ap
, format
);
3568 #ifdef HAVE_VSNPRINTF
3569 r
= vsnprintf( str
, size
, format
, ap
);
3570 #else /* HAVE_VSNPRINTF */
3571 r
= vsprintf( str
, format
, ap
);
3572 #endif /* HAVE_VSNPRINTF */
3580 atoll( const char* str
)
3585 while ( isspace( *str
) )
3589 case '-': sign
= -1; ++str
; break;
3590 case '+': sign
= 1; ++str
; break;
3591 default: sign
= 1; break;
3594 while ( isdigit( *str
) )
3596 value
= value
* 10 + ( *str
- '0' );
3599 return sign
* value
;
3601 #endif /* HAVE_ATOLL */
3604 /* Read the requested buffer completely, accounting for interruptions. */
3606 httpd_read_fully( int fd
, void* buf
, size_t nbytes
)
3611 while ( nread
< nbytes
)
3615 r
= read( fd
, (char*) buf
+ nread
, nbytes
- nread
);
3616 if ( r
< 0 && ( errno
== EINTR
|| errno
== EAGAIN
) )
3632 /* Write the requested buffer completely, accounting for interruptions. */
3634 httpd_write_fully( int fd
, const void* buf
, size_t nbytes
)
3639 while ( nwritten
< nbytes
)
3643 r
= write( fd
, (char*) buf
+ nwritten
, nbytes
- nwritten
);
3644 if ( r
< 0 && ( errno
== EINTR
|| errno
== EAGAIN
) )
3660 /* Generate debugging statistics syslog message. */
3662 httpd_logstats( long secs
)
3664 if ( str_alloc_count
> 0 )
3666 " libhttpd - %d strings allocated, %lu bytes (%g bytes/str)",
3667 str_alloc_count, (unsigned long) str_alloc_size,
3668 (float) str_alloc_size / str_alloc_count )*/;