]>
git.gir.st - ttxd.git/blob - src/thttpd-2.27/libhttpd.c
1 /* libhttpd.c - HTTP protocol library
3 ** Copyright © 1995,1998,1999,2000,2001,2015 by
4 ** Jef Poskanzer <jef@mail.acme.com>. 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 "thttpd"
36 #endif /* SHOW_SERVER_VERSION */
38 #include <sys/types.h>
39 #include <sys/param.h>
48 #endif /* HAVE_MEMORY_H */
58 #ifdef HAVE_OSRELDATE_H
59 #include <osreldate.h>
60 #endif /* HAVE_OSRELDATE_H */
64 # define NAMLEN(dirent) strlen((dirent)->d_name)
66 # define dirent direct
67 # define NAMLEN(dirent) (dirent)->d_namlen
68 # ifdef HAVE_SYS_NDIR_H
69 # include <sys/ndir.h>
71 # ifdef HAVE_SYS_DIR_H
83 #include "tdate_parse.h"
86 #define STDIN_FILENO 0
89 #define STDOUT_FILENO 1
92 #define STDERR_FILENO 2
100 typedef long long int64_t ;
103 #ifndef HAVE_SOCKLENT
104 typedef int socklen_t
;
108 #define timezone _timezone
112 #define MAX(a,b) ((a) > (b) ? (a) : (b))
115 #define MIN(a,b) ((a) < (b) ? (a) : (b))
120 static void check_options ( void );
121 static void free_httpd_server ( httpd_server
* hs
);
122 static int initialize_listen_socket ( httpd_sockaddr
* saP
);
123 static void add_response ( httpd_conn
* hc
, char * str
);
124 static void send_mime ( httpd_conn
* hc
, int status
, char * title
, char * encodings
, char * extraheads
, char * type
, off_t length
, time_t mod
);
125 static void send_response ( httpd_conn
* hc
, int status
, char * title
, char * extraheads
, char * form
, char * arg
);
126 static void send_response_tail ( httpd_conn
* hc
);
127 static void defang ( char * str
, char * dfstr
, int dfsize
);
129 static int send_err_file ( httpd_conn
* hc
, int status
, char * title
, char * extraheads
, char * filename
);
132 static void send_authenticate ( httpd_conn
* hc
, char * realm
);
133 static int b64_decode ( const char * str
, unsigned char * space
, int size
);
134 static int auth_check ( httpd_conn
* hc
, char * dirname
);
135 static int auth_check2 ( httpd_conn
* hc
, char * dirname
);
136 #endif /* AUTH_FILE */
137 static void send_dirredirect ( httpd_conn
* hc
);
138 static int hexit ( char c
);
139 static void strdecode ( char * to
, char * from
);
140 #ifdef GENERATE_INDEXES
141 static void strencode ( char * to
, int tosize
, char * from
);
142 #endif /* GENERATE_INDEXES */
144 static int tilde_map_1 ( httpd_conn
* hc
);
145 #endif /* TILDE_MAP_1 */
147 static int tilde_map_2 ( httpd_conn
* hc
);
148 #endif /* TILDE_MAP_2 */
149 static int vhost_map ( httpd_conn
* hc
);
150 static char * expand_symlinks ( char * path
, char ** restP
, int no_symlink_check
, int tildemapped
);
151 static char * bufgets ( httpd_conn
* hc
);
152 static void de_dotdot ( char * file
);
153 static void init_mime ( void );
154 static void figure_mime ( httpd_conn
* hc
);
156 static void cgi_kill2 ( ClientData client_data
, struct timeval
* nowP
);
157 static void cgi_kill ( ClientData client_data
, struct timeval
* nowP
);
158 #endif /* CGI_TIMELIMIT */
159 #ifdef GENERATE_INDEXES
160 static int ls ( httpd_conn
* hc
);
161 #endif /* GENERATE_INDEXES */
162 static char * build_env ( char * fmt
, char * arg
);
163 #ifdef SERVER_NAME_LIST
164 static char * hostname_map ( char * hostname
);
165 #endif /* SERVER_NAME_LIST */
166 static char ** make_envp ( httpd_conn
* hc
);
167 static char ** make_argp ( httpd_conn
* hc
);
168 static void cgi_interpose_input ( httpd_conn
* hc
, int wfd
);
169 static void post_post_garbage_hack ( httpd_conn
* hc
);
170 static void cgi_interpose_output ( httpd_conn
* hc
, int rfd
);
171 static void cgi_child ( httpd_conn
* hc
);
172 static int cgi ( httpd_conn
* hc
);
173 static int really_start_request ( httpd_conn
* hc
, struct timeval
* nowP
);
174 static void make_log_entry ( httpd_conn
* hc
, struct timeval
* nowP
);
175 static int check_referrer ( httpd_conn
* hc
);
176 static int really_check_referrer ( httpd_conn
* hc
);
177 static int sockaddr_check ( httpd_sockaddr
* saP
);
178 static size_t sockaddr_len ( httpd_sockaddr
* saP
);
179 static int my_snprintf ( char * str
, size_t size
, const char * format
, ... );
181 static long long atoll ( const char * str
);
182 #endif /* HAVE_ATOLL */
185 /* This global keeps track of whether we are in the main process or a
186 ** sub-process. The reason is that httpd_write_response() can get called
187 ** in either context; when it is called from the main process it must use
188 ** non-blocking I/O to avoid stalling the server, but when it is called
189 ** from a sub-process it wants to use blocking I/O so that the whole
190 ** response definitely gets written. So, it checks this variable. A bit
191 ** of a hack but it seems to do the right thing.
193 static int sub_process
= 0 ;
197 check_options ( void )
199 #if defined(TILDE_MAP_1) && defined(TILDE_MAP_2)
200 syslog ( LOG_CRIT
, "both TILDE_MAP_1 and TILDE_MAP_2 are defined" );
207 free_httpd_server ( httpd_server
* hs
)
209 if ( hs
-> binding_hostname
!= ( char *) 0 )
210 free ( ( void *) hs
-> binding_hostname
);
211 if ( hs
-> cwd
!= ( char *) 0 )
212 free ( ( void *) hs
-> cwd
);
213 if ( hs
-> cgi_pattern
!= ( char *) 0 )
214 free ( ( void *) hs
-> cgi_pattern
);
215 if ( hs
-> charset
!= ( char *) 0 )
216 free ( ( void *) hs
-> charset
);
217 if ( hs
-> p3p
!= ( char *) 0 )
218 free ( ( void *) hs
-> p3p
);
219 if ( hs
-> url_pattern
!= ( char *) 0 )
220 free ( ( void *) hs
-> url_pattern
);
221 if ( hs
-> local_pattern
!= ( char *) 0 )
222 free ( ( void *) hs
-> local_pattern
);
229 char * hostname
, httpd_sockaddr
* sa4P
, httpd_sockaddr
* sa6P
,
230 unsigned short port
, char * cgi_pattern
, int cgi_limit
, char * charset
,
231 char * p3p
, int max_age
, char * cwd
, int no_log
, FILE * logfp
,
232 int no_symlink_check
, int vhost
, int global_passwd
, char * url_pattern
,
233 char * local_pattern
, int no_empty_referrers
)
236 static char ghnbuf
[ 256 ];
241 hs
= NEW ( httpd_server
, 1 );
242 if ( hs
== ( httpd_server
*) 0 )
244 syslog ( LOG_CRIT
, "out of memory allocating an httpd_server" );
245 return ( httpd_server
*) 0 ;
248 if ( hostname
!= ( char *) 0 )
250 hs
-> binding_hostname
= strdup ( hostname
);
251 if ( hs
-> binding_hostname
== ( char *) 0 )
253 syslog ( LOG_CRIT
, "out of memory copying hostname" );
254 return ( httpd_server
*) 0 ;
256 hs
-> server_hostname
= hs
-> binding_hostname
;
260 hs
-> binding_hostname
= ( char *) 0 ;
261 hs
-> server_hostname
= ( char *) 0 ;
262 if ( gethostname ( ghnbuf
, sizeof ( ghnbuf
) ) < 0 )
264 #ifdef SERVER_NAME_LIST
265 if ( ghnbuf
[ 0 ] != '\0' )
266 hs
-> server_hostname
= hostname_map ( ghnbuf
);
267 #endif /* SERVER_NAME_LIST */
268 if ( hs
-> server_hostname
== ( char *) 0 )
271 hs
-> server_hostname
= SERVER_NAME
;
272 #else /* SERVER_NAME */
273 if ( ghnbuf
[ 0 ] != '\0' )
274 hs
-> server_hostname
= ghnbuf
;
275 #endif /* SERVER_NAME */
280 if ( cgi_pattern
== ( char *) 0 )
281 hs
-> cgi_pattern
= ( char *) 0 ;
284 /* Nuke any leading slashes. */
285 if ( cgi_pattern
[ 0 ] == '/' )
287 hs
-> cgi_pattern
= strdup ( cgi_pattern
);
288 if ( hs
-> cgi_pattern
== ( char *) 0 )
290 syslog ( LOG_CRIT
, "out of memory copying cgi_pattern" );
291 return ( httpd_server
*) 0 ;
293 /* Nuke any leading slashes in the cgi pattern. */
294 while ( ( cp
= strstr ( hs
-> cgi_pattern
, "|/" ) ) != ( char *) 0 )
295 ( void ) ol_strcpy ( cp
+ 1 , cp
+ 2 );
297 hs
-> cgi_limit
= cgi_limit
;
299 hs
-> charset
= strdup ( charset
);
300 hs
-> p3p
= strdup ( p3p
);
301 hs
-> max_age
= max_age
;
302 hs
-> cwd
= strdup ( cwd
);
303 if ( hs
-> cwd
== ( char *) 0 )
305 syslog ( LOG_CRIT
, "out of memory copying cwd" );
306 return ( httpd_server
*) 0 ;
308 if ( url_pattern
== ( char *) 0 )
309 hs
-> url_pattern
= ( char *) 0 ;
312 hs
-> url_pattern
= strdup ( url_pattern
);
313 if ( hs
-> url_pattern
== ( char *) 0 )
315 syslog ( LOG_CRIT
, "out of memory copying url_pattern" );
316 return ( httpd_server
*) 0 ;
319 if ( local_pattern
== ( char *) 0 )
320 hs
-> local_pattern
= ( char *) 0 ;
323 hs
-> local_pattern
= strdup ( local_pattern
);
324 if ( hs
-> local_pattern
== ( char *) 0 )
326 syslog ( LOG_CRIT
, "out of memory copying local_pattern" );
327 return ( httpd_server
*) 0 ;
331 hs
-> logfp
= ( FILE *) 0 ;
332 httpd_set_logfp ( hs
, logfp
);
333 hs
-> no_symlink_check
= no_symlink_check
;
335 hs
-> global_passwd
= global_passwd
;
336 hs
-> no_empty_referrers
= no_empty_referrers
;
338 /* Initialize listen sockets. Try v6 first because of a Linux peculiarity;
339 ** like some other systems, it has magical v6 sockets that also listen for
340 ** v4, but in Linux if you bind a v4 socket first then the v6 bind fails.
342 if ( sa6P
== ( httpd_sockaddr
*) 0 )
345 hs
-> listen6_fd
= initialize_listen_socket ( sa6P
);
346 if ( sa4P
== ( httpd_sockaddr
*) 0 )
349 hs
-> listen4_fd
= initialize_listen_socket ( sa4P
);
350 /* If we didn't get any valid sockets, fail. */
351 if ( hs
-> listen4_fd
== - 1 && hs
-> listen6_fd
== - 1 )
353 free_httpd_server ( hs
);
354 return ( httpd_server
*) 0 ;
359 /* Done initializing. */
360 if ( hs
-> binding_hostname
== ( char *) 0 )
362 LOG_NOTICE
, "%.80s starting on port %d" , SERVER_SOFTWARE
,
366 LOG_NOTICE
, "%.80s starting on %.80s, port %d" , SERVER_SOFTWARE
,
367 httpd_ntoa ( hs
-> listen4_fd
!= - 1 ? sa4P
: sa6P
),
374 initialize_listen_socket ( httpd_sockaddr
* saP
)
379 /* Check sockaddr. */
380 if ( ! sockaddr_check ( saP
) )
382 syslog ( LOG_CRIT
, "unknown sockaddr family on listen socket" );
387 listen_fd
= socket ( saP
-> sa
. sa_family
, SOCK_STREAM
, 0 );
390 syslog ( LOG_CRIT
, "socket %.80s - %m" , httpd_ntoa ( saP
) );
393 ( void ) fcntl ( listen_fd
, F_SETFD
, 1 );
395 /* Allow reuse of local addresses. */
398 listen_fd
, SOL_SOCKET
, SO_REUSEADDR
, ( char *) & on
,
400 syslog ( LOG_CRIT
, "setsockopt SO_REUSEADDR - %m" );
403 if ( bind ( listen_fd
, & saP
-> sa
, sockaddr_len ( saP
) ) < 0 )
406 LOG_CRIT
, "bind %.80s - %m" , httpd_ntoa ( saP
) );
407 ( void ) close ( listen_fd
);
411 /* Set the listen file descriptor to no-delay / non-blocking mode. */
412 flags
= fcntl ( listen_fd
, F_GETFL
, 0 );
415 syslog ( LOG_CRIT
, "fcntl F_GETFL - %m" );
416 ( void ) close ( listen_fd
);
419 if ( fcntl ( listen_fd
, F_SETFL
, flags
| O_NDELAY
) < 0 )
421 syslog ( LOG_CRIT
, "fcntl O_NDELAY - %m" );
422 ( void ) close ( listen_fd
);
426 /* Start a listen going. */
427 if ( listen ( listen_fd
, LISTEN_BACKLOG
) < 0 )
429 syslog ( LOG_CRIT
, "listen - %m" );
430 ( void ) close ( listen_fd
);
434 /* Use accept filtering, if available. */
435 #ifdef SO_ACCEPTFILTER
437 #if ( __FreeBSD_version >= 411000 )
438 #define ACCEPT_FILTER_NAME "httpready"
440 #define ACCEPT_FILTER_NAME "dataready"
442 struct accept_filter_arg af
;
443 ( void ) bzero ( & af
, sizeof ( af
) );
444 ( void ) strcpy ( af
. af_name
, ACCEPT_FILTER_NAME
);
446 listen_fd
, SOL_SOCKET
, SO_ACCEPTFILTER
, ( char *) & af
, sizeof ( af
) );
448 #endif /* SO_ACCEPTFILTER */
455 httpd_set_logfp ( httpd_server
* hs
, FILE * logfp
)
457 if ( hs
-> logfp
!= ( FILE *) 0 )
458 ( void ) fclose ( hs
-> logfp
);
464 httpd_terminate ( httpd_server
* hs
)
466 httpd_unlisten ( hs
);
467 if ( hs
-> logfp
!= ( FILE *) 0 )
468 ( void ) fclose ( hs
-> logfp
);
469 free_httpd_server ( hs
);
474 httpd_unlisten ( httpd_server
* hs
)
476 if ( hs
-> listen4_fd
!= - 1 )
478 ( void ) close ( hs
-> listen4_fd
);
481 if ( hs
-> listen6_fd
!= - 1 )
483 ( void ) close ( hs
-> listen6_fd
);
489 /* Conditional macro to allow two alternate forms for use in the built-in
490 ** error pages. If EXPLICIT_ERROR_PAGES is defined, the second and more
491 ** explicit error form is used; otherwise, the first and more generic
494 #ifdef EXPLICIT_ERROR_PAGES
495 #define ERROR_FORM(a,b) b
496 #else /* EXPLICIT_ERROR_PAGES */
497 #define ERROR_FORM(a,b) a
498 #endif /* EXPLICIT_ERROR_PAGES */
501 static char * ok200title
= "OK" ;
502 static char * ok206title
= "Partial Content" ;
504 static char * err302title
= "Found" ;
505 static char * err302form
= "The actual URL is '%.80s'. \n " ;
507 static char * err304title
= "Not Modified" ;
509 char * httpd_err400title
= "Bad Request" ;
510 char * httpd_err400form
=
511 "Your request has bad syntax or is inherently impossible to satisfy. \n " ;
514 static char * err401title
= "Unauthorized" ;
515 static char * err401form
=
516 "Authorization required for the URL '%.80s'. \n " ;
517 #endif /* AUTH_FILE */
519 static char * err403title
= "Forbidden" ;
520 #ifndef EXPLICIT_ERROR_PAGES
521 static char * err403form
=
522 "You do not have permission to get URL '%.80s' from this server. \n " ;
523 #endif /* !EXPLICIT_ERROR_PAGES */
525 static char * err404title
= "Not Found" ;
526 static char * err404form
=
527 "The requested URL '%.80s' was not found on this server. \n " ;
529 char * httpd_err408title
= "Request Timeout" ;
530 char * httpd_err408form
=
531 "No request appeared within a reasonable time period. \n " ;
533 static char * err451title
= "Unavailable For Legal Reasons" ;
534 static char * err451form
=
535 "You do not have legal permission to get URL '%.80s' from this server. \n " ;
537 static char * err500title
= "Internal Error" ;
538 static char * err500form
=
539 "There was an unusual problem serving the requested URL '%.80s'. \n " ;
541 static char * err501title
= "Not Implemented" ;
542 static char * err501form
=
543 "The requested method '%.80s' is not implemented by this server. \n " ;
545 char * httpd_err503title
= "Service Temporarily Overloaded" ;
546 char * httpd_err503form
=
547 "The requested URL '%.80s' is temporarily overloaded. Please try again later. \n " ;
550 /* Append a string to the buffer waiting to be sent as response. */
552 add_response ( httpd_conn
* hc
, char * str
)
557 httpd_realloc_str ( & hc
-> response
, & hc
-> maxresponse
, hc
-> responselen
+ len
);
558 ( void ) memmove ( &( hc
-> response
[ hc
-> responselen
]), str
, len
);
559 hc
-> responselen
+= len
;
562 /* Send the buffered response. */
564 httpd_write_response ( httpd_conn
* hc
)
566 /* If we are in a sub-process, turn off no-delay mode. */
568 httpd_clear_ndelay ( hc
-> conn_fd
);
569 /* Send the response, if necessary. */
570 if ( hc
-> responselen
> 0 )
572 ( void ) httpd_write_fully ( hc
-> conn_fd
, hc
-> response
, hc
-> responselen
);
578 /* Set no-delay / non-blocking mode on a socket. */
580 httpd_set_ndelay ( int fd
)
584 flags
= fcntl ( fd
, F_GETFL
, 0 );
587 newflags
= flags
| ( int ) O_NDELAY
;
588 if ( newflags
!= flags
)
589 ( void ) fcntl ( fd
, F_SETFL
, newflags
);
594 /* Clear no-delay / non-blocking mode on a socket. */
596 httpd_clear_ndelay ( int fd
)
600 flags
= fcntl ( fd
, F_GETFL
, 0 );
603 newflags
= flags
& ~ ( int ) O_NDELAY
;
604 if ( newflags
!= flags
)
605 ( void ) fcntl ( fd
, F_SETFL
, newflags
);
611 send_mime ( httpd_conn
* hc
, int status
, char * title
, char * encodings
, char * extraheads
, char * type
, off_t length
, time_t mod
)
614 const char * rfc1123fmt
= "%a, %d %b %Y %H:%M:%S GMT" ;
618 char fixed_type
[ 500 ];
624 hc
-> bytes_to_send
= length
;
627 if ( status
== 200 && hc
-> got_range
&&
628 ( hc
-> last_byte_index
>= hc
-> first_byte_index
) &&
629 ( ( hc
-> last_byte_index
!= length
- 1 ) ||
630 ( hc
-> first_byte_index
!= 0 ) ) &&
631 ( hc
-> range_if
== ( time_t ) - 1 ||
632 hc
-> range_if
== hc
-> sb
. st_mtime
) )
635 hc
-> status
= status
= 206 ;
644 now
= time ( ( time_t *) 0 );
645 if ( mod
== ( time_t ) 0 )
647 ( void ) strftime ( nowbuf
, sizeof ( nowbuf
), rfc1123fmt
, gmtime ( & now
) );
648 ( void ) strftime ( modbuf
, sizeof ( modbuf
), rfc1123fmt
, gmtime ( & mod
) );
650 fixed_type
, sizeof ( fixed_type
), type
, hc
-> hs
-> charset
);
651 ( void ) my_snprintf ( buf
, sizeof ( buf
),
652 "%.20s %d %s \015\012 Server: %s \015\012 Content-Type: %s \015\012 Date: %s \015\012 Last-Modified: %s \015\012 Accept-Ranges: bytes \015\012 Connection: close \015\012 " ,
653 hc
-> protocol
, status
, title
, EXPOSED_SERVER_SOFTWARE
, fixed_type
,
655 add_response ( hc
, buf
);
657 if ( s100
!= 2 && s100
!= 3 )
659 ( void ) my_snprintf ( buf
, sizeof ( buf
),
660 "Cache-Control: no-cache,no-store \015\012 " );
661 add_response ( hc
, buf
);
663 if ( encodings
[ 0 ] != '\0' )
665 ( void ) my_snprintf ( buf
, sizeof ( buf
),
666 "Content-Encoding: %s \015\012 " , encodings
);
667 add_response ( hc
, buf
);
669 if ( partial_content
)
671 ( void ) my_snprintf ( buf
, sizeof ( buf
),
672 "Content-Range: bytes %lld-%lld/%lld \015\012 Content-Length: %lld \015\012 " ,
673 ( long long ) hc
-> first_byte_index
,
674 ( long long ) hc
-> last_byte_index
,
676 ( long long ) ( hc
-> last_byte_index
- hc
-> first_byte_index
+ 1 ) );
677 add_response ( hc
, buf
);
679 else if ( length
>= 0 )
681 ( void ) my_snprintf ( buf
, sizeof ( buf
),
682 "Content-Length: %lld \015\012 " , ( long long ) length
);
683 add_response ( hc
, buf
);
685 if ( hc
-> hs
-> p3p
[ 0 ] != '\0' )
687 ( void ) my_snprintf ( buf
, sizeof ( buf
), "P3P: %s \015\012 " , hc
-> hs
-> p3p
);
688 add_response ( hc
, buf
);
690 if ( hc
-> hs
-> max_age
>= 0 )
692 expires
= now
+ hc
-> hs
-> max_age
;
694 expbuf
, sizeof ( expbuf
), rfc1123fmt
, gmtime ( & expires
) );
695 ( void ) my_snprintf ( buf
, sizeof ( buf
),
696 "Cache-Control: max-age=%d \015\012 Expires: %s \015\012 " ,
697 hc
-> hs
-> max_age
, expbuf
);
698 add_response ( hc
, buf
);
700 if ( extraheads
[ 0 ] != '\0' )
701 add_response ( hc
, extraheads
);
702 add_response ( hc
, " \015\012 " );
707 static int str_alloc_count
= 0 ;
708 static size_t str_alloc_size
= 0 ;
711 httpd_realloc_str ( char ** strP
, size_t * maxsizeP
, size_t size
)
713 if ( * maxsizeP
== 0 )
715 * maxsizeP
= MAX ( 200 , size
+ 100 );
716 * strP
= NEW ( char , * maxsizeP
+ 1 );
718 str_alloc_size
+= * maxsizeP
;
720 else if ( size
> * maxsizeP
)
722 str_alloc_size
-= * maxsizeP
;
723 * maxsizeP
= MAX ( * maxsizeP
* 2 , size
* 5 / 4 );
724 * strP
= RENEW ( * strP
, char , * maxsizeP
+ 1 );
725 str_alloc_size
+= * maxsizeP
;
729 if ( * strP
== ( char *) 0 )
732 LOG_ERR
, "out of memory reallocating a string to %ld bytes" ,
740 send_response ( httpd_conn
* hc
, int status
, char * title
, char * extraheads
, char * form
, char * arg
)
742 char defanged_arg
[ 1000 ], buf
[ 2000 ];
745 hc
, status
, title
, "" , extraheads
, "text/html; charset=%s" , ( off_t
) - 1 ,
747 ( void ) my_snprintf ( buf
, sizeof ( buf
), "\
748 <!DOCTYPE html PUBLIC \" -//W3C//DTD HTML 4.01 Transitional//EN \" \" http://www.w3.org/TR/html4/loose.dtd \" > \n \
753 <meta http-equiv= \" Content-type \" content= \" text/html;charset=UTF-8 \" > \n \
754 <title>%d %s</title> \n \
757 <body bgcolor= \" #cc9999 \" text= \" #000000 \" link= \" #2020ff \" vlink= \" #4040cc \" > \n \
760 status
, title
, status
, title
);
761 add_response ( hc
, buf
);
762 defang ( arg
, defanged_arg
, sizeof ( defanged_arg
) );
763 ( void ) my_snprintf ( buf
, sizeof ( buf
), form
, defanged_arg
);
764 add_response ( hc
, buf
);
765 if ( match ( "**MSIE**" , hc
-> useragent
) )
768 add_response ( hc
, "<!-- \n " );
769 for ( n
= 0 ; n
< 6 ; ++ n
)
770 add_response ( hc
, "Padding so that MSIE deigns to show this error instead of its own canned one. \n " );
771 add_response ( hc
, "--> \n " );
773 send_response_tail ( hc
);
778 send_response_tail ( httpd_conn
* hc
)
782 ( void ) my_snprintf ( buf
, sizeof ( buf
), "\
785 <address><a href= \" %s \" >%s</a></address> \n \
790 SERVER_ADDRESS
, EXPOSED_SERVER_SOFTWARE
);
791 add_response ( hc
, buf
);
796 defang ( char * str
, char * dfstr
, int dfsize
)
801 for ( cp1
= str
, cp2
= dfstr
;
802 * cp1
!= '\0' && cp2
- dfstr
< dfsize
- 5 ;
829 httpd_send_err ( httpd_conn
* hc
, int status
, char * title
, char * extraheads
, char * form
, char * arg
)
835 /* Try virtual host error page. */
836 if ( hc
-> hs
-> vhost
&& hc
-> hostdir
[ 0 ] != '\0' )
838 ( void ) my_snprintf ( filename
, sizeof ( filename
),
839 "%s/%s/err%d.html" , hc
-> hostdir
, ERR_DIR
, status
);
840 if ( send_err_file ( hc
, status
, title
, extraheads
, filename
) )
844 /* Try server-wide error page. */
845 ( void ) my_snprintf ( filename
, sizeof ( filename
),
846 "%s/err%d.html" , ERR_DIR
, status
);
847 if ( send_err_file ( hc
, status
, title
, extraheads
, filename
) )
850 /* Fall back on built-in error page. */
851 send_response ( hc
, status
, title
, extraheads
, form
, arg
);
855 send_response ( hc
, status
, title
, extraheads
, form
, arg
);
863 send_err_file ( httpd_conn
* hc
, int status
, char * title
, char * extraheads
, char * filename
)
869 fp
= fopen ( filename
, "r" );
870 if ( fp
== ( FILE *) 0 )
873 hc
, status
, title
, "" , extraheads
, "text/html; charset=%s" , ( off_t
) - 1 ,
877 r
= fread ( buf
, 1 , sizeof ( buf
) - 1 , fp
);
881 add_response ( hc
, buf
);
885 #ifdef ERR_APPEND_SERVER_INFO
886 send_response_tail ( hc
);
887 #endif /* ERR_APPEND_SERVER_INFO */
897 send_authenticate ( httpd_conn
* hc
, char * realm
)
900 static size_t maxheader
= 0 ;
901 static char headstr
[] = "WWW-Authenticate: Basic realm= \" " ;
904 & header
, & maxheader
, sizeof ( headstr
) + strlen ( realm
) + 3 );
905 ( void ) my_snprintf ( header
, maxheader
, "%s%s \"\015\012 " , headstr
, realm
);
906 httpd_send_err ( hc
, 401 , err401title
, header
, err401form
, hc
-> encodedurl
);
907 /* If the request was a POST then there might still be data to be read,
908 ** so we need to do a lingering close.
910 if ( hc
-> method
== METHOD_POST
)
911 hc
-> should_linger
= 1 ;
915 /* Base-64 decoding. This represents binary data as printable ASCII
916 ** characters. Three 8-bit binary bytes are turned into four 6-bit
919 ** [11111111] [22222222] [33333333]
921 ** [111111] [112222] [222233] [333333]
923 ** Then the 6-bit values are represented using the characters "A-Za-z0-9+/".
926 static int b64_decode_table
[ 256 ] = {
927 - 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 , /* 00-0F */
928 - 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 , /* 10-1F */
929 - 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 , 62 ,- 1 ,- 1 ,- 1 , 63 , /* 20-2F */
930 52 , 53 , 54 , 55 , 56 , 57 , 58 , 59 , 60 , 61 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 , /* 30-3F */
931 - 1 , 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , /* 40-4F */
932 15 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 , 25 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 , /* 50-5F */
933 - 1 , 26 , 27 , 28 , 29 , 30 , 31 , 32 , 33 , 34 , 35 , 36 , 37 , 38 , 39 , 40 , /* 60-6F */
934 41 , 42 , 43 , 44 , 45 , 46 , 47 , 48 , 49 , 50 , 51 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 , /* 70-7F */
935 - 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 , /* 80-8F */
936 - 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 , /* 90-9F */
937 - 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 , /* A0-AF */
938 - 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 , /* B0-BF */
939 - 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 , /* C0-CF */
940 - 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 , /* D0-DF */
941 - 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 , /* E0-EF */
942 - 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 ,- 1 /* F0-FF */
945 /* Do base-64 decoding on a string. Ignore any non-base64 bytes.
946 ** Return the actual number of bytes generated. The decoded size will
947 ** be at most 3/4 the size of the encoded, and may be smaller if there
948 ** are padding characters (blanks, newlines).
951 b64_decode ( const char * str
, unsigned char * space
, int size
)
954 int space_idx
, phase
;
960 for ( cp
= str
; * cp
!= '\0' ; ++ cp
)
962 d
= b64_decode_table
[( int ) (( unsigned char ) * cp
)];
971 c
= ( ( prev_d
<< 2 ) | ( ( d
& 0x30 ) >> 4 ) );
972 if ( space_idx
< size
)
973 space
[ space_idx
++] = c
;
977 c
= ( ( ( prev_d
& 0xf ) << 4 ) | ( ( d
& 0x3c ) >> 2 ) );
978 if ( space_idx
< size
)
979 space
[ space_idx
++] = c
;
983 c
= ( ( ( prev_d
& 0x03 ) << 6 ) | d
);
984 if ( space_idx
< size
)
985 space
[ space_idx
++] = c
;
996 /* Returns -1 == unauthorized, 0 == no auth file, 1 = authorized. */
998 auth_check ( httpd_conn
* hc
, char * dirname
)
1000 if ( hc
-> hs
-> global_passwd
)
1003 if ( hc
-> hs
-> vhost
&& hc
-> hostdir
[ 0 ] != '\0' )
1004 topdir
= hc
-> hostdir
;
1007 switch ( auth_check2 ( hc
, topdir
) )
1015 return auth_check2 ( hc
, dirname
);
1019 /* Returns -1 == unauthorized, 0 == no auth file, 1 = authorized. */
1021 auth_check2 ( httpd_conn
* hc
, char * dirname
)
1023 static char * authpath
;
1024 static size_t maxauthpath
= 0 ;
1033 static char * prevauthpath
;
1034 static size_t maxprevauthpath
= 0 ;
1035 static time_t prevmtime
;
1036 static char * prevuser
;
1037 static size_t maxprevuser
= 0 ;
1038 static char * prevcryp
;
1039 static size_t maxprevcryp
= 0 ;
1041 /* Construct auth filename. */
1043 & authpath
, & maxauthpath
, strlen ( dirname
) + 1 + sizeof ( AUTH_FILE
) );
1044 ( void ) my_snprintf ( authpath
, maxauthpath
, "%s/%s" , dirname
, AUTH_FILE
);
1046 /* Does this directory have an auth file? */
1047 if ( stat ( authpath
, & sb
) < 0 )
1048 /* Nope, let the request go through. */
1051 /* Does this request contain basic authorization info? */
1052 if ( hc
-> authorization
[ 0 ] == '\0' ||
1053 strncmp ( hc
-> authorization
, "Basic " , 6 ) != 0 )
1055 /* Nope, return a 401 Unauthorized. */
1056 send_authenticate ( hc
, dirname
);
1062 &( hc
-> authorization
[ 6 ]), ( unsigned char *) authinfo
,
1063 sizeof ( authinfo
) - 1 );
1065 /* Split into user and password. */
1066 authpass
= strchr ( authinfo
, ':' );
1067 if ( authpass
== ( char *) 0 )
1069 /* No colon? Bogus auth info. */
1070 send_authenticate ( hc
, dirname
);
1074 /* If there are more fields, cut them off. */
1075 colon
= strchr ( authpass
, ':' );
1076 if ( colon
!= ( char *) 0 )
1079 /* See if we have a cached entry and can use it. */
1080 if ( maxprevauthpath
!= 0 &&
1081 strcmp ( authpath
, prevauthpath
) == 0 &&
1082 sb
. st_mtime
== prevmtime
&&
1083 strcmp ( authinfo
, prevuser
) == 0 )
1085 /* Yes. Check against the cached encrypted password. */
1086 if ( strcmp ( crypt ( authpass
, prevcryp
), prevcryp
) == 0 )
1090 & hc
-> remoteuser
, & hc
-> maxremoteuser
, strlen ( authinfo
) );
1091 ( void ) strcpy ( hc
-> remoteuser
, authinfo
);
1097 send_authenticate ( hc
, dirname
);
1102 /* Open the password file. */
1103 fp
= fopen ( authpath
, "r" );
1104 if ( fp
== ( FILE *) 0 )
1106 /* The file exists but we can't open it? Disallow access. */
1108 LOG_ERR
, "%.80s auth file %.80s could not be opened - %m" ,
1109 httpd_ntoa ( & hc
-> client_addr
), authpath
);
1111 hc
, 403 , err403title
, "" ,
1112 ERROR_FORM ( err403form
, "The requested URL '%.80s' is protected by an authentication file, but the authentication file cannot be opened. \n " ),
1118 while ( fgets ( line
, sizeof ( line
), fp
) != ( char *) 0 )
1122 if ( line
[ l
- 1 ] == ' \n ' )
1124 /* Split into user and encrypted password. */
1125 cryp
= strchr ( line
, ':' );
1126 if ( cryp
== ( char *) 0 )
1129 /* Is this the right user? */
1130 if ( strcmp ( line
, authinfo
) == 0 )
1133 ( void ) fclose ( fp
);
1134 /* So is the password right? */
1135 if ( strcmp ( crypt ( authpass
, cryp
), cryp
) == 0 )
1139 & hc
-> remoteuser
, & hc
-> maxremoteuser
, strlen ( line
) );
1140 ( void ) strcpy ( hc
-> remoteuser
, line
);
1141 /* And cache this user's info for next time. */
1143 & prevauthpath
, & maxprevauthpath
, strlen ( authpath
) );
1144 ( void ) strcpy ( prevauthpath
, authpath
);
1145 prevmtime
= sb
. st_mtime
;
1147 & prevuser
, & maxprevuser
, strlen ( authinfo
) );
1148 ( void ) strcpy ( prevuser
, authinfo
);
1149 httpd_realloc_str ( & prevcryp
, & maxprevcryp
, strlen ( cryp
) );
1150 ( void ) strcpy ( prevcryp
, cryp
);
1156 send_authenticate ( hc
, dirname
);
1162 /* Didn't find that user. Access denied. */
1163 ( void ) fclose ( fp
);
1164 send_authenticate ( hc
, dirname
);
1168 #endif /* AUTH_FILE */
1172 send_dirredirect ( httpd_conn
* hc
)
1174 static char * location
;
1175 static char * header
;
1176 static size_t maxlocation
= 0 , maxheader
= 0 ;
1177 static char headstr
[] = "Location: " ;
1179 if ( hc
-> query
[ 0 ] != '\0' )
1181 char * cp
= strchr ( hc
-> encodedurl
, '?' );
1182 if ( cp
!= ( char *) 0 ) /* should always find it */
1185 & location
, & maxlocation
,
1186 strlen ( hc
-> encodedurl
) + 2 + strlen ( hc
-> query
) );
1187 ( void ) my_snprintf ( location
, maxlocation
,
1188 "%s/?%s" , hc
-> encodedurl
, hc
-> query
);
1193 & location
, & maxlocation
, strlen ( hc
-> encodedurl
) + 1 );
1194 ( void ) my_snprintf ( location
, maxlocation
,
1195 "%s/" , hc
-> encodedurl
);
1198 & header
, & maxheader
, sizeof ( headstr
) + strlen ( location
) );
1199 ( void ) my_snprintf ( header
, maxheader
,
1200 "%s%s \015\012 " , headstr
, location
);
1201 send_response ( hc
, 302 , err302title
, header
, err302form
, location
);
1206 httpd_method_str ( int method
)
1210 case METHOD_GET
: return "GET" ;
1211 case METHOD_HEAD
: return "HEAD" ;
1212 case METHOD_POST
: return "POST" ;
1213 default : return "UNKNOWN" ;
1221 if ( c
>= '0' && c
<= '9' )
1223 if ( c
>= 'a' && c
<= 'f' )
1224 return c
- 'a' + 10 ;
1225 if ( c
>= 'A' && c
<= 'F' )
1226 return c
- 'A' + 10 ;
1227 return 0 ; /* shouldn't happen, we're guarded by isxdigit() */
1231 /* Copies and decodes a string. It's ok for from and to to be the
1235 strdecode ( char * to
, char * from
)
1237 for ( ; * from
!= '\0' ; ++ to
, ++ from
)
1239 if ( from
[ 0 ] == '%' && isxdigit ( from
[ 1 ] ) && isxdigit ( from
[ 2 ] ) )
1241 * to
= hexit ( from
[ 1 ] ) * 16 + hexit ( from
[ 2 ] );
1251 #ifdef GENERATE_INDEXES
1252 /* Copies and encodes a string. */
1254 strencode ( char * to
, int tosize
, char * from
)
1258 for ( tolen
= 0 ; * from
!= '\0' && tolen
+ 4 < tosize
; ++ from
)
1260 if ( isalnum (* from
) || strchr ( "/_.-~" , * from
) != ( char *) 0 )
1268 ( void ) sprintf ( to
, "%%%02x" , ( int ) * from
& 0xff );
1275 #endif /* GENERATE_INDEXES */
1279 /* Map a ~username/whatever URL into <prefix>/username. */
1281 tilde_map_1 ( httpd_conn
* hc
)
1284 static size_t maxtemp
= 0 ;
1286 static char * prefix
= TILDE_MAP_1
;
1288 len
= strlen ( hc
-> expnfilename
) - 1 ;
1289 httpd_realloc_str ( & temp
, & maxtemp
, len
);
1290 ( void ) strcpy ( temp
, & hc
-> expnfilename
[ 1 ] );
1292 & hc
-> expnfilename
, & hc
-> maxexpnfilename
, strlen ( prefix
) + 1 + len
);
1293 ( void ) strcpy ( hc
-> expnfilename
, prefix
);
1294 if ( prefix
[ 0 ] != '\0' )
1295 ( void ) strcat ( hc
-> expnfilename
, "/" );
1296 ( void ) strcat ( hc
-> expnfilename
, temp
);
1299 #endif /* TILDE_MAP_1 */
1302 /* Map a ~username/whatever URL into <user's homedir>/<postfix>. */
1304 tilde_map_2 ( httpd_conn
* hc
)
1307 static size_t maxtemp
= 0 ;
1308 static char * postfix
= TILDE_MAP_2
;
1314 /* Get the username. */
1315 httpd_realloc_str ( & temp
, & maxtemp
, strlen ( hc
-> expnfilename
) - 1 );
1316 ( void ) strcpy ( temp
, & hc
-> expnfilename
[ 1 ] );
1317 cp
= strchr ( temp
, '/' );
1318 if ( cp
!= ( char *) 0 )
1323 /* Get the passwd entry. */
1324 pw
= getpwnam ( temp
);
1325 if ( pw
== ( struct passwd
*) 0 )
1328 /* Set up altdir. */
1330 & hc
-> altdir
, & hc
-> maxaltdir
,
1331 strlen ( pw
-> pw_dir
) + 1 + strlen ( postfix
) );
1332 ( void ) strcpy ( hc
-> altdir
, pw
-> pw_dir
);
1333 if ( postfix
[ 0 ] != '\0' )
1335 ( void ) strcat ( hc
-> altdir
, "/" );
1336 ( void ) strcat ( hc
-> altdir
, postfix
);
1338 alt
= expand_symlinks ( hc
-> altdir
, & rest
, 0 , 1 );
1339 if ( rest
[ 0 ] != '\0' )
1341 httpd_realloc_str ( & hc
-> altdir
, & hc
-> maxaltdir
, strlen ( alt
) );
1342 ( void ) strcpy ( hc
-> altdir
, alt
);
1344 /* And the filename becomes altdir plus the post-~ part of the original. */
1346 & hc
-> expnfilename
, & hc
-> maxexpnfilename
,
1347 strlen ( hc
-> altdir
) + 1 + strlen ( cp
) );
1348 ( void ) my_snprintf ( hc
-> expnfilename
, hc
-> maxexpnfilename
,
1349 "%s/%s" , hc
-> altdir
, cp
);
1351 /* For this type of tilde mapping, we want to defeat vhost mapping. */
1352 hc
-> tildemapped
= 1 ;
1356 #endif /* TILDE_MAP_2 */
1359 /* Virtual host mapping. */
1361 vhost_map ( httpd_conn
* hc
)
1365 static char * tempfilename
;
1366 static size_t maxtempfilename
= 0 ;
1369 #ifdef VHOST_DIRLEVELS
1372 #endif /* VHOST_DIRLEVELS */
1374 /* Figure out the virtual hostname. */
1375 if ( hc
-> reqhost
[ 0 ] != '\0' )
1376 hc
-> hostname
= hc
-> reqhost
;
1377 else if ( hc
-> hdrhost
[ 0 ] != '\0' )
1378 hc
-> hostname
= hc
-> hdrhost
;
1382 if ( getsockname ( hc
-> conn_fd
, & sa
. sa
, & sz
) < 0 )
1384 syslog ( LOG_ERR
, "getsockname - %m" );
1387 hc
-> hostname
= httpd_ntoa ( & sa
);
1389 /* Pound it to lower case. */
1390 for ( cp1
= hc
-> hostname
; * cp1
!= '\0' ; ++ cp1
)
1391 if ( isupper ( * cp1
) )
1392 * cp1
= tolower ( * cp1
);
1394 if ( hc
-> tildemapped
)
1397 /* Figure out the host directory. */
1398 #ifdef VHOST_DIRLEVELS
1400 & hc
-> hostdir
, & hc
-> maxhostdir
,
1401 strlen ( hc
-> hostname
) + 2 * VHOST_DIRLEVELS
);
1402 if ( strncmp ( hc
-> hostname
, "www." , 4 ) == 0 )
1403 cp1
= & hc
-> hostname
[ 4 ];
1406 for ( cp2
= hc
-> hostdir
, i
= 0 ; i
< VHOST_DIRLEVELS
; ++ i
)
1408 /* Skip dots in the hostname. If we don't, then we get vhost
1409 ** directories in higher level of filestructure if dot gets
1410 ** involved into path construction. It's `while' used here instead
1411 ** of `if' for it's possible to have a hostname formed with two
1412 ** dots at the end of it.
1414 while ( * cp1
== '.' )
1416 /* Copy a character from the hostname, or '_' if we ran out. */
1424 ( void ) strcpy ( cp2
, hc
-> hostname
);
1425 #else /* VHOST_DIRLEVELS */
1426 httpd_realloc_str ( & hc
-> hostdir
, & hc
-> maxhostdir
, strlen ( hc
-> hostname
) );
1427 ( void ) strcpy ( hc
-> hostdir
, hc
-> hostname
);
1428 #endif /* VHOST_DIRLEVELS */
1430 /* Prepend hostdir to the filename. */
1431 len
= strlen ( hc
-> expnfilename
);
1432 httpd_realloc_str ( & tempfilename
, & maxtempfilename
, len
);
1433 ( void ) strcpy ( tempfilename
, hc
-> expnfilename
);
1435 & hc
-> expnfilename
, & hc
-> maxexpnfilename
,
1436 strlen ( hc
-> hostdir
) + 1 + len
);
1437 ( void ) strcpy ( hc
-> expnfilename
, hc
-> hostdir
);
1438 ( void ) strcat ( hc
-> expnfilename
, "/" );
1439 ( void ) strcat ( hc
-> expnfilename
, tempfilename
);
1444 /* Expands all symlinks in the given filename, eliding ..'s and leading /'s.
1445 ** Returns the expanded path (pointer to static string), or (char*) 0 on
1446 ** errors. Also returns, in the string pointed to by restP, any trailing
1447 ** parts of the path that don't exist.
1449 ** This is a fairly nice little routine. It handles any size filenames
1450 ** without excessive mallocs.
1453 expand_symlinks ( char * path
, char ** restP
, int no_symlink_check
, int tildemapped
)
1455 static char * checked
;
1458 static size_t maxchecked
= 0 , maxrest
= 0 ;
1459 size_t checkedlen
, restlen
, linklen
, prevcheckedlen
, prevrestlen
;
1465 if ( no_symlink_check
)
1467 /* If we are chrooted, we can actually skip the symlink-expansion,
1468 ** since it's impossible to get out of the tree. However, we still
1469 ** need to do the pathinfo check, and the existing symlink expansion
1470 ** code is a pretty reasonable way to do this. So, what we do is
1471 ** a single stat() of the whole filename - if it exists, then we
1472 ** return it as is with nothing in restP. If it doesn't exist, we
1473 ** fall through to the existing code.
1475 ** One side-effect of this is that users can't symlink to central
1476 ** approved CGIs any more. The workaround is to use the central
1477 ** URL for the CGI instead of a local symlinked one.
1480 if ( stat ( path
, & sb
) != - 1 )
1482 checkedlen
= strlen ( path
);
1483 httpd_realloc_str ( & checked
, & maxchecked
, checkedlen
);
1484 ( void ) strcpy ( checked
, path
);
1485 /* Trim trailing slashes. */
1486 while ( checked
[ checkedlen
- 1 ] == '/' )
1488 checked
[ checkedlen
- 1 ] = '\0' ;
1491 httpd_realloc_str ( & rest
, & maxrest
, 0 );
1498 /* Start out with nothing in checked and the whole filename in rest. */
1499 httpd_realloc_str ( & checked
, & maxchecked
, 1 );
1502 restlen
= strlen ( path
);
1503 httpd_realloc_str ( & rest
, & maxrest
, restlen
);
1504 ( void ) strcpy ( rest
, path
);
1505 if ( rest
[ restlen
- 1 ] == '/' )
1506 rest
[-- restlen
] = '\0' ; /* trim trailing slash */
1507 if ( ! tildemapped
)
1508 /* Remove any leading slashes. */
1509 while ( rest
[ 0 ] == '/' )
1511 ( void ) ol_strcpy ( rest
, &( rest
[ 1 ]) );
1517 /* While there are still components to check... */
1518 while ( restlen
> 0 )
1520 /* Save current checkedlen in case we get a symlink. Save current
1521 ** restlen in case we get a non-existant component.
1523 prevcheckedlen
= checkedlen
;
1524 prevrestlen
= restlen
;
1526 /* Grab one component from r and transfer it to checked. */
1527 cp1
= strchr ( r
, '/' );
1528 if ( cp1
!= ( char *) 0 )
1533 /* Special case for absolute paths. */
1534 httpd_realloc_str ( & checked
, & maxchecked
, checkedlen
+ 1 );
1535 ( void ) strncpy ( & checked
[ checkedlen
], r
, 1 );
1538 else if ( strncmp ( r
, ".." , MAX ( i
, 2 ) ) == 0 )
1540 /* Ignore ..'s that go above the start of the path. */
1541 if ( checkedlen
!= 0 )
1543 cp2
= strrchr ( checked
, '/' );
1544 if ( cp2
== ( char *) 0 )
1546 else if ( cp2
== checked
)
1549 checkedlen
= cp2
- checked
;
1554 httpd_realloc_str ( & checked
, & maxchecked
, checkedlen
+ 1 + i
);
1555 if ( checkedlen
> 0 && checked
[ checkedlen
- 1 ] != '/' )
1556 checked
[ checkedlen
++] = '/' ;
1557 ( void ) strncpy ( & checked
[ checkedlen
], r
, i
);
1560 checked
[ checkedlen
] = '\0' ;
1566 /* No slashes remaining, r is all one component. */
1567 if ( strcmp ( r
, ".." ) == 0 )
1569 /* Ignore ..'s that go above the start of the path. */
1570 if ( checkedlen
!= 0 )
1572 cp2
= strrchr ( checked
, '/' );
1573 if ( cp2
== ( char *) 0 )
1575 else if ( cp2
== checked
)
1578 checkedlen
= cp2
- checked
;
1579 checked
[ checkedlen
] = '\0' ;
1585 & checked
, & maxchecked
, checkedlen
+ 1 + restlen
);
1586 if ( checkedlen
> 0 && checked
[ checkedlen
- 1 ] != '/' )
1587 checked
[ checkedlen
++] = '/' ;
1588 ( void ) strcpy ( & checked
[ checkedlen
], r
);
1589 checkedlen
+= restlen
;
1595 /* Try reading the current filename as a symlink */
1596 if ( checked
[ 0 ] == '\0' )
1598 linklen
= readlink ( checked
, lnk
, sizeof ( lnk
) - 1 );
1599 if ( linklen
== - 1 )
1601 if ( errno
== EINVAL
)
1602 continue ; /* not a symlink */
1603 if ( errno
== EACCES
|| errno
== ENOENT
|| errno
== ENOTDIR
)
1605 /* That last component was bogus. Restore and return. */
1606 * restP
= r
- ( prevrestlen
- restlen
);
1607 if ( prevcheckedlen
== 0 )
1608 ( void ) strcpy ( checked
, "." );
1610 checked
[ prevcheckedlen
] = '\0' ;
1613 syslog ( LOG_ERR
, "readlink %.80s - %m" , checked
);
1617 if ( nlinks
> MAX_LINKS
)
1619 syslog ( LOG_ERR
, "too many symlinks in %.80s" , path
);
1622 lnk
[ linklen
] = '\0' ;
1623 if ( lnk
[ linklen
- 1 ] == '/' )
1624 lnk
[-- linklen
] = '\0' ; /* trim trailing slash */
1626 /* Insert the link contents in front of the rest of the filename. */
1629 ( void ) ol_strcpy ( rest
, r
);
1630 httpd_realloc_str ( & rest
, & maxrest
, restlen
+ linklen
+ 1 );
1631 for ( i
= restlen
; i
>= 0 ; -- i
)
1632 rest
[ i
+ linklen
+ 1 ] = rest
[ i
];
1633 ( void ) strcpy ( rest
, lnk
);
1634 rest
[ linklen
] = '/' ;
1635 restlen
+= linklen
+ 1 ;
1640 /* There's nothing left in the filename, so the link contents
1641 ** becomes the rest.
1643 httpd_realloc_str ( & rest
, & maxrest
, linklen
);
1644 ( void ) strcpy ( rest
, lnk
);
1649 if ( rest
[ 0 ] == '/' )
1651 /* There must have been an absolute symlink - zero out checked. */
1657 /* Re-check this component. */
1658 checkedlen
= prevcheckedlen
;
1659 checked
[ checkedlen
] = '\0' ;
1665 if ( checked
[ 0 ] == '\0' )
1666 ( void ) strcpy ( checked
, "." );
1672 httpd_get_conn ( httpd_server
* hs
, int listen_fd
, httpd_conn
* hc
)
1677 if ( ! hc
-> initialized
)
1680 httpd_realloc_str ( & hc
-> read_buf
, & hc
-> read_size
, 500 );
1682 hc
-> maxorigfilename
= hc
-> maxexpnfilename
= hc
-> maxencodings
=
1683 hc
-> maxpathinfo
= hc
-> maxquery
= hc
-> maxaccept
=
1684 hc
-> maxaccepte
= hc
-> maxreqhost
= hc
-> maxhostdir
=
1685 hc
-> maxremoteuser
= hc
-> maxresponse
= 0 ;
1688 #endif /* TILDE_MAP_2 */
1689 httpd_realloc_str ( & hc
-> decodedurl
, & hc
-> maxdecodedurl
, 1 );
1690 httpd_realloc_str ( & hc
-> origfilename
, & hc
-> maxorigfilename
, 1 );
1691 httpd_realloc_str ( & hc
-> expnfilename
, & hc
-> maxexpnfilename
, 0 );
1692 httpd_realloc_str ( & hc
-> encodings
, & hc
-> maxencodings
, 0 );
1693 httpd_realloc_str ( & hc
-> pathinfo
, & hc
-> maxpathinfo
, 0 );
1694 httpd_realloc_str ( & hc
-> query
, & hc
-> maxquery
, 0 );
1695 httpd_realloc_str ( & hc
-> accept
, & hc
-> maxaccept
, 0 );
1696 httpd_realloc_str ( & hc
-> accepte
, & hc
-> maxaccepte
, 0 );
1697 httpd_realloc_str ( & hc
-> reqhost
, & hc
-> maxreqhost
, 0 );
1698 httpd_realloc_str ( & hc
-> hostdir
, & hc
-> maxhostdir
, 0 );
1699 httpd_realloc_str ( & hc
-> remoteuser
, & hc
-> maxremoteuser
, 0 );
1700 httpd_realloc_str ( & hc
-> response
, & hc
-> maxresponse
, 0 );
1702 httpd_realloc_str ( & hc
-> altdir
, & hc
-> maxaltdir
, 0 );
1703 #endif /* TILDE_MAP_2 */
1704 hc
-> initialized
= 1 ;
1707 /* Accept the new connection. */
1709 hc
-> conn_fd
= accept ( listen_fd
, & sa
. sa
, & sz
);
1710 if ( hc
-> conn_fd
< 0 )
1712 if ( errno
== EWOULDBLOCK
)
1714 /* ECONNABORTED means the connection was closed by the client while
1715 ** it was waiting in the listen queue. It's not worth logging.
1717 if ( errno
!= ECONNABORTED
)
1718 syslog ( LOG_ERR
, "accept - %m" );
1721 if ( ! sockaddr_check ( & sa
) )
1723 syslog ( LOG_ERR
, "unknown sockaddr family" );
1724 close ( hc
-> conn_fd
);
1728 ( void ) fcntl ( hc
-> conn_fd
, F_SETFD
, 1 );
1730 ( void ) memset ( & hc
-> client_addr
, 0 , sizeof ( hc
-> client_addr
) );
1731 ( void ) memmove ( & hc
-> client_addr
, & sa
, sockaddr_len ( & sa
) );
1733 hc
-> checked_idx
= 0 ;
1734 hc
-> checked_state
= CHST_FIRSTWORD
;
1735 hc
-> method
= METHOD_UNKNOWN
;
1737 hc
-> bytes_to_send
= 0 ;
1739 hc
-> encodedurl
= "" ;
1740 hc
-> decodedurl
[ 0 ] = '\0' ;
1741 hc
-> protocol
= "UNKNOWN" ;
1742 hc
-> origfilename
[ 0 ] = '\0' ;
1743 hc
-> expnfilename
[ 0 ] = '\0' ;
1744 hc
-> encodings
[ 0 ] = '\0' ;
1745 hc
-> pathinfo
[ 0 ] = '\0' ;
1746 hc
-> query
[ 0 ] = '\0' ;
1749 hc
-> accept
[ 0 ] = '\0' ;
1750 hc
-> accepte
[ 0 ] = '\0' ;
1753 hc
-> contenttype
= "" ;
1754 hc
-> reqhost
[ 0 ] = '\0' ;
1756 hc
-> hostdir
[ 0 ] = '\0' ;
1757 hc
-> authorization
= "" ;
1758 hc
-> remoteuser
[ 0 ] = '\0' ;
1759 hc
-> response
[ 0 ] = '\0' ;
1761 hc
-> altdir
[ 0 ] = '\0' ;
1762 #endif /* TILDE_MAP_2 */
1763 hc
-> responselen
= 0 ;
1764 hc
-> if_modified_since
= ( time_t ) - 1 ;
1765 hc
-> range_if
= ( time_t ) - 1 ;
1766 hc
-> contentlength
= - 1 ;
1768 hc
-> hostname
= ( char *) 0 ;
1772 hc
-> tildemapped
= 0 ;
1773 hc
-> first_byte_index
= 0 ;
1774 hc
-> last_byte_index
= - 1 ;
1776 hc
-> should_linger
= 0 ;
1777 hc
-> file_address
= ( char *) 0 ;
1782 /* Checks hc->read_buf to see whether a complete request has been read so far;
1783 ** either the first line has two words (an HTTP/0.9 request), or the first
1784 ** line has three words and there's a blank line present.
1786 ** hc->read_idx is how much has been read in; hc->checked_idx is how much we
1787 ** have checked so far; and hc->checked_state is the current state of the
1788 ** finite state machine.
1791 httpd_got_request ( httpd_conn
* hc
)
1795 for ( ; hc
-> checked_idx
< hc
-> read_idx
; ++ hc
-> checked_idx
)
1797 c
= hc
-> read_buf
[ hc
-> checked_idx
];
1798 switch ( hc
-> checked_state
)
1800 case CHST_FIRSTWORD
:
1803 case ' ' : case ' \t ' :
1804 hc
-> checked_state
= CHST_FIRSTWS
;
1806 case ' \012 ' : case ' \015 ' :
1807 hc
-> checked_state
= CHST_BOGUS
;
1808 return GR_BAD_REQUEST
;
1814 case ' ' : case ' \t ' :
1816 case ' \012 ' : case ' \015 ' :
1817 hc
-> checked_state
= CHST_BOGUS
;
1818 return GR_BAD_REQUEST
;
1820 hc
-> checked_state
= CHST_SECONDWORD
;
1824 case CHST_SECONDWORD
:
1827 case ' ' : case ' \t ' :
1828 hc
-> checked_state
= CHST_SECONDWS
;
1830 case ' \012 ' : case ' \015 ' :
1831 /* The first line has only two words - an HTTP/0.9 request. */
1832 return GR_GOT_REQUEST
;
1838 case ' ' : case ' \t ' :
1840 case ' \012 ' : case ' \015 ' :
1841 hc
-> checked_state
= CHST_BOGUS
;
1842 return GR_BAD_REQUEST
;
1844 hc
-> checked_state
= CHST_THIRDWORD
;
1848 case CHST_THIRDWORD
:
1851 case ' ' : case ' \t ' :
1852 hc
-> checked_state
= CHST_THIRDWS
;
1855 hc
-> checked_state
= CHST_LF
;
1858 hc
-> checked_state
= CHST_CR
;
1865 case ' ' : case ' \t ' :
1868 hc
-> checked_state
= CHST_LF
;
1871 hc
-> checked_state
= CHST_CR
;
1874 hc
-> checked_state
= CHST_BOGUS
;
1875 return GR_BAD_REQUEST
;
1882 hc
-> checked_state
= CHST_LF
;
1885 hc
-> checked_state
= CHST_CR
;
1893 /* Two newlines in a row - a blank line - end of request. */
1894 return GR_GOT_REQUEST
;
1896 hc
-> checked_state
= CHST_CR
;
1899 hc
-> checked_state
= CHST_LINE
;
1907 hc
-> checked_state
= CHST_CRLF
;
1910 /* Two returns in a row - end of request. */
1911 return GR_GOT_REQUEST
;
1913 hc
-> checked_state
= CHST_LINE
;
1921 /* Two newlines in a row - end of request. */
1922 return GR_GOT_REQUEST
;
1924 hc
-> checked_state
= CHST_CRLFCR
;
1927 hc
-> checked_state
= CHST_LINE
;
1934 case ' \012 ' : case ' \015 ' :
1935 /* Two CRLFs or two CRs in a row - end of request. */
1936 return GR_GOT_REQUEST
;
1938 hc
-> checked_state
= CHST_LINE
;
1943 return GR_BAD_REQUEST
;
1946 return GR_NO_REQUEST
;
1951 httpd_parse_request ( httpd_conn
* hc
)
1962 hc
-> checked_idx
= 0 ; /* reset */
1963 method_str
= bufgets ( hc
);
1964 url
= strpbrk ( method_str
, " \t\012\015 " );
1965 if ( url
== ( char *) 0 )
1967 httpd_send_err ( hc
, 400 , httpd_err400title
, "" , httpd_err400form
, "" );
1971 url
+= strspn ( url
, " \t\012\015 " );
1972 protocol
= strpbrk ( url
, " \t\012\015 " );
1973 if ( protocol
== ( char *) 0 )
1975 protocol
= "HTTP/0.9" ;
1981 protocol
+= strspn ( protocol
, " \t\012\015 " );
1982 if ( * protocol
!= '\0' )
1984 eol
= strpbrk ( protocol
, " \t\012\015 " );
1985 if ( eol
!= ( char *) 0 )
1987 if ( strcasecmp ( protocol
, "HTTP/1.0" ) != 0 )
1991 hc
-> protocol
= protocol
;
1993 /* Check for HTTP/1.1 absolute URL. */
1994 if ( strncasecmp ( url
, "http://" , 7 ) == 0 )
1996 if ( ! hc
-> one_one
)
1998 httpd_send_err ( hc
, 400 , httpd_err400title
, "" , httpd_err400form
, "" );
2002 url
= strchr ( reqhost
, '/' );
2003 if ( url
== ( char *) 0 )
2005 httpd_send_err ( hc
, 400 , httpd_err400title
, "" , httpd_err400form
, "" );
2009 if ( strchr ( reqhost
, '/' ) != ( char *) 0 || reqhost
[ 0 ] == '.' )
2011 httpd_send_err ( hc
, 400 , httpd_err400title
, "" , httpd_err400form
, "" );
2014 httpd_realloc_str ( & hc
-> reqhost
, & hc
-> maxreqhost
, strlen ( reqhost
) );
2015 ( void ) strcpy ( hc
-> reqhost
, reqhost
);
2021 httpd_send_err ( hc
, 400 , httpd_err400title
, "" , httpd_err400form
, "" );
2025 if ( strcasecmp ( method_str
, httpd_method_str ( METHOD_GET
) ) == 0 )
2026 hc
-> method
= METHOD_GET
;
2027 else if ( strcasecmp ( method_str
, httpd_method_str ( METHOD_HEAD
) ) == 0 )
2028 hc
-> method
= METHOD_HEAD
;
2029 else if ( strcasecmp ( method_str
, httpd_method_str ( METHOD_POST
) ) == 0 )
2030 hc
-> method
= METHOD_POST
;
2033 httpd_send_err ( hc
, 501 , err501title
, "" , err501form
, method_str
);
2037 hc
-> encodedurl
= url
;
2039 & hc
-> decodedurl
, & hc
-> maxdecodedurl
, strlen ( hc
-> encodedurl
) );
2040 strdecode ( hc
-> decodedurl
, hc
-> encodedurl
);
2043 & hc
-> origfilename
, & hc
-> maxorigfilename
, strlen ( hc
-> decodedurl
) );
2044 ( void ) strcpy ( hc
-> origfilename
, & hc
-> decodedurl
[ 1 ] );
2045 /* Special case for top-level URL. */
2046 if ( hc
-> origfilename
[ 0 ] == '\0' )
2047 ( void ) strcpy ( hc
-> origfilename
, "." );
2049 /* Extract query string from encoded URL. */
2050 cp
= strchr ( hc
-> encodedurl
, '?' );
2051 if ( cp
!= ( char *) 0 )
2054 httpd_realloc_str ( & hc
-> query
, & hc
-> maxquery
, strlen ( cp
) );
2055 ( void ) strcpy ( hc
-> query
, cp
);
2056 /* Remove query from (decoded) origfilename. */
2057 cp
= strchr ( hc
-> origfilename
, '?' );
2058 if ( cp
!= ( char *) 0 )
2062 de_dotdot ( hc
-> origfilename
);
2063 if ( hc
-> origfilename
[ 0 ] == '/' ||
2064 ( hc
-> origfilename
[ 0 ] == '.' && hc
-> origfilename
[ 1 ] == '.' &&
2065 ( hc
-> origfilename
[ 2 ] == '\0' || hc
-> origfilename
[ 2 ] == '/' ) ) )
2067 httpd_send_err ( hc
, 400 , httpd_err400title
, "" , httpd_err400form
, "" );
2071 if ( hc
-> mime_flag
)
2073 /* Read the MIME headers. */
2074 while ( ( buf
= bufgets ( hc
) ) != ( char *) 0 )
2076 if ( buf
[ 0 ] == '\0' )
2078 if ( strncasecmp ( buf
, "Referer:" , 8 ) == 0 )
2081 cp
+= strspn ( cp
, " \t " );
2084 else if ( strncasecmp ( buf
, "Referrer:" , 9 ) == 0 )
2087 cp
+= strspn ( cp
, " \t " );
2090 else if ( strncasecmp ( buf
, "User-Agent:" , 11 ) == 0 )
2093 cp
+= strspn ( cp
, " \t " );
2096 else if ( strncasecmp ( buf
, "Host:" , 5 ) == 0 )
2099 cp
+= strspn ( cp
, " \t " );
2101 cp
= strchr ( hc
-> hdrhost
, ':' );
2102 if ( cp
!= ( char *) 0 )
2104 if ( strchr ( hc
-> hdrhost
, '/' ) != ( char *) 0 || hc
-> hdrhost
[ 0 ] == '.' )
2106 httpd_send_err ( hc
, 400 , httpd_err400title
, "" , httpd_err400form
, "" );
2110 else if ( strncasecmp ( buf
, "Accept:" , 7 ) == 0 )
2113 cp
+= strspn ( cp
, " \t " );
2114 if ( hc
-> accept
[ 0 ] != '\0' )
2116 if ( strlen ( hc
-> accept
) > 5000 )
2119 LOG_ERR
, "%.80s way too much Accept: data" ,
2120 httpd_ntoa ( & hc
-> client_addr
) );
2124 & hc
-> accept
, & hc
-> maxaccept
,
2125 strlen ( hc
-> accept
) + 2 + strlen ( cp
) );
2126 ( void ) strcat ( hc
-> accept
, ", " );
2130 & hc
-> accept
, & hc
-> maxaccept
, strlen ( cp
) );
2131 ( void ) strcat ( hc
-> accept
, cp
);
2133 else if ( strncasecmp ( buf
, "Accept-Encoding:" , 16 ) == 0 )
2136 cp
+= strspn ( cp
, " \t " );
2137 if ( hc
-> accepte
[ 0 ] != '\0' )
2139 if ( strlen ( hc
-> accepte
) > 5000 )
2142 LOG_ERR
, "%.80s way too much Accept-Encoding: data" ,
2143 httpd_ntoa ( & hc
-> client_addr
) );
2147 & hc
-> accepte
, & hc
-> maxaccepte
,
2148 strlen ( hc
-> accepte
) + 2 + strlen ( cp
) );
2149 ( void ) strcat ( hc
-> accepte
, ", " );
2153 & hc
-> accepte
, & hc
-> maxaccepte
, strlen ( cp
) );
2154 ( void ) strcpy ( hc
-> accepte
, cp
);
2156 else if ( strncasecmp ( buf
, "Accept-Language:" , 16 ) == 0 )
2159 cp
+= strspn ( cp
, " \t " );
2162 else if ( strncasecmp ( buf
, "If-Modified-Since:" , 18 ) == 0 )
2165 hc
-> if_modified_since
= tdate_parse ( cp
);
2166 if ( hc
-> if_modified_since
== ( time_t ) - 1 )
2167 syslog ( LOG_DEBUG
, "unparsable time: %.80s" , cp
);
2169 else if ( strncasecmp ( buf
, "Cookie:" , 7 ) == 0 )
2172 cp
+= strspn ( cp
, " \t " );
2175 else if ( strncasecmp ( buf
, "Range:" , 6 ) == 0 )
2177 /* Only support %d- and %d-%d, not %d-%d,%d-%d or -%d. */
2178 if ( strchr ( buf
, ',' ) == ( char *) 0 )
2181 cp
= strpbrk ( buf
, "=" );
2182 if ( cp
!= ( char *) 0 )
2184 cp_dash
= strchr ( cp
+ 1 , '-' );
2185 if ( cp_dash
!= ( char *) 0 && cp_dash
!= cp
+ 1 )
2189 hc
-> first_byte_index
= atoll ( cp
+ 1 );
2190 if ( hc
-> first_byte_index
< 0 )
2191 hc
-> first_byte_index
= 0 ;
2192 if ( isdigit ( ( int ) cp_dash
[ 1 ] ) )
2194 hc
-> last_byte_index
= atoll ( cp_dash
+ 1 );
2195 if ( hc
-> last_byte_index
< 0 )
2196 hc
-> last_byte_index
= - 1 ;
2202 else if ( strncasecmp ( buf
, "Range-If:" , 9 ) == 0 ||
2203 strncasecmp ( buf
, "If-Range:" , 9 ) == 0 )
2206 hc
-> range_if
= tdate_parse ( cp
);
2207 if ( hc
-> range_if
== ( time_t ) - 1 )
2208 syslog ( LOG_DEBUG
, "unparsable time: %.80s" , cp
);
2210 else if ( strncasecmp ( buf
, "Content-Type:" , 13 ) == 0 )
2213 cp
+= strspn ( cp
, " \t " );
2214 hc
-> contenttype
= cp
;
2216 else if ( strncasecmp ( buf
, "Content-Length:" , 15 ) == 0 )
2219 hc
-> contentlength
= atol ( cp
);
2221 else if ( strncasecmp ( buf
, "Authorization:" , 14 ) == 0 )
2224 cp
+= strspn ( cp
, " \t " );
2225 hc
-> authorization
= cp
;
2227 else if ( strncasecmp ( buf
, "Connection:" , 11 ) == 0 )
2230 cp
+= strspn ( cp
, " \t " );
2231 if ( strcasecmp ( cp
, "keep-alive" ) == 0 )
2234 #ifdef LOG_UNKNOWN_HEADERS
2235 else if ( strncasecmp ( buf
, "Accept-Charset:" , 15 ) == 0 ||
2236 strncasecmp ( buf
, "Accept-Language:" , 16 ) == 0 ||
2237 strncasecmp ( buf
, "Agent:" , 6 ) == 0 ||
2238 strncasecmp ( buf
, "Cache-Control:" , 14 ) == 0 ||
2239 strncasecmp ( buf
, "Cache-Info:" , 11 ) == 0 ||
2240 strncasecmp ( buf
, "Charge-To:" , 10 ) == 0 ||
2241 strncasecmp ( buf
, "Client-IP:" , 10 ) == 0 ||
2242 strncasecmp ( buf
, "Date:" , 5 ) == 0 ||
2243 strncasecmp ( buf
, "Extension:" , 10 ) == 0 ||
2244 strncasecmp ( buf
, "Forwarded:" , 10 ) == 0 ||
2245 strncasecmp ( buf
, "From:" , 5 ) == 0 ||
2246 strncasecmp ( buf
, "HTTP-Version:" , 13 ) == 0 ||
2247 strncasecmp ( buf
, "Max-Forwards:" , 13 ) == 0 ||
2248 strncasecmp ( buf
, "Message-Id:" , 11 ) == 0 ||
2249 strncasecmp ( buf
, "MIME-Version:" , 13 ) == 0 ||
2250 strncasecmp ( buf
, "Negotiate:" , 10 ) == 0 ||
2251 strncasecmp ( buf
, "Pragma:" , 7 ) == 0 ||
2252 strncasecmp ( buf
, "Proxy-Agent:" , 12 ) == 0 ||
2253 strncasecmp ( buf
, "Proxy-Connection:" , 17 ) == 0 ||
2254 strncasecmp ( buf
, "Security-Scheme:" , 16 ) == 0 ||
2255 strncasecmp ( buf
, "Session-Id:" , 11 ) == 0 ||
2256 strncasecmp ( buf
, "UA-Color:" , 9 ) == 0 ||
2257 strncasecmp ( buf
, "UA-CPU:" , 7 ) == 0 ||
2258 strncasecmp ( buf
, "UA-Disp:" , 8 ) == 0 ||
2259 strncasecmp ( buf
, "UA-OS:" , 6 ) == 0 ||
2260 strncasecmp ( buf
, "UA-Pixels:" , 10 ) == 0 ||
2261 strncasecmp ( buf
, "User:" , 5 ) == 0 ||
2262 strncasecmp ( buf
, "Via:" , 4 ) == 0 ||
2263 strncasecmp ( buf
, "X-" , 2 ) == 0 )
2266 syslog ( LOG_DEBUG
, "unknown request header: %.80s" , buf
);
2267 #endif /* LOG_UNKNOWN_HEADERS */
2273 /* Check that HTTP/1.1 requests specify a host, as required. */
2274 if ( hc
-> reqhost
[ 0 ] == '\0' && hc
-> hdrhost
[ 0 ] == '\0' )
2276 httpd_send_err ( hc
, 400 , httpd_err400title
, "" , httpd_err400form
, "" );
2280 /* If the client wants to do keep-alives, it might also be doing
2281 ** pipelining. There's no way for us to tell. Since we don't
2282 ** implement keep-alives yet, if we close such a connection there
2283 ** might be unread pipelined requests waiting. So, we have to
2284 ** do a lingering close.
2286 if ( hc
-> keep_alive
)
2287 hc
-> should_linger
= 1 ;
2290 /* Ok, the request has been parsed. Now we resolve stuff that
2291 ** may require the entire request.
2294 /* Copy original filename to expanded filename. */
2296 & hc
-> expnfilename
, & hc
-> maxexpnfilename
, strlen ( hc
-> origfilename
) );
2297 ( void ) strcpy ( hc
-> expnfilename
, hc
-> origfilename
);
2299 /* Tilde mapping. */
2300 if ( hc
-> expnfilename
[ 0 ] == '~' )
2303 if ( ! tilde_map_1 ( hc
) )
2305 httpd_send_err ( hc
, 404 , err404title
, "" , err404form
, hc
-> encodedurl
);
2308 #endif /* TILDE_MAP_1 */
2310 if ( ! tilde_map_2 ( hc
) )
2312 httpd_send_err ( hc
, 404 , err404title
, "" , err404form
, hc
-> encodedurl
);
2315 #endif /* TILDE_MAP_2 */
2318 /* Virtual host mapping. */
2319 if ( hc
-> hs
-> vhost
)
2320 if ( ! vhost_map ( hc
) )
2322 httpd_send_err ( hc
, 500 , err500title
, "" , err500form
, hc
-> encodedurl
);
2326 /* Expand all symbolic links in the filename. This also gives us
2327 ** any trailing non-existing components, for pathinfo.
2329 cp
= expand_symlinks ( hc
-> expnfilename
, & pi
, hc
-> hs
-> no_symlink_check
, hc
-> tildemapped
);
2330 if ( cp
== ( char *) 0 )
2332 httpd_send_err ( hc
, 500 , err500title
, "" , err500form
, hc
-> encodedurl
);
2335 httpd_realloc_str ( & hc
-> expnfilename
, & hc
-> maxexpnfilename
, strlen ( cp
) );
2336 ( void ) strcpy ( hc
-> expnfilename
, cp
);
2337 httpd_realloc_str ( & hc
-> pathinfo
, & hc
-> maxpathinfo
, strlen ( pi
) );
2338 ( void ) strcpy ( hc
-> pathinfo
, pi
);
2340 /* Remove pathinfo stuff from the original filename too. */
2341 if ( hc
-> pathinfo
[ 0 ] != '\0' )
2344 i
= strlen ( hc
-> origfilename
) - strlen ( hc
-> pathinfo
);
2345 if ( i
> 0 && strcmp ( & hc
-> origfilename
[ i
], hc
-> pathinfo
) == 0 )
2346 hc
-> origfilename
[ i
- 1 ] = '\0' ;
2349 /* If the expanded filename is an absolute path, check that it's still
2350 ** within the current directory or the alternate directory.
2352 if ( hc
-> expnfilename
[ 0 ] == '/' )
2355 hc
-> expnfilename
, hc
-> hs
-> cwd
, strlen ( hc
-> hs
-> cwd
) ) == 0 )
2357 /* Elide the current directory. */
2359 hc
-> expnfilename
, & hc
-> expnfilename
[ strlen ( hc
-> hs
-> cwd
)] );
2362 else if ( hc
-> altdir
[ 0 ] != '\0' &&
2364 hc
-> expnfilename
, hc
-> altdir
,
2365 strlen ( hc
-> altdir
) ) == 0 &&
2366 ( hc
-> expnfilename
[ strlen ( hc
-> altdir
)] == '\0' ||
2367 hc
-> expnfilename
[ strlen ( hc
-> altdir
)] == '/' ) ) )
2369 #endif /* TILDE_MAP_2 */
2373 LOG_NOTICE
, "%.80s URL \" %.80s \" goes outside the web tree" ,
2374 httpd_ntoa ( & hc
-> client_addr
), hc
-> encodedurl
);
2376 hc
, 403 , err403title
, "" ,
2377 ERROR_FORM ( err403form
, "The requested URL '%.80s' resolves to a file outside the permitted web server directory tree. \n " ),
2388 bufgets ( httpd_conn
* hc
)
2393 for ( i
= hc
-> checked_idx
; hc
-> checked_idx
< hc
-> read_idx
; ++ hc
-> checked_idx
)
2395 c
= hc
-> read_buf
[ hc
-> checked_idx
];
2396 if ( c
== ' \012 ' || c
== ' \015 ' )
2398 hc
-> read_buf
[ hc
-> checked_idx
] = '\0' ;
2400 if ( c
== ' \015 ' && hc
-> checked_idx
< hc
-> read_idx
&&
2401 hc
-> read_buf
[ hc
-> checked_idx
] == ' \012 ' )
2403 hc
-> read_buf
[ hc
-> checked_idx
] = '\0' ;
2406 return &( hc
-> read_buf
[ i
]);
2414 de_dotdot ( char * file
)
2420 /* Collapse any multiple / sequences. */
2421 while ( ( cp
= strstr ( file
, "//" ) ) != ( char *) 0 )
2423 for ( cp2
= cp
+ 2 ; * cp2
== '/' ; ++ cp2
)
2425 ( void ) ol_strcpy ( cp
+ 1 , cp2
);
2428 /* Remove leading ./ and any /./ sequences. */
2429 while ( strncmp ( file
, "./" , 2 ) == 0 )
2430 ( void ) ol_strcpy ( file
, file
+ 2 );
2431 while ( ( cp
= strstr ( file
, "/./" ) ) != ( char *) 0 )
2432 ( void ) ol_strcpy ( cp
, cp
+ 2 );
2434 /* Alternate between removing leading ../ and removing xxx/../ */
2437 while ( strncmp ( file
, "../" , 3 ) == 0 )
2438 ( void ) ol_strcpy ( file
, file
+ 3 );
2439 cp
= strstr ( file
, "/../" );
2440 if ( cp
== ( char *) 0 )
2442 for ( cp2
= cp
- 1 ; cp2
>= file
&& * cp2
!= '/' ; -- cp2
)
2444 ( void ) ol_strcpy ( cp2
+ 1 , cp
+ 4 );
2447 /* Also elide any xxx/.. at the end. */
2448 while ( ( l
= strlen ( file
) ) > 3 &&
2449 strcmp ( ( cp
= file
+ l
- 3 ), "/.." ) == 0 )
2451 for ( cp2
= cp
- 1 ; cp2
>= file
&& * cp2
!= '/' ; -- cp2
)
2461 httpd_close_conn ( httpd_conn
* hc
, struct timeval
* nowP
)
2463 make_log_entry ( hc
, nowP
);
2465 if ( hc
-> file_address
!= ( char *) 0 )
2467 mmc_unmap ( hc
-> file_address
, &( hc
-> sb
), nowP
);
2468 hc
-> file_address
= ( char *) 0 ;
2470 if ( hc
-> conn_fd
>= 0 )
2472 ( void ) close ( hc
-> conn_fd
);
2478 httpd_destroy_conn ( httpd_conn
* hc
)
2480 if ( hc
-> initialized
)
2482 free ( ( void *) hc
-> read_buf
);
2483 free ( ( void *) hc
-> decodedurl
);
2484 free ( ( void *) hc
-> origfilename
);
2485 free ( ( void *) hc
-> expnfilename
);
2486 free ( ( void *) hc
-> encodings
);
2487 free ( ( void *) hc
-> pathinfo
);
2488 free ( ( void *) hc
-> query
);
2489 free ( ( void *) hc
-> accept
);
2490 free ( ( void *) hc
-> accepte
);
2491 free ( ( void *) hc
-> reqhost
);
2492 free ( ( void *) hc
-> hostdir
);
2493 free ( ( void *) hc
-> remoteuser
);
2494 free ( ( void *) hc
-> response
);
2496 free ( ( void *) hc
-> altdir
);
2497 #endif /* TILDE_MAP_2 */
2498 hc
-> initialized
= 0 ;
2509 static struct mime_entry enc_tab
[] = {
2510 #include "mime_encodings.h"
2512 static const int n_enc_tab
= sizeof ( enc_tab
) / sizeof (* enc_tab
);
2513 static struct mime_entry typ_tab
[] = {
2514 #include "mime_types.h"
2516 static const int n_typ_tab
= sizeof ( typ_tab
) / sizeof (* typ_tab
);
2519 /* qsort comparison routine */
2521 ext_compare ( const void * v1
, const void * v2
)
2523 const struct mime_entry
* m1
= ( const struct mime_entry
*) v1
;
2524 const struct mime_entry
* m2
= ( const struct mime_entry
*) v2
;
2526 return strcmp ( m1
-> ext
, m2
-> ext
);
2535 /* Sort the tables so we can do binary search. */
2536 qsort ( enc_tab
, n_enc_tab
, sizeof (* enc_tab
), ext_compare
);
2537 qsort ( typ_tab
, n_typ_tab
, sizeof (* typ_tab
), ext_compare
);
2539 /* Fill in the lengths. */
2540 for ( i
= 0 ; i
< n_enc_tab
; ++ i
)
2542 enc_tab
[ i
]. ext_len
= strlen ( enc_tab
[ i
]. ext
);
2543 enc_tab
[ i
]. val_len
= strlen ( enc_tab
[ i
]. val
);
2545 for ( i
= 0 ; i
< n_typ_tab
; ++ i
)
2547 typ_tab
[ i
]. ext_len
= strlen ( typ_tab
[ i
]. ext
);
2548 typ_tab
[ i
]. val_len
= strlen ( typ_tab
[ i
]. val
);
2554 /* Figure out MIME encodings and type based on the filename. Multiple
2555 ** encodings are separated by commas, and are listed in the order in
2556 ** which they were applied to the file.
2559 figure_mime ( httpd_conn
* hc
)
2564 int me_indexes
[ 100 ], n_me_indexes
;
2565 size_t ext_len
, encodings_len
;
2566 int i
, top
, bot
, mid
;
2568 char * default_type
= "text/plain; charset=%s" ;
2570 /* Peel off encoding extensions until there aren't any more. */
2572 for ( prev_dot
= & hc
-> expnfilename
[ strlen ( hc
-> expnfilename
)]; ; prev_dot
= dot
)
2574 for ( dot
= prev_dot
- 1 ; dot
>= hc
-> expnfilename
&& * dot
!= '.' ; -- dot
)
2576 if ( dot
< hc
-> expnfilename
)
2578 /* No dot found. No more encoding extensions, and no type
2579 ** extension either.
2581 hc
-> type
= default_type
;
2585 ext_len
= prev_dot
- ext
;
2586 /* Search the encodings table. Linear search is fine here, there
2587 ** are only a few entries.
2589 for ( i
= 0 ; i
< n_enc_tab
; ++ i
)
2591 if ( ext_len
== enc_tab
[ i
]. ext_len
&& strncasecmp ( ext
, enc_tab
[ i
]. ext
, ext_len
) == 0 )
2593 if ( n_me_indexes
< sizeof ( me_indexes
)/ sizeof (* me_indexes
) )
2595 me_indexes
[ n_me_indexes
] = i
;
2601 /* No encoding extension found. Break and look for a type extension. */
2607 /* Binary search for a matching type extension. */
2608 top
= n_typ_tab
- 1 ;
2610 while ( top
>= bot
)
2612 mid
= ( top
+ bot
) / 2 ;
2613 r
= strncasecmp ( ext
, typ_tab
[ mid
]. ext
, ext_len
);
2619 if ( ext_len
< typ_tab
[ mid
]. ext_len
)
2621 else if ( ext_len
> typ_tab
[ mid
]. ext_len
)
2625 hc
-> type
= typ_tab
[ mid
]. val
;
2629 hc
-> type
= default_type
;
2633 /* The last thing we do is actually generate the mime-encoding header. */
2634 hc
-> encodings
[ 0 ] = '\0' ;
2636 for ( i
= n_me_indexes
- 1 ; i
>= 0 ; -- i
)
2639 & hc
-> encodings
, & hc
-> maxencodings
,
2640 encodings_len
+ enc_tab
[ me_indexes
[ i
]]. val_len
+ 1 );
2641 if ( hc
-> encodings
[ 0 ] != '\0' )
2643 ( void ) strcpy ( & hc
-> encodings
[ encodings_len
], "," );
2646 ( void ) strcpy ( & hc
-> encodings
[ encodings_len
], enc_tab
[ me_indexes
[ i
]]. val
);
2647 encodings_len
+= enc_tab
[ me_indexes
[ i
]]. val_len
;
2653 #ifdef CGI_TIMELIMIT
2655 cgi_kill2 ( ClientData client_data
, struct timeval
* nowP
)
2659 pid
= ( pid_t
) client_data
. i
;
2660 if ( kill ( pid
, SIGKILL
) == 0 )
2661 syslog ( LOG_WARNING
, "hard-killed CGI process %d" , pid
);
2665 cgi_kill ( ClientData client_data
, struct timeval
* nowP
)
2669 pid
= ( pid_t
) client_data
. i
;
2670 if ( kill ( pid
, SIGINT
) == 0 )
2672 syslog ( LOG_WARNING
, "killed CGI process %d" , pid
);
2673 /* In case this isn't enough, schedule an uncatchable kill. */
2674 if ( tmr_create ( nowP
, cgi_kill2
, client_data
, 5 * 1000L , 0 ) == ( Timer
*) 0 )
2676 syslog ( LOG_CRIT
, "tmr_create(cgi_kill2) failed" );
2681 #endif /* CGI_TIMELIMIT */
2684 #ifdef GENERATE_INDEXES
2686 /* qsort comparison routine */
2688 name_compare ( const void * v1
, const void * v2
)
2690 const char ** c1
= ( const char **) v1
;
2691 const char ** c2
= ( const char **) v2
;
2692 return strcmp ( * c1
, * c2
);
2697 ls ( httpd_conn
* hc
)
2702 static int maxnames
= 0 ;
2705 static char ** nameptrs
;
2707 static size_t maxname
= 0 ;
2709 static size_t maxrname
= 0 ;
2710 static char * encrname
;
2711 static size_t maxencrname
= 0 ;
2718 char lnk
[ MAXPATHLEN
+ 1 ];
2723 ClientData client_data
;
2725 dirp
= opendir ( hc
-> expnfilename
);
2726 if ( dirp
== ( DIR *) 0 )
2728 syslog ( LOG_ERR
, "opendir %.80s - %m" , hc
-> expnfilename
);
2729 httpd_send_err ( hc
, 404 , err404title
, "" , err404form
, hc
-> encodedurl
);
2733 if ( hc
-> method
== METHOD_HEAD
)
2737 hc
, 200 , ok200title
, "" , "" , "text/html; charset=%s" , ( off_t
) - 1 ,
2740 else if ( hc
-> method
== METHOD_GET
)
2742 if ( hc
-> hs
-> cgi_limit
!= 0 && hc
-> hs
-> cgi_count
>= hc
-> hs
-> cgi_limit
)
2746 hc
, 503 , httpd_err503title
, "" , httpd_err503form
,
2750 ++ hc
-> hs
-> cgi_count
;
2754 syslog ( LOG_ERR
, "fork - %m" );
2757 hc
, 500 , err500title
, "" , err500form
, hc
-> encodedurl
);
2762 /* Child process. */
2764 httpd_unlisten ( hc
-> hs
);
2766 hc
, 200 , ok200title
, "" , "" , "text/html; charset=%s" ,
2767 ( off_t
) - 1 , hc
-> sb
. st_mtime
);
2768 httpd_write_response ( hc
);
2772 ( void ) nice ( CGI_NICE
);
2773 #endif /* CGI_NICE */
2775 /* Open a stdio stream so that we can use fprintf, which is more
2776 ** efficient than a bunch of separate write()s. We don't have
2777 ** to worry about double closes or file descriptor leaks cause
2778 ** we're in a subprocess.
2780 fp
= fdopen ( hc
-> conn_fd
, "w" );
2781 if ( fp
== ( FILE *) 0 )
2783 syslog ( LOG_ERR
, "fdopen - %m" );
2785 hc
, 500 , err500title
, "" , err500form
, hc
-> encodedurl
);
2786 httpd_write_response ( hc
);
2791 ( void ) fprintf ( fp
, "\
2792 <!DOCTYPE html PUBLIC \" -//W3C//DTD HTML 4.01 Transitional//EN \" \" http://www.w3.org/TR/html4/loose.dtd \" > \n \
2797 <meta http-equiv= \" Content-type \" content= \" text/html;charset=UTF-8 \" > \n \
2798 <title>Index of %.80s</title> \n \
2801 <body bgcolor= \" #99cc99 \" text= \" #000000 \" link= \" #2020ff \" vlink= \" #4040cc \" > \n \
2803 <h2>Index of %.80s</h2> \n \
2806 mode links bytes last-changed name \n \
2808 hc
-> encodedurl
, hc
-> encodedurl
);
2810 /* Read in names. */
2812 while ( ( de
= readdir ( dirp
) ) != 0 ) /* dirent or direct */
2814 if ( nnames
>= maxnames
)
2816 if ( maxnames
== 0 )
2819 names
= NEW ( char , maxnames
* ( MAXPATHLEN
+ 1 ) );
2820 nameptrs
= NEW ( char *, maxnames
);
2825 names
= RENEW ( names
, char , maxnames
* ( MAXPATHLEN
+ 1 ) );
2826 nameptrs
= RENEW ( nameptrs
, char *, maxnames
);
2828 if ( names
== ( char *) 0 || nameptrs
== ( char **) 0 )
2830 syslog ( LOG_ERR
, "out of memory reallocating directory names" );
2833 for ( i
= 0 ; i
< maxnames
; ++ i
)
2834 nameptrs
[ i
] = & names
[ i
* ( MAXPATHLEN
+ 1 )];
2836 namlen
= NAMLEN ( de
);
2837 ( void ) strncpy ( nameptrs
[ nnames
], de
-> d_name
, namlen
);
2838 nameptrs
[ nnames
][ namlen
] = '\0' ;
2843 /* Sort the names. */
2844 qsort ( nameptrs
, nnames
, sizeof (* nameptrs
), name_compare
);
2846 /* Generate output. */
2847 for ( i
= 0 ; i
< nnames
; ++ i
)
2851 strlen ( hc
-> expnfilename
) + 1 + strlen ( nameptrs
[ i
] ) );
2854 strlen ( hc
-> origfilename
) + 1 + strlen ( nameptrs
[ i
] ) );
2855 if ( hc
-> expnfilename
[ 0 ] == '\0' ||
2856 strcmp ( hc
-> expnfilename
, "." ) == 0 )
2858 ( void ) strcpy ( name
, nameptrs
[ i
] );
2859 ( void ) strcpy ( rname
, nameptrs
[ i
] );
2863 ( void ) my_snprintf ( name
, maxname
,
2864 "%s/%s" , hc
-> expnfilename
, nameptrs
[ i
] );
2865 if ( strcmp ( hc
-> origfilename
, "." ) == 0 )
2866 ( void ) my_snprintf ( rname
, maxrname
,
2867 "%s" , nameptrs
[ i
] );
2869 ( void ) my_snprintf ( rname
, maxrname
,
2870 "%s%s" , hc
-> origfilename
, nameptrs
[ i
] );
2873 & encrname
, & maxencrname
, 3 * strlen ( rname
) + 1 );
2874 strencode ( encrname
, maxencrname
, rname
);
2876 if ( stat ( name
, & sb
) < 0 || lstat ( name
, & lsb
) < 0 )
2881 /* Break down mode word. First the file type. */
2882 switch ( lsb
. st_mode
& S_IFMT
)
2884 case S_IFIFO
: modestr
[ 0 ] = 'p' ; break ;
2885 case S_IFCHR
: modestr
[ 0 ] = 'c' ; break ;
2886 case S_IFDIR
: modestr
[ 0 ] = 'd' ; break ;
2887 case S_IFBLK
: modestr
[ 0 ] = 'b' ; break ;
2888 case S_IFREG
: modestr
[ 0 ] = '-' ; break ;
2889 case S_IFSOCK
: modestr
[ 0 ] = 's' ; break ;
2890 case S_IFLNK
: modestr
[ 0 ] = 'l' ;
2891 linklen
= readlink ( name
, lnk
, sizeof ( lnk
) - 1 );
2892 if ( linklen
!= - 1 )
2894 lnk
[ linklen
] = '\0' ;
2895 linkprefix
= " -> " ;
2898 default : modestr
[ 0 ] = '?' ; break ;
2900 /* Now the world permissions. Owner and group permissions
2901 ** are not of interest to web clients.
2903 modestr
[ 1 ] = ( lsb
. st_mode
& S_IROTH
) ? 'r' : '-' ;
2904 modestr
[ 2 ] = ( lsb
. st_mode
& S_IWOTH
) ? 'w' : '-' ;
2905 modestr
[ 3 ] = ( lsb
. st_mode
& S_IXOTH
) ? 'x' : '-' ;
2908 /* We also leave out the owner and group name, they are
2909 ** also not of interest to web clients. Plus if we're
2910 ** running under chroot(), they would require a copy
2911 ** of /etc/passwd and /etc/group, which we want to avoid.
2914 /* Get time string. */
2915 now
= time ( ( time_t *) 0 );
2916 timestr
= ctime ( & lsb
. st_mtime
);
2917 timestr
[ 0 ] = timestr
[ 4 ];
2918 timestr
[ 1 ] = timestr
[ 5 ];
2919 timestr
[ 2 ] = timestr
[ 6 ];
2921 timestr
[ 4 ] = timestr
[ 8 ];
2922 timestr
[ 5 ] = timestr
[ 9 ];
2924 if ( now
- lsb
. st_mtime
> 60 * 60 * 24 * 182 ) /* 1/2 year */
2927 timestr
[ 8 ] = timestr
[ 20 ];
2928 timestr
[ 9 ] = timestr
[ 21 ];
2929 timestr
[ 10 ] = timestr
[ 22 ];
2930 timestr
[ 11 ] = timestr
[ 23 ];
2934 timestr
[ 7 ] = timestr
[ 11 ];
2935 timestr
[ 8 ] = timestr
[ 12 ];
2937 timestr
[ 10 ] = timestr
[ 14 ];
2938 timestr
[ 11 ] = timestr
[ 15 ];
2942 /* The ls -F file class. */
2943 switch ( sb
. st_mode
& S_IFMT
)
2945 case S_IFDIR
: fileclass
= "/" ; break ;
2946 case S_IFSOCK
: fileclass
= "=" ; break ;
2947 case S_IFLNK
: fileclass
= "@" ; break ;
2949 fileclass
= ( sb
. st_mode
& S_IXOTH
) ? "*" : "" ;
2955 "%s %3ld %10lld %s <a href= \" /%.500s%s \" >%.80s</a>%s%s%s \n " ,
2956 modestr
, ( long ) lsb
. st_nlink
, ( long long ) lsb
. st_size
,
2957 timestr
, encrname
, S_ISDIR ( sb
. st_mode
) ? "/" : "" ,
2958 nameptrs
[ i
], linkprefix
, lnk
, fileclass
);
2961 ( void ) fprintf ( fp
, " </pre> \n </body> \n </html> \n " );
2962 ( void ) fclose ( fp
);
2966 /* Parent process. */
2968 syslog ( LOG_DEBUG
, "spawned indexing process %d for directory '%.200s'" , r
, hc
-> expnfilename
);
2969 #ifdef CGI_TIMELIMIT
2970 /* Schedule a kill for the child process, in case it runs too long */
2972 if ( tmr_create ( ( struct timeval
*) 0 , cgi_kill
, client_data
, CGI_TIMELIMIT
* 1000L , 0 ) == ( Timer
*) 0 )
2974 syslog ( LOG_CRIT
, "tmr_create(cgi_kill ls) failed" );
2977 #endif /* CGI_TIMELIMIT */
2979 hc
-> bytes_sent
= CGI_BYTECOUNT
;
2980 hc
-> should_linger
= 0 ;
2986 hc
, 501 , err501title
, "" , err501form
, httpd_method_str ( hc
-> method
) );
2993 #endif /* GENERATE_INDEXES */
2997 build_env ( char * fmt
, char * arg
)
3002 static size_t maxbuf
= 0 ;
3004 size
= strlen ( fmt
) + strlen ( arg
);
3005 if ( size
> maxbuf
)
3006 httpd_realloc_str ( & buf
, & maxbuf
, size
);
3007 ( void ) my_snprintf ( buf
, maxbuf
, fmt
, arg
);
3009 if ( cp
== ( char *) 0 )
3011 syslog ( LOG_ERR
, "out of memory copying environment variable" );
3018 #ifdef SERVER_NAME_LIST
3020 hostname_map ( char * hostname
)
3023 static char * list
[] = { SERVER_NAME_LIST
};
3025 len
= strlen ( hostname
);
3026 for ( n
= sizeof ( list
) / sizeof (* list
) - 1 ; n
>= 0 ; -- n
)
3027 if ( strncasecmp ( hostname
, list
[ n
], len
) == 0 )
3028 if ( list
[ n
][ len
] == '/' ) /* check in case of a substring match */
3029 return & list
[ n
][ len
+ 1 ];
3032 #endif /* SERVER_NAME_LIST */
3035 /* Set up environment variables. Be real careful here to avoid
3036 ** letting malicious clients overrun a buffer. We don't have
3037 ** to worry about freeing stuff since we're a sub-process.
3040 make_envp ( httpd_conn
* hc
)
3042 static char * envp
[ 50 ];
3048 envp
[ envn
++] = build_env ( "PATH=%s" , CGI_PATH
);
3049 #ifdef CGI_LD_LIBRARY_PATH
3050 envp
[ envn
++] = build_env ( "LD_LIBRARY_PATH=%s" , CGI_LD_LIBRARY_PATH
);
3051 #endif /* CGI_LD_LIBRARY_PATH */
3052 envp
[ envn
++] = build_env ( "SERVER_SOFTWARE=%s" , SERVER_SOFTWARE
);
3053 if ( hc
-> hs
-> vhost
&& hc
-> hostname
!= ( char *) 0 && hc
-> hostname
[ 0 ] != '\0' )
3055 else if ( hc
-> hdrhost
!= ( char *) 0 && hc
-> hdrhost
[ 0 ] != '\0' )
3057 else if ( hc
-> reqhost
!= ( char *) 0 && hc
-> reqhost
[ 0 ] != '\0' )
3060 cp
= hc
-> hs
-> server_hostname
;
3061 if ( cp
!= ( char *) 0 )
3062 envp
[ envn
++] = build_env ( "SERVER_NAME=%s" , cp
);
3063 envp
[ envn
++] = "GATEWAY_INTERFACE=CGI/1.1" ;
3064 envp
[ envn
++] = build_env ( "SERVER_PROTOCOL=%s" , hc
-> protocol
);
3065 ( void ) my_snprintf ( buf
, sizeof ( buf
), "%d" , ( int ) hc
-> hs
-> port
);
3066 envp
[ envn
++] = build_env ( "SERVER_PORT=%s" , buf
);
3067 envp
[ envn
++] = build_env (
3068 "REQUEST_METHOD=%s" , httpd_method_str ( hc
-> method
) );
3069 if ( hc
-> pathinfo
[ 0 ] != '\0' )
3073 envp
[ envn
++] = build_env ( "PATH_INFO=/%s" , hc
-> pathinfo
);
3074 l
= strlen ( hc
-> hs
-> cwd
) + strlen ( hc
-> pathinfo
) + 1 ;
3075 cp2
= NEW ( char , l
);
3076 if ( cp2
!= ( char *) 0 )
3078 ( void ) my_snprintf ( cp2
, l
, "%s%s" , hc
-> hs
-> cwd
, hc
-> pathinfo
);
3079 envp
[ envn
++] = build_env ( "PATH_TRANSLATED=%s" , cp2
);
3082 envp
[ envn
++] = build_env (
3083 "SCRIPT_NAME=/%s" , strcmp ( hc
-> origfilename
, "." ) == 0 ?
3084 "" : hc
-> origfilename
);
3085 if ( hc
-> query
[ 0 ] != '\0' )
3086 envp
[ envn
++] = build_env ( "QUERY_STRING=%s" , hc
-> query
);
3087 envp
[ envn
++] = build_env (
3088 "REMOTE_ADDR=%s" , httpd_ntoa ( & hc
-> client_addr
) );
3089 if ( hc
-> referrer
[ 0 ] != '\0' )
3091 envp
[ envn
++] = build_env ( "HTTP_REFERER=%s" , hc
-> referrer
);
3092 envp
[ envn
++] = build_env ( "HTTP_REFERRER=%s" , hc
-> referrer
);
3094 if ( hc
-> useragent
[ 0 ] != '\0' )
3095 envp
[ envn
++] = build_env ( "HTTP_USER_AGENT=%s" , hc
-> useragent
);
3096 if ( hc
-> accept
[ 0 ] != '\0' )
3097 envp
[ envn
++] = build_env ( "HTTP_ACCEPT=%s" , hc
-> accept
);
3098 if ( hc
-> accepte
[ 0 ] != '\0' )
3099 envp
[ envn
++] = build_env ( "HTTP_ACCEPT_ENCODING=%s" , hc
-> accepte
);
3100 if ( hc
-> acceptl
[ 0 ] != '\0' )
3101 envp
[ envn
++] = build_env ( "HTTP_ACCEPT_LANGUAGE=%s" , hc
-> acceptl
);
3102 if ( hc
-> cookie
[ 0 ] != '\0' )
3103 envp
[ envn
++] = build_env ( "HTTP_COOKIE=%s" , hc
-> cookie
);
3104 if ( hc
-> contenttype
[ 0 ] != '\0' )
3105 envp
[ envn
++] = build_env ( "CONTENT_TYPE=%s" , hc
-> contenttype
);
3106 if ( hc
-> hdrhost
[ 0 ] != '\0' )
3107 envp
[ envn
++] = build_env ( "HTTP_HOST=%s" , hc
-> hdrhost
);
3108 if ( hc
-> contentlength
!= - 1 )
3111 buf
, sizeof ( buf
), "%lu" , ( unsigned long ) hc
-> contentlength
);
3112 envp
[ envn
++] = build_env ( "CONTENT_LENGTH=%s" , buf
);
3114 if ( hc
-> remoteuser
[ 0 ] != '\0' )
3115 envp
[ envn
++] = build_env ( "REMOTE_USER=%s" , hc
-> remoteuser
);
3116 if ( hc
-> authorization
[ 0 ] != '\0' )
3117 envp
[ envn
++] = build_env ( "AUTH_TYPE=%s" , "Basic" );
3118 /* We only support Basic auth at the moment. */
3119 if ( getenv ( "TZ" ) != ( char *) 0 )
3120 envp
[ envn
++] = build_env ( "TZ=%s" , getenv ( "TZ" ) );
3121 envp
[ envn
++] = build_env ( "CGI_PATTERN=%s" , hc
-> hs
-> cgi_pattern
);
3123 envp
[ envn
] = ( char *) 0 ;
3128 /* Set up argument vector. Again, we don't have to worry about freeing stuff
3129 ** since we're a sub-process. This gets done after make_envp() because we
3130 ** scribble on hc->query.
3133 make_argp ( httpd_conn
* hc
)
3140 /* By allocating an arg slot for every character in the query, plus
3141 ** one for the filename and one for the NULL, we are guaranteed to
3142 ** have enough. We could actually use strlen/2.
3144 argp
= NEW ( char *, strlen ( hc
-> query
) + 2 );
3145 if ( argp
== ( char **) 0 )
3148 argp
[ 0 ] = strrchr ( hc
-> expnfilename
, '/' );
3149 if ( argp
[ 0 ] != ( char *) 0 )
3152 argp
[ 0 ] = hc
-> expnfilename
;
3155 /* According to the CGI spec at http://hoohoo.ncsa.uiuc.edu/cgi/cl.html,
3156 ** "The server should search the query information for a non-encoded =
3157 ** character to determine if the command line is to be used, if it finds
3158 ** one, the command line is not to be used."
3160 if ( strchr ( hc
-> query
, '=' ) == ( char *) 0 )
3162 for ( cp1
= cp2
= hc
-> query
; * cp2
!= '\0' ; ++ cp2
)
3167 strdecode ( cp1
, cp1
);
3174 strdecode ( cp1
, cp1
);
3179 argp
[ argn
] = ( char *) 0 ;
3184 /* This routine is used only for POST requests. It reads the data
3185 ** from the request and sends it to the child process. The only reason
3186 ** we need to do it this way instead of just letting the child read
3187 ** directly is that we have already read part of the data into our
3191 cgi_interpose_input ( httpd_conn
* hc
, int wfd
)
3197 c
= hc
-> read_idx
- hc
-> checked_idx
;
3200 if ( httpd_write_fully ( wfd
, &( hc
-> read_buf
[ hc
-> checked_idx
]), c
) != c
)
3203 while ( c
< hc
-> contentlength
)
3205 r
= read ( hc
-> conn_fd
, buf
, MIN ( sizeof ( buf
), hc
-> contentlength
- c
) );
3206 if ( r
< 0 && ( errno
== EINTR
|| errno
== EAGAIN
) )
3213 if ( httpd_write_fully ( wfd
, buf
, r
) != r
)
3217 post_post_garbage_hack ( hc
);
3221 /* Special hack to deal with broken browsers that send a LF or CRLF
3222 ** after POST data, causing TCP resets - we just read and discard up
3223 ** to 2 bytes. Unfortunately this doesn't fix the problem for CGIs
3224 ** which avoid the interposer process due to their POST data being
3225 ** short. Creating an interposer process for all POST CGIs is
3226 ** unacceptably expensive. The eventual fix will come when interposing
3227 ** gets integrated into the main loop as a tasklet instead of a process.
3230 post_post_garbage_hack ( httpd_conn
* hc
)
3234 /* If we are in a sub-process, turn on no-delay mode in case we
3235 ** previously cleared it.
3238 httpd_set_ndelay ( hc
-> conn_fd
);
3239 /* And read up to 2 bytes. */
3240 ( void ) read ( hc
-> conn_fd
, buf
, sizeof ( buf
) );
3244 /* This routine is used for parsed-header CGIs. The idea here is that the
3245 ** CGI can return special headers such as "Status:" and "Location:" which
3246 ** change the return status of the response. Since the return status has to
3247 ** be the very first line written out, we have to accumulate all the headers
3248 ** and check for the special ones before writing the status. Then we write
3249 ** out the saved headers and proceed to echo the rest of the response.
3252 cgi_interpose_output ( httpd_conn
* hc
, int rfd
)
3256 size_t headers_size
, headers_len
;
3263 /* Make sure the connection is in blocking mode. It should already
3264 ** be blocking, but we might as well be sure.
3266 httpd_clear_ndelay ( hc
-> conn_fd
);
3268 /* Slurp in all headers. */
3270 httpd_realloc_str ( & headers
, & headers_size
, 500 );
3274 r
= read ( rfd
, buf
, sizeof ( buf
) );
3275 if ( r
< 0 && ( errno
== EINTR
|| errno
== EAGAIN
) )
3282 br
= &( headers
[ headers_len
]);
3285 httpd_realloc_str ( & headers
, & headers_size
, headers_len
+ r
);
3286 ( void ) memmove ( &( headers
[ headers_len
]), buf
, r
);
3288 headers
[ headers_len
] = '\0' ;
3289 if ( ( br
= strstr ( headers
, " \015\012\015\012 " ) ) != ( char *) 0 ||
3290 ( br
= strstr ( headers
, " \012\012 " ) ) != ( char *) 0 )
3294 /* If there were no headers, bail. */
3295 if ( headers
[ 0 ] == '\0' )
3298 /* Figure out the status. Look for a Status: or Location: header;
3299 ** else if there's an HTTP header line, get it from there; else
3303 if ( strncmp ( headers
, "HTTP/" , 5 ) == 0 )
3306 cp
+= strcspn ( cp
, " \t " );
3307 status
= atoi ( cp
);
3309 if ( ( cp
= strstr ( headers
, "Location:" ) ) != ( char *) 0 &&
3311 ( cp
== headers
|| *( cp
- 1 ) == ' \012 ' ) )
3313 if ( ( cp
= strstr ( headers
, "Status:" ) ) != ( char *) 0 &&
3315 ( cp
== headers
|| *( cp
- 1 ) == ' \012 ' ) )
3318 cp
+= strspn ( cp
, " \t " );
3319 status
= atoi ( cp
);
3322 /* Write the status line. */
3325 case 200 : title
= ok200title
; break ;
3326 case 302 : title
= err302title
; break ;
3327 case 304 : title
= err304title
; break ;
3328 case 400 : title
= httpd_err400title
; break ;
3330 case 401 : title
= err401title
; break ;
3331 #endif /* AUTH_FILE */
3332 case 403 : title
= err403title
; break ;
3333 case 404 : title
= err404title
; break ;
3334 case 408 : title
= httpd_err408title
; break ;
3335 case 451 : title
= err451title
; break ;
3336 case 500 : title
= err500title
; break ;
3337 case 501 : title
= err501title
; break ;
3338 case 503 : title
= httpd_err503title
; break ;
3339 default : title
= "Something" ; break ;
3341 ( void ) my_snprintf ( buf
, sizeof ( buf
), "HTTP/1.0 %d %s \015\012 " , status
, title
);
3342 ( void ) httpd_write_fully ( hc
-> conn_fd
, buf
, strlen ( buf
) );
3344 /* Write the saved headers. */
3345 ( void ) httpd_write_fully ( hc
-> conn_fd
, headers
, headers_len
);
3347 /* Echo the rest of the output. */
3350 r
= read ( rfd
, buf
, sizeof ( buf
) );
3351 if ( r
< 0 && ( errno
== EINTR
|| errno
== EAGAIN
) )
3358 if ( httpd_write_fully ( hc
-> conn_fd
, buf
, r
) != r
)
3361 shutdown ( hc
-> conn_fd
, SHUT_WR
);
3365 /* CGI child process. */
3367 cgi_child ( httpd_conn
* hc
)
3375 /* Unset close-on-exec flag for this socket. This actually shouldn't
3376 ** be necessary, according to POSIX a dup()'d file descriptor does
3377 ** *not* inherit the close-on-exec flag, its flag is always clear.
3378 ** However, Linux messes this up and does copy the flag to the
3379 ** dup()'d descriptor, so we have to clear it. This could be
3380 ** ifdeffed for Linux only.
3382 ( void ) fcntl ( hc
-> conn_fd
, F_SETFD
, 0 );
3384 /* Close the syslog descriptor so that the CGI program can't
3385 ** mess with it. All other open descriptors should be either
3386 ** the listen socket(s), sockets from accept(), or the file-logging
3387 ** fd, and all of those are set to close-on-exec, so we don't
3388 ** have to close anything else.
3392 /* If the socket happens to be using one of the stdin/stdout/stderr
3393 ** descriptors, move it to another descriptor so that the dup2 calls
3394 ** below don't screw things up. We arbitrarily pick fd 3 - if there
3395 ** was already something on it, we clobber it, but that doesn't matter
3396 ** since at this point the only fd of interest is the connection.
3397 ** All others will be closed on exec.
3399 if ( hc
-> conn_fd
== STDIN_FILENO
|| hc
-> conn_fd
== STDOUT_FILENO
|| hc
-> conn_fd
== STDERR_FILENO
)
3401 int newfd
= dup2 ( hc
-> conn_fd
, STDERR_FILENO
+ 1 );
3403 hc
-> conn_fd
= newfd
;
3404 /* If the dup2 fails, shrug. We'll just take our chances.
3405 ** Shouldn't happen though.
3409 /* Make the environment vector. */
3410 envp
= make_envp ( hc
);
3412 /* Make the argument vector. */
3413 argp
= make_argp ( hc
);
3415 /* Set up stdin. For POSTs we may have to set up a pipe from an
3416 ** interposer process, depending on if we've read some of the data
3419 if ( hc
-> method
== METHOD_POST
&& hc
-> read_idx
> hc
-> checked_idx
)
3423 if ( pipe ( p
) < 0 )
3425 syslog ( LOG_ERR
, "pipe - %m" );
3426 httpd_send_err ( hc
, 500 , err500title
, "" , err500form
, hc
-> encodedurl
);
3427 httpd_write_response ( hc
);
3433 syslog ( LOG_ERR
, "fork - %m" );
3434 httpd_send_err ( hc
, 500 , err500title
, "" , err500form
, hc
-> encodedurl
);
3435 httpd_write_response ( hc
);
3440 /* Interposer process. */
3442 ( void ) close ( p
[ 0 ] );
3443 cgi_interpose_input ( hc
, p
[ 1 ] );
3446 /* Need to schedule a kill for process r; but in the main process! */
3447 ( void ) close ( p
[ 1 ] );
3448 if ( p
[ 0 ] != STDIN_FILENO
)
3450 ( void ) dup2 ( p
[ 0 ], STDIN_FILENO
);
3451 ( void ) close ( p
[ 0 ] );
3456 /* Otherwise, the request socket is stdin. */
3457 if ( hc
-> conn_fd
!= STDIN_FILENO
)
3458 ( void ) dup2 ( hc
-> conn_fd
, STDIN_FILENO
);
3461 /* Set up stdout/stderr. If we're doing CGI header parsing,
3462 ** we need an output interposer too.
3464 if ( strncmp ( argp
[ 0 ], "nph-" , 4 ) != 0 && hc
-> mime_flag
)
3468 if ( pipe ( p
) < 0 )
3470 syslog ( LOG_ERR
, "pipe - %m" );
3471 httpd_send_err ( hc
, 500 , err500title
, "" , err500form
, hc
-> encodedurl
);
3472 httpd_write_response ( hc
);
3478 syslog ( LOG_ERR
, "fork - %m" );
3479 httpd_send_err ( hc
, 500 , err500title
, "" , err500form
, hc
-> encodedurl
);
3480 httpd_write_response ( hc
);
3485 /* Interposer process. */
3487 ( void ) close ( p
[ 1 ] );
3488 cgi_interpose_output ( hc
, p
[ 0 ] );
3491 /* Need to schedule a kill for process r; but in the main process! */
3492 ( void ) close ( p
[ 0 ] );
3493 if ( p
[ 1 ] != STDOUT_FILENO
)
3494 ( void ) dup2 ( p
[ 1 ], STDOUT_FILENO
);
3495 if ( p
[ 1 ] != STDERR_FILENO
)
3496 ( void ) dup2 ( p
[ 1 ], STDERR_FILENO
);
3497 if ( p
[ 1 ] != STDOUT_FILENO
&& p
[ 1 ] != STDERR_FILENO
)
3498 ( void ) close ( p
[ 1 ] );
3502 /* Otherwise, the request socket is stdout/stderr. */
3503 if ( hc
-> conn_fd
!= STDOUT_FILENO
)
3504 ( void ) dup2 ( hc
-> conn_fd
, STDOUT_FILENO
);
3505 if ( hc
-> conn_fd
!= STDERR_FILENO
)
3506 ( void ) dup2 ( hc
-> conn_fd
, STDERR_FILENO
);
3509 /* At this point we would like to set close-on-exec again for hc->conn_fd
3510 ** (see previous comments on Linux's broken behavior re: close-on-exec
3511 ** and dup.) Unfortunately there seems to be another Linux problem, or
3512 ** perhaps a different aspect of the same problem - if we do this
3513 ** close-on-exec in Linux, the socket stays open but stderr gets
3514 ** closed - the last fd duped from the socket. What a mess. So we'll
3515 ** just leave the socket as is, which under other OSs means an extra
3516 ** file descriptor gets passed to the child process. Since the child
3517 ** probably already has that file open via stdin stdout and/or stderr,
3518 ** this is not a problem.
3520 /* (void) fcntl( hc->conn_fd, F_SETFD, 1 ); */
3524 ( void ) nice ( CGI_NICE
);
3525 #endif /* CGI_NICE */
3527 /* Split the program into directory and binary, so we can chdir()
3528 ** to the program's own directory. This isn't in the CGI 1.1
3529 ** spec, but it's what other HTTP servers do.
3531 directory
= strdup ( hc
-> expnfilename
);
3532 if ( directory
== ( char *) 0 )
3533 binary
= hc
-> expnfilename
; /* ignore errors */
3536 binary
= strrchr ( directory
, '/' );
3537 if ( binary
== ( char *) 0 )
3538 binary
= hc
-> expnfilename
;
3542 ( void ) chdir ( directory
); /* ignore errors */
3546 /* Default behavior for SIGPIPE. */
3548 ( void ) sigset ( SIGPIPE
, SIG_DFL
);
3549 #else /* HAVE_SIGSET */
3550 ( void ) signal ( SIGPIPE
, SIG_DFL
);
3551 #endif /* HAVE_SIGSET */
3553 /* Run the program. */
3554 ( void ) execve ( binary
, argp
, envp
);
3556 /* Something went wrong. */
3557 syslog ( LOG_ERR
, "execve %.80s - %m" , hc
-> expnfilename
);
3558 httpd_send_err ( hc
, 500 , err500title
, "" , err500form
, hc
-> encodedurl
);
3559 httpd_write_response ( hc
);
3565 cgi ( httpd_conn
* hc
)
3568 ClientData client_data
;
3570 if ( hc
-> method
== METHOD_GET
|| hc
-> method
== METHOD_POST
)
3572 if ( hc
-> hs
-> cgi_limit
!= 0 && hc
-> hs
-> cgi_count
>= hc
-> hs
-> cgi_limit
)
3575 hc
, 503 , httpd_err503title
, "" , httpd_err503form
,
3579 ++ hc
-> hs
-> cgi_count
;
3580 httpd_clear_ndelay ( hc
-> conn_fd
);
3584 syslog ( LOG_ERR
, "fork - %m" );
3586 hc
, 500 , err500title
, "" , err500form
, hc
-> encodedurl
);
3591 /* Child process. */
3593 httpd_unlisten ( hc
-> hs
);
3597 /* Parent process. */
3598 syslog ( LOG_DEBUG
, "spawned CGI process %d for file '%.200s'" , r
, hc
-> expnfilename
);
3599 #ifdef CGI_TIMELIMIT
3600 /* Schedule a kill for the child process, in case it runs too long */
3602 if ( tmr_create ( ( struct timeval
*) 0 , cgi_kill
, client_data
, CGI_TIMELIMIT
* 1000L , 0 ) == ( Timer
*) 0 )
3604 syslog ( LOG_CRIT
, "tmr_create(cgi_kill child) failed" );
3607 #endif /* CGI_TIMELIMIT */
3609 hc
-> bytes_sent
= CGI_BYTECOUNT
;
3610 hc
-> should_linger
= 0 ;
3615 hc
, 501 , err501title
, "" , err501form
, httpd_method_str ( hc
-> method
) );
3624 really_start_request ( httpd_conn
* hc
, struct timeval
* nowP
)
3626 static char * indexname
;
3627 static size_t maxindexname
= 0 ;
3628 static const char * index_names
[] = { INDEX_NAMES
};
3631 static char * dirname
;
3632 static size_t maxdirname
= 0 ;
3633 #endif /* AUTH_FILE */
3634 size_t expnlen
, indxlen
;
3638 expnlen
= strlen ( hc
-> expnfilename
);
3640 if ( hc
-> method
!= METHOD_GET
&& hc
-> method
!= METHOD_HEAD
&&
3641 hc
-> method
!= METHOD_POST
)
3644 hc
, 501 , err501title
, "" , err501form
, httpd_method_str ( hc
-> method
) );
3648 /* Stat the file. */
3649 if ( stat ( hc
-> expnfilename
, & hc
-> sb
) < 0 )
3651 httpd_send_err ( hc
, 500 , err500title
, "" , err500form
, hc
-> encodedurl
);
3655 /* Is it world-readable or world-executable? We check explicitly instead
3656 ** of just trying to open it, so that no one ever gets surprised by
3657 ** a file that's not set world-readable and yet somehow is
3658 ** readable by the HTTP server and therefore the *whole* world.
3660 if ( ! ( hc
-> sb
. st_mode
& ( S_IROTH
| S_IXOTH
) ) )
3664 "%.80s URL \" %.80s \" resolves to a non world-readable file" ,
3665 httpd_ntoa ( & hc
-> client_addr
), hc
-> encodedurl
);
3667 hc
, 403 , err403title
, "" ,
3668 ERROR_FORM ( err403form
, "The requested URL '%.80s' resolves to a file that is not world-readable. \n " ),
3673 /* Is it a directory? */
3674 if ( S_ISDIR ( hc
-> sb
. st_mode
) )
3676 /* If there's pathinfo, it's just a non-existent file. */
3677 if ( hc
-> pathinfo
[ 0 ] != '\0' )
3679 httpd_send_err ( hc
, 404 , err404title
, "" , err404form
, hc
-> encodedurl
);
3683 /* Special handling for directory URLs that don't end in a slash.
3684 ** We send back an explicit redirect with the slash, because
3685 ** otherwise many clients can't build relative URLs properly.
3687 if ( strcmp ( hc
-> origfilename
, "" ) != 0 &&
3688 strcmp ( hc
-> origfilename
, "." ) != 0 &&
3689 hc
-> origfilename
[ strlen ( hc
-> origfilename
) - 1 ] != '/' )
3691 send_dirredirect ( hc
);
3695 /* Check for an index file. */
3696 for ( i
= 0 ; i
< sizeof ( index_names
) / sizeof ( char *); ++ i
)
3699 & indexname
, & maxindexname
,
3700 expnlen
+ 1 + strlen ( index_names
[ i
] ) );
3701 ( void ) strcpy ( indexname
, hc
-> expnfilename
);
3702 indxlen
= strlen ( indexname
);
3703 if ( indxlen
== 0 || indexname
[ indxlen
- 1 ] != '/' )
3704 ( void ) strcat ( indexname
, "/" );
3705 if ( strcmp ( indexname
, "./" ) == 0 )
3706 indexname
[ 0 ] = '\0' ;
3707 ( void ) strcat ( indexname
, index_names
[ i
] );
3708 if ( stat ( indexname
, & hc
-> sb
) >= 0 )
3712 /* Nope, no index file, so it's an actual directory request. */
3713 #ifdef GENERATE_INDEXES
3714 /* Directories must be readable for indexing. */
3715 if ( ! ( hc
-> sb
. st_mode
& S_IROTH
) )
3719 "%.80s URL \" %.80s \" tried to index a directory with indexing disabled" ,
3720 httpd_ntoa ( & hc
-> client_addr
), hc
-> encodedurl
);
3722 hc
, 403 , err403title
, "" ,
3723 ERROR_FORM ( err403form
, "The requested URL '%.80s' resolves to a directory that has indexing disabled. \n " ),
3728 /* Check authorization for this directory. */
3729 if ( auth_check ( hc
, hc
-> expnfilename
) == - 1 )
3731 #endif /* AUTH_FILE */
3732 /* Referrer check. */
3733 if ( ! check_referrer ( hc
) )
3735 /* Ok, generate an index. */
3737 #else /* GENERATE_INDEXES */
3739 LOG_INFO
, "%.80s URL \" %.80s \" tried to index a directory" ,
3740 httpd_ntoa ( & hc
-> client_addr
), hc
-> encodedurl
);
3742 hc
, 403 , err403title
, "" ,
3743 ERROR_FORM ( err403form
, "The requested URL '%.80s' is a directory, and directory indexing is disabled on this server. \n " ),
3746 #endif /* GENERATE_INDEXES */
3749 /* Got an index file. Expand symlinks again. More pathinfo means
3750 ** something went wrong.
3752 cp
= expand_symlinks ( indexname
, & pi
, hc
-> hs
-> no_symlink_check
, hc
-> tildemapped
);
3753 if ( cp
== ( char *) 0 || pi
[ 0 ] != '\0' )
3755 httpd_send_err ( hc
, 500 , err500title
, "" , err500form
, hc
-> encodedurl
);
3758 expnlen
= strlen ( cp
);
3759 httpd_realloc_str ( & hc
-> expnfilename
, & hc
-> maxexpnfilename
, expnlen
);
3760 ( void ) strcpy ( hc
-> expnfilename
, cp
);
3762 /* Now, is the index version world-readable or world-executable? */
3763 if ( ! ( hc
-> sb
. st_mode
& ( S_IROTH
| S_IXOTH
) ) )
3767 "%.80s URL \" %.80s \" resolves to a non-world-readable index file" ,
3768 httpd_ntoa ( & hc
-> client_addr
), hc
-> encodedurl
);
3770 hc
, 403 , err403title
, "" ,
3771 ERROR_FORM ( err403form
, "The requested URL '%.80s' resolves to an index file that is not world-readable. \n " ),
3778 /* Check authorization for this directory. */
3779 httpd_realloc_str ( & dirname
, & maxdirname
, expnlen
);
3780 ( void ) strcpy ( dirname
, hc
-> expnfilename
);
3781 cp
= strrchr ( dirname
, '/' );
3782 if ( cp
== ( char *) 0 )
3783 ( void ) strcpy ( dirname
, "." );
3786 if ( auth_check ( hc
, dirname
) == - 1 )
3789 /* Check if the filename is the AUTH_FILE itself - that's verboten. */
3790 if ( expnlen
== sizeof ( AUTH_FILE
) - 1 )
3792 if ( strcmp ( hc
-> expnfilename
, AUTH_FILE
) == 0 )
3796 "%.80s URL \" %.80s \" tried to retrieve an auth file" ,
3797 httpd_ntoa ( & hc
-> client_addr
), hc
-> encodedurl
);
3799 hc
, 403 , err403title
, "" ,
3800 ERROR_FORM ( err403form
, "The requested URL '%.80s' is an authorization file, retrieving it is not permitted. \n " ),
3805 else if ( expnlen
>= sizeof ( AUTH_FILE
) &&
3806 strcmp ( &( hc
-> expnfilename
[ expnlen
- sizeof ( AUTH_FILE
) + 1 ]), AUTH_FILE
) == 0 &&
3807 hc
-> expnfilename
[ expnlen
- sizeof ( AUTH_FILE
)] == '/' )
3811 "%.80s URL \" %.80s \" tried to retrieve an auth file" ,
3812 httpd_ntoa ( & hc
-> client_addr
), hc
-> encodedurl
);
3814 hc
, 403 , err403title
, "" ,
3815 ERROR_FORM ( err403form
, "The requested URL '%.80s' is an authorization file, retrieving it is not permitted. \n " ),
3819 #endif /* AUTH_FILE */
3821 /* Referrer check. */
3822 if ( ! check_referrer ( hc
) )
3825 /* Is it world-executable and in the CGI area? */
3826 if ( hc
-> hs
-> cgi_pattern
!= ( char *) 0 &&
3827 ( hc
-> sb
. st_mode
& S_IXOTH
) &&
3828 match ( hc
-> hs
-> cgi_pattern
, hc
-> expnfilename
) )
3831 /* It's not CGI. If it's executable or there's pathinfo, someone's
3832 ** trying to either serve or run a non-CGI file as CGI. Either case
3835 if ( hc
-> sb
. st_mode
& S_IXOTH
)
3838 LOG_NOTICE
, "%.80s URL \" %.80s \" is executable but isn't CGI" ,
3839 httpd_ntoa ( & hc
-> client_addr
), hc
-> encodedurl
);
3841 hc
, 403 , err403title
, "" ,
3842 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 " ),
3846 if ( hc
-> pathinfo
[ 0 ] != '\0' )
3849 LOG_INFO
, "%.80s URL \" %.80s \" has pathinfo but isn't CGI" ,
3850 httpd_ntoa ( & hc
-> client_addr
), hc
-> encodedurl
);
3852 hc
, 403 , err403title
, "" ,
3853 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 " ),
3858 /* Fill in last_byte_index, if necessary. */
3859 if ( hc
-> got_range
&&
3860 ( hc
-> last_byte_index
== - 1 || hc
-> last_byte_index
>= hc
-> sb
. st_size
) )
3861 hc
-> last_byte_index
= hc
-> sb
. st_size
- 1 ;
3865 if ( hc
-> method
== METHOD_HEAD
)
3868 hc
, 200 , ok200title
, hc
-> encodings
, "" , hc
-> type
, hc
-> sb
. st_size
,
3871 else if ( hc
-> if_modified_since
!= ( time_t ) - 1 &&
3872 hc
-> if_modified_since
>= hc
-> sb
. st_mtime
)
3875 hc
, 304 , err304title
, hc
-> encodings
, "" , hc
-> type
, ( off_t
) - 1 ,
3880 hc
-> file_address
= mmc_map ( hc
-> expnfilename
, &( hc
-> sb
), nowP
);
3881 if ( hc
-> file_address
== ( char *) 0 )
3883 httpd_send_err ( hc
, 500 , err500title
, "" , err500form
, hc
-> encodedurl
);
3887 hc
, 200 , ok200title
, hc
-> encodings
, "" , hc
-> type
, hc
-> sb
. st_size
,
3896 httpd_start_request ( httpd_conn
* hc
, struct timeval
* nowP
)
3900 /* Really start the request. */
3901 r
= really_start_request ( hc
, nowP
);
3903 /* And return the status. */
3909 make_log_entry ( httpd_conn
* hc
, struct timeval
* nowP
)
3915 if ( hc
-> hs
-> no_log
)
3918 /* This is straight CERN Combined Log Format - the only tweak
3919 ** being that if we're using syslog() we leave out the date, because
3920 ** syslogd puts it in. The included syslogtocern script turns the
3921 ** results into true CERN format.
3924 /* Format remote user. */
3925 if ( hc
-> remoteuser
[ 0 ] != '\0' )
3926 ru
= hc
-> remoteuser
;
3929 /* If we're vhosting, prepend the hostname to the url. This is
3930 ** a little weird, perhaps writing separate log files for
3931 ** each vhost would make more sense.
3933 if ( hc
-> hs
-> vhost
&& ! hc
-> tildemapped
)
3934 ( void ) my_snprintf ( url
, sizeof ( url
),
3936 hc
-> hostname
== ( char *) 0 ? hc
-> hs
-> server_hostname
: hc
-> hostname
,
3939 ( void ) my_snprintf ( url
, sizeof ( url
),
3940 "%.200s" , hc
-> encodedurl
);
3941 /* Format the bytes. */
3942 if ( hc
-> bytes_sent
>= 0 )
3944 bytes
, sizeof ( bytes
), "%lld" , ( long long ) hc
-> bytes_sent
);
3946 ( void ) strcpy ( bytes
, "-" );
3948 /* Logfile or syslog? */
3949 if ( hc
-> hs
-> logfp
!= ( FILE *) 0 )
3953 const char * cernfmt_nozone
= "%d/%b/%Y:%H:%M:%S" ;
3954 char date_nozone
[ 100 ];
3959 /* Get the current time, if necessary. */
3960 if ( nowP
!= ( struct timeval
*) 0 )
3963 now
= time ( ( time_t *) 0 );
3964 /* Format the time, forcing a numeric timezone (some log analyzers
3965 ** are stoooopid about this).
3967 t
= localtime ( & now
);
3968 ( void ) strftime ( date_nozone
, sizeof ( date_nozone
), cernfmt_nozone
, t
);
3969 #ifdef HAVE_TM_GMTOFF
3970 zone
= t
-> tm_gmtoff
/ 60L ;
3972 zone
= - timezone
/ 60L ;
3973 /* Probably have to add something about daylight time here. */
3982 zone
= ( zone
/ 60 ) * 100 + zone
% 60 ;
3983 ( void ) my_snprintf ( date
, sizeof ( date
),
3984 "%s %c%04d" , date_nozone
, sign
, zone
);
3985 /* And write the log entry. */
3986 ( void ) fprintf ( hc
-> hs
-> logfp
,
3987 "%.80s - %.80s [%s] \" %.80s %.300s %.80s \" %d %s \" %.200s \" \" %.200s \"\n " ,
3988 httpd_ntoa ( & hc
-> client_addr
), ru
, date
,
3989 httpd_method_str ( hc
-> method
), url
, hc
-> protocol
,
3990 hc
-> status
, bytes
, hc
-> referrer
, hc
-> useragent
);
3991 #ifdef FLUSH_LOG_EVERY_TIME
3992 ( void ) fflush ( hc
-> hs
-> logfp
);
3997 "%.80s - %.80s \" %.80s %.200s %.80s \" %d %s \" %.200s \" \" %.200s \" " ,
3998 httpd_ntoa ( & hc
-> client_addr
), ru
,
3999 httpd_method_str ( hc
-> method
), url
, hc
-> protocol
,
4000 hc
-> status
, bytes
, hc
-> referrer
, hc
-> useragent
);
4004 /* Returns 1 if ok to serve the url, 0 if not. */
4006 check_referrer ( httpd_conn
* hc
)
4011 /* Are we doing referrer checking at all? */
4012 if ( hc
-> hs
-> url_pattern
== ( char *) 0 )
4015 r
= really_check_referrer ( hc
);
4019 if ( hc
-> hs
-> vhost
&& hc
-> hostname
!= ( char *) 0 )
4022 cp
= hc
-> hs
-> server_hostname
;
4023 if ( cp
== ( char *) 0 )
4026 LOG_INFO
, "%.80s non-local referrer \" %.80s%.80s \" \" %.80s \" " ,
4027 httpd_ntoa ( & hc
-> client_addr
), cp
, hc
-> encodedurl
, hc
-> referrer
);
4029 hc
, 403 , err403title
, "" ,
4030 ERROR_FORM ( err403form
, "You must supply a local referrer to get URL '%.80s' from this server. \n " ),
4037 /* Returns 1 if ok to serve the url, 0 if not. */
4039 really_check_referrer ( httpd_conn
* hc
)
4045 static char * refhost
= ( char *) 0 ;
4046 static size_t refhost_size
= 0 ;
4051 /* Check for an empty referrer. */
4052 if ( hc
-> referrer
== ( char *) 0 || hc
-> referrer
[ 0 ] == '\0' ||
4053 ( cp1
= strstr ( hc
-> referrer
, "//" ) ) == ( char *) 0 )
4055 /* Disallow if we require a referrer and the url matches. */
4056 if ( hs
-> no_empty_referrers
&& match ( hs
-> url_pattern
, hc
-> origfilename
) )
4062 /* Extract referrer host. */
4064 for ( cp2
= cp1
; * cp2
!= '/' && * cp2
!= ':' && * cp2
!= '\0' ; ++ cp2
)
4066 httpd_realloc_str ( & refhost
, & refhost_size
, cp2
- cp1
);
4067 for ( cp3
= refhost
; cp1
< cp2
; ++ cp1
, ++ cp3
)
4068 if ( isupper (* cp1
) )
4069 * cp3
= tolower (* cp1
);
4074 /* Local pattern? */
4075 if ( hs
-> local_pattern
!= ( char *) 0 )
4076 lp
= hs
-> local_pattern
;
4079 /* No local pattern. What's our hostname? */
4082 /* Not vhosting, use the server name. */
4083 lp
= hs
-> server_hostname
;
4084 if ( lp
== ( char *) 0 )
4085 /* Couldn't figure out local hostname - give up. */
4090 /* We are vhosting, use the hostname on this connection. */
4092 if ( lp
== ( char *) 0 )
4093 /* Oops, no hostname. Maybe it's an old browser that
4094 ** doesn't send a Host: header. We could figure out
4095 ** the default hostname for this IP address, but it's
4096 ** not worth it for the few requests like this.
4102 /* If the referrer host doesn't match the local host pattern, and
4103 ** the filename does match the url pattern, it's an illegal reference.
4105 if ( ! match ( lp
, refhost
) && match ( hs
-> url_pattern
, hc
-> origfilename
) )
4113 httpd_ntoa ( httpd_sockaddr
* saP
)
4116 static char str
[ 200 ];
4118 if ( getnameinfo ( & saP
-> sa
, sockaddr_len ( saP
), str
, sizeof ( str
), 0 , 0 , NI_NUMERICHOST
) != 0 )
4123 else if ( IN6_IS_ADDR_V4MAPPED ( & saP
-> sa_in6
. sin6_addr
) && strncmp ( str
, "::ffff:" , 7 ) == 0 )
4124 /* Elide IPv6ish prefix for IPv4 addresses. */
4125 ( void ) ol_strcpy ( str
, & str
[ 7 ] );
4129 #else /* USE_IPV6 */
4131 return inet_ntoa ( saP
-> sa_in
. sin_addr
);
4133 #endif /* USE_IPV6 */
4138 sockaddr_check ( httpd_sockaddr
* saP
)
4140 switch ( saP
-> sa
. sa_family
)
4142 case AF_INET
: return 1 ;
4144 case AF_INET6
: return 1 ;
4145 #endif /* USE_IPV6 */
4153 sockaddr_len ( httpd_sockaddr
* saP
)
4155 switch ( saP
-> sa
. sa_family
)
4157 case AF_INET
: return sizeof ( struct sockaddr_in
);
4159 case AF_INET6
: return sizeof ( struct sockaddr_in6
);
4160 #endif /* USE_IPV6 */
4162 return 0 ; /* shouldn't happen */
4167 /* Some systems don't have snprintf(), so we make our own that uses
4168 ** either vsnprintf() or vsprintf(). If your system doesn't have
4169 ** vsnprintf(), it is probably vulnerable to buffer overruns.
4173 my_snprintf ( char * str
, size_t size
, const char * format
, ... )
4178 va_start ( ap
, format
);
4179 #ifdef HAVE_VSNPRINTF
4180 r
= vsnprintf ( str
, size
, format
, ap
);
4181 #else /* HAVE_VSNPRINTF */
4182 r
= vsprintf ( str
, format
, ap
);
4183 #endif /* HAVE_VSNPRINTF */
4191 atoll ( const char * str
)
4196 while ( isspace ( * str
) )
4200 case '-' : sign
= - 1 ; ++ str
; break ;
4201 case '+' : sign
= 1 ; ++ str
; break ;
4202 default : sign
= 1 ; break ;
4205 while ( isdigit ( * str
) )
4207 value
= value
* 10 + ( * str
- '0' );
4210 return sign
* value
;
4212 #endif /* HAVE_ATOLL */
4215 /* Read the requested buffer completely, accounting for interruptions. */
4217 httpd_read_fully ( int fd
, void * buf
, size_t nbytes
)
4222 while ( nread
< nbytes
)
4226 r
= read ( fd
, ( char *) buf
+ nread
, nbytes
- nread
);
4227 if ( r
< 0 && ( errno
== EINTR
|| errno
== EAGAIN
) )
4243 /* Write the requested buffer completely, accounting for interruptions. */
4245 httpd_write_fully ( int fd
, const char * buf
, size_t nbytes
)
4250 while ( nwritten
< nbytes
)
4254 r
= write ( fd
, buf
+ nwritten
, nbytes
- nwritten
);
4255 if ( r
< 0 && ( errno
== EINTR
|| errno
== EAGAIN
) )
4271 /* Generate debugging statistics syslog message. */
4273 httpd_logstats ( long secs
)
4275 if ( str_alloc_count
> 0 )
4277 " libhttpd - %d strings allocated, %lu bytes (%g bytes/str)" ,
4278 str_alloc_count
, ( unsigned long ) str_alloc_size
,
4279 ( float ) str_alloc_size
/ str_alloc_count
);