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);