summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app/config/config.c10
-rw-r--r--src/app/config/statefile.c2
-rw-r--r--src/app/main/main.c1515
-rw-r--r--src/app/main/main.h29
-rw-r--r--src/app/main/ntmain.c2
-rw-r--r--src/core/crypto/onion_crypto.c311
-rw-r--r--src/core/crypto/onion_crypto.h47
-rw-r--r--src/core/include.am40
-rw-r--r--src/core/mainloop/connection.c3
-rw-r--r--src/core/mainloop/cpuworker.c5
-rw-r--r--src/core/mainloop/mainloop.c (renamed from src/core/mainloop/main.c)1512
-rw-r--r--src/core/mainloop/mainloop.h (renamed from src/core/mainloop/main.h)34
-rw-r--r--src/core/mainloop/periodic.c2
-rw-r--r--src/core/or/channel.c2
-rw-r--r--src/core/or/channelpadding.c2
-rw-r--r--src/core/or/channeltls.c2
-rw-r--r--src/core/or/circuitbuild.c7
-rw-r--r--src/core/or/circuitlist.c5
-rw-r--r--src/core/or/circuitstats.c2
-rw-r--r--src/core/or/command.c4
-rw-r--r--src/core/or/connection_edge.c51
-rw-r--r--src/core/or/connection_edge.h6
-rw-r--r--src/core/or/connection_or.c5
-rw-r--r--src/core/or/dos.c2
-rw-r--r--src/core/or/onion.c (renamed from src/core/crypto/onion.c)636
-rw-r--r--src/core/or/onion.h (renamed from src/core/crypto/onion.h)38
-rw-r--r--src/core/or/or.h1
-rw-r--r--src/core/or/policies.c4
-rw-r--r--src/core/or/protover.c15
-rw-r--r--src/core/or/reasons.c2
-rw-r--r--src/core/or/relay.c4
-rw-r--r--src/core/or/scheduler.c2
-rw-r--r--src/core/or/status.c2
-rw-r--r--src/feature/client/bridges.c2
-rw-r--r--src/feature/client/dnsserv.c2
-rw-r--r--src/feature/client/entrynodes.c4
-rw-r--r--src/feature/control/control.c6
-rw-r--r--src/feature/control/fmt_serverstatus.c103
-rw-r--r--src/feature/control/fmt_serverstatus.h18
-rw-r--r--src/feature/dirauth/bwauth.c453
-rw-r--r--src/feature/dirauth/bwauth.h58
-rw-r--r--src/feature/dirauth/dirvote.c7
-rw-r--r--src/feature/dirauth/dirvote.h8
-rw-r--r--src/feature/dirauth/guardfraction.c333
-rw-r--r--src/feature/dirauth/guardfraction.h24
-rw-r--r--src/feature/dirauth/process_descs.c835
-rw-r--r--src/feature/dirauth/process_descs.h38
-rw-r--r--src/feature/dirauth/reachability.c205
-rw-r--r--src/feature/dirauth/reachability.h36
-rw-r--r--src/feature/dirauth/recommend_pkg.c90
-rw-r--r--src/feature/dirauth/recommend_pkg.h17
-rw-r--r--src/feature/dirauth/shared_random.c3
-rw-r--r--src/feature/dirauth/voteflags.c644
-rw-r--r--src/feature/dirauth/voteflags.h31
-rw-r--r--src/feature/dircache/directory.c7
-rw-r--r--src/feature/dircache/dirserv.c2718
-rw-r--r--src/feature/dircache/dirserv.h130
-rw-r--r--src/feature/hibernate/hibernate.c2
-rw-r--r--src/feature/hs/hs_client.c4
-rw-r--r--src/feature/hs/hs_client.h2
-rw-r--r--src/feature/hs/hs_common.c5
-rw-r--r--src/feature/hs/hs_config.c48
-rw-r--r--src/feature/hs/hs_ident.h4
-rw-r--r--src/feature/hs/hs_service.c17
-rw-r--r--src/feature/hs/hs_service.h15
-rw-r--r--src/feature/nodelist/authcert.c1205
-rw-r--r--src/feature/nodelist/authcert.h60
-rw-r--r--src/feature/nodelist/dirlist.c421
-rw-r--r--src/feature/nodelist/dirlist.h47
-rw-r--r--src/feature/nodelist/fmt_routerstatus.c253
-rw-r--r--src/feature/nodelist/fmt_routerstatus.h41
-rw-r--r--src/feature/nodelist/microdesc.c1
-rw-r--r--src/feature/nodelist/networkstatus.c8
-rw-r--r--src/feature/nodelist/node_select.c1108
-rw-r--r--src/feature/nodelist/node_select.h102
-rw-r--r--src/feature/nodelist/nodelist.c10
-rw-r--r--src/feature/nodelist/routerlist.c2664
-rw-r--r--src/feature/nodelist/routerlist.h154
-rw-r--r--src/feature/nodelist/routerparse.c2
-rw-r--r--src/feature/relay/dns.c2
-rw-r--r--src/feature/relay/ext_orport.c2
-rw-r--r--src/feature/relay/onion_queue.c361
-rw-r--r--src/feature/relay/onion_queue.h23
-rw-r--r--src/feature/relay/router.c6
-rw-r--r--src/feature/rend/rendclient.c2
-rw-r--r--src/feature/rend/rendservice.c4
-rw-r--r--src/feature/stats/rephist.c10
-rw-r--r--src/feature/stats/rephist.h9
-rw-r--r--src/lib/net/socket.c88
-rw-r--r--src/lib/net/socket.h1
-rw-r--r--src/lib/time/compat_time.c22
-rw-r--r--src/lib/time/compat_time.h1
-rw-r--r--src/lib/tls/tortls_nss.c6
-rw-r--r--src/test/test.c8
-rw-r--r--src/test/test_cell_formats.c2
-rw-r--r--src/test/test_channelpadding.c4
-rw-r--r--src/test/test_circuitmux.c13
-rw-r--r--src/test/test_config.c3
-rw-r--r--src/test/test_connection.c4
-rw-r--r--src/test/test_controller.c2
-rw-r--r--src/test/test_dir.c16
-rw-r--r--src/test/test_dir_handle_get.c2
-rw-r--r--src/test/test_extorport.c23
-rw-r--r--src/test/test_guardfraction.c5
-rw-r--r--src/test/test_helpers.c25
-rw-r--r--src/test/test_helpers.h3
-rw-r--r--src/test/test_hs_client.c121
-rw-r--r--src/test/test_hs_service.c100
-rw-r--r--src/test/test_mainloop.c2
-rw-r--r--src/test/test_oos.c2
-rw-r--r--src/test/test_options.c2
-rw-r--r--src/test/test_periodic_event.c4
-rw-r--r--src/test/test_protover.c4
-rw-r--r--src/test/test_relay.c14
-rw-r--r--src/test/test_relaycell.c2
-rw-r--r--src/test/test_router.c2
-rw-r--r--src/test/test_routerlist.c3
-rw-r--r--src/test/test_shared_random.c3
-rw-r--r--src/test/test_status.c2
-rw-r--r--src/test/test_workqueue.c2
-rw-r--r--src/test/testing_common.c4
-rw-r--r--src/tools/tor-print-ed-signing-cert.c2
-rw-r--r--src/win32/orconfig.h2
123 files changed, 9194 insertions, 7973 deletions
diff --git a/src/app/config/config.c b/src/app/config/config.c
index 253d7ac9e8..abb3407043 100644
--- a/src/app/config/config.c
+++ b/src/app/config/config.c
@@ -1,4 +1,3 @@
-
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
@@ -87,14 +86,17 @@
#else
#include "lib/crypt_ops/crypto_openssl_mgt.h"
#endif
+#include "feature/dirauth/bwauth.h"
#include "feature/dircache/dirserv.h"
+#include "feature/dirauth/guardfraction.h"
#include "feature/relay/dns.h"
#include "core/or/dos.h"
#include "feature/client/entrynodes.h"
#include "lib/log/git_revision.h"
#include "feature/stats/geoip.h"
#include "feature/hibernate/hibernate.h"
-#include "core/mainloop/main.h"
+#include "app/main/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
#include "core/or/policies.h"
@@ -105,7 +107,7 @@
#include "feature/stats/rephist.h"
#include "feature/relay/router.h"
#include "lib/sandbox/sandbox.h"
-#include "feature/nodelist/routerlist.h"
+#include "feature/nodelist/dirlist.h"
#include "feature/nodelist/routerset.h"
#include "core/or/scheduler.h"
#include "app/config/statefile.h"
@@ -141,6 +143,7 @@
#include "lib/evloop/procmon.h"
#include "feature/dirauth/dirvote.h"
+#include "feature/dirauth/recommend_pkg.h"
#include "feature/dirauth/mode.h"
#include "core/or/connection_st.h"
@@ -457,6 +460,7 @@ static config_var_t option_vars_[] = {
VAR("HiddenServiceMaxStreams",LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceMaxStreamsCloseCircuit",LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceNumIntroductionPoints", LINELIST_S, RendConfigLines, NULL),
+ VAR("HiddenServiceExportCircuitID", LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceStatistics", BOOL, HiddenServiceStatistics_option, "1"),
V(HidServAuth, LINELIST, NULL),
V(ClientOnionAuthDir, FILENAME, NULL),
diff --git a/src/app/config/statefile.c b/src/app/config/statefile.c
index 656dc2eec3..208189db44 100644
--- a/src/app/config/statefile.c
+++ b/src/app/config/statefile.c
@@ -33,11 +33,11 @@
#include "core/or/circuitstats.h"
#include "app/config/config.h"
#include "app/config/confparse.h"
+#include "core/mainloop/mainloop.h"
#include "core/mainloop/connection.h"
#include "feature/control/control.h"
#include "feature/client/entrynodes.h"
#include "feature/hibernate/hibernate.h"
-#include "core/mainloop/main.h"
#include "feature/stats/rephist.h"
#include "feature/relay/router.h"
#include "lib/sandbox/sandbox.h"
diff --git a/src/app/main/main.c b/src/app/main/main.c
new file mode 100644
index 0000000000..ab66105116
--- /dev/null
+++ b/src/app/main/main.c
@@ -0,0 +1,1515 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file main.c
+ * \brief Invocation module. Initializes subsystems and runs the main loop.
+ **/
+
+#include "core/or/or.h"
+
+#include "feature/client/addressmap.h"
+#include "lib/err/backtrace.h"
+#include "feature/client/bridges.h"
+#include "lib/container/buffers.h"
+#include "core/or/channel.h"
+#include "core/or/channeltls.h"
+#include "core/or/channelpadding.h"
+#include "core/or/circuitlist.h"
+#include "core/or/circuitmux_ewma.h"
+#include "core/or/command.h"
+#include "lib/compress/compress.h"
+#include "app/config/config.h"
+#include "core/mainloop/connection.h"
+#include "core/or/connection_edge.h"
+#include "core/or/connection_or.h"
+#include "feature/dircache/consdiffmgr.h"
+#include "feature/control/control.h"
+#include "core/mainloop/cpuworker.h"
+#include "lib/crypt_ops/crypto_s2k.h"
+#include "lib/crypt_ops/crypto_rand.h"
+#include "feature/dircache/dirserv.h"
+#include "feature/dirauth/bwauth.h"
+#include "feature/dirauth/process_descs.h"
+#include "feature/relay/dns.h"
+#include "feature/client/entrynodes.h"
+#include "feature/stats/geoip.h"
+#include "feature/hibernate/hibernate.h"
+#include "feature/hs/hs_cache.h"
+#include "feature/dirauth/keypin.h"
+#include "app/main/main.h"
+#include "core/mainloop/mainloop.h"
+#include "feature/nodelist/microdesc.h"
+#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/nodelist.h"
+#include "app/main/ntmain.h"
+#include "feature/relay/onion_queue.h"
+#include "core/or/policies.h"
+#include "core/or/protover.h"
+#include "feature/client/transports.h"
+#include "core/or/relay.h"
+#include "feature/rend/rendcache.h"
+#include "feature/rend/rendclient.h"
+#include "feature/rend/rendservice.h"
+#include "feature/stats/rephist.h"
+#include "feature/relay/router.h"
+#include "feature/relay/routerkeys.h"
+#include "feature/nodelist/authcert.h"
+#include "feature/nodelist/routerlist.h"
+#include "feature/nodelist/routerparse.h"
+#include "core/or/scheduler.h"
+#include "app/config/statefile.h"
+#include "core/or/status.h"
+#include "feature/api/tor_api.h"
+#include "feature/api/tor_api_internal.h"
+#include "lib/process/waitpid.h"
+#include "feature/relay/ext_orport.h"
+#include "lib/meminfo/meminfo.h"
+#include "lib/osinfo/uname.h"
+#include "lib/sandbox/sandbox.h"
+#include "lib/fs/lockfile.h"
+#include "lib/net/resolve.h"
+#include "lib/tls/tortls.h"
+#include "lib/evloop/compat_libevent.h"
+#include "lib/encoding/confline.h"
+#include "lib/evloop/timers.h"
+#include "lib/crypt_ops/crypto_init.h"
+
+#include <event2/event.h>
+
+#include "feature/dirauth/dirvote.h"
+#include "feature/dirauth/mode.h"
+#include "feature/dirauth/shared_random.h"
+
+#include "core/or/or_connection_st.h"
+#include "core/or/port_cfg_st.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYSTEMD
+# if defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__)
+/* Systemd's use of gcc's __INCLUDE_LEVEL__ extension macro appears to confuse
+ * Coverity. Here's a kludge to unconfuse it.
+ */
+# define __INCLUDE_LEVEL__ 2
+#endif /* defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__) */
+#include <systemd/sd-daemon.h>
+#endif /* defined(HAVE_SYSTEMD) */
+
+void evdns_shutdown(int);
+
+#ifdef HAVE_RUST
+// helper function defined in Rust to output a log message indicating if tor is
+// running with Rust enabled. See src/rust/tor_util
+void rust_log_welcome_string(void);
+#endif
+
+/********* PROTOTYPES **********/
+
+static void dumpmemusage(int severity);
+static void dumpstats(int severity); /* log stats */
+static void process_signal(int sig);
+
+/********* START VARIABLES **********/
+
+/** Decides our behavior when no logs are configured/before any
+ * logs have been configured. For 0, we log notice to stdout as normal.
+ * For 1, we log warnings only. For 2, we log nothing.
+ */
+int quiet_level = 0;
+
+/********* END VARIABLES ************/
+
+/** Called when we get a SIGHUP: reload configuration files and keys,
+ * retry all connections, and so on. */
+static int
+do_hup(void)
+{
+ const or_options_t *options = get_options();
+
+ log_notice(LD_GENERAL,"Received reload signal (hup). Reloading config and "
+ "resetting internal state.");
+ if (accounting_is_enabled(options))
+ accounting_record_bandwidth_usage(time(NULL), get_or_state());
+
+ router_reset_warnings();
+ routerlist_reset_warnings();
+ /* first, reload config variables, in case they've changed */
+ if (options->ReloadTorrcOnSIGHUP) {
+ /* no need to provide argc/v, they've been cached in init_from_config */
+ int init_rv = options_init_from_torrc(0, NULL);
+ if (init_rv < 0) {
+ log_err(LD_CONFIG,"Reading config failed--see warnings above. "
+ "For usage, try -h.");
+ return -1;
+ } else if (BUG(init_rv > 0)) {
+ // LCOV_EXCL_START
+ /* This should be impossible: the only "return 1" cases in
+ * options_init_from_torrc are ones caused by command-line arguments;
+ * but they can't change while Tor is running. */
+ return -1;
+ // LCOV_EXCL_STOP
+ }
+ options = get_options(); /* they have changed now */
+ /* Logs are only truncated the first time they are opened, but were
+ probably intended to be cleaned up on signal. */
+ if (options->TruncateLogFile)
+ truncate_logs();
+ } else {
+ char *msg = NULL;
+ log_notice(LD_GENERAL, "Not reloading config file: the controller told "
+ "us not to.");
+ /* Make stuff get rescanned, reloaded, etc. */
+ if (set_options((or_options_t*)options, &msg) < 0) {
+ if (!msg)
+ msg = tor_strdup("Unknown error");
+ log_warn(LD_GENERAL, "Unable to re-set previous options: %s", msg);
+ tor_free(msg);
+ }
+ }
+ if (authdir_mode(options)) {
+ /* reload the approved-routers file */
+ if (dirserv_load_fingerprint_file() < 0) {
+ /* warnings are logged from dirserv_load_fingerprint_file() directly */
+ log_info(LD_GENERAL, "Error reloading fingerprints. "
+ "Continuing with old list.");
+ }
+ }
+
+ /* Rotate away from the old dirty circuits. This has to be done
+ * after we've read the new options, but before we start using
+ * circuits for directory fetches. */
+ circuit_mark_all_dirty_circs_as_unusable();
+
+ /* retry appropriate downloads */
+ router_reset_status_download_failures();
+ router_reset_descriptor_download_failures();
+ if (!net_is_disabled())
+ update_networkstatus_downloads(time(NULL));
+
+ /* We'll retry routerstatus downloads in about 10 seconds; no need to
+ * force a retry there. */
+
+ if (server_mode(options)) {
+ /* Maybe we've been given a new ed25519 key or certificate?
+ */
+ time_t now = approx_time();
+ int new_signing_key = load_ed_keys(options, now);
+ if (new_signing_key < 0 ||
+ generate_ed_link_cert(options, now, new_signing_key > 0)) {
+ log_warn(LD_OR, "Problem reloading Ed25519 keys; still using old keys.");
+ }
+
+ /* Update cpuworker and dnsworker processes, so they get up-to-date
+ * configuration options. */
+ cpuworkers_rotate_keyinfo();
+ dns_reset();
+ }
+ return 0;
+}
+
+/** Libevent callback: invoked when we get a signal.
+ */
+static void
+signal_callback(evutil_socket_t fd, short events, void *arg)
+{
+ const int *sigptr = arg;
+ const int sig = *sigptr;
+ (void)fd;
+ (void)events;
+
+ update_current_time(time(NULL));
+ process_signal(sig);
+}
+
+/** Do the work of acting on a signal received in <b>sig</b> */
+static void
+process_signal(int sig)
+{
+ switch (sig)
+ {
+ case SIGTERM:
+ log_notice(LD_GENERAL,"Catching signal TERM, exiting cleanly.");
+ tor_shutdown_event_loop_and_exit(0);
+ break;
+ case SIGINT:
+ if (!server_mode(get_options())) { /* do it now */
+ log_notice(LD_GENERAL,"Interrupt: exiting cleanly.");
+ tor_shutdown_event_loop_and_exit(0);
+ return;
+ }
+#ifdef HAVE_SYSTEMD
+ sd_notify(0, "STOPPING=1");
+#endif
+ hibernate_begin_shutdown();
+ break;
+#ifdef SIGPIPE
+ case SIGPIPE:
+ log_debug(LD_GENERAL,"Caught SIGPIPE. Ignoring.");
+ break;
+#endif
+ case SIGUSR1:
+ /* prefer to log it at INFO, but make sure we always see it */
+ dumpstats(get_min_log_level()<LOG_INFO ? get_min_log_level() : LOG_INFO);
+ control_event_signal(sig);
+ break;
+ case SIGUSR2:
+ switch_logs_debug();
+ log_debug(LD_GENERAL,"Caught USR2, going to loglevel debug. "
+ "Send HUP to change back.");
+ control_event_signal(sig);
+ break;
+ case SIGHUP:
+#ifdef HAVE_SYSTEMD
+ sd_notify(0, "RELOADING=1");
+#endif
+ if (do_hup() < 0) {
+ log_warn(LD_CONFIG,"Restart failed (config error?). Exiting.");
+ tor_shutdown_event_loop_and_exit(1);
+ return;
+ }
+#ifdef HAVE_SYSTEMD
+ sd_notify(0, "READY=1");
+#endif
+ control_event_signal(sig);
+ break;
+#ifdef SIGCHLD
+ case SIGCHLD:
+ notify_pending_waitpid_callbacks();
+ break;
+#endif
+ case SIGNEWNYM: {
+ do_signewnym(time(NULL));
+ break;
+ }
+ case SIGCLEARDNSCACHE:
+ addressmap_clear_transient();
+ control_event_signal(sig);
+ break;
+ case SIGHEARTBEAT:
+ log_heartbeat(time(NULL));
+ control_event_signal(sig);
+ break;
+ }
+}
+
+/**
+ * Write current memory usage information to the log.
+ */
+static void
+dumpmemusage(int severity)
+{
+ connection_dump_buffer_mem_stats(severity);
+ tor_log(severity, LD_GENERAL, "In rephist: %"PRIu64" used by %d Tors.",
+ (rephist_total_alloc), rephist_total_num);
+ dump_routerlist_mem_usage(severity);
+ dump_cell_pool_usage(severity);
+ dump_dns_mem_usage(severity);
+ tor_log_mallinfo(severity);
+}
+
+/** Write all statistics to the log, with log level <b>severity</b>. Called
+ * in response to a SIGUSR1. */
+static void
+dumpstats(int severity)
+{
+ time_t now = time(NULL);
+ time_t elapsed;
+ size_t rbuf_cap, wbuf_cap, rbuf_len, wbuf_len;
+
+ tor_log(severity, LD_GENERAL, "Dumping stats:");
+
+ SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) {
+ int i = conn_sl_idx;
+ tor_log(severity, LD_GENERAL,
+ "Conn %d (socket %d) type %d (%s), state %d (%s), created %d secs ago",
+ i, (int)conn->s, conn->type, conn_type_to_string(conn->type),
+ conn->state, conn_state_to_string(conn->type, conn->state),
+ (int)(now - conn->timestamp_created));
+ if (!connection_is_listener(conn)) {
+ tor_log(severity,LD_GENERAL,
+ "Conn %d is to %s:%d.", i,
+ safe_str_client(conn->address),
+ conn->port);
+ tor_log(severity,LD_GENERAL,
+ "Conn %d: %d bytes waiting on inbuf (len %d, last read %d secs ago)",
+ i,
+ (int)connection_get_inbuf_len(conn),
+ (int)buf_allocation(conn->inbuf),
+ (int)(now - conn->timestamp_last_read_allowed));
+ tor_log(severity,LD_GENERAL,
+ "Conn %d: %d bytes waiting on outbuf "
+ "(len %d, last written %d secs ago)",i,
+ (int)connection_get_outbuf_len(conn),
+ (int)buf_allocation(conn->outbuf),
+ (int)(now - conn->timestamp_last_write_allowed));
+ if (conn->type == CONN_TYPE_OR) {
+ or_connection_t *or_conn = TO_OR_CONN(conn);
+ if (or_conn->tls) {
+ if (tor_tls_get_buffer_sizes(or_conn->tls, &rbuf_cap, &rbuf_len,
+ &wbuf_cap, &wbuf_len) == 0) {
+ tor_log(severity, LD_GENERAL,
+ "Conn %d: %d/%d bytes used on OpenSSL read buffer; "
+ "%d/%d bytes used on write buffer.",
+ i, (int)rbuf_len, (int)rbuf_cap, (int)wbuf_len, (int)wbuf_cap);
+ }
+ }
+ }
+ }
+ circuit_dump_by_conn(conn, severity); /* dump info about all the circuits
+ * using this conn */
+ } SMARTLIST_FOREACH_END(conn);
+
+ channel_dumpstats(severity);
+ channel_listener_dumpstats(severity);
+
+ tor_log(severity, LD_NET,
+ "Cells processed: %"PRIu64" padding\n"
+ " %"PRIu64" create\n"
+ " %"PRIu64" created\n"
+ " %"PRIu64" relay\n"
+ " (%"PRIu64" relayed)\n"
+ " (%"PRIu64" delivered)\n"
+ " %"PRIu64" destroy",
+ (stats_n_padding_cells_processed),
+ (stats_n_create_cells_processed),
+ (stats_n_created_cells_processed),
+ (stats_n_relay_cells_processed),
+ (stats_n_relay_cells_relayed),
+ (stats_n_relay_cells_delivered),
+ (stats_n_destroy_cells_processed));
+ if (stats_n_data_cells_packaged)
+ tor_log(severity,LD_NET,"Average packaged cell fullness: %2.3f%%",
+ 100*(((double)stats_n_data_bytes_packaged) /
+ ((double)stats_n_data_cells_packaged*RELAY_PAYLOAD_SIZE)) );
+ if (stats_n_data_cells_received)
+ tor_log(severity,LD_NET,"Average delivered cell fullness: %2.3f%%",
+ 100*(((double)stats_n_data_bytes_received) /
+ ((double)stats_n_data_cells_received*RELAY_PAYLOAD_SIZE)) );
+
+ cpuworker_log_onionskin_overhead(severity, ONION_HANDSHAKE_TYPE_TAP, "TAP");
+ cpuworker_log_onionskin_overhead(severity, ONION_HANDSHAKE_TYPE_NTOR,"ntor");
+
+ if (now - time_of_process_start >= 0)
+ elapsed = now - time_of_process_start;
+ else
+ elapsed = 0;
+
+ if (elapsed) {
+ tor_log(severity, LD_NET,
+ "Average bandwidth: %"PRIu64"/%d = %d bytes/sec reading",
+ (get_bytes_read()),
+ (int)elapsed,
+ (int) (get_bytes_read()/elapsed));
+ tor_log(severity, LD_NET,
+ "Average bandwidth: %"PRIu64"/%d = %d bytes/sec writing",
+ (get_bytes_written()),
+ (int)elapsed,
+ (int) (get_bytes_written()/elapsed));
+ }
+
+ tor_log(severity, LD_NET, "--------------- Dumping memory information:");
+ dumpmemusage(severity);
+
+ rep_hist_dump_stats(now,severity);
+ rend_service_dump_stats(severity);
+ dump_distinct_digest_count(severity);
+}
+
+/** Called by exit() as we shut down the process.
+ */
+static void
+exit_function(void)
+{
+ /* NOTE: If we ever daemonize, this gets called immediately. That's
+ * okay for now, because we only use this on Windows. */
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+#ifdef _WIN32
+#define UNIX_ONLY 0
+#else
+#define UNIX_ONLY 1
+#endif
+
+static struct {
+ /** A numeric code for this signal. Must match the signal value if
+ * try_to_register is true. */
+ int signal_value;
+ /** True if we should try to register this signal with libevent and catch
+ * corresponding posix signals. False otherwise. */
+ int try_to_register;
+ /** Pointer to hold the event object constructed for this signal. */
+ struct event *signal_event;
+} signal_handlers[] = {
+#ifdef SIGINT
+ { SIGINT, UNIX_ONLY, NULL }, /* do a controlled slow shutdown */
+#endif
+#ifdef SIGTERM
+ { SIGTERM, UNIX_ONLY, NULL }, /* to terminate now */
+#endif
+#ifdef SIGPIPE
+ { SIGPIPE, UNIX_ONLY, NULL }, /* otherwise SIGPIPE kills us */
+#endif
+#ifdef SIGUSR1
+ { SIGUSR1, UNIX_ONLY, NULL }, /* dump stats */
+#endif
+#ifdef SIGUSR2
+ { SIGUSR2, UNIX_ONLY, NULL }, /* go to loglevel debug */
+#endif
+#ifdef SIGHUP
+ { SIGHUP, UNIX_ONLY, NULL }, /* to reload config, retry conns, etc */
+#endif
+#ifdef SIGXFSZ
+ { SIGXFSZ, UNIX_ONLY, NULL }, /* handle file-too-big resource exhaustion */
+#endif
+#ifdef SIGCHLD
+ { SIGCHLD, UNIX_ONLY, NULL }, /* handle dns/cpu workers that exit */
+#endif
+ /* These are controller-only */
+ { SIGNEWNYM, 0, NULL },
+ { SIGCLEARDNSCACHE, 0, NULL },
+ { SIGHEARTBEAT, 0, NULL },
+ { -1, -1, NULL }
+};
+
+/** Set up the signal handler events for this process, and register them
+ * with libevent if appropriate. */
+void
+handle_signals(void)
+{
+ int i;
+ const int enabled = !get_options()->DisableSignalHandlers;
+
+ for (i = 0; signal_handlers[i].signal_value >= 0; ++i) {
+ /* Signal handlers are only registered with libevent if they need to catch
+ * real POSIX signals. We construct these signal handler events in either
+ * case, though, so that controllers can activate them with the SIGNAL
+ * command.
+ */
+ if (enabled && signal_handlers[i].try_to_register) {
+ signal_handlers[i].signal_event =
+ tor_evsignal_new(tor_libevent_get_base(),
+ signal_handlers[i].signal_value,
+ signal_callback,
+ &signal_handlers[i].signal_value);
+ if (event_add(signal_handlers[i].signal_event, NULL))
+ log_warn(LD_BUG, "Error from libevent when adding "
+ "event for signal %d",
+ signal_handlers[i].signal_value);
+ } else {
+ signal_handlers[i].signal_event =
+ tor_event_new(tor_libevent_get_base(), -1,
+ EV_SIGNAL, signal_callback,
+ &signal_handlers[i].signal_value);
+ }
+ }
+}
+
+/* Cause the signal handler for signal_num to be called in the event loop. */
+void
+activate_signal(int signal_num)
+{
+ int i;
+ for (i = 0; signal_handlers[i].signal_value >= 0; ++i) {
+ if (signal_handlers[i].signal_value == signal_num) {
+ event_active(signal_handlers[i].signal_event, EV_SIGNAL, 1);
+ return;
+ }
+ }
+}
+
+/** Main entry point for the Tor command-line client. Return 0 on "success",
+ * negative on "failure", and positive on "success and exit".
+ */
+int
+tor_init(int argc, char *argv[])
+{
+ char progname[256];
+ int quiet = 0;
+
+ time_of_process_start = time(NULL);
+ tor_init_connection_lists();
+ /* Have the log set up with our application name. */
+ tor_snprintf(progname, sizeof(progname), "Tor %s", get_version());
+ log_set_application_name(progname);
+
+ /* Set up the crypto nice and early */
+ if (crypto_early_init() < 0) {
+ log_err(LD_GENERAL, "Unable to initialize the crypto subsystem!");
+ return -1;
+ }
+
+ /* Initialize the history structures. */
+ rep_hist_init();
+ /* Initialize the service cache. */
+ rend_cache_init();
+ addressmap_init(); /* Init the client dns cache. Do it always, since it's
+ * cheap. */
+ /* Initialize the HS subsystem. */
+ hs_init();
+
+ {
+ /* We search for the "quiet" option first, since it decides whether we
+ * will log anything at all to the command line. */
+ config_line_t *opts = NULL, *cmdline_opts = NULL;
+ const config_line_t *cl;
+ (void) config_parse_commandline(argc, argv, 1, &opts, &cmdline_opts);
+ for (cl = cmdline_opts; cl; cl = cl->next) {
+ if (!strcmp(cl->key, "--hush"))
+ quiet = 1;
+ if (!strcmp(cl->key, "--quiet") ||
+ !strcmp(cl->key, "--dump-config"))
+ quiet = 2;
+ /* The following options imply --hush */
+ if (!strcmp(cl->key, "--version") || !strcmp(cl->key, "--digests") ||
+ !strcmp(cl->key, "--list-torrc-options") ||
+ !strcmp(cl->key, "--library-versions") ||
+ !strcmp(cl->key, "--hash-password") ||
+ !strcmp(cl->key, "-h") || !strcmp(cl->key, "--help")) {
+ if (quiet < 1)
+ quiet = 1;
+ }
+ }
+ config_free_lines(opts);
+ config_free_lines(cmdline_opts);
+ }
+
+ /* give it somewhere to log to initially */
+ switch (quiet) {
+ case 2:
+ /* no initial logging */
+ break;
+ case 1:
+ add_temp_log(LOG_WARN);
+ break;
+ default:
+ add_temp_log(LOG_NOTICE);
+ }
+ quiet_level = quiet;
+
+ {
+ const char *version = get_version();
+
+ log_notice(LD_GENERAL, "Tor %s running on %s with Libevent %s, "
+ "%s %s, Zlib %s, Liblzma %s, and Libzstd %s.", version,
+ get_uname(),
+ tor_libevent_get_version_str(),
+ crypto_get_library_name(),
+ crypto_get_library_version_string(),
+ tor_compress_supports_method(ZLIB_METHOD) ?
+ tor_compress_version_str(ZLIB_METHOD) : "N/A",
+ tor_compress_supports_method(LZMA_METHOD) ?
+ tor_compress_version_str(LZMA_METHOD) : "N/A",
+ tor_compress_supports_method(ZSTD_METHOD) ?
+ tor_compress_version_str(ZSTD_METHOD) : "N/A");
+
+ log_notice(LD_GENERAL, "Tor can't help you if you use it wrong! "
+ "Learn how to be safe at "
+ "https://www.torproject.org/download/download#warning");
+
+ if (strstr(version, "alpha") || strstr(version, "beta"))
+ log_notice(LD_GENERAL, "This version is not a stable Tor release. "
+ "Expect more bugs than usual.");
+
+ tor_compress_log_init_warnings();
+ }
+
+#ifdef HAVE_RUST
+ rust_log_welcome_string();
+#endif /* defined(HAVE_RUST) */
+
+ if (network_init()<0) {
+ log_err(LD_BUG,"Error initializing network; exiting.");
+ return -1;
+ }
+ atexit(exit_function);
+
+ int init_rv = options_init_from_torrc(argc,argv);
+ if (init_rv < 0) {
+ log_err(LD_CONFIG,"Reading config failed--see warnings above.");
+ return -1;
+ } else if (init_rv > 0) {
+ // We succeeded, and should exit anyway -- probably the user just said
+ // "--version" or something like that.
+ return 1;
+ }
+
+ /* The options are now initialised */
+ const or_options_t *options = get_options();
+
+ /* Initialize channelpadding parameters to defaults until we get
+ * a consensus */
+ channelpadding_new_consensus_params(NULL);
+
+ /* Initialize predicted ports list after loading options */
+ predicted_ports_init();
+
+#ifndef _WIN32
+ if (geteuid()==0)
+ log_warn(LD_GENERAL,"You are running Tor as root. You don't need to, "
+ "and you probably shouldn't.");
+#endif
+
+ if (crypto_global_init(options->HardwareAccel,
+ options->AccelName,
+ options->AccelDir)) {
+ log_err(LD_BUG, "Unable to initialize OpenSSL. Exiting.");
+ return -1;
+ }
+ stream_choice_seed_weak_rng();
+ if (tor_init_libevent_rng() < 0) {
+ log_warn(LD_NET, "Problem initializing libevent RNG.");
+ }
+
+ /* Scan/clean unparseable descriptors; after reading config */
+ routerparse_init();
+
+ return 0;
+}
+
+/** A lockfile structure, used to prevent two Tors from messing with the
+ * data directory at once. If this variable is non-NULL, we're holding
+ * the lockfile. */
+static tor_lockfile_t *lockfile = NULL;
+
+/** Try to grab the lock file described in <b>options</b>, if we do not
+ * already have it. If <b>err_if_locked</b> is true, warn if somebody else is
+ * holding the lock, and exit if we can't get it after waiting. Otherwise,
+ * return -1 if we can't get the lockfile. Return 0 on success.
+ */
+int
+try_locking(const or_options_t *options, int err_if_locked)
+{
+ if (lockfile)
+ return 0;
+ else {
+ char *fname = options_get_datadir_fname(options, "lock");
+ int already_locked = 0;
+ tor_lockfile_t *lf = tor_lockfile_lock(fname, 0, &already_locked);
+ tor_free(fname);
+ if (!lf) {
+ if (err_if_locked && already_locked) {
+ int r;
+ log_warn(LD_GENERAL, "It looks like another Tor process is running "
+ "with the same data directory. Waiting 5 seconds to see "
+ "if it goes away.");
+#ifndef _WIN32
+ sleep(5);
+#else
+ Sleep(5000);
+#endif
+ r = try_locking(options, 0);
+ if (r<0) {
+ log_err(LD_GENERAL, "No, it's still there. Exiting.");
+ return -1;
+ }
+ return r;
+ }
+ return -1;
+ }
+ lockfile = lf;
+ return 0;
+ }
+}
+
+/** Return true iff we've successfully acquired the lock file. */
+int
+have_lockfile(void)
+{
+ return lockfile != NULL;
+}
+
+/** If we have successfully acquired the lock file, release it. */
+void
+release_lockfile(void)
+{
+ if (lockfile) {
+ tor_lockfile_unlock(lockfile);
+ lockfile = NULL;
+ }
+}
+
+/** Free all memory that we might have allocated somewhere.
+ * If <b>postfork</b>, we are a worker process and we want to free
+ * only the parts of memory that we won't touch. If !<b>postfork</b>,
+ * Tor is shutting down and we should free everything.
+ *
+ * Helps us find the real leaks with sanitizers and the like. Also valgrind
+ * should then report 0 reachable in its leak report (in an ideal world --
+ * in practice libevent, SSL, libc etc never quite free everything). */
+void
+tor_free_all(int postfork)
+{
+ if (!postfork) {
+ evdns_shutdown(1);
+ }
+ geoip_free_all();
+ dirvote_free_all();
+ routerlist_free_all();
+ networkstatus_free_all();
+ addressmap_free_all();
+ dirserv_free_fingerprint_list();
+ dirserv_free_all();
+ dirserv_clear_measured_bw_cache();
+ rend_cache_free_all();
+ rend_service_authorization_free_all();
+ rep_hist_free_all();
+ dns_free_all();
+ clear_pending_onions();
+ circuit_free_all();
+ entry_guards_free_all();
+ pt_free_all();
+ channel_tls_free_all();
+ channel_free_all();
+ connection_free_all();
+ connection_edge_free_all();
+ scheduler_free_all();
+ nodelist_free_all();
+ microdesc_free_all();
+ routerparse_free_all();
+ ext_orport_free_all();
+ control_free_all();
+ tor_free_getaddrinfo_cache();
+ protover_free_all();
+ bridges_free_all();
+ consdiffmgr_free_all();
+ hs_free_all();
+ dos_free_all();
+ circuitmux_ewma_free_all();
+ accounting_free_all();
+
+ if (!postfork) {
+ config_free_all();
+ or_state_free_all();
+ router_free_all();
+ routerkeys_free_all();
+ policies_free_all();
+ }
+ if (!postfork) {
+ tor_tls_free_all();
+#ifndef _WIN32
+ tor_getpwnam(NULL);
+#endif
+ }
+ /* stuff in main.c */
+
+ tor_mainloop_free_all();
+
+#ifdef HAVE_SYSTEMD_209
+ periodic_timer_free(systemd_watchdog_timer);
+#endif
+
+ if (!postfork) {
+ release_lockfile();
+ }
+ tor_libevent_free_all();
+ /* Stuff in util.c and address.c*/
+ if (!postfork) {
+ escaped(NULL);
+ esc_router_info(NULL);
+ clean_up_backtrace_handler();
+ logs_free_all(); /* free log strings. do this last so logs keep working. */
+ }
+}
+
+/**
+ * Remove the specified file, and log a warning if the operation fails for
+ * any reason other than the file not existing. Ignores NULL filenames.
+ */
+void
+tor_remove_file(const char *filename)
+{
+ if (filename && tor_unlink(filename) != 0 && errno != ENOENT) {
+ log_warn(LD_FS, "Couldn't unlink %s: %s",
+ filename, strerror(errno));
+ }
+}
+
+/** Do whatever cleanup is necessary before shutting Tor down. */
+void
+tor_cleanup(void)
+{
+ const or_options_t *options = get_options();
+ if (options->command == CMD_RUN_TOR) {
+ time_t now = time(NULL);
+ /* Remove our pid file. We don't care if there was an error when we
+ * unlink, nothing we could do about it anyways. */
+ tor_remove_file(options->PidFile);
+ /* Remove control port file */
+ tor_remove_file(options->ControlPortWriteToFile);
+ /* Remove cookie authentication file */
+ {
+ char *cookie_fname = get_controller_cookie_file_name();
+ tor_remove_file(cookie_fname);
+ tor_free(cookie_fname);
+ }
+ /* Remove Extended ORPort cookie authentication file */
+ {
+ char *cookie_fname = get_ext_or_auth_cookie_file_name();
+ tor_remove_file(cookie_fname);
+ tor_free(cookie_fname);
+ }
+ if (accounting_is_enabled(options))
+ accounting_record_bandwidth_usage(now, get_or_state());
+ or_state_mark_dirty(get_or_state(), 0); /* force an immediate save. */
+ or_state_save(now);
+ if (authdir_mode(options)) {
+ sr_save_and_cleanup();
+ }
+ if (authdir_mode_tests_reachability(options))
+ rep_hist_record_mtbf_data(now, 0);
+ keypin_close_journal();
+ }
+
+ timers_shutdown();
+
+ tor_free_all(0); /* We could move tor_free_all back into the ifdef below
+ later, if it makes shutdown unacceptably slow. But for
+ now, leave it here: it's helped us catch bugs in the
+ past. */
+ crypto_global_cleanup();
+}
+
+/** Read/create keys as needed, and echo our fingerprint to stdout. */
+static int
+do_list_fingerprint(void)
+{
+ char buf[FINGERPRINT_LEN+1];
+ crypto_pk_t *k;
+ const char *nickname = get_options()->Nickname;
+ sandbox_disable_getaddrinfo_cache();
+ if (!server_mode(get_options())) {
+ log_err(LD_GENERAL,
+ "Clients don't have long-term identity keys. Exiting.");
+ return -1;
+ }
+ tor_assert(nickname);
+ if (init_keys() < 0) {
+ log_err(LD_GENERAL,"Error initializing keys; exiting.");
+ return -1;
+ }
+ if (!(k = get_server_identity_key())) {
+ log_err(LD_GENERAL,"Error: missing identity key.");
+ return -1;
+ }
+ if (crypto_pk_get_fingerprint(k, buf, 1)<0) {
+ log_err(LD_BUG, "Error computing fingerprint");
+ return -1;
+ }
+ printf("%s %s\n", nickname, buf);
+ return 0;
+}
+
+/** Entry point for password hashing: take the desired password from
+ * the command line, and print its salted hash to stdout. **/
+static void
+do_hash_password(void)
+{
+
+ char output[256];
+ char key[S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN];
+
+ crypto_rand(key, S2K_RFC2440_SPECIFIER_LEN-1);
+ key[S2K_RFC2440_SPECIFIER_LEN-1] = (uint8_t)96; /* Hash 64 K of data. */
+ secret_to_key_rfc2440(key+S2K_RFC2440_SPECIFIER_LEN, DIGEST_LEN,
+ get_options()->command_arg, strlen(get_options()->command_arg),
+ key);
+ base16_encode(output, sizeof(output), key, sizeof(key));
+ printf("16:%s\n",output);
+}
+
+/** Entry point for configuration dumping: write the configuration to
+ * stdout. */
+static int
+do_dump_config(void)
+{
+ const or_options_t *options = get_options();
+ const char *arg = options->command_arg;
+ int how;
+ char *opts;
+
+ if (!strcmp(arg, "short")) {
+ how = OPTIONS_DUMP_MINIMAL;
+ } else if (!strcmp(arg, "non-builtin")) {
+ how = OPTIONS_DUMP_DEFAULTS;
+ } else if (!strcmp(arg, "full")) {
+ how = OPTIONS_DUMP_ALL;
+ } else {
+ fprintf(stderr, "No valid argument to --dump-config found!\n");
+ fprintf(stderr, "Please select 'short', 'non-builtin', or 'full'.\n");
+
+ return -1;
+ }
+
+ opts = options_dump(options, how);
+ printf("%s", opts);
+ tor_free(opts);
+
+ return 0;
+}
+
+static void
+init_addrinfo(void)
+{
+ if (! server_mode(get_options()) ||
+ (get_options()->Address && strlen(get_options()->Address) > 0)) {
+ /* We don't need to seed our own hostname, because we won't be calling
+ * resolve_my_address on it.
+ */
+ return;
+ }
+ char hname[256];
+
+ // host name to sandbox
+ gethostname(hname, sizeof(hname));
+ tor_add_addrinfo(hname);
+}
+
+static sandbox_cfg_t*
+sandbox_init_filter(void)
+{
+ const or_options_t *options = get_options();
+ sandbox_cfg_t *cfg = sandbox_cfg_new();
+ int i;
+
+ sandbox_cfg_allow_openat_filename(&cfg,
+ get_cachedir_fname("cached-status"));
+
+#define OPEN(name) \
+ sandbox_cfg_allow_open_filename(&cfg, tor_strdup(name))
+
+#define OPEN_DATADIR(name) \
+ sandbox_cfg_allow_open_filename(&cfg, get_datadir_fname(name))
+
+#define OPEN_DATADIR2(name, name2) \
+ sandbox_cfg_allow_open_filename(&cfg, get_datadir_fname2((name), (name2)))
+
+#define OPEN_DATADIR_SUFFIX(name, suffix) do { \
+ OPEN_DATADIR(name); \
+ OPEN_DATADIR(name suffix); \
+ } while (0)
+
+#define OPEN_DATADIR2_SUFFIX(name, name2, suffix) do { \
+ OPEN_DATADIR2(name, name2); \
+ OPEN_DATADIR2(name, name2 suffix); \
+ } while (0)
+
+#define OPEN_KEY_DIRECTORY() \
+ sandbox_cfg_allow_open_filename(&cfg, tor_strdup(options->KeyDirectory))
+#define OPEN_CACHEDIR(name) \
+ sandbox_cfg_allow_open_filename(&cfg, get_cachedir_fname(name))
+#define OPEN_CACHEDIR_SUFFIX(name, suffix) do { \
+ OPEN_CACHEDIR(name); \
+ OPEN_CACHEDIR(name suffix); \
+ } while (0)
+#define OPEN_KEYDIR(name) \
+ sandbox_cfg_allow_open_filename(&cfg, get_keydir_fname(name))
+#define OPEN_KEYDIR_SUFFIX(name, suffix) do { \
+ OPEN_KEYDIR(name); \
+ OPEN_KEYDIR(name suffix); \
+ } while (0)
+
+ OPEN(options->DataDirectory);
+ OPEN_KEY_DIRECTORY();
+
+ OPEN_CACHEDIR_SUFFIX("cached-certs", ".tmp");
+ OPEN_CACHEDIR_SUFFIX("cached-consensus", ".tmp");
+ OPEN_CACHEDIR_SUFFIX("unverified-consensus", ".tmp");
+ OPEN_CACHEDIR_SUFFIX("unverified-microdesc-consensus", ".tmp");
+ OPEN_CACHEDIR_SUFFIX("cached-microdesc-consensus", ".tmp");
+ OPEN_CACHEDIR_SUFFIX("cached-microdescs", ".tmp");
+ OPEN_CACHEDIR_SUFFIX("cached-microdescs.new", ".tmp");
+ OPEN_CACHEDIR_SUFFIX("cached-descriptors", ".tmp");
+ OPEN_CACHEDIR_SUFFIX("cached-descriptors.new", ".tmp");
+ OPEN_CACHEDIR("cached-descriptors.tmp.tmp");
+ OPEN_CACHEDIR_SUFFIX("cached-extrainfo", ".tmp");
+ OPEN_CACHEDIR_SUFFIX("cached-extrainfo.new", ".tmp");
+ OPEN_CACHEDIR("cached-extrainfo.tmp.tmp");
+
+ OPEN_DATADIR_SUFFIX("state", ".tmp");
+ OPEN_DATADIR_SUFFIX("sr-state", ".tmp");
+ OPEN_DATADIR_SUFFIX("unparseable-desc", ".tmp");
+ OPEN_DATADIR_SUFFIX("v3-status-votes", ".tmp");
+ OPEN_DATADIR("key-pinning-journal");
+ OPEN("/dev/srandom");
+ OPEN("/dev/urandom");
+ OPEN("/dev/random");
+ OPEN("/etc/hosts");
+ OPEN("/proc/meminfo");
+
+ if (options->BridgeAuthoritativeDir)
+ OPEN_DATADIR_SUFFIX("networkstatus-bridges", ".tmp");
+
+ if (authdir_mode(options))
+ OPEN_DATADIR("approved-routers");
+
+ if (options->ServerDNSResolvConfFile)
+ sandbox_cfg_allow_open_filename(&cfg,
+ tor_strdup(options->ServerDNSResolvConfFile));
+ else
+ sandbox_cfg_allow_open_filename(&cfg, tor_strdup("/etc/resolv.conf"));
+
+ for (i = 0; i < 2; ++i) {
+ if (get_torrc_fname(i)) {
+ sandbox_cfg_allow_open_filename(&cfg, tor_strdup(get_torrc_fname(i)));
+ }
+ }
+
+ SMARTLIST_FOREACH(options->FilesOpenedByIncludes, char *, f, {
+ OPEN(f);
+ });
+
+#define RENAME_SUFFIX(name, suffix) \
+ sandbox_cfg_allow_rename(&cfg, \
+ get_datadir_fname(name suffix), \
+ get_datadir_fname(name))
+
+#define RENAME_SUFFIX2(prefix, name, suffix) \
+ sandbox_cfg_allow_rename(&cfg, \
+ get_datadir_fname2(prefix, name suffix), \
+ get_datadir_fname2(prefix, name))
+
+#define RENAME_CACHEDIR_SUFFIX(name, suffix) \
+ sandbox_cfg_allow_rename(&cfg, \
+ get_cachedir_fname(name suffix), \
+ get_cachedir_fname(name))
+
+#define RENAME_KEYDIR_SUFFIX(name, suffix) \
+ sandbox_cfg_allow_rename(&cfg, \
+ get_keydir_fname(name suffix), \
+ get_keydir_fname(name))
+
+ RENAME_CACHEDIR_SUFFIX("cached-certs", ".tmp");
+ RENAME_CACHEDIR_SUFFIX("cached-consensus", ".tmp");
+ RENAME_CACHEDIR_SUFFIX("unverified-consensus", ".tmp");
+ RENAME_CACHEDIR_SUFFIX("unverified-microdesc-consensus", ".tmp");
+ RENAME_CACHEDIR_SUFFIX("cached-microdesc-consensus", ".tmp");
+ RENAME_CACHEDIR_SUFFIX("cached-microdescs", ".tmp");
+ RENAME_CACHEDIR_SUFFIX("cached-microdescs", ".new");
+ RENAME_CACHEDIR_SUFFIX("cached-microdescs.new", ".tmp");
+ RENAME_CACHEDIR_SUFFIX("cached-descriptors", ".tmp");
+ RENAME_CACHEDIR_SUFFIX("cached-descriptors", ".new");
+ RENAME_CACHEDIR_SUFFIX("cached-descriptors.new", ".tmp");
+ RENAME_CACHEDIR_SUFFIX("cached-extrainfo", ".tmp");
+ RENAME_CACHEDIR_SUFFIX("cached-extrainfo", ".new");
+ RENAME_CACHEDIR_SUFFIX("cached-extrainfo.new", ".tmp");
+
+ RENAME_SUFFIX("state", ".tmp");
+ RENAME_SUFFIX("sr-state", ".tmp");
+ RENAME_SUFFIX("unparseable-desc", ".tmp");
+ RENAME_SUFFIX("v3-status-votes", ".tmp");
+
+ if (options->BridgeAuthoritativeDir)
+ RENAME_SUFFIX("networkstatus-bridges", ".tmp");
+
+#define STAT_DATADIR(name) \
+ sandbox_cfg_allow_stat_filename(&cfg, get_datadir_fname(name))
+
+#define STAT_CACHEDIR(name) \
+ sandbox_cfg_allow_stat_filename(&cfg, get_cachedir_fname(name))
+
+#define STAT_DATADIR2(name, name2) \
+ sandbox_cfg_allow_stat_filename(&cfg, get_datadir_fname2((name), (name2)))
+
+#define STAT_KEY_DIRECTORY() \
+ sandbox_cfg_allow_stat_filename(&cfg, tor_strdup(options->KeyDirectory))
+
+ STAT_DATADIR(NULL);
+ STAT_DATADIR("lock");
+ STAT_DATADIR("state");
+ STAT_DATADIR("router-stability");
+
+ STAT_CACHEDIR("cached-extrainfo.new");
+
+ {
+ smartlist_t *files = smartlist_new();
+ tor_log_get_logfile_names(files);
+ SMARTLIST_FOREACH(files, char *, file_name, {
+ /* steals reference */
+ sandbox_cfg_allow_open_filename(&cfg, file_name);
+ });
+ smartlist_free(files);
+ }
+
+ {
+ smartlist_t *files = smartlist_new();
+ smartlist_t *dirs = smartlist_new();
+ hs_service_lists_fnames_for_sandbox(files, dirs);
+ SMARTLIST_FOREACH(files, char *, file_name, {
+ char *tmp_name = NULL;
+ tor_asprintf(&tmp_name, "%s.tmp", file_name);
+ sandbox_cfg_allow_rename(&cfg,
+ tor_strdup(tmp_name), tor_strdup(file_name));
+ /* steals references */
+ sandbox_cfg_allow_open_filename(&cfg, file_name);
+ sandbox_cfg_allow_open_filename(&cfg, tmp_name);
+ });
+ SMARTLIST_FOREACH(dirs, char *, dir, {
+ /* steals reference */
+ sandbox_cfg_allow_stat_filename(&cfg, dir);
+ });
+ smartlist_free(files);
+ smartlist_free(dirs);
+ }
+
+ {
+ char *fname;
+ if ((fname = get_controller_cookie_file_name())) {
+ sandbox_cfg_allow_open_filename(&cfg, fname);
+ }
+ if ((fname = get_ext_or_auth_cookie_file_name())) {
+ sandbox_cfg_allow_open_filename(&cfg, fname);
+ }
+ }
+
+ SMARTLIST_FOREACH_BEGIN(get_configured_ports(), port_cfg_t *, port) {
+ if (!port->is_unix_addr)
+ continue;
+ /* When we open an AF_UNIX address, we want permission to open the
+ * directory that holds it. */
+ char *dirname = tor_strdup(port->unix_addr);
+ if (get_parent_directory(dirname) == 0) {
+ OPEN(dirname);
+ }
+ tor_free(dirname);
+ sandbox_cfg_allow_chmod_filename(&cfg, tor_strdup(port->unix_addr));
+ sandbox_cfg_allow_chown_filename(&cfg, tor_strdup(port->unix_addr));
+ } SMARTLIST_FOREACH_END(port);
+
+ if (options->DirPortFrontPage) {
+ sandbox_cfg_allow_open_filename(&cfg,
+ tor_strdup(options->DirPortFrontPage));
+ }
+
+ // orport
+ if (server_mode(get_options())) {
+
+ OPEN_KEYDIR_SUFFIX("secret_id_key", ".tmp");
+ OPEN_KEYDIR_SUFFIX("secret_onion_key", ".tmp");
+ OPEN_KEYDIR_SUFFIX("secret_onion_key_ntor", ".tmp");
+ OPEN_KEYDIR("secret_id_key.old");
+ OPEN_KEYDIR("secret_onion_key.old");
+ OPEN_KEYDIR("secret_onion_key_ntor.old");
+
+ OPEN_KEYDIR_SUFFIX("ed25519_master_id_secret_key", ".tmp");
+ OPEN_KEYDIR_SUFFIX("ed25519_master_id_secret_key_encrypted", ".tmp");
+ OPEN_KEYDIR_SUFFIX("ed25519_master_id_public_key", ".tmp");
+ OPEN_KEYDIR_SUFFIX("ed25519_signing_secret_key", ".tmp");
+ OPEN_KEYDIR_SUFFIX("ed25519_signing_secret_key_encrypted", ".tmp");
+ OPEN_KEYDIR_SUFFIX("ed25519_signing_public_key", ".tmp");
+ OPEN_KEYDIR_SUFFIX("ed25519_signing_cert", ".tmp");
+
+ OPEN_DATADIR2_SUFFIX("stats", "bridge-stats", ".tmp");
+ OPEN_DATADIR2_SUFFIX("stats", "dirreq-stats", ".tmp");
+
+ OPEN_DATADIR2_SUFFIX("stats", "entry-stats", ".tmp");
+ OPEN_DATADIR2_SUFFIX("stats", "exit-stats", ".tmp");
+ OPEN_DATADIR2_SUFFIX("stats", "buffer-stats", ".tmp");
+ OPEN_DATADIR2_SUFFIX("stats", "conn-stats", ".tmp");
+ OPEN_DATADIR2_SUFFIX("stats", "hidserv-stats", ".tmp");
+
+ OPEN_DATADIR("approved-routers");
+ OPEN_DATADIR_SUFFIX("fingerprint", ".tmp");
+ OPEN_DATADIR_SUFFIX("hashed-fingerprint", ".tmp");
+ OPEN_DATADIR_SUFFIX("router-stability", ".tmp");
+
+ OPEN("/etc/resolv.conf");
+
+ RENAME_SUFFIX("fingerprint", ".tmp");
+ RENAME_KEYDIR_SUFFIX("secret_onion_key_ntor", ".tmp");
+
+ RENAME_KEYDIR_SUFFIX("secret_id_key", ".tmp");
+ RENAME_KEYDIR_SUFFIX("secret_id_key.old", ".tmp");
+ RENAME_KEYDIR_SUFFIX("secret_onion_key", ".tmp");
+ RENAME_KEYDIR_SUFFIX("secret_onion_key.old", ".tmp");
+
+ RENAME_SUFFIX2("stats", "bridge-stats", ".tmp");
+ RENAME_SUFFIX2("stats", "dirreq-stats", ".tmp");
+ RENAME_SUFFIX2("stats", "entry-stats", ".tmp");
+ RENAME_SUFFIX2("stats", "exit-stats", ".tmp");
+ RENAME_SUFFIX2("stats", "buffer-stats", ".tmp");
+ RENAME_SUFFIX2("stats", "conn-stats", ".tmp");
+ RENAME_SUFFIX2("stats", "hidserv-stats", ".tmp");
+ RENAME_SUFFIX("hashed-fingerprint", ".tmp");
+ RENAME_SUFFIX("router-stability", ".tmp");
+
+ RENAME_KEYDIR_SUFFIX("ed25519_master_id_secret_key", ".tmp");
+ RENAME_KEYDIR_SUFFIX("ed25519_master_id_secret_key_encrypted", ".tmp");
+ RENAME_KEYDIR_SUFFIX("ed25519_master_id_public_key", ".tmp");
+ RENAME_KEYDIR_SUFFIX("ed25519_signing_secret_key", ".tmp");
+ RENAME_KEYDIR_SUFFIX("ed25519_signing_cert", ".tmp");
+
+ sandbox_cfg_allow_rename(&cfg,
+ get_keydir_fname("secret_onion_key"),
+ get_keydir_fname("secret_onion_key.old"));
+ sandbox_cfg_allow_rename(&cfg,
+ get_keydir_fname("secret_onion_key_ntor"),
+ get_keydir_fname("secret_onion_key_ntor.old"));
+
+ STAT_KEY_DIRECTORY();
+ OPEN_DATADIR("stats");
+ STAT_DATADIR("stats");
+ STAT_DATADIR2("stats", "dirreq-stats");
+
+ consdiffmgr_register_with_sandbox(&cfg);
+ }
+
+ init_addrinfo();
+
+ return cfg;
+}
+
+static int
+run_tor_main_loop(void)
+{
+ handle_signals();
+ monotime_init();
+ timers_initialize();
+
+ /* load the private keys, if we're supposed to have them, and set up the
+ * TLS context. */
+ if (! client_identity_key_is_set()) {
+ if (init_keys() < 0) {
+ log_err(LD_OR, "Error initializing keys; exiting");
+ return -1;
+ }
+ }
+
+ /* Set up our buckets */
+ connection_bucket_init();
+
+ /* initialize the bootstrap status events to know we're starting up */
+ control_event_bootstrap(BOOTSTRAP_STATUS_STARTING, 0);
+
+ /* Initialize the keypinning log. */
+ if (authdir_mode_v3(get_options())) {
+ char *fname = get_datadir_fname("key-pinning-journal");
+ int r = 0;
+ if (keypin_load_journal(fname)<0) {
+ log_err(LD_DIR, "Error loading key-pinning journal: %s",strerror(errno));
+ r = -1;
+ }
+ if (keypin_open_journal(fname)<0) {
+ log_err(LD_DIR, "Error opening key-pinning journal: %s",strerror(errno));
+ r = -1;
+ }
+ tor_free(fname);
+ if (r)
+ return r;
+ }
+ {
+ /* This is the old name for key-pinning-journal. These got corrupted
+ * in a couple of cases by #16530, so we started over. See #16580 for
+ * the rationale and for other options we didn't take. We can remove
+ * this code once all the authorities that ran 0.2.7.1-alpha-dev are
+ * upgraded.
+ */
+ char *fname = get_datadir_fname("key-pinning-entries");
+ unlink(fname);
+ tor_free(fname);
+ }
+
+ if (trusted_dirs_reload_certs()) {
+ log_warn(LD_DIR,
+ "Couldn't load all cached v3 certificates. Starting anyway.");
+ }
+ if (router_reload_consensus_networkstatus()) {
+ return -1;
+ }
+ /* load the routers file, or assign the defaults. */
+ if (router_reload_router_list()) {
+ return -1;
+ }
+ /* load the networkstatuses. (This launches a download for new routers as
+ * appropriate.)
+ */
+ const time_t now = time(NULL);
+ directory_info_has_arrived(now, 1, 0);
+
+ if (server_mode(get_options()) || dir_server_mode(get_options())) {
+ /* launch cpuworkers. Need to do this *after* we've read the onion key. */
+ cpu_init();
+ }
+ consdiffmgr_enable_background_compression();
+
+ /* Setup shared random protocol subsystem. */
+ if (authdir_mode_v3(get_options())) {
+ if (sr_init(1) < 0) {
+ return -1;
+ }
+ }
+
+ /* initialize dns resolve map, spawn workers if needed */
+ if (dns_init() < 0) {
+ if (get_options()->ServerDNSAllowBrokenConfig)
+ log_warn(LD_GENERAL, "Couldn't set up any working nameservers. "
+ "Network not up yet? Will try again soon.");
+ else {
+ log_err(LD_GENERAL,"Error initializing dns subsystem; exiting. To "
+ "retry instead, set the ServerDNSAllowBrokenResolvConf option.");
+ }
+ }
+
+#ifdef HAVE_SYSTEMD
+ {
+ const int r = sd_notify(0, "READY=1");
+ if (r < 0) {
+ log_warn(LD_GENERAL, "Unable to send readiness to systemd: %s",
+ strerror(r));
+ } else if (r > 0) {
+ log_notice(LD_GENERAL, "Signaled readiness to systemd");
+ } else {
+ log_info(LD_GENERAL, "Systemd NOTIFY_SOCKET not present.");
+ }
+ }
+#endif /* defined(HAVE_SYSTEMD) */
+
+ return do_main_loop();
+}
+
+/* Main entry point for the Tor process. Called from tor_main(), and by
+ * anybody embedding Tor. */
+int
+tor_run_main(const tor_main_configuration_t *tor_cfg)
+{
+ int result = 0;
+
+#ifdef _WIN32
+#ifndef HeapEnableTerminationOnCorruption
+#define HeapEnableTerminationOnCorruption 1
+#endif
+ /* On heap corruption, just give up; don't try to play along. */
+ HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
+
+ /* SetProcessDEPPolicy is only supported on 32-bit Windows.
+ * (On 64-bit Windows it always fails, and some compilers don't like the
+ * PSETDEP cast.)
+ * 32-bit Windows defines _WIN32.
+ * 64-bit Windows defines _WIN32 and _WIN64. */
+#ifndef _WIN64
+ /* Call SetProcessDEPPolicy to permanently enable DEP.
+ The function will not resolve on earlier versions of Windows,
+ and failure is not dangerous. */
+ HMODULE hMod = GetModuleHandleA("Kernel32.dll");
+ if (hMod) {
+ typedef BOOL (WINAPI *PSETDEP)(DWORD);
+ PSETDEP setdeppolicy = (PSETDEP)GetProcAddress(hMod,
+ "SetProcessDEPPolicy");
+ if (setdeppolicy) {
+ /* PROCESS_DEP_ENABLE | PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION */
+ setdeppolicy(3);
+ }
+ }
+#endif /* !defined(_WIN64) */
+#endif /* defined(_WIN32) */
+
+ {
+ int bt_err = configure_backtrace_handler(get_version());
+ if (bt_err < 0) {
+ log_warn(LD_BUG, "Unable to install backtrace handler: %s",
+ strerror(-bt_err));
+ }
+ }
+
+#ifdef EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED
+ event_set_mem_functions(tor_malloc_, tor_realloc_, tor_free_);
+#endif
+
+ init_protocol_warning_severity_level();
+
+ update_approx_time(time(NULL));
+ tor_threads_init();
+ tor_compress_init();
+ init_logging(0);
+ monotime_init();
+
+ int argc = tor_cfg->argc + tor_cfg->argc_owned;
+ char **argv = tor_calloc(argc, sizeof(char*));
+ memcpy(argv, tor_cfg->argv, tor_cfg->argc*sizeof(char*));
+ if (tor_cfg->argc_owned)
+ memcpy(argv + tor_cfg->argc, tor_cfg->argv_owned,
+ tor_cfg->argc_owned*sizeof(char*));
+
+#ifdef NT_SERVICE
+ {
+ int done = 0;
+ result = nt_service_parse_options(argc, argv, &done);
+ if (done) {
+ goto done;
+ }
+ }
+#endif /* defined(NT_SERVICE) */
+ {
+ int init_rv = tor_init(argc, argv);
+ if (init_rv) {
+ tor_free_all(0);
+ result = (init_rv < 0) ? -1 : 0;
+ goto done;
+ }
+ }
+
+ if (get_options()->Sandbox && get_options()->command == CMD_RUN_TOR) {
+ sandbox_cfg_t* cfg = sandbox_init_filter();
+
+ if (sandbox_init(cfg)) {
+ tor_free(argv);
+ log_err(LD_BUG,"Failed to create syscall sandbox filter");
+ tor_free_all(0);
+ return -1;
+ }
+
+ // registering libevent rng
+#ifdef HAVE_EVUTIL_SECURE_RNG_SET_URANDOM_DEVICE_FILE
+ evutil_secure_rng_set_urandom_device_file(
+ (char*) sandbox_intern_string("/dev/urandom"));
+#endif
+ }
+
+ switch (get_options()->command) {
+ case CMD_RUN_TOR:
+#ifdef NT_SERVICE
+ nt_service_set_state(SERVICE_RUNNING);
+#endif
+ result = run_tor_main_loop();
+ break;
+ case CMD_KEYGEN:
+ result = load_ed_keys(get_options(), time(NULL)) < 0;
+ break;
+ case CMD_KEY_EXPIRATION:
+ init_keys();
+ result = log_cert_expiration();
+ break;
+ case CMD_LIST_FINGERPRINT:
+ result = do_list_fingerprint();
+ break;
+ case CMD_HASH_PASSWORD:
+ do_hash_password();
+ result = 0;
+ break;
+ case CMD_VERIFY_CONFIG:
+ if (quiet_level == 0)
+ printf("Configuration was valid\n");
+ result = 0;
+ break;
+ case CMD_DUMP_CONFIG:
+ result = do_dump_config();
+ break;
+ case CMD_RUN_UNITTESTS: /* only set by test.c */
+ default:
+ log_warn(LD_BUG,"Illegal command number %d: internal error.",
+ get_options()->command);
+ result = -1;
+ }
+ tor_cleanup();
+ done:
+ tor_free(argv);
+ return result;
+}
diff --git a/src/app/main/main.h b/src/app/main/main.h
new file mode 100644
index 0000000000..b64f2ef417
--- /dev/null
+++ b/src/app/main/main.h
@@ -0,0 +1,29 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file main.h
+ * \brief Header file for main.c.
+ **/
+
+#ifndef TOR_MAIN_H
+#define TOR_MAIN_H
+
+void handle_signals(void);
+void activate_signal(int signal_num);
+
+int try_locking(const or_options_t *options, int err_if_locked);
+int have_lockfile(void);
+void release_lockfile(void);
+
+void tor_remove_file(const char *filename);
+
+void tor_cleanup(void);
+void tor_free_all(int postfork);
+
+int tor_init(int argc, char **argv);
+
+#endif /* !defined(TOR_MAIN_H) */
diff --git a/src/app/main/ntmain.c b/src/app/main/ntmain.c
index 232529a803..eab6d23b5d 100644
--- a/src/app/main/ntmain.c
+++ b/src/app/main/ntmain.c
@@ -21,7 +21,7 @@
#include "core/or/or.h"
#include "app/config/config.h"
-#include "core/mainloop/main.h"
+#include "app/main/main.h"
#include "app/main/ntmain.h"
#include "lib/log/win32err.h"
#include "lib/fs/winlib.h"
diff --git a/src/core/crypto/onion_crypto.c b/src/core/crypto/onion_crypto.c
new file mode 100644
index 0000000000..4978e0d46c
--- /dev/null
+++ b/src/core/crypto/onion_crypto.c
@@ -0,0 +1,311 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file onion_crypto.c
+ * \brief Functions to handle different kinds of circuit extension crypto.
+ *
+ * In this module, we provide a set of abstractions to create a uniform
+ * interface over the three circuit extension handshakes that Tor has used
+ * over the years (TAP, CREATE_FAST, and ntor). These handshakes are
+ * implemented in onion_tap.c, onion_fast.c, and onion_ntor.c respectively.
+ *
+ * All[*] of these handshakes follow a similar pattern: a client, knowing
+ * some key from the relay it wants to extend through, generates the
+ * first part of a handshake. A relay receives that handshake, and sends
+ * a reply. Once the client handles the reply, it knows that it is
+ * talking to the right relay, and it shares some freshly negotiated key
+ * material with that relay.
+ *
+ * We sometimes call the client's part of the handshake an "onionskin".
+ * We do this because historically, Onion Routing used a multi-layer
+ * structure called an "onion" to construct circuits. Each layer of the
+ * onion contained key material chosen by the client, the identity of
+ * the next relay in the circuit, and a smaller onion, encrypted with
+ * the key of the next relay. When we changed Tor to use a telescoping
+ * circuit extension design, it corresponded to sending each layer of the
+ * onion separately -- as a series of onionskins.
+ **/
+
+#include "core/or/or.h"
+#include "core/or/circuitbuild.h"
+#include "core/crypto/onion_crypto.h"
+#include "core/crypto/onion_fast.h"
+#include "core/crypto/onion_ntor.h"
+#include "core/crypto/onion_tap.h"
+#include "feature/relay/router.h"
+#include "lib/crypt_ops/crypto_dh.h"
+#include "lib/crypt_ops/crypto_util.h"
+
+#include "core/or/crypt_path_st.h"
+#include "core/or/extend_info_st.h"
+
+/** Return a new server_onion_keys_t object with all of the keys
+ * and other info we might need to do onion handshakes. (We make a copy of
+ * our keys for each cpuworker to avoid race conditions with the main thread,
+ * and to avoid locking) */
+server_onion_keys_t *
+server_onion_keys_new(void)
+{
+ server_onion_keys_t *keys = tor_malloc_zero(sizeof(server_onion_keys_t));
+ memcpy(keys->my_identity, router_get_my_id_digest(), DIGEST_LEN);
+ dup_onion_keys(&keys->onion_key, &keys->last_onion_key);
+ keys->curve25519_key_map = construct_ntor_key_map();
+ keys->junk_keypair = tor_malloc_zero(sizeof(curve25519_keypair_t));
+ curve25519_keypair_generate(keys->junk_keypair, 0);
+ return keys;
+}
+/** Release all storage held in <b>keys</b>. */
+void
+server_onion_keys_free_(server_onion_keys_t *keys)
+{
+ if (! keys)
+ return;
+
+ crypto_pk_free(keys->onion_key);
+ crypto_pk_free(keys->last_onion_key);
+ ntor_key_map_free(keys->curve25519_key_map);
+ tor_free(keys->junk_keypair);
+ memwipe(keys, 0, sizeof(server_onion_keys_t));
+ tor_free(keys);
+}
+
+/** Release whatever storage is held in <b>state</b>, depending on its
+ * type, and clear its pointer. */
+void
+onion_handshake_state_release(onion_handshake_state_t *state)
+{
+ switch (state->tag) {
+ case ONION_HANDSHAKE_TYPE_TAP:
+ crypto_dh_free(state->u.tap);
+ state->u.tap = NULL;
+ break;
+ case ONION_HANDSHAKE_TYPE_FAST:
+ fast_handshake_state_free(state->u.fast);
+ state->u.fast = NULL;
+ break;
+ case ONION_HANDSHAKE_TYPE_NTOR:
+ ntor_handshake_state_free(state->u.ntor);
+ state->u.ntor = NULL;
+ break;
+ default:
+ /* LCOV_EXCL_START
+ * This state should not even exist. */
+ log_warn(LD_BUG, "called with unknown handshake state type %d",
+ (int)state->tag);
+ tor_fragile_assert();
+ /* LCOV_EXCL_STOP */
+ }
+}
+
+/** Perform the first step of a circuit-creation handshake of type <b>type</b>
+ * (one of ONION_HANDSHAKE_TYPE_*): generate the initial "onion skin" in
+ * <b>onion_skin_out</b>, and store any state information in <b>state_out</b>.
+ * Return -1 on failure, and the length of the onionskin on acceptance.
+ */
+int
+onion_skin_create(int type,
+ const extend_info_t *node,
+ onion_handshake_state_t *state_out,
+ uint8_t *onion_skin_out)
+{
+ int r = -1;
+
+ switch (type) {
+ case ONION_HANDSHAKE_TYPE_TAP:
+ if (!node->onion_key)
+ return -1;
+
+ if (onion_skin_TAP_create(node->onion_key,
+ &state_out->u.tap,
+ (char*)onion_skin_out) < 0)
+ return -1;
+
+ r = TAP_ONIONSKIN_CHALLENGE_LEN;
+ break;
+ case ONION_HANDSHAKE_TYPE_FAST:
+ if (fast_onionskin_create(&state_out->u.fast, onion_skin_out) < 0)
+ return -1;
+
+ r = CREATE_FAST_LEN;
+ break;
+ case ONION_HANDSHAKE_TYPE_NTOR:
+ if (!extend_info_supports_ntor(node))
+ return -1;
+ if (onion_skin_ntor_create((const uint8_t*)node->identity_digest,
+ &node->curve25519_onion_key,
+ &state_out->u.ntor,
+ onion_skin_out) < 0)
+ return -1;
+
+ r = NTOR_ONIONSKIN_LEN;
+ break;
+ default:
+ /* LCOV_EXCL_START
+ * We should never try to create an impossible handshake type. */
+ log_warn(LD_BUG, "called with unknown handshake state type %d", type);
+ tor_fragile_assert();
+ r = -1;
+ /* LCOV_EXCL_STOP */
+ }
+
+ if (r > 0)
+ state_out->tag = (uint16_t) type;
+
+ return r;
+}
+
+/* This is the maximum value for keys_out_len passed to
+ * onion_skin_server_handshake, plus 16. We can make it bigger if needed:
+ * It just defines how many bytes to stack-allocate. */
+#define MAX_KEYS_TMP_LEN 128
+
+/** Perform the second (server-side) step of a circuit-creation handshake of
+ * type <b>type</b>, responding to the client request in <b>onion_skin</b>
+ * using the keys in <b>keys</b>. On success, write our response into
+ * <b>reply_out</b>, generate <b>keys_out_len</b> bytes worth of key material
+ * in <b>keys_out_len</b>, a hidden service nonce to <b>rend_nonce_out</b>,
+ * and return the length of the reply. On failure, return -1.
+ */
+int
+onion_skin_server_handshake(int type,
+ const uint8_t *onion_skin, size_t onionskin_len,
+ const server_onion_keys_t *keys,
+ uint8_t *reply_out,
+ uint8_t *keys_out, size_t keys_out_len,
+ uint8_t *rend_nonce_out)
+{
+ int r = -1;
+
+ switch (type) {
+ case ONION_HANDSHAKE_TYPE_TAP:
+ if (onionskin_len != TAP_ONIONSKIN_CHALLENGE_LEN)
+ return -1;
+ if (onion_skin_TAP_server_handshake((const char*)onion_skin,
+ keys->onion_key, keys->last_onion_key,
+ (char*)reply_out,
+ (char*)keys_out, keys_out_len)<0)
+ return -1;
+ r = TAP_ONIONSKIN_REPLY_LEN;
+ memcpy(rend_nonce_out, reply_out+DH1024_KEY_LEN, DIGEST_LEN);
+ break;
+ case ONION_HANDSHAKE_TYPE_FAST:
+ if (onionskin_len != CREATE_FAST_LEN)
+ return -1;
+ if (fast_server_handshake(onion_skin, reply_out, keys_out, keys_out_len)<0)
+ return -1;
+ r = CREATED_FAST_LEN;
+ memcpy(rend_nonce_out, reply_out+DIGEST_LEN, DIGEST_LEN);
+ break;
+ case ONION_HANDSHAKE_TYPE_NTOR:
+ if (onionskin_len < NTOR_ONIONSKIN_LEN)
+ return -1;
+ {
+ size_t keys_tmp_len = keys_out_len + DIGEST_LEN;
+ tor_assert(keys_tmp_len <= MAX_KEYS_TMP_LEN);
+ uint8_t keys_tmp[MAX_KEYS_TMP_LEN];
+
+ if (onion_skin_ntor_server_handshake(
+ onion_skin, keys->curve25519_key_map,
+ keys->junk_keypair,
+ keys->my_identity,
+ reply_out, keys_tmp, keys_tmp_len)<0) {
+ /* no need to memwipe here, since the output will never be used */
+ return -1;
+ }
+
+ memcpy(keys_out, keys_tmp, keys_out_len);
+ memcpy(rend_nonce_out, keys_tmp+keys_out_len, DIGEST_LEN);
+ memwipe(keys_tmp, 0, sizeof(keys_tmp));
+ r = NTOR_REPLY_LEN;
+ }
+ break;
+ default:
+ /* LCOV_EXCL_START
+ * We should have rejected this far before this point */
+ log_warn(LD_BUG, "called with unknown handshake state type %d", type);
+ tor_fragile_assert();
+ return -1;
+ /* LCOV_EXCL_STOP */
+ }
+
+ return r;
+}
+
+/** Perform the final (client-side) step of a circuit-creation handshake of
+ * type <b>type</b>, using our state in <b>handshake_state</b> and the
+ * server's response in <b>reply</b>. On success, generate <b>keys_out_len</b>
+ * bytes worth of key material in <b>keys_out_len</b>, set
+ * <b>rend_authenticator_out</b> to the "KH" field that can be used to
+ * establish introduction points at this hop, and return 0. On failure,
+ * return -1, and set *msg_out to an error message if this is worth
+ * complaining to the user about. */
+int
+onion_skin_client_handshake(int type,
+ const onion_handshake_state_t *handshake_state,
+ const uint8_t *reply, size_t reply_len,
+ uint8_t *keys_out, size_t keys_out_len,
+ uint8_t *rend_authenticator_out,
+ const char **msg_out)
+{
+ if (handshake_state->tag != type)
+ return -1;
+
+ switch (type) {
+ case ONION_HANDSHAKE_TYPE_TAP:
+ if (reply_len != TAP_ONIONSKIN_REPLY_LEN) {
+ if (msg_out)
+ *msg_out = "TAP reply was not of the correct length.";
+ return -1;
+ }
+ if (onion_skin_TAP_client_handshake(handshake_state->u.tap,
+ (const char*)reply,
+ (char *)keys_out, keys_out_len,
+ msg_out) < 0)
+ return -1;
+
+ memcpy(rend_authenticator_out, reply+DH1024_KEY_LEN, DIGEST_LEN);
+
+ return 0;
+ case ONION_HANDSHAKE_TYPE_FAST:
+ if (reply_len != CREATED_FAST_LEN) {
+ if (msg_out)
+ *msg_out = "TAP reply was not of the correct length.";
+ return -1;
+ }
+ if (fast_client_handshake(handshake_state->u.fast, reply,
+ keys_out, keys_out_len, msg_out) < 0)
+ return -1;
+
+ memcpy(rend_authenticator_out, reply+DIGEST_LEN, DIGEST_LEN);
+ return 0;
+ case ONION_HANDSHAKE_TYPE_NTOR:
+ if (reply_len < NTOR_REPLY_LEN) {
+ if (msg_out)
+ *msg_out = "ntor reply was not of the correct length.";
+ return -1;
+ }
+ {
+ size_t keys_tmp_len = keys_out_len + DIGEST_LEN;
+ uint8_t *keys_tmp = tor_malloc(keys_tmp_len);
+ if (onion_skin_ntor_client_handshake(handshake_state->u.ntor,
+ reply,
+ keys_tmp, keys_tmp_len, msg_out) < 0) {
+ tor_free(keys_tmp);
+ return -1;
+ }
+ memcpy(keys_out, keys_tmp, keys_out_len);
+ memcpy(rend_authenticator_out, keys_tmp + keys_out_len, DIGEST_LEN);
+ memwipe(keys_tmp, 0, keys_tmp_len);
+ tor_free(keys_tmp);
+ }
+ return 0;
+ default:
+ log_warn(LD_BUG, "called with unknown handshake state type %d", type);
+ tor_fragile_assert();
+ return -1;
+ }
+}
diff --git a/src/core/crypto/onion_crypto.h b/src/core/crypto/onion_crypto.h
new file mode 100644
index 0000000000..ed410ef252
--- /dev/null
+++ b/src/core/crypto/onion_crypto.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file onion_crypto.h
+ * \brief Header file for onion_crypto.c.
+ **/
+
+#ifndef TOR_ONION_CRYPTO_H
+#define TOR_ONION_CRYPTO_H
+
+typedef struct server_onion_keys_t {
+ uint8_t my_identity[DIGEST_LEN];
+ crypto_pk_t *onion_key;
+ crypto_pk_t *last_onion_key;
+ struct di_digest256_map_t *curve25519_key_map;
+ struct curve25519_keypair_t *junk_keypair;
+} server_onion_keys_t;
+
+void onion_handshake_state_release(onion_handshake_state_t *state);
+
+int onion_skin_create(int type,
+ const extend_info_t *node,
+ onion_handshake_state_t *state_out,
+ uint8_t *onion_skin_out);
+int onion_skin_server_handshake(int type,
+ const uint8_t *onion_skin, size_t onionskin_len,
+ const server_onion_keys_t *keys,
+ uint8_t *reply_out,
+ uint8_t *keys_out, size_t key_out_len,
+ uint8_t *rend_nonce_out);
+int onion_skin_client_handshake(int type,
+ const onion_handshake_state_t *handshake_state,
+ const uint8_t *reply, size_t reply_len,
+ uint8_t *keys_out, size_t key_out_len,
+ uint8_t *rend_authenticator_out,
+ const char **msg_out);
+
+server_onion_keys_t *server_onion_keys_new(void);
+void server_onion_keys_free_(server_onion_keys_t *keys);
+#define server_onion_keys_free(keys) \
+ FREE_AND_NULL(server_onion_keys_t, server_onion_keys_free_, (keys))
+
+#endif
diff --git a/src/core/include.am b/src/core/include.am
index 4ccabe4385..d042f697b6 100644
--- a/src/core/include.am
+++ b/src/core/include.am
@@ -10,15 +10,16 @@ LIBTOR_APP_A_SOURCES = \
src/app/config/config.c \
src/app/config/confparse.c \
src/app/config/statefile.c \
+ src/app/main/main.c \
src/core/crypto/hs_ntor.c \
- src/core/crypto/onion.c \
+ src/core/crypto/onion_crypto.c \
src/core/crypto/onion_fast.c \
src/core/crypto/onion_ntor.c \
src/core/crypto/onion_tap.c \
src/core/crypto/relay_crypto.c \
src/core/mainloop/connection.c \
src/core/mainloop/cpuworker.c \
- src/core/mainloop/main.c \
+ src/core/mainloop/mainloop.c \
src/core/mainloop/periodic.c \
src/core/or/address_set.c \
src/core/or/channel.c \
@@ -34,6 +35,7 @@ LIBTOR_APP_A_SOURCES = \
src/core/or/connection_edge.c \
src/core/or/connection_or.c \
src/core/or/dos.c \
+ src/core/or/onion.c \
src/core/or/policies.c \
src/core/or/protover.c \
src/core/or/reasons.c \
@@ -56,6 +58,7 @@ LIBTOR_APP_A_SOURCES = \
src/feature/client/entrynodes.c \
src/feature/client/transports.c \
src/feature/control/control.c \
+ src/feature/control/fmt_serverstatus.c \
src/feature/dirauth/keypin.c \
src/feature/dircache/conscache.c \
src/feature/dircache/consdiffmgr.c \
@@ -80,16 +83,21 @@ LIBTOR_APP_A_SOURCES = \
src/feature/hs/hs_stats.c \
src/feature/hs_common/replaycache.c \
src/feature/hs_common/shared_random_client.c \
+ src/feature/nodelist/authcert.c \
+ src/feature/nodelist/dirlist.c \
src/feature/nodelist/microdesc.c \
src/feature/nodelist/networkstatus.c \
src/feature/nodelist/nodelist.c \
+ src/feature/nodelist/node_select.c \
src/feature/nodelist/parsecommon.c \
src/feature/nodelist/routerlist.c \
src/feature/nodelist/routerparse.c \
src/feature/nodelist/routerset.c \
+ src/feature/nodelist/fmt_routerstatus.c \
src/feature/nodelist/torcert.c \
src/feature/relay/dns.c \
src/feature/relay/ext_orport.c \
+ src/feature/relay/onion_queue.c \
src/feature/relay/router.c \
src/feature/relay/routerkeys.c \
src/feature/rend/rendcache.c \
@@ -100,6 +108,16 @@ LIBTOR_APP_A_SOURCES = \
src/feature/stats/geoip.c \
src/feature/stats/rephist.c
+# These should eventually move into module_dirauth_sources, but for now
+# the separation is only in the code location.
+LIBTOR_APP_A_SOURCES += \
+ src/feature/dirauth/bwauth.c \
+ src/feature/dirauth/guardfraction.c \
+ src/feature/dirauth/reachability.c \
+ src/feature/dirauth/recommend_pkg.c \
+ src/feature/dirauth/process_descs.c \
+ src/feature/dirauth/voteflags.c
+
if BUILD_NT_SERVICES
LIBTOR_APP_A_SOURCES += src/app/main/ntmain.c
endif
@@ -147,16 +165,17 @@ noinst_HEADERS += \
src/app/config/or_options_st.h \
src/app/config/or_state_st.h \
src/app/config/statefile.h \
+ src/app/main/main.h \
src/app/main/ntmain.h \
src/core/crypto/hs_ntor.h \
- src/core/crypto/onion.h \
+ src/core/crypto/onion_crypto.h \
src/core/crypto/onion_fast.h \
src/core/crypto/onion_ntor.h \
src/core/crypto/onion_tap.h \
src/core/crypto/relay_crypto.h \
src/core/mainloop/connection.h \
src/core/mainloop/cpuworker.h \
- src/core/mainloop/main.h \
+ src/core/mainloop/mainloop.h \
src/core/mainloop/periodic.h \
src/core/or/addr_policy_st.h \
src/core/or/address_set.h \
@@ -187,6 +206,7 @@ noinst_HEADERS += \
src/core/or/entry_port_cfg_st.h \
src/core/or/extend_info_st.h \
src/core/or/listener_connection_st.h \
+ src/core/or/onion.h \
src/core/or/or.h \
src/core/or/or_circuit_st.h \
src/core/or/or_connection_st.h \
@@ -219,14 +239,21 @@ noinst_HEADERS += \
src/feature/client/transports.h \
src/feature/control/control.h \
src/feature/control/control_connection_st.h \
+ src/feature/control/fmt_serverstatus.h \
+ src/feature/dirauth/bwauth.h \
src/feature/dirauth/dircollate.h \
src/feature/dirauth/dirvote.h \
+ src/feature/dirauth/guardfraction.h \
src/feature/dirauth/keypin.h \
src/feature/dirauth/mode.h \
src/feature/dirauth/ns_detached_signatures_st.h \
+ src/feature/dirauth/reachability.h \
+ src/feature/dirauth/recommend_pkg.h \
+ src/feature/dirauth/process_descs.h \
src/feature/dirauth/shared_random.h \
src/feature/dirauth/shared_random_state.h \
src/feature/dirauth/vote_microdesc_hash_st.h \
+ src/feature/dirauth/voteflags.h \
src/feature/dircache/cached_dir_st.h \
src/feature/dircache/conscache.h \
src/feature/dircache/consdiffmgr.h \
@@ -256,8 +283,10 @@ noinst_HEADERS += \
src/feature/hs/hsdir_index_st.h \
src/feature/hs_common/replaycache.h \
src/feature/hs_common/shared_random_client.h \
+ src/feature/nodelist/authcert.h \
src/feature/nodelist/authority_cert_st.h \
src/feature/nodelist/desc_store_st.h \
+ src/feature/nodelist/dirlist.h \
src/feature/nodelist/document_signature_st.h \
src/feature/nodelist/extrainfo_st.h \
src/feature/nodelist/microdesc.h \
@@ -268,12 +297,14 @@ noinst_HEADERS += \
src/feature/nodelist/networkstatus_voter_info_st.h \
src/feature/nodelist/node_st.h \
src/feature/nodelist/nodelist.h \
+ src/feature/nodelist/node_select.h \
src/feature/nodelist/parsecommon.h \
src/feature/nodelist/routerinfo_st.h \
src/feature/nodelist/routerlist.h \
src/feature/nodelist/routerlist_st.h \
src/feature/nodelist/routerparse.h \
src/feature/nodelist/routerset.h \
+ src/feature/nodelist/fmt_routerstatus.h \
src/feature/nodelist/routerstatus_st.h \
src/feature/nodelist/signed_descriptor_st.h \
src/feature/nodelist/torcert.h \
@@ -281,6 +312,7 @@ noinst_HEADERS += \
src/feature/relay/dns.h \
src/feature/relay/dns_structs.h \
src/feature/relay/ext_orport.h \
+ src/feature/relay/onion_queue.h \
src/feature/relay/router.h \
src/feature/relay/routerkeys.h \
src/feature/rend/rend_authorized_client_st.h \
diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c
index 0c3abc8442..f85a2b61d5 100644
--- a/src/core/mainloop/connection.c
+++ b/src/core/mainloop/connection.c
@@ -85,7 +85,7 @@
#include "feature/client/entrynodes.h"
#include "feature/relay/ext_orport.h"
#include "feature/stats/geoip.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/hibernate/hibernate.h"
#include "feature/hs/hs_common.h"
#include "feature/hs/hs_ident.h"
@@ -646,6 +646,7 @@ connection_free_minimal(connection_t *conn)
} else {
/* The tor_tls_free() call below will close the socket; we must tell
* the code below not to close it a second time. */
+ tor_release_socket_ownership(conn->s);
conn->s = TOR_INVALID_SOCKET;
}
tor_tls_free(or_conn->tls);
diff --git a/src/core/mainloop/cpuworker.c b/src/core/mainloop/cpuworker.c
index a372db3f17..8d0d23ab91 100644
--- a/src/core/mainloop/cpuworker.c
+++ b/src/core/mainloop/cpuworker.c
@@ -26,11 +26,12 @@
#include "core/mainloop/cpuworker.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_util.h"
-#include "core/mainloop/main.h"
-#include "core/crypto/onion.h"
+#include "core/or/onion.h"
+#include "feature/relay/onion_queue.h"
#include "feature/stats/rephist.h"
#include "feature/relay/router.h"
#include "lib/evloop/workqueue.h"
+#include "core/crypto/onion_crypto.h"
#include "core/or/or_circuit_st.h"
#include "lib/intmath/weakrng.h"
diff --git a/src/core/mainloop/main.c b/src/core/mainloop/mainloop.c
index 0a31a3145b..4ab00f92d7 100644
--- a/src/core/mainloop/main.c
+++ b/src/core/mainloop/mainloop.c
@@ -5,9 +5,9 @@
/* See LICENSE for licensing information */
/**
- * \file main.c
+ * \file mainloop.c
* \brief Toplevel module. Handles signals, multiplexes between
- * connections, implements main loop, and drives scheduled events.
+ * connections, implements main loop, and drives scheduled events.
*
* For the main loop itself; see run_main_loop_once(). It invokes the rest of
* Tor mostly through Libevent callbacks. Libevent callbacks can happen when
@@ -46,8 +46,9 @@
*
**/
-#define MAIN_PRIVATE
+#define MAINLOOP_PRIVATE
#include "core/or/or.h"
+
#include "feature/client/addressmap.h"
#include "lib/err/backtrace.h"
#include "feature/client/bridges.h"
@@ -59,21 +60,17 @@
#include "core/or/circuitbuild.h"
#include "core/or/circuitlist.h"
#include "core/or/circuituse.h"
-#include "core/or/circuitmux_ewma.h"
-#include "core/or/command.h"
-#include "lib/compress/compress.h"
#include "app/config/config.h"
-#include "app/config/confparse.h"
#include "core/mainloop/connection.h"
#include "core/or/connection_edge.h"
#include "core/or/connection_or.h"
#include "feature/dircache/consdiffmgr.h"
#include "feature/control/control.h"
#include "core/mainloop/cpuworker.h"
-#include "lib/crypt_ops/crypto_s2k.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "feature/dircache/directory.h"
#include "feature/dircache/dirserv.h"
+#include "feature/dirauth/reachability.h"
#include "feature/relay/dns.h"
#include "feature/client/dnsserv.h"
#include "core/or/dos.h"
@@ -81,60 +78,36 @@
#include "feature/stats/geoip.h"
#include "feature/hibernate/hibernate.h"
#include "feature/hs/hs_cache.h"
-#include "feature/hs/hs_circuitmap.h"
#include "feature/hs/hs_client.h"
-#include "feature/dirauth/keypin.h"
-#include "core/mainloop/main.h"
+#include "feature/hs/hs_service.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
#include "app/main/ntmain.h"
-#include "core/crypto/onion.h"
#include "core/mainloop/periodic.h"
-#include "core/or/policies.h"
-#include "core/or/protover.h"
#include "feature/client/transports.h"
-#include "core/or/relay.h"
-#include "feature/rend/rendclient.h"
-#include "feature/rend/rendcommon.h"
+#include "feature/rend/rendcache.h"
#include "feature/rend/rendservice.h"
#include "feature/stats/rephist.h"
#include "feature/relay/router.h"
#include "feature/relay/routerkeys.h"
#include "feature/nodelist/routerlist.h"
-#include "feature/nodelist/routerparse.h"
-#include "core/or/scheduler.h"
#include "app/config/statefile.h"
#include "core/or/status.h"
-#include "feature/api/tor_api.h"
-#include "feature/api/tor_api_internal.h"
-#include "lib/process/waitpid.h"
-#include "feature/relay/ext_orport.h"
-#include "lib/memarea/memarea.h"
-#include "lib/meminfo/meminfo.h"
-#include "lib/osinfo/uname.h"
-#include "lib/sandbox/sandbox.h"
-#include "lib/fs/lockfile.h"
#include "lib/net/buffers_net.h"
-#include "lib/net/resolve.h"
-#include "lib/tls/tortls.h"
#include "lib/evloop/compat_libevent.h"
-#include "lib/encoding/confline.h"
-#include "lib/evloop/timers.h"
-#include "lib/crypt_ops/crypto_init.h"
#include <event2/event.h>
#include "feature/dirauth/dirvote.h"
#include "feature/dirauth/mode.h"
-#include "feature/dirauth/shared_random.h"
#include "core/or/cell_st.h"
#include "core/or/entry_connection_st.h"
#include "feature/nodelist/networkstatus_st.h"
#include "core/or/or_connection_st.h"
#include "app/config/or_state_st.h"
-#include "core/or/port_cfg_st.h"
#include "feature/nodelist/routerinfo_st.h"
#include "core/or/socks_request_st.h"
@@ -152,31 +125,6 @@
#include <systemd/sd-daemon.h>
#endif /* defined(HAVE_SYSTEMD) */
-void evdns_shutdown(int);
-
-#ifdef HAVE_RUST
-// helper function defined in Rust to output a log message indicating if tor is
-// running with Rust enabled. See src/rust/tor_util
-void rust_log_welcome_string(void);
-#endif
-
-/********* PROTOTYPES **********/
-
-static void dumpmemusage(int severity);
-static void dumpstats(int severity); /* log stats */
-static void conn_read_callback(evutil_socket_t fd, short event, void *_conn);
-static void conn_write_callback(evutil_socket_t fd, short event, void *_conn);
-static void second_elapsed_callback(periodic_timer_t *timer, void *args);
-static int conn_close_if_marked(int i);
-static void connection_start_reading_from_linked_conn(connection_t *conn);
-static int connection_should_read_from_linked_conn(connection_t *conn);
-static int run_main_loop_until_done(void);
-static void process_signal(int sig);
-static void shutdown_did_not_work_callback(evutil_socket_t fd, short event,
- void *arg) ATTR_NORETURN;
-
-/********* START VARIABLES **********/
-
/* Token bucket for all traffic. */
token_bucket_rw_t global_bucket;
@@ -246,13 +194,15 @@ static int can_complete_circuits = 0;
* when we have enough directory info? */
#define LAZY_DESCRIPTOR_RETRY_INTERVAL (60)
-/** Decides our behavior when no logs are configured/before any
- * logs have been configured. For 0, we log notice to stdout as normal.
- * For 1, we log warnings only. For 2, we log nothing.
- */
-int quiet_level = 0;
-
-/********* END VARIABLES ************/
+static int conn_close_if_marked(int i);
+static int run_main_loop_until_done(void);
+static void connection_start_reading_from_linked_conn(connection_t *conn);
+static int connection_should_read_from_linked_conn(connection_t *conn);
+static void conn_read_callback(evutil_socket_t fd, short event, void *_conn);
+static void conn_write_callback(evutil_socket_t fd, short event, void *_conn);
+static void second_elapsed_callback(periodic_timer_t *timer, void *args);
+static void shutdown_did_not_work_callback(evutil_socket_t fd, short event,
+ void *arg) ATTR_NORETURN;
/****************************************************************************
*
@@ -447,8 +397,8 @@ static mainloop_event_t *schedule_active_linked_connections_event = NULL;
/** Initialize the global connection list, closeable connection list,
* and active connection list. */
-STATIC void
-init_connection_lists(void)
+void
+tor_init_connection_lists(void)
{
if (!connection_array)
connection_array = smartlist_new();
@@ -1343,7 +1293,31 @@ handle_deferred_signewnym_cb(mainloop_event_t *event, void *arg)
(void)event;
(void)arg;
log_info(LD_CONTROL, "Honoring delayed NEWNYM request");
- signewnym_impl(time(NULL));
+ do_signewnym(time(NULL));
+}
+
+/** Either perform a signewnym or schedule one, depending on rate limiting. */
+void
+do_signewnym(time_t now)
+{
+ if (time_of_last_signewnym + MAX_SIGNEWNYM_RATE > now) {
+ const time_t delay_sec =
+ time_of_last_signewnym + MAX_SIGNEWNYM_RATE - now;
+ if (! signewnym_is_pending) {
+ signewnym_is_pending = 1;
+ if (!handle_deferred_signewnym_ev) {
+ handle_deferred_signewnym_ev =
+ mainloop_event_postloop_new(handle_deferred_signewnym_cb, NULL);
+ }
+ const struct timeval delay_tv = { delay_sec, 0 };
+ mainloop_event_schedule(handle_deferred_signewnym_ev, &delay_tv);
+ }
+ log_notice(LD_CONTROL,
+ "Rate limiting NEWNYM request: delaying by %d second(s)",
+ (int)(delay_sec));
+ } else {
+ signewnym_impl(now);
+ }
}
/** Return the number of times that signewnym has been called. */
@@ -2700,94 +2674,6 @@ dns_servers_relaunch_checks(void)
}
}
-/** Called when we get a SIGHUP: reload configuration files and keys,
- * retry all connections, and so on. */
-static int
-do_hup(void)
-{
- const or_options_t *options = get_options();
-
- log_notice(LD_GENERAL,"Received reload signal (hup). Reloading config and "
- "resetting internal state.");
- if (accounting_is_enabled(options))
- accounting_record_bandwidth_usage(time(NULL), get_or_state());
-
- router_reset_warnings();
- routerlist_reset_warnings();
- /* first, reload config variables, in case they've changed */
- if (options->ReloadTorrcOnSIGHUP) {
- /* no need to provide argc/v, they've been cached in init_from_config */
- int init_rv = options_init_from_torrc(0, NULL);
- if (init_rv < 0) {
- log_err(LD_CONFIG,"Reading config failed--see warnings above. "
- "For usage, try -h.");
- return -1;
- } else if (BUG(init_rv > 0)) {
- // LCOV_EXCL_START
- /* This should be impossible: the only "return 1" cases in
- * options_init_from_torrc are ones caused by command-line arguments;
- * but they can't change while Tor is running. */
- return -1;
- // LCOV_EXCL_STOP
- }
- options = get_options(); /* they have changed now */
- /* Logs are only truncated the first time they are opened, but were
- probably intended to be cleaned up on signal. */
- if (options->TruncateLogFile)
- truncate_logs();
- } else {
- char *msg = NULL;
- log_notice(LD_GENERAL, "Not reloading config file: the controller told "
- "us not to.");
- /* Make stuff get rescanned, reloaded, etc. */
- if (set_options((or_options_t*)options, &msg) < 0) {
- if (!msg)
- msg = tor_strdup("Unknown error");
- log_warn(LD_GENERAL, "Unable to re-set previous options: %s", msg);
- tor_free(msg);
- }
- }
- if (authdir_mode(options)) {
- /* reload the approved-routers file */
- if (dirserv_load_fingerprint_file() < 0) {
- /* warnings are logged from dirserv_load_fingerprint_file() directly */
- log_info(LD_GENERAL, "Error reloading fingerprints. "
- "Continuing with old list.");
- }
- }
-
- /* Rotate away from the old dirty circuits. This has to be done
- * after we've read the new options, but before we start using
- * circuits for directory fetches. */
- circuit_mark_all_dirty_circs_as_unusable();
-
- /* retry appropriate downloads */
- router_reset_status_download_failures();
- router_reset_descriptor_download_failures();
- if (!net_is_disabled())
- update_networkstatus_downloads(time(NULL));
-
- /* We'll retry routerstatus downloads in about 10 seconds; no need to
- * force a retry there. */
-
- if (server_mode(options)) {
- /* Maybe we've been given a new ed25519 key or certificate?
- */
- time_t now = approx_time();
- int new_signing_key = load_ed_keys(options, now);
- if (new_signing_key < 0 ||
- generate_ed_link_cert(options, now, new_signing_key > 0)) {
- log_warn(LD_OR, "Problem reloading Ed25519 keys; still using old keys.");
- }
-
- /* Update cpuworker and dnsworker processes, so they get up-to-date
- * configuration options. */
- cpuworkers_rotate_keyinfo();
- dns_reset();
- }
- return 0;
-}
-
/** Initialize some mainloop_event_t objects that we require. */
STATIC void
initialize_mainloop_events(void)
@@ -2806,8 +2692,6 @@ initialize_mainloop_events(void)
int
do_main_loop(void)
{
- time_t now;
-
/* initialize the periodic events first, so that code that depends on the
* events being present does not assert.
*/
@@ -2817,94 +2701,6 @@ do_main_loop(void)
initialize_mainloop_events();
- /* initialize dns resolve map, spawn workers if needed */
- if (dns_init() < 0) {
- if (get_options()->ServerDNSAllowBrokenConfig)
- log_warn(LD_GENERAL, "Couldn't set up any working nameservers. "
- "Network not up yet? Will try again soon.");
- else {
- log_err(LD_GENERAL,"Error initializing dns subsystem; exiting. To "
- "retry instead, set the ServerDNSAllowBrokenResolvConf option.");
- }
- }
-
- handle_signals();
- monotime_init();
- timers_initialize();
-
- /* load the private keys, if we're supposed to have them, and set up the
- * TLS context. */
- if (! client_identity_key_is_set()) {
- if (init_keys() < 0) {
- log_err(LD_OR, "Error initializing keys; exiting");
- return -1;
- }
- }
-
- /* Set up our buckets */
- connection_bucket_init();
-
- /* initialize the bootstrap status events to know we're starting up */
- control_event_bootstrap(BOOTSTRAP_STATUS_STARTING, 0);
-
- /* Initialize the keypinning log. */
- if (authdir_mode_v3(get_options())) {
- char *fname = get_datadir_fname("key-pinning-journal");
- int r = 0;
- if (keypin_load_journal(fname)<0) {
- log_err(LD_DIR, "Error loading key-pinning journal: %s",strerror(errno));
- r = -1;
- }
- if (keypin_open_journal(fname)<0) {
- log_err(LD_DIR, "Error opening key-pinning journal: %s",strerror(errno));
- r = -1;
- }
- tor_free(fname);
- if (r)
- return r;
- }
- {
- /* This is the old name for key-pinning-journal. These got corrupted
- * in a couple of cases by #16530, so we started over. See #16580 for
- * the rationale and for other options we didn't take. We can remove
- * this code once all the authorities that ran 0.2.7.1-alpha-dev are
- * upgraded.
- */
- char *fname = get_datadir_fname("key-pinning-entries");
- unlink(fname);
- tor_free(fname);
- }
-
- if (trusted_dirs_reload_certs()) {
- log_warn(LD_DIR,
- "Couldn't load all cached v3 certificates. Starting anyway.");
- }
- if (router_reload_consensus_networkstatus()) {
- return -1;
- }
- /* load the routers file, or assign the defaults. */
- if (router_reload_router_list()) {
- return -1;
- }
- /* load the networkstatuses. (This launches a download for new routers as
- * appropriate.)
- */
- now = time(NULL);
- directory_info_has_arrived(now, 1, 0);
-
- if (server_mode(get_options()) || dir_server_mode(get_options())) {
- /* launch cpuworkers. Need to do this *after* we've read the onion key. */
- cpu_init();
- }
- consdiffmgr_enable_background_compression();
-
- /* Setup shared random protocol subsystem. */
- if (authdir_mode_v3(get_options())) {
- if (sr_init(1) < 0) {
- return -1;
- }
- }
-
/* set up once-a-second callback. */
reschedule_per_second_timer();
@@ -2930,20 +2726,6 @@ do_main_loop(void)
}
#endif /* defined(HAVE_SYSTEMD_209) */
-#ifdef HAVE_SYSTEMD
- {
- const int r = sd_notify(0, "READY=1");
- if (r < 0) {
- log_warn(LD_GENERAL, "Unable to send readiness to systemd: %s",
- strerror(r));
- } else if (r > 0) {
- log_notice(LD_GENERAL, "Signaled readiness to systemd");
- } else {
- log_info(LD_GENERAL, "Systemd NOTIFY_SOCKET not present.");
- }
- }
-#endif /* defined(HAVE_SYSTEMD) */
-
main_loop_should_exit = 0;
main_loop_exit_value = 0;
@@ -3086,109 +2868,6 @@ run_main_loop_until_done(void)
return loop_result;
}
-/** Libevent callback: invoked when we get a signal.
- */
-static void
-signal_callback(evutil_socket_t fd, short events, void *arg)
-{
- const int *sigptr = arg;
- const int sig = *sigptr;
- (void)fd;
- (void)events;
-
- update_current_time(time(NULL));
- process_signal(sig);
-}
-
-/** Do the work of acting on a signal received in <b>sig</b> */
-static void
-process_signal(int sig)
-{
- switch (sig)
- {
- case SIGTERM:
- log_notice(LD_GENERAL,"Catching signal TERM, exiting cleanly.");
- tor_shutdown_event_loop_and_exit(0);
- break;
- case SIGINT:
- if (!server_mode(get_options())) { /* do it now */
- log_notice(LD_GENERAL,"Interrupt: exiting cleanly.");
- tor_shutdown_event_loop_and_exit(0);
- return;
- }
-#ifdef HAVE_SYSTEMD
- sd_notify(0, "STOPPING=1");
-#endif
- hibernate_begin_shutdown();
- break;
-#ifdef SIGPIPE
- case SIGPIPE:
- log_debug(LD_GENERAL,"Caught SIGPIPE. Ignoring.");
- break;
-#endif
- case SIGUSR1:
- /* prefer to log it at INFO, but make sure we always see it */
- dumpstats(get_min_log_level()<LOG_INFO ? get_min_log_level() : LOG_INFO);
- control_event_signal(sig);
- break;
- case SIGUSR2:
- switch_logs_debug();
- log_debug(LD_GENERAL,"Caught USR2, going to loglevel debug. "
- "Send HUP to change back.");
- control_event_signal(sig);
- break;
- case SIGHUP:
-#ifdef HAVE_SYSTEMD
- sd_notify(0, "RELOADING=1");
-#endif
- if (do_hup() < 0) {
- log_warn(LD_CONFIG,"Restart failed (config error?). Exiting.");
- tor_shutdown_event_loop_and_exit(1);
- return;
- }
-#ifdef HAVE_SYSTEMD
- sd_notify(0, "READY=1");
-#endif
- control_event_signal(sig);
- break;
-#ifdef SIGCHLD
- case SIGCHLD:
- notify_pending_waitpid_callbacks();
- break;
-#endif
- case SIGNEWNYM: {
- time_t now = time(NULL);
- if (time_of_last_signewnym + MAX_SIGNEWNYM_RATE > now) {
- const time_t delay_sec =
- time_of_last_signewnym + MAX_SIGNEWNYM_RATE - now;
- if (! signewnym_is_pending) {
- signewnym_is_pending = 1;
- if (!handle_deferred_signewnym_ev) {
- handle_deferred_signewnym_ev =
- mainloop_event_postloop_new(handle_deferred_signewnym_cb, NULL);
- }
- const struct timeval delay_tv = { delay_sec, 0 };
- mainloop_event_schedule(handle_deferred_signewnym_ev, &delay_tv);
- }
- log_notice(LD_CONTROL,
- "Rate limiting NEWNYM request: delaying by %d second(s)",
- (int)(delay_sec));
- } else {
- signewnym_impl(now);
- }
- break;
- }
- case SIGCLEARDNSCACHE:
- addressmap_clear_transient();
- control_event_signal(sig);
- break;
- case SIGHEARTBEAT:
- log_heartbeat(time(NULL));
- control_event_signal(sig);
- break;
- }
-}
-
/** Returns Tor's uptime. */
MOCK_IMPL(long,
get_uptime,(void))
@@ -3203,507 +2882,9 @@ reset_uptime,(void))
stats_n_seconds_working = 0;
}
-/**
- * Write current memory usage information to the log.
- */
-static void
-dumpmemusage(int severity)
-{
- connection_dump_buffer_mem_stats(severity);
- tor_log(severity, LD_GENERAL, "In rephist: %"PRIu64" used by %d Tors.",
- (rephist_total_alloc), rephist_total_num);
- dump_routerlist_mem_usage(severity);
- dump_cell_pool_usage(severity);
- dump_dns_mem_usage(severity);
- tor_log_mallinfo(severity);
-}
-
-/** Write all statistics to the log, with log level <b>severity</b>. Called
- * in response to a SIGUSR1. */
-static void
-dumpstats(int severity)
-{
- time_t now = time(NULL);
- time_t elapsed;
- size_t rbuf_cap, wbuf_cap, rbuf_len, wbuf_len;
-
- tor_log(severity, LD_GENERAL, "Dumping stats:");
-
- SMARTLIST_FOREACH_BEGIN(connection_array, connection_t *, conn) {
- int i = conn_sl_idx;
- tor_log(severity, LD_GENERAL,
- "Conn %d (socket %d) type %d (%s), state %d (%s), created %d secs ago",
- i, (int)conn->s, conn->type, conn_type_to_string(conn->type),
- conn->state, conn_state_to_string(conn->type, conn->state),
- (int)(now - conn->timestamp_created));
- if (!connection_is_listener(conn)) {
- tor_log(severity,LD_GENERAL,
- "Conn %d is to %s:%d.", i,
- safe_str_client(conn->address),
- conn->port);
- tor_log(severity,LD_GENERAL,
- "Conn %d: %d bytes waiting on inbuf (len %d, last read %d secs ago)",
- i,
- (int)connection_get_inbuf_len(conn),
- (int)buf_allocation(conn->inbuf),
- (int)(now - conn->timestamp_last_read_allowed));
- tor_log(severity,LD_GENERAL,
- "Conn %d: %d bytes waiting on outbuf "
- "(len %d, last written %d secs ago)",i,
- (int)connection_get_outbuf_len(conn),
- (int)buf_allocation(conn->outbuf),
- (int)(now - conn->timestamp_last_write_allowed));
- if (conn->type == CONN_TYPE_OR) {
- or_connection_t *or_conn = TO_OR_CONN(conn);
- if (or_conn->tls) {
- if (tor_tls_get_buffer_sizes(or_conn->tls, &rbuf_cap, &rbuf_len,
- &wbuf_cap, &wbuf_len) == 0) {
- tor_log(severity, LD_GENERAL,
- "Conn %d: %d/%d bytes used on OpenSSL read buffer; "
- "%d/%d bytes used on write buffer.",
- i, (int)rbuf_len, (int)rbuf_cap, (int)wbuf_len, (int)wbuf_cap);
- }
- }
- }
- }
- circuit_dump_by_conn(conn, severity); /* dump info about all the circuits
- * using this conn */
- } SMARTLIST_FOREACH_END(conn);
-
- channel_dumpstats(severity);
- channel_listener_dumpstats(severity);
-
- tor_log(severity, LD_NET,
- "Cells processed: %"PRIu64" padding\n"
- " %"PRIu64" create\n"
- " %"PRIu64" created\n"
- " %"PRIu64" relay\n"
- " (%"PRIu64" relayed)\n"
- " (%"PRIu64" delivered)\n"
- " %"PRIu64" destroy",
- (stats_n_padding_cells_processed),
- (stats_n_create_cells_processed),
- (stats_n_created_cells_processed),
- (stats_n_relay_cells_processed),
- (stats_n_relay_cells_relayed),
- (stats_n_relay_cells_delivered),
- (stats_n_destroy_cells_processed));
- if (stats_n_data_cells_packaged)
- tor_log(severity,LD_NET,"Average packaged cell fullness: %2.3f%%",
- 100*(((double)stats_n_data_bytes_packaged) /
- ((double)stats_n_data_cells_packaged*RELAY_PAYLOAD_SIZE)) );
- if (stats_n_data_cells_received)
- tor_log(severity,LD_NET,"Average delivered cell fullness: %2.3f%%",
- 100*(((double)stats_n_data_bytes_received) /
- ((double)stats_n_data_cells_received*RELAY_PAYLOAD_SIZE)) );
-
- cpuworker_log_onionskin_overhead(severity, ONION_HANDSHAKE_TYPE_TAP, "TAP");
- cpuworker_log_onionskin_overhead(severity, ONION_HANDSHAKE_TYPE_NTOR,"ntor");
-
- if (now - time_of_process_start >= 0)
- elapsed = now - time_of_process_start;
- else
- elapsed = 0;
-
- if (elapsed) {
- tor_log(severity, LD_NET,
- "Average bandwidth: %"PRIu64"/%d = %d bytes/sec reading",
- (stats_n_bytes_read),
- (int)elapsed,
- (int) (stats_n_bytes_read/elapsed));
- tor_log(severity, LD_NET,
- "Average bandwidth: %"PRIu64"/%d = %d bytes/sec writing",
- (stats_n_bytes_written),
- (int)elapsed,
- (int) (stats_n_bytes_written/elapsed));
- }
-
- tor_log(severity, LD_NET, "--------------- Dumping memory information:");
- dumpmemusage(severity);
-
- rep_hist_dump_stats(now,severity);
- rend_service_dump_stats(severity);
- dump_distinct_digest_count(severity);
-}
-
-/** Called by exit() as we shut down the process.
- */
-static void
-exit_function(void)
-{
- /* NOTE: If we ever daemonize, this gets called immediately. That's
- * okay for now, because we only use this on Windows. */
-#ifdef _WIN32
- WSACleanup();
-#endif
-}
-
-#ifdef _WIN32
-#define UNIX_ONLY 0
-#else
-#define UNIX_ONLY 1
-#endif
-
-static struct {
- /** A numeric code for this signal. Must match the signal value if
- * try_to_register is true. */
- int signal_value;
- /** True if we should try to register this signal with libevent and catch
- * corresponding posix signals. False otherwise. */
- int try_to_register;
- /** Pointer to hold the event object constructed for this signal. */
- struct event *signal_event;
-} signal_handlers[] = {
-#ifdef SIGINT
- { SIGINT, UNIX_ONLY, NULL }, /* do a controlled slow shutdown */
-#endif
-#ifdef SIGTERM
- { SIGTERM, UNIX_ONLY, NULL }, /* to terminate now */
-#endif
-#ifdef SIGPIPE
- { SIGPIPE, UNIX_ONLY, NULL }, /* otherwise SIGPIPE kills us */
-#endif
-#ifdef SIGUSR1
- { SIGUSR1, UNIX_ONLY, NULL }, /* dump stats */
-#endif
-#ifdef SIGUSR2
- { SIGUSR2, UNIX_ONLY, NULL }, /* go to loglevel debug */
-#endif
-#ifdef SIGHUP
- { SIGHUP, UNIX_ONLY, NULL }, /* to reload config, retry conns, etc */
-#endif
-#ifdef SIGXFSZ
- { SIGXFSZ, UNIX_ONLY, NULL }, /* handle file-too-big resource exhaustion */
-#endif
-#ifdef SIGCHLD
- { SIGCHLD, UNIX_ONLY, NULL }, /* handle dns/cpu workers that exit */
-#endif
- /* These are controller-only */
- { SIGNEWNYM, 0, NULL },
- { SIGCLEARDNSCACHE, 0, NULL },
- { SIGHEARTBEAT, 0, NULL },
- { -1, -1, NULL }
-};
-
-/** Set up the signal handler events for this process, and register them
- * with libevent if appropriate. */
-void
-handle_signals(void)
-{
- int i;
- const int enabled = !get_options()->DisableSignalHandlers;
-
- for (i = 0; signal_handlers[i].signal_value >= 0; ++i) {
- /* Signal handlers are only registered with libevent if they need to catch
- * real POSIX signals. We construct these signal handler events in either
- * case, though, so that controllers can activate them with the SIGNAL
- * command.
- */
- if (enabled && signal_handlers[i].try_to_register) {
- signal_handlers[i].signal_event =
- tor_evsignal_new(tor_libevent_get_base(),
- signal_handlers[i].signal_value,
- signal_callback,
- &signal_handlers[i].signal_value);
- if (event_add(signal_handlers[i].signal_event, NULL))
- log_warn(LD_BUG, "Error from libevent when adding "
- "event for signal %d",
- signal_handlers[i].signal_value);
- } else {
- signal_handlers[i].signal_event =
- tor_event_new(tor_libevent_get_base(), -1,
- EV_SIGNAL, signal_callback,
- &signal_handlers[i].signal_value);
- }
- }
-}
-
-/* Cause the signal handler for signal_num to be called in the event loop. */
-void
-activate_signal(int signal_num)
-{
- int i;
- for (i = 0; signal_handlers[i].signal_value >= 0; ++i) {
- if (signal_handlers[i].signal_value == signal_num) {
- event_active(signal_handlers[i].signal_event, EV_SIGNAL, 1);
- return;
- }
- }
-}
-
-/** Main entry point for the Tor command-line client. Return 0 on "success",
- * negative on "failure", and positive on "success and exit".
- */
-int
-tor_init(int argc, char *argv[])
-{
- char progname[256];
- int quiet = 0;
-
- time_of_process_start = time(NULL);
- init_connection_lists();
- /* Have the log set up with our application name. */
- tor_snprintf(progname, sizeof(progname), "Tor %s", get_version());
- log_set_application_name(progname);
-
- /* Set up the crypto nice and early */
- if (crypto_early_init() < 0) {
- log_err(LD_GENERAL, "Unable to initialize the crypto subsystem!");
- return -1;
- }
-
- /* Initialize the history structures. */
- rep_hist_init();
- /* Initialize the service cache. */
- rend_cache_init();
- addressmap_init(); /* Init the client dns cache. Do it always, since it's
- * cheap. */
- /* Initialize the HS subsystem. */
- hs_init();
-
- {
- /* We search for the "quiet" option first, since it decides whether we
- * will log anything at all to the command line. */
- config_line_t *opts = NULL, *cmdline_opts = NULL;
- const config_line_t *cl;
- (void) config_parse_commandline(argc, argv, 1, &opts, &cmdline_opts);
- for (cl = cmdline_opts; cl; cl = cl->next) {
- if (!strcmp(cl->key, "--hush"))
- quiet = 1;
- if (!strcmp(cl->key, "--quiet") ||
- !strcmp(cl->key, "--dump-config"))
- quiet = 2;
- /* The following options imply --hush */
- if (!strcmp(cl->key, "--version") || !strcmp(cl->key, "--digests") ||
- !strcmp(cl->key, "--list-torrc-options") ||
- !strcmp(cl->key, "--library-versions") ||
- !strcmp(cl->key, "--hash-password") ||
- !strcmp(cl->key, "-h") || !strcmp(cl->key, "--help")) {
- if (quiet < 1)
- quiet = 1;
- }
- }
- config_free_lines(opts);
- config_free_lines(cmdline_opts);
- }
-
- /* give it somewhere to log to initially */
- switch (quiet) {
- case 2:
- /* no initial logging */
- break;
- case 1:
- add_temp_log(LOG_WARN);
- break;
- default:
- add_temp_log(LOG_NOTICE);
- }
- quiet_level = quiet;
-
- {
- const char *version = get_version();
-
- log_notice(LD_GENERAL, "Tor %s running on %s with Libevent %s, "
- "%s %s, Zlib %s, Liblzma %s, and Libzstd %s.", version,
- get_uname(),
- tor_libevent_get_version_str(),
- crypto_get_library_name(),
- crypto_get_library_version_string(),
- tor_compress_supports_method(ZLIB_METHOD) ?
- tor_compress_version_str(ZLIB_METHOD) : "N/A",
- tor_compress_supports_method(LZMA_METHOD) ?
- tor_compress_version_str(LZMA_METHOD) : "N/A",
- tor_compress_supports_method(ZSTD_METHOD) ?
- tor_compress_version_str(ZSTD_METHOD) : "N/A");
-
- log_notice(LD_GENERAL, "Tor can't help you if you use it wrong! "
- "Learn how to be safe at "
- "https://www.torproject.org/download/download#warning");
-
- if (strstr(version, "alpha") || strstr(version, "beta"))
- log_notice(LD_GENERAL, "This version is not a stable Tor release. "
- "Expect more bugs than usual.");
-
- tor_compress_log_init_warnings();
- }
-
-#ifdef HAVE_RUST
- rust_log_welcome_string();
-#endif /* defined(HAVE_RUST) */
-
- if (network_init()<0) {
- log_err(LD_BUG,"Error initializing network; exiting.");
- return -1;
- }
- atexit(exit_function);
-
- int init_rv = options_init_from_torrc(argc,argv);
- if (init_rv < 0) {
- log_err(LD_CONFIG,"Reading config failed--see warnings above.");
- return -1;
- } else if (init_rv > 0) {
- // We succeeded, and should exit anyway -- probably the user just said
- // "--version" or something like that.
- return 1;
- }
-
- /* The options are now initialised */
- const or_options_t *options = get_options();
-
- /* Initialize channelpadding parameters to defaults until we get
- * a consensus */
- channelpadding_new_consensus_params(NULL);
-
- /* Initialize predicted ports list after loading options */
- predicted_ports_init();
-
-#ifndef _WIN32
- if (geteuid()==0)
- log_warn(LD_GENERAL,"You are running Tor as root. You don't need to, "
- "and you probably shouldn't.");
-#endif
-
- if (crypto_global_init(options->HardwareAccel,
- options->AccelName,
- options->AccelDir)) {
- log_err(LD_BUG, "Unable to initialize OpenSSL. Exiting.");
- return -1;
- }
- stream_choice_seed_weak_rng();
- if (tor_init_libevent_rng() < 0) {
- log_warn(LD_NET, "Problem initializing libevent RNG.");
- }
-
- /* Scan/clean unparseable descriptors; after reading config */
- routerparse_init();
-
- return 0;
-}
-
-/** A lockfile structure, used to prevent two Tors from messing with the
- * data directory at once. If this variable is non-NULL, we're holding
- * the lockfile. */
-static tor_lockfile_t *lockfile = NULL;
-
-/** Try to grab the lock file described in <b>options</b>, if we do not
- * already have it. If <b>err_if_locked</b> is true, warn if somebody else is
- * holding the lock, and exit if we can't get it after waiting. Otherwise,
- * return -1 if we can't get the lockfile. Return 0 on success.
- */
-int
-try_locking(const or_options_t *options, int err_if_locked)
-{
- if (lockfile)
- return 0;
- else {
- char *fname = options_get_datadir_fname(options, "lock");
- int already_locked = 0;
- tor_lockfile_t *lf = tor_lockfile_lock(fname, 0, &already_locked);
- tor_free(fname);
- if (!lf) {
- if (err_if_locked && already_locked) {
- int r;
- log_warn(LD_GENERAL, "It looks like another Tor process is running "
- "with the same data directory. Waiting 5 seconds to see "
- "if it goes away.");
-#ifndef _WIN32
- sleep(5);
-#else
- Sleep(5000);
-#endif
- r = try_locking(options, 0);
- if (r<0) {
- log_err(LD_GENERAL, "No, it's still there. Exiting.");
- return -1;
- }
- return r;
- }
- return -1;
- }
- lockfile = lf;
- return 0;
- }
-}
-
-/** Return true iff we've successfully acquired the lock file. */
-int
-have_lockfile(void)
-{
- return lockfile != NULL;
-}
-
-/** If we have successfully acquired the lock file, release it. */
-void
-release_lockfile(void)
-{
- if (lockfile) {
- tor_lockfile_unlock(lockfile);
- lockfile = NULL;
- }
-}
-
-/** Free all memory that we might have allocated somewhere.
- * If <b>postfork</b>, we are a worker process and we want to free
- * only the parts of memory that we won't touch. If !<b>postfork</b>,
- * Tor is shutting down and we should free everything.
- *
- * Helps us find the real leaks with sanitizers and the like. Also valgrind
- * should then report 0 reachable in its leak report (in an ideal world --
- * in practice libevent, SSL, libc etc never quite free everything). */
void
-tor_free_all(int postfork)
+tor_mainloop_free_all(void)
{
- if (!postfork) {
- evdns_shutdown(1);
- }
- geoip_free_all();
- dirvote_free_all();
- routerlist_free_all();
- networkstatus_free_all();
- addressmap_free_all();
- dirserv_free_all();
- rend_cache_free_all();
- rend_service_authorization_free_all();
- rep_hist_free_all();
- dns_free_all();
- clear_pending_onions();
- circuit_free_all();
- entry_guards_free_all();
- pt_free_all();
- channel_tls_free_all();
- channel_free_all();
- connection_free_all();
- connection_edge_free_all();
- scheduler_free_all();
- nodelist_free_all();
- microdesc_free_all();
- routerparse_free_all();
- ext_orport_free_all();
- control_free_all();
- tor_free_getaddrinfo_cache();
- protover_free_all();
- bridges_free_all();
- consdiffmgr_free_all();
- hs_free_all();
- dos_free_all();
- circuitmux_ewma_free_all();
- accounting_free_all();
-
- if (!postfork) {
- config_free_all();
- or_state_free_all();
- router_free_all();
- routerkeys_free_all();
- policies_free_all();
- }
- if (!postfork) {
- tor_tls_free_all();
-#ifndef _WIN32
- tor_getpwnam(NULL);
-#endif
- }
- /* stuff in main.c */
-
smartlist_free(connection_array);
smartlist_free(closeable_connection_lst);
smartlist_free(active_linked_connection_lst);
@@ -3716,13 +2897,10 @@ tor_free_all(int postfork)
mainloop_event_free(postloop_cleanup_ev);
mainloop_event_free(handle_deferred_signewnym_ev);
-#ifdef HAVE_SYSTEMD_209
- periodic_timer_free(systemd_watchdog_timer);
-#endif
+ stats_n_bytes_read = stats_n_bytes_written = 0;
memset(&global_bucket, 0, sizeof(global_bucket));
memset(&global_relayed_bucket, 0, sizeof(global_relayed_bucket));
- stats_n_bytes_read = stats_n_bytes_written = 0;
time_of_process_start = 0;
time_of_last_signewnym = 0;
signewnym_is_pending = 0;
@@ -3738,602 +2916,4 @@ tor_free_all(int postfork)
current_second = 0;
memset(&current_second_last_changed, 0,
sizeof(current_second_last_changed));
-
- if (!postfork) {
- release_lockfile();
- }
- tor_libevent_free_all();
- /* Stuff in util.c and address.c*/
- if (!postfork) {
- escaped(NULL);
- esc_router_info(NULL);
- clean_up_backtrace_handler();
- logs_free_all(); /* free log strings. do this last so logs keep working. */
- }
-}
-
-/**
- * Remove the specified file, and log a warning if the operation fails for
- * any reason other than the file not existing. Ignores NULL filenames.
- */
-void
-tor_remove_file(const char *filename)
-{
- if (filename && tor_unlink(filename) != 0 && errno != ENOENT) {
- log_warn(LD_FS, "Couldn't unlink %s: %s",
- filename, strerror(errno));
- }
-}
-
-/** Do whatever cleanup is necessary before shutting Tor down. */
-void
-tor_cleanup(void)
-{
- const or_options_t *options = get_options();
- if (options->command == CMD_RUN_TOR) {
- time_t now = time(NULL);
- /* Remove our pid file. We don't care if there was an error when we
- * unlink, nothing we could do about it anyways. */
- tor_remove_file(options->PidFile);
- /* Remove control port file */
- tor_remove_file(options->ControlPortWriteToFile);
- /* Remove cookie authentication file */
- {
- char *cookie_fname = get_controller_cookie_file_name();
- tor_remove_file(cookie_fname);
- tor_free(cookie_fname);
- }
- /* Remove Extended ORPort cookie authentication file */
- {
- char *cookie_fname = get_ext_or_auth_cookie_file_name();
- tor_remove_file(cookie_fname);
- tor_free(cookie_fname);
- }
- if (accounting_is_enabled(options))
- accounting_record_bandwidth_usage(now, get_or_state());
- or_state_mark_dirty(get_or_state(), 0); /* force an immediate save. */
- or_state_save(now);
- if (authdir_mode(options)) {
- sr_save_and_cleanup();
- }
- if (authdir_mode_tests_reachability(options))
- rep_hist_record_mtbf_data(now, 0);
- keypin_close_journal();
- }
-
- timers_shutdown();
-
- tor_free_all(0); /* We could move tor_free_all back into the ifdef below
- later, if it makes shutdown unacceptably slow. But for
- now, leave it here: it's helped us catch bugs in the
- past. */
- crypto_global_cleanup();
-}
-
-/** Read/create keys as needed, and echo our fingerprint to stdout. */
-static int
-do_list_fingerprint(void)
-{
- char buf[FINGERPRINT_LEN+1];
- crypto_pk_t *k;
- const char *nickname = get_options()->Nickname;
- sandbox_disable_getaddrinfo_cache();
- if (!server_mode(get_options())) {
- log_err(LD_GENERAL,
- "Clients don't have long-term identity keys. Exiting.");
- return -1;
- }
- tor_assert(nickname);
- if (init_keys() < 0) {
- log_err(LD_GENERAL,"Error initializing keys; exiting.");
- return -1;
- }
- if (!(k = get_server_identity_key())) {
- log_err(LD_GENERAL,"Error: missing identity key.");
- return -1;
- }
- if (crypto_pk_get_fingerprint(k, buf, 1)<0) {
- log_err(LD_BUG, "Error computing fingerprint");
- return -1;
- }
- printf("%s %s\n", nickname, buf);
- return 0;
-}
-
-/** Entry point for password hashing: take the desired password from
- * the command line, and print its salted hash to stdout. **/
-static void
-do_hash_password(void)
-{
-
- char output[256];
- char key[S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN];
-
- crypto_rand(key, S2K_RFC2440_SPECIFIER_LEN-1);
- key[S2K_RFC2440_SPECIFIER_LEN-1] = (uint8_t)96; /* Hash 64 K of data. */
- secret_to_key_rfc2440(key+S2K_RFC2440_SPECIFIER_LEN, DIGEST_LEN,
- get_options()->command_arg, strlen(get_options()->command_arg),
- key);
- base16_encode(output, sizeof(output), key, sizeof(key));
- printf("16:%s\n",output);
-}
-
-/** Entry point for configuration dumping: write the configuration to
- * stdout. */
-static int
-do_dump_config(void)
-{
- const or_options_t *options = get_options();
- const char *arg = options->command_arg;
- int how;
- char *opts;
-
- if (!strcmp(arg, "short")) {
- how = OPTIONS_DUMP_MINIMAL;
- } else if (!strcmp(arg, "non-builtin")) {
- how = OPTIONS_DUMP_DEFAULTS;
- } else if (!strcmp(arg, "full")) {
- how = OPTIONS_DUMP_ALL;
- } else {
- fprintf(stderr, "No valid argument to --dump-config found!\n");
- fprintf(stderr, "Please select 'short', 'non-builtin', or 'full'.\n");
-
- return -1;
- }
-
- opts = options_dump(options, how);
- printf("%s", opts);
- tor_free(opts);
-
- return 0;
-}
-
-static void
-init_addrinfo(void)
-{
- if (! server_mode(get_options()) ||
- (get_options()->Address && strlen(get_options()->Address) > 0)) {
- /* We don't need to seed our own hostname, because we won't be calling
- * resolve_my_address on it.
- */
- return;
- }
- char hname[256];
-
- // host name to sandbox
- gethostname(hname, sizeof(hname));
- tor_add_addrinfo(hname);
-}
-
-static sandbox_cfg_t*
-sandbox_init_filter(void)
-{
- const or_options_t *options = get_options();
- sandbox_cfg_t *cfg = sandbox_cfg_new();
- int i;
-
- sandbox_cfg_allow_openat_filename(&cfg,
- get_cachedir_fname("cached-status"));
-
-#define OPEN(name) \
- sandbox_cfg_allow_open_filename(&cfg, tor_strdup(name))
-
-#define OPEN_DATADIR(name) \
- sandbox_cfg_allow_open_filename(&cfg, get_datadir_fname(name))
-
-#define OPEN_DATADIR2(name, name2) \
- sandbox_cfg_allow_open_filename(&cfg, get_datadir_fname2((name), (name2)))
-
-#define OPEN_DATADIR_SUFFIX(name, suffix) do { \
- OPEN_DATADIR(name); \
- OPEN_DATADIR(name suffix); \
- } while (0)
-
-#define OPEN_DATADIR2_SUFFIX(name, name2, suffix) do { \
- OPEN_DATADIR2(name, name2); \
- OPEN_DATADIR2(name, name2 suffix); \
- } while (0)
-
-#define OPEN_KEY_DIRECTORY() \
- sandbox_cfg_allow_open_filename(&cfg, tor_strdup(options->KeyDirectory))
-#define OPEN_CACHEDIR(name) \
- sandbox_cfg_allow_open_filename(&cfg, get_cachedir_fname(name))
-#define OPEN_CACHEDIR_SUFFIX(name, suffix) do { \
- OPEN_CACHEDIR(name); \
- OPEN_CACHEDIR(name suffix); \
- } while (0)
-#define OPEN_KEYDIR(name) \
- sandbox_cfg_allow_open_filename(&cfg, get_keydir_fname(name))
-#define OPEN_KEYDIR_SUFFIX(name, suffix) do { \
- OPEN_KEYDIR(name); \
- OPEN_KEYDIR(name suffix); \
- } while (0)
-
- OPEN(options->DataDirectory);
- OPEN_KEY_DIRECTORY();
-
- OPEN_CACHEDIR_SUFFIX("cached-certs", ".tmp");
- OPEN_CACHEDIR_SUFFIX("cached-consensus", ".tmp");
- OPEN_CACHEDIR_SUFFIX("unverified-consensus", ".tmp");
- OPEN_CACHEDIR_SUFFIX("unverified-microdesc-consensus", ".tmp");
- OPEN_CACHEDIR_SUFFIX("cached-microdesc-consensus", ".tmp");
- OPEN_CACHEDIR_SUFFIX("cached-microdescs", ".tmp");
- OPEN_CACHEDIR_SUFFIX("cached-microdescs.new", ".tmp");
- OPEN_CACHEDIR_SUFFIX("cached-descriptors", ".tmp");
- OPEN_CACHEDIR_SUFFIX("cached-descriptors.new", ".tmp");
- OPEN_CACHEDIR("cached-descriptors.tmp.tmp");
- OPEN_CACHEDIR_SUFFIX("cached-extrainfo", ".tmp");
- OPEN_CACHEDIR_SUFFIX("cached-extrainfo.new", ".tmp");
- OPEN_CACHEDIR("cached-extrainfo.tmp.tmp");
-
- OPEN_DATADIR_SUFFIX("state", ".tmp");
- OPEN_DATADIR_SUFFIX("sr-state", ".tmp");
- OPEN_DATADIR_SUFFIX("unparseable-desc", ".tmp");
- OPEN_DATADIR_SUFFIX("v3-status-votes", ".tmp");
- OPEN_DATADIR("key-pinning-journal");
- OPEN("/dev/srandom");
- OPEN("/dev/urandom");
- OPEN("/dev/random");
- OPEN("/etc/hosts");
- OPEN("/proc/meminfo");
-
- if (options->BridgeAuthoritativeDir)
- OPEN_DATADIR_SUFFIX("networkstatus-bridges", ".tmp");
-
- if (authdir_mode(options))
- OPEN_DATADIR("approved-routers");
-
- if (options->ServerDNSResolvConfFile)
- sandbox_cfg_allow_open_filename(&cfg,
- tor_strdup(options->ServerDNSResolvConfFile));
- else
- sandbox_cfg_allow_open_filename(&cfg, tor_strdup("/etc/resolv.conf"));
-
- for (i = 0; i < 2; ++i) {
- if (get_torrc_fname(i)) {
- sandbox_cfg_allow_open_filename(&cfg, tor_strdup(get_torrc_fname(i)));
- }
- }
-
- SMARTLIST_FOREACH(options->FilesOpenedByIncludes, char *, f, {
- OPEN(f);
- });
-
-#define RENAME_SUFFIX(name, suffix) \
- sandbox_cfg_allow_rename(&cfg, \
- get_datadir_fname(name suffix), \
- get_datadir_fname(name))
-
-#define RENAME_SUFFIX2(prefix, name, suffix) \
- sandbox_cfg_allow_rename(&cfg, \
- get_datadir_fname2(prefix, name suffix), \
- get_datadir_fname2(prefix, name))
-
-#define RENAME_CACHEDIR_SUFFIX(name, suffix) \
- sandbox_cfg_allow_rename(&cfg, \
- get_cachedir_fname(name suffix), \
- get_cachedir_fname(name))
-
-#define RENAME_KEYDIR_SUFFIX(name, suffix) \
- sandbox_cfg_allow_rename(&cfg, \
- get_keydir_fname(name suffix), \
- get_keydir_fname(name))
-
- RENAME_CACHEDIR_SUFFIX("cached-certs", ".tmp");
- RENAME_CACHEDIR_SUFFIX("cached-consensus", ".tmp");
- RENAME_CACHEDIR_SUFFIX("unverified-consensus", ".tmp");
- RENAME_CACHEDIR_SUFFIX("unverified-microdesc-consensus", ".tmp");
- RENAME_CACHEDIR_SUFFIX("cached-microdesc-consensus", ".tmp");
- RENAME_CACHEDIR_SUFFIX("cached-microdescs", ".tmp");
- RENAME_CACHEDIR_SUFFIX("cached-microdescs", ".new");
- RENAME_CACHEDIR_SUFFIX("cached-microdescs.new", ".tmp");
- RENAME_CACHEDIR_SUFFIX("cached-descriptors", ".tmp");
- RENAME_CACHEDIR_SUFFIX("cached-descriptors", ".new");
- RENAME_CACHEDIR_SUFFIX("cached-descriptors.new", ".tmp");
- RENAME_CACHEDIR_SUFFIX("cached-extrainfo", ".tmp");
- RENAME_CACHEDIR_SUFFIX("cached-extrainfo", ".new");
- RENAME_CACHEDIR_SUFFIX("cached-extrainfo.new", ".tmp");
-
- RENAME_SUFFIX("state", ".tmp");
- RENAME_SUFFIX("sr-state", ".tmp");
- RENAME_SUFFIX("unparseable-desc", ".tmp");
- RENAME_SUFFIX("v3-status-votes", ".tmp");
-
- if (options->BridgeAuthoritativeDir)
- RENAME_SUFFIX("networkstatus-bridges", ".tmp");
-
-#define STAT_DATADIR(name) \
- sandbox_cfg_allow_stat_filename(&cfg, get_datadir_fname(name))
-
-#define STAT_CACHEDIR(name) \
- sandbox_cfg_allow_stat_filename(&cfg, get_cachedir_fname(name))
-
-#define STAT_DATADIR2(name, name2) \
- sandbox_cfg_allow_stat_filename(&cfg, get_datadir_fname2((name), (name2)))
-
-#define STAT_KEY_DIRECTORY() \
- sandbox_cfg_allow_stat_filename(&cfg, tor_strdup(options->KeyDirectory))
-
- STAT_DATADIR(NULL);
- STAT_DATADIR("lock");
- STAT_DATADIR("state");
- STAT_DATADIR("router-stability");
-
- STAT_CACHEDIR("cached-extrainfo.new");
-
- {
- smartlist_t *files = smartlist_new();
- tor_log_get_logfile_names(files);
- SMARTLIST_FOREACH(files, char *, file_name, {
- /* steals reference */
- sandbox_cfg_allow_open_filename(&cfg, file_name);
- });
- smartlist_free(files);
- }
-
- {
- smartlist_t *files = smartlist_new();
- smartlist_t *dirs = smartlist_new();
- hs_service_lists_fnames_for_sandbox(files, dirs);
- SMARTLIST_FOREACH(files, char *, file_name, {
- char *tmp_name = NULL;
- tor_asprintf(&tmp_name, "%s.tmp", file_name);
- sandbox_cfg_allow_rename(&cfg,
- tor_strdup(tmp_name), tor_strdup(file_name));
- /* steals references */
- sandbox_cfg_allow_open_filename(&cfg, file_name);
- sandbox_cfg_allow_open_filename(&cfg, tmp_name);
- });
- SMARTLIST_FOREACH(dirs, char *, dir, {
- /* steals reference */
- sandbox_cfg_allow_stat_filename(&cfg, dir);
- });
- smartlist_free(files);
- smartlist_free(dirs);
- }
-
- {
- char *fname;
- if ((fname = get_controller_cookie_file_name())) {
- sandbox_cfg_allow_open_filename(&cfg, fname);
- }
- if ((fname = get_ext_or_auth_cookie_file_name())) {
- sandbox_cfg_allow_open_filename(&cfg, fname);
- }
- }
-
- SMARTLIST_FOREACH_BEGIN(get_configured_ports(), port_cfg_t *, port) {
- if (!port->is_unix_addr)
- continue;
- /* When we open an AF_UNIX address, we want permission to open the
- * directory that holds it. */
- char *dirname = tor_strdup(port->unix_addr);
- if (get_parent_directory(dirname) == 0) {
- OPEN(dirname);
- }
- tor_free(dirname);
- sandbox_cfg_allow_chmod_filename(&cfg, tor_strdup(port->unix_addr));
- sandbox_cfg_allow_chown_filename(&cfg, tor_strdup(port->unix_addr));
- } SMARTLIST_FOREACH_END(port);
-
- if (options->DirPortFrontPage) {
- sandbox_cfg_allow_open_filename(&cfg,
- tor_strdup(options->DirPortFrontPage));
- }
-
- // orport
- if (server_mode(get_options())) {
-
- OPEN_KEYDIR_SUFFIX("secret_id_key", ".tmp");
- OPEN_KEYDIR_SUFFIX("secret_onion_key", ".tmp");
- OPEN_KEYDIR_SUFFIX("secret_onion_key_ntor", ".tmp");
- OPEN_KEYDIR("secret_id_key.old");
- OPEN_KEYDIR("secret_onion_key.old");
- OPEN_KEYDIR("secret_onion_key_ntor.old");
-
- OPEN_KEYDIR_SUFFIX("ed25519_master_id_secret_key", ".tmp");
- OPEN_KEYDIR_SUFFIX("ed25519_master_id_secret_key_encrypted", ".tmp");
- OPEN_KEYDIR_SUFFIX("ed25519_master_id_public_key", ".tmp");
- OPEN_KEYDIR_SUFFIX("ed25519_signing_secret_key", ".tmp");
- OPEN_KEYDIR_SUFFIX("ed25519_signing_secret_key_encrypted", ".tmp");
- OPEN_KEYDIR_SUFFIX("ed25519_signing_public_key", ".tmp");
- OPEN_KEYDIR_SUFFIX("ed25519_signing_cert", ".tmp");
-
- OPEN_DATADIR2_SUFFIX("stats", "bridge-stats", ".tmp");
- OPEN_DATADIR2_SUFFIX("stats", "dirreq-stats", ".tmp");
-
- OPEN_DATADIR2_SUFFIX("stats", "entry-stats", ".tmp");
- OPEN_DATADIR2_SUFFIX("stats", "exit-stats", ".tmp");
- OPEN_DATADIR2_SUFFIX("stats", "buffer-stats", ".tmp");
- OPEN_DATADIR2_SUFFIX("stats", "conn-stats", ".tmp");
- OPEN_DATADIR2_SUFFIX("stats", "hidserv-stats", ".tmp");
-
- OPEN_DATADIR("approved-routers");
- OPEN_DATADIR_SUFFIX("fingerprint", ".tmp");
- OPEN_DATADIR_SUFFIX("hashed-fingerprint", ".tmp");
- OPEN_DATADIR_SUFFIX("router-stability", ".tmp");
-
- OPEN("/etc/resolv.conf");
-
- RENAME_SUFFIX("fingerprint", ".tmp");
- RENAME_KEYDIR_SUFFIX("secret_onion_key_ntor", ".tmp");
-
- RENAME_KEYDIR_SUFFIX("secret_id_key", ".tmp");
- RENAME_KEYDIR_SUFFIX("secret_id_key.old", ".tmp");
- RENAME_KEYDIR_SUFFIX("secret_onion_key", ".tmp");
- RENAME_KEYDIR_SUFFIX("secret_onion_key.old", ".tmp");
-
- RENAME_SUFFIX2("stats", "bridge-stats", ".tmp");
- RENAME_SUFFIX2("stats", "dirreq-stats", ".tmp");
- RENAME_SUFFIX2("stats", "entry-stats", ".tmp");
- RENAME_SUFFIX2("stats", "exit-stats", ".tmp");
- RENAME_SUFFIX2("stats", "buffer-stats", ".tmp");
- RENAME_SUFFIX2("stats", "conn-stats", ".tmp");
- RENAME_SUFFIX2("stats", "hidserv-stats", ".tmp");
- RENAME_SUFFIX("hashed-fingerprint", ".tmp");
- RENAME_SUFFIX("router-stability", ".tmp");
-
- RENAME_KEYDIR_SUFFIX("ed25519_master_id_secret_key", ".tmp");
- RENAME_KEYDIR_SUFFIX("ed25519_master_id_secret_key_encrypted", ".tmp");
- RENAME_KEYDIR_SUFFIX("ed25519_master_id_public_key", ".tmp");
- RENAME_KEYDIR_SUFFIX("ed25519_signing_secret_key", ".tmp");
- RENAME_KEYDIR_SUFFIX("ed25519_signing_cert", ".tmp");
-
- sandbox_cfg_allow_rename(&cfg,
- get_keydir_fname("secret_onion_key"),
- get_keydir_fname("secret_onion_key.old"));
- sandbox_cfg_allow_rename(&cfg,
- get_keydir_fname("secret_onion_key_ntor"),
- get_keydir_fname("secret_onion_key_ntor.old"));
-
- STAT_KEY_DIRECTORY();
- OPEN_DATADIR("stats");
- STAT_DATADIR("stats");
- STAT_DATADIR2("stats", "dirreq-stats");
-
- consdiffmgr_register_with_sandbox(&cfg);
- }
-
- init_addrinfo();
-
- return cfg;
-}
-
-/* Main entry point for the Tor process. Called from tor_main(), and by
- * anybody embedding Tor. */
-int
-tor_run_main(const tor_main_configuration_t *tor_cfg)
-{
- int result = 0;
-
-#ifdef _WIN32
-#ifndef HeapEnableTerminationOnCorruption
-#define HeapEnableTerminationOnCorruption 1
-#endif
- /* On heap corruption, just give up; don't try to play along. */
- HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
-
- /* SetProcessDEPPolicy is only supported on 32-bit Windows.
- * (On 64-bit Windows it always fails, and some compilers don't like the
- * PSETDEP cast.)
- * 32-bit Windows defines _WIN32.
- * 64-bit Windows defines _WIN32 and _WIN64. */
-#ifndef _WIN64
- /* Call SetProcessDEPPolicy to permanently enable DEP.
- The function will not resolve on earlier versions of Windows,
- and failure is not dangerous. */
- HMODULE hMod = GetModuleHandleA("Kernel32.dll");
- if (hMod) {
- typedef BOOL (WINAPI *PSETDEP)(DWORD);
- PSETDEP setdeppolicy = (PSETDEP)GetProcAddress(hMod,
- "SetProcessDEPPolicy");
- if (setdeppolicy) {
- /* PROCESS_DEP_ENABLE | PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION */
- setdeppolicy(3);
- }
- }
-#endif /* !defined(_WIN64) */
-#endif /* defined(_WIN32) */
-
- {
- int bt_err = configure_backtrace_handler(get_version());
- if (bt_err < 0) {
- log_warn(LD_BUG, "Unable to install backtrace handler: %s",
- strerror(-bt_err));
- }
- }
-
-#ifdef EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED
- event_set_mem_functions(tor_malloc_, tor_realloc_, tor_free_);
-#endif
-
- init_protocol_warning_severity_level();
-
- update_approx_time(time(NULL));
- tor_threads_init();
- tor_compress_init();
- init_logging(0);
- monotime_init();
-
- int argc = tor_cfg->argc + tor_cfg->argc_owned;
- char **argv = tor_calloc(argc, sizeof(char*));
- memcpy(argv, tor_cfg->argv, tor_cfg->argc*sizeof(char*));
- if (tor_cfg->argc_owned)
- memcpy(argv + tor_cfg->argc, tor_cfg->argv_owned,
- tor_cfg->argc_owned*sizeof(char*));
-
-#ifdef NT_SERVICE
- {
- int done = 0;
- result = nt_service_parse_options(argc, argv, &done);
- if (done) {
- goto done;
- }
- }
-#endif /* defined(NT_SERVICE) */
- {
- int init_rv = tor_init(argc, argv);
- if (init_rv) {
- tor_free_all(0);
- result = (init_rv < 0) ? -1 : 0;
- goto done;
- }
- }
-
- if (get_options()->Sandbox && get_options()->command == CMD_RUN_TOR) {
- sandbox_cfg_t* cfg = sandbox_init_filter();
-
- if (sandbox_init(cfg)) {
- tor_free(argv);
- log_err(LD_BUG,"Failed to create syscall sandbox filter");
- tor_free_all(0);
- return -1;
- }
-
- // registering libevent rng
-#ifdef HAVE_EVUTIL_SECURE_RNG_SET_URANDOM_DEVICE_FILE
- evutil_secure_rng_set_urandom_device_file(
- (char*) sandbox_intern_string("/dev/urandom"));
-#endif
- }
-
- switch (get_options()->command) {
- case CMD_RUN_TOR:
-#ifdef NT_SERVICE
- nt_service_set_state(SERVICE_RUNNING);
-#endif
- result = do_main_loop();
- break;
- case CMD_KEYGEN:
- result = load_ed_keys(get_options(), time(NULL)) < 0;
- break;
- case CMD_KEY_EXPIRATION:
- init_keys();
- result = log_cert_expiration();
- break;
- case CMD_LIST_FINGERPRINT:
- result = do_list_fingerprint();
- break;
- case CMD_HASH_PASSWORD:
- do_hash_password();
- result = 0;
- break;
- case CMD_VERIFY_CONFIG:
- if (quiet_level == 0)
- printf("Configuration was valid\n");
- result = 0;
- break;
- case CMD_DUMP_CONFIG:
- result = do_dump_config();
- break;
- case CMD_RUN_UNITTESTS: /* only set by test.c */
- default:
- log_warn(LD_BUG,"Illegal command number %d: internal error.",
- get_options()->command);
- result = -1;
- }
- tor_cleanup();
- done:
- tor_free(argv);
- return result;
}
diff --git a/src/core/mainloop/main.h b/src/core/mainloop/mainloop.h
index 2b44596706..e3a5acd0d0 100644
--- a/src/core/mainloop/main.h
+++ b/src/core/mainloop/mainloop.h
@@ -5,12 +5,12 @@
/* See LICENSE for licensing information */
/**
- * \file main.h
- * \brief Header file for main.c.
+ * \file mainloop.h
+ * \brief Header file for mainloop.c.
**/
-#ifndef TOR_MAIN_H
-#define TOR_MAIN_H
+#ifndef TOR_MAINLOOP_H
+#define TOR_MAINLOOP_H
int have_completed_a_circuit(void);
void note_that_we_completed_a_circuit(void);
@@ -73,20 +73,7 @@ MOCK_DECL(void,reset_uptime,(void));
unsigned get_signewnym_epoch(void);
-void handle_signals(void);
-void activate_signal(int signal_num);
-
-int try_locking(const or_options_t *options, int err_if_locked);
-int have_lockfile(void);
-void release_lockfile(void);
-
-void tor_remove_file(const char *filename);
-
-void tor_cleanup(void);
-void tor_free_all(int postfork);
-
int do_main_loop(void);
-int tor_init(int argc, char **argv);
void reset_main_loop_counters(void);
uint64_t get_main_loop_success_count(void);
@@ -96,6 +83,12 @@ uint64_t get_main_loop_idle_count(void);
void periodic_events_on_new_options(const or_options_t *options);
void reschedule_per_second_timer(void);
+void do_signewnym(time_t);
+time_t get_last_signewnym_time(void);
+
+void tor_init_connection_lists(void);
+void tor_mainloop_free_all(void);
+
struct token_bucket_rw_t;
extern time_t time_of_process_start;
@@ -103,13 +96,12 @@ extern int quiet_level;
extern struct token_bucket_rw_t global_bucket;
extern struct token_bucket_rw_t global_relayed_bucket;
-#ifdef MAIN_PRIVATE
-STATIC void init_connection_lists(void);
+#ifdef MAINLOOP_PRIVATE
STATIC void initialize_mainloop_events(void);
STATIC void close_closeable_connections(void);
STATIC void initialize_periodic_events(void);
STATIC void teardown_periodic_events(void);
-STATIC int get_my_roles(const or_options_t *options);
+STATIC int get_my_roles(const or_options_t *);
#ifdef TOR_UNIT_TESTS
extern smartlist_t *connection_array;
@@ -119,4 +111,4 @@ extern periodic_event_item_t periodic_events[];
#endif
#endif /* defined(MAIN_PRIVATE) */
-#endif /* !defined(TOR_MAIN_H) */
+#endif
diff --git a/src/core/mainloop/periodic.c b/src/core/mainloop/periodic.c
index df5317eb34..c1785eb38f 100644
--- a/src/core/mainloop/periodic.c
+++ b/src/core/mainloop/periodic.c
@@ -14,7 +14,7 @@
#include "core/or/or.h"
#include "lib/evloop/compat_libevent.h"
#include "app/config/config.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "core/mainloop/periodic.h"
#include "lib/evloop/compat_libevent.h"
diff --git a/src/core/or/channel.c b/src/core/or/channel.c
index 0c204ddfb6..e9011520ae 100644
--- a/src/core/or/channel.c
+++ b/src/core/or/channel.c
@@ -69,7 +69,7 @@
#include "core/or/circuitmux.h"
#include "feature/client/entrynodes.h"
#include "feature/stats/geoip.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/nodelist.h"
#include "core/or/relay.h"
#include "feature/stats/rephist.h"
diff --git a/src/core/or/channelpadding.c b/src/core/or/channelpadding.c
index 7c3a77f62c..6a38d13e32 100644
--- a/src/core/or/channelpadding.c
+++ b/src/core/or/channelpadding.c
@@ -17,7 +17,7 @@
#include "core/mainloop/connection.h"
#include "core/or/connection_or.h"
#include "lib/crypt_ops/crypto_rand.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/stats/rephist.h"
#include "feature/relay/router.h"
#include "lib/time/compat_time.h"
diff --git a/src/core/or/channeltls.c b/src/core/or/channeltls.c
index b90a2ea872..c81a00b497 100644
--- a/src/core/or/channeltls.c
+++ b/src/core/or/channeltls.c
@@ -53,7 +53,7 @@
#include "core/or/relay.h"
#include "feature/stats/rephist.h"
#include "feature/relay/router.h"
-#include "feature/nodelist/routerlist.h"
+#include "feature/nodelist/dirlist.h"
#include "core/or/scheduler.h"
#include "feature/nodelist/torcert.h"
#include "feature/nodelist/networkstatus.h"
diff --git a/src/core/or/circuitbuild.c b/src/core/or/circuitbuild.c
index b1dcfb8536..74f60e6c49 100644
--- a/src/core/or/circuitbuild.c
+++ b/src/core/or/circuitbuild.c
@@ -47,11 +47,12 @@
#include "feature/dircache/directory.h"
#include "feature/client/entrynodes.h"
#include "core/crypto/hs_ntor.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
-#include "core/crypto/onion.h"
+#include "core/or/onion.h"
+#include "core/crypto/onion_crypto.h"
#include "core/crypto/onion_tap.h"
#include "core/crypto/onion_fast.h"
#include "core/or/policies.h"
@@ -60,6 +61,7 @@
#include "feature/rend/rendcommon.h"
#include "feature/stats/rephist.h"
#include "feature/relay/router.h"
+#include "feature/nodelist/node_select.h"
#include "feature/nodelist/routerlist.h"
#include "feature/nodelist/routerparse.h"
#include "feature/nodelist/routerset.h"
@@ -3007,4 +3009,3 @@ circuit_upgrade_circuits_from_guard_wait(void)
smartlist_free(to_upgrade);
}
-
diff --git a/src/core/or/circuitlist.c b/src/core/or/circuitlist.c
index 637feec8d0..f231beb61d 100644
--- a/src/core/or/circuitlist.c
+++ b/src/core/or/circuitlist.c
@@ -71,13 +71,14 @@
#include "lib/crypt_ops/crypto_dh.h"
#include "feature/dircache/directory.h"
#include "feature/client/entrynodes.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/hs/hs_circuit.h"
#include "feature/hs/hs_circuitmap.h"
#include "feature/hs/hs_ident.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
-#include "core/crypto/onion.h"
+#include "feature/relay/onion_queue.h"
+#include "core/crypto/onion_crypto.h"
#include "core/crypto/onion_fast.h"
#include "core/or/policies.h"
#include "core/or/relay.h"
diff --git a/src/core/or/circuitstats.c b/src/core/or/circuitstats.c
index 9ebf618b45..2f37cdfa1a 100644
--- a/src/core/or/circuitstats.c
+++ b/src/core/or/circuitstats.c
@@ -32,7 +32,7 @@
#include "app/config/confparse.h"
#include "feature/control/control.h"
#include "lib/crypt_ops/crypto_rand.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/rend/rendclient.h"
#include "feature/rend/rendservice.h"
diff --git a/src/core/or/command.c b/src/core/or/command.c
index ebddc4a352..f93eb8d857 100644
--- a/src/core/or/command.c
+++ b/src/core/or/command.c
@@ -50,7 +50,8 @@
#include "core/or/dos.h"
#include "feature/hibernate/hibernate.h"
#include "feature/nodelist/nodelist.h"
-#include "core/crypto/onion.h"
+#include "core/or/onion.h"
+#include "core/crypto/onion_crypto.h"
#include "feature/stats/rephist.h"
#include "core/or/relay.h"
#include "feature/relay/router.h"
@@ -699,4 +700,3 @@ command_setup_listener(channel_listener_t *listener)
channel_listener_set_listener_fn(listener, command_handle_incoming_channel);
}
-
diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c
index d49e040219..d0fad67009 100644
--- a/src/core/or/connection_edge.c
+++ b/src/core/or/connection_edge.c
@@ -80,7 +80,7 @@
#include "feature/hs/hs_cache.h"
#include "feature/hs/hs_client.h"
#include "feature/hs/hs_circuit.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
#include "core/or/policies.h"
@@ -839,6 +839,46 @@ connected_cell_format_payload(uint8_t *payload_out,
return connected_payload_len;
}
+/* This is an onion service client connection: Export the client circuit ID
+ * according to the HAProxy proxy protocol. */
+STATIC void
+export_hs_client_circuit_id(edge_connection_t *edge_conn,
+ hs_circuit_id_protocol_t protocol)
+{
+ /* We only support HAProxy right now. */
+ if (protocol != HS_CIRCUIT_ID_PROTOCOL_HAPROXY)
+ return;
+
+ char *buf = NULL;
+ const char dst_ipv6[] = "::1";
+ /* See RFC4193 regarding fc00::/7 */
+ const char src_ipv6_prefix[] = "fc00:dead:beef:4dad:";
+ uint16_t dst_port = 0;
+ uint16_t src_port = 1; /* default value */
+ uint32_t gid = 0; /* default value */
+
+ /* Generate a GID and source port for this client */
+ if (edge_conn->on_circuit != NULL) {
+ gid = TO_ORIGIN_CIRCUIT(edge_conn->on_circuit)->global_identifier;
+ src_port = gid & 0x0000ffff;
+ }
+
+ /* Grab the original dest port from the hs ident */
+ if (edge_conn->hs_ident) {
+ dst_port = edge_conn->hs_ident->orig_virtual_port;
+ }
+
+ /* Build the string */
+ tor_asprintf(&buf, "PROXY TCP6 %s:%x:%x %s %d %d\r\n",
+ src_ipv6_prefix,
+ gid >> 16, gid & 0x0000ffff,
+ dst_ipv6, src_port, dst_port);
+
+ connection_buf_add(buf, strlen(buf), TO_CONN(edge_conn));
+
+ tor_free(buf);
+}
+
/** Connected handler for exit connections: start writing pending
* data, deliver 'CONNECTED' relay cells as appropriate, and check
* any pending data that may have been received. */
@@ -859,6 +899,7 @@ connection_edge_finished_connecting(edge_connection_t *edge_conn)
rep_hist_note_exit_stream_opened(conn->port);
conn->state = EXIT_CONN_STATE_OPEN;
+
connection_watch_events(conn, READ_EVENT); /* stop writing, keep reading */
if (connection_get_outbuf_len(conn)) /* in case there are any queued relay
* cells */
@@ -3652,6 +3693,14 @@ handle_hs_exit_conn(circuit_t *circ, edge_connection_t *conn)
hs_inc_rdv_stream_counter(origin_circ);
+ /* If it's an onion service connection, we might want to include the proxy
+ * protocol header: */
+ if (conn->hs_ident) {
+ hs_circuit_id_protocol_t circuit_id_protocol =
+ hs_service_exports_circuit_id(&conn->hs_ident->identity_pk);
+ export_hs_client_circuit_id(conn, circuit_id_protocol);
+ }
+
/* Connect tor to the hidden service destination. */
connection_exit_connect(conn);
diff --git a/src/core/or/connection_edge.h b/src/core/or/connection_edge.h
index 71ea95d6e5..6dee93f1f9 100644
--- a/src/core/or/connection_edge.h
+++ b/src/core/or/connection_edge.h
@@ -14,6 +14,8 @@
#include "lib/testsupport/testsupport.h"
+#include "feature/hs/hs_service.h"
+
edge_connection_t *TO_EDGE_CONN(connection_t *);
entry_connection_t *TO_ENTRY_CONN(connection_t *);
entry_connection_t *EDGE_TO_ENTRY_CONN(edge_connection_t *);
@@ -260,6 +262,10 @@ STATIC void connection_ap_handshake_rewrite(entry_connection_t *conn,
rewrite_result_t *out);
STATIC int connection_ap_process_http_connect(entry_connection_t *conn);
+STATIC void
+export_hs_client_circuit_id(edge_connection_t *edge_conn,
+ hs_circuit_id_protocol_t protocol);
+
#endif /* defined(CONNECTION_EDGE_PRIVATE) */
#endif /* !defined(TOR_CONNECTION_EDGE_H) */
diff --git a/src/core/or/connection_or.c b/src/core/or/connection_or.c
index e559e6b981..ca69fa00d4 100644
--- a/src/core/or/connection_or.c
+++ b/src/core/or/connection_or.c
@@ -41,10 +41,10 @@
#include "feature/control/control.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_util.h"
-#include "feature/dircache/dirserv.h"
+#include "feature/dirauth/reachability.h"
#include "feature/client/entrynodes.h"
#include "feature/stats/geoip.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "trunnel/link_handshake.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
@@ -56,6 +56,7 @@
#include "feature/stats/rephist.h"
#include "feature/relay/router.h"
#include "feature/relay/routerkeys.h"
+#include "feature/nodelist/dirlist.h"
#include "feature/nodelist/routerlist.h"
#include "feature/relay/ext_orport.h"
#include "core/or/scheduler.h"
diff --git a/src/core/or/dos.c b/src/core/or/dos.c
index 52879c34d7..a75c2070d8 100644
--- a/src/core/or/dos.c
+++ b/src/core/or/dos.c
@@ -15,7 +15,7 @@
#include "core/or/connection_or.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "feature/stats/geoip.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
#include "core/or/relay.h"
diff --git a/src/core/crypto/onion.c b/src/core/or/onion.c
index e71bfc1fd9..5c29441947 100644
--- a/src/core/crypto/onion.c
+++ b/src/core/or/onion.c
@@ -6,7 +6,7 @@
/**
* \file onion.c
- * \brief Functions to queue create cells, wrap the various onionskin types,
+ * \brief Functions to queue create cells,
* and parse and create the CREATE cell and its allies.
*
* This module has a few functions, all related to the CREATE/CREATED
@@ -14,27 +14,6 @@
* related EXTEND/EXTENDED handshake that we use over circuits in order to
* extend them an additional hop.
*
- * In this module, we provide a set of abstractions to create a uniform
- * interface over the three circuit extension handshakes that Tor has used
- * over the years (TAP, CREATE_FAST, and ntor). These handshakes are
- * implemented in onion_tap.c, onion_fast.c, and onion_ntor.c respectively.
- *
- * All[*] of these handshakes follow a similar pattern: a client, knowing
- * some key from the relay it wants to extend through, generates the
- * first part of a handshake. A relay receives that handshake, and sends
- * a reply. Once the client handles the reply, it knows that it is
- * talking to the right relay, and it shares some freshly negotiated key
- * material with that relay.
- *
- * We sometimes call the client's part of the handshake an "onionskin".
- * We do this because historically, Onion Routing used a multi-layer
- * structure called an "onion" to construct circuits. Each layer of the
- * onion contained key material chosen by the client, the identity of
- * the next relay in the circuit, and a smaller onion, encrypted with
- * the key of the next relay. When we changed Tor to use a telescoping
- * circuit extension design, it corresponded to sending each layer of the
- * onion separately -- as a series of onionskins.
- *
* Clients invoke these functions when creating or extending a circuit,
* from circuitbuild.c.
*
@@ -57,628 +36,23 @@
* <li>Encoding and decodign EXTEND, EXTENDED, EXTEND2, and EXTENDED2
* relay cells.
* </ul>
- *
- * [*] The CREATE_FAST handshake is weaker than described here; see
- * onion_fast.c for more information.
**/
#include "core/or/or.h"
-#include "core/or/circuitbuild.h"
-#include "core/or/circuitlist.h"
+
#include "app/config/config.h"
-#include "core/mainloop/cpuworker.h"
-#include "lib/crypt_ops/crypto_util.h"
-#include "lib/crypt_ops/crypto_dh.h"
-#include "feature/nodelist/networkstatus.h"
-#include "core/crypto/onion.h"
+#include "core/crypto/onion_crypto.h"
#include "core/crypto/onion_fast.h"
#include "core/crypto/onion_ntor.h"
#include "core/crypto/onion_tap.h"
-#include "core/or/relay.h"
-#include "feature/stats/rephist.h"
-#include "feature/relay/router.h"
+#include "core/or/onion.h"
+#include "feature/nodelist/networkstatus.h"
#include "core/or/cell_st.h"
-#include "core/or/extend_info_st.h"
-#include "core/or/or_circuit_st.h"
// trunnel
#include "trunnel/ed25519_cert.h"
-/** Type for a linked list of circuits that are waiting for a free CPU worker
- * to process a waiting onion handshake. */
-typedef struct onion_queue_t {
- TOR_TAILQ_ENTRY(onion_queue_t) next;
- or_circuit_t *circ;
- uint16_t handshake_type;
- create_cell_t *onionskin;
- time_t when_added;
-} onion_queue_t;
-
-/** 5 seconds on the onion queue til we just send back a destroy */
-#define ONIONQUEUE_WAIT_CUTOFF 5
-
-/** Array of queues of circuits waiting for CPU workers. An element is NULL
- * if that queue is empty.*/
-static TOR_TAILQ_HEAD(onion_queue_head_t, onion_queue_t)
- ol_list[MAX_ONION_HANDSHAKE_TYPE+1] =
-{ TOR_TAILQ_HEAD_INITIALIZER(ol_list[0]), /* tap */
- TOR_TAILQ_HEAD_INITIALIZER(ol_list[1]), /* fast */
- TOR_TAILQ_HEAD_INITIALIZER(ol_list[2]), /* ntor */
-};
-
-/** Number of entries of each type currently in each element of ol_list[]. */
-static int ol_entries[MAX_ONION_HANDSHAKE_TYPE+1];
-
-static int num_ntors_per_tap(void);
-static void onion_queue_entry_remove(onion_queue_t *victim);
-
-/* XXXX Check lengths vs MAX_ONIONSKIN_{CHALLENGE,REPLY}_LEN.
- *
- * (By which I think I meant, "make sure that no
- * X_ONIONSKIN_CHALLENGE/REPLY_LEN is greater than
- * MAX_ONIONSKIN_CHALLENGE/REPLY_LEN." Also, make sure that we can pass
- * over-large values via EXTEND2/EXTENDED2, for future-compatibility.*/
-
-/** Return true iff we have room to queue another onionskin of type
- * <b>type</b>. */
-static int
-have_room_for_onionskin(uint16_t type)
-{
- const or_options_t *options = get_options();
- int num_cpus;
- uint64_t tap_usec, ntor_usec;
- uint64_t ntor_during_tap_usec, tap_during_ntor_usec;
-
- /* If we've got fewer than 50 entries, we always have room for one more. */
- if (ol_entries[type] < 50)
- return 1;
- num_cpus = get_num_cpus(options);
- /* Compute how many microseconds we'd expect to need to clear all
- * onionskins in various combinations of the queues. */
-
- /* How long would it take to process all the TAP cells in the queue? */
- tap_usec = estimated_usec_for_onionskins(
- ol_entries[ONION_HANDSHAKE_TYPE_TAP],
- ONION_HANDSHAKE_TYPE_TAP) / num_cpus;
-
- /* How long would it take to process all the NTor cells in the queue? */
- ntor_usec = estimated_usec_for_onionskins(
- ol_entries[ONION_HANDSHAKE_TYPE_NTOR],
- ONION_HANDSHAKE_TYPE_NTOR) / num_cpus;
-
- /* How long would it take to process the tap cells that we expect to
- * process while draining the ntor queue? */
- tap_during_ntor_usec = estimated_usec_for_onionskins(
- MIN(ol_entries[ONION_HANDSHAKE_TYPE_TAP],
- ol_entries[ONION_HANDSHAKE_TYPE_NTOR] / num_ntors_per_tap()),
- ONION_HANDSHAKE_TYPE_TAP) / num_cpus;
-
- /* How long would it take to process the ntor cells that we expect to
- * process while draining the tap queue? */
- ntor_during_tap_usec = estimated_usec_for_onionskins(
- MIN(ol_entries[ONION_HANDSHAKE_TYPE_NTOR],
- ol_entries[ONION_HANDSHAKE_TYPE_TAP] * num_ntors_per_tap()),
- ONION_HANDSHAKE_TYPE_NTOR) / num_cpus;
-
- /* See whether that exceeds MaxOnionQueueDelay. If so, we can't queue
- * this. */
- if (type == ONION_HANDSHAKE_TYPE_NTOR &&
- (ntor_usec + tap_during_ntor_usec) / 1000 >
- (uint64_t)options->MaxOnionQueueDelay)
- return 0;
-
- if (type == ONION_HANDSHAKE_TYPE_TAP &&
- (tap_usec + ntor_during_tap_usec) / 1000 >
- (uint64_t)options->MaxOnionQueueDelay)
- return 0;
-
- /* If we support the ntor handshake, then don't let TAP handshakes use
- * more than 2/3 of the space on the queue. */
- if (type == ONION_HANDSHAKE_TYPE_TAP &&
- tap_usec / 1000 > (uint64_t)options->MaxOnionQueueDelay * 2 / 3)
- return 0;
-
- return 1;
-}
-
-/** Add <b>circ</b> to the end of ol_list and return 0, except
- * if ol_list is too long, in which case do nothing and return -1.
- */
-int
-onion_pending_add(or_circuit_t *circ, create_cell_t *onionskin)
-{
- onion_queue_t *tmp;
- time_t now = time(NULL);
-
- if (onionskin->handshake_type > MAX_ONION_HANDSHAKE_TYPE) {
- /* LCOV_EXCL_START
- * We should have rejected this far before this point */
- log_warn(LD_BUG, "Handshake %d out of range! Dropping.",
- onionskin->handshake_type);
- return -1;
- /* LCOV_EXCL_STOP */
- }
-
- tmp = tor_malloc_zero(sizeof(onion_queue_t));
- tmp->circ = circ;
- tmp->handshake_type = onionskin->handshake_type;
- tmp->onionskin = onionskin;
- tmp->when_added = now;
-
- if (!have_room_for_onionskin(onionskin->handshake_type)) {
-#define WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL (60)
- static ratelim_t last_warned =
- RATELIM_INIT(WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL);
- char *m;
- if (onionskin->handshake_type == ONION_HANDSHAKE_TYPE_NTOR &&
- (m = rate_limit_log(&last_warned, approx_time()))) {
- log_warn(LD_GENERAL,
- "Your computer is too slow to handle this many circuit "
- "creation requests! Please consider using the "
- "MaxAdvertisedBandwidth config option or choosing a more "
- "restricted exit policy.%s",m);
- tor_free(m);
- }
- tor_free(tmp);
- return -1;
- }
-
- ++ol_entries[onionskin->handshake_type];
- log_info(LD_OR, "New create (%s). Queues now ntor=%d and tap=%d.",
- onionskin->handshake_type == ONION_HANDSHAKE_TYPE_NTOR ? "ntor" : "tap",
- ol_entries[ONION_HANDSHAKE_TYPE_NTOR],
- ol_entries[ONION_HANDSHAKE_TYPE_TAP]);
-
- circ->onionqueue_entry = tmp;
- TOR_TAILQ_INSERT_TAIL(&ol_list[onionskin->handshake_type], tmp, next);
-
- /* cull elderly requests. */
- while (1) {
- onion_queue_t *head = TOR_TAILQ_FIRST(&ol_list[onionskin->handshake_type]);
- if (now - head->when_added < (time_t)ONIONQUEUE_WAIT_CUTOFF)
- break;
-
- circ = head->circ;
- circ->onionqueue_entry = NULL;
- onion_queue_entry_remove(head);
- log_info(LD_CIRC,
- "Circuit create request is too old; canceling due to overload.");
- if (! TO_CIRCUIT(circ)->marked_for_close) {
- circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT);
- }
- }
- return 0;
-}
-
-/** Return a fairness parameter, to prefer processing NTOR style
- * handshakes but still slowly drain the TAP queue so we don't starve
- * it entirely. */
-static int
-num_ntors_per_tap(void)
-{
-#define DEFAULT_NUM_NTORS_PER_TAP 10
-#define MIN_NUM_NTORS_PER_TAP 1
-#define MAX_NUM_NTORS_PER_TAP 100000
-
- return networkstatus_get_param(NULL, "NumNTorsPerTAP",
- DEFAULT_NUM_NTORS_PER_TAP,
- MIN_NUM_NTORS_PER_TAP,
- MAX_NUM_NTORS_PER_TAP);
-}
-
-/** Choose which onion queue we'll pull from next. If one is empty choose
- * the other; if they both have elements, load balance across them but
- * favoring NTOR. */
-static uint16_t
-decide_next_handshake_type(void)
-{
- /* The number of times we've chosen ntor lately when both were available. */
- static int recently_chosen_ntors = 0;
-
- if (!ol_entries[ONION_HANDSHAKE_TYPE_NTOR])
- return ONION_HANDSHAKE_TYPE_TAP; /* no ntors? try tap */
-
- if (!ol_entries[ONION_HANDSHAKE_TYPE_TAP]) {
-
- /* Nick wants us to prioritize new tap requests when there aren't
- * any in the queue and we've processed k ntor cells since the last
- * tap cell. This strategy is maybe a good idea, since it starves tap
- * less in the case where tap is rare, or maybe a poor idea, since it
- * makes the new tap cell unfairly jump in front of ntor cells that
- * got here first. In any case this edge case will only become relevant
- * once tap is rare. We should reevaluate whether we like this decision
- * once tap gets more rare. */
- if (ol_entries[ONION_HANDSHAKE_TYPE_NTOR] &&
- recently_chosen_ntors <= num_ntors_per_tap())
- ++recently_chosen_ntors;
-
- return ONION_HANDSHAKE_TYPE_NTOR; /* no taps? try ntor */
- }
-
- /* They both have something queued. Pick ntor if we haven't done that
- * too much lately. */
- if (++recently_chosen_ntors <= num_ntors_per_tap()) {
- return ONION_HANDSHAKE_TYPE_NTOR;
- }
-
- /* Else, it's time to let tap have its turn. */
- recently_chosen_ntors = 0;
- return ONION_HANDSHAKE_TYPE_TAP;
-}
-
-/** Remove the highest priority item from ol_list[] and return it, or
- * return NULL if the lists are empty.
- */
-or_circuit_t *
-onion_next_task(create_cell_t **onionskin_out)
-{
- or_circuit_t *circ;
- uint16_t handshake_to_choose = decide_next_handshake_type();
- onion_queue_t *head = TOR_TAILQ_FIRST(&ol_list[handshake_to_choose]);
-
- if (!head)
- return NULL; /* no onions pending, we're done */
-
- tor_assert(head->circ);
- tor_assert(head->handshake_type <= MAX_ONION_HANDSHAKE_TYPE);
-// tor_assert(head->circ->p_chan); /* make sure it's still valid */
-/* XXX I only commented out the above line to make the unit tests
- * more manageable. That's probably not good long-term. -RD */
- circ = head->circ;
- if (head->onionskin)
- --ol_entries[head->handshake_type];
- log_info(LD_OR, "Processing create (%s). Queues now ntor=%d and tap=%d.",
- head->handshake_type == ONION_HANDSHAKE_TYPE_NTOR ? "ntor" : "tap",
- ol_entries[ONION_HANDSHAKE_TYPE_NTOR],
- ol_entries[ONION_HANDSHAKE_TYPE_TAP]);
-
- *onionskin_out = head->onionskin;
- head->onionskin = NULL; /* prevent free. */
- circ->onionqueue_entry = NULL;
- onion_queue_entry_remove(head);
- return circ;
-}
-
-/** Return the number of <b>handshake_type</b>-style create requests pending.
- */
-int
-onion_num_pending(uint16_t handshake_type)
-{
- return ol_entries[handshake_type];
-}
-
-/** Go through ol_list, find the onion_queue_t element which points to
- * circ, remove and free that element. Leave circ itself alone.
- */
-void
-onion_pending_remove(or_circuit_t *circ)
-{
- onion_queue_t *victim;
-
- if (!circ)
- return;
-
- victim = circ->onionqueue_entry;
- if (victim)
- onion_queue_entry_remove(victim);
-
- cpuworker_cancel_circ_handshake(circ);
-}
-
-/** Remove a queue entry <b>victim</b> from the queue, unlinking it from
- * its circuit and freeing it and any structures it owns.*/
-static void
-onion_queue_entry_remove(onion_queue_t *victim)
-{
- if (victim->handshake_type > MAX_ONION_HANDSHAKE_TYPE) {
- /* LCOV_EXCL_START
- * We should have rejected this far before this point */
- log_warn(LD_BUG, "Handshake %d out of range! Dropping.",
- victim->handshake_type);
- /* XXX leaks */
- return;
- /* LCOV_EXCL_STOP */
- }
-
- TOR_TAILQ_REMOVE(&ol_list[victim->handshake_type], victim, next);
-
- if (victim->circ)
- victim->circ->onionqueue_entry = NULL;
-
- if (victim->onionskin)
- --ol_entries[victim->handshake_type];
-
- tor_free(victim->onionskin);
- tor_free(victim);
-}
-
-/** Remove all circuits from the pending list. Called from tor_free_all. */
-void
-clear_pending_onions(void)
-{
- onion_queue_t *victim, *next;
- int i;
- for (i=0; i<=MAX_ONION_HANDSHAKE_TYPE; i++) {
- for (victim = TOR_TAILQ_FIRST(&ol_list[i]); victim; victim = next) {
- next = TOR_TAILQ_NEXT(victim,next);
- onion_queue_entry_remove(victim);
- }
- tor_assert(TOR_TAILQ_EMPTY(&ol_list[i]));
- }
- memset(ol_entries, 0, sizeof(ol_entries));
-}
-
-/* ============================================================ */
-
-/** Return a new server_onion_keys_t object with all of the keys
- * and other info we might need to do onion handshakes. (We make a copy of
- * our keys for each cpuworker to avoid race conditions with the main thread,
- * and to avoid locking) */
-server_onion_keys_t *
-server_onion_keys_new(void)
-{
- server_onion_keys_t *keys = tor_malloc_zero(sizeof(server_onion_keys_t));
- memcpy(keys->my_identity, router_get_my_id_digest(), DIGEST_LEN);
- dup_onion_keys(&keys->onion_key, &keys->last_onion_key);
- keys->curve25519_key_map = construct_ntor_key_map();
- keys->junk_keypair = tor_malloc_zero(sizeof(curve25519_keypair_t));
- curve25519_keypair_generate(keys->junk_keypair, 0);
- return keys;
-}
-
-/** Release all storage held in <b>keys</b>. */
-void
-server_onion_keys_free_(server_onion_keys_t *keys)
-{
- if (! keys)
- return;
-
- crypto_pk_free(keys->onion_key);
- crypto_pk_free(keys->last_onion_key);
- ntor_key_map_free(keys->curve25519_key_map);
- tor_free(keys->junk_keypair);
- memwipe(keys, 0, sizeof(server_onion_keys_t));
- tor_free(keys);
-}
-
-/** Release whatever storage is held in <b>state</b>, depending on its
- * type, and clear its pointer. */
-void
-onion_handshake_state_release(onion_handshake_state_t *state)
-{
- switch (state->tag) {
- case ONION_HANDSHAKE_TYPE_TAP:
- crypto_dh_free(state->u.tap);
- state->u.tap = NULL;
- break;
- case ONION_HANDSHAKE_TYPE_FAST:
- fast_handshake_state_free(state->u.fast);
- state->u.fast = NULL;
- break;
- case ONION_HANDSHAKE_TYPE_NTOR:
- ntor_handshake_state_free(state->u.ntor);
- state->u.ntor = NULL;
- break;
- default:
- /* LCOV_EXCL_START
- * This state should not even exist. */
- log_warn(LD_BUG, "called with unknown handshake state type %d",
- (int)state->tag);
- tor_fragile_assert();
- /* LCOV_EXCL_STOP */
- }
-}
-
-/** Perform the first step of a circuit-creation handshake of type <b>type</b>
- * (one of ONION_HANDSHAKE_TYPE_*): generate the initial "onion skin" in
- * <b>onion_skin_out</b>, and store any state information in <b>state_out</b>.
- * Return -1 on failure, and the length of the onionskin on acceptance.
- */
-int
-onion_skin_create(int type,
- const extend_info_t *node,
- onion_handshake_state_t *state_out,
- uint8_t *onion_skin_out)
-{
- int r = -1;
-
- switch (type) {
- case ONION_HANDSHAKE_TYPE_TAP:
- if (!node->onion_key)
- return -1;
-
- if (onion_skin_TAP_create(node->onion_key,
- &state_out->u.tap,
- (char*)onion_skin_out) < 0)
- return -1;
-
- r = TAP_ONIONSKIN_CHALLENGE_LEN;
- break;
- case ONION_HANDSHAKE_TYPE_FAST:
- if (fast_onionskin_create(&state_out->u.fast, onion_skin_out) < 0)
- return -1;
-
- r = CREATE_FAST_LEN;
- break;
- case ONION_HANDSHAKE_TYPE_NTOR:
- if (!extend_info_supports_ntor(node))
- return -1;
- if (onion_skin_ntor_create((const uint8_t*)node->identity_digest,
- &node->curve25519_onion_key,
- &state_out->u.ntor,
- onion_skin_out) < 0)
- return -1;
-
- r = NTOR_ONIONSKIN_LEN;
- break;
- default:
- /* LCOV_EXCL_START
- * We should never try to create an impossible handshake type. */
- log_warn(LD_BUG, "called with unknown handshake state type %d", type);
- tor_fragile_assert();
- r = -1;
- /* LCOV_EXCL_STOP */
- }
-
- if (r > 0)
- state_out->tag = (uint16_t) type;
-
- return r;
-}
-
-/* This is the maximum value for keys_out_len passed to
- * onion_skin_server_handshake, plus 16. We can make it bigger if needed:
- * It just defines how many bytes to stack-allocate. */
-#define MAX_KEYS_TMP_LEN 128
-
-/** Perform the second (server-side) step of a circuit-creation handshake of
- * type <b>type</b>, responding to the client request in <b>onion_skin</b>
- * using the keys in <b>keys</b>. On success, write our response into
- * <b>reply_out</b>, generate <b>keys_out_len</b> bytes worth of key material
- * in <b>keys_out_len</b>, a hidden service nonce to <b>rend_nonce_out</b>,
- * and return the length of the reply. On failure, return -1.
- */
-int
-onion_skin_server_handshake(int type,
- const uint8_t *onion_skin, size_t onionskin_len,
- const server_onion_keys_t *keys,
- uint8_t *reply_out,
- uint8_t *keys_out, size_t keys_out_len,
- uint8_t *rend_nonce_out)
-{
- int r = -1;
-
- switch (type) {
- case ONION_HANDSHAKE_TYPE_TAP:
- if (onionskin_len != TAP_ONIONSKIN_CHALLENGE_LEN)
- return -1;
- if (onion_skin_TAP_server_handshake((const char*)onion_skin,
- keys->onion_key, keys->last_onion_key,
- (char*)reply_out,
- (char*)keys_out, keys_out_len)<0)
- return -1;
- r = TAP_ONIONSKIN_REPLY_LEN;
- memcpy(rend_nonce_out, reply_out+DH1024_KEY_LEN, DIGEST_LEN);
- break;
- case ONION_HANDSHAKE_TYPE_FAST:
- if (onionskin_len != CREATE_FAST_LEN)
- return -1;
- if (fast_server_handshake(onion_skin, reply_out, keys_out, keys_out_len)<0)
- return -1;
- r = CREATED_FAST_LEN;
- memcpy(rend_nonce_out, reply_out+DIGEST_LEN, DIGEST_LEN);
- break;
- case ONION_HANDSHAKE_TYPE_NTOR:
- if (onionskin_len < NTOR_ONIONSKIN_LEN)
- return -1;
- {
- size_t keys_tmp_len = keys_out_len + DIGEST_LEN;
- tor_assert(keys_tmp_len <= MAX_KEYS_TMP_LEN);
- uint8_t keys_tmp[MAX_KEYS_TMP_LEN];
-
- if (onion_skin_ntor_server_handshake(
- onion_skin, keys->curve25519_key_map,
- keys->junk_keypair,
- keys->my_identity,
- reply_out, keys_tmp, keys_tmp_len)<0) {
- /* no need to memwipe here, since the output will never be used */
- return -1;
- }
-
- memcpy(keys_out, keys_tmp, keys_out_len);
- memcpy(rend_nonce_out, keys_tmp+keys_out_len, DIGEST_LEN);
- memwipe(keys_tmp, 0, sizeof(keys_tmp));
- r = NTOR_REPLY_LEN;
- }
- break;
- default:
- /* LCOV_EXCL_START
- * We should have rejected this far before this point */
- log_warn(LD_BUG, "called with unknown handshake state type %d", type);
- tor_fragile_assert();
- return -1;
- /* LCOV_EXCL_STOP */
- }
-
- return r;
-}
-
-/** Perform the final (client-side) step of a circuit-creation handshake of
- * type <b>type</b>, using our state in <b>handshake_state</b> and the
- * server's response in <b>reply</b>. On success, generate <b>keys_out_len</b>
- * bytes worth of key material in <b>keys_out_len</b>, set
- * <b>rend_authenticator_out</b> to the "KH" field that can be used to
- * establish introduction points at this hop, and return 0. On failure,
- * return -1, and set *msg_out to an error message if this is worth
- * complaining to the user about. */
-int
-onion_skin_client_handshake(int type,
- const onion_handshake_state_t *handshake_state,
- const uint8_t *reply, size_t reply_len,
- uint8_t *keys_out, size_t keys_out_len,
- uint8_t *rend_authenticator_out,
- const char **msg_out)
-{
- if (handshake_state->tag != type)
- return -1;
-
- switch (type) {
- case ONION_HANDSHAKE_TYPE_TAP:
- if (reply_len != TAP_ONIONSKIN_REPLY_LEN) {
- if (msg_out)
- *msg_out = "TAP reply was not of the correct length.";
- return -1;
- }
- if (onion_skin_TAP_client_handshake(handshake_state->u.tap,
- (const char*)reply,
- (char *)keys_out, keys_out_len,
- msg_out) < 0)
- return -1;
-
- memcpy(rend_authenticator_out, reply+DH1024_KEY_LEN, DIGEST_LEN);
-
- return 0;
- case ONION_HANDSHAKE_TYPE_FAST:
- if (reply_len != CREATED_FAST_LEN) {
- if (msg_out)
- *msg_out = "TAP reply was not of the correct length.";
- return -1;
- }
- if (fast_client_handshake(handshake_state->u.fast, reply,
- keys_out, keys_out_len, msg_out) < 0)
- return -1;
-
- memcpy(rend_authenticator_out, reply+DIGEST_LEN, DIGEST_LEN);
- return 0;
- case ONION_HANDSHAKE_TYPE_NTOR:
- if (reply_len < NTOR_REPLY_LEN) {
- if (msg_out)
- *msg_out = "ntor reply was not of the correct length.";
- return -1;
- }
- {
- size_t keys_tmp_len = keys_out_len + DIGEST_LEN;
- uint8_t *keys_tmp = tor_malloc(keys_tmp_len);
- if (onion_skin_ntor_client_handshake(handshake_state->u.ntor,
- reply,
- keys_tmp, keys_tmp_len, msg_out) < 0) {
- tor_free(keys_tmp);
- return -1;
- }
- memcpy(keys_out, keys_tmp, keys_out_len);
- memcpy(rend_authenticator_out, keys_tmp + keys_out_len, DIGEST_LEN);
- memwipe(keys_tmp, 0, keys_tmp_len);
- tor_free(keys_tmp);
- }
- return 0;
- default:
- log_warn(LD_BUG, "called with unknown handshake state type %d", type);
- tor_fragile_assert();
- return -1;
- }
-}
-
/** Helper: return 0 if <b>cell</b> appears valid, -1 otherwise. If
* <b>unknown_ok</b> is true, allow cells with handshake types we don't
* recognize. */
diff --git a/src/core/crypto/onion.h b/src/core/or/onion.h
index ff70f299d5..2049fdf419 100644
--- a/src/core/crypto/onion.h
+++ b/src/core/or/onion.h
@@ -17,47 +17,9 @@ struct curve25519_keypair_t;
struct curve25519_public_key_t;
#include "lib/crypt_ops/crypto_ed25519.h"
-int onion_pending_add(or_circuit_t *circ, struct create_cell_t *onionskin);
-or_circuit_t *onion_next_task(struct create_cell_t **onionskin_out);
-int onion_num_pending(uint16_t handshake_type);
-void onion_pending_remove(or_circuit_t *circ);
-void clear_pending_onions(void);
-
-typedef struct server_onion_keys_t {
- uint8_t my_identity[DIGEST_LEN];
- crypto_pk_t *onion_key;
- crypto_pk_t *last_onion_key;
- struct di_digest256_map_t *curve25519_key_map;
- struct curve25519_keypair_t *junk_keypair;
-} server_onion_keys_t;
-
#define MAX_ONIONSKIN_CHALLENGE_LEN 255
#define MAX_ONIONSKIN_REPLY_LEN 255
-server_onion_keys_t *server_onion_keys_new(void);
-void server_onion_keys_free_(server_onion_keys_t *keys);
-#define server_onion_keys_free(keys) \
- FREE_AND_NULL(server_onion_keys_t, server_onion_keys_free_, (keys))
-
-void onion_handshake_state_release(onion_handshake_state_t *state);
-
-int onion_skin_create(int type,
- const extend_info_t *node,
- onion_handshake_state_t *state_out,
- uint8_t *onion_skin_out);
-int onion_skin_server_handshake(int type,
- const uint8_t *onion_skin, size_t onionskin_len,
- const server_onion_keys_t *keys,
- uint8_t *reply_out,
- uint8_t *keys_out, size_t key_out_len,
- uint8_t *rend_nonce_out);
-int onion_skin_client_handshake(int type,
- const onion_handshake_state_t *handshake_state,
- const uint8_t *reply, size_t reply_len,
- uint8_t *keys_out, size_t key_out_len,
- uint8_t *rend_authenticator_out,
- const char **msg_out);
-
/** A parsed CREATE, CREATE_FAST, or CREATE2 cell. */
typedef struct create_cell_t {
/** The cell command. One of CREATE{,_FAST,2} */
diff --git a/src/core/or/or.h b/src/core/or/or.h
index 4d8b6d7871..7557c1321e 100644
--- a/src/core/or/or.h
+++ b/src/core/or/or.h
@@ -26,6 +26,7 @@
#include "lib/cc/compat_compiler.h"
#include "lib/cc/torint.h"
#include "lib/container/map.h"
+#include "lib/container/buffers.h"
#include "lib/container/smartlist.h"
#include "lib/crypt_ops/crypto_cipher.h"
#include "lib/crypt_ops/crypto_rsa.h"
diff --git a/src/core/or/policies.c b/src/core/or/policies.c
index 3cb5e53599..3443a17107 100644
--- a/src/core/or/policies.c
+++ b/src/core/or/policies.c
@@ -20,7 +20,6 @@
#include "core/or/or.h"
#include "feature/client/bridges.h"
#include "app/config/config.h"
-#include "feature/dircache/dirserv.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
@@ -39,6 +38,9 @@
#include "feature/nodelist/routerinfo_st.h"
#include "feature/nodelist/routerstatus_st.h"
+/** Maximum length of an exit policy summary. */
+#define MAX_EXITPOLICY_SUMMARY_LEN 1000
+
/** Policy that addresses for incoming SOCKS connections must match. */
static smartlist_t *socks_policy = NULL;
/** Policy that addresses for incoming directory connections must match. */
diff --git a/src/core/or/protover.c b/src/core/or/protover.c
index 0f6f0e79ff..f0791366fe 100644
--- a/src/core/or/protover.c
+++ b/src/core/or/protover.c
@@ -179,6 +179,16 @@ parse_version_range(const char *s, const char *end_of_range,
return -1;
}
+static int
+is_valid_keyword(const char *s, size_t n)
+{
+ for (size_t i = 0; i < n; i++) {
+ if (!TOR_ISALNUM(s[i]) && s[i] != '-')
+ return 0;
+ }
+ return 1;
+}
+
/** Parse a single protocol entry from <b>s</b> up to an optional
* <b>end_of_entry</b> pointer, and return that protocol entry. Return NULL
* on error.
@@ -213,6 +223,11 @@ parse_single_entry(const char *s, const char *end_of_entry)
MAX_PROTOCOL_NAME_LENGTH, escaped(out->name));
goto error;
}
+
+ /* The name must contain only alphanumeric characters and hyphens. */
+ if (!is_valid_keyword(s, equals-s))
+ goto error;
+
out->name = tor_strndup(s, equals-s);
tor_assert(equals < end_of_entry);
diff --git a/src/core/or/reasons.c b/src/core/or/reasons.c
index 7a1e09e9f8..610338ee1d 100644
--- a/src/core/or/reasons.c
+++ b/src/core/or/reasons.c
@@ -17,7 +17,7 @@
#include "core/or/or.h"
#include "app/config/config.h"
#include "core/or/reasons.h"
-#include "feature/nodelist/routerlist.h"
+#include "feature/nodelist/node_select.h"
#include "lib/tls/tortls.h"
/***************************** Edge (stream) reasons **********************/
diff --git a/src/core/or/relay.c b/src/core/or/relay.c
index 2c77abbeec..4638d9f212 100644
--- a/src/core/or/relay.c
+++ b/src/core/or/relay.c
@@ -67,10 +67,10 @@
#include "feature/relay/dns.h"
#include "feature/stats/geoip.h"
#include "feature/hs/hs_cache.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
-#include "core/crypto/onion.h"
+#include "core/or/onion.h"
#include "core/or/policies.h"
#include "core/or/reasons.h"
#include "core/or/relay.h"
diff --git a/src/core/or/scheduler.c b/src/core/or/scheduler.c
index dd028fc785..326e0d65d9 100644
--- a/src/core/or/scheduler.c
+++ b/src/core/or/scheduler.c
@@ -8,7 +8,7 @@
#define SCHEDULER_PRIVATE_
#define SCHEDULER_KIST_PRIVATE
#include "core/or/scheduler.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "lib/container/buffers.h"
#define TOR_CHANNEL_INTERNAL_
#include "core/or/channeltls.h"
diff --git a/src/core/or/status.c b/src/core/or/status.c
index 30a65b1d4c..45b8217d9a 100644
--- a/src/core/or/status.c
+++ b/src/core/or/status.c
@@ -22,7 +22,7 @@
#include "core/or/relay.h"
#include "feature/relay/router.h"
#include "core/or/circuitlist.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/stats/rephist.h"
#include "feature/hibernate/hibernate.h"
#include "app/config/statefile.h"
diff --git a/src/feature/client/bridges.c b/src/feature/client/bridges.c
index 7f4422f787..5ab0f6f4f5 100644
--- a/src/feature/client/bridges.c
+++ b/src/feature/client/bridges.c
@@ -23,7 +23,9 @@
#include "feature/nodelist/nodelist.h"
#include "core/or/policies.h"
#include "feature/relay/router.h"
+#include "feature/nodelist/dirlist.h"
#include "feature/nodelist/routerlist.h"
+
#include "feature/nodelist/routerset.h"
#include "feature/client/transports.h"
diff --git a/src/feature/client/dnsserv.c b/src/feature/client/dnsserv.c
index 31cc3eff79..ea4951f915 100644
--- a/src/feature/client/dnsserv.c
+++ b/src/feature/client/dnsserv.c
@@ -27,7 +27,7 @@
#include "core/mainloop/connection.h"
#include "core/or/connection_edge.h"
#include "feature/control/control.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "core/or/policies.h"
#include "feature/control/control_connection_st.h"
diff --git a/src/feature/client/entrynodes.c b/src/feature/client/entrynodes.c
index 494ad33528..94bb011fb9 100644
--- a/src/feature/client/entrynodes.c
+++ b/src/feature/client/entrynodes.c
@@ -127,13 +127,13 @@
#include "lib/crypt_ops/crypto_rand.h"
#include "feature/dircache/directory.h"
#include "feature/client/entrynodes.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
#include "core/or/policies.h"
#include "feature/relay/router.h"
-#include "feature/nodelist/routerlist.h"
+#include "feature/nodelist/node_select.h"
#include "feature/nodelist/routerparse.h"
#include "feature/nodelist/routerset.h"
#include "feature/client/transports.h"
diff --git a/src/feature/control/control.c b/src/feature/control/control.c
index 5ac7f6d995..b5a0dfeafe 100644
--- a/src/feature/control/control.c
+++ b/src/feature/control/control.c
@@ -53,6 +53,7 @@
#include "core/or/connection_edge.h"
#include "core/or/connection_or.h"
#include "feature/control/control.h"
+#include "feature/control/fmt_serverstatus.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_util.h"
#include "feature/dircache/directory.h"
@@ -64,7 +65,8 @@
#include "feature/hs/hs_cache.h"
#include "feature/hs/hs_common.h"
#include "feature/hs/hs_control.h"
-#include "core/mainloop/main.h"
+#include "app/main/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
@@ -77,6 +79,8 @@
#include "feature/rend/rendservice.h"
#include "feature/stats/rephist.h"
#include "feature/relay/router.h"
+#include "feature/nodelist/authcert.h"
+#include "feature/nodelist/dirlist.h"
#include "feature/nodelist/routerlist.h"
#include "feature/nodelist/routerparse.h"
#include "feature/hs_common/shared_random_client.h"
diff --git a/src/feature/control/fmt_serverstatus.c b/src/feature/control/fmt_serverstatus.c
new file mode 100644
index 0000000000..f150832ce3
--- /dev/null
+++ b/src/feature/control/fmt_serverstatus.c
@@ -0,0 +1,103 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "core/or/or.h"
+#include "feature/control/fmt_serverstatus.h"
+
+#include "app/config/config.h"
+#include "feature/dirauth/voteflags.h"// XXXX remove
+#include "feature/nodelist/nodelist.h"
+#include "feature/relay/router.h"
+
+#include "feature/nodelist/node_st.h"
+#include "feature/nodelist/routerinfo_st.h"
+
+/**
+ * Allocate and return a description of the status of the server <b>desc</b>,
+ * for use in a v1-style router-status line. The server is listed
+ * as running iff <b>is_live</b> is true.
+ *
+ * This is deprecated: it's only used for controllers that want outputs in
+ * the old format.
+ */
+static char *
+list_single_server_status(const routerinfo_t *desc, int is_live)
+{
+ char buf[MAX_NICKNAME_LEN+HEX_DIGEST_LEN+4]; /* !nickname=$hexdigest\0 */
+ char *cp;
+ const node_t *node;
+
+ tor_assert(desc);
+
+ cp = buf;
+ if (!is_live) {
+ *cp++ = '!';
+ }
+ node = node_get_by_id(desc->cache_info.identity_digest);
+ if (node && node->is_valid) {
+ strlcpy(cp, desc->nickname, sizeof(buf)-(cp-buf));
+ cp += strlen(cp);
+ *cp++ = '=';
+ }
+ *cp++ = '$';
+ base16_encode(cp, HEX_DIGEST_LEN+1, desc->cache_info.identity_digest,
+ DIGEST_LEN);
+ return tor_strdup(buf);
+}
+
+/** Based on the routerinfo_ts in <b>routers</b>, allocate the
+ * contents of a v1-style router-status line, and store it in
+ * *<b>router_status_out</b>. Return 0 on success, -1 on failure.
+ *
+ * If for_controller is true, include the routers with very old descriptors.
+ *
+ * This is deprecated: it's only used for controllers that want outputs in
+ * the old format.
+ */
+int
+list_server_status_v1(smartlist_t *routers, char **router_status_out,
+ int for_controller)
+{
+ /* List of entries in a router-status style: An optional !, then an optional
+ * equals-suffixed nickname, then a dollar-prefixed hexdigest. */
+ smartlist_t *rs_entries;
+ time_t now = time(NULL);
+ time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH;
+ const or_options_t *options = get_options();
+ /* We include v2 dir auths here too, because they need to answer
+ * controllers. Eventually we'll deprecate this whole function;
+ * see also networkstatus_getinfo_by_purpose(). */
+ int authdir = authdir_mode_publishes_statuses(options);
+ tor_assert(router_status_out);
+
+ rs_entries = smartlist_new();
+
+ SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) {
+ const node_t *node = node_get_by_id(ri->cache_info.identity_digest);
+ tor_assert(node);
+ if (authdir) {
+ /* Update router status in routerinfo_t. */
+ dirserv_set_router_is_running(ri, now);
+ }
+ if (for_controller) {
+ char name_buf[MAX_VERBOSE_NICKNAME_LEN+2];
+ char *cp = name_buf;
+ if (!node->is_running)
+ *cp++ = '!';
+ router_get_verbose_nickname(cp, ri);
+ smartlist_add_strdup(rs_entries, name_buf);
+ } else if (ri->cache_info.published_on >= cutoff) {
+ smartlist_add(rs_entries, list_single_server_status(ri,
+ node->is_running));
+ }
+ } SMARTLIST_FOREACH_END(ri);
+
+ *router_status_out = smartlist_join_strings(rs_entries, " ", 0, NULL);
+
+ SMARTLIST_FOREACH(rs_entries, char *, cp, tor_free(cp));
+ smartlist_free(rs_entries);
+
+ return 0;
+}
diff --git a/src/feature/control/fmt_serverstatus.h b/src/feature/control/fmt_serverstatus.h
new file mode 100644
index 0000000000..2ae9c1778a
--- /dev/null
+++ b/src/feature/control/fmt_serverstatus.h
@@ -0,0 +1,18 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file fmt_serverstatus.h
+ * \brief Header file for fmt_serverstatus.c.
+ **/
+
+#ifndef TOR_FMT_SERVERSTATUS_H
+#define TOR_FMT_SERVERSTATUS_H
+
+int list_server_status_v1(smartlist_t *routers, char **router_status_out,
+ int for_controller);
+
+#endif
diff --git a/src/feature/dirauth/bwauth.c b/src/feature/dirauth/bwauth.c
new file mode 100644
index 0000000000..90497a3b7c
--- /dev/null
+++ b/src/feature/dirauth/bwauth.c
@@ -0,0 +1,453 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file bwauth.c
+ * \brief Code to read and apply bandwidth authority data.
+ **/
+
+#define BWAUTH_PRIVATE
+#include "core/or/or.h"
+#include "feature/dirauth/bwauth.h"
+
+#include "app/config/config.h"
+#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/routerlist.h"
+#include "feature/nodelist/routerparse.h"
+
+#include "feature/nodelist/routerinfo_st.h"
+#include "feature/nodelist/vote_routerstatus_st.h"
+
+#include "lib/encoding/keyval.h"
+
+/** Total number of routers with measured bandwidth; this is set by
+ * dirserv_count_measured_bs() before the loop in
+ * dirserv_generate_networkstatus_vote_obj() and checked by
+ * dirserv_get_credible_bandwidth() and
+ * dirserv_compute_performance_thresholds() */
+static int routers_with_measured_bw = 0;
+
+/** Look through the routerlist, and using the measured bandwidth cache count
+ * how many measured bandwidths we know. This is used to decide whether we
+ * ever trust advertised bandwidths for purposes of assigning flags. */
+void
+dirserv_count_measured_bws(const smartlist_t *routers)
+{
+ /* Initialize this first */
+ routers_with_measured_bw = 0;
+
+ /* Iterate over the routerlist and count measured bandwidths */
+ SMARTLIST_FOREACH_BEGIN(routers, const routerinfo_t *, ri) {
+ /* Check if we know a measured bandwidth for this one */
+ if (dirserv_has_measured_bw(ri->cache_info.identity_digest)) {
+ ++routers_with_measured_bw;
+ }
+ } SMARTLIST_FOREACH_END(ri);
+}
+
+/** Return the last-computed result from dirserv_count_mesured_bws(). */
+int
+dirserv_get_last_n_measured_bws(void)
+{
+ return routers_with_measured_bw;
+}
+
+/** Measured bandwidth cache entry */
+typedef struct mbw_cache_entry_s {
+ long mbw_kb;
+ time_t as_of;
+} mbw_cache_entry_t;
+
+/** Measured bandwidth cache - keys are identity_digests, values are
+ * mbw_cache_entry_t *. */
+static digestmap_t *mbw_cache = NULL;
+
+/** Store a measured bandwidth cache entry when reading the measured
+ * bandwidths file. */
+STATIC void
+dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line,
+ time_t as_of)
+{
+ mbw_cache_entry_t *e = NULL;
+
+ tor_assert(parsed_line);
+
+ /* Allocate a cache if we need */
+ if (!mbw_cache) mbw_cache = digestmap_new();
+
+ /* Check if we have an existing entry */
+ e = digestmap_get(mbw_cache, parsed_line->node_id);
+ /* If we do, we can re-use it */
+ if (e) {
+ /* Check that we really are newer, and update */
+ if (as_of > e->as_of) {
+ e->mbw_kb = parsed_line->bw_kb;
+ e->as_of = as_of;
+ }
+ } else {
+ /* We'll have to insert a new entry */
+ e = tor_malloc(sizeof(*e));
+ e->mbw_kb = parsed_line->bw_kb;
+ e->as_of = as_of;
+ digestmap_set(mbw_cache, parsed_line->node_id, e);
+ }
+}
+
+/** Clear and free the measured bandwidth cache */
+void
+dirserv_clear_measured_bw_cache(void)
+{
+ if (mbw_cache) {
+ /* Free the map and all entries */
+ digestmap_free(mbw_cache, tor_free_);
+ mbw_cache = NULL;
+ }
+}
+
+/** Scan the measured bandwidth cache and remove expired entries */
+STATIC void
+dirserv_expire_measured_bw_cache(time_t now)
+{
+
+ if (mbw_cache) {
+ /* Iterate through the cache and check each entry */
+ DIGESTMAP_FOREACH_MODIFY(mbw_cache, k, mbw_cache_entry_t *, e) {
+ if (now > e->as_of + MAX_MEASUREMENT_AGE) {
+ tor_free(e);
+ MAP_DEL_CURRENT(k);
+ }
+ } DIGESTMAP_FOREACH_END;
+
+ /* Check if we cleared the whole thing and free if so */
+ if (digestmap_size(mbw_cache) == 0) {
+ digestmap_free(mbw_cache, tor_free_);
+ mbw_cache = 0;
+ }
+ }
+}
+
+/** Query the cache by identity digest, return value indicates whether
+ * we found it. The bw_out and as_of_out pointers receive the cached
+ * bandwidth value and the time it was cached if not NULL. */
+int
+dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_kb_out,
+ time_t *as_of_out)
+{
+ mbw_cache_entry_t *v = NULL;
+ int rv = 0;
+
+ if (mbw_cache && node_id) {
+ v = digestmap_get(mbw_cache, node_id);
+ if (v) {
+ /* Found something */
+ rv = 1;
+ if (bw_kb_out) *bw_kb_out = v->mbw_kb;
+ if (as_of_out) *as_of_out = v->as_of;
+ }
+ }
+
+ return rv;
+}
+
+/** Predicate wrapper for dirserv_query_measured_bw_cache() */
+int
+dirserv_has_measured_bw(const char *node_id)
+{
+ return dirserv_query_measured_bw_cache_kb(node_id, NULL, NULL);
+}
+
+/** Get the current size of the measured bandwidth cache */
+int
+dirserv_get_measured_bw_cache_size(void)
+{
+ if (mbw_cache) return digestmap_size(mbw_cache);
+ else return 0;
+}
+
+/** Return the bandwidth we believe for assigning flags; prefer measured
+ * over advertised, and if we have above a threshold quantity of measured
+ * bandwidths, we don't want to ever give flags to unmeasured routers, so
+ * return 0. */
+uint32_t
+dirserv_get_credible_bandwidth_kb(const routerinfo_t *ri)
+{
+ int threshold;
+ uint32_t bw_kb = 0;
+ long mbw_kb;
+
+ tor_assert(ri);
+ /* Check if we have a measured bandwidth, and check the threshold if not */
+ if (!(dirserv_query_measured_bw_cache_kb(ri->cache_info.identity_digest,
+ &mbw_kb, NULL))) {
+ threshold = get_options()->MinMeasuredBWsForAuthToIgnoreAdvertised;
+ if (routers_with_measured_bw > threshold) {
+ /* Return zero for unmeasured bandwidth if we are above threshold */
+ bw_kb = 0;
+ } else {
+ /* Return an advertised bandwidth otherwise */
+ bw_kb = router_get_advertised_bandwidth_capped(ri) / 1000;
+ }
+ } else {
+ /* We have the measured bandwidth in mbw */
+ bw_kb = (uint32_t)mbw_kb;
+ }
+
+ return bw_kb;
+}
+
+/**
+ * Read the measured bandwidth list file, apply it to the list of
+ * vote_routerstatus_t and store all the headers in <b>bw_file_headers</b>.
+ * Returns -1 on error, 0 otherwise.
+ */
+int
+dirserv_read_measured_bandwidths(const char *from_file,
+ smartlist_t *routerstatuses,
+ smartlist_t *bw_file_headers)
+{
+ FILE *fp = tor_fopen_cloexec(from_file, "r");
+ int applied_lines = 0;
+ time_t file_time, now;
+ int ok;
+ /* This flag will be 1 only when the first successful bw measurement line
+ * has been encountered, so that measured_bw_line_parse don't give warnings
+ * if there are additional header lines, as introduced in Bandwidth List spec
+ * version 1.1.0 */
+ int line_is_after_headers = 0;
+ int rv = -1;
+ char *line = NULL;
+ size_t n = 0;
+
+ /* Initialise line, so that we can't possibly run off the end. */
+
+ if (fp == NULL) {
+ log_warn(LD_CONFIG, "Can't open bandwidth file at configured location: %s",
+ from_file);
+ goto err;
+ }
+
+ /* If fgets fails, line is either unmodified, or indeterminate. */
+ if (tor_getline(&line,&n,fp) <= 0) {
+ log_warn(LD_DIRSERV, "Empty bandwidth file");
+ goto err;
+ }
+
+ if (!strlen(line) || line[strlen(line)-1] != '\n') {
+ log_warn(LD_DIRSERV, "Long or truncated time in bandwidth file: %s",
+ escaped(line));
+ goto err;
+ }
+
+ line[strlen(line)-1] = '\0';
+ file_time = (time_t)tor_parse_ulong(line, 10, 0, ULONG_MAX, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_DIRSERV, "Non-integer time in bandwidth file: %s",
+ escaped(line));
+ goto err;
+ }
+
+ now = time(NULL);
+ if ((now - file_time) > MAX_MEASUREMENT_AGE) {
+ log_warn(LD_DIRSERV, "Bandwidth measurement file stale. Age: %u",
+ (unsigned)(time(NULL) - file_time));
+ goto err;
+ }
+
+ /* If timestamp was correct and bw_file_headers is not NULL,
+ * add timestamp to bw_file_headers */
+ if (bw_file_headers)
+ smartlist_add_asprintf(bw_file_headers, "timestamp=%lu",
+ (unsigned long)file_time);
+
+ if (routerstatuses)
+ smartlist_sort(routerstatuses, compare_vote_routerstatus_entries);
+
+ while (!feof(fp)) {
+ measured_bw_line_t parsed_line;
+ if (tor_getline(&line, &n, fp) >= 0) {
+ if (measured_bw_line_parse(&parsed_line, line,
+ line_is_after_headers) != -1) {
+ /* This condition will be true when the first complete valid bw line
+ * has been encountered, which means the end of the header lines. */
+ line_is_after_headers = 1;
+ /* Also cache the line for dirserv_get_bandwidth_for_router() */
+ dirserv_cache_measured_bw(&parsed_line, file_time);
+ if (measured_bw_line_apply(&parsed_line, routerstatuses) > 0)
+ applied_lines++;
+ /* if the terminator is found, it is the end of header lines, set the
+ * flag but do not store anything */
+ } else if (strcmp(line, BW_FILE_HEADERS_TERMINATOR) == 0) {
+ line_is_after_headers = 1;
+ /* if the line was not a correct relay line nor the terminator and
+ * the end of the header lines has not been detected yet
+ * and it is key_value and bw_file_headers did not reach the maximum
+ * number of headers,
+ * then assume this line is a header and add it to bw_file_headers */
+ } else if (bw_file_headers &&
+ (line_is_after_headers == 0) &&
+ string_is_key_value(LOG_DEBUG, line) &&
+ !strchr(line, ' ') &&
+ (smartlist_len(bw_file_headers)
+ < MAX_BW_FILE_HEADER_COUNT_IN_VOTE)) {
+ line[strlen(line)-1] = '\0';
+ smartlist_add_strdup(bw_file_headers, line);
+ };
+ }
+ }
+
+ /* Now would be a nice time to clean the cache, too */
+ dirserv_expire_measured_bw_cache(now);
+
+ log_info(LD_DIRSERV,
+ "Bandwidth measurement file successfully read. "
+ "Applied %d measurements.", applied_lines);
+ rv = 0;
+
+ err:
+ if (line) {
+ // we need to raw_free this buffer because we got it from tor_getdelim()
+ raw_free(line);
+ }
+ if (fp)
+ fclose(fp);
+ return rv;
+}
+
+/**
+ * Helper function to parse out a line in the measured bandwidth file
+ * into a measured_bw_line_t output structure.
+ *
+ * If <b>line_is_after_headers</b> is true, then if we encounter an incomplete
+ * bw line, return -1 and warn, since we are after the headers and we should
+ * only parse bw lines. Return 0 otherwise.
+ *
+ * If <b>line_is_after_headers</b> is false then it means that we are not past
+ * the header block yet. If we encounter an incomplete bw line, return -1 but
+ * don't warn since there could be additional header lines coming. If we
+ * encounter a proper bw line, return 0 (and we got past the headers).
+ */
+STATIC int
+measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line,
+ int line_is_after_headers)
+{
+ char *line = tor_strdup(orig_line);
+ char *cp = line;
+ int got_bw = 0;
+ int got_node_id = 0;
+ char *strtok_state; /* lame sauce d'jour */
+
+ if (strlen(line) == 0) {
+ log_warn(LD_DIRSERV, "Empty line in bandwidth file");
+ tor_free(line);
+ return -1;
+ }
+
+ /* Remove end of line character, so that is not part of the token */
+ if (line[strlen(line) - 1] == '\n') {
+ line[strlen(line) - 1] = '\0';
+ }
+
+ cp = tor_strtok_r(cp, " \t", &strtok_state);
+
+ if (!cp) {
+ log_warn(LD_DIRSERV, "Invalid line in bandwidth file: %s",
+ escaped(orig_line));
+ tor_free(line);
+ return -1;
+ }
+
+ if (orig_line[strlen(orig_line)-1] != '\n') {
+ log_warn(LD_DIRSERV, "Incomplete line in bandwidth file: %s",
+ escaped(orig_line));
+ tor_free(line);
+ return -1;
+ }
+
+ do {
+ if (strcmpstart(cp, "bw=") == 0) {
+ int parse_ok = 0;
+ char *endptr;
+ if (got_bw) {
+ log_warn(LD_DIRSERV, "Double bw= in bandwidth file line: %s",
+ escaped(orig_line));
+ tor_free(line);
+ return -1;
+ }
+ cp+=strlen("bw=");
+
+ out->bw_kb = tor_parse_long(cp, 10, 0, LONG_MAX, &parse_ok, &endptr);
+ if (!parse_ok || (*endptr && !TOR_ISSPACE(*endptr))) {
+ log_warn(LD_DIRSERV, "Invalid bandwidth in bandwidth file line: %s",
+ escaped(orig_line));
+ tor_free(line);
+ return -1;
+ }
+ got_bw=1;
+ } else if (strcmpstart(cp, "node_id=$") == 0) {
+ if (got_node_id) {
+ log_warn(LD_DIRSERV, "Double node_id= in bandwidth file line: %s",
+ escaped(orig_line));
+ tor_free(line);
+ return -1;
+ }
+ cp+=strlen("node_id=$");
+
+ if (strlen(cp) != HEX_DIGEST_LEN ||
+ base16_decode(out->node_id, DIGEST_LEN,
+ cp, HEX_DIGEST_LEN) != DIGEST_LEN) {
+ log_warn(LD_DIRSERV, "Invalid node_id in bandwidth file line: %s",
+ escaped(orig_line));
+ tor_free(line);
+ return -1;
+ }
+ strlcpy(out->node_hex, cp, sizeof(out->node_hex));
+ got_node_id=1;
+ }
+ } while ((cp = tor_strtok_r(NULL, " \t", &strtok_state)));
+
+ if (got_bw && got_node_id) {
+ tor_free(line);
+ return 0;
+ } else if (line_is_after_headers == 0) {
+ /* There could be additional header lines, therefore do not give warnings
+ * but returns -1 since it's not a complete bw line. */
+ log_debug(LD_DIRSERV, "Missing bw or node_id in bandwidth file line: %s",
+ escaped(orig_line));
+ tor_free(line);
+ return -1;
+ } else {
+ log_warn(LD_DIRSERV, "Incomplete line in bandwidth file: %s",
+ escaped(orig_line));
+ tor_free(line);
+ return -1;
+ }
+}
+
+/**
+ * Helper function to apply a parsed measurement line to a list
+ * of bandwidth statuses. Returns true if a line is found,
+ * false otherwise.
+ */
+STATIC int
+measured_bw_line_apply(measured_bw_line_t *parsed_line,
+ smartlist_t *routerstatuses)
+{
+ vote_routerstatus_t *rs = NULL;
+ if (!routerstatuses)
+ return 0;
+
+ rs = smartlist_bsearch(routerstatuses, parsed_line->node_id,
+ compare_digest_to_vote_routerstatus_entry);
+
+ if (rs) {
+ rs->has_measured_bw = 1;
+ rs->measured_bw_kb = (uint32_t)parsed_line->bw_kb;
+ } else {
+ log_info(LD_DIRSERV, "Node ID %s not found in routerstatus list",
+ parsed_line->node_hex);
+ }
+
+ return rs != NULL;
+}
diff --git a/src/feature/dirauth/bwauth.h b/src/feature/dirauth/bwauth.h
new file mode 100644
index 0000000000..f10f8227af
--- /dev/null
+++ b/src/feature/dirauth/bwauth.h
@@ -0,0 +1,58 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file bwauth.h
+ * \brief Header file for bwauth.c
+ **/
+
+#ifndef TOR_BWAUTH_H
+#define TOR_BWAUTH_H
+
+/** Maximum allowable length of bandwidth headers in a bandwidth file */
+#define MAX_BW_FILE_HEADER_COUNT_IN_VOTE 50
+
+/** Terminatore that separates bandwidth file headers from bandwidth file
+ * relay lines */
+#define BW_FILE_HEADERS_TERMINATOR "=====\n"
+
+int dirserv_read_measured_bandwidths(const char *from_file,
+ smartlist_t *routerstatuses,
+ smartlist_t *bw_file_headers);
+
+int dirserv_query_measured_bw_cache_kb(const char *node_id,
+ long *bw_out,
+ time_t *as_of_out);
+void dirserv_clear_measured_bw_cache(void);
+int dirserv_has_measured_bw(const char *node_id);
+int dirserv_get_measured_bw_cache_size(void);
+void dirserv_count_measured_bws(const smartlist_t *routers);
+int dirserv_get_last_n_measured_bws(void);
+
+uint32_t dirserv_get_credible_bandwidth_kb(const routerinfo_t *ri);
+
+#ifdef BWAUTH_PRIVATE
+typedef struct measured_bw_line_t {
+ char node_id[DIGEST_LEN];
+ char node_hex[MAX_HEX_NICKNAME_LEN+1];
+ long int bw_kb;
+} measured_bw_line_t;
+
+/* Put the MAX_MEASUREMENT_AGE #define here so unit tests can see it */
+#define MAX_MEASUREMENT_AGE (3*24*60*60) /* 3 days */
+
+STATIC int measured_bw_line_parse(measured_bw_line_t *out, const char *line,
+ int line_is_after_headers);
+
+STATIC int measured_bw_line_apply(measured_bw_line_t *parsed_line,
+ smartlist_t *routerstatuses);
+
+STATIC void dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line,
+ time_t as_of);
+STATIC void dirserv_expire_measured_bw_cache(time_t now);
+#endif /* defined(BWAUTH_PRIVATE) */
+
+#endif
diff --git a/src/feature/dirauth/dirvote.c b/src/feature/dirauth/dirvote.c
index 14357c770e..5ca9ed4a4a 100644
--- a/src/feature/dirauth/dirvote.c
+++ b/src/feature/dirauth/dirvote.c
@@ -7,8 +7,12 @@
#include "core/or/or.h"
#include "app/config/config.h"
#include "feature/dirauth/dircollate.h"
+#include "feature/dirauth/recommend_pkg.h"
+#include "feature/dirauth/voteflags.h"
#include "feature/dircache/directory.h"
+#include "feature/dirauth/bwauth.h"
#include "feature/dircache/dirserv.h"
+#include "feature/dirauth/guardfraction.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
@@ -19,8 +23,11 @@
#include "feature/stats/rephist.h"
#include "feature/relay/router.h"
#include "feature/relay/routerkeys.h"
+#include "feature/nodelist/authcert.h"
+#include "feature/nodelist/dirlist.h"
#include "feature/nodelist/routerlist.h"
#include "feature/nodelist/routerparse.h"
+#include "feature/nodelist/fmt_routerstatus.h"
#include "feature/client/entrynodes.h" /* needed for guardfraction methods */
#include "feature/nodelist/torcert.h"
#include "feature/dircommon/voting_schedule.h"
diff --git a/src/feature/dirauth/dirvote.h b/src/feature/dirauth/dirvote.h
index 979a2be8a6..a21e9f3455 100644
--- a/src/feature/dirauth/dirvote.h
+++ b/src/feature/dirauth/dirvote.h
@@ -115,6 +115,10 @@ int dirvote_add_signatures(const char *detached_signatures_body,
const char *source,
const char **msg_out);
+struct config_line_t;
+char *format_recommended_version_list(const struct config_line_t *line,
+ int warn);
+
#else /* HAVE_MODULE_DIRAUTH */
static inline time_t
@@ -192,10 +196,6 @@ const cached_dir_t *dirvote_get_vote(const char *fp, int flags);
* API used _only_ by the dirauth subsystem.
*/
-void set_routerstatus_from_routerinfo(routerstatus_t *rs,
- node_t *node,
- routerinfo_t *ri, time_t now,
- int listbadexits);
networkstatus_t *
dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
authority_cert_t *cert);
diff --git a/src/feature/dirauth/guardfraction.c b/src/feature/dirauth/guardfraction.c
new file mode 100644
index 0000000000..60ba316cfe
--- /dev/null
+++ b/src/feature/dirauth/guardfraction.c
@@ -0,0 +1,333 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file bwauth.c
+ * \brief Code to read and apply guard fraction data.
+ **/
+
+#define GUARDFRACTION_PRIVATE
+#include "core/or/or.h"
+#include "feature/dirauth/guardfraction.h"
+#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/routerparse.h"
+
+#include "feature/nodelist/vote_routerstatus_st.h"
+
+#include "lib/encoding/confline.h"
+
+/** The guardfraction of the guard with identity fingerprint <b>guard_id</b>
+ * is <b>guardfraction_percentage</b>. See if we have a vote routerstatus for
+ * this guard in <b>vote_routerstatuses</b>, and if we do, register the
+ * information to it.
+ *
+ * Return 1 if we applied the information and 0 if we couldn't find a
+ * matching guard.
+ *
+ * Requires that <b>vote_routerstatuses</b> be sorted.
+ */
+static int
+guardfraction_line_apply(const char *guard_id,
+ uint32_t guardfraction_percentage,
+ smartlist_t *vote_routerstatuses)
+{
+ vote_routerstatus_t *vrs = NULL;
+
+ tor_assert(vote_routerstatuses);
+
+ vrs = smartlist_bsearch(vote_routerstatuses, guard_id,
+ compare_digest_to_vote_routerstatus_entry);
+
+ if (!vrs) {
+ return 0;
+ }
+
+ vrs->status.has_guardfraction = 1;
+ vrs->status.guardfraction_percentage = guardfraction_percentage;
+
+ return 1;
+}
+
+/* Given a guard line from a guardfraction file, parse it and register
+ * its information to <b>vote_routerstatuses</b>.
+ *
+ * Return:
+ * * 1 if the line was proper and its information got registered.
+ * * 0 if the line was proper but no currently active guard was found
+ * to register the guardfraction information to.
+ * * -1 if the line could not be parsed and set <b>err_msg</b> to a
+ newly allocated string containing the error message.
+ */
+static int
+guardfraction_file_parse_guard_line(const char *guard_line,
+ smartlist_t *vote_routerstatuses,
+ char **err_msg)
+{
+ char guard_id[DIGEST_LEN];
+ uint32_t guardfraction;
+ char *inputs_tmp = NULL;
+ int num_ok = 1;
+
+ smartlist_t *sl = smartlist_new();
+ int retval = -1;
+
+ tor_assert(err_msg);
+
+ /* guard_line should contain something like this:
+ <hex digest> <guardfraction> <appearances> */
+ smartlist_split_string(sl, guard_line, " ",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 3);
+ if (smartlist_len(sl) < 3) {
+ tor_asprintf(err_msg, "bad line '%s'", guard_line);
+ goto done;
+ }
+
+ inputs_tmp = smartlist_get(sl, 0);
+ if (strlen(inputs_tmp) != HEX_DIGEST_LEN ||
+ base16_decode(guard_id, DIGEST_LEN,
+ inputs_tmp, HEX_DIGEST_LEN) != DIGEST_LEN) {
+ tor_asprintf(err_msg, "bad digest '%s'", inputs_tmp);
+ goto done;
+ }
+
+ inputs_tmp = smartlist_get(sl, 1);
+ /* Guardfraction is an integer in [0, 100]. */
+ guardfraction =
+ (uint32_t) tor_parse_long(inputs_tmp, 10, 0, 100, &num_ok, NULL);
+ if (!num_ok) {
+ tor_asprintf(err_msg, "wrong percentage '%s'", inputs_tmp);
+ goto done;
+ }
+
+ /* If routerstatuses were provided, apply this info to actual routers. */
+ if (vote_routerstatuses) {
+ retval = guardfraction_line_apply(guard_id, guardfraction,
+ vote_routerstatuses);
+ } else {
+ retval = 0; /* If we got this far, line was correctly formatted. */
+ }
+
+ done:
+
+ SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ smartlist_free(sl);
+
+ return retval;
+}
+
+/** Given an inputs line from a guardfraction file, parse it and
+ * register its information to <b>total_consensuses</b> and
+ * <b>total_days</b>.
+ *
+ * Return 0 if it parsed well. Return -1 if there was an error, and
+ * set <b>err_msg</b> to a newly allocated string containing the
+ * error message.
+ */
+static int
+guardfraction_file_parse_inputs_line(const char *inputs_line,
+ int *total_consensuses,
+ int *total_days,
+ char **err_msg)
+{
+ int retval = -1;
+ char *inputs_tmp = NULL;
+ int num_ok = 1;
+ smartlist_t *sl = smartlist_new();
+
+ tor_assert(err_msg);
+
+ /* Second line is inputs information:
+ * n-inputs <total_consensuses> <total_days>. */
+ smartlist_split_string(sl, inputs_line, " ",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 3);
+ if (smartlist_len(sl) < 2) {
+ tor_asprintf(err_msg, "incomplete line '%s'", inputs_line);
+ goto done;
+ }
+
+ inputs_tmp = smartlist_get(sl, 0);
+ *total_consensuses =
+ (int) tor_parse_long(inputs_tmp, 10, 0, INT_MAX, &num_ok, NULL);
+ if (!num_ok) {
+ tor_asprintf(err_msg, "unparseable consensus '%s'", inputs_tmp);
+ goto done;
+ }
+
+ inputs_tmp = smartlist_get(sl, 1);
+ *total_days =
+ (int) tor_parse_long(inputs_tmp, 10, 0, INT_MAX, &num_ok, NULL);
+ if (!num_ok) {
+ tor_asprintf(err_msg, "unparseable days '%s'", inputs_tmp);
+ goto done;
+ }
+
+ retval = 0;
+
+ done:
+ SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ smartlist_free(sl);
+
+ return retval;
+}
+
+/* Maximum age of a guardfraction file that we are willing to accept. */
+#define MAX_GUARDFRACTION_FILE_AGE (7*24*60*60) /* approx a week */
+
+/** Static strings of guardfraction files. */
+#define GUARDFRACTION_DATE_STR "written-at"
+#define GUARDFRACTION_INPUTS "n-inputs"
+#define GUARDFRACTION_GUARD "guard-seen"
+#define GUARDFRACTION_VERSION "guardfraction-file-version"
+
+/** Given a guardfraction file in a string, parse it and register the
+ * guardfraction information to the provided vote routerstatuses.
+ *
+ * This is the rough format of the guardfraction file:
+ *
+ * guardfraction-file-version 1
+ * written-at <date and time>
+ * n-inputs <number of consesuses parsed> <number of days considered>
+ *
+ * guard-seen <fpr 1> <guardfraction percentage> <consensus appearances>
+ * guard-seen <fpr 2> <guardfraction percentage> <consensus appearances>
+ * guard-seen <fpr 3> <guardfraction percentage> <consensus appearances>
+ * guard-seen <fpr 4> <guardfraction percentage> <consensus appearances>
+ * guard-seen <fpr 5> <guardfraction percentage> <consensus appearances>
+ * ...
+ *
+ * Return -1 if the parsing failed and 0 if it went smoothly. Parsing
+ * should tolerate errors in all lines but the written-at header.
+ */
+STATIC int
+dirserv_read_guardfraction_file_from_str(const char *guardfraction_file_str,
+ smartlist_t *vote_routerstatuses)
+{
+ config_line_t *front=NULL, *line;
+ int ret_tmp;
+ int retval = -1;
+ int current_line_n = 0; /* line counter for better log messages */
+
+ /* Guardfraction info to be parsed */
+ int total_consensuses = 0;
+ int total_days = 0;
+
+ /* Stats */
+ int guards_read_n = 0;
+ int guards_applied_n = 0;
+
+ /* Parse file and split it in lines */
+ ret_tmp = config_get_lines(guardfraction_file_str, &front, 0);
+ if (ret_tmp < 0) {
+ log_warn(LD_CONFIG, "Error reading from guardfraction file");
+ goto done;
+ }
+
+ /* Sort routerstatuses (needed later when applying guardfraction info) */
+ if (vote_routerstatuses)
+ smartlist_sort(vote_routerstatuses, compare_vote_routerstatus_entries);
+
+ for (line = front; line; line=line->next) {
+ current_line_n++;
+
+ if (!strcmp(line->key, GUARDFRACTION_VERSION)) {
+ int num_ok = 1;
+ unsigned int version;
+
+ version =
+ (unsigned int) tor_parse_long(line->value,
+ 10, 0, INT_MAX, &num_ok, NULL);
+
+ if (!num_ok || version != 1) {
+ log_warn(LD_GENERAL, "Got unknown guardfraction version %d.", version);
+ goto done;
+ }
+ } else if (!strcmp(line->key, GUARDFRACTION_DATE_STR)) {
+ time_t file_written_at;
+ time_t now = time(NULL);
+
+ /* First line is 'written-at <date>' */
+ if (parse_iso_time(line->value, &file_written_at) < 0) {
+ log_warn(LD_CONFIG, "Guardfraction:%d: Bad date '%s'. Ignoring",
+ current_line_n, line->value);
+ goto done; /* don't tolerate failure here. */
+ }
+ if (file_written_at < now - MAX_GUARDFRACTION_FILE_AGE) {
+ log_warn(LD_CONFIG, "Guardfraction:%d: was written very long ago '%s'",
+ current_line_n, line->value);
+ goto done; /* don't tolerate failure here. */
+ }
+ } else if (!strcmp(line->key, GUARDFRACTION_INPUTS)) {
+ char *err_msg = NULL;
+
+ if (guardfraction_file_parse_inputs_line(line->value,
+ &total_consensuses,
+ &total_days,
+ &err_msg) < 0) {
+ log_warn(LD_CONFIG, "Guardfraction:%d: %s",
+ current_line_n, err_msg);
+ tor_free(err_msg);
+ continue;
+ }
+
+ } else if (!strcmp(line->key, GUARDFRACTION_GUARD)) {
+ char *err_msg = NULL;
+
+ ret_tmp = guardfraction_file_parse_guard_line(line->value,
+ vote_routerstatuses,
+ &err_msg);
+ if (ret_tmp < 0) { /* failed while parsing the guard line */
+ log_warn(LD_CONFIG, "Guardfraction:%d: %s",
+ current_line_n, err_msg);
+ tor_free(err_msg);
+ continue;
+ }
+
+ /* Successfully parsed guard line. Check if it was applied properly. */
+ guards_read_n++;
+ if (ret_tmp > 0) {
+ guards_applied_n++;
+ }
+ } else {
+ log_warn(LD_CONFIG, "Unknown guardfraction line %d (%s %s)",
+ current_line_n, line->key, line->value);
+ }
+ }
+
+ retval = 0;
+
+ log_info(LD_CONFIG,
+ "Successfully parsed guardfraction file with %d consensuses over "
+ "%d days. Parsed %d nodes and applied %d of them%s.",
+ total_consensuses, total_days, guards_read_n, guards_applied_n,
+ vote_routerstatuses ? "" : " (no routerstatus provided)" );
+
+ done:
+ config_free_lines(front);
+
+ if (retval < 0) {
+ return retval;
+ } else {
+ return guards_read_n;
+ }
+}
+
+/** Read a guardfraction file at <b>fname</b> and load all its
+ * information to <b>vote_routerstatuses</b>. */
+int
+dirserv_read_guardfraction_file(const char *fname,
+ smartlist_t *vote_routerstatuses)
+{
+ char *guardfraction_file_str;
+
+ /* Read file to a string */
+ guardfraction_file_str = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL);
+ if (!guardfraction_file_str) {
+ log_warn(LD_FS, "Cannot open guardfraction file '%s'. Failing.", fname);
+ return -1;
+ }
+
+ return dirserv_read_guardfraction_file_from_str(guardfraction_file_str,
+ vote_routerstatuses);
+}
diff --git a/src/feature/dirauth/guardfraction.h b/src/feature/dirauth/guardfraction.h
new file mode 100644
index 0000000000..38a0781dbb
--- /dev/null
+++ b/src/feature/dirauth/guardfraction.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file guardfraction.h
+ * \brief Header file for guardfraction.c
+ **/
+
+#ifndef TOR_GUARDFRACTION_H
+#define TOR_GUARDFRACTION_H
+
+#ifdef GUARDFRACTION_PRIVATE
+STATIC int
+dirserv_read_guardfraction_file_from_str(const char *guardfraction_file_str,
+ smartlist_t *vote_routerstatuses);
+#endif /* defined(DIRSERV_PRIVATE) */
+
+int dirserv_read_guardfraction_file(const char *fname,
+ smartlist_t *vote_routerstatuses);
+
+#endif
diff --git a/src/feature/dirauth/process_descs.c b/src/feature/dirauth/process_descs.c
new file mode 100644
index 0000000000..2c2fefca77
--- /dev/null
+++ b/src/feature/dirauth/process_descs.c
@@ -0,0 +1,835 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file process_descs.c
+ * \brief Make decisions about uploaded descriptors
+ *
+ * Authorities use the code in this module to decide what to do with just-
+ * uploaded descriptors, and to manage the fingerprint file that helps
+ * them make those decisions.
+ **/
+
+#include "core/or/or.h"
+#include "feature/dirauth/process_descs.h"
+
+#include "app/config/config.h"
+#include "core/or/policies.h"
+#include "feature/dirauth/keypin.h"
+#include "feature/dirauth/reachability.h"
+#include "feature/dircache/directory.h"
+#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/nodelist.h"
+#include "feature/nodelist/routerlist.h"
+#include "feature/nodelist/routerparse.h"
+#include "feature/nodelist/torcert.h"
+#include "feature/relay/router.h"
+
+#include "core/or/tor_version_st.h"
+#include "feature/nodelist/extrainfo_st.h"
+#include "feature/nodelist/node_st.h"
+#include "feature/nodelist/routerinfo_st.h"
+#include "feature/nodelist/routerstatus_st.h"
+
+#include "lib/encoding/confline.h"
+
+/** How far in the future do we allow a router to get? (seconds) */
+#define ROUTER_ALLOW_SKEW (60*60*12)
+
+static void directory_remove_invalid(void);
+struct authdir_config_t;
+static was_router_added_t dirserv_add_extrainfo(extrainfo_t *ei,
+ const char **msg);
+static uint32_t
+dirserv_get_status_impl(const char *fp, const char *nickname,
+ uint32_t addr, uint16_t or_port,
+ const char *platform, const char **msg,
+ int severity);
+
+/* 1 Historically used to indicate Named */
+#define FP_INVALID 2 /**< Believed invalid. */
+#define FP_REJECT 4 /**< We will not publish this router. */
+/* 8 Historically used to avoid using this as a dir. */
+#define FP_BADEXIT 16 /**< We'll tell clients not to use this as an exit. */
+/* 32 Historically used to indicade Unnamed */
+
+/** Target of status_by_digest map. */
+typedef uint32_t router_status_t;
+
+static void add_fingerprint_to_dir(const char *fp,
+ struct authdir_config_t *list,
+ router_status_t add_status);
+
+/** List of nickname-\>identity fingerprint mappings for all the routers
+ * that we name. Used to prevent router impersonation. */
+typedef struct authdir_config_t {
+ strmap_t *fp_by_name; /**< Map from lc nickname to fingerprint. */
+ digestmap_t *status_by_digest; /**< Map from digest to router_status_t. */
+} authdir_config_t;
+
+/** Should be static; exposed for testing. */
+static authdir_config_t *fingerprint_list = NULL;
+
+/** Allocate and return a new, empty, authdir_config_t. */
+static authdir_config_t *
+authdir_config_new(void)
+{
+ authdir_config_t *list = tor_malloc_zero(sizeof(authdir_config_t));
+ list->fp_by_name = strmap_new();
+ list->status_by_digest = digestmap_new();
+ return list;
+}
+
+/** Add the fingerprint <b>fp</b> to the smartlist of fingerprint_entry_t's
+ * <b>list</b>, or-ing the currently set status flags with
+ * <b>add_status</b>.
+ */
+/* static */ void
+add_fingerprint_to_dir(const char *fp, authdir_config_t *list,
+ router_status_t add_status)
+{
+ char *fingerprint;
+ char d[DIGEST_LEN];
+ router_status_t *status;
+ tor_assert(fp);
+ tor_assert(list);
+
+ fingerprint = tor_strdup(fp);
+ tor_strstrip(fingerprint, " ");
+ if (base16_decode(d, DIGEST_LEN,
+ fingerprint, strlen(fingerprint)) != DIGEST_LEN) {
+ log_warn(LD_DIRSERV, "Couldn't decode fingerprint \"%s\"",
+ escaped(fp));
+ tor_free(fingerprint);
+ return;
+ }
+
+ status = digestmap_get(list->status_by_digest, d);
+ if (!status) {
+ status = tor_malloc_zero(sizeof(router_status_t));
+ digestmap_set(list->status_by_digest, d, status);
+ }
+
+ tor_free(fingerprint);
+ *status |= add_status;
+ return;
+}
+
+/** Add the fingerprint for this OR to the global list of recognized
+ * identity key fingerprints. */
+int
+dirserv_add_own_fingerprint(crypto_pk_t *pk)
+{
+ char fp[FINGERPRINT_LEN+1];
+ if (crypto_pk_get_fingerprint(pk, fp, 0)<0) {
+ log_err(LD_BUG, "Error computing fingerprint");
+ return -1;
+ }
+ if (!fingerprint_list)
+ fingerprint_list = authdir_config_new();
+ add_fingerprint_to_dir(fp, fingerprint_list, 0);
+ return 0;
+}
+
+/** Load the nickname-\>fingerprint mappings stored in the approved-routers
+ * file. The file format is line-based, with each non-blank holding one
+ * nickname, some space, and a fingerprint for that nickname. On success,
+ * replace the current fingerprint list with the new list and return 0. On
+ * failure, leave the current fingerprint list untouched, and return -1. */
+int
+dirserv_load_fingerprint_file(void)
+{
+ char *fname;
+ char *cf;
+ char *nickname, *fingerprint;
+ authdir_config_t *fingerprint_list_new;
+ int result;
+ config_line_t *front=NULL, *list;
+
+ fname = get_datadir_fname("approved-routers");
+ log_info(LD_GENERAL,
+ "Reloading approved fingerprints from \"%s\"...", fname);
+
+ cf = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL);
+ if (!cf) {
+ log_warn(LD_FS, "Cannot open fingerprint file '%s'. That's ok.", fname);
+ tor_free(fname);
+ return 0;
+ }
+ tor_free(fname);
+
+ result = config_get_lines(cf, &front, 0);
+ tor_free(cf);
+ if (result < 0) {
+ log_warn(LD_CONFIG, "Error reading from fingerprint file");
+ return -1;
+ }
+
+ fingerprint_list_new = authdir_config_new();
+
+ for (list=front; list; list=list->next) {
+ char digest_tmp[DIGEST_LEN];
+ router_status_t add_status = 0;
+ nickname = list->key; fingerprint = list->value;
+ tor_strstrip(fingerprint, " "); /* remove spaces */
+ if (strlen(fingerprint) != HEX_DIGEST_LEN ||
+ base16_decode(digest_tmp, sizeof(digest_tmp),
+ fingerprint, HEX_DIGEST_LEN) != sizeof(digest_tmp)) {
+ log_notice(LD_CONFIG,
+ "Invalid fingerprint (nickname '%s', "
+ "fingerprint %s). Skipping.",
+ nickname, fingerprint);
+ continue;
+ }
+ if (!strcasecmp(nickname, "!reject")) {
+ add_status = FP_REJECT;
+ } else if (!strcasecmp(nickname, "!badexit")) {
+ add_status = FP_BADEXIT;
+ } else if (!strcasecmp(nickname, "!invalid")) {
+ add_status = FP_INVALID;
+ }
+ add_fingerprint_to_dir(fingerprint, fingerprint_list_new, add_status);
+ }
+
+ config_free_lines(front);
+ dirserv_free_fingerprint_list();
+ fingerprint_list = fingerprint_list_new;
+ /* Delete any routers whose fingerprints we no longer recognize */
+ directory_remove_invalid();
+ return 0;
+}
+
+/* If this is set, then we don't allow routers that have advertised an Ed25519
+ * identity to stop doing so. This is going to be essential for good identity
+ * security: otherwise anybody who can attack RSA-1024 but not Ed25519 could
+ * just sign fake descriptors missing the Ed25519 key. But we won't actually
+ * be able to prevent that kind of thing until we're confident that there isn't
+ * actually a legit reason to downgrade to 0.2.5. Now we are not recommending
+ * 0.2.5 anymore so there is no reason to keep the #undef.
+ */
+
+#define DISABLE_DISABLING_ED25519
+
+/** Check whether <b>router</b> has a nickname/identity key combination that
+ * we recognize from the fingerprint list, or an IP we automatically act on
+ * according to our configuration. Return the appropriate router status.
+ *
+ * If the status is 'FP_REJECT' and <b>msg</b> is provided, set
+ * *<b>msg</b> to an explanation of why. */
+uint32_t
+dirserv_router_get_status(const routerinfo_t *router, const char **msg,
+ int severity)
+{
+ char d[DIGEST_LEN];
+ const int key_pinning = get_options()->AuthDirPinKeys;
+
+ if (crypto_pk_get_digest(router->identity_pkey, d)) {
+ log_warn(LD_BUG,"Error computing fingerprint");
+ if (msg)
+ *msg = "Bug: Error computing fingerprint";
+ return FP_REJECT;
+ }
+
+ /* Check for the more usual versions to reject a router first. */
+ const uint32_t r = dirserv_get_status_impl(d, router->nickname,
+ router->addr, router->or_port,
+ router->platform, msg, severity);
+ if (r)
+ return r;
+
+ /* dirserv_get_status_impl already rejects versions older than 0.2.4.18-rc,
+ * and onion_curve25519_pkey was introduced in 0.2.4.8-alpha.
+ * But just in case a relay doesn't provide or lies about its version, or
+ * doesn't include an ntor key in its descriptor, check that it exists,
+ * and is non-zero (clients check that it's non-zero before using it). */
+ if (!routerinfo_has_curve25519_onion_key(router)) {
+ log_fn(severity, LD_DIR,
+ "Descriptor from router %s is missing an ntor curve25519 onion "
+ "key.", router_describe(router));
+ if (msg)
+ *msg = "Missing ntor curve25519 onion key. Please upgrade!";
+ return FP_REJECT;
+ }
+
+ if (router->cache_info.signing_key_cert) {
+ /* This has an ed25519 identity key. */
+ if (KEYPIN_MISMATCH ==
+ keypin_check((const uint8_t*)router->cache_info.identity_digest,
+ router->cache_info.signing_key_cert->signing_key.pubkey)) {
+ log_fn(severity, LD_DIR,
+ "Descriptor from router %s has an Ed25519 key, "
+ "but the <rsa,ed25519> keys don't match what they were before.",
+ router_describe(router));
+ if (key_pinning) {
+ if (msg) {
+ *msg = "Ed25519 identity key or RSA identity key has changed.";
+ }
+ return FP_REJECT;
+ }
+ }
+ } else {
+ /* No ed25519 key */
+ if (KEYPIN_MISMATCH == keypin_check_lone_rsa(
+ (const uint8_t*)router->cache_info.identity_digest)) {
+ log_fn(severity, LD_DIR,
+ "Descriptor from router %s has no Ed25519 key, "
+ "when we previously knew an Ed25519 for it. Ignoring for now, "
+ "since Ed25519 keys are fairly new.",
+ router_describe(router));
+#ifdef DISABLE_DISABLING_ED25519
+ if (key_pinning) {
+ if (msg) {
+ *msg = "Ed25519 identity key has disappeared.";
+ }
+ return FP_REJECT;
+ }
+#endif /* defined(DISABLE_DISABLING_ED25519) */
+ }
+ }
+
+ return 0;
+}
+
+/** Return true if there is no point in downloading the router described by
+ * <b>rs</b> because this directory would reject it. */
+int
+dirserv_would_reject_router(const routerstatus_t *rs)
+{
+ uint32_t res;
+
+ res = dirserv_get_status_impl(rs->identity_digest, rs->nickname,
+ rs->addr, rs->or_port,
+ NULL, NULL, LOG_DEBUG);
+
+ return (res & FP_REJECT) != 0;
+}
+
+/** Helper: As dirserv_router_get_status, but takes the router fingerprint
+ * (hex, no spaces), nickname, address (used for logging only), IP address, OR
+ * port and platform (logging only) as arguments.
+ *
+ * Log messages at 'severity'. (There's not much point in
+ * logging that we're rejecting servers we'll not download.)
+ */
+static uint32_t
+dirserv_get_status_impl(const char *id_digest, const char *nickname,
+ uint32_t addr, uint16_t or_port,
+ const char *platform, const char **msg, int severity)
+{
+ uint32_t result = 0;
+ router_status_t *status_by_digest;
+
+ if (!fingerprint_list)
+ fingerprint_list = authdir_config_new();
+
+ log_debug(LD_DIRSERV, "%d fingerprints, %d digests known.",
+ strmap_size(fingerprint_list->fp_by_name),
+ digestmap_size(fingerprint_list->status_by_digest));
+
+ if (platform) {
+ tor_version_t ver_tmp;
+ if (tor_version_parse_platform(platform, &ver_tmp, 1) < 0) {
+ if (msg) {
+ *msg = "Malformed platform string.";
+ }
+ return FP_REJECT;
+ }
+ }
+
+ /* Versions before Tor 0.2.4.18-rc are too old to support, and are
+ * missing some important security fixes too. Disable them. */
+ if (platform && !tor_version_as_new_as(platform,"0.2.4.18-rc")) {
+ if (msg)
+ *msg = "Tor version is insecure or unsupported. Please upgrade!";
+ return FP_REJECT;
+ }
+
+ /* Tor 0.2.9.x where x<5 suffers from bug #20499, where relays don't
+ * keep their consensus up to date so they make bad guards.
+ * The simple fix is to just drop them from the network. */
+ if (platform &&
+ tor_version_as_new_as(platform,"0.2.9.0-alpha") &&
+ !tor_version_as_new_as(platform,"0.2.9.5-alpha")) {
+ if (msg)
+ *msg = "Tor version contains bug 20499. Please upgrade!";
+ return FP_REJECT;
+ }
+
+ status_by_digest = digestmap_get(fingerprint_list->status_by_digest,
+ id_digest);
+ if (status_by_digest)
+ result |= *status_by_digest;
+
+ if (result & FP_REJECT) {
+ if (msg)
+ *msg = "Fingerprint is marked rejected -- if you think this is a "
+ "mistake please set a valid email address in ContactInfo and "
+ "send an email to bad-relays@lists.torproject.org mentioning "
+ "your fingerprint(s)?";
+ return FP_REJECT;
+ } else if (result & FP_INVALID) {
+ if (msg)
+ *msg = "Fingerprint is marked invalid";
+ }
+
+ if (authdir_policy_badexit_address(addr, or_port)) {
+ log_fn(severity, LD_DIRSERV,
+ "Marking '%s' as bad exit because of address '%s'",
+ nickname, fmt_addr32(addr));
+ result |= FP_BADEXIT;
+ }
+
+ if (!authdir_policy_permits_address(addr, or_port)) {
+ log_fn(severity, LD_DIRSERV, "Rejecting '%s' because of address '%s'",
+ nickname, fmt_addr32(addr));
+ if (msg)
+ *msg = "Suspicious relay address range -- if you think this is a "
+ "mistake please set a valid email address in ContactInfo and "
+ "send an email to bad-relays@lists.torproject.org mentioning "
+ "your address(es) and fingerprint(s)?";
+ return FP_REJECT;
+ }
+ if (!authdir_policy_valid_address(addr, or_port)) {
+ log_fn(severity, LD_DIRSERV,
+ "Not marking '%s' valid because of address '%s'",
+ nickname, fmt_addr32(addr));
+ result |= FP_INVALID;
+ }
+
+ return result;
+}
+
+/** Clear the current fingerprint list. */
+void
+dirserv_free_fingerprint_list(void)
+{
+ if (!fingerprint_list)
+ return;
+
+ strmap_free(fingerprint_list->fp_by_name, tor_free_);
+ digestmap_free(fingerprint_list->status_by_digest, tor_free_);
+ tor_free(fingerprint_list);
+}
+
+/*
+ * Descriptor list
+ */
+
+/** Return -1 if <b>ri</b> has a private or otherwise bad address,
+ * unless we're configured to not care. Return 0 if all ok. */
+static int
+dirserv_router_has_valid_address(routerinfo_t *ri)
+{
+ tor_addr_t addr;
+ if (get_options()->DirAllowPrivateAddresses)
+ return 0; /* whatever it is, we're fine with it */
+ tor_addr_from_ipv4h(&addr, ri->addr);
+
+ if (tor_addr_is_internal(&addr, 0)) {
+ log_info(LD_DIRSERV,
+ "Router %s published internal IP address. Refusing.",
+ router_describe(ri));
+ return -1; /* it's a private IP, we should reject it */
+ }
+ return 0;
+}
+
+/** Check whether we, as a directory server, want to accept <b>ri</b>. If so,
+ * set its is_valid,running fields and return 0. Otherwise, return -1.
+ *
+ * If the router is rejected, set *<b>msg</b> to an explanation of why.
+ *
+ * If <b>complain</b> then explain at log-level 'notice' why we refused
+ * a descriptor; else explain at log-level 'info'.
+ */
+int
+authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg,
+ int complain, int *valid_out)
+{
+ /* Okay. Now check whether the fingerprint is recognized. */
+ time_t now;
+ int severity = (complain && ri->contact_info) ? LOG_NOTICE : LOG_INFO;
+ uint32_t status = dirserv_router_get_status(ri, msg, severity);
+ tor_assert(msg);
+ if (status & FP_REJECT)
+ return -1; /* msg is already set. */
+
+ /* Is there too much clock skew? */
+ now = time(NULL);
+ if (ri->cache_info.published_on > now+ROUTER_ALLOW_SKEW) {
+ log_fn(severity, LD_DIRSERV, "Publication time for %s is too "
+ "far (%d minutes) in the future; possible clock skew. Not adding "
+ "(%s)",
+ router_describe(ri),
+ (int)((ri->cache_info.published_on-now)/60),
+ esc_router_info(ri));
+ *msg = "Rejected: Your clock is set too far in the future, or your "
+ "timezone is not correct.";
+ return -1;
+ }
+ if (ri->cache_info.published_on < now-ROUTER_MAX_AGE_TO_PUBLISH) {
+ log_fn(severity, LD_DIRSERV,
+ "Publication time for %s is too far "
+ "(%d minutes) in the past. Not adding (%s)",
+ router_describe(ri),
+ (int)((now-ri->cache_info.published_on)/60),
+ esc_router_info(ri));
+ *msg = "Rejected: Server is expired, or your clock is too far in the past,"
+ " or your timezone is not correct.";
+ return -1;
+ }
+ if (dirserv_router_has_valid_address(ri) < 0) {
+ log_fn(severity, LD_DIRSERV,
+ "Router %s has invalid address. Not adding (%s).",
+ router_describe(ri),
+ esc_router_info(ri));
+ *msg = "Rejected: Address is a private address.";
+ return -1;
+ }
+
+ *valid_out = ! (status & FP_INVALID);
+
+ return 0;
+}
+
+/** Update the relevant flags of <b>node</b> based on our opinion as a
+ * directory authority in <b>authstatus</b>, as returned by
+ * dirserv_router_get_status or equivalent. */
+void
+dirserv_set_node_flags_from_authoritative_status(node_t *node,
+ uint32_t authstatus)
+{
+ node->is_valid = (authstatus & FP_INVALID) ? 0 : 1;
+ node->is_bad_exit = (authstatus & FP_BADEXIT) ? 1 : 0;
+}
+
+/** True iff <b>a</b> is more severe than <b>b</b>. */
+static int
+WRA_MORE_SEVERE(was_router_added_t a, was_router_added_t b)
+{
+ return a < b;
+}
+
+/** As for dirserv_add_descriptor(), but accepts multiple documents, and
+ * returns the most severe error that occurred for any one of them. */
+was_router_added_t
+dirserv_add_multiple_descriptors(const char *desc, uint8_t purpose,
+ const char *source,
+ const char **msg)
+{
+ was_router_added_t r, r_tmp;
+ const char *msg_out;
+ smartlist_t *list;
+ const char *s;
+ int n_parsed = 0;
+ time_t now = time(NULL);
+ char annotation_buf[ROUTER_ANNOTATION_BUF_LEN];
+ char time_buf[ISO_TIME_LEN+1];
+ int general = purpose == ROUTER_PURPOSE_GENERAL;
+ tor_assert(msg);
+
+ r=ROUTER_ADDED_SUCCESSFULLY; /*Least severe return value. */
+
+ format_iso_time(time_buf, now);
+ if (tor_snprintf(annotation_buf, sizeof(annotation_buf),
+ "@uploaded-at %s\n"
+ "@source %s\n"
+ "%s%s%s", time_buf, escaped(source),
+ !general ? "@purpose " : "",
+ !general ? router_purpose_to_string(purpose) : "",
+ !general ? "\n" : "")<0) {
+ *msg = "Couldn't format annotations";
+ /* XXX Not cool: we return -1 below, but (was_router_added_t)-1 is
+ * ROUTER_BAD_EI, which isn't what's gone wrong here. :( */
+ return -1;
+ }
+
+ s = desc;
+ list = smartlist_new();
+ if (!router_parse_list_from_string(&s, NULL, list, SAVED_NOWHERE, 0, 0,
+ annotation_buf, NULL)) {
+ SMARTLIST_FOREACH(list, routerinfo_t *, ri, {
+ msg_out = NULL;
+ tor_assert(ri->purpose == purpose);
+ r_tmp = dirserv_add_descriptor(ri, &msg_out, source);
+ if (WRA_MORE_SEVERE(r_tmp, r)) {
+ r = r_tmp;
+ *msg = msg_out;
+ }
+ });
+ }
+ n_parsed += smartlist_len(list);
+ smartlist_clear(list);
+
+ s = desc;
+ if (!router_parse_list_from_string(&s, NULL, list, SAVED_NOWHERE, 1, 0,
+ NULL, NULL)) {
+ SMARTLIST_FOREACH(list, extrainfo_t *, ei, {
+ msg_out = NULL;
+
+ r_tmp = dirserv_add_extrainfo(ei, &msg_out);
+ if (WRA_MORE_SEVERE(r_tmp, r)) {
+ r = r_tmp;
+ *msg = msg_out;
+ }
+ });
+ }
+ n_parsed += smartlist_len(list);
+ smartlist_free(list);
+
+ if (! *msg) {
+ if (!n_parsed) {
+ *msg = "No descriptors found in your POST.";
+ if (WRA_WAS_ADDED(r))
+ r = ROUTER_IS_ALREADY_KNOWN;
+ } else {
+ *msg = "(no message)";
+ }
+ }
+
+ return r;
+}
+
+/** Examine the parsed server descriptor in <b>ri</b> and maybe insert it into
+ * the list of server descriptors. Set *<b>msg</b> to a message that should be
+ * passed back to the origin of this descriptor, or NULL if there is no such
+ * message. Use <b>source</b> to produce better log messages.
+ *
+ * If <b>ri</b> is not added to the list of server descriptors, free it.
+ * That means the caller must not access <b>ri</b> after this function
+ * returns, since it might have been freed.
+ *
+ * Return the status of the operation.
+ *
+ * This function is only called when fresh descriptors are posted, not when
+ * we re-load the cache.
+ */
+was_router_added_t
+dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
+{
+ was_router_added_t r;
+ routerinfo_t *ri_old;
+ char *desc, *nickname;
+ const size_t desclen = ri->cache_info.signed_descriptor_len +
+ ri->cache_info.annotations_len;
+ const int key_pinning = get_options()->AuthDirPinKeys;
+ *msg = NULL;
+
+ /* If it's too big, refuse it now. Otherwise we'll cache it all over the
+ * network and it'll clog everything up. */
+ if (ri->cache_info.signed_descriptor_len > MAX_DESCRIPTOR_UPLOAD_SIZE) {
+ log_notice(LD_DIR, "Somebody attempted to publish a router descriptor '%s'"
+ " (source: %s) with size %d. Either this is an attack, or the "
+ "MAX_DESCRIPTOR_UPLOAD_SIZE (%d) constant is too low.",
+ ri->nickname, source, (int)ri->cache_info.signed_descriptor_len,
+ MAX_DESCRIPTOR_UPLOAD_SIZE);
+ *msg = "Router descriptor was too large.";
+ r = ROUTER_AUTHDIR_REJECTS;
+ goto fail;
+ }
+
+ /* Check whether this descriptor is semantically identical to the last one
+ * from this server. (We do this here and not in router_add_to_routerlist
+ * because we want to be able to accept the newest router descriptor that
+ * another authority has, so we all converge on the same one.) */
+ ri_old = router_get_mutable_by_digest(ri->cache_info.identity_digest);
+ if (ri_old && ri_old->cache_info.published_on < ri->cache_info.published_on
+ && router_differences_are_cosmetic(ri_old, ri)
+ && !router_is_me(ri)) {
+ log_info(LD_DIRSERV,
+ "Not replacing descriptor from %s (source: %s); "
+ "differences are cosmetic.",
+ router_describe(ri), source);
+ *msg = "Not replacing router descriptor; no information has changed since "
+ "the last one with this identity.";
+ r = ROUTER_IS_ALREADY_KNOWN;
+ goto fail;
+ }
+
+ /* Do keypinning again ... this time, to add the pin if appropriate */
+ int keypin_status;
+ if (ri->cache_info.signing_key_cert) {
+ ed25519_public_key_t *pkey = &ri->cache_info.signing_key_cert->signing_key;
+ /* First let's validate this pubkey before pinning it */
+ if (ed25519_validate_pubkey(pkey) < 0) {
+ log_warn(LD_DIRSERV, "Received bad key from %s (source %s)",
+ router_describe(ri), source);
+ routerinfo_free(ri);
+ return ROUTER_AUTHDIR_REJECTS;
+ }
+
+ /* Now pin it! */
+ keypin_status = keypin_check_and_add(
+ (const uint8_t*)ri->cache_info.identity_digest,
+ pkey->pubkey, ! key_pinning);
+ } else {
+ keypin_status = keypin_check_lone_rsa(
+ (const uint8_t*)ri->cache_info.identity_digest);
+#ifndef DISABLE_DISABLING_ED25519
+ if (keypin_status == KEYPIN_MISMATCH)
+ keypin_status = KEYPIN_NOT_FOUND;
+#endif
+ }
+ if (keypin_status == KEYPIN_MISMATCH && key_pinning) {
+ log_info(LD_DIRSERV, "Dropping descriptor from %s (source: %s) because "
+ "its key did not match an older RSA/Ed25519 keypair",
+ router_describe(ri), source);
+ *msg = "Looks like your keypair has changed? This authority previously "
+ "recorded a different RSA identity for this Ed25519 identity (or vice "
+ "versa.) Did you replace or copy some of your key files, but not "
+ "the others? You should either restore the expected keypair, or "
+ "delete your keys and restart Tor to start your relay with a new "
+ "identity.";
+ r = ROUTER_AUTHDIR_REJECTS;
+ goto fail;
+ }
+
+ /* Make a copy of desc, since router_add_to_routerlist might free
+ * ri and its associated signed_descriptor_t. */
+ desc = tor_strndup(ri->cache_info.signed_descriptor_body, desclen);
+ nickname = tor_strdup(ri->nickname);
+
+ /* Tell if we're about to need to launch a test if we add this. */
+ ri->needs_retest_if_added =
+ dirserv_should_launch_reachability_test(ri, ri_old);
+
+ r = router_add_to_routerlist(ri, msg, 0, 0);
+ if (!WRA_WAS_ADDED(r)) {
+ /* unless the routerinfo was fine, just out-of-date */
+ log_info(LD_DIRSERV,
+ "Did not add descriptor from '%s' (source: %s): %s.",
+ nickname, source, *msg ? *msg : "(no message)");
+ } else {
+ smartlist_t *changed;
+
+ changed = smartlist_new();
+ smartlist_add(changed, ri);
+ routerlist_descriptors_added(changed, 0);
+ smartlist_free(changed);
+ if (!*msg) {
+ *msg = "Descriptor accepted";
+ }
+ log_info(LD_DIRSERV,
+ "Added descriptor from '%s' (source: %s): %s.",
+ nickname, source, *msg);
+ }
+ tor_free(desc);
+ tor_free(nickname);
+ return r;
+ fail:
+ {
+ const char *desc_digest = ri->cache_info.signed_descriptor_digest;
+ download_status_t *dls =
+ router_get_dl_status_by_descriptor_digest(desc_digest);
+ if (dls) {
+ log_info(LD_GENERAL, "Marking router with descriptor %s as rejected, "
+ "and therefore undownloadable",
+ hex_str(desc_digest, DIGEST_LEN));
+ download_status_mark_impossible(dls);
+ }
+ routerinfo_free(ri);
+ }
+ return r;
+}
+
+/** As dirserv_add_descriptor, but for an extrainfo_t <b>ei</b>. */
+static was_router_added_t
+dirserv_add_extrainfo(extrainfo_t *ei, const char **msg)
+{
+ routerinfo_t *ri;
+ int r;
+ was_router_added_t rv;
+ tor_assert(msg);
+ *msg = NULL;
+
+ /* Needs to be mutable so routerinfo_incompatible_with_extrainfo
+ * can mess with some of the flags in ri->cache_info. */
+ ri = router_get_mutable_by_digest(ei->cache_info.identity_digest);
+ if (!ri) {
+ *msg = "No corresponding router descriptor for extra-info descriptor";
+ rv = ROUTER_BAD_EI;
+ goto fail;
+ }
+
+ /* If it's too big, refuse it now. Otherwise we'll cache it all over the
+ * network and it'll clog everything up. */
+ if (ei->cache_info.signed_descriptor_len > MAX_EXTRAINFO_UPLOAD_SIZE) {
+ log_notice(LD_DIR, "Somebody attempted to publish an extrainfo "
+ "with size %d. Either this is an attack, or the "
+ "MAX_EXTRAINFO_UPLOAD_SIZE (%d) constant is too low.",
+ (int)ei->cache_info.signed_descriptor_len,
+ MAX_EXTRAINFO_UPLOAD_SIZE);
+ *msg = "Extrainfo document was too large";
+ rv = ROUTER_BAD_EI;
+ goto fail;
+ }
+
+ if ((r = routerinfo_incompatible_with_extrainfo(ri->identity_pkey, ei,
+ &ri->cache_info, msg))) {
+ if (r<0) {
+ extrainfo_free(ei);
+ return ROUTER_IS_ALREADY_KNOWN;
+ }
+ rv = ROUTER_BAD_EI;
+ goto fail;
+ }
+ router_add_extrainfo_to_routerlist(ei, msg, 0, 0);
+ return ROUTER_ADDED_SUCCESSFULLY;
+ fail:
+ {
+ const char *d = ei->cache_info.signed_descriptor_digest;
+ signed_descriptor_t *sd = router_get_by_extrainfo_digest((char*)d);
+ if (sd) {
+ log_info(LD_GENERAL, "Marking extrainfo with descriptor %s as "
+ "rejected, and therefore undownloadable",
+ hex_str((char*)d,DIGEST_LEN));
+ download_status_mark_impossible(&sd->ei_dl_status);
+ }
+ extrainfo_free(ei);
+ }
+ return rv;
+}
+
+/** Remove all descriptors whose nicknames or fingerprints no longer
+ * are allowed by our fingerprint list. (Descriptors that used to be
+ * good can become bad when we reload the fingerprint list.)
+ */
+static void
+directory_remove_invalid(void)
+{
+ routerlist_t *rl = router_get_routerlist();
+ smartlist_t *nodes = smartlist_new();
+ smartlist_add_all(nodes, nodelist_get_list());
+
+ SMARTLIST_FOREACH_BEGIN(nodes, node_t *, node) {
+ const char *msg = NULL;
+ const char *description;
+ routerinfo_t *ent = node->ri;
+ uint32_t r;
+ if (!ent)
+ continue;
+ r = dirserv_router_get_status(ent, &msg, LOG_INFO);
+ description = router_describe(ent);
+ if (r & FP_REJECT) {
+ log_info(LD_DIRSERV, "Router %s is now rejected: %s",
+ description, msg?msg:"");
+ routerlist_remove(rl, ent, 0, time(NULL));
+ continue;
+ }
+ if (bool_neq((r & FP_INVALID), !node->is_valid)) {
+ log_info(LD_DIRSERV, "Router '%s' is now %svalid.", description,
+ (r&FP_INVALID) ? "in" : "");
+ node->is_valid = (r&FP_INVALID)?0:1;
+ }
+ if (bool_neq((r & FP_BADEXIT), node->is_bad_exit)) {
+ log_info(LD_DIRSERV, "Router '%s' is now a %s exit", description,
+ (r & FP_BADEXIT) ? "bad" : "good");
+ node->is_bad_exit = (r&FP_BADEXIT) ? 1: 0;
+ }
+ } SMARTLIST_FOREACH_END(node);
+
+ routerlist_assert_ok(rl);
+ smartlist_free(nodes);
+}
diff --git a/src/feature/dirauth/process_descs.h b/src/feature/dirauth/process_descs.h
new file mode 100644
index 0000000000..ad9d5c3d4c
--- /dev/null
+++ b/src/feature/dirauth/process_descs.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file process_descs.h
+ * \brief Header file for process_descs.c.
+ **/
+
+#ifndef TOR_RECV_UPLOADS_H
+#define TOR_RECV_UPLOADS_H
+
+int dirserv_load_fingerprint_file(void);
+void dirserv_free_fingerprint_list(void);
+int dirserv_add_own_fingerprint(crypto_pk_t *pk);
+
+enum was_router_added_t dirserv_add_multiple_descriptors(
+ const char *desc, uint8_t purpose,
+ const char *source,
+ const char **msg);
+enum was_router_added_t dirserv_add_descriptor(routerinfo_t *ri,
+ const char **msg,
+ const char *source);
+
+int authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg,
+ int complain,
+ int *valid_out);
+uint32_t dirserv_router_get_status(const routerinfo_t *router,
+ const char **msg,
+ int severity);
+void dirserv_set_node_flags_from_authoritative_status(node_t *node,
+ uint32_t authstatus);
+
+int dirserv_would_reject_router(const routerstatus_t *rs);
+
+#endif
diff --git a/src/feature/dirauth/reachability.c b/src/feature/dirauth/reachability.c
new file mode 100644
index 0000000000..abc7249b65
--- /dev/null
+++ b/src/feature/dirauth/reachability.c
@@ -0,0 +1,205 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file reachability.c
+ * \brief Router reachability testing; run by authorities to tell who is
+ * running.
+ */
+
+#include "core/or/or.h"
+#include "feature/dirauth/reachability.h"
+
+#include "app/config/config.h"
+#include "core/or/channel.h"
+#include "core/or/channeltls.h"
+#include "core/or/command.h"
+#include "feature/nodelist/nodelist.h"
+#include "feature/nodelist/routerlist.h"
+#include "feature/nodelist/torcert.h"
+#include "feature/relay/router.h"
+#include "feature/stats/rephist.h"
+
+#include "feature/nodelist/node_st.h"
+#include "feature/nodelist/routerinfo_st.h"
+#include "feature/nodelist/routerlist_st.h"
+
+/** Called when a TLS handshake has completed successfully with a
+ * router listening at <b>address</b>:<b>or_port</b>, and has yielded
+ * a certificate with digest <b>digest_rcvd</b>.
+ *
+ * Inform the reachability checker that we could get to this relay.
+ */
+void
+dirserv_orconn_tls_done(const tor_addr_t *addr,
+ uint16_t or_port,
+ const char *digest_rcvd,
+ const ed25519_public_key_t *ed_id_rcvd)
+{
+ node_t *node = NULL;
+ tor_addr_port_t orport;
+ routerinfo_t *ri = NULL;
+ time_t now = time(NULL);
+ tor_assert(addr);
+ tor_assert(digest_rcvd);
+
+ node = node_get_mutable_by_id(digest_rcvd);
+ if (node == NULL || node->ri == NULL)
+ return;
+
+ ri = node->ri;
+
+ if (get_options()->AuthDirTestEd25519LinkKeys &&
+ node_supports_ed25519_link_authentication(node, 1) &&
+ ri->cache_info.signing_key_cert) {
+ /* We allow the node to have an ed25519 key if we haven't been told one in
+ * the routerinfo, but if we *HAVE* been told one in the routerinfo, it
+ * needs to match. */
+ const ed25519_public_key_t *expected_id =
+ &ri->cache_info.signing_key_cert->signing_key;
+ tor_assert(!ed25519_public_key_is_zero(expected_id));
+ if (! ed_id_rcvd || ! ed25519_pubkey_eq(ed_id_rcvd, expected_id)) {
+ log_info(LD_DIRSERV, "Router at %s:%d with RSA ID %s "
+ "did not present expected Ed25519 ID.",
+ fmt_addr(addr), or_port, hex_str(digest_rcvd, DIGEST_LEN));
+ return; /* Don't mark it as reachable. */
+ }
+ }
+
+ tor_addr_copy(&orport.addr, addr);
+ orport.port = or_port;
+ if (router_has_orport(ri, &orport)) {
+ /* Found the right router. */
+ if (!authdir_mode_bridge(get_options()) ||
+ ri->purpose == ROUTER_PURPOSE_BRIDGE) {
+ char addrstr[TOR_ADDR_BUF_LEN];
+ /* This is a bridge or we're not a bridge authority --
+ mark it as reachable. */
+ log_info(LD_DIRSERV, "Found router %s to be reachable at %s:%d. Yay.",
+ router_describe(ri),
+ tor_addr_to_str(addrstr, addr, sizeof(addrstr), 1),
+ ri->or_port);
+ if (tor_addr_family(addr) == AF_INET) {
+ rep_hist_note_router_reachable(digest_rcvd, addr, or_port, now);
+ node->last_reachable = now;
+ } else if (tor_addr_family(addr) == AF_INET6) {
+ /* No rephist for IPv6. */
+ node->last_reachable6 = now;
+ }
+ }
+ }
+}
+
+/** Called when we, as an authority, receive a new router descriptor either as
+ * an upload or a download. Used to decide whether to relaunch reachability
+ * testing for the server. */
+int
+dirserv_should_launch_reachability_test(const routerinfo_t *ri,
+ const routerinfo_t *ri_old)
+{
+ if (!authdir_mode_handles_descs(get_options(), ri->purpose))
+ return 0;
+ if (!ri_old) {
+ /* New router: Launch an immediate reachability test, so we will have an
+ * opinion soon in case we're generating a consensus soon */
+ return 1;
+ }
+ if (ri_old->is_hibernating && !ri->is_hibernating) {
+ /* It just came out of hibernation; launch a reachability test */
+ return 1;
+ }
+ if (! routers_have_same_or_addrs(ri, ri_old)) {
+ /* Address or port changed; launch a reachability test */
+ return 1;
+ }
+ return 0;
+}
+
+/** Helper function for dirserv_test_reachability(). Start a TLS
+ * connection to <b>router</b>, and annotate it with when we started
+ * the test. */
+void
+dirserv_single_reachability_test(time_t now, routerinfo_t *router)
+{
+ const or_options_t *options = get_options();
+ channel_t *chan = NULL;
+ const node_t *node = NULL;
+ tor_addr_t router_addr;
+ const ed25519_public_key_t *ed_id_key;
+ (void) now;
+
+ tor_assert(router);
+ node = node_get_by_id(router->cache_info.identity_digest);
+ tor_assert(node);
+
+ if (options->AuthDirTestEd25519LinkKeys &&
+ node_supports_ed25519_link_authentication(node, 1) &&
+ router->cache_info.signing_key_cert) {
+ ed_id_key = &router->cache_info.signing_key_cert->signing_key;
+ } else {
+ ed_id_key = NULL;
+ }
+
+ /* IPv4. */
+ log_debug(LD_OR,"Testing reachability of %s at %s:%u.",
+ router->nickname, fmt_addr32(router->addr), router->or_port);
+ tor_addr_from_ipv4h(&router_addr, router->addr);
+ chan = channel_tls_connect(&router_addr, router->or_port,
+ router->cache_info.identity_digest,
+ ed_id_key);
+ if (chan) command_setup_channel(chan);
+
+ /* Possible IPv6. */
+ if (get_options()->AuthDirHasIPv6Connectivity == 1 &&
+ !tor_addr_is_null(&router->ipv6_addr)) {
+ char addrstr[TOR_ADDR_BUF_LEN];
+ log_debug(LD_OR, "Testing reachability of %s at %s:%u.",
+ router->nickname,
+ tor_addr_to_str(addrstr, &router->ipv6_addr, sizeof(addrstr), 1),
+ router->ipv6_orport);
+ chan = channel_tls_connect(&router->ipv6_addr, router->ipv6_orport,
+ router->cache_info.identity_digest,
+ ed_id_key);
+ if (chan) command_setup_channel(chan);
+ }
+}
+
+/** Auth dir server only: load balance such that we only
+ * try a few connections per call.
+ *
+ * The load balancing is such that if we get called once every ten
+ * seconds, we will cycle through all the tests in
+ * REACHABILITY_TEST_CYCLE_PERIOD seconds (a bit over 20 minutes).
+ */
+void
+dirserv_test_reachability(time_t now)
+{
+ /* XXX decide what to do here; see or-talk thread "purging old router
+ * information, revocation." -NM
+ * We can't afford to mess with this in 0.1.2.x. The reason is that
+ * if we stop doing reachability tests on some of routerlist, then
+ * we'll for-sure think they're down, which may have unexpected
+ * effects in other parts of the code. It doesn't hurt much to do
+ * the testing, and directory authorities are easy to upgrade. Let's
+ * wait til 0.2.0. -RD */
+// time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH;
+ routerlist_t *rl = router_get_routerlist();
+ static char ctr = 0;
+ int bridge_auth = authdir_mode_bridge(get_options());
+
+ SMARTLIST_FOREACH_BEGIN(rl->routers, routerinfo_t *, router) {
+ const char *id_digest = router->cache_info.identity_digest;
+ if (router_is_me(router))
+ continue;
+ if (bridge_auth && router->purpose != ROUTER_PURPOSE_BRIDGE)
+ continue; /* bridge authorities only test reachability on bridges */
+// if (router->cache_info.published_on > cutoff)
+// continue;
+ if ((((uint8_t)id_digest[0]) % REACHABILITY_MODULO_PER_TEST) == ctr) {
+ dirserv_single_reachability_test(now, router);
+ }
+ } SMARTLIST_FOREACH_END(router);
+ ctr = (ctr + 1) % REACHABILITY_MODULO_PER_TEST; /* increment ctr */
+}
diff --git a/src/feature/dirauth/reachability.h b/src/feature/dirauth/reachability.h
new file mode 100644
index 0000000000..6e4bf28ca9
--- /dev/null
+++ b/src/feature/dirauth/reachability.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file reachability.h
+ * \brief Header file for reachability.c.
+ **/
+
+#ifndef TOR_REACHABILITY_H
+#define TOR_REACHABILITY_H
+
+/** What fraction (1 over this number) of the relay ID space do we
+ * (as a directory authority) launch connections to at each reachability
+ * test? */
+#define REACHABILITY_MODULO_PER_TEST 128
+
+/** How often (in seconds) do we launch reachability tests? */
+#define REACHABILITY_TEST_INTERVAL 10
+
+/** How many seconds apart are the reachability tests for a given relay? */
+#define REACHABILITY_TEST_CYCLE_PERIOD \
+ (REACHABILITY_TEST_INTERVAL*REACHABILITY_MODULO_PER_TEST)
+
+void dirserv_orconn_tls_done(const tor_addr_t *addr,
+ uint16_t or_port,
+ const char *digest_rcvd,
+ const struct ed25519_public_key_t *ed_id_rcvd);
+int dirserv_should_launch_reachability_test(const routerinfo_t *ri,
+ const routerinfo_t *ri_old);
+void dirserv_single_reachability_test(time_t now, routerinfo_t *router);
+void dirserv_test_reachability(time_t now);
+
+#endif
diff --git a/src/feature/dirauth/recommend_pkg.c b/src/feature/dirauth/recommend_pkg.c
new file mode 100644
index 0000000000..41c091455e
--- /dev/null
+++ b/src/feature/dirauth/recommend_pkg.c
@@ -0,0 +1,90 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file recommend_pkg.c
+ * \brief Code related to the recommended-packages subsystem.
+ *
+ * Currently unused.
+ **/
+
+#include "core/or/or.h"
+#include "feature/dirauth/recommend_pkg.h"
+
+/** Return true iff <b>line</b> is a valid RecommendedPackages line.
+ */
+/*
+ The grammar is:
+
+ "package" SP PACKAGENAME SP VERSION SP URL SP DIGESTS NL
+
+ PACKAGENAME = NONSPACE
+ VERSION = NONSPACE
+ URL = NONSPACE
+ DIGESTS = DIGEST | DIGESTS SP DIGEST
+ DIGEST = DIGESTTYPE "=" DIGESTVAL
+
+ NONSPACE = one or more non-space printing characters
+
+ DIGESTVAL = DIGESTTYPE = one or more non-=, non-" " characters.
+
+ SP = " "
+ NL = a newline
+
+ */
+int
+validate_recommended_package_line(const char *line)
+{
+ const char *cp = line;
+
+#define WORD() \
+ do { \
+ if (*cp == ' ') \
+ return 0; \
+ cp = strchr(cp, ' '); \
+ if (!cp) \
+ return 0; \
+ } while (0)
+
+ WORD(); /* skip packagename */
+ ++cp;
+ WORD(); /* skip version */
+ ++cp;
+ WORD(); /* Skip URL */
+ ++cp;
+
+ /* Skip digesttype=digestval + */
+ int n_entries = 0;
+ while (1) {
+ const char *start_of_word = cp;
+ const char *end_of_word = strchr(cp, ' ');
+ if (! end_of_word)
+ end_of_word = cp + strlen(cp);
+
+ if (start_of_word == end_of_word)
+ return 0;
+
+ const char *eq = memchr(start_of_word, '=', end_of_word - start_of_word);
+
+ if (!eq)
+ return 0;
+ if (eq == start_of_word)
+ return 0;
+ if (eq == end_of_word - 1)
+ return 0;
+ if (memchr(eq+1, '=', end_of_word - (eq+1)))
+ return 0;
+
+ ++n_entries;
+ if (0 == *end_of_word)
+ break;
+
+ cp = end_of_word + 1;
+ }
+
+ /* If we reach this point, we have at least 1 entry. */
+ tor_assert(n_entries > 0);
+ return 1;
+}
diff --git a/src/feature/dirauth/recommend_pkg.h b/src/feature/dirauth/recommend_pkg.h
new file mode 100644
index 0000000000..29a41d6dff
--- /dev/null
+++ b/src/feature/dirauth/recommend_pkg.h
@@ -0,0 +1,17 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file recommend_pkg.h
+ * \brief Header file for recommend_pkg.c
+ **/
+
+#ifndef TOR_RECOMMEND_PKG_H
+#define TOR_RECOMMEND_PKG_H
+
+int validate_recommended_package_line(const char *line);
+
+#endif
diff --git a/src/feature/dirauth/shared_random.c b/src/feature/dirauth/shared_random.c
index 8ae2679779..85ec82ac00 100644
--- a/src/feature/dirauth/shared_random.c
+++ b/src/feature/dirauth/shared_random.c
@@ -96,7 +96,7 @@
#include "feature/nodelist/networkstatus.h"
#include "feature/relay/router.h"
#include "feature/relay/routerkeys.h"
-#include "feature/nodelist/routerlist.h"
+#include "feature/nodelist/dirlist.h"
#include "feature/hs_common/shared_random_client.h"
#include "feature/dirauth/shared_random_state.h"
#include "feature/dircommon/voting_schedule.h"
@@ -1288,4 +1288,3 @@ set_num_srv_agreements(int32_t value)
}
#endif /* defined(TOR_UNIT_TESTS) */
-
diff --git a/src/feature/dirauth/voteflags.c b/src/feature/dirauth/voteflags.c
new file mode 100644
index 0000000000..4a24dcb50d
--- /dev/null
+++ b/src/feature/dirauth/voteflags.c
@@ -0,0 +1,644 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file voteflags.c
+ * \brief Authority code for deciding the performance thresholds for flags,
+ * and assigning flags to routers.
+ **/
+
+#define VOTEFLAGS_PRIVATE
+#include "core/or/or.h"
+#include "feature/dirauth/voteflags.h"
+
+#include "app/config/config.h"
+#include "core/mainloop/mainloop.h"
+#include "core/or/policies.h"
+#include "feature/dirauth/bwauth.h"
+#include "feature/dirauth/reachability.h"
+#include "feature/hibernate/hibernate.h"
+#include "feature/nodelist/dirlist.h"
+#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/nodelist.h"
+#include "feature/nodelist/routerlist.h"
+#include "feature/nodelist/routerset.h"
+#include "feature/relay/router.h"
+#include "feature/stats/rephist.h"
+
+#include "feature/nodelist/node_st.h"
+#include "feature/nodelist/routerinfo_st.h"
+#include "feature/nodelist/vote_routerstatus_st.h"
+
+#include "lib/container/order.h"
+
+/** If a router's uptime is at least this value, then it is always
+ * considered stable, regardless of the rest of the network. This
+ * way we resist attacks where an attacker doubles the size of the
+ * network using allegedly high-uptime nodes, displacing all the
+ * current guards. */
+#define UPTIME_TO_GUARANTEE_STABLE (3600*24*30)
+/** If a router's MTBF is at least this value, then it is always stable.
+ * See above. (Corresponds to about 7 days for current decay rates.) */
+#define MTBF_TO_GUARANTEE_STABLE (60*60*24*5)
+/** Similarly, every node with at least this much weighted time known can be
+ * considered familiar enough to be a guard. Corresponds to about 20 days for
+ * current decay rates.
+ */
+#define TIME_KNOWN_TO_GUARANTEE_FAMILIAR (8*24*60*60)
+/** Similarly, every node with sufficient WFU is around enough to be a guard.
+ */
+#define WFU_TO_GUARANTEE_GUARD (0.98)
+
+/* Thresholds for server performance: set by
+ * dirserv_compute_performance_thresholds, and used by
+ * generate_v2_networkstatus */
+
+/** Any router with an uptime of at least this value is stable. */
+static uint32_t stable_uptime = 0; /* start at a safe value */
+/** Any router with an mtbf of at least this value is stable. */
+static double stable_mtbf = 0.0;
+/** If true, we have measured enough mtbf info to look at stable_mtbf rather
+ * than stable_uptime. */
+static int enough_mtbf_info = 0;
+/** Any router with a weighted fractional uptime of at least this much might
+ * be good as a guard. */
+static double guard_wfu = 0.0;
+/** Don't call a router a guard unless we've known about it for at least this
+ * many seconds. */
+static long guard_tk = 0;
+/** Any router with a bandwidth at least this high is "Fast" */
+static uint32_t fast_bandwidth_kb = 0;
+/** If exits can be guards, then all guards must have a bandwidth this
+ * high. */
+static uint32_t guard_bandwidth_including_exits_kb = 0;
+/** If exits can't be guards, then all guards must have a bandwidth this
+ * high. */
+static uint32_t guard_bandwidth_excluding_exits_kb = 0;
+
+/** Helper: estimate the uptime of a router given its stated uptime and the
+ * amount of time since it last stated its stated uptime. */
+static inline long
+real_uptime(const routerinfo_t *router, time_t now)
+{
+ if (now < router->cache_info.published_on)
+ return router->uptime;
+ else
+ return router->uptime + (now - router->cache_info.published_on);
+}
+
+/** Return 1 if <b>router</b> is not suitable for these parameters, else 0.
+ * If <b>need_uptime</b> is non-zero, we require a minimum uptime.
+ * If <b>need_capacity</b> is non-zero, we require a minimum advertised
+ * bandwidth.
+ */
+static int
+dirserv_thinks_router_is_unreliable(time_t now,
+ routerinfo_t *router,
+ int need_uptime, int need_capacity)
+{
+ if (need_uptime) {
+ if (!enough_mtbf_info) {
+ /* XXXX We should change the rule from
+ * "use uptime if we don't have mtbf data" to "don't advertise Stable on
+ * v3 if we don't have enough mtbf data." Or maybe not, since if we ever
+ * hit a point where we need to reset a lot of authorities at once,
+ * none of them would be in a position to declare Stable.
+ */
+ long uptime = real_uptime(router, now);
+ if ((unsigned)uptime < stable_uptime &&
+ (unsigned)uptime < UPTIME_TO_GUARANTEE_STABLE)
+ return 1;
+ } else {
+ double mtbf =
+ rep_hist_get_stability(router->cache_info.identity_digest, now);
+ if (mtbf < stable_mtbf &&
+ mtbf < MTBF_TO_GUARANTEE_STABLE)
+ return 1;
+ }
+ }
+ if (need_capacity) {
+ uint32_t bw_kb = dirserv_get_credible_bandwidth_kb(router);
+ if (bw_kb < fast_bandwidth_kb)
+ return 1;
+ }
+ return 0;
+}
+
+/** Return 1 if <b>ri</b>'s descriptor is "active" -- running, valid,
+ * not hibernating, having observed bw greater 0, and not too old. Else
+ * return 0.
+ */
+static int
+router_is_active(const routerinfo_t *ri, const node_t *node, time_t now)
+{
+ time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH;
+ if (ri->cache_info.published_on < cutoff) {
+ return 0;
+ }
+ if (!node->is_running || !node->is_valid || ri->is_hibernating) {
+ return 0;
+ }
+ /* Only require bandwidth capacity in non-test networks, or
+ * if TestingTorNetwork, and TestingMinExitFlagThreshold is non-zero */
+ if (!ri->bandwidthcapacity) {
+ if (get_options()->TestingTorNetwork) {
+ if (get_options()->TestingMinExitFlagThreshold > 0) {
+ /* If we're in a TestingTorNetwork, and TestingMinExitFlagThreshold is,
+ * then require bandwidthcapacity */
+ return 0;
+ }
+ } else {
+ /* If we're not in a TestingTorNetwork, then require bandwidthcapacity */
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/** Return true iff <b>router</b> should be assigned the "HSDir" flag.
+ *
+ * Right now this means it advertises support for it, it has a high uptime,
+ * it's a directory cache, it has the Stable and Fast flags, and it's currently
+ * considered Running.
+ *
+ * This function needs to be called after router-\>is_running has
+ * been set.
+ */
+static int
+dirserv_thinks_router_is_hs_dir(const routerinfo_t *router,
+ const node_t *node, time_t now)
+{
+
+ long uptime;
+
+ /* If we haven't been running for at least
+ * get_options()->MinUptimeHidServDirectoryV2 seconds, we can't
+ * have accurate data telling us a relay has been up for at least
+ * that long. We also want to allow a bit of slack: Reachability
+ * tests aren't instant. If we haven't been running long enough,
+ * trust the relay. */
+
+ if (get_uptime() >
+ get_options()->MinUptimeHidServDirectoryV2 * 1.1)
+ uptime = MIN(rep_hist_get_uptime(router->cache_info.identity_digest, now),
+ real_uptime(router, now));
+ else
+ uptime = real_uptime(router, now);
+
+ return (router->wants_to_be_hs_dir &&
+ router->supports_tunnelled_dir_requests &&
+ node->is_stable && node->is_fast &&
+ uptime >= get_options()->MinUptimeHidServDirectoryV2 &&
+ router_is_active(router, node, now));
+}
+
+/** Don't consider routers with less bandwidth than this when computing
+ * thresholds. */
+#define ABSOLUTE_MIN_BW_VALUE_TO_CONSIDER_KB 4
+
+/** Helper for dirserv_compute_performance_thresholds(): Decide whether to
+ * include a router in our calculations, and return true iff we should; the
+ * require_mbw parameter is passed in by
+ * dirserv_compute_performance_thresholds() and controls whether we ever
+ * count routers with only advertised bandwidths */
+static int
+router_counts_toward_thresholds(const node_t *node, time_t now,
+ const digestmap_t *omit_as_sybil,
+ int require_mbw)
+{
+ /* Have measured bw? */
+ int have_mbw =
+ dirserv_has_measured_bw(node->identity);
+ uint64_t min_bw_kb = ABSOLUTE_MIN_BW_VALUE_TO_CONSIDER_KB;
+ const or_options_t *options = get_options();
+
+ if (options->TestingTorNetwork) {
+ min_bw_kb = (int64_t)options->TestingMinExitFlagThreshold / 1000;
+ }
+
+ return node->ri && router_is_active(node->ri, node, now) &&
+ !digestmap_get(omit_as_sybil, node->identity) &&
+ (dirserv_get_credible_bandwidth_kb(node->ri) >= min_bw_kb) &&
+ (have_mbw || !require_mbw);
+}
+
+/** Look through the routerlist, the Mean Time Between Failure history, and
+ * the Weighted Fractional Uptime history, and use them to set thresholds for
+ * the Stable, Fast, and Guard flags. Update the fields stable_uptime,
+ * stable_mtbf, enough_mtbf_info, guard_wfu, guard_tk, fast_bandwidth,
+ * guard_bandwidth_including_exits, and guard_bandwidth_excluding_exits.
+ *
+ * Also, set the is_exit flag of each router appropriately. */
+void
+dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil)
+{
+ int n_active, n_active_nonexit, n_familiar;
+ uint32_t *uptimes, *bandwidths_kb, *bandwidths_excluding_exits_kb;
+ long *tks;
+ double *mtbfs, *wfus;
+ smartlist_t *nodelist;
+ time_t now = time(NULL);
+ const or_options_t *options = get_options();
+
+ /* Require mbw? */
+ int require_mbw =
+ (dirserv_get_last_n_measured_bws() >
+ options->MinMeasuredBWsForAuthToIgnoreAdvertised) ? 1 : 0;
+
+ /* initialize these all here, in case there are no routers */
+ stable_uptime = 0;
+ stable_mtbf = 0;
+ fast_bandwidth_kb = 0;
+ guard_bandwidth_including_exits_kb = 0;
+ guard_bandwidth_excluding_exits_kb = 0;
+ guard_tk = 0;
+ guard_wfu = 0;
+
+ nodelist_assert_ok();
+ nodelist = nodelist_get_list();
+
+ /* Initialize arrays that will hold values for each router. We'll
+ * sort them and use that to compute thresholds. */
+ n_active = n_active_nonexit = 0;
+ /* Uptime for every active router. */
+ uptimes = tor_calloc(smartlist_len(nodelist), sizeof(uint32_t));
+ /* Bandwidth for every active router. */
+ bandwidths_kb = tor_calloc(smartlist_len(nodelist), sizeof(uint32_t));
+ /* Bandwidth for every active non-exit router. */
+ bandwidths_excluding_exits_kb =
+ tor_calloc(smartlist_len(nodelist), sizeof(uint32_t));
+ /* Weighted mean time between failure for each active router. */
+ mtbfs = tor_calloc(smartlist_len(nodelist), sizeof(double));
+ /* Time-known for each active router. */
+ tks = tor_calloc(smartlist_len(nodelist), sizeof(long));
+ /* Weighted fractional uptime for each active router. */
+ wfus = tor_calloc(smartlist_len(nodelist), sizeof(double));
+
+ /* Now, fill in the arrays. */
+ SMARTLIST_FOREACH_BEGIN(nodelist, node_t *, node) {
+ if (options->BridgeAuthoritativeDir &&
+ node->ri &&
+ node->ri->purpose != ROUTER_PURPOSE_BRIDGE)
+ continue;
+
+ routerinfo_t *ri = node->ri;
+ if (ri) {
+ node->is_exit = (!router_exit_policy_rejects_all(ri) &&
+ exit_policy_is_general_exit(ri->exit_policy));
+ }
+
+ if (router_counts_toward_thresholds(node, now, omit_as_sybil,
+ require_mbw)) {
+ const char *id = node->identity;
+ uint32_t bw_kb;
+
+ /* resolve spurious clang shallow analysis null pointer errors */
+ tor_assert(ri);
+
+ uptimes[n_active] = (uint32_t)real_uptime(ri, now);
+ mtbfs[n_active] = rep_hist_get_stability(id, now);
+ tks [n_active] = rep_hist_get_weighted_time_known(id, now);
+ bandwidths_kb[n_active] = bw_kb = dirserv_get_credible_bandwidth_kb(ri);
+ if (!node->is_exit || node->is_bad_exit) {
+ bandwidths_excluding_exits_kb[n_active_nonexit] = bw_kb;
+ ++n_active_nonexit;
+ }
+ ++n_active;
+ }
+ } SMARTLIST_FOREACH_END(node);
+
+ /* Now, compute thresholds. */
+ if (n_active) {
+ /* The median uptime is stable. */
+ stable_uptime = median_uint32(uptimes, n_active);
+ /* The median mtbf is stable, if we have enough mtbf info */
+ stable_mtbf = median_double(mtbfs, n_active);
+ /* The 12.5th percentile bandwidth is fast. */
+ fast_bandwidth_kb = find_nth_uint32(bandwidths_kb, n_active, n_active/8);
+ /* (Now bandwidths is sorted.) */
+ if (fast_bandwidth_kb < RELAY_REQUIRED_MIN_BANDWIDTH/(2 * 1000))
+ fast_bandwidth_kb = bandwidths_kb[n_active/4];
+ guard_bandwidth_including_exits_kb =
+ third_quartile_uint32(bandwidths_kb, n_active);
+ guard_tk = find_nth_long(tks, n_active, n_active/8);
+ }
+
+ if (guard_tk > TIME_KNOWN_TO_GUARANTEE_FAMILIAR)
+ guard_tk = TIME_KNOWN_TO_GUARANTEE_FAMILIAR;
+
+ {
+ /* We can vote on a parameter for the minimum and maximum. */
+#define ABSOLUTE_MIN_VALUE_FOR_FAST_FLAG 4
+ int32_t min_fast_kb, max_fast_kb, min_fast, max_fast;
+ min_fast = networkstatus_get_param(NULL, "FastFlagMinThreshold",
+ ABSOLUTE_MIN_VALUE_FOR_FAST_FLAG,
+ ABSOLUTE_MIN_VALUE_FOR_FAST_FLAG,
+ INT32_MAX);
+ if (options->TestingTorNetwork) {
+ min_fast = (int32_t)options->TestingMinFastFlagThreshold;
+ }
+ max_fast = networkstatus_get_param(NULL, "FastFlagMaxThreshold",
+ INT32_MAX, min_fast, INT32_MAX);
+ min_fast_kb = min_fast / 1000;
+ max_fast_kb = max_fast / 1000;
+
+ if (fast_bandwidth_kb < (uint32_t)min_fast_kb)
+ fast_bandwidth_kb = min_fast_kb;
+ if (fast_bandwidth_kb > (uint32_t)max_fast_kb)
+ fast_bandwidth_kb = max_fast_kb;
+ }
+ /* Protect sufficiently fast nodes from being pushed out of the set
+ * of Fast nodes. */
+ if (options->AuthDirFastGuarantee &&
+ fast_bandwidth_kb > options->AuthDirFastGuarantee/1000)
+ fast_bandwidth_kb = (uint32_t)options->AuthDirFastGuarantee/1000;
+
+ /* Now that we have a time-known that 7/8 routers are known longer than,
+ * fill wfus with the wfu of every such "familiar" router. */
+ n_familiar = 0;
+
+ SMARTLIST_FOREACH_BEGIN(nodelist, node_t *, node) {
+ if (router_counts_toward_thresholds(node, now,
+ omit_as_sybil, require_mbw)) {
+ routerinfo_t *ri = node->ri;
+ const char *id = ri->cache_info.identity_digest;
+ long tk = rep_hist_get_weighted_time_known(id, now);
+ if (tk < guard_tk)
+ continue;
+ wfus[n_familiar++] = rep_hist_get_weighted_fractional_uptime(id, now);
+ }
+ } SMARTLIST_FOREACH_END(node);
+ if (n_familiar)
+ guard_wfu = median_double(wfus, n_familiar);
+ if (guard_wfu > WFU_TO_GUARANTEE_GUARD)
+ guard_wfu = WFU_TO_GUARANTEE_GUARD;
+
+ enough_mtbf_info = rep_hist_have_measured_enough_stability();
+
+ if (n_active_nonexit) {
+ guard_bandwidth_excluding_exits_kb =
+ find_nth_uint32(bandwidths_excluding_exits_kb,
+ n_active_nonexit, n_active_nonexit*3/4);
+ }
+
+ log_info(LD_DIRSERV,
+ "Cutoffs: For Stable, %lu sec uptime, %lu sec MTBF. "
+ "For Fast: %lu kilobytes/sec. "
+ "For Guard: WFU %.03f%%, time-known %lu sec, "
+ "and bandwidth %lu or %lu kilobytes/sec. "
+ "We%s have enough stability data.",
+ (unsigned long)stable_uptime,
+ (unsigned long)stable_mtbf,
+ (unsigned long)fast_bandwidth_kb,
+ guard_wfu*100,
+ (unsigned long)guard_tk,
+ (unsigned long)guard_bandwidth_including_exits_kb,
+ (unsigned long)guard_bandwidth_excluding_exits_kb,
+ enough_mtbf_info ? "" : " don't");
+
+ tor_free(uptimes);
+ tor_free(mtbfs);
+ tor_free(bandwidths_kb);
+ tor_free(bandwidths_excluding_exits_kb);
+ tor_free(tks);
+ tor_free(wfus);
+}
+
+/* Use dirserv_compute_performance_thresholds() to compute the thresholds
+ * for the status flags, specifically for bridges.
+ *
+ * This is only called by a Bridge Authority from
+ * networkstatus_getinfo_by_purpose().
+ */
+void
+dirserv_compute_bridge_flag_thresholds(void)
+{
+ digestmap_t *omit_as_sybil = digestmap_new();
+ dirserv_compute_performance_thresholds(omit_as_sybil);
+ digestmap_free(omit_as_sybil, NULL);
+}
+
+/** Give a statement of our current performance thresholds for inclusion
+ * in a vote document. */
+char *
+dirserv_get_flag_thresholds_line(void)
+{
+ char *result=NULL;
+ const int measured_threshold =
+ get_options()->MinMeasuredBWsForAuthToIgnoreAdvertised;
+ const int enough_measured_bw =
+ dirserv_get_last_n_measured_bws() > measured_threshold;
+
+ tor_asprintf(&result,
+ "stable-uptime=%lu stable-mtbf=%lu "
+ "fast-speed=%lu "
+ "guard-wfu=%.03f%% guard-tk=%lu "
+ "guard-bw-inc-exits=%lu guard-bw-exc-exits=%lu "
+ "enough-mtbf=%d ignoring-advertised-bws=%d",
+ (unsigned long)stable_uptime,
+ (unsigned long)stable_mtbf,
+ (unsigned long)fast_bandwidth_kb*1000,
+ guard_wfu*100,
+ (unsigned long)guard_tk,
+ (unsigned long)guard_bandwidth_including_exits_kb*1000,
+ (unsigned long)guard_bandwidth_excluding_exits_kb*1000,
+ enough_mtbf_info ? 1 : 0,
+ enough_measured_bw ? 1 : 0);
+
+ return result;
+}
+
+/* DOCDOC running_long_enough_to_decide_unreachable */
+int
+running_long_enough_to_decide_unreachable(void)
+{
+ return time_of_process_start
+ + get_options()->TestingAuthDirTimeToLearnReachability < approx_time();
+}
+
+/** Each server needs to have passed a reachability test no more
+ * than this number of seconds ago, or it is listed as down in
+ * the directory. */
+#define REACHABLE_TIMEOUT (45*60)
+
+/** If we tested a router and found it reachable _at least this long_ after it
+ * declared itself hibernating, it is probably done hibernating and we just
+ * missed a descriptor from it. */
+#define HIBERNATION_PUBLICATION_SKEW (60*60)
+
+/** Treat a router as alive if
+ * - It's me, and I'm not hibernating.
+ * or - We've found it reachable recently. */
+void
+dirserv_set_router_is_running(routerinfo_t *router, time_t now)
+{
+ /*XXXX This function is a mess. Separate out the part that calculates
+ whether it's reachable and the part that tells rephist that the router was
+ unreachable.
+ */
+ int answer;
+ const or_options_t *options = get_options();
+ node_t *node = node_get_mutable_by_id(router->cache_info.identity_digest);
+ tor_assert(node);
+
+ if (router_is_me(router)) {
+ /* We always know if we are shutting down or hibernating ourselves. */
+ answer = ! we_are_hibernating();
+ } else if (router->is_hibernating &&
+ (router->cache_info.published_on +
+ HIBERNATION_PUBLICATION_SKEW) > node->last_reachable) {
+ /* A hibernating router is down unless we (somehow) had contact with it
+ * since it declared itself to be hibernating. */
+ answer = 0;
+ } else if (options->AssumeReachable) {
+ /* If AssumeReachable, everybody is up unless they say they are down! */
+ answer = 1;
+ } else {
+ /* Otherwise, a router counts as up if we found all announced OR
+ ports reachable in the last REACHABLE_TIMEOUT seconds.
+
+ XXX prop186 For now there's always one IPv4 and at most one
+ IPv6 OR port.
+
+ If we're not on IPv6, don't consider reachability of potential
+ IPv6 OR port since that'd kill all dual stack relays until a
+ majority of the dir auths have IPv6 connectivity. */
+ answer = (now < node->last_reachable + REACHABLE_TIMEOUT &&
+ (options->AuthDirHasIPv6Connectivity != 1 ||
+ tor_addr_is_null(&router->ipv6_addr) ||
+ now < node->last_reachable6 + REACHABLE_TIMEOUT));
+ }
+
+ if (!answer && running_long_enough_to_decide_unreachable()) {
+ /* Not considered reachable. tell rephist about that.
+
+ Because we launch a reachability test for each router every
+ REACHABILITY_TEST_CYCLE_PERIOD seconds, then the router has probably
+ been down since at least that time after we last successfully reached
+ it.
+
+ XXX ipv6
+ */
+ time_t when = now;
+ if (node->last_reachable &&
+ node->last_reachable + REACHABILITY_TEST_CYCLE_PERIOD < now)
+ when = node->last_reachable + REACHABILITY_TEST_CYCLE_PERIOD;
+ rep_hist_note_router_unreachable(router->cache_info.identity_digest, when);
+ }
+
+ node->is_running = answer;
+}
+
+/** Extract status information from <b>ri</b> and from other authority
+ * functions and store it in <b>rs</b>. <b>rs</b> is zeroed out before it is
+ * set.
+ *
+ * We assume that ri-\>is_running has already been set, e.g. by
+ * dirserv_set_router_is_running(ri, now);
+ */
+void
+set_routerstatus_from_routerinfo(routerstatus_t *rs,
+ node_t *node,
+ routerinfo_t *ri,
+ time_t now,
+ int listbadexits)
+{
+ const or_options_t *options = get_options();
+ uint32_t routerbw_kb = dirserv_get_credible_bandwidth_kb(ri);
+
+ memset(rs, 0, sizeof(routerstatus_t));
+
+ rs->is_authority =
+ router_digest_is_trusted_dir(ri->cache_info.identity_digest);
+
+ /* Already set by compute_performance_thresholds. */
+ rs->is_exit = node->is_exit;
+ rs->is_stable = node->is_stable =
+ !dirserv_thinks_router_is_unreliable(now, ri, 1, 0);
+ rs->is_fast = node->is_fast =
+ !dirserv_thinks_router_is_unreliable(now, ri, 0, 1);
+ rs->is_flagged_running = node->is_running; /* computed above */
+
+ rs->is_valid = node->is_valid;
+
+ if (node->is_fast && node->is_stable &&
+ ri->supports_tunnelled_dir_requests &&
+ ((options->AuthDirGuardBWGuarantee &&
+ routerbw_kb >= options->AuthDirGuardBWGuarantee/1000) ||
+ routerbw_kb >= MIN(guard_bandwidth_including_exits_kb,
+ guard_bandwidth_excluding_exits_kb))) {
+ long tk = rep_hist_get_weighted_time_known(
+ node->identity, now);
+ double wfu = rep_hist_get_weighted_fractional_uptime(
+ node->identity, now);
+ rs->is_possible_guard = (wfu >= guard_wfu && tk >= guard_tk) ? 1 : 0;
+ } else {
+ rs->is_possible_guard = 0;
+ }
+
+ rs->is_bad_exit = listbadexits && node->is_bad_exit;
+ rs->is_hs_dir = node->is_hs_dir =
+ dirserv_thinks_router_is_hs_dir(ri, node, now);
+
+ rs->is_named = rs->is_unnamed = 0;
+
+ rs->published_on = ri->cache_info.published_on;
+ memcpy(rs->identity_digest, node->identity, DIGEST_LEN);
+ memcpy(rs->descriptor_digest, ri->cache_info.signed_descriptor_digest,
+ DIGEST_LEN);
+ rs->addr = ri->addr;
+ strlcpy(rs->nickname, ri->nickname, sizeof(rs->nickname));
+ rs->or_port = ri->or_port;
+ rs->dir_port = ri->dir_port;
+ rs->is_v2_dir = ri->supports_tunnelled_dir_requests;
+ if (options->AuthDirHasIPv6Connectivity == 1 &&
+ !tor_addr_is_null(&ri->ipv6_addr) &&
+ node->last_reachable6 >= now - REACHABLE_TIMEOUT) {
+ /* We're configured as having IPv6 connectivity. There's an IPv6
+ OR port and it's reachable so copy it to the routerstatus. */
+ tor_addr_copy(&rs->ipv6_addr, &ri->ipv6_addr);
+ rs->ipv6_orport = ri->ipv6_orport;
+ } else {
+ tor_addr_make_null(&rs->ipv6_addr, AF_INET6);
+ rs->ipv6_orport = 0;
+ }
+
+ if (options->TestingTorNetwork) {
+ dirserv_set_routerstatus_testing(rs);
+ }
+}
+
+/** Use TestingDirAuthVoteExit, TestingDirAuthVoteGuard, and
+ * TestingDirAuthVoteHSDir to give out the Exit, Guard, and HSDir flags,
+ * respectively. But don't set the corresponding node flags.
+ * Should only be called if TestingTorNetwork is set. */
+STATIC void
+dirserv_set_routerstatus_testing(routerstatus_t *rs)
+{
+ const or_options_t *options = get_options();
+
+ tor_assert(options->TestingTorNetwork);
+
+ if (routerset_contains_routerstatus(options->TestingDirAuthVoteExit,
+ rs, 0)) {
+ rs->is_exit = 1;
+ } else if (options->TestingDirAuthVoteExitIsStrict) {
+ rs->is_exit = 0;
+ }
+
+ if (routerset_contains_routerstatus(options->TestingDirAuthVoteGuard,
+ rs, 0)) {
+ rs->is_possible_guard = 1;
+ } else if (options->TestingDirAuthVoteGuardIsStrict) {
+ rs->is_possible_guard = 0;
+ }
+
+ if (routerset_contains_routerstatus(options->TestingDirAuthVoteHSDir,
+ rs, 0)) {
+ rs->is_hs_dir = 1;
+ } else if (options->TestingDirAuthVoteHSDirIsStrict) {
+ rs->is_hs_dir = 0;
+ }
+}
diff --git a/src/feature/dirauth/voteflags.h b/src/feature/dirauth/voteflags.h
new file mode 100644
index 0000000000..2f0e061ea4
--- /dev/null
+++ b/src/feature/dirauth/voteflags.h
@@ -0,0 +1,31 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file voteflags.h
+ * \brief Header file for voteflags.c
+ **/
+
+#ifndef TOR_VOTEFLAGS_H
+#define TOR_VOTEFLAGS_H
+
+void dirserv_set_router_is_running(routerinfo_t *router, time_t now);
+char *dirserv_get_flag_thresholds_line(void);
+void dirserv_compute_bridge_flag_thresholds(void);
+int running_long_enough_to_decide_unreachable(void);
+
+void set_routerstatus_from_routerinfo(routerstatus_t *rs,
+ node_t *node,
+ routerinfo_t *ri, time_t now,
+ int listbadexits);
+
+void dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil);
+
+#ifdef VOTEFLAGS_PRIVATE
+STATIC void dirserv_set_routerstatus_testing(routerstatus_t *rs);
+#endif
+
+#endif
diff --git a/src/feature/dircache/directory.c b/src/feature/dircache/directory.c
index b94c5317af..7066e92c22 100644
--- a/src/feature/dircache/directory.c
+++ b/src/feature/dircache/directory.c
@@ -22,6 +22,7 @@
#include "lib/crypt_ops/crypto_util.h"
#include "feature/dircache/directory.h"
#include "feature/dircache/dirserv.h"
+#include "feature/dirauth/process_descs.h"
#include "feature/client/entrynodes.h"
#include "feature/dircommon/fp_pair.h"
#include "feature/stats/geoip.h"
@@ -29,7 +30,7 @@
#include "feature/hs/hs_common.h"
#include "feature/hs/hs_control.h"
#include "feature/hs/hs_client.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
@@ -40,7 +41,11 @@
#include "feature/rend/rendservice.h"
#include "feature/stats/rephist.h"
#include "feature/relay/router.h"
+#include "feature/nodelist/authcert.h"
+#include "feature/nodelist/dirlist.h"
+#include "feature/nodelist/node_select.h"
#include "feature/nodelist/routerlist.h"
+
#include "feature/nodelist/routerparse.h"
#include "feature/nodelist/routerset.h"
#include "lib/encoding/confline.h"
diff --git a/src/feature/dircache/dirserv.c b/src/feature/dircache/dirserv.c
index b85db8324f..2fd2572a2c 100644
--- a/src/feature/dircache/dirserv.c
+++ b/src/feature/dircache/dirserv.c
@@ -5,53 +5,27 @@
#define DIRSERV_PRIVATE
#include "core/or/or.h"
-#include "lib/container/buffers.h"
+
#include "app/config/config.h"
-#include "app/config/confparse.h"
-#include "core/or/channel.h"
-#include "core/or/channeltls.h"
-#include "core/or/command.h"
#include "core/mainloop/connection.h"
-#include "core/or/connection_or.h"
#include "feature/dircache/conscache.h"
#include "feature/dircache/consdiffmgr.h"
-#include "feature/control/control.h"
#include "feature/dircache/directory.h"
#include "feature/dircache/dirserv.h"
-#include "feature/hibernate/hibernate.h"
-#include "feature/dirauth/keypin.h"
-#include "core/mainloop/main.h"
#include "feature/nodelist/microdesc.h"
-#include "feature/nodelist/networkstatus.h"
-#include "feature/nodelist/nodelist.h"
-#include "core/or/policies.h"
-#include "core/or/protover.h"
-#include "feature/stats/rephist.h"
-#include "feature/relay/router.h"
#include "feature/nodelist/routerlist.h"
-#include "feature/nodelist/routerparse.h"
-#include "feature/nodelist/routerset.h"
-#include "feature/nodelist/torcert.h"
-#include "feature/dircommon/voting_schedule.h"
-
-#include "feature/dirauth/dirvote.h"
+#include "feature/relay/router.h"
+#include "feature/stats/rephist.h"
#include "feature/dircache/cached_dir_st.h"
#include "feature/dircommon/dir_connection_st.h"
#include "feature/nodelist/extrainfo_st.h"
#include "feature/nodelist/microdesc_st.h"
-#include "feature/nodelist/node_st.h"
#include "feature/nodelist/routerinfo_st.h"
#include "feature/nodelist/routerlist_st.h"
-#include "core/or/tor_version_st.h"
-#include "feature/nodelist/vote_routerstatus_st.h"
#include "lib/compress/compress.h"
-#include "lib/container/order.h"
-#include "lib/crypt_ops/crypto_format.h"
-#include "lib/encoding/confline.h"
-#include "lib/encoding/keyval.h"
/**
* \file dirserv.c
* \brief Directory server core implementation. Manages directory
@@ -76,35 +50,10 @@
* shared among the v1, v2, and v3 directory code.)
*/
-/** How far in the future do we allow a router to get? (seconds) */
-#define ROUTER_ALLOW_SKEW (60*60*12)
-/** How many seconds do we wait before regenerating the directory? */
-#define DIR_REGEN_SLACK_TIME 30
-/** If we're a cache, keep this many networkstatuses around from non-trusted
- * directory authorities. */
-#define MAX_UNTRUSTED_NETWORKSTATUSES 16
-
-/** Total number of routers with measured bandwidth; this is set by
- * dirserv_count_measured_bws() before the loop in
- * dirserv_generate_networkstatus_vote_obj() and checked by
- * dirserv_get_credible_bandwidth() and
- * dirserv_compute_performance_thresholds() */
-static int routers_with_measured_bw = 0;
-
-static void directory_remove_invalid(void);
-struct authdir_config_t;
-static uint32_t
-dirserv_get_status_impl(const char *fp, const char *nickname,
- uint32_t addr, uint16_t or_port,
- const char *platform, const char **msg,
- int severity);
static void clear_cached_dir(cached_dir_t *d);
static const signed_descriptor_t *get_signed_descriptor_by_fp(
const uint8_t *fp,
int extrainfo);
-static was_router_added_t dirserv_add_extrainfo(extrainfo_t *ei,
- const char **msg);
-static uint32_t dirserv_get_credible_bandwidth_kb(const routerinfo_t *ri);
static int spooled_resource_lookup_body(const spooled_resource_t *spooled,
int conn_is_encrypted,
@@ -116,994 +65,6 @@ static cached_dir_t *spooled_resource_lookup_cached_dir(
time_t *published_out);
static cached_dir_t *lookup_cached_dir_by_fp(const uint8_t *fp);
-/************** Fingerprint handling code ************/
-
-/* 1 Historically used to indicate Named */
-#define FP_INVALID 2 /**< Believed invalid. */
-#define FP_REJECT 4 /**< We will not publish this router. */
-/* 8 Historically used to avoid using this as a dir. */
-#define FP_BADEXIT 16 /**< We'll tell clients not to use this as an exit. */
-/* 32 Historically used to indicade Unnamed */
-
-/** Target of status_by_digest map. */
-typedef uint32_t router_status_t;
-
-static void add_fingerprint_to_dir(const char *fp,
- struct authdir_config_t *list,
- router_status_t add_status);
-
-/** List of nickname-\>identity fingerprint mappings for all the routers
- * that we name. Used to prevent router impersonation. */
-typedef struct authdir_config_t {
- strmap_t *fp_by_name; /**< Map from lc nickname to fingerprint. */
- digestmap_t *status_by_digest; /**< Map from digest to router_status_t. */
-} authdir_config_t;
-
-/** Should be static; exposed for testing. */
-static authdir_config_t *fingerprint_list = NULL;
-
-/** Allocate and return a new, empty, authdir_config_t. */
-static authdir_config_t *
-authdir_config_new(void)
-{
- authdir_config_t *list = tor_malloc_zero(sizeof(authdir_config_t));
- list->fp_by_name = strmap_new();
- list->status_by_digest = digestmap_new();
- return list;
-}
-
-/** Add the fingerprint <b>fp</b> to the smartlist of fingerprint_entry_t's
- * <b>list</b>, or-ing the currently set status flags with
- * <b>add_status</b>.
- */
-/* static */ void
-add_fingerprint_to_dir(const char *fp, authdir_config_t *list,
- router_status_t add_status)
-{
- char *fingerprint;
- char d[DIGEST_LEN];
- router_status_t *status;
- tor_assert(fp);
- tor_assert(list);
-
- fingerprint = tor_strdup(fp);
- tor_strstrip(fingerprint, " ");
- if (base16_decode(d, DIGEST_LEN,
- fingerprint, strlen(fingerprint)) != DIGEST_LEN) {
- log_warn(LD_DIRSERV, "Couldn't decode fingerprint \"%s\"",
- escaped(fp));
- tor_free(fingerprint);
- return;
- }
-
- status = digestmap_get(list->status_by_digest, d);
- if (!status) {
- status = tor_malloc_zero(sizeof(router_status_t));
- digestmap_set(list->status_by_digest, d, status);
- }
-
- tor_free(fingerprint);
- *status |= add_status;
- return;
-}
-
-/** Add the fingerprint for this OR to the global list of recognized
- * identity key fingerprints. */
-int
-dirserv_add_own_fingerprint(crypto_pk_t *pk)
-{
- char fp[FINGERPRINT_LEN+1];
- if (crypto_pk_get_fingerprint(pk, fp, 0)<0) {
- log_err(LD_BUG, "Error computing fingerprint");
- return -1;
- }
- if (!fingerprint_list)
- fingerprint_list = authdir_config_new();
- add_fingerprint_to_dir(fp, fingerprint_list, 0);
- return 0;
-}
-
-/** Load the nickname-\>fingerprint mappings stored in the approved-routers
- * file. The file format is line-based, with each non-blank holding one
- * nickname, some space, and a fingerprint for that nickname. On success,
- * replace the current fingerprint list with the new list and return 0. On
- * failure, leave the current fingerprint list untouched, and return -1. */
-int
-dirserv_load_fingerprint_file(void)
-{
- char *fname;
- char *cf;
- char *nickname, *fingerprint;
- authdir_config_t *fingerprint_list_new;
- int result;
- config_line_t *front=NULL, *list;
-
- fname = get_datadir_fname("approved-routers");
- log_info(LD_GENERAL,
- "Reloading approved fingerprints from \"%s\"...", fname);
-
- cf = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL);
- if (!cf) {
- log_warn(LD_FS, "Cannot open fingerprint file '%s'. That's ok.", fname);
- tor_free(fname);
- return 0;
- }
- tor_free(fname);
-
- result = config_get_lines(cf, &front, 0);
- tor_free(cf);
- if (result < 0) {
- log_warn(LD_CONFIG, "Error reading from fingerprint file");
- return -1;
- }
-
- fingerprint_list_new = authdir_config_new();
-
- for (list=front; list; list=list->next) {
- char digest_tmp[DIGEST_LEN];
- router_status_t add_status = 0;
- nickname = list->key; fingerprint = list->value;
- tor_strstrip(fingerprint, " "); /* remove spaces */
- if (strlen(fingerprint) != HEX_DIGEST_LEN ||
- base16_decode(digest_tmp, sizeof(digest_tmp),
- fingerprint, HEX_DIGEST_LEN) != sizeof(digest_tmp)) {
- log_notice(LD_CONFIG,
- "Invalid fingerprint (nickname '%s', "
- "fingerprint %s). Skipping.",
- nickname, fingerprint);
- continue;
- }
- if (!strcasecmp(nickname, "!reject")) {
- add_status = FP_REJECT;
- } else if (!strcasecmp(nickname, "!badexit")) {
- add_status = FP_BADEXIT;
- } else if (!strcasecmp(nickname, "!invalid")) {
- add_status = FP_INVALID;
- }
- add_fingerprint_to_dir(fingerprint, fingerprint_list_new, add_status);
- }
-
- config_free_lines(front);
- dirserv_free_fingerprint_list();
- fingerprint_list = fingerprint_list_new;
- /* Delete any routers whose fingerprints we no longer recognize */
- directory_remove_invalid();
- return 0;
-}
-
-/* If this is set, then we don't allow routers that have advertised an Ed25519
- * identity to stop doing so. This is going to be essential for good identity
- * security: otherwise anybody who can attack RSA-1024 but not Ed25519 could
- * just sign fake descriptors missing the Ed25519 key. But we won't actually
- * be able to prevent that kind of thing until we're confident that there isn't
- * actually a legit reason to downgrade to 0.2.5. Now we are not recommending
- * 0.2.5 anymore so there is no reason to keep the #undef.
- */
-
-#define DISABLE_DISABLING_ED25519
-
-/** Check whether <b>router</b> has a nickname/identity key combination that
- * we recognize from the fingerprint list, or an IP we automatically act on
- * according to our configuration. Return the appropriate router status.
- *
- * If the status is 'FP_REJECT' and <b>msg</b> is provided, set
- * *<b>msg</b> to an explanation of why. */
-uint32_t
-dirserv_router_get_status(const routerinfo_t *router, const char **msg,
- int severity)
-{
- char d[DIGEST_LEN];
- const int key_pinning = get_options()->AuthDirPinKeys;
-
- if (crypto_pk_get_digest(router->identity_pkey, d)) {
- log_warn(LD_BUG,"Error computing fingerprint");
- if (msg)
- *msg = "Bug: Error computing fingerprint";
- return FP_REJECT;
- }
-
- /* Check for the more usual versions to reject a router first. */
- const uint32_t r = dirserv_get_status_impl(d, router->nickname,
- router->addr, router->or_port,
- router->platform, msg, severity);
- if (r)
- return r;
-
- /* dirserv_get_status_impl already rejects versions older than 0.2.4.18-rc,
- * and onion_curve25519_pkey was introduced in 0.2.4.8-alpha.
- * But just in case a relay doesn't provide or lies about its version, or
- * doesn't include an ntor key in its descriptor, check that it exists,
- * and is non-zero (clients check that it's non-zero before using it). */
- if (!routerinfo_has_curve25519_onion_key(router)) {
- log_fn(severity, LD_DIR,
- "Descriptor from router %s is missing an ntor curve25519 onion "
- "key.", router_describe(router));
- if (msg)
- *msg = "Missing ntor curve25519 onion key. Please upgrade!";
- return FP_REJECT;
- }
-
- if (router->cache_info.signing_key_cert) {
- /* This has an ed25519 identity key. */
- if (KEYPIN_MISMATCH ==
- keypin_check((const uint8_t*)router->cache_info.identity_digest,
- router->cache_info.signing_key_cert->signing_key.pubkey)) {
- log_fn(severity, LD_DIR,
- "Descriptor from router %s has an Ed25519 key, "
- "but the <rsa,ed25519> keys don't match what they were before.",
- router_describe(router));
- if (key_pinning) {
- if (msg) {
- *msg = "Ed25519 identity key or RSA identity key has changed.";
- }
- return FP_REJECT;
- }
- }
- } else {
- /* No ed25519 key */
- if (KEYPIN_MISMATCH == keypin_check_lone_rsa(
- (const uint8_t*)router->cache_info.identity_digest)) {
- log_fn(severity, LD_DIR,
- "Descriptor from router %s has no Ed25519 key, "
- "when we previously knew an Ed25519 for it. Ignoring for now, "
- "since Ed25519 keys are fairly new.",
- router_describe(router));
-#ifdef DISABLE_DISABLING_ED25519
- if (key_pinning) {
- if (msg) {
- *msg = "Ed25519 identity key has disappeared.";
- }
- return FP_REJECT;
- }
-#endif /* defined(DISABLE_DISABLING_ED25519) */
- }
- }
-
- return 0;
-}
-
-/** Return true if there is no point in downloading the router described by
- * <b>rs</b> because this directory would reject it. */
-int
-dirserv_would_reject_router(const routerstatus_t *rs)
-{
- uint32_t res;
-
- res = dirserv_get_status_impl(rs->identity_digest, rs->nickname,
- rs->addr, rs->or_port,
- NULL, NULL, LOG_DEBUG);
-
- return (res & FP_REJECT) != 0;
-}
-
-/** Helper: As dirserv_router_get_status, but takes the router fingerprint
- * (hex, no spaces), nickname, address (used for logging only), IP address, OR
- * port and platform (logging only) as arguments.
- *
- * Log messages at 'severity'. (There's not much point in
- * logging that we're rejecting servers we'll not download.)
- */
-static uint32_t
-dirserv_get_status_impl(const char *id_digest, const char *nickname,
- uint32_t addr, uint16_t or_port,
- const char *platform, const char **msg, int severity)
-{
- uint32_t result = 0;
- router_status_t *status_by_digest;
-
- if (!fingerprint_list)
- fingerprint_list = authdir_config_new();
-
- log_debug(LD_DIRSERV, "%d fingerprints, %d digests known.",
- strmap_size(fingerprint_list->fp_by_name),
- digestmap_size(fingerprint_list->status_by_digest));
-
- if (platform) {
- tor_version_t ver_tmp;
- if (tor_version_parse_platform(platform, &ver_tmp, 1) < 0) {
- if (msg) {
- *msg = "Malformed platform string.";
- }
- return FP_REJECT;
- }
- }
-
- /* Versions before Tor 0.2.4.18-rc are too old to support, and are
- * missing some important security fixes too. Disable them. */
- if (platform && !tor_version_as_new_as(platform,"0.2.4.18-rc")) {
- if (msg)
- *msg = "Tor version is insecure or unsupported. Please upgrade!";
- return FP_REJECT;
- }
-
- /* Tor 0.2.9.x where x<5 suffers from bug #20499, where relays don't
- * keep their consensus up to date so they make bad guards.
- * The simple fix is to just drop them from the network. */
- if (platform &&
- tor_version_as_new_as(platform,"0.2.9.0-alpha") &&
- !tor_version_as_new_as(platform,"0.2.9.5-alpha")) {
- if (msg)
- *msg = "Tor version contains bug 20499. Please upgrade!";
- return FP_REJECT;
- }
-
- status_by_digest = digestmap_get(fingerprint_list->status_by_digest,
- id_digest);
- if (status_by_digest)
- result |= *status_by_digest;
-
- if (result & FP_REJECT) {
- if (msg)
- *msg = "Fingerprint is marked rejected -- if you think this is a "
- "mistake please set a valid email address in ContactInfo and "
- "send an email to bad-relays@lists.torproject.org mentioning "
- "your fingerprint(s)?";
- return FP_REJECT;
- } else if (result & FP_INVALID) {
- if (msg)
- *msg = "Fingerprint is marked invalid";
- }
-
- if (authdir_policy_badexit_address(addr, or_port)) {
- log_fn(severity, LD_DIRSERV,
- "Marking '%s' as bad exit because of address '%s'",
- nickname, fmt_addr32(addr));
- result |= FP_BADEXIT;
- }
-
- if (!authdir_policy_permits_address(addr, or_port)) {
- log_fn(severity, LD_DIRSERV, "Rejecting '%s' because of address '%s'",
- nickname, fmt_addr32(addr));
- if (msg)
- *msg = "Suspicious relay address range -- if you think this is a "
- "mistake please set a valid email address in ContactInfo and "
- "send an email to bad-relays@lists.torproject.org mentioning "
- "your address(es) and fingerprint(s)?";
- return FP_REJECT;
- }
- if (!authdir_policy_valid_address(addr, or_port)) {
- log_fn(severity, LD_DIRSERV,
- "Not marking '%s' valid because of address '%s'",
- nickname, fmt_addr32(addr));
- result |= FP_INVALID;
- }
-
- return result;
-}
-
-/** Clear the current fingerprint list. */
-void
-dirserv_free_fingerprint_list(void)
-{
- if (!fingerprint_list)
- return;
-
- strmap_free(fingerprint_list->fp_by_name, tor_free_);
- digestmap_free(fingerprint_list->status_by_digest, tor_free_);
- tor_free(fingerprint_list);
-}
-
-/*
- * Descriptor list
- */
-
-/** Return -1 if <b>ri</b> has a private or otherwise bad address,
- * unless we're configured to not care. Return 0 if all ok. */
-static int
-dirserv_router_has_valid_address(routerinfo_t *ri)
-{
- tor_addr_t addr;
- if (get_options()->DirAllowPrivateAddresses)
- return 0; /* whatever it is, we're fine with it */
- tor_addr_from_ipv4h(&addr, ri->addr);
-
- if (tor_addr_is_internal(&addr, 0)) {
- log_info(LD_DIRSERV,
- "Router %s published internal IP address. Refusing.",
- router_describe(ri));
- return -1; /* it's a private IP, we should reject it */
- }
- return 0;
-}
-
-/** Check whether we, as a directory server, want to accept <b>ri</b>. If so,
- * set its is_valid,running fields and return 0. Otherwise, return -1.
- *
- * If the router is rejected, set *<b>msg</b> to an explanation of why.
- *
- * If <b>complain</b> then explain at log-level 'notice' why we refused
- * a descriptor; else explain at log-level 'info'.
- */
-int
-authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg,
- int complain, int *valid_out)
-{
- /* Okay. Now check whether the fingerprint is recognized. */
- time_t now;
- int severity = (complain && ri->contact_info) ? LOG_NOTICE : LOG_INFO;
- uint32_t status = dirserv_router_get_status(ri, msg, severity);
- tor_assert(msg);
- if (status & FP_REJECT)
- return -1; /* msg is already set. */
-
- /* Is there too much clock skew? */
- now = time(NULL);
- if (ri->cache_info.published_on > now+ROUTER_ALLOW_SKEW) {
- log_fn(severity, LD_DIRSERV, "Publication time for %s is too "
- "far (%d minutes) in the future; possible clock skew. Not adding "
- "(%s)",
- router_describe(ri),
- (int)((ri->cache_info.published_on-now)/60),
- esc_router_info(ri));
- *msg = "Rejected: Your clock is set too far in the future, or your "
- "timezone is not correct.";
- return -1;
- }
- if (ri->cache_info.published_on < now-ROUTER_MAX_AGE_TO_PUBLISH) {
- log_fn(severity, LD_DIRSERV,
- "Publication time for %s is too far "
- "(%d minutes) in the past. Not adding (%s)",
- router_describe(ri),
- (int)((now-ri->cache_info.published_on)/60),
- esc_router_info(ri));
- *msg = "Rejected: Server is expired, or your clock is too far in the past,"
- " or your timezone is not correct.";
- return -1;
- }
- if (dirserv_router_has_valid_address(ri) < 0) {
- log_fn(severity, LD_DIRSERV,
- "Router %s has invalid address. Not adding (%s).",
- router_describe(ri),
- esc_router_info(ri));
- *msg = "Rejected: Address is a private address.";
- return -1;
- }
-
- *valid_out = ! (status & FP_INVALID);
-
- return 0;
-}
-
-/** Update the relevant flags of <b>node</b> based on our opinion as a
- * directory authority in <b>authstatus</b>, as returned by
- * dirserv_router_get_status or equivalent. */
-void
-dirserv_set_node_flags_from_authoritative_status(node_t *node,
- uint32_t authstatus)
-{
- node->is_valid = (authstatus & FP_INVALID) ? 0 : 1;
- node->is_bad_exit = (authstatus & FP_BADEXIT) ? 1 : 0;
-}
-
-/** True iff <b>a</b> is more severe than <b>b</b>. */
-static int
-WRA_MORE_SEVERE(was_router_added_t a, was_router_added_t b)
-{
- return a < b;
-}
-
-/** As for dirserv_add_descriptor(), but accepts multiple documents, and
- * returns the most severe error that occurred for any one of them. */
-was_router_added_t
-dirserv_add_multiple_descriptors(const char *desc, uint8_t purpose,
- const char *source,
- const char **msg)
-{
- was_router_added_t r, r_tmp;
- const char *msg_out;
- smartlist_t *list;
- const char *s;
- int n_parsed = 0;
- time_t now = time(NULL);
- char annotation_buf[ROUTER_ANNOTATION_BUF_LEN];
- char time_buf[ISO_TIME_LEN+1];
- int general = purpose == ROUTER_PURPOSE_GENERAL;
- tor_assert(msg);
-
- r=ROUTER_ADDED_SUCCESSFULLY; /*Least severe return value. */
-
- format_iso_time(time_buf, now);
- if (tor_snprintf(annotation_buf, sizeof(annotation_buf),
- "@uploaded-at %s\n"
- "@source %s\n"
- "%s%s%s", time_buf, escaped(source),
- !general ? "@purpose " : "",
- !general ? router_purpose_to_string(purpose) : "",
- !general ? "\n" : "")<0) {
- *msg = "Couldn't format annotations";
- /* XXX Not cool: we return -1 below, but (was_router_added_t)-1 is
- * ROUTER_BAD_EI, which isn't what's gone wrong here. :( */
- return -1;
- }
-
- s = desc;
- list = smartlist_new();
- if (!router_parse_list_from_string(&s, NULL, list, SAVED_NOWHERE, 0, 0,
- annotation_buf, NULL)) {
- SMARTLIST_FOREACH(list, routerinfo_t *, ri, {
- msg_out = NULL;
- tor_assert(ri->purpose == purpose);
- r_tmp = dirserv_add_descriptor(ri, &msg_out, source);
- if (WRA_MORE_SEVERE(r_tmp, r)) {
- r = r_tmp;
- *msg = msg_out;
- }
- });
- }
- n_parsed += smartlist_len(list);
- smartlist_clear(list);
-
- s = desc;
- if (!router_parse_list_from_string(&s, NULL, list, SAVED_NOWHERE, 1, 0,
- NULL, NULL)) {
- SMARTLIST_FOREACH(list, extrainfo_t *, ei, {
- msg_out = NULL;
-
- r_tmp = dirserv_add_extrainfo(ei, &msg_out);
- if (WRA_MORE_SEVERE(r_tmp, r)) {
- r = r_tmp;
- *msg = msg_out;
- }
- });
- }
- n_parsed += smartlist_len(list);
- smartlist_free(list);
-
- if (! *msg) {
- if (!n_parsed) {
- *msg = "No descriptors found in your POST.";
- if (WRA_WAS_ADDED(r))
- r = ROUTER_IS_ALREADY_KNOWN;
- } else {
- *msg = "(no message)";
- }
- }
-
- return r;
-}
-
-/** Examine the parsed server descriptor in <b>ri</b> and maybe insert it into
- * the list of server descriptors. Set *<b>msg</b> to a message that should be
- * passed back to the origin of this descriptor, or NULL if there is no such
- * message. Use <b>source</b> to produce better log messages.
- *
- * If <b>ri</b> is not added to the list of server descriptors, free it.
- * That means the caller must not access <b>ri</b> after this function
- * returns, since it might have been freed.
- *
- * Return the status of the operation.
- *
- * This function is only called when fresh descriptors are posted, not when
- * we re-load the cache.
- */
-was_router_added_t
-dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
-{
- was_router_added_t r;
- routerinfo_t *ri_old;
- char *desc, *nickname;
- const size_t desclen = ri->cache_info.signed_descriptor_len +
- ri->cache_info.annotations_len;
- const int key_pinning = get_options()->AuthDirPinKeys;
- *msg = NULL;
-
- /* If it's too big, refuse it now. Otherwise we'll cache it all over the
- * network and it'll clog everything up. */
- if (ri->cache_info.signed_descriptor_len > MAX_DESCRIPTOR_UPLOAD_SIZE) {
- log_notice(LD_DIR, "Somebody attempted to publish a router descriptor '%s'"
- " (source: %s) with size %d. Either this is an attack, or the "
- "MAX_DESCRIPTOR_UPLOAD_SIZE (%d) constant is too low.",
- ri->nickname, source, (int)ri->cache_info.signed_descriptor_len,
- MAX_DESCRIPTOR_UPLOAD_SIZE);
- *msg = "Router descriptor was too large.";
- r = ROUTER_AUTHDIR_REJECTS;
- goto fail;
- }
-
- /* Check whether this descriptor is semantically identical to the last one
- * from this server. (We do this here and not in router_add_to_routerlist
- * because we want to be able to accept the newest router descriptor that
- * another authority has, so we all converge on the same one.) */
- ri_old = router_get_mutable_by_digest(ri->cache_info.identity_digest);
- if (ri_old && ri_old->cache_info.published_on < ri->cache_info.published_on
- && router_differences_are_cosmetic(ri_old, ri)
- && !router_is_me(ri)) {
- log_info(LD_DIRSERV,
- "Not replacing descriptor from %s (source: %s); "
- "differences are cosmetic.",
- router_describe(ri), source);
- *msg = "Not replacing router descriptor; no information has changed since "
- "the last one with this identity.";
- r = ROUTER_IS_ALREADY_KNOWN;
- goto fail;
- }
-
- /* Do keypinning again ... this time, to add the pin if appropriate */
- int keypin_status;
- if (ri->cache_info.signing_key_cert) {
- ed25519_public_key_t *pkey = &ri->cache_info.signing_key_cert->signing_key;
- /* First let's validate this pubkey before pinning it */
- if (ed25519_validate_pubkey(pkey) < 0) {
- log_warn(LD_DIRSERV, "Received bad key from %s (source %s)",
- router_describe(ri), source);
- routerinfo_free(ri);
- return ROUTER_AUTHDIR_REJECTS;
- }
-
- /* Now pin it! */
- keypin_status = keypin_check_and_add(
- (const uint8_t*)ri->cache_info.identity_digest,
- pkey->pubkey, ! key_pinning);
- } else {
- keypin_status = keypin_check_lone_rsa(
- (const uint8_t*)ri->cache_info.identity_digest);
-#ifndef DISABLE_DISABLING_ED25519
- if (keypin_status == KEYPIN_MISMATCH)
- keypin_status = KEYPIN_NOT_FOUND;
-#endif
- }
- if (keypin_status == KEYPIN_MISMATCH && key_pinning) {
- log_info(LD_DIRSERV, "Dropping descriptor from %s (source: %s) because "
- "its key did not match an older RSA/Ed25519 keypair",
- router_describe(ri), source);
- *msg = "Looks like your keypair has changed? This authority previously "
- "recorded a different RSA identity for this Ed25519 identity (or vice "
- "versa.) Did you replace or copy some of your key files, but not "
- "the others? You should either restore the expected keypair, or "
- "delete your keys and restart Tor to start your relay with a new "
- "identity.";
- r = ROUTER_AUTHDIR_REJECTS;
- goto fail;
- }
-
- /* Make a copy of desc, since router_add_to_routerlist might free
- * ri and its associated signed_descriptor_t. */
- desc = tor_strndup(ri->cache_info.signed_descriptor_body, desclen);
- nickname = tor_strdup(ri->nickname);
-
- /* Tell if we're about to need to launch a test if we add this. */
- ri->needs_retest_if_added =
- dirserv_should_launch_reachability_test(ri, ri_old);
-
- r = router_add_to_routerlist(ri, msg, 0, 0);
- if (!WRA_WAS_ADDED(r)) {
- /* unless the routerinfo was fine, just out-of-date */
- log_info(LD_DIRSERV,
- "Did not add descriptor from '%s' (source: %s): %s.",
- nickname, source, *msg ? *msg : "(no message)");
- } else {
- smartlist_t *changed;
-
- changed = smartlist_new();
- smartlist_add(changed, ri);
- routerlist_descriptors_added(changed, 0);
- smartlist_free(changed);
- if (!*msg) {
- *msg = "Descriptor accepted";
- }
- log_info(LD_DIRSERV,
- "Added descriptor from '%s' (source: %s): %s.",
- nickname, source, *msg);
- }
- tor_free(desc);
- tor_free(nickname);
- return r;
- fail:
- {
- const char *desc_digest = ri->cache_info.signed_descriptor_digest;
- download_status_t *dls =
- router_get_dl_status_by_descriptor_digest(desc_digest);
- if (dls) {
- log_info(LD_GENERAL, "Marking router with descriptor %s as rejected, "
- "and therefore undownloadable",
- hex_str(desc_digest, DIGEST_LEN));
- download_status_mark_impossible(dls);
- }
- routerinfo_free(ri);
- }
- return r;
-}
-
-/** As dirserv_add_descriptor, but for an extrainfo_t <b>ei</b>. */
-static was_router_added_t
-dirserv_add_extrainfo(extrainfo_t *ei, const char **msg)
-{
- routerinfo_t *ri;
- int r;
- was_router_added_t rv;
- tor_assert(msg);
- *msg = NULL;
-
- /* Needs to be mutable so routerinfo_incompatible_with_extrainfo
- * can mess with some of the flags in ri->cache_info. */
- ri = router_get_mutable_by_digest(ei->cache_info.identity_digest);
- if (!ri) {
- *msg = "No corresponding router descriptor for extra-info descriptor";
- rv = ROUTER_BAD_EI;
- goto fail;
- }
-
- /* If it's too big, refuse it now. Otherwise we'll cache it all over the
- * network and it'll clog everything up. */
- if (ei->cache_info.signed_descriptor_len > MAX_EXTRAINFO_UPLOAD_SIZE) {
- log_notice(LD_DIR, "Somebody attempted to publish an extrainfo "
- "with size %d. Either this is an attack, or the "
- "MAX_EXTRAINFO_UPLOAD_SIZE (%d) constant is too low.",
- (int)ei->cache_info.signed_descriptor_len,
- MAX_EXTRAINFO_UPLOAD_SIZE);
- *msg = "Extrainfo document was too large";
- rv = ROUTER_BAD_EI;
- goto fail;
- }
-
- if ((r = routerinfo_incompatible_with_extrainfo(ri->identity_pkey, ei,
- &ri->cache_info, msg))) {
- if (r<0) {
- extrainfo_free(ei);
- return ROUTER_IS_ALREADY_KNOWN;
- }
- rv = ROUTER_BAD_EI;
- goto fail;
- }
- router_add_extrainfo_to_routerlist(ei, msg, 0, 0);
- return ROUTER_ADDED_SUCCESSFULLY;
- fail:
- {
- const char *d = ei->cache_info.signed_descriptor_digest;
- signed_descriptor_t *sd = router_get_by_extrainfo_digest((char*)d);
- if (sd) {
- log_info(LD_GENERAL, "Marking extrainfo with descriptor %s as "
- "rejected, and therefore undownloadable",
- hex_str((char*)d,DIGEST_LEN));
- download_status_mark_impossible(&sd->ei_dl_status);
- }
- extrainfo_free(ei);
- }
- return rv;
-}
-
-/** Remove all descriptors whose nicknames or fingerprints no longer
- * are allowed by our fingerprint list. (Descriptors that used to be
- * good can become bad when we reload the fingerprint list.)
- */
-static void
-directory_remove_invalid(void)
-{
- routerlist_t *rl = router_get_routerlist();
- smartlist_t *nodes = smartlist_new();
- smartlist_add_all(nodes, nodelist_get_list());
-
- SMARTLIST_FOREACH_BEGIN(nodes, node_t *, node) {
- const char *msg = NULL;
- const char *description;
- routerinfo_t *ent = node->ri;
- uint32_t r;
- if (!ent)
- continue;
- r = dirserv_router_get_status(ent, &msg, LOG_INFO);
- description = router_describe(ent);
- if (r & FP_REJECT) {
- log_info(LD_DIRSERV, "Router %s is now rejected: %s",
- description, msg?msg:"");
- routerlist_remove(rl, ent, 0, time(NULL));
- continue;
- }
- if (bool_neq((r & FP_INVALID), !node->is_valid)) {
- log_info(LD_DIRSERV, "Router '%s' is now %svalid.", description,
- (r&FP_INVALID) ? "in" : "");
- node->is_valid = (r&FP_INVALID)?0:1;
- }
- if (bool_neq((r & FP_BADEXIT), node->is_bad_exit)) {
- log_info(LD_DIRSERV, "Router '%s' is now a %s exit", description,
- (r & FP_BADEXIT) ? "bad" : "good");
- node->is_bad_exit = (r&FP_BADEXIT) ? 1: 0;
- }
- } SMARTLIST_FOREACH_END(node);
-
- routerlist_assert_ok(rl);
- smartlist_free(nodes);
-}
-
-/**
- * Allocate and return a description of the status of the server <b>desc</b>,
- * for use in a v1-style router-status line. The server is listed
- * as running iff <b>is_live</b> is true.
- *
- * This is deprecated: it's only used for controllers that want outputs in
- * the old format.
- */
-static char *
-list_single_server_status(const routerinfo_t *desc, int is_live)
-{
- char buf[MAX_NICKNAME_LEN+HEX_DIGEST_LEN+4]; /* !nickname=$hexdigest\0 */
- char *cp;
- const node_t *node;
-
- tor_assert(desc);
-
- cp = buf;
- if (!is_live) {
- *cp++ = '!';
- }
- node = node_get_by_id(desc->cache_info.identity_digest);
- if (node && node->is_valid) {
- strlcpy(cp, desc->nickname, sizeof(buf)-(cp-buf));
- cp += strlen(cp);
- *cp++ = '=';
- }
- *cp++ = '$';
- base16_encode(cp, HEX_DIGEST_LEN+1, desc->cache_info.identity_digest,
- DIGEST_LEN);
- return tor_strdup(buf);
-}
-
-/* DOCDOC running_long_enough_to_decide_unreachable */
-int
-running_long_enough_to_decide_unreachable(void)
-{
- return time_of_process_start
- + get_options()->TestingAuthDirTimeToLearnReachability < approx_time();
-}
-
-/** Each server needs to have passed a reachability test no more
- * than this number of seconds ago, or it is listed as down in
- * the directory. */
-#define REACHABLE_TIMEOUT (45*60)
-
-/** If we tested a router and found it reachable _at least this long_ after it
- * declared itself hibernating, it is probably done hibernating and we just
- * missed a descriptor from it. */
-#define HIBERNATION_PUBLICATION_SKEW (60*60)
-
-/** Treat a router as alive if
- * - It's me, and I'm not hibernating.
- * or - We've found it reachable recently. */
-void
-dirserv_set_router_is_running(routerinfo_t *router, time_t now)
-{
- /*XXXX This function is a mess. Separate out the part that calculates
- whether it's reachable and the part that tells rephist that the router was
- unreachable.
- */
- int answer;
- const or_options_t *options = get_options();
- node_t *node = node_get_mutable_by_id(router->cache_info.identity_digest);
- tor_assert(node);
-
- if (router_is_me(router)) {
- /* We always know if we are shutting down or hibernating ourselves. */
- answer = ! we_are_hibernating();
- } else if (router->is_hibernating &&
- (router->cache_info.published_on +
- HIBERNATION_PUBLICATION_SKEW) > node->last_reachable) {
- /* A hibernating router is down unless we (somehow) had contact with it
- * since it declared itself to be hibernating. */
- answer = 0;
- } else if (options->AssumeReachable) {
- /* If AssumeReachable, everybody is up unless they say they are down! */
- answer = 1;
- } else {
- /* Otherwise, a router counts as up if we found all announced OR
- ports reachable in the last REACHABLE_TIMEOUT seconds.
-
- XXX prop186 For now there's always one IPv4 and at most one
- IPv6 OR port.
-
- If we're not on IPv6, don't consider reachability of potential
- IPv6 OR port since that'd kill all dual stack relays until a
- majority of the dir auths have IPv6 connectivity. */
- answer = (now < node->last_reachable + REACHABLE_TIMEOUT &&
- (options->AuthDirHasIPv6Connectivity != 1 ||
- tor_addr_is_null(&router->ipv6_addr) ||
- now < node->last_reachable6 + REACHABLE_TIMEOUT));
- }
-
- if (!answer && running_long_enough_to_decide_unreachable()) {
- /* Not considered reachable. tell rephist about that.
-
- Because we launch a reachability test for each router every
- REACHABILITY_TEST_CYCLE_PERIOD seconds, then the router has probably
- been down since at least that time after we last successfully reached
- it.
-
- XXX ipv6
- */
- time_t when = now;
- if (node->last_reachable &&
- node->last_reachable + REACHABILITY_TEST_CYCLE_PERIOD < now)
- when = node->last_reachable + REACHABILITY_TEST_CYCLE_PERIOD;
- rep_hist_note_router_unreachable(router->cache_info.identity_digest, when);
- }
-
- node->is_running = answer;
-}
-
-/** Based on the routerinfo_ts in <b>routers</b>, allocate the
- * contents of a v1-style router-status line, and store it in
- * *<b>router_status_out</b>. Return 0 on success, -1 on failure.
- *
- * If for_controller is true, include the routers with very old descriptors.
- *
- * This is deprecated: it's only used for controllers that want outputs in
- * the old format.
- */
-int
-list_server_status_v1(smartlist_t *routers, char **router_status_out,
- int for_controller)
-{
- /* List of entries in a router-status style: An optional !, then an optional
- * equals-suffixed nickname, then a dollar-prefixed hexdigest. */
- smartlist_t *rs_entries;
- time_t now = time(NULL);
- time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH;
- const or_options_t *options = get_options();
- /* We include v2 dir auths here too, because they need to answer
- * controllers. Eventually we'll deprecate this whole function;
- * see also networkstatus_getinfo_by_purpose(). */
- int authdir = authdir_mode_publishes_statuses(options);
- tor_assert(router_status_out);
-
- rs_entries = smartlist_new();
-
- SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) {
- const node_t *node = node_get_by_id(ri->cache_info.identity_digest);
- tor_assert(node);
- if (authdir) {
- /* Update router status in routerinfo_t. */
- dirserv_set_router_is_running(ri, now);
- }
- if (for_controller) {
- char name_buf[MAX_VERBOSE_NICKNAME_LEN+2];
- char *cp = name_buf;
- if (!node->is_running)
- *cp++ = '!';
- router_get_verbose_nickname(cp, ri);
- smartlist_add_strdup(rs_entries, name_buf);
- } else if (ri->cache_info.published_on >= cutoff) {
- smartlist_add(rs_entries, list_single_server_status(ri,
- node->is_running));
- }
- } SMARTLIST_FOREACH_END(ri);
-
- *router_status_out = smartlist_join_strings(rs_entries, " ", 0, NULL);
-
- SMARTLIST_FOREACH(rs_entries, char *, cp, tor_free(cp));
- smartlist_free(rs_entries);
-
- return 0;
-}
-
-/** Return 1 if <b>ri</b>'s descriptor is "active" -- running, valid,
- * not hibernating, having observed bw greater 0, and not too old. Else
- * return 0.
- */
-static int
-router_is_active(const routerinfo_t *ri, const node_t *node, time_t now)
-{
- time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH;
- if (ri->cache_info.published_on < cutoff) {
- return 0;
- }
- if (!node->is_running || !node->is_valid || ri->is_hibernating) {
- return 0;
- }
- /* Only require bandwidth capacity in non-test networks, or
- * if TestingTorNetwork, and TestingMinExitFlagThreshold is non-zero */
- if (!ri->bandwidthcapacity) {
- if (get_options()->TestingTorNetwork) {
- if (get_options()->TestingMinExitFlagThreshold > 0) {
- /* If we're in a TestingTorNetwork, and TestingMinExitFlagThreshold is,
- * then require bandwidthcapacity */
- return 0;
- }
- } else {
- /* If we're not in a TestingTorNetwork, then require bandwidthcapacity */
- return 0;
- }
- }
- return 1;
-}
-
/********************************************************************/
/* A set of functions to answer questions about how we'd like to behave
@@ -1302,1421 +263,6 @@ dirserv_get_consensus(const char *flavor_name)
return strmap_get(cached_consensuses, flavor_name);
}
-/** If a router's uptime is at least this value, then it is always
- * considered stable, regardless of the rest of the network. This
- * way we resist attacks where an attacker doubles the size of the
- * network using allegedly high-uptime nodes, displacing all the
- * current guards. */
-#define UPTIME_TO_GUARANTEE_STABLE (3600*24*30)
-/** If a router's MTBF is at least this value, then it is always stable.
- * See above. (Corresponds to about 7 days for current decay rates.) */
-#define MTBF_TO_GUARANTEE_STABLE (60*60*24*5)
-/** Similarly, every node with at least this much weighted time known can be
- * considered familiar enough to be a guard. Corresponds to about 20 days for
- * current decay rates.
- */
-#define TIME_KNOWN_TO_GUARANTEE_FAMILIAR (8*24*60*60)
-/** Similarly, every node with sufficient WFU is around enough to be a guard.
- */
-#define WFU_TO_GUARANTEE_GUARD (0.98)
-
-/* Thresholds for server performance: set by
- * dirserv_compute_performance_thresholds, and used by
- * generate_v2_networkstatus */
-
-/** Any router with an uptime of at least this value is stable. */
-static uint32_t stable_uptime = 0; /* start at a safe value */
-/** Any router with an mtbf of at least this value is stable. */
-static double stable_mtbf = 0.0;
-/** If true, we have measured enough mtbf info to look at stable_mtbf rather
- * than stable_uptime. */
-static int enough_mtbf_info = 0;
-/** Any router with a weighted fractional uptime of at least this much might
- * be good as a guard. */
-static double guard_wfu = 0.0;
-/** Don't call a router a guard unless we've known about it for at least this
- * many seconds. */
-static long guard_tk = 0;
-/** Any router with a bandwidth at least this high is "Fast" */
-static uint32_t fast_bandwidth_kb = 0;
-/** If exits can be guards, then all guards must have a bandwidth this
- * high. */
-static uint32_t guard_bandwidth_including_exits_kb = 0;
-/** If exits can't be guards, then all guards must have a bandwidth this
- * high. */
-static uint32_t guard_bandwidth_excluding_exits_kb = 0;
-
-/** Helper: estimate the uptime of a router given its stated uptime and the
- * amount of time since it last stated its stated uptime. */
-static inline long
-real_uptime(const routerinfo_t *router, time_t now)
-{
- if (now < router->cache_info.published_on)
- return router->uptime;
- else
- return router->uptime + (now - router->cache_info.published_on);
-}
-
-/** Return 1 if <b>router</b> is not suitable for these parameters, else 0.
- * If <b>need_uptime</b> is non-zero, we require a minimum uptime.
- * If <b>need_capacity</b> is non-zero, we require a minimum advertised
- * bandwidth.
- */
-static int
-dirserv_thinks_router_is_unreliable(time_t now,
- routerinfo_t *router,
- int need_uptime, int need_capacity)
-{
- if (need_uptime) {
- if (!enough_mtbf_info) {
- /* XXXX We should change the rule from
- * "use uptime if we don't have mtbf data" to "don't advertise Stable on
- * v3 if we don't have enough mtbf data." Or maybe not, since if we ever
- * hit a point where we need to reset a lot of authorities at once,
- * none of them would be in a position to declare Stable.
- */
- long uptime = real_uptime(router, now);
- if ((unsigned)uptime < stable_uptime &&
- (unsigned)uptime < UPTIME_TO_GUARANTEE_STABLE)
- return 1;
- } else {
- double mtbf =
- rep_hist_get_stability(router->cache_info.identity_digest, now);
- if (mtbf < stable_mtbf &&
- mtbf < MTBF_TO_GUARANTEE_STABLE)
- return 1;
- }
- }
- if (need_capacity) {
- uint32_t bw_kb = dirserv_get_credible_bandwidth_kb(router);
- if (bw_kb < fast_bandwidth_kb)
- return 1;
- }
- return 0;
-}
-
-/** Return true iff <b>router</b> should be assigned the "HSDir" flag.
- *
- * Right now this means it advertises support for it, it has a high uptime,
- * it's a directory cache, it has the Stable and Fast flags, and it's currently
- * considered Running.
- *
- * This function needs to be called after router-\>is_running has
- * been set.
- */
-static int
-dirserv_thinks_router_is_hs_dir(const routerinfo_t *router,
- const node_t *node, time_t now)
-{
-
- long uptime;
-
- /* If we haven't been running for at least
- * get_options()->MinUptimeHidServDirectoryV2 seconds, we can't
- * have accurate data telling us a relay has been up for at least
- * that long. We also want to allow a bit of slack: Reachability
- * tests aren't instant. If we haven't been running long enough,
- * trust the relay. */
-
- if (get_uptime() >
- get_options()->MinUptimeHidServDirectoryV2 * 1.1)
- uptime = MIN(rep_hist_get_uptime(router->cache_info.identity_digest, now),
- real_uptime(router, now));
- else
- uptime = real_uptime(router, now);
-
- return (router->wants_to_be_hs_dir &&
- router->supports_tunnelled_dir_requests &&
- node->is_stable && node->is_fast &&
- uptime >= get_options()->MinUptimeHidServDirectoryV2 &&
- router_is_active(router, node, now));
-}
-
-/** Don't consider routers with less bandwidth than this when computing
- * thresholds. */
-#define ABSOLUTE_MIN_BW_VALUE_TO_CONSIDER_KB 4
-
-/** Helper for dirserv_compute_performance_thresholds(): Decide whether to
- * include a router in our calculations, and return true iff we should; the
- * require_mbw parameter is passed in by
- * dirserv_compute_performance_thresholds() and controls whether we ever
- * count routers with only advertised bandwidths */
-static int
-router_counts_toward_thresholds(const node_t *node, time_t now,
- const digestmap_t *omit_as_sybil,
- int require_mbw)
-{
- /* Have measured bw? */
- int have_mbw =
- dirserv_has_measured_bw(node->identity);
- uint64_t min_bw_kb = ABSOLUTE_MIN_BW_VALUE_TO_CONSIDER_KB;
- const or_options_t *options = get_options();
-
- if (options->TestingTorNetwork) {
- min_bw_kb = (int64_t)options->TestingMinExitFlagThreshold / 1000;
- }
-
- return node->ri && router_is_active(node->ri, node, now) &&
- !digestmap_get(omit_as_sybil, node->identity) &&
- (dirserv_get_credible_bandwidth_kb(node->ri) >= min_bw_kb) &&
- (have_mbw || !require_mbw);
-}
-
-/** Look through the routerlist, and using the measured bandwidth cache count
- * how many measured bandwidths we know. This is used to decide whether we
- * ever trust advertised bandwidths for purposes of assigning flags. */
-void
-dirserv_count_measured_bws(const smartlist_t *routers)
-{
- /* Initialize this first */
- routers_with_measured_bw = 0;
-
- /* Iterate over the routerlist and count measured bandwidths */
- SMARTLIST_FOREACH_BEGIN(routers, const routerinfo_t *, ri) {
- /* Check if we know a measured bandwidth for this one */
- if (dirserv_has_measured_bw(ri->cache_info.identity_digest)) {
- ++routers_with_measured_bw;
- }
- } SMARTLIST_FOREACH_END(ri);
-}
-
-/** Look through the routerlist, the Mean Time Between Failure history, and
- * the Weighted Fractional Uptime history, and use them to set thresholds for
- * the Stable, Fast, and Guard flags. Update the fields stable_uptime,
- * stable_mtbf, enough_mtbf_info, guard_wfu, guard_tk, fast_bandwidth,
- * guard_bandwidth_including_exits, and guard_bandwidth_excluding_exits.
- *
- * Also, set the is_exit flag of each router appropriately. */
-void
-dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil)
-{
- int n_active, n_active_nonexit, n_familiar;
- uint32_t *uptimes, *bandwidths_kb, *bandwidths_excluding_exits_kb;
- long *tks;
- double *mtbfs, *wfus;
- smartlist_t *nodelist;
- time_t now = time(NULL);
- const or_options_t *options = get_options();
-
- /* Require mbw? */
- int require_mbw =
- (routers_with_measured_bw >
- options->MinMeasuredBWsForAuthToIgnoreAdvertised) ? 1 : 0;
-
- /* initialize these all here, in case there are no routers */
- stable_uptime = 0;
- stable_mtbf = 0;
- fast_bandwidth_kb = 0;
- guard_bandwidth_including_exits_kb = 0;
- guard_bandwidth_excluding_exits_kb = 0;
- guard_tk = 0;
- guard_wfu = 0;
-
- nodelist_assert_ok();
- nodelist = nodelist_get_list();
-
- /* Initialize arrays that will hold values for each router. We'll
- * sort them and use that to compute thresholds. */
- n_active = n_active_nonexit = 0;
- /* Uptime for every active router. */
- uptimes = tor_calloc(smartlist_len(nodelist), sizeof(uint32_t));
- /* Bandwidth for every active router. */
- bandwidths_kb = tor_calloc(smartlist_len(nodelist), sizeof(uint32_t));
- /* Bandwidth for every active non-exit router. */
- bandwidths_excluding_exits_kb =
- tor_calloc(smartlist_len(nodelist), sizeof(uint32_t));
- /* Weighted mean time between failure for each active router. */
- mtbfs = tor_calloc(smartlist_len(nodelist), sizeof(double));
- /* Time-known for each active router. */
- tks = tor_calloc(smartlist_len(nodelist), sizeof(long));
- /* Weighted fractional uptime for each active router. */
- wfus = tor_calloc(smartlist_len(nodelist), sizeof(double));
-
- /* Now, fill in the arrays. */
- SMARTLIST_FOREACH_BEGIN(nodelist, node_t *, node) {
- if (options->BridgeAuthoritativeDir &&
- node->ri &&
- node->ri->purpose != ROUTER_PURPOSE_BRIDGE)
- continue;
-
- routerinfo_t *ri = node->ri;
- if (ri) {
- node->is_exit = (!router_exit_policy_rejects_all(ri) &&
- exit_policy_is_general_exit(ri->exit_policy));
- }
-
- if (router_counts_toward_thresholds(node, now, omit_as_sybil,
- require_mbw)) {
- const char *id = node->identity;
- uint32_t bw_kb;
-
- /* resolve spurious clang shallow analysis null pointer errors */
- tor_assert(ri);
-
- uptimes[n_active] = (uint32_t)real_uptime(ri, now);
- mtbfs[n_active] = rep_hist_get_stability(id, now);
- tks [n_active] = rep_hist_get_weighted_time_known(id, now);
- bandwidths_kb[n_active] = bw_kb = dirserv_get_credible_bandwidth_kb(ri);
- if (!node->is_exit || node->is_bad_exit) {
- bandwidths_excluding_exits_kb[n_active_nonexit] = bw_kb;
- ++n_active_nonexit;
- }
- ++n_active;
- }
- } SMARTLIST_FOREACH_END(node);
-
- /* Now, compute thresholds. */
- if (n_active) {
- /* The median uptime is stable. */
- stable_uptime = median_uint32(uptimes, n_active);
- /* The median mtbf is stable, if we have enough mtbf info */
- stable_mtbf = median_double(mtbfs, n_active);
- /* The 12.5th percentile bandwidth is fast. */
- fast_bandwidth_kb = find_nth_uint32(bandwidths_kb, n_active, n_active/8);
- /* (Now bandwidths is sorted.) */
- if (fast_bandwidth_kb < RELAY_REQUIRED_MIN_BANDWIDTH/(2 * 1000))
- fast_bandwidth_kb = bandwidths_kb[n_active/4];
- guard_bandwidth_including_exits_kb =
- third_quartile_uint32(bandwidths_kb, n_active);
- guard_tk = find_nth_long(tks, n_active, n_active/8);
- }
-
- if (guard_tk > TIME_KNOWN_TO_GUARANTEE_FAMILIAR)
- guard_tk = TIME_KNOWN_TO_GUARANTEE_FAMILIAR;
-
- {
- /* We can vote on a parameter for the minimum and maximum. */
-#define ABSOLUTE_MIN_VALUE_FOR_FAST_FLAG 4
- int32_t min_fast_kb, max_fast_kb, min_fast, max_fast;
- min_fast = networkstatus_get_param(NULL, "FastFlagMinThreshold",
- ABSOLUTE_MIN_VALUE_FOR_FAST_FLAG,
- ABSOLUTE_MIN_VALUE_FOR_FAST_FLAG,
- INT32_MAX);
- if (options->TestingTorNetwork) {
- min_fast = (int32_t)options->TestingMinFastFlagThreshold;
- }
- max_fast = networkstatus_get_param(NULL, "FastFlagMaxThreshold",
- INT32_MAX, min_fast, INT32_MAX);
- min_fast_kb = min_fast / 1000;
- max_fast_kb = max_fast / 1000;
-
- if (fast_bandwidth_kb < (uint32_t)min_fast_kb)
- fast_bandwidth_kb = min_fast_kb;
- if (fast_bandwidth_kb > (uint32_t)max_fast_kb)
- fast_bandwidth_kb = max_fast_kb;
- }
- /* Protect sufficiently fast nodes from being pushed out of the set
- * of Fast nodes. */
- if (options->AuthDirFastGuarantee &&
- fast_bandwidth_kb > options->AuthDirFastGuarantee/1000)
- fast_bandwidth_kb = (uint32_t)options->AuthDirFastGuarantee/1000;
-
- /* Now that we have a time-known that 7/8 routers are known longer than,
- * fill wfus with the wfu of every such "familiar" router. */
- n_familiar = 0;
-
- SMARTLIST_FOREACH_BEGIN(nodelist, node_t *, node) {
- if (router_counts_toward_thresholds(node, now,
- omit_as_sybil, require_mbw)) {
- routerinfo_t *ri = node->ri;
- const char *id = ri->cache_info.identity_digest;
- long tk = rep_hist_get_weighted_time_known(id, now);
- if (tk < guard_tk)
- continue;
- wfus[n_familiar++] = rep_hist_get_weighted_fractional_uptime(id, now);
- }
- } SMARTLIST_FOREACH_END(node);
- if (n_familiar)
- guard_wfu = median_double(wfus, n_familiar);
- if (guard_wfu > WFU_TO_GUARANTEE_GUARD)
- guard_wfu = WFU_TO_GUARANTEE_GUARD;
-
- enough_mtbf_info = rep_hist_have_measured_enough_stability();
-
- if (n_active_nonexit) {
- guard_bandwidth_excluding_exits_kb =
- find_nth_uint32(bandwidths_excluding_exits_kb,
- n_active_nonexit, n_active_nonexit*3/4);
- }
-
- log_info(LD_DIRSERV,
- "Cutoffs: For Stable, %lu sec uptime, %lu sec MTBF. "
- "For Fast: %lu kilobytes/sec. "
- "For Guard: WFU %.03f%%, time-known %lu sec, "
- "and bandwidth %lu or %lu kilobytes/sec. "
- "We%s have enough stability data.",
- (unsigned long)stable_uptime,
- (unsigned long)stable_mtbf,
- (unsigned long)fast_bandwidth_kb,
- guard_wfu*100,
- (unsigned long)guard_tk,
- (unsigned long)guard_bandwidth_including_exits_kb,
- (unsigned long)guard_bandwidth_excluding_exits_kb,
- enough_mtbf_info ? "" : " don't");
-
- tor_free(uptimes);
- tor_free(mtbfs);
- tor_free(bandwidths_kb);
- tor_free(bandwidths_excluding_exits_kb);
- tor_free(tks);
- tor_free(wfus);
-}
-
-/* Use dirserv_compute_performance_thresholds() to compute the thresholds
- * for the status flags, specifically for bridges.
- *
- * This is only called by a Bridge Authority from
- * networkstatus_getinfo_by_purpose().
- */
-void
-dirserv_compute_bridge_flag_thresholds(void)
-{
- digestmap_t *omit_as_sybil = digestmap_new();
- dirserv_compute_performance_thresholds(omit_as_sybil);
- digestmap_free(omit_as_sybil, NULL);
-}
-
-/** Measured bandwidth cache entry */
-typedef struct mbw_cache_entry_s {
- long mbw_kb;
- time_t as_of;
-} mbw_cache_entry_t;
-
-/** Measured bandwidth cache - keys are identity_digests, values are
- * mbw_cache_entry_t *. */
-static digestmap_t *mbw_cache = NULL;
-
-/** Store a measured bandwidth cache entry when reading the measured
- * bandwidths file. */
-STATIC void
-dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line,
- time_t as_of)
-{
- mbw_cache_entry_t *e = NULL;
-
- tor_assert(parsed_line);
-
- /* Allocate a cache if we need */
- if (!mbw_cache) mbw_cache = digestmap_new();
-
- /* Check if we have an existing entry */
- e = digestmap_get(mbw_cache, parsed_line->node_id);
- /* If we do, we can re-use it */
- if (e) {
- /* Check that we really are newer, and update */
- if (as_of > e->as_of) {
- e->mbw_kb = parsed_line->bw_kb;
- e->as_of = as_of;
- }
- } else {
- /* We'll have to insert a new entry */
- e = tor_malloc(sizeof(*e));
- e->mbw_kb = parsed_line->bw_kb;
- e->as_of = as_of;
- digestmap_set(mbw_cache, parsed_line->node_id, e);
- }
-}
-
-/** Clear and free the measured bandwidth cache */
-void
-dirserv_clear_measured_bw_cache(void)
-{
- if (mbw_cache) {
- /* Free the map and all entries */
- digestmap_free(mbw_cache, tor_free_);
- mbw_cache = NULL;
- }
-}
-
-/** Scan the measured bandwidth cache and remove expired entries */
-STATIC void
-dirserv_expire_measured_bw_cache(time_t now)
-{
-
- if (mbw_cache) {
- /* Iterate through the cache and check each entry */
- DIGESTMAP_FOREACH_MODIFY(mbw_cache, k, mbw_cache_entry_t *, e) {
- if (now > e->as_of + MAX_MEASUREMENT_AGE) {
- tor_free(e);
- MAP_DEL_CURRENT(k);
- }
- } DIGESTMAP_FOREACH_END;
-
- /* Check if we cleared the whole thing and free if so */
- if (digestmap_size(mbw_cache) == 0) {
- digestmap_free(mbw_cache, tor_free_);
- mbw_cache = 0;
- }
- }
-}
-
-/** Query the cache by identity digest, return value indicates whether
- * we found it. The bw_out and as_of_out pointers receive the cached
- * bandwidth value and the time it was cached if not NULL. */
-int
-dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_kb_out,
- time_t *as_of_out)
-{
- mbw_cache_entry_t *v = NULL;
- int rv = 0;
-
- if (mbw_cache && node_id) {
- v = digestmap_get(mbw_cache, node_id);
- if (v) {
- /* Found something */
- rv = 1;
- if (bw_kb_out) *bw_kb_out = v->mbw_kb;
- if (as_of_out) *as_of_out = v->as_of;
- }
- }
-
- return rv;
-}
-
-/** Predicate wrapper for dirserv_query_measured_bw_cache() */
-int
-dirserv_has_measured_bw(const char *node_id)
-{
- return dirserv_query_measured_bw_cache_kb(node_id, NULL, NULL);
-}
-
-/** Get the current size of the measured bandwidth cache */
-int
-dirserv_get_measured_bw_cache_size(void)
-{
- if (mbw_cache) return digestmap_size(mbw_cache);
- else return 0;
-}
-
-/** Return the bandwidth we believe for assigning flags; prefer measured
- * over advertised, and if we have above a threshold quantity of measured
- * bandwidths, we don't want to ever give flags to unmeasured routers, so
- * return 0. */
-static uint32_t
-dirserv_get_credible_bandwidth_kb(const routerinfo_t *ri)
-{
- int threshold;
- uint32_t bw_kb = 0;
- long mbw_kb;
-
- tor_assert(ri);
- /* Check if we have a measured bandwidth, and check the threshold if not */
- if (!(dirserv_query_measured_bw_cache_kb(ri->cache_info.identity_digest,
- &mbw_kb, NULL))) {
- threshold = get_options()->MinMeasuredBWsForAuthToIgnoreAdvertised;
- if (routers_with_measured_bw > threshold) {
- /* Return zero for unmeasured bandwidth if we are above threshold */
- bw_kb = 0;
- } else {
- /* Return an advertised bandwidth otherwise */
- bw_kb = router_get_advertised_bandwidth_capped(ri) / 1000;
- }
- } else {
- /* We have the measured bandwidth in mbw */
- bw_kb = (uint32_t)mbw_kb;
- }
-
- return bw_kb;
-}
-
-/** Give a statement of our current performance thresholds for inclusion
- * in a vote document. */
-char *
-dirserv_get_flag_thresholds_line(void)
-{
- char *result=NULL;
- const int measured_threshold =
- get_options()->MinMeasuredBWsForAuthToIgnoreAdvertised;
- const int enough_measured_bw = routers_with_measured_bw > measured_threshold;
-
- tor_asprintf(&result,
- "stable-uptime=%lu stable-mtbf=%lu "
- "fast-speed=%lu "
- "guard-wfu=%.03f%% guard-tk=%lu "
- "guard-bw-inc-exits=%lu guard-bw-exc-exits=%lu "
- "enough-mtbf=%d ignoring-advertised-bws=%d",
- (unsigned long)stable_uptime,
- (unsigned long)stable_mtbf,
- (unsigned long)fast_bandwidth_kb*1000,
- guard_wfu*100,
- (unsigned long)guard_tk,
- (unsigned long)guard_bandwidth_including_exits_kb*1000,
- (unsigned long)guard_bandwidth_excluding_exits_kb*1000,
- enough_mtbf_info ? 1 : 0,
- enough_measured_bw ? 1 : 0);
-
- return result;
-}
-
-/** Helper: write the router-status information in <b>rs</b> into a newly
- * allocated character buffer. Use the same format as in network-status
- * documents. If <b>version</b> is non-NULL, add a "v" line for the platform.
- *
- * consensus_method is the current consensus method when format is
- * NS_V3_CONSENSUS or NS_V3_CONSENSUS_MICRODESC. It is ignored for other
- * formats: pass ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD.
- *
- * Return 0 on success, -1 on failure.
- *
- * The format argument has one of the following values:
- * NS_V2 - Output an entry suitable for a V2 NS opinion document
- * NS_V3_CONSENSUS - Output the first portion of a V3 NS consensus entry
- * for consensus_method.
- * NS_V3_CONSENSUS_MICRODESC - Output the first portion of a V3 microdesc
- * consensus entry for consensus_method.
- * NS_V3_VOTE - Output a complete V3 NS vote. If <b>vrs</b> is present,
- * it contains additional information for the vote.
- * NS_CONTROL_PORT - Output a NS document for the control port.
- */
-char *
-routerstatus_format_entry(const routerstatus_t *rs, const char *version,
- const char *protocols,
- routerstatus_format_type_t format,
- int consensus_method,
- const vote_routerstatus_t *vrs)
-{
- char *summary;
- char *result = NULL;
-
- char published[ISO_TIME_LEN+1];
- char identity64[BASE64_DIGEST_LEN+1];
- char digest64[BASE64_DIGEST_LEN+1];
- smartlist_t *chunks = smartlist_new();
-
- format_iso_time(published, rs->published_on);
- digest_to_base64(identity64, rs->identity_digest);
- digest_to_base64(digest64, rs->descriptor_digest);
-
- smartlist_add_asprintf(chunks,
- "r %s %s %s%s%s %s %d %d\n",
- rs->nickname,
- identity64,
- (format==NS_V3_CONSENSUS_MICRODESC)?"":digest64,
- (format==NS_V3_CONSENSUS_MICRODESC)?"":" ",
- published,
- fmt_addr32(rs->addr),
- (int)rs->or_port,
- (int)rs->dir_port);
-
- /* TODO: Maybe we want to pass in what we need to build the rest of
- * this here, instead of in the caller. Then we could use the
- * networkstatus_type_t values, with an additional control port value
- * added -MP */
-
- /* V3 microdesc consensuses only have "a" lines in later consensus methods
- */
- if (format == NS_V3_CONSENSUS_MICRODESC &&
- consensus_method < MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS)
- goto done;
-
- /* Possible "a" line. At most one for now. */
- if (!tor_addr_is_null(&rs->ipv6_addr)) {
- smartlist_add_asprintf(chunks, "a %s\n",
- fmt_addrport(&rs->ipv6_addr, rs->ipv6_orport));
- }
-
- if (format == NS_V3_CONSENSUS || format == NS_V3_CONSENSUS_MICRODESC)
- goto done;
-
- smartlist_add_asprintf(chunks,
- "s%s%s%s%s%s%s%s%s%s%s\n",
- /* These must stay in alphabetical order. */
- rs->is_authority?" Authority":"",
- rs->is_bad_exit?" BadExit":"",
- rs->is_exit?" Exit":"",
- rs->is_fast?" Fast":"",
- rs->is_possible_guard?" Guard":"",
- rs->is_hs_dir?" HSDir":"",
- rs->is_flagged_running?" Running":"",
- rs->is_stable?" Stable":"",
- rs->is_v2_dir?" V2Dir":"",
- rs->is_valid?" Valid":"");
-
- /* length of "opt v \n" */
-#define V_LINE_OVERHEAD 7
- if (version && strlen(version) < MAX_V_LINE_LEN - V_LINE_OVERHEAD) {
- smartlist_add_asprintf(chunks, "v %s\n", version);
- }
- if (protocols) {
- smartlist_add_asprintf(chunks, "pr %s\n", protocols);
- }
-
- if (format != NS_V2) {
- const routerinfo_t* desc = router_get_by_id_digest(rs->identity_digest);
- uint32_t bw_kb;
-
- if (format != NS_CONTROL_PORT) {
- /* Blow up more or less nicely if we didn't get anything or not the
- * thing we expected.
- */
- if (!desc) {
- char id[HEX_DIGEST_LEN+1];
- char dd[HEX_DIGEST_LEN+1];
-
- base16_encode(id, sizeof(id), rs->identity_digest, DIGEST_LEN);
- base16_encode(dd, sizeof(dd), rs->descriptor_digest, DIGEST_LEN);
- log_warn(LD_BUG, "Cannot get any descriptor for %s "
- "(wanted descriptor %s).",
- id, dd);
- goto err;
- }
-
- /* This assert could fire for the control port, because
- * it can request NS documents before all descriptors
- * have been fetched. Therefore, we only do this test when
- * format != NS_CONTROL_PORT. */
- if (tor_memneq(desc->cache_info.signed_descriptor_digest,
- rs->descriptor_digest,
- DIGEST_LEN)) {
- char rl_d[HEX_DIGEST_LEN+1];
- char rs_d[HEX_DIGEST_LEN+1];
- char id[HEX_DIGEST_LEN+1];
-
- base16_encode(rl_d, sizeof(rl_d),
- desc->cache_info.signed_descriptor_digest, DIGEST_LEN);
- base16_encode(rs_d, sizeof(rs_d), rs->descriptor_digest, DIGEST_LEN);
- base16_encode(id, sizeof(id), rs->identity_digest, DIGEST_LEN);
- log_err(LD_BUG, "descriptor digest in routerlist does not match "
- "the one in routerstatus: %s vs %s "
- "(router %s)\n",
- rl_d, rs_d, id);
-
- tor_assert(tor_memeq(desc->cache_info.signed_descriptor_digest,
- rs->descriptor_digest,
- DIGEST_LEN));
- }
- }
-
- if (format == NS_CONTROL_PORT && rs->has_bandwidth) {
- bw_kb = rs->bandwidth_kb;
- } else {
- tor_assert(desc);
- bw_kb = router_get_advertised_bandwidth_capped(desc) / 1000;
- }
- smartlist_add_asprintf(chunks,
- "w Bandwidth=%d", bw_kb);
-
- if (format == NS_V3_VOTE && vrs && vrs->has_measured_bw) {
- smartlist_add_asprintf(chunks,
- " Measured=%d", vrs->measured_bw_kb);
- }
- /* Write down guardfraction information if we have it. */
- if (format == NS_V3_VOTE && vrs && vrs->status.has_guardfraction) {
- smartlist_add_asprintf(chunks,
- " GuardFraction=%d",
- vrs->status.guardfraction_percentage);
- }
-
- smartlist_add_strdup(chunks, "\n");
-
- if (desc) {
- summary = policy_summarize(desc->exit_policy, AF_INET);
- smartlist_add_asprintf(chunks, "p %s\n", summary);
- tor_free(summary);
- }
-
- if (format == NS_V3_VOTE && vrs) {
- if (tor_mem_is_zero((char*)vrs->ed25519_id, ED25519_PUBKEY_LEN)) {
- smartlist_add_strdup(chunks, "id ed25519 none\n");
- } else {
- char ed_b64[BASE64_DIGEST256_LEN+1];
- digest256_to_base64(ed_b64, (const char*)vrs->ed25519_id);
- smartlist_add_asprintf(chunks, "id ed25519 %s\n", ed_b64);
- }
- }
- }
-
- done:
- result = smartlist_join_strings(chunks, "", 0, NULL);
-
- err:
- SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
- smartlist_free(chunks);
-
- return result;
-}
-
-/** Extract status information from <b>ri</b> and from other authority
- * functions and store it in <b>rs</b>. <b>rs</b> is zeroed out before it is
- * set.
- *
- * We assume that ri-\>is_running has already been set, e.g. by
- * dirserv_set_router_is_running(ri, now);
- */
-void
-set_routerstatus_from_routerinfo(routerstatus_t *rs,
- node_t *node,
- routerinfo_t *ri,
- time_t now,
- int listbadexits)
-{
- const or_options_t *options = get_options();
- uint32_t routerbw_kb = dirserv_get_credible_bandwidth_kb(ri);
-
- memset(rs, 0, sizeof(routerstatus_t));
-
- rs->is_authority =
- router_digest_is_trusted_dir(ri->cache_info.identity_digest);
-
- /* Already set by compute_performance_thresholds. */
- rs->is_exit = node->is_exit;
- rs->is_stable = node->is_stable =
- !dirserv_thinks_router_is_unreliable(now, ri, 1, 0);
- rs->is_fast = node->is_fast =
- !dirserv_thinks_router_is_unreliable(now, ri, 0, 1);
- rs->is_flagged_running = node->is_running; /* computed above */
-
- rs->is_valid = node->is_valid;
-
- if (node->is_fast && node->is_stable &&
- ri->supports_tunnelled_dir_requests &&
- ((options->AuthDirGuardBWGuarantee &&
- routerbw_kb >= options->AuthDirGuardBWGuarantee/1000) ||
- routerbw_kb >= MIN(guard_bandwidth_including_exits_kb,
- guard_bandwidth_excluding_exits_kb))) {
- long tk = rep_hist_get_weighted_time_known(
- node->identity, now);
- double wfu = rep_hist_get_weighted_fractional_uptime(
- node->identity, now);
- rs->is_possible_guard = (wfu >= guard_wfu && tk >= guard_tk) ? 1 : 0;
- } else {
- rs->is_possible_guard = 0;
- }
-
- rs->is_bad_exit = listbadexits && node->is_bad_exit;
- rs->is_hs_dir = node->is_hs_dir =
- dirserv_thinks_router_is_hs_dir(ri, node, now);
-
- rs->is_named = rs->is_unnamed = 0;
-
- rs->published_on = ri->cache_info.published_on;
- memcpy(rs->identity_digest, node->identity, DIGEST_LEN);
- memcpy(rs->descriptor_digest, ri->cache_info.signed_descriptor_digest,
- DIGEST_LEN);
- rs->addr = ri->addr;
- strlcpy(rs->nickname, ri->nickname, sizeof(rs->nickname));
- rs->or_port = ri->or_port;
- rs->dir_port = ri->dir_port;
- rs->is_v2_dir = ri->supports_tunnelled_dir_requests;
- if (options->AuthDirHasIPv6Connectivity == 1 &&
- !tor_addr_is_null(&ri->ipv6_addr) &&
- node->last_reachable6 >= now - REACHABLE_TIMEOUT) {
- /* We're configured as having IPv6 connectivity. There's an IPv6
- OR port and it's reachable so copy it to the routerstatus. */
- tor_addr_copy(&rs->ipv6_addr, &ri->ipv6_addr);
- rs->ipv6_orport = ri->ipv6_orport;
- } else {
- tor_addr_make_null(&rs->ipv6_addr, AF_INET6);
- rs->ipv6_orport = 0;
- }
-
- if (options->TestingTorNetwork) {
- dirserv_set_routerstatus_testing(rs);
- }
-}
-
-/** Use TestingDirAuthVoteExit, TestingDirAuthVoteGuard, and
- * TestingDirAuthVoteHSDir to give out the Exit, Guard, and HSDir flags,
- * respectively. But don't set the corresponding node flags.
- * Should only be called if TestingTorNetwork is set. */
-STATIC void
-dirserv_set_routerstatus_testing(routerstatus_t *rs)
-{
- const or_options_t *options = get_options();
-
- tor_assert(options->TestingTorNetwork);
-
- if (routerset_contains_routerstatus(options->TestingDirAuthVoteExit,
- rs, 0)) {
- rs->is_exit = 1;
- } else if (options->TestingDirAuthVoteExitIsStrict) {
- rs->is_exit = 0;
- }
-
- if (routerset_contains_routerstatus(options->TestingDirAuthVoteGuard,
- rs, 0)) {
- rs->is_possible_guard = 1;
- } else if (options->TestingDirAuthVoteGuardIsStrict) {
- rs->is_possible_guard = 0;
- }
-
- if (routerset_contains_routerstatus(options->TestingDirAuthVoteHSDir,
- rs, 0)) {
- rs->is_hs_dir = 1;
- } else if (options->TestingDirAuthVoteHSDirIsStrict) {
- rs->is_hs_dir = 0;
- }
-}
-
-/** The guardfraction of the guard with identity fingerprint <b>guard_id</b>
- * is <b>guardfraction_percentage</b>. See if we have a vote routerstatus for
- * this guard in <b>vote_routerstatuses</b>, and if we do, register the
- * information to it.
- *
- * Return 1 if we applied the information and 0 if we couldn't find a
- * matching guard.
- *
- * Requires that <b>vote_routerstatuses</b> be sorted.
- */
-static int
-guardfraction_line_apply(const char *guard_id,
- uint32_t guardfraction_percentage,
- smartlist_t *vote_routerstatuses)
-{
- vote_routerstatus_t *vrs = NULL;
-
- tor_assert(vote_routerstatuses);
-
- vrs = smartlist_bsearch(vote_routerstatuses, guard_id,
- compare_digest_to_vote_routerstatus_entry);
-
- if (!vrs) {
- return 0;
- }
-
- vrs->status.has_guardfraction = 1;
- vrs->status.guardfraction_percentage = guardfraction_percentage;
-
- return 1;
-}
-
-/* Given a guard line from a guardfraction file, parse it and register
- * its information to <b>vote_routerstatuses</b>.
- *
- * Return:
- * * 1 if the line was proper and its information got registered.
- * * 0 if the line was proper but no currently active guard was found
- * to register the guardfraction information to.
- * * -1 if the line could not be parsed and set <b>err_msg</b> to a
- newly allocated string containing the error message.
- */
-static int
-guardfraction_file_parse_guard_line(const char *guard_line,
- smartlist_t *vote_routerstatuses,
- char **err_msg)
-{
- char guard_id[DIGEST_LEN];
- uint32_t guardfraction;
- char *inputs_tmp = NULL;
- int num_ok = 1;
-
- smartlist_t *sl = smartlist_new();
- int retval = -1;
-
- tor_assert(err_msg);
-
- /* guard_line should contain something like this:
- <hex digest> <guardfraction> <appearances> */
- smartlist_split_string(sl, guard_line, " ",
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 3);
- if (smartlist_len(sl) < 3) {
- tor_asprintf(err_msg, "bad line '%s'", guard_line);
- goto done;
- }
-
- inputs_tmp = smartlist_get(sl, 0);
- if (strlen(inputs_tmp) != HEX_DIGEST_LEN ||
- base16_decode(guard_id, DIGEST_LEN,
- inputs_tmp, HEX_DIGEST_LEN) != DIGEST_LEN) {
- tor_asprintf(err_msg, "bad digest '%s'", inputs_tmp);
- goto done;
- }
-
- inputs_tmp = smartlist_get(sl, 1);
- /* Guardfraction is an integer in [0, 100]. */
- guardfraction =
- (uint32_t) tor_parse_long(inputs_tmp, 10, 0, 100, &num_ok, NULL);
- if (!num_ok) {
- tor_asprintf(err_msg, "wrong percentage '%s'", inputs_tmp);
- goto done;
- }
-
- /* If routerstatuses were provided, apply this info to actual routers. */
- if (vote_routerstatuses) {
- retval = guardfraction_line_apply(guard_id, guardfraction,
- vote_routerstatuses);
- } else {
- retval = 0; /* If we got this far, line was correctly formatted. */
- }
-
- done:
-
- SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
- smartlist_free(sl);
-
- return retval;
-}
-
-/** Given an inputs line from a guardfraction file, parse it and
- * register its information to <b>total_consensuses</b> and
- * <b>total_days</b>.
- *
- * Return 0 if it parsed well. Return -1 if there was an error, and
- * set <b>err_msg</b> to a newly allocated string containing the
- * error message.
- */
-static int
-guardfraction_file_parse_inputs_line(const char *inputs_line,
- int *total_consensuses,
- int *total_days,
- char **err_msg)
-{
- int retval = -1;
- char *inputs_tmp = NULL;
- int num_ok = 1;
- smartlist_t *sl = smartlist_new();
-
- tor_assert(err_msg);
-
- /* Second line is inputs information:
- * n-inputs <total_consensuses> <total_days>. */
- smartlist_split_string(sl, inputs_line, " ",
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 3);
- if (smartlist_len(sl) < 2) {
- tor_asprintf(err_msg, "incomplete line '%s'", inputs_line);
- goto done;
- }
-
- inputs_tmp = smartlist_get(sl, 0);
- *total_consensuses =
- (int) tor_parse_long(inputs_tmp, 10, 0, INT_MAX, &num_ok, NULL);
- if (!num_ok) {
- tor_asprintf(err_msg, "unparseable consensus '%s'", inputs_tmp);
- goto done;
- }
-
- inputs_tmp = smartlist_get(sl, 1);
- *total_days =
- (int) tor_parse_long(inputs_tmp, 10, 0, INT_MAX, &num_ok, NULL);
- if (!num_ok) {
- tor_asprintf(err_msg, "unparseable days '%s'", inputs_tmp);
- goto done;
- }
-
- retval = 0;
-
- done:
- SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
- smartlist_free(sl);
-
- return retval;
-}
-
-/* Maximum age of a guardfraction file that we are willing to accept. */
-#define MAX_GUARDFRACTION_FILE_AGE (7*24*60*60) /* approx a week */
-
-/** Static strings of guardfraction files. */
-#define GUARDFRACTION_DATE_STR "written-at"
-#define GUARDFRACTION_INPUTS "n-inputs"
-#define GUARDFRACTION_GUARD "guard-seen"
-#define GUARDFRACTION_VERSION "guardfraction-file-version"
-
-/** Given a guardfraction file in a string, parse it and register the
- * guardfraction information to the provided vote routerstatuses.
- *
- * This is the rough format of the guardfraction file:
- *
- * guardfraction-file-version 1
- * written-at <date and time>
- * n-inputs <number of consesuses parsed> <number of days considered>
- *
- * guard-seen <fpr 1> <guardfraction percentage> <consensus appearances>
- * guard-seen <fpr 2> <guardfraction percentage> <consensus appearances>
- * guard-seen <fpr 3> <guardfraction percentage> <consensus appearances>
- * guard-seen <fpr 4> <guardfraction percentage> <consensus appearances>
- * guard-seen <fpr 5> <guardfraction percentage> <consensus appearances>
- * ...
- *
- * Return -1 if the parsing failed and 0 if it went smoothly. Parsing
- * should tolerate errors in all lines but the written-at header.
- */
-STATIC int
-dirserv_read_guardfraction_file_from_str(const char *guardfraction_file_str,
- smartlist_t *vote_routerstatuses)
-{
- config_line_t *front=NULL, *line;
- int ret_tmp;
- int retval = -1;
- int current_line_n = 0; /* line counter for better log messages */
-
- /* Guardfraction info to be parsed */
- int total_consensuses = 0;
- int total_days = 0;
-
- /* Stats */
- int guards_read_n = 0;
- int guards_applied_n = 0;
-
- /* Parse file and split it in lines */
- ret_tmp = config_get_lines(guardfraction_file_str, &front, 0);
- if (ret_tmp < 0) {
- log_warn(LD_CONFIG, "Error reading from guardfraction file");
- goto done;
- }
-
- /* Sort routerstatuses (needed later when applying guardfraction info) */
- if (vote_routerstatuses)
- smartlist_sort(vote_routerstatuses, compare_vote_routerstatus_entries);
-
- for (line = front; line; line=line->next) {
- current_line_n++;
-
- if (!strcmp(line->key, GUARDFRACTION_VERSION)) {
- int num_ok = 1;
- unsigned int version;
-
- version =
- (unsigned int) tor_parse_long(line->value,
- 10, 0, INT_MAX, &num_ok, NULL);
-
- if (!num_ok || version != 1) {
- log_warn(LD_GENERAL, "Got unknown guardfraction version %d.", version);
- goto done;
- }
- } else if (!strcmp(line->key, GUARDFRACTION_DATE_STR)) {
- time_t file_written_at;
- time_t now = time(NULL);
-
- /* First line is 'written-at <date>' */
- if (parse_iso_time(line->value, &file_written_at) < 0) {
- log_warn(LD_CONFIG, "Guardfraction:%d: Bad date '%s'. Ignoring",
- current_line_n, line->value);
- goto done; /* don't tolerate failure here. */
- }
- if (file_written_at < now - MAX_GUARDFRACTION_FILE_AGE) {
- log_warn(LD_CONFIG, "Guardfraction:%d: was written very long ago '%s'",
- current_line_n, line->value);
- goto done; /* don't tolerate failure here. */
- }
- } else if (!strcmp(line->key, GUARDFRACTION_INPUTS)) {
- char *err_msg = NULL;
-
- if (guardfraction_file_parse_inputs_line(line->value,
- &total_consensuses,
- &total_days,
- &err_msg) < 0) {
- log_warn(LD_CONFIG, "Guardfraction:%d: %s",
- current_line_n, err_msg);
- tor_free(err_msg);
- continue;
- }
-
- } else if (!strcmp(line->key, GUARDFRACTION_GUARD)) {
- char *err_msg = NULL;
-
- ret_tmp = guardfraction_file_parse_guard_line(line->value,
- vote_routerstatuses,
- &err_msg);
- if (ret_tmp < 0) { /* failed while parsing the guard line */
- log_warn(LD_CONFIG, "Guardfraction:%d: %s",
- current_line_n, err_msg);
- tor_free(err_msg);
- continue;
- }
-
- /* Successfully parsed guard line. Check if it was applied properly. */
- guards_read_n++;
- if (ret_tmp > 0) {
- guards_applied_n++;
- }
- } else {
- log_warn(LD_CONFIG, "Unknown guardfraction line %d (%s %s)",
- current_line_n, line->key, line->value);
- }
- }
-
- retval = 0;
-
- log_info(LD_CONFIG,
- "Successfully parsed guardfraction file with %d consensuses over "
- "%d days. Parsed %d nodes and applied %d of them%s.",
- total_consensuses, total_days, guards_read_n, guards_applied_n,
- vote_routerstatuses ? "" : " (no routerstatus provided)" );
-
- done:
- config_free_lines(front);
-
- if (retval < 0) {
- return retval;
- } else {
- return guards_read_n;
- }
-}
-
-/** Read a guardfraction file at <b>fname</b> and load all its
- * information to <b>vote_routerstatuses</b>. */
-int
-dirserv_read_guardfraction_file(const char *fname,
- smartlist_t *vote_routerstatuses)
-{
- char *guardfraction_file_str;
-
- /* Read file to a string */
- guardfraction_file_str = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL);
- if (!guardfraction_file_str) {
- log_warn(LD_FS, "Cannot open guardfraction file '%s'. Failing.", fname);
- return -1;
- }
-
- return dirserv_read_guardfraction_file_from_str(guardfraction_file_str,
- vote_routerstatuses);
-}
-
-/**
- * Helper function to parse out a line in the measured bandwidth file
- * into a measured_bw_line_t output structure.
- *
- * If <b>line_is_after_headers</b> is true, then if we encounter an incomplete
- * bw line, return -1 and warn, since we are after the headers and we should
- * only parse bw lines. Return 0 otherwise.
- *
- * If <b>line_is_after_headers</b> is false then it means that we are not past
- * the header block yet. If we encounter an incomplete bw line, return -1 but
- * don't warn since there could be additional header lines coming. If we
- * encounter a proper bw line, return 0 (and we got past the headers).
- */
-STATIC int
-measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line,
- int line_is_after_headers)
-{
- char *line = tor_strdup(orig_line);
- char *cp = line;
- int got_bw = 0;
- int got_node_id = 0;
- char *strtok_state; /* lame sauce d'jour */
-
- if (strlen(line) == 0) {
- log_warn(LD_DIRSERV, "Empty line in bandwidth file");
- tor_free(line);
- return -1;
- }
-
- /* Remove end of line character, so that is not part of the token */
- if (line[strlen(line) - 1] == '\n') {
- line[strlen(line) - 1] = '\0';
- }
-
- cp = tor_strtok_r(cp, " \t", &strtok_state);
-
- if (!cp) {
- log_warn(LD_DIRSERV, "Invalid line in bandwidth file: %s",
- escaped(orig_line));
- tor_free(line);
- return -1;
- }
-
- if (orig_line[strlen(orig_line)-1] != '\n') {
- log_warn(LD_DIRSERV, "Incomplete line in bandwidth file: %s",
- escaped(orig_line));
- tor_free(line);
- return -1;
- }
-
- do {
- if (strcmpstart(cp, "bw=") == 0) {
- int parse_ok = 0;
- char *endptr;
- if (got_bw) {
- log_warn(LD_DIRSERV, "Double bw= in bandwidth file line: %s",
- escaped(orig_line));
- tor_free(line);
- return -1;
- }
- cp+=strlen("bw=");
-
- out->bw_kb = tor_parse_long(cp, 10, 0, LONG_MAX, &parse_ok, &endptr);
- if (!parse_ok || (*endptr && !TOR_ISSPACE(*endptr))) {
- log_warn(LD_DIRSERV, "Invalid bandwidth in bandwidth file line: %s",
- escaped(orig_line));
- tor_free(line);
- return -1;
- }
- got_bw=1;
- } else if (strcmpstart(cp, "node_id=$") == 0) {
- if (got_node_id) {
- log_warn(LD_DIRSERV, "Double node_id= in bandwidth file line: %s",
- escaped(orig_line));
- tor_free(line);
- return -1;
- }
- cp+=strlen("node_id=$");
-
- if (strlen(cp) != HEX_DIGEST_LEN ||
- base16_decode(out->node_id, DIGEST_LEN,
- cp, HEX_DIGEST_LEN) != DIGEST_LEN) {
- log_warn(LD_DIRSERV, "Invalid node_id in bandwidth file line: %s",
- escaped(orig_line));
- tor_free(line);
- return -1;
- }
- strlcpy(out->node_hex, cp, sizeof(out->node_hex));
- got_node_id=1;
- }
- } while ((cp = tor_strtok_r(NULL, " \t", &strtok_state)));
-
- if (got_bw && got_node_id) {
- tor_free(line);
- return 0;
- } else if (line_is_after_headers == 0) {
- /* There could be additional header lines, therefore do not give warnings
- * but returns -1 since it's not a complete bw line. */
- log_debug(LD_DIRSERV, "Missing bw or node_id in bandwidth file line: %s",
- escaped(orig_line));
- tor_free(line);
- return -1;
- } else {
- log_warn(LD_DIRSERV, "Incomplete line in bandwidth file: %s",
- escaped(orig_line));
- tor_free(line);
- return -1;
- }
-}
-
-/**
- * Helper function to apply a parsed measurement line to a list
- * of bandwidth statuses. Returns true if a line is found,
- * false otherwise.
- */
-STATIC int
-measured_bw_line_apply(measured_bw_line_t *parsed_line,
- smartlist_t *routerstatuses)
-{
- vote_routerstatus_t *rs = NULL;
- if (!routerstatuses)
- return 0;
-
- rs = smartlist_bsearch(routerstatuses, parsed_line->node_id,
- compare_digest_to_vote_routerstatus_entry);
-
- if (rs) {
- rs->has_measured_bw = 1;
- rs->measured_bw_kb = (uint32_t)parsed_line->bw_kb;
- } else {
- log_info(LD_DIRSERV, "Node ID %s not found in routerstatus list",
- parsed_line->node_hex);
- }
-
- return rs != NULL;
-}
-
-/**
- * Read the measured bandwidth list file, apply it to the list of
- * vote_routerstatus_t and store all the headers in <b>bw_file_headers</b>.
- * Returns -1 on error, 0 otherwise.
- */
-int
-dirserv_read_measured_bandwidths(const char *from_file,
- smartlist_t *routerstatuses,
- smartlist_t *bw_file_headers)
-{
- FILE *fp = tor_fopen_cloexec(from_file, "r");
- int applied_lines = 0;
- time_t file_time, now;
- int ok;
- /* This flag will be 1 only when the first successful bw measurement line
- * has been encountered, so that measured_bw_line_parse don't give warnings
- * if there are additional header lines, as introduced in Bandwidth List spec
- * version 1.1.0 */
- int line_is_after_headers = 0;
- int rv = -1;
- char *line = NULL;
- size_t n = 0;
-
- /* Initialise line, so that we can't possibly run off the end. */
-
- if (fp == NULL) {
- log_warn(LD_CONFIG, "Can't open bandwidth file at configured location: %s",
- from_file);
- goto err;
- }
-
- /* If fgets fails, line is either unmodified, or indeterminate. */
- if (tor_getline(&line,&n,fp) <= 0) {
- log_warn(LD_DIRSERV, "Empty bandwidth file");
- goto err;
- }
-
- if (!strlen(line) || line[strlen(line)-1] != '\n') {
- log_warn(LD_DIRSERV, "Long or truncated time in bandwidth file: %s",
- escaped(line));
- goto err;
- }
-
- line[strlen(line)-1] = '\0';
- file_time = (time_t)tor_parse_ulong(line, 10, 0, ULONG_MAX, &ok, NULL);
- if (!ok) {
- log_warn(LD_DIRSERV, "Non-integer time in bandwidth file: %s",
- escaped(line));
- goto err;
- }
-
- now = time(NULL);
- if ((now - file_time) > MAX_MEASUREMENT_AGE) {
- log_warn(LD_DIRSERV, "Bandwidth measurement file stale. Age: %u",
- (unsigned)(time(NULL) - file_time));
- goto err;
- }
-
- /* If timestamp was correct and bw_file_headers is not NULL,
- * add timestamp to bw_file_headers */
- if (bw_file_headers)
- smartlist_add_asprintf(bw_file_headers, "timestamp=%lu",
- (unsigned long)file_time);
-
- if (routerstatuses)
- smartlist_sort(routerstatuses, compare_vote_routerstatus_entries);
-
- while (!feof(fp)) {
- measured_bw_line_t parsed_line;
- if (tor_getline(&line, &n, fp) >= 0) {
- if (measured_bw_line_parse(&parsed_line, line,
- line_is_after_headers) != -1) {
- /* This condition will be true when the first complete valid bw line
- * has been encountered, which means the end of the header lines. */
- line_is_after_headers = 1;
- /* Also cache the line for dirserv_get_bandwidth_for_router() */
- dirserv_cache_measured_bw(&parsed_line, file_time);
- if (measured_bw_line_apply(&parsed_line, routerstatuses) > 0)
- applied_lines++;
- /* if the terminator is found, it is the end of header lines, set the
- * flag but do not store anything */
- } else if (strcmp(line, BW_FILE_HEADERS_TERMINATOR) == 0) {
- line_is_after_headers = 1;
- /* if the line was not a correct relay line nor the terminator and
- * the end of the header lines has not been detected yet
- * and it is key_value and bw_file_headers did not reach the maximum
- * number of headers,
- * then assume this line is a header and add it to bw_file_headers */
- } else if (bw_file_headers &&
- (line_is_after_headers == 0) &&
- string_is_key_value(LOG_DEBUG, line) &&
- !strchr(line, ' ') &&
- (smartlist_len(bw_file_headers)
- < MAX_BW_FILE_HEADER_COUNT_IN_VOTE)) {
- line[strlen(line)-1] = '\0';
- smartlist_add_strdup(bw_file_headers, line);
- };
- }
- }
-
- /* Now would be a nice time to clean the cache, too */
- dirserv_expire_measured_bw_cache(now);
-
- log_info(LD_DIRSERV,
- "Bandwidth measurement file successfully read. "
- "Applied %d measurements.", applied_lines);
- rv = 0;
-
- err:
- if (line) {
- // we need to raw_free this buffer because we got it from tor_getdelim()
- raw_free(line);
- }
- if (fp)
- fclose(fp);
- return rv;
-}
-
/** As dirserv_get_routerdescs(), but instead of getting signed_descriptor_t
* pointers, adds copies of digests to fps_out, and doesn't use the
* /tor/server/ prefix. For a /d/ request, adds descriptor digests; for other
@@ -2864,184 +410,6 @@ dirserv_get_routerdescs(smartlist_t *descs_out, const char *key,
return 0;
}
-/** Called when a TLS handshake has completed successfully with a
- * router listening at <b>address</b>:<b>or_port</b>, and has yielded
- * a certificate with digest <b>digest_rcvd</b>.
- *
- * Inform the reachability checker that we could get to this relay.
- */
-void
-dirserv_orconn_tls_done(const tor_addr_t *addr,
- uint16_t or_port,
- const char *digest_rcvd,
- const ed25519_public_key_t *ed_id_rcvd)
-{
- node_t *node = NULL;
- tor_addr_port_t orport;
- routerinfo_t *ri = NULL;
- time_t now = time(NULL);
- tor_assert(addr);
- tor_assert(digest_rcvd);
-
- node = node_get_mutable_by_id(digest_rcvd);
- if (node == NULL || node->ri == NULL)
- return;
-
- ri = node->ri;
-
- if (get_options()->AuthDirTestEd25519LinkKeys &&
- node_supports_ed25519_link_authentication(node, 1) &&
- ri->cache_info.signing_key_cert) {
- /* We allow the node to have an ed25519 key if we haven't been told one in
- * the routerinfo, but if we *HAVE* been told one in the routerinfo, it
- * needs to match. */
- const ed25519_public_key_t *expected_id =
- &ri->cache_info.signing_key_cert->signing_key;
- tor_assert(!ed25519_public_key_is_zero(expected_id));
- if (! ed_id_rcvd || ! ed25519_pubkey_eq(ed_id_rcvd, expected_id)) {
- log_info(LD_DIRSERV, "Router at %s:%d with RSA ID %s "
- "did not present expected Ed25519 ID.",
- fmt_addr(addr), or_port, hex_str(digest_rcvd, DIGEST_LEN));
- return; /* Don't mark it as reachable. */
- }
- }
-
- tor_addr_copy(&orport.addr, addr);
- orport.port = or_port;
- if (router_has_orport(ri, &orport)) {
- /* Found the right router. */
- if (!authdir_mode_bridge(get_options()) ||
- ri->purpose == ROUTER_PURPOSE_BRIDGE) {
- char addrstr[TOR_ADDR_BUF_LEN];
- /* This is a bridge or we're not a bridge authority --
- mark it as reachable. */
- log_info(LD_DIRSERV, "Found router %s to be reachable at %s:%d. Yay.",
- router_describe(ri),
- tor_addr_to_str(addrstr, addr, sizeof(addrstr), 1),
- ri->or_port);
- if (tor_addr_family(addr) == AF_INET) {
- rep_hist_note_router_reachable(digest_rcvd, addr, or_port, now);
- node->last_reachable = now;
- } else if (tor_addr_family(addr) == AF_INET6) {
- /* No rephist for IPv6. */
- node->last_reachable6 = now;
- }
- }
- }
-}
-
-/** Called when we, as an authority, receive a new router descriptor either as
- * an upload or a download. Used to decide whether to relaunch reachability
- * testing for the server. */
-int
-dirserv_should_launch_reachability_test(const routerinfo_t *ri,
- const routerinfo_t *ri_old)
-{
- if (!authdir_mode_handles_descs(get_options(), ri->purpose))
- return 0;
- if (!ri_old) {
- /* New router: Launch an immediate reachability test, so we will have an
- * opinion soon in case we're generating a consensus soon */
- return 1;
- }
- if (ri_old->is_hibernating && !ri->is_hibernating) {
- /* It just came out of hibernation; launch a reachability test */
- return 1;
- }
- if (! routers_have_same_or_addrs(ri, ri_old)) {
- /* Address or port changed; launch a reachability test */
- return 1;
- }
- return 0;
-}
-
-/** Helper function for dirserv_test_reachability(). Start a TLS
- * connection to <b>router</b>, and annotate it with when we started
- * the test. */
-void
-dirserv_single_reachability_test(time_t now, routerinfo_t *router)
-{
- const or_options_t *options = get_options();
- channel_t *chan = NULL;
- const node_t *node = NULL;
- tor_addr_t router_addr;
- const ed25519_public_key_t *ed_id_key;
- (void) now;
-
- tor_assert(router);
- node = node_get_by_id(router->cache_info.identity_digest);
- tor_assert(node);
-
- if (options->AuthDirTestEd25519LinkKeys &&
- node_supports_ed25519_link_authentication(node, 1) &&
- router->cache_info.signing_key_cert) {
- ed_id_key = &router->cache_info.signing_key_cert->signing_key;
- } else {
- ed_id_key = NULL;
- }
-
- /* IPv4. */
- log_debug(LD_OR,"Testing reachability of %s at %s:%u.",
- router->nickname, fmt_addr32(router->addr), router->or_port);
- tor_addr_from_ipv4h(&router_addr, router->addr);
- chan = channel_tls_connect(&router_addr, router->or_port,
- router->cache_info.identity_digest,
- ed_id_key);
- if (chan) command_setup_channel(chan);
-
- /* Possible IPv6. */
- if (get_options()->AuthDirHasIPv6Connectivity == 1 &&
- !tor_addr_is_null(&router->ipv6_addr)) {
- char addrstr[TOR_ADDR_BUF_LEN];
- log_debug(LD_OR, "Testing reachability of %s at %s:%u.",
- router->nickname,
- tor_addr_to_str(addrstr, &router->ipv6_addr, sizeof(addrstr), 1),
- router->ipv6_orport);
- chan = channel_tls_connect(&router->ipv6_addr, router->ipv6_orport,
- router->cache_info.identity_digest,
- ed_id_key);
- if (chan) command_setup_channel(chan);
- }
-}
-
-/** Auth dir server only: load balance such that we only
- * try a few connections per call.
- *
- * The load balancing is such that if we get called once every ten
- * seconds, we will cycle through all the tests in
- * REACHABILITY_TEST_CYCLE_PERIOD seconds (a bit over 20 minutes).
- */
-void
-dirserv_test_reachability(time_t now)
-{
- /* XXX decide what to do here; see or-talk thread "purging old router
- * information, revocation." -NM
- * We can't afford to mess with this in 0.1.2.x. The reason is that
- * if we stop doing reachability tests on some of routerlist, then
- * we'll for-sure think they're down, which may have unexpected
- * effects in other parts of the code. It doesn't hurt much to do
- * the testing, and directory authorities are easy to upgrade. Let's
- * wait til 0.2.0. -RD */
-// time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH;
- routerlist_t *rl = router_get_routerlist();
- static char ctr = 0;
- int bridge_auth = authdir_mode_bridge(get_options());
-
- SMARTLIST_FOREACH_BEGIN(rl->routers, routerinfo_t *, router) {
- const char *id_digest = router->cache_info.identity_digest;
- if (router_is_me(router))
- continue;
- if (bridge_auth && router->purpose != ROUTER_PURPOSE_BRIDGE)
- continue; /* bridge authorities only test reachability on bridges */
-// if (router->cache_info.published_on > cutoff)
-// continue;
- if ((((uint8_t)id_digest[0]) % REACHABILITY_MODULO_PER_TEST) == ctr) {
- dirserv_single_reachability_test(now, router);
- }
- } SMARTLIST_FOREACH_END(router);
- ctr = (ctr + 1) % REACHABILITY_MODULO_PER_TEST; /* increment ctr */
-}
-
/* ==========
* Spooling code.
* ========== */
@@ -3540,90 +908,10 @@ dir_conn_clear_spool(dir_connection_t *conn)
conn->spool = NULL;
}
-/** Return true iff <b>line</b> is a valid RecommendedPackages line.
- */
-/*
- The grammar is:
-
- "package" SP PACKAGENAME SP VERSION SP URL SP DIGESTS NL
-
- PACKAGENAME = NONSPACE
- VERSION = NONSPACE
- URL = NONSPACE
- DIGESTS = DIGEST | DIGESTS SP DIGEST
- DIGEST = DIGESTTYPE "=" DIGESTVAL
-
- NONSPACE = one or more non-space printing characters
-
- DIGESTVAL = DIGESTTYPE = one or more non-=, non-" " characters.
-
- SP = " "
- NL = a newline
-
- */
-int
-validate_recommended_package_line(const char *line)
-{
- const char *cp = line;
-
-#define WORD() \
- do { \
- if (*cp == ' ') \
- return 0; \
- cp = strchr(cp, ' '); \
- if (!cp) \
- return 0; \
- } while (0)
-
- WORD(); /* skip packagename */
- ++cp;
- WORD(); /* skip version */
- ++cp;
- WORD(); /* Skip URL */
- ++cp;
-
- /* Skip digesttype=digestval + */
- int n_entries = 0;
- while (1) {
- const char *start_of_word = cp;
- const char *end_of_word = strchr(cp, ' ');
- if (! end_of_word)
- end_of_word = cp + strlen(cp);
-
- if (start_of_word == end_of_word)
- return 0;
-
- const char *eq = memchr(start_of_word, '=', end_of_word - start_of_word);
-
- if (!eq)
- return 0;
- if (eq == start_of_word)
- return 0;
- if (eq == end_of_word - 1)
- return 0;
- if (memchr(eq+1, '=', end_of_word - (eq+1)))
- return 0;
-
- ++n_entries;
- if (0 == *end_of_word)
- break;
-
- cp = end_of_word + 1;
- }
-
- /* If we reach this point, we have at least 1 entry. */
- tor_assert(n_entries > 0);
- return 1;
-}
-
/** Release all storage used by the directory server. */
void
dirserv_free_all(void)
{
- dirserv_free_fingerprint_list();
-
strmap_free(cached_consensuses, free_cached_dir_);
cached_consensuses = NULL;
-
- dirserv_clear_measured_bw_cache();
}
diff --git a/src/feature/dircache/dirserv.h b/src/feature/dircache/dirserv.h
index 9be4bf9db2..41e1376688 100644
--- a/src/feature/dircache/dirserv.h
+++ b/src/feature/dircache/dirserv.h
@@ -16,46 +16,6 @@ struct ed25519_public_key_t;
#include "lib/testsupport/testsupport.h"
-/** An enum to describe what format we're generating a routerstatus line in.
- */
-typedef enum {
- /** For use in a v2 opinion */
- NS_V2,
- /** For use in a consensus networkstatus document (ns flavor) */
- NS_V3_CONSENSUS,
- /** For use in a vote networkstatus document */
- NS_V3_VOTE,
- /** For passing to the controlport in response to a GETINFO request */
- NS_CONTROL_PORT,
- /** For use in a consensus networkstatus document (microdesc flavor) */
- NS_V3_CONSENSUS_MICRODESC
-} routerstatus_format_type_t;
-
-/** What fraction (1 over this number) of the relay ID space do we
- * (as a directory authority) launch connections to at each reachability
- * test? */
-#define REACHABILITY_MODULO_PER_TEST 128
-
-/** How often (in seconds) do we launch reachability tests? */
-#define REACHABILITY_TEST_INTERVAL 10
-
-/** How many seconds apart are the reachability tests for a given relay? */
-#define REACHABILITY_TEST_CYCLE_PERIOD \
- (REACHABILITY_TEST_INTERVAL*REACHABILITY_MODULO_PER_TEST)
-
-/** Maximum length of an exit policy summary. */
-#define MAX_EXITPOLICY_SUMMARY_LEN 1000
-
-/** Maximum allowable length of a version line in a networkstatus. */
-#define MAX_V_LINE_LEN 128
-
-/** Maximum allowable length of bandwidth headers in a bandwidth file */
-#define MAX_BW_FILE_HEADER_COUNT_IN_VOTE 50
-
-/** Terminatore that separates bandwidth file headers from bandwidth file
- * relay lines */
-#define BW_FILE_HEADERS_TERMINATOR "=====\n"
-
/** Ways to convert a spoolable_resource_t to a bunch of bytes. */
typedef enum dir_spool_source_t {
DIR_SPOOL_SERVER_BY_DIGEST=1, DIR_SPOOL_SERVER_BY_FP,
@@ -111,32 +71,8 @@ typedef struct spooled_resource_t {
off_t cached_dir_offset;
} spooled_resource_t;
-#ifdef DIRSERV_PRIVATE
-typedef struct measured_bw_line_t {
- char node_id[DIGEST_LEN];
- char node_hex[MAX_HEX_NICKNAME_LEN+1];
- long int bw_kb;
-} measured_bw_line_t;
-#endif /* defined(DIRSERV_PRIVATE) */
-
int connection_dirserv_flushed_some(dir_connection_t *conn);
-int dirserv_add_own_fingerprint(crypto_pk_t *pk);
-int dirserv_load_fingerprint_file(void);
-void dirserv_free_fingerprint_list(void);
-enum was_router_added_t dirserv_add_multiple_descriptors(
- const char *desc, uint8_t purpose,
- const char *source,
- const char **msg);
-enum was_router_added_t dirserv_add_descriptor(routerinfo_t *ri,
- const char **msg,
- const char *source);
-void dirserv_set_router_is_running(routerinfo_t *router, time_t now);
-int list_server_status_v1(smartlist_t *routers, char **router_status_out,
- int for_controller);
-char *dirserv_get_flag_thresholds_line(void);
-void dirserv_compute_bridge_flag_thresholds(void);
-
int directory_fetches_from_authorities(const or_options_t *options);
int directory_fetches_dir_info_early(const or_options_t *options);
int directory_fetches_dir_info_later(const or_options_t *options);
@@ -159,76 +95,10 @@ int dirserv_get_routerdesc_spool(smartlist_t *spools_out, const char *key,
const char **msg_out);
int dirserv_get_routerdescs(smartlist_t *descs_out, const char *key,
const char **msg);
-void dirserv_orconn_tls_done(const tor_addr_t *addr,
- uint16_t or_port,
- const char *digest_rcvd,
- const struct ed25519_public_key_t *ed_id_rcvd);
-int dirserv_should_launch_reachability_test(const routerinfo_t *ri,
- const routerinfo_t *ri_old);
-void dirserv_single_reachability_test(time_t now, routerinfo_t *router);
-void dirserv_test_reachability(time_t now);
-int authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg,
- int complain,
- int *valid_out);
-uint32_t dirserv_router_get_status(const routerinfo_t *router,
- const char **msg,
- int severity);
-void dirserv_set_node_flags_from_authoritative_status(node_t *node,
- uint32_t authstatus);
-int dirserv_would_reject_router(const routerstatus_t *rs);
-char *routerstatus_format_entry(
- const routerstatus_t *rs,
- const char *version,
- const char *protocols,
- routerstatus_format_type_t format,
- int consensus_method,
- const vote_routerstatus_t *vrs);
void dirserv_free_all(void);
void cached_dir_decref(cached_dir_t *d);
cached_dir_t *new_cached_dir(char *s, time_t published);
-struct config_line_t;
-char *format_recommended_version_list(const struct config_line_t *line,
- int warn);
-int validate_recommended_package_line(const char *line);
-int dirserv_query_measured_bw_cache_kb(const char *node_id,
- long *bw_out,
- time_t *as_of_out);
-void dirserv_clear_measured_bw_cache(void);
-int dirserv_has_measured_bw(const char *node_id);
-int dirserv_get_measured_bw_cache_size(void);
-void dirserv_count_measured_bws(const smartlist_t *routers);
-int running_long_enough_to_decide_unreachable(void);
-void dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil);
-
-#ifdef DIRSERV_PRIVATE
-
-STATIC void dirserv_set_routerstatus_testing(routerstatus_t *rs);
-
-/* Put the MAX_MEASUREMENT_AGE #define here so unit tests can see it */
-#define MAX_MEASUREMENT_AGE (3*24*60*60) /* 3 days */
-
-STATIC int measured_bw_line_parse(measured_bw_line_t *out, const char *line,
- int line_is_after_headers);
-
-STATIC int measured_bw_line_apply(measured_bw_line_t *parsed_line,
- smartlist_t *routerstatuses);
-
-STATIC void dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line,
- time_t as_of);
-STATIC void dirserv_expire_measured_bw_cache(time_t now);
-
-STATIC int
-dirserv_read_guardfraction_file_from_str(const char *guardfraction_file_str,
- smartlist_t *vote_routerstatuses);
-#endif /* defined(DIRSERV_PRIVATE) */
-
-int dirserv_read_measured_bandwidths(const char *from_file,
- smartlist_t *routerstatuses,
- smartlist_t *bw_file_headers);
-
-int dirserv_read_guardfraction_file(const char *fname,
- smartlist_t *vote_routerstatuses);
spooled_resource_t *spooled_resource_new(dir_spool_source_t source,
const uint8_t *digest,
diff --git a/src/feature/hibernate/hibernate.c b/src/feature/hibernate/hibernate.c
index d37ba5b8b1..02b05ca3a2 100644
--- a/src/feature/hibernate/hibernate.c
+++ b/src/feature/hibernate/hibernate.c
@@ -38,7 +38,7 @@ hibernating, phase 2:
#include "feature/control/control.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "feature/hibernate/hibernate.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/relay/router.h"
#include "app/config/statefile.h"
#include "lib/evloop/compat_libevent.h"
diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c
index 441edc3247..52a4e9a007 100644
--- a/src/feature/hs/hs_client.c
+++ b/src/feature/hs/hs_client.c
@@ -247,7 +247,7 @@ close_all_socks_conns_waiting_for_desc(const ed25519_public_key_t *identity_pk,
/* Find all pending SOCKS connection waiting for a descriptor and retry them
* all. This is called when the directory information changed. */
-static void
+STATIC void
retry_all_socks_conn_waiting_for_desc(void)
{
smartlist_t *conns =
@@ -1699,7 +1699,7 @@ hs_client_desc_has_arrived(const hs_ident_dir_conn_t *ident)
/* We are unable to use the descriptor so remove the directory request
* from the cache so the next connection can try again. */
note_connection_attempt_succeeded(edge_conn->hs_ident);
- goto end;
+ continue;
}
log_info(LD_REND, "Descriptor has arrived. Launching circuits.");
diff --git a/src/feature/hs/hs_client.h b/src/feature/hs/hs_client.h
index 1ba0338dc3..fb4f9e9e9f 100644
--- a/src/feature/hs/hs_client.h
+++ b/src/feature/hs/hs_client.h
@@ -104,6 +104,8 @@ STATIC int handle_rendezvous2(origin_circuit_t *circ, const uint8_t *payload,
MOCK_DECL(STATIC hs_client_fetch_status_t,
fetch_v3_desc, (const ed25519_public_key_t *onion_identity_pk));
+STATIC void retry_all_socks_conn_waiting_for_desc(void);
+
#ifdef TOR_UNIT_TESTS
STATIC digest256map_t *get_hs_client_auths_map(void);
diff --git a/src/feature/hs/hs_common.c b/src/feature/hs/hs_common.c
index 12405a79cb..c36892e0f8 100644
--- a/src/feature/hs/hs_common.c
+++ b/src/feature/hs/hs_common.c
@@ -882,6 +882,11 @@ hs_set_conn_addr_port(const smartlist_t *ports, edge_connection_t *conn)
smartlist_free(matching_ports);
if (chosen_port) {
if (!(chosen_port->is_unix_addr)) {
+ /* save the original destination before we overwrite it */
+ if (conn->hs_ident) {
+ conn->hs_ident->orig_virtual_port = TO_CONN(conn)->port;
+ }
+
/* Get a non-AF_UNIX connection ready for connection_exit_connect() */
tor_addr_copy(&TO_CONN(conn)->addr, &chosen_port->real_addr);
TO_CONN(conn)->port = chosen_port->real_port;
diff --git a/src/feature/hs/hs_config.c b/src/feature/hs/hs_config.c
index eaeb58829a..93d7403dfb 100644
--- a/src/feature/hs/hs_config.c
+++ b/src/feature/hs/hs_config.c
@@ -145,6 +145,34 @@ helper_parse_uint64(const char *opt, const char *value, uint64_t min,
return ret;
}
+/** Helper function: Given a configuration option and its value, parse the
+ * value as a hs_circuit_id_protocol_t. On success, ok is set to 1 and ret is
+ * the parse value. On error, ok is set to 0 and the "none"
+ * hs_circuit_id_protocol_t is returned. This function logs on error. */
+static hs_circuit_id_protocol_t
+helper_parse_circuit_id_protocol(const char *key, const char *value, int *ok)
+{
+ tor_assert(value);
+ tor_assert(ok);
+
+ hs_circuit_id_protocol_t ret = HS_CIRCUIT_ID_PROTOCOL_NONE;
+ *ok = 0;
+
+ if (! strcasecmp(value, "haproxy")) {
+ *ok = 1;
+ ret = HS_CIRCUIT_ID_PROTOCOL_HAPROXY;
+ } else if (! strcasecmp(value, "none")) {
+ *ok = 1;
+ ret = HS_CIRCUIT_ID_PROTOCOL_NONE;
+ } else {
+ log_warn(LD_CONFIG, "%s must be 'haproxy' or 'none'.", key);
+ goto err;
+ }
+
+ err:
+ return ret;
+}
+
/* Return the service version by trying to learn it from the key on disk if
* any. If nothing is found, the current service configured version is
* returned. */
@@ -188,6 +216,11 @@ config_has_invalid_options(const config_line_t *line_,
NULL /* End marker. */
};
+ const char *opts_exclude_v2[] = {
+ "HiddenServiceExportCircuitID",
+ NULL /* End marker. */
+ };
+
/* Defining the size explicitly allows us to take advantage of the compiler
* which warns us if we ever bump the max version but forget to grow this
* array. The plus one is because we have a version 0 :). */
@@ -196,7 +229,7 @@ config_has_invalid_options(const config_line_t *line_,
} exclude_lists[HS_VERSION_MAX + 1] = {
{ NULL }, /* v0. */
{ NULL }, /* v1. */
- { NULL }, /* v2 */
+ { opts_exclude_v2 }, /* v2 */
{ opts_exclude_v3 }, /* v3. */
};
@@ -262,6 +295,7 @@ config_service_v3(const config_line_t *line_,
hs_service_config_t *config)
{
int have_num_ip = 0;
+ bool export_circuit_id = false; /* just to detect duplicate options */
const char *dup_opt_seen = NULL;
const config_line_t *line;
@@ -288,6 +322,18 @@ config_service_v3(const config_line_t *line_,
have_num_ip = 1;
continue;
}
+ if (!strcasecmp(line->key, "HiddenServiceExportCircuitID")) {
+ config->circuit_id_protocol =
+ helper_parse_circuit_id_protocol(line->key, line->value, &ok);
+ if (!ok || export_circuit_id) {
+ if (export_circuit_id) {
+ dup_opt_seen = line->key;
+ }
+ goto err;
+ }
+ export_circuit_id = true;
+ continue;
+ }
}
/* We do not load the key material for the service at this stage. This is
diff --git a/src/feature/hs/hs_ident.h b/src/feature/hs/hs_ident.h
index 92d15b0523..ab87d16d17 100644
--- a/src/feature/hs/hs_ident.h
+++ b/src/feature/hs/hs_ident.h
@@ -111,6 +111,10 @@ typedef struct hs_ident_edge_conn_t {
* in the onion address. */
ed25519_public_key_t identity_pk;
+ /* The original virtual port that was used by the client to access the onion
+ * service, regardless of the internal port forwarding that might have
+ * happened on the service-side. */
+ uint16_t orig_virtual_port;
/* XXX: Client authorization. */
} hs_ident_edge_conn_t;
diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c
index 243ecb9b52..643240fb68 100644
--- a/src/feature/hs/hs_service.c
+++ b/src/feature/hs/hs_service.c
@@ -20,14 +20,14 @@
#include "lib/crypt_ops/crypto_ope.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "feature/dircache/directory.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
#include "core/or/relay.h"
#include "feature/rend/rendservice.h"
#include "feature/relay/router.h"
#include "feature/relay/routerkeys.h"
-#include "feature/nodelist/routerlist.h"
+#include "feature/nodelist/node_select.h"
#include "feature/hs_common/shared_random_client.h"
#include "app/config/statefile.h"
@@ -3767,6 +3767,19 @@ hs_service_set_conn_addr_port(const origin_circuit_t *circ,
return -1;
}
+/** Does the service with identity pubkey <b>pk</b> export the circuit IDs of
+ * its clients? */
+hs_circuit_id_protocol_t
+hs_service_exports_circuit_id(const ed25519_public_key_t *pk)
+{
+ hs_service_t *service = find_service(hs_service_map, pk);
+ if (!service) {
+ return HS_CIRCUIT_ID_PROTOCOL_NONE;
+ }
+
+ return service->config.circuit_id_protocol;
+}
+
/* Add to file_list every filename used by a configured hidden service, and to
* dir_list every directory path used by a configured hidden service. This is
* used by the sandbox subsystem to whitelist those. */
diff --git a/src/feature/hs/hs_service.h b/src/feature/hs/hs_service.h
index 735266071f..6fb15b9d37 100644
--- a/src/feature/hs/hs_service.h
+++ b/src/feature/hs/hs_service.h
@@ -161,6 +161,15 @@ typedef struct hs_service_authorized_client_t {
curve25519_public_key_t client_pk;
} hs_service_authorized_client_t;
+/** Which protocol to use for exporting HS client circuit ID. */
+typedef enum {
+ /** Don't expose the circuit id. */
+ HS_CIRCUIT_ID_PROTOCOL_NONE,
+
+ /** Use the HAProxy proxy protocol. */
+ HS_CIRCUIT_ID_PROTOCOL_HAPROXY
+} hs_circuit_id_protocol_t;
+
/* Service configuration. The following are set from the torrc options either
* set by the configuration file or by the control port. Nothing else should
* change those values. */
@@ -210,6 +219,9 @@ typedef struct hs_service_config_t {
/* Is this service ephemeral? */
unsigned int is_ephemeral : 1;
+
+ /* Does this service export the circuit ID of its clients? */
+ hs_circuit_id_protocol_t circuit_id_protocol;
} hs_service_config_t;
/* Service state. */
@@ -316,6 +328,9 @@ void hs_service_upload_desc_to_dir(const char *encoded_desc,
const ed25519_public_key_t *blinded_pk,
const routerstatus_t *hsdir_rs);
+hs_circuit_id_protocol_t
+hs_service_exports_circuit_id(const ed25519_public_key_t *pk);
+
#ifdef HS_SERVICE_PRIVATE
#ifdef TOR_UNIT_TESTS
diff --git a/src/feature/nodelist/authcert.c b/src/feature/nodelist/authcert.c
new file mode 100644
index 0000000000..74a6c5b5fa
--- /dev/null
+++ b/src/feature/nodelist/authcert.c
@@ -0,0 +1,1205 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file authcert.c
+ * \brief Code to maintain directory authorities' certificates.
+ *
+ * Authority certificates are signed with authority identity keys; they
+ * are used to authenticate shorter-term authority signing keys. We
+ * fetch them when we find a consensus or a vote that has been signed
+ * with a signing key we don't recognize. We cache them on disk and
+ * load them on startup. Authority operators generate them with the
+ * "tor-gencert" utility.
+ */
+
+#include "core/or/or.h"
+
+#include "app/config/config.h"
+#include "core/mainloop/connection.h"
+#include "core/mainloop/mainloop.h"
+#include "core/or/policies.h"
+#include "feature/client/bridges.h"
+#include "feature/dircache/directory.h"
+#include "feature/dircommon/fp_pair.h"
+#include "feature/nodelist/authcert.h"
+#include "feature/nodelist/dirlist.h"
+#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/node_select.h"
+#include "feature/nodelist/nodelist.h"
+#include "feature/nodelist/routerlist.h"
+#include "feature/nodelist/routerparse.h"
+#include "feature/relay/router.h"
+
+#include "core/or/connection_st.h"
+#include "feature/dirclient/dir_server_st.h"
+#include "feature/dircommon/dir_connection_st.h"
+#include "feature/nodelist/authority_cert_st.h"
+#include "feature/nodelist/document_signature_st.h"
+#include "feature/nodelist/networkstatus_st.h"
+#include "feature/nodelist/networkstatus_voter_info_st.h"
+#include "feature/nodelist/node_st.h"
+
+DECLARE_TYPED_DIGESTMAP_FNS(dsmap_, digest_ds_map_t, download_status_t)
+#define DSMAP_FOREACH(map, keyvar, valvar) \
+ DIGESTMAP_FOREACH(dsmap_to_digestmap(map), keyvar, download_status_t *, \
+ valvar)
+#define dsmap_free(map, fn) MAP_FREE_AND_NULL(dsmap, (map), (fn))
+
+/* Forward declaration for cert_list_t */
+typedef struct cert_list_t cert_list_t;
+
+static void download_status_reset_by_sk_in_cl(cert_list_t *cl,
+ const char *digest);
+static int download_status_is_ready_by_sk_in_cl(cert_list_t *cl,
+ const char *digest,
+ time_t now);
+static void list_pending_fpsk_downloads(fp_pair_map_t *result);
+
+/** List of certificates for a single authority, and download status for
+ * latest certificate.
+ */
+struct cert_list_t {
+ /*
+ * The keys of download status map are cert->signing_key_digest for pending
+ * downloads by (identity digest/signing key digest) pair; functions such
+ * as authority_cert_get_by_digest() already assume these are unique.
+ */
+ struct digest_ds_map_t *dl_status_map;
+ /* There is also a dlstatus for the download by identity key only */
+ download_status_t dl_status_by_id;
+ smartlist_t *certs;
+};
+/** Map from v3 identity key digest to cert_list_t. */
+static digestmap_t *trusted_dir_certs = NULL;
+
+/** True iff any key certificate in at least one member of
+ * <b>trusted_dir_certs</b> has changed since we last flushed the
+ * certificates to disk. */
+static int trusted_dir_servers_certs_changed = 0;
+
+/** Initialise schedule, want_authority, and increment_on in the download
+ * status dlstatus, then call download_status_reset() on it.
+ * It is safe to call this function or download_status_reset() multiple times
+ * on a new dlstatus. But it should *not* be called after a dlstatus has been
+ * used to count download attempts or failures. */
+static void
+download_status_cert_init(download_status_t *dlstatus)
+{
+ dlstatus->schedule = DL_SCHED_CONSENSUS;
+ dlstatus->want_authority = DL_WANT_ANY_DIRSERVER;
+ dlstatus->increment_on = DL_SCHED_INCREMENT_FAILURE;
+ dlstatus->last_backoff_position = 0;
+ dlstatus->last_delay_used = 0;
+
+ /* Use the new schedule to set next_attempt_at */
+ download_status_reset(dlstatus);
+}
+
+/** Reset the download status of a specified element in a dsmap */
+static void
+download_status_reset_by_sk_in_cl(cert_list_t *cl, const char *digest)
+{
+ download_status_t *dlstatus = NULL;
+
+ tor_assert(cl);
+ tor_assert(digest);
+
+ /* Make sure we have a dsmap */
+ if (!(cl->dl_status_map)) {
+ cl->dl_status_map = dsmap_new();
+ }
+ /* Look for a download_status_t in the map with this digest */
+ dlstatus = dsmap_get(cl->dl_status_map, digest);
+ /* Got one? */
+ if (!dlstatus) {
+ /* Insert before we reset */
+ dlstatus = tor_malloc_zero(sizeof(*dlstatus));
+ dsmap_set(cl->dl_status_map, digest, dlstatus);
+ download_status_cert_init(dlstatus);
+ }
+ tor_assert(dlstatus);
+ /* Go ahead and reset it */
+ download_status_reset(dlstatus);
+}
+
+/**
+ * Return true if the download for this signing key digest in cl is ready
+ * to be re-attempted.
+ */
+static int
+download_status_is_ready_by_sk_in_cl(cert_list_t *cl,
+ const char *digest,
+ time_t now)
+{
+ int rv = 0;
+ download_status_t *dlstatus = NULL;
+
+ tor_assert(cl);
+ tor_assert(digest);
+
+ /* Make sure we have a dsmap */
+ if (!(cl->dl_status_map)) {
+ cl->dl_status_map = dsmap_new();
+ }
+ /* Look for a download_status_t in the map with this digest */
+ dlstatus = dsmap_get(cl->dl_status_map, digest);
+ /* Got one? */
+ if (dlstatus) {
+ /* Use download_status_is_ready() */
+ rv = download_status_is_ready(dlstatus, now);
+ } else {
+ /*
+ * If we don't know anything about it, return 1, since we haven't
+ * tried this one before. We need to create a new entry here,
+ * too.
+ */
+ dlstatus = tor_malloc_zero(sizeof(*dlstatus));
+ download_status_cert_init(dlstatus);
+ dsmap_set(cl->dl_status_map, digest, dlstatus);
+ rv = 1;
+ }
+
+ return rv;
+}
+
+/** Helper: Return the cert_list_t for an authority whose authority ID is
+ * <b>id_digest</b>, allocating a new list if necessary. */
+static cert_list_t *
+get_cert_list(const char *id_digest)
+{
+ cert_list_t *cl;
+ if (!trusted_dir_certs)
+ trusted_dir_certs = digestmap_new();
+ cl = digestmap_get(trusted_dir_certs, id_digest);
+ if (!cl) {
+ cl = tor_malloc_zero(sizeof(cert_list_t));
+ download_status_cert_init(&cl->dl_status_by_id);
+ cl->certs = smartlist_new();
+ cl->dl_status_map = dsmap_new();
+ digestmap_set(trusted_dir_certs, id_digest, cl);
+ }
+ return cl;
+}
+
+/** Return a list of authority ID digests with potentially enumerable lists
+ * of download_status_t objects; used by controller GETINFO queries.
+ */
+
+MOCK_IMPL(smartlist_t *,
+list_authority_ids_with_downloads, (void))
+{
+ smartlist_t *ids = smartlist_new();
+ digestmap_iter_t *i;
+ const char *digest;
+ char *tmp;
+ void *cl;
+
+ if (trusted_dir_certs) {
+ for (i = digestmap_iter_init(trusted_dir_certs);
+ !(digestmap_iter_done(i));
+ i = digestmap_iter_next(trusted_dir_certs, i)) {
+ /*
+ * We always have at least dl_status_by_id to query, so no need to
+ * probe deeper than the existence of a cert_list_t.
+ */
+ digestmap_iter_get(i, &digest, &cl);
+ tmp = tor_malloc(DIGEST_LEN);
+ memcpy(tmp, digest, DIGEST_LEN);
+ smartlist_add(ids, tmp);
+ }
+ }
+ /* else definitely no downloads going since nothing even has a cert list */
+
+ return ids;
+}
+
+/** Given an authority ID digest, return a pointer to the default download
+ * status, or NULL if there is no such entry in trusted_dir_certs */
+
+MOCK_IMPL(download_status_t *,
+id_only_download_status_for_authority_id, (const char *digest))
+{
+ download_status_t *dl = NULL;
+ cert_list_t *cl;
+
+ if (trusted_dir_certs) {
+ cl = digestmap_get(trusted_dir_certs, digest);
+ if (cl) {
+ dl = &(cl->dl_status_by_id);
+ }
+ }
+
+ return dl;
+}
+
+/** Given an authority ID digest, return a smartlist of signing key digests
+ * for which download_status_t is potentially queryable, or NULL if no such
+ * authority ID digest is known. */
+
+MOCK_IMPL(smartlist_t *,
+list_sk_digests_for_authority_id, (const char *digest))
+{
+ smartlist_t *sks = NULL;
+ cert_list_t *cl;
+ dsmap_iter_t *i;
+ const char *sk_digest;
+ char *tmp;
+ download_status_t *dl;
+
+ if (trusted_dir_certs) {
+ cl = digestmap_get(trusted_dir_certs, digest);
+ if (cl) {
+ sks = smartlist_new();
+ if (cl->dl_status_map) {
+ for (i = dsmap_iter_init(cl->dl_status_map);
+ !(dsmap_iter_done(i));
+ i = dsmap_iter_next(cl->dl_status_map, i)) {
+ /* Pull the digest out and add it to the list */
+ dsmap_iter_get(i, &sk_digest, &dl);
+ tmp = tor_malloc(DIGEST_LEN);
+ memcpy(tmp, sk_digest, DIGEST_LEN);
+ smartlist_add(sks, tmp);
+ }
+ }
+ }
+ }
+
+ return sks;
+}
+
+/** Given an authority ID digest and a signing key digest, return the
+ * download_status_t or NULL if none exists. */
+
+MOCK_IMPL(download_status_t *,
+download_status_for_authority_id_and_sk,(const char *id_digest,
+ const char *sk_digest))
+{
+ download_status_t *dl = NULL;
+ cert_list_t *cl = NULL;
+
+ if (trusted_dir_certs) {
+ cl = digestmap_get(trusted_dir_certs, id_digest);
+ if (cl && cl->dl_status_map) {
+ dl = dsmap_get(cl->dl_status_map, sk_digest);
+ }
+ }
+
+ return dl;
+}
+
+#define cert_list_free(val) \
+ FREE_AND_NULL(cert_list_t, cert_list_free_, (val))
+
+/** Release all space held by a cert_list_t */
+static void
+cert_list_free_(cert_list_t *cl)
+{
+ if (!cl)
+ return;
+
+ SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
+ authority_cert_free(cert));
+ smartlist_free(cl->certs);
+ dsmap_free(cl->dl_status_map, tor_free_);
+ tor_free(cl);
+}
+
+/** Wrapper for cert_list_free so we can pass it to digestmap_free */
+static void
+cert_list_free_void(void *cl)
+{
+ cert_list_free_(cl);
+}
+
+/** Reload the cached v3 key certificates from the cached-certs file in
+ * the data directory. Return 0 on success, -1 on failure. */
+int
+trusted_dirs_reload_certs(void)
+{
+ char *filename;
+ char *contents;
+ int r;
+
+ filename = get_cachedir_fname("cached-certs");
+ contents = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL);
+ tor_free(filename);
+ if (!contents)
+ return 0;
+ r = trusted_dirs_load_certs_from_string(
+ contents,
+ TRUSTED_DIRS_CERTS_SRC_FROM_STORE, 1, NULL);
+ tor_free(contents);
+ return r;
+}
+
+/** Helper: return true iff we already have loaded the exact cert
+ * <b>cert</b>. */
+static inline int
+already_have_cert(authority_cert_t *cert)
+{
+ cert_list_t *cl = get_cert_list(cert->cache_info.identity_digest);
+
+ SMARTLIST_FOREACH(cl->certs, authority_cert_t *, c,
+ {
+ if (tor_memeq(c->cache_info.signed_descriptor_digest,
+ cert->cache_info.signed_descriptor_digest,
+ DIGEST_LEN))
+ return 1;
+ });
+ return 0;
+}
+
+/** Load a bunch of new key certificates from the string <b>contents</b>. If
+ * <b>source</b> is TRUSTED_DIRS_CERTS_SRC_FROM_STORE, the certificates are
+ * from the cache, and we don't need to flush them to disk. If we are a
+ * dirauth loading our own cert, source is TRUSTED_DIRS_CERTS_SRC_SELF.
+ * Otherwise, source is download type: TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST
+ * or TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_SK_DIGEST. If <b>flush</b> is true, we
+ * need to flush any changed certificates to disk now. Return 0 on success,
+ * -1 if any certs fail to parse.
+ *
+ * If source_dir is non-NULL, it's the identity digest for a directory that
+ * we've just successfully retrieved certificates from, so try it first to
+ * fetch any missing certificates.
+ */
+int
+trusted_dirs_load_certs_from_string(const char *contents, int source,
+ int flush, const char *source_dir)
+{
+ dir_server_t *ds;
+ const char *s, *eos;
+ int failure_code = 0;
+ int from_store = (source == TRUSTED_DIRS_CERTS_SRC_FROM_STORE);
+ int added_trusted_cert = 0;
+
+ for (s = contents; *s; s = eos) {
+ authority_cert_t *cert = authority_cert_parse_from_string(s, &eos);
+ cert_list_t *cl;
+ if (!cert) {
+ failure_code = -1;
+ break;
+ }
+ ds = trusteddirserver_get_by_v3_auth_digest(
+ cert->cache_info.identity_digest);
+ log_debug(LD_DIR, "Parsed certificate for %s",
+ ds ? ds->nickname : "unknown authority");
+
+ if (already_have_cert(cert)) {
+ /* we already have this one. continue. */
+ log_info(LD_DIR, "Skipping %s certificate for %s that we "
+ "already have.",
+ from_store ? "cached" : "downloaded",
+ ds ? ds->nickname : "an old or new authority");
+
+ /*
+ * A duplicate on download should be treated as a failure, so we call
+ * authority_cert_dl_failed() to reset the download status to make sure
+ * we can't try again. Since we've implemented the fp-sk mechanism
+ * to download certs by signing key, this should be much rarer than it
+ * was and is perhaps cause for concern.
+ */
+ if (!from_store) {
+ if (authdir_mode(get_options())) {
+ log_warn(LD_DIR,
+ "Got a certificate for %s, but we already have it. "
+ "Maybe they haven't updated it. Waiting for a while.",
+ ds ? ds->nickname : "an old or new authority");
+ } else {
+ log_info(LD_DIR,
+ "Got a certificate for %s, but we already have it. "
+ "Maybe they haven't updated it. Waiting for a while.",
+ ds ? ds->nickname : "an old or new authority");
+ }
+
+ /*
+ * This is where we care about the source; authority_cert_dl_failed()
+ * needs to know whether the download was by fp or (fp,sk) pair to
+ * twiddle the right bit in the download map.
+ */
+ if (source == TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST) {
+ authority_cert_dl_failed(cert->cache_info.identity_digest,
+ NULL, 404);
+ } else if (source == TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_SK_DIGEST) {
+ authority_cert_dl_failed(cert->cache_info.identity_digest,
+ cert->signing_key_digest, 404);
+ }
+ }
+
+ authority_cert_free(cert);
+ continue;
+ }
+
+ if (ds) {
+ added_trusted_cert = 1;
+ log_info(LD_DIR, "Adding %s certificate for directory authority %s with "
+ "signing key %s", from_store ? "cached" : "downloaded",
+ ds->nickname, hex_str(cert->signing_key_digest,DIGEST_LEN));
+ } else {
+ int adding = we_want_to_fetch_unknown_auth_certs(get_options());
+ log_info(LD_DIR, "%s %s certificate for unrecognized directory "
+ "authority with signing key %s",
+ adding ? "Adding" : "Not adding",
+ from_store ? "cached" : "downloaded",
+ hex_str(cert->signing_key_digest,DIGEST_LEN));
+ if (!adding) {
+ authority_cert_free(cert);
+ continue;
+ }
+ }
+
+ cl = get_cert_list(cert->cache_info.identity_digest);
+ smartlist_add(cl->certs, cert);
+ if (ds && cert->cache_info.published_on > ds->addr_current_at) {
+ /* Check to see whether we should update our view of the authority's
+ * address. */
+ if (cert->addr && cert->dir_port &&
+ (ds->addr != cert->addr ||
+ ds->dir_port != cert->dir_port)) {
+ char *a = tor_dup_ip(cert->addr);
+ log_notice(LD_DIR, "Updating address for directory authority %s "
+ "from %s:%d to %s:%d based on certificate.",
+ ds->nickname, ds->address, (int)ds->dir_port,
+ a, cert->dir_port);
+ tor_free(a);
+ ds->addr = cert->addr;
+ ds->dir_port = cert->dir_port;
+ }
+ ds->addr_current_at = cert->cache_info.published_on;
+ }
+
+ if (!from_store)
+ trusted_dir_servers_certs_changed = 1;
+ }
+
+ if (flush)
+ trusted_dirs_flush_certs_to_disk();
+
+ /* call this even if failure_code is <0, since some certs might have
+ * succeeded, but only pass source_dir if there were no failures,
+ * and at least one more authority certificate was added to the store.
+ * This avoids retrying a directory that's serving bad or entirely duplicate
+ * certificates. */
+ if (failure_code == 0 && added_trusted_cert) {
+ networkstatus_note_certs_arrived(source_dir);
+ } else {
+ networkstatus_note_certs_arrived(NULL);
+ }
+
+ return failure_code;
+}
+
+/** Save all v3 key certificates to the cached-certs file. */
+void
+trusted_dirs_flush_certs_to_disk(void)
+{
+ char *filename;
+ smartlist_t *chunks;
+
+ if (!trusted_dir_servers_certs_changed || !trusted_dir_certs)
+ return;
+
+ chunks = smartlist_new();
+ DIGESTMAP_FOREACH(trusted_dir_certs, key, cert_list_t *, cl) {
+ SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
+ {
+ sized_chunk_t *c = tor_malloc(sizeof(sized_chunk_t));
+ c->bytes = cert->cache_info.signed_descriptor_body;
+ c->len = cert->cache_info.signed_descriptor_len;
+ smartlist_add(chunks, c);
+ });
+ } DIGESTMAP_FOREACH_END;
+
+ filename = get_cachedir_fname("cached-certs");
+ if (write_chunks_to_file(filename, chunks, 0, 0)) {
+ log_warn(LD_FS, "Error writing certificates to disk.");
+ }
+ tor_free(filename);
+ SMARTLIST_FOREACH(chunks, sized_chunk_t *, c, tor_free(c));
+ smartlist_free(chunks);
+
+ trusted_dir_servers_certs_changed = 0;
+}
+
+static int
+compare_certs_by_pubdates(const void **_a, const void **_b)
+{
+ const authority_cert_t *cert1 = *_a, *cert2=*_b;
+
+ if (cert1->cache_info.published_on < cert2->cache_info.published_on)
+ return -1;
+ else if (cert1->cache_info.published_on > cert2->cache_info.published_on)
+ return 1;
+ else
+ return 0;
+}
+
+/** Remove all expired v3 authority certificates that have been superseded for
+ * more than 48 hours or, if not expired, that were published more than 7 days
+ * before being superseded. (If the most recent cert was published more than 48
+ * hours ago, then we aren't going to get any consensuses signed with older
+ * keys.) */
+void
+trusted_dirs_remove_old_certs(void)
+{
+ time_t now = time(NULL);
+#define DEAD_CERT_LIFETIME (2*24*60*60)
+#define SUPERSEDED_CERT_LIFETIME (2*24*60*60)
+ if (!trusted_dir_certs)
+ return;
+
+ DIGESTMAP_FOREACH(trusted_dir_certs, key, cert_list_t *, cl) {
+ /* Sort the list from first-published to last-published */
+ smartlist_sort(cl->certs, compare_certs_by_pubdates);
+
+ SMARTLIST_FOREACH_BEGIN(cl->certs, authority_cert_t *, cert) {
+ if (cert_sl_idx == smartlist_len(cl->certs) - 1) {
+ /* This is the most recently published cert. Keep it. */
+ continue;
+ }
+ authority_cert_t *next_cert = smartlist_get(cl->certs, cert_sl_idx+1);
+ const time_t next_cert_published = next_cert->cache_info.published_on;
+ if (next_cert_published > now) {
+ /* All later certs are published in the future. Keep everything
+ * we didn't discard. */
+ break;
+ }
+ int should_remove = 0;
+ if (cert->expires + DEAD_CERT_LIFETIME < now) {
+ /* Certificate has been expired for at least DEAD_CERT_LIFETIME.
+ * Remove it. */
+ should_remove = 1;
+ } else if (next_cert_published + SUPERSEDED_CERT_LIFETIME < now) {
+ /* Certificate has been superseded for OLD_CERT_LIFETIME.
+ * Remove it.
+ */
+ should_remove = 1;
+ }
+ if (should_remove) {
+ SMARTLIST_DEL_CURRENT_KEEPORDER(cl->certs, cert);
+ authority_cert_free(cert);
+ trusted_dir_servers_certs_changed = 1;
+ }
+ } SMARTLIST_FOREACH_END(cert);
+
+ } DIGESTMAP_FOREACH_END;
+#undef DEAD_CERT_LIFETIME
+#undef OLD_CERT_LIFETIME
+
+ trusted_dirs_flush_certs_to_disk();
+}
+
+/** Return the newest v3 authority certificate whose v3 authority identity key
+ * has digest <b>id_digest</b>. Return NULL if no such authority is known,
+ * or it has no certificate. */
+authority_cert_t *
+authority_cert_get_newest_by_id(const char *id_digest)
+{
+ cert_list_t *cl;
+ authority_cert_t *best = NULL;
+ if (!trusted_dir_certs ||
+ !(cl = digestmap_get(trusted_dir_certs, id_digest)))
+ return NULL;
+
+ SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
+ {
+ if (!best || cert->cache_info.published_on > best->cache_info.published_on)
+ best = cert;
+ });
+ return best;
+}
+
+/** Return the newest v3 authority certificate whose directory signing key has
+ * digest <b>sk_digest</b>. Return NULL if no such certificate is known.
+ */
+authority_cert_t *
+authority_cert_get_by_sk_digest(const char *sk_digest)
+{
+ authority_cert_t *c;
+ if (!trusted_dir_certs)
+ return NULL;
+
+ if ((c = get_my_v3_authority_cert()) &&
+ tor_memeq(c->signing_key_digest, sk_digest, DIGEST_LEN))
+ return c;
+ if ((c = get_my_v3_legacy_cert()) &&
+ tor_memeq(c->signing_key_digest, sk_digest, DIGEST_LEN))
+ return c;
+
+ DIGESTMAP_FOREACH(trusted_dir_certs, key, cert_list_t *, cl) {
+ SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
+ {
+ if (tor_memeq(cert->signing_key_digest, sk_digest, DIGEST_LEN))
+ return cert;
+ });
+ } DIGESTMAP_FOREACH_END;
+ return NULL;
+}
+
+/** Return the v3 authority certificate with signing key matching
+ * <b>sk_digest</b>, for the authority with identity digest <b>id_digest</b>.
+ * Return NULL if no such authority is known. */
+authority_cert_t *
+authority_cert_get_by_digests(const char *id_digest,
+ const char *sk_digest)
+{
+ cert_list_t *cl;
+ if (!trusted_dir_certs ||
+ !(cl = digestmap_get(trusted_dir_certs, id_digest)))
+ return NULL;
+ SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
+ if (tor_memeq(cert->signing_key_digest, sk_digest, DIGEST_LEN))
+ return cert; );
+
+ return NULL;
+}
+
+/** Add every known authority_cert_t to <b>certs_out</b>. */
+void
+authority_cert_get_all(smartlist_t *certs_out)
+{
+ tor_assert(certs_out);
+ if (!trusted_dir_certs)
+ return;
+
+ DIGESTMAP_FOREACH(trusted_dir_certs, key, cert_list_t *, cl) {
+ SMARTLIST_FOREACH(cl->certs, authority_cert_t *, c,
+ smartlist_add(certs_out, c));
+ } DIGESTMAP_FOREACH_END;
+}
+
+/** Called when an attempt to download a certificate with the authority with
+ * ID <b>id_digest</b> and, if not NULL, signed with key signing_key_digest
+ * fails with HTTP response code <b>status</b>: remember the failure, so we
+ * don't try again immediately. */
+void
+authority_cert_dl_failed(const char *id_digest,
+ const char *signing_key_digest, int status)
+{
+ cert_list_t *cl;
+ download_status_t *dlstatus = NULL;
+ char id_digest_str[2*DIGEST_LEN+1];
+ char sk_digest_str[2*DIGEST_LEN+1];
+
+ if (!trusted_dir_certs ||
+ !(cl = digestmap_get(trusted_dir_certs, id_digest)))
+ return;
+
+ /*
+ * Are we noting a failed download of the latest cert for the id digest,
+ * or of a download by (id, signing key) digest pair?
+ */
+ if (!signing_key_digest) {
+ /* Just by id digest */
+ download_status_failed(&cl->dl_status_by_id, status);
+ } else {
+ /* Reset by (id, signing key) digest pair
+ *
+ * Look for a download_status_t in the map with this digest
+ */
+ dlstatus = dsmap_get(cl->dl_status_map, signing_key_digest);
+ /* Got one? */
+ if (dlstatus) {
+ download_status_failed(dlstatus, status);
+ } else {
+ /*
+ * Do this rather than hex_str(), since hex_str clobbers
+ * old results and we call twice in the param list.
+ */
+ base16_encode(id_digest_str, sizeof(id_digest_str),
+ id_digest, DIGEST_LEN);
+ base16_encode(sk_digest_str, sizeof(sk_digest_str),
+ signing_key_digest, DIGEST_LEN);
+ log_warn(LD_BUG,
+ "Got failure for cert fetch with (fp,sk) = (%s,%s), with "
+ "status %d, but knew nothing about the download.",
+ id_digest_str, sk_digest_str, status);
+ }
+ }
+}
+
+static const char *BAD_SIGNING_KEYS[] = {
+ "09CD84F751FD6E955E0F8ADB497D5401470D697E", // Expires 2015-01-11 16:26:31
+ "0E7E9C07F0969D0468AD741E172A6109DC289F3C", // Expires 2014-08-12 10:18:26
+ "57B85409891D3FB32137F642FDEDF8B7F8CDFDCD", // Expires 2015-02-11 17:19:09
+ "87326329007AF781F587AF5B594E540B2B6C7630", // Expires 2014-07-17 11:10:09
+ "98CC82342DE8D298CF99D3F1A396475901E0D38E", // Expires 2014-11-10 13:18:56
+ "9904B52336713A5ADCB13E4FB14DC919E0D45571", // Expires 2014-04-20 20:01:01
+ "9DCD8E3F1DD1597E2AD476BBA28A1A89F3095227", // Expires 2015-01-16 03:52:30
+ "A61682F34B9BB9694AC98491FE1ABBFE61923941", // Expires 2014-06-11 09:25:09
+ "B59F6E99C575113650C99F1C425BA7B20A8C071D", // Expires 2014-07-31 13:22:10
+ "D27178388FA75B96D37FA36E0B015227DDDBDA51", // Expires 2014-08-04 04:01:57
+ NULL,
+};
+
+/** Return true iff <b>cert</b> authenticates some atuhority signing key
+ * which, because of the old openssl heartbleed vulnerability, should
+ * never be trusted. */
+int
+authority_cert_is_blacklisted(const authority_cert_t *cert)
+{
+ char hex_digest[HEX_DIGEST_LEN+1];
+ int i;
+ base16_encode(hex_digest, sizeof(hex_digest),
+ cert->signing_key_digest, sizeof(cert->signing_key_digest));
+
+ for (i = 0; BAD_SIGNING_KEYS[i]; ++i) {
+ if (!strcasecmp(hex_digest, BAD_SIGNING_KEYS[i])) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/** Return true iff when we've been getting enough failures when trying to
+ * download the certificate with ID digest <b>id_digest</b> that we're willing
+ * to start bugging the user about it. */
+int
+authority_cert_dl_looks_uncertain(const char *id_digest)
+{
+#define N_AUTH_CERT_DL_FAILURES_TO_BUG_USER 2
+ cert_list_t *cl;
+ int n_failures;
+ if (!trusted_dir_certs ||
+ !(cl = digestmap_get(trusted_dir_certs, id_digest)))
+ return 0;
+
+ n_failures = download_status_get_n_failures(&cl->dl_status_by_id);
+ return n_failures >= N_AUTH_CERT_DL_FAILURES_TO_BUG_USER;
+}
+
+/* Fetch the authority certificates specified in resource.
+ * If we are a bridge client, and node is a configured bridge, fetch from node
+ * using dir_hint as the fingerprint. Otherwise, if rs is not NULL, fetch from
+ * rs. Otherwise, fetch from a random directory mirror. */
+static void
+authority_certs_fetch_resource_impl(const char *resource,
+ const char *dir_hint,
+ const node_t *node,
+ const routerstatus_t *rs)
+{
+ const or_options_t *options = get_options();
+ int get_via_tor = purpose_needs_anonymity(DIR_PURPOSE_FETCH_CERTIFICATE, 0,
+ resource);
+
+ /* Make sure bridge clients never connect to anything but a bridge */
+ if (options->UseBridges) {
+ if (node && !node_is_a_configured_bridge(node)) {
+ /* If we're using bridges, and node is not a bridge, use a 3-hop path. */
+ get_via_tor = 1;
+ } else if (!node) {
+ /* If we're using bridges, and there's no node, use a 3-hop path. */
+ get_via_tor = 1;
+ }
+ }
+
+ const dir_indirection_t indirection = get_via_tor ? DIRIND_ANONYMOUS
+ : DIRIND_ONEHOP;
+
+ directory_request_t *req = NULL;
+ /* If we've just downloaded a consensus from a bridge, re-use that
+ * bridge */
+ if (options->UseBridges && node && node->ri && !get_via_tor) {
+ /* clients always make OR connections to bridges */
+ tor_addr_port_t or_ap;
+ /* we are willing to use a non-preferred address if we need to */
+ fascist_firewall_choose_address_node(node, FIREWALL_OR_CONNECTION, 0,
+ &or_ap);
+
+ req = directory_request_new(DIR_PURPOSE_FETCH_CERTIFICATE);
+ directory_request_set_or_addr_port(req, &or_ap);
+ if (dir_hint)
+ directory_request_set_directory_id_digest(req, dir_hint);
+ } else if (rs) {
+ /* And if we've just downloaded a consensus from a directory, re-use that
+ * directory */
+ req = directory_request_new(DIR_PURPOSE_FETCH_CERTIFICATE);
+ directory_request_set_routerstatus(req, rs);
+ }
+
+ if (req) {
+ /* We've set up a request object -- fill in the other request fields, and
+ * send the request. */
+ directory_request_set_indirection(req, indirection);
+ directory_request_set_resource(req, resource);
+ directory_initiate_request(req);
+ directory_request_free(req);
+ return;
+ }
+
+ /* Otherwise, we want certs from a random fallback or directory
+ * mirror, because they will almost always succeed. */
+ directory_get_from_dirserver(DIR_PURPOSE_FETCH_CERTIFICATE, 0,
+ resource, PDS_RETRY_IF_NO_SERVERS,
+ DL_WANT_ANY_DIRSERVER);
+}
+
+/** Try to download any v3 authority certificates that we may be missing. If
+ * <b>status</b> is provided, try to get all the ones that were used to sign
+ * <b>status</b>. Additionally, try to have a non-expired certificate for
+ * every V3 authority in trusted_dir_servers. Don't fetch certificates we
+ * already have.
+ *
+ * If dir_hint is non-NULL, it's the identity digest for a directory that
+ * we've just successfully retrieved a consensus or certificates from, so try
+ * it first to fetch any missing certificates.
+ **/
+void
+authority_certs_fetch_missing(networkstatus_t *status, time_t now,
+ const char *dir_hint)
+{
+ /*
+ * The pending_id digestmap tracks pending certificate downloads by
+ * identity digest; the pending_cert digestmap tracks pending downloads
+ * by (identity digest, signing key digest) pairs.
+ */
+ digestmap_t *pending_id;
+ fp_pair_map_t *pending_cert;
+ /*
+ * The missing_id_digests smartlist will hold a list of id digests
+ * we want to fetch the newest cert for; the missing_cert_digests
+ * smartlist will hold a list of fp_pair_t with an identity and
+ * signing key digest.
+ */
+ smartlist_t *missing_cert_digests, *missing_id_digests;
+ char *resource = NULL;
+ cert_list_t *cl;
+ const or_options_t *options = get_options();
+ const int keep_unknown = we_want_to_fetch_unknown_auth_certs(options);
+ fp_pair_t *fp_tmp = NULL;
+ char id_digest_str[2*DIGEST_LEN+1];
+ char sk_digest_str[2*DIGEST_LEN+1];
+
+ if (should_delay_dir_fetches(options, NULL))
+ return;
+
+ pending_cert = fp_pair_map_new();
+ pending_id = digestmap_new();
+ missing_cert_digests = smartlist_new();
+ missing_id_digests = smartlist_new();
+
+ /*
+ * First, we get the lists of already pending downloads so we don't
+ * duplicate effort.
+ */
+ list_pending_downloads(pending_id, NULL,
+ DIR_PURPOSE_FETCH_CERTIFICATE, "fp/");
+ list_pending_fpsk_downloads(pending_cert);
+
+ /*
+ * Now, we download any trusted authority certs we don't have by
+ * identity digest only. This gets the latest cert for that authority.
+ */
+ SMARTLIST_FOREACH_BEGIN(router_get_trusted_dir_servers(),
+ dir_server_t *, ds) {
+ int found = 0;
+ if (!(ds->type & V3_DIRINFO))
+ continue;
+ if (smartlist_contains_digest(missing_id_digests,
+ ds->v3_identity_digest))
+ continue;
+ cl = get_cert_list(ds->v3_identity_digest);
+ SMARTLIST_FOREACH_BEGIN(cl->certs, authority_cert_t *, cert) {
+ if (now < cert->expires) {
+ /* It's not expired, and we weren't looking for something to
+ * verify a consensus with. Call it done. */
+ download_status_reset(&(cl->dl_status_by_id));
+ /* No sense trying to download it specifically by signing key hash */
+ download_status_reset_by_sk_in_cl(cl, cert->signing_key_digest);
+ found = 1;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(cert);
+ if (!found &&
+ download_status_is_ready(&(cl->dl_status_by_id), now) &&
+ !digestmap_get(pending_id, ds->v3_identity_digest)) {
+ log_info(LD_DIR,
+ "No current certificate known for authority %s "
+ "(ID digest %s); launching request.",
+ ds->nickname, hex_str(ds->v3_identity_digest, DIGEST_LEN));
+ smartlist_add(missing_id_digests, ds->v3_identity_digest);
+ }
+ } SMARTLIST_FOREACH_END(ds);
+
+ /*
+ * Next, if we have a consensus, scan through it and look for anything
+ * signed with a key from a cert we don't have. Those get downloaded
+ * by (fp,sk) pair, but if we don't know any certs at all for the fp
+ * (identity digest), and it's one of the trusted dir server certs
+ * we started off above or a pending download in pending_id, don't
+ * try to get it yet. Most likely, the one we'll get for that will
+ * have the right signing key too, and we'd just be downloading
+ * redundantly.
+ */
+ if (status) {
+ SMARTLIST_FOREACH_BEGIN(status->voters, networkstatus_voter_info_t *,
+ voter) {
+ if (!smartlist_len(voter->sigs))
+ continue; /* This authority never signed this consensus, so don't
+ * go looking for a cert with key digest 0000000000. */
+ if (!keep_unknown &&
+ !trusteddirserver_get_by_v3_auth_digest(voter->identity_digest))
+ continue; /* We don't want unknown certs, and we don't know this
+ * authority.*/
+
+ /*
+ * If we don't know *any* cert for this authority, and a download by ID
+ * is pending or we added it to missing_id_digests above, skip this
+ * one for now to avoid duplicate downloads.
+ */
+ cl = get_cert_list(voter->identity_digest);
+ if (smartlist_len(cl->certs) == 0) {
+ /* We have no certs at all for this one */
+
+ /* Do we have a download of one pending? */
+ if (digestmap_get(pending_id, voter->identity_digest))
+ continue;
+
+ /*
+ * Are we about to launch a download of one due to the trusted
+ * dir server check above?
+ */
+ if (smartlist_contains_digest(missing_id_digests,
+ voter->identity_digest))
+ continue;
+ }
+
+ SMARTLIST_FOREACH_BEGIN(voter->sigs, document_signature_t *, sig) {
+ authority_cert_t *cert =
+ authority_cert_get_by_digests(voter->identity_digest,
+ sig->signing_key_digest);
+ if (cert) {
+ if (now < cert->expires)
+ download_status_reset_by_sk_in_cl(cl, sig->signing_key_digest);
+ continue;
+ }
+ if (download_status_is_ready_by_sk_in_cl(
+ cl, sig->signing_key_digest, now) &&
+ !fp_pair_map_get_by_digests(pending_cert,
+ voter->identity_digest,
+ sig->signing_key_digest)) {
+ /*
+ * Do this rather than hex_str(), since hex_str clobbers
+ * old results and we call twice in the param list.
+ */
+ base16_encode(id_digest_str, sizeof(id_digest_str),
+ voter->identity_digest, DIGEST_LEN);
+ base16_encode(sk_digest_str, sizeof(sk_digest_str),
+ sig->signing_key_digest, DIGEST_LEN);
+
+ if (voter->nickname) {
+ log_info(LD_DIR,
+ "We're missing a certificate from authority %s "
+ "(ID digest %s) with signing key %s: "
+ "launching request.",
+ voter->nickname, id_digest_str, sk_digest_str);
+ } else {
+ log_info(LD_DIR,
+ "We're missing a certificate from authority ID digest "
+ "%s with signing key %s: launching request.",
+ id_digest_str, sk_digest_str);
+ }
+
+ /* Allocate a new fp_pair_t to append */
+ fp_tmp = tor_malloc(sizeof(*fp_tmp));
+ memcpy(fp_tmp->first, voter->identity_digest, sizeof(fp_tmp->first));
+ memcpy(fp_tmp->second, sig->signing_key_digest,
+ sizeof(fp_tmp->second));
+ smartlist_add(missing_cert_digests, fp_tmp);
+ }
+ } SMARTLIST_FOREACH_END(sig);
+ } SMARTLIST_FOREACH_END(voter);
+ }
+
+ /* Bridge clients look up the node for the dir_hint */
+ const node_t *node = NULL;
+ /* All clients, including bridge clients, look up the routerstatus for the
+ * dir_hint */
+ const routerstatus_t *rs = NULL;
+
+ /* If we still need certificates, try the directory that just successfully
+ * served us a consensus or certificates.
+ * As soon as the directory fails to provide additional certificates, we try
+ * another, randomly selected directory. This avoids continual retries.
+ * (We only ever have one outstanding request per certificate.)
+ */
+ if (dir_hint) {
+ if (options->UseBridges) {
+ /* Bridge clients try the nodelist. If the dir_hint is from an authority,
+ * or something else fetched over tor, we won't find the node here, but
+ * we will find the rs. */
+ node = node_get_by_id(dir_hint);
+ }
+
+ /* All clients try the consensus routerstatus, then the fallback
+ * routerstatus */
+ rs = router_get_consensus_status_by_id(dir_hint);
+ if (!rs) {
+ /* This will also find authorities */
+ const dir_server_t *ds = router_get_fallback_dirserver_by_digest(
+ dir_hint);
+ if (ds) {
+ rs = &ds->fake_status;
+ }
+ }
+
+ if (!node && !rs) {
+ log_warn(LD_BUG, "Directory %s delivered a consensus, but %s"
+ "no routerstatus could be found for it.",
+ options->UseBridges ? "no node and " : "",
+ hex_str(dir_hint, DIGEST_LEN));
+ }
+ }
+
+ /* Do downloads by identity digest */
+ if (smartlist_len(missing_id_digests) > 0) {
+ int need_plus = 0;
+ smartlist_t *fps = smartlist_new();
+
+ smartlist_add_strdup(fps, "fp/");
+
+ SMARTLIST_FOREACH_BEGIN(missing_id_digests, const char *, d) {
+ char *fp = NULL;
+
+ if (digestmap_get(pending_id, d))
+ continue;
+
+ base16_encode(id_digest_str, sizeof(id_digest_str),
+ d, DIGEST_LEN);
+
+ if (need_plus) {
+ tor_asprintf(&fp, "+%s", id_digest_str);
+ } else {
+ /* No need for tor_asprintf() in this case; first one gets no '+' */
+ fp = tor_strdup(id_digest_str);
+ need_plus = 1;
+ }
+
+ smartlist_add(fps, fp);
+ } SMARTLIST_FOREACH_END(d);
+
+ if (smartlist_len(fps) > 1) {
+ resource = smartlist_join_strings(fps, "", 0, NULL);
+ /* node and rs are directories that just gave us a consensus or
+ * certificates */
+ authority_certs_fetch_resource_impl(resource, dir_hint, node, rs);
+ tor_free(resource);
+ }
+ /* else we didn't add any: they were all pending */
+
+ SMARTLIST_FOREACH(fps, char *, cp, tor_free(cp));
+ smartlist_free(fps);
+ }
+
+ /* Do downloads by identity digest/signing key pair */
+ if (smartlist_len(missing_cert_digests) > 0) {
+ int need_plus = 0;
+ smartlist_t *fp_pairs = smartlist_new();
+
+ smartlist_add_strdup(fp_pairs, "fp-sk/");
+
+ SMARTLIST_FOREACH_BEGIN(missing_cert_digests, const fp_pair_t *, d) {
+ char *fp_pair = NULL;
+
+ if (fp_pair_map_get(pending_cert, d))
+ continue;
+
+ /* Construct string encodings of the digests */
+ base16_encode(id_digest_str, sizeof(id_digest_str),
+ d->first, DIGEST_LEN);
+ base16_encode(sk_digest_str, sizeof(sk_digest_str),
+ d->second, DIGEST_LEN);
+
+ /* Now tor_asprintf() */
+ if (need_plus) {
+ tor_asprintf(&fp_pair, "+%s-%s", id_digest_str, sk_digest_str);
+ } else {
+ /* First one in the list doesn't get a '+' */
+ tor_asprintf(&fp_pair, "%s-%s", id_digest_str, sk_digest_str);
+ need_plus = 1;
+ }
+
+ /* Add it to the list of pairs to request */
+ smartlist_add(fp_pairs, fp_pair);
+ } SMARTLIST_FOREACH_END(d);
+
+ if (smartlist_len(fp_pairs) > 1) {
+ resource = smartlist_join_strings(fp_pairs, "", 0, NULL);
+ /* node and rs are directories that just gave us a consensus or
+ * certificates */
+ authority_certs_fetch_resource_impl(resource, dir_hint, node, rs);
+ tor_free(resource);
+ }
+ /* else they were all pending */
+
+ SMARTLIST_FOREACH(fp_pairs, char *, p, tor_free(p));
+ smartlist_free(fp_pairs);
+ }
+
+ smartlist_free(missing_id_digests);
+ SMARTLIST_FOREACH(missing_cert_digests, fp_pair_t *, p, tor_free(p));
+ smartlist_free(missing_cert_digests);
+ digestmap_free(pending_id, NULL);
+ fp_pair_map_free(pending_cert, NULL);
+}
+
+void
+authcert_free_all(void)
+{
+ if (trusted_dir_certs) {
+ digestmap_free(trusted_dir_certs, cert_list_free_void);
+ trusted_dir_certs = NULL;
+ }
+}
+
+/** Free storage held in <b>cert</b>. */
+void
+authority_cert_free_(authority_cert_t *cert)
+{
+ if (!cert)
+ return;
+
+ tor_free(cert->cache_info.signed_descriptor_body);
+ crypto_pk_free(cert->signing_key);
+ crypto_pk_free(cert->identity_key);
+
+ tor_free(cert);
+}
+
+/** For every certificate we are currently downloading by (identity digest,
+ * signing key digest) pair, set result[fp_pair] to (void *1).
+ */
+static void
+list_pending_fpsk_downloads(fp_pair_map_t *result)
+{
+ const char *pfx = "fp-sk/";
+ smartlist_t *tmp;
+ smartlist_t *conns;
+ const char *resource;
+
+ tor_assert(result);
+
+ tmp = smartlist_new();
+ conns = get_connection_array();
+
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
+ if (conn->type == CONN_TYPE_DIR &&
+ conn->purpose == DIR_PURPOSE_FETCH_CERTIFICATE &&
+ !conn->marked_for_close) {
+ resource = TO_DIR_CONN(conn)->requested_resource;
+ if (!strcmpstart(resource, pfx))
+ dir_split_resource_into_fingerprint_pairs(resource + strlen(pfx),
+ tmp);
+ }
+ } SMARTLIST_FOREACH_END(conn);
+
+ SMARTLIST_FOREACH_BEGIN(tmp, fp_pair_t *, fp) {
+ fp_pair_map_set(result, fp, (void*)1);
+ tor_free(fp);
+ } SMARTLIST_FOREACH_END(fp);
+
+ smartlist_free(tmp);
+}
diff --git a/src/feature/nodelist/authcert.h b/src/feature/nodelist/authcert.h
new file mode 100644
index 0000000000..48326d7bd0
--- /dev/null
+++ b/src/feature/nodelist/authcert.h
@@ -0,0 +1,60 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file authcert.h
+ * \brief Header file for authcert.c
+ **/
+
+#ifndef TOR_AUTHCERT_H
+#define TOR_AUTHCERT_H
+
+#include "lib/testsupport/testsupport.h"
+
+int trusted_dirs_reload_certs(void);
+
+/*
+ * Pass one of these as source to trusted_dirs_load_certs_from_string()
+ * to indicate whence string originates; this controls error handling
+ * behavior such as marking downloads as failed.
+ */
+
+#define TRUSTED_DIRS_CERTS_SRC_SELF 0
+#define TRUSTED_DIRS_CERTS_SRC_FROM_STORE 1
+#define TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST 2
+#define TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_SK_DIGEST 3
+#define TRUSTED_DIRS_CERTS_SRC_FROM_VOTE 4
+
+int trusted_dirs_load_certs_from_string(const char *contents, int source,
+ int flush, const char *source_dir);
+void trusted_dirs_remove_old_certs(void);
+void trusted_dirs_flush_certs_to_disk(void);
+authority_cert_t *authority_cert_get_newest_by_id(const char *id_digest);
+authority_cert_t *authority_cert_get_by_sk_digest(const char *sk_digest);
+authority_cert_t *authority_cert_get_by_digests(const char *id_digest,
+ const char *sk_digest);
+void authority_cert_get_all(smartlist_t *certs_out);
+void authority_cert_dl_failed(const char *id_digest,
+ const char *signing_key_digest, int status);
+void authority_certs_fetch_missing(networkstatus_t *status, time_t now,
+ const char *dir_hint);
+int authority_cert_dl_looks_uncertain(const char *id_digest);
+int authority_cert_is_blacklisted(const authority_cert_t *cert);
+
+void authority_cert_free_(authority_cert_t *cert);
+#define authority_cert_free(cert) \
+ FREE_AND_NULL(authority_cert_t, authority_cert_free_, (cert))
+
+MOCK_DECL(smartlist_t *, list_authority_ids_with_downloads, (void));
+MOCK_DECL(download_status_t *, id_only_download_status_for_authority_id,
+ (const char *digest));
+MOCK_DECL(smartlist_t *, list_sk_digests_for_authority_id,
+ (const char *digest));
+MOCK_DECL(download_status_t *, download_status_for_authority_id_and_sk,
+ (const char *id_digest, const char *sk_digest));
+
+void authcert_free_all(void);
+
+#endif
diff --git a/src/feature/nodelist/dirlist.c b/src/feature/nodelist/dirlist.c
new file mode 100644
index 0000000000..8f78df5216
--- /dev/null
+++ b/src/feature/nodelist/dirlist.c
@@ -0,0 +1,421 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file dirlist.c
+ * \brief Code to maintain our lists of directory authorities and
+ * fallback directories.
+ *
+ * For the directory authorities, we have a list containing the public
+ * identity key, and contact points, for each authority. The
+ * authorities receive descriptors from relays, and publish consensuses,
+ * descriptors, and microdescriptors. This list is pre-configured.
+ *
+ * Fallback directories are well-known, stable, but untrusted directory
+ * caches that clients which have not yet bootstrapped can use to get
+ * their first networkstatus consensus, in order to find out where the
+ * Tor network really is. This list is pre-configured in
+ * fallback_dirs.inc. Every authority also serves as a fallback.
+ *
+ * Both fallback directories and directory authorities are are
+ * represented by a dir_server_t.
+ */
+
+#include "core/or/or.h"
+
+#include "app/config/config.h"
+#include "core/or/policies.h"
+#include "feature/control/control.h"
+#include "feature/dircache/directory.h"
+#include "feature/nodelist/dirlist.h"
+#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/nodelist.h"
+#include "feature/nodelist/routerlist.h"
+#include "feature/nodelist/routerset.h"
+#include "feature/relay/router.h"
+#include "lib/net/resolve.h"
+
+#include "feature/dirclient/dir_server_st.h"
+#include "feature/nodelist/node_st.h"
+
+/** Global list of a dir_server_t object for each directory
+ * authority. */
+static smartlist_t *trusted_dir_servers = NULL;
+/** Global list of dir_server_t objects for all directory authorities
+ * and all fallback directory servers. */
+static smartlist_t *fallback_dir_servers = NULL;
+
+/** Return the number of directory authorities whose type matches some bit set
+ * in <b>type</b> */
+int
+get_n_authorities(dirinfo_type_t type)
+{
+ int n = 0;
+ if (!trusted_dir_servers)
+ return 0;
+ SMARTLIST_FOREACH(trusted_dir_servers, dir_server_t *, ds,
+ if (ds->type & type)
+ ++n);
+ return n;
+}
+
+/** Return a smartlist containing a list of dir_server_t * for all
+ * known trusted dirservers. Callers must not modify the list or its
+ * contents.
+ */
+smartlist_t *
+router_get_trusted_dir_servers_mutable(void)
+{
+ if (!trusted_dir_servers)
+ trusted_dir_servers = smartlist_new();
+
+ return trusted_dir_servers;
+}
+
+smartlist_t *
+router_get_fallback_dir_servers_mutable(void)
+{
+ if (!fallback_dir_servers)
+ fallback_dir_servers = smartlist_new();
+
+ return fallback_dir_servers;
+}
+
+const smartlist_t *
+router_get_trusted_dir_servers(void)
+{
+ return router_get_trusted_dir_servers_mutable();
+}
+
+const smartlist_t *
+router_get_fallback_dir_servers(void)
+{
+ return router_get_fallback_dir_servers_mutable();
+}
+
+/** Reset all internal variables used to count failed downloads of network
+ * status objects. */
+void
+router_reset_status_download_failures(void)
+{
+ mark_all_dirservers_up(fallback_dir_servers);
+}
+
+/** Return the dir_server_t for the directory authority whose identity
+ * key hashes to <b>digest</b>, or NULL if no such authority is known.
+ */
+dir_server_t *
+router_get_trusteddirserver_by_digest(const char *digest)
+{
+ if (!trusted_dir_servers)
+ return NULL;
+
+ SMARTLIST_FOREACH(trusted_dir_servers, dir_server_t *, ds,
+ {
+ if (tor_memeq(ds->digest, digest, DIGEST_LEN))
+ return ds;
+ });
+
+ return NULL;
+}
+
+/** Return the dir_server_t for the fallback dirserver whose identity
+ * key hashes to <b>digest</b>, or NULL if no such fallback is in the list of
+ * fallback_dir_servers. (fallback_dir_servers is affected by the FallbackDir
+ * and UseDefaultFallbackDirs torrc options.)
+ * The list of fallback directories includes the list of authorities.
+ */
+dir_server_t *
+router_get_fallback_dirserver_by_digest(const char *digest)
+{
+ if (!fallback_dir_servers)
+ return NULL;
+
+ if (!digest)
+ return NULL;
+
+ SMARTLIST_FOREACH(fallback_dir_servers, dir_server_t *, ds,
+ {
+ if (tor_memeq(ds->digest, digest, DIGEST_LEN))
+ return ds;
+ });
+
+ return NULL;
+}
+
+/** Return 1 if any fallback dirserver's identity key hashes to <b>digest</b>,
+ * or 0 if no such fallback is in the list of fallback_dir_servers.
+ * (fallback_dir_servers is affected by the FallbackDir and
+ * UseDefaultFallbackDirs torrc options.)
+ * The list of fallback directories includes the list of authorities.
+ */
+int
+router_digest_is_fallback_dir(const char *digest)
+{
+ return (router_get_fallback_dirserver_by_digest(digest) != NULL);
+}
+
+/** Return the dir_server_t for the directory authority whose
+ * v3 identity key hashes to <b>digest</b>, or NULL if no such authority
+ * is known.
+ */
+MOCK_IMPL(dir_server_t *,
+trusteddirserver_get_by_v3_auth_digest, (const char *digest))
+{
+ if (!trusted_dir_servers)
+ return NULL;
+
+ SMARTLIST_FOREACH(trusted_dir_servers, dir_server_t *, ds,
+ {
+ if (tor_memeq(ds->v3_identity_digest, digest, DIGEST_LEN) &&
+ (ds->type & V3_DIRINFO))
+ return ds;
+ });
+
+ return NULL;
+}
+
+/** Mark as running every dir_server_t in <b>server_list</b>. */
+void
+mark_all_dirservers_up(smartlist_t *server_list)
+{
+ if (server_list) {
+ SMARTLIST_FOREACH_BEGIN(server_list, dir_server_t *, dir) {
+ routerstatus_t *rs;
+ node_t *node;
+ dir->is_running = 1;
+ node = node_get_mutable_by_id(dir->digest);
+ if (node)
+ node->is_running = 1;
+ rs = router_get_mutable_consensus_status_by_id(dir->digest);
+ if (rs) {
+ rs->last_dir_503_at = 0;
+ control_event_networkstatus_changed_single(rs);
+ }
+ } SMARTLIST_FOREACH_END(dir);
+ }
+ router_dir_info_changed();
+}
+
+/** Return true iff <b>digest</b> is the digest of the identity key of a
+ * trusted directory matching at least one bit of <b>type</b>. If <b>type</b>
+ * is zero (NO_DIRINFO), or ALL_DIRINFO, any authority is okay. */
+int
+router_digest_is_trusted_dir_type(const char *digest, dirinfo_type_t type)
+{
+ if (!trusted_dir_servers)
+ return 0;
+ if (authdir_mode(get_options()) && router_digest_is_me(digest))
+ return 1;
+ SMARTLIST_FOREACH(trusted_dir_servers, dir_server_t *, ent,
+ if (tor_memeq(digest, ent->digest, DIGEST_LEN)) {
+ return (!type) || ((type & ent->type) != 0);
+ });
+ return 0;
+}
+
+/** Create a directory server at <b>address</b>:<b>port</b>, with OR identity
+ * key <b>digest</b> which has DIGEST_LEN bytes. If <b>address</b> is NULL,
+ * add ourself. If <b>is_authority</b>, this is a directory authority. Return
+ * the new directory server entry on success or NULL on failure. */
+static dir_server_t *
+dir_server_new(int is_authority,
+ const char *nickname,
+ const tor_addr_t *addr,
+ const char *hostname,
+ uint16_t dir_port, uint16_t or_port,
+ const tor_addr_port_t *addrport_ipv6,
+ const char *digest, const char *v3_auth_digest,
+ dirinfo_type_t type,
+ double weight)
+{
+ dir_server_t *ent;
+ uint32_t a;
+ char *hostname_ = NULL;
+
+ tor_assert(digest);
+
+ if (weight < 0)
+ return NULL;
+
+ if (tor_addr_family(addr) == AF_INET)
+ a = tor_addr_to_ipv4h(addr);
+ else
+ return NULL;
+
+ if (!hostname)
+ hostname_ = tor_addr_to_str_dup(addr);
+ else
+ hostname_ = tor_strdup(hostname);
+
+ ent = tor_malloc_zero(sizeof(dir_server_t));
+ ent->nickname = nickname ? tor_strdup(nickname) : NULL;
+ ent->address = hostname_;
+ ent->addr = a;
+ ent->dir_port = dir_port;
+ ent->or_port = or_port;
+ ent->is_running = 1;
+ ent->is_authority = is_authority;
+ ent->type = type;
+ ent->weight = weight;
+ if (addrport_ipv6) {
+ if (tor_addr_family(&addrport_ipv6->addr) != AF_INET6) {
+ log_warn(LD_BUG, "Hey, I got a non-ipv6 addr as addrport_ipv6.");
+ tor_addr_make_unspec(&ent->ipv6_addr);
+ } else {
+ tor_addr_copy(&ent->ipv6_addr, &addrport_ipv6->addr);
+ ent->ipv6_orport = addrport_ipv6->port;
+ }
+ } else {
+ tor_addr_make_unspec(&ent->ipv6_addr);
+ }
+
+ memcpy(ent->digest, digest, DIGEST_LEN);
+ if (v3_auth_digest && (type & V3_DIRINFO))
+ memcpy(ent->v3_identity_digest, v3_auth_digest, DIGEST_LEN);
+
+ if (nickname)
+ tor_asprintf(&ent->description, "directory server \"%s\" at %s:%d",
+ nickname, hostname_, (int)dir_port);
+ else
+ tor_asprintf(&ent->description, "directory server at %s:%d",
+ hostname_, (int)dir_port);
+
+ ent->fake_status.addr = ent->addr;
+ tor_addr_copy(&ent->fake_status.ipv6_addr, &ent->ipv6_addr);
+ memcpy(ent->fake_status.identity_digest, digest, DIGEST_LEN);
+ if (nickname)
+ strlcpy(ent->fake_status.nickname, nickname,
+ sizeof(ent->fake_status.nickname));
+ else
+ ent->fake_status.nickname[0] = '\0';
+ ent->fake_status.dir_port = ent->dir_port;
+ ent->fake_status.or_port = ent->or_port;
+ ent->fake_status.ipv6_orport = ent->ipv6_orport;
+
+ return ent;
+}
+
+/** Create an authoritative directory server at
+ * <b>address</b>:<b>port</b>, with identity key <b>digest</b>. If
+ * <b>address</b> is NULL, add ourself. Return the new trusted directory
+ * server entry on success or NULL if we couldn't add it. */
+dir_server_t *
+trusted_dir_server_new(const char *nickname, const char *address,
+ uint16_t dir_port, uint16_t or_port,
+ const tor_addr_port_t *ipv6_addrport,
+ const char *digest, const char *v3_auth_digest,
+ dirinfo_type_t type, double weight)
+{
+ uint32_t a;
+ tor_addr_t addr;
+ char *hostname=NULL;
+ dir_server_t *result;
+
+ if (!address) { /* The address is us; we should guess. */
+ if (resolve_my_address(LOG_WARN, get_options(),
+ &a, NULL, &hostname) < 0) {
+ log_warn(LD_CONFIG,
+ "Couldn't find a suitable address when adding ourself as a "
+ "trusted directory server.");
+ return NULL;
+ }
+ if (!hostname)
+ hostname = tor_dup_ip(a);
+ } else {
+ if (tor_lookup_hostname(address, &a)) {
+ log_warn(LD_CONFIG,
+ "Unable to lookup address for directory server at '%s'",
+ address);
+ return NULL;
+ }
+ hostname = tor_strdup(address);
+ }
+ tor_addr_from_ipv4h(&addr, a);
+
+ result = dir_server_new(1, nickname, &addr, hostname,
+ dir_port, or_port,
+ ipv6_addrport,
+ digest,
+ v3_auth_digest, type, weight);
+ tor_free(hostname);
+ return result;
+}
+
+/** Return a new dir_server_t for a fallback directory server at
+ * <b>addr</b>:<b>or_port</b>/<b>dir_port</b>, with identity key digest
+ * <b>id_digest</b> */
+dir_server_t *
+fallback_dir_server_new(const tor_addr_t *addr,
+ uint16_t dir_port, uint16_t or_port,
+ const tor_addr_port_t *addrport_ipv6,
+ const char *id_digest, double weight)
+{
+ return dir_server_new(0, NULL, addr, NULL, dir_port, or_port,
+ addrport_ipv6,
+ id_digest,
+ NULL, ALL_DIRINFO, weight);
+}
+
+/** Add a directory server to the global list(s). */
+void
+dir_server_add(dir_server_t *ent)
+{
+ if (!trusted_dir_servers)
+ trusted_dir_servers = smartlist_new();
+ if (!fallback_dir_servers)
+ fallback_dir_servers = smartlist_new();
+
+ if (ent->is_authority)
+ smartlist_add(trusted_dir_servers, ent);
+
+ smartlist_add(fallback_dir_servers, ent);
+ router_dir_info_changed();
+}
+
+#define dir_server_free(val) \
+ FREE_AND_NULL(dir_server_t, dir_server_free_, (val))
+
+/** Free storage held in <b>ds</b>. */
+static void
+dir_server_free_(dir_server_t *ds)
+{
+ if (!ds)
+ return;
+
+ tor_free(ds->nickname);
+ tor_free(ds->description);
+ tor_free(ds->address);
+ tor_free(ds);
+}
+
+/** Remove all members from the list of dir servers. */
+void
+clear_dir_servers(void)
+{
+ if (fallback_dir_servers) {
+ SMARTLIST_FOREACH(fallback_dir_servers, dir_server_t *, ent,
+ dir_server_free(ent));
+ smartlist_clear(fallback_dir_servers);
+ } else {
+ fallback_dir_servers = smartlist_new();
+ }
+ if (trusted_dir_servers) {
+ smartlist_clear(trusted_dir_servers);
+ } else {
+ trusted_dir_servers = smartlist_new();
+ }
+ router_dir_info_changed();
+}
+
+void
+dirlist_free_all(void)
+{
+ clear_dir_servers();
+ smartlist_free(trusted_dir_servers);
+ smartlist_free(fallback_dir_servers);
+ trusted_dir_servers = fallback_dir_servers = NULL;
+}
diff --git a/src/feature/nodelist/dirlist.h b/src/feature/nodelist/dirlist.h
new file mode 100644
index 0000000000..6baa5686c5
--- /dev/null
+++ b/src/feature/nodelist/dirlist.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file dirlist.h
+ * \brief Header file for dirlist.c
+ **/
+
+#ifndef TOR_DIRLIST_H
+#define TOR_DIRLIST_H
+
+int get_n_authorities(dirinfo_type_t type);
+const smartlist_t *router_get_trusted_dir_servers(void);
+const smartlist_t *router_get_fallback_dir_servers(void);
+smartlist_t *router_get_trusted_dir_servers_mutable(void);
+smartlist_t *router_get_fallback_dir_servers_mutable(void);
+void mark_all_dirservers_up(smartlist_t *server_list);
+
+dir_server_t *router_get_trusteddirserver_by_digest(const char *d);
+dir_server_t *router_get_fallback_dirserver_by_digest(
+ const char *digest);
+int router_digest_is_fallback_dir(const char *digest);
+MOCK_DECL(dir_server_t *, trusteddirserver_get_by_v3_auth_digest,
+ (const char *d));
+
+int router_digest_is_trusted_dir_type(const char *digest,
+ dirinfo_type_t type);
+#define router_digest_is_trusted_dir(d) \
+ router_digest_is_trusted_dir_type((d), NO_DIRINFO)
+
+dir_server_t *trusted_dir_server_new(const char *nickname, const char *address,
+ uint16_t dir_port, uint16_t or_port,
+ const tor_addr_port_t *addrport_ipv6,
+ const char *digest, const char *v3_auth_digest,
+ dirinfo_type_t type, double weight);
+dir_server_t *fallback_dir_server_new(const tor_addr_t *addr,
+ uint16_t dir_port, uint16_t or_port,
+ const tor_addr_port_t *addrport_ipv6,
+ const char *id_digest, double weight);
+void dir_server_add(dir_server_t *ent);
+
+void clear_dir_servers(void);
+void dirlist_free_all(void);
+
+#endif
diff --git a/src/feature/nodelist/fmt_routerstatus.c b/src/feature/nodelist/fmt_routerstatus.c
new file mode 100644
index 0000000000..e70aeb2950
--- /dev/null
+++ b/src/feature/nodelist/fmt_routerstatus.c
@@ -0,0 +1,253 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file fmt_routerstatus.h
+ * \brief Format routerstatus entries for controller, vote, or consensus.
+ *
+ * (Because controllers consume this format, we can't make this
+ * code dirauth-only.)
+ **/
+
+#include "core/or/or.h"
+#include "feature/nodelist/fmt_routerstatus.h"
+
+/* #include "lib/container/buffers.h" */
+/* #include "app/config/config.h" */
+/* #include "app/config/confparse.h" */
+/* #include "core/or/channel.h" */
+/* #include "core/or/channeltls.h" */
+/* #include "core/or/command.h" */
+/* #include "core/mainloop/connection.h" */
+/* #include "core/or/connection_or.h" */
+/* #include "feature/dircache/conscache.h" */
+/* #include "feature/dircache/consdiffmgr.h" */
+/* #include "feature/control/control.h" */
+/* #include "feature/dircache/directory.h" */
+/* #include "feature/dircache/dirserv.h" */
+/* #include "feature/hibernate/hibernate.h" */
+/* #include "feature/dirauth/keypin.h" */
+/* #include "core/mainloop/mainloop.h" */
+/* #include "feature/nodelist/microdesc.h" */
+/* #include "feature/nodelist/networkstatus.h" */
+/* #include "feature/nodelist/nodelist.h" */
+#include "core/or/policies.h"
+/* #include "core/or/protover.h" */
+/* #include "feature/stats/rephist.h" */
+/* #include "feature/relay/router.h" */
+/* #include "feature/nodelist/dirlist.h" */
+#include "feature/nodelist/routerlist.h"
+
+/* #include "feature/nodelist/routerparse.h" */
+/* #include "feature/nodelist/routerset.h" */
+/* #include "feature/nodelist/torcert.h" */
+/* #include "feature/dircommon/voting_schedule.h" */
+
+#include "feature/dirauth/dirvote.h"
+
+/* #include "feature/dircache/cached_dir_st.h" */
+/* #include "feature/dircommon/dir_connection_st.h" */
+/* #include "feature/nodelist/extrainfo_st.h" */
+/* #include "feature/nodelist/microdesc_st.h" */
+/* #include "feature/nodelist/node_st.h" */
+#include "feature/nodelist/routerinfo_st.h"
+/* #include "feature/nodelist/routerlist_st.h" */
+/* #include "core/or/tor_version_st.h" */
+#include "feature/nodelist/vote_routerstatus_st.h"
+
+/* #include "lib/compress/compress.h" */
+/* #include "lib/container/order.h" */
+#include "lib/crypt_ops/crypto_format.h"
+/* #include "lib/encoding/confline.h" */
+
+/* #include "lib/encoding/keyval.h" */
+
+/** Helper: write the router-status information in <b>rs</b> into a newly
+ * allocated character buffer. Use the same format as in network-status
+ * documents. If <b>version</b> is non-NULL, add a "v" line for the platform.
+ *
+ * consensus_method is the current consensus method when format is
+ * NS_V3_CONSENSUS or NS_V3_CONSENSUS_MICRODESC. It is ignored for other
+ * formats: pass ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD.
+ *
+ * Return 0 on success, -1 on failure.
+ *
+ * The format argument has one of the following values:
+ * NS_V2 - Output an entry suitable for a V2 NS opinion document
+ * NS_V3_CONSENSUS - Output the first portion of a V3 NS consensus entry
+ * for consensus_method.
+ * NS_V3_CONSENSUS_MICRODESC - Output the first portion of a V3 microdesc
+ * consensus entry for consensus_method.
+ * NS_V3_VOTE - Output a complete V3 NS vote. If <b>vrs</b> is present,
+ * it contains additional information for the vote.
+ * NS_CONTROL_PORT - Output a NS document for the control port.
+ */
+char *
+routerstatus_format_entry(const routerstatus_t *rs, const char *version,
+ const char *protocols,
+ routerstatus_format_type_t format,
+ int consensus_method,
+ const vote_routerstatus_t *vrs)
+{
+ char *summary;
+ char *result = NULL;
+
+ char published[ISO_TIME_LEN+1];
+ char identity64[BASE64_DIGEST_LEN+1];
+ char digest64[BASE64_DIGEST_LEN+1];
+ smartlist_t *chunks = smartlist_new();
+
+ format_iso_time(published, rs->published_on);
+ digest_to_base64(identity64, rs->identity_digest);
+ digest_to_base64(digest64, rs->descriptor_digest);
+
+ smartlist_add_asprintf(chunks,
+ "r %s %s %s%s%s %s %d %d\n",
+ rs->nickname,
+ identity64,
+ (format==NS_V3_CONSENSUS_MICRODESC)?"":digest64,
+ (format==NS_V3_CONSENSUS_MICRODESC)?"":" ",
+ published,
+ fmt_addr32(rs->addr),
+ (int)rs->or_port,
+ (int)rs->dir_port);
+
+ /* TODO: Maybe we want to pass in what we need to build the rest of
+ * this here, instead of in the caller. Then we could use the
+ * networkstatus_type_t values, with an additional control port value
+ * added -MP */
+
+ /* V3 microdesc consensuses only have "a" lines in later consensus methods
+ */
+ if (format == NS_V3_CONSENSUS_MICRODESC &&
+ consensus_method < MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS)
+ goto done;
+
+ /* Possible "a" line. At most one for now. */
+ if (!tor_addr_is_null(&rs->ipv6_addr)) {
+ smartlist_add_asprintf(chunks, "a %s\n",
+ fmt_addrport(&rs->ipv6_addr, rs->ipv6_orport));
+ }
+
+ if (format == NS_V3_CONSENSUS || format == NS_V3_CONSENSUS_MICRODESC)
+ goto done;
+
+ smartlist_add_asprintf(chunks,
+ "s%s%s%s%s%s%s%s%s%s%s\n",
+ /* These must stay in alphabetical order. */
+ rs->is_authority?" Authority":"",
+ rs->is_bad_exit?" BadExit":"",
+ rs->is_exit?" Exit":"",
+ rs->is_fast?" Fast":"",
+ rs->is_possible_guard?" Guard":"",
+ rs->is_hs_dir?" HSDir":"",
+ rs->is_flagged_running?" Running":"",
+ rs->is_stable?" Stable":"",
+ rs->is_v2_dir?" V2Dir":"",
+ rs->is_valid?" Valid":"");
+
+ /* length of "opt v \n" */
+#define V_LINE_OVERHEAD 7
+ if (version && strlen(version) < MAX_V_LINE_LEN - V_LINE_OVERHEAD) {
+ smartlist_add_asprintf(chunks, "v %s\n", version);
+ }
+ if (protocols) {
+ smartlist_add_asprintf(chunks, "pr %s\n", protocols);
+ }
+
+ if (format != NS_V2) {
+ const routerinfo_t* desc = router_get_by_id_digest(rs->identity_digest);
+ uint32_t bw_kb;
+
+ if (format != NS_CONTROL_PORT) {
+ /* Blow up more or less nicely if we didn't get anything or not the
+ * thing we expected.
+ */
+ if (!desc) {
+ char id[HEX_DIGEST_LEN+1];
+ char dd[HEX_DIGEST_LEN+1];
+
+ base16_encode(id, sizeof(id), rs->identity_digest, DIGEST_LEN);
+ base16_encode(dd, sizeof(dd), rs->descriptor_digest, DIGEST_LEN);
+ log_warn(LD_BUG, "Cannot get any descriptor for %s "
+ "(wanted descriptor %s).",
+ id, dd);
+ goto err;
+ }
+
+ /* This assert could fire for the control port, because
+ * it can request NS documents before all descriptors
+ * have been fetched. Therefore, we only do this test when
+ * format != NS_CONTROL_PORT. */
+ if (tor_memneq(desc->cache_info.signed_descriptor_digest,
+ rs->descriptor_digest,
+ DIGEST_LEN)) {
+ char rl_d[HEX_DIGEST_LEN+1];
+ char rs_d[HEX_DIGEST_LEN+1];
+ char id[HEX_DIGEST_LEN+1];
+
+ base16_encode(rl_d, sizeof(rl_d),
+ desc->cache_info.signed_descriptor_digest, DIGEST_LEN);
+ base16_encode(rs_d, sizeof(rs_d), rs->descriptor_digest, DIGEST_LEN);
+ base16_encode(id, sizeof(id), rs->identity_digest, DIGEST_LEN);
+ log_err(LD_BUG, "descriptor digest in routerlist does not match "
+ "the one in routerstatus: %s vs %s "
+ "(router %s)\n",
+ rl_d, rs_d, id);
+
+ tor_assert(tor_memeq(desc->cache_info.signed_descriptor_digest,
+ rs->descriptor_digest,
+ DIGEST_LEN));
+ }
+ }
+
+ if (format == NS_CONTROL_PORT && rs->has_bandwidth) {
+ bw_kb = rs->bandwidth_kb;
+ } else {
+ tor_assert(desc);
+ bw_kb = router_get_advertised_bandwidth_capped(desc) / 1000;
+ }
+ smartlist_add_asprintf(chunks,
+ "w Bandwidth=%d", bw_kb);
+
+ if (format == NS_V3_VOTE && vrs && vrs->has_measured_bw) {
+ smartlist_add_asprintf(chunks,
+ " Measured=%d", vrs->measured_bw_kb);
+ }
+ /* Write down guardfraction information if we have it. */
+ if (format == NS_V3_VOTE && vrs && vrs->status.has_guardfraction) {
+ smartlist_add_asprintf(chunks,
+ " GuardFraction=%d",
+ vrs->status.guardfraction_percentage);
+ }
+
+ smartlist_add_strdup(chunks, "\n");
+
+ if (desc) {
+ summary = policy_summarize(desc->exit_policy, AF_INET);
+ smartlist_add_asprintf(chunks, "p %s\n", summary);
+ tor_free(summary);
+ }
+
+ if (format == NS_V3_VOTE && vrs) {
+ if (tor_mem_is_zero((char*)vrs->ed25519_id, ED25519_PUBKEY_LEN)) {
+ smartlist_add_strdup(chunks, "id ed25519 none\n");
+ } else {
+ char ed_b64[BASE64_DIGEST256_LEN+1];
+ digest256_to_base64(ed_b64, (const char*)vrs->ed25519_id);
+ smartlist_add_asprintf(chunks, "id ed25519 %s\n", ed_b64);
+ }
+ }
+ }
+
+ done:
+ result = smartlist_join_strings(chunks, "", 0, NULL);
+
+ err:
+ SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
+ smartlist_free(chunks);
+
+ return result;
+}
diff --git a/src/feature/nodelist/fmt_routerstatus.h b/src/feature/nodelist/fmt_routerstatus.h
new file mode 100644
index 0000000000..1a6630d266
--- /dev/null
+++ b/src/feature/nodelist/fmt_routerstatus.h
@@ -0,0 +1,41 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file fmt_routerstatus.h
+ * \brief Header file for fmt_routerstatus.c.
+ **/
+
+#ifndef TOR_FMT_ROUTERSTATUS_H
+#define TOR_FMT_ROUTERSTATUS_H
+
+/** An enum to describe what format we're generating a routerstatus line in.
+ */
+typedef enum {
+ /** For use in a v2 opinion */
+ NS_V2,
+ /** For use in a consensus networkstatus document (ns flavor) */
+ NS_V3_CONSENSUS,
+ /** For use in a vote networkstatus document */
+ NS_V3_VOTE,
+ /** For passing to the controlport in response to a GETINFO request */
+ NS_CONTROL_PORT,
+ /** For use in a consensus networkstatus document (microdesc flavor) */
+ NS_V3_CONSENSUS_MICRODESC
+} routerstatus_format_type_t;
+
+/** Maximum allowable length of a version line in a networkstatus. */
+#define MAX_V_LINE_LEN 128
+
+char *routerstatus_format_entry(
+ const routerstatus_t *rs,
+ const char *version,
+ const char *protocols,
+ routerstatus_format_type_t format,
+ int consensus_method,
+ const vote_routerstatus_t *vrs);
+
+#endif /* !defined(TOR_FMT_ROUTERSTATUS_H) */
diff --git a/src/feature/nodelist/microdesc.c b/src/feature/nodelist/microdesc.c
index aa5e6b1dbe..203d079b11 100644
--- a/src/feature/nodelist/microdesc.c
+++ b/src/feature/nodelist/microdesc.c
@@ -22,6 +22,7 @@
#include "feature/nodelist/nodelist.h"
#include "core/or/policies.h"
#include "feature/relay/router.h"
+#include "feature/nodelist/dirlist.h"
#include "feature/nodelist/routerlist.h"
#include "feature/nodelist/routerparse.h"
diff --git a/src/feature/nodelist/networkstatus.c b/src/feature/nodelist/networkstatus.c
index 81da9756aa..c64c5176e5 100644
--- a/src/feature/nodelist/networkstatus.c
+++ b/src/feature/nodelist/networkstatus.c
@@ -53,16 +53,20 @@
#include "lib/crypt_ops/crypto_util.h"
#include "feature/dircache/directory.h"
#include "feature/dircache/dirserv.h"
+#include "feature/dirauth/reachability.h"
#include "core/or/dos.h"
#include "feature/client/entrynodes.h"
#include "feature/hibernate/hibernate.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
#include "core/or/protover.h"
#include "core/or/relay.h"
#include "feature/relay/router.h"
+#include "feature/nodelist/authcert.h"
+#include "feature/nodelist/dirlist.h"
+#include "feature/nodelist/node_select.h"
#include "feature/nodelist/routerlist.h"
#include "feature/nodelist/routerparse.h"
#include "core/or/scheduler.h"
@@ -70,10 +74,12 @@
#include "feature/nodelist/torcert.h"
#include "core/or/channelpadding.h"
#include "feature/dircommon/voting_schedule.h"
+#include "feature/nodelist/fmt_routerstatus.h"
#include "feature/dirauth/dirvote.h"
#include "feature/dirauth/mode.h"
#include "feature/dirauth/shared_random.h"
+#include "feature/dirauth/voteflags.h"
#include "feature/nodelist/authority_cert_st.h"
#include "feature/dircommon/dir_connection_st.h"
diff --git a/src/feature/nodelist/node_select.c b/src/feature/nodelist/node_select.c
new file mode 100644
index 0000000000..a9f045d456
--- /dev/null
+++ b/src/feature/nodelist/node_select.c
@@ -0,0 +1,1108 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file node_select.c
+ * \brief Code to choose nodes randomly based on restrictions and
+ * weighted probabilities.
+ **/
+
+#define NODE_SELECT_PRIVATE
+#include "core/or/or.h"
+
+#include "app/config/config.h"
+#include "core/mainloop/connection.h"
+#include "core/or/policies.h"
+#include "core/or/reasons.h"
+#include "feature/client/entrynodes.h"
+#include "feature/dircache/directory.h"
+#include "feature/nodelist/dirlist.h"
+#include "feature/nodelist/microdesc.h"
+#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/node_select.h"
+#include "feature/nodelist/nodelist.h"
+#include "feature/nodelist/routerlist.h"
+#include "feature/nodelist/routerset.h"
+#include "feature/relay/router.h"
+#include "lib/crypt_ops/crypto_rand.h"
+#include "lib/math/fp.h"
+
+#include "feature/dirclient/dir_server_st.h"
+#include "feature/nodelist/networkstatus_st.h"
+#include "feature/nodelist/node_st.h"
+#include "feature/nodelist/routerinfo_st.h"
+#include "feature/nodelist/routerstatus_st.h"
+
+static int compute_weighted_bandwidths(const smartlist_t *sl,
+ bandwidth_weight_rule_t rule,
+ double **bandwidths_out,
+ double *total_bandwidth_out);
+static const routerstatus_t *router_pick_trusteddirserver_impl(
+ const smartlist_t *sourcelist, dirinfo_type_t auth,
+ int flags, int *n_busy_out);
+static const routerstatus_t *router_pick_dirserver_generic(
+ smartlist_t *sourcelist,
+ dirinfo_type_t type, int flags);
+
+/** Try to find a running dirserver that supports operations of <b>type</b>.
+ *
+ * If there are no running dirservers in our routerlist and the
+ * <b>PDS_RETRY_IF_NO_SERVERS</b> flag is set, set all the fallback ones
+ * (including authorities) as running again, and pick one.
+ *
+ * If the <b>PDS_IGNORE_FASCISTFIREWALL</b> flag is set, then include
+ * dirservers that we can't reach.
+ *
+ * If the <b>PDS_ALLOW_SELF</b> flag is not set, then don't include ourself
+ * (if we're a dirserver).
+ *
+ * Don't pick a fallback directory mirror if any non-fallback is viable;
+ * (the fallback directory mirrors include the authorities)
+ * try to avoid using servers that have returned 503 recently.
+ */
+const routerstatus_t *
+router_pick_directory_server(dirinfo_type_t type, int flags)
+{
+ int busy = 0;
+ const routerstatus_t *choice;
+
+ choice = router_pick_directory_server_impl(type, flags, &busy);
+ if (choice || !(flags & PDS_RETRY_IF_NO_SERVERS))
+ return choice;
+
+ if (busy) {
+ /* If the reason that we got no server is that servers are "busy",
+ * we must be excluding good servers because we already have serverdesc
+ * fetches with them. Do not mark down servers up because of this. */
+ tor_assert((flags & (PDS_NO_EXISTING_SERVERDESC_FETCH|
+ PDS_NO_EXISTING_MICRODESC_FETCH)));
+ return NULL;
+ }
+
+ log_info(LD_DIR,
+ "No reachable router entries for dirservers. "
+ "Trying them all again.");
+ /* mark all fallback directory mirrors as up again */
+ router_reset_status_download_failures();
+ /* try again */
+ choice = router_pick_directory_server_impl(type, flags, NULL);
+ return choice;
+}
+
+/** Try to find a running fallback directory. Flags are as for
+ * router_pick_directory_server.
+ */
+const routerstatus_t *
+router_pick_dirserver_generic(smartlist_t *sourcelist,
+ dirinfo_type_t type, int flags)
+{
+ const routerstatus_t *choice;
+ int busy = 0;
+
+ if (smartlist_len(sourcelist) == 1) {
+ /* If there's only one choice, then we should disable the logic that
+ * would otherwise prevent us from choosing ourself. */
+ flags |= PDS_ALLOW_SELF;
+ }
+
+ choice = router_pick_trusteddirserver_impl(sourcelist, type, flags, &busy);
+ if (choice || !(flags & PDS_RETRY_IF_NO_SERVERS))
+ return choice;
+ if (busy) {
+ /* If the reason that we got no server is that servers are "busy",
+ * we must be excluding good servers because we already have serverdesc
+ * fetches with them. Do not mark down servers up because of this. */
+ tor_assert((flags & (PDS_NO_EXISTING_SERVERDESC_FETCH|
+ PDS_NO_EXISTING_MICRODESC_FETCH)));
+ return NULL;
+ }
+
+ log_info(LD_DIR,
+ "No dirservers are reachable. Trying them all again.");
+ mark_all_dirservers_up(sourcelist);
+ return router_pick_trusteddirserver_impl(sourcelist, type, flags, NULL);
+}
+
+/* Common retry code for router_pick_directory_server_impl and
+ * router_pick_trusteddirserver_impl. Retry with the non-preferred IP version.
+ * Must be called before RETRY_WITHOUT_EXCLUDE().
+ *
+ * If we got no result, and we are applying IP preferences, and we are a
+ * client that could use an alternate IP version, try again with the
+ * opposite preferences. */
+#define RETRY_ALTERNATE_IP_VERSION(retry_label) \
+ STMT_BEGIN \
+ if (result == NULL && try_ip_pref && options->ClientUseIPv4 \
+ && fascist_firewall_use_ipv6(options) && !server_mode(options) \
+ && !n_busy) { \
+ n_excluded = 0; \
+ n_busy = 0; \
+ try_ip_pref = 0; \
+ goto retry_label; \
+ } \
+ STMT_END \
+
+/* Common retry code for router_pick_directory_server_impl and
+ * router_pick_trusteddirserver_impl. Retry without excluding nodes, but with
+ * the preferred IP version. Must be called after RETRY_ALTERNATE_IP_VERSION().
+ *
+ * If we got no result, and we are excluding nodes, and StrictNodes is
+ * not set, try again without excluding nodes. */
+#define RETRY_WITHOUT_EXCLUDE(retry_label) \
+ STMT_BEGIN \
+ if (result == NULL && try_excluding && !options->StrictNodes \
+ && n_excluded && !n_busy) { \
+ try_excluding = 0; \
+ n_excluded = 0; \
+ n_busy = 0; \
+ try_ip_pref = 1; \
+ goto retry_label; \
+ } \
+ STMT_END
+
+/* Common code used in the loop within router_pick_directory_server_impl and
+ * router_pick_trusteddirserver_impl.
+ *
+ * Check if the given <b>identity</b> supports extrainfo. If not, skip further
+ * checks.
+ */
+#define SKIP_MISSING_TRUSTED_EXTRAINFO(type, identity) \
+ STMT_BEGIN \
+ int is_trusted_extrainfo = router_digest_is_trusted_dir_type( \
+ (identity), EXTRAINFO_DIRINFO); \
+ if (((type) & EXTRAINFO_DIRINFO) && \
+ !router_supports_extrainfo((identity), is_trusted_extrainfo)) \
+ continue; \
+ STMT_END
+
+#ifndef LOG_FALSE_POSITIVES_DURING_BOOTSTRAP
+#define LOG_FALSE_POSITIVES_DURING_BOOTSTRAP 0
+#endif
+
+/* Log a message if rs is not found or not a preferred address */
+static void
+router_picked_poor_directory_log(const routerstatus_t *rs)
+{
+ const networkstatus_t *usable_consensus;
+ usable_consensus = networkstatus_get_reasonably_live_consensus(time(NULL),
+ usable_consensus_flavor());
+
+#if !LOG_FALSE_POSITIVES_DURING_BOOTSTRAP
+ /* Don't log early in the bootstrap process, it's normal to pick from a
+ * small pool of nodes. Of course, this won't help if we're trying to
+ * diagnose bootstrap issues. */
+ if (!smartlist_len(nodelist_get_list()) || !usable_consensus
+ || !router_have_minimum_dir_info()) {
+ return;
+ }
+#endif /* !LOG_FALSE_POSITIVES_DURING_BOOTSTRAP */
+
+ /* We couldn't find a node, or the one we have doesn't fit our preferences.
+ * Sometimes this is normal, sometimes it can be a reachability issue. */
+ if (!rs) {
+ /* This happens a lot, so it's at debug level */
+ log_debug(LD_DIR, "Wanted to make an outgoing directory connection, but "
+ "we couldn't find a directory that fit our criteria. "
+ "Perhaps we will succeed next time with less strict criteria.");
+ } else if (!fascist_firewall_allows_rs(rs, FIREWALL_OR_CONNECTION, 1)
+ && !fascist_firewall_allows_rs(rs, FIREWALL_DIR_CONNECTION, 1)
+ ) {
+ /* This is rare, and might be interesting to users trying to diagnose
+ * connection issues on dual-stack machines. */
+ log_info(LD_DIR, "Selected a directory %s with non-preferred OR and Dir "
+ "addresses for launching an outgoing connection: "
+ "IPv4 %s OR %d Dir %d IPv6 %s OR %d Dir %d",
+ routerstatus_describe(rs),
+ fmt_addr32(rs->addr), rs->or_port,
+ rs->dir_port, fmt_addr(&rs->ipv6_addr),
+ rs->ipv6_orport, rs->dir_port);
+ }
+}
+
+#undef LOG_FALSE_POSITIVES_DURING_BOOTSTRAP
+
+/* Check if we already have a directory fetch from ap, for serverdesc
+ * (including extrainfo) or microdesc documents.
+ * If so, return 1, if not, return 0.
+ * Also returns 0 if addr is NULL, tor_addr_is_null(addr), or dir_port is 0.
+ */
+STATIC int
+router_is_already_dir_fetching(const tor_addr_port_t *ap, int serverdesc,
+ int microdesc)
+{
+ if (!ap || tor_addr_is_null(&ap->addr) || !ap->port) {
+ return 0;
+ }
+
+ /* XX/teor - we're not checking tunnel connections here, see #17848
+ */
+ if (serverdesc && (
+ connection_get_by_type_addr_port_purpose(
+ CONN_TYPE_DIR, &ap->addr, ap->port, DIR_PURPOSE_FETCH_SERVERDESC)
+ || connection_get_by_type_addr_port_purpose(
+ CONN_TYPE_DIR, &ap->addr, ap->port, DIR_PURPOSE_FETCH_EXTRAINFO))) {
+ return 1;
+ }
+
+ if (microdesc && (
+ connection_get_by_type_addr_port_purpose(
+ CONN_TYPE_DIR, &ap->addr, ap->port, DIR_PURPOSE_FETCH_MICRODESC))) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Check if we already have a directory fetch from the ipv4 or ipv6
+ * router, for serverdesc (including extrainfo) or microdesc documents.
+ * If so, return 1, if not, return 0.
+ */
+static int
+router_is_already_dir_fetching_(uint32_t ipv4_addr,
+ const tor_addr_t *ipv6_addr,
+ uint16_t dir_port,
+ int serverdesc,
+ int microdesc)
+{
+ tor_addr_port_t ipv4_dir_ap, ipv6_dir_ap;
+
+ /* Assume IPv6 DirPort is the same as IPv4 DirPort */
+ tor_addr_from_ipv4h(&ipv4_dir_ap.addr, ipv4_addr);
+ ipv4_dir_ap.port = dir_port;
+ tor_addr_copy(&ipv6_dir_ap.addr, ipv6_addr);
+ ipv6_dir_ap.port = dir_port;
+
+ return (router_is_already_dir_fetching(&ipv4_dir_ap, serverdesc, microdesc)
+ || router_is_already_dir_fetching(&ipv6_dir_ap, serverdesc, microdesc));
+}
+
+/** Pick a random running valid directory server/mirror from our
+ * routerlist. Arguments are as for router_pick_directory_server(), except:
+ *
+ * If <b>n_busy_out</b> is provided, set *<b>n_busy_out</b> to the number of
+ * directories that we excluded for no other reason than
+ * PDS_NO_EXISTING_SERVERDESC_FETCH or PDS_NO_EXISTING_MICRODESC_FETCH.
+ */
+STATIC const routerstatus_t *
+router_pick_directory_server_impl(dirinfo_type_t type, int flags,
+ int *n_busy_out)
+{
+ const or_options_t *options = get_options();
+ const node_t *result;
+ smartlist_t *direct, *tunnel;
+ smartlist_t *trusted_direct, *trusted_tunnel;
+ smartlist_t *overloaded_direct, *overloaded_tunnel;
+ time_t now = time(NULL);
+ const networkstatus_t *consensus = networkstatus_get_latest_consensus();
+ const int requireother = ! (flags & PDS_ALLOW_SELF);
+ const int fascistfirewall = ! (flags & PDS_IGNORE_FASCISTFIREWALL);
+ const int no_serverdesc_fetching =(flags & PDS_NO_EXISTING_SERVERDESC_FETCH);
+ const int no_microdesc_fetching = (flags & PDS_NO_EXISTING_MICRODESC_FETCH);
+ int try_excluding = 1, n_excluded = 0, n_busy = 0;
+ int try_ip_pref = 1;
+
+ if (!consensus)
+ return NULL;
+
+ retry_search:
+
+ direct = smartlist_new();
+ tunnel = smartlist_new();
+ trusted_direct = smartlist_new();
+ trusted_tunnel = smartlist_new();
+ overloaded_direct = smartlist_new();
+ overloaded_tunnel = smartlist_new();
+
+ const int skip_or_fw = router_skip_or_reachability(options, try_ip_pref);
+ const int skip_dir_fw = router_skip_dir_reachability(options, try_ip_pref);
+ const int must_have_or = directory_must_use_begindir(options);
+
+ /* Find all the running dirservers we know about. */
+ SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) {
+ int is_trusted;
+ int is_overloaded;
+ const routerstatus_t *status = node->rs;
+ const country_t country = node->country;
+ if (!status)
+ continue;
+
+ if (!node->is_running || !node_is_dir(node) || !node->is_valid)
+ continue;
+ if (requireother && router_digest_is_me(node->identity))
+ continue;
+
+ SKIP_MISSING_TRUSTED_EXTRAINFO(type, node->identity);
+
+ if (try_excluding &&
+ routerset_contains_routerstatus(options->ExcludeNodes, status,
+ country)) {
+ ++n_excluded;
+ continue;
+ }
+
+ if (router_is_already_dir_fetching_(status->addr,
+ &status->ipv6_addr,
+ status->dir_port,
+ no_serverdesc_fetching,
+ no_microdesc_fetching)) {
+ ++n_busy;
+ continue;
+ }
+
+ is_overloaded = status->last_dir_503_at + DIR_503_TIMEOUT > now;
+ is_trusted = router_digest_is_trusted_dir(node->identity);
+
+ /* Clients use IPv6 addresses if the server has one and the client
+ * prefers IPv6.
+ * Add the router if its preferred address and port are reachable.
+ * If we don't get any routers, we'll try again with the non-preferred
+ * address for each router (if any). (To ensure correct load-balancing
+ * we try routers that only have one address both times.)
+ */
+ if (!fascistfirewall || skip_or_fw ||
+ fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION,
+ try_ip_pref))
+ smartlist_add(is_trusted ? trusted_tunnel :
+ is_overloaded ? overloaded_tunnel : tunnel, (void*)node);
+ else if (!must_have_or && (skip_dir_fw ||
+ fascist_firewall_allows_node(node, FIREWALL_DIR_CONNECTION,
+ try_ip_pref)))
+ smartlist_add(is_trusted ? trusted_direct :
+ is_overloaded ? overloaded_direct : direct, (void*)node);
+ } SMARTLIST_FOREACH_END(node);
+
+ if (smartlist_len(tunnel)) {
+ result = node_sl_choose_by_bandwidth(tunnel, WEIGHT_FOR_DIR);
+ } else if (smartlist_len(overloaded_tunnel)) {
+ result = node_sl_choose_by_bandwidth(overloaded_tunnel,
+ WEIGHT_FOR_DIR);
+ } else if (smartlist_len(trusted_tunnel)) {
+ /* FFFF We don't distinguish between trusteds and overloaded trusteds
+ * yet. Maybe one day we should. */
+ /* FFFF We also don't load balance over authorities yet. I think this
+ * is a feature, but it could easily be a bug. -RD */
+ result = smartlist_choose(trusted_tunnel);
+ } else if (smartlist_len(direct)) {
+ result = node_sl_choose_by_bandwidth(direct, WEIGHT_FOR_DIR);
+ } else if (smartlist_len(overloaded_direct)) {
+ result = node_sl_choose_by_bandwidth(overloaded_direct,
+ WEIGHT_FOR_DIR);
+ } else {
+ result = smartlist_choose(trusted_direct);
+ }
+ smartlist_free(direct);
+ smartlist_free(tunnel);
+ smartlist_free(trusted_direct);
+ smartlist_free(trusted_tunnel);
+ smartlist_free(overloaded_direct);
+ smartlist_free(overloaded_tunnel);
+
+ RETRY_ALTERNATE_IP_VERSION(retry_search);
+
+ RETRY_WITHOUT_EXCLUDE(retry_search);
+
+ if (n_busy_out)
+ *n_busy_out = n_busy;
+
+ router_picked_poor_directory_log(result ? result->rs : NULL);
+
+ return result ? result->rs : NULL;
+}
+
+/** Given an array of double/uint64_t unions that are currently being used as
+ * doubles, convert them to uint64_t, and try to scale them linearly so as to
+ * much of the range of uint64_t. If <b>total_out</b> is provided, set it to
+ * the sum of all elements in the array _before_ scaling. */
+STATIC void
+scale_array_elements_to_u64(uint64_t *entries_out, const double *entries_in,
+ int n_entries,
+ uint64_t *total_out)
+{
+ double total = 0.0;
+ double scale_factor = 0.0;
+ int i;
+
+ for (i = 0; i < n_entries; ++i)
+ total += entries_in[i];
+
+ if (total > 0.0) {
+ scale_factor = ((double)INT64_MAX) / total;
+ scale_factor /= 4.0; /* make sure we're very far away from overflowing */
+ }
+
+ for (i = 0; i < n_entries; ++i)
+ entries_out[i] = tor_llround(entries_in[i] * scale_factor);
+
+ if (total_out)
+ *total_out = (uint64_t) total;
+}
+
+/** Pick a random element of <b>n_entries</b>-element array <b>entries</b>,
+ * choosing each element with a probability proportional to its (uint64_t)
+ * value, and return the index of that element. If all elements are 0, choose
+ * an index at random. Return -1 on error.
+ */
+STATIC int
+choose_array_element_by_weight(const uint64_t *entries, int n_entries)
+{
+ int i;
+ uint64_t rand_val;
+ uint64_t total = 0;
+
+ for (i = 0; i < n_entries; ++i)
+ total += entries[i];
+
+ if (n_entries < 1)
+ return -1;
+
+ if (total == 0)
+ return crypto_rand_int(n_entries);
+
+ tor_assert(total < INT64_MAX);
+
+ rand_val = crypto_rand_uint64(total);
+
+ return select_array_member_cumulative_timei(
+ entries, n_entries, total, rand_val);
+}
+
+/** Return bw*1000, unless bw*1000 would overflow, in which case return
+ * INT32_MAX. */
+static inline int32_t
+kb_to_bytes(uint32_t bw)
+{
+ return (bw > (INT32_MAX/1000)) ? INT32_MAX : bw*1000;
+}
+
+/** Helper function:
+ * choose a random element of smartlist <b>sl</b> of nodes, weighted by
+ * the advertised bandwidth of each element using the consensus
+ * bandwidth weights.
+ *
+ * If <b>rule</b>==WEIGHT_FOR_EXIT. we're picking an exit node: consider all
+ * nodes' bandwidth equally regardless of their Exit status, since there may
+ * be some in the list because they exit to obscure ports. If
+ * <b>rule</b>==NO_WEIGHTING, we're picking a non-exit node: weight
+ * exit-node's bandwidth less depending on the smallness of the fraction of
+ * Exit-to-total bandwidth. If <b>rule</b>==WEIGHT_FOR_GUARD, we're picking a
+ * guard node: consider all guard's bandwidth equally. Otherwise, weight
+ * guards proportionally less.
+ */
+static const node_t *
+smartlist_choose_node_by_bandwidth_weights(const smartlist_t *sl,
+ bandwidth_weight_rule_t rule)
+{
+ double *bandwidths_dbl=NULL;
+ uint64_t *bandwidths_u64=NULL;
+
+ if (compute_weighted_bandwidths(sl, rule, &bandwidths_dbl, NULL) < 0)
+ return NULL;
+
+ bandwidths_u64 = tor_calloc(smartlist_len(sl), sizeof(uint64_t));
+ scale_array_elements_to_u64(bandwidths_u64, bandwidths_dbl,
+ smartlist_len(sl), NULL);
+
+ {
+ int idx = choose_array_element_by_weight(bandwidths_u64,
+ smartlist_len(sl));
+ tor_free(bandwidths_dbl);
+ tor_free(bandwidths_u64);
+ return idx < 0 ? NULL : smartlist_get(sl, idx);
+ }
+}
+
+/** When weighting bridges, enforce these values as lower and upper
+ * bound for believable bandwidth, because there is no way for us
+ * to verify a bridge's bandwidth currently. */
+#define BRIDGE_MIN_BELIEVABLE_BANDWIDTH 20000 /* 20 kB/sec */
+#define BRIDGE_MAX_BELIEVABLE_BANDWIDTH 100000 /* 100 kB/sec */
+
+/** Return the smaller of the router's configured BandwidthRate
+ * and its advertised capacity, making sure to stay within the
+ * interval between bridge-min-believe-bw and
+ * bridge-max-believe-bw. */
+static uint32_t
+bridge_get_advertised_bandwidth_bounded(routerinfo_t *router)
+{
+ uint32_t result = router->bandwidthcapacity;
+ if (result > router->bandwidthrate)
+ result = router->bandwidthrate;
+ if (result > BRIDGE_MAX_BELIEVABLE_BANDWIDTH)
+ result = BRIDGE_MAX_BELIEVABLE_BANDWIDTH;
+ else if (result < BRIDGE_MIN_BELIEVABLE_BANDWIDTH)
+ result = BRIDGE_MIN_BELIEVABLE_BANDWIDTH;
+ return result;
+}
+
+/** Given a list of routers and a weighting rule as in
+ * smartlist_choose_node_by_bandwidth_weights, compute weighted bandwidth
+ * values for each node and store them in a freshly allocated
+ * *<b>bandwidths_out</b> of the same length as <b>sl</b>, and holding results
+ * as doubles. If <b>total_bandwidth_out</b> is non-NULL, set it to the total
+ * of all the bandwidths.
+ * Return 0 on success, -1 on failure. */
+static int
+compute_weighted_bandwidths(const smartlist_t *sl,
+ bandwidth_weight_rule_t rule,
+ double **bandwidths_out,
+ double *total_bandwidth_out)
+{
+ int64_t weight_scale;
+ double Wg = -1, Wm = -1, We = -1, Wd = -1;
+ double Wgb = -1, Wmb = -1, Web = -1, Wdb = -1;
+ guardfraction_bandwidth_t guardfraction_bw;
+ double *bandwidths = NULL;
+ double total_bandwidth = 0.0;
+
+ tor_assert(sl);
+ tor_assert(bandwidths_out);
+
+ /* Can't choose exit and guard at same time */
+ tor_assert(rule == NO_WEIGHTING ||
+ rule == WEIGHT_FOR_EXIT ||
+ rule == WEIGHT_FOR_GUARD ||
+ rule == WEIGHT_FOR_MID ||
+ rule == WEIGHT_FOR_DIR);
+
+ *bandwidths_out = NULL;
+
+ if (total_bandwidth_out) {
+ *total_bandwidth_out = 0.0;
+ }
+
+ if (smartlist_len(sl) == 0) {
+ log_info(LD_CIRC,
+ "Empty routerlist passed in to consensus weight node "
+ "selection for rule %s",
+ bandwidth_weight_rule_to_string(rule));
+ return -1;
+ }
+
+ weight_scale = networkstatus_get_weight_scale_param(NULL);
+
+ if (rule == WEIGHT_FOR_GUARD) {
+ Wg = networkstatus_get_bw_weight(NULL, "Wgg", -1);
+ Wm = networkstatus_get_bw_weight(NULL, "Wgm", -1); /* Bridges */
+ We = 0;
+ Wd = networkstatus_get_bw_weight(NULL, "Wgd", -1);
+
+ Wgb = networkstatus_get_bw_weight(NULL, "Wgb", -1);
+ Wmb = networkstatus_get_bw_weight(NULL, "Wmb", -1);
+ Web = networkstatus_get_bw_weight(NULL, "Web", -1);
+ Wdb = networkstatus_get_bw_weight(NULL, "Wdb", -1);
+ } else if (rule == WEIGHT_FOR_MID) {
+ Wg = networkstatus_get_bw_weight(NULL, "Wmg", -1);
+ Wm = networkstatus_get_bw_weight(NULL, "Wmm", -1);
+ We = networkstatus_get_bw_weight(NULL, "Wme", -1);
+ Wd = networkstatus_get_bw_weight(NULL, "Wmd", -1);
+
+ Wgb = networkstatus_get_bw_weight(NULL, "Wgb", -1);
+ Wmb = networkstatus_get_bw_weight(NULL, "Wmb", -1);
+ Web = networkstatus_get_bw_weight(NULL, "Web", -1);
+ Wdb = networkstatus_get_bw_weight(NULL, "Wdb", -1);
+ } else if (rule == WEIGHT_FOR_EXIT) {
+ // Guards CAN be exits if they have weird exit policies
+ // They are d then I guess...
+ We = networkstatus_get_bw_weight(NULL, "Wee", -1);
+ Wm = networkstatus_get_bw_weight(NULL, "Wem", -1); /* Odd exit policies */
+ Wd = networkstatus_get_bw_weight(NULL, "Wed", -1);
+ Wg = networkstatus_get_bw_weight(NULL, "Weg", -1); /* Odd exit policies */
+
+ Wgb = networkstatus_get_bw_weight(NULL, "Wgb", -1);
+ Wmb = networkstatus_get_bw_weight(NULL, "Wmb", -1);
+ Web = networkstatus_get_bw_weight(NULL, "Web", -1);
+ Wdb = networkstatus_get_bw_weight(NULL, "Wdb", -1);
+ } else if (rule == WEIGHT_FOR_DIR) {
+ We = networkstatus_get_bw_weight(NULL, "Wbe", -1);
+ Wm = networkstatus_get_bw_weight(NULL, "Wbm", -1);
+ Wd = networkstatus_get_bw_weight(NULL, "Wbd", -1);
+ Wg = networkstatus_get_bw_weight(NULL, "Wbg", -1);
+
+ Wgb = Wmb = Web = Wdb = weight_scale;
+ } else if (rule == NO_WEIGHTING) {
+ Wg = Wm = We = Wd = weight_scale;
+ Wgb = Wmb = Web = Wdb = weight_scale;
+ }
+
+ if (Wg < 0 || Wm < 0 || We < 0 || Wd < 0 || Wgb < 0 || Wmb < 0 || Wdb < 0
+ || Web < 0) {
+ log_debug(LD_CIRC,
+ "Got negative bandwidth weights. Defaulting to naive selection"
+ " algorithm.");
+ Wg = Wm = We = Wd = weight_scale;
+ Wgb = Wmb = Web = Wdb = weight_scale;
+ }
+
+ Wg /= weight_scale;
+ Wm /= weight_scale;
+ We /= weight_scale;
+ Wd /= weight_scale;
+
+ Wgb /= weight_scale;
+ Wmb /= weight_scale;
+ Web /= weight_scale;
+ Wdb /= weight_scale;
+
+ bandwidths = tor_calloc(smartlist_len(sl), sizeof(double));
+
+ // Cycle through smartlist and total the bandwidth.
+ static int warned_missing_bw = 0;
+ SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) {
+ int is_exit = 0, is_guard = 0, is_dir = 0, this_bw = 0;
+ double weight = 1;
+ double weight_without_guard_flag = 0; /* Used for guardfraction */
+ double final_weight = 0;
+ is_exit = node->is_exit && ! node->is_bad_exit;
+ is_guard = node->is_possible_guard;
+ is_dir = node_is_dir(node);
+ if (node->rs) {
+ if (!node->rs->has_bandwidth) {
+ /* This should never happen, unless all the authorities downgrade
+ * to 0.2.0 or rogue routerstatuses get inserted into our consensus. */
+ if (! warned_missing_bw) {
+ log_warn(LD_BUG,
+ "Consensus is missing some bandwidths. Using a naive "
+ "router selection algorithm");
+ warned_missing_bw = 1;
+ }
+ this_bw = 30000; /* Chosen arbitrarily */
+ } else {
+ this_bw = kb_to_bytes(node->rs->bandwidth_kb);
+ }
+ } else if (node->ri) {
+ /* bridge or other descriptor not in our consensus */
+ this_bw = bridge_get_advertised_bandwidth_bounded(node->ri);
+ } else {
+ /* We can't use this one. */
+ continue;
+ }
+
+ if (is_guard && is_exit) {
+ weight = (is_dir ? Wdb*Wd : Wd);
+ weight_without_guard_flag = (is_dir ? Web*We : We);
+ } else if (is_guard) {
+ weight = (is_dir ? Wgb*Wg : Wg);
+ weight_without_guard_flag = (is_dir ? Wmb*Wm : Wm);
+ } else if (is_exit) {
+ weight = (is_dir ? Web*We : We);
+ } else { // middle
+ weight = (is_dir ? Wmb*Wm : Wm);
+ }
+ /* These should be impossible; but overflows here would be bad, so let's
+ * make sure. */
+ if (this_bw < 0)
+ this_bw = 0;
+ if (weight < 0.0)
+ weight = 0.0;
+ if (weight_without_guard_flag < 0.0)
+ weight_without_guard_flag = 0.0;
+
+ /* If guardfraction information is available in the consensus, we
+ * want to calculate this router's bandwidth according to its
+ * guardfraction. Quoting from proposal236:
+ *
+ * Let Wpf denote the weight from the 'bandwidth-weights' line a
+ * client would apply to N for position p if it had the guard
+ * flag, Wpn the weight if it did not have the guard flag, and B the
+ * measured bandwidth of N in the consensus. Then instead of choosing
+ * N for position p proportionally to Wpf*B or Wpn*B, clients should
+ * choose N proportionally to F*Wpf*B + (1-F)*Wpn*B.
+ */
+ if (node->rs && node->rs->has_guardfraction && rule != WEIGHT_FOR_GUARD) {
+ /* XXX The assert should actually check for is_guard. However,
+ * that crashes dirauths because of #13297. This should be
+ * equivalent: */
+ tor_assert(node->rs->is_possible_guard);
+
+ guard_get_guardfraction_bandwidth(&guardfraction_bw,
+ this_bw,
+ node->rs->guardfraction_percentage);
+
+ /* Calculate final_weight = F*Wpf*B + (1-F)*Wpn*B */
+ final_weight =
+ guardfraction_bw.guard_bw * weight +
+ guardfraction_bw.non_guard_bw * weight_without_guard_flag;
+
+ log_debug(LD_GENERAL, "%s: Guardfraction weight %f instead of %f (%s)",
+ node->rs->nickname, final_weight, weight*this_bw,
+ bandwidth_weight_rule_to_string(rule));
+ } else { /* no guardfraction information. calculate the weight normally. */
+ final_weight = weight*this_bw;
+ }
+
+ bandwidths[node_sl_idx] = final_weight;
+ total_bandwidth += final_weight;
+ } SMARTLIST_FOREACH_END(node);
+
+ log_debug(LD_CIRC, "Generated weighted bandwidths for rule %s based "
+ "on weights "
+ "Wg=%f Wm=%f We=%f Wd=%f with total bw %f",
+ bandwidth_weight_rule_to_string(rule),
+ Wg, Wm, We, Wd, total_bandwidth);
+
+ *bandwidths_out = bandwidths;
+
+ if (total_bandwidth_out) {
+ *total_bandwidth_out = total_bandwidth;
+ }
+
+ return 0;
+}
+
+/** For all nodes in <b>sl</b>, return the fraction of those nodes, weighted
+ * by their weighted bandwidths with rule <b>rule</b>, for which we have
+ * descriptors.
+ *
+ * If <b>for_direct_connect</b> is true, we intend to connect to the node
+ * directly, as the first hop of a circuit; otherwise, we intend to connect
+ * to it indirectly, or use it as if we were connecting to it indirectly. */
+double
+frac_nodes_with_descriptors(const smartlist_t *sl,
+ bandwidth_weight_rule_t rule,
+ int for_direct_conn)
+{
+ double *bandwidths = NULL;
+ double total, present;
+
+ if (smartlist_len(sl) == 0)
+ return 0.0;
+
+ if (compute_weighted_bandwidths(sl, rule, &bandwidths, &total) < 0 ||
+ total <= 0.0) {
+ int n_with_descs = 0;
+ SMARTLIST_FOREACH(sl, const node_t *, node, {
+ if (node_has_preferred_descriptor(node, for_direct_conn))
+ n_with_descs++;
+ });
+ tor_free(bandwidths);
+ return ((double)n_with_descs) / smartlist_len(sl);
+ }
+
+ present = 0.0;
+ SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) {
+ if (node_has_preferred_descriptor(node, for_direct_conn))
+ present += bandwidths[node_sl_idx];
+ } SMARTLIST_FOREACH_END(node);
+
+ tor_free(bandwidths);
+
+ return present / total;
+}
+
+/** Choose a random element of status list <b>sl</b>, weighted by
+ * the advertised bandwidth of each node */
+const node_t *
+node_sl_choose_by_bandwidth(const smartlist_t *sl,
+ bandwidth_weight_rule_t rule)
+{ /*XXXX MOVE */
+ return smartlist_choose_node_by_bandwidth_weights(sl, rule);
+}
+
+/** Given a <b>router</b>, add every node_t in its family (including the
+ * node itself!) to <b>sl</b>.
+ *
+ * Note the type mismatch: This function takes a routerinfo, but adds nodes
+ * to the smartlist!
+ */
+static void
+routerlist_add_node_and_family(smartlist_t *sl, const routerinfo_t *router)
+{
+ /* XXXX MOVE ? */
+ node_t fake_node;
+ const node_t *node = node_get_by_id(router->cache_info.identity_digest);
+ if (node == NULL) {
+ memset(&fake_node, 0, sizeof(fake_node));
+ fake_node.ri = (routerinfo_t *)router;
+ memcpy(fake_node.identity, router->cache_info.identity_digest, DIGEST_LEN);
+ node = &fake_node;
+ }
+ nodelist_add_node_and_family(sl, node);
+}
+
+/** Return a random running node from the nodelist. Never
+ * pick a node that is in
+ * <b>excludedsmartlist</b>, or which matches <b>excludedset</b>,
+ * even if they are the only nodes available.
+ * If <b>CRN_NEED_UPTIME</b> is set in flags and any router has more than
+ * a minimum uptime, return one of those.
+ * If <b>CRN_NEED_CAPACITY</b> is set in flags, weight your choice by the
+ * advertised capacity of each router.
+ * If <b>CRN_NEED_GUARD</b> is set in flags, consider only Guard routers.
+ * If <b>CRN_WEIGHT_AS_EXIT</b> is set in flags, we weight bandwidths as if
+ * picking an exit node, otherwise we weight bandwidths for picking a relay
+ * node (that is, possibly discounting exit nodes).
+ * If <b>CRN_NEED_DESC</b> is set in flags, we only consider nodes that
+ * have a routerinfo or microdescriptor -- that is, enough info to be
+ * used to build a circuit.
+ * If <b>CRN_PREF_ADDR</b> is set in flags, we only consider nodes that
+ * have an address that is preferred by the ClientPreferIPv6ORPort setting
+ * (regardless of this flag, we exclude nodes that aren't allowed by the
+ * firewall, including ClientUseIPv4 0 and fascist_firewall_use_ipv6() == 0).
+ */
+const node_t *
+router_choose_random_node(smartlist_t *excludedsmartlist,
+ routerset_t *excludedset,
+ router_crn_flags_t flags)
+{ /* XXXX MOVE */
+ const int need_uptime = (flags & CRN_NEED_UPTIME) != 0;
+ const int need_capacity = (flags & CRN_NEED_CAPACITY) != 0;
+ const int need_guard = (flags & CRN_NEED_GUARD) != 0;
+ const int weight_for_exit = (flags & CRN_WEIGHT_AS_EXIT) != 0;
+ const int need_desc = (flags & CRN_NEED_DESC) != 0;
+ const int pref_addr = (flags & CRN_PREF_ADDR) != 0;
+ const int direct_conn = (flags & CRN_DIRECT_CONN) != 0;
+ const int rendezvous_v3 = (flags & CRN_RENDEZVOUS_V3) != 0;
+
+ smartlist_t *sl=smartlist_new(),
+ *excludednodes=smartlist_new();
+ const node_t *choice = NULL;
+ const routerinfo_t *r;
+ bandwidth_weight_rule_t rule;
+
+ tor_assert(!(weight_for_exit && need_guard));
+ rule = weight_for_exit ? WEIGHT_FOR_EXIT :
+ (need_guard ? WEIGHT_FOR_GUARD : WEIGHT_FOR_MID);
+
+ SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) {
+ if (node_allows_single_hop_exits(node)) {
+ /* Exclude relays that allow single hop exit circuits. This is an
+ * obsolete option since 0.2.9.2-alpha and done by default in
+ * 0.3.1.0-alpha. */
+ smartlist_add(excludednodes, node);
+ } else if (rendezvous_v3 &&
+ !node_supports_v3_rendezvous_point(node)) {
+ /* Exclude relays that do not support to rendezvous for a hidden service
+ * version 3. */
+ smartlist_add(excludednodes, node);
+ }
+ } SMARTLIST_FOREACH_END(node);
+
+ /* If the node_t is not found we won't be to exclude ourself but we
+ * won't be able to pick ourself in router_choose_random_node() so
+ * this is fine to at least try with our routerinfo_t object. */
+ if ((r = router_get_my_routerinfo()))
+ routerlist_add_node_and_family(excludednodes, r);
+
+ router_add_running_nodes_to_smartlist(sl, need_uptime, need_capacity,
+ need_guard, need_desc, pref_addr,
+ direct_conn);
+ log_debug(LD_CIRC,
+ "We found %d running nodes.",
+ smartlist_len(sl));
+
+ smartlist_subtract(sl,excludednodes);
+ log_debug(LD_CIRC,
+ "We removed %d excludednodes, leaving %d nodes.",
+ smartlist_len(excludednodes),
+ smartlist_len(sl));
+
+ if (excludedsmartlist) {
+ smartlist_subtract(sl,excludedsmartlist);
+ log_debug(LD_CIRC,
+ "We removed %d excludedsmartlist, leaving %d nodes.",
+ smartlist_len(excludedsmartlist),
+ smartlist_len(sl));
+ }
+ if (excludedset) {
+ routerset_subtract_nodes(sl,excludedset);
+ log_debug(LD_CIRC,
+ "We removed excludedset, leaving %d nodes.",
+ smartlist_len(sl));
+ }
+
+ // Always weight by bandwidth
+ choice = node_sl_choose_by_bandwidth(sl, rule);
+
+ smartlist_free(sl);
+ if (!choice && (need_uptime || need_capacity || need_guard || pref_addr)) {
+ /* try once more -- recurse but with fewer restrictions. */
+ log_info(LD_CIRC,
+ "We couldn't find any live%s%s%s routers; falling back "
+ "to list of all routers.",
+ need_capacity?", fast":"",
+ need_uptime?", stable":"",
+ need_guard?", guard":"");
+ flags &= ~ (CRN_NEED_UPTIME|CRN_NEED_CAPACITY|CRN_NEED_GUARD|
+ CRN_PREF_ADDR);
+ choice = router_choose_random_node(
+ excludedsmartlist, excludedset, flags);
+ }
+ smartlist_free(excludednodes);
+ if (!choice) {
+ log_warn(LD_CIRC,
+ "No available nodes when trying to choose node. Failing.");
+ }
+ return choice;
+}
+
+/** Try to find a running directory authority. Flags are as for
+ * router_pick_directory_server.
+ */
+const routerstatus_t *
+router_pick_trusteddirserver(dirinfo_type_t type, int flags)
+{
+ return router_pick_dirserver_generic(
+ router_get_trusted_dir_servers_mutable(),
+ type, flags);
+}
+
+/** Try to find a running fallback directory. Flags are as for
+ * router_pick_directory_server.
+ */
+const routerstatus_t *
+router_pick_fallback_dirserver(dirinfo_type_t type, int flags)
+{
+ return router_pick_dirserver_generic(
+ router_get_fallback_dir_servers_mutable(),
+ type, flags);
+}
+
+/** Pick a random element from a list of dir_server_t, weighting by their
+ * <b>weight</b> field. */
+static const dir_server_t *
+dirserver_choose_by_weight(const smartlist_t *servers, double authority_weight)
+{
+ int n = smartlist_len(servers);
+ int i;
+ double *weights_dbl;
+ uint64_t *weights_u64;
+ const dir_server_t *ds;
+
+ weights_dbl = tor_calloc(n, sizeof(double));
+ weights_u64 = tor_calloc(n, sizeof(uint64_t));
+ for (i = 0; i < n; ++i) {
+ ds = smartlist_get(servers, i);
+ weights_dbl[i] = ds->weight;
+ if (ds->is_authority)
+ weights_dbl[i] *= authority_weight;
+ }
+
+ scale_array_elements_to_u64(weights_u64, weights_dbl, n, NULL);
+ i = choose_array_element_by_weight(weights_u64, n);
+ tor_free(weights_dbl);
+ tor_free(weights_u64);
+ return (i < 0) ? NULL : smartlist_get(servers, i);
+}
+
+/** Choose randomly from among the dir_server_ts in sourcelist that
+ * are up. Flags are as for router_pick_directory_server_impl().
+ */
+static const routerstatus_t *
+router_pick_trusteddirserver_impl(const smartlist_t *sourcelist,
+ dirinfo_type_t type, int flags,
+ int *n_busy_out)
+{
+ const or_options_t *options = get_options();
+ smartlist_t *direct, *tunnel;
+ smartlist_t *overloaded_direct, *overloaded_tunnel;
+ const routerinfo_t *me = router_get_my_routerinfo();
+ const routerstatus_t *result = NULL;
+ time_t now = time(NULL);
+ const int requireother = ! (flags & PDS_ALLOW_SELF);
+ const int fascistfirewall = ! (flags & PDS_IGNORE_FASCISTFIREWALL);
+ const int no_serverdesc_fetching =(flags & PDS_NO_EXISTING_SERVERDESC_FETCH);
+ const int no_microdesc_fetching =(flags & PDS_NO_EXISTING_MICRODESC_FETCH);
+ const double auth_weight =
+ (sourcelist == router_get_fallback_dir_servers()) ?
+ options->DirAuthorityFallbackRate : 1.0;
+ smartlist_t *pick_from;
+ int n_busy = 0;
+ int try_excluding = 1, n_excluded = 0;
+ int try_ip_pref = 1;
+
+ if (!sourcelist)
+ return NULL;
+
+ retry_search:
+
+ direct = smartlist_new();
+ tunnel = smartlist_new();
+ overloaded_direct = smartlist_new();
+ overloaded_tunnel = smartlist_new();
+
+ const int skip_or_fw = router_skip_or_reachability(options, try_ip_pref);
+ const int skip_dir_fw = router_skip_dir_reachability(options, try_ip_pref);
+ const int must_have_or = directory_must_use_begindir(options);
+
+ SMARTLIST_FOREACH_BEGIN(sourcelist, const dir_server_t *, d)
+ {
+ int is_overloaded =
+ d->fake_status.last_dir_503_at + DIR_503_TIMEOUT > now;
+ if (!d->is_running) continue;
+ if ((type & d->type) == 0)
+ continue;
+
+ SKIP_MISSING_TRUSTED_EXTRAINFO(type, d->digest);
+
+ if (requireother && me && router_digest_is_me(d->digest))
+ continue;
+ if (try_excluding &&
+ routerset_contains_routerstatus(options->ExcludeNodes,
+ &d->fake_status, -1)) {
+ ++n_excluded;
+ continue;
+ }
+
+ if (router_is_already_dir_fetching_(d->addr,
+ &d->ipv6_addr,
+ d->dir_port,
+ no_serverdesc_fetching,
+ no_microdesc_fetching)) {
+ ++n_busy;
+ continue;
+ }
+
+ /* Clients use IPv6 addresses if the server has one and the client
+ * prefers IPv6.
+ * Add the router if its preferred address and port are reachable.
+ * If we don't get any routers, we'll try again with the non-preferred
+ * address for each router (if any). (To ensure correct load-balancing
+ * we try routers that only have one address both times.)
+ */
+ if (!fascistfirewall || skip_or_fw ||
+ fascist_firewall_allows_dir_server(d, FIREWALL_OR_CONNECTION,
+ try_ip_pref))
+ smartlist_add(is_overloaded ? overloaded_tunnel : tunnel, (void*)d);
+ else if (!must_have_or && (skip_dir_fw ||
+ fascist_firewall_allows_dir_server(d, FIREWALL_DIR_CONNECTION,
+ try_ip_pref)))
+ smartlist_add(is_overloaded ? overloaded_direct : direct, (void*)d);
+ }
+ SMARTLIST_FOREACH_END(d);
+
+ if (smartlist_len(tunnel)) {
+ pick_from = tunnel;
+ } else if (smartlist_len(overloaded_tunnel)) {
+ pick_from = overloaded_tunnel;
+ } else if (smartlist_len(direct)) {
+ pick_from = direct;
+ } else {
+ pick_from = overloaded_direct;
+ }
+
+ {
+ const dir_server_t *selection =
+ dirserver_choose_by_weight(pick_from, auth_weight);
+
+ if (selection)
+ result = &selection->fake_status;
+ }
+
+ smartlist_free(direct);
+ smartlist_free(tunnel);
+ smartlist_free(overloaded_direct);
+ smartlist_free(overloaded_tunnel);
+
+ RETRY_ALTERNATE_IP_VERSION(retry_search);
+
+ RETRY_WITHOUT_EXCLUDE(retry_search);
+
+ router_picked_poor_directory_log(result);
+
+ if (n_busy_out)
+ *n_busy_out = n_busy;
+ return result;
+}
diff --git a/src/feature/nodelist/node_select.h b/src/feature/nodelist/node_select.h
new file mode 100644
index 0000000000..05dabd1234
--- /dev/null
+++ b/src/feature/nodelist/node_select.h
@@ -0,0 +1,102 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file node_select.h
+ * \brief Header file for node_select.c
+ **/
+
+#ifndef TOR_NODE_SELECT_H
+#define TOR_NODE_SELECT_H
+
+/** Flags to be passed to control router_choose_random_node() to indicate what
+ * kind of nodes to pick according to what algorithm. */
+typedef enum router_crn_flags_t {
+ CRN_NEED_UPTIME = 1<<0,
+ CRN_NEED_CAPACITY = 1<<1,
+ CRN_NEED_GUARD = 1<<2,
+ /* XXXX not used, apparently. */
+ CRN_WEIGHT_AS_EXIT = 1<<5,
+ CRN_NEED_DESC = 1<<6,
+ /* On clients, only provide nodes that satisfy ClientPreferIPv6OR */
+ CRN_PREF_ADDR = 1<<7,
+ /* On clients, only provide nodes that we can connect to directly, based on
+ * our firewall rules */
+ CRN_DIRECT_CONN = 1<<8,
+ /* On clients, only provide nodes with HSRend >= 2 protocol version which
+ * is required for hidden service version >= 3. */
+ CRN_RENDEZVOUS_V3 = 1<<9,
+} router_crn_flags_t;
+
+/** Possible ways to weight routers when choosing one randomly. See
+ * routerlist_sl_choose_by_bandwidth() for more information.*/
+typedef enum bandwidth_weight_rule_t {
+ NO_WEIGHTING, WEIGHT_FOR_EXIT, WEIGHT_FOR_MID, WEIGHT_FOR_GUARD,
+ WEIGHT_FOR_DIR
+} bandwidth_weight_rule_t;
+
+/* Flags for pick_directory_server() and pick_trusteddirserver(). */
+/** Flag to indicate that we should not automatically be willing to use
+ * ourself to answer a directory request.
+ * Passed to router_pick_directory_server (et al).*/
+#define PDS_ALLOW_SELF (1<<0)
+/** Flag to indicate that if no servers seem to be up, we should mark all
+ * directory servers as up and try again.
+ * Passed to router_pick_directory_server (et al).*/
+#define PDS_RETRY_IF_NO_SERVERS (1<<1)
+/** Flag to indicate that we should not exclude directory servers that
+ * our ReachableAddress settings would exclude. This usually means that
+ * we're going to connect to the server over Tor, and so we don't need to
+ * worry about our firewall telling us we can't.
+ * Passed to router_pick_directory_server (et al).*/
+#define PDS_IGNORE_FASCISTFIREWALL (1<<2)
+/** Flag to indicate that we should not use any directory authority to which
+ * we have an existing directory connection for downloading server descriptors
+ * or extrainfo documents.
+ *
+ * Passed to router_pick_directory_server (et al)
+ */
+#define PDS_NO_EXISTING_SERVERDESC_FETCH (1<<3)
+/** Flag to indicate that we should not use any directory authority to which
+ * we have an existing directory connection for downloading microdescs.
+ *
+ * Passed to router_pick_directory_server (et al)
+ */
+#define PDS_NO_EXISTING_MICRODESC_FETCH (1<<4)
+
+const routerstatus_t *router_pick_directory_server(dirinfo_type_t type,
+ int flags);
+
+int router_get_my_share_of_directory_requests(double *v3_share_out);
+
+const node_t *node_sl_choose_by_bandwidth(const smartlist_t *sl,
+ bandwidth_weight_rule_t rule);
+double frac_nodes_with_descriptors(const smartlist_t *sl,
+ bandwidth_weight_rule_t rule,
+ int for_direct_conn);
+const node_t *router_choose_random_node(smartlist_t *excludedsmartlist,
+ struct routerset_t *excludedset,
+ router_crn_flags_t flags);
+
+const routerstatus_t *router_pick_trusteddirserver(dirinfo_type_t type,
+ int flags);
+const routerstatus_t *router_pick_fallback_dirserver(dirinfo_type_t type,
+ int flags);
+
+#ifdef NODE_SELECT_PRIVATE
+STATIC int choose_array_element_by_weight(const uint64_t *entries,
+ int n_entries);
+STATIC void scale_array_elements_to_u64(uint64_t *entries_out,
+ const double *entries_in,
+ int n_entries,
+ uint64_t *total_out);
+STATIC const routerstatus_t *router_pick_directory_server_impl(
+ dirinfo_type_t auth, int flags,
+ int *n_busy_out);
+STATIC int router_is_already_dir_fetching(const tor_addr_port_t *ap,
+ int serverdesc, int microdesc);
+#endif
+
+#endif
diff --git a/src/feature/nodelist/nodelist.c b/src/feature/nodelist/nodelist.c
index 50dc8f7d3c..ce77d71c64 100644
--- a/src/feature/nodelist/nodelist.c
+++ b/src/feature/nodelist/nodelist.c
@@ -47,11 +47,12 @@
#include "app/config/config.h"
#include "feature/control/control.h"
#include "feature/dircache/dirserv.h"
+#include "feature/dirauth/process_descs.h"
#include "feature/client/entrynodes.h"
#include "feature/stats/geoip.h"
#include "feature/hs/hs_common.h"
#include "feature/hs/hs_client.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
@@ -59,6 +60,8 @@
#include "core/or/protover.h"
#include "feature/rend/rendservice.h"
#include "feature/relay/router.h"
+#include "feature/nodelist/dirlist.h"
+#include "feature/nodelist/node_select.h"
#include "feature/nodelist/routerlist.h"
#include "feature/nodelist/routerparse.h"
#include "feature/nodelist/routerset.h"
@@ -1141,6 +1144,11 @@ node_supports_v3_rendezvous_point(const node_t *node)
{
tor_assert(node);
+ /* We can't use a v3 rendezvous point without the curve25519 onion pk. */
+ if (!node_get_curve25519_onion_key(node)) {
+ return 0;
+ }
+
return node_get_protover_summary_flags(node)->supports_v3_rendezvous_point;
}
diff --git a/src/feature/nodelist/routerlist.c b/src/feature/nodelist/routerlist.c
index bcc5c1f074..9a4e48b076 100644
--- a/src/feature/nodelist/routerlist.c
+++ b/src/feature/nodelist/routerlist.c
@@ -57,78 +57,37 @@
* nodes according to different rules and weights. Historically, they
* were all in this module. Now, they are spread across this module,
* nodelist.c, and networkstatus.c. (TODO: Fix that.)
- *
- * <br>
- *
- * (For historical reasons) this module also contains code for handling
- * the list of fallback directories, the list of directory authorities,
- * and the list of authority certificates.
- *
- * For the directory authorities, we have a list containing the public
- * identity key, and contact points, for each authority. The
- * authorities receive descriptors from relays, and publish consensuses,
- * descriptors, and microdescriptors. This list is pre-configured.
- *
- * Fallback directories are well-known, stable, but untrusted directory
- * caches that clients which have not yet bootstrapped can use to get
- * their first networkstatus consensus, in order to find out where the
- * Tor network really is. This list is pre-configured in
- * fallback_dirs.inc. Every authority also serves as a fallback.
- *
- * Both fallback directories and directory authorities are are
- * represented by a dir_server_t.
- *
- * Authority certificates are signed with authority identity keys; they
- * are used to authenticate shorter-term authority signing keys. We
- * fetch them when we find a consensus or a vote that has been signed
- * with a signing key we don't recognize. We cache them on disk and
- * load them on startup. Authority operators generate them with the
- * "tor-gencert" utility.
- *
- * TODO: Authority certificates should be a separate module.
- *
- * TODO: dir_server_t stuff should be in a separate module.
**/
#define ROUTERLIST_PRIVATE
#include "core/or/or.h"
-#include "lib/err/backtrace.h"
-#include "feature/client/bridges.h"
-#include "lib/crypt_ops/crypto_ed25519.h"
-#include "lib/crypt_ops/crypto_format.h"
-#include "core/or/circuitstats.h"
+
#include "app/config/config.h"
#include "core/mainloop/connection.h"
+#include "core/mainloop/mainloop.h"
+#include "core/or/policies.h"
+#include "feature/client/bridges.h"
#include "feature/control/control.h"
-#include "lib/crypt_ops/crypto_rand.h"
+#include "feature/dirauth/mode.h"
#include "feature/dircache/directory.h"
#include "feature/dircache/dirserv.h"
-#include "feature/client/entrynodes.h"
-#include "feature/dircommon/fp_pair.h"
-#include "feature/stats/geoip.h"
-#include "feature/hibernate/hibernate.h"
-#include "core/mainloop/main.h"
+#include "feature/dirauth/reachability.h"
+#include "feature/dirauth/process_descs.h"
+#include "feature/nodelist/authcert.h"
+#include "feature/nodelist/dirlist.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
-#include "core/or/policies.h"
-#include "core/or/reasons.h"
-#include "feature/rend/rendcommon.h"
-#include "feature/rend/rendservice.h"
-#include "feature/stats/rephist.h"
-#include "feature/relay/router.h"
+#include "feature/nodelist/node_select.h"
#include "feature/nodelist/routerlist.h"
#include "feature/nodelist/routerparse.h"
#include "feature/nodelist/routerset.h"
-#include "lib/sandbox/sandbox.h"
#include "feature/nodelist/torcert.h"
-#include "lib/math/fp.h"
-#include "lib/net/resolve.h"
-
-#include "feature/dirauth/dirvote.h"
-#include "feature/dirauth/mode.h"
+#include "feature/relay/router.h"
+#include "feature/stats/rephist.h"
+#include "lib/crypt_ops/crypto_format.h"
+#include "lib/crypt_ops/crypto_rand.h"
-#include "feature/nodelist/authority_cert_st.h"
#include "feature/dircommon/dir_connection_st.h"
#include "feature/dirclient/dir_server_st.h"
#include "feature/nodelist/document_signature_st.h"
@@ -156,7 +115,6 @@
DECLARE_TYPED_DIGESTMAP_FNS(sdmap_, digest_sd_map_t, signed_descriptor_t)
DECLARE_TYPED_DIGESTMAP_FNS(rimap_, digest_ri_map_t, routerinfo_t)
DECLARE_TYPED_DIGESTMAP_FNS(eimap_, digest_ei_map_t, extrainfo_t)
-DECLARE_TYPED_DIGESTMAP_FNS(dsmap_, digest_ds_map_t, download_status_t)
#define SDMAP_FOREACH(map, keyvar, valvar) \
DIGESTMAP_FOREACH(sdmap_to_digestmap(map), keyvar, signed_descriptor_t *, \
valvar)
@@ -164,75 +122,20 @@ DECLARE_TYPED_DIGESTMAP_FNS(dsmap_, digest_ds_map_t, download_status_t)
DIGESTMAP_FOREACH(rimap_to_digestmap(map), keyvar, routerinfo_t *, valvar)
#define EIMAP_FOREACH(map, keyvar, valvar) \
DIGESTMAP_FOREACH(eimap_to_digestmap(map), keyvar, extrainfo_t *, valvar)
-#define DSMAP_FOREACH(map, keyvar, valvar) \
- DIGESTMAP_FOREACH(dsmap_to_digestmap(map), keyvar, download_status_t *, \
- valvar)
#define eimap_free(map, fn) MAP_FREE_AND_NULL(eimap, (map), (fn))
#define rimap_free(map, fn) MAP_FREE_AND_NULL(rimap, (map), (fn))
-#define dsmap_free(map, fn) MAP_FREE_AND_NULL(dsmap, (map), (fn))
#define sdmap_free(map, fn) MAP_FREE_AND_NULL(sdmap, (map), (fn))
-/* Forward declaration for cert_list_t */
-typedef struct cert_list_t cert_list_t;
-
/* static function prototypes */
-static int compute_weighted_bandwidths(const smartlist_t *sl,
- bandwidth_weight_rule_t rule,
- double **bandwidths_out,
- double *total_bandwidth_out);
-static const routerstatus_t *router_pick_trusteddirserver_impl(
- const smartlist_t *sourcelist, dirinfo_type_t auth,
- int flags, int *n_busy_out);
-static const routerstatus_t *router_pick_dirserver_generic(
- smartlist_t *sourcelist,
- dirinfo_type_t type, int flags);
-static void mark_all_dirservers_up(smartlist_t *server_list);
static int signed_desc_digest_is_recognized(signed_descriptor_t *desc);
static const char *signed_descriptor_get_body_impl(
const signed_descriptor_t *desc,
int with_annotations);
-static void list_pending_downloads(digestmap_t *result,
- digest256map_t *result256,
- int purpose, const char *prefix);
-static void list_pending_fpsk_downloads(fp_pair_map_t *result);
static void launch_dummy_descriptor_download_as_needed(time_t now,
const or_options_t *options);
-static void download_status_reset_by_sk_in_cl(cert_list_t *cl,
- const char *digest);
-static int download_status_is_ready_by_sk_in_cl(cert_list_t *cl,
- const char *digest,
- time_t now);
/****************************************************************************/
-/** Global list of a dir_server_t object for each directory
- * authority. */
-static smartlist_t *trusted_dir_servers = NULL;
-/** Global list of dir_server_t objects for all directory authorities
- * and all fallback directory servers. */
-static smartlist_t *fallback_dir_servers = NULL;
-
-/** List of certificates for a single authority, and download status for
- * latest certificate.
- */
-struct cert_list_t {
- /*
- * The keys of download status map are cert->signing_key_digest for pending
- * downloads by (identity digest/signing key digest) pair; functions such
- * as authority_cert_get_by_digest() already assume these are unique.
- */
- struct digest_ds_map_t *dl_status_map;
- /* There is also a dlstatus for the download by identity key only */
- download_status_t dl_status_by_id;
- smartlist_t *certs;
-};
-/** Map from v3 identity key digest to cert_list_t. */
-static digestmap_t *trusted_dir_certs = NULL;
-/** True iff any key certificate in at least one member of
- * <b>trusted_dir_certs</b> has changed since we last flushed the
- * certificates to disk. */
-static int trusted_dir_servers_certs_changed = 0;
-
/** Global list of all of the routers that we know about. */
static routerlist_t *routerlist = NULL;
@@ -245,1084 +148,6 @@ static smartlist_t *warned_nicknames = NULL;
* download is low. */
static time_t last_descriptor_download_attempted = 0;
-/** Return the number of directory authorities whose type matches some bit set
- * in <b>type</b> */
-int
-get_n_authorities(dirinfo_type_t type)
-{
- int n = 0;
- if (!trusted_dir_servers)
- return 0;
- SMARTLIST_FOREACH(trusted_dir_servers, dir_server_t *, ds,
- if (ds->type & type)
- ++n);
- return n;
-}
-
-/** Initialise schedule, want_authority, and increment_on in the download
- * status dlstatus, then call download_status_reset() on it.
- * It is safe to call this function or download_status_reset() multiple times
- * on a new dlstatus. But it should *not* be called after a dlstatus has been
- * used to count download attempts or failures. */
-static void
-download_status_cert_init(download_status_t *dlstatus)
-{
- dlstatus->schedule = DL_SCHED_CONSENSUS;
- dlstatus->want_authority = DL_WANT_ANY_DIRSERVER;
- dlstatus->increment_on = DL_SCHED_INCREMENT_FAILURE;
- dlstatus->last_backoff_position = 0;
- dlstatus->last_delay_used = 0;
-
- /* Use the new schedule to set next_attempt_at */
- download_status_reset(dlstatus);
-}
-
-/** Reset the download status of a specified element in a dsmap */
-static void
-download_status_reset_by_sk_in_cl(cert_list_t *cl, const char *digest)
-{
- download_status_t *dlstatus = NULL;
-
- tor_assert(cl);
- tor_assert(digest);
-
- /* Make sure we have a dsmap */
- if (!(cl->dl_status_map)) {
- cl->dl_status_map = dsmap_new();
- }
- /* Look for a download_status_t in the map with this digest */
- dlstatus = dsmap_get(cl->dl_status_map, digest);
- /* Got one? */
- if (!dlstatus) {
- /* Insert before we reset */
- dlstatus = tor_malloc_zero(sizeof(*dlstatus));
- dsmap_set(cl->dl_status_map, digest, dlstatus);
- download_status_cert_init(dlstatus);
- }
- tor_assert(dlstatus);
- /* Go ahead and reset it */
- download_status_reset(dlstatus);
-}
-
-/**
- * Return true if the download for this signing key digest in cl is ready
- * to be re-attempted.
- */
-static int
-download_status_is_ready_by_sk_in_cl(cert_list_t *cl,
- const char *digest,
- time_t now)
-{
- int rv = 0;
- download_status_t *dlstatus = NULL;
-
- tor_assert(cl);
- tor_assert(digest);
-
- /* Make sure we have a dsmap */
- if (!(cl->dl_status_map)) {
- cl->dl_status_map = dsmap_new();
- }
- /* Look for a download_status_t in the map with this digest */
- dlstatus = dsmap_get(cl->dl_status_map, digest);
- /* Got one? */
- if (dlstatus) {
- /* Use download_status_is_ready() */
- rv = download_status_is_ready(dlstatus, now);
- } else {
- /*
- * If we don't know anything about it, return 1, since we haven't
- * tried this one before. We need to create a new entry here,
- * too.
- */
- dlstatus = tor_malloc_zero(sizeof(*dlstatus));
- download_status_cert_init(dlstatus);
- dsmap_set(cl->dl_status_map, digest, dlstatus);
- rv = 1;
- }
-
- return rv;
-}
-
-/** Helper: Return the cert_list_t for an authority whose authority ID is
- * <b>id_digest</b>, allocating a new list if necessary. */
-static cert_list_t *
-get_cert_list(const char *id_digest)
-{
- cert_list_t *cl;
- if (!trusted_dir_certs)
- trusted_dir_certs = digestmap_new();
- cl = digestmap_get(trusted_dir_certs, id_digest);
- if (!cl) {
- cl = tor_malloc_zero(sizeof(cert_list_t));
- download_status_cert_init(&cl->dl_status_by_id);
- cl->certs = smartlist_new();
- cl->dl_status_map = dsmap_new();
- digestmap_set(trusted_dir_certs, id_digest, cl);
- }
- return cl;
-}
-
-/** Return a list of authority ID digests with potentially enumerable lists
- * of download_status_t objects; used by controller GETINFO queries.
- */
-
-MOCK_IMPL(smartlist_t *,
-list_authority_ids_with_downloads, (void))
-{
- smartlist_t *ids = smartlist_new();
- digestmap_iter_t *i;
- const char *digest;
- char *tmp;
- void *cl;
-
- if (trusted_dir_certs) {
- for (i = digestmap_iter_init(trusted_dir_certs);
- !(digestmap_iter_done(i));
- i = digestmap_iter_next(trusted_dir_certs, i)) {
- /*
- * We always have at least dl_status_by_id to query, so no need to
- * probe deeper than the existence of a cert_list_t.
- */
- digestmap_iter_get(i, &digest, &cl);
- tmp = tor_malloc(DIGEST_LEN);
- memcpy(tmp, digest, DIGEST_LEN);
- smartlist_add(ids, tmp);
- }
- }
- /* else definitely no downloads going since nothing even has a cert list */
-
- return ids;
-}
-
-/** Given an authority ID digest, return a pointer to the default download
- * status, or NULL if there is no such entry in trusted_dir_certs */
-
-MOCK_IMPL(download_status_t *,
-id_only_download_status_for_authority_id, (const char *digest))
-{
- download_status_t *dl = NULL;
- cert_list_t *cl;
-
- if (trusted_dir_certs) {
- cl = digestmap_get(trusted_dir_certs, digest);
- if (cl) {
- dl = &(cl->dl_status_by_id);
- }
- }
-
- return dl;
-}
-
-/** Given an authority ID digest, return a smartlist of signing key digests
- * for which download_status_t is potentially queryable, or NULL if no such
- * authority ID digest is known. */
-
-MOCK_IMPL(smartlist_t *,
-list_sk_digests_for_authority_id, (const char *digest))
-{
- smartlist_t *sks = NULL;
- cert_list_t *cl;
- dsmap_iter_t *i;
- const char *sk_digest;
- char *tmp;
- download_status_t *dl;
-
- if (trusted_dir_certs) {
- cl = digestmap_get(trusted_dir_certs, digest);
- if (cl) {
- sks = smartlist_new();
- if (cl->dl_status_map) {
- for (i = dsmap_iter_init(cl->dl_status_map);
- !(dsmap_iter_done(i));
- i = dsmap_iter_next(cl->dl_status_map, i)) {
- /* Pull the digest out and add it to the list */
- dsmap_iter_get(i, &sk_digest, &dl);
- tmp = tor_malloc(DIGEST_LEN);
- memcpy(tmp, sk_digest, DIGEST_LEN);
- smartlist_add(sks, tmp);
- }
- }
- }
- }
-
- return sks;
-}
-
-/** Given an authority ID digest and a signing key digest, return the
- * download_status_t or NULL if none exists. */
-
-MOCK_IMPL(download_status_t *,
-download_status_for_authority_id_and_sk,(const char *id_digest,
- const char *sk_digest))
-{
- download_status_t *dl = NULL;
- cert_list_t *cl = NULL;
-
- if (trusted_dir_certs) {
- cl = digestmap_get(trusted_dir_certs, id_digest);
- if (cl && cl->dl_status_map) {
- dl = dsmap_get(cl->dl_status_map, sk_digest);
- }
- }
-
- return dl;
-}
-
-#define cert_list_free(val) \
- FREE_AND_NULL(cert_list_t, cert_list_free_, (val))
-
-/** Release all space held by a cert_list_t */
-static void
-cert_list_free_(cert_list_t *cl)
-{
- if (!cl)
- return;
-
- SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
- authority_cert_free(cert));
- smartlist_free(cl->certs);
- dsmap_free(cl->dl_status_map, tor_free_);
- tor_free(cl);
-}
-
-/** Wrapper for cert_list_free so we can pass it to digestmap_free */
-static void
-cert_list_free_void(void *cl)
-{
- cert_list_free_(cl);
-}
-
-/** Reload the cached v3 key certificates from the cached-certs file in
- * the data directory. Return 0 on success, -1 on failure. */
-int
-trusted_dirs_reload_certs(void)
-{
- char *filename;
- char *contents;
- int r;
-
- filename = get_cachedir_fname("cached-certs");
- contents = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL);
- tor_free(filename);
- if (!contents)
- return 0;
- r = trusted_dirs_load_certs_from_string(
- contents,
- TRUSTED_DIRS_CERTS_SRC_FROM_STORE, 1, NULL);
- tor_free(contents);
- return r;
-}
-
-/** Helper: return true iff we already have loaded the exact cert
- * <b>cert</b>. */
-static inline int
-already_have_cert(authority_cert_t *cert)
-{
- cert_list_t *cl = get_cert_list(cert->cache_info.identity_digest);
-
- SMARTLIST_FOREACH(cl->certs, authority_cert_t *, c,
- {
- if (tor_memeq(c->cache_info.signed_descriptor_digest,
- cert->cache_info.signed_descriptor_digest,
- DIGEST_LEN))
- return 1;
- });
- return 0;
-}
-
-/** Load a bunch of new key certificates from the string <b>contents</b>. If
- * <b>source</b> is TRUSTED_DIRS_CERTS_SRC_FROM_STORE, the certificates are
- * from the cache, and we don't need to flush them to disk. If we are a
- * dirauth loading our own cert, source is TRUSTED_DIRS_CERTS_SRC_SELF.
- * Otherwise, source is download type: TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST
- * or TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_SK_DIGEST. If <b>flush</b> is true, we
- * need to flush any changed certificates to disk now. Return 0 on success,
- * -1 if any certs fail to parse.
- *
- * If source_dir is non-NULL, it's the identity digest for a directory that
- * we've just successfully retrieved certificates from, so try it first to
- * fetch any missing certificates.
- */
-int
-trusted_dirs_load_certs_from_string(const char *contents, int source,
- int flush, const char *source_dir)
-{
- dir_server_t *ds;
- const char *s, *eos;
- int failure_code = 0;
- int from_store = (source == TRUSTED_DIRS_CERTS_SRC_FROM_STORE);
- int added_trusted_cert = 0;
-
- for (s = contents; *s; s = eos) {
- authority_cert_t *cert = authority_cert_parse_from_string(s, &eos);
- cert_list_t *cl;
- if (!cert) {
- failure_code = -1;
- break;
- }
- ds = trusteddirserver_get_by_v3_auth_digest(
- cert->cache_info.identity_digest);
- log_debug(LD_DIR, "Parsed certificate for %s",
- ds ? ds->nickname : "unknown authority");
-
- if (already_have_cert(cert)) {
- /* we already have this one. continue. */
- log_info(LD_DIR, "Skipping %s certificate for %s that we "
- "already have.",
- from_store ? "cached" : "downloaded",
- ds ? ds->nickname : "an old or new authority");
-
- /*
- * A duplicate on download should be treated as a failure, so we call
- * authority_cert_dl_failed() to reset the download status to make sure
- * we can't try again. Since we've implemented the fp-sk mechanism
- * to download certs by signing key, this should be much rarer than it
- * was and is perhaps cause for concern.
- */
- if (!from_store) {
- if (authdir_mode(get_options())) {
- log_warn(LD_DIR,
- "Got a certificate for %s, but we already have it. "
- "Maybe they haven't updated it. Waiting for a while.",
- ds ? ds->nickname : "an old or new authority");
- } else {
- log_info(LD_DIR,
- "Got a certificate for %s, but we already have it. "
- "Maybe they haven't updated it. Waiting for a while.",
- ds ? ds->nickname : "an old or new authority");
- }
-
- /*
- * This is where we care about the source; authority_cert_dl_failed()
- * needs to know whether the download was by fp or (fp,sk) pair to
- * twiddle the right bit in the download map.
- */
- if (source == TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST) {
- authority_cert_dl_failed(cert->cache_info.identity_digest,
- NULL, 404);
- } else if (source == TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_SK_DIGEST) {
- authority_cert_dl_failed(cert->cache_info.identity_digest,
- cert->signing_key_digest, 404);
- }
- }
-
- authority_cert_free(cert);
- continue;
- }
-
- if (ds) {
- added_trusted_cert = 1;
- log_info(LD_DIR, "Adding %s certificate for directory authority %s with "
- "signing key %s", from_store ? "cached" : "downloaded",
- ds->nickname, hex_str(cert->signing_key_digest,DIGEST_LEN));
- } else {
- int adding = we_want_to_fetch_unknown_auth_certs(get_options());
- log_info(LD_DIR, "%s %s certificate for unrecognized directory "
- "authority with signing key %s",
- adding ? "Adding" : "Not adding",
- from_store ? "cached" : "downloaded",
- hex_str(cert->signing_key_digest,DIGEST_LEN));
- if (!adding) {
- authority_cert_free(cert);
- continue;
- }
- }
-
- cl = get_cert_list(cert->cache_info.identity_digest);
- smartlist_add(cl->certs, cert);
- if (ds && cert->cache_info.published_on > ds->addr_current_at) {
- /* Check to see whether we should update our view of the authority's
- * address. */
- if (cert->addr && cert->dir_port &&
- (ds->addr != cert->addr ||
- ds->dir_port != cert->dir_port)) {
- char *a = tor_dup_ip(cert->addr);
- log_notice(LD_DIR, "Updating address for directory authority %s "
- "from %s:%d to %s:%d based on certificate.",
- ds->nickname, ds->address, (int)ds->dir_port,
- a, cert->dir_port);
- tor_free(a);
- ds->addr = cert->addr;
- ds->dir_port = cert->dir_port;
- }
- ds->addr_current_at = cert->cache_info.published_on;
- }
-
- if (!from_store)
- trusted_dir_servers_certs_changed = 1;
- }
-
- if (flush)
- trusted_dirs_flush_certs_to_disk();
-
- /* call this even if failure_code is <0, since some certs might have
- * succeeded, but only pass source_dir if there were no failures,
- * and at least one more authority certificate was added to the store.
- * This avoids retrying a directory that's serving bad or entirely duplicate
- * certificates. */
- if (failure_code == 0 && added_trusted_cert) {
- networkstatus_note_certs_arrived(source_dir);
- } else {
- networkstatus_note_certs_arrived(NULL);
- }
-
- return failure_code;
-}
-
-/** Save all v3 key certificates to the cached-certs file. */
-void
-trusted_dirs_flush_certs_to_disk(void)
-{
- char *filename;
- smartlist_t *chunks;
-
- if (!trusted_dir_servers_certs_changed || !trusted_dir_certs)
- return;
-
- chunks = smartlist_new();
- DIGESTMAP_FOREACH(trusted_dir_certs, key, cert_list_t *, cl) {
- SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
- {
- sized_chunk_t *c = tor_malloc(sizeof(sized_chunk_t));
- c->bytes = cert->cache_info.signed_descriptor_body;
- c->len = cert->cache_info.signed_descriptor_len;
- smartlist_add(chunks, c);
- });
- } DIGESTMAP_FOREACH_END;
-
- filename = get_cachedir_fname("cached-certs");
- if (write_chunks_to_file(filename, chunks, 0, 0)) {
- log_warn(LD_FS, "Error writing certificates to disk.");
- }
- tor_free(filename);
- SMARTLIST_FOREACH(chunks, sized_chunk_t *, c, tor_free(c));
- smartlist_free(chunks);
-
- trusted_dir_servers_certs_changed = 0;
-}
-
-static int
-compare_certs_by_pubdates(const void **_a, const void **_b)
-{
- const authority_cert_t *cert1 = *_a, *cert2=*_b;
-
- if (cert1->cache_info.published_on < cert2->cache_info.published_on)
- return -1;
- else if (cert1->cache_info.published_on > cert2->cache_info.published_on)
- return 1;
- else
- return 0;
-}
-
-/** Remove all expired v3 authority certificates that have been superseded for
- * more than 48 hours or, if not expired, that were published more than 7 days
- * before being superseded. (If the most recent cert was published more than 48
- * hours ago, then we aren't going to get any consensuses signed with older
- * keys.) */
-static void
-trusted_dirs_remove_old_certs(void)
-{
- time_t now = time(NULL);
-#define DEAD_CERT_LIFETIME (2*24*60*60)
-#define SUPERSEDED_CERT_LIFETIME (2*24*60*60)
- if (!trusted_dir_certs)
- return;
-
- DIGESTMAP_FOREACH(trusted_dir_certs, key, cert_list_t *, cl) {
- /* Sort the list from first-published to last-published */
- smartlist_sort(cl->certs, compare_certs_by_pubdates);
-
- SMARTLIST_FOREACH_BEGIN(cl->certs, authority_cert_t *, cert) {
- if (cert_sl_idx == smartlist_len(cl->certs) - 1) {
- /* This is the most recently published cert. Keep it. */
- continue;
- }
- authority_cert_t *next_cert = smartlist_get(cl->certs, cert_sl_idx+1);
- const time_t next_cert_published = next_cert->cache_info.published_on;
- if (next_cert_published > now) {
- /* All later certs are published in the future. Keep everything
- * we didn't discard. */
- break;
- }
- int should_remove = 0;
- if (cert->expires + DEAD_CERT_LIFETIME < now) {
- /* Certificate has been expired for at least DEAD_CERT_LIFETIME.
- * Remove it. */
- should_remove = 1;
- } else if (next_cert_published + SUPERSEDED_CERT_LIFETIME < now) {
- /* Certificate has been superseded for OLD_CERT_LIFETIME.
- * Remove it.
- */
- should_remove = 1;
- }
- if (should_remove) {
- SMARTLIST_DEL_CURRENT_KEEPORDER(cl->certs, cert);
- authority_cert_free(cert);
- trusted_dir_servers_certs_changed = 1;
- }
- } SMARTLIST_FOREACH_END(cert);
-
- } DIGESTMAP_FOREACH_END;
-#undef DEAD_CERT_LIFETIME
-#undef OLD_CERT_LIFETIME
-
- trusted_dirs_flush_certs_to_disk();
-}
-
-/** Return the newest v3 authority certificate whose v3 authority identity key
- * has digest <b>id_digest</b>. Return NULL if no such authority is known,
- * or it has no certificate. */
-authority_cert_t *
-authority_cert_get_newest_by_id(const char *id_digest)
-{
- cert_list_t *cl;
- authority_cert_t *best = NULL;
- if (!trusted_dir_certs ||
- !(cl = digestmap_get(trusted_dir_certs, id_digest)))
- return NULL;
-
- SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
- {
- if (!best || cert->cache_info.published_on > best->cache_info.published_on)
- best = cert;
- });
- return best;
-}
-
-/** Return the newest v3 authority certificate whose directory signing key has
- * digest <b>sk_digest</b>. Return NULL if no such certificate is known.
- */
-authority_cert_t *
-authority_cert_get_by_sk_digest(const char *sk_digest)
-{
- authority_cert_t *c;
- if (!trusted_dir_certs)
- return NULL;
-
- if ((c = get_my_v3_authority_cert()) &&
- tor_memeq(c->signing_key_digest, sk_digest, DIGEST_LEN))
- return c;
- if ((c = get_my_v3_legacy_cert()) &&
- tor_memeq(c->signing_key_digest, sk_digest, DIGEST_LEN))
- return c;
-
- DIGESTMAP_FOREACH(trusted_dir_certs, key, cert_list_t *, cl) {
- SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
- {
- if (tor_memeq(cert->signing_key_digest, sk_digest, DIGEST_LEN))
- return cert;
- });
- } DIGESTMAP_FOREACH_END;
- return NULL;
-}
-
-/** Return the v3 authority certificate with signing key matching
- * <b>sk_digest</b>, for the authority with identity digest <b>id_digest</b>.
- * Return NULL if no such authority is known. */
-authority_cert_t *
-authority_cert_get_by_digests(const char *id_digest,
- const char *sk_digest)
-{
- cert_list_t *cl;
- if (!trusted_dir_certs ||
- !(cl = digestmap_get(trusted_dir_certs, id_digest)))
- return NULL;
- SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
- if (tor_memeq(cert->signing_key_digest, sk_digest, DIGEST_LEN))
- return cert; );
-
- return NULL;
-}
-
-/** Add every known authority_cert_t to <b>certs_out</b>. */
-void
-authority_cert_get_all(smartlist_t *certs_out)
-{
- tor_assert(certs_out);
- if (!trusted_dir_certs)
- return;
-
- DIGESTMAP_FOREACH(trusted_dir_certs, key, cert_list_t *, cl) {
- SMARTLIST_FOREACH(cl->certs, authority_cert_t *, c,
- smartlist_add(certs_out, c));
- } DIGESTMAP_FOREACH_END;
-}
-
-/** Called when an attempt to download a certificate with the authority with
- * ID <b>id_digest</b> and, if not NULL, signed with key signing_key_digest
- * fails with HTTP response code <b>status</b>: remember the failure, so we
- * don't try again immediately. */
-void
-authority_cert_dl_failed(const char *id_digest,
- const char *signing_key_digest, int status)
-{
- cert_list_t *cl;
- download_status_t *dlstatus = NULL;
- char id_digest_str[2*DIGEST_LEN+1];
- char sk_digest_str[2*DIGEST_LEN+1];
-
- if (!trusted_dir_certs ||
- !(cl = digestmap_get(trusted_dir_certs, id_digest)))
- return;
-
- /*
- * Are we noting a failed download of the latest cert for the id digest,
- * or of a download by (id, signing key) digest pair?
- */
- if (!signing_key_digest) {
- /* Just by id digest */
- download_status_failed(&cl->dl_status_by_id, status);
- } else {
- /* Reset by (id, signing key) digest pair
- *
- * Look for a download_status_t in the map with this digest
- */
- dlstatus = dsmap_get(cl->dl_status_map, signing_key_digest);
- /* Got one? */
- if (dlstatus) {
- download_status_failed(dlstatus, status);
- } else {
- /*
- * Do this rather than hex_str(), since hex_str clobbers
- * old results and we call twice in the param list.
- */
- base16_encode(id_digest_str, sizeof(id_digest_str),
- id_digest, DIGEST_LEN);
- base16_encode(sk_digest_str, sizeof(sk_digest_str),
- signing_key_digest, DIGEST_LEN);
- log_warn(LD_BUG,
- "Got failure for cert fetch with (fp,sk) = (%s,%s), with "
- "status %d, but knew nothing about the download.",
- id_digest_str, sk_digest_str, status);
- }
- }
-}
-
-static const char *BAD_SIGNING_KEYS[] = {
- "09CD84F751FD6E955E0F8ADB497D5401470D697E", // Expires 2015-01-11 16:26:31
- "0E7E9C07F0969D0468AD741E172A6109DC289F3C", // Expires 2014-08-12 10:18:26
- "57B85409891D3FB32137F642FDEDF8B7F8CDFDCD", // Expires 2015-02-11 17:19:09
- "87326329007AF781F587AF5B594E540B2B6C7630", // Expires 2014-07-17 11:10:09
- "98CC82342DE8D298CF99D3F1A396475901E0D38E", // Expires 2014-11-10 13:18:56
- "9904B52336713A5ADCB13E4FB14DC919E0D45571", // Expires 2014-04-20 20:01:01
- "9DCD8E3F1DD1597E2AD476BBA28A1A89F3095227", // Expires 2015-01-16 03:52:30
- "A61682F34B9BB9694AC98491FE1ABBFE61923941", // Expires 2014-06-11 09:25:09
- "B59F6E99C575113650C99F1C425BA7B20A8C071D", // Expires 2014-07-31 13:22:10
- "D27178388FA75B96D37FA36E0B015227DDDBDA51", // Expires 2014-08-04 04:01:57
- NULL,
-};
-
-/** Return true iff <b>cert</b> authenticates some atuhority signing key
- * which, because of the old openssl heartbleed vulnerability, should
- * never be trusted. */
-int
-authority_cert_is_blacklisted(const authority_cert_t *cert)
-{
- char hex_digest[HEX_DIGEST_LEN+1];
- int i;
- base16_encode(hex_digest, sizeof(hex_digest),
- cert->signing_key_digest, sizeof(cert->signing_key_digest));
-
- for (i = 0; BAD_SIGNING_KEYS[i]; ++i) {
- if (!strcasecmp(hex_digest, BAD_SIGNING_KEYS[i])) {
- return 1;
- }
- }
- return 0;
-}
-
-/** Return true iff when we've been getting enough failures when trying to
- * download the certificate with ID digest <b>id_digest</b> that we're willing
- * to start bugging the user about it. */
-int
-authority_cert_dl_looks_uncertain(const char *id_digest)
-{
-#define N_AUTH_CERT_DL_FAILURES_TO_BUG_USER 2
- cert_list_t *cl;
- int n_failures;
- if (!trusted_dir_certs ||
- !(cl = digestmap_get(trusted_dir_certs, id_digest)))
- return 0;
-
- n_failures = download_status_get_n_failures(&cl->dl_status_by_id);
- return n_failures >= N_AUTH_CERT_DL_FAILURES_TO_BUG_USER;
-}
-
-/* Fetch the authority certificates specified in resource.
- * If we are a bridge client, and node is a configured bridge, fetch from node
- * using dir_hint as the fingerprint. Otherwise, if rs is not NULL, fetch from
- * rs. Otherwise, fetch from a random directory mirror. */
-static void
-authority_certs_fetch_resource_impl(const char *resource,
- const char *dir_hint,
- const node_t *node,
- const routerstatus_t *rs)
-{
- const or_options_t *options = get_options();
- int get_via_tor = purpose_needs_anonymity(DIR_PURPOSE_FETCH_CERTIFICATE, 0,
- resource);
-
- /* Make sure bridge clients never connect to anything but a bridge */
- if (options->UseBridges) {
- if (node && !node_is_a_configured_bridge(node)) {
- /* If we're using bridges, and node is not a bridge, use a 3-hop path. */
- get_via_tor = 1;
- } else if (!node) {
- /* If we're using bridges, and there's no node, use a 3-hop path. */
- get_via_tor = 1;
- }
- }
-
- const dir_indirection_t indirection = get_via_tor ? DIRIND_ANONYMOUS
- : DIRIND_ONEHOP;
-
- directory_request_t *req = NULL;
- /* If we've just downloaded a consensus from a bridge, re-use that
- * bridge */
- if (options->UseBridges && node && node->ri && !get_via_tor) {
- /* clients always make OR connections to bridges */
- tor_addr_port_t or_ap;
- /* we are willing to use a non-preferred address if we need to */
- fascist_firewall_choose_address_node(node, FIREWALL_OR_CONNECTION, 0,
- &or_ap);
-
- req = directory_request_new(DIR_PURPOSE_FETCH_CERTIFICATE);
- directory_request_set_or_addr_port(req, &or_ap);
- if (dir_hint)
- directory_request_set_directory_id_digest(req, dir_hint);
- } else if (rs) {
- /* And if we've just downloaded a consensus from a directory, re-use that
- * directory */
- req = directory_request_new(DIR_PURPOSE_FETCH_CERTIFICATE);
- directory_request_set_routerstatus(req, rs);
- }
-
- if (req) {
- /* We've set up a request object -- fill in the other request fields, and
- * send the request. */
- directory_request_set_indirection(req, indirection);
- directory_request_set_resource(req, resource);
- directory_initiate_request(req);
- directory_request_free(req);
- return;
- }
-
- /* Otherwise, we want certs from a random fallback or directory
- * mirror, because they will almost always succeed. */
- directory_get_from_dirserver(DIR_PURPOSE_FETCH_CERTIFICATE, 0,
- resource, PDS_RETRY_IF_NO_SERVERS,
- DL_WANT_ANY_DIRSERVER);
-}
-
-/** Try to download any v3 authority certificates that we may be missing. If
- * <b>status</b> is provided, try to get all the ones that were used to sign
- * <b>status</b>. Additionally, try to have a non-expired certificate for
- * every V3 authority in trusted_dir_servers. Don't fetch certificates we
- * already have.
- *
- * If dir_hint is non-NULL, it's the identity digest for a directory that
- * we've just successfully retrieved a consensus or certificates from, so try
- * it first to fetch any missing certificates.
- **/
-void
-authority_certs_fetch_missing(networkstatus_t *status, time_t now,
- const char *dir_hint)
-{
- /*
- * The pending_id digestmap tracks pending certificate downloads by
- * identity digest; the pending_cert digestmap tracks pending downloads
- * by (identity digest, signing key digest) pairs.
- */
- digestmap_t *pending_id;
- fp_pair_map_t *pending_cert;
- /*
- * The missing_id_digests smartlist will hold a list of id digests
- * we want to fetch the newest cert for; the missing_cert_digests
- * smartlist will hold a list of fp_pair_t with an identity and
- * signing key digest.
- */
- smartlist_t *missing_cert_digests, *missing_id_digests;
- char *resource = NULL;
- cert_list_t *cl;
- const or_options_t *options = get_options();
- const int keep_unknown = we_want_to_fetch_unknown_auth_certs(options);
- fp_pair_t *fp_tmp = NULL;
- char id_digest_str[2*DIGEST_LEN+1];
- char sk_digest_str[2*DIGEST_LEN+1];
-
- if (should_delay_dir_fetches(options, NULL))
- return;
-
- pending_cert = fp_pair_map_new();
- pending_id = digestmap_new();
- missing_cert_digests = smartlist_new();
- missing_id_digests = smartlist_new();
-
- /*
- * First, we get the lists of already pending downloads so we don't
- * duplicate effort.
- */
- list_pending_downloads(pending_id, NULL,
- DIR_PURPOSE_FETCH_CERTIFICATE, "fp/");
- list_pending_fpsk_downloads(pending_cert);
-
- /*
- * Now, we download any trusted authority certs we don't have by
- * identity digest only. This gets the latest cert for that authority.
- */
- SMARTLIST_FOREACH_BEGIN(trusted_dir_servers, dir_server_t *, ds) {
- int found = 0;
- if (!(ds->type & V3_DIRINFO))
- continue;
- if (smartlist_contains_digest(missing_id_digests,
- ds->v3_identity_digest))
- continue;
- cl = get_cert_list(ds->v3_identity_digest);
- SMARTLIST_FOREACH_BEGIN(cl->certs, authority_cert_t *, cert) {
- if (now < cert->expires) {
- /* It's not expired, and we weren't looking for something to
- * verify a consensus with. Call it done. */
- download_status_reset(&(cl->dl_status_by_id));
- /* No sense trying to download it specifically by signing key hash */
- download_status_reset_by_sk_in_cl(cl, cert->signing_key_digest);
- found = 1;
- break;
- }
- } SMARTLIST_FOREACH_END(cert);
- if (!found &&
- download_status_is_ready(&(cl->dl_status_by_id), now) &&
- !digestmap_get(pending_id, ds->v3_identity_digest)) {
- log_info(LD_DIR,
- "No current certificate known for authority %s "
- "(ID digest %s); launching request.",
- ds->nickname, hex_str(ds->v3_identity_digest, DIGEST_LEN));
- smartlist_add(missing_id_digests, ds->v3_identity_digest);
- }
- } SMARTLIST_FOREACH_END(ds);
-
- /*
- * Next, if we have a consensus, scan through it and look for anything
- * signed with a key from a cert we don't have. Those get downloaded
- * by (fp,sk) pair, but if we don't know any certs at all for the fp
- * (identity digest), and it's one of the trusted dir server certs
- * we started off above or a pending download in pending_id, don't
- * try to get it yet. Most likely, the one we'll get for that will
- * have the right signing key too, and we'd just be downloading
- * redundantly.
- */
- if (status) {
- SMARTLIST_FOREACH_BEGIN(status->voters, networkstatus_voter_info_t *,
- voter) {
- if (!smartlist_len(voter->sigs))
- continue; /* This authority never signed this consensus, so don't
- * go looking for a cert with key digest 0000000000. */
- if (!keep_unknown &&
- !trusteddirserver_get_by_v3_auth_digest(voter->identity_digest))
- continue; /* We don't want unknown certs, and we don't know this
- * authority.*/
-
- /*
- * If we don't know *any* cert for this authority, and a download by ID
- * is pending or we added it to missing_id_digests above, skip this
- * one for now to avoid duplicate downloads.
- */
- cl = get_cert_list(voter->identity_digest);
- if (smartlist_len(cl->certs) == 0) {
- /* We have no certs at all for this one */
-
- /* Do we have a download of one pending? */
- if (digestmap_get(pending_id, voter->identity_digest))
- continue;
-
- /*
- * Are we about to launch a download of one due to the trusted
- * dir server check above?
- */
- if (smartlist_contains_digest(missing_id_digests,
- voter->identity_digest))
- continue;
- }
-
- SMARTLIST_FOREACH_BEGIN(voter->sigs, document_signature_t *, sig) {
- authority_cert_t *cert =
- authority_cert_get_by_digests(voter->identity_digest,
- sig->signing_key_digest);
- if (cert) {
- if (now < cert->expires)
- download_status_reset_by_sk_in_cl(cl, sig->signing_key_digest);
- continue;
- }
- if (download_status_is_ready_by_sk_in_cl(
- cl, sig->signing_key_digest, now) &&
- !fp_pair_map_get_by_digests(pending_cert,
- voter->identity_digest,
- sig->signing_key_digest)) {
- /*
- * Do this rather than hex_str(), since hex_str clobbers
- * old results and we call twice in the param list.
- */
- base16_encode(id_digest_str, sizeof(id_digest_str),
- voter->identity_digest, DIGEST_LEN);
- base16_encode(sk_digest_str, sizeof(sk_digest_str),
- sig->signing_key_digest, DIGEST_LEN);
-
- if (voter->nickname) {
- log_info(LD_DIR,
- "We're missing a certificate from authority %s "
- "(ID digest %s) with signing key %s: "
- "launching request.",
- voter->nickname, id_digest_str, sk_digest_str);
- } else {
- log_info(LD_DIR,
- "We're missing a certificate from authority ID digest "
- "%s with signing key %s: launching request.",
- id_digest_str, sk_digest_str);
- }
-
- /* Allocate a new fp_pair_t to append */
- fp_tmp = tor_malloc(sizeof(*fp_tmp));
- memcpy(fp_tmp->first, voter->identity_digest, sizeof(fp_tmp->first));
- memcpy(fp_tmp->second, sig->signing_key_digest,
- sizeof(fp_tmp->second));
- smartlist_add(missing_cert_digests, fp_tmp);
- }
- } SMARTLIST_FOREACH_END(sig);
- } SMARTLIST_FOREACH_END(voter);
- }
-
- /* Bridge clients look up the node for the dir_hint */
- const node_t *node = NULL;
- /* All clients, including bridge clients, look up the routerstatus for the
- * dir_hint */
- const routerstatus_t *rs = NULL;
-
- /* If we still need certificates, try the directory that just successfully
- * served us a consensus or certificates.
- * As soon as the directory fails to provide additional certificates, we try
- * another, randomly selected directory. This avoids continual retries.
- * (We only ever have one outstanding request per certificate.)
- */
- if (dir_hint) {
- if (options->UseBridges) {
- /* Bridge clients try the nodelist. If the dir_hint is from an authority,
- * or something else fetched over tor, we won't find the node here, but
- * we will find the rs. */
- node = node_get_by_id(dir_hint);
- }
-
- /* All clients try the consensus routerstatus, then the fallback
- * routerstatus */
- rs = router_get_consensus_status_by_id(dir_hint);
- if (!rs) {
- /* This will also find authorities */
- const dir_server_t *ds = router_get_fallback_dirserver_by_digest(
- dir_hint);
- if (ds) {
- rs = &ds->fake_status;
- }
- }
-
- if (!node && !rs) {
- log_warn(LD_BUG, "Directory %s delivered a consensus, but %s"
- "no routerstatus could be found for it.",
- options->UseBridges ? "no node and " : "",
- hex_str(dir_hint, DIGEST_LEN));
- }
- }
-
- /* Do downloads by identity digest */
- if (smartlist_len(missing_id_digests) > 0) {
- int need_plus = 0;
- smartlist_t *fps = smartlist_new();
-
- smartlist_add_strdup(fps, "fp/");
-
- SMARTLIST_FOREACH_BEGIN(missing_id_digests, const char *, d) {
- char *fp = NULL;
-
- if (digestmap_get(pending_id, d))
- continue;
-
- base16_encode(id_digest_str, sizeof(id_digest_str),
- d, DIGEST_LEN);
-
- if (need_plus) {
- tor_asprintf(&fp, "+%s", id_digest_str);
- } else {
- /* No need for tor_asprintf() in this case; first one gets no '+' */
- fp = tor_strdup(id_digest_str);
- need_plus = 1;
- }
-
- smartlist_add(fps, fp);
- } SMARTLIST_FOREACH_END(d);
-
- if (smartlist_len(fps) > 1) {
- resource = smartlist_join_strings(fps, "", 0, NULL);
- /* node and rs are directories that just gave us a consensus or
- * certificates */
- authority_certs_fetch_resource_impl(resource, dir_hint, node, rs);
- tor_free(resource);
- }
- /* else we didn't add any: they were all pending */
-
- SMARTLIST_FOREACH(fps, char *, cp, tor_free(cp));
- smartlist_free(fps);
- }
-
- /* Do downloads by identity digest/signing key pair */
- if (smartlist_len(missing_cert_digests) > 0) {
- int need_plus = 0;
- smartlist_t *fp_pairs = smartlist_new();
-
- smartlist_add_strdup(fp_pairs, "fp-sk/");
-
- SMARTLIST_FOREACH_BEGIN(missing_cert_digests, const fp_pair_t *, d) {
- char *fp_pair = NULL;
-
- if (fp_pair_map_get(pending_cert, d))
- continue;
-
- /* Construct string encodings of the digests */
- base16_encode(id_digest_str, sizeof(id_digest_str),
- d->first, DIGEST_LEN);
- base16_encode(sk_digest_str, sizeof(sk_digest_str),
- d->second, DIGEST_LEN);
-
- /* Now tor_asprintf() */
- if (need_plus) {
- tor_asprintf(&fp_pair, "+%s-%s", id_digest_str, sk_digest_str);
- } else {
- /* First one in the list doesn't get a '+' */
- tor_asprintf(&fp_pair, "%s-%s", id_digest_str, sk_digest_str);
- need_plus = 1;
- }
-
- /* Add it to the list of pairs to request */
- smartlist_add(fp_pairs, fp_pair);
- } SMARTLIST_FOREACH_END(d);
-
- if (smartlist_len(fp_pairs) > 1) {
- resource = smartlist_join_strings(fp_pairs, "", 0, NULL);
- /* node and rs are directories that just gave us a consensus or
- * certificates */
- authority_certs_fetch_resource_impl(resource, dir_hint, node, rs);
- tor_free(resource);
- }
- /* else they were all pending */
-
- SMARTLIST_FOREACH(fp_pairs, char *, p, tor_free(p));
- smartlist_free(fp_pairs);
- }
-
- smartlist_free(missing_id_digests);
- SMARTLIST_FOREACH(missing_cert_digests, fp_pair_t *, p, tor_free(p));
- smartlist_free(missing_cert_digests);
- digestmap_free(pending_id, NULL);
- fp_pair_map_free(pending_cert, NULL);
-}
-
/* Router descriptor storage.
*
* Routerdescs are stored in a big file, named "cached-descriptors". As new
@@ -1635,358 +460,6 @@ router_reload_router_list(void)
return 0;
}
-/** Return a smartlist containing a list of dir_server_t * for all
- * known trusted dirservers. Callers must not modify the list or its
- * contents.
- */
-const smartlist_t *
-router_get_trusted_dir_servers(void)
-{
- if (!trusted_dir_servers)
- trusted_dir_servers = smartlist_new();
-
- return trusted_dir_servers;
-}
-
-const smartlist_t *
-router_get_fallback_dir_servers(void)
-{
- if (!fallback_dir_servers)
- fallback_dir_servers = smartlist_new();
-
- return fallback_dir_servers;
-}
-
-/** Try to find a running dirserver that supports operations of <b>type</b>.
- *
- * If there are no running dirservers in our routerlist and the
- * <b>PDS_RETRY_IF_NO_SERVERS</b> flag is set, set all the fallback ones
- * (including authorities) as running again, and pick one.
- *
- * If the <b>PDS_IGNORE_FASCISTFIREWALL</b> flag is set, then include
- * dirservers that we can't reach.
- *
- * If the <b>PDS_ALLOW_SELF</b> flag is not set, then don't include ourself
- * (if we're a dirserver).
- *
- * Don't pick a fallback directory mirror if any non-fallback is viable;
- * (the fallback directory mirrors include the authorities)
- * try to avoid using servers that have returned 503 recently.
- */
-const routerstatus_t *
-router_pick_directory_server(dirinfo_type_t type, int flags)
-{
- int busy = 0;
- const routerstatus_t *choice;
-
- if (!routerlist)
- return NULL;
-
- choice = router_pick_directory_server_impl(type, flags, &busy);
- if (choice || !(flags & PDS_RETRY_IF_NO_SERVERS))
- return choice;
-
- if (busy) {
- /* If the reason that we got no server is that servers are "busy",
- * we must be excluding good servers because we already have serverdesc
- * fetches with them. Do not mark down servers up because of this. */
- tor_assert((flags & (PDS_NO_EXISTING_SERVERDESC_FETCH|
- PDS_NO_EXISTING_MICRODESC_FETCH)));
- return NULL;
- }
-
- log_info(LD_DIR,
- "No reachable router entries for dirservers. "
- "Trying them all again.");
- /* mark all fallback directory mirrors as up again */
- mark_all_dirservers_up(fallback_dir_servers);
- /* try again */
- choice = router_pick_directory_server_impl(type, flags, NULL);
- return choice;
-}
-
-/** Return the dir_server_t for the directory authority whose identity
- * key hashes to <b>digest</b>, or NULL if no such authority is known.
- */
-dir_server_t *
-router_get_trusteddirserver_by_digest(const char *digest)
-{
- if (!trusted_dir_servers)
- return NULL;
-
- SMARTLIST_FOREACH(trusted_dir_servers, dir_server_t *, ds,
- {
- if (tor_memeq(ds->digest, digest, DIGEST_LEN))
- return ds;
- });
-
- return NULL;
-}
-
-/** Return the dir_server_t for the fallback dirserver whose identity
- * key hashes to <b>digest</b>, or NULL if no such fallback is in the list of
- * fallback_dir_servers. (fallback_dir_servers is affected by the FallbackDir
- * and UseDefaultFallbackDirs torrc options.)
- * The list of fallback directories includes the list of authorities.
- */
-dir_server_t *
-router_get_fallback_dirserver_by_digest(const char *digest)
-{
- if (!fallback_dir_servers)
- return NULL;
-
- if (!digest)
- return NULL;
-
- SMARTLIST_FOREACH(fallback_dir_servers, dir_server_t *, ds,
- {
- if (tor_memeq(ds->digest, digest, DIGEST_LEN))
- return ds;
- });
-
- return NULL;
-}
-
-/** Return 1 if any fallback dirserver's identity key hashes to <b>digest</b>,
- * or 0 if no such fallback is in the list of fallback_dir_servers.
- * (fallback_dir_servers is affected by the FallbackDir and
- * UseDefaultFallbackDirs torrc options.)
- * The list of fallback directories includes the list of authorities.
- */
-int
-router_digest_is_fallback_dir(const char *digest)
-{
- return (router_get_fallback_dirserver_by_digest(digest) != NULL);
-}
-
-/** Return the dir_server_t for the directory authority whose
- * v3 identity key hashes to <b>digest</b>, or NULL if no such authority
- * is known.
- */
-MOCK_IMPL(dir_server_t *,
-trusteddirserver_get_by_v3_auth_digest, (const char *digest))
-{
- if (!trusted_dir_servers)
- return NULL;
-
- SMARTLIST_FOREACH(trusted_dir_servers, dir_server_t *, ds,
- {
- if (tor_memeq(ds->v3_identity_digest, digest, DIGEST_LEN) &&
- (ds->type & V3_DIRINFO))
- return ds;
- });
-
- return NULL;
-}
-
-/** Try to find a running directory authority. Flags are as for
- * router_pick_directory_server.
- */
-const routerstatus_t *
-router_pick_trusteddirserver(dirinfo_type_t type, int flags)
-{
- return router_pick_dirserver_generic(trusted_dir_servers, type, flags);
-}
-
-/** Try to find a running fallback directory. Flags are as for
- * router_pick_directory_server.
- */
-const routerstatus_t *
-router_pick_fallback_dirserver(dirinfo_type_t type, int flags)
-{
- return router_pick_dirserver_generic(fallback_dir_servers, type, flags);
-}
-
-/** Try to find a running fallback directory. Flags are as for
- * router_pick_directory_server.
- */
-static const routerstatus_t *
-router_pick_dirserver_generic(smartlist_t *sourcelist,
- dirinfo_type_t type, int flags)
-{
- const routerstatus_t *choice;
- int busy = 0;
-
- if (smartlist_len(sourcelist) == 1) {
- /* If there's only one choice, then we should disable the logic that
- * would otherwise prevent us from choosing ourself. */
- flags |= PDS_ALLOW_SELF;
- }
-
- choice = router_pick_trusteddirserver_impl(sourcelist, type, flags, &busy);
- if (choice || !(flags & PDS_RETRY_IF_NO_SERVERS))
- return choice;
- if (busy) {
- /* If the reason that we got no server is that servers are "busy",
- * we must be excluding good servers because we already have serverdesc
- * fetches with them. Do not mark down servers up because of this. */
- tor_assert((flags & (PDS_NO_EXISTING_SERVERDESC_FETCH|
- PDS_NO_EXISTING_MICRODESC_FETCH)));
- return NULL;
- }
-
- log_info(LD_DIR,
- "No dirservers are reachable. Trying them all again.");
- mark_all_dirservers_up(sourcelist);
- return router_pick_trusteddirserver_impl(sourcelist, type, flags, NULL);
-}
-
-/* Check if we already have a directory fetch from ap, for serverdesc
- * (including extrainfo) or microdesc documents.
- * If so, return 1, if not, return 0.
- * Also returns 0 if addr is NULL, tor_addr_is_null(addr), or dir_port is 0.
- */
-STATIC int
-router_is_already_dir_fetching(const tor_addr_port_t *ap, int serverdesc,
- int microdesc)
-{
- if (!ap || tor_addr_is_null(&ap->addr) || !ap->port) {
- return 0;
- }
-
- /* XX/teor - we're not checking tunnel connections here, see #17848
- */
- if (serverdesc && (
- connection_get_by_type_addr_port_purpose(
- CONN_TYPE_DIR, &ap->addr, ap->port, DIR_PURPOSE_FETCH_SERVERDESC)
- || connection_get_by_type_addr_port_purpose(
- CONN_TYPE_DIR, &ap->addr, ap->port, DIR_PURPOSE_FETCH_EXTRAINFO))) {
- return 1;
- }
-
- if (microdesc && (
- connection_get_by_type_addr_port_purpose(
- CONN_TYPE_DIR, &ap->addr, ap->port, DIR_PURPOSE_FETCH_MICRODESC))) {
- return 1;
- }
-
- return 0;
-}
-
-/* Check if we already have a directory fetch from the ipv4 or ipv6
- * router, for serverdesc (including extrainfo) or microdesc documents.
- * If so, return 1, if not, return 0.
- */
-static int
-router_is_already_dir_fetching_(uint32_t ipv4_addr,
- const tor_addr_t *ipv6_addr,
- uint16_t dir_port,
- int serverdesc,
- int microdesc)
-{
- tor_addr_port_t ipv4_dir_ap, ipv6_dir_ap;
-
- /* Assume IPv6 DirPort is the same as IPv4 DirPort */
- tor_addr_from_ipv4h(&ipv4_dir_ap.addr, ipv4_addr);
- ipv4_dir_ap.port = dir_port;
- tor_addr_copy(&ipv6_dir_ap.addr, ipv6_addr);
- ipv6_dir_ap.port = dir_port;
-
- return (router_is_already_dir_fetching(&ipv4_dir_ap, serverdesc, microdesc)
- || router_is_already_dir_fetching(&ipv6_dir_ap, serverdesc, microdesc));
-}
-
-#ifndef LOG_FALSE_POSITIVES_DURING_BOOTSTRAP
-#define LOG_FALSE_POSITIVES_DURING_BOOTSTRAP 0
-#endif
-
-/* Log a message if rs is not found or not a preferred address */
-static void
-router_picked_poor_directory_log(const routerstatus_t *rs)
-{
- const networkstatus_t *usable_consensus;
- usable_consensus = networkstatus_get_reasonably_live_consensus(time(NULL),
- usable_consensus_flavor());
-
-#if !LOG_FALSE_POSITIVES_DURING_BOOTSTRAP
- /* Don't log early in the bootstrap process, it's normal to pick from a
- * small pool of nodes. Of course, this won't help if we're trying to
- * diagnose bootstrap issues. */
- if (!smartlist_len(nodelist_get_list()) || !usable_consensus
- || !router_have_minimum_dir_info()) {
- return;
- }
-#endif /* !LOG_FALSE_POSITIVES_DURING_BOOTSTRAP */
-
- /* We couldn't find a node, or the one we have doesn't fit our preferences.
- * Sometimes this is normal, sometimes it can be a reachability issue. */
- if (!rs) {
- /* This happens a lot, so it's at debug level */
- log_debug(LD_DIR, "Wanted to make an outgoing directory connection, but "
- "we couldn't find a directory that fit our criteria. "
- "Perhaps we will succeed next time with less strict criteria.");
- } else if (!fascist_firewall_allows_rs(rs, FIREWALL_OR_CONNECTION, 1)
- && !fascist_firewall_allows_rs(rs, FIREWALL_DIR_CONNECTION, 1)
- ) {
- /* This is rare, and might be interesting to users trying to diagnose
- * connection issues on dual-stack machines. */
- log_info(LD_DIR, "Selected a directory %s with non-preferred OR and Dir "
- "addresses for launching an outgoing connection: "
- "IPv4 %s OR %d Dir %d IPv6 %s OR %d Dir %d",
- routerstatus_describe(rs),
- fmt_addr32(rs->addr), rs->or_port,
- rs->dir_port, fmt_addr(&rs->ipv6_addr),
- rs->ipv6_orport, rs->dir_port);
- }
-}
-
-#undef LOG_FALSE_POSITIVES_DURING_BOOTSTRAP
-
-/** How long do we avoid using a directory server after it's given us a 503? */
-#define DIR_503_TIMEOUT (60*60)
-
-/* Common retry code for router_pick_directory_server_impl and
- * router_pick_trusteddirserver_impl. Retry with the non-preferred IP version.
- * Must be called before RETRY_WITHOUT_EXCLUDE().
- *
- * If we got no result, and we are applying IP preferences, and we are a
- * client that could use an alternate IP version, try again with the
- * opposite preferences. */
-#define RETRY_ALTERNATE_IP_VERSION(retry_label) \
- STMT_BEGIN \
- if (result == NULL && try_ip_pref && options->ClientUseIPv4 \
- && fascist_firewall_use_ipv6(options) && !server_mode(options) \
- && !n_busy) { \
- n_excluded = 0; \
- n_busy = 0; \
- try_ip_pref = 0; \
- goto retry_label; \
- } \
- STMT_END \
-
-/* Common retry code for router_pick_directory_server_impl and
- * router_pick_trusteddirserver_impl. Retry without excluding nodes, but with
- * the preferred IP version. Must be called after RETRY_ALTERNATE_IP_VERSION().
- *
- * If we got no result, and we are excluding nodes, and StrictNodes is
- * not set, try again without excluding nodes. */
-#define RETRY_WITHOUT_EXCLUDE(retry_label) \
- STMT_BEGIN \
- if (result == NULL && try_excluding && !options->StrictNodes \
- && n_excluded && !n_busy) { \
- try_excluding = 0; \
- n_excluded = 0; \
- n_busy = 0; \
- try_ip_pref = 1; \
- goto retry_label; \
- } \
- STMT_END
-
-/* Common code used in the loop within router_pick_directory_server_impl and
- * router_pick_trusteddirserver_impl.
- *
- * Check if the given <b>identity</b> supports extrainfo. If not, skip further
- * checks.
- */
-#define SKIP_MISSING_TRUSTED_EXTRAINFO(type, identity) \
- STMT_BEGIN \
- int is_trusted_extrainfo = router_digest_is_trusted_dir_type( \
- (identity), EXTRAINFO_DIRINFO); \
- if (((type) & EXTRAINFO_DIRINFO) && \
- !router_supports_extrainfo((identity), is_trusted_extrainfo)) \
- continue; \
- STMT_END
-
/* When iterating through the routerlist, can OR address/port preference
* and reachability checks be skipped?
*/
@@ -2002,7 +475,7 @@ router_skip_or_reachability(const or_options_t *options, int try_ip_pref)
/* When iterating through the routerlist, can Dir address/port preference
* and reachability checks be skipped?
*/
-static int
+int
router_skip_dir_reachability(const or_options_t *options, int try_ip_pref)
{
/* Servers always have and prefer IPv4.
@@ -2011,307 +484,6 @@ router_skip_dir_reachability(const or_options_t *options, int try_ip_pref)
return server_mode(options) || (!try_ip_pref && !firewall_is_fascist_dir());
}
-/** Pick a random running valid directory server/mirror from our
- * routerlist. Arguments are as for router_pick_directory_server(), except:
- *
- * If <b>n_busy_out</b> is provided, set *<b>n_busy_out</b> to the number of
- * directories that we excluded for no other reason than
- * PDS_NO_EXISTING_SERVERDESC_FETCH or PDS_NO_EXISTING_MICRODESC_FETCH.
- */
-STATIC const routerstatus_t *
-router_pick_directory_server_impl(dirinfo_type_t type, int flags,
- int *n_busy_out)
-{
- const or_options_t *options = get_options();
- const node_t *result;
- smartlist_t *direct, *tunnel;
- smartlist_t *trusted_direct, *trusted_tunnel;
- smartlist_t *overloaded_direct, *overloaded_tunnel;
- time_t now = time(NULL);
- const networkstatus_t *consensus = networkstatus_get_latest_consensus();
- const int requireother = ! (flags & PDS_ALLOW_SELF);
- const int fascistfirewall = ! (flags & PDS_IGNORE_FASCISTFIREWALL);
- const int no_serverdesc_fetching =(flags & PDS_NO_EXISTING_SERVERDESC_FETCH);
- const int no_microdesc_fetching = (flags & PDS_NO_EXISTING_MICRODESC_FETCH);
- int try_excluding = 1, n_excluded = 0, n_busy = 0;
- int try_ip_pref = 1;
-
- if (!consensus)
- return NULL;
-
- retry_search:
-
- direct = smartlist_new();
- tunnel = smartlist_new();
- trusted_direct = smartlist_new();
- trusted_tunnel = smartlist_new();
- overloaded_direct = smartlist_new();
- overloaded_tunnel = smartlist_new();
-
- const int skip_or_fw = router_skip_or_reachability(options, try_ip_pref);
- const int skip_dir_fw = router_skip_dir_reachability(options, try_ip_pref);
- const int must_have_or = directory_must_use_begindir(options);
-
- /* Find all the running dirservers we know about. */
- SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) {
- int is_trusted;
- int is_overloaded;
- const routerstatus_t *status = node->rs;
- const country_t country = node->country;
- if (!status)
- continue;
-
- if (!node->is_running || !node_is_dir(node) || !node->is_valid)
- continue;
- if (requireother && router_digest_is_me(node->identity))
- continue;
-
- SKIP_MISSING_TRUSTED_EXTRAINFO(type, node->identity);
-
- if (try_excluding &&
- routerset_contains_routerstatus(options->ExcludeNodes, status,
- country)) {
- ++n_excluded;
- continue;
- }
-
- if (router_is_already_dir_fetching_(status->addr,
- &status->ipv6_addr,
- status->dir_port,
- no_serverdesc_fetching,
- no_microdesc_fetching)) {
- ++n_busy;
- continue;
- }
-
- is_overloaded = status->last_dir_503_at + DIR_503_TIMEOUT > now;
- is_trusted = router_digest_is_trusted_dir(node->identity);
-
- /* Clients use IPv6 addresses if the server has one and the client
- * prefers IPv6.
- * Add the router if its preferred address and port are reachable.
- * If we don't get any routers, we'll try again with the non-preferred
- * address for each router (if any). (To ensure correct load-balancing
- * we try routers that only have one address both times.)
- */
- if (!fascistfirewall || skip_or_fw ||
- fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION,
- try_ip_pref))
- smartlist_add(is_trusted ? trusted_tunnel :
- is_overloaded ? overloaded_tunnel : tunnel, (void*)node);
- else if (!must_have_or && (skip_dir_fw ||
- fascist_firewall_allows_node(node, FIREWALL_DIR_CONNECTION,
- try_ip_pref)))
- smartlist_add(is_trusted ? trusted_direct :
- is_overloaded ? overloaded_direct : direct, (void*)node);
- } SMARTLIST_FOREACH_END(node);
-
- if (smartlist_len(tunnel)) {
- result = node_sl_choose_by_bandwidth(tunnel, WEIGHT_FOR_DIR);
- } else if (smartlist_len(overloaded_tunnel)) {
- result = node_sl_choose_by_bandwidth(overloaded_tunnel,
- WEIGHT_FOR_DIR);
- } else if (smartlist_len(trusted_tunnel)) {
- /* FFFF We don't distinguish between trusteds and overloaded trusteds
- * yet. Maybe one day we should. */
- /* FFFF We also don't load balance over authorities yet. I think this
- * is a feature, but it could easily be a bug. -RD */
- result = smartlist_choose(trusted_tunnel);
- } else if (smartlist_len(direct)) {
- result = node_sl_choose_by_bandwidth(direct, WEIGHT_FOR_DIR);
- } else if (smartlist_len(overloaded_direct)) {
- result = node_sl_choose_by_bandwidth(overloaded_direct,
- WEIGHT_FOR_DIR);
- } else {
- result = smartlist_choose(trusted_direct);
- }
- smartlist_free(direct);
- smartlist_free(tunnel);
- smartlist_free(trusted_direct);
- smartlist_free(trusted_tunnel);
- smartlist_free(overloaded_direct);
- smartlist_free(overloaded_tunnel);
-
- RETRY_ALTERNATE_IP_VERSION(retry_search);
-
- RETRY_WITHOUT_EXCLUDE(retry_search);
-
- if (n_busy_out)
- *n_busy_out = n_busy;
-
- router_picked_poor_directory_log(result ? result->rs : NULL);
-
- return result ? result->rs : NULL;
-}
-
-/** Pick a random element from a list of dir_server_t, weighting by their
- * <b>weight</b> field. */
-static const dir_server_t *
-dirserver_choose_by_weight(const smartlist_t *servers, double authority_weight)
-{
- int n = smartlist_len(servers);
- int i;
- double *weights_dbl;
- uint64_t *weights_u64;
- const dir_server_t *ds;
-
- weights_dbl = tor_calloc(n, sizeof(double));
- weights_u64 = tor_calloc(n, sizeof(uint64_t));
- for (i = 0; i < n; ++i) {
- ds = smartlist_get(servers, i);
- weights_dbl[i] = ds->weight;
- if (ds->is_authority)
- weights_dbl[i] *= authority_weight;
- }
-
- scale_array_elements_to_u64(weights_u64, weights_dbl, n, NULL);
- i = choose_array_element_by_weight(weights_u64, n);
- tor_free(weights_dbl);
- tor_free(weights_u64);
- return (i < 0) ? NULL : smartlist_get(servers, i);
-}
-
-/** Choose randomly from among the dir_server_ts in sourcelist that
- * are up. Flags are as for router_pick_directory_server_impl().
- */
-static const routerstatus_t *
-router_pick_trusteddirserver_impl(const smartlist_t *sourcelist,
- dirinfo_type_t type, int flags,
- int *n_busy_out)
-{
- const or_options_t *options = get_options();
- smartlist_t *direct, *tunnel;
- smartlist_t *overloaded_direct, *overloaded_tunnel;
- const routerinfo_t *me = router_get_my_routerinfo();
- const routerstatus_t *result = NULL;
- time_t now = time(NULL);
- const int requireother = ! (flags & PDS_ALLOW_SELF);
- const int fascistfirewall = ! (flags & PDS_IGNORE_FASCISTFIREWALL);
- const int no_serverdesc_fetching =(flags & PDS_NO_EXISTING_SERVERDESC_FETCH);
- const int no_microdesc_fetching =(flags & PDS_NO_EXISTING_MICRODESC_FETCH);
- const double auth_weight = (sourcelist == fallback_dir_servers) ?
- options->DirAuthorityFallbackRate : 1.0;
- smartlist_t *pick_from;
- int n_busy = 0;
- int try_excluding = 1, n_excluded = 0;
- int try_ip_pref = 1;
-
- if (!sourcelist)
- return NULL;
-
- retry_search:
-
- direct = smartlist_new();
- tunnel = smartlist_new();
- overloaded_direct = smartlist_new();
- overloaded_tunnel = smartlist_new();
-
- const int skip_or_fw = router_skip_or_reachability(options, try_ip_pref);
- const int skip_dir_fw = router_skip_dir_reachability(options, try_ip_pref);
- const int must_have_or = directory_must_use_begindir(options);
-
- SMARTLIST_FOREACH_BEGIN(sourcelist, const dir_server_t *, d)
- {
- int is_overloaded =
- d->fake_status.last_dir_503_at + DIR_503_TIMEOUT > now;
- if (!d->is_running) continue;
- if ((type & d->type) == 0)
- continue;
-
- SKIP_MISSING_TRUSTED_EXTRAINFO(type, d->digest);
-
- if (requireother && me && router_digest_is_me(d->digest))
- continue;
- if (try_excluding &&
- routerset_contains_routerstatus(options->ExcludeNodes,
- &d->fake_status, -1)) {
- ++n_excluded;
- continue;
- }
-
- if (router_is_already_dir_fetching_(d->addr,
- &d->ipv6_addr,
- d->dir_port,
- no_serverdesc_fetching,
- no_microdesc_fetching)) {
- ++n_busy;
- continue;
- }
-
- /* Clients use IPv6 addresses if the server has one and the client
- * prefers IPv6.
- * Add the router if its preferred address and port are reachable.
- * If we don't get any routers, we'll try again with the non-preferred
- * address for each router (if any). (To ensure correct load-balancing
- * we try routers that only have one address both times.)
- */
- if (!fascistfirewall || skip_or_fw ||
- fascist_firewall_allows_dir_server(d, FIREWALL_OR_CONNECTION,
- try_ip_pref))
- smartlist_add(is_overloaded ? overloaded_tunnel : tunnel, (void*)d);
- else if (!must_have_or && (skip_dir_fw ||
- fascist_firewall_allows_dir_server(d, FIREWALL_DIR_CONNECTION,
- try_ip_pref)))
- smartlist_add(is_overloaded ? overloaded_direct : direct, (void*)d);
- }
- SMARTLIST_FOREACH_END(d);
-
- if (smartlist_len(tunnel)) {
- pick_from = tunnel;
- } else if (smartlist_len(overloaded_tunnel)) {
- pick_from = overloaded_tunnel;
- } else if (smartlist_len(direct)) {
- pick_from = direct;
- } else {
- pick_from = overloaded_direct;
- }
-
- {
- const dir_server_t *selection =
- dirserver_choose_by_weight(pick_from, auth_weight);
-
- if (selection)
- result = &selection->fake_status;
- }
-
- smartlist_free(direct);
- smartlist_free(tunnel);
- smartlist_free(overloaded_direct);
- smartlist_free(overloaded_tunnel);
-
- RETRY_ALTERNATE_IP_VERSION(retry_search);
-
- RETRY_WITHOUT_EXCLUDE(retry_search);
-
- router_picked_poor_directory_log(result);
-
- if (n_busy_out)
- *n_busy_out = n_busy;
- return result;
-}
-
-/** Mark as running every dir_server_t in <b>server_list</b>. */
-static void
-mark_all_dirservers_up(smartlist_t *server_list)
-{
- if (server_list) {
- SMARTLIST_FOREACH_BEGIN(server_list, dir_server_t *, dir) {
- routerstatus_t *rs;
- node_t *node;
- dir->is_running = 1;
- node = node_get_mutable_by_id(dir->digest);
- if (node)
- node->is_running = 1;
- rs = router_get_mutable_consensus_status_by_id(dir->digest);
- if (rs) {
- rs->last_dir_503_at = 0;
- control_event_networkstatus_changed_single(rs);
- }
- } SMARTLIST_FOREACH_END(dir);
- }
- router_dir_info_changed();
-}
-
/** Return true iff r1 and r2 have the same address and OR port. */
int
routers_have_same_or_addrs(const routerinfo_t *r1, const routerinfo_t *r2)
@@ -2321,35 +493,6 @@ routers_have_same_or_addrs(const routerinfo_t *r1, const routerinfo_t *r2)
r1->ipv6_orport == r2->ipv6_orport;
}
-/** Reset all internal variables used to count failed downloads of network
- * status objects. */
-void
-router_reset_status_download_failures(void)
-{
- mark_all_dirservers_up(fallback_dir_servers);
-}
-
-/** Given a <b>router</b>, add every node_t in its family (including the
- * node itself!) to <b>sl</b>.
- *
- * Note the type mismatch: This function takes a routerinfo, but adds nodes
- * to the smartlist!
- */
-static void
-routerlist_add_node_and_family(smartlist_t *sl, const routerinfo_t *router)
-{
- /* XXXX MOVE ? */
- node_t fake_node;
- const node_t *node = node_get_by_id(router->cache_info.identity_digest);
- if (node == NULL) {
- memset(&fake_node, 0, sizeof(fake_node));
- fake_node.ri = (routerinfo_t *)router;
- memcpy(fake_node.identity, router->cache_info.identity_digest, DIGEST_LEN);
- node = &fake_node;
- }
- nodelist_add_node_and_family(sl, node);
-}
-
/** Add every suitable node from our nodelist to <b>sl</b>, so that
* we can pick a node for a circuit.
*/
@@ -2431,511 +574,6 @@ router_get_advertised_bandwidth_capped(const routerinfo_t *router)
return result;
}
-/** Given an array of double/uint64_t unions that are currently being used as
- * doubles, convert them to uint64_t, and try to scale them linearly so as to
- * much of the range of uint64_t. If <b>total_out</b> is provided, set it to
- * the sum of all elements in the array _before_ scaling. */
-STATIC void
-scale_array_elements_to_u64(uint64_t *entries_out, const double *entries_in,
- int n_entries,
- uint64_t *total_out)
-{
- double total = 0.0;
- double scale_factor = 0.0;
- int i;
-
- for (i = 0; i < n_entries; ++i)
- total += entries_in[i];
-
- if (total > 0.0) {
- scale_factor = ((double)INT64_MAX) / total;
- scale_factor /= 4.0; /* make sure we're very far away from overflowing */
- }
-
- for (i = 0; i < n_entries; ++i)
- entries_out[i] = tor_llround(entries_in[i] * scale_factor);
-
- if (total_out)
- *total_out = (uint64_t) total;
-}
-
-/** Pick a random element of <b>n_entries</b>-element array <b>entries</b>,
- * choosing each element with a probability proportional to its (uint64_t)
- * value, and return the index of that element. If all elements are 0, choose
- * an index at random. Return -1 on error.
- */
-STATIC int
-choose_array_element_by_weight(const uint64_t *entries, int n_entries)
-{
- int i;
- uint64_t rand_val;
- uint64_t total = 0;
-
- for (i = 0; i < n_entries; ++i)
- total += entries[i];
-
- if (n_entries < 1)
- return -1;
-
- if (total == 0)
- return crypto_rand_int(n_entries);
-
- tor_assert(total < INT64_MAX);
-
- rand_val = crypto_rand_uint64(total);
-
- return select_array_member_cumulative_timei(
- entries, n_entries, total, rand_val);
-}
-
-/** When weighting bridges, enforce these values as lower and upper
- * bound for believable bandwidth, because there is no way for us
- * to verify a bridge's bandwidth currently. */
-#define BRIDGE_MIN_BELIEVABLE_BANDWIDTH 20000 /* 20 kB/sec */
-#define BRIDGE_MAX_BELIEVABLE_BANDWIDTH 100000 /* 100 kB/sec */
-
-/** Return the smaller of the router's configured BandwidthRate
- * and its advertised capacity, making sure to stay within the
- * interval between bridge-min-believe-bw and
- * bridge-max-believe-bw. */
-static uint32_t
-bridge_get_advertised_bandwidth_bounded(routerinfo_t *router)
-{
- uint32_t result = router->bandwidthcapacity;
- if (result > router->bandwidthrate)
- result = router->bandwidthrate;
- if (result > BRIDGE_MAX_BELIEVABLE_BANDWIDTH)
- result = BRIDGE_MAX_BELIEVABLE_BANDWIDTH;
- else if (result < BRIDGE_MIN_BELIEVABLE_BANDWIDTH)
- result = BRIDGE_MIN_BELIEVABLE_BANDWIDTH;
- return result;
-}
-
-/** Return bw*1000, unless bw*1000 would overflow, in which case return
- * INT32_MAX. */
-static inline int32_t
-kb_to_bytes(uint32_t bw)
-{
- return (bw > (INT32_MAX/1000)) ? INT32_MAX : bw*1000;
-}
-
-/** Helper function:
- * choose a random element of smartlist <b>sl</b> of nodes, weighted by
- * the advertised bandwidth of each element using the consensus
- * bandwidth weights.
- *
- * If <b>rule</b>==WEIGHT_FOR_EXIT. we're picking an exit node: consider all
- * nodes' bandwidth equally regardless of their Exit status, since there may
- * be some in the list because they exit to obscure ports. If
- * <b>rule</b>==NO_WEIGHTING, we're picking a non-exit node: weight
- * exit-node's bandwidth less depending on the smallness of the fraction of
- * Exit-to-total bandwidth. If <b>rule</b>==WEIGHT_FOR_GUARD, we're picking a
- * guard node: consider all guard's bandwidth equally. Otherwise, weight
- * guards proportionally less.
- */
-static const node_t *
-smartlist_choose_node_by_bandwidth_weights(const smartlist_t *sl,
- bandwidth_weight_rule_t rule)
-{
- double *bandwidths_dbl=NULL;
- uint64_t *bandwidths_u64=NULL;
-
- if (compute_weighted_bandwidths(sl, rule, &bandwidths_dbl, NULL) < 0)
- return NULL;
-
- bandwidths_u64 = tor_calloc(smartlist_len(sl), sizeof(uint64_t));
- scale_array_elements_to_u64(bandwidths_u64, bandwidths_dbl,
- smartlist_len(sl), NULL);
-
- {
- int idx = choose_array_element_by_weight(bandwidths_u64,
- smartlist_len(sl));
- tor_free(bandwidths_dbl);
- tor_free(bandwidths_u64);
- return idx < 0 ? NULL : smartlist_get(sl, idx);
- }
-}
-
-/** Given a list of routers and a weighting rule as in
- * smartlist_choose_node_by_bandwidth_weights, compute weighted bandwidth
- * values for each node and store them in a freshly allocated
- * *<b>bandwidths_out</b> of the same length as <b>sl</b>, and holding results
- * as doubles. If <b>total_bandwidth_out</b> is non-NULL, set it to the total
- * of all the bandwidths.
- * Return 0 on success, -1 on failure. */
-static int
-compute_weighted_bandwidths(const smartlist_t *sl,
- bandwidth_weight_rule_t rule,
- double **bandwidths_out,
- double *total_bandwidth_out)
-{
- int64_t weight_scale;
- double Wg = -1, Wm = -1, We = -1, Wd = -1;
- double Wgb = -1, Wmb = -1, Web = -1, Wdb = -1;
- guardfraction_bandwidth_t guardfraction_bw;
- double *bandwidths = NULL;
- double total_bandwidth = 0.0;
-
- tor_assert(sl);
- tor_assert(bandwidths_out);
-
- /* Can't choose exit and guard at same time */
- tor_assert(rule == NO_WEIGHTING ||
- rule == WEIGHT_FOR_EXIT ||
- rule == WEIGHT_FOR_GUARD ||
- rule == WEIGHT_FOR_MID ||
- rule == WEIGHT_FOR_DIR);
-
- *bandwidths_out = NULL;
-
- if (total_bandwidth_out) {
- *total_bandwidth_out = 0.0;
- }
-
- if (smartlist_len(sl) == 0) {
- log_info(LD_CIRC,
- "Empty routerlist passed in to consensus weight node "
- "selection for rule %s",
- bandwidth_weight_rule_to_string(rule));
- return -1;
- }
-
- weight_scale = networkstatus_get_weight_scale_param(NULL);
-
- if (rule == WEIGHT_FOR_GUARD) {
- Wg = networkstatus_get_bw_weight(NULL, "Wgg", -1);
- Wm = networkstatus_get_bw_weight(NULL, "Wgm", -1); /* Bridges */
- We = 0;
- Wd = networkstatus_get_bw_weight(NULL, "Wgd", -1);
-
- Wgb = networkstatus_get_bw_weight(NULL, "Wgb", -1);
- Wmb = networkstatus_get_bw_weight(NULL, "Wmb", -1);
- Web = networkstatus_get_bw_weight(NULL, "Web", -1);
- Wdb = networkstatus_get_bw_weight(NULL, "Wdb", -1);
- } else if (rule == WEIGHT_FOR_MID) {
- Wg = networkstatus_get_bw_weight(NULL, "Wmg", -1);
- Wm = networkstatus_get_bw_weight(NULL, "Wmm", -1);
- We = networkstatus_get_bw_weight(NULL, "Wme", -1);
- Wd = networkstatus_get_bw_weight(NULL, "Wmd", -1);
-
- Wgb = networkstatus_get_bw_weight(NULL, "Wgb", -1);
- Wmb = networkstatus_get_bw_weight(NULL, "Wmb", -1);
- Web = networkstatus_get_bw_weight(NULL, "Web", -1);
- Wdb = networkstatus_get_bw_weight(NULL, "Wdb", -1);
- } else if (rule == WEIGHT_FOR_EXIT) {
- // Guards CAN be exits if they have weird exit policies
- // They are d then I guess...
- We = networkstatus_get_bw_weight(NULL, "Wee", -1);
- Wm = networkstatus_get_bw_weight(NULL, "Wem", -1); /* Odd exit policies */
- Wd = networkstatus_get_bw_weight(NULL, "Wed", -1);
- Wg = networkstatus_get_bw_weight(NULL, "Weg", -1); /* Odd exit policies */
-
- Wgb = networkstatus_get_bw_weight(NULL, "Wgb", -1);
- Wmb = networkstatus_get_bw_weight(NULL, "Wmb", -1);
- Web = networkstatus_get_bw_weight(NULL, "Web", -1);
- Wdb = networkstatus_get_bw_weight(NULL, "Wdb", -1);
- } else if (rule == WEIGHT_FOR_DIR) {
- We = networkstatus_get_bw_weight(NULL, "Wbe", -1);
- Wm = networkstatus_get_bw_weight(NULL, "Wbm", -1);
- Wd = networkstatus_get_bw_weight(NULL, "Wbd", -1);
- Wg = networkstatus_get_bw_weight(NULL, "Wbg", -1);
-
- Wgb = Wmb = Web = Wdb = weight_scale;
- } else if (rule == NO_WEIGHTING) {
- Wg = Wm = We = Wd = weight_scale;
- Wgb = Wmb = Web = Wdb = weight_scale;
- }
-
- if (Wg < 0 || Wm < 0 || We < 0 || Wd < 0 || Wgb < 0 || Wmb < 0 || Wdb < 0
- || Web < 0) {
- log_debug(LD_CIRC,
- "Got negative bandwidth weights. Defaulting to naive selection"
- " algorithm.");
- Wg = Wm = We = Wd = weight_scale;
- Wgb = Wmb = Web = Wdb = weight_scale;
- }
-
- Wg /= weight_scale;
- Wm /= weight_scale;
- We /= weight_scale;
- Wd /= weight_scale;
-
- Wgb /= weight_scale;
- Wmb /= weight_scale;
- Web /= weight_scale;
- Wdb /= weight_scale;
-
- bandwidths = tor_calloc(smartlist_len(sl), sizeof(double));
-
- // Cycle through smartlist and total the bandwidth.
- static int warned_missing_bw = 0;
- SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) {
- int is_exit = 0, is_guard = 0, is_dir = 0, this_bw = 0;
- double weight = 1;
- double weight_without_guard_flag = 0; /* Used for guardfraction */
- double final_weight = 0;
- is_exit = node->is_exit && ! node->is_bad_exit;
- is_guard = node->is_possible_guard;
- is_dir = node_is_dir(node);
- if (node->rs) {
- if (!node->rs->has_bandwidth) {
- /* This should never happen, unless all the authorities downgrade
- * to 0.2.0 or rogue routerstatuses get inserted into our consensus. */
- if (! warned_missing_bw) {
- log_warn(LD_BUG,
- "Consensus is missing some bandwidths. Using a naive "
- "router selection algorithm");
- warned_missing_bw = 1;
- }
- this_bw = 30000; /* Chosen arbitrarily */
- } else {
- this_bw = kb_to_bytes(node->rs->bandwidth_kb);
- }
- } else if (node->ri) {
- /* bridge or other descriptor not in our consensus */
- this_bw = bridge_get_advertised_bandwidth_bounded(node->ri);
- } else {
- /* We can't use this one. */
- continue;
- }
-
- if (is_guard && is_exit) {
- weight = (is_dir ? Wdb*Wd : Wd);
- weight_without_guard_flag = (is_dir ? Web*We : We);
- } else if (is_guard) {
- weight = (is_dir ? Wgb*Wg : Wg);
- weight_without_guard_flag = (is_dir ? Wmb*Wm : Wm);
- } else if (is_exit) {
- weight = (is_dir ? Web*We : We);
- } else { // middle
- weight = (is_dir ? Wmb*Wm : Wm);
- }
- /* These should be impossible; but overflows here would be bad, so let's
- * make sure. */
- if (this_bw < 0)
- this_bw = 0;
- if (weight < 0.0)
- weight = 0.0;
- if (weight_without_guard_flag < 0.0)
- weight_without_guard_flag = 0.0;
-
- /* If guardfraction information is available in the consensus, we
- * want to calculate this router's bandwidth according to its
- * guardfraction. Quoting from proposal236:
- *
- * Let Wpf denote the weight from the 'bandwidth-weights' line a
- * client would apply to N for position p if it had the guard
- * flag, Wpn the weight if it did not have the guard flag, and B the
- * measured bandwidth of N in the consensus. Then instead of choosing
- * N for position p proportionally to Wpf*B or Wpn*B, clients should
- * choose N proportionally to F*Wpf*B + (1-F)*Wpn*B.
- */
- if (node->rs && node->rs->has_guardfraction && rule != WEIGHT_FOR_GUARD) {
- /* XXX The assert should actually check for is_guard. However,
- * that crashes dirauths because of #13297. This should be
- * equivalent: */
- tor_assert(node->rs->is_possible_guard);
-
- guard_get_guardfraction_bandwidth(&guardfraction_bw,
- this_bw,
- node->rs->guardfraction_percentage);
-
- /* Calculate final_weight = F*Wpf*B + (1-F)*Wpn*B */
- final_weight =
- guardfraction_bw.guard_bw * weight +
- guardfraction_bw.non_guard_bw * weight_without_guard_flag;
-
- log_debug(LD_GENERAL, "%s: Guardfraction weight %f instead of %f (%s)",
- node->rs->nickname, final_weight, weight*this_bw,
- bandwidth_weight_rule_to_string(rule));
- } else { /* no guardfraction information. calculate the weight normally. */
- final_weight = weight*this_bw;
- }
-
- bandwidths[node_sl_idx] = final_weight;
- total_bandwidth += final_weight;
- } SMARTLIST_FOREACH_END(node);
-
- log_debug(LD_CIRC, "Generated weighted bandwidths for rule %s based "
- "on weights "
- "Wg=%f Wm=%f We=%f Wd=%f with total bw %f",
- bandwidth_weight_rule_to_string(rule),
- Wg, Wm, We, Wd, total_bandwidth);
-
- *bandwidths_out = bandwidths;
-
- if (total_bandwidth_out) {
- *total_bandwidth_out = total_bandwidth;
- }
-
- return 0;
-}
-
-/** For all nodes in <b>sl</b>, return the fraction of those nodes, weighted
- * by their weighted bandwidths with rule <b>rule</b>, for which we have
- * descriptors.
- *
- * If <b>for_direct_connect</b> is true, we intend to connect to the node
- * directly, as the first hop of a circuit; otherwise, we intend to connect
- * to it indirectly, or use it as if we were connecting to it indirectly. */
-double
-frac_nodes_with_descriptors(const smartlist_t *sl,
- bandwidth_weight_rule_t rule,
- int for_direct_conn)
-{
- double *bandwidths = NULL;
- double total, present;
-
- if (smartlist_len(sl) == 0)
- return 0.0;
-
- if (compute_weighted_bandwidths(sl, rule, &bandwidths, &total) < 0 ||
- total <= 0.0) {
- int n_with_descs = 0;
- SMARTLIST_FOREACH(sl, const node_t *, node, {
- if (node_has_preferred_descriptor(node, for_direct_conn))
- n_with_descs++;
- });
- tor_free(bandwidths);
- return ((double)n_with_descs) / smartlist_len(sl);
- }
-
- present = 0.0;
- SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) {
- if (node_has_preferred_descriptor(node, for_direct_conn))
- present += bandwidths[node_sl_idx];
- } SMARTLIST_FOREACH_END(node);
-
- tor_free(bandwidths);
-
- return present / total;
-}
-
-/** Choose a random element of status list <b>sl</b>, weighted by
- * the advertised bandwidth of each node */
-const node_t *
-node_sl_choose_by_bandwidth(const smartlist_t *sl,
- bandwidth_weight_rule_t rule)
-{ /*XXXX MOVE */
- return smartlist_choose_node_by_bandwidth_weights(sl, rule);
-}
-
-/** Return a random running node from the nodelist. Never
- * pick a node that is in
- * <b>excludedsmartlist</b>, or which matches <b>excludedset</b>,
- * even if they are the only nodes available.
- * If <b>CRN_NEED_UPTIME</b> is set in flags and any router has more than
- * a minimum uptime, return one of those.
- * If <b>CRN_NEED_CAPACITY</b> is set in flags, weight your choice by the
- * advertised capacity of each router.
- * If <b>CRN_NEED_GUARD</b> is set in flags, consider only Guard routers.
- * If <b>CRN_WEIGHT_AS_EXIT</b> is set in flags, we weight bandwidths as if
- * picking an exit node, otherwise we weight bandwidths for picking a relay
- * node (that is, possibly discounting exit nodes).
- * If <b>CRN_NEED_DESC</b> is set in flags, we only consider nodes that
- * have a routerinfo or microdescriptor -- that is, enough info to be
- * used to build a circuit.
- * If <b>CRN_PREF_ADDR</b> is set in flags, we only consider nodes that
- * have an address that is preferred by the ClientPreferIPv6ORPort setting
- * (regardless of this flag, we exclude nodes that aren't allowed by the
- * firewall, including ClientUseIPv4 0 and fascist_firewall_use_ipv6() == 0).
- */
-const node_t *
-router_choose_random_node(smartlist_t *excludedsmartlist,
- routerset_t *excludedset,
- router_crn_flags_t flags)
-{ /* XXXX MOVE */
- const int need_uptime = (flags & CRN_NEED_UPTIME) != 0;
- const int need_capacity = (flags & CRN_NEED_CAPACITY) != 0;
- const int need_guard = (flags & CRN_NEED_GUARD) != 0;
- const int weight_for_exit = (flags & CRN_WEIGHT_AS_EXIT) != 0;
- const int need_desc = (flags & CRN_NEED_DESC) != 0;
- const int pref_addr = (flags & CRN_PREF_ADDR) != 0;
- const int direct_conn = (flags & CRN_DIRECT_CONN) != 0;
- const int rendezvous_v3 = (flags & CRN_RENDEZVOUS_V3) != 0;
-
- smartlist_t *sl=smartlist_new(),
- *excludednodes=smartlist_new();
- const node_t *choice = NULL;
- const routerinfo_t *r;
- bandwidth_weight_rule_t rule;
-
- tor_assert(!(weight_for_exit && need_guard));
- rule = weight_for_exit ? WEIGHT_FOR_EXIT :
- (need_guard ? WEIGHT_FOR_GUARD : WEIGHT_FOR_MID);
-
- SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) {
- if (node_allows_single_hop_exits(node)) {
- /* Exclude relays that allow single hop exit circuits. This is an
- * obsolete option since 0.2.9.2-alpha and done by default in
- * 0.3.1.0-alpha. */
- smartlist_add(excludednodes, node);
- } else if (rendezvous_v3 &&
- !node_supports_v3_rendezvous_point(node)) {
- /* Exclude relays that do not support to rendezvous for a hidden service
- * version 3. */
- smartlist_add(excludednodes, node);
- }
- } SMARTLIST_FOREACH_END(node);
-
- /* If the node_t is not found we won't be to exclude ourself but we
- * won't be able to pick ourself in router_choose_random_node() so
- * this is fine to at least try with our routerinfo_t object. */
- if ((r = router_get_my_routerinfo()))
- routerlist_add_node_and_family(excludednodes, r);
-
- router_add_running_nodes_to_smartlist(sl, need_uptime, need_capacity,
- need_guard, need_desc, pref_addr,
- direct_conn);
- log_debug(LD_CIRC,
- "We found %d running nodes.",
- smartlist_len(sl));
-
- smartlist_subtract(sl,excludednodes);
- log_debug(LD_CIRC,
- "We removed %d excludednodes, leaving %d nodes.",
- smartlist_len(excludednodes),
- smartlist_len(sl));
-
- if (excludedsmartlist) {
- smartlist_subtract(sl,excludedsmartlist);
- log_debug(LD_CIRC,
- "We removed %d excludedsmartlist, leaving %d nodes.",
- smartlist_len(excludedsmartlist),
- smartlist_len(sl));
- }
- if (excludedset) {
- routerset_subtract_nodes(sl,excludedset);
- log_debug(LD_CIRC,
- "We removed excludedset, leaving %d nodes.",
- smartlist_len(sl));
- }
-
- // Always weight by bandwidth
- choice = node_sl_choose_by_bandwidth(sl, rule);
-
- smartlist_free(sl);
- if (!choice && (need_uptime || need_capacity || need_guard || pref_addr)) {
- /* try once more -- recurse but with fewer restrictions. */
- log_info(LD_CIRC,
- "We couldn't find any live%s%s%s routers; falling back "
- "to list of all routers.",
- need_capacity?", fast":"",
- need_uptime?", stable":"",
- need_guard?", guard":"");
- flags &= ~ (CRN_NEED_UPTIME|CRN_NEED_CAPACITY|CRN_NEED_GUARD|
- CRN_PREF_ADDR);
- choice = router_choose_random_node(
- excludedsmartlist, excludedset, flags);
- }
- smartlist_free(excludednodes);
- if (!choice) {
- log_warn(LD_CIRC,
- "No available nodes when trying to choose node. Failing.");
- }
- return choice;
-}
-
/** Helper: given an extended nickname in <b>hexdigest</b> try to decode it.
* Return 0 on success, -1 on failure. Store the result into the
* DIGEST_LEN-byte buffer at <b>digest_out</b>, the single character at
@@ -3013,23 +651,6 @@ hex_digest_nickname_matches(const char *hexdigest, const char *identity_digest,
return tor_memeq(digest, identity_digest, DIGEST_LEN);
}
-/** Return true iff <b>digest</b> is the digest of the identity key of a
- * trusted directory matching at least one bit of <b>type</b>. If <b>type</b>
- * is zero (NO_DIRINFO), or ALL_DIRINFO, any authority is okay. */
-int
-router_digest_is_trusted_dir_type(const char *digest, dirinfo_type_t type)
-{
- if (!trusted_dir_servers)
- return 0;
- if (authdir_mode(get_options()) && router_digest_is_me(digest))
- return 1;
- SMARTLIST_FOREACH(trusted_dir_servers, dir_server_t *, ent,
- if (tor_memeq(digest, ent->digest, DIGEST_LEN)) {
- return (!type) || ((type & ent->type) != 0);
- });
- return 0;
-}
-
/** If hexdigest is correctly formed, base16_decode it into
* digest, which must have DIGEST_LEN space in it.
* Return 0 on success, -1 on failure.
@@ -3803,19 +1424,13 @@ routerlist_free_all(void)
{
routerlist_free(routerlist);
routerlist = NULL;
+ dirlist_free_all();
if (warned_nicknames) {
SMARTLIST_FOREACH(warned_nicknames, char *, cp, tor_free(cp));
smartlist_free(warned_nicknames);
warned_nicknames = NULL;
}
- clear_dir_servers();
- smartlist_free(trusted_dir_servers);
- smartlist_free(fallback_dir_servers);
- trusted_dir_servers = fallback_dir_servers = NULL;
- if (trusted_dir_certs) {
- digestmap_free(trusted_dir_certs, cert_list_free_void);
- trusted_dir_certs = NULL;
- }
+ authcert_free_all();
}
/** Forget that we have issued any router-related warnings, so that we'll
@@ -4621,221 +2236,13 @@ router_exit_policy_rejects_all(const routerinfo_t *router)
return router->policy_is_reject_star;
}
-/** Create a directory server at <b>address</b>:<b>port</b>, with OR identity
- * key <b>digest</b> which has DIGEST_LEN bytes. If <b>address</b> is NULL,
- * add ourself. If <b>is_authority</b>, this is a directory authority. Return
- * the new directory server entry on success or NULL on failure. */
-static dir_server_t *
-dir_server_new(int is_authority,
- const char *nickname,
- const tor_addr_t *addr,
- const char *hostname,
- uint16_t dir_port, uint16_t or_port,
- const tor_addr_port_t *addrport_ipv6,
- const char *digest, const char *v3_auth_digest,
- dirinfo_type_t type,
- double weight)
-{
- dir_server_t *ent;
- uint32_t a;
- char *hostname_ = NULL;
-
- tor_assert(digest);
-
- if (weight < 0)
- return NULL;
-
- if (tor_addr_family(addr) == AF_INET)
- a = tor_addr_to_ipv4h(addr);
- else
- return NULL;
-
- if (!hostname)
- hostname_ = tor_addr_to_str_dup(addr);
- else
- hostname_ = tor_strdup(hostname);
-
- ent = tor_malloc_zero(sizeof(dir_server_t));
- ent->nickname = nickname ? tor_strdup(nickname) : NULL;
- ent->address = hostname_;
- ent->addr = a;
- ent->dir_port = dir_port;
- ent->or_port = or_port;
- ent->is_running = 1;
- ent->is_authority = is_authority;
- ent->type = type;
- ent->weight = weight;
- if (addrport_ipv6) {
- if (tor_addr_family(&addrport_ipv6->addr) != AF_INET6) {
- log_warn(LD_BUG, "Hey, I got a non-ipv6 addr as addrport_ipv6.");
- tor_addr_make_unspec(&ent->ipv6_addr);
- } else {
- tor_addr_copy(&ent->ipv6_addr, &addrport_ipv6->addr);
- ent->ipv6_orport = addrport_ipv6->port;
- }
- } else {
- tor_addr_make_unspec(&ent->ipv6_addr);
- }
-
- memcpy(ent->digest, digest, DIGEST_LEN);
- if (v3_auth_digest && (type & V3_DIRINFO))
- memcpy(ent->v3_identity_digest, v3_auth_digest, DIGEST_LEN);
-
- if (nickname)
- tor_asprintf(&ent->description, "directory server \"%s\" at %s:%d",
- nickname, hostname_, (int)dir_port);
- else
- tor_asprintf(&ent->description, "directory server at %s:%d",
- hostname_, (int)dir_port);
-
- ent->fake_status.addr = ent->addr;
- tor_addr_copy(&ent->fake_status.ipv6_addr, &ent->ipv6_addr);
- memcpy(ent->fake_status.identity_digest, digest, DIGEST_LEN);
- if (nickname)
- strlcpy(ent->fake_status.nickname, nickname,
- sizeof(ent->fake_status.nickname));
- else
- ent->fake_status.nickname[0] = '\0';
- ent->fake_status.dir_port = ent->dir_port;
- ent->fake_status.or_port = ent->or_port;
- ent->fake_status.ipv6_orport = ent->ipv6_orport;
-
- return ent;
-}
-
-/** Create an authoritative directory server at
- * <b>address</b>:<b>port</b>, with identity key <b>digest</b>. If
- * <b>address</b> is NULL, add ourself. Return the new trusted directory
- * server entry on success or NULL if we couldn't add it. */
-dir_server_t *
-trusted_dir_server_new(const char *nickname, const char *address,
- uint16_t dir_port, uint16_t or_port,
- const tor_addr_port_t *ipv6_addrport,
- const char *digest, const char *v3_auth_digest,
- dirinfo_type_t type, double weight)
-{
- uint32_t a;
- tor_addr_t addr;
- char *hostname=NULL;
- dir_server_t *result;
-
- if (!address) { /* The address is us; we should guess. */
- if (resolve_my_address(LOG_WARN, get_options(),
- &a, NULL, &hostname) < 0) {
- log_warn(LD_CONFIG,
- "Couldn't find a suitable address when adding ourself as a "
- "trusted directory server.");
- return NULL;
- }
- if (!hostname)
- hostname = tor_dup_ip(a);
- } else {
- if (tor_lookup_hostname(address, &a)) {
- log_warn(LD_CONFIG,
- "Unable to lookup address for directory server at '%s'",
- address);
- return NULL;
- }
- hostname = tor_strdup(address);
- }
- tor_addr_from_ipv4h(&addr, a);
-
- result = dir_server_new(1, nickname, &addr, hostname,
- dir_port, or_port,
- ipv6_addrport,
- digest,
- v3_auth_digest, type, weight);
- tor_free(hostname);
- return result;
-}
-
-/** Return a new dir_server_t for a fallback directory server at
- * <b>addr</b>:<b>or_port</b>/<b>dir_port</b>, with identity key digest
- * <b>id_digest</b> */
-dir_server_t *
-fallback_dir_server_new(const tor_addr_t *addr,
- uint16_t dir_port, uint16_t or_port,
- const tor_addr_port_t *addrport_ipv6,
- const char *id_digest, double weight)
-{
- return dir_server_new(0, NULL, addr, NULL, dir_port, or_port,
- addrport_ipv6,
- id_digest,
- NULL, ALL_DIRINFO, weight);
-}
-
-/** Add a directory server to the global list(s). */
-void
-dir_server_add(dir_server_t *ent)
-{
- if (!trusted_dir_servers)
- trusted_dir_servers = smartlist_new();
- if (!fallback_dir_servers)
- fallback_dir_servers = smartlist_new();
-
- if (ent->is_authority)
- smartlist_add(trusted_dir_servers, ent);
-
- smartlist_add(fallback_dir_servers, ent);
- router_dir_info_changed();
-}
-
-/** Free storage held in <b>cert</b>. */
-void
-authority_cert_free_(authority_cert_t *cert)
-{
- if (!cert)
- return;
-
- tor_free(cert->cache_info.signed_descriptor_body);
- crypto_pk_free(cert->signing_key);
- crypto_pk_free(cert->identity_key);
-
- tor_free(cert);
-}
-
-#define dir_server_free(val) \
- FREE_AND_NULL(dir_server_t, dir_server_free_, (val))
-
-/** Free storage held in <b>ds</b>. */
-static void
-dir_server_free_(dir_server_t *ds)
-{
- if (!ds)
- return;
-
- tor_free(ds->nickname);
- tor_free(ds->description);
- tor_free(ds->address);
- tor_free(ds);
-}
-
-/** Remove all members from the list of dir servers. */
-void
-clear_dir_servers(void)
-{
- if (fallback_dir_servers) {
- SMARTLIST_FOREACH(fallback_dir_servers, dir_server_t *, ent,
- dir_server_free(ent));
- smartlist_clear(fallback_dir_servers);
- } else {
- fallback_dir_servers = smartlist_new();
- }
- if (trusted_dir_servers) {
- smartlist_clear(trusted_dir_servers);
- } else {
- trusted_dir_servers = smartlist_new();
- }
- router_dir_info_changed();
-}
-
/** For every current directory connection whose purpose is <b>purpose</b>,
* and where the resource being downloaded begins with <b>prefix</b>, split
* rest of the resource into base16 fingerprints (or base64 fingerprints if
* purpose==DIR_PURPOSE_FETCH_MICRODESC), decode them, and set the
* corresponding elements of <b>result</b> to a nonzero value.
*/
-static void
+void
list_pending_downloads(digestmap_t *result, digest256map_t *result256,
int purpose, const char *prefix)
{
@@ -4895,41 +2302,6 @@ list_pending_microdesc_downloads(digest256map_t *result)
list_pending_downloads(NULL, result, DIR_PURPOSE_FETCH_MICRODESC, "d/");
}
-/** For every certificate we are currently downloading by (identity digest,
- * signing key digest) pair, set result[fp_pair] to (void *1).
- */
-static void
-list_pending_fpsk_downloads(fp_pair_map_t *result)
-{
- const char *pfx = "fp-sk/";
- smartlist_t *tmp;
- smartlist_t *conns;
- const char *resource;
-
- tor_assert(result);
-
- tmp = smartlist_new();
- conns = get_connection_array();
-
- SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
- if (conn->type == CONN_TYPE_DIR &&
- conn->purpose == DIR_PURPOSE_FETCH_CERTIFICATE &&
- !conn->marked_for_close) {
- resource = TO_DIR_CONN(conn)->requested_resource;
- if (!strcmpstart(resource, pfx))
- dir_split_resource_into_fingerprint_pairs(resource + strlen(pfx),
- tmp);
- }
- } SMARTLIST_FOREACH_END(conn);
-
- SMARTLIST_FOREACH_BEGIN(tmp, fp_pair_t *, fp) {
- fp_pair_map_set(result, fp, (void*)1);
- tor_free(fp);
- } SMARTLIST_FOREACH_END(fp);
-
- smartlist_free(tmp);
-}
-
/** Launch downloads for all the descriptors whose digests or digests256
* are listed as digests[i] for lo <= i < hi. (Lo and hi may be out of
* range.) If <b>source</b> is given, download from <b>source</b>;
diff --git a/src/feature/nodelist/routerlist.h b/src/feature/nodelist/routerlist.h
index 4b7406364f..c3e97d9dd2 100644
--- a/src/feature/nodelist/routerlist.h
+++ b/src/feature/nodelist/routerlist.h
@@ -42,108 +42,13 @@ typedef enum was_router_added_t {
ROUTER_CERTS_EXPIRED = -8
} was_router_added_t;
-/** Flags to be passed to control router_choose_random_node() to indicate what
- * kind of nodes to pick according to what algorithm. */
-typedef enum router_crn_flags_t {
- CRN_NEED_UPTIME = 1<<0,
- CRN_NEED_CAPACITY = 1<<1,
- CRN_NEED_GUARD = 1<<2,
- /* XXXX not used, apparently. */
- CRN_WEIGHT_AS_EXIT = 1<<5,
- CRN_NEED_DESC = 1<<6,
- /* On clients, only provide nodes that satisfy ClientPreferIPv6OR */
- CRN_PREF_ADDR = 1<<7,
- /* On clients, only provide nodes that we can connect to directly, based on
- * our firewall rules */
- CRN_DIRECT_CONN = 1<<8,
- /* On clients, only provide nodes with HSRend >= 2 protocol version which
- * is required for hidden service version >= 3. */
- CRN_RENDEZVOUS_V3 = 1<<9,
-} router_crn_flags_t;
+/** How long do we avoid using a directory server after it's given us a 503? */
+#define DIR_503_TIMEOUT (60*60)
-/** Possible ways to weight routers when choosing one randomly. See
- * routerlist_sl_choose_by_bandwidth() for more information.*/
-typedef enum bandwidth_weight_rule_t {
- NO_WEIGHTING, WEIGHT_FOR_EXIT, WEIGHT_FOR_MID, WEIGHT_FOR_GUARD,
- WEIGHT_FOR_DIR
-} bandwidth_weight_rule_t;
-
-/* Flags for pick_directory_server() and pick_trusteddirserver(). */
-/** Flag to indicate that we should not automatically be willing to use
- * ourself to answer a directory request.
- * Passed to router_pick_directory_server (et al).*/
-#define PDS_ALLOW_SELF (1<<0)
-/** Flag to indicate that if no servers seem to be up, we should mark all
- * directory servers as up and try again.
- * Passed to router_pick_directory_server (et al).*/
-#define PDS_RETRY_IF_NO_SERVERS (1<<1)
-/** Flag to indicate that we should not exclude directory servers that
- * our ReachableAddress settings would exclude. This usually means that
- * we're going to connect to the server over Tor, and so we don't need to
- * worry about our firewall telling us we can't.
- * Passed to router_pick_directory_server (et al).*/
-#define PDS_IGNORE_FASCISTFIREWALL (1<<2)
-/** Flag to indicate that we should not use any directory authority to which
- * we have an existing directory connection for downloading server descriptors
- * or extrainfo documents.
- *
- * Passed to router_pick_directory_server (et al)
- */
-#define PDS_NO_EXISTING_SERVERDESC_FETCH (1<<3)
-/** Flag to indicate that we should not use any directory authority to which
- * we have an existing directory connection for downloading microdescs.
- *
- * Passed to router_pick_directory_server (et al)
- */
-#define PDS_NO_EXISTING_MICRODESC_FETCH (1<<4)
-
-int get_n_authorities(dirinfo_type_t type);
-int trusted_dirs_reload_certs(void);
-
-/*
- * Pass one of these as source to trusted_dirs_load_certs_from_string()
- * to indicate whence string originates; this controls error handling
- * behavior such as marking downloads as failed.
- */
-
-#define TRUSTED_DIRS_CERTS_SRC_SELF 0
-#define TRUSTED_DIRS_CERTS_SRC_FROM_STORE 1
-#define TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST 2
-#define TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_SK_DIGEST 3
-#define TRUSTED_DIRS_CERTS_SRC_FROM_VOTE 4
-
-int trusted_dirs_load_certs_from_string(const char *contents, int source,
- int flush, const char *source_dir);
-void trusted_dirs_flush_certs_to_disk(void);
-authority_cert_t *authority_cert_get_newest_by_id(const char *id_digest);
-authority_cert_t *authority_cert_get_by_sk_digest(const char *sk_digest);
-authority_cert_t *authority_cert_get_by_digests(const char *id_digest,
- const char *sk_digest);
-void authority_cert_get_all(smartlist_t *certs_out);
-void authority_cert_dl_failed(const char *id_digest,
- const char *signing_key_digest, int status);
-void authority_certs_fetch_missing(networkstatus_t *status, time_t now,
- const char *dir_hint);
int router_reload_router_list(void);
-int authority_cert_dl_looks_uncertain(const char *id_digest);
-const smartlist_t *router_get_trusted_dir_servers(void);
-const smartlist_t *router_get_fallback_dir_servers(void);
-int authority_cert_is_blacklisted(const authority_cert_t *cert);
-const routerstatus_t *router_pick_directory_server(dirinfo_type_t type,
- int flags);
-dir_server_t *router_get_trusteddirserver_by_digest(const char *d);
-dir_server_t *router_get_fallback_dirserver_by_digest(
- const char *digest);
-int router_digest_is_fallback_dir(const char *digest);
-MOCK_DECL(dir_server_t *, trusteddirserver_get_by_v3_auth_digest,
- (const char *d));
-const routerstatus_t *router_pick_trusteddirserver(dirinfo_type_t type,
- int flags);
-const routerstatus_t *router_pick_fallback_dirserver(dirinfo_type_t type,
- int flags);
int router_skip_or_reachability(const or_options_t *options, int try_ip_pref);
-int router_get_my_share_of_directory_requests(double *v3_share_out);
+int router_skip_dir_reachability(const or_options_t *options, int try_ip_pref);
void router_reset_status_download_failures(void);
int routers_have_same_or_addrs(const routerinfo_t *r1, const routerinfo_t *r2);
void router_add_running_nodes_to_smartlist(smartlist_t *sl, int need_uptime,
@@ -155,21 +60,6 @@ const routerinfo_t *routerlist_find_my_routerinfo(void);
uint32_t router_get_advertised_bandwidth(const routerinfo_t *router);
uint32_t router_get_advertised_bandwidth_capped(const routerinfo_t *router);
-const node_t *node_sl_choose_by_bandwidth(const smartlist_t *sl,
- bandwidth_weight_rule_t rule);
-double frac_nodes_with_descriptors(const smartlist_t *sl,
- bandwidth_weight_rule_t rule,
- int for_direct_conn);
-
-const node_t *router_choose_random_node(smartlist_t *excludedsmartlist,
- struct routerset_t *excludedset,
- router_crn_flags_t flags);
-
-int router_digest_is_trusted_dir_type(const char *digest,
- dirinfo_type_t type);
-#define router_digest_is_trusted_dir(d) \
- router_digest_is_trusted_dir_type((d), NO_DIRINFO)
-
int hexdigest_to_digest(const char *hexdigest, char *digest);
const routerinfo_t *router_get_by_id_digest(const char *digest);
routerinfo_t *router_get_mutable_by_digest(const char *digest);
@@ -194,13 +84,10 @@ void routerlist_remove(routerlist_t *rl, routerinfo_t *ri, int make_old,
void routerlist_free_all(void);
void routerlist_reset_warnings(void);
-MOCK_DECL(smartlist_t *, list_authority_ids_with_downloads, (void));
-MOCK_DECL(download_status_t *, id_only_download_status_for_authority_id,
- (const char *digest));
-MOCK_DECL(smartlist_t *, list_sk_digests_for_authority_id,
- (const char *digest));
-MOCK_DECL(download_status_t *, download_status_for_authority_id_and_sk,
- (const char *id_digest, const char *sk_digest));
+/* XXXX move this */
+void list_pending_downloads(digestmap_t *result,
+ digest256map_t *result256,
+ int purpose, const char *prefix);
static int WRA_WAS_ADDED(was_router_added_t s);
static int WRA_WAS_OUTDATED(was_router_added_t s);
@@ -269,21 +156,6 @@ void routerlist_retry_directory_downloads(time_t now);
int router_exit_policy_rejects_all(const routerinfo_t *router);
-dir_server_t *trusted_dir_server_new(const char *nickname, const char *address,
- uint16_t dir_port, uint16_t or_port,
- const tor_addr_port_t *addrport_ipv6,
- const char *digest, const char *v3_auth_digest,
- dirinfo_type_t type, double weight);
-dir_server_t *fallback_dir_server_new(const tor_addr_t *addr,
- uint16_t dir_port, uint16_t or_port,
- const tor_addr_port_t *addrport_ipv6,
- const char *id_digest, double weight);
-void dir_server_add(dir_server_t *ent);
-
-void authority_cert_free_(authority_cert_t *cert);
-#define authority_cert_free(cert) \
- FREE_AND_NULL(authority_cert_t, authority_cert_free_, (cert))
-void clear_dir_servers(void);
void update_consensus_router_descriptor_downloads(time_t now, int is_vote,
networkstatus_t *consensus);
void update_router_descriptor_downloads(time_t now);
@@ -321,16 +193,6 @@ int hex_digest_nickname_matches(const char *hexdigest,
const char *nickname);
#ifdef ROUTERLIST_PRIVATE
-STATIC int choose_array_element_by_weight(const uint64_t *entries,
- int n_entries);
-STATIC void scale_array_elements_to_u64(uint64_t *entries_out,
- const double *entries_in,
- int n_entries,
- uint64_t *total_out);
-STATIC const routerstatus_t *router_pick_directory_server_impl(
- dirinfo_type_t auth, int flags,
- int *n_busy_out);
-
MOCK_DECL(int, router_descriptor_is_older_than, (const routerinfo_t *router,
int seconds));
MOCK_DECL(STATIC was_router_added_t, extrainfo_insert,
@@ -339,8 +201,6 @@ MOCK_DECL(STATIC was_router_added_t, extrainfo_insert,
MOCK_DECL(STATIC void, initiate_descriptor_downloads,
(const routerstatus_t *source, int purpose, smartlist_t *digests,
int lo, int hi, int pds_flags));
-STATIC int router_is_already_dir_fetching(const tor_addr_port_t *ap,
- int serverdesc, int microdesc);
#endif /* defined(ROUTERLIST_PRIVATE) */
diff --git a/src/feature/nodelist/routerparse.c b/src/feature/nodelist/routerparse.c
index b76b2974fa..a72cf98f56 100644
--- a/src/feature/nodelist/routerparse.c
+++ b/src/feature/nodelist/routerparse.c
@@ -61,7 +61,6 @@
#include "lib/crypt_ops/crypto_format.h"
#include "lib/crypt_ops/crypto_util.h"
#include "feature/dirauth/shared_random.h"
-#include "feature/dircache/dirserv.h"
#include "feature/client/entrynodes.h"
#include "lib/memarea/memarea.h"
#include "feature/nodelist/microdesc.h"
@@ -75,6 +74,7 @@
#include "feature/relay/routerkeys.h"
#include "feature/nodelist/routerlist.h"
#include "feature/nodelist/routerparse.h"
+#include "feature/nodelist/authcert.h"
#include "lib/sandbox/sandbox.h"
#include "feature/hs_common/shared_random_client.h"
#include "feature/nodelist/torcert.h"
diff --git a/src/feature/relay/dns.c b/src/feature/relay/dns.c
index 48319b47c8..6e9be5e2e7 100644
--- a/src/feature/relay/dns.c
+++ b/src/feature/relay/dns.c
@@ -58,7 +58,7 @@
#include "feature/control/control.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "feature/relay/dns.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "core/or/policies.h"
#include "core/or/relay.h"
#include "feature/relay/router.h"
diff --git a/src/feature/relay/ext_orport.c b/src/feature/relay/ext_orport.c
index 7de57ac65d..3607bdede4 100644
--- a/src/feature/relay/ext_orport.c
+++ b/src/feature/relay/ext_orport.c
@@ -25,7 +25,7 @@
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_util.h"
#include "feature/relay/ext_orport.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "core/proto/proto_ext_or.h"
#include "core/or/or_connection_st.h"
diff --git a/src/feature/relay/onion_queue.c b/src/feature/relay/onion_queue.c
new file mode 100644
index 0000000000..13142bb053
--- /dev/null
+++ b/src/feature/relay/onion_queue.c
@@ -0,0 +1,361 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file onion_queue.c
+ * \brief Functions to queue create cells for processing.
+ *
+ * Relays invoke these functions when they receive a CREATE or EXTEND
+ * cell in command.c or relay.c, in order to queue the pending request.
+ * They also invoke them from cpuworker.c, which handles dispatching
+ * onionskin requests to different worker threads.
+ *
+ * <br>
+ *
+ * This module also handles:
+ * <ul>
+ * <li> Queueing incoming onionskins on the relay side before passing
+ * them to worker threads.
+ * <li>Expiring onionskins on the relay side if they have waited for
+ * too long.
+ * </ul>
+ **/
+
+#include "core/or/or.h"
+
+#include "feature/relay/onion_queue.h"
+
+#include "app/config/config.h"
+#include "core/mainloop/cpuworker.h"
+#include "core/or/circuitlist.h"
+#include "core/or/onion.h"
+#include "feature/nodelist/networkstatus.h"
+
+#include "core/or/or_circuit_st.h"
+
+/** Type for a linked list of circuits that are waiting for a free CPU worker
+ * to process a waiting onion handshake. */
+typedef struct onion_queue_t {
+ TOR_TAILQ_ENTRY(onion_queue_t) next;
+ or_circuit_t *circ;
+ uint16_t handshake_type;
+ create_cell_t *onionskin;
+ time_t when_added;
+} onion_queue_t;
+
+/** 5 seconds on the onion queue til we just send back a destroy */
+#define ONIONQUEUE_WAIT_CUTOFF 5
+
+/** Array of queues of circuits waiting for CPU workers. An element is NULL
+ * if that queue is empty.*/
+static TOR_TAILQ_HEAD(onion_queue_head_t, onion_queue_t)
+ ol_list[MAX_ONION_HANDSHAKE_TYPE+1] =
+{ TOR_TAILQ_HEAD_INITIALIZER(ol_list[0]), /* tap */
+ TOR_TAILQ_HEAD_INITIALIZER(ol_list[1]), /* fast */
+ TOR_TAILQ_HEAD_INITIALIZER(ol_list[2]), /* ntor */
+};
+
+/** Number of entries of each type currently in each element of ol_list[]. */
+static int ol_entries[MAX_ONION_HANDSHAKE_TYPE+1];
+
+static int num_ntors_per_tap(void);
+static void onion_queue_entry_remove(onion_queue_t *victim);
+
+/* XXXX Check lengths vs MAX_ONIONSKIN_{CHALLENGE,REPLY}_LEN.
+ *
+ * (By which I think I meant, "make sure that no
+ * X_ONIONSKIN_CHALLENGE/REPLY_LEN is greater than
+ * MAX_ONIONSKIN_CHALLENGE/REPLY_LEN." Also, make sure that we can pass
+ * over-large values via EXTEND2/EXTENDED2, for future-compatibility.*/
+
+/** Return true iff we have room to queue another onionskin of type
+ * <b>type</b>. */
+static int
+have_room_for_onionskin(uint16_t type)
+{
+ const or_options_t *options = get_options();
+ int num_cpus;
+ uint64_t tap_usec, ntor_usec;
+ uint64_t ntor_during_tap_usec, tap_during_ntor_usec;
+
+ /* If we've got fewer than 50 entries, we always have room for one more. */
+ if (ol_entries[type] < 50)
+ return 1;
+ num_cpus = get_num_cpus(options);
+ /* Compute how many microseconds we'd expect to need to clear all
+ * onionskins in various combinations of the queues. */
+
+ /* How long would it take to process all the TAP cells in the queue? */
+ tap_usec = estimated_usec_for_onionskins(
+ ol_entries[ONION_HANDSHAKE_TYPE_TAP],
+ ONION_HANDSHAKE_TYPE_TAP) / num_cpus;
+
+ /* How long would it take to process all the NTor cells in the queue? */
+ ntor_usec = estimated_usec_for_onionskins(
+ ol_entries[ONION_HANDSHAKE_TYPE_NTOR],
+ ONION_HANDSHAKE_TYPE_NTOR) / num_cpus;
+
+ /* How long would it take to process the tap cells that we expect to
+ * process while draining the ntor queue? */
+ tap_during_ntor_usec = estimated_usec_for_onionskins(
+ MIN(ol_entries[ONION_HANDSHAKE_TYPE_TAP],
+ ol_entries[ONION_HANDSHAKE_TYPE_NTOR] / num_ntors_per_tap()),
+ ONION_HANDSHAKE_TYPE_TAP) / num_cpus;
+
+ /* How long would it take to process the ntor cells that we expect to
+ * process while draining the tap queue? */
+ ntor_during_tap_usec = estimated_usec_for_onionskins(
+ MIN(ol_entries[ONION_HANDSHAKE_TYPE_NTOR],
+ ol_entries[ONION_HANDSHAKE_TYPE_TAP] * num_ntors_per_tap()),
+ ONION_HANDSHAKE_TYPE_NTOR) / num_cpus;
+
+ /* See whether that exceeds MaxOnionQueueDelay. If so, we can't queue
+ * this. */
+ if (type == ONION_HANDSHAKE_TYPE_NTOR &&
+ (ntor_usec + tap_during_ntor_usec) / 1000 >
+ (uint64_t)options->MaxOnionQueueDelay)
+ return 0;
+
+ if (type == ONION_HANDSHAKE_TYPE_TAP &&
+ (tap_usec + ntor_during_tap_usec) / 1000 >
+ (uint64_t)options->MaxOnionQueueDelay)
+ return 0;
+
+ /* If we support the ntor handshake, then don't let TAP handshakes use
+ * more than 2/3 of the space on the queue. */
+ if (type == ONION_HANDSHAKE_TYPE_TAP &&
+ tap_usec / 1000 > (uint64_t)options->MaxOnionQueueDelay * 2 / 3)
+ return 0;
+
+ return 1;
+}
+
+/** Add <b>circ</b> to the end of ol_list and return 0, except
+ * if ol_list is too long, in which case do nothing and return -1.
+ */
+int
+onion_pending_add(or_circuit_t *circ, create_cell_t *onionskin)
+{
+ onion_queue_t *tmp;
+ time_t now = time(NULL);
+
+ if (onionskin->handshake_type > MAX_ONION_HANDSHAKE_TYPE) {
+ /* LCOV_EXCL_START
+ * We should have rejected this far before this point */
+ log_warn(LD_BUG, "Handshake %d out of range! Dropping.",
+ onionskin->handshake_type);
+ return -1;
+ /* LCOV_EXCL_STOP */
+ }
+
+ tmp = tor_malloc_zero(sizeof(onion_queue_t));
+ tmp->circ = circ;
+ tmp->handshake_type = onionskin->handshake_type;
+ tmp->onionskin = onionskin;
+ tmp->when_added = now;
+
+ if (!have_room_for_onionskin(onionskin->handshake_type)) {
+#define WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL (60)
+ static ratelim_t last_warned =
+ RATELIM_INIT(WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL);
+ char *m;
+ if (onionskin->handshake_type == ONION_HANDSHAKE_TYPE_NTOR &&
+ (m = rate_limit_log(&last_warned, approx_time()))) {
+ log_warn(LD_GENERAL,
+ "Your computer is too slow to handle this many circuit "
+ "creation requests! Please consider using the "
+ "MaxAdvertisedBandwidth config option or choosing a more "
+ "restricted exit policy.%s",m);
+ tor_free(m);
+ }
+ tor_free(tmp);
+ return -1;
+ }
+
+ ++ol_entries[onionskin->handshake_type];
+ log_info(LD_OR, "New create (%s). Queues now ntor=%d and tap=%d.",
+ onionskin->handshake_type == ONION_HANDSHAKE_TYPE_NTOR ? "ntor" : "tap",
+ ol_entries[ONION_HANDSHAKE_TYPE_NTOR],
+ ol_entries[ONION_HANDSHAKE_TYPE_TAP]);
+
+ circ->onionqueue_entry = tmp;
+ TOR_TAILQ_INSERT_TAIL(&ol_list[onionskin->handshake_type], tmp, next);
+
+ /* cull elderly requests. */
+ while (1) {
+ onion_queue_t *head = TOR_TAILQ_FIRST(&ol_list[onionskin->handshake_type]);
+ if (now - head->when_added < (time_t)ONIONQUEUE_WAIT_CUTOFF)
+ break;
+
+ circ = head->circ;
+ circ->onionqueue_entry = NULL;
+ onion_queue_entry_remove(head);
+ log_info(LD_CIRC,
+ "Circuit create request is too old; canceling due to overload.");
+ if (! TO_CIRCUIT(circ)->marked_for_close) {
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT);
+ }
+ }
+ return 0;
+}
+
+/** Return a fairness parameter, to prefer processing NTOR style
+ * handshakes but still slowly drain the TAP queue so we don't starve
+ * it entirely. */
+static int
+num_ntors_per_tap(void)
+{
+#define DEFAULT_NUM_NTORS_PER_TAP 10
+#define MIN_NUM_NTORS_PER_TAP 1
+#define MAX_NUM_NTORS_PER_TAP 100000
+
+ return networkstatus_get_param(NULL, "NumNTorsPerTAP",
+ DEFAULT_NUM_NTORS_PER_TAP,
+ MIN_NUM_NTORS_PER_TAP,
+ MAX_NUM_NTORS_PER_TAP);
+}
+
+/** Choose which onion queue we'll pull from next. If one is empty choose
+ * the other; if they both have elements, load balance across them but
+ * favoring NTOR. */
+static uint16_t
+decide_next_handshake_type(void)
+{
+ /* The number of times we've chosen ntor lately when both were available. */
+ static int recently_chosen_ntors = 0;
+
+ if (!ol_entries[ONION_HANDSHAKE_TYPE_NTOR])
+ return ONION_HANDSHAKE_TYPE_TAP; /* no ntors? try tap */
+
+ if (!ol_entries[ONION_HANDSHAKE_TYPE_TAP]) {
+
+ /* Nick wants us to prioritize new tap requests when there aren't
+ * any in the queue and we've processed k ntor cells since the last
+ * tap cell. This strategy is maybe a good idea, since it starves tap
+ * less in the case where tap is rare, or maybe a poor idea, since it
+ * makes the new tap cell unfairly jump in front of ntor cells that
+ * got here first. In any case this edge case will only become relevant
+ * once tap is rare. We should reevaluate whether we like this decision
+ * once tap gets more rare. */
+ if (ol_entries[ONION_HANDSHAKE_TYPE_NTOR] &&
+ recently_chosen_ntors <= num_ntors_per_tap())
+ ++recently_chosen_ntors;
+
+ return ONION_HANDSHAKE_TYPE_NTOR; /* no taps? try ntor */
+ }
+
+ /* They both have something queued. Pick ntor if we haven't done that
+ * too much lately. */
+ if (++recently_chosen_ntors <= num_ntors_per_tap()) {
+ return ONION_HANDSHAKE_TYPE_NTOR;
+ }
+
+ /* Else, it's time to let tap have its turn. */
+ recently_chosen_ntors = 0;
+ return ONION_HANDSHAKE_TYPE_TAP;
+}
+
+/** Remove the highest priority item from ol_list[] and return it, or
+ * return NULL if the lists are empty.
+ */
+or_circuit_t *
+onion_next_task(create_cell_t **onionskin_out)
+{
+ or_circuit_t *circ;
+ uint16_t handshake_to_choose = decide_next_handshake_type();
+ onion_queue_t *head = TOR_TAILQ_FIRST(&ol_list[handshake_to_choose]);
+
+ if (!head)
+ return NULL; /* no onions pending, we're done */
+
+ tor_assert(head->circ);
+ tor_assert(head->handshake_type <= MAX_ONION_HANDSHAKE_TYPE);
+// tor_assert(head->circ->p_chan); /* make sure it's still valid */
+/* XXX I only commented out the above line to make the unit tests
+ * more manageable. That's probably not good long-term. -RD */
+ circ = head->circ;
+ if (head->onionskin)
+ --ol_entries[head->handshake_type];
+ log_info(LD_OR, "Processing create (%s). Queues now ntor=%d and tap=%d.",
+ head->handshake_type == ONION_HANDSHAKE_TYPE_NTOR ? "ntor" : "tap",
+ ol_entries[ONION_HANDSHAKE_TYPE_NTOR],
+ ol_entries[ONION_HANDSHAKE_TYPE_TAP]);
+
+ *onionskin_out = head->onionskin;
+ head->onionskin = NULL; /* prevent free. */
+ circ->onionqueue_entry = NULL;
+ onion_queue_entry_remove(head);
+ return circ;
+}
+
+/** Return the number of <b>handshake_type</b>-style create requests pending.
+ */
+int
+onion_num_pending(uint16_t handshake_type)
+{
+ return ol_entries[handshake_type];
+}
+
+/** Go through ol_list, find the onion_queue_t element which points to
+ * circ, remove and free that element. Leave circ itself alone.
+ */
+void
+onion_pending_remove(or_circuit_t *circ)
+{
+ onion_queue_t *victim;
+
+ if (!circ)
+ return;
+
+ victim = circ->onionqueue_entry;
+ if (victim)
+ onion_queue_entry_remove(victim);
+
+ cpuworker_cancel_circ_handshake(circ);
+}
+
+/** Remove a queue entry <b>victim</b> from the queue, unlinking it from
+ * its circuit and freeing it and any structures it owns.*/
+static void
+onion_queue_entry_remove(onion_queue_t *victim)
+{
+ if (victim->handshake_type > MAX_ONION_HANDSHAKE_TYPE) {
+ /* LCOV_EXCL_START
+ * We should have rejected this far before this point */
+ log_warn(LD_BUG, "Handshake %d out of range! Dropping.",
+ victim->handshake_type);
+ /* XXX leaks */
+ return;
+ /* LCOV_EXCL_STOP */
+ }
+
+ TOR_TAILQ_REMOVE(&ol_list[victim->handshake_type], victim, next);
+
+ if (victim->circ)
+ victim->circ->onionqueue_entry = NULL;
+
+ if (victim->onionskin)
+ --ol_entries[victim->handshake_type];
+
+ tor_free(victim->onionskin);
+ tor_free(victim);
+}
+
+/** Remove all circuits from the pending list. Called from tor_free_all. */
+void
+clear_pending_onions(void)
+{
+ onion_queue_t *victim, *next;
+ int i;
+ for (i=0; i<=MAX_ONION_HANDSHAKE_TYPE; i++) {
+ for (victim = TOR_TAILQ_FIRST(&ol_list[i]); victim; victim = next) {
+ next = TOR_TAILQ_NEXT(victim,next);
+ onion_queue_entry_remove(victim);
+ }
+ tor_assert(TOR_TAILQ_EMPTY(&ol_list[i]));
+ }
+ memset(ol_entries, 0, sizeof(ol_entries));
+}
diff --git a/src/feature/relay/onion_queue.h b/src/feature/relay/onion_queue.h
new file mode 100644
index 0000000000..a71f497e34
--- /dev/null
+++ b/src/feature/relay/onion_queue.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file onion_queue.h
+ * \brief Header file for onion_queue.c.
+ **/
+
+#ifndef TOR_ONION_QUEUE_H
+#define TOR_ONION_QUEUE_H
+
+struct create_cell_t;
+
+int onion_pending_add(or_circuit_t *circ, struct create_cell_t *onionskin);
+or_circuit_t *onion_next_task(struct create_cell_t **onionskin_out);
+int onion_num_pending(uint16_t handshake_type);
+void onion_pending_remove(or_circuit_t *circ);
+void clear_pending_onions(void);
+
+#endif
diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c
index 1f316ebf08..447c21e897 100644
--- a/src/feature/relay/router.c
+++ b/src/feature/relay/router.c
@@ -18,10 +18,12 @@
#include "lib/crypt_ops/crypto_curve25519.h"
#include "feature/dircache/directory.h"
#include "feature/dircache/dirserv.h"
+#include "feature/dirauth/process_descs.h"
#include "feature/relay/dns.h"
#include "feature/stats/geoip.h"
#include "feature/hibernate/hibernate.h"
-#include "core/mainloop/main.h"
+#include "app/main/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
#include "core/or/policies.h"
@@ -30,6 +32,8 @@
#include "feature/stats/rephist.h"
#include "feature/relay/router.h"
#include "feature/relay/routerkeys.h"
+#include "feature/nodelist/authcert.h"
+#include "feature/nodelist/dirlist.h"
#include "feature/nodelist/routerlist.h"
#include "feature/nodelist/routerparse.h"
#include "app/config/statefile.h"
diff --git a/src/feature/rend/rendclient.c b/src/feature/rend/rendclient.c
index 0efeb3b77c..a2b494bd27 100644
--- a/src/feature/rend/rendclient.c
+++ b/src/feature/rend/rendclient.c
@@ -23,7 +23,7 @@
#include "feature/hs/hs_circuit.h"
#include "feature/hs/hs_client.h"
#include "feature/hs/hs_common.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
#include "core/or/relay.h"
diff --git a/src/feature/rend/rendservice.c b/src/feature/rend/rendservice.c
index 37e604d07d..ceb4d958d7 100644
--- a/src/feature/rend/rendservice.c
+++ b/src/feature/rend/rendservice.c
@@ -22,7 +22,7 @@
#include "feature/dircache/directory.h"
#include "feature/hs/hs_common.h"
#include "feature/hs/hs_config.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
#include "core/or/policies.h"
@@ -33,7 +33,7 @@
#include "core/or/relay.h"
#include "feature/stats/rephist.h"
#include "feature/hs_common/replaycache.h"
-#include "feature/nodelist/routerlist.h"
+#include "feature/nodelist/node_select.h"
#include "feature/nodelist/routerparse.h"
#include "feature/nodelist/routerset.h"
#include "lib/encoding/confline.h"
diff --git a/src/feature/stats/rephist.c b/src/feature/stats/rephist.c
index edb079ef2d..a2dbb0ff16 100644
--- a/src/feature/stats/rephist.c
+++ b/src/feature/stats/rephist.c
@@ -74,6 +74,7 @@
* (The "rephist" name originally stood for "reputation and history". )
**/
+#define REPHIST_PRIVATE
#include "core/or/or.h"
#include "core/or/circuitlist.h"
#include "core/or/circuituse.h"
@@ -106,11 +107,6 @@
static void bw_arrays_init(void);
static void predicted_ports_alloc(void);
-typedef struct bw_array_t bw_array_t;
-STATIC uint64_t find_largest_max(bw_array_t *b);
-STATIC void commit_max(bw_array_t *b);
-STATIC void advance_obs(bw_array_t *b);
-
/** Total number of bytes currently allocated in fields used by rephist.c. */
uint64_t rephist_total_alloc=0;
/** Number of or_history_t objects currently allocated. */
@@ -998,7 +994,7 @@ rep_hist_load_mtbf_data(time_t now)
/** Structure to track bandwidth use, and remember the maxima for a given
* time period.
*/
-typedef struct bw_array_t {
+struct bw_array_t {
/** Observation array: Total number of bytes transferred in each of the last
* NUM_SECS_ROLLING_MEASURE seconds. This is used as a circular array. */
uint64_t obs[NUM_SECS_ROLLING_MEASURE];
@@ -1025,7 +1021,7 @@ typedef struct bw_array_t {
/** Circular array of the total bandwidth usage for the last NUM_TOTALS
* periods */
uint64_t totals[NUM_TOTALS];
-} bw_array_t;
+};
/** Shift the current period of b forward by one. */
STATIC void
diff --git a/src/feature/stats/rephist.h b/src/feature/stats/rephist.h
index e17a722489..842184f815 100644
--- a/src/feature/stats/rephist.h
+++ b/src/feature/stats/rephist.h
@@ -109,8 +109,14 @@ extern uint32_t rephist_total_num;
#ifdef TOR_UNIT_TESTS
extern int onion_handshakes_requested[MAX_ONION_HANDSHAKE_TYPE+1];
extern int onion_handshakes_assigned[MAX_ONION_HANDSHAKE_TYPE+1];
+extern struct bw_array_t *write_array;
+#endif
+
+#ifdef REPHIST_PRIVATE
typedef struct bw_array_t bw_array_t;
-extern bw_array_t *write_array;
+STATIC uint64_t find_largest_max(bw_array_t *b);
+STATIC void commit_max(bw_array_t *b);
+STATIC void advance_obs(bw_array_t *b);
#endif
/**
@@ -139,4 +145,3 @@ void rep_hist_prep_published_padding_counts(time_t now);
void rep_hist_padding_count_timers(uint64_t num_timers);
#endif /* !defined(TOR_REPHIST_H) */
-
diff --git a/src/lib/net/socket.c b/src/lib/net/socket.c
index 06421b080d..cd7c9685cd 100644
--- a/src/lib/net/socket.c
+++ b/src/lib/net/socket.c
@@ -142,41 +142,6 @@ tor_close_socket_simple(tor_socket_t s)
return r;
}
-/** As tor_close_socket_simple(), but keeps track of the number
- * of open sockets. Returns 0 on success, -1 on failure. */
-MOCK_IMPL(int,
-tor_close_socket,(tor_socket_t s))
-{
- int r = tor_close_socket_simple(s);
-
- socket_accounting_lock();
-#ifdef DEBUG_SOCKET_COUNTING
- if (s > max_socket || ! bitarray_is_set(open_sockets, s)) {
- log_warn(LD_BUG, "Closing a socket (%d) that wasn't returned by tor_open_"
- "socket(), or that was already closed or something.", s);
- } else {
- tor_assert(open_sockets && s <= max_socket);
- bitarray_clear(open_sockets, s);
- }
-#endif /* defined(DEBUG_SOCKET_COUNTING) */
- if (r == 0) {
- --n_sockets_open;
- } else {
-#ifdef _WIN32
- if (r != WSAENOTSOCK)
- --n_sockets_open;
-#else
- if (r != EBADF)
- --n_sockets_open; // LCOV_EXCL_LINE -- EIO and EINTR too hard to force.
-#endif /* defined(_WIN32) */
- r = -1;
- }
-
- tor_assert_nonfatal(n_sockets_open >= 0);
- socket_accounting_unlock();
- return r;
-}
-
/** @{ */
#ifdef DEBUG_SOCKET_COUNTING
/** Helper: if DEBUG_SOCKET_COUNTING is enabled, remember that <b>s</b> is
@@ -201,11 +166,50 @@ mark_socket_open(tor_socket_t s)
}
bitarray_set(open_sockets, s);
}
+static inline void
+mark_socket_closed(tor_socket_t s)
+{
+ if (s > max_socket || ! bitarray_is_set(open_sockets, s)) {
+ log_warn(LD_BUG, "Closing a socket (%d) that wasn't returned by tor_open_"
+ "socket(), or that was already closed or something.", s);
+ } else {
+ tor_assert(open_sockets && s <= max_socket);
+ bitarray_clear(open_sockets, s);
+ }
+}
#else /* !(defined(DEBUG_SOCKET_COUNTING)) */
#define mark_socket_open(s) ((void) (s))
+#define mark_socket_closed(s) ((void) (s))
#endif /* defined(DEBUG_SOCKET_COUNTING) */
/** @} */
+/** As tor_close_socket_simple(), but keeps track of the number
+ * of open sockets. Returns 0 on success, -1 on failure. */
+MOCK_IMPL(int,
+tor_close_socket,(tor_socket_t s))
+{
+ int r = tor_close_socket_simple(s);
+
+ socket_accounting_lock();
+ mark_socket_closed(s);
+ if (r == 0) {
+ --n_sockets_open;
+ } else {
+#ifdef _WIN32
+ if (r != WSAENOTSOCK)
+ --n_sockets_open;
+#else
+ if (r != EBADF)
+ --n_sockets_open; // LCOV_EXCL_LINE -- EIO and EINTR too hard to force.
+#endif /* defined(_WIN32) */
+ r = -1;
+ }
+
+ tor_assert_nonfatal(n_sockets_open >= 0);
+ socket_accounting_unlock();
+ return r;
+}
+
/** As socket(), but counts the number of open sockets. */
MOCK_IMPL(tor_socket_t,
tor_open_socket,(int domain, int type, int protocol))
@@ -307,6 +311,20 @@ tor_take_socket_ownership(tor_socket_t s)
socket_accounting_unlock();
}
+/**
+ * For socket accounting: declare that we are no longer the owner of the
+ * socket <b>s</b>. This will prevent us from overallocating sockets, and
+ * prevent us from asserting later when we close the socket <b>s</b>.
+ */
+void
+tor_release_socket_ownership(tor_socket_t s)
+{
+ socket_accounting_lock();
+ --n_sockets_open;
+ mark_socket_closed(s);
+ socket_accounting_unlock();
+}
+
/** As accept(), but counts the number of open sockets. */
tor_socket_t
tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len)
diff --git a/src/lib/net/socket.h b/src/lib/net/socket.h
index 5b7d6dbbc6..2b87441fc6 100644
--- a/src/lib/net/socket.h
+++ b/src/lib/net/socket.h
@@ -23,6 +23,7 @@ struct sockaddr;
int tor_close_socket_simple(tor_socket_t s);
MOCK_DECL(int, tor_close_socket, (tor_socket_t s));
void tor_take_socket_ownership(tor_socket_t s);
+void tor_release_socket_ownership(tor_socket_t s);
tor_socket_t tor_open_socket_with_extensions(
int domain, int type, int protocol,
int cloexec, int nonblock);
diff --git a/src/lib/time/compat_time.c b/src/lib/time/compat_time.c
index d26cb6880d..f1ddb4fdc4 100644
--- a/src/lib/time/compat_time.c
+++ b/src/lib/time/compat_time.c
@@ -237,6 +237,7 @@ monotime_reset_ratchets_for_testing(void)
*/
static struct mach_timebase_info mach_time_info;
static struct mach_timebase_info mach_time_info_msec_cvt;
+static int32_t mach_time_msec_cvt_threshold;
static int monotime_shift = 0;
static void
@@ -256,11 +257,15 @@ monotime_init_internal(void)
}
{
// For converting ticks to milliseconds in a 32-bit-friendly way, we
- // will first right-shift by 20, and then multiply by 20/19, since
- // (1<<20) * 19/20 is about 1e6. We precompute a new numerate and
+ // will first right-shift by 20, and then multiply by 2048/1953, since
+ // (1<<20) * 1953/2048 is about 1e6. We precompute a new numerator and
// denominator here to avoid multiple multiplies.
- mach_time_info_msec_cvt.numer = mach_time_info.numer * 20;
- mach_time_info_msec_cvt.denom = mach_time_info.denom * 19;
+ mach_time_info_msec_cvt.numer = mach_time_info.numer * 2048;
+ mach_time_info_msec_cvt.denom = mach_time_info.denom * 1953;
+ // For any value above this amount, we should divide before multiplying,
+ // to avoid overflow. For a value below this, we should multiply
+ // before dividing, to improve accuracy.
+ mach_time_msec_cvt_threshold = INT32_MAX / mach_time_info_msec_cvt.numer;
}
}
@@ -323,8 +328,13 @@ monotime_coarse_diff_msec32_(const monotime_coarse_t *start,
/* We already require in di_ops.c that right-shift performs a sign-extend. */
const int32_t diff_microticks = (int32_t)(diff_ticks >> 20);
- return (diff_microticks * mach_time_info_msec_cvt.numer) /
- mach_time_info_msec_cvt.denom;
+ if (diff_microticks >= mach_time_msec_cvt_threshold) {
+ return (diff_microticks / mach_time_info_msec_cvt.denom) *
+ mach_time_info_msec_cvt.numer;
+ } else {
+ return (diff_microticks * mach_time_info_msec_cvt.numer) /
+ mach_time_info_msec_cvt.denom;
+ }
}
uint32_t
diff --git a/src/lib/time/compat_time.h b/src/lib/time/compat_time.h
index 4427ce8f92..44fab62de5 100644
--- a/src/lib/time/compat_time.h
+++ b/src/lib/time/compat_time.h
@@ -200,6 +200,7 @@ monotime_coarse_diff_msec32(const monotime_coarse_t *start,
// on a 64-bit platform, let's assume 64/64 division is cheap.
return (int32_t) monotime_coarse_diff_msec(start, end);
#else
+#define USING_32BIT_MSEC_HACK
return monotime_coarse_diff_msec32_(start, end);
#endif
}
diff --git a/src/lib/tls/tortls_nss.c b/src/lib/tls/tortls_nss.c
index 88d2be8cee..462cd5b0ff 100644
--- a/src/lib/tls/tortls_nss.c
+++ b/src/lib/tls/tortls_nss.c
@@ -444,7 +444,7 @@ tor_tls_release_socket(tor_tls_t *tls)
*/
tor_socket_t sock =
tor_open_socket_nonblocking(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (!sock) {
+ if (! SOCKET_OK(sock)) {
log_warn(LD_NET, "Out of sockets when trying to shut down an NSS "
"connection");
return;
@@ -452,10 +452,14 @@ tor_tls_release_socket(tor_tls_t *tls)
PRFileDesc *tcp = PR_GetIdentitiesLayer(tls->ssl, PR_NSPR_IO_LAYER);
if (BUG(! tcp)) {
+ tor_close_socket(sock);
return;
}
PR_ChangeFileDescNativeHandle(tcp, sock);
+ /* Tell our socket accounting layer that we don't own this socket any more:
+ * NSS is about to free it for us. */
+ tor_release_socket_ownership(sock);
}
void
diff --git a/src/test/test.c b/src/test/test.c
index dc8e3bede3..fff87a2b56 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -11,7 +11,6 @@
#include "orconfig.h"
#include "lib/crypt_ops/crypto_dh.h"
#include "lib/crypt_ops/crypto_rand.h"
-
#include "app/config/or_state_st.h"
#include <stdio.h>
@@ -33,7 +32,7 @@
#define ROUTER_PRIVATE
#define CIRCUITSTATS_PRIVATE
#define CIRCUITLIST_PRIVATE
-#define MAIN_PRIVATE
+#define MAINLOOP_PRIVATE
#define STATEFILE_PRIVATE
#include "core/or/or.h"
@@ -47,9 +46,9 @@
#include "feature/rend/rendcommon.h"
#include "feature/rend/rendcache.h"
#include "test/test.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "lib/memarea/memarea.h"
-#include "core/crypto/onion.h"
+#include "core/or/onion.h"
#include "core/crypto/onion_ntor.h"
#include "core/crypto/onion_fast.h"
#include "core/crypto/onion_tap.h"
@@ -64,6 +63,7 @@
#include "feature/rend/rend_encoded_v2_service_descriptor_st.h"
#include "feature/rend/rend_intro_point_st.h"
#include "feature/rend/rend_service_descriptor_st.h"
+#include "feature/relay/onion_queue.h"
/** Run unit tests for the onion handshake code. */
static void
diff --git a/src/test/test_cell_formats.c b/src/test/test_cell_formats.c
index 2753c42191..daf0296e2a 100644
--- a/src/test/test_cell_formats.c
+++ b/src/test/test_cell_formats.c
@@ -13,7 +13,7 @@
#include "core/or/connection_or.h"
#include "app/config/config.h"
#include "lib/crypt_ops/crypto_rand.h"
-#include "core/crypto/onion.h"
+#include "core/or/onion.h"
#include "core/crypto/onion_tap.h"
#include "core/crypto/onion_fast.h"
#include "core/crypto/onion_ntor.h"
diff --git a/src/test/test_channelpadding.c b/src/test/test_channelpadding.c
index de673de543..0fd60d0a92 100644
--- a/src/test/test_channelpadding.c
+++ b/src/test/test_channelpadding.c
@@ -2,7 +2,7 @@
/* See LICENSE for licensing information */
#define TOR_CHANNEL_INTERNAL_
-#define MAIN_PRIVATE
+#define MAINLOOP_PRIVATE
#define NETWORKSTATUS_PRIVATE
#define TOR_TIMERS_PRIVATE
#include "core/or/or.h"
@@ -16,7 +16,7 @@
#include "lib/evloop/compat_libevent.h"
#include "app/config/config.h"
#include "lib/time/compat_time.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/networkstatus.h"
#include "test/log_test_helpers.h"
#include "lib/tls/tortls.h"
diff --git a/src/test/test_circuitmux.c b/src/test/test_circuitmux.c
index 1d46f8de0d..1be2ff5281 100644
--- a/src/test/test_circuitmux.c
+++ b/src/test/test_circuitmux.c
@@ -15,6 +15,8 @@
#include "core/or/destroy_cell_queue_st.h"
+#include <math.h>
+
/* XXXX duplicated function from test_circuitlist.c */
static channel_t *
new_fake_channel(void)
@@ -105,16 +107,19 @@ test_cmux_compute_ticks(void *arg)
monotime_coarse_set_mock_time_nsec(now);
tick = cell_ewma_get_current_tick_and_fraction(&rem);
tt_uint_op(tick, OP_EQ, tick_zero);
- tt_double_op(rem, OP_GT, .149999999);
- tt_double_op(rem, OP_LT, .150000001);
+#ifdef USING_32BIT_MSEC_HACK
+ const double tolerance = .0005;
+#else
+ const double tolerance = .00000001;
+#endif
+ tt_double_op(fabs(rem - .15), OP_LT, tolerance);
/* 25 second later and we should be in another tick. */
now = START_NS + NS_PER_S * 25;
monotime_coarse_set_mock_time_nsec(now);
tick = cell_ewma_get_current_tick_and_fraction(&rem);
tt_uint_op(tick, OP_EQ, tick_zero + 2);
- tt_double_op(rem, OP_GT, .499999999);
- tt_double_op(rem, OP_LT, .500000001);
+ tt_double_op(fabs(rem - .5), OP_LT, tolerance);
done:
;
diff --git a/src/test/test_config.c b/src/test/test_config.c
index bf21a8d512..4dc1b8c0ec 100644
--- a/src/test/test_config.c
+++ b/src/test/test_config.c
@@ -31,13 +31,14 @@
#include "feature/relay/ext_orport.h"
#include "feature/stats/geoip.h"
#include "feature/hibernate/hibernate.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
#include "core/or/policies.h"
#include "feature/rend/rendclient.h"
#include "feature/rend/rendservice.h"
#include "feature/relay/router.h"
+#include "feature/nodelist/dirlist.h"
#include "feature/nodelist/routerlist.h"
#include "feature/nodelist/routerset.h"
#include "app/config/statefile.h"
diff --git a/src/test/test_connection.c b/src/test/test_connection.c
index e716c83fe1..d85ede61b1 100644
--- a/src/test/test_connection.c
+++ b/src/test/test_connection.c
@@ -4,7 +4,7 @@
#include "orconfig.h"
#define CONNECTION_PRIVATE
-#define MAIN_PRIVATE
+#define MAINLOOP_PRIVATE
#define CONNECTION_OR_PRIVATE
#include "core/or/or.h"
@@ -13,7 +13,7 @@
#include "core/mainloop/connection.h"
#include "core/or/connection_edge.h"
#include "feature/hs/hs_common.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/nodelist.h"
#include "feature/nodelist/networkstatus.h"
diff --git a/src/test/test_controller.c b/src/test/test_controller.c
index 0428ac6fce..4f5a9f58d5 100644
--- a/src/test/test_controller.c
+++ b/src/test/test_controller.c
@@ -10,7 +10,7 @@
#include "feature/hs/hs_common.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/rend/rendservice.h"
-#include "feature/nodelist/routerlist.h"
+#include "feature/nodelist/authcert.h"
#include "feature/nodelist/nodelist.h"
#include "test/test.h"
#include "test/test_helpers.h"
diff --git a/src/test/test_dir.c b/src/test/test_dir.c
index 723799ee8a..b2c3f4426c 100644
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@ -6,16 +6,19 @@
#include "orconfig.h"
#include <math.h>
+#define BWAUTH_PRIVATE
#define CONFIG_PRIVATE
#define CONTROL_PRIVATE
#define DIRSERV_PRIVATE
#define DIRVOTE_PRIVATE
-#define ROUTER_PRIVATE
-#define ROUTERLIST_PRIVATE
-#define ROUTERPARSE_PRIVATE
#define HIBERNATE_PRIVATE
#define NETWORKSTATUS_PRIVATE
+#define NODE_SELECT_PRIVATE
#define RELAY_PRIVATE
+#define ROUTERLIST_PRIVATE
+#define ROUTERPARSE_PRIVATE
+#define ROUTER_PRIVATE
+#define VOTEFLAGS_PRIVATE
#include "core/or/or.h"
#include "feature/client/bridges.h"
@@ -28,8 +31,12 @@
#include "lib/crypt_ops/crypto_format.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "feature/dircache/directory.h"
+#include "feature/dirauth/bwauth.h"
#include "feature/dircache/dirserv.h"
+#include "feature/dirauth/process_descs.h"
#include "feature/dirauth/dirvote.h"
+#include "feature/dirauth/recommend_pkg.h"
+#include "feature/dirauth/voteflags.h"
#include "feature/client/entrynodes.h"
#include "feature/dircommon/fp_pair.h"
#include "feature/hibernate/hibernate.h"
@@ -38,6 +45,9 @@
#include "feature/nodelist/networkstatus.h"
#include "feature/relay/router.h"
#include "feature/relay/routerkeys.h"
+#include "feature/nodelist/authcert.h"
+#include "feature/nodelist/dirlist.h"
+#include "feature/nodelist/node_select.h"
#include "feature/nodelist/routerlist.h"
#include "feature/nodelist/routerparse.h"
#include "feature/nodelist/routerset.h"
diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c
index 09799a0e5f..2cfed16b51 100644
--- a/src/test/test_dir_handle_get.c
+++ b/src/test/test_dir_handle_get.c
@@ -19,6 +19,8 @@
#include "feature/rend/rendcommon.h"
#include "feature/rend/rendcache.h"
#include "feature/relay/router.h"
+#include "feature/nodelist/authcert.h"
+#include "feature/nodelist/dirlist.h"
#include "feature/nodelist/routerlist.h"
#include "test/rend_test_helpers.h"
#include "feature/nodelist/microdesc.h"
diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c
index ff987563c6..71be9131c7 100644
--- a/src/test/test_extorport.c
+++ b/src/test/test_extorport.c
@@ -3,7 +3,7 @@
#define CONNECTION_PRIVATE
#define EXT_ORPORT_PRIVATE
-#define MAIN_PRIVATE
+#define MAINLOOP_PRIVATE
#include "core/or/or.h"
#include "lib/container/buffers.h"
#include "core/mainloop/connection.h"
@@ -12,11 +12,12 @@
#include "feature/control/control.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "feature/relay/ext_orport.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "core/or/or_connection_st.h"
#include "test/test.h"
+#include "test/test_helpers.h"
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
@@ -89,22 +90,6 @@ connection_write_to_buf_impl_replacement(const char *string, size_t len,
buf_add(conn->outbuf, string, len);
}
-static char *
-buf_get_contents(buf_t *buf, size_t *sz_out)
-{
- char *out;
- *sz_out = buf_datalen(buf);
- if (*sz_out >= ULONG_MAX)
- return NULL; /* C'mon, really? */
- out = tor_malloc(*sz_out + 1);
- if (buf_get_bytes(buf, out, (unsigned long)*sz_out) != 0) {
- tor_free(out);
- return NULL;
- }
- out[*sz_out] = '\0'; /* Hopefully gratuitous. */
- return out;
-}
-
static void
test_ext_or_write_command(void *arg)
{
@@ -463,7 +448,7 @@ test_ext_or_handshake(void *arg)
memcpy(ext_or_auth_cookie, "Gliding wrapt in a brown mantle," , 32);
ext_or_auth_cookie_is_set = 1;
- init_connection_lists();
+ tor_init_connection_lists();
conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
tt_int_op(0, OP_EQ, connection_ext_or_start_auth(conn));
diff --git a/src/test/test_guardfraction.c b/src/test/test_guardfraction.c
index f45a723295..7d4a959bb4 100644
--- a/src/test/test_guardfraction.c
+++ b/src/test/test_guardfraction.c
@@ -1,14 +1,14 @@
/* Copyright (c) 2014-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
-#define DIRSERV_PRIVATE
+#define GUARDFRACTION_PRIVATE
#define ROUTERPARSE_PRIVATE
#define NETWORKSTATUS_PRIVATE
#include "orconfig.h"
#include "core/or/or.h"
#include "app/config/config.h"
-#include "feature/dircache/dirserv.h"
+#include "feature/dirauth/guardfraction.h"
#include "feature/client/entrynodes.h"
#include "feature/nodelist/routerparse.h"
#include "feature/nodelist/networkstatus.h"
@@ -422,4 +422,3 @@ struct testcase_t guardfraction_tests[] = {
END_OF_TESTCASES
};
-
diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c
index c9138611d8..6ac73bff5c 100644
--- a/src/test/test_helpers.c
+++ b/src/test/test_helpers.c
@@ -9,7 +9,7 @@
#define ROUTERLIST_PRIVATE
#define CONFIG_PRIVATE
#define CONNECTION_PRIVATE
-#define MAIN_PRIVATE
+#define MAINLOOP_PRIVATE
#include "orconfig.h"
#include "core/or/or.h"
@@ -19,7 +19,7 @@
#include "app/config/confparse.h"
#include "core/mainloop/connection.h"
#include "lib/crypt_ops/crypto_rand.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/nodelist.h"
#include "core/or/relay.h"
#include "feature/nodelist/routerlist.h"
@@ -125,6 +125,25 @@ connection_write_to_buf_mock(const char *string, size_t len,
buf_add(conn->outbuf, string, len);
}
+char *
+buf_get_contents(buf_t *buf, size_t *sz_out)
+{
+ tor_assert(buf);
+ tor_assert(sz_out);
+
+ char *out;
+ *sz_out = buf_datalen(buf);
+ if (*sz_out >= ULONG_MAX)
+ return NULL; /* C'mon, really? */
+ out = tor_malloc(*sz_out + 1);
+ if (buf_get_bytes(buf, out, (unsigned long)*sz_out) != 0) {
+ tor_free(out);
+ return NULL;
+ }
+ out[*sz_out] = '\0'; /* Hopefully gratuitous. */
+ return out;
+}
+
/* Set up a fake origin circuit with the specified number of cells,
* Return a pointer to the newly-created dummy circuit */
circuit_t *
@@ -217,7 +236,7 @@ test_conn_get_connection(uint8_t state, uint8_t type, uint8_t purpose)
mock_connection_connect_sockaddr);
MOCK(tor_close_socket, fake_close_socket);
- init_connection_lists();
+ tor_init_connection_lists();
conn = connection_new(type, TEST_CONN_FAMILY);
tt_assert(conn);
diff --git a/src/test/test_helpers.h b/src/test/test_helpers.h
index 3196c93e6b..72bf7f2f71 100644
--- a/src/test/test_helpers.h
+++ b/src/test/test_helpers.h
@@ -4,6 +4,8 @@
#ifndef TOR_TEST_HELPERS_H
#define TOR_TEST_HELPERS_H
+#define BUFFERS_PRIVATE
+
#include "core/or/or.h"
const char *get_yesterday_date_str(void);
@@ -18,6 +20,7 @@ void helper_setup_fake_routerlist(void);
#define GET(path) "GET " path " HTTP/1.0\r\n\r\n"
void connection_write_to_buf_mock(const char *string, size_t len,
connection_t *conn, int compressed);
+char *buf_get_contents(buf_t *buf, size_t *sz_out);
int mock_tor_addr_lookup__fail_on_bad_addrs(const char *name,
uint16_t family, tor_addr_t *out);
diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c
index c91e82ed4a..31a2816c12 100644
--- a/src/test/test_hs_client.c
+++ b/src/test/test_hs_client.c
@@ -8,7 +8,7 @@
#define CONFIG_PRIVATE
#define CRYPTO_PRIVATE
-#define MAIN_PRIVATE
+#define MAINLOOP_PRIVATE
#define HS_CLIENT_PRIVATE
#define TOR_CHANNEL_INTERNAL_
#define CIRCUITBUILD_PRIVATE
@@ -26,7 +26,7 @@
#include "lib/crypt_ops/crypto_dh.h"
#include "core/or/channeltls.h"
#include "feature/dircache/directory.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/nodelist.h"
#include "feature/nodelist/routerset.h"
@@ -523,6 +523,9 @@ mock_connection_mark_unattached_ap_(entry_connection_t *conn, int endreason,
(void) line;
(void) file;
conn->edge_.end_reason = endreason;
+ /* This function ultimately will flag this so make sure we do also in the
+ * MOCK one so we can assess closed connections vs open ones. */
+ conn->edge_.base_.marked_for_close = 1;
}
static void
@@ -771,6 +774,117 @@ test_config_client_authorization(void *arg)
UNMOCK(check_private_dir);
}
+static entry_connection_t *
+helper_build_socks_connection(const ed25519_public_key_t *service_pk,
+ int conn_state)
+{
+ entry_connection_t *socks = entry_connection_new(CONN_TYPE_AP, AF_INET);
+ ENTRY_TO_EDGE_CONN(socks)->hs_ident = hs_ident_edge_conn_new(service_pk);
+ TO_CONN(ENTRY_TO_EDGE_CONN(socks))->state = conn_state;
+ smartlist_add(get_connection_array(), &socks->edge_.base_);
+ return socks;
+}
+
+static void
+test_desc_has_arrived_cleanup(void *arg)
+{
+ /* The goal of this test is to make sure we clean up everything in between
+ * two descriptors from the same .onion. Because intro points can change
+ * from one descriptor to another, once we received a new descriptor, we
+ * need to cleanup the remaining circuits so they aren't used or selected
+ * when establishing a connection with the newly stored descriptor.
+ *
+ * This test was created because of #27410. */
+
+ int ret;
+ char *desc_str = NULL;
+ hs_descriptor_t *desc = NULL;
+ const hs_descriptor_t *cached_desc;
+ ed25519_keypair_t signing_kp;
+ entry_connection_t *socks1 = NULL, *socks2 = NULL;
+ hs_ident_dir_conn_t hs_dir_ident;
+
+ (void) arg;
+
+ hs_init();
+
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus);
+ MOCK(connection_mark_unattached_ap_,
+ mock_connection_mark_unattached_ap_);
+ MOCK(router_have_minimum_dir_info,
+ mock_router_have_minimum_dir_info_true);
+
+ /* Set consensus time before our time so the cache lookup can always
+ * validate that the entry is not expired. */
+ parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC", &mock_ns.valid_after);
+ parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC", &mock_ns.fresh_until);
+ parse_rfc1123_time("Sat, 26 Oct 1985 16:00:00 UTC", &mock_ns.valid_until);
+
+ /* Build a descriptor for a specific .onion. */
+ ret = ed25519_keypair_generate(&signing_kp, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
+ tt_assert(desc);
+ ret = hs_desc_encode_descriptor(desc, &signing_kp, NULL, &desc_str);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* Store in the client cache. */
+ ret = hs_cache_store_as_client(desc_str, &signing_kp.pubkey);
+ tt_int_op(ret, OP_EQ, 0);
+ cached_desc = hs_cache_lookup_as_client(&signing_kp.pubkey);
+ tt_assert(cached_desc);
+ hs_helper_desc_equal(desc, cached_desc);
+
+ /* Create two SOCKS connection for the same .onion both in the waiting for a
+ * descriptor state. */
+ socks1 = helper_build_socks_connection(&signing_kp.pubkey,
+ AP_CONN_STATE_RENDDESC_WAIT);
+ tt_assert(socks1);
+ socks2 = helper_build_socks_connection(&signing_kp.pubkey,
+ AP_CONN_STATE_RENDDESC_WAIT);
+ tt_assert(socks2);
+
+ /* Now, we'll make the intro points in the current descriptor unusable so
+ * the hs_client_desc_has_arrived() will take the right code path that we
+ * want to test that is the fetched descriptor has bad intro points. */
+ SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
+ hs_desc_intro_point_t *, ip) {
+ hs_cache_client_intro_state_note(&signing_kp.pubkey,
+ &ip->auth_key_cert->signed_key,
+ INTRO_POINT_FAILURE_GENERIC);
+ } SMARTLIST_FOREACH_END(ip);
+
+ /* Simulate that a new descriptor just arrived. We should have both of our
+ * SOCKS connection to be ended with a resolved failed. */
+ hs_ident_dir_conn_init(&signing_kp.pubkey,
+ &desc->plaintext_data.blinded_pubkey, &hs_dir_ident);
+ hs_client_desc_has_arrived(&hs_dir_ident);
+ tt_int_op(socks1->edge_.end_reason, OP_EQ, END_STREAM_REASON_RESOLVEFAILED);
+ /* XXX: MUST work with OP_EQ. */
+ tt_int_op(socks2->edge_.end_reason, OP_EQ, END_STREAM_REASON_RESOLVEFAILED);
+
+ /* Now let say tor cleans up the intro state cache which resets all intro
+ * point failure count. */
+ hs_cache_client_intro_state_purge();
+
+ /* Retrying all SOCKS which should basically do nothing since we don't have
+ * any pending SOCKS connection in AP_CONN_STATE_RENDDESC_WAIT state. */
+ /* XXX: BUG() is triggered here, shouldn't if socks2 wasn't alive. */
+ retry_all_socks_conn_waiting_for_desc();
+
+ done:
+ connection_free_minimal(ENTRY_TO_CONN(socks1));
+ connection_free_minimal(ENTRY_TO_CONN(socks2));
+ hs_descriptor_free(desc);
+ tor_free(desc_str);
+ hs_free_all();
+
+ UNMOCK(networkstatus_get_live_consensus);
+ UNMOCK(connection_mark_unattached_ap_);
+ UNMOCK(router_have_minimum_dir_info);
+}
+
struct testcase_t hs_client_tests[] = {
{ "e2e_rend_circuit_setup_legacy", test_e2e_rend_circuit_setup_legacy,
TT_FORK, NULL, NULL },
@@ -786,5 +900,8 @@ struct testcase_t hs_client_tests[] = {
NULL, NULL },
{ "config_client_authorization", test_config_client_authorization,
TT_FORK, NULL, NULL },
+ { "desc_has_arrived_cleanup", test_desc_has_arrived_cleanup,
+ TT_FORK, NULL, NULL },
+
END_OF_TESTCASES
};
diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c
index bceeafd149..334bde3e7b 100644
--- a/src/test/test_hs_service.c
+++ b/src/test/test_hs_service.c
@@ -10,12 +10,13 @@
#define CIRCUITLIST_PRIVATE
#define CONFIG_PRIVATE
#define CONNECTION_PRIVATE
+#define CONNECTION_EDGE_PRIVATE
#define CRYPTO_PRIVATE
#define HS_COMMON_PRIVATE
#define HS_SERVICE_PRIVATE
#define HS_INTROPOINT_PRIVATE
#define HS_CIRCUIT_PRIVATE
-#define MAIN_PRIVATE
+#define MAINLOOP_PRIVATE
#define NETWORKSTATUS_PRIVATE
#define STATEFILE_PRIVATE
#define TOR_CHANNEL_INTERNAL_
@@ -33,6 +34,9 @@
#include "core/or/circuitbuild.h"
#include "core/or/circuitlist.h"
#include "core/or/circuituse.h"
+#include "core/mainloop/connection.h"
+#include "core/or/connection_edge.h"
+#include "core/or/edge_connection_st.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/fs/dir.h"
#include "feature/dirauth/dirvote.h"
@@ -49,7 +53,7 @@
#include "feature/hs/hs_circuitmap.h"
#include "feature/hs/hs_service.h"
#include "feature/hs/hs_client.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/rend/rendservice.h"
#include "app/config/statefile.h"
#include "feature/dirauth/shared_random_state.h"
@@ -2003,6 +2007,96 @@ test_authorized_client_config_equal(void *arg)
tor_free(config2);
}
+/** Test that client circuit ID gets correctly exported */
+static void
+test_export_client_circuit_id(void *arg)
+{
+ origin_circuit_t *or_circ = NULL;
+ size_t sz;
+ char *cp1=NULL, *cp2=NULL;
+ connection_t *conn = NULL;
+
+ (void) arg;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ hs_service_init();
+
+ /* Create service */
+ hs_service_t *service = helper_create_service();
+ /* Check that export circuit ID detection works */
+ service->config.circuit_id_protocol = HS_CIRCUIT_ID_PROTOCOL_NONE;
+ tt_int_op(0, OP_EQ,
+ hs_service_exports_circuit_id(&service->keys.identity_pk));
+ service->config.circuit_id_protocol = HS_CIRCUIT_ID_PROTOCOL_HAPROXY;
+ tt_int_op(1, OP_EQ,
+ hs_service_exports_circuit_id(&service->keys.identity_pk));
+
+ /* Create client connection */
+ conn = test_conn_get_connection(AP_CONN_STATE_CIRCUIT_WAIT, CONN_TYPE_AP, 0);
+
+ /* Create client edge conn hs_ident */
+ edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
+ edge_conn->hs_ident = hs_ident_edge_conn_new(&service->keys.identity_pk);
+ edge_conn->hs_ident->orig_virtual_port = 42;
+
+ /* Create rend circuit */
+ or_circ = origin_circuit_new();
+ or_circ->base_.purpose = CIRCUIT_PURPOSE_C_REND_JOINED;
+ edge_conn->on_circuit = TO_CIRCUIT(or_circ);
+ or_circ->global_identifier = 666;
+
+ /* Export circuit ID */
+ export_hs_client_circuit_id(edge_conn, service->config.circuit_id_protocol);
+
+ /* Check contents */
+ cp1 = buf_get_contents(conn->outbuf, &sz);
+ tt_str_op(cp1, OP_EQ,
+ "PROXY TCP6 fc00:dead:beef:4dad::0:29a ::1 666 42\r\n");
+
+ /* Change circ GID and see that the reported circuit ID also changes */
+ or_circ->global_identifier = 22;
+
+ /* check changes */
+ export_hs_client_circuit_id(edge_conn, service->config.circuit_id_protocol);
+ cp2 = buf_get_contents(conn->outbuf, &sz);
+ tt_str_op(cp1, OP_NE, cp2);
+ tor_free(cp1);
+
+ /* Check that GID with UINT32_MAX works. */
+ or_circ->global_identifier = UINT32_MAX;
+
+ export_hs_client_circuit_id(edge_conn, service->config.circuit_id_protocol);
+ cp1 = buf_get_contents(conn->outbuf, &sz);
+ tt_str_op(cp1, OP_EQ,
+ "PROXY TCP6 fc00:dead:beef:4dad::ffff:ffff ::1 65535 42\r\n");
+ tor_free(cp1);
+
+ /* Check that GID with UINT16_MAX works. */
+ or_circ->global_identifier = UINT16_MAX;
+
+ export_hs_client_circuit_id(edge_conn, service->config.circuit_id_protocol);
+ cp1 = buf_get_contents(conn->outbuf, &sz);
+ tt_str_op(cp1, OP_EQ,
+ "PROXY TCP6 fc00:dead:beef:4dad::0:ffff ::1 65535 42\r\n");
+ tor_free(cp1);
+
+ /* Check that GID with UINT16_MAX + 7 works. */
+ or_circ->global_identifier = UINT16_MAX + 7;
+
+ export_hs_client_circuit_id(edge_conn, service->config.circuit_id_protocol);
+ cp1 = buf_get_contents(conn->outbuf, &sz);
+ tt_str_op(cp1, OP_EQ, "PROXY TCP6 fc00:dead:beef:4dad::1:6 ::1 6 42\r\n");
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ circuit_free_(TO_CIRCUIT(or_circ));
+ connection_free_minimal(conn);
+ hs_service_free(service);
+ tor_free(cp1);
+ tor_free(cp2);
+}
+
struct testcase_t hs_service_tests[] = {
{ "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup, TT_FORK,
NULL, NULL },
@@ -2044,6 +2138,8 @@ struct testcase_t hs_service_tests[] = {
NULL, NULL },
{ "authorized_client_config_equal", test_authorized_client_config_equal,
TT_FORK, NULL, NULL },
+ { "export_client_circuit_id", test_export_client_circuit_id, TT_FORK,
+ NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_mainloop.c b/src/test/test_mainloop.c
index f85c224ae9..92ce2e9918 100644
--- a/src/test/test_mainloop.c
+++ b/src/test/test_mainloop.c
@@ -10,7 +10,7 @@
#include "test/log_test_helpers.h"
#include "core/or/or.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
static const uint64_t BILLION = 1000000000;
diff --git a/src/test/test_oos.c b/src/test/test_oos.c
index 5f9942d8ae..b8ff3030a2 100644
--- a/src/test/test_oos.c
+++ b/src/test/test_oos.c
@@ -10,7 +10,7 @@
#include "core/mainloop/connection.h"
#include "core/or/connection_or.h"
#include "feature/dircache/directory.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "test/test.h"
#include "feature/dircommon/dir_connection_st.h"
diff --git a/src/test/test_options.c b/src/test/test_options.c
index 56b7f3cf0f..a4de0e992a 100644
--- a/src/test/test_options.c
+++ b/src/test/test_options.c
@@ -12,7 +12,7 @@
#define ROUTERSET_PRIVATE
#include "feature/nodelist/routerset.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "test/log_test_helpers.h"
#include "lib/sandbox/sandbox.h"
diff --git a/src/test/test_periodic_event.c b/src/test/test_periodic_event.c
index b447ae8888..7804a9d8fb 100644
--- a/src/test/test_periodic_event.c
+++ b/src/test/test_periodic_event.c
@@ -9,7 +9,7 @@
#define CONFIG_PRIVATE
#define HS_SERVICE_PRIVATE
-#define MAIN_PRIVATE
+#define MAINLOOP_PRIVATE
#include "test/test.h"
#include "test/test_helpers.h"
@@ -18,7 +18,7 @@
#include "app/config/config.h"
#include "feature/hibernate/hibernate.h"
#include "feature/hs/hs_service.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "core/mainloop/periodic.h"
/** Helper function: This is replaced in some tests for the event callbacks so
diff --git a/src/test/test_protover.c b/src/test/test_protover.c
index 123faccdab..b835e28ab5 100644
--- a/src/test/test_protover.c
+++ b/src/test/test_protover.c
@@ -554,6 +554,10 @@ test_protover_vote_roundtrip(void *args)
const char *input;
const char *expected_output;
} examples[] = {
+ { "Risqu\u00e9=1", NULL },
+ { ",,,=1", NULL },
+ { "\xc1=1", NULL },
+ { "Foo_Bar=1", NULL },
{ "Fkrkljdsf", NULL },
{ "Zn=4294967295", NULL },
{ "Zn=4294967295-1", NULL },
diff --git a/src/test/test_relay.c b/src/test/test_relay.c
index 65b9a2f940..4311392be8 100644
--- a/src/test/test_relay.c
+++ b/src/test/test_relay.c
@@ -1,14 +1,17 @@
/* Copyright (c) 2014-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
-#include "core/or/or.h"
#define CIRCUITBUILD_PRIVATE
+#define RELAY_PRIVATE
+#define REPHIST_PRIVATE
+#include "core/or/or.h"
#include "core/or/circuitbuild.h"
#include "core/or/circuitlist.h"
#include "core/or/channeltls.h"
#include "feature/stats/rephist.h"
-#define RELAY_PRIVATE
#include "core/or/relay.h"
+#include "feature/stats/rephist.h"
+#include "lib/container/order.h"
/* For init/free stuff */
#include "core/or/scheduler.h"
@@ -22,9 +25,6 @@
static or_circuit_t * new_fake_orcirc(channel_t *nchan, channel_t *pchan);
static void test_relay_append_cell_to_circuit_queue(void *arg);
-uint64_t find_largest_max(bw_array_t *b);
-void commit_max(bw_array_t *b);
-void advance_obs(bw_array_t *b);
static or_circuit_t *
new_fake_orcirc(channel_t *nchan, channel_t *pchan)
@@ -113,7 +113,7 @@ test_relay_close_circuit(void *arg)
tt_int_op(new_count, OP_EQ, old_count + 1);
/* Ensure our write totals are 0 */
- tt_int_op(find_largest_max(write_array), OP_EQ, 0);
+ tt_u64_op(find_largest_max(write_array), OP_EQ, 0);
/* Mark the circuit for close */
circuit_mark_for_close(TO_CIRCUIT(orcirc), 0);
@@ -122,7 +122,7 @@ test_relay_close_circuit(void *arg)
advance_obs(write_array);
commit_max(write_array);
/* Check for two cells plus overhead */
- tt_int_op(find_largest_max(write_array), OP_EQ,
+ tt_u64_op(find_largest_max(write_array), OP_EQ,
2*(get_cell_network_size(nchan->wide_circ_ids)
+TLS_PER_CELL_OVERHEAD));
diff --git a/src/test/test_relaycell.c b/src/test/test_relaycell.c
index 2d020ec472..0f72a575ab 100644
--- a/src/test/test_relaycell.c
+++ b/src/test/test_relaycell.c
@@ -6,7 +6,7 @@
#define RELAY_PRIVATE
#define CIRCUITLIST_PRIVATE
#include "core/or/or.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "app/config/config.h"
#include "core/mainloop/connection.h"
#include "lib/crypt_ops/crypto_cipher.h"
diff --git a/src/test/test_router.c b/src/test/test_router.c
index 533135669f..921ec42904 100644
--- a/src/test/test_router.c
+++ b/src/test/test_router.c
@@ -9,7 +9,7 @@
#include "core/or/or.h"
#include "app/config/config.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/hibernate/hibernate.h"
#include "feature/nodelist/routerinfo_st.h"
#include "feature/nodelist/routerlist.h"
diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c
index 89d1f4f90f..f629596c5b 100644
--- a/src/test/test_routerlist.c
+++ b/src/test/test_routerlist.c
@@ -12,6 +12,7 @@
#define HIBERNATE_PRIVATE
#define NETWORKSTATUS_PRIVATE
#define ROUTERLIST_PRIVATE
+#define NODE_SELECT_PRIVATE
#define TOR_UNIT_TESTING
#include "core/or/or.h"
#include "app/config/config.h"
@@ -27,6 +28,8 @@
#include "feature/nodelist/nodelist.h"
#include "core/or/policies.h"
#include "feature/relay/router.h"
+#include "feature/nodelist/authcert.h"
+#include "feature/nodelist/node_select.h"
#include "feature/nodelist/routerlist.h"
#include "feature/nodelist/routerset.h"
#include "feature/nodelist/routerparse.h"
diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c
index 70adf580ab..725724aa56 100644
--- a/src/test/test_shared_random.c
+++ b/src/test/test_shared_random.c
@@ -17,7 +17,8 @@
#include "feature/nodelist/networkstatus.h"
#include "feature/relay/router.h"
#include "feature/relay/routerkeys.h"
-#include "feature/nodelist/routerlist.h"
+#include "feature/nodelist/authcert.h"
+#include "feature/nodelist/dirlist.h"
#include "feature/nodelist/routerparse.h"
#include "feature/hs_common/shared_random_client.h"
#include "feature/dircommon/voting_schedule.h"
diff --git a/src/test/test_status.c b/src/test/test_status.c
index 15c406d2ff..46dbd9dcd1 100644
--- a/src/test/test_status.c
+++ b/src/test/test_status.c
@@ -21,7 +21,7 @@
#include "feature/stats/rephist.h"
#include "core/or/relay.h"
#include "feature/relay/router.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "feature/nodelist/nodelist.h"
#include "app/config/statefile.h"
#include "lib/tls/tortls.h"
diff --git a/src/test/test_workqueue.c b/src/test/test_workqueue.c
index 9d48d92773..28fbd6fb9f 100644
--- a/src/test/test_workqueue.c
+++ b/src/test/test_workqueue.c
@@ -5,7 +5,7 @@
#include "core/or/or.h"
#include "lib/thread/threads.h"
-#include "core/crypto/onion.h"
+#include "core/or/onion.h"
#include "lib/evloop/workqueue.h"
#include "lib/crypt_ops/crypto_curve25519.h"
#include "lib/crypt_ops/crypto_rand.h"
diff --git a/src/test/testing_common.c b/src/test/testing_common.c
index 5d4c2f15af..2d00ecb651 100644
--- a/src/test/testing_common.c
+++ b/src/test/testing_common.c
@@ -8,7 +8,7 @@
* \brief Common pieces to implement unit tests.
**/
-#define MAIN_PRIVATE
+#define MAINLOOP_PRIVATE
#include "orconfig.h"
#include "core/or/or.h"
#include "feature/control/control.h"
@@ -20,7 +20,7 @@
#include "lib/err/backtrace.h"
#include "test/test.h"
#include "core/or/channelpadding.h"
-#include "core/mainloop/main.h"
+#include "core/mainloop/mainloop.h"
#include "lib/compress/compress.h"
#include "lib/evloop/compat_libevent.h"
#include "lib/crypt_ops/crypto_init.h"
diff --git a/src/tools/tor-print-ed-signing-cert.c b/src/tools/tor-print-ed-signing-cert.c
index 0f64059d84..9bb4db0a6e 100644
--- a/src/tools/tor-print-ed-signing-cert.c
+++ b/src/tools/tor-print-ed-signing-cert.c
@@ -6,7 +6,7 @@
#include <string.h>
#include <time.h>
-#include "ed25519_cert.h"
+#include "trunnel/ed25519_cert.h"
#include "lib/cc/torint.h" /* TOR_PRIdSZ */
#include "lib/crypt_ops/crypto_format.h"
#include "lib/malloc/malloc.h"
diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h
index 457cfa7946..09a29fa5ac 100644
--- a/src/win32/orconfig.h
+++ b/src/win32/orconfig.h
@@ -218,7 +218,7 @@
#define USING_TWOS_COMPLEMENT
/* Version number of package */
-#define VERSION "0.3.5.1-alpha-dev"
+#define VERSION "0.3.5.2-alpha-dev"