Ticket #17975: patch.txt

File patch.txt, 16.6 KB (added by michaelsonntag, 8 months ago)

Patch suggestion (v2)

Line 
1diff --git a/tor-0.2.8.8_new/change_separate_exit_and_relay.txt b/tor-0.2.8.8_new/change_separate_exit_and_relay.txt
2new file mode 100755
3index 0000000..1ae374a
4--- /dev/null
5+++ b/tor-0.2.8.8_new/change_separate_exit_and_relay.txt
6@@ -0,0 +1,2 @@
7+- Minor features:
8+    - Allow separation of exit and relay traffic to different source IP addresses (Ticket #17975). Written by Michael Sonntag.
9\ No newline at end of file
10diff --git a/tor-0.2.8.8/doc/tor.1.txt b/tor-0.2.8.8_new/doc/tor.1.txt
11index 74915b7..1bde9a0 100755
12--- a/tor-0.2.8.8/doc/tor.1.txt
13+++ b/tor-0.2.8.8_new/doc/tor.1.txt
14@@ -603,6 +603,21 @@ GENERAL OPTIONS
15     This setting will be ignored for connections to the loopback addresses
16     (127.0.0.0/8 and ::1).
17 
18+[[OutboundBindAddressOR]] **OutboundBindAddressOR** __IP__::
19+    Make all outbound non-exit (=relay and other) connections originate from the IP
20+    address specified. This option cannot be used together with
21+    **OutboundBindAddress**, unless they specify a different protocol. This option
22+    may be used twice, once with an IPv4 address and once with an IPv6 address. This
23+    setting will be ignored for connections to the loopback addresses
24+    (127.0.0.0/8 and ::1).
25+
26+[[OutboundBindAddressExit]] **OutboundBindAddressExit** __IP__::
27+    Make all outbound exit connections originate from the IP address specified. This
28+    option cannot be used together with **OutboundBindAddress**, unless they specify
29+    a different protocol. This option may be used twice, once with an IPv4 address
30+    and once with an IPv6 address. This setting will be ignored for connections to
31+    the loopback addresses (127.0.0.0/8 and ::1).
32+
33 [[PidFile]] **PidFile** __FILE__::
34     On startup, write our PID to FILE. On clean shutdown, remove
35     FILE.
36diff --git a/tor-0.2.8.8/src/config/torrc.sample.in b/tor-0.2.8.8_new/src/config/torrc.sample.in
37index 248cb5c..d8cedeb 100755
38--- a/tor-0.2.8.8/src/config/torrc.sample.in
39+++ b/tor-0.2.8.8_new/src/config/torrc.sample.in
40@@ -95,7 +95,12 @@
41 
42 ## If you have multiple network interfaces, you can specify one for
43 ## outgoing traffic to use.
44-# OutboundBindAddress 10.0.0.5
45+## OutboundBindAddressExit will be used for all exit traffic, while
46+## OutboundBindAddressOR will be used for all other connections.
47+## If you do not wish to differentiate, use OutboundBindAddress to
48+## specify the same address for both in a single line.
49+#OutboundBindAddressExit 10.0.0.4
50+#OutboundBindAddressOR 10.0.0.5
51 
52 ## A handle for your relay, so people don't have to refer to it by key.
53 #Nickname ididnteditheconfig
54diff --git a/tor-0.2.8.8/src/or/config.c b/tor-0.2.8.8_new/src/or/config.c
55index 4b065a0..1bb6e34 100755
56--- a/tor-0.2.8.8/src/or/config.c
57+++ b/tor-0.2.8.8_new/src/or/config.c
58@@ -345,6 +345,8 @@ static config_var_t option_vars_[] = {
59   V(ORListenAddress,             LINELIST, NULL),
60   VPORT(ORPort,                      LINELIST, NULL),
61   V(OutboundBindAddress,         LINELIST,   NULL),
62+  V(OutboundBindAddressOR,       LINELIST,   NULL),
63+  V(OutboundBindAddressExit,     LINELIST,   NULL),
64 
65   OBSOLETE("PathBiasDisableRate"),
66   V(PathBiasCircThreshold,       INT,      "-1"),
67@@ -7481,55 +7483,105 @@ getinfo_helper_config(control_connection_t *conn,
68   return 0;
69 }
70 
71+/* Check whether an address has already been set against a list of
72+ * storage locations. Any existing address in any location will lead
73+ * to a fail, even if it is the same value. If not set and not only
74+ * validating, copy it into all those locations. destinationAddresses
75+ * is an array of addresses (of various adress types) which are
76+ * conflicting; for each IPv4 and IPv6 (or any other types) can be
77+ * set separately. Returns 0 on success or -1 if one of the
78+ * addresses is already set.
79+ */
80+static int
81+verify_and_store_address(const int af, tor_addr_t *addr,
82+           tor_addr_t *destinationAddresses[][AF_MAX], const int adrCount,
83+           const int validate_only)
84+{
85+  int found=0;
86+  for (int i=0;i<adrCount && !found;i++) {
87+    found = !tor_addr_is_null(destinationAddresses[i][af]);
88+  }
89+  if (found) {
90+    return -1;
91+  }
92+  if (!validate_only) {
93+    for (int i=0;i<adrCount;i++) {
94+      tor_addr_copy(destinationAddresses[i][af], addr);
95+    }
96+  }
97+  return 0;
98+}
99+
100 /** Parse outbound bind address option lines. If <b>validate_only</b>
101- * is not 0 update OutboundBindAddressIPv4_ and
102- * OutboundBindAddressIPv6_ in <b>options</b>. On failure, set
103- * <b>msg</b> (if provided) to a newly allocated string containing a
104- * description of the problem and return -1. */
105+ * is not 0 update OutboundBindAddressORIPv4_, OutboundBindAddressORIPv6_,
106+ * OutboundBindAddressExitIPv4_, and OutboundBindAddressExitIPv6_,
107+ * in <b>options</b>.
108+ * Only one address can be set for any of these four values.
109+ * OutBoundBindAddress will set both (OR and Exit), the OR/Exit version
110+ * only the specific one. On failure, set <b>msg</b> (if provided) to a
111+ * newly allocated string containing a description of the problem and
112+ * return -1. */
113 static int
114 parse_outbound_addresses(or_options_t *options, int validate_only, char **msg)
115 {
116   const config_line_t *lines = options->OutboundBindAddress;
117-  int found_v4 = 0, found_v6 = 0;
118-
119   if (!validate_only) {
120-    memset(&options->OutboundBindAddressIPv4_, 0,
121-           sizeof(options->OutboundBindAddressIPv4_));
122-    memset(&options->OutboundBindAddressIPv6_, 0,
123-           sizeof(options->OutboundBindAddressIPv6_));
124+    memset(&options->OutboundBindAddressORIPv4_, 0,
125+           sizeof(options->OutboundBindAddressORIPv4_));
126+    memset(&options->OutboundBindAddressORIPv6_, 0,
127+           sizeof(options->OutboundBindAddressORIPv6_));
128+    memset(&options->OutboundBindAddressExitIPv4_, 0,
129+           sizeof(options->OutboundBindAddressExitIPv4_));
130+    memset(&options->OutboundBindAddressExitIPv6_, 0,
131+           sizeof(options->OutboundBindAddressExitIPv6_));
132   }
133+  tor_addr_t addr;
134+  tor_addr_t *addresses[2][AF_MAX] = {{0}};
135+  int af;
136+  addresses[0][AF_INET] = &options->OutboundBindAddressORIPv4_;
137+  addresses[0][AF_INET6] = &options->OutboundBindAddressORIPv6_;
138+  addresses[1][AF_INET] = &options->OutboundBindAddressExitIPv4_;
139+  addresses[1][AF_INET6] = &options->OutboundBindAddressExitIPv6_;
140   while (lines) {
141-    tor_addr_t addr, *dst_addr = NULL;
142-    int af = tor_addr_parse(&addr, lines->value);
143-    switch (af) {
144-    case AF_INET:
145-      if (found_v4) {
146-        if (msg)
147-          tor_asprintf(msg, "Multiple IPv4 outbound bind addresses "
148-                       "configured: %s", lines->value);
149-        return -1;
150-      }
151-      found_v4 = 1;
152-      dst_addr = &options->OutboundBindAddressIPv4_;
153-      break;
154-    case AF_INET6:
155-      if (found_v6) {
156-        if (msg)
157-          tor_asprintf(msg, "Multiple IPv6 outbound bind addresses "
158-                       "configured: %s", lines->value);
159-        return -1;
160-      }
161-      found_v6 = 1;
162-      dst_addr = &options->OutboundBindAddressIPv6_;
163-      break;
164-    default:
165+    af = tor_addr_parse(&addr, lines->value);
166+    if (verify_and_store_address(af, &addr, addresses, 2, validate_only)) {
167       if (msg)
168-        tor_asprintf(msg, "Outbound bind address '%s' didn't parse.",
169-                     lines->value);
170+        tor_asprintf(msg, "Multiple%s outbound bind addresses "
171+                     "configured: %s", af==AF_INET?" IPv4":
172+                     (af==AF_INET6?" IPv6":""), lines->value);
173+      return -1;
174+    }
175+    lines = lines->next;
176+  }
177+  // Parse OR addresses
178+  lines = options->OutboundBindAddressOR;
179+  addresses[0][AF_INET] = &options->OutboundBindAddressORIPv4_;
180+  addresses[0][AF_INET6] = &options->OutboundBindAddressORIPv6_;
181+  while (lines) {
182+    af = tor_addr_parse(&addr, lines->value);
183+    if (verify_and_store_address(af, &addr, addresses, 1, validate_only)) {
184+      if (msg)
185+        tor_asprintf(msg, "Multiple%s OR outbound bind addresses "
186+                     "configured: %s", af==AF_INET?" IPv4":
187+                     (af==AF_INET6?" IPv6":""), lines->value);
188+      return -1;
189+    }
190+    lines = lines->next;
191+  }
192+  // Parse exit addresses
193+  lines = options->OutboundBindAddressExit;
194+  addresses[0][AF_INET] = &options->OutboundBindAddressExitIPv4_;
195+  addresses[0][AF_INET6] = &options->OutboundBindAddressExitIPv6_;
196+  while (lines) {
197+    af = tor_addr_parse(&addr, lines->value);
198+    if (verify_and_store_address(af, &addr, addresses, 1, validate_only)) {
199+      if (msg)
200+        tor_asprintf(msg, "Multiple%s exit outbound bind addresses "
201+                     "configured: %s", af==AF_INET?" IPv4":
202+                     (af==AF_INET6?" IPv6":""), lines->value);
203       return -1;
204     }
205-    if (!validate_only)
206-      tor_addr_copy(dst_addr, &addr);
207     lines = lines->next;
208   }
209   return 0;
210diff --git a/tor-0.2.8.8/src/or/connection.c b/tor-0.2.8.8_new/src/or/connection.c
211index 4fbbaf1..cb9d98f 100755
212--- a/tor-0.2.8.8/src/or/connection.c
213+++ b/tor-0.2.8.8_new/src/or/connection.c
214@@ -94,6 +94,8 @@ static int connection_read_https_proxy_response(connection_t *conn);
215 static void connection_send_socks5_connect(connection_t *conn);
216 static const char *proxy_type_to_string(int proxy_type);
217 static int get_proxy_type(void);
218+const tor_addr_t *conn_get_outbound_address(const sa_family_t protocol_family,
219+                  const or_options_t *options, const unsigned int conn_type);
220 
221 /** The last addresses that our network interface seemed to have been
222  * binding to.  We use this as one way to detect when our IP changes.
223@@ -1836,6 +1838,39 @@ connection_connect_log_client_use_ip_version(const connection_t *conn)
224   }
225 }
226 
227+/** Retrieve the outbound address depending on the protocol (IPv4 or IPv6)
228+ * and the connection type (relay, exit, ...)
229+ * Return a socket address or NULL in case nothing is configured.
230+ **/
231+const tor_addr_t *
232+conn_get_outbound_address(const sa_family_t protocol_family,
233+             const or_options_t *options, const unsigned int conn_type)
234+{
235+  const tor_addr_t *ext_addr = NULL;
236+
237+  // If an exit connection, use the exit address (if present)
238+  if (conn_type == CONN_TYPE_EXIT) {
239+    if (protocol_family == AF_INET6) {
240+      if (!tor_addr_is_null(&options->OutboundBindAddressExitIPv6_))
241+        ext_addr = &options->OutboundBindAddressExitIPv6_;
242+    } else {
243+      if (!tor_addr_is_null(&options->OutboundBindAddressExitIPv4_))
244+         ext_addr = &options->OutboundBindAddressExitIPv4_;
245+    }
246+  }
247+  // Otherwise, or as fallback if no exit address was specified, use OR address
248+  if (ext_addr == NULL) {
249+    if (protocol_family == AF_INET6) {
250+      if (!tor_addr_is_null(&options->OutboundBindAddressORIPv6_))
251+        ext_addr = &options->OutboundBindAddressORIPv6_;
252+    } else {
253+      if (!tor_addr_is_null(&options->OutboundBindAddressORIPv4_))
254+         ext_addr = &options->OutboundBindAddressORIPv4_;
255+    }
256+  }
257+  return ext_addr;
258+}
259+
260 /** Take conn, make a nonblocking socket; try to connect to
261  * addr:port (port arrives in *host order*). If fail, return -1 and if
262  * applicable put your best guess about errno into *<b>socket_error</b>.
263@@ -1857,26 +1892,15 @@ connection_connect(connection_t *conn, const char *address,
264   struct sockaddr *bind_addr = NULL;
265   struct sockaddr *dest_addr;
266   int dest_addr_len, bind_addr_len = 0;
267-  const or_options_t *options = get_options();
268-  int protocol_family;
269 
270   /* Log if we didn't stick to ClientUseIPv4/6 or ClientPreferIPv6OR/DirPort
271    */
272   connection_connect_log_client_use_ip_version(conn);
273 
274-  if (tor_addr_family(addr) == AF_INET6)
275-    protocol_family = PF_INET6;
276-  else
277-    protocol_family = PF_INET;
278-
279   if (!tor_addr_is_loopback(addr)) {
280     const tor_addr_t *ext_addr = NULL;
281-    if (protocol_family == AF_INET &&
282-        !tor_addr_is_null(&options->OutboundBindAddressIPv4_))
283-      ext_addr = &options->OutboundBindAddressIPv4_;
284-    else if (protocol_family == AF_INET6 &&
285-             !tor_addr_is_null(&options->OutboundBindAddressIPv6_))
286-      ext_addr = &options->OutboundBindAddressIPv6_;
287+    ext_addr = conn_get_outbound_address(tor_addr_family(addr), get_options(),
288+                                         conn->type);
289     if (ext_addr) {
290       memset(&bind_addr_ss, 0, sizeof(bind_addr_ss));
291       bind_addr_len = tor_addr_to_sockaddr(ext_addr, 0,
292diff --git a/tor-0.2.8.8/src/or/dns.c b/tor-0.2.8.8_new/src/or/dns.c
293index c7adfbc..d10b672 100755
294--- a/tor-0.2.8.8/src/or/dns.c
295+++ b/tor-0.2.8.8_new/src/or/dns.c
296@@ -1374,10 +1374,10 @@ configure_nameservers(int force)
297   }
298 
299 #ifdef HAVE_EVDNS_SET_DEFAULT_OUTGOING_BIND_ADDRESS
300-  if (! tor_addr_is_null(&options->OutboundBindAddressIPv4_)) {
301+  if (! tor_addr_is_null(&options->OutboundBindAddressExitIPv4_)) {
302     int socklen;
303     struct sockaddr_storage ss;
304-    socklen = tor_addr_to_sockaddr(&options->OutboundBindAddressIPv4_, 0,
305+    socklen = tor_addr_to_sockaddr(&options->OutboundBindAddressExitIPv4_, 0,
306                                    (struct sockaddr *)&ss, sizeof(ss));
307     if (socklen <= 0) {
308       log_warn(LD_BUG, "Couldn't convert outbound bind address to sockaddr."
309diff --git a/tor-0.2.8.8/src/or/or.h b/tor-0.2.8.8_new/src/or/or.h
310index da84128..635caa5 100755
311--- a/tor-0.2.8.8/src/or/or.h
312+++ b/tor-0.2.8.8_new/src/or/or.h
313@@ -3578,10 +3578,18 @@ typedef struct {
314   config_line_t *ControlListenAddress;
315   /** Local address to bind outbound sockets */
316   config_line_t *OutboundBindAddress;
317-  /** IPv4 address derived from OutboundBindAddress. */
318-  tor_addr_t OutboundBindAddressIPv4_;
319-  /** IPv6 address derived from OutboundBindAddress. */
320-  tor_addr_t OutboundBindAddressIPv6_;
321+  /** Local address to bind outbound relay sockets */
322+  config_line_t *OutboundBindAddressOR;
323+  /** Local address to bind outbound exit sockets */
324+  config_line_t *OutboundBindAddressExit;
325+  /** IPv4 address derived from OutboundBindAddress or OutboundBindAddressOR */
326+  tor_addr_t OutboundBindAddressORIPv4_;
327+  /** IPv6 address derived from OutboundBindAddress or OutboundBindAddressOR */
328+  tor_addr_t OutboundBindAddressORIPv6_;
329+  /** IPv4 address derived from OutboundBindAddress/OutboundBindAddressExit */
330+  tor_addr_t OutboundBindAddressExitIPv4_;
331+  /** IPv6 address derived from OutboundBindAddress/OutboundBindAddressExit */
332+  tor_addr_t OutboundBindAddressExitIPv6_;
333   /** Directory server only: which versions of
334    * Tor should we tell users to run? */
335   config_line_t *RecommendedVersions;
336diff --git a/tor-0.2.8.8/src/or/policies.c b/tor-0.2.8.8_new/src/or/policies.c
337index 50fec3a..8e49120 100755
338--- a/tor-0.2.8.8/src/or/policies.c
339+++ b/tor-0.2.8.8_new/src/or/policies.c
340@@ -1957,8 +1957,8 @@ policies_copy_ipv4h_to_smartlist(smartlist_t *addr_list, uint32_t ipv4h_addr)
341 }
342 
343 /** Helper function that adds copies of
344- * or_options->OutboundBindAddressIPv[4|6]_ to a smartlist as tor_addr_t *, as
345- * long as or_options is non-NULL, and the addresses are not
346+ * or_options->OutboundBindAddressExitIPv[4|6]_ to a smartlist as tor_addr_t *,
347+ * as long as or_options is non-NULL, and the addresses are not
348  * tor_addr_is_null(), by passing them to policies_add_addr_to_smartlist.
349  *
350  * The caller is responsible for freeing all the tor_addr_t* in the smartlist.
351@@ -1968,10 +1968,15 @@ policies_copy_outbound_addresses_to_smartlist(smartlist_t *addr_list,
352                                               const or_options_t *or_options)
353 {
354   if (or_options) {
355-    policies_copy_addr_to_smartlist(addr_list,
356-                                    &or_options->OutboundBindAddressIPv4_);
357-    policies_copy_addr_to_smartlist(addr_list,
358-                                    &or_options->OutboundBindAddressIPv6_);
359+    // Only used for exit policy, so use exit address.
360+    if (!tor_addr_is_null(&or_options->OutboundBindAddressExitIPv4_)) {
361+      policies_copy_addr_to_smartlist(addr_list,
362+                          &or_options->OutboundBindAddressExitIPv4_);
363+    }
364+    if (!tor_addr_is_null(&or_options->OutboundBindAddressExitIPv6_)) {
365+      policies_copy_addr_to_smartlist(addr_list,
366+                          &or_options->OutboundBindAddressExitIPv6_);
367+    }
368   }
369 }
370 
371diff --git a/tor-0.2.8.8/src/test/test_policy.c b/tor-0.2.8.8_new/src/test/test_policy.c
372index a939ebf..690dfec 100755
373--- a/tor-0.2.8.8/src/test/test_policy.c
374+++ b/tor-0.2.8.8_new/src/test/test_policy.c
375@@ -1083,8 +1083,9 @@ test_policies_getinfo_helper_policies(void *arg)
376 
377   mock_options.IPv6Exit = 1;
378   mock_options.ExitPolicyRejectPrivate = 1;
379-  tor_addr_from_ipv4h(&mock_options.OutboundBindAddressIPv4_, TEST_IPV4_ADDR);
380-  tor_addr_parse(&mock_options.OutboundBindAddressIPv6_, TEST_IPV6_ADDR);
381+  tor_addr_from_ipv4h(&mock_options.OutboundBindAddressExitIPv4_,
382+                      TEST_IPV4_ADDR);
383+  tor_addr_parse(&mock_options.OutboundBindAddressExitIPv6_, TEST_IPV6_ADDR);
384 
385   rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay",
386                                &answer, &errmsg);