From 4a377a712d5a17d1cd2c5e46f8c0873824a2448f Mon Sep 17 00:00:00 2001 From: Robert James Kaes Date: Thu, 29 May 2003 19:44:00 +0000 Subject: Improved the upstream proxy support by making the upstream proxy server configurable based on the destination host. [Code written by Peter da Silva] --- doc/tinyproxy.conf | 26 +++++++++ src/grammar.y | 21 ++++++- src/reqs.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++++----- src/reqs.h | 3 +- src/tinyproxy.h | 14 ++++- 5 files changed, 210 insertions(+), 23 deletions(-) diff --git a/doc/tinyproxy.conf b/doc/tinyproxy.conf index 651c221..62b52bc 100644 --- a/doc/tinyproxy.conf +++ b/doc/tinyproxy.conf @@ -92,6 +92,32 @@ PidFile "/var/run/tinyproxy.pid" # # Turns on upstream proxy support. # +# The upstream rules allow you to selectively route upstream connections +# based on the host/domain of the site being accessed. +# +# For example: +# # connection to test domain goes through testproxy +# upstream testproxy:8008 .test.domain.invalid +# upstream testproxy:8008 .our_testbed.example.com +# +# # no upstream proxy for internal websites and unqualified hosts +# no upstream .internal.example.com +# no upstream www.example.com +# no upstream . +# +# # connection to these boxes go through their DMZ firewalls +# upstream cust1_firewall:8008 testbed_for_cust1 +# upstream cust2_firewall:8008 testbed_for_cust2 +# +# # default upstream is internet firewall +# upstream firewall.internal.example.com:80 +# +# The LAST matching rule wins the route decision. As you can see, you +# can use a host, or a domain: +# name matches host exactly +# .name matches any host in domain "name" +# . matches any host with no domain (in 'empty' domain) +# #Upstream some.remote.proxy:port # diff --git a/src/grammar.y b/src/grammar.y index 7be9368..404f450 100644 --- a/src/grammar.y +++ b/src/grammar.y @@ -1,4 +1,4 @@ -/* $Id: grammar.y,v 1.20 2003-03-13 21:42:46 rjkaes Exp $ +/* $Id: grammar.y,v 1.21 2003-05-29 19:43:58 rjkaes Exp $ * * This is the grammar for tinyproxy's configuration file. It needs to be * in sync with scanner.l. If you know more about yacc and lex than I do @@ -171,12 +171,27 @@ statement | KW_UPSTREAM unique_address ':' NUMBER { #ifdef UPSTREAM_SUPPORT - config.upstream_name = $2; - config.upstream_port = $4; + upstream_add($2, $4, NULL); #else log_message(LOG_WARNING, "Upstream proxy support was not compiled in."); #endif } + | KW_UPSTREAM unique_address ':' NUMBER STRING + { +#ifdef UPSTREAM_SUPPORT + upstream_add($2, $4, $5); +#else + log_message(LOG_WARNING, "Upstream proxy support was not compiled in."); +#endif + } + | KW_NO KW_UPSTREAM STRING + { +#ifdef UPSTREAM_SUPPORT + upstream_add(NULL, 0, $3); +#else + log_message(LOG_WARNING, "Upstream proxy support was not compiled in."); +#endif + } | KW_LISTEN NUMERIC_ADDRESS { log_message(LOG_INFO, "Establishing listening socket on IP %s", $2); diff --git a/src/reqs.c b/src/reqs.c index ad68588..9fbec99 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1,4 +1,4 @@ -/* $Id: reqs.c,v 1.97 2003-05-05 16:46:05 rjkaes Exp $ +/* $Id: reqs.c,v 1.98 2003-05-29 19:43:57 rjkaes Exp $ * * This is where all the work in tinyproxy is actually done. Incoming * connections have a new child created for them. The child then @@ -57,9 +57,11 @@ * enabled. */ #ifdef UPSTREAM_SUPPORT -# define UPSTREAM_CONFIGURED() (config.upstream_name && config.upstream_port != -1) +# define UPSTREAM_CONFIGURED() (config.upstream_list != NULL) +# define UPSTREAM_HOST(host) upstream_get(host) #else # define UPSTREAM_CONFIGURED() (0) +# define UPSTREAM_HOST(host) (NULL) #endif /* @@ -73,6 +75,19 @@ */ static vector_t ports_allowed_by_connect = NULL; +/* + * This structure holds the information pulled from a URL request. + */ +struct request_s { + char *method; + char *protocol; + + char *host; + uint16_t port; + + char *path; +}; + /* * Now, this routine adds a "port" to the list. It also creates the list if * it hasn't already by done. @@ -163,18 +178,8 @@ read_request_line(struct conn_s *connptr) } /* - * This structure holds the information pulled from a URL request. + * Free all the memory allocated in a request. */ -struct request_s { - char *method; - char *protocol; - - char *host; - uint16_t port; - - char *path; -}; - static void free_request_struct(struct request_s *request) { @@ -304,6 +309,129 @@ build_url(char **url, const char *host, int port, const char *path) } #endif /* TRANSPARENT_PROXY */ +#ifdef UPSTREAM_SUPPORT +/* + * Add an entry to the upstream list + */ +void +upstream_add(const char *host, int port, const char *domain) +{ + struct upstream *up = safemalloc(sizeof (struct upstream)); + + if (!up) { + log_message(LOG_WARNING, + "Could not allocate memory for upstream host configuration"); + return; + } + + if (domain && domain[0] != '\0') + up->domain = safestrdup(domain); + else + up->domain = NULL; + + if (host && host[0] != '\0' && port > 0) + up->host = safestrdup(host); + else + up->host = NULL; + + if (port > 0) + up->port = port; + else + up->port = 0; + + if (host) { + log_message(LOG_INFO, "Adding upstream %s:%d for %s", + host, port, domain ? domain : "[default]"); + } else if (domain) { + log_message(LOG_INFO, "Adding no-upstream for %s", + domain ? domain : "[default]"); + } else { + log_message(LOG_WARNING, + "Nonsense upstream rule: no proxy or domain"); + + goto upstream_cleanup; + } + + if (!up->domain) { + /* always add default to end */ + struct upstream *tmp = config.upstream_list; + + while (tmp) { + if (!tmp->domain) { + log_message(LOG_WARNING, + "Duplicate default upstream"); + + goto upstream_cleanup; + } + + if (!tmp->next) { + up->next = NULL; + tmp->next = up; + return; + } + tmp = tmp->next; + } + } + + up->next = config.upstream_list; + config.upstream_list = up; + + return; + + upstream_cleanup: + safefree(up->host); + safefree(up->domain); + safefree(up); + + return; +} + +/* + * Check if a host is in the upstream list + */ +static struct upstream * +upstream_get(char *host) +{ + struct upstream *up = config.upstream_list; + + while (up) { + if (!up->domain) + break; /* no domain, default, match */ + + if (strcasecmp(host, up->domain) == 0) + break; /* exact match */ + + if (up->domain[0] == '.') { /* domain starts with dot... */ + char *dot = strchr(host, '.'); + if (!dot && !up->domain[1]) + break; /* domain exactly ".", host is local */ + + while (dot) { + if (strcasecmp(dot, up->domain) == 0) + break; /* subdomain match */ + dot = strchr(dot+1, '.'); + } + + if (dot) + break; /* trailing part of domain matches */ + } + + up = up->next; + } + + if (up && (!up->host || !up->port)) + up = NULL; + + if (up) + log_message(LOG_INFO, "Found proxy %s:%d for %s", + up->host, up->port, host); + else + log_message(LOG_INFO, "No proxy for %s", host); + + return up; +} +#endif + /* * Create a connection for HTTP connections. */ @@ -1249,8 +1377,17 @@ connect_to_upstream(struct conn_s *connptr, struct request_s *request) char *combined_string; int len; + struct upstream *cur_upstream = upstream_get(request->host); + if(!cur_upstream) { + log_message(LOG_WARNING, + "No upstream proxy defined for %s.", + request->host); + indicate_http_error(connptr, 404, "Unable to connect to upstream proxy."); + return -1; + } + connptr->server_fd = - opensock(config.upstream_name, config.upstream_port); + opensock(cur_upstream->host, cur_upstream->port); if (connptr->server_fd < 0) { log_message(LOG_WARNING, @@ -1263,7 +1400,7 @@ connect_to_upstream(struct conn_s *connptr, struct request_s *request) log_message(LOG_CONN, "Established connection to upstream proxy \"%s\" using file descriptor %d.", - config.upstream_name, connptr->server_fd); + cur_upstream->host, connptr->server_fd); /* * We need to re-write the "path" part of the request so that we @@ -1383,7 +1520,7 @@ handle_connection(int fd) goto send_error; } - if (UPSTREAM_CONFIGURED()) { + if (UPSTREAM_CONFIGURED() && (UPSTREAM_HOST(request->host) != NULL)) { if (connect_to_upstream(connptr, request) < 0) { goto send_error; } diff --git a/src/reqs.h b/src/reqs.h index b73ac0e..b324858 100644 --- a/src/reqs.h +++ b/src/reqs.h @@ -1,4 +1,4 @@ -/* $Id: reqs.h,v 1.3 2002-04-12 17:00:42 rjkaes Exp $ +/* $Id: reqs.h,v 1.4 2003-05-29 19:43:57 rjkaes Exp $ * * See 'reqs.c' for a detailed description. * @@ -21,5 +21,6 @@ extern void handle_connection(int fd); extern void add_connect_port_allowed(int port); +extern void upstream_add(const char *host, int port, const char *domain); #endif diff --git a/src/tinyproxy.h b/src/tinyproxy.h index e5c9e1c..0e8ee59 100644 --- a/src/tinyproxy.h +++ b/src/tinyproxy.h @@ -1,4 +1,4 @@ -/* $Id: tinyproxy.h,v 1.38 2003-03-13 21:32:33 rjkaes Exp $ +/* $Id: tinyproxy.h,v 1.39 2003-05-29 19:43:57 rjkaes Exp $ * * See 'tinyproxy.c' for a detailed description. * @@ -25,6 +25,15 @@ #define MAXBUFFSIZE ((size_t)(1024 * 96)) /* Max size of buffer */ #define MAX_IDLE_TIME (60 * 10) /* 10 minutes of no activity */ +#ifdef UPSTREAM_SUPPORT +struct upstream { + struct upstream *next; + char *domain; /* optional */ + char *host; + int port; +}; +#endif + struct config_s { char *logf_name; char *config_file; @@ -45,8 +54,7 @@ struct config_s { char *my_domain; #endif #ifdef UPSTREAM_SUPPORT - char *upstream_name; - int upstream_port; + struct upstream *upstream_list; #endif /* UPSTREAM_SUPPORT */ char *pidpath; unsigned int idletimeout; -- cgit v1.2.3