Browse Source

r8997@Kushana: nickm | 2006-09-28 23:40:22 -0400
[Needs review.] Add a BEGIN_DIR relay cell type for an easier
in-protocol way to connect to directory servers through Tor.
Previously, clients could only connect to director servers over Tor
from exit nodes, but couldn't get directory information anonymously
from a non-exit cache without getting a directory server involved.

This needs testing, and needs client-side code to actually exercise it.



svn:r8527

Nick Mathewson 19 years ago
parent
commit
9dbfb91f17
6 changed files with 153 additions and 28 deletions
  1. 5 0
      ChangeLog
  2. 6 4
      doc/TODO
  3. 13 0
      doc/tor-spec.txt
  4. 126 24
      src/or/connection_edge.c
  5. 2 0
      src/or/or.h
  6. 1 0
      src/or/relay.c

+ 5 - 0
ChangeLog

@@ -8,6 +8,11 @@ Changes in version 0.1.2.2-alpha - 2006-??-??
       support eventdns.
       support eventdns.
     - Specify and implement client-side SOCKS5 interface for reverse DNS
     - Specify and implement client-side SOCKS5 interface for reverse DNS
       lookups; see doc/socks-extensions.txt for full information.
       lookups; see doc/socks-extensions.txt for full information.
+    - Add a BEGIN_DIR relay cell type for an easier in-protocol way to
+      connect to directory servers through Tor.  Previously, clients could
+      only connect to director servers over Tor from exit nodes, but couldn't
+      get directory information anonymously from a non-exit cache without
+      getting a directory server involved.
 
 
   o Minor features:
   o Minor features:
     - Check for name servers (like Earthlink's) that hijack failing DNS
     - Check for name servers (like Earthlink's) that hijack failing DNS

+ 6 - 4
doc/TODO

@@ -52,7 +52,7 @@ P - Figure out why openssl 0.9.8c "make test" fails at sha256t test.
 Items for 0.1.2.x:
 Items for 0.1.2.x:
   o re-enable blossom functionality: let tor servers decide if they
   o re-enable blossom functionality: let tor servers decide if they
     will use local search when resolving, or not.
     will use local search when resolving, or not.
-N   - Document it.
+    o Document it.
   - enumerate events of important things that occur in tor, so vidalia can
   - enumerate events of important things that occur in tor, so vidalia can
     react.
     react.
 N   - Backend implementation
 N   - Backend implementation
@@ -61,11 +61,13 @@ R   - Actually list all the events (notice and warn log messages are a good
 N   - Specify general event system
 N   - Specify general event system
 R   - Specify actual events.
 R   - Specify actual events.
 
 
-N - Have (and document) a BEGIN_DIR relay cell that means "Connect to your
+N . Have (and document) a BEGIN_DIR relay cell that means "Connect to your
     directory port."
     directory port."
-    - Specify
-    - Implement
+    o Specify
+    o Implement
     - Use for something, so we can be sure it works.
     - Use for something, so we can be sure it works.
+    - Test and debug
+
 
 
 x - We should ship with a list of stable dir mirrors -- they're not
 x - We should ship with a list of stable dir mirrors -- they're not
     trusted like the authorities, but they'll provide more robustness
     trusted like the authorities, but they'll provide more robustness

+ 13 - 0
doc/tor-spec.txt

@@ -624,6 +624,7 @@ TODO:
         10 -- RELAY_DROP      [forward or backward]
         10 -- RELAY_DROP      [forward or backward]
         11 -- RELAY_RESOLVE   [forward]
         11 -- RELAY_RESOLVE   [forward]
         12 -- RELAY_RESOLVED  [backward]
         12 -- RELAY_RESOLVED  [backward]
+        13 -- RELAY_BEGIN_DIR [forward]
 
 
    Commands labelled as "forward" must only be sent by the originator
    Commands labelled as "forward" must only be sent by the originator
    of the circuit. Commands labelled as "backward" must only be sent by
    of the circuit. Commands labelled as "backward" must only be sent by
@@ -710,6 +711,16 @@ TODO:
    Relay RELAY_DROP cells are long-range dummies; upon receiving such
    Relay RELAY_DROP cells are long-range dummies; upon receiving such
    a cell, the OR or OP must drop it.
    a cell, the OR or OP must drop it.
 
 
+6.2.1. Opening a directory stream
+
+   If a Tor server is a directory server, it should respond to a
+   RELAY_BEGIN_DIR cell as if it had received a BEGIN cell requesting a
+   connection to its directory port.  RELAY_BEGIN_DIR cells ignore exit
+   policy, since the stream is local to the Tor process.
+
+   If the Tor server is not running a directory service, it should respond
+   with a REASON_NOTDIRECTORY RELAY_END cell.
+
 6.3. Closing streams
 6.3. Closing streams
 
 
    When an anonymized TCP connection is closed, or an edge node
    When an anonymized TCP connection is closed, or an edge node
@@ -738,6 +749,8 @@ TODO:
       12 -- REASON_CONNRESET      (Connection was unexpectedly reset)
       12 -- REASON_CONNRESET      (Connection was unexpectedly reset)
       13 -- REASON_TORPROTOCOL    (Sent when closing connection because of
       13 -- REASON_TORPROTOCOL    (Sent when closing connection because of
                                    Tor protocol violations.)
                                    Tor protocol violations.)
+      14 -- REASON_NOTDIRECTORY   (Client send RELAY_BEGIN_DIR to a
+                                   non-directory server.)
 
 
    (With REASON_EXITPOLICY, the 4-byte IPv4 address or 16-byte IPv6 address
    (With REASON_EXITPOLICY, the 4-byte IPv4 address or 16-byte IPv6 address
    forms the optional data; no other reason currently has extra data.
    forms the optional data; no other reason currently has extra data.

+ 126 - 24
src/or/connection_edge.c

@@ -29,6 +29,7 @@ static smartlist_t *redirect_exit_list = NULL;
 
 
 static int connection_ap_handshake_process_socks(edge_connection_t *conn);
 static int connection_ap_handshake_process_socks(edge_connection_t *conn);
 static int connection_ap_process_transparent(edge_connection_t *conn);
 static int connection_ap_process_transparent(edge_connection_t *conn);
+static int connection_exit_connect_dir(edge_connection_t *exit_conn);
 
 
 /** An AP stream has failed/finished. If it hasn't already sent back
 /** An AP stream has failed/finished. If it hasn't already sent back
  * a socks reply, send one now (based on endreason). Also set
  * a socks reply, send one now (based on endreason). Also set
@@ -1813,33 +1814,41 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
   }
   }
 
 
   relay_header_unpack(&rh, cell->payload);
   relay_header_unpack(&rh, cell->payload);
-
-  if (!memchr(cell->payload+RELAY_HEADER_SIZE, 0, rh.length)) {
-    log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
-           "Relay begin cell has no \\0. Dropping.");
-    return 0;
-  }
-  if (parse_addr_port(LOG_PROTOCOL_WARN, cell->payload+RELAY_HEADER_SIZE,
-                      &address,NULL,&port)<0) {
-    log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
-           "Unable to parse addr:port in relay begin cell. Dropping.");
-    return 0;
-  }
-  if (port==0) {
-    log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
-           "Missing port in relay begin cell. Dropping.");
-    tor_free(address);
-    return 0;
-  }
+  if (rh.command == RELAY_COMMAND_BEGIN) {
+    if (!memchr(cell->payload+RELAY_HEADER_SIZE, 0, rh.length)) {
+      log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+             "Relay begin cell has no \\0. Dropping.");
+      return 0;
+    }
+    if (parse_addr_port(LOG_PROTOCOL_WARN, cell->payload+RELAY_HEADER_SIZE,
+                        &address,NULL,&port)<0) {
+      log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+             "Unable to parse addr:port in relay begin cell. Dropping.");
+      return 0;
+    }
+    if (port==0) {
+      log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+             "Missing port in relay begin cell. Dropping.");
+      tor_free(address);
+      return 0;
+    }
 #if 0
 #if 0
-  if (!tor_strisprint(address)) {
-    log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
-           "Non-printing characters in address %s in relay "
-           "begin cell. Dropping.", escaped(address));
-    tor_free(address);
+    if (!tor_strisprint(address)) {
+      log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+             "Non-printing characters in address %s in relay "
+             "begin cell. Dropping.", escaped(address));
+      tor_free(address);
+      return 0;
+    }
+#endif
+  } else if (rh.command == RELAY_COMMAND_BEGIN_DIR) {
+    or_options_t *options = get_options();
+    address = tor_strdup("127.0.0.1");
+    port = options->DirPort; /* not actually used. */
+  } else {
+    log_warn(LD_BUG, "Got an unexpected command %d", (int)rh.command);
     return 0;
     return 0;
   }
   }
-#endif
 
 
   log_debug(LD_EXIT,"Creating new exit connection.");
   log_debug(LD_EXIT,"Creating new exit connection.");
   n_stream = TO_EDGE_CONN(connection_new(CONN_TYPE_EXIT));
   n_stream = TO_EDGE_CONN(connection_new(CONN_TYPE_EXIT));
@@ -1851,6 +1860,15 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
   n_stream->package_window = STREAMWINDOW_START;
   n_stream->package_window = STREAMWINDOW_START;
   n_stream->deliver_window = STREAMWINDOW_START;
   n_stream->deliver_window = STREAMWINDOW_START;
 
 
+  if (rh.command == RELAY_COMMAND_BEGIN_DIR &&
+      (!get_options()->DirPort || circ->purpose != CIRCUIT_PURPOSE_OR)) {
+    connection_edge_end(n_stream, END_STREAM_REASON_NOTDIRECTORY,
+                        n_stream->cpath_layer);
+    connection_free(TO_CONN(n_stream));
+    tor_free(address);
+    return 0;
+  }
+
   if (circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED) {
   if (circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED) {
     origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ);
     origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ);
     log_debug(LD_REND,"begin is for rendezvous. configuring stream.");
     log_debug(LD_REND,"begin is for rendezvous. configuring stream.");
@@ -1898,6 +1916,13 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
   }
   }
   log_debug(LD_EXIT,"about to start the dns_resolve().");
   log_debug(LD_EXIT,"about to start the dns_resolve().");
 
 
+  if (rh.command == RELAY_COMMAND_BEGIN_DIR) {
+    n_stream->next_stream = TO_OR_CIRCUIT(circ)->n_streams;
+    n_stream->on_circuit = circ;
+    TO_OR_CIRCUIT(circ)->n_streams = n_stream;
+    return connection_exit_connect_dir(n_stream);
+  }
+
   /* send it off to the gethostbyname farm */
   /* send it off to the gethostbyname farm */
   switch (dns_resolve(n_stream, NULL)) {
   switch (dns_resolve(n_stream, NULL)) {
     case 1: /* resolve worked */
     case 1: /* resolve worked */
@@ -2067,6 +2092,83 @@ connection_exit_connect(edge_connection_t *edge_conn)
   }
   }
 }
 }
 
 
