tor

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

commit 892dc508dbbbcacf583eb879325c97f36d468c99
parent e09c024c33d602ee363b8cb42f6a6a0cf4174932
Author: dzwdz <not@dzwdz.net>
Date:   Sun, 24 Aug 2025 20:25:08 +0200

save PT bridge lines to disk

Solves https://gitlab.torproject.org/tpo/core/tor/-/issues/29128.

pt_update_bridge_lines() was based on pt_get_extra_info_descriptor_string().
In particular, I copied the behaviour of not printing IPv6 addresses if IPv4 is
present.  I'll make both functions use IPv6 in another MR.

That function is called when the IP or key changes.  Sadly, I don't remember
how I decided from where exactly to call it (I wrote this ages ago), but it
seems fine?

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