A _tonne_ of changes. It would almost be easier to just read the source

itself to find out all the changes.  Changed the process_client_header()
function to use the hashmap and vector modules.  I've made this change to
better handle the Connection header.  The Connection header, it it's
present, lists all the headers which should _not_ be transmitted any
further along.  An HTTP/1.1 proxy must respect this.

Other changes are basically cosmetic.
This commit is contained in:
Robert James Kaes 2002-04-07 21:35:59 +00:00
parent 5e9aa90009
commit fb736ec3fb

View File

@ -1,4 +1,4 @@
/* $Id: reqs.c,v 1.52 2001-12-24 00:01:02 rjkaes Exp $ /* $Id: reqs.c,v 1.53 2002-04-07 21:35:59 rjkaes Exp $
* *
* This is where all the work in tinyproxy is actually done. Incoming * This is where all the work in tinyproxy is actually done. Incoming
* connections have a new thread created for them. The thread then * connections have a new thread created for them. The thread then
@ -30,6 +30,7 @@
#include "buffer.h" #include "buffer.h"
#include "conns.h" #include "conns.h"
#include "filter.h" #include "filter.h"
#include "hashmap.h"
#include "log.h" #include "log.h"
#include "regexp.h" #include "regexp.h"
#include "reqs.h" #include "reqs.h"
@ -75,39 +76,38 @@
* connections. The request line is allocated from the heap, but it must * connections. The request line is allocated from the heap, but it must
* be freed in another function. * be freed in another function.
*/ */
static char * static int
read_request_line(struct conn_s *connptr) read_request_line(struct conn_s *connptr)
{ {
char *request_buffer;
size_t len; size_t len;
retry: retry:
len = readline(connptr->client_fd, &request_buffer); len = readline(connptr->client_fd, &connptr->request_line);
if (len <= 0) { if (len <= 0) {
log_message(LOG_ERR, log_message(LOG_ERR,
"read_request_line: Client (file descriptor: %d) closed socket before read.", "read_request_line: Client (file descriptor: %d) closed socket before read.",
connptr->client_fd); connptr->client_fd);
safefree(request_buffer); safefree(connptr->request_line);
return NULL; return -1;
} }
/* /*
* Strip the new line and character return from the string. * Strip the new line and character return from the string.
*/ */
if (chomp(request_buffer, len) == len) { if (chomp(connptr->request_line, len) == len) {
/* /*
* If the number of characters removed is the same as the * If the number of characters removed is the same as the
* length then it was a blank line. Free the buffer and * length then it was a blank line. Free the buffer and
* try again (since we're looking for a request line.) * try again (since we're looking for a request line.)
*/ */
safefree(request_buffer); safefree(connptr->request_line);
goto retry; goto retry;
} }
log_message(LOG_CONN, "Request (file descriptor %d): %s", log_message(LOG_CONN, "Request (file descriptor %d): %s",
connptr->client_fd, request_buffer); connptr->client_fd, connptr->request_line);
return request_buffer; return 0;
} }
/* /*
@ -118,8 +118,9 @@ struct request_s {
char *protocol; char *protocol;
char *host; char *host;
uint16_t port;
char *path; char *path;
int port;
}; };
static void static void
@ -154,11 +155,11 @@ extract_http_url(const char *url, struct request_s *request)
} }
if (sscanf if (sscanf
(url, "http://%[^:/]:%d%s", request->host, &request->port, (url, "http://%[^:/]:%hu%s", request->host, &request->port,
request->path) == 3) ; request->path) == 3) ;
else if (sscanf(url, "http://%[^/]%s", request->host, request->path) == 2) else if (sscanf(url, "http://%[^/]%s", request->host, request->path) == 2)
request->port = 80; request->port = 80;
else if (sscanf(url, "http://%[^:/]:%d", request->host, &request->port) else if (sscanf(url, "http://%[^:/]:%hu", request->host, &request->port)
== 2) == 2)
strcpy(request->path, "/"); strcpy(request->path, "/");
else if (sscanf(url, "http://%[^/]", request->host) == 1) { else if (sscanf(url, "http://%[^/]", request->host) == 1) {
@ -186,7 +187,7 @@ extract_ssl_url(const char *url, struct request_s *request)
if (!request->host) if (!request->host)
return -1; return -1;
if (sscanf(url, "%[^:]:%d", request->host, &request->port) == 2) ; if (sscanf(url, "%[^:]:%hu", request->host, &request->port) == 2) ;
else if (sscanf(url, "%s", request->host) == 1) else if (sscanf(url, "%s", request->host) == 1)
request->port = 443; request->port = 443;
else { else {
@ -255,7 +256,7 @@ send_ssl_response(struct conn_s *connptr)
* build a new request line. Finally connect to the remote server. * build a new request line. Finally connect to the remote server.
*/ */
static struct request_s * static struct request_s *
process_request(struct conn_s *connptr, char *request_line) process_request(struct conn_s *connptr)
{ {
char *url; char *url;
struct request_s *request; struct request_s *request;
@ -269,7 +270,7 @@ process_request(struct conn_s *connptr, char *request_line)
if (!request) if (!request)
return NULL; return NULL;
request_len = strlen(request_line) + 1; request_len = strlen(connptr->request_line) + 1;
request->method = safemalloc(request_len); request->method = safemalloc(request_len);
url = safemalloc(request_len); url = safemalloc(request_len);
@ -283,8 +284,8 @@ process_request(struct conn_s *connptr, char *request_line)
} }
ret = ret =
sscanf(request_line, "%[^ ] %[^ ] %[^ ]", request->method, url, sscanf(connptr->request_line, "%[^ ] %[^ ] %[^ ]",
request->protocol); request->method, url, request->protocol);
if (ret < 2) { if (ret < 2) {
log_message(LOG_ERR, log_message(LOG_ERR,
"process_request: Bad Request on file descriptor %d", "process_request: Bad Request on file descriptor %d",
@ -397,33 +398,6 @@ process_request(struct conn_s *connptr, char *request_line)
return request; return request;
} }
/*
* Check to see if the line is allowed or not depending on the anonymous
* headers which are to be allowed. If the header is found in the
* anonymous list return 0, otherwise return -1.
*/
static int
compare_header(char *line)
{
char *buffer;
char *ptr;
int ret;
if ((ptr = strstr(line, ":")) == NULL)
return -1;
if ((buffer = safemalloc(ptr - line + 1)) == NULL)
return -1;
memcpy(buffer, line, (size_t) (ptr - line));
buffer[ptr - line] = '\0';
ret = anonymous_search(buffer);
safefree(buffer);
return ret;
}
/* /*
* pull_client_data is used to pull across any client data (like in a * 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 * POST) which needs to be handled before an error can be reported, or
@ -450,7 +424,7 @@ pull_client_data(struct conn_s *connptr, unsigned long int length)
return -1; return -1;
} }
if (!connptr->send_response_message) { if (!connptr->response_message_sent) {
if (safe_write(connptr->server_fd, buffer, len) < 0) { if (safe_write(connptr->server_fd, buffer, len) < 0) {
safefree(buffer); safefree(buffer);
return -1; return -1;
@ -487,6 +461,112 @@ add_xtinyproxy_header(struct conn_s *connptr)
} }
#endif /* XTINYPROXY */ #endif /* XTINYPROXY */
/*
* Check to see if the header is allowed or not depending on the anonymous
* headers which are to be allowed. If the header is found in the
* anonymous list return 0, otherwise return -1.
*/
static inline int
compare_header(char *header)
{
return anonymous_search(header);
}
/*
* Take a complete header line and break it apart (into a key and the data.)
* Now insert this information into the hashmap for the connection so it
* can be retrieved and manipulated later.
*/
static inline int
add_header_to_connection(hashmap_t hashofheaders, char *header, size_t len)
{
char *sep;
size_t data_len;
/* Get rid of the new line and return at the end */
chomp(header, len);
sep = strchr(header, ':');
if (!sep)
return -1;
/* Blank out colons, spaces, and tabs. */
while (*sep == ':' || *sep == ' ' || *sep == '\t')
*sep++ = '\0';
data_len = strlen(sep) + 1; /* need to add the null to the length */
return hashmap_insert(hashofheaders, header, sep, data_len);
}
/*
* Read all the headers from the stream
*/
static int
get_all_headers(int fd, hashmap_t hashofheaders)
{
char *header;
ssize_t len;
for (;;) {
if ((len = readline(fd, &header)) <= 0) {
return -1;
}
/*
* If we received just a CR LF on a line, the headers are
* finished.
*/
if (CHECK_CRLF(header, len))
break;
if (add_header_to_connection(hashofheaders, header, len) < 0)
return -1;
}
return 0;
}
/*
* Extract the headers to remove. These headers were listed in the Connection
* header sent via the client (which is stored in data right now.)
*/
static int
remove_connection_headers(hashmap_t hashofheaders, char* data, ssize_t len)
{
char* ptr;
/*
* Go through the data line and replace any special characters with
* a NULL.
*/
ptr = data;
while ((ptr = strpbrk(ptr, "()<>@,;:\\\"/[]?={} \t"))) {
*ptr++ = '\0';
}
/*
* All the tokens are separated by NULLs. Now go through the tokens
* and remove them from the hashofheaders.
*/
ptr = data;
while (ptr < data + len) {
DEBUG2("Removing header [%s]", ptr);
hashmap_remove(hashofheaders, ptr);
/* Advance ptr to the next token */
ptr += strlen(ptr) + 1;
while (*ptr == '\0')
ptr++;
}
return 0;
}
/*
* Number of buckets to use internally in the hashmap.
*/
#define HEADER_BUCKETS 32
/* /*
* Here we loop through all the headers the client is sending. If we * 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 * are running in anonymous mode, we will _only_ send the headers listed
@ -496,11 +576,6 @@ add_xtinyproxy_header(struct conn_s *connptr)
static int static int
process_client_headers(struct conn_s *connptr) process_client_headers(struct conn_s *connptr)
{ {
char *header;
long content_length = -1;
short int sent_via_header = 0;
ssize_t len;
static char *skipheaders[] = { static char *skipheaders[] = {
"host", "host",
"connection", "connection",
@ -513,122 +588,120 @@ process_client_headers(struct conn_s *connptr)
"upgrade" "upgrade"
}; };
int i; int i;
hashmap_t hashofheaders;
vector_t listofheaders;
long content_length = -1;
for (;;) { char *data, *header;
if ((len = readline(connptr->client_fd, &header)) <= 0) { size_t len;
DEBUG2("Client (file descriptor %d) closed connection.",
connptr->client_fd);
return -1;
}
/* hashofheaders = hashmap_create(HEADER_BUCKETS);
* If we receive a CR LF (or just a LF) on a line by itself, if (!hashofheaders)
* the headers are finished. return -1;
*/
if (CHECK_CRLF(header, len))
break;
/* /*
* Don't send headers if there's already an error, or if * Get all the headers from the client in a big hash.
* this was a CONNECT method (unless upstream proxy is in */
* use.) if (get_all_headers(connptr->client_fd, hashofheaders) < 0) {
*/ hashmap_delete(hashofheaders);
if (connptr->server_fd == -1 return -1;
|| (connptr->connect_method && !UPSTREAM_CONFIGURED())) {
safefree(header);
continue;
}
/*
* If we find a Via header we need to append our information
* to the end of it.
*/
if (strncasecmp(header, "via", 3) == 0) {
if (sent_via_header == 0) {
char hostname[128];
chomp(header, len);
gethostname(hostname, sizeof(hostname));
write_message(connptr->server_fd,
"%s, %hu.%hu %s (%s/%s)\r\n",
header,
connptr->protocol.major,
connptr->protocol.minor,
hostname, PACKAGE, VERSION);
sent_via_header = 1;
}
safefree(header);
continue;
}
/*
* Don't send certain headers.
*/
for (i = 0; i < (sizeof(skipheaders) / sizeof(char *)); i++) {
if (strncasecmp
(header, skipheaders[i],
strlen(skipheaders[i])) == 0) {
break;
}
}
if (i != (sizeof(skipheaders) / sizeof(char *))) {
safefree(header);
continue;
}
if (is_anonymous_enabled() && compare_header(header) < 0) {
safefree(header);
continue;
}
if (content_length == -1
&& strncasecmp(header, "content-length", 14) == 0) {
char *content_ptr = strchr(header, ':') + 1;
content_length = atol(content_ptr);
}
if (safe_write(connptr->server_fd, header, strlen(header)) < 0) {
safefree(header);
return -1;
}
safefree(header);
} }
if (!connptr->send_response_message
&& (!connptr->connect_method || UPSTREAM_CONFIGURED())) {
#ifdef XTINYPROXY_ENABLE
if (config.my_domain && add_xtinyproxy_header(connptr) < 0) {
safefree(header);
return -1;
}
#endif /* XTINYPROXY */
if (sent_via_header == 0) { /*
/* * Don't send headers if there's already an error, or if this was
* We're the first proxy so send the first Via header. * a CONNECT method (unless upstream proxy is in use.)
*/ */
char hostname[128]; if (connptr->server_fd == -1
|| (connptr->connect_method && !UPSTREAM_CONFIGURED())) {
hashmap_delete(hashofheaders);
return 0;
}
gethostname(hostname, sizeof(hostname)); /*
* See if there is a "Connection" header. If so, we need to do a bit
* of processing. :)
*/
len = hashmap_search(hashofheaders, "connection", (void **)&data);
if (len > 0) {
/*
* Go through the tokens in the connection header and
* remove the headers from the hash.
*/
remove_connection_headers(hashofheaders, data, len);
hashmap_remove(hashofheaders, "connection");
}
/*
* See if there is a "Content-Length" header. If so, again we need
* to do a bit of processing.
*/
len = hashmap_search(hashofheaders, "content-length", (void **)&data);
if (len > 0) {
content_length = atol(data);
}
/*
* See if there is a "Via" header. If so, again we need to do a bit
* of processing.
*/
len = hashmap_search(hashofheaders, "via", (void **)&data);
if (len > 0) {
/* Take on our information */
char hostname[128];
gethostname(hostname, sizeof(hostname));
write_message(connptr->server_fd,
"Via: %s, %hu.%hu %s (%s/%s)\r\n",
data,
connptr->protocol.major, connptr->protocol.minor,
hostname, PACKAGE, VERSION);
hashmap_remove(hashofheaders, "via");
} else {
/* There is no header, so we need to create it. */
char hostname[128];
gethostname(hostname, sizeof(hostname));
write_message(connptr->server_fd,
"Via: %hu.%hu %s (%s/%s)\r\n",
connptr->protocol.major, connptr->protocol.minor,
hostname, PACKAGE, VERSION);
}
/*
* Delete the headers listed in the skipheaders list
*/
for (i = 0; i < (sizeof(skipheaders) / sizeof(char *)); i++) {
hashmap_remove(hashofheaders, skipheaders[i]);
}
/*
* Output all the remaining headers to the remote machine.
*/
listofheaders = hashmap_keys(hashofheaders);
for (i = 0; i < vector_length(listofheaders); i++) {
len = vector_getentry(listofheaders, i, (void **)&data);
hashmap_search(hashofheaders, data, (void **)&header);
if (!is_anonymous_enabled() || compare_header(data) == 0) {
write_message(connptr->server_fd, write_message(connptr->server_fd,
"Via: %hu.%hu %s (%s/%s)\r\n", "%s: %s\r\n",
connptr->protocol.major, data, header);
connptr->protocol.minor,
hostname, PACKAGE, VERSION);
}
if ((connptr->server_fd != -1)
&& safe_write(connptr->server_fd, header,
strlen(header)) < 0) {
safefree(header);
return -1;
} }
} }
vector_delete(listofheaders);
safefree(header); /* Free the hashofheaders since it's no longer needed */
hashmap_delete(hashofheaders);
#if defined(XTINYPROXY_ENABLE)
add_xtinyproxy_header(connptr);
#endif
/* Write the final "blank" line to signify the end of the headers */
safe_write(connptr->server_fd, "\r\n", 2);
/* /*
* Spin here pulling the data from the client. * Spin here pulling the data from the client.
@ -896,20 +969,15 @@ handle_connection(int fd)
char peer_ipaddr[PEER_IP_LENGTH]; char peer_ipaddr[PEER_IP_LENGTH];
char peer_string[PEER_STRING_LENGTH]; char peer_string[PEER_STRING_LENGTH];
char *request_line = NULL;
log_message(LOG_CONN, "Connect (file descriptor %d): %s [%s]", log_message(LOG_CONN, "Connect (file descriptor %d): %s [%s]",
fd, fd,
getpeer_string(fd, peer_string), getpeer_string(fd, peer_string),
getpeer_ip(fd, peer_ipaddr)); getpeer_ip(fd, peer_ipaddr));
connptr = safemalloc(sizeof(struct conn_s)); connptr = initialize_conn(fd);
if (!connptr) if (!connptr)
return; return;
initialize_conn(connptr);
connptr->client_fd = fd;
if (check_acl(fd) <= 0) { if (check_acl(fd) <= 0) {
update_stats(STAT_DENIED); update_stats(STAT_DENIED);
httperr(connptr, 403, httperr(connptr, 403,
@ -925,18 +993,15 @@ handle_connection(int fd)
} }
internal_proxy: internal_proxy:
request_line = read_request_line(connptr); if (read_request_line(connptr) < 0) {
if (!request_line) {
update_stats(STAT_BADCONN); update_stats(STAT_BADCONN);
destroy_conn(connptr); destroy_conn(connptr);
return; return;
} }
request = process_request(connptr, request_line); request = process_request(connptr);
safefree(request_line);
if (!request) { if (!request) {
if (!connptr->send_response_message) { if (!connptr->response_message_sent) {
update_stats(STAT_BADCONN); update_stats(STAT_BADCONN);
destroy_conn(connptr); destroy_conn(connptr);
return; return;
@ -967,13 +1032,13 @@ handle_connection(int fd)
if (process_client_headers(connptr) < 0) { if (process_client_headers(connptr) < 0) {
update_stats(STAT_BADCONN); update_stats(STAT_BADCONN);
if (!connptr->send_response_message) { if (!connptr->response_message_sent) {
destroy_conn(connptr); destroy_conn(connptr);
return; return;
} }
} }
if (connptr->send_response_message) { if (connptr->response_message_sent) {
destroy_conn(connptr); destroy_conn(connptr);
return; return;
} }