2013-06-28 18:48:22 +02:00
/**
* @ PROJECT Kagera uHTTP Daemon
* @ COPYRIGHT See COPYING in the top level directory
* @ FILE kuhttpd . c
* @ PURPOSE HTTP Server
* @ DEVELOPERS Eric Bishop < eric @ gargoyle - router . com >
* Rafal Kupiec < belliash @ asiotec . eu . org >
* Jef Poskanzer < jef @ acme . com >
*/
# include <unistd.h>
# include <stdlib.h>
# include <stdarg.h>
# include <stdio.h>
# include <string.h>
# include <syslog.h>
# include <limits.h>
# include <sys/param.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <sys/mman.h>
# include <time.h>
# include <pwd.h>
# include <errno.h>
# include <fcntl.h>
# include <signal.h>
# include <ctype.h>
# include <sys/wait.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <netinet/tcp.h>
# include <arpa/inet.h>
# include <netdb.h>
# include <dirent.h>
# include <cyassl/openssl/ssl.h>
# include <cyassl/openssl/err.h>
# include <cyassl/error.h>
# ifdef CROSS_BUILD
# include <cyassl/cyassl_error.h>
# endif
# include "kuhttpd.h"
# include "dateparse.h"
# include "match.h"
# include "mime_enc.h"
# include "mime_typ.h"
static void add_headers ( int s , char * title , char * extra_header , char * me , char * mt , off_t b , time_t mod ) {
time_t now ;
char timebuf [ 100 ] ;
char buf [ 10000 ] ;
int buflen ;
const char * rfc1123_fmt = " %a, %d %b %Y %H:%M:%S GMT " ;
status = s ;
bytes = b ;
make_log_entry ( ) ;
start_response ( ) ;
buflen = snprintf ( buf , sizeof ( buf ) , " %s %d %s \015 \012 " , protocol , status , title ) ;
add_to_response ( buf , buflen ) ;
buflen = snprintf ( buf , sizeof ( buf ) , " Server: %s \015 \012 " , SERVER_SOFTWARE ) ;
add_to_response ( buf , buflen ) ;
now = time ( ( time_t * ) 0 ) ;
( void ) strftime ( timebuf , sizeof ( timebuf ) , rfc1123_fmt , gmtime ( & now ) ) ;
buflen = snprintf ( buf , sizeof ( buf ) , " Date: %s \015 \012 " , timebuf ) ;
add_to_response ( buf , buflen ) ;
buflen = snprintf ( buf , sizeof ( buf ) , " Expires: %s \015 \012 " , timebuf ) ;
add_to_response ( buf , buflen ) ;
if ( mod ! = ( time_t ) - 1 ) {
( void ) strftime ( timebuf , sizeof ( timebuf ) , rfc1123_fmt , gmtime ( & mod ) ) ;
buflen = snprintf ( buf , sizeof ( buf ) , " Last-Modified: %s \015 \012 " , timebuf ) ;
add_to_response ( buf , buflen ) ;
}
if ( extra_header ! = ( char * ) 0 & & extra_header [ 0 ] ! = ' \0 ' ) {
buflen = snprintf ( buf , sizeof ( buf ) , " %s \015 \012 " , extra_header ) ;
add_to_response ( buf , buflen ) ;
}
if ( me ! = ( char * ) 0 & & me [ 0 ] ! = ' \0 ' ) {
buflen = snprintf ( buf , sizeof ( buf ) , " Content-Encoding: %s \015 \012 " , me ) ;
add_to_response ( buf , buflen ) ;
}
if ( mt ! = ( char * ) 0 & & mt [ 0 ] ! = ' \0 ' ) {
buflen = snprintf ( buf , sizeof ( buf ) , " Content-Type: %s \015 \012 " , mt ) ;
add_to_response ( buf , buflen ) ;
}
if ( bytes > = 0 ) {
buflen = snprintf ( buf , sizeof ( buf ) , " Content-Length: %lld \015 \012 " , ( long long int ) bytes ) ;
add_to_response ( buf , buflen ) ;
}
if ( p3p ! = ( char * ) 0 & & p3p [ 0 ] ! = ' \0 ' ) {
buflen = snprintf ( buf , sizeof ( buf ) , " P3P: %s \015 \012 " , p3p ) ;
add_to_response ( buf , buflen ) ;
}
buflen = snprintf ( buf , sizeof ( buf ) , " \015 \012 " ) ;
add_to_response ( buf , buflen ) ;
}
static void add_to_buf ( char * * bufP , size_t * bufsizeP , size_t * buflenP , char * str , size_t len ) {
if ( * bufsizeP = = 0 ) {
* bufsizeP = len + 500 ;
* buflenP = 0 ;
* bufP = ( char * ) e_malloc ( * bufsizeP ) ;
} else if ( * buflenP + len > = * bufsizeP ) {
* bufsizeP = * buflenP + len + 500 ;
* bufP = ( char * ) e_realloc ( ( void * ) * bufP , * bufsizeP ) ;
}
( void ) memmove ( & ( ( * bufP ) [ * buflenP ] ) , str , len ) ;
* buflenP + = len ;
( * bufP ) [ * buflenP ] = ' \0 ' ;
}
static void add_to_request ( char * str , size_t len ) {
add_to_buf ( & request , & request_size , & request_len , str , len ) ;
}
static void add_to_response ( char * str , size_t len ) {
add_to_buf ( & response , & response_size , & response_len , str , len ) ;
}
static void auth_check ( char * dirname , int is_ssl ) {
char authpath [ 10000 ] ;
char realmName [ 500 ] ;
struct stat sb ;
char authinfo [ 500 ] ;
char * authpass ;
char * colon ;
static char line [ 10000 ] ;
int l ;
FILE * fp ;
char * cryp ;
if ( dirname [ strlen ( dirname ) - 1 ] = = ' / ' ) {
( void ) snprintf ( authpath , sizeof ( authpath ) , " %s%s " , dirname , AUTH_FILE ) ;
} else {
( void ) snprintf ( authpath , sizeof ( authpath ) , " %s/%s " , dirname , AUTH_FILE ) ;
}
if ( stat ( authpath , & sb ) < 0 ) {
if ( defaultRealmName = = NULL | | defaultRealmPasswordFile = = NULL ) {
return ;
} else {
snprintf ( authpath , sizeof ( authpath ) , " %s " , defaultRealmPasswordFile ) ;
}
}
if ( strcmp ( authpath , defaultRealmPasswordFile ) = = 0 ) {
snprintf ( realmName , sizeof ( realmName ) , " %s " , defaultRealmName ) ;
} else {
snprintf ( realmName , sizeof ( realmName ) , " %s " , dirname ) ;
}
if ( authorization = = ( char * ) 0 ) {
send_authenticate ( realmName , is_ssl ) ;
}
if ( strncmp ( authorization , " Basic " , 6 ) ! = 0 ) {
send_authenticate ( realmName , is_ssl ) ;
}
l = b64_decode ( & ( authorization [ 6 ] ) , ( unsigned char * ) authinfo , sizeof ( authinfo ) - 1 ) ;
authinfo [ l ] = ' \0 ' ;
authpass = strchr ( authinfo , ' : ' ) ;
if ( authpass = = ( char * ) 0 ) {
send_authenticate ( realmName , is_ssl ) ;
}
* authpass + + = ' \0 ' ;
colon = strchr ( authpass , ' : ' ) ;
if ( colon ! = ( char * ) 0 ) {
* colon = ' \0 ' ;
}
fp = fopen ( authpath , " r " ) ;
if ( fp = = ( FILE * ) 0 ) {
syslog ( LOG_ERR , " %.80s auth file %.80s could not be opened - %m " , ntoa ( & client_addr ) , authpath ) ;
send_error ( 403 , " Forbidden " , " " , " File is protected. " , is_ssl ) ;
}
while ( fgets ( line , sizeof ( line ) , fp ) ! = ( char * ) 0 ) {
l = strlen ( line ) ;
if ( line [ l - 1 ] = = ' \n ' ) {
line [ l - 1 ] = ' \0 ' ;
}
cryp = strchr ( line , ' : ' ) ;
if ( cryp = = ( char * ) 0 ) {
continue ;
}
* cryp + + = ' \0 ' ;
if ( strcmp ( line , authinfo ) = = 0 ) {
( void ) fclose ( fp ) ;
if ( strcmp ( crypt ( authpass , cryp ) , cryp ) = = 0 ) {
remoteuser = line ;
return ;
} else {
send_authenticate ( realmName , is_ssl ) ;
}
}
}
( void ) fclose ( fp ) ;
send_authenticate ( realmName , is_ssl ) ;
}
static int b64_decode ( const char * str , unsigned char * space , int size ) {
const char * cp ;
int space_idx , phase ;
int d , prev_d = 0 ;
unsigned char c ;
space_idx = 0 ;
phase = 0 ;
for ( cp = str ; * cp ! = ' \0 ' ; + + cp ) {
d = b64_decode_table [ ( int ) * cp ] ;
if ( d ! = - 1 ) {
switch ( phase ) {
case 0 :
+ + phase ;
break ;
case 1 :
c = ( ( prev_d < < 2 ) | ( ( d & 0x30 ) > > 4 ) ) ;
if ( space_idx < size ) {
space [ space_idx + + ] = c ;
}
+ + phase ;
break ;
case 2 :
c = ( ( ( prev_d & 0xf ) < < 4 ) | ( ( d & 0x3c ) > > 2 ) ) ;
if ( space_idx < size ) {
space [ space_idx + + ] = c ;
}
+ + phase ;
break ;
case 3 :
c = ( ( ( prev_d & 0x03 ) < < 6 ) | d ) ;
if ( space_idx < size ) {
space [ space_idx + + ] = c ;
}
phase = 0 ;
break ;
}
prev_d = d ;
}
}
return space_idx ;
}
static char * build_env ( char * fmt , char * arg ) {
char * cp ;
int size ;
static char * buf ;
static int maxbuf = 0 ;
size = strlen ( fmt ) + strlen ( arg ) ;
if ( size > maxbuf ) {
if ( maxbuf = = 0 ) {
maxbuf = MAX ( 200 , size + 100 ) ;
buf = ( char * ) e_malloc ( maxbuf ) ;
} else {
maxbuf = MAX ( maxbuf * 2 , size * 5 / 4 ) ;
buf = ( char * ) e_realloc ( ( void * ) buf , maxbuf ) ;
}
}
( void ) snprintf ( buf , maxbuf , fmt , arg ) ;
cp = e_strdup ( buf ) ;
return cp ;
}
static void cgi_interpose_input ( int wfd , int is_ssl ) {
size_t c ;
ssize_t r , r2 ;
char buf [ 1024 ] ;
c = request_len - request_idx ;
if ( c > 0 ) {
if ( write ( wfd , & ( request [ request_idx ] ) , c ) ! = c ) {
return ;
}
}
while ( c < content_length ) {
r = my_read ( buf , MIN ( sizeof ( buf ) , content_length - c ) , is_ssl ) ;
if ( r < 0 & & ( errno = = EINTR | | errno = = EAGAIN ) ) {
sleep ( 1 ) ;
continue ;
}
if ( r < = 0 ) {
return ;
}
for ( ; ; ) {
r2 = write ( wfd , buf , r ) ;
if ( r2 < 0 & & ( errno = = EINTR | | errno = = EAGAIN ) ) {
sleep ( 1 ) ;
continue ;
}
if ( r2 ! = r ) {
return ;
}
break ;
}
c + = r ;
}
post_post_garbage_hack ( is_ssl ) ;
}
static void cgi_interpose_output ( int rfd , int parse_headers , int is_ssl ) {
ssize_t r , r2 ;
char buf [ 1024 ] ;
if ( ! parse_headers ) {
char http_head [ ] = " HTTP/1.0 200 OK \015 \012 " ;
( void ) my_write ( http_head , sizeof ( http_head ) , is_ssl ) ;
} else {
size_t headers_size , headers_len ;
char * headers ;
char * br ;
int status ;
char * title ;
char * cp ;
headers_size = 0 ;
add_to_buf ( & headers , & headers_size , & headers_len , ( char * ) 0 , 0 ) ;
for ( ; ; ) {
r = read ( rfd , buf , sizeof ( buf ) ) ;
if ( r < 0 & & ( errno = = EINTR | | errno = = EAGAIN ) ) {
sleep ( 1 ) ;
continue ;
}
if ( r < = 0 ) {
br = & ( headers [ headers_len ] ) ;
break ;
}
add_to_buf ( & headers , & headers_size , & headers_len , buf , r ) ;
if ( ( br = strstr ( headers , " \015 \012 \015 \012 " ) ) ! = ( char * ) 0 | | ( br = strstr ( headers , " \012 \012 " ) ) ! = ( char * ) 0 ) {
break ;
}
}
if ( headers [ 0 ] = = ' \0 ' ) {
return ;
}
status = 200 ;
if ( ( cp = strstr ( headers , " Status: " ) ) ! = ( char * ) 0 & & cp < br & & ( cp = = headers | | * ( cp - 1 ) = = ' \012 ' ) ) {
cp + = 7 ;
cp + = strspn ( cp , " \t " ) ;
status = atoi ( cp ) ;
}
if ( ( cp = strstr ( headers , " Location: " ) ) ! = ( char * ) 0 & & cp < br & & ( cp = = headers | | * ( cp - 1 ) = = ' \012 ' ) ) {
status = 302 ;
}
switch ( status ) {
case 200 :
title = " OK " ;
break ;
case 302 :
title = " Found " ;
break ;
case 304 :
title = " Not Modified " ;
break ;
case 400 :
title = " Bad Request " ;
break ;
case 401 :
title = " Unauthorized " ;
break ;
case 403 :
title = " Forbidden " ;
break ;
case 404 :
title = " Not Found " ;
break ;
case 408 :
title = " Request Timeout " ;
break ;
case 500 :
title = " Internal Error " ;
break ;
case 501 :
title = " Not Implemented " ;
break ;
case 503 :
title = " Service Temporarily Overloaded " ;
break ;
default :
title = " Something " ;
break ;
}
( void ) snprintf ( buf , sizeof ( buf ) , " HTTP/1.0 %d %s \015 \012 " , status , title ) ;
( void ) my_write ( buf , strlen ( buf ) , is_ssl ) ;
if ( strstr ( headers , " Server: " ) = = NULL ) {
char line [ 200 ] ;
sprintf ( line , " Server: %s \015 \012 " , SERVER_SOFTWARE ) ;
( void ) my_write ( line , strlen ( line ) , is_ssl ) ;
}
if ( strstr ( headers , " Date: " ) = = NULL ) {
char line [ 200 ] ;
char timebuf [ 100 ] ;
time_t now = time ( ( time_t * ) 0 ) ;
const char * rfc1123_fmt = " %a, %d %b %Y %H:%M:%S GMT " ;
( void ) strftime ( timebuf , sizeof ( timebuf ) , rfc1123_fmt , gmtime ( & now ) ) ;
sprintf ( line , " Date: %s \015 \012 " , timebuf ) ;
( void ) my_write ( line , strlen ( line ) , is_ssl ) ;
if ( strstr ( headers , " Expires: " ) = = NULL ) {
sprintf ( line , " Expires: %s \015 \012 " , timebuf ) ;
( void ) my_write ( line , strlen ( line ) , is_ssl ) ;
}
} else if ( strstr ( headers , " Expires: " ) = = NULL ) {
char * line = " Expires: Thu, 01 Jan 1970 00:00:00 GMT \015 \012 " ;
( void ) my_write ( line , strlen ( line ) , is_ssl ) ;
}
( void ) my_write ( headers , headers_len , is_ssl ) ;
}
for ( ; ; ) {
r = read ( rfd , buf , sizeof ( buf ) ) ;
if ( r < 0 & & ( errno = = EINTR | | errno = = EAGAIN ) ) {
sleep ( 1 ) ;
continue ;
}
if ( r < = 0 ) {
goto done ;
}
for ( ; ; ) {
r2 = my_write ( buf , r , is_ssl ) ;
if ( r2 < 0 & & ( errno = = EINTR | | errno = = EAGAIN ) ) {
sleep ( 1 ) ;
continue ;
}
if ( r2 ! = r ) {
goto done ;
}
break ;
}
}
done :
shutdown ( conn_fd , SHUT_WR ) ;
}
static void check_referer ( int is_ssl ) {
char * cp ;
if ( url_pattern = = ( char * ) 0 ) {
return ;
}
if ( really_check_referer ( ) ) {
return ;
}
cp = hostname ;
if ( cp = = ( char * ) 0 ) {
cp = " " ;
}
syslog ( LOG_INFO , " %.80s non-local referer \" %.80s%.80s \" \" %.80s \" " , ntoa ( & client_addr ) , cp , path , referer ) ;
send_error ( 403 , " Forbidden " , " " , " You must supply a local referer. " , is_ssl ) ;
}
static void clear_ndelay ( int fd ) {
int flags , newflags ;
flags = fcntl ( fd , F_GETFL , 0 ) ;
if ( flags ! = - 1 ) {
newflags = flags & ~ ( int ) O_NDELAY ;
if ( newflags ! = flags ) {
( void ) fcntl ( fd , F_SETFL , newflags ) ;
}
}
}
static void de_dotdot ( char * file ) {
char * cp ;
char * cp2 ;
int l ;
while ( ( cp = strstr ( file , " // " ) ) ! = ( char * ) 0 ) {
for ( cp2 = cp + 2 ; * cp2 = = ' / ' ; + + cp2 ) {
continue ;
}
( void ) strcpy ( cp + 1 , cp2 ) ;
}
while ( strncmp ( file , " ./ " , 2 ) = = 0 ) {
( void ) strcpy ( file , file + 2 ) ;
}
while ( ( cp = strstr ( file , " /./ " ) ) ! = ( char * ) 0 ) {
( void ) strcpy ( cp , cp + 2 ) ;
}
for ( ; ; ) {
while ( strncmp ( file , " ../ " , 3 ) = = 0 ) {
( void ) strcpy ( file , file + 3 ) ;
}
cp = strstr ( file , " /../ " ) ;
if ( cp = = ( char * ) 0 ) {
break ;
}
for ( cp2 = cp - 1 ; cp2 > = file & & * cp2 ! = ' / ' ; - - cp2 ) {
continue ;
}
( void ) strcpy ( cp2 + 1 , cp + 4 ) ;
}
while ( ( l = strlen ( file ) ) > 3 & & strcmp ( ( cp = file + l - 3 ) , " /.. " ) = = 0 ) {
for ( cp2 = cp - 1 ; cp2 > = file & & * cp2 ! = ' / ' ; - - cp2 ) {
continue ;
}
if ( cp2 < file ) {
break ;
}
* cp2 = ' \0 ' ;
}
}
static void do_cgi ( int is_ssl , unsigned short conn_port ) {
char * * argp ;
char * * envp ;
int parse_headers ;
char * binary ;
char * directory ;
if ( method ! = METHOD_GET & & method ! = METHOD_POST ) {
send_error ( 501 , " Not Implemented " , " " , " That method is not implemented for CGI. " , is_ssl ) ;
}
if ( conn_fd = = STDIN_FILENO | | conn_fd = = STDOUT_FILENO | | conn_fd = = STDERR_FILENO ) {
int newfd = dup2 ( conn_fd , STDERR_FILENO + 1 ) ;
if ( newfd > = 0 ) {
conn_fd = newfd ;
}
}
envp = make_envp ( is_ssl , conn_port ) ;
argp = make_argp ( ) ;
if ( ( method = = METHOD_POST & & request_len > request_idx ) | | is_ssl ) {
int p [ 2 ] ;
int r ;
if ( pipe ( p ) < 0 ) {
send_error ( 500 , " Internal Error " , " " , " Something unexpected went wrong making a pipe. " , is_ssl ) ;
}
r = fork ( ) ;
if ( r < 0 ) {
send_error ( 500 , " Internal Error " , " " , " Something unexpected went wrong forking an interposer. " , is_ssl ) ;
}
if ( r = = 0 ) {
( void ) close ( p [ 0 ] ) ;
cgi_interpose_input ( p [ 1 ] , is_ssl ) ;
exit ( 0 ) ;
}
( void ) close ( p [ 1 ] ) ;
if ( p [ 0 ] ! = STDIN_FILENO ) {
( void ) dup2 ( p [ 0 ] , STDIN_FILENO ) ;
( void ) close ( p [ 0 ] ) ;
}
} else {
if ( conn_fd ! = STDIN_FILENO ) {
( void ) dup2 ( conn_fd , STDIN_FILENO ) ;
}
}
if ( strncmp ( argp [ 0 ] , " nph- " , 4 ) = = 0 ) {
parse_headers = 0 ;
} else {
parse_headers = 1 ;
}
if ( parse_headers | | is_ssl ) {
int p [ 2 ] ;
int r ;
if ( pipe ( p ) < 0 ) {
send_error ( 500 , " Internal Error " , " " , " Something unexpected went wrong making a pipe. " , is_ssl ) ;
}
r = fork ( ) ;
if ( r < 0 ) {
send_error ( 500 , " Internal Error " , " " , " Something unexpected went wrong forking an interposer. " , is_ssl ) ;
}
if ( r = = 0 ) {
( void ) close ( p [ 1 ] ) ;
cgi_interpose_output ( p [ 0 ] , parse_headers , is_ssl ) ;
exit ( 0 ) ;
}
( void ) close ( p [ 0 ] ) ;
if ( p [ 1 ] ! = STDOUT_FILENO ) {
( void ) dup2 ( p [ 1 ] , STDOUT_FILENO ) ;
}
if ( p [ 1 ] ! = STDERR_FILENO ) {
( void ) dup2 ( p [ 1 ] , STDERR_FILENO ) ;
}
if ( p [ 1 ] ! = STDOUT_FILENO & & p [ 1 ] ! = STDERR_FILENO ) {
( void ) close ( p [ 1 ] ) ;
}
} else {
if ( conn_fd ! = STDOUT_FILENO ) {
( void ) dup2 ( conn_fd , STDOUT_FILENO ) ;
}
if ( conn_fd ! = STDERR_FILENO ) {
( void ) dup2 ( conn_fd , STDERR_FILENO ) ;
}
}
if ( logfp ! = ( FILE * ) 0 ) {
( void ) fclose ( logfp ) ;
}
closelog ( ) ;
if ( nice ( CGI_NICE ) < 0 ) { ; }
directory = e_strdup ( file ) ;
binary = strrchr ( directory , ' / ' ) ;
if ( binary = = ( char * ) 0 ) {
binary = file ;
} else {
* binary + + = ' \0 ' ;
if ( chdir ( directory ) < 0 ) { ; }
}
( void ) signal ( SIGPIPE , SIG_DFL ) ;
( void ) execve ( binary , argp , envp ) ;
send_error ( 500 , " Internal Error " , " " , " Something unexpected went wrong running a CGI program. " , is_ssl ) ;
}
static void do_dir ( int is_ssl ) {
char buf [ 10000 ] ;
size_t buflen ;
char * contents ;
size_t contents_size , contents_len ;
int n , i ;
struct dirent * * dl ;
char * name_info ;
if ( pathinfo ! = ( char * ) 0 ) {
send_error ( 404 , " Not Found " , " " , " File not found. " , is_ssl ) ;
}
auth_check ( file , is_ssl ) ;
check_referer ( is_ssl ) ;
n = scandir ( file , & dl , NULL , alphasort ) ;
if ( n < 0 ) {
syslog ( LOG_INFO , " %.80s Directory \" %.80s \" is protected " , ntoa ( & client_addr ) , path ) ;
send_error ( 403 , " Forbidden " , " " , " Directory is protected. " , is_ssl ) ;
}
contents_size = 0 ;
buflen = snprintf ( buf , sizeof ( buf ) , " <html> \n <head><title>Index of %s</title></head> \n <body bgcolor= \" #FFFFFF \" > \n <h1>Index of %s</h1><hr><pre> \n " , file , file ) ;
add_to_buf ( & contents , & contents_size , & contents_len , buf , buflen ) ;
for ( i = 0 ; i < n ; + + i ) {
if ( strcmp ( dl [ i ] - > d_name , " . " ) = = 0 ) {
continue ;
}
name_info = file_details ( file , dl [ i ] - > d_name ) ;
add_to_buf ( & contents , & contents_size , & contents_len , name_info , strlen ( name_info ) ) ;
}
buflen = snprintf ( buf , sizeof ( buf ) , " </pre><hr> \n %s \n </body> \n </html> \n " , SERVER_SOFTWARE ) ;
add_to_buf ( & contents , & contents_size , & contents_len , buf , buflen ) ;
add_headers ( 200 , " Ok " , " " , " " , " text/html; charset=%s " , contents_len , sb . st_mtime ) ;
if ( method ! = METHOD_HEAD ) {
add_to_response ( contents , contents_len ) ;
}
send_response ( is_ssl ) ;
}
static void do_file ( int is_ssl , unsigned short conn_port ) {
char buf [ 10000 ] ;
char mime_encodings [ 500 ] ;
const char * mime_type ;
char fixed_mime_type [ 500 ] ;
char * cp ;
int fd ;
( void ) strncpy ( buf , file , sizeof ( buf ) ) ;
cp = strrchr ( buf , ' / ' ) ;
if ( cp = = ( char * ) 0 ) {
( void ) strcpy ( buf , " . " ) ;
} else {
* cp = ' \0 ' ;
}
auth_check ( buf , is_ssl ) ;
if ( strcmp ( file , AUTH_FILE ) = = 0 | | ( strcmp ( & ( file [ strlen ( file ) - sizeof ( AUTH_FILE ) + 1 ] ) , AUTH_FILE ) = = 0 & & file [ strlen ( file ) - sizeof ( AUTH_FILE ) ] = = ' / ' ) ) {
syslog ( LOG_NOTICE , " %.80s URL \" %.80s \" tried to retrieve an auth file " , ntoa ( & client_addr ) , path ) ;
send_error ( 403 , " Forbidden " , " " , " File is protected. " , is_ssl ) ;
}
if ( defaultRealmName ! = NULL & & defaultRealmPasswordFile ! = NULL ) {
if ( strcmp ( file , defaultRealmPasswordFile ) = = 0 | | strcmp ( & ( file [ strlen ( file ) - sizeof ( defaultRealmPasswordFile ) + 1 ] ) , defaultRealmPasswordFile ) = = 0 & & file [ strlen ( file ) - sizeof ( defaultRealmPasswordFile ) ] = = ' / ' ) {
syslog ( LOG_NOTICE , " %.80s URL \" %.80s \" tried to retrieve an auth file " , ntoa ( & client_addr ) , path ) ;
send_error ( 403 , " Forbidden " , " " , " File is protected. " , is_ssl ) ;
}
}
check_referer ( is_ssl ) ;
if ( cgi_pattern ! = ( char * ) 0 & & match ( cgi_pattern , file ) ) {
do_cgi ( is_ssl , conn_port ) ;
return ;
} else if ( pathinfo ! = ( char * ) 0 ) {
send_error ( 404 , " Not Found " , " " , " File not found. " , is_ssl ) ;
}
fd = open ( file , O_RDONLY ) ;
if ( fd < 0 ) {
syslog ( LOG_INFO , " %.80s File \" %.80s \" is protected " , ntoa ( & client_addr ) , path ) ;
send_error ( 403 , " Forbidden " , " " , " File is protected. " , is_ssl ) ;
}
mime_type = figure_mime ( file , mime_encodings , sizeof ( mime_encodings ) ) ;
( void ) snprintf ( fixed_mime_type , sizeof ( fixed_mime_type ) , mime_type , charset ) ;
if ( if_modified_since ! = ( time_t ) - 1 & & if_modified_since > = sb . st_mtime ) {
add_headers ( 304 , " Not Modified " , " " , mime_encodings , fixed_mime_type , ( off_t ) - 1 , sb . st_mtime ) ;
send_response ( is_ssl ) ;
return ;
}
add_headers ( 200 , " Ok " , " " , mime_encodings , fixed_mime_type , sb . st_size , sb . st_mtime ) ;
send_response ( is_ssl ) ;
if ( method = = METHOD_HEAD ) {
return ;
}
if ( sb . st_size > 0 ) {
send_via_write ( fd , sb . st_size , is_ssl ) ;
}
( void ) close ( fd ) ;
}
static void * e_malloc ( size_t size ) {
void * ptr ;
ptr = malloc ( size ) ;
if ( ptr = = ( void * ) 0 ) {
syslog ( LOG_CRIT , " out of memory " ) ;
( void ) fprintf ( stderr , " %s: out of memory \n " , argv0 ) ;
exit ( 1 ) ;
}
return ptr ;
}
static void * e_realloc ( void * optr , size_t size ) {
void * ptr ;
ptr = realloc ( optr , size ) ;
if ( ptr = = ( void * ) 0 ) {
syslog ( LOG_CRIT , " out of memory " ) ;
( void ) fprintf ( stderr , " %s: out of memory \n " , argv0 ) ;
exit ( 1 ) ;
}
return ptr ;
}
static char * e_strdup ( char * ostr ) {
char * str ;
str = strdup ( ostr ) ;
if ( str = = ( char * ) 0 ) {
syslog ( LOG_CRIT , " out of memory copying a string " ) ;
( void ) fprintf ( stderr , " %s: out of memory copying a string \n " , argv0 ) ;
exit ( 1 ) ;
}
return str ;
}
static int ext_compare ( struct mime_entry * a , struct mime_entry * b ) {
return strcmp ( a - > ext , b - > ext ) ;
}
static const char * figure_mime ( char * name , char * me , size_t me_size ) {
char * prev_dot ;
char * dot ;
char * ext ;
int me_indexes [ 100 ] , n_me_indexes ;
size_t ext_len , me_len ;
int i , top , bot , mid ;
int r ;
const char * default_type = " application/octet-stream " ;
const char * type ;
n_me_indexes = 0 ;
for ( prev_dot = & name [ strlen ( name ) ] ; ; prev_dot = dot ) {
for ( dot = prev_dot - 1 ; dot > = name & & * dot ! = ' . ' ; - - dot ) ;
if ( dot < name ) {
type = default_type ;
goto done ;
}
ext = dot + 1 ;
ext_len = prev_dot - ext ;
for ( i = 0 ; i < n_enc_tab ; + + i ) {
if ( ext_len = = enc_tab [ i ] . ext_len & & strncasecmp ( ext , enc_tab [ i ] . ext , ext_len ) = = 0 ) {
if ( n_me_indexes < sizeof ( me_indexes ) / sizeof ( * me_indexes ) ) {
me_indexes [ n_me_indexes ] = i ;
+ + n_me_indexes ;
}
goto next ;
}
}
break ;
next : ;
}
top = n_typ_tab - 1 ;
bot = 0 ;
while ( top > = bot ) {
mid = ( top + bot ) / 2 ;
r = strncasecmp ( ext , typ_tab [ mid ] . ext , ext_len ) ;
if ( r < 0 ) {
top = mid - 1 ;
} else if ( r > 0 ) {
bot = mid + 1 ;
} else if ( ext_len < typ_tab [ mid ] . ext_len ) {
top = mid - 1 ;
} else if ( ext_len > typ_tab [ mid ] . ext_len ) {
bot = mid + 1 ;
} else {
type = typ_tab [ mid ] . val ;
goto done ;
}
}
type = default_type ;
done :
me [ 0 ] = ' \0 ' ;
me_len = 0 ;
for ( i = n_me_indexes - 1 ; i > = 0 ; - - i ) {
if ( me_len + enc_tab [ me_indexes [ i ] ] . val_len + 1 < me_size ) {
if ( me [ 0 ] ! = ' \0 ' ) {
( void ) strcpy ( & me [ me_len ] , " , " ) ;
+ + me_len ;
}
( void ) strcpy ( & me [ me_len ] , enc_tab [ me_indexes [ i ] ] . val ) ;
me_len + = enc_tab [ me_indexes [ i ] ] . val_len ;
}
}
return type ;
}
static char * file_details ( const char * dir , const char * name ) {
struct stat sb ;
char f_time [ 20 ] ;
static char encname [ 1000 ] ;
static char buf [ 2000 ] ;
( void ) snprintf ( buf , sizeof ( buf ) , " %s/%s " , dir , name ) ;
if ( lstat ( buf , & sb ) < 0 )
return " ??? " ;
( void ) strftime ( f_time , sizeof ( f_time ) , " %d-%b-%Y %H:%M " , localtime ( & sb . st_mtime ) ) ;
strencode ( encname , sizeof ( encname ) , name ) ;
( void ) snprintf ( buf , sizeof ( buf ) , " <a href= \" %s \" >%-50s</a> %15s %14lld \n " , encname , name , f_time , ( long long int ) sb . st_size ) ;
return buf ;
}
static char * get_method_str ( int m ) {
switch ( m ) {
case METHOD_GET :
return " GET " ;
case METHOD_HEAD :
return " HEAD " ;
case METHOD_POST :
return " POST " ;
default :
return " UNKNOWN " ;
}
}
static int get_pathinfo ( void ) {
int r ;
pathinfo = & file [ strlen ( file ) ] ;
for ( ; ; ) {
do {
- - pathinfo ;
if ( pathinfo < = file ) {
pathinfo = ( char * ) 0 ;
return - 1 ;
}
} while ( * pathinfo ! = ' / ' ) ;
* pathinfo = ' \0 ' ;
r = stat ( file , & sb ) ;
if ( r > = 0 ) {
+ + pathinfo ;
return r ;
} else {
* pathinfo = ' / ' ;
}
}
}
static char * get_request_line ( void ) {
int i ;
char c ;
for ( i = request_idx ; request_idx < request_len ; + + request_idx ) {
c = request [ request_idx ] ;
if ( c = = ' \012 ' | | c = = ' \015 ' ) {
request [ request_idx ] = ' \0 ' ;
+ + request_idx ;
if ( c = = ' \015 ' & & request_idx < request_len & & request [ request_idx ] = = ' \012 ' ) {
request [ request_idx ] = ' \0 ' ;
+ + request_idx ;
}
return & ( request [ i ] ) ;
}
}
return ( char * ) 0 ;
}
static void handle_read_timeout ( int sig , int is_ssl ) {
syslog ( LOG_INFO , " %.80s connection timed out reading " , ntoa ( & client_addr ) ) ;
send_error ( 408 , " Request Timeout " , " " , " No request appeared within a reasonable time period. " , is_ssl ) ;
}
static void handle_read_timeout_sig ( int sig ) {
handle_read_timeout ( 0 , 0 ) ;
}
static void handle_request ( int is_ssl , unsigned short conn_port ) {
char * method_str ;
char * line ;
char * cp ;
int r , file_len , i ;
const char * index_names [ ] = { " default.html " , " default.htm " , " index.html " , " index.htm " , " index.cgi " , " index.sh " } ;
( void ) signal ( SIGALRM , handle_read_timeout_sig ) ;
( void ) alarm ( READ_TIMEOUT ) ;
remoteuser = ( char * ) 0 ;
method = METHOD_UNKNOWN ;
path = ( char * ) 0 ;
file = ( char * ) 0 ;
pathinfo = ( char * ) 0 ;
query = " " ;
protocol = ( char * ) 0 ;
status = 0 ;
bytes = - 1 ;
authorization = ( char * ) 0 ;
content_type = ( char * ) 0 ;
content_length = - 1 ;
cookie = ( char * ) 0 ;
host = ( char * ) 0 ;
if_modified_since = ( time_t ) - 1 ;
referer = " " ;
useragent = " " ;
# ifdef TCP_NOPUSH
r = 1 ;
( void ) setsockopt ( conn_fd , IPPROTO_TCP , TCP_NOPUSH , ( void * ) & r , sizeof ( r ) ) ;
# endif
if ( is_ssl ) {
ssl = SSL_new ( ssl_ctx ) ;
SSL_set_fd ( ssl , conn_fd ) ;
int accept_ret = SSL_accept ( ssl ) ;
if ( accept_ret < = 0 ) {
int e = SSL_get_error ( ssl , accept_ret ) ;
if ( e ! = VERSION_ERROR ) {
syslog ( LOG_CRIT , " error: can't initialize ssl connection, error = %d \n " , e ) ;
}
exit ( 1 ) ;
}
}
start_request ( ) ;
for ( ; ; ) {
char buf [ 10000 ] ;
int r = my_read ( buf , sizeof ( buf ) , is_ssl ) ;
if ( r < 0 & & ( errno = = EINTR | | errno = = EAGAIN ) ) {
continue ;
}
if ( r < = 0 ) {
break ;
}
( void ) alarm ( READ_TIMEOUT ) ;
add_to_request ( buf , r ) ;
if ( strstr ( request , " \015 \012 \015 \012 " ) ! = ( char * ) 0 | | strstr ( request , " \012 \012 " ) ! = ( char * ) 0 ) {
break ;
}
}
method_str = get_request_line ( ) ;
if ( method_str = = ( char * ) 0 ) {
send_error ( 400 , " Bad Request " , " " , " Can't parse request. " , is_ssl ) ;
}
path = strpbrk ( method_str , " \t \012 \015 " ) ;
if ( path = = ( char * ) 0 ) {
send_error ( 400 , " Bad Request " , " " , " Can't parse request. " , is_ssl ) ;
}
* path + + = ' \0 ' ;
path + = strspn ( path , " \t \012 \015 " ) ;
protocol = strpbrk ( path , " \t \012 \015 " ) ;
if ( protocol = = ( char * ) 0 ) {
send_error ( 400 , " Bad Request " , " " , " Can't parse request. " , is_ssl ) ;
}
* protocol + + = ' \0 ' ;
protocol + = strspn ( protocol , " \t \012 \015 " ) ;
query = strchr ( path , ' ? ' ) ;
if ( query = = ( char * ) 0 ) {
query = " " ;
} else {
* query + + = ' \0 ' ;
}
while ( ( line = get_request_line ( ) ) ! = ( char * ) 0 ) {
if ( line [ 0 ] = = ' \0 ' ) {
break ;
} else if ( strncasecmp ( line , " Authorization: " , 14 ) = = 0 ) {
cp = & line [ 14 ] ;
cp + = strspn ( cp , " \t " ) ;
authorization = cp ;
} else if ( strncasecmp ( line , " Content-Length: " , 15 ) = = 0 ) {
cp = & line [ 15 ] ;
cp + = strspn ( cp , " \t " ) ;
content_length = atol ( cp ) ;
} else if ( strncasecmp ( line , " Content-Type: " , 13 ) = = 0 ) {
cp = & line [ 13 ] ;
cp + = strspn ( cp , " \t " ) ;
content_type = cp ;
} else if ( strncasecmp ( line , " Cookie: " , 7 ) = = 0 ) {
cp = & line [ 7 ] ;
cp + = strspn ( cp , " \t " ) ;
cookie = cp ;
} else if ( strncasecmp ( line , " Host: " , 5 ) = = 0 ) {
cp = & line [ 5 ] ;
cp + = strspn ( cp , " \t " ) ;
host = cp ;
if ( strchr ( host , ' / ' ) ! = ( char * ) 0 | | host [ 0 ] = = ' . ' ) {
send_error ( 400 , " Bad Request " , " " , " Can't parse request. " , is_ssl ) ;
}
} else if ( strncasecmp ( line , " If-Modified-Since: " , 18 ) = = 0 ) {
cp = & line [ 18 ] ;
cp + = strspn ( cp , " \t " ) ;
if_modified_since = dateparse ( cp ) ;
} else if ( strncasecmp ( line , " Referer: " , 8 ) = = 0 ) {
cp = & line [ 8 ] ;
cp + = strspn ( cp , " \t " ) ;
referer = cp ;
} else if ( strncasecmp ( line , " User-Agent: " , 11 ) = = 0 ) {
cp = & line [ 11 ] ;
cp + = strspn ( cp , " \t " ) ;
useragent = cp ;
}
}
if ( strcasecmp ( method_str , get_method_str ( METHOD_GET ) ) = = 0 ) {
method = METHOD_GET ;
} else if ( strcasecmp ( method_str , get_method_str ( METHOD_HEAD ) ) = = 0 ) {
method = METHOD_HEAD ;
} else if ( strcasecmp ( method_str , get_method_str ( METHOD_POST ) ) = = 0 ) {
method = METHOD_POST ;
} else {
send_error ( 501 , " Not Implemented " , " " , " That method is not implemented. " , is_ssl ) ;
}
strdecode ( path , path ) ;
if ( path [ 0 ] ! = ' / ' ) {
send_error ( 400 , " Bad Request " , " " , " Bad filename. " , is_ssl ) ;
}
file = & ( path [ 1 ] ) ;
de_dotdot ( file ) ;
if ( file [ 0 ] = = ' \0 ' ) {
file = " ./ " ;
}
if ( file [ 0 ] = = ' / ' | | ( file [ 0 ] = = ' . ' & & file [ 1 ] = = ' . ' & & ( file [ 2 ] = = ' \0 ' | | file [ 2 ] = = ' / ' ) ) ) {
send_error ( 400 , " Bad Request " , " " , " Illegal filename. " , is_ssl ) ;
}
( void ) signal ( SIGALRM , handle_write_timeout ) ;
( void ) alarm ( WRITE_TIMEOUT ) ;
r = stat ( file , & sb ) ;
if ( r < 0 ) {
r = get_pathinfo ( ) ;
}
if ( r < 0 ) {
if ( pageNotFoundFile ! = NULL & & host ! = NULL ) {
send_redirect ( " " , host , pageNotFoundFile , is_ssl ) ;
} else {
send_error ( 404 , " Not Found " , " " , " File not found. " , is_ssl ) ;
}
}
file_len = strlen ( file ) ;
if ( ! S_ISDIR ( sb . st_mode ) ) {
while ( file [ file_len - 1 ] = = ' / ' ) {
file [ file_len - 1 ] = ' \0 ' ;
- - file_len ;
}
do_file ( is_ssl , conn_port ) ;
} else {
char idx [ 10000 ] ;
unsigned char found_index = 0 ;
if ( file [ file_len - 1 ] ! = ' / ' & & pathinfo = = ( char * ) 0 ) {
char location [ 10000 ] ;
if ( query [ 0 ] ! = ' \0 ' ) {
( void ) snprintf ( location , sizeof ( location ) , " Location: %s/?%s " , path , query ) ;
} else {
( void ) snprintf ( location , sizeof ( location ) , " Location: %s/ " , path ) ;
send_error ( 302 , " Found " , location , " Directories must end with a slash. " , is_ssl ) ;
}
}
if ( defaultPageFile ! = NULL ) {
( void ) snprintf ( idx , sizeof ( idx ) , " %s%s " , file , defaultPageFile ) ;
if ( stat ( idx , & sb ) > = 0 ) {
file = idx ;
do_file ( is_ssl , conn_port ) ;
found_index = 1 ;
}
}
for ( i = 0 ; i < ( sizeof ( index_names ) / sizeof ( char * ) ) & & found_index = = 0 ; + + i ) {
( void ) snprintf ( idx , sizeof ( idx ) , " %s%s " , file , index_names [ i ] ) ;
if ( stat ( idx , & sb ) > = 0 ) {
file = idx ;
do_file ( is_ssl , conn_port ) ;
found_index = 1 ;
}
}
if ( found_index = = 0 ) {
if ( allowDirectoryListing = = 0 ) {
if ( pageNotFoundFile ! = NULL & & host ! = NULL ) {
send_redirect ( " " , host , pageNotFoundFile , is_ssl ) ;
} else {
send_error ( 404 , " Not Found " , " " , " File not found. " , is_ssl ) ;
}
} else {
do_dir ( is_ssl ) ;
}
}
}
if ( is_ssl ) {
SSL_free ( ssl ) ;
}
}
static void handle_sigchld ( int sig ) {
const int oerrno = errno ;
pid_t pid ;
int status ;
( void ) signal ( SIGCHLD , handle_sigchld ) ;
for ( ; ; ) {
pid = waitpid ( ( pid_t ) - 1 , & status , WNOHANG ) ;
if ( ( int ) pid = = 0 ) {
break ;
}
if ( ( int ) pid < 0 ) {
if ( errno = = EINTR | | errno = = EAGAIN ) {
continue ;
}
if ( errno ! = ECHILD ) {
syslog ( LOG_ERR , " child wait - %m " ) ;
perror ( " child wait " ) ;
}
break ;
}
}
errno = oerrno ;
}
static void handle_sighup ( int sig ) {
const int oerrno = errno ;
( void ) signal ( SIGHUP , handle_sighup ) ;
got_hup = 1 ;
errno = oerrno ;
}
static void handle_sigterm ( int sig ) {
syslog ( LOG_NOTICE , " exiting due to signal %d " , sig ) ;
( void ) fprintf ( stderr , " %s: exiting due to signal %d \n " , argv0 , sig ) ;
closelog ( ) ;
exit ( 1 ) ;
}
static void handle_write_timeout ( int sig ) {
syslog ( LOG_INFO , " %.80s connection timed out writing " , ntoa ( & client_addr ) ) ;
exit ( 1 ) ;
}
static int hexit ( char c ) {
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
return c - ' 0 ' ;
}
if ( c > = ' a ' & & c < = ' f ' ) {
return c - ' a ' + 10 ;
}
if ( c > = ' A ' & & c < = ' F ' ) {
return c - ' A ' + 10 ;
}
return 0 ;
}
static void init_mime ( void ) {
int i ;
qsort ( enc_tab , n_enc_tab , sizeof ( * enc_tab ) , ( int ( * ) ( const void * , const void * ) ) ext_compare ) ;
qsort ( typ_tab , n_typ_tab , sizeof ( * typ_tab ) , ( int ( * ) ( const void * , const void * ) ) ext_compare ) ;
for ( i = 0 ; i < n_enc_tab ; + + i ) {
enc_tab [ i ] . ext_len = strlen ( enc_tab [ i ] . ext ) ;
enc_tab [ i ] . val_len = strlen ( enc_tab [ i ] . val ) ;
}
for ( i = 0 ; i < n_typ_tab ; + + i ) {
typ_tab [ i ] . ext_len = strlen ( typ_tab [ i ] . ext ) ;
typ_tab [ i ] . val_len = strlen ( typ_tab [ i ] . val ) ;
}
}
static int initialize_listen_socket ( usockaddr * usaP ) {
int listen_fd ;
int i ;
if ( ! sockaddr_check ( usaP ) ) {
syslog ( LOG_ERR , " unknown sockaddr family on listen socket - %d " , usaP - > sa . sa_family ) ;
( void ) fprintf ( stderr , " %s: unknown sockaddr family on listen socket - %d \n " , argv0 , usaP - > sa . sa_family ) ;
return - 1 ;
}
listen_fd = socket ( usaP - > sa . sa_family , SOCK_STREAM , 0 ) ;
if ( listen_fd < 0 ) {
if ( usaP - > sa . sa_family = = AF_INET6 & & ( errno = = ENOPROTOOPT | | errno = = EPROTONOSUPPORT | | errno = = ESOCKTNOSUPPORT | | EPFNOSUPPORT | | EAFNOSUPPORT ) ) {
/* IPv6 not compiled into kernel, no big deal, don't print errors */
} else {
syslog ( LOG_CRIT , " socket %.80s - %m " , ntoa ( usaP ) ) ;
perror ( " socket " ) ;
}
return - 1 ;
}
( void ) fcntl ( listen_fd , F_SETFD , 1 ) ;
i = 1 ;
if ( setsockopt ( listen_fd , SOL_SOCKET , SO_REUSEADDR , ( void * ) & i , sizeof ( i ) ) < 0 ) {
syslog ( LOG_CRIT , " setsockopt SO_REUSEADDR - %m " ) ;
perror ( " setsockopt SO_REUSEADDR " ) ;
return - 1 ;
}
if ( bind ( listen_fd , & usaP - > sa , sockaddr_len ( usaP ) ) < 0 ) {
syslog ( LOG_CRIT , " bind %.80s - %m " , ntoa ( usaP ) ) ;
perror ( " bind " ) ;
return - 1 ;
}
if ( listen ( listen_fd , 1024 ) < 0 ) {
syslog ( LOG_CRIT , " listen - %m " ) ;
perror ( " listen " ) ;
return - 1 ;
}
return listen_fd ;
}
static void lookup_hostname ( usockaddr * usa4P , usockaddr * usa4sP , size_t sa4_len , int * gotv4P , int * gotv4sP , usockaddr * usa6P , usockaddr * usa6sP , size_t sa6_len , int * gotv6P , int * gotv6sP ) {
int port_index ;
unsigned short port_list [ 4 ] ;
port_list [ 0 ] = port ;
port_list [ 1 ] = sslPort ;
port_list [ 2 ] = 0 ;
* gotv6P = 0 ;
* gotv6sP = 0 ;
* gotv4P = 0 ;
* gotv4sP = 0 ;
for ( port_index = 0 ; port_list [ port_index ] ! = 0 ; port_index + + ) {
struct addrinfo hints ;
char portstr [ 10 ] ;
int gaierr ;
struct addrinfo * ai ;
struct addrinfo * ai2 ;
struct addrinfo * aiv6 ;
struct addrinfo * aiv4 ;
( void ) memset ( & hints , 0 , sizeof ( hints ) ) ;
hints . ai_family = PF_UNSPEC ;
hints . ai_flags = AI_PASSIVE ;
hints . ai_socktype = SOCK_STREAM ;
( void ) snprintf ( portstr , sizeof ( portstr ) , " %d " , ( int ) port_list [ port_index ] ) ;
if ( ( gaierr = getaddrinfo ( hostname , portstr , & hints , & ai ) ) ! = 0 ) {
syslog ( LOG_CRIT , " getaddrinfo %.80s - %s " , hostname , gai_strerror ( gaierr ) ) ;
( void ) fprintf ( stderr , " %s: getaddrinfo %.80s - %s \n " , argv0 , hostname , gai_strerror ( gaierr ) ) ;
exit ( 1 ) ;
}
aiv6 = ( struct addrinfo * ) 0 ;
aiv4 = ( struct addrinfo * ) 0 ;
for ( ai2 = ai ; ai2 ! = ( struct addrinfo * ) 0 ; ai2 = ai2 - > ai_next ) {
switch ( ai2 - > ai_family ) {
case AF_INET6 :
if ( aiv6 = = ( struct addrinfo * ) 0 ) {
aiv6 = ai2 ;
}
break ;
case AF_INET :
if ( aiv4 = = ( struct addrinfo * ) 0 ) {
aiv4 = ai2 ;
}
break ;
}
}
if ( aiv6 ! = ( struct addrinfo * ) 0 ) {
if ( sa6_len < aiv6 - > ai_addrlen ) {
syslog ( LOG_CRIT , " %.80s - sockaddr too small (%lu < %lu) " , hostname , ( unsigned long ) sa6_len , ( unsigned long ) aiv6 - > ai_addrlen ) ;
( void ) fprintf ( stderr , " %s: %.80s - sockaddr too small (%lu < %lu) \n " , argv0 , hostname , ( unsigned long ) sa6_len , ( unsigned long ) aiv6 - > ai_addrlen ) ;
exit ( 1 ) ;
}
if ( port_index = = 0 ) {
* gotv6P = 1 ;
( void ) memset ( usa6P , 0 , sa6_len ) ;
( void ) memmove ( usa6P , aiv6 - > ai_addr , aiv6 - > ai_addrlen ) ;
} else {
* gotv6sP = 1 ;
( void ) memset ( usa6sP , 0 , sa6_len ) ;
( void ) memmove ( usa6sP , aiv6 - > ai_addr , aiv6 - > ai_addrlen ) ;
}
}
if ( aiv4 ! = ( struct addrinfo * ) 0 ) {
if ( sa4_len < aiv4 - > ai_addrlen ) {
syslog ( LOG_CRIT , " %.80s - sockaddr too small (%lu < %lu) " , hostname , ( unsigned long ) sa4_len , ( unsigned long ) aiv4 - > ai_addrlen ) ;
( void ) fprintf ( stderr , " %s: %.80s - sockaddr too small (%lu < %lu) \n " , argv0 , hostname , ( unsigned long ) sa4_len , ( unsigned long ) aiv4 - > ai_addrlen ) ;
exit ( 1 ) ;
}
if ( port_index = = 0 ) {
* gotv4P = 1 ;
( void ) memset ( usa4P , 0 , sa4_len ) ;
( void ) memmove ( usa4P , aiv4 - > ai_addr , aiv4 - > ai_addrlen ) ;
} else {
* gotv4sP = 1 ;
( void ) memset ( usa4sP , 0 , sa4_len ) ;
( void ) memmove ( usa4sP , aiv4 - > ai_addr , aiv4 - > ai_addrlen ) ;
}
* gotv4P = 1 ;
}
freeaddrinfo ( ai ) ;
}
}
int main ( int argc , char * * argv ) {
int argn ;
struct passwd * pwd ;
uid_t uid = 32767 ;
gid_t gid = 32767 ;
usockaddr host_addr4 ;
usockaddr host_addr6 ;
int gotv4 , gotv4s , gotv6 , gotv6s ;
fd_set lfdset ;
int maxfd ;
usockaddr usa ;
int sz , r ;
char * cp ;
usockaddr host_addr4s ;
usockaddr host_addr6s ;
argv0 = argv [ 0 ] ;
debug = 0 ;
port = 0 ;
dir = ( char * ) 0 ;
data_dir = ( char * ) 0 ;
cgi_pattern = DEFAULT_CGI_PATTERN ;
url_pattern = ( char * ) 0 ;
no_empty_referers = 0 ;
local_pattern = ( char * ) 0 ;
charset = DEFAULT_CHARSET ;
language = DEFAULT_LANGUAGE ;
p3p = ( char * ) 0 ;
max_age = - 1 ;
user = DEFAULT_USER ;
hostname = ( char * ) 0 ;
logfile = ( char * ) 0 ;
pidfile = ( char * ) 0 ;
logfp = ( FILE * ) 0 ;
do_ssl = 0 ;
certfile = DEFAULT_CERTFILE ;
cipher = ( char * ) 0 ;
defaultRealmName = NULL ;
defaultRealmPasswordFile = NULL ;
defaultPageFile = NULL ;
pageNotFoundFile = NULL ;
allowDirectoryListing = 1 ;
sslPort = 0 ;
argn = 1 ;
while ( argn < argc & & argv [ argn ] [ 0 ] = = ' - ' ) {
if ( strcmp ( argv [ argn ] , " -V " ) = = 0 ) {
( void ) printf ( " %s \n " , SERVER_SOFTWARE ) ;
exit ( 0 ) ;
} else if ( strcmp ( argv [ argn ] , " -C " ) = = 0 & & argn + 1 < argc ) {
+ + argn ;
read_config ( argv [ argn ] ) ;
} else if ( strcmp ( argv [ argn ] , " -D " ) = = 0 ) {
debug = 1 ;
} else if ( strcmp ( argv [ argn ] , " -S " ) = = 0 ) {
do_ssl = 1 ;
} else if ( strcmp ( argv [ argn ] , " -E " ) = = 0 & & argn + 1 < argc ) {
+ + argn ;
certfile = argv [ argn ] ;
} else if ( strcmp ( argv [ argn ] , " -Y " ) = = 0 & & argn + 1 < argc ) {
+ + argn ;
cipher = argv [ argn ] ;
} else if ( strcmp ( argv [ argn ] , " -p " ) = = 0 & & argn + 1 < argc ) {
+ + argn ;
port = ( unsigned short ) atoi ( argv [ argn ] ) ;
} else if ( strcmp ( argv [ argn ] , " -d " ) = = 0 & & argn + 1 < argc ) {
+ + argn ;
dir = argv [ argn ] ;
} else if ( strcmp ( argv [ argn ] , " -dd " ) = = 0 & & argn + 1 < argc ) {
+ + argn ;
data_dir = argv [ argn ] ;
} else if ( strcmp ( argv [ argn ] , " -c " ) = = 0 & & argn + 1 < argc ) {
+ + argn ;
cgi_pattern = argv [ argn ] ;
} else if ( strcmp ( argv [ argn ] , " -u " ) = = 0 & & argn + 1 < argc ) {
+ + argn ;
user = argv [ argn ] ;
} else if ( strcmp ( argv [ argn ] , " -h " ) = = 0 & & argn + 1 < argc ) {
+ + argn ;
hostname = argv [ argn ] ;
} else if ( strcmp ( argv [ argn ] , " -l " ) = = 0 & & argn + 1 < argc ) {
+ + argn ;
logfile = argv [ argn ] ;
} else if ( strcmp ( argv [ argn ] , " -i " ) = = 0 & & argn + 1 < argc ) {
+ + argn ;
pidfile = argv [ argn ] ;
} else if ( strcmp ( argv [ argn ] , " -T " ) = = 0 & & argn + 1 < argc ) {
+ + argn ;
charset = argv [ argn ] ;
} else if ( strcmp ( argv [ argn ] , " -L " ) = = 0 & & argn + 1 < argc ) {
+ + argn ;
language = argv [ argn ] ;
} else if ( strcmp ( argv [ argn ] , " -P " ) = = 0 & & argn + 1 < argc ) {
+ + argn ;
p3p = argv [ argn ] ;
} else if ( strcmp ( argv [ argn ] , " -M " ) = = 0 & & argn + 1 < argc ) {
+ + argn ;
max_age = atoi ( argv [ argn ] ) ;
} else if ( strcmp ( argv [ argn ] , " -DRN " ) = = 0 & & argn + 1 < argc ) {
+ + argn ;
defaultRealmName = argv [ argn ] ;
} else if ( strcmp ( argv [ argn ] , " -DRP " ) = = 0 & & argn + 1 < argc ) {
+ + argn ;
defaultRealmPasswordFile = argv [ argn ] ;
} else if ( strcmp ( argv [ argn ] , " -DPF " ) = = 0 & & argn + 1 < argc ) {
+ + argn ;
defaultPageFile = argv [ argn ] ;
} else if ( strcmp ( argv [ argn ] , " -PNF " ) = = 0 & & argn + 1 < argc ) {
+ + argn ;
pageNotFoundFile = argv [ argn ] ;
} else if ( strcmp ( argv [ argn ] , " -ADL " ) = = 0 & & argn + 1 < argc ) {
+ + argn ;
if ( strcmp ( argv [ argn ] , " 1 " ) = = 0 | | strcmp ( argv [ argn ] , " true " ) = = 0 | | strcmp ( argv [ argn ] , " TRUE " ) = = 0 | | strcmp ( argv [ argn ] , " yes " ) = = 0 | | strcmp ( argv [ argn ] , " YES " ) = = 0 ) {
allowDirectoryListing = 1 ;
} else {
allowDirectoryListing = 0 ;
}
} else if ( strcmp ( argv [ argn ] , " -SP " ) = = 0 & & argn + 1 < argc ) {
+ + argn ;
sslPort = ( unsigned short ) atoi ( argv [ argn ] ) ;
} else {
usage ( ) ;
}
+ + argn ;
}
if ( argn ! = argc ) {
usage ( ) ;
}
cp = strrchr ( argv0 , ' / ' ) ;
if ( cp ! = ( char * ) 0 ) {
+ + cp ;
} else {
cp = argv0 ;
}
openlog ( cp , LOG_NDELAY | LOG_PID , LOG_DAEMON ) ;
if ( port = = 0 ) {
if ( sslPort ! = 0 & & do_ssl ) {
port = sslPort ;
sslPort = 0 ;
}
if ( do_ssl ) {
port = DEFAULT_HTTPS_PORT ;
} else {
port = DEFAULT_HTTP_PORT ;
}
}
if ( getuid ( ) = = 0 ) {
pwd = getpwnam ( user ) ;
if ( pwd = = ( struct passwd * ) 0 ) {
syslog ( LOG_CRIT , " unknown user - '%s' " , user ) ;
( void ) fprintf ( stderr , " %s: unknown user - '%s' \n " , argv0 , user ) ;
exit ( 1 ) ;
}
uid = pwd - > pw_uid ;
gid = pwd - > pw_gid ;
}
if ( logfile ! = ( char * ) 0 ) {
logfp = fopen ( logfile , " a " ) ;
if ( logfp = = ( FILE * ) 0 ) {
syslog ( LOG_CRIT , " %s - %m " , logfile ) ;
perror ( logfile ) ;
exit ( 1 ) ;
}
if ( logfile [ 0 ] ! = ' / ' ) {
syslog ( LOG_WARNING , " logfile is not an absolute path, you may not be able to re-open it " ) ;
( void ) fprintf ( stderr , " %s: logfile is not an absolute path, you may not be able to re-open it \n " , argv0 ) ;
}
if ( getuid ( ) = = 0 ) {
if ( fchown ( fileno ( logfp ) , uid , gid ) < 0 ) {
syslog ( LOG_WARNING , " fchown logfile - %m " ) ;
perror ( " fchown logfile " ) ;
}
}
}
lookup_hostname ( & host_addr4 , & host_addr4s , sizeof ( struct sockaddr_in ) , & gotv4 , & gotv4s , & host_addr6 , & host_addr6s , sizeof ( struct sockaddr_in6 ) , & gotv6 , & gotv6s ) ;
if ( hostname = = ( char * ) 0 ) {
( void ) gethostname ( hostname_buf , sizeof ( hostname_buf ) ) ;
hostname = hostname_buf ;
}
if ( ! ( gotv4 | | gotv4s | | gotv6 | | gotv6s ) ) {
syslog ( LOG_CRIT , " can't find any valid address " ) ;
( void ) fprintf ( stderr , " %s: can't find any valid address \n " , argv0 ) ;
exit ( 1 ) ;
}
listen6_fd = - 1 ;
listen6s_fd = - 1 ;
listen4_fd = - 1 ;
listen4s_fd = - 1 ;
if ( gotv6 ) {
listen6_fd = initialize_listen_socket ( & host_addr6 ) ;
}
if ( gotv6s ) {
listen6s_fd = initialize_listen_socket ( & host_addr6s ) ;
}
if ( gotv4 ) {
listen4_fd = initialize_listen_socket ( & host_addr4 ) ;
}
if ( gotv4s ) {
listen4s_fd = initialize_listen_socket ( & host_addr4s ) ;
}
if ( listen4_fd = = - 1 & & listen6_fd = = - 1 & & listen4s_fd = = - 1 & & listen6s_fd = = - 1 ) {
syslog ( LOG_CRIT , " can't bind to any address " ) ;
( void ) fprintf ( stderr , " %s: can't bind to any address \n " , argv0 ) ;
exit ( 1 ) ;
}
if ( do_ssl ) {
ssl_ctx = SSL_CTX_new ( TLSv1_server_method ( ) ) ;
SSL_CTX_set_verify ( ssl_ctx , SSL_VERIFY_NONE , 0 ) ;
if ( certfile [ 0 ] ! = ' \0 ' ) {
if ( SSL_CTX_use_certificate_file ( ssl_ctx , certfile , SSL_FILETYPE_PEM ) ! = SSL_SUCCESS | | SSL_CTX_use_PrivateKey_file ( ssl_ctx , certfile , SSL_FILETYPE_PEM ) ! = SSL_SUCCESS ) {
syslog ( LOG_CRIT , " can't load certificate and/or private key \n " ) ;
exit ( 1 ) ;
}
}
if ( cipher ! = ( char * ) 0 ) {
if ( SSL_CTX_set_cipher_list ( ssl_ctx , cipher ) < = 0 ) {
syslog ( LOG_CRIT , " can't load certificate and/or private key \n " ) ;
exit ( 1 ) ;
}
}
}
if ( ! debug ) {
if ( daemon ( 1 , 0 ) < 0 ) {
syslog ( LOG_CRIT , " daemon - %m " ) ;
perror ( " daemon " ) ;
exit ( 1 ) ;
}
} else {
( void ) setsid ( ) ;
}
if ( pidfile ! = ( char * ) 0 ) {
FILE * pidfp = fopen ( pidfile , " w " ) ;
if ( pidfp = = ( FILE * ) 0 ) {
syslog ( LOG_CRIT , " %s - %m " , pidfile ) ;
perror ( pidfile ) ;
exit ( 1 ) ;
}
( void ) fprintf ( pidfp , " %d \n " , ( int ) getpid ( ) ) ;
( void ) fclose ( pidfp ) ;
}
tzset ( ) ;
if ( getuid ( ) = = 0 ) {
if ( setgroups ( 0 , ( gid_t * ) 0 ) < 0 ) {
syslog ( LOG_CRIT , " setgroups - %m " ) ;
perror ( " setgroups " ) ;
exit ( 1 ) ;
}
if ( setgid ( gid ) < 0 ) {
syslog ( LOG_CRIT , " setgid - %m " ) ;
perror ( " setgid " ) ;
exit ( 1 ) ;
}
if ( initgroups ( user , gid ) < 0 ) {
syslog ( LOG_ERR , " initgroups - %m " ) ;
perror ( " initgroups " ) ;
}
}
if ( dir ! = ( char * ) 0 ) {
if ( chdir ( dir ) < 0 ) {
syslog ( LOG_CRIT , " chdir - %m " ) ;
perror ( " chdir " ) ;
exit ( 1 ) ;
}
}
if ( getcwd ( cwd , sizeof ( cwd ) - 1 ) = = NULL ) { ; }
if ( cwd [ strlen ( cwd ) - 1 ] ! = ' / ' ) {
( void ) strcat ( cwd , " / " ) ;
}
if ( data_dir ! = ( char * ) 0 ) {
if ( chdir ( data_dir ) < 0 ) {
syslog ( LOG_CRIT , " data_dir chdir - %m " ) ;
perror ( " data_dir chdir " ) ;
exit ( 1 ) ;
}
}
if ( getuid ( ) = = 0 ) {
if ( setuid ( uid ) < 0 ) {
syslog ( LOG_CRIT , " setuid - %m " ) ;
perror ( " setuid " ) ;
exit ( 1 ) ;
}
}
( void ) signal ( SIGTERM , handle_sigterm ) ;
( void ) signal ( SIGINT , handle_sigterm ) ;
( void ) signal ( SIGUSR1 , handle_sigterm ) ;
( void ) signal ( SIGHUP , handle_sighup ) ;
( void ) signal ( SIGCHLD , handle_sigchld ) ;
( void ) signal ( SIGPIPE , SIG_IGN ) ;
got_hup = 0 ;
init_mime ( ) ;
if ( hostname = = ( char * ) 0 ) {
syslog ( LOG_NOTICE , " %.80s starting on port %d " , SERVER_SOFTWARE , ( int ) port ) ;
} else {
syslog ( LOG_NOTICE , " %.80s starting on %.80s, port %d " , SERVER_SOFTWARE , hostname , ( int ) port ) ;
}
for ( ; ; ) {
int is_ssl ;
unsigned short conn_port ;
if ( got_hup ) {
reopen_logfile ( ) ;
got_hup = 0 ;
}
FD_ZERO ( & lfdset ) ;
maxfd = - 1 ;
if ( listen4_fd ! = - 1 ) {
FD_SET ( listen4_fd , & lfdset ) ;
if ( listen4_fd > maxfd ) {
maxfd = listen4_fd ;
}
}
if ( listen4s_fd ! = - 1 ) {
FD_SET ( listen4s_fd , & lfdset ) ;
if ( listen4s_fd > maxfd ) {
maxfd = listen4s_fd ;
}
}
if ( listen6_fd ! = - 1 ) {
FD_SET ( listen6_fd , & lfdset ) ;
if ( listen6_fd > maxfd ) {
maxfd = listen6_fd ;
}
}
if ( listen6s_fd ! = - 1 ) {
FD_SET ( listen6s_fd , & lfdset ) ;
if ( listen6s_fd > maxfd ) {
maxfd = listen6s_fd ;
}
}
if ( select ( maxfd + 1 , & lfdset , ( fd_set * ) 0 , ( fd_set * ) 0 , ( struct timeval * ) 0 ) < 0 ) {
if ( errno = = EINTR | | errno = = EAGAIN ) {
continue ;
}
syslog ( LOG_CRIT , " select - %m " ) ;
perror ( " select " ) ;
exit ( 1 ) ;
}
is_ssl = 0 ;
conn_port = port ;
if ( do_ssl ) {
if ( sslPort = = 0 ) {
is_ssl = 1 ;
} else if ( listen4s_fd ! = - 1 & & FD_ISSET ( listen4s_fd , & lfdset ) ) {
is_ssl = 1 ;
conn_port = sslPort ;
} else if ( listen6s_fd ! = - 1 & & FD_ISSET ( listen6s_fd , & lfdset ) ) {
is_ssl = 1 ;
conn_port = sslPort ;
}
}
sz = sizeof ( usa ) ;
if ( listen4_fd ! = - 1 & & FD_ISSET ( listen4_fd , & lfdset ) ) {
conn_fd = accept ( listen4_fd , & usa . sa , & sz ) ;
} else if ( listen4s_fd ! = - 1 & & FD_ISSET ( listen4s_fd , & lfdset ) ) {
conn_fd = accept ( listen4s_fd , & usa . sa , & sz ) ;
} else if ( listen6_fd ! = - 1 & & FD_ISSET ( listen6_fd , & lfdset ) ) {
conn_fd = accept ( listen6_fd , & usa . sa , & sz ) ;
} else if ( listen6s_fd ! = - 1 & & FD_ISSET ( listen6s_fd , & lfdset ) ) {
conn_fd = accept ( listen6s_fd , & usa . sa , & sz ) ;
} else {
syslog ( LOG_CRIT , " select failure " ) ;
( void ) fprintf ( stderr , " %s: select failure \n " , argv0 ) ;
exit ( 1 ) ;
}
if ( conn_fd < 0 ) {
if ( errno = = EINTR | | errno = = EAGAIN ) {
continue ;
}
# ifdef EPROTO
if ( errno = = EPROTO ) {
continue ;
}
# endif
syslog ( LOG_CRIT , " accept - %m " ) ;
perror ( " accept " ) ;
exit ( 1 ) ;
}
r = fork ( ) ;
if ( r < 0 ) {
syslog ( LOG_CRIT , " fork - %m " ) ;
perror ( " fork " ) ;
exit ( 1 ) ;
}
if ( r = = 0 ) {
client_addr = usa ;
if ( listen4_fd ! = - 1 ) {
( void ) close ( listen4_fd ) ;
}
if ( listen4s_fd ! = - 1 ) {
( void ) close ( listen4s_fd ) ;
}
if ( listen6_fd ! = - 1 ) {
( void ) close ( listen6_fd ) ;
}
if ( listen6s_fd ! = - 1 ) {
( void ) close ( listen6s_fd ) ;
}
handle_request ( is_ssl , conn_port ) ;
exit ( 0 ) ;
}
( void ) close ( conn_fd ) ;
}
}
static char * * make_argp ( void ) {
char * * argp ;
int argn ;
char * cp1 ;
char * cp2 ;
argp = ( char * * ) malloc ( ( strlen ( query ) + 2 ) * sizeof ( char * ) ) ;
2013-08-12 19:25:06 +02:00
if ( argp = = NULL ) {
return NULL ;
2013-06-28 18:48:22 +02:00
}
argp [ 0 ] = strrchr ( file , ' / ' ) ;
2013-08-12 19:25:06 +02:00
if ( argp [ 0 ] ! = NULL ) {
2013-06-28 18:48:22 +02:00
+ + argp [ 0 ] ;
} else {
argp [ 0 ] = file ;
}
argn = 1 ;
2013-08-12 19:25:06 +02:00
if ( strchr ( query , ' = ' ) = = NULL ) {
2013-06-28 18:48:22 +02:00
for ( cp1 = cp2 = query ; * cp2 ! = ' \0 ' ; + + cp2 ) {
if ( * cp2 = = ' + ' ) {
* cp2 = ' \0 ' ;
strdecode ( cp1 , cp1 ) ;
argp [ argn + + ] = cp1 ;
cp1 = cp2 + 1 ;
}
}
if ( cp2 ! = cp1 ) {
strdecode ( cp1 , cp1 ) ;
argp [ argn + + ] = cp1 ;
}
}
argp [ argn ] = ( char * ) 0 ;
return argp ;
}
static char * * make_envp ( int is_ssl , unsigned short conn_port ) {
static char * envp [ 50 ] ;
int envn ;
char * cp ;
char buf [ 256 ] ;
envn = 0 ;
envp [ envn + + ] = build_env ( " PATH=%s " , CGI_PATH ) ;
envp [ envn + + ] = build_env ( " LD_LIBRARY_PATH=%s " , CGI_LD_LIBRARY_PATH ) ;
envp [ envn + + ] = build_env ( " SERVER_SOFTWARE=%s " , SERVER_SOFTWARE ) ;
2013-07-05 23:11:35 +02:00
envp [ envn + + ] = build_env ( " WWW_LANGUAGE=%s " , language ) ;
2013-06-28 18:48:22 +02:00
cp = hostname ;
if ( cp ! = ( char * ) 0 ) {
envp [ envn + + ] = build_env ( " SERVER_NAME=%s " , cp ) ;
}
envp [ envn + + ] = " GATEWAY_INTERFACE=CGI/1.1 " ;
envp [ envn + + ] = " SERVER_PROTOCOL=HTTP/1.0 " ;
( void ) snprintf ( buf , sizeof ( buf ) , " %d " , ( int ) conn_port ) ;
envp [ envn + + ] = build_env ( " SERVER_PORT=%s " , buf ) ;
envp [ envn + + ] = build_env ( " REQUEST_METHOD=%s " , get_method_str ( method ) ) ;
envp [ envn + + ] = build_env ( " SCRIPT_NAME=%s " , path ) ;
if ( pathinfo ! = ( char * ) 0 ) {
envp [ envn + + ] = build_env ( " PATH_INFO=/%s " , pathinfo ) ;
( void ) snprintf ( buf , sizeof ( buf ) , " %s%s " , cwd , pathinfo ) ;
envp [ envn + + ] = build_env ( " PATH_TRANSLATED=%s " , buf ) ;
}
if ( query [ 0 ] ! = ' \0 ' ) {
envp [ envn + + ] = build_env ( " QUERY_STRING=%s " , query ) ;
}
envp [ envn + + ] = build_env ( " REMOTE_ADDR=%s " , ntoa ( & client_addr ) ) ;
if ( referer [ 0 ] ! = ' \0 ' ) {
envp [ envn + + ] = build_env ( " HTTP_REFERER=%s " , referer ) ;
}
if ( useragent [ 0 ] ! = ' \0 ' ) {
envp [ envn + + ] = build_env ( " HTTP_USER_AGENT=%s " , useragent ) ;
}
if ( cookie ! = ( char * ) 0 ) {
envp [ envn + + ] = build_env ( " HTTP_COOKIE=%s " , cookie ) ;
}
if ( host ! = ( char * ) 0 ) {
envp [ envn + + ] = build_env ( " HTTP_HOST=%s " , host ) ;
}
if ( content_type ! = ( char * ) 0 ) {
envp [ envn + + ] = build_env ( " CONTENT_TYPE=%s " , content_type ) ;
}
if ( content_length ! = - 1 ) {
( void ) snprintf ( buf , sizeof ( buf ) , " %lu " , ( unsigned long ) content_length ) ;
envp [ envn + + ] = build_env ( " CONTENT_LENGTH=%s " , buf ) ;
}
if ( remoteuser ! = ( char * ) 0 ) {
envp [ envn + + ] = build_env ( " REMOTE_USER=%s " , remoteuser ) ;
}
if ( authorization ! = ( char * ) 0 ) {
envp [ envn + + ] = build_env ( " AUTH_TYPE=%s " , " Basic " ) ;
}
if ( getenv ( " TZ " ) ! = ( char * ) 0 ) {
envp [ envn + + ] = build_env ( " TZ=%s " , getenv ( " TZ " ) ) ;
}
envp [ envn ] = ( char * ) 0 ;
return envp ;
}
static void make_log_entry ( void ) {
char * ru ;
char url [ 500 ] ;
char bytes_str [ 40 ] ;
time_t now ;
struct tm * t ;
const char * cernfmt_nozone = " %d/%b/%Y:%H:%M:%S " ;
char date_nozone [ 100 ] ;
int zone ;
char sign ;
char date [ 100 ] ;
if ( logfp = = ( FILE * ) 0 ) {
return ;
}
if ( protocol = = ( char * ) 0 ) {
protocol = " UNKNOWN " ;
}
if ( path = = ( char * ) 0 ) {
path = " " ;
}
if ( remoteuser ! = ( char * ) 0 ) {
ru = remoteuser ;
} else {
ru = " - " ;
}
now = time ( ( time_t * ) 0 ) ;
( void ) snprintf ( url , sizeof ( url ) , " %s " , path ) ;
if ( bytes > = 0 ) {
( void ) snprintf ( bytes_str , sizeof ( bytes_str ) , " %lld " , ( long long int ) bytes ) ;
} else {
( void ) strcpy ( bytes_str , " - " ) ;
}
t = localtime ( & now ) ;
( void ) strftime ( date_nozone , sizeof ( date_nozone ) , cernfmt_nozone , t ) ;
zone = t - > tm_gmtoff / 60L ;
if ( zone > = 0 ) {
sign = ' + ' ;
} else {
sign = ' - ' ;
zone = - zone ;
}
zone = ( zone / 60 ) * 100 + zone % 60 ;
( void ) snprintf ( date , sizeof ( date ) , " %s %c%04d " , date_nozone , sign , zone ) ;
( void ) fprintf ( logfp , " %.80s - %.80s [%s] \" %.80s %.200s %.80s \" %d %s \" %.200s \" \" %.200s \" \n " , ntoa ( & client_addr ) , ru , date , get_method_str ( method ) , url , protocol , status , bytes_str , referer , useragent ) ;
( void ) fflush ( logfp ) ;
}
static ssize_t my_read ( char * buf , size_t size , int is_ssl ) {
if ( is_ssl ) {
return SSL_read ( ssl , buf , size ) ;
} else {
return read ( conn_fd , buf , size ) ;
}
}
static ssize_t my_write ( char * buf , size_t size , int is_ssl ) {
if ( is_ssl ) {
return SSL_write ( ssl , buf , size ) ;
} else {
return write ( conn_fd , buf , size ) ;
}
}
static void no_value_required ( char * name , char * value ) {
if ( value ! = ( char * ) 0 ) {
( void ) fprintf ( stderr , " %s: no value required for %s option \n " , argv0 , name ) ;
exit ( 1 ) ;
}
}
static char * ntoa ( usockaddr * usaP ) {
static char str [ 200 ] ;
if ( getnameinfo ( & usaP - > sa , sockaddr_len ( usaP ) , str , sizeof ( str ) , 0 , 0 , NI_NUMERICHOST ) ! = 0 ) {
str [ 0 ] = ' ? ' ;
str [ 1 ] = ' \0 ' ;
} else if ( IN6_IS_ADDR_V4MAPPED ( & usaP - > sa_in6 . sin6_addr ) & & strncmp ( str , " ::ffff: " , 7 ) = = 0 ) {
( void ) strcpy ( str , & str [ 7 ] ) ;
}
return str ;
}
static void post_post_garbage_hack ( int is_ssl ) {
char buf [ 2 ] ;
if ( is_ssl ) {
return ;
}
set_ndelay ( conn_fd ) ;
if ( read ( conn_fd , buf , sizeof ( buf ) ) < 0 ) { ; } ;
clear_ndelay ( conn_fd ) ;
}
static void read_config ( char * filename ) {
FILE * fp ;
char line [ 10000 ] ;
char * cp ;
char * cp2 ;
char * name ;
char * value ;
fp = fopen ( filename , " r " ) ;
if ( fp = = ( FILE * ) 0 ) {
syslog ( LOG_CRIT , " %s - %m " , filename ) ;
perror ( filename ) ;
exit ( 1 ) ;
}
while ( fgets ( line , sizeof ( line ) , fp ) ! = ( char * ) 0 ) {
if ( ( cp = strchr ( line , ' # ' ) ) ! = ( char * ) 0 ) {
* cp = ' \0 ' ;
}
cp = line ;
cp + = strspn ( cp , " \t \012 \015 " ) ;
while ( * cp ! = ' \0 ' ) {
cp2 = cp + strcspn ( cp , " \t \012 \015 " ) ;
while ( * cp2 = = ' ' | | * cp2 = = ' \t ' | | * cp2 = = ' \012 ' | | * cp2 = = ' \015 ' ) {
* cp2 + + = ' \0 ' ;
}
name = cp ;
value = strchr ( name , ' = ' ) ;
if ( value ! = ( char * ) 0 ) {
* value + + = ' \0 ' ;
}
if ( strcasecmp ( name , " debug " ) = = 0 ) {
no_value_required ( name , value ) ;
debug = 1 ;
} else if ( strcasecmp ( name , " port " ) = = 0 ) {
value_required ( name , value ) ;
port = ( unsigned short ) atoi ( value ) ;
} else if ( strcasecmp ( name , " dir " ) = = 0 ) {
value_required ( name , value ) ;
dir = e_strdup ( value ) ;
} else if ( strcasecmp ( name , " data_dir " ) = = 0 ) {
value_required ( name , value ) ;
data_dir = e_strdup ( value ) ;
} else if ( strcasecmp ( name , " user " ) = = 0 ) {
value_required ( name , value ) ;
user = e_strdup ( value ) ;
} else if ( strcasecmp ( name , " cgipat " ) = = 0 ) {
value_required ( name , value ) ;
cgi_pattern = e_strdup ( value ) ;
} else if ( strcasecmp ( name , " urlpat " ) = = 0 ) {
value_required ( name , value ) ;
url_pattern = e_strdup ( value ) ;
} else if ( strcasecmp ( name , " noemptyreferers " ) = = 0 ) {
value_required ( name , value ) ;
no_empty_referers = 1 ;
} else if ( strcasecmp ( name , " localpat " ) = = 0 ) {
value_required ( name , value ) ;
local_pattern = e_strdup ( value ) ;
} else if ( strcasecmp ( name , " host " ) = = 0 ) {
value_required ( name , value ) ;
hostname = e_strdup ( value ) ;
} else if ( strcasecmp ( name , " logfile " ) = = 0 ) {
value_required ( name , value ) ;
logfile = e_strdup ( value ) ;
} else if ( strcasecmp ( name , " pidfile " ) = = 0 ) {
value_required ( name , value ) ;
pidfile = e_strdup ( value ) ;
} else if ( strcasecmp ( name , " charset " ) = = 0 ) {
value_required ( name , value ) ;
charset = e_strdup ( value ) ;
} else if ( strcasecmp ( name , " p3p " ) = = 0 ) {
value_required ( name , value ) ;
p3p = e_strdup ( value ) ;
} else if ( strcasecmp ( name , " max_age " ) = = 0 ) {
value_required ( name , value ) ;
max_age = atoi ( value ) ;
} else if ( strcasecmp ( name , " default_realm_name " ) = = 0 ) {
value_required ( name , value ) ;
defaultRealmName = e_strdup ( value ) ;
} else if ( strcasecmp ( name , " default_realm_password_file " ) = = 0 ) {
value_required ( name , value ) ;
defaultRealmPasswordFile = e_strdup ( value ) ;
} else if ( strcasecmp ( name , " default_page_file " ) = = 0 ) {
value_required ( name , value ) ;
defaultPageFile = e_strdup ( value ) ;
} else if ( strcasecmp ( name , " page_not_found_file " ) = = 0 ) {
value_required ( name , value ) ;
pageNotFoundFile = e_strdup ( value ) ;
} else if ( strcasecmp ( name , " allow_directory_listing " ) = = 0 ) {
value_required ( name , value ) ;
if ( strcmp ( value , " 1 " ) = = 0 | | strcmp ( value , " true " ) = = 0 | | strcmp ( value , " TRUE " ) = = 0 | | strcmp ( value , " yes " ) = = 0 | | strcmp ( value , " YES " ) = = 0 ) {
allowDirectoryListing = 1 ;
} else {
allowDirectoryListing = 0 ;
}
} else if ( strcasecmp ( name , " ssl " ) = = 0 ) {
no_value_required ( name , value ) ;
do_ssl = 1 ;
} else if ( strcasecmp ( name , " certfile " ) = = 0 ) {
value_required ( name , value ) ;
certfile = e_strdup ( value ) ;
} else if ( strcasecmp ( name , " cipher " ) = = 0 ) {
value_required ( name , value ) ;
cipher = e_strdup ( value ) ;
} else {
( void ) fprintf ( stderr , " %s: unknown config option '%s' \n " , argv0 , name ) ;
exit ( 1 ) ;
}
cp = cp2 ;
cp + = strspn ( cp , " \t \012 \015 " ) ;
}
}
( void ) fclose ( fp ) ;
}
static int really_check_referer ( void ) {
char * cp1 ;
char * cp2 ;
char * cp3 ;
char * refhost ;
char * lp ;
if ( referer = = ( char * ) 0 | | referer [ 0 ] = = ' \0 ' | | ( cp1 = strstr ( referer , " // " ) ) = = ( char * ) 0 ) {
if ( no_empty_referers & & match ( url_pattern , path ) ) {
return 0 ;
} else {
return 1 ;
}
}
cp1 + = 2 ;
for ( cp2 = cp1 ; * cp2 ! = ' / ' & & * cp2 ! = ' : ' & & * cp2 ! = ' \0 ' ; + + cp2 ) {
continue ;
}
refhost = ( char * ) e_malloc ( cp2 - cp1 + 1 ) ;
for ( cp3 = refhost ; cp1 < cp2 ; + + cp1 , + + cp3 ) {
if ( isupper ( * cp1 ) ) {
* cp3 = tolower ( * cp1 ) ;
} else {
* cp3 = * cp1 ;
}
}
* cp3 = ' \0 ' ;
if ( local_pattern ! = ( char * ) 0 ) {
lp = local_pattern ;
} else {
lp = hostname ;
if ( lp = = ( char * ) 0 ) {
return 1 ;
}
}
if ( ! match ( lp , refhost ) & & match ( url_pattern , path ) ) {
return 0 ;
} else {
return 1 ;
}
}
static void reopen_logfile ( void ) {
if ( logfp ! = ( FILE * ) 0 ) {
( void ) fclose ( logfp ) ;
logfp = ( FILE * ) 0 ;
}
if ( logfile ! = ( char * ) 0 ) {
syslog ( LOG_NOTICE , " re-opening logfile " ) ;
logfp = fopen ( logfile , " a " ) ;
if ( logfp = = ( FILE * ) 0 ) {
syslog ( LOG_CRIT , " %s - %m " , logfile ) ;
perror ( logfile ) ;
exit ( 1 ) ;
}
}
}
static void send_authenticate ( char * realm , int is_ssl ) {
char header [ 10000 ] ;
( void ) snprintf ( header , sizeof ( header ) , " WWW-Authenticate: Basic realm= \" %s \" " , realm ) ;
send_error ( 401 , " Unauthorized " , header , " Authorization required. " , is_ssl ) ;
}
static void send_error ( int s , char * title , char * extra_header , char * text , int is_ssl ) {
add_headers ( s , title , extra_header , " " , " text/html; charset=%s " , ( off_t ) - 1 , ( time_t ) - 1 ) ;
send_error_body ( s , title , text ) ;
send_error_tail ( ) ;
send_response ( is_ssl ) ;
SSL_free ( ssl ) ;
exit ( 1 ) ;
}
static void send_error_body ( int s , char * title , char * text ) {
char filename [ 1000 ] ;
char buf [ 10000 ] ;
int buflen ;
( void ) snprintf ( filename , sizeof ( filename ) , " %s/err%d.html " , ERR_DIR , s ) ;
if ( send_error_file ( filename ) ) {
return ;
}
buflen = snprintf ( buf , sizeof ( buf ) , " <html> \n <head><title>Error %d - %s</title></head> \n <body bgcolor= \" #cc9999 \" text= \" #000000 \" > \n <h2>Error %d</h2> \n " , s , title , s ) ;
add_to_response ( buf , buflen ) ;
buflen = snprintf ( buf , sizeof ( buf ) , " %s \n " , text ) ;
add_to_response ( buf , buflen ) ;
}
static int send_error_file ( char * filename ) {
FILE * fp ;
char buf [ 1000 ] ;
size_t r ;
fp = fopen ( filename , " r " ) ;
if ( fp = = ( FILE * ) 0 ) {
return 0 ;
}
for ( ; ; ) {
r = fread ( buf , 1 , sizeof ( buf ) , fp ) ;
if ( r = = 0 ) {
break ;
}
add_to_response ( buf , r ) ;
}
( void ) fclose ( fp ) ;
return 1 ;
}
static void send_error_tail ( void ) {
char buf [ 500 ] ;
int buflen ;
if ( match ( " **MSIE** " , useragent ) ) {
int n ;
buflen = snprintf ( buf , sizeof ( buf ) , " <!-- \n " ) ;
add_to_response ( buf , buflen ) ;
for ( n = 0 ; n < 6 ; + + n ) {
buflen = snprintf ( buf , sizeof ( buf ) , " Padding so that MSIE deigns to show this error instead of its own canned one. \n " ) ;
add_to_response ( buf , buflen ) ;
}
buflen = snprintf ( buf , sizeof ( buf ) , " --> \n " ) ;
add_to_response ( buf , buflen ) ;
}
buflen = snprintf ( buf , sizeof ( buf ) , " <hr> \n %s \n </body> \n </html> \n " , SERVER_SOFTWARE ) ;
add_to_response ( buf , buflen ) ;
}
static void send_redirect ( char * extra_header , char * hostname , char * new_location , int is_ssl ) {
char extra_header_buf [ 5000 ] ;
const char * sep = new_location [ 0 ] = = ' / ' ? " " : " / " ;
const char * proto = is_ssl = = 1 ? " https:// " : " http:// " ;
extra_header = extra_header = = NULL ? " " : extra_header ;
if ( strcmp ( extra_header , " " ) = = 0 ) {
sprintf ( extra_header_buf , " Location: %s%s%s%s " , proto , hostname , sep , new_location ) ;
} else {
sprintf ( extra_header_buf , " %s \r \n Location: %s%s%s%s " , extra_header , proto , hostname , sep , new_location ) ;
}
add_headers ( 301 , " Moved Permanently " , extra_header_buf , " " , " text/html; charset=%s " , ( off_t ) - 1 , ( time_t ) - 1 ) ;
send_error_body ( 301 , " Moved Permanently " , " Moved Permanently " ) ;
send_error_tail ( ) ;
send_response ( is_ssl ) ;
SSL_free ( ssl ) ;
exit ( 1 ) ;
}
static void send_response ( is_ssl ) {
( void ) my_write ( response , response_len , is_ssl ) ;
}
static void send_via_write ( int fd , off_t size , int is_ssl ) {
if ( size < = SIZE_T_MAX ) {
size_t size_size = ( size_t ) size ;
void * ptr = mmap ( 0 , size_size , PROT_READ , MAP_PRIVATE , fd , 0 ) ;
if ( ptr ! = ( void * ) - 1 ) {
( void ) my_write ( ptr , size_size , is_ssl ) ;
( void ) munmap ( ptr , size_size ) ;
}
# ifdef MADV_SEQUENTIAL
( void ) madvise ( ptr , size_size , MADV_SEQUENTIAL ) ;
# endif
} else {
char buf [ 30000 ] ;
ssize_t r , r2 ;
for ( ; ; ) {
r = read ( fd , buf , sizeof ( buf ) ) ;
if ( r < 0 & & ( errno = = EINTR | | errno = = EAGAIN ) ) {
sleep ( 1 ) ;
continue ;
}
if ( r < = 0 ) {
return ;
}
for ( ; ; ) {
r2 = my_write ( buf , r , is_ssl ) ;
if ( r2 < 0 & & ( errno = = EINTR | | errno = = EAGAIN ) ) {
sleep ( 1 ) ;
continue ;
}
if ( r2 ! = r ) {
return ;
}
break ;
}
}
}
}
static void set_ndelay ( int fd ) {
int flags , newflags ;
flags = fcntl ( fd , F_GETFL , 0 ) ;
if ( flags ! = - 1 ) {
newflags = flags | ( int ) O_NDELAY ;
if ( newflags ! = flags ) {
( void ) fcntl ( fd , F_SETFL , newflags ) ;
}
}
}
static int sockaddr_check ( usockaddr * usaP ) {
switch ( usaP - > sa . sa_family ) {
case AF_INET :
return 1 ;
case AF_INET6 :
return 1 ;
default :
return 0 ;
}
}
static size_t sockaddr_len ( usockaddr * usaP ) {
switch ( usaP - > sa . sa_family ) {
case AF_INET :
return sizeof ( struct sockaddr_in ) ;
case AF_INET6 :
return sizeof ( struct sockaddr_in6 ) ;
default :
return 0 ;
}
}
static void start_request ( void ) {
request_size = 0 ;
request_idx = 0 ;
}
static void start_response ( void ) {
response_size = 0 ;
}
static void strdecode ( char * to , char * from ) {
for ( ; * from ! = ' \0 ' ; + + to , + + from ) {
if ( from [ 0 ] = = ' % ' & & isxdigit ( from [ 1 ] ) & & isxdigit ( from [ 2 ] ) ) {
* to = hexit ( from [ 1 ] ) * 16 + hexit ( from [ 2 ] ) ;
from + = 2 ;
} else {
* to = * from ;
}
}
* to = ' \0 ' ;
}
static void strencode ( char * to , size_t tosize , const char * from ) {
int tolen ;
for ( tolen = 0 ; * from ! = ' \0 ' & & tolen + 4 < tosize ; + + from ) {
if ( isalnum ( * from ) | | strchr ( " /_.-~ " , * from ) ! = ( char * ) 0 ) {
* to = * from ;
+ + to ;
+ + tolen ;
} else {
( void ) sprintf ( to , " %%%02x " , ( int ) * from & 0xff ) ;
to + = 3 ;
tolen + = 3 ;
}
}
* to = ' \0 ' ;
}
static void usage ( void ) {
( void ) fprintf ( stderr , " Usage: %s [-C configfile] [-D] [-S use ssl, if no ssl port is specified all connections will be SSL] [-E certfile] [-SP ssl port] [-Y cipher] [-p port] [-d dir] [-dd data_dir] [-c cgipat] [-u user] [-h hostname] [-l logfile] [-i pidfile] [-T charset] [-L language] [-P P3P] [-M maxage] [-DRN default realm name] [-DRP default realm password file] [-DPF default page file] [-PNF Page to load when 404 Not Found error occurs] \n " , argv0 ) ;
exit ( 1 ) ;
}
static void value_required ( char * name , char * value ) {
if ( value = = ( char * ) 0 ) {
( void ) fprintf ( stderr , " %s: value required for %s option \n " , argv0 , name ) ;
exit ( 1 ) ;
}
}