]> git.ipfire.org Git - thirdparty/tor.git/commitdiff
save PT bridge lines to disk
authordzwdz <not@dzwdz.net>
Sun, 24 Aug 2025 18:25:08 +0000 (20:25 +0200)
committerdzwdz <not@dzwdz.net>
Sun, 24 Aug 2025 18:53:15 +0000 (20:53 +0200)
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?

changes/ticket29128 [new file with mode: 0644]
doc/man/tor.1.txt
src/app/config/resolve_addr.c
src/core/mainloop/mainloop.c
src/feature/client/transports.c
src/feature/client/transports.h
src/feature/relay/router.c

diff --git a/changes/ticket29128 b/changes/ticket29128
new file mode 100644 (file)
index 0000000..7831038
--- /dev/null
@@ -0,0 +1,2 @@
+  o Minor features (bridges):
+    - Save complete bridge lines to 'datadir/bridgelines'. Closes ticket 29128.
index 89df857f5830bbe4b264d2be210eb49ce025fcda..c1a3e5fe422a7bc2e0e489ef8ab5d0df84b9b506 100644 (file)
@@ -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
index 130998b7c8571037cc78377433f21c66f90b5b26..90c99d423db44600b84ee4763ee905bc9aac0b55 100644 (file)
@@ -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.
index 658b4b47ee3f9426a541d3912ddbed25034cb6c4..963cb00128f3c5c5462f6ccb71cd1643d2e8fe88 100644 (file)
@@ -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
index 878675ac4dc8c121841c55881786271481f84d9b..c5d56697b90c10d29e61beed23598f75b3f3b3c8 100644 (file)
@@ -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. */
index 71e7feea379991fe41efd2689b2320766f470556..14a1284697488e8142855574de6e4dfc40d33fd8 100644 (file)
@@ -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);
 
index d9adc638c49fa59c7bcf7b7bfe284ad3635c8db1..257eb790de89c9af06f3ba86e970d6ad176a85c3 100644 (file)
@@ -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