diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 97fdb4813f..8245ff4199 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -472,8 +472,10 @@ GENERAL OPTIONS **OutboundBindAddress** __IP__:: Make all outbound connections originate from the IP address specified. This is only useful when you have multiple network interfaces, and you want all - of Tor's outgoing connections to use a single one. This setting will be - ignored for connections to the loopback addresses (127.0.0.0/8 and ::1). + of Tor's outgoing connections to use a single one. This option may + be used twice, once with an IPv4 address and once with an IPv6 address. + This setting will be ignored for connections to the loopback addresses + (127.0.0.0/8 and ::1). **PidFile** __FILE__:: On startup, write our PID to FILE. On clean shutdown, remove diff --git a/src/or/config.c b/src/or/config.c index affe2388bf..05c702893d 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -302,7 +302,7 @@ static config_var_t _option_vars[] = { V(NumEntryGuards, UINT, "3"), V(ORListenAddress, LINELIST, NULL), VPORT(ORPort, LINELIST, NULL), - V(OutboundBindAddress, STRING, NULL), + V(OutboundBindAddress, LINELIST, NULL), V(PathBiasCircThreshold, INT, "-1"), V(PathBiasNoticeRate, DOUBLE, "-1"), @@ -474,6 +474,8 @@ static int options_init_logs(or_options_t *options, int validate_only); static void init_libevent(const or_options_t *options); static int opt_streq(const char *s1, const char *s2); +static int parse_outbound_addresses(or_options_t *options, int validate_only, + char **msg); /** Magic value for or_options_t. */ #define OR_OPTIONS_MAGIC 9090909 @@ -1390,6 +1392,12 @@ options_act(const or_options_t *old_options) tor_free(http_authenticator); } + if (parse_outbound_addresses(options, 0, &msg) < 0) { + log_warn(LD_BUG, "Failed parsing oubound bind addresses: %s", msg); + tor_free(msg); + return -1; + } + /* Check for transitions that need action. */ if (old_options) { int revise_trackexithosts = 0; @@ -2164,6 +2172,9 @@ options_validate(or_options_t *old_options, or_options_t *options, if (parse_ports(options, 1, msg, &n_ports) < 0) return -1; + if (parse_outbound_addresses(options, 1, msg) < 0) + return -1; + if (validate_data_directory(options)<0) REJECT("Invalid DataDirectory"); @@ -5482,3 +5493,57 @@ getinfo_helper_config(control_connection_t *conn, return 0; } +/** Parse outbound bind address option lines. If validate_only + * is not 0 update _OutboundBindAddressIPv4 and + * _OutboundBindAddressIPv6 in options. On failure, set + * msg (if provided) to a newly allocated string containing a + * description of the problem and return -1. */ +static int +parse_outbound_addresses(or_options_t *options, int validate_only, char **msg) +{ + const config_line_t *lines = options->OutboundBindAddress; + int found_v4 = 0, found_v6 = 0; + + if (!validate_only) { + memset(&options->_OutboundBindAddressIPv4, 0, + sizeof(options->_OutboundBindAddressIPv4)); + memset(&options->_OutboundBindAddressIPv6, 0, + sizeof(options->_OutboundBindAddressIPv6)); + } + while (lines) { + tor_addr_t addr, *dst_addr = NULL; + int af = tor_addr_parse(&addr, lines->value); + switch (af) { + case AF_INET: + if (found_v4) { + if (msg) + tor_asprintf(msg, "Multiple IPv4 outbound bind addresses " + "configured: %s", lines->value); + return -1; + } + found_v4 = 1; + dst_addr = &options->_OutboundBindAddressIPv4; + break; + case AF_INET6: + if (found_v6) { + if (msg) + tor_asprintf(msg, "Multiple IPv6 outbound bind addresses " + "configured: %s", lines->value); + return -1; + } + found_v6 = 1; + dst_addr = &options->_OutboundBindAddressIPv6; + break; + default: + if (msg) + tor_asprintf(msg, "Outbound bind address '%s' didn't parse.", + lines->value); + return -1; + } + if (!validate_only) + tor_addr_copy(dst_addr, &addr); + lines = lines->next; + } + return 0; +} + diff --git a/src/or/connection.c b/src/or/connection.c index d8f5d875c8..d64c676bfb 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1377,23 +1377,34 @@ connection_connect(connection_t *conn, const char *address, make_socket_reuseable(s); - if (options->OutboundBindAddress && !tor_addr_is_loopback(addr)) { - struct sockaddr_in ext_addr; - - memset(&ext_addr, 0, sizeof(ext_addr)); - ext_addr.sin_family = AF_INET; - ext_addr.sin_port = 0; - if (!tor_inet_aton(options->OutboundBindAddress, &ext_addr.sin_addr)) { - log_warn(LD_CONFIG,"Outbound bind address '%s' didn't parse. Ignoring.", - options->OutboundBindAddress); - } else { - if (bind(s, (struct sockaddr*)&ext_addr, - (socklen_t)sizeof(ext_addr)) < 0) { - *socket_error = tor_socket_errno(s); - log_warn(LD_NET,"Error binding network socket: %s", - tor_socket_strerror(*socket_error)); - tor_close_socket(s); - return -1; + if (!tor_addr_is_loopback(addr)) { + const tor_addr_t *ext_addr = NULL; + if (protocol_family == AF_INET && + !tor_addr_is_null(&options->_OutboundBindAddressIPv4)) + ext_addr = &options->_OutboundBindAddressIPv4; + else if (protocol_family == AF_INET6 && + !tor_addr_is_null(&options->_OutboundBindAddressIPv6)) + ext_addr = &options->_OutboundBindAddressIPv6; + if (ext_addr) { + struct sockaddr_storage ext_addr_sa; + socklen_t ext_addr_len = 0; + memset(&ext_addr_sa, 0, sizeof(ext_addr_sa)); + ext_addr_len = tor_addr_to_sockaddr(ext_addr, 0, + (struct sockaddr *) &ext_addr_sa, + sizeof(ext_addr_sa)); + if (ext_addr_len == 0) { + log_warn(LD_NET, + "Error converting OutboundBindAddress %s into sockaddr. " + "Ignoring.", fmt_addr(ext_addr)); + } else { + if (bind(s, (struct sockaddr *) &ext_addr_sa, ext_addr_len) < 0) { + *socket_error = tor_socket_errno(s); + log_warn(LD_NET,"Error binding network socket to %s: %s", + fmt_addr(ext_addr), + tor_socket_strerror(*socket_error)); + tor_close_socket(s); + return -1; + } } } } diff --git a/src/or/or.h b/src/or/or.h index f7914b830d..577cf6dab8 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -3028,7 +3028,11 @@ typedef struct { /** Addresses to bind for listening for control connections. */ config_line_t *ControlListenAddress; /** Local address to bind outbound sockets */ - char *OutboundBindAddress; + config_line_t *OutboundBindAddress; + /** IPv4 address derived from OutboundBindAddress. */ + tor_addr_t _OutboundBindAddressIPv4; + /** IPv6 address derived from OutboundBindAddress. */ + tor_addr_t _OutboundBindAddressIPv6; /** Directory server only: which versions of * Tor should we tell users to run? */ config_line_t *RecommendedVersions;