[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: Option to reuse same exit node?



Thus spake Mike Perry (mikepery@xxxxxxxxxx):

> Thus spake Echo Nolan (hellish@xxxxxxxxxxx):
> 
> > Mike Perry wrote:
> > | Or is there a reason why this option should not exist?
> > It removes anonymity and replaces it with pseudonymity. If this becomes
> > available it should be optional. Write the patch, but try and think of a
> > way for it to be optional based on site and easily configurable.
> 
> for making the config option take hostnames instead (and it is easy to
> code), maybe I'll hack that up as well.

So I couldn't sleep and it turned out to be not too hard to satisfy
both our wishes. So it's done. 

The new option accepts a CSV that can contain host names or domain
names. Domain names start with a '.'. So for example, to have the
option apply to fscked.org and all subdomains, you would include
'.fscked.org'. Otherwise, you can just list plain hosts, like
'www.google.com'. This is explained in the manpage, but it might be a
little muddled.. Also, '.' by itself matches every host.

One thing I don't do is free any of the memory in the exit node cache
on exit (I do free it when the entries are reused). I don't think this
is a big problem, unless there is some policy you guys have on always
freeing all memory before exiting.

It should also be noted I did not test this feature with the .exit
SOCKS capabilities, nor any of the config options that allow you to
otherwise specify/limit exit nodes.. I think it should be ok though. I
believe that I placed the modifications such that they take precedence
over everything else once a host appears in the cache, which IMO is
how it should work.

-- 
Mike Perry
Mad Computer Scientist
fscked.org evil labs
diff -ur tor-0.0.9.2/doc/tor.1.in tor-0.0.9.2-MP/doc/tor.1.in
--- tor-0.0.9.2/doc/tor.1.in	2004-12-11 08:13:15.000000000 -0800
+++ tor-0.0.9.2-MP/doc/tor.1.in	2005-01-17 04:21:42.466745264 -0800
@@ -151,6 +151,18 @@
 If 1, Tor will never use any nodes besides those listed in "entrynodes" for
 the first hop of a circuit.
 .TP
+\fBTrackHostExits \fR\fIhost1\fR,\fI.domain1\fR|\fI.\fR\fP
+For each value in the comma separated list, Tor will track recent connections 
+to hosts that match this value and attempt to
+reuse the same exit node for each. If the value is prepended with a '.', it is
+treated as matching an entire domain. If one of the values is just a '.', it
+means match everything. This option is useful if you frequently connect to
+sites that will expire all your authentication cookies (ie log you out) if
+your IP address changes. Note that this option does have the disadvantage of
+making it more clear that a given history is
+associated with a single user. However, most people who would wish to observe
+this will observe it through cookies or other protocol-specific means anyhow.
+.TP
 \fBFascistFirewall \fR\fB0\fR|\fB1\fR\fP
 If 1, Tor will only create outgoing connections to ORs running on ports that
 your firewall allows (defaults to 80 and 443; see \fBFirewallPorts\fR).  This will
diff -ur tor-0.0.9.2/src/or/circuituse.c tor-0.0.9.2-MP/src/or/circuituse.c
--- tor-0.0.9.2/src/or/circuituse.c	2004-12-12 16:44:38.000000000 -0800
+++ tor-0.0.9.2-MP/src/or/circuituse.c	2005-01-17 04:17:23.691427996 -0800
@@ -17,6 +17,10 @@
 
 /********* START VARIABLES **********/
 
+
+exit_history_pair_t exit_history[EXIT_HISTORY_LENGTH]; /* 8k */
+static int exit_history_current;
+
 extern circuit_t *global_circuitlist; /* from circuitlist.c */
 extern int has_fetched_directory; /* from main.c */
 
@@ -863,8 +867,53 @@
 
     link_apconn_to_circ(conn, circ);
     tor_assert(conn->socks_request);
-    if (conn->socks_request->command == SOCKS_COMMAND_CONNECT)
+    if (conn->socks_request->command == SOCKS_COMMAND_CONNECT) {
+
+      or_options_t *options = get_options();
+      /* If an exit wasn't specifically chosen, save the history for future
+       * use */ 
+      if(!conn->chosen_exit_name && options->TrackHostExits) {
+        int found_needle = 0;
+        SMARTLIST_FOREACH(options->TrackHostExits, const char *, cp, {
+            if(cp[0] == '.') { /* match end */
+              char *str;
+              if((str = strstr(conn->socks_request->address, &cp[1]))) {
+                if(str == conn->socks_request->address 
+                  || strcmp(str, &cp[1]) == 0) {
+                  found_needle = 1; 
+                }
+              }
+            } else if(strcmp(cp, conn->socks_request->address) == 0) { 
+              found_needle = 1; 
+            }
+        });
+
+        if(found_needle) {
+
+          /* Add connection to our most recently received */
+          if(exit_history[exit_history_current].exit_nickname != NULL) {
+            tor_assert(exit_history[exit_history_current].bobs_hostname);
+            free(exit_history[exit_history_current].exit_nickname);
+            free(exit_history[exit_history_current].bobs_hostname);
+          }
+
+          exit_history[exit_history_current].exit_nickname 
+            = strdup(circ->build_state->chosen_exit_name);
+          exit_history[exit_history_current].bobs_hostname 
+            = strdup(conn->socks_request->address); 
+
+          log_fn(LOG_INFO, 
+              "Tracking host %s to always connect with exit node %s (history entry %d)",
+              exit_history[exit_history_current].bobs_hostname,
+              exit_history[exit_history_current].exit_nickname,
+              exit_history_current);
+
+          exit_history_current = (exit_history_current+1) % EXIT_HISTORY_LENGTH;
+        }
+      }
+
       connection_ap_handshake_send_begin(conn, circ);
+    }
     else
       connection_ap_handshake_send_resolve(conn, circ);
 
diff -ur tor-0.0.9.2/src/or/config.c tor-0.0.9.2-MP/src/or/config.c
--- tor-0.0.9.2/src/or/config.c	2005-01-03 15:22:01.000000000 -0800
+++ tor-0.0.9.2-MP/src/or/config.c	2005-01-17 04:17:23.694427559 -0800
@@ -118,6 +118,7 @@
   VAR("StrictEntryNodes",    BOOL,     StrictEntryNodes,     "0"),
   VAR("ExitPolicy",          LINELIST, ExitPolicy,           NULL),
   VAR("ExcludeNodes",        STRING,   ExcludeNodes,         NULL),
+  VAR("TrackHostExits",      CSV,     TrackHostExits,       "0"),
   VAR("FascistFirewall",     BOOL,     FascistFirewall,      "0"),
   VAR("FirewallPorts",       CSV,      FirewallPorts,        "80,443"),
   VAR("MyFamily",            STRING,   MyFamily,             NULL),
diff -ur tor-0.0.9.2/src/or/connection_edge.c tor-0.0.9.2-MP/src/or/connection_edge.c
--- tor-0.0.9.2/src/or/connection_edge.c	2004-12-24 01:44:45.000000000 -0800
+++ tor-0.0.9.2-MP/src/or/connection_edge.c	2005-01-17 04:17:23.696427268 -0800
@@ -19,6 +19,8 @@
 
 static int connection_ap_handshake_process_socks(connection_t *conn);
 
+extern exit_history_pair_t exit_history[EXIT_HISTORY_LENGTH];
+
 /** There was an EOF. Send an end and mark the connection for close.
  */
 int connection_edge_reached_eof(connection_t *conn) {
@@ -378,10 +380,12 @@
     conn->chosen_exit_name = tor_strdup(s+1);
     *s = 0;
   }
+  
 
   if (addresstype != ONION_HOSTNAME) {
-    /* not a hidden-service request (i.e. normal or .exit) */
+    int i = 0;
 
+    /* not a hidden-service request (i.e. normal or .exit) */
     if (socks->command == SOCKS_COMMAND_RESOLVE) {
       uint32_t answer = 0;
       struct in_addr in;
@@ -409,6 +413,27 @@
       log_fn(LOG_NOTICE,"Application asked to connect to port 0. Refusing.");
       return -1;
     }
+
+    /* Check exit history for a chosen_exit_name */
+    if(get_options()->TrackHostExits) {
+      for(i = 0; i < EXIT_HISTORY_LENGTH; i++) {
+        if(!exit_history[i].bobs_hostname) /* End of the line */
+          break;
+
+        /* If we've connected to this IP before, try to prefer the same exit */
+        if(strcmp(exit_history[i].bobs_hostname, conn->socks_request->address)
+            == 0) {
+          conn->chosen_exit_name = strdup(exit_history[i].exit_nickname);
+
+          log_fn(LOG_INFO, 
+              "Reusing cached exit: %s should always connect through exit node %s (history entry %d)",
+              exit_history[i].bobs_hostname,
+              exit_history[i].exit_nickname, i);
+          break;
+        }
+      }
+    }
+
     conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
     rep_hist_note_used_port(socks->port, time(NULL)); /* help predict this next time */
     return connection_ap_handshake_attach_circuit(conn);
diff -ur tor-0.0.9.2/src/or/or.h tor-0.0.9.2-MP/src/or/or.h
--- tor-0.0.9.2/src/or/or.h	2005-01-03 17:11:08.000000000 -0800
+++ tor-0.0.9.2-MP/src/or/or.h	2005-01-17 04:17:23.697427123 -0800
@@ -478,6 +478,16 @@
 typedef struct buf_t buf_t;
 typedef struct socks_request_t socks_request_t;
 
+#define EXIT_HISTORY_LENGTH                1024
+
+typedef struct 
+{
+  char *bobs_hostname;
+  char *exit_nickname;
+} exit_history_pair_t;
+
+
+
 #define CONNECTION_MAGIC 0x7C3C304Eu
 /** Description of a connection to another host or process, and associated
  * data. */
@@ -928,6 +938,7 @@
   int IgnoreVersion; /**< If true, run no matter what versions of Tor the
                       * directory recommends. */
   int RunAsDaemon; /**< If true, run in the background. (Unix only) */
+  smartlist_t *TrackHostExits; /**< Should we try to reuse the same exit node for a given host */
   int FascistFirewall; /**< Whether to prefer ORs reachable on open ports. */
   smartlist_t *FirewallPorts; /** Which ports our firewall allows. */
   int DirFetchPeriod; /**< How often do we fetch new directories? */
diff -ur tor/doc/tor.1.in tor-MP/doc/tor.1.in
--- tor/doc/tor.1.in	2005-01-15 15:18:42.000000000 -0800
+++ tor-MP/doc/tor.1.in	2005-01-17 04:24:38.062170447 -0800
@@ -151,6 +151,18 @@
 If 1, Tor will never use any nodes besides those listed in "entrynodes" for
 the first hop of a circuit.
 .TP
+\fBTrackHostExits \fR\fIhost1\fR,\fI.domain1\fR|\fI.\fR\fP
+For each value in the comma separated list, Tor will track recent connections 
+to hosts that match this value and attempt to
+reuse the same exit node for each. If the value is prepended with a '.', it is
+treated as matching an entire domain. If one of the values is just a '.', it
+means match everything. This option is useful if you frequently connect to
+sites that will expire all your authentication cookies (ie log you out) if
+your IP address changes. Note that this option does have the disadvantage of
+making it more clear that a given history is
+associated with a single user. However, most people who would wish to observe
+this will observe it through cookies or other protocol-specific means anyhow.
+.TP
 \fBFascistFirewall \fR\fB0\fR|\fB1\fR\fP
 If 1, Tor will only create outgoing connections to ORs running on ports that
 your firewall allows (defaults to 80 and 443; see \fBFirewallPorts\fR).  This will
diff -ur tor/src/or/circuituse.c tor-MP/src/or/circuituse.c
--- tor/src/or/circuituse.c	2005-01-13 12:22:37.000000000 -0800
+++ tor-MP/src/or/circuituse.c	2005-01-17 04:24:38.064170155 -0800
@@ -17,6 +17,10 @@
 
 /********* START VARIABLES **********/
 
+
+exit_history_pair_t exit_history[EXIT_HISTORY_LENGTH]; /* 8k */
+static int exit_history_current;
+
 extern circuit_t *global_circuitlist; /* from circuitlist.c */
 extern int has_fetched_directory; /* from main.c */
 
@@ -893,8 +897,53 @@
 
     link_apconn_to_circ(conn, circ);
     tor_assert(conn->socks_request);
-    if (conn->socks_request->command == SOCKS_COMMAND_CONNECT)
+    if (conn->socks_request->command == SOCKS_COMMAND_CONNECT) {
+
+      or_options_t *options = get_options();
+      /* If an exit wasn't specifically chosen, save the history for future
+       * use */ 
+      if(!conn->chosen_exit_name && options->TrackHostExits) {
+        int found_needle = 0;
+        SMARTLIST_FOREACH(options->TrackHostExits, const char *, cp, {
+            if(cp[0] == '.') { /* match end */
+              char *str;
+              if((str = strstr(conn->socks_request->address, &cp[1]))) {
+                if(str == conn->socks_request->address 
+                  || strcmp(str, &cp[1]) == 0) {
+                  found_needle = 1; 
+                }
+              }
+            } else if(strcmp(cp, conn->socks_request->address) == 0) { 
+              found_needle = 1; 
+            }
+        });
+
+        if(found_needle) {
+
+          /* Add connection to our most recently received */
+          if(exit_history[exit_history_current].exit_nickname != NULL) {
+            tor_assert(exit_history[exit_history_current].bobs_hostname);
+            free(exit_history[exit_history_current].exit_nickname);
+            free(exit_history[exit_history_current].bobs_hostname);
+          }
+
+          exit_history[exit_history_current].exit_nickname 
+            = strdup(circ->build_state->chosen_exit_name);
+          exit_history[exit_history_current].bobs_hostname 
+            = strdup(conn->socks_request->address); 
+
+          log_fn(LOG_INFO, 
+              "Tracking host %s to always connect with exit node %s (history entry %d)",
+              exit_history[exit_history_current].bobs_hostname,
+              exit_history[exit_history_current].exit_nickname,
+              exit_history_current);
+
+          exit_history_current = (exit_history_current+1) % EXIT_HISTORY_LENGTH;
+        }
+      }
+
       connection_ap_handshake_send_begin(conn, circ);
+    }
     else
       connection_ap_handshake_send_resolve(conn, circ);
 
diff -ur tor/src/or/config.c tor-MP/src/or/config.c
--- tor/src/or/config.c	2005-01-13 13:32:08.000000000 -0800
+++ tor-MP/src/or/config.c	2005-01-17 04:24:38.066169864 -0800
@@ -120,6 +120,7 @@
   VAR("StrictEntryNodes",    BOOL,     StrictEntryNodes,     "0"),
   VAR("ExitPolicy",          LINELIST, ExitPolicy,           NULL),
   VAR("ExcludeNodes",        STRING,   ExcludeNodes,         NULL),
+  VAR("TrackHostExits",      CSV,     TrackHostExits,       "0"),
   VAR("FascistFirewall",     BOOL,     FascistFirewall,      "0"),
   VAR("FirewallPorts",       CSV,      FirewallPorts,        "80,443"),
   VAR("MyFamily",            STRING,   MyFamily,             NULL),
diff -ur tor/src/or/connection_edge.c tor-MP/src/or/connection_edge.c
--- tor/src/or/connection_edge.c	2005-01-13 12:22:38.000000000 -0800
+++ tor-MP/src/or/connection_edge.c	2005-01-17 04:24:38.068169573 -0800
@@ -19,6 +19,8 @@
 
 static int connection_ap_handshake_process_socks(connection_t *conn);
 
+extern exit_history_pair_t exit_history[EXIT_HISTORY_LENGTH];
+
 /** There was an EOF. Send an end and mark the connection for close.
  */
 int connection_edge_reached_eof(connection_t *conn) {
@@ -378,10 +380,12 @@
     conn->chosen_exit_name = tor_strdup(s+1);
     *s = 0;
   }
+  
 
   if (addresstype != ONION_HOSTNAME) {
-    /* not a hidden-service request (i.e. normal or .exit) */
+    int i = 0;
 
+    /* not a hidden-service request (i.e. normal or .exit) */
     if (socks->command == SOCKS_COMMAND_RESOLVE) {
       uint32_t answer = 0;
       struct in_addr in;
@@ -409,6 +413,27 @@
       log_fn(LOG_NOTICE,"Application asked to connect to port 0. Refusing.");
       return -1;
     }
+
+    /* Check exit history for a chosen_exit_name */
+    if(get_options()->TrackHostExits) {
+      for(i = 0; i < EXIT_HISTORY_LENGTH; i++) {
+        if(!exit_history[i].bobs_hostname) /* End of the line */
+          break;
+
+        /* If we've connected to this IP before, try to prefer the same exit */
+        if(strcmp(exit_history[i].bobs_hostname, conn->socks_request->address)
+            == 0) {
+          conn->chosen_exit_name = strdup(exit_history[i].exit_nickname);
+
+          log_fn(LOG_INFO, 
+              "Reusing cached exit: %s should always connect through exit node %s (history entry %d)",
+              exit_history[i].bobs_hostname,
+              exit_history[i].exit_nickname, i);
+          break;
+        }
+      }
+    }
+
     conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
     rep_hist_note_used_port(socks->port, time(NULL)); /* help predict this next time */
     return connection_ap_handshake_attach_circuit(conn);
diff -ur tor/src/or/or.h tor-MP/src/or/or.h
--- tor/src/or/or.h	2005-01-13 12:22:38.000000000 -0800
+++ tor-MP/src/or/or.h	2005-01-17 04:24:38.071169136 -0800
@@ -481,6 +481,16 @@
 typedef struct buf_t buf_t;
 typedef struct socks_request_t socks_request_t;
 
+#define EXIT_HISTORY_LENGTH                1024
+
+typedef struct 
+{
+  char *bobs_hostname;
+  char *exit_nickname;
+} exit_history_pair_t;
+
+
+
 #define CONNECTION_MAGIC 0x7C3C304Eu
 /** Description of a connection to another host or process, and associated
  * data. */
@@ -938,6 +948,7 @@
   int IgnoreVersion; /**< If true, run no matter what versions of Tor the
                       * directory recommends. */
   int RunAsDaemon; /**< If true, run in the background. (Unix only) */
+  smartlist_t *TrackHostExits; /**< Should we try to reuse the same exit node for a given host */
   int FascistFirewall; /**< Whether to prefer ORs reachable on open ports. */
   smartlist_t *FirewallPorts; /**< Which ports our firewall allows (strings). */
   /** Application ports that require all nodes in circ to have sufficient uptime. */