From 9dbfb91f179be5dbce705e62d267a2aeed54b1cb Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 29 Sep 2006 03:50:11 +0000 Subject: [PATCH] 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 --- ChangeLog | 5 ++ doc/TODO | 10 +-- doc/tor-spec.txt | 13 ++++ src/or/connection_edge.c | 150 ++++++++++++++++++++++++++++++++------- src/or/or.h | 2 + src/or/relay.c | 1 + 6 files changed, 153 insertions(+), 28 deletions(-) diff --git a/ChangeLog b/ChangeLog index 123a55be10..18bb36ae90 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,6 +8,11 @@ Changes in version 0.1.2.2-alpha - 2006-??-?? support eventdns. - Specify and implement client-side SOCKS5 interface for reverse DNS 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: - Check for name servers (like Earthlink's) that hijack failing DNS diff --git a/doc/TODO b/doc/TODO index 82c41af43a..ee4bc31ecf 100644 --- a/doc/TODO +++ b/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: o re-enable blossom functionality: let tor servers decide if they 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 react. 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 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." - - Specify - - Implement + o Specify + o Implement - 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 trusted like the authorities, but they'll provide more robustness diff --git a/doc/tor-spec.txt b/doc/tor-spec.txt index 61f5fd7f15..af3b73cc0c 100644 --- a/doc/tor-spec.txt +++ b/doc/tor-spec.txt @@ -624,6 +624,7 @@ TODO: 10 -- RELAY_DROP [forward or backward] 11 -- RELAY_RESOLVE [forward] 12 -- RELAY_RESOLVED [backward] + 13 -- RELAY_BEGIN_DIR [forward] Commands labelled as "forward" must only be sent by the originator 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 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 When an anonymized TCP connection is closed, or an edge node @@ -738,6 +749,8 @@ TODO: 12 -- REASON_CONNRESET (Connection was unexpectedly reset) 13 -- REASON_TORPROTOCOL (Sent when closing connection because of 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 forms the optional data; no other reason currently has extra data. diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index ad9adc983c..6047bf8dd7 100644 --- a/src/or/connection_edge.c +++ b/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_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 * 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); - - 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 (!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; } -#endif log_debug(LD_EXIT,"Creating new exit connection."); 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->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) { origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ); 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()."); + 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 */ switch (dns_resolve(n_stream, NULL)) { 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 conn is a rendezvous stream, or 0 if * it is a general stream. */ diff --git a/src/or/or.h b/src/or/or.h index eb8f694b90..cba4db60dd 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -450,6 +450,7 @@ typedef enum { #define RELAY_COMMAND_DROP 10 #define RELAY_COMMAND_RESOLVE 11 #define RELAY_COMMAND_RESOLVED 12 +#define RELAY_COMMAND_BEGIN_DIR 13 #define RELAY_COMMAND_ESTABLISH_INTRO 32 #define RELAY_COMMAND_ESTABLISH_RENDEZVOUS 33 @@ -474,6 +475,7 @@ typedef enum { #define END_STREAM_REASON_RESOURCELIMIT 11 #define END_STREAM_REASON_CONNRESET 12 #define END_STREAM_REASON_TORPROTOCOL 13 +#define END_STREAM_REASON_NOTDIRECTORY 14 /* 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 diff --git a/src/or/relay.c b/src/or/relay.c index 8d9ce04fb1..16ecdd1be8 100644 --- a/src/or/relay.c +++ b/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."); return 0; case RELAY_COMMAND_BEGIN: + case RELAY_COMMAND_BEGIN_DIR: if (layer_hint && circ->purpose != CIRCUIT_PURPOSE_S_REND_JOINED) { log_warn(LD_APP,"relay begin request unsupported at AP. Dropping.");