aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2020-01-06 13:41:20 -0500
committerNick Mathewson <nickm@torproject.org>2020-01-06 13:41:20 -0500
commit1b63eea66cbb8793a3cff05de8d856ce3b93fc17 (patch)
tree2cf4f6ff12522ed24a610b3acab0b107f73b1977
parent9276c07a916e4dc3b159c95c578e43be102f26e6 (diff)
parent14d781fff6bd88c4e0cd5b741629c3e79c8612d9 (diff)
downloadtor-1b63eea66cbb8793a3cff05de8d856ce3b93fc17.tar.gz
tor-1b63eea66cbb8793a3cff05de8d856ce3b93fc17.zip
Merge branch 'haxxpop/tcp_proxy_squashed' into tcp_proxy_squshed_and_merged
-rw-r--r--changes/ticket315186
-rw-r--r--doc/tor.1.txt16
-rw-r--r--src/app/config/config.c89
-rw-r--r--src/app/config/config.h2
-rw-r--r--src/app/config/or_options_st.h11
-rw-r--r--src/core/include.am2
-rw-r--r--src/core/mainloop/connection.c341
-rw-r--r--src/core/mainloop/connection.h4
-rw-r--r--src/core/or/connection_or.c26
-rw-r--r--src/core/or/connection_or.h7
-rw-r--r--src/core/or/or.h5
-rw-r--r--src/core/or/or_connection_st.h2
-rw-r--r--src/core/proto/.may_include6
-rw-r--r--src/core/proto/proto_haproxy.c45
-rw-r--r--src/core/proto/proto_haproxy.h12
-rw-r--r--src/feature/client/transports.c3
-rw-r--r--src/feature/control/btrack_orconn_cevent.c1
-rw-r--r--src/test/include.am1
-rw-r--r--src/test/test.c1
-rw-r--r--src/test/test.h1
-rw-r--r--src/test/test_config.c47
-rw-r--r--src/test/test_connection.c96
-rw-r--r--src/test/test_connection.h1
-rw-r--r--src/test/test_helpers.c81
-rw-r--r--src/test/test_helpers.h3
-rw-r--r--src/test/test_options.c9
-rw-r--r--src/test/test_proto_haproxy.c66
27 files changed, 731 insertions, 153 deletions
diff --git a/changes/ticket31518 b/changes/ticket31518
new file mode 100644
index 0000000000..8deac56b98
--- /dev/null
+++ b/changes/ticket31518
@@ -0,0 +1,6 @@
+ o Major features (proxy):
+ - In addition to HTTP CONNECT, SOCKS4, and SOCKS5, Tor can make all OR
+ connections through the HAProxy server. A new torrc option was added to
+ specify the address/port of the server: TCPProxy <protocol>
+ <host>:<port>. Currently the only supported protocol in the option is
+ haproxy. Close ticket 31518. Patch done by Suphanat Chunhapanya (haxxpop).
diff --git a/doc/tor.1.txt b/doc/tor.1.txt
index 85369dfcef..e1738c9ba3 100644
--- a/doc/tor.1.txt
+++ b/doc/tor.1.txt
@@ -918,6 +918,22 @@ forward slash (/) in the configuration file and on the command line.
log entries are marked with "Tor-__tag__". Can not be changed while tor is
running. (Default: none)
+[[TCPProxy]] **TCPProxy** __protocol__ __host__:__port__::
+ Tor will use the given protocol to make all its OR (SSL) connections through
+ a TCP proxy on host:port, rather than connecting directly to servers. You may
+ want to set **FascistFirewall** to restrict the set of ports you might try to
+ connect to, if your proxy only allows connecting to certain ports. There is no
+ equivalent option for directory connections, because all Tor client versions
+ that support this option download directory documents via OR connections. +
++
+ The only protocol supported right now 'haproxy'. This option is only for
+ clients. (Default: none) +
++
+ The HAProxy version 1 proxy protocol is described in detail at
+ https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt +
++
+ Both source IP address and source port will be set to zero.
+
[[TruncateLogFile]] **TruncateLogFile** **0**|**1**::
If 1, Tor will overwrite logs at startup and in response to a HUP signal,
instead of appending to them. (Default: 0)
diff --git a/src/app/config/config.c b/src/app/config/config.c
index 72ab065fb5..65a4dcb0ee 100644
--- a/src/app/config/config.c
+++ b/src/app/config/config.c
@@ -538,6 +538,7 @@ static const config_var_t option_vars_[] = {
V(Socks5Proxy, STRING, NULL),
V(Socks5ProxyUsername, STRING, NULL),
V(Socks5ProxyPassword, STRING, NULL),
+ V(TCPProxy, STRING, NULL),
VAR_IMMUTABLE("KeyDirectory", FILENAME, KeyDirectory_option, NULL),
V(KeyDirectoryGroupReadable, AUTOBOOL, "auto"),
VAR_D("HSLayer2Nodes", ROUTERSET, HSLayer2Nodes, NULL),
@@ -3940,19 +3941,28 @@ options_validate_cb(const void *old_options_, void *options_, char **msg)
}
}
+ if (options->TCPProxy) {
+ int res = parse_tcp_proxy_line(options->TCPProxy, options, msg);
+ if (res < 0) {
+ return res;
+ }
+ }
+
/* Check if more than one exclusive proxy type has been enabled. */
if (!!options->Socks4Proxy + !!options->Socks5Proxy +
- !!options->HTTPSProxy > 1)
+ !!options->HTTPSProxy + !!options->TCPProxy > 1)
REJECT("You have configured more than one proxy type. "
- "(Socks4Proxy|Socks5Proxy|HTTPSProxy)");
+ "(Socks4Proxy|Socks5Proxy|HTTPSProxy|TCPProxy)");
/* Check if the proxies will give surprising behavior. */
if (options->HTTPProxy && !(options->Socks4Proxy ||
options->Socks5Proxy ||
- options->HTTPSProxy)) {
- log_warn(LD_CONFIG, "HTTPProxy configured, but no SOCKS proxy or "
- "HTTPS proxy configured. Watch out: this configuration will "
- "proxy unencrypted directory connections only.");
+ options->HTTPSProxy ||
+ options->TCPProxy)) {
+ log_warn(LD_CONFIG, "HTTPProxy configured, but no SOCKS proxy, "
+ "HTTPS proxy, or any other TCP proxy configured. Watch out: "
+ "this configuration will proxy unencrypted directory "
+ "connections only.");
}
if (options->Socks5ProxyUsername) {
@@ -5348,6 +5358,68 @@ parse_bridge_line(const char *line)
return bridge_line;
}
+/** Parse the contents of a TCPProxy line from <b>line</b> and put it
+ * in <b>options</b>. Return 0 if the line is well-formed, and -1 if it
+ * isn't.
+ *
+ * This will mutate only options->TCPProxyProtocol, options->TCPProxyAddr,
+ * and options->TCPProxyPort.
+ *
+ * On error, tor_strdup an error explanation into *<b>msg</b>.
+ */
+STATIC int
+parse_tcp_proxy_line(const char *line, or_options_t *options, char **msg)
+{
+ int ret = 0;
+ tor_assert(line);
+ tor_assert(options);
+ tor_assert(msg);
+
+ smartlist_t *sl = smartlist_new();
+ /* Split between the protocol and the address/port. */
+ smartlist_split_string(sl, line, " ",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2);
+
+ /* The address/port is not specified. */
+ if (smartlist_len(sl) < 2) {
+ *msg = tor_strdup("TCPProxy has no address/port. Please fix.");
+ goto err;
+ }
+
+ char *protocol_string = smartlist_get(sl, 0);
+ char *addrport_string = smartlist_get(sl, 1);
+
+ /* The only currently supported protocol is 'haproxy'. */
+ if (strcasecmp(protocol_string, "haproxy")) {
+ *msg = tor_strdup("TCPProxy protocol is not supported. Currently "
+ "the only supported protocol is 'haproxy'. "
+ "Please fix.");
+ goto err;
+ } else {
+ /* Otherwise, set the correct protocol. */
+ options->TCPProxyProtocol = TCP_PROXY_PROTOCOL_HAPROXY;
+ }
+
+ /* Parse the address/port. */
+ if (tor_addr_port_lookup(addrport_string, &options->TCPProxyAddr,
+ &options->TCPProxyPort) < 0) {
+ *msg = tor_strdup("TCPProxy address/port failed to parse or resolve. "
+ "Please fix.");
+ goto err;
+ }
+
+ /* Success. */
+ ret = 0;
+ goto end;
+
+ err:
+ ret = -1;
+ end:
+ SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ smartlist_free(sl);
+ return ret;
+}
+
/** Read the contents of a ClientTransportPlugin or ServerTransportPlugin
* line from <b>line</b>, depending on the value of <b>server</b>. Return 0
* if the line is well-formed, and -1 if it isn't.
@@ -5495,9 +5567,10 @@ pt_parse_transport_line(const or_options_t *options,
/* ClientTransportPlugins connecting through a proxy is managed only. */
if (!server && (options->Socks4Proxy || options->Socks5Proxy ||
- options->HTTPSProxy)) {
+ options->HTTPSProxy || options->TCPProxy)) {
log_warn(LD_CONFIG, "You have configured an external proxy with another "
- "proxy type. (Socks4Proxy|Socks5Proxy|HTTPSProxy)");
+ "proxy type. (Socks4Proxy|Socks5Proxy|HTTPSProxy|"
+ "TCPProxy)");
goto err;
}
diff --git a/src/app/config/config.h b/src/app/config/config.h
index 15c9352467..2c03714fd7 100644
--- a/src/app/config/config.h
+++ b/src/app/config/config.h
@@ -286,6 +286,8 @@ STATIC const struct config_mgr_t *get_options_mgr(void);
STATIC void or_options_free_(or_options_t *options);
STATIC int options_validate_single_onion(or_options_t *options,
char **msg);
+STATIC int parse_tcp_proxy_line(const char *line, or_options_t *options,
+ char **msg);
STATIC int consider_adding_dir_servers(const or_options_t *options,
const or_options_t *old_options);
STATIC void add_default_trusted_dir_authorities(dirinfo_type_t type);
diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h
index 4915be187c..abd695464c 100644
--- a/src/app/config/or_options_st.h
+++ b/src/app/config/or_options_st.h
@@ -27,6 +27,12 @@ typedef enum {OUTBOUND_ADDR_EXIT, OUTBOUND_ADDR_OR,
OUTBOUND_ADDR_EXIT_AND_OR,
OUTBOUND_ADDR_MAX} outbound_addr_t;
+/** Which protocol to use for TCPProxy. */
+typedef enum {
+ /** Use the HAProxy proxy protocol. */
+ TCP_PROXY_PROTOCOL_HAPROXY
+} tcp_proxy_protocol_t;
+
/** Configuration options for a Tor process. */
struct or_options_t {
uint32_t magic_;
@@ -419,6 +425,11 @@ struct or_options_t {
char *Socks5ProxyUsername; /**< Username for SOCKS5 authentication, if any */
char *Socks5ProxyPassword; /**< Password for SOCKS5 authentication, if any */
+ char *TCPProxy; /**< protocol and hostname:port to use as a proxy, if any. */
+ tcp_proxy_protocol_t TCPProxyProtocol; /**< Derived from TCPProxy. */
+ tor_addr_t TCPProxyAddr; /**< Derived from TCPProxy. */
+ uint16_t TCPProxyPort; /**< Derived from TCPProxy. */
+
/** List of configuration lines for replacement directory authorities.
* If you just want to replace one class of authority at a time,
* use the "Alternate*Authority" options below instead. */
diff --git a/src/core/include.am b/src/core/include.am
index 911932d46b..50a2e2eeb8 100644
--- a/src/core/include.am
+++ b/src/core/include.am
@@ -64,6 +64,7 @@ LIBTOR_APP_A_SOURCES = \
src/core/proto/proto_cell.c \
src/core/proto/proto_control0.c \
src/core/proto/proto_ext_or.c \
+ src/core/proto/proto_haproxy.c \
src/core/proto/proto_http.c \
src/core/proto/proto_socks.c \
src/feature/api/tor_api.c \
@@ -324,6 +325,7 @@ noinst_HEADERS += \
src/core/proto/proto_cell.h \
src/core/proto/proto_control0.h \
src/core/proto/proto_ext_or.h \
+ src/core/proto/proto_haproxy.h \
src/core/proto/proto_http.h \
src/core/proto/proto_socks.h \
src/feature/api/tor_api_internal.h \
diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c
index 50fd12319e..15b0258037 100644
--- a/src/core/mainloop/connection.c
+++ b/src/core/mainloop/connection.c
@@ -82,6 +82,7 @@
#include "core/or/reasons.h"
#include "core/or/relay.h"
#include "core/or/crypt_path.h"
+#include "core/proto/proto_haproxy.h"
#include "core/proto/proto_http.h"
#include "core/proto/proto_socks.h"
#include "feature/client/dnsserv.h"
@@ -2318,7 +2319,11 @@ conn_get_proxy_type(const connection_t *conn)
return PROXY_SOCKS4;
else if (options->Socks5Proxy)
return PROXY_SOCKS5;
- else
+ else if (options->TCPProxy) {
+ /* The only supported protocol in TCPProxy is haproxy. */
+ tor_assert(options->TCPProxyProtocol == TCP_PROXY_PROTOCOL_HAPROXY);
+ return PROXY_HAPROXY;
+ } else
return PROXY_NONE;
}
@@ -2327,165 +2332,245 @@ conn_get_proxy_type(const connection_t *conn)
username NUL: */
#define SOCKS4_STANDARD_BUFFER_SIZE (1 + 1 + 2 + 4 + 1)
-/** Write a proxy request of <b>type</b> (socks4, socks5, https) to conn
- * for conn->addr:conn->port, authenticating with the auth details given
- * in the configuration (if available). SOCKS 5 and HTTP CONNECT proxies
- * support authentication.
+/** Write a proxy request of https to conn for conn->addr:conn->port,
+ * authenticating with the auth details given in the configuration
+ * (if available).
*
* Returns -1 if conn->addr is incompatible with the proxy protocol, and
* 0 otherwise.
- *
- * Use connection_read_proxy_handshake() to complete the handshake.
*/
-int
-connection_proxy_connect(connection_t *conn, int type)
+static int
+connection_https_proxy_connect(connection_t *conn)
{
- const or_options_t *options;
+ tor_assert(conn);
+
+ const or_options_t *options = get_options();
+ char buf[1024];
+ char *base64_authenticator = NULL;
+ const char *authenticator = options->HTTPSProxyAuthenticator;
+
+ /* Send HTTP CONNECT and authentication (if available) in
+ * one request */
+
+ if (authenticator) {
+ base64_authenticator = alloc_http_authenticator(authenticator);
+ if (!base64_authenticator)
+ log_warn(LD_OR, "Encoding https authenticator failed");
+ }
+
+ if (base64_authenticator) {
+ const char *addrport = fmt_addrport(&conn->addr, conn->port);
+ tor_snprintf(buf, sizeof(buf), "CONNECT %s HTTP/1.1\r\n"
+ "Host: %s\r\n"
+ "Proxy-Authorization: Basic %s\r\n\r\n",
+ addrport,
+ addrport,
+ base64_authenticator);
+ tor_free(base64_authenticator);
+ } else {
+ tor_snprintf(buf, sizeof(buf), "CONNECT %s HTTP/1.0\r\n\r\n",
+ fmt_addrport(&conn->addr, conn->port));
+ }
+
+ connection_buf_add(buf, strlen(buf), conn);
+ conn->proxy_state = PROXY_HTTPS_WANT_CONNECT_OK;
+
+ return 0;
+}
+/** Write a proxy request of socks4 to conn for conn->addr:conn->port.
+ *
+ * Returns -1 if conn->addr is incompatible with the proxy protocol, and
+ * 0 otherwise.
+ */
+static int
+connection_socks4_proxy_connect(connection_t *conn)
+{
tor_assert(conn);
- options = get_options();
+ unsigned char *buf;
+ uint16_t portn;
+ uint32_t ip4addr;
+ size_t buf_size = 0;
+ char *socks_args_string = NULL;
- switch (type) {
- case PROXY_CONNECT: {
- char buf[1024];
- char *base64_authenticator=NULL;
- const char *authenticator = options->HTTPSProxyAuthenticator;
-
- /* Send HTTP CONNECT and authentication (if available) in
- * one request */
-
- if (authenticator) {
- base64_authenticator = alloc_http_authenticator(authenticator);
- if (!base64_authenticator)
- log_warn(LD_OR, "Encoding https authenticator failed");
- }
+ /* Send a SOCKS4 connect request */
- if (base64_authenticator) {
- const char *addrport = fmt_addrport(&conn->addr, conn->port);
- tor_snprintf(buf, sizeof(buf), "CONNECT %s HTTP/1.1\r\n"
- "Host: %s\r\n"
- "Proxy-Authorization: Basic %s\r\n\r\n",
- addrport,
- addrport,
- base64_authenticator);
- tor_free(base64_authenticator);
- } else {
- tor_snprintf(buf, sizeof(buf), "CONNECT %s HTTP/1.0\r\n\r\n",
- fmt_addrport(&conn->addr, conn->port));
- }
+ if (tor_addr_family(&conn->addr) != AF_INET) {
+ log_warn(LD_NET, "SOCKS4 client is incompatible with IPv6");
+ return -1;
+ }
- connection_buf_add(buf, strlen(buf), conn);
- conn->proxy_state = PROXY_HTTPS_WANT_CONNECT_OK;
- break;
+ { /* If we are here because we are trying to connect to a
+ pluggable transport proxy, check if we have any SOCKS
+ arguments to transmit. If we do, compress all arguments to
+ a single string in 'socks_args_string': */
+
+ if (conn_get_proxy_type(conn) == PROXY_PLUGGABLE) {
+ socks_args_string =
+ pt_get_socks_args_for_proxy_addrport(&conn->addr, conn->port);
+ if (socks_args_string)
+ log_debug(LD_NET, "Sending out '%s' as our SOCKS argument string.",
+ socks_args_string);
}
+ }
- case PROXY_SOCKS4: {
- unsigned char *buf;
- uint16_t portn;
- uint32_t ip4addr;
- size_t buf_size = 0;
- char *socks_args_string = NULL;
+ { /* Figure out the buffer size we need for the SOCKS message: */
- /* Send a SOCKS4 connect request */
+ buf_size = SOCKS4_STANDARD_BUFFER_SIZE;
- if (tor_addr_family(&conn->addr) != AF_INET) {
- log_warn(LD_NET, "SOCKS4 client is incompatible with IPv6");
- return -1;
- }
+ /* If we have a SOCKS argument string, consider its size when
+ calculating the buffer size: */
+ if (socks_args_string)
+ buf_size += strlen(socks_args_string);
+ }
- { /* If we are here because we are trying to connect to a
- pluggable transport proxy, check if we have any SOCKS
- arguments to transmit. If we do, compress all arguments to
- a single string in 'socks_args_string': */
+ buf = tor_malloc_zero(buf_size);
- if (conn_get_proxy_type(conn) == PROXY_PLUGGABLE) {
- socks_args_string =
- pt_get_socks_args_for_proxy_addrport(&conn->addr, conn->port);
- if (socks_args_string)
- log_debug(LD_NET, "Sending out '%s' as our SOCKS argument string.",
- socks_args_string);
- }
- }
+ ip4addr = tor_addr_to_ipv4n(&conn->addr);
+ portn = htons(conn->port);
- { /* Figure out the buffer size we need for the SOCKS message: */
+ buf[0] = 4; /* version */
+ buf[1] = SOCKS_COMMAND_CONNECT; /* command */
+ memcpy(buf + 2, &portn, 2); /* port */
+ memcpy(buf + 4, &ip4addr, 4); /* addr */
+
+ /* Next packet field is the userid. If we have pluggable
+ transport SOCKS arguments, we have to embed them
+ there. Otherwise, we use an empty userid. */
+ if (socks_args_string) { /* place the SOCKS args string: */
+ tor_assert(strlen(socks_args_string) > 0);
+ tor_assert(buf_size >=
+ SOCKS4_STANDARD_BUFFER_SIZE + strlen(socks_args_string));
+ strlcpy((char *)buf + 8, socks_args_string, buf_size - 8);
+ tor_free(socks_args_string);
+ } else {
+ buf[8] = 0; /* no userid */
+ }
- buf_size = SOCKS4_STANDARD_BUFFER_SIZE;
+ connection_buf_add((char *)buf, buf_size, conn);
+ tor_free(buf);
- /* If we have a SOCKS argument string, consider its size when
- calculating the buffer size: */
- if (socks_args_string)
- buf_size += strlen(socks_args_string);
- }
+ conn->proxy_state = PROXY_SOCKS4_WANT_CONNECT_OK;
+ return 0;
+}
- buf = tor_malloc_zero(buf_size);
-
- ip4addr = tor_addr_to_ipv4n(&conn->addr);
- portn = htons(conn->port);
-
- buf[0] = 4; /* version */
- buf[1] = SOCKS_COMMAND_CONNECT; /* command */
- memcpy(buf + 2, &portn, 2); /* port */
- memcpy(buf + 4, &ip4addr, 4); /* addr */
-
- /* Next packet field is the userid. If we have pluggable
- transport SOCKS arguments, we have to embed them
- there. Otherwise, we use an empty userid. */
- if (socks_args_string) { /* place the SOCKS args string: */
- tor_assert(strlen(socks_args_string) > 0);
- tor_assert(buf_size >=
- SOCKS4_STANDARD_BUFFER_SIZE + strlen(socks_args_string));
- strlcpy((char *)buf + 8, socks_args_string, buf_size - 8);
- tor_free(socks_args_string);
- } else {
- buf[8] = 0; /* no userid */
- }
+/** Write a proxy request of socks5 to conn for conn->addr:conn->port,
+ * authenticating with the auth details given in the configuration
+ * (if available).
+ *
+ * Returns -1 if conn->addr is incompatible with the proxy protocol, and
+ * 0 otherwise.
+ */
+static int
+connection_socks5_proxy_connect(connection_t *conn)
+{
+ tor_assert(conn);
- connection_buf_add((char *)buf, buf_size, conn);
- tor_free(buf);
+ const or_options_t *options = get_options();
+ unsigned char buf[4]; /* fields: vers, num methods, method list */
- conn->proxy_state = PROXY_SOCKS4_WANT_CONNECT_OK;
- break;
- }
+ /* Send a SOCKS5 greeting (connect request must wait) */
- case PROXY_SOCKS5: {
- unsigned char buf[4]; /* fields: vers, num methods, method list */
+ buf[0] = 5; /* version */
- /* Send a SOCKS5 greeting (connect request must wait) */
+ /* We have to use SOCKS5 authentication, if we have a
+ Socks5ProxyUsername or if we want to pass arguments to our
+ pluggable transport proxy: */
+ if ((options->Socks5ProxyUsername) ||
+ (conn_get_proxy_type(conn) == PROXY_PLUGGABLE &&
+ (get_socks_args_by_bridge_addrport(&conn->addr, conn->port)))) {
+ /* number of auth methods */
+ buf[1] = 2;
+ buf[2] = 0x00; /* no authentication */
+ buf[3] = 0x02; /* rfc1929 Username/Passwd auth */
+ conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929;
+ } else {
+ buf[1] = 1;
+ buf[2] = 0x00; /* no authentication */
+ conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_METHOD_NONE;
+ }
- buf[0] = 5; /* version */
+ connection_buf_add((char *)buf, 2 + buf[1], conn);
+ return 0;
+}
- /* We have to use SOCKS5 authentication, if we have a
- Socks5ProxyUsername or if we want to pass arguments to our
- pluggable transport proxy: */
- if ((options->Socks5ProxyUsername) ||
- (conn_get_proxy_type(conn) == PROXY_PLUGGABLE &&
- (get_socks_args_by_bridge_addrport(&conn->addr, conn->port)))) {
- /* number of auth methods */
- buf[1] = 2;
- buf[2] = 0x00; /* no authentication */
- buf[3] = 0x02; /* rfc1929 Username/Passwd auth */
- conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929;
- } else {
- buf[1] = 1;
- buf[2] = 0x00; /* no authentication */
- conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_METHOD_NONE;
- }
+/** Write a proxy request of haproxy to conn for conn->addr:conn->port.
+ *
+ * Returns -1 if conn->addr is incompatible with the proxy protocol, and
+ * 0 otherwise.
+ */
+static int
+connection_haproxy_proxy_connect(connection_t *conn)
+{
+ int ret = 0;
+ tor_addr_port_t *addr_port = tor_addr_port_new(&conn->addr, conn->port);
+ char *buf = haproxy_format_proxy_header_line(addr_port);
+
+ if (buf == NULL) {
+ ret = -1;
+ goto done;
+ }
+
+ connection_buf_add(buf, strlen(buf), conn);
+ /* In haproxy, we don't have to wait for the response, but we wait for ack.
+ * So we can set the state to be PROXY_HAPROXY_WAIT_FOR_FLUSH. */
+ conn->proxy_state = PROXY_HAPROXY_WAIT_FOR_FLUSH;
+
+ ret = 0;
+ done:
+ tor_free(buf);
+ tor_free(addr_port);
+ return ret;
+}
+
+/** Write a proxy request of <b>type</b> (socks4, socks5, https, haproxy)
+ * to conn for conn->addr:conn->port, authenticating with the auth details
+ * given in the configuration (if available). SOCKS 5 and HTTP CONNECT
+ * proxies support authentication.
+ *
+ * Returns -1 if conn->addr is incompatible with the proxy protocol, and
+ * 0 otherwise.
+ *
+ * Use connection_read_proxy_handshake() to complete the handshake.
+ */
+int
+connection_proxy_connect(connection_t *conn, int type)
+{
+ int ret = 0;
+
+ tor_assert(conn);
- connection_buf_add((char *)buf, 2 + buf[1], conn);
+ switch (type) {
+ case PROXY_CONNECT:
+ ret = connection_https_proxy_connect(conn);
+ break;
+
+ case PROXY_SOCKS4:
+ ret = connection_socks4_proxy_connect(conn);
+ break;
+
+ case PROXY_SOCKS5:
+ ret = connection_socks5_proxy_connect(conn);
+ break;
+
+ case PROXY_HAPROXY:
+ ret = connection_haproxy_proxy_connect(conn);
break;
- }
default:
log_err(LD_BUG, "Invalid proxy protocol, %d", type);
tor_fragile_assert();
- return -1;
+ ret = -1;
+ break;
}
- log_debug(LD_NET, "set state %s",
- connection_proxy_state_to_string(conn->proxy_state));
+ if (ret == 0) {
+ log_debug(LD_NET, "set state %s",
+ connection_proxy_state_to_string(conn->proxy_state));
+ }
- return 0;
+ return ret;
}
/** Read conn's inbuf. If the http response from the proxy is all
@@ -5452,6 +5537,13 @@ get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type,
*port = options->Socks5ProxyPort;
*proxy_type = PROXY_SOCKS5;
return 0;
+ } else if (options->TCPProxy) {
+ tor_addr_copy(addr, &options->TCPProxyAddr);
+ *port = options->TCPProxyPort;
+ /* The only supported protocol in TCPProxy is haproxy. */
+ tor_assert(options->TCPProxyProtocol == TCP_PROXY_PROTOCOL_HAPROXY);
+ *proxy_type = PROXY_HAPROXY;
+ return 0;
}
tor_addr_make_unspec(addr);
@@ -5489,6 +5581,7 @@ proxy_type_to_string(int proxy_type)
case PROXY_CONNECT: return "HTTP";
case PROXY_SOCKS4: return "SOCKS4";
case PROXY_SOCKS5: return "SOCKS5";
+ case PROXY_HAPROXY: return "HAPROXY";
case PROXY_PLUGGABLE: return "pluggable transports SOCKS";
case PROXY_NONE: return "NULL";
default: tor_assert(0);
diff --git a/src/core/mainloop/connection.h b/src/core/mainloop/connection.h
index c93f1ef8e8..2ebb053cca 100644
--- a/src/core/mainloop/connection.h
+++ b/src/core/mainloop/connection.h
@@ -75,8 +75,10 @@ struct buf_t;
#define PROXY_SOCKS5_WANT_AUTH_RFC1929_OK 6
/* We use a SOCKS5 proxy and we just sent our CONNECT command. */
#define PROXY_SOCKS5_WANT_CONNECT_OK 7
+/* We use an HAPROXY proxy and we just sent the proxy header. */
+#define PROXY_HAPROXY_WAIT_FOR_FLUSH 8
/* We use a proxy and we CONNECTed successfully!. */
-#define PROXY_CONNECTED 8
+#define PROXY_CONNECTED 9
/** State for any listener connection. */
#define LISTENER_STATE_READY 0
diff --git a/src/core/or/connection_or.c b/src/core/or/connection_or.c
index 24a4af045e..efd44a5e6d 100644
--- a/src/core/or/connection_or.c
+++ b/src/core/or/connection_or.c
@@ -95,13 +95,6 @@ static unsigned int
connection_or_is_bad_for_new_circs(or_connection_t *or_conn);
static void connection_or_mark_bad_for_new_circs(or_connection_t *or_conn);
-/*
- * Call this when changing connection state, so notifications to the owning
- * channel can be handled.
- */
-
-static void connection_or_change_state(or_connection_t *conn, uint8_t state);
-
static void connection_or_check_canonicity(or_connection_t *conn,
int started_here);
@@ -457,8 +450,8 @@ connection_or_state_publish(const or_connection_t *conn, uint8_t state)
* be notified.
*/
-static void
-connection_or_change_state(or_connection_t *conn, uint8_t state)
+MOCK_IMPL(STATIC void,
+connection_or_change_state,(or_connection_t *conn, uint8_t state))
{
tor_assert(conn);
@@ -726,6 +719,18 @@ connection_or_finished_flushing(or_connection_t *conn)
switch (conn->base_.state) {
case OR_CONN_STATE_PROXY_HANDSHAKING:
+ /* PROXY_HAPROXY gets connected by receiving an ack. */
+ if (conn->proxy_type == PROXY_HAPROXY) {
+ tor_assert(TO_CONN(conn)->proxy_state == PROXY_HAPROXY_WAIT_FOR_FLUSH);
+ TO_CONN(conn)->proxy_state = PROXY_CONNECTED;
+
+ if (connection_tls_start_handshake(conn, 0) < 0) {
+ /* TLS handshaking error of some kind. */
+ connection_or_close_for_error(conn, 0);
+ return -1;
+ }
+ break;
+ }
case OR_CONN_STATE_OPEN:
case OR_CONN_STATE_OR_HANDSHAKING_V2:
case OR_CONN_STATE_OR_HANDSHAKING_V3:
@@ -765,8 +770,9 @@ connection_or_finished_connecting(or_connection_t *or_conn)
return -1;
}
- connection_start_reading(conn);
connection_or_change_state(or_conn, OR_CONN_STATE_PROXY_HANDSHAKING);
+ connection_start_reading(conn);
+
return 0;
}
diff --git a/src/core/or/connection_or.h b/src/core/or/connection_or.h
index 272f536b83..9d414254a6 100644
--- a/src/core/or/connection_or.h
+++ b/src/core/or/connection_or.h
@@ -134,6 +134,13 @@ void connection_or_group_set_badness_(smartlist_t *group, int force);
#ifdef CONNECTION_OR_PRIVATE
STATIC int should_connect_to_relay(const or_connection_t *or_conn);
STATIC void note_or_connect_failed(const or_connection_t *or_conn);
+
+/*
+ * Call this when changing connection state, so notifications to the owning
+ * channel can be handled.
+ */
+MOCK_DECL(STATIC void,connection_or_change_state,
+ (or_connection_t *conn, uint8_t state));
#endif
#ifdef TOR_UNIT_TESTS
diff --git a/src/core/or/or.h b/src/core/or/or.h
index 4d6afc2b0b..3b9955755d 100644
--- a/src/core/or/or.h
+++ b/src/core/or/or.h
@@ -167,12 +167,13 @@ struct curve25519_public_key_t;
#define PROXY_CONNECT 1
#define PROXY_SOCKS4 2
#define PROXY_SOCKS5 3
-/* !!!! If there is ever a PROXY_* type over 3, we must grow the proxy_type
+#define PROXY_HAPROXY 4
+/* !!!! If there is ever a PROXY_* type over 7, we must grow the proxy_type
* field in or_connection_t */
/* Pluggable transport proxy type. Don't use this in or_connection_t,
* instead use the actual underlying proxy type (see above). */
-#define PROXY_PLUGGABLE 4
+#define PROXY_PLUGGABLE 5
/** How many circuits do we want simultaneously in-progress to handle
* a given stream? */
diff --git a/src/core/or/or_connection_st.h b/src/core/or/or_connection_st.h
index c364117a35..a4f21c4c03 100644
--- a/src/core/or/or_connection_st.h
+++ b/src/core/or/or_connection_st.h
@@ -63,7 +63,7 @@ struct or_connection_t {
/** True iff this is an outgoing connection. */
unsigned int is_outgoing:1;
- unsigned int proxy_type:2; /**< One of PROXY_NONE...PROXY_SOCKS5 */
+ unsigned int proxy_type:3; /**< One of PROXY_NONE...PROXY_HAPROXY */
unsigned int wide_circ_ids:1;
/** True iff this connection has had its bootstrap failure logged with
* control_event_bootstrap_problem. */
diff --git a/src/core/proto/.may_include b/src/core/proto/.may_include
index c1647a5cf9..a66c3f83a6 100644
--- a/src/core/proto/.may_include
+++ b/src/core/proto/.may_include
@@ -4,7 +4,11 @@ orconfig.h
lib/crypt_ops/*.h
lib/buf/*.h
+lib/malloc/*.h
+lib/string/*.h
+
+lib/net/address.h
trunnel/*.h
-core/proto/*.h \ No newline at end of file
+core/proto/*.h
diff --git a/src/core/proto/proto_haproxy.c b/src/core/proto/proto_haproxy.c
new file mode 100644
index 0000000000..856f2ab152
--- /dev/null
+++ b/src/core/proto/proto_haproxy.c
@@ -0,0 +1,45 @@
+/* Copyright (c) 2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define PROTO_HAPROXY_PRIVATE
+#include "lib/malloc/malloc.h"
+#include "lib/net/address.h"
+#include "lib/string/printf.h"
+#include "core/proto/proto_haproxy.h"
+
+/** Return a newly allocated PROXY header null-terminated string. Returns NULL
+ * if addr_port->addr is incompatible with the proxy protocol.
+ */
+char *
+haproxy_format_proxy_header_line(const tor_addr_port_t *addr_port)
+{
+ tor_assert(addr_port);
+
+ sa_family_t family = tor_addr_family(&addr_port->addr);
+ const char *family_string = NULL;
+ const char *src_addr_string = NULL;
+
+ switch (family) {
+ case AF_INET:
+ family_string = "TCP4";
+ src_addr_string = "0.0.0.0";
+ break;
+ case AF_INET6:
+ family_string = "TCP6";
+ src_addr_string = "::";
+ break;
+ default:
+ /* Unknown family. */
+ return NULL;
+ }
+
+ char *buf;
+ char addrbuf[TOR_ADDR_BUF_LEN];
+
+ tor_addr_to_str(addrbuf, &addr_port->addr, sizeof(addrbuf), 0);
+
+ tor_asprintf(&buf, "PROXY %s %s %s 0 %d\r\n", family_string, src_addr_string,
+ addrbuf, addr_port->port);
+
+ return buf;
+}
diff --git a/src/core/proto/proto_haproxy.h b/src/core/proto/proto_haproxy.h
new file mode 100644
index 0000000000..fd4240f5dd
--- /dev/null
+++ b/src/core/proto/proto_haproxy.h
@@ -0,0 +1,12 @@
+/* Copyright (c) 2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_PROTO_HAPROXY_H
+#define TOR_PROTO_HAPROXY_H
+
+struct tor_addr_port_t;
+
+char *haproxy_format_proxy_header_line(
+ const struct tor_addr_port_t *addr_port);
+
+#endif /* !defined(TOR_PROTO_HAPROXY_H) */
diff --git a/src/feature/client/transports.c b/src/feature/client/transports.c
index 6537a4b2da..a67cd3583f 100644
--- a/src/feature/client/transports.c
+++ b/src/feature/client/transports.c
@@ -735,6 +735,9 @@ get_pt_proxy_uri(void)
const or_options_t *options = get_options();
char *uri = NULL;
+ /* XXX: Currently TCPProxy is not supported in TOR_PT_PROXY because
+ * there isn't a standard URI scheme for some proxy protocols, such as
+ * haproxy. */
if (options->Socks4Proxy || options->Socks5Proxy || options->HTTPSProxy) {
char addr[TOR_ADDR_BUF_LEN+1];
diff --git a/src/feature/control/btrack_orconn_cevent.c b/src/feature/control/btrack_orconn_cevent.c
index 535aa8f614..d4ba7f79a1 100644
--- a/src/feature/control/btrack_orconn_cevent.c
+++ b/src/feature/control/btrack_orconn_cevent.c
@@ -45,6 +45,7 @@ using_proxy(const bt_orconn_t *bto)
case PROXY_CONNECT:
case PROXY_SOCKS4:
case PROXY_SOCKS5:
+ case PROXY_HAPROXY:
return true;
default:
return false;
diff --git a/src/test/include.am b/src/test/include.am
index 94352c8644..3433e848ea 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -190,6 +190,7 @@ src_test_test_SOURCES += \
src/test/test_process_descs.c \
src/test/test_prob_distr.c \
src/test/test_procmon.c \
+ src/test/test_proto_haproxy.c \
src/test/test_proto_http.c \
src/test/test_proto_misc.c \
src/test/test_protover.c \
diff --git a/src/test/test.c b/src/test/test.c
index 292082d4fb..f0409f0dce 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -739,6 +739,7 @@ struct testgroup_t testgroups[] = {
{ "prob_distr/", prob_distr_tests },
{ "procmon/", procmon_tests },
{ "process/", process_tests },
+ { "proto/haproxy/", proto_haproxy_tests },
{ "proto/http/", proto_http_tests },
{ "proto/misc/", proto_misc_tests },
{ "protover/", protover_tests },
diff --git a/src/test/test.h b/src/test/test.h
index 45c22d70f7..d0728a2d88 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -262,6 +262,7 @@ extern struct testcase_t slow_stochastic_prob_distr_tests[];
extern struct testcase_t procmon_tests[];
extern struct testcase_t process_tests[];
extern struct testcase_t process_descs_tests[];
+extern struct testcase_t proto_haproxy_tests[];
extern struct testcase_t proto_http_tests[];
extern struct testcase_t proto_misc_tests[];
extern struct testcase_t protover_tests[];
diff --git a/src/test/test_config.c b/src/test/test_config.c
index 8f705da7e0..fb6958565c 100644
--- a/src/test/test_config.c
+++ b/src/test/test_config.c
@@ -676,6 +676,52 @@ transport_is_needed_mock(const char *transport_name)
return transport_is_needed_mock_return;
}
+static void
+test_config_parse_tcp_proxy_line(void *arg)
+{
+ (void)arg;
+
+ int ret;
+ char *msg = NULL;
+ or_options_t *options = get_options_mutable();
+
+ /* Bad TCPProxy line - too short. */
+ ret = parse_tcp_proxy_line("haproxy", options, &msg);
+ /* Return error. */
+ tt_int_op(ret, OP_EQ, -1);
+ /* Correct error message. */
+ tt_str_op(msg, OP_EQ, "TCPProxy has no address/port. Please fix.");
+ /* Free error message. */
+ tor_free(msg);
+
+ /* Bad TCPProxy line - unsupported protocol. */
+ ret = parse_tcp_proxy_line("unsupported 95.216.163.36:443", options, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "TCPProxy protocol is not supported. Currently the "
+ "only supported protocol is 'haproxy'. Please fix.");
+ tor_free(msg);
+
+ /* Bad TCPProxy line - unparsable address/port. */
+ ret = parse_tcp_proxy_line("haproxy 95.216.163.36/443", options, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "TCPProxy address/port failed to parse or resolve. "
+ "Please fix.");
+ tor_free(msg);
+
+ /* Good TCPProxy line - ipv4. */
+ ret = parse_tcp_proxy_line("haproxy 95.216.163.36:443", options, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+ tt_int_op(options->TCPProxyProtocol, OP_EQ, TCP_PROXY_PROTOCOL_HAPROXY);
+ /* Correct the address. */
+ tt_assert(tor_addr_eq_ipv4h(&options->TCPProxyAddr, 0x5fd8a324));
+ tt_int_op(options->TCPProxyPort, OP_EQ, 443);
+ tor_free(msg);
+
+ done:
+ ;
+}
+
/**
* Test parsing for the ClientTransportPlugin and ServerTransportPlugin config
* options.
@@ -6129,6 +6175,7 @@ struct testcase_t config_tests[] = {
CONFIG_TEST(parse_bridge_line, 0),
CONFIG_TEST(parse_transport_options_line, 0),
CONFIG_TEST(parse_transport_plugin_line, TT_FORK),
+ CONFIG_TEST(parse_tcp_proxy_line, TT_FORK),
CONFIG_TEST(check_or_create_data_subdir, TT_FORK),
CONFIG_TEST(write_to_data_subdir, TT_FORK),
CONFIG_TEST(fix_my_family, 0),
diff --git a/src/test/test_connection.c b/src/test/test_connection.c
index 0d28276702..7369981284 100644
--- a/src/test/test_connection.c
+++ b/src/test/test_connection.c
@@ -10,6 +10,7 @@
#include "core/or/or.h"
#include "test/test.h"
+#include "app/config/or_options_st.h"
#include "core/mainloop/connection.h"
#include "core/or/connection_edge.h"
#include "feature/hs/hs_common.h"
@@ -312,6 +313,25 @@ test_conn_download_status_teardown(const struct testcase_t *tc, void *arg)
return rv;
}
+static void *
+test_conn_proxy_connect_setup(const struct testcase_t *tc)
+{
+ return test_conn_get_proxy_or_connection(*(unsigned int *)tc->setup_data);
+}
+
+static int
+test_conn_proxy_connect_teardown(const struct testcase_t *tc, void *arg)
+{
+ (void)tc;
+ or_connection_t *conn = arg;
+
+ tt_assert(conn);
+ assert_connection_ok(&conn->base_, time(NULL));
+
+ done:
+ return 1;
+}
+
/* Like connection_ap_make_link(), but does much less */
static connection_t *
test_conn_get_linked_connection(connection_t *l_conn, uint8_t state)
@@ -360,6 +380,10 @@ static struct testcase_setup_t test_conn_download_status_st = {
test_conn_download_status_setup, test_conn_download_status_teardown
};
+static struct testcase_setup_t test_conn_proxy_connect_st = {
+ test_conn_proxy_connect_setup, test_conn_proxy_connect_teardown
+};
+
static void
test_conn_get_basic(void *arg)
{
@@ -788,6 +812,64 @@ test_conn_download_status(void *arg)
/* the teardown function removes all the connections in the global list*/;
}
+static void
+test_conn_https_proxy_connect(void *arg)
+{
+ size_t sz;
+ char *buf = NULL;
+ or_connection_t *conn = arg;
+
+ MOCK(connection_or_change_state, mock_connection_or_change_state);
+
+ tt_int_op(conn->base_.proxy_state, OP_EQ, PROXY_HTTPS_WANT_CONNECT_OK);
+
+ buf = buf_get_contents(conn->base_.outbuf, &sz);
+ tt_str_op(buf, OP_EQ, "CONNECT 127.0.0.1:12345 HTTP/1.0\r\n\r\n");
+
+ done:
+ UNMOCK(connection_or_change_state);
+ tor_free(buf);
+}
+
+static int handshake_start_called = 0;
+
+static int
+handshake_start(or_connection_t *conn, int receiving)
+{
+ (void)receiving;
+
+ tor_assert(conn);
+
+ handshake_start_called = 1;
+ return 0;
+}
+
+static void
+test_conn_haproxy_proxy_connect(void *arg)
+{
+ size_t sz;
+ char *buf = NULL;
+ or_connection_t *conn = arg;
+
+ MOCK(connection_or_change_state, mock_connection_or_change_state);
+ MOCK(connection_tls_start_handshake, handshake_start);
+
+ tt_int_op(conn->base_.proxy_state, OP_EQ, PROXY_HAPROXY_WAIT_FOR_FLUSH);
+
+ buf = buf_get_contents(conn->base_.outbuf, &sz);
+ tt_str_op(buf, OP_EQ, "PROXY TCP4 0.0.0.0 127.0.0.1 0 12345\r\n");
+
+ connection_or_finished_flushing(conn);
+
+ tt_int_op(conn->base_.proxy_state, OP_EQ, PROXY_CONNECTED);
+ tt_int_op(handshake_start_called, OP_EQ, 1);
+
+ done:
+ UNMOCK(connection_or_change_state);
+ UNMOCK(connection_tls_start_handshake);
+ tor_free(buf);
+}
+
static node_t test_node;
static node_t *
@@ -890,14 +972,24 @@ test_failed_orconn_tracker(void *arg)
{ #name "_" #arg, test_conn_##name, fork, &setup, (void *)arg }
#endif /* !defined(COCCI) */
+static const unsigned int PROXY_CONNECT_ARG = PROXY_CONNECT;
+static const unsigned int PROXY_HAPROXY_ARG = PROXY_HAPROXY;
+
struct testcase_t connection_tests[] = {
CONNECTION_TESTCASE(get_basic, TT_FORK, test_conn_get_basic_st),
CONNECTION_TESTCASE(get_rend, TT_FORK, test_conn_get_rend_st),
CONNECTION_TESTCASE(get_rsrc, TT_FORK, test_conn_get_rsrc_st),
- CONNECTION_TESTCASE_ARG(download_status, TT_FORK,
+
+ CONNECTION_TESTCASE_ARG(download_status, TT_FORK,
test_conn_download_status_st, FLAV_MICRODESC),
- CONNECTION_TESTCASE_ARG(download_status, TT_FORK,
+ CONNECTION_TESTCASE_ARG(download_status, TT_FORK,
test_conn_download_status_st, FLAV_NS),
+
+ CONNECTION_TESTCASE_ARG(https_proxy_connect, TT_FORK,
+ test_conn_proxy_connect_st, &PROXY_CONNECT_ARG),
+ CONNECTION_TESTCASE_ARG(haproxy_proxy_connect, TT_FORK,
+ test_conn_proxy_connect_st, &PROXY_HAPROXY_ARG),
+
//CONNECTION_TESTCASE(func_suffix, TT_FORK, setup_func_pair),
{ "failed_orconn_tracker", test_failed_orconn_tracker, TT_FORK, NULL, NULL },
END_OF_TESTCASES
diff --git a/src/test/test_connection.h b/src/test/test_connection.h
index 40121e6d38..9efe31ebc6 100644
--- a/src/test/test_connection.h
+++ b/src/test/test_connection.h
@@ -7,6 +7,7 @@
/** Some constants used by test_connection and helpers */
#define TEST_CONN_FAMILY (AF_INET)
#define TEST_CONN_ADDRESS "127.0.0.1"
+#define TEST_CONN_ADDRESS_2 "127.0.0.2"
#define TEST_CONN_PORT (12345)
#define TEST_CONN_ADDRESS_PORT "127.0.0.1:12345"
#define TEST_CONN_FD_INIT 50
diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c
index 29743a0d15..31bf1fcc2a 100644
--- a/src/test/test_helpers.c
+++ b/src/test/test_helpers.c
@@ -9,6 +9,7 @@
#define ROUTERLIST_PRIVATE
#define CONFIG_PRIVATE
#define CONNECTION_PRIVATE
+#define CONNECTION_OR_PRIVATE
#define MAINLOOP_PRIVATE
#include "orconfig.h"
@@ -19,6 +20,7 @@
#include "lib/confmgt/confmgt.h"
#include "app/main/subsysmgr.h"
#include "core/mainloop/connection.h"
+#include "core/or/connection_or.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "core/mainloop/mainloop.h"
#include "feature/nodelist/nodelist.h"
@@ -33,6 +35,7 @@
#include "core/or/cell_st.h"
#include "core/or/connection_st.h"
+#include "core/or/or_connection_st.h"
#include "feature/nodelist/node_st.h"
#include "core/or/origin_circuit_st.h"
#include "feature/nodelist/routerlist_st.h"
@@ -194,6 +197,14 @@ fake_close_socket(tor_socket_t sock)
return 0;
}
+/* Helper for test_conn_get_proxy_or_connection() */
+void
+mock_connection_or_change_state(or_connection_t *conn, uint8_t state)
+{
+ tor_assert(conn);
+ conn->base_.state = state;
+}
+
static int mock_connection_connect_sockaddr_called = 0;
static int fake_socket_number = TEST_CONN_FD_INIT;
@@ -228,6 +239,76 @@ mock_connection_connect_sockaddr(connection_t *conn,
return 1;
}
+or_connection_t *
+test_conn_get_proxy_or_connection(unsigned int proxy_type)
+{
+ or_connection_t *conn = NULL;
+ tor_addr_t dst_addr;
+ tor_addr_t proxy_addr;
+ int socket_err = 0;
+ int in_progress = 0;
+
+ MOCK(connection_connect_sockaddr,
+ mock_connection_connect_sockaddr);
+ MOCK(connection_write_to_buf_impl_,
+ connection_write_to_buf_mock);
+ MOCK(connection_or_change_state,
+ mock_connection_or_change_state);
+ MOCK(tor_close_socket, fake_close_socket);
+
+ tor_init_connection_lists();
+
+ conn = or_connection_new(CONN_TYPE_OR, TEST_CONN_FAMILY);
+ tt_assert(conn);
+
+ /* Set up a destination address. */
+ test_conn_lookup_addr_helper(TEST_CONN_ADDRESS, TEST_CONN_FAMILY,
+ &dst_addr);
+ tt_assert(!tor_addr_is_null(&dst_addr));
+
+ conn->proxy_type = proxy_type;
+ conn->base_.proxy_state = PROXY_INFANT;
+
+ tor_addr_copy_tight(&conn->base_.addr, &dst_addr);
+ conn->base_.address = tor_addr_to_str_dup(&dst_addr);
+ conn->base_.port = TEST_CONN_PORT;
+
+ /* Set up a proxy address. */
+ test_conn_lookup_addr_helper(TEST_CONN_ADDRESS_2, TEST_CONN_FAMILY,
+ &proxy_addr);
+ tt_assert(!tor_addr_is_null(&proxy_addr));
+
+ conn->base_.state = OR_CONN_STATE_CONNECTING;
+
+ mock_connection_connect_sockaddr_called = 0;
+ in_progress = connection_connect(TO_CONN(conn), TEST_CONN_ADDRESS_PORT,
+ &proxy_addr, TEST_CONN_PORT, &socket_err);
+ tt_int_op(mock_connection_connect_sockaddr_called, OP_EQ, 1);
+ tt_assert(!socket_err);
+ tt_assert(in_progress == 0 || in_progress == 1);
+
+ assert_connection_ok(TO_CONN(conn), time(NULL));
+
+ in_progress = connection_or_finished_connecting(conn);
+ tt_int_op(in_progress, OP_EQ, 0);
+
+ assert_connection_ok(TO_CONN(conn), time(NULL));
+
+ UNMOCK(connection_connect_sockaddr);
+ UNMOCK(connection_write_to_buf_impl_);
+ UNMOCK(connection_or_change_state);
+ UNMOCK(tor_close_socket);
+ return conn;
+
+ /* On failure */
+ done:
+ UNMOCK(connection_connect_sockaddr);
+ UNMOCK(connection_write_to_buf_impl_);
+ UNMOCK(connection_or_change_state);
+ UNMOCK(tor_close_socket);
+ return NULL;
+}
+
/** Create and return a new connection/stream */
connection_t *
test_conn_get_connection(uint8_t state, uint8_t type, uint8_t purpose)
diff --git a/src/test/test_helpers.h b/src/test/test_helpers.h
index d82072bb34..d5a5417983 100644
--- a/src/test/test_helpers.h
+++ b/src/test/test_helpers.h
@@ -26,6 +26,9 @@ char *buf_get_contents(buf_t *buf, size_t *sz_out);
int mock_tor_addr_lookup__fail_on_bad_addrs(const char *name,
uint16_t family, tor_addr_t *out);
+void mock_connection_or_change_state(or_connection_t *conn, uint8_t state);
+
+or_connection_t *test_conn_get_proxy_or_connection(unsigned int proxy_type);
connection_t *test_conn_get_connection(uint8_t state,
uint8_t type, uint8_t purpose);
or_options_t *helper_parse_options(const char *conf);
diff --git a/src/test/test_options.c b/src/test/test_options.c
index b99ae78932..ce05eb01f7 100644
--- a/src/test/test_options.c
+++ b/src/test/test_options.c
@@ -2807,7 +2807,7 @@ test_options_validate__proxy(void *ignored)
ret = options_validate(NULL, tdata->opt, &msg);
tt_int_op(ret, OP_EQ, -1);
tt_str_op(msg, OP_EQ, "You have configured more than one proxy type. "
- "(Socks4Proxy|Socks5Proxy|HTTPSProxy)");
+ "(Socks4Proxy|Socks5Proxy|HTTPSProxy|TCPProxy)");
tor_free(msg);
free_options_test_data(tdata);
@@ -2815,9 +2815,10 @@ test_options_validate__proxy(void *ignored)
mock_clean_saved_logs();
ret = options_validate(NULL, tdata->opt, &msg);
tt_int_op(ret, OP_EQ, 0);
- expect_log_msg("HTTPProxy configured, but no SOCKS "
- "proxy or HTTPS proxy configured. Watch out: this configuration "
- "will proxy unencrypted directory connections only.\n");
+ expect_log_msg("HTTPProxy configured, but no SOCKS proxy, "
+ "HTTPS proxy, or any other TCP proxy configured. Watch out: "
+ "this configuration will proxy unencrypted directory "
+ "connections only.\n");
tor_free(msg);
free_options_test_data(tdata);
diff --git a/src/test/test_proto_haproxy.c b/src/test/test_proto_haproxy.c
new file mode 100644
index 0000000000..653bf67e23
--- /dev/null
+++ b/src/test/test_proto_haproxy.c
@@ -0,0 +1,66 @@
+/* Copyright (c) 2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_proto_haproxy.c
+ * \brief Tests for our HAProxy protocol parser code
+ */
+
+#define PROTO_HAPROXY_PRIVATE
+
+#include "test/test.h"
+#include "core/proto/proto_haproxy.h"
+#include "test/log_test_helpers.h"
+
+static void
+test_format_proxy_header_line(void *arg)
+{
+ tor_addr_t addr;
+ tor_addr_port_t *addr_port = NULL;
+ char *output = NULL;
+
+ (void) arg;
+
+ /* IPv4 address. */
+ tor_addr_parse(&addr, "192.168.1.2");
+ addr_port = tor_addr_port_new(&addr, 8000);
+ output = haproxy_format_proxy_header_line(addr_port);
+
+ tt_str_op(output, OP_EQ, "PROXY TCP4 0.0.0.0 192.168.1.2 0 8000\r\n");
+
+ tor_free(addr_port);
+ tor_free(output);
+
+ /* IPv6 address. */
+ tor_addr_parse(&addr, "123:45:6789::5005:11");
+ addr_port = tor_addr_port_new(&addr, 8000);
+ output = haproxy_format_proxy_header_line(addr_port);
+
+ tt_str_op(output, OP_EQ, "PROXY TCP6 :: 123:45:6789::5005:11 0 8000\r\n");
+
+ tor_free(addr_port);
+ tor_free(output);
+
+ /* UNIX socket address. */
+ memset(&addr, 0, sizeof(addr));
+ addr.family = AF_UNIX;
+ addr_port = tor_addr_port_new(&addr, 8000);
+ output = haproxy_format_proxy_header_line(addr_port);
+
+ /* If it's not an IPv4 or IPv6 address, haproxy_format_proxy_header_line
+ * must return NULL. */
+ tt_ptr_op(output, OP_EQ, NULL);
+
+ tor_free(addr_port);
+ tor_free(output);
+
+ done:
+ tor_free(addr_port);
+ tor_free(output);
+}
+
+struct testcase_t proto_haproxy_tests[] = {
+ { "format_proxy_header_line", test_format_proxy_header_line, 0, NULL, NULL },
+
+ END_OF_TESTCASES
+};