Merge branch 'haxxpop/tcp_proxy_squashed' into tcp_proxy_squshed_and_merged

This commit is contained in:
Nick Mathewson 2020-01-06 13:41:20 -05:00
commit 1b63eea66c
27 changed files with 742 additions and 164 deletions

6
changes/ticket31518 Normal file
View File

@ -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).

View File

@ -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)

View File

@ -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;
}

View File

@ -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);

View File

@ -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. */

View File

@ -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 \

View File

@ -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,10 +2332,202 @@ 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.
*/
static int
connection_https_proxy_connect(connection_t *conn)
{
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);
unsigned char *buf;
uint16_t portn;
uint32_t ip4addr;
size_t buf_size = 0;
char *socks_args_string = NULL;
/* Send a SOCKS4 connect request */
if (tor_addr_family(&conn->addr) != AF_INET) {
log_warn(LD_NET, "SOCKS4 client is incompatible with IPv6");
return -1;
}
{ /* 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);
}
}
{ /* Figure out the buffer size we need for the SOCKS message: */
buf_size = SOCKS4_STANDARD_BUFFER_SIZE;
/* 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);
}
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 */
}
connection_buf_add((char *)buf, buf_size, conn);
tor_free(buf);
conn->proxy_state = PROXY_SOCKS4_WANT_CONNECT_OK;
return 0;
}
/** 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);
const or_options_t *options = get_options();
unsigned char buf[4]; /* fields: vers, num methods, method list */
/* Send a SOCKS5 greeting (connect request must wait) */
buf[0] = 5; /* version */
/* 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;
}
connection_buf_add((char *)buf, 2 + buf[1], conn);
return 0;
}
/** 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.
@ -2340,152 +2537,40 @@ conn_get_proxy_type(const connection_t *conn)
int
connection_proxy_connect(connection_t *conn, int type)
{
const or_options_t *options;
int ret = 0;
tor_assert(conn);
options = get_options();
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");
}
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;
case PROXY_CONNECT:
ret = connection_https_proxy_connect(conn);
break;
}
case PROXY_SOCKS4: {
unsigned char *buf;
uint16_t portn;
uint32_t ip4addr;
size_t buf_size = 0;
char *socks_args_string = NULL;
/* Send a SOCKS4 connect request */
if (tor_addr_family(&conn->addr) != AF_INET) {
log_warn(LD_NET, "SOCKS4 client is incompatible with IPv6");
return -1;
}
{ /* 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);
}
}
{ /* Figure out the buffer size we need for the SOCKS message: */
buf_size = SOCKS4_STANDARD_BUFFER_SIZE;
/* 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);
}
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 */
}
connection_buf_add((char *)buf, buf_size, conn);
tor_free(buf);
conn->proxy_state = PROXY_SOCKS4_WANT_CONNECT_OK;
case PROXY_SOCKS4:
ret = connection_socks4_proxy_connect(conn);
break;
}
case PROXY_SOCKS5: {
unsigned char buf[4]; /* fields: vers, num methods, method list */
/* Send a SOCKS5 greeting (connect request must wait) */
buf[0] = 5; /* version */
/* 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;
}
connection_buf_add((char *)buf, 2 + buf[1], conn);
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);

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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? */

View File

@ -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. */

View File

@ -4,6 +4,10 @@ orconfig.h
lib/crypt_ops/*.h
lib/buf/*.h
lib/malloc/*.h
lib/string/*.h
lib/net/address.h
trunnel/*.h

View File

@ -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;
}

View File

@ -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) */

View File

@ -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];

View File

@ -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;

View File

@ -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 \

View File

@ -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 },

View File

@ -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[];

View File

@ -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),

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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
};