add SOCKS upstream proxy support (socks4/socks5)

original patch submitted in 2006 to debian mailing list:
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=392848%29#12

this version was rebased to git and updated by Russ Dill <russ.dill@gmail.com>
in 2015 (the original patch used a different config file format).

as discussed in #40.

commit message by @rofl0r.
This commit is contained in:
Gonzalo Tornaria 2016-12-20 21:30:43 +00:00 committed by rofl0r
parent 116e59e933
commit 8906b0734e
4 changed files with 146 additions and 17 deletions

View File

@ -160,6 +160,8 @@ static HANDLE_FUNC (handle_xtinyproxy);
#ifdef UPSTREAM_SUPPORT #ifdef UPSTREAM_SUPPORT
static HANDLE_FUNC (handle_upstream); static HANDLE_FUNC (handle_upstream);
static HANDLE_FUNC (handle_upstream4);
static HANDLE_FUNC (handle_upstream5);
static HANDLE_FUNC (handle_upstream_no); static HANDLE_FUNC (handle_upstream_no);
#endif #endif
@ -257,6 +259,14 @@ struct {
BEGIN "(upstream)" WS "(" IP "|" ALNUM ")" ":" INT "(" WS STR BEGIN "(upstream)" WS "(" IP "|" ALNUM ")" ":" INT "(" WS STR
")?" END, handle_upstream, NULL ")?" END, handle_upstream, NULL
}, },
{
BEGIN "(upstream4)" WS "(" IP "|" ALNUM ")" ":" INT "(" WS STR
")?" END, handle_upstream4, NULL
},
{
BEGIN "(upstream5)" WS "(" IP "|" ALNUM ")" ":" INT "(" WS STR
")?" END, handle_upstream5, NULL
},
#endif #endif
/* loglevel */ /* loglevel */
STDCONF ("loglevel", "(critical|error|warning|notice|connect|info)", STDCONF ("loglevel", "(critical|error|warning|notice|connect|info)",
@ -1066,7 +1076,8 @@ static HANDLE_FUNC (handle_reversepath)
#endif #endif
#ifdef UPSTREAM_SUPPORT #ifdef UPSTREAM_SUPPORT
static HANDLE_FUNC (handle_upstream) static int _handle_upstream(struct config_s* conf, const char* line,
regmatch_t match[], proxy_type type)
{ {
char *ip; char *ip;
int port; int port;
@ -1080,11 +1091,11 @@ static HANDLE_FUNC (handle_upstream)
if (match[10].rm_so != -1) { if (match[10].rm_so != -1) {
domain = get_string_arg (line, &match[10]); domain = get_string_arg (line, &match[10]);
if (domain) { if (domain) {
upstream_add (ip, port, domain, &conf->upstream_list); upstream_add (ip, port, domain, type, &conf->upstream_list);
safefree (domain); safefree (domain);
} }
} else { } else {
upstream_add (ip, port, NULL, &conf->upstream_list); upstream_add (ip, port, NULL, type, &conf->upstream_list);
} }
safefree (ip); safefree (ip);
@ -1092,6 +1103,21 @@ static HANDLE_FUNC (handle_upstream)
return 0; return 0;
} }
static HANDLE_FUNC (handle_upstream)
{
return _handle_upstream(conf, line, match, HTTP_TYPE);
}
static HANDLE_FUNC (handle_upstream4)
{
return _handle_upstream(conf, line, match, SOCKS4_TYPE);
}
static HANDLE_FUNC (handle_upstream5)
{
return _handle_upstream(conf, line, match, SOCKS5_TYPE);
}
static HANDLE_FUNC (handle_upstream_no) static HANDLE_FUNC (handle_upstream_no)
{ {
char *domain; char *domain;
@ -1100,7 +1126,7 @@ static HANDLE_FUNC (handle_upstream_no)
if (!domain) if (!domain)
return -1; return -1;
upstream_add (NULL, 0, domain, &conf->upstream_list); upstream_add (NULL, 0, domain, HTTP_TYPE, &conf->upstream_list);
safefree (domain); safefree (domain);
return 0; return 0;

View File

@ -61,9 +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_HOST(host) upstream_get(host, config.upstream_list)
# define UPSTREAM_IS_HTTP(conn) (conn->upstream_proxy != NULL && conn->upstream_proxy->type == HTTP_TYPE)
#else #else
# define UPSTREAM_CONFIGURED() (0) # define UPSTREAM_CONFIGURED() (0)
# define UPSTREAM_HOST(host) (NULL) # define UPSTREAM_HOST(host) (NULL)
# define UPSTREAM_IS_HTTP(up) (0)
#endif #endif
/* /*
@ -853,10 +855,10 @@ process_client_headers (struct conn_s *connptr, hashmap_t hashofheaders)
/* /*
* Don't send headers if there's already an error, if the request was * Don't send headers if there's already an error, if the request was
* a stats request, or if this was a CONNECT method (unless upstream * a stats request, or if this was a CONNECT method (unless upstream
* proxy is in use.) * http proxy is in use.)
*/ */
if (connptr->server_fd == -1 || connptr->show_stats if (connptr->server_fd == -1 || connptr->show_stats
|| (connptr->connect_method && (connptr->upstream_proxy == NULL))) { || (connptr->connect_method && ! UPSTREAM_IS_HTTP(connptr))) {
log_message (LOG_INFO, log_message (LOG_INFO,
"Not sending client headers to remote machine"); "Not sending client headers to remote machine");
return 0; return 0;
@ -1265,6 +1267,88 @@ static void relay_connection (struct conn_s *connptr)
return; return;
} }
static int
connect_to_upstream_proxy(struct conn_s *connptr, struct request_s *request)
{
int len;
char buff[512]; /* won't use more than 7 + 255 */
unsigned short port;
struct hostent *host;
struct upstream *cur_upstream = connptr->upstream_proxy;
log_message(LOG_CONN,
"Established connection to %s proxy \"%s\" using file descriptor %d.",
proxy_type_name(cur_upstream->type), cur_upstream->host, connptr->server_fd);
if (cur_upstream->type == SOCKS4_TYPE) {
buff[0] = 4; /* socks version */
buff[1] = 1; /* connect command */
port = htons(request->port);
memcpy(&buff[2], &port, 2); /* dest port */
host = gethostbyname(request->host);
memcpy(&buff[4], host->h_addr_list[0], 4); /* dest ip */
buff[8] = 0; /* user */
if (9 != safe_write(connptr->server_fd, buff, 9))
return -1;
if (8 != safe_read(connptr->server_fd, buff, 8))
return -1;
if (buff[0]!=0 || buff[1]!=90)
return -1;
} else if (cur_upstream->type == SOCKS5_TYPE) {
/* init */
buff[0] = 5; /* socks version */
buff[1] = 1; /* number of methods */
buff[2] = 0; /* no auth method */
if (3 != safe_write(connptr->server_fd, buff, 3))
return -1;
if (2 != safe_read(connptr->server_fd, buff, 2))
return -1;
if (buff[0]!=5 || buff[1]!=0)
return -1;
/* connect */
buff[0] = 5; /* socks version */
buff[1] = 1; /* connect */
buff[2] = 0; /* reserved */
buff[3] = 3; /* domainname */
len=strlen(request->host);
if(len>255)
return -1;
buff[4] = len; /* length of domainname */
memcpy(&buff[5], request->host, len); /* dest ip */
port = htons(request->port);
memcpy(&buff[5+len], &port, 2); /* dest port */
if (7+len != safe_write(connptr->server_fd, buff, 7+len))
return -1;
if (4 != safe_read(connptr->server_fd, buff, 4))
return -1;
if (buff[0]!=5 || buff[1]!=0)
return -1;
switch(buff[3]) {
case 1: len=4; break; /* ip v4 */
case 4: len=16; break; /* ip v6 */
case 3: /* domainname */
if (1 != safe_read(connptr->server_fd, buff, 1))
return -1;
len = buff[0]; /* max = 255 */
break;
default: return -1;
}
if (2+len != safe_read(connptr->server_fd, buff, 2+len))
return -1;
} else {
return -1;
}
if (connptr->connect_method)
return 0;
return establish_http_connection(connptr, request);
}
/* /*
* Establish a connection to the upstream proxy server. * Establish a connection to the upstream proxy server.
*/ */
@ -1308,6 +1392,9 @@ connect_to_upstream (struct conn_s *connptr, struct request_s *request)
return -1; return -1;
} }
if (cur_upstream->type != HTTP_TYPE)
return connect_to_upstream_proxy(connptr, request);
log_message (LOG_CONN, log_message (LOG_CONN,
"Established connection to upstream proxy \"%s\" " "Established connection to upstream proxy \"%s\" "
"using file descriptor %d.", "using file descriptor %d.",
@ -1527,7 +1614,7 @@ void handle_connection (int fd)
goto fail; goto fail;
} }
if (!(connptr->connect_method && (connptr->upstream_proxy == NULL))) { if (!connptr->connect_method || UPSTREAM_IS_HTTP(connptr)) {
if (process_server_headers (connptr) < 0) { if (process_server_headers (connptr) < 0) {
update_stats (STAT_BADCONN); update_stats (STAT_BADCONN);
goto fail; goto fail;

View File

@ -29,10 +29,22 @@
#include "log.h" #include "log.h"
#ifdef UPSTREAM_SUPPORT #ifdef UPSTREAM_SUPPORT
const char *
proxy_type_name(proxy_type type)
{
switch(type) {
case HTTP_TYPE: return "http";
case SOCKS4_TYPE: return "socks4";
case SOCKS5_TYPE: 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 *domain,
proxy_type type)
{ {
char *ptr; char *ptr;
struct upstream *up; struct upstream *up;
@ -44,6 +56,7 @@ static struct upstream *upstream_build (const char *host, int port, const char *
return NULL; return NULL;
} }
up->type = type;
up->host = up->domain = NULL; up->host = up->domain = NULL;
up->ip = up->mask = 0; up->ip = up->mask = 0;
@ -57,8 +70,8 @@ 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;
log_message (LOG_INFO, "Added upstream %s:%d for [default]", log_message (LOG_INFO, "Added upstream %s %s:%d for [default]",
host, port); proxy_type_name(type), host, port);
} else if (host == NULL) { } else if (host == NULL) {
if (!domain || domain[0] == '\0') { if (!domain || domain[0] == '\0') {
log_message (LOG_WARNING, log_message (LOG_WARNING,
@ -101,8 +114,8 @@ static struct upstream *upstream_build (const char *host, int port, const char *
up->port = port; up->port = port;
up->domain = safestrdup (domain); up->domain = safestrdup (domain);
log_message (LOG_INFO, "Added upstream %s:%d for %s", log_message (LOG_INFO, "Added upstream %s %s:%d for %s",
host, port, domain); proxy_type_name(type), host, port, domain);
} }
return up; return up;
@ -119,11 +132,11 @@ fail:
* Add an entry to the upstream list * Add an entry to the upstream list
*/ */
void upstream_add (const char *host, int port, const char *domain, void upstream_add (const char *host, int port, const char *domain,
struct upstream **upstream_list) proxy_type type, struct upstream **upstream_list)
{ {
struct upstream *up; struct upstream *up;
up = upstream_build (host, port, domain); up = upstream_build (host, port, domain, type);
if (up == NULL) { if (up == NULL) {
return; return;
} }
@ -202,8 +215,8 @@ struct upstream *upstream_get (char *host, struct upstream *up)
up = NULL; up = NULL;
if (up) if (up)
log_message (LOG_INFO, "Found upstream proxy %s:%d for %s", log_message (LOG_INFO, "Found upstream proxy %s %s:%d for %s",
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

@ -31,17 +31,20 @@
* Even if upstream support is not compiled into tinyproxy, this * Even if upstream support is not compiled into tinyproxy, this
* structure still needs to be defined. * structure still needs to be defined.
*/ */
typedef enum {HTTP_TYPE, SOCKS4_TYPE, SOCKS5_TYPE} proxy_type;
struct upstream { struct upstream {
struct upstream *next; struct upstream *next;
char *domain; /* optional */ char *domain; /* optional */
char *host; char *host;
int port; int port;
in_addr_t ip, mask; in_addr_t ip, mask;
proxy_type type;
}; };
#ifdef UPSTREAM_SUPPORT #ifdef UPSTREAM_SUPPORT
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,
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 (char *host, 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 */