+/** Given an exit conn that should attach to us as a directory server, open a
+ * bridge connection with a socketpair, create a new directory conn, and join
+ * them together.  Return 0 on success (or if there was an error we could send
+ * back an end cell for).  Return -1 if the circuit needs to be torn down.
+ * Either connects exit_conn, or frees it, or marks it as appropriate.
+ */
+static int
+connection_exit_connect_dir(edge_connection_t *exit_conn)
+{
+  int fd[2];
+  int err;
+  dir_connection_t *dir_conn = NULL;
+
+  log_info(LD_EXIT, "Opening dir bridge");
+
+  if ((err = tor_socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) < 0) {
+    log_warn(LD_NET,
+             "Couldn't construct socketpair (%s). Network down? Delaying.",
+             tor_socket_strerror(-err));
+    connection_edge_end(exit_conn, END_STREAM_REASON_RESOURCELIMIT,
+                        exit_conn->cpath_layer);
+    connection_free(TO_CONN(exit_conn));
+    return 0;
+  }
+
+  tor_assert(fd[0] >= 0);
+  tor_assert(fd[1] >= 0);
+
+  set_socket_nonblocking(fd[0]);
+  set_socket_nonblocking(fd[1]);
+
+  exit_conn->_base.s = fd[0];
+  exit_conn->_base.state = EXIT_CONN_STATE_OPEN;
+
+  dir_conn = TO_DIR_CONN(connection_new(CONN_TYPE_DIR));
+  dir_conn->_base.s = fd[1];
+
+  dir_conn->_base.addr = 0x7f000001;
+  dir_conn->_base.port = 0;
+  dir_conn->_base.address = tor_strdup("Tor network");
+  dir_conn->_base.type = CONN_TYPE_DIR;
+  dir_conn->_base.purpose = DIR_PURPOSE_SERVER;
+  dir_conn->_base.state = DIR_CONN_STATE_SERVER_COMMAND_WAIT;
+
+  if (connection_add(TO_CONN(exit_conn))<0) {
+    connection_edge_end(exit_conn, END_STREAM_REASON_RESOURCELIMIT,
+                        exit_conn->cpath_layer);
+    /* XXXX Have I got the free/mark distinction right? -NM */
+    connection_free(TO_CONN(exit_conn));
+    connection_free(TO_CONN(dir_conn));
+    return 0;
+  }
+
+  if (connection_add(TO_CONN(dir_conn))<0) {
+    connection_edge_end(exit_conn, END_STREAM_REASON_RESOURCELIMIT,
+                        exit_conn->cpath_layer);
+    connection_close_immediate(TO_CONN(exit_conn));
+    connection_mark_for_close(TO_CONN(exit_conn));
+    connection_free(TO_CONN(dir_conn));
+    return 0;
+  }
+
+  connection_start_reading(TO_CONN(dir_conn));
+  connection_start_reading(TO_CONN(exit_conn));
+
+  if (connection_edge_send_command(exit_conn,
+                                   circuit_get_by_edge_conn(exit_conn),
+                                   RELAY_COMMAND_CONNECTED, NULL, 0,
+                                   exit_conn->cpath_layer) < 0) {
+    connection_mark_for_close(TO_CONN(exit_conn));
+    connection_mark_for_close(TO_CONN(dir_conn));
+    return 0;
+  }
+
+  return 0;
+}
+
 /** Return 1 if <b>conn</b> is a rendezvous stream, or 0 if
 /** Return 1 if <b>conn</b> is a rendezvous stream, or 0 if
  * it is a general stream.
  * it is a general stream.
  */
  */

