Add regex option for upstream proxy selection

This commit is contained in:
Markus Moeller 2020-01-06 20:17:23 +00:00
parent 69c86b987b
commit 93e9c156e8
5 changed files with 330 additions and 207 deletions

View File

@ -64,6 +64,16 @@ if test x"$filter_enabled" = x"yes"; then
AC_DEFINE(FILTER_ENABLE) AC_DEFINE(FILTER_ENABLE)
fi fi
dnl Include support for regex in upstream proxies?
AH_TEMPLATE([UPSTREAM_REGEX],
[Include support for connecting to an upstream proxy based on a regex.])
TP_ARG_ENABLE(upstream_regex,
[Enable upstream proxying (default is YES)],
yes)
if test x"$upstream_regex_enabled" = x"yes"; then
AC_DEFINE(UPSTREAM_REGEX)
fi
dnl Include support for upstream proxies? dnl Include support for upstream proxies?
AH_TEMPLATE([UPSTREAM_SUPPORT], AH_TEMPLATE([UPSTREAM_SUPPORT],
[Include support for connecting to an upstream proxy.]) [Include support for connecting to an upstream proxy.])

View File

@ -61,11 +61,11 @@
*/ */
#ifdef UPSTREAM_SUPPORT #ifdef UPSTREAM_SUPPORT
#define UPSTREAM_CONFIGURED() (config.upstream_list != NULL) #define UPSTREAM_CONFIGURED() (config.upstream_list != NULL)
# define UPSTREAM_HOST(host) upstream_get(host, config.upstream_list) #define UPSTREAM_REQUEST(request) upstream_get(request, config.upstream_list)
#define UPSTREAM_IS_HTTP(conn) (conn->upstream_proxy != NULL && conn->upstream_proxy->type == PT_HTTP) #define UPSTREAM_IS_HTTP(conn) (conn->upstream_proxy != NULL && conn->upstream_proxy->type == PT_HTTP)
#else #else
#define UPSTREAM_CONFIGURED() (0) #define UPSTREAM_CONFIGURED() (0)
# define UPSTREAM_HOST(host) (NULL) #define UPSTREAM_REQUEST(request) (request->host)
#define UPSTREAM_IS_HTTP(up) (0) #define UPSTREAM_IS_HTTP(up) (0)
#endif #endif
@ -130,6 +130,7 @@ static void free_request_struct (struct request_s *request)
safefree (request->method); safefree (request->method);
safefree (request->protocol); safefree (request->protocol);
safefree (request->url);
if (request->host) if (request->host)
safefree (request->host); safefree (request->host);
@ -195,23 +196,25 @@ static int strip_return_port (char *host)
* (proxied) ftp:// urls and https-requests that * (proxied) ftp:// urls and https-requests that
* come in without the proto:// part via CONNECT. * come in without the proto:// part via CONNECT.
*/ */
static int extract_url (const char *url, int default_port, static int extract_url (const char *lurl, const char *surl, int default_port,
struct request_s *request) struct request_s *request)
{ {
char *p; char *p;
int port; int port;
/* Split the URL on the slash to separate host from path */ /* Split the URL on the slash to separate host from path */
p = strchr (url, '/');
request->url = safestrdup (lurl);
p = strchr (surl, '/');
if (p != NULL) { if (p != NULL) {
int len; int len;
len = p - url; len = p - surl;
request->host = (char *) safemalloc (len + 1); request->host = (char *) safemalloc (len + 1);
memcpy (request->host, url, len); memcpy (request->host, surl, len);
request->host[len] = '\0'; request->host[len] = '\0';
request->path = safestrdup (p); request->path = safestrdup (p);
} else { } else {
request->host = safestrdup (url); request->host = safestrdup (surl);
request->path = safestrdup ("/"); request->path = safestrdup ("/");
} }
@ -238,6 +241,7 @@ static int extract_url (const char *url, int default_port,
return 0; return 0;
ERROR_EXIT: ERROR_EXIT:
safefree (request->url);
if (request->host) if (request->host)
safefree (request->host); safefree (request->host);
if (request->path) if (request->path)
@ -403,14 +407,14 @@ BAD_REQUEST_ERROR:
{ {
char *skipped_type = strstr (url, "//") + 2; char *skipped_type = strstr (url, "//") + 2;
if (extract_url (skipped_type, HTTP_PORT, request) < 0) { if (extract_url (url, skipped_type, HTTP_PORT, request) < 0) {
indicate_http_error (connptr, 400, "Bad Request", indicate_http_error (connptr, 400, "Bad Request",
"detail", "Could not parse URL", "detail", "Could not parse URL",
"url", url, NULL); "url", url, NULL);
goto fail; goto fail;
} }
} else if (strcmp (request->method, "CONNECT") == 0) { } else if (strcmp (request->method, "CONNECT") == 0) {
if (extract_url (url, HTTP_PORT_SSL, request) < 0) { if (extract_url (url, url, HTTP_PORT_SSL, request) < 0) {
indicate_http_error (connptr, 400, "Bad Request", indicate_http_error (connptr, 400, "Bad Request",
"detail", "Could not parse URL", "detail", "Could not parse URL",
"url", url, NULL); "url", url, NULL);
@ -419,8 +423,7 @@ BAD_REQUEST_ERROR:
/* Verify that the port in the CONNECT method is allowed */ /* Verify that the port in the CONNECT method is allowed */
if (!check_allowed_connect_ports (request->port, if (!check_allowed_connect_ports (request->port,
config.connect_ports)) config.connect_ports)) {
{
indicate_http_error (connptr, 403, "Access violation", indicate_http_error (connptr, 403, "Access violation",
"detail", "detail",
"The CONNECT method not allowed " "The CONNECT method not allowed "
@ -481,7 +484,6 @@ BAD_REQUEST_ERROR:
} }
#endif #endif
/* /*
* Check to see if they're requesting the stat host * Check to see if they're requesting the stat host
*/ */
@ -1089,8 +1091,8 @@ retry:
while (reverse) { while (reverse) {
if (strncasecmp (header, if (strncasecmp (header,
reverse->url, (len = reverse->url, (len =
strlen (reverse-> strlen (reverse->url)))
url))) == 0) == 0)
break; break;
reverse = reverse->next; reverse = reverse->next;
} }
@ -1293,10 +1295,10 @@ connect_to_upstream_proxy(struct conn_s *connptr, struct request_s *request)
ulen = cur_upstream->ua.user ? strlen (cur_upstream->ua.user) : 0; ulen = cur_upstream->ua.user ? strlen (cur_upstream->ua.user) : 0;
passlen = cur_upstream->pass ? strlen (cur_upstream->pass) : 0; passlen = cur_upstream->pass ? strlen (cur_upstream->pass) : 0;
log_message (LOG_CONN, log_message (LOG_CONN,
"Established connection to %s proxy \"%s\" using file descriptor %d.", "Established connection to %s proxy \"%s\" using file descriptor %d.",
proxy_type_name(cur_upstream->type), cur_upstream->host, connptr->server_fd); proxy_type_name (cur_upstream->type), cur_upstream->host,
connptr->server_fd);
if (cur_upstream->type == PT_SOCKS4) { if (cur_upstream->type == PT_SOCKS4) {
@ -1321,8 +1323,10 @@ connect_to_upstream_proxy(struct conn_s *connptr, struct request_s *request)
buff[0] = 5; /* socks version */ buff[0] = 5; /* socks version */
buff[1] = n_methods; /* number of methods */ buff[1] = n_methods; /* number of methods */
buff[2] = 0; /* no auth method */ buff[2] = 0; /* no auth method */
if (ulen) buff[3] = 2; /* auth method -> username / password */ if (ulen)
if (2+n_methods != safe_write(connptr->server_fd, buff, 2+n_methods)) buff[3] = 2; /* auth method -> username / password */
if (2 + n_methods !=
safe_write (connptr->server_fd, buff, 2 + n_methods))
return -1; return -1;
if (2 != safe_read (connptr->server_fd, buff, 2)) if (2 != safe_read (connptr->server_fd, buff, 2))
return -1; return -1;
@ -1345,7 +1349,8 @@ connect_to_upstream_proxy(struct conn_s *connptr, struct request_s *request)
memcpy (cur, cur_upstream->pass, c); memcpy (cur, cur_upstream->pass, c);
cur += c; cur += c;
if((cur - out) != safe_write(connptr->server_fd, out, cur - out)) if ((cur - out) !=
safe_write (connptr->server_fd, out, cur - out))
return -1; return -1;
if (2 != safe_read (connptr->server_fd, in, 2)) if (2 != safe_read (connptr->server_fd, in, 2))
@ -1373,14 +1378,19 @@ connect_to_upstream_proxy(struct conn_s *connptr, struct request_s *request)
if (buff[0] != 5 || buff[1] != 0) if (buff[0] != 5 || buff[1] != 0)
return -1; return -1;
switch (buff[3]) { switch (buff[3]) {
case 1: len=4; break; /* ip v4 */ case 1:
case 4: len=16; break; /* ip v6 */ len = 4;
break; /* ip v4 */
case 4:
len = 16;
break; /* ip v6 */
case 3: /* domainname */ case 3: /* domainname */
if (1 != safe_read (connptr->server_fd, buff, 1)) if (1 != safe_read (connptr->server_fd, buff, 1))
return -1; return -1;
len = buff[0]; /* max = 255 */ len = buff[0]; /* max = 255 */
break; break;
default: return -1; default:
return -1;
} }
if (2 + len != safe_read (connptr->server_fd, buff, 2 + len)) if (2 + len != safe_read (connptr->server_fd, buff, 2 + len))
return -1; return -1;
@ -1394,7 +1404,6 @@ connect_to_upstream_proxy(struct conn_s *connptr, struct request_s *request)
return establish_http_connection (connptr, request); return establish_http_connection (connptr, request);
} }
/* /*
* Establish a connection to the upstream proxy server. * Establish a connection to the upstream proxy server.
*/ */
@ -1480,8 +1489,7 @@ connect_to_upstream (struct conn_s *connptr, struct request_s *request)
#endif #endif
} }
static int static int get_request_entity (struct conn_s *connptr)
get_request_entity(struct conn_s *connptr)
{ {
int ret; int ret;
fd_set rset; fd_set rset;
@ -1509,8 +1517,7 @@ get_request_entity(struct conn_s *connptr)
ret = -1; ret = -1;
} else { } else {
log_message (LOG_INFO, log_message (LOG_INFO,
"Read request entity of %d bytes", "Read request entity of %d bytes", nread);
nread);
ret = 0; ret = 0;
} }
} else { } else {
@ -1523,7 +1530,6 @@ get_request_entity(struct conn_s *connptr)
return ret; return ret;
} }
/* /*
* This is the main drive for each connection. As you can tell, for the * 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 * first few steps we are using a blocking socket. If you remember the
@ -1612,23 +1618,32 @@ void handle_connection (int fd)
ssize_t len; ssize_t len;
char *authstring; char *authstring;
int failure = 1, stathost_connect = 0; int failure = 1, stathost_connect = 0;
len = hashmap_entry_by_key (hashofheaders, "proxy-authorization", len =
hashmap_entry_by_key (hashofheaders, "proxy-authorization",
(void **) &authstring); (void **) &authstring);
if (len == 0 && config.stathost) { if (len == 0 && config.stathost) {
len = hashmap_entry_by_key (hashofheaders, "host", len = hashmap_entry_by_key (hashofheaders, "host",
(void **) &authstring); (void **) &authstring);
if (len && !strncmp(authstring, config.stathost, strlen(config.stathost))) { if (len
len = hashmap_entry_by_key (hashofheaders, "authorization", && !strncmp (authstring, config.stathost,
(void **) &authstring); strlen (config.stathost))) {
len =
hashmap_entry_by_key (hashofheaders,
"authorization",
(void **)
&authstring);
stathost_connect = 1; stathost_connect = 1;
} else len = 0; } else
len = 0;
} }
if (len == 0) { if (len == 0) {
if (stathost_connect) goto e401; if (stathost_connect)
goto e401;
update_stats (STAT_DENIED); update_stats (STAT_DENIED);
indicate_http_error (connptr, 407, "Proxy Authentication Required", indicate_http_error (connptr, 407,
"Proxy Authentication Required",
"detail", "detail",
"This proxy requires authentication.", "This proxy requires authentication.",
NULL); NULL);
@ -1637,7 +1652,8 @@ void handle_connection (int fd)
if ( /* currently only "basic" auth supported */ if ( /* currently only "basic" auth supported */
(strncmp (authstring, "Basic ", 6) == 0 || (strncmp (authstring, "Basic ", 6) == 0 ||
strncmp (authstring, "basic ", 6) == 0) && strncmp (authstring, "basic ", 6) == 0) &&
basicauth_check (config.basicauth_list, authstring + 6) == 1) basicauth_check (config.basicauth_list,
authstring + 6) == 1)
failure = 0; failure = 0;
if (failure) { if (failure) {
e401: e401:
@ -1673,7 +1689,7 @@ e401:
goto fail; goto fail;
} }
connptr->upstream_proxy = UPSTREAM_HOST (request->host); connptr->upstream_proxy = UPSTREAM_REQUEST (request);
if (connptr->upstream_proxy != NULL) { if (connptr->upstream_proxy != NULL) {
if (connect_to_upstream (connptr, request) < 0) { if (connect_to_upstream (connptr, request) < 0) {
goto fail; goto fail;
@ -1736,8 +1752,7 @@ fail:
* to send our data properly. * to send our data properly.
*/ */
if (get_request_entity (connptr) < 0) { if (get_request_entity (connptr) < 0) {
log_message (LOG_WARNING, log_message (LOG_WARNING, "Could not retrieve request entity");
"Could not retrieve request entity");
indicate_http_error (connptr, 400, "Bad Request", indicate_http_error (connptr, 400, "Bad Request",
"detail", "detail",
"Could not retrieve the request entity " "Could not retrieve the request entity "

View File

@ -37,6 +37,7 @@ struct request_s {
char *method; char *method;
char *protocol; char *protocol;
char *url;
char *host; char *host;
uint16_t port; uint16_t port;

View File

@ -31,27 +31,36 @@
#include "basicauth.h" #include "basicauth.h"
#ifdef UPSTREAM_SUPPORT #ifdef UPSTREAM_SUPPORT
const char * const char *proxy_type_name (proxy_type type)
proxy_type_name(proxy_type type)
{ {
switch (type) { switch (type) {
case PT_NONE: return "none"; case PT_NONE:
case PT_HTTP: return "http"; return "none";
case PT_SOCKS4: return "socks4"; case PT_HTTP:
case PT_SOCKS5: return "socks5"; return "http";
default: return "unknown"; case PT_SOCKS4:
return "socks4";
case PT_SOCKS5:
return "socks5";
default:
return "unknown";
} }
} }
/** /**
* Construct an upstream struct from input data. * Construct an upstream struct from input data.
*/ */
static struct upstream *upstream_build (const char *host, int port, const char *domain, static struct upstream *upstream_build (const char *host, int port,
const char *user, const char *pass, const char *domain, const char *user,
proxy_type type) const char *pass, proxy_type type)
{ {
char *ptr; char *ptr;
struct upstream *up; struct upstream *up;
#ifdef UPSTREAM_REGEX
int cflags = REG_NEWLINE | REG_NOSUB;
int rflag = 0;
const char *rptr = NULL;
#endif
up = (struct upstream *) safemalloc (sizeof (struct upstream)); up = (struct upstream *) safemalloc (sizeof (struct upstream));
if (!up) { if (!up) {
@ -62,6 +71,40 @@ static struct upstream *upstream_build (const char *host, int port, const char *
up->type = type; up->type = type;
up->host = up->domain = up->ua.user = up->pass = NULL; up->host = up->domain = up->ua.user = up->pass = NULL;
#ifdef UPSTREAM_REGEX
up->pat = NULL;
up->cpat = NULL;
if (domain && !strncasecmp (domain, "regex(", 6)) { /* basic regex case senstive */
rflag = 1;
rptr = domain + 6;
}
if (domain && !strncasecmp (domain, "regexe(", 7)) { /* extended regex case senstive */
rflag = 1;
rptr = domain + 7;
cflags |= REG_EXTENDED;
}
if (domain && !strncasecmp (domain, "regexi(", 7)) { /* basic regex case insenstive */
rflag = 1;
rptr = domain + 7;
cflags |= REG_ICASE;
}
if ((domain && (!strncasecmp (domain, "regexie(", 8) || /* extended regex case insenstive */
!strncasecmp (domain, "regexei(", 8)))) { /* extended regex case insenstive */
rflag = 1;
rptr = domain + 8;
cflags |= REG_ICASE;
cflags |= REG_ICASE;
}
if (rflag) {
if (domain[strlen (domain) - 1] != ')') {
log_message (LOG_WARNING, "Bad regex: %s", domain);
goto fail;
} else {
up->pat = safestrdup (rptr);
up->pat[strlen (rptr) - 1] = '\0';
}
}
#endif
up->ip = up->mask = 0; up->ip = up->mask = 0;
if (user) { if (user) {
if (type == PT_HTTP) { if (type == PT_HTTP) {
@ -98,7 +141,9 @@ static struct upstream *upstream_build (const char *host, int port, const char *
"Nonsense no-upstream rule: empty domain"); "Nonsense no-upstream rule: empty domain");
goto fail; goto fail;
} }
#ifdef UPSTREAM_REGEX
if (!rflag) {
#endif
ptr = strchr (domain, '/'); ptr = strchr (domain, '/');
if (ptr) { if (ptr) {
struct in_addr addrstruct; struct in_addr addrstruct;
@ -109,17 +154,32 @@ static struct upstream *upstream_build (const char *host, int port, const char *
*ptr++ = '/'; *ptr++ = '/';
if (strchr (ptr, '.')) { if (strchr (ptr, '.')) {
if (inet_aton (ptr, &addrstruct) != 0) if (inet_aton (ptr, &addrstruct)
!= 0)
up->mask = up->mask =
ntohl (addrstruct.s_addr); ntohl
(addrstruct.s_addr);
} else { } else {
up->mask = up->mask =
~((1 << (32 - atoi (ptr))) - 1); ~((1 << (32 - atoi (ptr))) -
1);
} }
} }
} else { } else {
up->domain = safestrdup (domain); up->domain = safestrdup (domain);
} }
#ifdef UPSTREAM_REGEX
} else {
int err = 0;
up->cpat = (regex_t *) safemalloc (sizeof (regex_t));
err = regcomp (up->cpat, up->pat, cflags);
if (err != 0) {
log_message (LOG_WARNING,
"Bad regex: %s", up->pat);
goto fail;
}
}
#endif
log_message (LOG_INFO, "Added no-upstream for %s", domain); log_message (LOG_INFO, "Added no-upstream for %s", domain);
} else { } else {
@ -133,7 +193,18 @@ static struct upstream *upstream_build (const char *host, int port, const char *
up->host = safestrdup (host); up->host = safestrdup (host);
up->port = port; up->port = port;
up->domain = safestrdup (domain); up->domain = safestrdup (domain);
#ifdef UPSTREAM_REGEX
if (rflag) {
int err = 0;
up->cpat = (regex_t *) safemalloc (sizeof (regex_t));
err = regcomp (up->cpat, up->pat, cflags);
if (err != 0) {
log_message (LOG_WARNING,
"Bad regex: %s", up->pat);
goto fail;
}
}
#endif
log_message (LOG_INFO, "Added upstream %s %s:%d for %s", log_message (LOG_INFO, "Added upstream %s %s:%d for %s",
proxy_type_name (type), host, port, domain); proxy_type_name (type), host, port, domain);
} }
@ -145,6 +216,10 @@ fail:
safefree (up->pass); safefree (up->pass);
safefree (up->host); safefree (up->host);
safefree (up->domain); safefree (up->domain);
#ifdef UPSTREAM_REGEX
safefree (up->pat);
safefree (up->cpat);
#endif
safefree (up); safefree (up);
return NULL; return NULL;
@ -200,12 +275,27 @@ upstream_cleanup:
/* /*
* Check if a host is in the upstream list * Check if a host is in the upstream list
*/ */
struct upstream *upstream_get (char *host, struct upstream *up) struct upstream *upstream_get (struct request_s *request, struct upstream *up)
{ {
char *host = request->host;
in_addr_t my_ip = INADDR_NONE; in_addr_t my_ip = INADDR_NONE;
while (up) { while (up) {
#ifdef UPSTREAM_REGEX
if (up->cpat) {
int result;
char *url = request->url;
result =
regexec (up->cpat, url, (size_t) 0,
(regmatch_t *) 0, 0);
if (result == 0)
break; /* regex match */
} else if (up->domain) {
#else
if (up->domain) { if (up->domain) {
#endif
if (strcasecmp (host, up->domain) == 0) if (strcasecmp (host, up->domain) == 0)
break; /* exact match */ break; /* exact match */
@ -239,7 +329,8 @@ struct upstream *upstream_get (char *host, struct upstream *up)
if (up) if (up)
log_message (LOG_INFO, "Found upstream proxy %s %s:%d for %s", log_message (LOG_INFO, "Found upstream proxy %s %s:%d for %s",
proxy_type_name(up->type), up->host, up->port, host); proxy_type_name (up->type), up->host, up->port,
host);
else else
log_message (LOG_INFO, "No upstream proxy for %s", host); log_message (LOG_INFO, "No upstream proxy for %s", host);

View File

@ -26,6 +26,7 @@
#define _TINYPROXY_UPSTREAM_H_ #define _TINYPROXY_UPSTREAM_H_
#include "common.h" #include "common.h"
#include "reqs.h"
/* /*
* Even if upstream support is not compiled into tinyproxy, this * Even if upstream support is not compiled into tinyproxy, this
@ -50,6 +51,10 @@ struct upstream {
int port; int port;
in_addr_t ip, mask; in_addr_t ip, mask;
proxy_type type; proxy_type type;
#if defined(UPSTREAM_SUPPORT) && defined(UPSTREAM_REGEX)
char *pat;
regex_t *cpat;
#endif
}; };
#ifdef UPSTREAM_SUPPORT #ifdef UPSTREAM_SUPPORT
@ -57,7 +62,8 @@ const char *proxy_type_name(proxy_type type);
extern void upstream_add (const char *host, int port, const char *domain, extern void upstream_add (const char *host, int port, const char *domain,
const char *user, const char *pass, const char *user, const char *pass,
proxy_type type, struct upstream **upstream_list); proxy_type type, struct upstream **upstream_list);
extern struct upstream *upstream_get (char *host, struct upstream *up); extern struct upstream *upstream_get (struct request_s *request,
struct upstream *up);
extern void free_upstream_list (struct upstream *up); extern void free_upstream_list (struct upstream *up);
#endif /* UPSTREAM_SUPPORT */ #endif /* UPSTREAM_SUPPORT */