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;