tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

commit 8a7ade95b64407fe29350f96def8782d049f4ddd
parent 7c1241bf778d8489aece0208167781b9369ac49b
Author: Alexander Hansen Færøy <ahf@torproject.org>
Date:   Thu, 28 Aug 2025 15:19:52 +0200

Merge branch 'bridgeline' into 'main'

bridge: output complete bridge lines to logs & disk

See merge request tpo/core/tor!782
Diffstat:
Achanges/ticket29128 | 2++
Mdoc/man/tor.1.txt | 4++++
Msrc/app/config/resolve_addr.c | 9+++++++--
Msrc/core/mainloop/mainloop.c | 1+
Msrc/feature/client/transports.c | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/feature/client/transports.h | 1+
Msrc/feature/relay/router.c | 1+
7 files changed, 103 insertions(+), 2 deletions(-)

diff --git a/changes/ticket29128 b/changes/ticket29128 @@ -0,0 +1,2 @@ + o Minor features (bridges): + - Save complete bridge lines to 'datadir/bridgelines'. Closes ticket 29128. diff --git a/doc/man/tor.1.txt b/doc/man/tor.1.txt @@ -4103,6 +4103,10 @@ __DataDirectory__/**`hashed-fingerprint`**:: Only used by bridges. Contains the hashed fingerprint of the bridge's identity key. (That is, the hash of the hash of the identity key.) +__DataDirectory__/**`bridgelines`**:: + Only used by bridges. Contains the bridge lines that clients can use to + connect using pluggable transports. + __DataDirectory__/**`approved-routers`**:: Only used by authoritative directory servers. Each line lists a status and an identity, separated by whitespace. Identities can be hex-encoded RSA diff --git a/src/app/config/resolve_addr.c b/src/app/config/resolve_addr.c @@ -13,6 +13,7 @@ #include "core/mainloop/mainloop.h" +#include "feature/client/transports.h" #include "feature/control/control_events.h" #include "feature/dirauth/authmode.h" @@ -132,14 +133,18 @@ resolved_addr_set_suggested(const tor_addr_t *addr) } /* In case we don't have a configured address, log that we will be using the - * one discovered from the dirauth. */ + * one discovered from the dirauth, and, if running a bridge, use the new IP + * for the bridge lines. */ const int idx = af_to_idx(tor_addr_family(addr)); if (tor_addr_is_null(&last_resolved_addrs[idx]) && !tor_addr_eq(&last_suggested_addrs[idx], addr)) { log_notice(LD_CONFIG, "External address seen and suggested by a " "directory authority: %s", fmt_addr(addr)); + tor_addr_copy(&last_suggested_addrs[idx], addr); + pt_update_bridge_lines(); + } else { + tor_addr_copy(&last_suggested_addrs[idx], addr); } - tor_addr_copy(&last_suggested_addrs[idx], addr); } /** Copy the last resolved address of family into addr_out. diff --git a/src/core/mainloop/mainloop.c b/src/core/mainloop/mainloop.c @@ -2332,6 +2332,7 @@ ip_address_changed(int on_client_conn) reset_bandwidth_test(); reset_uptime(); router_reset_reachability(); + pt_update_bridge_lines(); /* All relays include their IP addresses as their ORPort addresses in * their descriptor. * Exit relays also incorporate interface addresses in their exit diff --git a/src/feature/client/transports.c b/src/feature/client/transports.c @@ -676,6 +676,7 @@ register_server_proxy(const managed_proxy_t *mp) t->name, fmt_addrport(&t->addr, t->port)); control_event_transport_launched("server", t->name, &t->addr, t->port); } SMARTLIST_FOREACH_END(t); + pt_update_bridge_lines(); } /** Register all the transports supported by client managed proxy @@ -1830,6 +1831,92 @@ pt_get_extra_info_descriptor_string(void) return the_string; } +/** Log the bridge lines that clients can use to connect. */ +void +pt_update_bridge_lines(void) +{ + char fingerprint[FINGERPRINT_LEN+1]; + smartlist_t *string_chunks = NULL; + + if (!server_identity_key_is_set() || !managed_proxy_list) + return; + + if (crypto_pk_get_fingerprint(get_server_identity_key(), fingerprint, 0)<0) { + log_err(LD_BUG, "Error computing fingerprint"); + return; + } + + string_chunks = smartlist_new(); + + SMARTLIST_FOREACH_BEGIN(managed_proxy_list, const managed_proxy_t *, mp) { + if (!mp->is_server) + continue; + + tor_assert(mp->transports); + + SMARTLIST_FOREACH_BEGIN(mp->transports, const transport_t *, t) { + char *transport_args = NULL; + const char *saddr = NULL; + + /* If the transport proxy returned "0.0.0.0" as its address, display + * our external address if we know it, or a placeholder if we don't */ + if (tor_addr_is_null(&t->addr)) { + tor_addr_t addr; + /* Attempt to find the IPv4 and then attempt to find the IPv6 if we + * can't find it. */ + bool found = relay_find_addr_to_publish(get_options(), AF_INET, + RELAY_FIND_ADDR_NO_FLAG, + &addr); + if (!found) { + found = relay_find_addr_to_publish(get_options(), AF_INET6, + RELAY_FIND_ADDR_NO_FLAG, &addr); + } + if (found && !tor_addr_is_null(&addr)) { + saddr = fmt_and_decorate_addr(&addr); + } else { + saddr = "<IP ADDRESS>"; + } + } else { + saddr = fmt_and_decorate_addr(&t->addr); + } + + /* If this transport has any arguments with it, prepend a space + * to them so that we can add them to the transport line, and replace + * commas with spaces to make it a valid bridge line. */ + if (t->extra_info_args) { + tor_asprintf(&transport_args, " %s", t->extra_info_args); + for (int i = 0; transport_args[i]; i++) { + if (transport_args[i] == ',') { + transport_args[i] = ' '; + } + } + } + + smartlist_add_asprintf(string_chunks, "Bridge %s %s:%d %s%s", + t->name, saddr, t->port, fingerprint, + transport_args ? transport_args : ""); + tor_free(transport_args); + } SMARTLIST_FOREACH_END(t); + } SMARTLIST_FOREACH_END(mp); + + /* If we have any valid bridgelines, join them into a single string, and + * save them to disk. Don't create an empty file. */ + if (smartlist_len(string_chunks) != 0) { + char *str = smartlist_join_strings(string_chunks, "\n", 1, NULL); + char *fname = get_datadir_fname("bridgelines"); + if (write_str_to_file_if_not_equal(fname, str)) { + log_warn(LD_FS, "Couldn't save bridge lines to disk"); + } else { + log_info(LD_FS, "Saved bridge lines to disk"); + } + tor_free(fname); + tor_free(str); + } + + SMARTLIST_FOREACH(string_chunks, char *, s, tor_free(s)); + smartlist_free(string_chunks); +} + /** Stringify the SOCKS arguments in <b>socks_args</b> according to * 180_pluggable_transport.txt. The string is allocated on the heap * and it's the responsibility of the caller to free it after use. */ diff --git a/src/feature/client/transports.h b/src/feature/client/transports.h @@ -57,6 +57,7 @@ void pt_configure_remaining_proxies(void); int pt_proxies_configuration_pending(void); char *pt_get_extra_info_descriptor_string(void); +void pt_update_bridge_lines(void); void pt_free_all(void); diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c @@ -382,6 +382,7 @@ set_server_identity_key(crypto_pk_t *k) log_err(LD_BUG, "Couldn't compute our own identity key digest."); tor_assert(0); } + pt_update_bridge_lines(); } #ifdef TOR_UNIT_TESTS