diff --git a/changes/bait-and-switch-bridges b/changes/bait-and-switch-bridges new file mode 100644 index 0000000000..5b89c4e1b4 --- /dev/null +++ b/changes/bait-and-switch-bridges @@ -0,0 +1,8 @@ + o Minor bugfixes: + - If you run a bridge that listens on multiple IP addresses, and + some user configures a bridge address that uses a different IP + address than your bridge writes in its router descriptor, and the + user doesn't specify an identity key, their Tor would discard the + descriptor because "it isn't one of our configured bridges", and + fail to bootstrap. Now believe the descriptor and bootstrap anyway. + Bugfix on 0.2.0.3-alpha. diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index fff56f0386..417d8ec8d8 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -4053,29 +4053,56 @@ clear_bridge_list(void) * (either by comparing keys if possible, else by comparing addr/port). * Else return NULL. */ static bridge_info_t * -routerinfo_get_configured_bridge(routerinfo_t *ri) +get_configured_bridge_by_addr_port_digest(tor_addr_t *addr, uint16_t port, + const char *digest) { if (!bridge_list) return NULL; SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge) { if (tor_digest_is_zero(bridge->identity) && - tor_addr_eq_ipv4h(&bridge->addr, ri->addr) && - bridge->port == ri->or_port) + !tor_addr_compare(&bridge->addr, addr, CMP_EXACT) && + bridge->port == port) return bridge; - if (!memcmp(bridge->identity, ri->cache_info.identity_digest, - DIGEST_LEN)) + if (!memcmp(bridge->identity, digest, DIGEST_LEN)) return bridge; } SMARTLIST_FOREACH_END(bridge); return NULL; } +/** Wrapper around get_configured_bridge_by_addr_port_digest() to look + * it up via router descriptor ri. */ +static bridge_info_t * +get_configured_bridge_by_routerinfo(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); +} + /** Return 1 if ri is one of our known bridges, else 0. */ int routerinfo_is_a_configured_bridge(routerinfo_t *ri) { - return routerinfo_get_configured_bridge(ri) ? 1 : 0; + return get_configured_bridge_by_routerinfo(ri) ? 1 : 0; +} + +/** We made a connection to a router at addr:port + * without knowing its digest. Its digest turned out to be digest. + * If it was a bridge, and we still don't know its digest, record it. + */ +void +learned_router_identity(tor_addr_t *addr, uint16_t port, const char *digest) +{ + bridge_info_t *bridge = + get_configured_bridge_by_addr_port_digest(addr, port, digest); + if (bridge && tor_digest_is_zero(bridge->identity)) { + memcpy(bridge->identity, digest, DIGEST_LEN); + log_notice(LD_DIR, "Learned fingerprint %s for bridge %s:%d", + hex_str(digest, DIGEST_LEN), fmt_addr(addr), port); + } } /** Remember a new bridge at addr:port. If digest @@ -4215,7 +4242,7 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache) tor_assert(ri->purpose == ROUTER_PURPOSE_BRIDGE); if (get_options()->UseBridges) { int first = !any_bridge_descriptors_known(); - bridge_info_t *bridge = routerinfo_get_configured_bridge(ri); + bridge_info_t *bridge = get_configured_bridge_by_routerinfo(ri); time_t now = time(NULL); ri->is_running = 1; diff --git a/src/or/connection_or.c b/src/or/connection_or.c index cdb4646cce..247cc4e595 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -926,16 +926,19 @@ connection_or_nonopen_was_started_here(or_connection_t *conn) * the certificate to be weird or absent. * * If we return 0, and the certificate is as expected, write a hash of the - * identity key into digest_rcvd, which must have DIGEST_LEN space in it. (If - * we return -1 this buffer is undefined.) If the certificate is invalid - * or missing on an incoming connection, we return 0 and set digest_rcvd to - * DIGEST_LEN 0 bytes. + * identity key into digest_rcvd_out, which must have DIGEST_LEN + * space in it. + * If the certificate is invalid or missing on an incoming connection, + * we return 0 and set digest_rcvd_out to DIGEST_LEN NUL bytes. + * (If we return -1, the contents of this buffer are undefined.) * * As side effects, * 1) Set conn->circ_id_type according to tor-spec.txt. * 2) If we're an authdirserver and we initiated the connection: drop all * descriptors that claim to be on that IP/port but that aren't * this guy; and note that this guy is reachable. + * 3) If this is a bridge and we didn't configure its identity + * fingerprint, remember the keyid we just learned. */ static int connection_or_check_valid_tls_handshake(or_connection_t *conn, @@ -1007,6 +1010,10 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn, log_info(LD_HANDSHAKE, "Connected to router %s at %s:%d without knowing " "its key. Hoping for the best.", conn->nickname, conn->_base.address, conn->_base.port); + /* if it's a bridge and we didn't know its identity fingerprint, now + * we do -- remember it for future attempts. */ + learned_router_identity(&conn->_base.addr, conn->_base.port, + digest_rcvd_out); } if (started_here) { diff --git a/src/or/or.h b/src/or/or.h index ec8dd3a5dc..832bdd6961 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2997,6 +2997,8 @@ int getinfo_helper_entry_guards(control_connection_t *conn, void clear_bridge_list(void); int routerinfo_is_a_configured_bridge(routerinfo_t *ri); +void +learned_router_identity(tor_addr_t *addr, uint16_t port, const char *digest); void bridge_add_from_config(const tor_addr_t *addr, uint16_t port, char *digest); void retry_bridge_descriptor_fetch_directly(const char *digest);