2001-10-17 12:15:35 +08:00
/* $Id: reqs.c,v 1.29 2001-10-17 04:15:35 rjkaes Exp $
2000-02-17 01:32:49 +08:00
*
* This is where all the work in tinyproxy is actually done . Incoming
2000-09-12 08:04:42 +08:00
* connections have a new thread created for them . The thread then
* processes the headers from the client , the response from the server ,
* and then relays the bytes between the two .
2001-08-28 12:32:14 +08:00
* If TUNNEL_SUPPORT is enabled , then tinyproxy will actually work
2000-09-12 08:04:42 +08:00
* as a simple buffering TCP tunnel . Very cool ! ( Robert actually uses
2001-05-27 10:29:06 +08:00
* this feature for a buffering NNTP tunnel . )
2000-02-17 01:32:49 +08:00
*
2000-09-12 08:04:42 +08:00
* Copyright ( C ) 1998 Steven Young
* Copyright ( C ) 1999 , 2000 Robert James Kaes ( rjkaes @ flarenet . com )
* Copyright ( C ) 2000 Chris Lightfoot ( chris @ ex - parrot . com )
2000-02-17 01:32:49 +08:00
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 , or ( at your option ) any
* later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*/
# include "tinyproxy.h"
2000-09-12 08:04:42 +08:00
# include "acl.h"
# include "anonymous.h"
2000-02-17 01:32:49 +08:00
# include "buffer.h"
# include "filter.h"
2000-09-12 08:04:42 +08:00
# include "log.h"
2000-02-17 01:32:49 +08:00
# include "regexp.h"
2000-09-12 08:04:42 +08:00
# include "reqs.h"
# include "sock.h"
# include "stats.h"
# include "utils.h"
2000-02-17 01:32:49 +08:00
# define HTTP400ERROR "Unrecognizable request. Only HTTP is allowed."
# define HTTP500ERROR "Unable to connect to remote server."
# define HTTP503ERROR "Internal server error."
2000-09-12 08:04:42 +08:00
# define LINE_LENGTH (MAXBUFFSIZE / 3)
2001-08-28 12:32:14 +08:00
/*
* Remove any new lines or carriage returns from the end of a string .
*/
static inline void trim ( char * string , unsigned int len )
{
char * ptr ;
assert ( string ! = NULL ) ;
assert ( len > 0 ) ;
ptr = string + len - 1 ;
while ( * ptr = = ' \r ' | | * ptr = = ' \n ' ) {
* ptr - - = ' \0 ' ;
/*
* Don ' t let the ptr back past the beginning of the
* string .
*/
if ( ptr < string )
return ;
}
}
2000-02-17 01:32:49 +08:00
/*
2001-09-14 12:56:29 +08:00
* Read in the first line from the client ( the request line for HTTP
* connections . The request line is allocated from the heap , but it must
* be freed in another function .
2000-02-17 01:32:49 +08:00
*/
2001-09-14 12:56:29 +08:00
static char * read_request_line ( struct conn_s * connptr )
2000-02-17 01:32:49 +08:00
{
2001-09-14 12:56:29 +08:00
char * request_buffer ;
2001-05-27 10:29:06 +08:00
size_t len ;
2000-02-17 01:32:49 +08:00
2001-09-14 12:56:29 +08:00
request_buffer = safemalloc ( LINE_LENGTH ) ;
2001-09-16 05:26:14 +08:00
if ( ! request_buffer )
2001-09-14 12:56:29 +08:00
return NULL ;
2001-09-12 03:26:49 +08:00
2001-09-14 12:56:29 +08:00
len = readline ( connptr - > client_fd , request_buffer , LINE_LENGTH ) ;
2001-08-27 05:11:55 +08:00
if ( len < = 0 ) {
2001-09-14 12:56:29 +08:00
log_message ( LOG_ERR , " Client (file descriptor: %d) closed socket before read. " , connptr - > client_fd ) ;
safefree ( request_buffer ) ;
return NULL ;
2000-02-17 01:32:49 +08:00
}
2001-08-27 05:11:55 +08:00
/*
2001-09-14 12:56:29 +08:00
* Strip the new line and character return from the string .
2001-08-27 05:11:55 +08:00
*/
2001-09-14 12:56:29 +08:00
trim ( request_buffer , len ) ;
2000-02-17 01:32:49 +08:00
2001-09-14 12:56:29 +08:00
log_message ( LOG_CONN , " Request (file descriptor %d): %s " ,
connptr - > client_fd , request_buffer ) ;
2000-02-17 01:32:49 +08:00
2001-09-14 12:56:29 +08:00
return request_buffer ;
}
/*
* This structure holds the information pulled from a URL request .
*/
struct request_s {
2001-09-17 04:10:19 +08:00
char * method ;
char * protocol ;
2001-09-14 12:56:29 +08:00
char * host ;
char * path ;
int port ;
} ;
2001-09-17 04:10:19 +08:00
static void free_request_struct ( struct request_s * request )
{
safefree ( request - > method ) ;
safefree ( request - > protocol ) ;
safefree ( request - > host ) ;
safefree ( request - > path ) ;
safefree ( request ) ;
}
2001-09-14 12:56:29 +08:00
/*
* Pull the information out of the URL line .
*/
static int extract_http_url ( const char * url , struct request_s * request )
{
request - > host = safemalloc ( strlen ( url ) + 1 ) ;
request - > path = safemalloc ( strlen ( url ) + 1 ) ;
if ( ! request - > host | | ! request - > path ) {
safefree ( request - > host ) ;
safefree ( request - > path ) ;
return - 1 ;
2000-02-17 01:32:49 +08:00
}
2001-09-14 12:56:29 +08:00
if ( sscanf ( url , " http://%[^:/]:%d%s " , request - > host , & request - > port , request - > path ) = = 3 )
;
else if ( sscanf ( url , " http://%[^/]%s " , request - > host , request - > path ) = = 2 )
request - > port = 80 ;
else if ( sscanf ( url , " http://%[^:/]:%d " , request - > host , & request - > port ) = = 2 )
strcpy ( request - > path , " / " ) ;
else if ( sscanf ( url , " http://%[^/] " , request - > host ) = = 1 ) {
request - > port = 80 ;
strcpy ( request - > path , " / " ) ;
} else {
log_message ( LOG_ERR , " Can't parse URL. " ) ;
safefree ( request - > host ) ;
safefree ( request - > path ) ;
return - 1 ;
2000-02-17 01:32:49 +08:00
}
2001-09-14 12:56:29 +08:00
return 0 ;
}
2000-02-17 01:32:49 +08:00
2001-09-14 12:56:29 +08:00
/*
* Extract the URL from a SSL connection .
*/
static int extract_ssl_url ( const char * url , struct request_s * request )
{
request - > host = safemalloc ( strlen ( url ) + 1 ) ;
2001-09-16 05:26:14 +08:00
if ( ! request - > host )
2001-09-14 12:56:29 +08:00
return - 1 ;
2000-02-17 01:32:49 +08:00
2001-09-14 12:56:29 +08:00
if ( sscanf ( url , " %[^:]:%d " , request - > host , & request - > port ) = = 2 )
;
else if ( sscanf ( url , " %s " , request - > host ) = = 1 )
request - > port = 443 ;
else {
log_message ( LOG_ERR , " Can't parse URL. " ) ;
safefree ( request - > host ) ;
return - 1 ;
2000-02-17 01:32:49 +08:00
}
2001-09-14 12:56:29 +08:00
return 0 ;
}
/*
* Create a connection for HTTP connections .
*/
2001-09-16 05:26:14 +08:00
static inline int establish_http_connection ( struct conn_s * connptr ,
struct request_s * request )
2001-09-14 12:56:29 +08:00
{
2001-09-15 03:50:45 +08:00
/*
* Send the request line
*/
2001-09-17 04:10:19 +08:00
if ( safe_write ( connptr - > server_fd , request - > method , strlen ( request - > method ) ) < 0 )
2001-09-14 12:56:29 +08:00
return - 1 ;
2001-09-15 03:50:45 +08:00
if ( safe_write ( connptr - > server_fd , " " , 1 ) < 0 )
return - 1 ;
if ( safe_write ( connptr - > server_fd , request - > path , strlen ( request - > path ) ) < 0 )
return - 1 ;
if ( safe_write ( connptr - > server_fd , " " , 1 ) < 0 )
return - 1 ;
if ( safe_write ( connptr - > server_fd , " HTTP/1.0 \r \n " , 10 ) < 0 )
2001-09-14 12:56:29 +08:00
return - 1 ;
/*
* Send headers
*/
2001-09-15 03:50:45 +08:00
if ( safe_write ( connptr - > server_fd , " Host: " , 6 ) < 0 )
2001-09-14 12:56:29 +08:00
return - 1 ;
2001-09-15 03:50:45 +08:00
if ( safe_write ( connptr - > server_fd , request - > host , strlen ( request - > host ) ) < 0 )
2001-09-14 12:56:29 +08:00
return - 1 ;
if ( safe_write ( connptr - > server_fd , " \r \n " , 2 ) < 0 )
return - 1 ;
/*
* Send the Connection header since we don ' t support persistant
* connections .
*/
if ( safe_write ( connptr - > server_fd , " Connection: close \r \n " , 19 ) < 0 )
return - 1 ;
return 0 ;
}
2000-02-17 01:32:49 +08:00
2001-09-16 05:26:14 +08:00
/*
* These two defines are for the SSL tunnelling .
*/
# define SSL_CONNECTION_RESPONSE "HTTP / 1.0 200 Connection established\r\n"
# define PROXY_AGENT "Proxy-agent: " PACKAGE " / " VERSION "\r\n"
/*
* Send the appropriate response to the client to establish a SSL
* connection .
*/
static inline int send_ssl_response ( struct conn_s * connptr )
{
if ( safe_write ( connptr - > client_fd , SSL_CONNECTION_RESPONSE , strlen ( SSL_CONNECTION_RESPONSE ) ) < 0 )
return - 1 ;
if ( safe_write ( connptr - > client_fd , PROXY_AGENT , strlen ( PROXY_AGENT ) ) < 0 )
return - 1 ;
if ( safe_write ( connptr - > client_fd , " \r \n " , 2 ) < 0 )
return - 1 ;
return 0 ;
}
2001-09-14 12:56:29 +08:00
/*
* Break the request line apart and figure out where to connect and
* build a new request line . Finally connect to the remote server .
*/
2001-09-17 04:10:19 +08:00
static struct request_s * process_request ( struct conn_s * connptr ,
char * request_line )
2001-09-14 12:56:29 +08:00
{
char * url ;
2001-09-17 04:10:19 +08:00
struct request_s * request ;
2001-09-14 12:56:29 +08:00
int ret ;
size_t request_len ;
/* NULL out all the fields so free's don't cause segfaults. */
2001-09-17 04:10:19 +08:00
request = safecalloc ( 1 , sizeof ( struct request_s ) ) ;
if ( ! request )
return NULL ;
2001-09-14 12:56:29 +08:00
request_len = strlen ( request_line ) + 1 ;
2001-09-17 04:10:19 +08:00
request - > method = safemalloc ( request_len ) ;
2001-09-14 12:56:29 +08:00
url = safemalloc ( request_len ) ;
2001-09-17 04:10:19 +08:00
request - > protocol = safemalloc ( request_len ) ;
2001-09-14 12:56:29 +08:00
2001-09-17 04:10:19 +08:00
if ( ! request - > method | | ! url | | ! request - > protocol ) {
2001-09-14 12:56:29 +08:00
safefree ( url ) ;
2001-09-17 04:10:19 +08:00
free_request_struct ( request ) ;
return NULL ;
2000-03-29 00:41:45 +08:00
}
2001-09-17 04:10:19 +08:00
ret = sscanf ( request_line , " %[^ ] %[^ ] %[^ ] " , request - > method , url , request - > protocol ) ;
2001-09-14 12:56:29 +08:00
if ( ret < 2 ) {
log_message ( LOG_ERR , " Bad Request on file descriptor %d " , connptr - > client_fd ) ;
httperr ( connptr , 400 , " Bad Request. No request found. " ) ;
safefree ( url ) ;
2001-09-17 04:10:19 +08:00
free_request_struct ( request ) ;
2001-09-14 12:56:29 +08:00
2001-09-17 04:10:19 +08:00
return NULL ;
2001-09-14 12:56:29 +08:00
} else if ( ret = = 2 ) {
connptr - > simple_req = TRUE ;
2000-02-17 01:32:49 +08:00
}
2001-09-14 12:56:29 +08:00
if ( ! url ) {
log_message ( LOG_ERR , " Null URL on file descriptor %d " , connptr - > client_fd ) ;
httperr ( connptr , 400 , " Bad Request. Null URL. " ) ;
safefree ( url ) ;
2001-09-17 04:10:19 +08:00
free_request_struct ( request ) ;
2001-09-14 12:56:29 +08:00
2001-09-17 04:10:19 +08:00
return NULL ;
2000-02-17 01:32:49 +08:00
}
2001-09-14 12:56:29 +08:00
if ( strncasecmp ( url , " http:// " , 7 ) = = 0 ) {
/* Make sure the first four characters are lowercase */
memcpy ( url , " http " , 4 ) ;
2001-09-17 04:10:19 +08:00
if ( extract_http_url ( url , request ) < 0 ) {
2001-09-14 12:56:29 +08:00
httperr ( connptr , 400 , " Bad Request. Could not parse URL. " ) ;
safefree ( url ) ;
2001-09-17 04:10:19 +08:00
free_request_struct ( request ) ;
2001-09-14 12:56:29 +08:00
2001-09-17 04:10:19 +08:00
return NULL ;
2000-02-17 01:32:49 +08:00
}
2001-09-14 12:56:29 +08:00
connptr - > ssl = FALSE ;
2001-09-17 04:10:19 +08:00
} else if ( strcmp ( request - > method , " CONNECT " ) = = 0 ) {
if ( extract_ssl_url ( url , request ) < 0 ) {
2001-09-14 12:56:29 +08:00
httperr ( connptr , 400 , " Bad Request. Could not parse URL. " ) ;
2000-02-17 01:32:49 +08:00
2001-09-14 12:56:29 +08:00
safefree ( url ) ;
2001-09-17 04:10:19 +08:00
free_request_struct ( request ) ;
2001-09-14 12:56:29 +08:00
2001-09-17 04:10:19 +08:00
return NULL ;
2000-04-01 06:55:22 +08:00
}
2001-09-14 12:56:29 +08:00
connptr - > ssl = TRUE ;
2000-04-01 06:55:22 +08:00
} else {
2001-09-14 12:56:29 +08:00
log_message ( LOG_ERR , " Unknown URL type on file descriptor %d " , connptr - > client_fd ) ;
httperr ( connptr , 400 , " Bad Request. Unknown URL type. " ) ;
2000-02-17 01:32:49 +08:00
2001-09-14 12:56:29 +08:00
safefree ( url ) ;
2001-09-17 04:10:19 +08:00
free_request_struct ( request ) ;
2001-09-14 12:56:29 +08:00
2001-09-17 04:10:19 +08:00
return NULL ;
2000-02-17 01:32:49 +08:00
}
2001-09-14 12:56:29 +08:00
safefree ( url ) ;
2000-02-17 01:32:49 +08:00
2001-09-14 12:56:29 +08:00
# ifdef FILTER_ENABLE
2000-09-12 08:04:42 +08:00
/*
2001-09-14 12:56:29 +08:00
* Filter restricted domains
2000-09-12 08:04:42 +08:00
*/
2001-09-14 12:56:29 +08:00
if ( config . filter ) {
2001-09-17 04:10:19 +08:00
if ( filter_url ( request - > host ) ) {
update_stats ( STAT_DENIED ) ;
2001-09-14 12:56:29 +08:00
2001-09-17 04:10:19 +08:00
log_message ( LOG_ERR , " Proxying refused on filtered domain \" %s \" " , request - > host ) ;
httperr ( connptr , 404 , " Connection to filtered domain is now allowed. " ) ;
2001-09-14 12:56:29 +08:00
2001-09-17 04:10:19 +08:00
free_request_struct ( request ) ;
2001-09-14 12:56:29 +08:00
2001-09-17 04:10:19 +08:00
return NULL ;
2001-09-14 12:56:29 +08:00
}
}
# endif
2000-02-17 01:32:49 +08:00
2001-09-16 05:26:14 +08:00
/*
* Check to see if they ' re requesting the stat host
*/
2001-09-17 04:10:19 +08:00
if ( config . stathost & & strcmp ( config . stathost , request - > host ) = = 0 ) {
log_message ( LOG_NOTICE , " tinyproxy stathost request. " ) ;
2001-09-16 05:26:14 +08:00
2001-09-17 04:10:19 +08:00
free_request_struct ( request ) ;
2001-09-16 05:26:14 +08:00
showstats ( connptr ) ;
2001-09-17 04:10:19 +08:00
return NULL ;
2001-09-14 12:56:29 +08:00
}
2001-09-17 04:10:19 +08:00
return request ;
2000-02-17 01:32:49 +08:00
}
/*
* Check to see if the line is allowed or not depending on the anonymous
2001-08-27 05:11:55 +08:00
* headers which are to be allowed . If the header is found in the
* anonymous list return 0 , otherwise return - 1.
2000-02-17 01:32:49 +08:00
*/
2000-09-12 08:04:42 +08:00
static int compare_header ( char * line )
2000-02-17 01:32:49 +08:00
{
2001-08-27 05:11:55 +08:00
char * buffer ;
char * ptr ;
2000-04-01 04:13:36 +08:00
int ret ;
2000-02-17 01:32:49 +08:00
2001-08-31 00:51:10 +08:00
if ( ( ptr = strstr ( line , " : " ) ) = = NULL )
2000-09-12 08:04:42 +08:00
return - 1 ;
2000-04-01 04:13:36 +08:00
2001-09-09 02:58:37 +08:00
if ( ( buffer = safemalloc ( ptr - line + 1 ) ) = = NULL )
2000-09-12 08:04:42 +08:00
return - 1 ;
2000-04-01 04:13:36 +08:00
2001-05-27 10:29:06 +08:00
memcpy ( buffer , line , ( size_t ) ( ptr - line ) ) ;
2000-04-01 04:13:36 +08:00
buffer [ ptr - line ] = ' \0 ' ;
2001-08-27 05:11:55 +08:00
ret = anonymous_search ( buffer ) ;
2000-09-12 08:04:42 +08:00
safefree ( buffer ) ;
2001-08-27 05:11:55 +08:00
return ret ;
2000-02-17 01:32:49 +08:00
}
/*
2000-09-12 08:04:42 +08:00
* pull_client_data is used to pull across any client data ( like in a
* POST ) which needs to be handled before an error can be reported , or
* server headers can be processed .
* - rjkaes
2000-02-17 01:32:49 +08:00
*/
2000-09-12 08:04:42 +08:00
static int pull_client_data ( struct conn_s * connptr , unsigned long int length )
2000-02-17 01:32:49 +08:00
{
2001-09-12 03:26:49 +08:00
char * buffer ;
2001-05-27 10:29:06 +08:00
ssize_t len ;
2000-02-17 01:32:49 +08:00
2001-09-12 03:26:49 +08:00
buffer = safemalloc ( MAXBUFFSIZE ) ;
if ( ! buffer )
return - 1 ;
2000-09-12 08:04:42 +08:00
do {
len = safe_read ( connptr - > client_fd , buffer , min ( MAXBUFFSIZE , length ) ) ;
2000-02-17 01:32:49 +08:00
2000-09-12 08:04:42 +08:00
if ( len < = 0 ) {
2001-09-12 03:26:49 +08:00
safefree ( buffer ) ;
2000-09-12 08:04:42 +08:00
return - 1 ;
}
2001-09-17 04:10:19 +08:00
if ( ! connptr - > send_message ) {
2000-09-12 08:04:42 +08:00
if ( safe_write ( connptr - > server_fd , buffer , len ) < 0 ) {
2001-09-12 03:26:49 +08:00
safefree ( buffer ) ;
2000-09-12 08:04:42 +08:00
return - 1 ;
}
}
length - = len ;
} while ( length > 0 ) ;
2000-02-17 01:32:49 +08:00
2001-09-12 03:26:49 +08:00
safefree ( buffer ) ;
2000-02-17 01:32:49 +08:00
return 0 ;
}
2000-09-12 08:04:42 +08:00
# ifdef XTINYPROXY_ENABLE
2000-02-17 01:32:49 +08:00
/*
2000-09-12 08:04:42 +08:00
* Add the X - Tinyproxy header to the collection of headers being sent to
* the server .
* - rjkaes
2000-02-17 01:32:49 +08:00
*/
2000-09-12 08:04:42 +08:00
static int add_xtinyproxy_header ( struct conn_s * connptr )
2000-02-17 01:32:49 +08:00
{
2000-09-12 08:04:42 +08:00
char ipaddr [ PEER_IP_LENGTH ] ;
char xtinyproxy [ 32 ] ;
int length ;
2000-02-17 01:32:49 +08:00
2000-09-12 08:04:42 +08:00
length = snprintf ( xtinyproxy , sizeof ( xtinyproxy ) ,
" X-Tinyproxy: %s \r \n " ,
getpeer_ip ( connptr - > client_fd , ipaddr ) ) ;
if ( safe_write ( connptr - > server_fd , xtinyproxy , length ) < 0 )
2000-02-17 01:32:49 +08:00
return - 1 ;
2000-09-12 08:04:42 +08:00
return 0 ;
2000-02-17 01:32:49 +08:00
}
2000-09-12 08:04:42 +08:00
# endif /* XTINYPROXY */
2000-02-17 01:32:49 +08:00
/*
2000-09-12 08:04:42 +08:00
* Here we loop through all the headers the client is sending . If we
* are running in anonymous mode , we will _only_ send the headers listed
* ( plus a few which are required for various methods ) .
* - rjkaes
2000-02-17 01:32:49 +08:00
*/
2000-09-12 08:04:42 +08:00
static int process_client_headers ( struct conn_s * connptr )
2000-02-17 01:32:49 +08:00
{
2001-09-12 03:26:49 +08:00
char * header ;
2000-09-12 08:04:42 +08:00
long content_length = - 1 ;
2001-09-15 03:50:45 +08:00
static char * skipheaders [ ] = {
2000-09-12 08:04:42 +08:00
" proxy-connection " ,
2001-09-15 05:16:56 +08:00
" host " ,
2000-09-12 08:04:42 +08:00
" connection "
} ;
int i ;
2001-09-12 03:26:49 +08:00
header = safemalloc ( LINE_LENGTH ) ;
if ( ! header )
return - 1 ;
2000-09-12 08:04:42 +08:00
for ( ; ; ) {
2001-09-16 05:26:14 +08:00
if ( readline ( connptr - > client_fd , header , LINE_LENGTH ) < = 0 ) {
2001-09-12 03:26:49 +08:00
safefree ( header ) ;
2000-09-12 08:04:42 +08:00
return - 1 ;
}
2000-02-17 01:32:49 +08:00
2000-09-12 08:04:42 +08:00
if ( header [ 0 ] = = ' \n '
| | ( header [ 0 ] = = ' \r ' & & header [ 1 ] = = ' \n ' ) ) {
break ;
}
2000-02-17 01:32:49 +08:00
2001-09-17 04:10:19 +08:00
if ( connptr - > send_message )
2000-09-12 08:04:42 +08:00
continue ;
2000-02-17 01:32:49 +08:00
2001-09-15 05:16:56 +08:00
/*
* Don ' t send any of the headers if we ' re in SSL mode .
*/
if ( connptr - > ssl )
2001-09-15 03:50:45 +08:00
continue ;
2000-09-12 08:04:42 +08:00
/*
* Don ' t send certain headers .
*/
for ( i = 0 ; i < ( sizeof ( skipheaders ) / sizeof ( char * ) ) ; i + + ) {
if ( strncasecmp ( header , skipheaders [ i ] , strlen ( skipheaders [ i ] ) ) = = 0 ) {
break ;
2000-02-17 01:32:49 +08:00
}
}
2000-09-12 08:04:42 +08:00
if ( i ! = ( sizeof ( skipheaders ) / sizeof ( char * ) ) )
continue ;
2001-09-05 02:22:00 +08:00
if ( is_anonymous_enabled ( ) & & compare_header ( header ) < 0 )
continue ;
2001-08-27 05:11:55 +08:00
if ( content_length = = - 1
& & strncasecmp ( header , " content-length " , 14 ) = = 0 ) {
2000-09-12 08:04:42 +08:00
char * content_ptr = strchr ( header , ' : ' ) + 1 ;
content_length = atol ( content_ptr ) ;
}
2001-09-12 03:26:49 +08:00
if ( safe_write ( connptr - > server_fd , header , strlen ( header ) ) < 0 ) {
safefree ( header ) ;
2000-09-12 08:04:42 +08:00
return - 1 ;
2001-09-12 03:26:49 +08:00
}
2000-02-17 01:32:49 +08:00
}
2001-09-17 04:10:19 +08:00
if ( ! connptr - > send_message & & ! connptr - > ssl ) {
2000-09-12 08:04:42 +08:00
# ifdef XTINYPROXY_ENABLE
if ( config . my_domain
& & add_xtinyproxy_header ( connptr ) < 0 ) {
2001-09-12 03:26:49 +08:00
safefree ( header ) ;
2000-09-12 08:04:42 +08:00
return - 1 ;
}
# endif /* XTINYPROXY */
2000-02-17 01:32:49 +08:00
2000-09-12 08:04:42 +08:00
if ( safe_write ( connptr - > server_fd , header , strlen ( header ) ) < 0 ) {
2001-09-12 03:26:49 +08:00
safefree ( header ) ;
2000-02-17 01:32:49 +08:00
return - 1 ;
}
}
2001-09-12 03:26:49 +08:00
safefree ( header ) ;
2000-09-12 08:04:42 +08:00
/*
* Spin here pulling the data from the client .
*/
if ( content_length > = 0 )
2001-05-27 10:29:06 +08:00
return pull_client_data ( connptr , ( unsigned long int ) content_length ) ;
2000-09-12 08:04:42 +08:00
else
return 0 ;
2000-02-17 01:32:49 +08:00
}
/*
2000-09-12 08:04:42 +08:00
* Loop through all the headers ( including the response code ) from the
* server .
2000-02-17 01:32:49 +08:00
*/
2000-09-12 08:04:42 +08:00
static int process_server_headers ( struct conn_s * connptr )
2000-02-17 01:32:49 +08:00
{
2001-09-12 03:26:49 +08:00
char * header ;
header = safemalloc ( LINE_LENGTH ) ;
if ( ! header )
return - 1 ;
2000-03-12 04:37:44 +08:00
2000-09-12 08:04:42 +08:00
for ( ; ; ) {
2001-09-16 05:26:14 +08:00
if ( readline ( connptr - > server_fd , header , LINE_LENGTH ) < = 0 ) {
2001-09-12 03:26:49 +08:00
safefree ( header ) ;
2000-09-12 08:04:42 +08:00
return - 1 ;
}
2000-02-17 01:32:49 +08:00
2000-09-12 08:04:42 +08:00
if ( header [ 0 ] = = ' \n '
| | ( header [ 0 ] = = ' \r ' & & header [ 1 ] = = ' \n ' ) ) {
break ;
}
2000-02-17 01:32:49 +08:00
2000-09-12 08:04:42 +08:00
if ( ! connptr - > simple_req
& & safe_write ( connptr - > client_fd , header , strlen ( header ) ) < 0 ) {
2001-09-12 03:26:49 +08:00
safefree ( header ) ;
2000-09-12 08:04:42 +08:00
return - 1 ;
2000-03-12 04:37:44 +08:00
}
2000-02-17 01:32:49 +08:00
}
2000-09-12 08:04:42 +08:00
if ( ! connptr - > simple_req
& & safe_write ( connptr - > client_fd , header , strlen ( header ) ) < 0 ) {
2001-09-12 03:26:49 +08:00
safefree ( header ) ;
2000-09-12 08:04:42 +08:00
return - 1 ;
}
2001-09-12 03:26:49 +08:00
safefree ( header ) ;
2000-09-12 08:04:42 +08:00
return 0 ;
2000-02-17 01:32:49 +08:00
}
2000-09-12 08:04:42 +08:00
/*
* Switch the sockets into nonblocking mode and begin relaying the bytes
* between the two connections . We continue to use the buffering code
* since we want to be able to buffer a certain amount for slower
* connections ( as this was the reason why I originally modified
* tinyproxy oh so long ago . . . )
* - rjkaes
*/
static void relay_connection ( struct conn_s * connptr )
2000-02-17 01:32:49 +08:00
{
2000-09-12 08:04:42 +08:00
fd_set rset , wset ;
struct timeval tv ;
time_t last_access ;
int ret ;
double tdiff ;
2001-05-27 10:29:06 +08:00
int maxfd = max ( connptr - > client_fd , connptr - > server_fd ) + 1 ;
2000-09-12 08:04:42 +08:00
socket_nonblocking ( connptr - > client_fd ) ;
socket_nonblocking ( connptr - > server_fd ) ;
last_access = time ( NULL ) ;
for ( ; ; ) {
FD_ZERO ( & rset ) ;
FD_ZERO ( & wset ) ;
tv . tv_sec = config . idletimeout - difftime ( time ( NULL ) , last_access ) ;
tv . tv_usec = 0 ;
if ( buffer_size ( connptr - > sbuffer ) > 0 )
FD_SET ( connptr - > client_fd , & wset ) ;
if ( buffer_size ( connptr - > cbuffer ) > 0 )
FD_SET ( connptr - > server_fd , & wset ) ;
if ( buffer_size ( connptr - > sbuffer ) < MAXBUFFSIZE )
FD_SET ( connptr - > server_fd , & rset ) ;
if ( buffer_size ( connptr - > cbuffer ) < MAXBUFFSIZE )
FD_SET ( connptr - > client_fd , & rset ) ;
2001-05-27 10:29:06 +08:00
ret = select ( maxfd , & rset , & wset , NULL , & tv ) ;
2001-05-24 01:58:19 +08:00
2000-09-12 08:04:42 +08:00
if ( ret = = 0 ) {
tdiff = difftime ( time ( NULL ) , last_access ) ;
if ( tdiff > config . idletimeout ) {
2001-09-07 12:21:07 +08:00
log_message ( LOG_INFO , " Idle Timeout (after select) as %g > %u. " , tdiff , config . idletimeout ) ;
2000-02-17 01:32:49 +08:00
return ;
2000-09-12 08:04:42 +08:00
} else {
continue ;
2000-02-17 01:32:49 +08:00
}
2001-05-27 10:29:06 +08:00
} else if ( ret < 0 ) {
2001-10-17 12:15:35 +08:00
log_message ( LOG_ERR , " Received an error in select() [ \" %s \" , %d], so closing connection (client_fd:%d, server_fd:%d) " , strerror ( errno ) , errno , connptr - > client_fd , connptr - > server_fd ) ;
2000-09-12 08:04:42 +08:00
return ;
2001-05-27 10:29:06 +08:00
} else {
/*
* Okay , something was actually selected so mark it .
*/
last_access = time ( NULL ) ;
}
2001-05-30 23:45:14 +08:00
2001-05-27 10:29:06 +08:00
if ( FD_ISSET ( connptr - > server_fd , & rset )
& & readbuff ( connptr - > server_fd , connptr - > sbuffer ) < 0 ) {
2001-05-30 23:45:14 +08:00
shutdown ( connptr - > server_fd , SHUT_WR ) ;
break ;
2000-09-12 08:04:42 +08:00
}
2001-05-27 10:29:06 +08:00
if ( FD_ISSET ( connptr - > client_fd , & rset )
& & readbuff ( connptr - > client_fd , connptr - > cbuffer ) < 0 ) {
2001-05-30 23:45:14 +08:00
return ;
2000-09-12 08:04:42 +08:00
}
2001-05-27 10:29:06 +08:00
if ( FD_ISSET ( connptr - > server_fd , & wset )
& & writebuff ( connptr - > server_fd , connptr - > cbuffer ) < 0 ) {
2001-05-30 23:45:14 +08:00
shutdown ( connptr - > server_fd , SHUT_WR ) ;
break ;
2000-09-12 08:04:42 +08:00
}
2001-05-27 10:29:06 +08:00
if ( FD_ISSET ( connptr - > client_fd , & wset )
& & writebuff ( connptr - > client_fd , connptr - > sbuffer ) < 0 ) {
2001-05-30 23:45:14 +08:00
return ;
2000-02-17 01:32:49 +08:00
}
}
2000-09-12 08:04:42 +08:00
/*
* Here the server has closed the connection . . . write the
* remainder to the client and then exit .
*/
socket_blocking ( connptr - > client_fd ) ;
while ( buffer_size ( connptr - > sbuffer ) > 0 ) {
2001-05-27 10:29:06 +08:00
if ( writebuff ( connptr - > client_fd , connptr - > sbuffer ) < 0 )
2000-02-17 01:32:49 +08:00
return ;
}
2000-09-12 08:04:42 +08:00
return ;
2000-02-17 01:32:49 +08:00
}
2000-09-12 08:04:42 +08:00
static void initialize_conn ( struct conn_s * connptr )
2000-02-17 01:32:49 +08:00
{
2000-09-12 08:04:42 +08:00
connptr - > client_fd = connptr - > server_fd = - 1 ;
connptr - > cbuffer = new_buffer ( ) ;
connptr - > sbuffer = new_buffer ( ) ;
2001-09-17 04:10:19 +08:00
connptr - > send_message = FALSE ;
2000-09-12 08:04:42 +08:00
connptr - > simple_req = FALSE ;
2000-02-17 01:32:49 +08:00
2001-09-14 12:56:29 +08:00
connptr - > ssl = FALSE ;
2000-09-12 08:04:42 +08:00
update_stats ( STAT_OPEN ) ;
2000-02-17 01:32:49 +08:00
}
2000-09-12 08:04:42 +08:00
static void destroy_conn ( struct conn_s * connptr )
2000-02-17 01:32:49 +08:00
{
2001-05-30 23:45:14 +08:00
if ( connptr - > client_fd ! = - 1 )
close ( connptr - > client_fd ) ;
2000-09-12 08:04:42 +08:00
if ( connptr - > server_fd ! = - 1 )
close ( connptr - > server_fd ) ;
2000-02-17 01:32:49 +08:00
2000-09-12 08:04:42 +08:00
if ( connptr - > cbuffer )
delete_buffer ( connptr - > cbuffer ) ;
if ( connptr - > sbuffer )
delete_buffer ( connptr - > sbuffer ) ;
2000-02-17 01:32:49 +08:00
2000-09-12 08:04:42 +08:00
safefree ( connptr ) ;
2000-02-17 01:32:49 +08:00
2000-09-12 08:04:42 +08:00
update_stats ( STAT_CLOSE ) ;
2000-02-17 01:32:49 +08:00
}
/*
2000-09-12 08:04:42 +08:00
* This is the main drive for each connection . As you can tell , for the
* first few steps we are using a blocking socket . If you remember the
* older tinyproxy code , this use to be a very confusing state machine .
* Well , no more ! : ) The sockets are only switched into nonblocking mode
* when we start the relay portion . This makes most of the original
* tinyproxy code , which was confusing , redundant . Hail progress .
* - rjkaes
2000-02-17 01:32:49 +08:00
*/
2000-09-12 08:04:42 +08:00
void handle_connection ( int fd )
2000-02-17 01:32:49 +08:00
{
struct conn_s * connptr ;
2001-09-17 04:10:19 +08:00
struct request_s * request ;
2000-02-17 01:32:49 +08:00
char peer_ipaddr [ PEER_IP_LENGTH ] ;
2000-09-12 08:04:42 +08:00
char peer_string [ PEER_STRING_LENGTH ] ;
2000-02-17 01:32:49 +08:00
2001-09-17 04:10:19 +08:00
char * request_line = NULL ;
2001-09-14 12:56:29 +08:00
log_message ( LOG_CONN , " Connect (file descriptor %d): %s [%s] " ,
fd ,
2001-08-27 05:11:55 +08:00
getpeer_string ( fd , peer_string ) ,
getpeer_ip ( fd , peer_ipaddr ) ) ;
2000-02-17 01:32:49 +08:00
2001-09-09 02:58:37 +08:00
connptr = safemalloc ( sizeof ( struct conn_s ) ) ;
2001-09-17 04:10:19 +08:00
if ( ! connptr )
2000-09-12 08:04:42 +08:00
return ;
2000-02-17 01:32:49 +08:00
2000-09-12 08:04:42 +08:00
initialize_conn ( connptr ) ;
connptr - > client_fd = fd ;
2000-02-17 01:32:49 +08:00
2000-09-12 08:04:42 +08:00
if ( check_acl ( fd ) < = 0 ) {
update_stats ( STAT_DENIED ) ;
httperr ( connptr , 403 , " You do not have authorization for using this service. " ) ;
goto send_error ;
}
2000-02-17 01:32:49 +08:00
2000-09-12 08:04:42 +08:00
# ifdef TUNNEL_SUPPORT
/*
2001-09-17 04:10:19 +08:00
* If tunnel has been configured then redirect any connections to
* it . I know I used GOTOs , but it seems to me to be the best way
* of handling this situations . So sue me . : )
2000-09-12 08:04:42 +08:00
* - rjkaes
*/
if ( config . tunnel_name & & config . tunnel_port ! = - 1 ) {
2001-05-27 10:29:06 +08:00
log_message ( LOG_INFO , " Redirecting to %s:%d " ,
2001-09-17 04:10:19 +08:00
config . tunnel_name , config . tunnel_port ) ;
2000-09-12 08:04:42 +08:00
connptr - > server_fd = opensock ( config . tunnel_name , config . tunnel_port ) ;
if ( connptr - > server_fd < 0 ) {
2001-09-17 04:10:19 +08:00
log_message ( LOG_WARNING , " Could not connect to tunnel. " ) ;
httperr ( connptr , 404 , " Unable to connect to tunnel. " ) ;
2000-09-12 08:04:42 +08:00
goto internal_proxy ;
2000-02-17 01:32:49 +08:00
}
2000-09-12 08:04:42 +08:00
/*
* I know GOTOs are evil , but duplicating the code is even
* more evil .
* - rjkaes
*/
goto relay_proxy ;
2000-02-17 01:32:49 +08:00
}
2000-09-12 08:04:42 +08:00
# endif /* TUNNEL_SUPPORT */
2000-02-17 01:32:49 +08:00
2000-09-12 08:04:42 +08:00
internal_proxy :
2001-09-14 12:56:29 +08:00
request_line = read_request_line ( connptr ) ;
if ( ! request_line ) {
2001-09-17 04:10:19 +08:00
update_stats ( STAT_BADCONN ) ;
2000-09-12 08:04:42 +08:00
destroy_conn ( connptr ) ;
return ;
2000-02-17 01:32:49 +08:00
}
2001-09-17 04:10:19 +08:00
request = process_request ( connptr , request_line ) ;
2001-09-14 12:56:29 +08:00
safefree ( request_line ) ;
2001-09-17 04:10:19 +08:00
if ( ! request ) {
update_stats ( STAT_BADCONN ) ;
if ( ! connptr - > send_message ) {
destroy_conn ( connptr ) ;
return ;
}
} else {
# ifdef UPSTREAM_SUPPORT
if ( config . upstream_name & & config . upstream_port ! = - 1 ) {
connptr - > server_fd = opensock ( config . upstream_name , config . upstream_port ) ;
if ( connptr - > server_fd < 0 ) {
log_message ( LOG_WARNING , " Could not connect to upstream proxy. " ) ;
httperr ( connptr , 404 , " Unable to connect to upstream proxy. " ) ;
goto send_error ;
}
2001-10-17 12:15:35 +08:00
if ( ! connptr - > ssl ) {
/*
* Send a new request line , plus the Host and
* Connection headers . The reason for the new
* request line is that we need to specify
* the HTTP / 1.0 protocol .
*/
safe_write ( connptr - > server_fd , request - > method , strlen ( request - > method ) ) ;
safe_write ( connptr - > server_fd , " http:// " , 8 ) ;
safe_write ( connptr - > server_fd , request - > host , strlen ( request - > host ) ) ;
if ( request - > port ! = 80 ) {
char port_string [ 16 ] ;
sprintf ( port_string , " :%d " , request - > port ) ;
safe_write ( connptr - > server_fd , port_string , strlen ( port_string ) ) ;
}
safe_write ( connptr - > server_fd , request - > path , strlen ( request - > path ) ) ;
safe_write ( connptr - > server_fd , " HTTP/1.0 \r \n " , 11 ) ;
safe_write ( connptr - > server_fd , " Host: " , 6 ) ;
safe_write ( connptr - > server_fd , request - > host , strlen ( request - > host ) ) ;
safe_write ( connptr - > server_fd , " \r \n Connection: close \r \n " , 21 ) ;
} else {
/*
* This is a CONNECT request , so send that .
*/
2001-09-17 04:10:19 +08:00
char port_string [ 16 ] ;
sprintf ( port_string , " :%d " , request - > port ) ;
2001-10-17 12:15:35 +08:00
safe_write ( connptr - > server_fd , request - > method , strlen ( request - > method ) ) ;
safe_write ( connptr - > server_fd , " " , 1 ) ;
safe_write ( connptr - > server_fd , request - > host , strlen ( request - > host ) ) ;
2001-09-17 04:10:19 +08:00
safe_write ( connptr - > server_fd , port_string , strlen ( port_string ) ) ;
2001-10-17 12:15:35 +08:00
safe_write ( connptr - > server_fd , " HTTP/1.0 \r \n " , 11 ) ;
2001-09-17 04:10:19 +08:00
}
free_request_struct ( request ) ;
} else {
# endif
connptr - > server_fd = opensock ( request - > host , request - > port ) ;
if ( connptr - > server_fd < 0 ) {
httperr ( connptr , 500 , HTTP500ERROR ) ;
free_request_struct ( request ) ;
goto send_error ;
}
if ( ! connptr - > ssl )
establish_http_connection ( connptr , request ) ;
free_request_struct ( request ) ;
# ifdef UPSTREAM_SUPPORT
}
# endif
}
2000-09-12 08:04:42 +08:00
send_error :
2001-09-15 03:50:45 +08:00
if ( ! connptr - > simple_req ) {
2000-09-12 08:04:42 +08:00
if ( process_client_headers ( connptr ) < 0 ) {
update_stats ( STAT_BADCONN ) ;
2001-09-17 04:10:19 +08:00
if ( ! connptr - > send_message ) {
destroy_conn ( connptr ) ;
return ;
}
2000-02-17 01:32:49 +08:00
}
}
2001-09-17 04:10:19 +08:00
if ( connptr - > send_message ) {
2000-09-12 08:04:42 +08:00
destroy_conn ( connptr ) ;
return ;
2000-02-17 01:32:49 +08:00
}
2001-09-14 12:56:29 +08:00
if ( ! connptr - > ssl ) {
if ( process_server_headers ( connptr ) < 0 ) {
update_stats ( STAT_BADCONN ) ;
destroy_conn ( connptr ) ;
return ;
}
} else {
2001-09-16 05:26:14 +08:00
if ( send_ssl_response ( connptr ) < 0 ) {
2001-09-14 12:56:29 +08:00
log_message ( LOG_ERR , " Could not send SSL greeting to client. " ) ;
2001-09-17 04:10:19 +08:00
update_stats ( STAT_BADCONN ) ;
2001-09-14 12:56:29 +08:00
destroy_conn ( connptr ) ;
return ;
}
2000-02-17 01:32:49 +08:00
}
2000-09-12 08:04:42 +08:00
relay_proxy :
relay_connection ( connptr ) ;
/*
* All done . . . close everything and go home . . . : )
*/
destroy_conn ( connptr ) ;
return ;
2000-02-17 01:32:49 +08:00
}