diff options
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 +}; |