Comment the heck out of the stream/circuit attaching process.

This commit is contained in:
Nick Mathewson 2016-10-27 12:03:52 -04:00
parent dc79504e2a
commit f3e158edf7
3 changed files with 168 additions and 59 deletions

View File

@ -376,29 +376,38 @@ addressmap_rewrite(char *address, size_t maxlen,
char *addr_orig = tor_strdup(address); char *addr_orig = tor_strdup(address);
char *log_addr_orig = NULL; char *log_addr_orig = NULL;
/* We use a loop here to limit the total number of rewrites we do,
* so that we can't hit an infinite loop. */
for (rewrites = 0; rewrites < 16; rewrites++) { for (rewrites = 0; rewrites < 16; rewrites++) {
int exact_match = 0; int exact_match = 0;
log_addr_orig = tor_strdup(escaped_safe_str_client(address)); log_addr_orig = tor_strdup(escaped_safe_str_client(address));
/* First check to see if there's an exact match for this address */
ent = strmap_get(addressmap, address); ent = strmap_get(addressmap, address);
if (!ent || !ent->new_address) { if (!ent || !ent->new_address) {
/* And if we don't have an exact match, try to check whether
* we have a pattern-based match.
*/
ent = addressmap_match_superdomains(address); ent = addressmap_match_superdomains(address);
} else { } else {
if (ent->src_wildcard && !ent->dst_wildcard && if (ent->src_wildcard && !ent->dst_wildcard &&
!strcasecmp(address, ent->new_address)) { !strcasecmp(address, ent->new_address)) {
/* This is a rule like *.example.com example.com, and we just got /* This is a rule like "rewrite *.example.com to example.com", and we
* "example.com" */ * just got "example.com". Instead of calling it an infinite loop,
* call it complete. */
goto done; goto done;
} }
exact_match = 1; exact_match = 1;
} }
if (!ent || !ent->new_address) { if (!ent || !ent->new_address) {
/* We still have no match at all. We're done! */
goto done; goto done;
} }
/* Check wither the flags we were passed tell us not to use this
* mapping. */
switch (ent->source) { switch (ent->source) {
case ADDRMAPSRC_DNS: case ADDRMAPSRC_DNS:
{ {
@ -431,6 +440,8 @@ addressmap_rewrite(char *address, size_t maxlen,
goto done; goto done;
} }
/* Now fill in the address with the new address. That might be via
* appending some new stuff to the end, or via just replacing it. */
if (ent->dst_wildcard && !exact_match) { if (ent->dst_wildcard && !exact_match) {
strlcat(address, ".", maxlen); strlcat(address, ".", maxlen);
strlcat(address, ent->new_address, maxlen); strlcat(address, ent->new_address, maxlen);
@ -438,6 +449,7 @@ addressmap_rewrite(char *address, size_t maxlen,
strlcpy(address, ent->new_address, maxlen); strlcpy(address, ent->new_address, maxlen);
} }
/* Is this now a .exit address? If so, remember where we got it.*/
if (!strcmpend(address, ".exit") && if (!strcmpend(address, ".exit") &&
strcmpend(addr_orig, ".exit") && strcmpend(addr_orig, ".exit") &&
exit_source == ADDRMAPSRC_NONE) { exit_source == ADDRMAPSRC_NONE) {

View File

@ -14,7 +14,7 @@
* module keeps track of which streams can be attached to which circuits (in * module keeps track of which streams can be attached to which circuits (in
* circuit_get_best()), and attaches streams to circuits (with * circuit_get_best()), and attaches streams to circuits (with
* circuit_try_attaching_streams(), connection_ap_handshake_attach_circuit(), * circuit_try_attaching_streams(), connection_ap_handshake_attach_circuit(),
* and connection_ap_handshake_attach_chosen_circuit(). * and connection_ap_handshake_attach_chosen_circuit() ).
* *
* This module also makes sure that we are building circuits for all of the * This module also makes sure that we are building circuits for all of the
* predicted ports, using circuit_remove_handled_ports(), * predicted ports, using circuit_remove_handled_ports(),
@ -1876,16 +1876,22 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
c->state, conn_state_to_string(c->type, c->state)); c->state, conn_state_to_string(c->type, c->state));
} }
tor_assert(ENTRY_TO_CONN(conn)->state == AP_CONN_STATE_CIRCUIT_WAIT); tor_assert(ENTRY_TO_CONN(conn)->state == AP_CONN_STATE_CIRCUIT_WAIT);
/* Will the exit policy of the exit node apply to this stream? */
check_exit_policy = check_exit_policy =
conn->socks_request->command == SOCKS_COMMAND_CONNECT && conn->socks_request->command == SOCKS_COMMAND_CONNECT &&
!conn->use_begindir && !conn->use_begindir &&
!connection_edge_is_rendezvous_stream(ENTRY_TO_EDGE_CONN(conn)); !connection_edge_is_rendezvous_stream(ENTRY_TO_EDGE_CONN(conn));
/* Does this connection want a one-hop circuit? */
want_onehop = conn->want_onehop; want_onehop = conn->want_onehop;
/* Do we need a high-uptime circuit? */
need_uptime = !conn->want_onehop && !conn->use_begindir && need_uptime = !conn->want_onehop && !conn->use_begindir &&
smartlist_contains_int_as_string(options->LongLivedPorts, smartlist_contains_int_as_string(options->LongLivedPorts,
conn->socks_request->port); conn->socks_request->port);
/* Do we need an "internal" circuit? */
if (desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL) if (desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL)
need_internal = 1; need_internal = 1;
else if (conn->use_begindir || conn->want_onehop) else if (conn->use_begindir || conn->want_onehop)
@ -1893,21 +1899,31 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
else else
need_internal = 0; need_internal = 0;
circ = circuit_get_best(conn, 1, desired_circuit_purpose, /* We now know what kind of circuit we need. See if there is an
* open circuit that we can use for this stream */
circ = circuit_get_best(conn, 1 /* Insist on open circuits */,
desired_circuit_purpose,
need_uptime, need_internal); need_uptime, need_internal);
if (circ) { if (circ) {
/* We got a circuit that will work for this stream! We can return it. */
*circp = circ; *circp = circ;
return 1; /* we're happy */ return 1; /* we're happy */
} }
/* Okay, there's no circuit open that will work for this stream. Let's
* see if there's an in-progress circuit or if we have to launch one */
/* Do we know enough directory info to build circuits at all? */
int have_path = have_enough_path_info(!need_internal); int have_path = have_enough_path_info(!need_internal);
if (!want_onehop && (!router_have_minimum_dir_info() || !have_path)) { if (!want_onehop && (!router_have_minimum_dir_info() || !have_path)) {
/* If we don't have enough directory information, we can't build
* multihop circuits.
*/
if (!connection_get_by_type(CONN_TYPE_DIR)) { if (!connection_get_by_type(CONN_TYPE_DIR)) {
int severity = LOG_NOTICE; int severity = LOG_NOTICE;
/* FFFF if this is a tunneled directory fetch, don't yell /* Retry some stuff that might help the connection work. */
* as loudly. the user doesn't even know it's happening. */
if (entry_list_is_constrained(options) && if (entry_list_is_constrained(options) &&
entries_known_but_down(options)) { entries_known_but_down(options)) {
log_fn(severity, LD_APP|LD_DIR, log_fn(severity, LD_APP|LD_DIR,
@ -1928,14 +1944,16 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
routerlist_retry_directory_downloads(time(NULL)); routerlist_retry_directory_downloads(time(NULL));
} }
} }
/* the stream will be dealt with when router_have_minimum_dir_info becomes /* Since we didn't have enough directory info, we can't attach now. The
* 1, or when all directory attempts fail and directory_all_unreachable() * stream will be dealt with when router_have_minimum_dir_info becomes 1,
* or when all directory attempts fail and directory_all_unreachable()
* kills it. * kills it.
*/ */
return 0; return 0;
} }
/* Do we need to check exit policy? */ /* Check whether the exit policy of the chosen exit, or the exit policies
* of _all_ nodes, would forbid this node. */
if (check_exit_policy) { if (check_exit_policy) {
if (!conn->chosen_exit_name) { if (!conn->chosen_exit_name) {
struct in_addr in; struct in_addr in;
@ -1976,16 +1994,25 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
} }
} }
/* is one already on the way? */ /* Now, check whether there already a circuit on the way that could handle
circ = circuit_get_best(conn, 0, desired_circuit_purpose, * this stream. This check matches the one above, but this time we
* do not require that the circuit will work. */
circ = circuit_get_best(conn, 0 /* don't insist on open circuits */,
desired_circuit_purpose,
need_uptime, need_internal); need_uptime, need_internal);
if (circ) if (circ)
log_debug(LD_CIRC, "one on the way!"); log_debug(LD_CIRC, "one on the way!");
if (!circ) { if (!circ) {
/* No open or in-progress circuit could handle this stream! We
* will have to launch one!
*/
/* THe chosen exit node, if there is one. */
extend_info_t *extend_info=NULL; extend_info_t *extend_info=NULL;
uint8_t new_circ_purpose;
const int n_pending = count_pending_general_client_circuits(); const int n_pending = count_pending_general_client_circuits();
/* Do we have too many pending circuits? */
if (n_pending >= options->MaxClientCircuitsPending) { if (n_pending >= options->MaxClientCircuitsPending) {
static ratelim_t delay_limit = RATELIM_INIT(10*60); static ratelim_t delay_limit = RATELIM_INIT(10*60);
char *m; char *m;
@ -1999,6 +2026,8 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
return 0; return 0;
} }
/* If this is a hidden service trying to start an introduction point,
* handle that case. */
if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) { if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
/* need to pick an intro point */ /* need to pick an intro point */
rend_data_t *rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data; rend_data_t *rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data;
@ -2036,7 +2065,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
"Discarding this circuit.", conn->chosen_exit_name); "Discarding this circuit.", conn->chosen_exit_name);
return -1; return -1;
} }
} else { } else { /* ! (r && node_has_descriptor(r)) */
log_debug(LD_DIR, "considering %d, %s", log_debug(LD_DIR, "considering %d, %s",
want_onehop, conn->chosen_exit_name); want_onehop, conn->chosen_exit_name);
if (want_onehop && conn->chosen_exit_name[0] == '$') { if (want_onehop && conn->chosen_exit_name[0] == '$') {
@ -2059,7 +2088,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
extend_info = extend_info_new(conn->chosen_exit_name+1, extend_info = extend_info_new(conn->chosen_exit_name+1,
digest, NULL, NULL, &addr, digest, NULL, NULL, &addr,
conn->socks_request->port); conn->socks_request->port);
} else { } else { /* ! (want_onehop && conn->chosen_exit_name[0] == '$') */
/* We will need an onion key for the router, and we /* We will need an onion key for the router, and we
* don't have one. Refuse or relax requirements. */ * don't have one. Refuse or relax requirements. */
log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP, log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP,
@ -2077,8 +2106,10 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
} }
} }
} }
} } /* Done checking for general circutis with chosen exits. */
/* What purpose do we need to launch this circuit with? */
uint8_t new_circ_purpose;
if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_REND_JOINED) if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_REND_JOINED)
new_circ_purpose = CIRCUIT_PURPOSE_C_ESTABLISH_REND; new_circ_purpose = CIRCUIT_PURPOSE_C_ESTABLISH_REND;
else if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) else if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT)
@ -2087,6 +2118,8 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
new_circ_purpose = desired_circuit_purpose; new_circ_purpose = desired_circuit_purpose;
#ifdef ENABLE_TOR2WEB_MODE #ifdef ENABLE_TOR2WEB_MODE
/* If tor2Web is on, then hidden service requests should be one-hop.
*/
if (options->Tor2webMode && if (options->Tor2webMode &&
(new_circ_purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND || (new_circ_purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND ||
new_circ_purpose == CIRCUIT_PURPOSE_C_INTRODUCING)) { new_circ_purpose == CIRCUIT_PURPOSE_C_INTRODUCING)) {
@ -2094,6 +2127,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
} }
#endif #endif
/* Determine what kind of a circuit to launch, and actually launch it. */
{ {
int flags = CIRCLAUNCH_NEED_CAPACITY; int flags = CIRCLAUNCH_NEED_CAPACITY;
if (want_onehop) flags |= CIRCLAUNCH_ONEHOP_TUNNEL; if (want_onehop) flags |= CIRCLAUNCH_ONEHOP_TUNNEL;
@ -2105,6 +2139,8 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
extend_info_free(extend_info); extend_info_free(extend_info);
/* Now trigger things that need to happen when we launch circuits */
if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL) { if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL) {
/* We just caused a circuit to get built because of this stream. /* We just caused a circuit to get built because of this stream.
* If this stream has caused a _lot_ of circuits to be built, that's * If this stream has caused a _lot_ of circuits to be built, that's
@ -2128,6 +2164,10 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
} }
} }
} /* endif (!circ) */ } /* endif (!circ) */
/* We either found a good circuit, or launched a new circuit, or failed to
* do so. Report success, and delay. */
if (circ) { if (circ) {
/* Mark the circuit with the isolation fields for this connection. /* Mark the circuit with the isolation fields for this connection.
* When the circuit arrives, we'll clear these flags: this is * When the circuit arrives, we'll clear these flags: this is
@ -2327,7 +2367,9 @@ connection_ap_handshake_attach_chosen_circuit(entry_connection_t *conn,
pathbias_count_use_attempt(circ); pathbias_count_use_attempt(circ);
/* Now, actually link the connection. */
link_apconn_to_circ(conn, circ, cpath); link_apconn_to_circ(conn, circ, cpath);
tor_assert(conn->socks_request); tor_assert(conn->socks_request);
if (conn->socks_request->command == SOCKS_COMMAND_CONNECT) { if (conn->socks_request->command == SOCKS_COMMAND_CONNECT) {
if (!conn->use_begindir) if (!conn->use_begindir)
@ -2342,12 +2384,11 @@ connection_ap_handshake_attach_chosen_circuit(entry_connection_t *conn,
return 1; return 1;
} }
/** Try to find a safe live circuit for CONN_TYPE_AP connection conn. If /** Try to find a safe live circuit for stream <b>conn</b>. If we find one,
* we don't find one: if conn cannot be handled by any known nodes, * attach the stream, send appropriate cells, and return 1. Otherwise,
* warn and return -1 (conn needs to die, and is maybe already marked); * try to launch new circuit(s) for the stream. If we can launch
* else launch new circuit (if necessary) and return 0. * circuits, return 0. Otherwise, if we simply can't proceed with
* Otherwise, associate conn with a safe live circuit, do the * this stream, return -1. (conn needs to die, and is maybe already marked).
* right next step, and return 1.
*/ */
/* XXXX this function should mark for close whenever it returns -1; /* XXXX this function should mark for close whenever it returns -1;
* its callers shouldn't have to worry about that. */ * its callers shouldn't have to worry about that. */
@ -2366,6 +2407,7 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
conn_age = (int)(time(NULL) - base_conn->timestamp_created); conn_age = (int)(time(NULL) - base_conn->timestamp_created);
/* Is this connection so old that we should give up on it? */
if (conn_age >= get_options()->SocksTimeout) { if (conn_age >= get_options()->SocksTimeout) {
int severity = (tor_addr_is_null(&base_conn->addr) && !base_conn->port) ? int severity = (tor_addr_is_null(&base_conn->addr) && !base_conn->port) ?
LOG_INFO : LOG_NOTICE; LOG_INFO : LOG_NOTICE;
@ -2376,12 +2418,14 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
return -1; return -1;
} }
/* We handle "general" (non-onion) connections much more straightforwardly.
*/
if (!connection_edge_is_rendezvous_stream(ENTRY_TO_EDGE_CONN(conn))) { if (!connection_edge_is_rendezvous_stream(ENTRY_TO_EDGE_CONN(conn))) {
/* we're a general conn */ /* we're a general conn */
origin_circuit_t *circ=NULL; origin_circuit_t *circ=NULL;
/* Are we linked to a dir conn that aims to fetch a consensus? /* Are we linked to a dir conn that aims to fetch a consensus?
* We check here because this conn might no longer be needed. */ * We check here because the conn might no longer be needed. */
if (base_conn->linked_conn && if (base_conn->linked_conn &&
base_conn->linked_conn->type == CONN_TYPE_DIR && base_conn->linked_conn->type == CONN_TYPE_DIR &&
base_conn->linked_conn->purpose == DIR_PURPOSE_FETCH_CONSENSUS) { base_conn->linked_conn->purpose == DIR_PURPOSE_FETCH_CONSENSUS) {
@ -2399,6 +2443,9 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
} }
} }
/* If we have a chosen exit, we need to use a circuit that's
* open to that exit. See what exit we meant, and whether we can use it.
*/
if (conn->chosen_exit_name) { if (conn->chosen_exit_name) {
const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1); const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1);
int opt = conn->chosen_exit_optional; int opt = conn->chosen_exit_optional;
@ -2412,6 +2459,7 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
"Requested exit point '%s' is not known. %s.", "Requested exit point '%s' is not known. %s.",
conn->chosen_exit_name, opt ? "Trying others" : "Closing"); conn->chosen_exit_name, opt ? "Trying others" : "Closing");
if (opt) { if (opt) {
/* If we are allowed to ignore the .exit request, do so */
conn->chosen_exit_optional = 0; conn->chosen_exit_optional = 0;
tor_free(conn->chosen_exit_name); tor_free(conn->chosen_exit_name);
return 0; return 0;
@ -2424,6 +2472,7 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
"would refuse request. %s.", "would refuse request. %s.",
conn->chosen_exit_name, opt ? "Trying others" : "Closing"); conn->chosen_exit_name, opt ? "Trying others" : "Closing");
if (opt) { if (opt) {
/* If we are allowed to ignore the .exit request, do so */
conn->chosen_exit_optional = 0; conn->chosen_exit_optional = 0;
tor_free(conn->chosen_exit_name); tor_free(conn->chosen_exit_name);
return 0; return 0;
@ -2432,11 +2481,15 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
} }
} }
/* find the circuit that we should use, if there is one. */ /* Find the circuit that we should use, if there is one. Otherwise
* launch it. */
retval = circuit_get_open_circ_or_launch( retval = circuit_get_open_circ_or_launch(
conn, CIRCUIT_PURPOSE_C_GENERAL, &circ); conn, CIRCUIT_PURPOSE_C_GENERAL, &circ);
if (retval < 1) // XXXX++ if we totally fail, this still returns 0 -RD if (retval < 1) {
/* We were either told "-1" (complete failure) or 0 (circuit in
* progress); we can't attach this stream yet. */
return retval; return retval;
}
log_debug(LD_APP|LD_CIRC, log_debug(LD_APP|LD_CIRC,
"Attaching apconn to circ %u (stream %d sec old).", "Attaching apconn to circ %u (stream %d sec old).",
@ -2445,7 +2498,8 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
* sucking. */ * sucking. */
circuit_log_path(LOG_INFO,LD_APP|LD_CIRC,circ); circuit_log_path(LOG_INFO,LD_APP|LD_CIRC,circ);
/* We have found a suitable circuit for our conn. Hurray. */ /* We have found a suitable circuit for our conn. Hurray. Do
* the attachment. */
return connection_ap_handshake_attach_chosen_circuit(conn, circ, NULL); return connection_ap_handshake_attach_chosen_circuit(conn, circ, NULL);
} else { /* we're a rendezvous conn */ } else { /* we're a rendezvous conn */

View File

@ -830,7 +830,8 @@ connection_ap_rescan_and_attach_pending(void)
#endif #endif
/** Tell any AP streams that are listed as waiting for a new circuit to try /** Tell any AP streams that are listed as waiting for a new circuit to try
* again, either attaching to an available circ or launching a new one. * again. If there is an available circuit for a stream, attach it. Otherwise,
* launch a new circuit.
* *
* If <b>retry</b> is false, only check the list if it contains at least one * If <b>retry</b> is false, only check the list if it contains at least one
* streams that we have not yet tried to attach to a circuit. * streams that we have not yet tried to attach to a circuit.
@ -845,8 +846,9 @@ connection_ap_attach_pending(int retry)
if (untried_pending_connections == 0 && !retry) if (untried_pending_connections == 0 && !retry)
return; return;
/* Don't allow modifications to pending_entry_connections while we are /* Don't allow any modifications to list while we are iterating over
* iterating over it. */ * it. We'll put streams back on this list if we can't attach them
* immediately. */
smartlist_t *pending = pending_entry_connections; smartlist_t *pending = pending_entry_connections;
pending_entry_connections = smartlist_new(); pending_entry_connections = smartlist_new();
@ -873,6 +875,7 @@ connection_ap_attach_pending(int retry)
continue; continue;
} }
/* Okay, we're through the sanity checks. Try to handle this stream. */
if (connection_ap_handshake_attach_circuit(entry_conn) < 0) { if (connection_ap_handshake_attach_circuit(entry_conn) < 0) {
if (!conn->marked_for_close) if (!conn->marked_for_close)
connection_mark_unattached_ap(entry_conn, connection_mark_unattached_ap(entry_conn,
@ -882,12 +885,17 @@ connection_ap_attach_pending(int retry)
if (! conn->marked_for_close && if (! conn->marked_for_close &&
conn->type == CONN_TYPE_AP && conn->type == CONN_TYPE_AP &&
conn->state == AP_CONN_STATE_CIRCUIT_WAIT) { conn->state == AP_CONN_STATE_CIRCUIT_WAIT) {
/* Is it still waiting for a circuit? If so, we didn't attach it,
* so it's still pending. Put it back on the list.
*/
if (!smartlist_contains(pending_entry_connections, entry_conn)) { if (!smartlist_contains(pending_entry_connections, entry_conn)) {
smartlist_add(pending_entry_connections, entry_conn); smartlist_add(pending_entry_connections, entry_conn);
continue; continue;
} }
} }
/* If we got here, then we either closed the connection, or
* we attached it. */
UNMARK(); UNMARK();
} SMARTLIST_FOREACH_END(entry_conn); } SMARTLIST_FOREACH_END(entry_conn);
@ -1186,6 +1194,8 @@ connection_ap_handshake_rewrite(entry_connection_t *conn,
/* Remember the original address so we can tell the user about what /* Remember the original address so we can tell the user about what
* they actually said, not just what it turned into. */ * they actually said, not just what it turned into. */
/* XXX yes, this is the same as out->orig_address above. One is
* in the output, and one is in the connection. */
if (! conn->original_dest_address) { if (! conn->original_dest_address) {
/* Is the 'if' necessary here? XXXX */ /* Is the 'if' necessary here? XXXX */
conn->original_dest_address = tor_strdup(conn->socks_request->address); conn->original_dest_address = tor_strdup(conn->socks_request->address);
@ -1193,7 +1203,7 @@ connection_ap_handshake_rewrite(entry_connection_t *conn,
/* First, apply MapAddress and MAPADDRESS mappings. We need to do /* First, apply MapAddress and MAPADDRESS mappings. We need to do
* these only for non-reverse lookups, since they don't exist for those. * these only for non-reverse lookups, since they don't exist for those.
* We need to do this before we consider automapping, since we might * We also need to do this before we consider automapping, since we might
* e.g. resolve irc.oftc.net into irconionaddress.onion, at which point * e.g. resolve irc.oftc.net into irconionaddress.onion, at which point
* we'd need to automap it. */ * we'd need to automap it. */
if (socks->command != SOCKS_COMMAND_RESOLVE_PTR) { if (socks->command != SOCKS_COMMAND_RESOLVE_PTR) {
@ -1205,9 +1215,12 @@ connection_ap_handshake_rewrite(entry_connection_t *conn,
} }
} }
/* Now, handle automapping. Automapping happens when we're asked to /* Now see if we need to create or return an existing Hostname->IP
* resolve a hostname, and AutomapHostsOnResolve is set, and * automapping. Automapping happens when we're asked to resolve a
* the hostname has a suffix listed in AutomapHostsSuffixes. * hostname, and AutomapHostsOnResolve is set, and the hostname has a
* suffix listed in AutomapHostsSuffixes. It's a handy feature
* that lets you have Tor assign e.g. IPv6 addresses for .onion
* names, and return them safely from DNSPort.
*/ */
if (socks->command == SOCKS_COMMAND_RESOLVE && if (socks->command == SOCKS_COMMAND_RESOLVE &&
tor_addr_parse(&addr_tmp, socks->address)<0 && tor_addr_parse(&addr_tmp, socks->address)<0 &&
@ -1247,7 +1260,8 @@ connection_ap_handshake_rewrite(entry_connection_t *conn,
} }
/* Now handle reverse lookups, if they're in the cache. This doesn't /* Now handle reverse lookups, if they're in the cache. This doesn't
* happen too often, since client-side DNS caching is off by default. */ * happen too often, since client-side DNS caching is off by default,
* and very deprecated. */
if (socks->command == SOCKS_COMMAND_RESOLVE_PTR) { if (socks->command == SOCKS_COMMAND_RESOLVE_PTR) {
unsigned rewrite_flags = 0; unsigned rewrite_flags = 0;
if (conn->entry_cfg.use_cached_ipv4_answers) if (conn->entry_cfg.use_cached_ipv4_answers)
@ -1292,11 +1306,12 @@ connection_ap_handshake_rewrite(entry_connection_t *conn,
} }
} }
/* If we didn't automap it before, then this is still the address /* If we didn't automap it before, then this is still the address that
* that came straight from the user, mapped according to any * came straight from the user, mapped according to any
* MapAddress/MAPADDRESS commands. Now other mappings, including * MapAddress/MAPADDRESS commands. Now apply other mappings,
* previously registered Automap entries, TrackHostExits entries, * including previously registered Automap entries (IP back to
* and client-side DNS cache entries (not recommended). * hostname), TrackHostExits entries, and client-side DNS cache
* entries (if they're turned on).
*/ */
if (socks->command != SOCKS_COMMAND_RESOLVE_PTR && if (socks->command != SOCKS_COMMAND_RESOLVE_PTR &&
!out->automap) { !out->automap) {
@ -1361,11 +1376,14 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
time_t now = time(NULL); time_t now = time(NULL);
rewrite_result_t rr; rewrite_result_t rr;
/* First we'll do the rewrite part. Let's see if we get a reasonable
* answer.
*/
memset(&rr, 0, sizeof(rr)); memset(&rr, 0, sizeof(rr));
connection_ap_handshake_rewrite(conn,&rr); connection_ap_handshake_rewrite(conn,&rr);
if (rr.should_close) { if (rr.should_close) {
/* connection_ap_handshake_rewrite told us to close the connection, /* connection_ap_handshake_rewrite told us to close the connection:
* either because it sent back an answer, or because it sent back an * either because it sent back an answer, or because it sent back an
* error */ * error */
connection_mark_unattached_ap(conn, rr.end_reason); connection_mark_unattached_ap(conn, rr.end_reason);
@ -1379,8 +1397,8 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
const int automap = rr.automap; const int automap = rr.automap;
const addressmap_entry_source_t exit_source = rr.exit_source; const addressmap_entry_source_t exit_source = rr.exit_source;
/* Parse the address provided by SOCKS. Modify it in-place if it /* Now, we parse the address to see if it's an .onion or .exit or
* specifies a hidden-service (.onion) or particular exit node (.exit). * other special address.
*/ */
const hostname_type_t addresstype = parse_extended_hostname(socks->address); const hostname_type_t addresstype = parse_extended_hostname(socks->address);
@ -1394,8 +1412,8 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
} }
/* If this is a .exit hostname, strip off the .name.exit part, and /* If this is a .exit hostname, strip off the .name.exit part, and
* see whether we're going to connect there, and otherwise handle it. * see whether we're willing to connect there, and and otherwise handle the
* (The ".exit" part got stripped off by "parse_extended_hostname"). * .exit address.
* *
* We'll set chosen_exit_name and/or close the connection as appropriate. * We'll set chosen_exit_name and/or close the connection as appropriate.
*/ */
@ -1407,7 +1425,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
const node_t *node = NULL; const node_t *node = NULL;
/* If this .exit was added by an AUTOMAP, then it came straight from /* If this .exit was added by an AUTOMAP, then it came straight from
* a user. Make sure that options->AllowDotExit permits that. */ * a user. Make sure that options->AllowDotExit permits that! */
if (exit_source == ADDRMAPSRC_AUTOMAP && !options->AllowDotExit) { if (exit_source == ADDRMAPSRC_AUTOMAP && !options->AllowDotExit) {
/* Whoops; this one is stale. It must have gotten added earlier, /* Whoops; this one is stale. It must have gotten added earlier,
* when AllowDotExit was on. */ * when AllowDotExit was on. */
@ -1436,7 +1454,12 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
} }
tor_assert(!automap); tor_assert(!automap);
/* Now, find the character before the .(name) part. */
/* Now, find the character before the .(name) part.
* (The ".exit" part got stripped off by "parse_extended_hostname").
*
* We're going to put the exit name into conn->chosen_exit_name, and
* look up a node correspondingly. */
char *s = strrchr(socks->address,'.'); char *s = strrchr(socks->address,'.');
if (s) { if (s) {
/* The address was of the form "(stuff).(name).exit */ /* The address was of the form "(stuff).(name).exit */
@ -1492,10 +1515,12 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
implies no. */ implies no. */
} }
/* Now, handle everything that isn't a .onion address. */ /* Now, we handle everything that isn't a .onion address. */
if (addresstype != ONION_HOSTNAME) { if (addresstype != ONION_HOSTNAME) {
/* Not a hidden-service request. It's either a hostname or an IP, /* Not a hidden-service request. It's either a hostname or an IP,
* possibly with a .exit that we stripped off. */ * possibly with a .exit that we stripped off. We're going to check
* if we're allowed to connect/resolve there, and then launch the
* appropriate request. */
/* Check for funny characters in the address. */ /* Check for funny characters in the address. */
if (address_is_invalid_destination(socks->address, 1)) { if (address_is_invalid_destination(socks->address, 1)) {
@ -1542,7 +1567,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
} }
/* Then check if we have a hostname or IP address, and whether DNS or /* Then check if we have a hostname or IP address, and whether DNS or
* the IP address family are permitted */ * the IP address family are permitted. Reject if not. */
tor_addr_t dummy_addr; tor_addr_t dummy_addr;
int socks_family = tor_addr_parse(&dummy_addr, socks->address); int socks_family = tor_addr_parse(&dummy_addr, socks->address);
/* family will be -1 for a non-onion hostname that's not an IP */ /* family will be -1 for a non-onion hostname that's not an IP */
@ -1564,8 +1589,9 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
safe_str_client(socks->address)); safe_str_client(socks->address));
connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY); connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY);
return -1; return -1;
} else {
tor_assert_nonfatal_unreached_once();
} }
/* No else, we've covered all possible returned value. */
/* See if this is a hostname lookup that we can answer immediately. /* See if this is a hostname lookup that we can answer immediately.
* (For example, an attempt to look up the IP address for an IP address.) * (For example, an attempt to look up the IP address for an IP address.)
@ -1585,7 +1611,10 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
} }
tor_assert(!automap); tor_assert(!automap);
rep_hist_note_used_resolve(now); /* help predict this next time */ rep_hist_note_used_resolve(now); /* help predict this next time */
} else if (socks->command == SOCKS_COMMAND_CONNECT) { }
/* Now see if this is a connect request that we can reject immediately */
if (socks->command == SOCKS_COMMAND_CONNECT) {
/* Special handling for attempts to connect */ /* Special handling for attempts to connect */
tor_assert(!automap); tor_assert(!automap);
/* Don't allow connections to port 0. */ /* Don't allow connections to port 0. */
@ -1639,7 +1668,9 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
} /* end "if we should check for internal addresses" */ } /* end "if we should check for internal addresses" */
/* Okay. We're still doing a CONNECT, and it wasn't a private /* Okay. We're still doing a CONNECT, and it wasn't a private
* address. Do special handling for literal IP addresses */ * address. Here we do special handling for literal IP addresses,
* to see if we should reject this preemptively, and to set up
* fields in conn->entry_cfg to tell the exit what AF we want. */
{ {
tor_addr_t addr; tor_addr_t addr;
/* XXX Duplicate call to tor_addr_parse. */ /* XXX Duplicate call to tor_addr_parse. */
@ -1682,11 +1713,15 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
} }
} }
/* we never allow IPv6 answers on socks4. (TODO: Is this smart?) */
if (socks->socks_version == 4) if (socks->socks_version == 4)
conn->entry_cfg.ipv6_traffic = 0; conn->entry_cfg.ipv6_traffic = 0;
/* Still handling CONNECT. Now, check for exit enclaves. (Which we /* Still handling CONNECT. Now, check for exit enclaves. (Which we
* don't do on BEGINDIR, or there is a chosen exit.) * don't do on BEGINDIR, or when there is a chosen exit.)
*
* TODO: Should we remove this? Exit enclaves are nutty and don't
* work very well
*/ */
if (!conn->use_begindir && !conn->chosen_exit_name && !circ) { if (!conn->use_begindir && !conn->chosen_exit_name && !circ) {
/* see if we can find a suitable enclave exit */ /* see if we can find a suitable enclave exit */
@ -1710,7 +1745,8 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
if (consider_plaintext_ports(conn, socks->port) < 0) if (consider_plaintext_ports(conn, socks->port) < 0)
return -1; return -1;
/* Remember the port so that we do predicted requests there. */ /* Remember the port so that we will predict that more requests
there will happen in the future. */
if (!conn->use_begindir) { if (!conn->use_begindir) {
/* help predict this next time */ /* help predict this next time */
rep_hist_note_used_port(now, socks->port); rep_hist_note_used_port(now, socks->port);
@ -1735,6 +1771,8 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
if (circ) { if (circ) {
rv = connection_ap_handshake_attach_chosen_circuit(conn, circ, cpath); rv = connection_ap_handshake_attach_chosen_circuit(conn, circ, cpath);
} else { } else {
/* We'll try to attach it at the next event loop, or whenever
* we call connection_ap_attach_pending() */
connection_ap_mark_as_pending_circuit(conn); connection_ap_mark_as_pending_circuit(conn);
rv = 0; rv = 0;
} }
@ -1811,8 +1849,8 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
log_info(LD_REND,"Got a hidden service request for ID '%s'", log_info(LD_REND,"Got a hidden service request for ID '%s'",
safe_str_client(rend_data->onion_address)); safe_str_client(rend_data->onion_address));
/* Lookup the given onion address. If invalid, stop right now else we /* Lookup the given onion address. If invalid, stop right now.
* might have it in the cache or not, it will be tested later on. */ * Otherwise, we might have it in the cache or not. */
unsigned int refetch_desc = 0; unsigned int refetch_desc = 0;
rend_cache_entry_t *entry = NULL; rend_cache_entry_t *entry = NULL;
const int rend_cache_lookup_result = const int rend_cache_lookup_result =
@ -1826,6 +1864,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
return -1; return -1;
case ENOENT: case ENOENT:
/* We didn't have this; we should look it up. */
refetch_desc = 1; refetch_desc = 1;
break; break;
default: default:
@ -1835,8 +1874,9 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
} }
} }
/* Help predict this next time. We're not sure if it will need /* Help predict that we'll want to do hidden service circuits in the
* a stable circuit yet, but we know we'll need *something*. */ * future. We're not sure if it will need a stable circuit yet, but
* we know we'll need *something*. */
rep_hist_note_used_internal(now, 0, 1); rep_hist_note_used_internal(now, 0, 1);
/* Now we have a descriptor but is it usable or not? If not, refetch. /* Now we have a descriptor but is it usable or not? If not, refetch.
@ -1851,9 +1891,12 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
return 0; return 0;
} }
/* We have the descriptor so launch a connection to the HS. */ /* We have the descriptor! So launch a connection to the HS. */
base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT; base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
log_info(LD_REND, "Descriptor is here. Great."); log_info(LD_REND, "Descriptor is here. Great.");
/* We'll try to attach it at the next event loop, or whenever
* we call connection_ap_attach_pending() */
connection_ap_mark_as_pending_circuit(conn); connection_ap_mark_as_pending_circuit(conn);
return 0; return 0;
} }