diff options
Diffstat (limited to 'src/or/circuitbuild.c')
-rw-r--r-- | src/or/circuitbuild.c | 1238 |
1 files changed, 838 insertions, 400 deletions
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 72ec9e4880..07598e242f 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -23,8 +23,10 @@ #include "directory.h" #include "main.h" #include "networkstatus.h" +#include "nodelist.h" #include "onion.h" #include "policies.h" +#include "transports.h" #include "relay.h" #include "rephist.h" #include "router.h" @@ -55,8 +57,8 @@ extern circuit_t *global_circuitlist; /** An entry_guard_t represents our information about a chosen long-term * first hop, known as a "helper" node in the literature. We can't just - * use a routerinfo_t, since we want to remember these even when we - * don't have a directory. */ + * use a node_t, since we want to remember these even when we + * don't have any directory info. */ typedef struct { char nickname[MAX_NICKNAME_LEN+1]; char identity[DIGEST_LEN]; @@ -78,6 +80,28 @@ typedef struct { * at which we last failed to connect to it. */ } entry_guard_t; +/** Information about a configured bridge. Currently this just matches the + * ones in the torrc file, but one day we may be able to learn about new + * bridges on our own, and remember them in the state file. */ +typedef struct { + /** Address of the bridge. */ + tor_addr_t addr; + /** TLS port for the bridge. */ + uint16_t port; + /** Boolean: We are re-parsing our bridge list, and we are going to remove + * this one if we don't find it in the list of configured bridges. */ + unsigned marked_for_removal : 1; + /** Expected identity digest, or all zero bytes if we don't know what the + * digest should be. */ + char identity[DIGEST_LEN]; + + /** Name of pluggable transport protocol taken from its config line. */ + char *transport_name; + + /** When should we next try to fetch a descriptor for this bridge? */ + download_status_t fetch_status; +} bridge_info_t; + /** A list of our chosen entry guards. */ static smartlist_t *entry_guards = NULL; /** A value of 1 means that the entry_guards list has changed @@ -95,11 +119,13 @@ static int circuit_deliver_create_cell(circuit_t *circ, static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit); static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath); static int onion_extend_cpath(origin_circuit_t *circ); -static int count_acceptable_routers(smartlist_t *routers); +static int count_acceptable_nodes(smartlist_t *routers); static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice); static void entry_guards_changed(void); +static void bridge_free(bridge_info_t *bridge); + /** * This function decides if CBT learning should be disabled. It returns * true if one or more of the following four conditions are met: @@ -649,8 +675,7 @@ circuit_build_times_update_state(circuit_build_times_t *cbt, if (histogram[i] == 0) continue; *next = line = tor_malloc_zero(sizeof(config_line_t)); line->key = tor_strdup("CircuitBuildTimeBin"); - line->value = tor_malloc(25); - tor_snprintf(line->value, 25, "%d %d", + tor_asprintf(&line->value, "%d %d", CBT_BIN_TO_MS(i), histogram[i]); next = &(line->next); } @@ -768,7 +793,7 @@ circuit_build_times_parse_state(circuit_build_times_t *cbt, loaded_times = tor_malloc_zero(sizeof(build_time_t)*state->TotalBuildTimes); for (line = state->BuildtimeHistogram; line; line = line->next) { - smartlist_t *args = smartlist_create(); + smartlist_t *args = smartlist_new(); smartlist_split_string(args, line->value, " ", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); if (smartlist_len(args) < 2) { @@ -1515,27 +1540,24 @@ circuit_list_path_impl(origin_circuit_t *circ, int verbose, int verbose_names) const char *states[] = {"closed", "waiting for keys", "open"}; char *s; - elements = smartlist_create(); + elements = smartlist_new(); if (verbose) { const char *nickname = build_state_get_exit_nickname(circ->build_state); - char *cp; - tor_asprintf(&cp, "%s%s circ (length %d%s%s):", + smartlist_add_asprintf(elements, "%s%s circ (length %d%s%s):", circ->build_state->is_internal ? "internal" : "exit", circ->build_state->need_uptime ? " (high-uptime)" : "", circ->build_state->desired_path_len, circ->_base.state == CIRCUIT_STATE_OPEN ? "" : ", last hop ", circ->_base.state == CIRCUIT_STATE_OPEN ? "" : (nickname?nickname:"*unnamed*")); - smartlist_add(elements, cp); } hop = circ->cpath; do { - routerinfo_t *ri; - routerstatus_t *rs; char *elt; const char *id; + const node_t *node; if (!hop) break; if (!verbose && hop->state != CPATH_STATE_OPEN) @@ -1545,10 +1567,8 @@ circuit_list_path_impl(origin_circuit_t *circ, int verbose, int verbose_names) id = hop->extend_info->identity_digest; if (verbose_names) { elt = tor_malloc(MAX_VERBOSE_NICKNAME_LEN+1); - if ((ri = router_get_by_digest(id))) { - router_get_verbose_nickname(elt, ri); - } else if ((rs = router_get_consensus_status_by_id(id))) { - routerstatus_get_verbose_nickname(elt, rs); + if ((node = node_get_by_id(id))) { + node_get_verbose_nickname(node, elt); } else if (is_legal_nickname(hop->extend_info->nickname)) { elt[0] = '$'; base16_encode(elt+1, HEX_DIGEST_LEN+1, id, DIGEST_LEN); @@ -1560,9 +1580,9 @@ circuit_list_path_impl(origin_circuit_t *circ, int verbose, int verbose_names) base16_encode(elt+1, HEX_DIGEST_LEN+1, id, DIGEST_LEN); } } else { /* ! verbose_names */ - if ((ri = router_get_by_digest(id)) && - ri->is_named) { - elt = tor_strdup(hop->extend_info->nickname); + node = node_get_by_id(id); + if (node && node_is_named(node)) { + elt = tor_strdup(node_get_nickname(node)); } else { elt = tor_malloc(HEX_DIGEST_LEN+2); elt[0] = '$'; @@ -1571,11 +1591,8 @@ circuit_list_path_impl(origin_circuit_t *circ, int verbose, int verbose_names) } tor_assert(elt); if (verbose) { - size_t len = strlen(elt)+2+strlen(states[hop->state])+1; - char *v = tor_malloc(len); tor_assert(hop->state <= 2); - tor_snprintf(v,len,"%s(%s)",elt,states[hop->state]); - smartlist_add(elements, v); + smartlist_add_asprintf(elements,"%s(%s)",elt,states[hop->state]); tor_free(elt); } else { smartlist_add(elements, elt); @@ -1631,31 +1648,28 @@ void circuit_rep_hist_note_result(origin_circuit_t *circ) { crypt_path_t *hop; - char *prev_digest = NULL; - routerinfo_t *router; + const char *prev_digest = NULL; hop = circ->cpath; if (!hop) /* circuit hasn't started building yet. */ return; if (server_mode(get_options())) { - routerinfo_t *me = router_get_my_routerinfo(); + const routerinfo_t *me = router_get_my_routerinfo(); if (!me) return; prev_digest = me->cache_info.identity_digest; } do { - router = router_get_by_digest(hop->extend_info->identity_digest); - if (router) { + const node_t *node = node_get_by_id(hop->extend_info->identity_digest); + if (node) { /* Why do we check this? We know the identity. -NM XXXX */ if (prev_digest) { if (hop->state == CPATH_STATE_OPEN) - rep_hist_note_extend_succeeded(prev_digest, - router->cache_info.identity_digest); + rep_hist_note_extend_succeeded(prev_digest, node->identity); else { - rep_hist_note_extend_failed(prev_digest, - router->cache_info.identity_digest); + rep_hist_note_extend_failed(prev_digest, node->identity); break; } } - prev_digest = router->cache_info.identity_digest; + prev_digest = node->identity; } else { prev_digest = NULL; } @@ -1811,7 +1825,7 @@ circuit_n_conn_done(or_connection_t *or_conn, int status) or_conn->nickname ? or_conn->nickname : "NULL", or_conn->_base.address, status); - pending_circs = smartlist_create(); + pending_circs = smartlist_new(); circuit_get_all_pending_on_or_conn(pending_circs, or_conn); SMARTLIST_FOREACH_BEGIN(pending_circs, circuit_t *, circ) @@ -1924,7 +1938,7 @@ int inform_testing_reachability(void) { char dirbuf[128]; - routerinfo_t *me = router_get_my_routerinfo(); + const routerinfo_t *me = router_get_my_routerinfo(); if (!me) return 0; control_event_server_status(LOG_NOTICE, @@ -1953,7 +1967,7 @@ inform_testing_reachability(void) static INLINE int should_use_create_fast_for_circuit(origin_circuit_t *circ) { - or_options_t *options = get_options(); + const or_options_t *options = get_options(); tor_assert(circ->cpath); tor_assert(circ->cpath->extend_info); @@ -1997,7 +2011,7 @@ int circuit_send_next_onion_skin(origin_circuit_t *circ) { crypt_path_t *hop; - routerinfo_t *router; + const node_t *node; char payload[2+4+DIGEST_LEN+ONIONSKIN_CHALLENGE_LEN]; char *onionskin; size_t payload_len; @@ -2013,7 +2027,7 @@ circuit_send_next_onion_skin(origin_circuit_t *circ) else control_event_bootstrap(BOOTSTRAP_STATUS_CIRCUIT_CREATE, 0); - router = router_get_by_digest(circ->_base.n_conn->identity_digest); + node = node_get_by_id(circ->_base.n_conn->identity_digest); fast = should_use_create_fast_for_circuit(circ); if (!fast) { /* We are an OR and we know the right onion key: we should @@ -2047,7 +2061,7 @@ circuit_send_next_onion_skin(origin_circuit_t *circ) circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING); log_info(LD_CIRC,"First hop: finished sending %s cell to '%s'", fast ? "CREATE_FAST" : "CREATE", - router ? router_describe(router) : "<unnamed>"); + node ? node_describe(node) : "<unnamed>"); } else { tor_assert(circ->cpath->state == CPATH_STATE_OPEN); tor_assert(circ->_base.state == CIRCUIT_STATE_BUILDING); @@ -2089,7 +2103,7 @@ circuit_send_next_onion_skin(origin_circuit_t *circ) if (circ->build_state->onehop_tunnel) control_event_bootstrap(BOOTSTRAP_STATUS_REQUESTING_STATUS, 0); if (!can_complete_circuit && !circ->build_state->onehop_tunnel) { - or_options_t *options = get_options(); + const or_options_t *options = get_options(); can_complete_circuit=1; /* FFFF Log a count of known routers here */ log_notice(LD_GENERAL, @@ -2097,6 +2111,7 @@ circuit_send_next_onion_skin(origin_circuit_t *circ) "Looks like client functionality is working."); control_event_bootstrap(BOOTSTRAP_STATUS_DONE, 0); control_event_client_status(LOG_NOTICE, "CIRCUIT_ESTABLISHED"); + clear_broken_connection_map(1); if (server_mode(options) && !check_whether_orport_reachable()) { inform_testing_reachability(); consider_testing_reachability(1, 1); @@ -2305,17 +2320,17 @@ int circuit_init_cpath_crypto(crypt_path_t *cpath, const char *key_data, int reverse) { - crypto_digest_env_t *tmp_digest; - crypto_cipher_env_t *tmp_crypto; + crypto_digest_t *tmp_digest; + crypto_cipher_t *tmp_crypto; tor_assert(cpath); tor_assert(key_data); tor_assert(!(cpath->f_crypto || cpath->b_crypto || cpath->f_digest || cpath->b_digest)); - cpath->f_digest = crypto_new_digest_env(); + cpath->f_digest = crypto_digest_new(); crypto_digest_add_bytes(cpath->f_digest, key_data, DIGEST_LEN); - cpath->b_digest = crypto_new_digest_env(); + cpath->b_digest = crypto_digest_new(); crypto_digest_add_bytes(cpath->b_digest, key_data+DIGEST_LEN, DIGEST_LEN); if (!(cpath->f_crypto = @@ -2503,7 +2518,8 @@ onionskin_answer(or_circuit_t *circ, uint8_t cell_type, const char *payload, append_cell_to_circuit_queue(TO_CIRCUIT(circ), circ->p_conn, &cell, CELL_DIRECTION_IN, 0); - log_debug(LD_CIRC,"Finished sending 'created' cell."); + log_debug(LD_CIRC,"Finished sending '%s' cell.", + circ->is_first_hop ? "created_fast" : "created"); if (!is_local_addr(&circ->p_conn->_base.addr) && !connection_or_nonopen_was_started_here(circ->p_conn)) { @@ -2524,12 +2540,12 @@ onionskin_answer(or_circuit_t *circ, uint8_t cell_type, const char *payload, */ static int new_route_len(uint8_t purpose, extend_info_t *exit, - smartlist_t *routers) + smartlist_t *nodes) { int num_acceptable_routers; int routelen; - tor_assert(routers); + tor_assert(nodes); routelen = DEFAULT_ROUTE_LEN; if (exit && @@ -2537,10 +2553,10 @@ new_route_len(uint8_t purpose, extend_info_t *exit, purpose != CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) routelen++; - num_acceptable_routers = count_acceptable_routers(routers); + num_acceptable_routers = count_acceptable_nodes(nodes); log_debug(LD_CIRC,"Chosen route length %d (%d/%d routers suitable).", - routelen, num_acceptable_routers, smartlist_len(routers)); + routelen, num_acceptable_routers, smartlist_len(nodes)); if (num_acceptable_routers < 2) { log_info(LD_CIRC, @@ -2558,24 +2574,12 @@ new_route_len(uint8_t purpose, extend_info_t *exit, return routelen; } -/** Fetch the list of predicted ports, dup it into a smartlist of - * uint16_t's, remove the ones that are already handled by an - * existing circuit, and return it. - */ +/** Return a newly allocated list of uint16_t * for each predicted port not + * handled by a current circuit. */ static smartlist_t * circuit_get_unhandled_ports(time_t now) { - smartlist_t *source = rep_hist_get_predicted_ports(now); - smartlist_t *dest = smartlist_create(); - uint16_t *tmp; - int i; - - for (i = 0; i < smartlist_len(source); ++i) { - tmp = tor_malloc(sizeof(uint16_t)); - memcpy(tmp, smartlist_get(source, i), sizeof(uint16_t)); - smartlist_add(dest, tmp); - } - + smartlist_t *dest = rep_hist_get_predicted_ports(now); circuit_remove_handled_ports(dest); return dest; } @@ -2609,12 +2613,12 @@ circuit_all_predicted_ports_handled(time_t now, int *need_uptime, return enough; } -/** Return 1 if <b>router</b> can handle one or more of the ports in +/** Return 1 if <b>node</b> can handle one or more of the ports in * <b>needed_ports</b>, else return 0. */ static int -router_handles_some_port(routerinfo_t *router, smartlist_t *needed_ports) -{ +node_handles_some_port(const node_t *node, smartlist_t *needed_ports) +{ /* XXXX MOVE */ int i; uint16_t port; @@ -2624,7 +2628,10 @@ router_handles_some_port(routerinfo_t *router, smartlist_t *needed_ports) needed_ports is explicitly a smartlist of uint16_t's */ port = *(uint16_t *)smartlist_get(needed_ports, i); tor_assert(port); - r = compare_addr_to_addr_policy(0, port, router->exit_policy); + if (node) + r = compare_tor_addr_to_node_policy(NULL, port, node); + else + continue; if (r != ADDR_POLICY_REJECTED && r != ADDR_POLICY_PROBABLY_REJECTED) return 1; } @@ -2636,14 +2643,18 @@ router_handles_some_port(routerinfo_t *router, smartlist_t *needed_ports) static int ap_stream_wants_exit_attention(connection_t *conn) { - if (conn->type == CONN_TYPE_AP && - conn->state == AP_CONN_STATE_CIRCUIT_WAIT && + entry_connection_t *entry; + if (conn->type != CONN_TYPE_AP) + return 0; + entry = TO_ENTRY_CONN(conn); + + if (conn->state == AP_CONN_STATE_CIRCUIT_WAIT && !conn->marked_for_close && - !(TO_EDGE_CONN(conn)->want_onehop) && /* ignore one-hop streams */ - !(TO_EDGE_CONN(conn)->use_begindir) && /* ignore targeted dir fetches */ - !(TO_EDGE_CONN(conn)->chosen_exit_name) && /* ignore defined streams */ + !(entry->want_onehop) && /* ignore one-hop streams */ + !(entry->use_begindir) && /* ignore targeted dir fetches */ + !(entry->chosen_exit_name) && /* ignore defined streams */ !connection_edge_is_rendezvous_stream(TO_EDGE_CONN(conn)) && - !circuit_stream_is_being_handled(TO_EDGE_CONN(conn), 0, + !circuit_stream_is_being_handled(TO_ENTRY_CONN(conn), 0, MIN_CIRCUITS_HANDLING_STREAM)) return 1; return 0; @@ -2657,18 +2668,17 @@ ap_stream_wants_exit_attention(connection_t *conn) * * Return NULL if we can't find any suitable routers. */ -static routerinfo_t * -choose_good_exit_server_general(routerlist_t *dir, int need_uptime, - int need_capacity) +static const node_t * +choose_good_exit_server_general(int need_uptime, int need_capacity) { int *n_supported; - int i; int n_pending_connections = 0; smartlist_t *connections; int best_support = -1; int n_best_support=0; - routerinfo_t *router; - or_options_t *options = get_options(); + const or_options_t *options = get_options(); + const smartlist_t *the_nodes; + const node_t *node=NULL; connections = get_connection_array(); @@ -2689,10 +2699,11 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime, * * -1 means "Don't use this router at all." */ - n_supported = tor_malloc(sizeof(int)*smartlist_len(dir->routers)); - for (i = 0; i < smartlist_len(dir->routers); ++i) {/* iterate over routers */ - router = smartlist_get(dir->routers, i); - if (router_is_me(router)) { + the_nodes = nodelist_get_list(); + n_supported = tor_malloc(sizeof(int)*smartlist_len(the_nodes)); + SMARTLIST_FOREACH_BEGIN(the_nodes, const node_t *, node) { + const int i = node_sl_idx; + if (router_digest_is_me(node->identity)) { n_supported[i] = -1; // log_fn(LOG_DEBUG,"Skipping node %s -- it's me.", router->nickname); /* XXX there's probably a reverse predecessor attack here, but @@ -2700,41 +2711,44 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime, */ continue; } - if (!router->is_running || router->is_bad_exit) { + if (!node_has_descriptor(node)) { + n_supported[i] = -1; + continue; + } + if (!node->is_running || node->is_bad_exit) { n_supported[i] = -1; continue; /* skip routers that are known to be down or bad exits */ } - - if (options->_ExcludeExitNodesUnion && - routerset_contains_router(options->_ExcludeExitNodesUnion, router)) { + if (routerset_contains_node(options->_ExcludeExitNodesUnion, node)) { n_supported[i] = -1; continue; /* user asked us not to use it, no matter what */ } if (options->ExitNodes && - !routerset_contains_router(options->ExitNodes, router)) { + !routerset_contains_node(options->ExitNodes, node)) { n_supported[i] = -1; continue; /* not one of our chosen exit nodes */ } - if (router_is_unreliable(router, need_uptime, need_capacity, 0)) { + if (node_is_unreliable(node, need_uptime, need_capacity, 0)) { n_supported[i] = -1; continue; /* skip routers that are not suitable. Don't worry if * this makes us reject all the possible routers: if so, * we'll retry later in this function with need_update and * need_capacity set to 0. */ } - if (!(router->is_valid || options->_AllowInvalid & ALLOW_INVALID_EXIT)) { + if (!(node->is_valid || options->_AllowInvalid & ALLOW_INVALID_EXIT)) { /* if it's invalid and we don't want it */ n_supported[i] = -1; // log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- invalid router.", // router->nickname, i); continue; /* skip invalid routers */ } - if (options->ExcludeSingleHopRelays && router->allow_single_hop_exits) { + if (options->ExcludeSingleHopRelays && + node_allows_single_hop_exits(node)) { n_supported[i] = -1; continue; } - if (router_exit_policy_rejects_all(router)) { + if (node_exit_policy_rejects_all(node)) { n_supported[i] = -1; // log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- it rejects all.", // router->nickname, i); @@ -2742,11 +2756,10 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime, } n_supported[i] = 0; /* iterate over connections */ - SMARTLIST_FOREACH(connections, connection_t *, conn, - { + SMARTLIST_FOREACH_BEGIN(connections, connection_t *, conn) { if (!ap_stream_wants_exit_attention(conn)) continue; /* Skip everything but APs in CIRCUIT_WAIT */ - if (connection_ap_can_use_exit(TO_EDGE_CONN(conn), router)) { + if (connection_ap_can_use_exit(TO_ENTRY_CONN(conn), node)) { ++n_supported[i]; // log_fn(LOG_DEBUG,"%s is supported. n_supported[%d] now %d.", // router->nickname, i, n_supported[i]); @@ -2754,7 +2767,7 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime, // log_fn(LOG_DEBUG,"%s (index %d) would reject this stream.", // router->nickname, i); } - }); /* End looping over connections. */ + } SMARTLIST_FOREACH_END(conn); if (n_pending_connections > 0 && n_supported[i] == 0) { /* Leave best_support at -1 if that's where it is, so we can * distinguish it later. */ @@ -2771,7 +2784,7 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime, * count of equally good routers.*/ ++n_best_support; } - } + } SMARTLIST_FOREACH_END(node); log_info(LD_CIRC, "Found %d servers that might support %d/%d pending connections.", n_best_support, best_support >= 0 ? best_support : 0, @@ -2780,13 +2793,14 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime, /* If any routers definitely support any pending connections, choose one * at random. */ if (best_support > 0) { - smartlist_t *supporting = smartlist_create(); + smartlist_t *supporting = smartlist_new(); - for (i = 0; i < smartlist_len(dir->routers); i++) - if (n_supported[i] == best_support) - smartlist_add(supporting, smartlist_get(dir->routers, i)); + SMARTLIST_FOREACH(the_nodes, const node_t *, node, { + if (n_supported[node_sl_idx] == best_support) + smartlist_add(supporting, (void*)node); + }); - router = routerlist_sl_choose_by_bandwidth(supporting, WEIGHT_FOR_EXIT); + node = node_sl_choose_by_bandwidth(supporting, WEIGHT_FOR_EXIT); smartlist_free(supporting); } else { /* Either there are no pending connections, or no routers even seem to @@ -2804,29 +2818,28 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime, need_capacity?", fast":"", need_uptime?", stable":""); tor_free(n_supported); - return choose_good_exit_server_general(dir, 0, 0); + return choose_good_exit_server_general(0, 0); } log_notice(LD_CIRC, "All routers are down or won't exit%s -- " "choosing a doomed exit at random.", options->_ExcludeExitNodesUnion ? " or are Excluded" : ""); } - supporting = smartlist_create(); + supporting = smartlist_new(); needed_ports = circuit_get_unhandled_ports(time(NULL)); for (attempt = 0; attempt < 2; attempt++) { /* try once to pick only from routers that satisfy a needed port, * then if there are none, pick from any that support exiting. */ - for (i = 0; i < smartlist_len(dir->routers); i++) { - router = smartlist_get(dir->routers, i); - if (n_supported[i] != -1 && - (attempt || router_handles_some_port(router, needed_ports))) { + SMARTLIST_FOREACH_BEGIN(the_nodes, const node_t *, node) { + if (n_supported[node_sl_idx] != -1 && + (attempt || node_handles_some_port(node, needed_ports))) { // log_fn(LOG_DEBUG,"Try %d: '%s' is a possibility.", // try, router->nickname); - smartlist_add(supporting, router); + smartlist_add(supporting, (void*)node); } - } + } SMARTLIST_FOREACH_END(node); - router = routerlist_sl_choose_by_bandwidth(supporting, WEIGHT_FOR_EXIT); - if (router) + node = node_sl_choose_by_bandwidth(supporting, WEIGHT_FOR_EXIT); + if (node) break; smartlist_clear(supporting); } @@ -2836,9 +2849,9 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime, } tor_free(n_supported); - if (router) { - log_info(LD_CIRC, "Chose exit server '%s'", router_describe(router)); - return router; + if (node) { + log_info(LD_CIRC, "Chose exit server '%s'", node_describe(node)); + return node; } if (options->ExitNodes) { log_warn(LD_CIRC, @@ -2859,12 +2872,12 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime, * For client-side rendezvous circuits, choose a random node, weighted * toward the preferences in 'options'. */ -static routerinfo_t * -choose_good_exit_server(uint8_t purpose, routerlist_t *dir, +static const node_t * +choose_good_exit_server(uint8_t purpose, int need_uptime, int need_capacity, int is_internal) { - or_options_t *options = get_options(); - router_crn_flags_t flags = 0; + const or_options_t *options = get_options(); + router_crn_flags_t flags = CRN_NEED_DESC; if (need_uptime) flags |= CRN_NEED_UPTIME; if (need_capacity) @@ -2877,7 +2890,7 @@ choose_good_exit_server(uint8_t purpose, routerlist_t *dir, if (is_internal) /* pick it like a middle hop */ return router_choose_random_node(NULL, options->ExcludeNodes, flags); else - return choose_good_exit_server_general(dir,need_uptime,need_capacity); + return choose_good_exit_server_general(need_uptime,need_capacity); case CIRCUIT_PURPOSE_C_ESTABLISH_REND: if (options->_AllowInvalid & ALLOW_INVALID_RENDEZVOUS) flags |= CRN_ALLOW_INVALID; @@ -2893,7 +2906,7 @@ choose_good_exit_server(uint8_t purpose, routerlist_t *dir, static void warn_if_last_router_excluded(origin_circuit_t *circ, const extend_info_t *exit) { - or_options_t *options = get_options(); + const or_options_t *options = get_options(); routerset_t *rs = options->ExcludeNodes; const char *description; uint8_t purpose = circ->_base.purpose; @@ -2970,13 +2983,12 @@ static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit) { cpath_build_state_t *state = circ->build_state; - routerlist_t *rl = router_get_routerlist(); if (state->onehop_tunnel) { log_debug(LD_CIRC, "Launching a one-hop circuit for dir tunnel."); state->desired_path_len = 1; } else { - int r = new_route_len(circ->_base.purpose, exit, rl->routers); + int r = new_route_len(circ->_base.purpose, exit, nodelist_get_list()); if (r < 1) /* must be at least 1 */ return -1; state->desired_path_len = r; @@ -2988,14 +3000,15 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit) extend_info_describe(exit)); exit = extend_info_dup(exit); } else { /* we have to decide one */ - routerinfo_t *router = - choose_good_exit_server(circ->_base.purpose, rl, state->need_uptime, + const node_t *node = + choose_good_exit_server(circ->_base.purpose, state->need_uptime, state->need_capacity, state->is_internal); - if (!router) { + if (!node) { log_warn(LD_CIRC,"failed to choose an exit server"); return -1; } - exit = extend_info_from_router(router); + exit = extend_info_from_node(node, 0); + tor_assert(exit); } state->chosen_exit = exit; return 0; @@ -3046,35 +3059,30 @@ circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *exit) * and available for building circuits through. */ static int -count_acceptable_routers(smartlist_t *routers) +count_acceptable_nodes(smartlist_t *nodes) { - int i, n; int num=0; - routerinfo_t *r; - n = smartlist_len(routers); - for (i=0;i<n;i++) { - r = smartlist_get(routers, i); -// log_debug(LD_CIRC, + SMARTLIST_FOREACH_BEGIN(nodes, const node_t *, node) { + // log_debug(LD_CIRC, // "Contemplating whether router %d (%s) is a new option.", // i, r->nickname); - if (r->is_running == 0) { + if (! node->is_running) // log_debug(LD_CIRC,"Nope, the directory says %d is not running.",i); - goto next_i_loop; - } - if (r->is_valid == 0) { + continue; + if (! node->is_valid) // log_debug(LD_CIRC,"Nope, the directory says %d is not valid.",i); - goto next_i_loop; + continue; + if (! node_has_descriptor(node)) + continue; /* XXX This clause makes us count incorrectly: if AllowInvalidRouters * allows this node in some places, then we're getting an inaccurate * count. For now, be conservative and don't count it. But later we * should try to be smarter. */ - } - num++; + ++num; + } SMARTLIST_FOREACH_END(node); + // log_debug(LD_CIRC,"I like %d. num_acceptable_routers now %d.",i, num); - next_i_loop: - ; /* C requires an explicit statement after the label */ - } return num; } @@ -3102,31 +3110,29 @@ onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop) * circuit. In particular, make sure we don't pick the exit node or its * family, and make sure we don't duplicate any previous nodes or their * families. */ -static routerinfo_t * +static const node_t * choose_good_middle_server(uint8_t purpose, cpath_build_state_t *state, crypt_path_t *head, int cur_len) { int i; - routerinfo_t *r, *choice; + const node_t *r, *choice; crypt_path_t *cpath; smartlist_t *excluded; - or_options_t *options = get_options(); - router_crn_flags_t flags = 0; + const or_options_t *options = get_options(); + router_crn_flags_t flags = CRN_NEED_DESC; tor_assert(_CIRCUIT_PURPOSE_MIN <= purpose && purpose <= _CIRCUIT_PURPOSE_MAX); log_debug(LD_CIRC, "Contemplating intermediate hop: random choice."); - excluded = smartlist_create(); - if ((r = build_state_get_exit_router(state))) { - smartlist_add(excluded, r); - routerlist_add_family(excluded, r); + excluded = smartlist_new(); + if ((r = build_state_get_exit_node(state))) { + nodelist_add_node_and_family(excluded, r); } for (i = 0, cpath = head; i < cur_len; ++i, cpath=cpath->next) { - if ((r = router_get_by_digest(cpath->extend_info->identity_digest))) { - smartlist_add(excluded, r); - routerlist_add_family(excluded, r); + if ((r = node_get_by_id(cpath->extend_info->identity_digest))) { + nodelist_add_node_and_family(excluded, r); } } @@ -3149,44 +3155,43 @@ choose_good_middle_server(uint8_t purpose, * If <b>state</b> is NULL, we're choosing a router to serve as an entry * guard, not for any particular circuit. */ -static routerinfo_t * +static const node_t * choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state) { - routerinfo_t *r, *choice; + const node_t *choice; smartlist_t *excluded; - or_options_t *options = get_options(); - router_crn_flags_t flags = CRN_NEED_GUARD; + const or_options_t *options = get_options(); + router_crn_flags_t flags = CRN_NEED_GUARD|CRN_NEED_DESC; + const node_t *node; if (state && options->UseEntryGuards && (purpose != CIRCUIT_PURPOSE_TESTING || options->BridgeRelay)) { + /* This is request for an entry server to use for a regular circuit, + * and we use entry guard nodes. Just return one of the guard nodes. */ return choose_random_entry(state); } - excluded = smartlist_create(); + excluded = smartlist_new(); - if (state && (r = build_state_get_exit_router(state))) { - smartlist_add(excluded, r); - routerlist_add_family(excluded, r); + if (state && (node = build_state_get_exit_node(state))) { + /* Exclude the exit node from the state, if we have one. Also exclude its + * family. */ + nodelist_add_node_and_family(excluded, node); } if (firewall_is_fascist_or()) { - /*XXXX This could slow things down a lot; use a smarter implementation */ - /* exclude all ORs that listen on the wrong port, if anybody notices. */ - routerlist_t *rl = router_get_routerlist(); - int i; - - for (i=0; i < smartlist_len(rl->routers); i++) { - r = smartlist_get(rl->routers, i); - if (!fascist_firewall_allows_or(r)) - smartlist_add(excluded, r); - } + /* Exclude all ORs that we can't reach through our firewall */ + smartlist_t *nodes = nodelist_get_list(); + SMARTLIST_FOREACH(nodes, const node_t *, node, { + if (!fascist_firewall_allows_node(node)) + smartlist_add(excluded, (void*)node); + }); } - /* and exclude current entry guards, if applicable */ + /* and exclude current entry guards and their families, if applicable */ if (options->UseEntryGuards && entry_guards) { SMARTLIST_FOREACH(entry_guards, entry_guard_t *, entry, { - if ((r = router_get_by_digest(entry->identity))) { - smartlist_add(excluded, r); - routerlist_add_family(excluded, r); + if ((node = node_get_by_id(entry->identity))) { + nodelist_add_node_and_family(excluded, node); } }); } @@ -3242,14 +3247,23 @@ onion_extend_cpath(origin_circuit_t *circ) if (cur_len == state->desired_path_len - 1) { /* Picking last node */ info = extend_info_dup(state->chosen_exit); } else if (cur_len == 0) { /* picking first node */ - routerinfo_t *r = choose_good_entry_server(purpose, state); - if (r) - info = extend_info_from_router(r); + const node_t *r = choose_good_entry_server(purpose, state); + if (r) { + /* If we're extending to a bridge, use the preferred address + rather than the primary, for potentially extending to an IPv6 + bridge. */ + int use_pref_addr = (r->ri != NULL && + r->ri->purpose == ROUTER_PURPOSE_BRIDGE); + info = extend_info_from_node(r, use_pref_addr); + tor_assert(info); + } } else { - routerinfo_t *r = + const node_t *r = choose_good_middle_server(purpose, state, circ->cpath, cur_len); - if (r) - info = extend_info_from_router(r); + if (r) { + info = extend_info_from_node(r, 0); + tor_assert(info); + } } if (!info) { @@ -3292,7 +3306,7 @@ onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice) /** Allocate a new extend_info object based on the various arguments. */ extend_info_t * extend_info_alloc(const char *nickname, const char *digest, - crypto_pk_env_t *onion_key, + crypto_pk_t *onion_key, const tor_addr_t *addr, uint16_t port) { extend_info_t *info = tor_malloc_zero(sizeof(extend_info_t)); @@ -3306,16 +3320,47 @@ extend_info_alloc(const char *nickname, const char *digest, return info; } -/** Allocate and return a new extend_info_t that can be used to build a - * circuit to or through the router <b>r</b>. */ +/** Allocate and return a new extend_info_t that can be used to build + * a circuit to or through the router <b>r</b>. Use the primary + * address of the router unless <b>for_direct_connect</b> is true, in + * which case the preferred address is used instead. */ extend_info_t * -extend_info_from_router(routerinfo_t *r) +extend_info_from_router(const routerinfo_t *r, int for_direct_connect) { - tor_addr_t addr; + tor_addr_port_t ap; tor_assert(r); - tor_addr_from_ipv4h(&addr, r->addr); + + if (for_direct_connect) + router_get_pref_orport(r, &ap); + else + router_get_prim_orport(r, &ap); return extend_info_alloc(r->nickname, r->cache_info.identity_digest, - r->onion_pkey, &addr, r->or_port); + r->onion_pkey, &ap.addr, ap.port); +} + +/** Allocate and return a new extend_info that can be used to build a + * circuit to or through the node <b>node</b>. Use the primary address + * of the node unless <b>for_direct_connect</b> is true, in which case + * the preferred address is used instead. May return NULL if there is + * not enough info about <b>node</b> to extend to it--for example, if + * there is no routerinfo_t or microdesc_t. + **/ +extend_info_t * +extend_info_from_node(const node_t *node, int for_direct_connect) +{ + if (node->ri) { + return extend_info_from_router(node->ri, for_direct_connect); + } else if (node->rs && node->md) { + tor_addr_t addr; + tor_addr_from_ipv4h(&addr, node->rs->addr); + return extend_info_alloc(node->rs->nickname, + node->identity, + node->md->onion_pkey, + &addr, + node->rs->or_port); + } else { + return NULL; + } } /** Release storage held by an extend_info_t struct. */ @@ -3324,7 +3369,7 @@ extend_info_free(extend_info_t *info) { if (!info) return; - crypto_free_pk_env(info->onion_key); + crypto_pk_free(info->onion_key); tor_free(info); } @@ -3348,12 +3393,12 @@ extend_info_dup(extend_info_t *info) * If there is no chosen exit, or if we don't know the routerinfo_t for * the chosen exit, return NULL. */ -routerinfo_t * -build_state_get_exit_router(cpath_build_state_t *state) +const node_t * +build_state_get_exit_node(cpath_build_state_t *state) { if (!state || !state->chosen_exit) return NULL; - return router_get_by_digest(state->chosen_exit->identity_digest); + return node_get_by_id(state->chosen_exit->identity_digest); } /** Return the nickname for the chosen exit router in <b>state</b>. If @@ -3375,10 +3420,10 @@ build_state_get_exit_nickname(cpath_build_state_t *state) * * If it's not usable, set *<b>reason</b> to a static string explaining why. */ -/*XXXX take a routerstatus, not a routerinfo. */ static int -entry_guard_set_status(entry_guard_t *e, routerinfo_t *ri, - time_t now, or_options_t *options, const char **reason) +entry_guard_set_status(entry_guard_t *e, const node_t *node, + time_t now, const or_options_t *options, + const char **reason) { char buf[HEX_DIGEST_LEN+1]; int changed = 0; @@ -3386,18 +3431,19 @@ entry_guard_set_status(entry_guard_t *e, routerinfo_t *ri, *reason = NULL; /* Do we want to mark this guard as bad? */ - if (!ri) + if (!node) *reason = "unlisted"; - else if (!ri->is_running) + else if (!node->is_running) *reason = "down"; - else if (options->UseBridges && ri->purpose != ROUTER_PURPOSE_BRIDGE) + else if (options->UseBridges && (!node->ri || + node->ri->purpose != ROUTER_PURPOSE_BRIDGE)) *reason = "not a bridge"; - else if (options->UseBridges && !routerinfo_is_a_configured_bridge(ri)) + else if (options->UseBridges && !node_is_a_configured_bridge(node)) *reason = "not a configured bridge"; - else if (!options->UseBridges && !ri->is_possible_guard && - !routerset_contains_router(options->EntryNodes,ri)) + else if (!options->UseBridges && !node->is_possible_guard && + !routerset_contains_node(options->EntryNodes,node)) *reason = "not recommended as a guard"; - else if (routerset_contains_router(options->ExcludeNodes, ri)) + else if (routerset_contains_node(options->ExcludeNodes, node)) *reason = "excluded"; if (*reason && ! e->bad_since) { @@ -3441,7 +3487,7 @@ entry_is_time_to_retry(entry_guard_t *e, time_t now) return now > (e->last_attempted + 36*60*60); } -/** Return the router corresponding to <b>e</b>, if <b>e</b> is +/** Return the node corresponding to <b>e</b>, if <b>e</b> is * working well enough that we are willing to use it as an entry * right now. (Else return NULL.) In particular, it must be * - Listed as either up or never yet contacted; @@ -3455,12 +3501,12 @@ entry_is_time_to_retry(entry_guard_t *e, time_t now) * * If the answer is no, set *<b>msg</b> to an explanation of why. */ -static INLINE routerinfo_t * +static INLINE const node_t * entry_is_live(entry_guard_t *e, int need_uptime, int need_capacity, int assume_reachable, const char **msg) { - routerinfo_t *r; - or_options_t *options = get_options(); + const node_t *node; + const or_options_t *options = get_options(); tor_assert(msg); if (e->bad_since) { @@ -3473,38 +3519,39 @@ entry_is_live(entry_guard_t *e, int need_uptime, int need_capacity, *msg = "unreachable"; return NULL; } - r = router_get_by_digest(e->identity); - if (!r) { + node = node_get_by_id(e->identity); + if (!node || !node_has_descriptor(node)) { *msg = "no descriptor"; return NULL; } - if (options->UseBridges) { - if (r->purpose != ROUTER_PURPOSE_BRIDGE) { + if (get_options()->UseBridges) { + if (node_get_purpose(node) != ROUTER_PURPOSE_BRIDGE) { *msg = "not a bridge"; return NULL; } - if (!routerinfo_is_a_configured_bridge(r)) { + if (!node_is_a_configured_bridge(node)) { *msg = "not a configured bridge"; return NULL; } - } else if (r->purpose != ROUTER_PURPOSE_GENERAL) { - *msg = "not general-purpose"; - return NULL; + } else { /* !get_options()->UseBridges */ + if (node_get_purpose(node) != ROUTER_PURPOSE_GENERAL) { + *msg = "not general-purpose"; + return NULL; + } } - if (options->EntryNodes && - routerset_contains_router(options->EntryNodes, r)) { + if (routerset_contains_node(options->EntryNodes, node)) { /* they asked for it, they get it */ need_uptime = need_capacity = 0; } - if (router_is_unreliable(r, need_uptime, need_capacity, 0)) { + if (node_is_unreliable(node, need_uptime, need_capacity, 0)) { *msg = "not fast/stable"; return NULL; } - if (!fascist_firewall_allows_or(r)) { + if (!fascist_firewall_allows_node(node)) { *msg = "unreachable by config"; return NULL; } - return r; + return node; } /** Return the number of entry guards that we think are usable. */ @@ -3540,25 +3587,23 @@ is_an_entry_guard(const char *digest) static void log_entry_guards(int severity) { - smartlist_t *elements = smartlist_create(); + smartlist_t *elements = smartlist_new(); char *s; SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) { const char *msg = NULL; - char *cp; if (entry_is_live(e, 0, 1, 0, &msg)) - tor_asprintf(&cp, "%s [%s] (up %s)", + smartlist_add_asprintf(elements, "%s [%s] (up %s)", e->nickname, hex_str(e->identity, DIGEST_LEN), e->made_contact ? "made-contact" : "never-contacted"); else - tor_asprintf(&cp, "%s [%s] (%s, %s)", + smartlist_add_asprintf(elements, "%s [%s] (%s, %s)", e->nickname, hex_str(e->identity, DIGEST_LEN), msg, e->made_contact ? "made-contact" : "never-contacted"); - smartlist_add(elements, cp); } SMARTLIST_FOREACH_END(e); @@ -3584,7 +3629,7 @@ control_event_guard_deferred(void) #if 0 int n = 0; const char *msg; - or_options_t *options = get_options(); + const or_options_t *options = get_options(); if (!entry_guards) return; SMARTLIST_FOREACH(entry_guards, entry_guard_t *, entry, @@ -3606,15 +3651,15 @@ control_event_guard_deferred(void) * If <b>chosen</b> is defined, use that one, and if it's not * already in our entry_guards list, put it at the *beginning*. * Else, put the one we pick at the end of the list. */ -static routerinfo_t * -add_an_entry_guard(routerinfo_t *chosen, int reset_status) +static const node_t * +add_an_entry_guard(const node_t *chosen, int reset_status, int prepend) { - routerinfo_t *router; + const node_t *node; entry_guard_t *entry; if (chosen) { - router = chosen; - entry = is_an_entry_guard(router->cache_info.identity_digest); + node = chosen; + entry = is_an_entry_guard(node->identity); if (entry) { if (reset_status) { entry->bad_since = 0; @@ -3623,15 +3668,15 @@ add_an_entry_guard(routerinfo_t *chosen, int reset_status) return NULL; } } else { - router = choose_good_entry_server(CIRCUIT_PURPOSE_C_GENERAL, NULL); - if (!router) + node = choose_good_entry_server(CIRCUIT_PURPOSE_C_GENERAL, NULL); + if (!node) return NULL; } entry = tor_malloc_zero(sizeof(entry_guard_t)); - log_info(LD_CIRC, "Chose '%s' as new entry guard.", - router_describe(router)); - strlcpy(entry->nickname, router->nickname, sizeof(entry->nickname)); - memcpy(entry->identity, router->cache_info.identity_digest, DIGEST_LEN); + log_info(LD_CIRC, "Chose %s as new entry guard.", + node_describe(node)); + strlcpy(entry->nickname, node_get_nickname(node), sizeof(entry->nickname)); + memcpy(entry->identity, node->identity, DIGEST_LEN); /* Choose expiry time smudged over the past month. The goal here * is to a) spread out when Tor clients rotate their guards, so they * don't all select them on the same day, and b) avoid leaving a @@ -3639,27 +3684,27 @@ add_an_entry_guard(routerinfo_t *chosen, int reset_status) * this guard. For details, see the Jan 2010 or-dev thread. */ entry->chosen_on_date = time(NULL) - crypto_rand_int(3600*24*30); entry->chosen_by_version = tor_strdup(VERSION); - if (chosen) /* prepend */ + if (prepend) smartlist_insert(entry_guards, 0, entry); - else /* append */ + else smartlist_add(entry_guards, entry); control_event_guard(entry->nickname, entry->identity, "NEW"); control_event_guard_deferred(); log_entry_guards(LOG_INFO); - return router; + return node; } /** If the use of entry guards is configured, choose more entry guards * until we have enough in the list. */ static void -pick_entry_guards(or_options_t *options) +pick_entry_guards(const or_options_t *options) { int changed = 0; tor_assert(entry_guards); while (num_live_entry_guards() < options->NumEntryGuards) { - if (!add_an_entry_guard(NULL, 0)) + if (!add_an_entry_guard(NULL, 0, 0)) break; changed = 1; } @@ -3702,9 +3747,8 @@ remove_obsolete_entry_guards(time_t now) msg = "does not seem to be from any recognized version of Tor"; version_is_bad = 1; } else { - size_t len = strlen(ver)+5; - char *tor_ver = tor_malloc(len); - tor_snprintf(tor_ver, len, "Tor %s", ver); + char *tor_ver = NULL; + tor_asprintf(&tor_ver, "Tor %s", ver); if ((tor_version_as_new_as(tor_ver, "0.1.0.10-alpha") && !tor_version_as_new_as(tor_ver, "0.1.2.16-dev")) || (tor_version_as_new_as(tor_ver, "0.2.0.0-alpha") && @@ -3785,7 +3829,7 @@ remove_dead_entry_guards(time_t now) * think that things are unlisted. */ void -entry_guards_compute_status(or_options_t *options, time_t now) +entry_guards_compute_status(const or_options_t *options, time_t now) { int changed = 0; digestmap_t *reasons; @@ -3799,7 +3843,7 @@ entry_guards_compute_status(or_options_t *options, time_t now) reasons = digestmap_new(); SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) { - routerinfo_t *r = router_get_by_digest(entry->identity); + const node_t *r = node_get_by_id(entry->identity); const char *reason = NULL; if (entry_guard_set_status(entry, r, now, options, &reason)) changed = 1; @@ -3818,7 +3862,7 @@ entry_guards_compute_status(or_options_t *options, time_t now) SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) { const char *reason = digestmap_get(reasons, entry->identity); const char *live_msg = ""; - routerinfo_t *r = entry_is_live(entry, 0, 1, 0, &live_msg); + const node_t *r = entry_is_live(entry, 0, 1, 0, &live_msg); log_info(LD_CIRC, "Summary: Entry %s [%s] is %s, %s%s%s, and %s%s.", entry->nickname, hex_str(entry->identity, DIGEST_LEN), @@ -3936,7 +3980,7 @@ entry_guard_register_connect_status(const char *digest, int succeeded, break; if (e->made_contact) { const char *msg; - routerinfo_t *r = entry_is_live(e, 0, 1, 1, &msg); + const node_t *r = entry_is_live(e, 0, 1, 1, &msg); if (r && e->unreachable_since) { refuse_conn = 1; e->can_retry = 1; @@ -3972,12 +4016,12 @@ entry_nodes_should_be_added(void) should_add_entry_nodes = 1; } -/** Add all nodes in EntryNodes that aren't currently guard nodes to the list - * of guard nodes, at the front. */ +/** Adjust the entry guards list so that it only contains entries from + * EntryNodes, adding new entries from EntryNodes to the list as needed. */ static void -entry_guards_prepend_from_config(or_options_t *options) +entry_guards_set_from_config(const or_options_t *options) { - smartlist_t *entry_routers, *entry_fps; + smartlist_t *entry_nodes, *worse_entry_nodes, *entry_fps; smartlist_t *old_entry_guards_on_list, *old_entry_guards_not_on_list; tor_assert(entry_guards); @@ -3997,23 +4041,19 @@ entry_guards_prepend_from_config(or_options_t *options) tor_free(string); } - entry_routers = smartlist_create(); - entry_fps = smartlist_create(); - old_entry_guards_on_list = smartlist_create(); - old_entry_guards_not_on_list = smartlist_create(); + entry_nodes = smartlist_new(); + worse_entry_nodes = smartlist_new(); + entry_fps = smartlist_new(); + old_entry_guards_on_list = smartlist_new(); + old_entry_guards_not_on_list = smartlist_new(); /* Split entry guards into those on the list and those not. */ - /* XXXX023 Now that we allow countries and IP ranges in EntryNodes, this is - * potentially an enormous list. For now, we disable such values for - * EntryNodes in options_validate(); really, this wants a better solution. - * Perhaps we should do this calculation once whenever the list of routers - * changes or the entrynodes setting changes. - */ - routerset_get_all_routers(entry_routers, options->EntryNodes, - options->ExcludeNodes, 0); - SMARTLIST_FOREACH(entry_routers, routerinfo_t *, ri, - smartlist_add(entry_fps,ri->cache_info.identity_digest)); + routerset_get_all_nodes(entry_nodes, options->EntryNodes, + options->ExcludeNodes, 0); + SMARTLIST_FOREACH(entry_nodes, const node_t *,node, + smartlist_add(entry_fps, (void*)node->identity)); + SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e, { if (smartlist_digest_isin(entry_fps, e->identity)) smartlist_add(old_entry_guards_on_list, e); @@ -4021,27 +4061,47 @@ entry_guards_prepend_from_config(or_options_t *options) smartlist_add(old_entry_guards_not_on_list, e); }); - /* Remove all currently configured entry guards from entry_routers. */ - SMARTLIST_FOREACH(entry_routers, routerinfo_t *, ri, { - if (is_an_entry_guard(ri->cache_info.identity_digest)) { - SMARTLIST_DEL_CURRENT(entry_routers, ri); + /* Remove all currently configured guard nodes, excluded nodes, unreachable + * nodes, or non-Guard nodes from entry_nodes. */ + SMARTLIST_FOREACH_BEGIN(entry_nodes, const node_t *, node) { + if (is_an_entry_guard(node->identity)) { + SMARTLIST_DEL_CURRENT(entry_nodes, node); + continue; + } else if (routerset_contains_node(options->ExcludeNodes, node)) { + SMARTLIST_DEL_CURRENT(entry_nodes, node); + continue; + } else if (!fascist_firewall_allows_node(node)) { + SMARTLIST_DEL_CURRENT(entry_nodes, node); + continue; + } else if (! node->is_possible_guard) { + smartlist_add(worse_entry_nodes, (node_t*)node); + SMARTLIST_DEL_CURRENT(entry_nodes, node); } - }); + } SMARTLIST_FOREACH_END(node); /* Now build the new entry_guards list. */ smartlist_clear(entry_guards); /* First, the previously configured guards that are in EntryNodes. */ smartlist_add_all(entry_guards, old_entry_guards_on_list); + /* Next, scramble the rest of EntryNodes, putting the guards first. */ + smartlist_shuffle(entry_nodes); + smartlist_shuffle(worse_entry_nodes); + smartlist_add_all(entry_nodes, worse_entry_nodes); + /* Next, the rest of EntryNodes */ - SMARTLIST_FOREACH(entry_routers, routerinfo_t *, ri, { - add_an_entry_guard(ri, 0); - }); + SMARTLIST_FOREACH_BEGIN(entry_nodes, const node_t *, node) { + add_an_entry_guard(node, 0, 0); + if (smartlist_len(entry_guards) > options->NumEntryGuards * 10) + break; + } SMARTLIST_FOREACH_END(node); + log_notice(LD_GENERAL, "%d entries in guards", smartlist_len(entry_guards)); /* Finally, free the remaining previously configured guards that are not in * EntryNodes. */ SMARTLIST_FOREACH(old_entry_guards_not_on_list, entry_guard_t *, e, entry_guard_free(e)); - smartlist_free(entry_routers); + smartlist_free(entry_nodes); + smartlist_free(worse_entry_nodes); smartlist_free(entry_fps); smartlist_free(old_entry_guards_on_list); smartlist_free(old_entry_guards_not_on_list); @@ -4053,7 +4113,7 @@ entry_guards_prepend_from_config(or_options_t *options) * list already and we must stick to it. */ int -entry_list_is_constrained(or_options_t *options) +entry_list_is_constrained(const or_options_t *options) { if (options->EntryNodes) return 1; @@ -4067,28 +4127,29 @@ entry_list_is_constrained(or_options_t *options) * make sure not to pick this circuit's exit or any node in the * exit's family. If <b>state</b> is NULL, we're looking for a random * guard (likely a bridge). */ -routerinfo_t * +const node_t * choose_random_entry(cpath_build_state_t *state) { - or_options_t *options = get_options(); - smartlist_t *live_entry_guards = smartlist_create(); - smartlist_t *exit_family = smartlist_create(); - routerinfo_t *chosen_exit = state?build_state_get_exit_router(state) : NULL; - routerinfo_t *r = NULL; + const or_options_t *options = get_options(); + smartlist_t *live_entry_guards = smartlist_new(); + smartlist_t *exit_family = smartlist_new(); + const node_t *chosen_exit = + state?build_state_get_exit_node(state) : NULL; + const node_t *node = NULL; int need_uptime = state ? state->need_uptime : 0; int need_capacity = state ? state->need_capacity : 0; int preferred_min, consider_exit_family = 0; if (chosen_exit) { - routerlist_add_family(exit_family, chosen_exit); + nodelist_add_node_and_family(exit_family, chosen_exit); consider_exit_family = 1; } if (!entry_guards) - entry_guards = smartlist_create(); + entry_guards = smartlist_new(); if (should_add_entry_nodes) - entry_guards_prepend_from_config(options); + entry_guards_set_from_config(options); if (!entry_list_is_constrained(options) && smartlist_len(entry_guards) < options->NumEntryGuards) @@ -4096,25 +4157,24 @@ choose_random_entry(cpath_build_state_t *state) retry: smartlist_clear(live_entry_guards); - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) - { + SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) { const char *msg; - r = entry_is_live(entry, need_uptime, need_capacity, 0, &msg); - if (!r) + node = entry_is_live(entry, need_uptime, need_capacity, 0, &msg); + if (!node) continue; /* down, no point */ - if (r == chosen_exit) + if (node == chosen_exit) continue; /* don't pick the same node for entry and exit */ - if (consider_exit_family && smartlist_isin(exit_family, r)) + if (consider_exit_family && smartlist_isin(exit_family, node)) continue; /* avoid relays that are family members of our exit */ #if 0 /* since EntryNodes is always strict now, this clause is moot */ if (options->EntryNodes && - !routerset_contains_router(options->EntryNodes, r)) { + !routerset_contains_node(options->EntryNodes, node)) { /* We've come to the end of our preferred entry nodes. */ if (smartlist_len(live_entry_guards)) goto choose_and_finish; /* only choose from the ones we like */ if (options->StrictNodes) { /* in theory this case should never happen, since - * entry_guards_prepend_from_config() drops unwanted relays */ + * entry_guards_set_from_config() drops unwanted relays */ tor_fragile_assert(); } else { log_info(LD_CIRC, @@ -4122,7 +4182,7 @@ choose_random_entry(cpath_build_state_t *state) } } #endif - smartlist_add(live_entry_guards, r); + smartlist_add(live_entry_guards, (void*)node); if (!entry->made_contact) { /* Always start with the first not-yet-contacted entry * guard. Otherwise we might add several new ones, pick @@ -4131,9 +4191,8 @@ choose_random_entry(cpath_build_state_t *state) goto choose_and_finish; } if (smartlist_len(live_entry_guards) >= options->NumEntryGuards) - break; /* we have enough */ - } - SMARTLIST_FOREACH_END(entry); + goto choose_and_finish; /* we have enough */ + } SMARTLIST_FOREACH_END(entry); if (entry_list_is_constrained(options)) { /* If we prefer the entry nodes we've got, and we have at least @@ -4153,8 +4212,8 @@ choose_random_entry(cpath_build_state_t *state) /* XXX if guard doesn't imply fast and stable, then we need * to tell add_an_entry_guard below what we want, or it might * be a long time til we get it. -RD */ - r = add_an_entry_guard(NULL, 0); - if (r) { + node = add_an_entry_guard(NULL, 0, 0); + if (node) { entry_guards_changed(); /* XXX we start over here in case the new node we added shares * a family with our exit node. There's a chance that we'll just @@ -4164,11 +4223,11 @@ choose_random_entry(cpath_build_state_t *state) goto retry; } } - if (!r && need_uptime) { + if (!node && need_uptime) { need_uptime = 0; /* try without that requirement */ goto retry; } - if (!r && need_capacity) { + if (!node && need_capacity) { /* still no? last attempt, try without requiring capacity */ need_capacity = 0; goto retry; @@ -4177,10 +4236,10 @@ choose_random_entry(cpath_build_state_t *state) /* Removing this retry logic: if we only allow one exit, and it is in the same family as all our entries, then we are just plain not going to win here. */ - if (!r && entry_list_is_constrained(options) && consider_exit_family) { - /* still no? if we're using bridges, - * and our chosen exit is in the same family as all our - * bridges, then be flexible about families. */ + if (!node && entry_list_is_constrained(options) && consider_exit_family) { + /* still no? if we're using bridges or have strictentrynodes + * set, and our chosen exit is in the same family as all our + * bridges/entry guards, then be flexible about families. */ consider_exit_family = 0; goto retry; } @@ -4192,16 +4251,16 @@ choose_random_entry(cpath_build_state_t *state) if (entry_list_is_constrained(options)) { /* We need to weight by bandwidth, because our bridges or entryguards * were not already selected proportional to their bandwidth. */ - r = routerlist_sl_choose_by_bandwidth(live_entry_guards, WEIGHT_FOR_GUARD); + node = node_sl_choose_by_bandwidth(live_entry_guards, WEIGHT_FOR_GUARD); } else { /* We choose uniformly at random here, because choose_good_entry_server() * already weights its choices by bandwidth, so we don't want to * *double*-weight our guard selection. */ - r = smartlist_choose(live_entry_guards); + node = smartlist_choose(live_entry_guards); } smartlist_free(live_entry_guards); smartlist_free(exit_family); - return r; + return node; } /** Parse <b>state</b> and learn about the entry guards it describes. @@ -4214,7 +4273,7 @@ int entry_guards_parse_state(or_state_t *state, int set, char **msg) { entry_guard_t *node = NULL; - smartlist_t *new_entry_guards = smartlist_create(); + smartlist_t *new_entry_guards = smartlist_new(); config_line_t *line; time_t now = time(NULL); const char *state_version = state->TorVersion; @@ -4223,7 +4282,7 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg) *msg = NULL; for (line = state->EntryGuards; line; line = line->next) { if (!strcasecmp(line->key, "EntryGuard")) { - smartlist_t *args = smartlist_create(); + smartlist_t *args = smartlist_new(); node = tor_malloc_zero(sizeof(entry_guard_t)); /* all entry guards on disk have been contacted */ node->made_contact = 1; @@ -4369,7 +4428,7 @@ entry_guards_update_state(or_state_t *state) next = &state->EntryGuards; *next = NULL; if (!entry_guards) - entry_guards = smartlist_create(); + entry_guards = smartlist_new(); SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e, { char dbuf[HEX_DIGEST_LEN+1]; @@ -4377,10 +4436,8 @@ entry_guards_update_state(or_state_t *state) continue; /* don't write this one to disk */ *next = line = tor_malloc_zero(sizeof(config_line_t)); line->key = tor_strdup("EntryGuard"); - line->value = tor_malloc(HEX_DIGEST_LEN+MAX_NICKNAME_LEN+2); base16_encode(dbuf, sizeof(dbuf), e->identity, DIGEST_LEN); - tor_snprintf(line->value,HEX_DIGEST_LEN+MAX_NICKNAME_LEN+2, - "%s %s", e->nickname, dbuf); + tor_asprintf(&line->value, "%s %s", e->nickname, dbuf); next = &(line->next); if (e->unreachable_since) { *next = line = tor_malloc_zero(sizeof(config_line_t)); @@ -4404,15 +4461,11 @@ entry_guards_update_state(or_state_t *state) !strchr(e->chosen_by_version, ' ')) { char d[HEX_DIGEST_LEN+1]; char t[ISO_TIME_LEN+1]; - size_t val_len; *next = line = tor_malloc_zero(sizeof(config_line_t)); line->key = tor_strdup("EntryGuardAddedBy"); - val_len = (HEX_DIGEST_LEN+1+strlen(e->chosen_by_version) - +1+ISO_TIME_LEN+1); - line->value = tor_malloc(val_len); base16_encode(d, sizeof(d), e->identity, DIGEST_LEN); format_iso_time(t, e->chosen_on_date); - tor_snprintf(line->value, val_len, "%s %s %s", + tor_asprintf(&line->value, "%s %s %s", d, e->chosen_by_version, t); next = &(line->next); } @@ -4438,17 +4491,15 @@ getinfo_helper_entry_guards(control_connection_t *conn, if (!strcmp(question,"entry-guards") || !strcmp(question,"helper-nodes")) { - smartlist_t *sl = smartlist_create(); + smartlist_t *sl = smartlist_new(); char tbuf[ISO_TIME_LEN+1]; char nbuf[MAX_VERBOSE_NICKNAME_LEN+1]; if (!entry_guards) - entry_guards = smartlist_create(); + entry_guards = smartlist_new(); SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) { - size_t len = MAX_VERBOSE_NICKNAME_LEN+ISO_TIME_LEN+32; - char *c = tor_malloc(len); const char *status = NULL; time_t when = 0; - routerinfo_t *ri; + const node_t *node; if (!e->made_contact) { status = "never-connected"; @@ -4459,9 +4510,9 @@ getinfo_helper_entry_guards(control_connection_t *conn, status = "up"; } - ri = router_get_by_digest(e->identity); - if (ri) { - router_get_verbose_nickname(nbuf, ri); + node = node_get_by_id(e->identity); + if (node) { + node_get_verbose_nickname(node, nbuf); } else { nbuf[0] = '$'; base16_encode(nbuf+1, sizeof(nbuf)-1, e->identity, DIGEST_LEN); @@ -4471,11 +4522,10 @@ getinfo_helper_entry_guards(control_connection_t *conn, if (when) { format_iso_time(tbuf, when); - tor_snprintf(c, len, "%s %s %s\n", nbuf, status, tbuf); + smartlist_add_asprintf(sl, "%s %s %s\n", nbuf, status, tbuf); } else { - tor_snprintf(c, len, "%s %s\n", nbuf, status); + smartlist_add_asprintf(sl, "%s %s\n", nbuf, status); } - smartlist_add(sl, c); } SMARTLIST_FOREACH_END(e); *answer = smartlist_join_strings(sl, "", 0, NULL); SMARTLIST_FOREACH(sl, char *, c, tor_free(c)); @@ -4484,24 +4534,6 @@ getinfo_helper_entry_guards(control_connection_t *conn, return 0; } -/** Information about a configured bridge. Currently this just matches the - * ones in the torrc file, but one day we may be able to learn about new - * bridges on our own, and remember them in the state file. */ -typedef struct { - /** Address of the bridge. */ - tor_addr_t addr; - /** TLS port for the bridge. */ - uint16_t port; - /** Boolean: We are re-parsing our bridge list, and we are going to remove - * this one if we don't find it in the list of configured bridges. */ - unsigned marked_for_removal : 1; - /** Expected identity digest, or all zero bytes if we don't know what the - * digest should be. */ - char identity[DIGEST_LEN]; - /** When should we next try to fetch a descriptor for this bridge? */ - download_status_t fetch_status; -} bridge_info_t; - /** A list of configured bridges. Whenever we actually get a descriptor * for one, we add it as an entry guard. Note that the order of bridges * in this list does not necessarily correspond to the order of bridges @@ -4514,7 +4546,7 @@ void mark_bridge_list(void) { if (!bridge_list) - bridge_list = smartlist_create(); + bridge_list = smartlist_new(); SMARTLIST_FOREACH(bridge_list, bridge_info_t *, b, b->marked_for_removal = 1); } @@ -4525,11 +4557,11 @@ void sweep_bridge_list(void) { if (!bridge_list) - bridge_list = smartlist_create(); + bridge_list = smartlist_new(); SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, b) { if (b->marked_for_removal) { SMARTLIST_DEL_CURRENT(bridge_list, b); - tor_free(b); + bridge_free(b); } } SMARTLIST_FOREACH_END(b); } @@ -4539,11 +4571,244 @@ static void clear_bridge_list(void) { if (!bridge_list) - bridge_list = smartlist_create(); - SMARTLIST_FOREACH(bridge_list, bridge_info_t *, b, tor_free(b)); + bridge_list = smartlist_new(); + SMARTLIST_FOREACH(bridge_list, bridge_info_t *, b, bridge_free(b)); smartlist_clear(bridge_list); } +/** Free the bridge <b>bridge</b>. */ +static void +bridge_free(bridge_info_t *bridge) +{ + if (!bridge) + return; + + tor_free(bridge->transport_name); + tor_free(bridge); +} + +/** A list of pluggable transports found in torrc. */ +static smartlist_t *transport_list = NULL; + +/** Mark every entry of the transport list to be removed on our next call to + * sweep_transport_list unless it has first been un-marked. */ +void +mark_transport_list(void) +{ + if (!transport_list) + transport_list = smartlist_new(); + SMARTLIST_FOREACH(transport_list, transport_t *, t, + t->marked_for_removal = 1); +} + +/** Remove every entry of the transport list that was marked with + * mark_transport_list if it has not subsequently been un-marked. */ +void +sweep_transport_list(void) +{ + if (!transport_list) + transport_list = smartlist_new(); + SMARTLIST_FOREACH_BEGIN(transport_list, transport_t *, t) { + if (t->marked_for_removal) { + SMARTLIST_DEL_CURRENT(transport_list, t); + transport_free(t); + } + } SMARTLIST_FOREACH_END(t); +} + +/** Initialize the pluggable transports list to empty, creating it if + * needed. */ +void +clear_transport_list(void) +{ + if (!transport_list) + transport_list = smartlist_new(); + SMARTLIST_FOREACH(transport_list, transport_t *, t, transport_free(t)); + smartlist_clear(transport_list); +} + +/** Free the pluggable transport struct <b>transport</b>. */ +void +transport_free(transport_t *transport) +{ + if (!transport) + return; + + tor_free(transport->name); + tor_free(transport); +} + +/** Returns the transport in our transport list that has the name <b>name</b>. + * Else returns NULL. */ +transport_t * +transport_get_by_name(const char *name) +{ + tor_assert(name); + + if (!transport_list) + return NULL; + + SMARTLIST_FOREACH_BEGIN(transport_list, transport_t *, transport) { + if (!strcmp(transport->name, name)) + return transport; + } SMARTLIST_FOREACH_END(transport); + + return NULL; +} + +/** Returns a transport_t struct for a transport proxy supporting the + protocol <b>name</b> listening at <b>addr</b>:<b>port</b> using + SOCKS version <b>socks_ver</b>. */ +transport_t * +transport_new(const tor_addr_t *addr, uint16_t port, + const char *name, int socks_ver) +{ + transport_t *t = tor_malloc_zero(sizeof(transport_t)); + + tor_addr_copy(&t->addr, addr); + t->port = port; + t->name = tor_strdup(name); + t->socks_version = socks_ver; + + return t; +} + +/** Resolve any conflicts that the insertion of transport <b>t</b> + * might cause. + * Return 0 if <b>t</b> is OK and should be registered, 1 if there is + * a transport identical to <b>t</b> already registered and -1 if + * <b>t</b> cannot be added due to conflicts. */ +static int +transport_resolve_conflicts(transport_t *t) +{ + /* This is how we resolve transport conflicts: + + If there is already a transport with the same name and addrport, + we either have duplicate torrc lines OR we are here post-HUP and + this transport was here pre-HUP as well. In any case, mark the + old transport so that it doesn't get removed and ignore the new + one. Our caller has to free the new transport so we return '1' to + signify this. + + If there is already a transport with the same name but different + addrport: + * if it's marked for removal, it means that it either has a lower + priority than 't' in torrc (otherwise the mark would have been + cleared by the paragraph above), or it doesn't exist at all in + the post-HUP torrc. We destroy the old transport and register 't'. + * if it's *not* marked for removal, it means that it was newly + added in the post-HUP torrc or that it's of higher priority, in + this case we ignore 't'. */ + transport_t *t_tmp = transport_get_by_name(t->name); + if (t_tmp) { /* same name */ + if (tor_addr_eq(&t->addr, &t_tmp->addr) && (t->port == t_tmp->port)) { + /* same name *and* addrport */ + t_tmp->marked_for_removal = 0; + return 1; + } else { /* same name but different addrport */ + if (t_tmp->marked_for_removal) { /* marked for removal */ + log_notice(LD_GENERAL, "You tried to add transport '%s' at '%s:%u' " + "but there was already a transport marked for deletion at " + "'%s:%u'. We deleted the old transport and registered the " + "new one.", t->name, fmt_addr(&t->addr), t->port, + fmt_addr(&t_tmp->addr), t_tmp->port); + smartlist_remove(transport_list, t_tmp); + transport_free(t_tmp); + } else { /* *not* marked for removal */ + log_notice(LD_GENERAL, "You tried to add transport '%s' at '%s:%u' " + "but the same transport already exists at '%s:%u'. " + "Skipping.", t->name, fmt_addr(&t->addr), t->port, + fmt_addr(&t_tmp->addr), t_tmp->port); + return -1; + } + } + } + + return 0; +} + +/** Add transport <b>t</b> to the internal list of pluggable + * transports. + * Returns 0 if the transport was added correctly, 1 if the same + * transport was already registered (in this case the caller must + * free the transport) and -1 if there was an error. */ +int +transport_add(transport_t *t) +{ + int r; + tor_assert(t); + + r = transport_resolve_conflicts(t); + + switch (r) { + case 0: /* should register transport */ + if (!transport_list) + transport_list = smartlist_new(); + smartlist_add(transport_list, t); + return 0; + default: /* let our caller know the return code */ + return r; + } +} + +/** Remember a new pluggable transport proxy at <b>addr</b>:<b>port</b>. + * <b>name</b> is set to the name of the protocol this proxy uses. + * <b>socks_ver</b> is set to the SOCKS version of the proxy. */ +int +transport_add_from_config(const tor_addr_t *addr, uint16_t port, + const char *name, int socks_ver) +{ + transport_t *t = transport_new(addr, port, name, socks_ver); + + int r = transport_add(t); + + switch (r) { + case -1: + default: + log_notice(LD_GENERAL, "Could not add transport %s at %s:%u. Skipping.", + t->name, fmt_addr(&t->addr), t->port); + transport_free(t); + return -1; + case 1: + log_info(LD_GENERAL, "Succesfully registered transport %s at %s:%u.", + t->name, fmt_addr(&t->addr), t->port); + transport_free(t); /* falling */ + return 0; + case 0: + log_info(LD_GENERAL, "Succesfully registered transport %s at %s:%u.", + t->name, fmt_addr(&t->addr), t->port); + return 0; + } +} + +/** Warn the user of possible pluggable transport misconfiguration. + * Return 0 if the validation happened, -1 if we should postpone the + * validation. */ +int +validate_pluggable_transports_config(void) +{ + /* Don't validate if managed proxies are not yet fully configured. */ + if (bridge_list && !pt_proxies_configuration_pending()) { + SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, b) { + /* Skip bridges without transports. */ + if (!b->transport_name) + continue; + /* See if the user has Bridges that specify nonexistent + pluggable transports. We should warn the user in such case, + since it's probably misconfiguration. */ + if (!transport_get_by_name(b->transport_name)) + log_warn(LD_CONFIG, "You have a Bridge line using the %s " + "pluggable transport, but there doesn't seem to be a " + "corresponding ClientTransportPlugin line.", + b->transport_name); + } SMARTLIST_FOREACH_END(b); + + return 0; + } else { + return -1; + } +} + /** Return a bridge pointer if <b>ri</b> is one of our known bridges * (either by comparing keys if possible, else by comparing addr/port). * Else return NULL. */ @@ -4570,21 +4835,53 @@ get_configured_bridge_by_addr_port_digest(const tor_addr_t *addr, /** Wrapper around get_configured_bridge_by_addr_port_digest() to look * it up via router descriptor <b>ri</b>. */ static bridge_info_t * -get_configured_bridge_by_routerinfo(routerinfo_t *ri) +get_configured_bridge_by_routerinfo(const routerinfo_t *ri) { - tor_addr_t addr; - tor_addr_from_ipv4h(&addr, ri->addr); - return get_configured_bridge_by_addr_port_digest(&addr, - ri->or_port, ri->cache_info.identity_digest); + tor_addr_port_t ap; + + router_get_pref_orport(ri, &ap); + return get_configured_bridge_by_addr_port_digest(&ap.addr, ap.port, + ri->cache_info.identity_digest); } /** Return 1 if <b>ri</b> is one of our known bridges, else 0. */ int -routerinfo_is_a_configured_bridge(routerinfo_t *ri) +routerinfo_is_a_configured_bridge(const routerinfo_t *ri) { return get_configured_bridge_by_routerinfo(ri) ? 1 : 0; } +/** Return 1 if <b>node</b> is one of our configured bridges, else 0. */ +int +node_is_a_configured_bridge(const node_t *node) +{ + int retval = 0; /* Negative. */ + smartlist_t *orports = NULL; + + if (!node) + goto out; + + orports = node_get_all_orports(node); + if (orports == NULL) + goto out; + + SMARTLIST_FOREACH_BEGIN(orports, tor_addr_port_t *, orport) { + if (get_configured_bridge_by_addr_port_digest(&orport->addr, orport->port, + node->identity) != NULL) { + retval = 1; + goto out; + } + } SMARTLIST_FOREACH_END(orport); + + out: + if (orports != NULL) { + SMARTLIST_FOREACH(orports, tor_addr_port_t *, p, tor_free(p)); + smartlist_free(orports); + orports = NULL; + } + return retval; +} + /** We made a connection to a router at <b>addr</b>:<b>port</b> * without knowing its digest. Its digest turned out to be <b>digest</b>. * If it was a bridge, and we still don't know its digest, record it. @@ -4604,10 +4901,12 @@ learned_router_identity(const tor_addr_t *addr, uint16_t port, /** Remember a new bridge at <b>addr</b>:<b>port</b>. If <b>digest</b> * is set, it tells us the identity key too. If we already had the - * bridge in our list, unmark it, and don't actually add anything new. */ + * bridge in our list, unmark it, and don't actually add anything new. + * If <b>transport_name</b> is non-NULL - the bridge is associated with a + * pluggable transport - we assign the transport to the bridge. */ void bridge_add_from_config(const tor_addr_t *addr, uint16_t port, - const char *digest) + const char *digest, const char *transport_name) { bridge_info_t *b; @@ -4621,9 +4920,11 @@ bridge_add_from_config(const tor_addr_t *addr, uint16_t port, b->port = port; if (digest) memcpy(b->identity, digest, DIGEST_LEN); + if (transport_name) + b->transport_name = tor_strdup(transport_name); b->fetch_status.schedule = DL_SCHED_BRIDGE; if (!bridge_list) - bridge_list = smartlist_create(); + bridge_list = smartlist_new(); smartlist_add(bridge_list, b); } @@ -4658,12 +4959,48 @@ find_bridge_by_digest(const char *digest) return NULL; } +/** If <b>addr</b> and <b>port</b> match the address and port of a + * bridge of ours that uses pluggable transports, place its transport + * in <b>transport</b>. + * + * Return 0 on success (found a transport, or found a bridge with no + * transport, or found no bridge); return -1 if we should be using a + * transport, but the transport could not be found. + */ +int +find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port, + const transport_t **transport) +{ + *transport = NULL; + if (!bridge_list) + return 0; + + SMARTLIST_FOREACH_BEGIN(bridge_list, const bridge_info_t *, bridge) { + if (tor_addr_eq(&bridge->addr, addr) && + (bridge->port == port)) { /* bridge matched */ + if (bridge->transport_name) { /* it also uses pluggable transports */ + *transport = transport_get_by_name(bridge->transport_name); + if (*transport == NULL) { /* it uses pluggable transports, but + the transport could not be found! */ + return -1; + } + return 0; + } else { /* bridge matched, but it doesn't use transports. */ + break; + } + } + } SMARTLIST_FOREACH_END(bridge); + + *transport = NULL; + return 0; +} + /** We need to ask <b>bridge</b> for its server descriptor. */ static void launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge) { char *address; - or_options_t *options = get_options(); + const or_options_t *options = get_options(); if (connection_get_by_type_addr_port_purpose( CONN_TYPE_DIR, &bridge->addr, bridge->port, @@ -4705,15 +5042,20 @@ retry_bridge_descriptor_fetch_directly(const char *digest) * descriptor, fetch a new copy of its descriptor -- either directly * from the bridge or via a bridge authority. */ void -fetch_bridge_descriptors(or_options_t *options, time_t now) +fetch_bridge_descriptors(const or_options_t *options, time_t now) { - int num_bridge_auths = get_n_authorities(BRIDGE_AUTHORITY); + int num_bridge_auths = get_n_authorities(BRIDGE_DIRINFO); int ask_bridge_directly; int can_use_bridge_authority; if (!bridge_list) return; + /* If we still have unconfigured managed proxies, don't go and + connect to a bridge. */ + if (pt_proxies_configuration_pending()) + return; + SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge) { if (!download_status_is_ready(&bridge->fetch_status, now, @@ -4772,26 +5114,89 @@ fetch_bridge_descriptors(or_options_t *options, time_t now) } /** If our <b>bridge</b> is configured to be a different address than - * the bridge gives in its routerinfo <b>ri</b>, rewrite the routerinfo + * the bridge gives in <b>node</b>, rewrite the routerinfo * we received to use the address we meant to use. Now we handle * multihomed bridges better. */ static void -rewrite_routerinfo_address_for_bridge(bridge_info_t *bridge, routerinfo_t *ri) +rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) { + /* XXXX move this function. */ + /* XXXX overridden addresses should really live in the node_t, so that the + * routerinfo_t and the microdesc_t can be immutable. But we can only + * do that safely if we know that no function that connects to an OR + * does so through an address from any source other than node_get_addr(). + */ tor_addr_t addr; - tor_addr_from_ipv4h(&addr, ri->addr); - if (!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && - bridge->port == ri->or_port) - return; /* they match, so no need to do anything */ + if (node->ri) { + routerinfo_t *ri = node->ri; + tor_addr_from_ipv4h(&addr, ri->addr); + + if ((!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && + bridge->port == ri->or_port) || + (!tor_addr_compare(&bridge->addr, &ri->ipv6_addr, CMP_EXACT) && + bridge->port == ri->ipv6_orport)) { + /* they match, so no need to do anything */ + } else { + if (tor_addr_family(&bridge->addr) == AF_INET) { + ri->addr = tor_addr_to_ipv4h(&bridge->addr); + tor_free(ri->address); + ri->address = tor_dup_ip(ri->addr); + ri->or_port = bridge->port; + log_info(LD_DIR, + "Adjusted bridge routerinfo for '%s' to match configured " + "address %s:%d.", + ri->nickname, ri->address, ri->or_port); + } else if (tor_addr_family(&bridge->addr) == AF_INET6) { + tor_addr_copy(&ri->ipv6_addr, &bridge->addr); + ri->ipv6_orport = bridge->port; + log_info(LD_DIR, + "Adjusted bridge routerinfo for '%s' to match configured " + "address %s:%d.", + ri->nickname, fmt_addr(&ri->ipv6_addr), ri->ipv6_orport); + } else { + log_err(LD_BUG, "Address family not supported: %d.", + tor_addr_family(&bridge->addr)); + return; + } + } + + /* Indicate that we prefer connecting to this bridge over the + protocol that the bridge address indicates. Last bridge + descriptor handled wins. */ + ri->ipv6_preferred = tor_addr_family(&bridge->addr) == AF_INET6; + + /* XXXipv6 we lack support for falling back to another address for + the same relay, warn the user */ + if (!tor_addr_is_null(&ri->ipv6_addr)) + { + tor_addr_port_t ap; + router_get_pref_orport(ri, &ap); + log_notice(LD_CONFIG, + "Bridge '%s' has both an IPv4 and an IPv6 address. " + "Will prefer using its %s address (%s:%d).", + ri->nickname, + ri->ipv6_preferred ? "IPv6" : "IPv4", + fmt_addr(&ap.addr), ap.port); + } + } + if (node->rs) { + routerstatus_t *rs = node->rs; + tor_addr_from_ipv4h(&addr, rs->addr); - ri->addr = tor_addr_to_ipv4h(&bridge->addr); - tor_free(ri->address); - ri->address = tor_dup_ip(ri->addr); - ri->or_port = bridge->port; - log_info(LD_DIR, "Adjusted bridge '%s' to match configured address %s:%d.", - ri->nickname, ri->address, ri->or_port); + if (!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && + bridge->port == rs->or_port) { + /* they match, so no need to do anything */ + } else { + rs->addr = tor_addr_to_ipv4h(&bridge->addr); + rs->or_port = bridge->port; + log_info(LD_DIR, + "Adjusted bridge routerstatus for '%s' to match " + "configured address %s:%d.", + rs->nickname, fmt_addr(&bridge->addr), rs->or_port); + } + } } /** We just learned a descriptor for a bridge. See if that @@ -4805,18 +5210,21 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache) int first = !any_bridge_descriptors_known(); bridge_info_t *bridge = get_configured_bridge_by_routerinfo(ri); time_t now = time(NULL); - ri->is_running = 1; + router_set_status(ri->cache_info.identity_digest, 1); if (bridge) { /* if we actually want to use this one */ + node_t *node; /* it's here; schedule its re-fetch for a long time from now. */ if (!from_cache) download_status_reset(&bridge->fetch_status); - rewrite_routerinfo_address_for_bridge(bridge, ri); + node = node_get_mutable_by_id(ri->cache_info.identity_digest); + tor_assert(node); + rewrite_node_address_for_bridge(bridge, node); + add_an_entry_guard(node, 1, 1); - add_an_entry_guard(ri, 1); - log_notice(LD_DIR, "new bridge descriptor '%s' (%s)", ri->nickname, - from_cache ? "cached" : "fresh"); + log_notice(LD_DIR, "new bridge descriptor '%s' (%s): %s", ri->nickname, + from_cache ? "cached" : "fresh", router_describe(ri)); /* set entry->made_contact so if it goes down we don't drop it from * our entry node list */ entry_guard_register_connect_status(ri->cache_info.identity_digest, @@ -4867,21 +5275,20 @@ any_pending_bridge_descriptor_fetches(void) * down. Else return 0. If <b>act</b> is 1, then mark the down guards * up; else just observe and report. */ static int -entries_retry_helper(or_options_t *options, int act) +entries_retry_helper(const or_options_t *options, int act) { - routerinfo_t *ri; + const node_t *node; int any_known = 0; int any_running = 0; - int purpose = options->UseBridges ? - ROUTER_PURPOSE_BRIDGE : ROUTER_PURPOSE_GENERAL; + int need_bridges = options->UseBridges != 0; if (!entry_guards) - entry_guards = smartlist_create(); - SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e, - { - ri = router_get_by_digest(e->identity); - if (ri && ri->purpose == purpose) { + entry_guards = smartlist_new(); + SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) { + node = node_get_by_id(e->identity); + if (node && node_has_descriptor(node) && + node_is_bridge(node) == need_bridges) { any_known = 1; - if (ri->is_running) + if (node->is_running) any_running = 1; /* some entry is both known and running */ else if (act) { /* Mark all current connections to this OR as unhealthy, since @@ -4890,15 +5297,15 @@ entries_retry_helper(or_options_t *options, int act) * the node down and undermine the retry attempt. We mark even * the established conns, since if the network just came back * we'll want to attach circuits to fresh conns. */ - connection_or_set_bad_connections(ri->cache_info.identity_digest, 1); + connection_or_set_bad_connections(node->identity, 1); /* mark this entry node for retry */ - router_set_status(ri->cache_info.identity_digest, 1); + router_set_status(node->identity, 1); e->can_retry = 1; e->bad_since = 0; } } - }); + } SMARTLIST_FOREACH_END(e); log_debug(LD_DIR, "%d: any_known %d, any_running %d", act, any_known, any_running); return any_known && !any_running; @@ -4907,7 +5314,7 @@ entries_retry_helper(or_options_t *options, int act) /** Do we know any descriptors for our bridges / entrynodes, and are * all the ones we have descriptors for down? */ int -entries_known_but_down(or_options_t *options) +entries_known_but_down(const or_options_t *options) { tor_assert(entry_list_is_constrained(options)); return entries_retry_helper(options, 0); @@ -4915,12 +5322,40 @@ entries_known_but_down(or_options_t *options) /** Mark all down known bridges / entrynodes up. */ void -entries_retry_all(or_options_t *options) +entries_retry_all(const or_options_t *options) { tor_assert(entry_list_is_constrained(options)); entries_retry_helper(options, 1); } +/** Return true if we've ever had a bridge running a Tor version that can't + * provide microdescriptors to us. In that case fall back to asking for + * full descriptors. Eventually all bridges will support microdescriptors + * and we can take this check out; see bug 4013. */ +int +any_bridges_dont_support_microdescriptors(void) +{ + const node_t *node; + static int ever_answered_yes = 0; + if (!get_options()->UseBridges || !entry_guards) + return 0; + if (ever_answered_yes) + return 1; /* if we ever answer 'yes', always answer 'yes' */ + SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) { + node = node_get_by_id(e->identity); + if (node && node->ri && + node_is_bridge(node) && node_is_a_configured_bridge(node) && + !tor_version_supports_microdescriptors(node->ri->platform)) { + /* This is one of our current bridges, and we know enough about + * it to know that it won't be able to answer our microdescriptor + * questions. */ + ever_answered_yes = 1; + return 1; + } + } SMARTLIST_FOREACH_END(e); + return 0; +} + /** Release all storage held by the list of entry guards and related * memory structs. */ void @@ -4933,7 +5368,10 @@ entry_guards_free_all(void) entry_guards = NULL; } clear_bridge_list(); + clear_transport_list(); smartlist_free(bridge_list); + smartlist_free(transport_list); bridge_list = NULL; + transport_list = NULL; } |