Just to show that yes, this is a useful feature, here's an updated patch.
My use case is a have a copy of Internet Explorer on my network that
needs to resolve and connect via socks5, but IE won't resolve hosts
via socks5 and instead leaks the requests over the network. tinyproxy
lets me direct IE instead to a HTTP/HTTPS proxy that forwards the
request over a socks5 proxy including the resolve.
This is against 1.8.0-122-g800c3a2, the config file format changed
slightly, upstream4 and upstream5 is used instead.
diff --git a/src/conf.c b/src/conf.c
index c003627..7033dff 100644
--- a/src/conf.c
+++ b/src/conf.c
@@ -160,6 +160,8 @@ static HANDLE_FUNC (handle_xtinyproxy);
#ifdef UPSTREAM_SUPPORT
static HANDLE_FUNC (handle_upstream);
+static HANDLE_FUNC (handle_upstream4);
+static HANDLE_FUNC (handle_upstream5);
static HANDLE_FUNC (handle_upstream_no);
#endif
@@ -257,6 +259,14 @@ struct {
BEGIN "(upstream)" WS "(" IP "|" ALNUM ")" ":" INT "(" WS STR
")?" 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
/* loglevel */
STDCONF ("loglevel", "(critical|error|warning|notice|connect|info)",
@@ -1070,7 +1080,8 @@ static HANDLE_FUNC (handle_reversepath)
#endif
#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;
int port;
@@ -1084,11 +1095,11 @@ static HANDLE_FUNC (handle_upstream)
if (match[10].rm_so != -1) {
domain = get_string_arg (line, &match[10]);
if (domain) {
- upstream_add (ip, port, domain, &conf->upstream_list);
+ upstream_add (ip, port, domain, type,
&conf->upstream_list);
safefree (domain);
}
} else {
- upstream_add (ip, port, NULL, &conf->upstream_list);
+ upstream_add (ip, port, NULL, type, &conf->upstream_list);
}
safefree (ip);
@@ -1096,6 +1107,21 @@ static HANDLE_FUNC (handle_upstream)
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)
{
char *domain;
@@ -1104,7 +1130,7 @@ static HANDLE_FUNC (handle_upstream_no)
if (!domain)
return -1;
- upstream_add (NULL, 0, domain, &conf->upstream_list);
+ upstream_add (NULL, 0, domain, HTTP_TYPE, &conf->upstream_list);
safefree (domain);
return 0;
diff --git a/src/reqs.c b/src/reqs.c
index d0f296f..dae13ce 100644
--- a/src/reqs.c
+++ b/src/reqs.c
@@ -61,9 +61,11 @@
#ifdef UPSTREAM_SUPPORT
# define UPSTREAM_CONFIGURED() (config.upstream_list != NULL)
# 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
# define UPSTREAM_CONFIGURED() (0)
# define UPSTREAM_HOST(host) (NULL)
+# define UPSTREAM_IS_HTTP(up) (0)
#endif
/*
@@ -847,10 +849,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
* 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
- || (connptr->connect_method && (connptr->upstream_proxy == NULL)))
{
+ || (connptr->connect_method && ! UPSTREAM_IS_HTTP(connptr))) {
log_message (LOG_INFO,
"Not sending client headers to remote machine");
return 0;
@@ -1259,6 +1261,88 @@ static void relay_connection (struct conn_s *connptr)
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.
*/
@@ -1302,6 +1386,9 @@ connect_to_upstream (struct conn_s *connptr, struct
request_s *request)
return -1;
}
+ if (cur_upstream->type != HTTP_TYPE)
+ return connect_to_upstream_proxy(connptr, request);
+
log_message (LOG_CONN,
"Established connection to upstream proxy \"%s\" "
"using file descriptor %d.",
@@ -1521,7 +1608,7 @@ void handle_connection (int fd)
goto fail;
}
- if (!(connptr->connect_method && (connptr->upstream_proxy == NULL))) {
+ if (!connptr->connect_method || UPSTREAM_IS_HTTP(connptr)) {
if (process_server_headers (connptr) < 0) {
update_stats (STAT_BADCONN);
goto fail;
diff --git a/src/upstream.c b/src/upstream.c
index 6b25f9b..91bf457 100644
--- a/src/upstream.c
+++ b/src/upstream.c
@@ -29,10 +29,22 @@
#include "log.h"
#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.
*/
-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;
struct upstream *up;
@@ -44,6 +56,7 @@ static struct upstream *upstream_build (const char *host, int
port, const char *
return NULL;
}
+ up->type = type;
up->host = up->domain = NULL;
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->port = port;
- log_message (LOG_INFO, "Added upstream %s:%d for [default]",
- host, port);
+ log_message (LOG_INFO, "Added upstream %s %s:%d for [default]",
+ proxy_type_name(type), host, port);
} else if (host == NULL) {
if (!domain || domain[0] == '\0') {
log_message (LOG_WARNING,
@@ -101,8 +114,8 @@ static struct upstream *upstream_build (const char *host,
int port, const char *
up->port = port;
up->domain = safestrdup (domain);
- log_message (LOG_INFO, "Added upstream %s:%d for %s",
- host, port, domain);
+ log_message (LOG_INFO, "Added upstream %s %s:%d for %s",
+ proxy_type_name(type), host, port, domain);
}
return up;
@@ -119,11 +132,11 @@ fail:
* Add an entry to the upstream list
*/
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;
- up = upstream_build (host, port, domain);
+ up = upstream_build (host, port, domain, type);
if (up == NULL) {
return;
}
@@ -202,8 +215,8 @@ struct upstream *upstream_get (char *host, struct upstream
*up)
up = NULL;
if (up)
- log_message (LOG_INFO, "Found upstream proxy %s:%d for %s",
- up->host, up->port, host);
+ log_message (LOG_INFO, "Found upstream proxy %s %s:%d for %s",
+ proxy_type_name(up->type), up->host, up->port,
host);
else
log_message (LOG_INFO, "No upstream proxy for %s", host);
diff --git a/src/upstream.h b/src/upstream.h
index 34dad68..63f40e3 100644
--- a/src/upstream.h
+++ b/src/upstream.h
@@ -31,17 +31,20 @@
* Even if upstream support is not compiled into tinyproxy, this
* structure still needs to be defined.
*/
+typedef enum {HTTP_TYPE, SOCKS4_TYPE, SOCKS5_TYPE} proxy_type;
struct upstream {
struct upstream *next;
char *domain; /* optional */
char *host;
int port;
in_addr_t ip, mask;
+ proxy_type type;
};
#ifdef UPSTREAM_SUPPORT
+const char *proxy_type_name(proxy_type type);
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 void free_upstream_list (struct upstream *up);
#endif /* UPSTREAM_SUPPORT */