diff options
Diffstat (limited to 'src/or/connection_edge.c')
-rw-r--r-- | src/or/connection_edge.c | 150 |
1 files changed, 126 insertions, 24 deletions
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 <b>conn</b> is a rendezvous stream, or 0 if * it is a general stream. */ |