+ 2 - 0
src/or/or.h

@@ -450,6 +450,7 @@ typedef enum {
 #define RELAY_COMMAND_DROP 10
 #define RELAY_COMMAND_DROP 10
 #define RELAY_COMMAND_RESOLVE 11
 #define RELAY_COMMAND_RESOLVE 11
 #define RELAY_COMMAND_RESOLVED 12
 #define RELAY_COMMAND_RESOLVED 12
+#define RELAY_COMMAND_BEGIN_DIR 13
 
 
 #define RELAY_COMMAND_ESTABLISH_INTRO 32
 #define RELAY_COMMAND_ESTABLISH_INTRO 32
 #define RELAY_COMMAND_ESTABLISH_RENDEZVOUS 33
 #define RELAY_COMMAND_ESTABLISH_RENDEZVOUS 33
@@ -474,6 +475,7 @@ typedef enum {
 #define END_STREAM_REASON_RESOURCELIMIT 11
 #define END_STREAM_REASON_RESOURCELIMIT 11
 #define END_STREAM_REASON_CONNRESET 12
 #define END_STREAM_REASON_CONNRESET 12
 #define END_STREAM_REASON_TORPROTOCOL 13
 #define END_STREAM_REASON_TORPROTOCOL 13
+#define END_STREAM_REASON_NOTDIRECTORY 14
 
 
 /* These high-numbered end reasons are not part of the official spec,
 /* These high-numbered end reasons are not part of the official spec,
  * and are not intended to be put in relay end cells. They are here
  * and are not intended to be put in relay end cells. They are here

+ 1 - 0
src/or/relay.c

@@ -944,6 +944,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
 //      log_info(domain,"Got a relay-level padding cell. Dropping.");
 //      log_info(domain,"Got a relay-level padding cell. Dropping.");
       return 0;
       return 0;
     case RELAY_COMMAND_BEGIN:
     case RELAY_COMMAND_BEGIN:
+    case RELAY_COMMAND_BEGIN_DIR:
       if (layer_hint &&
       if (layer_hint &&
           circ->purpose != CIRCUIT_PURPOSE_S_REND_JOINED) {
           circ->purpose != CIRCUIT_PURPOSE_S_REND_JOINED) {
         log_warn(LD_APP,"relay begin request unsupported at AP. Dropping.");
         log_warn(LD_APP,"relay begin request unsupported at AP. Dropping.");