aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app/config/config.c62
-rw-r--r--src/app/include.am6
-rw-r--r--src/app/main/main.c42
-rw-r--r--src/core/mainloop/connection.c9
-rw-r--r--src/core/mainloop/mainloop.c6
-rw-r--r--src/core/or/circuitlist.h4
-rw-r--r--src/core/or/command.c29
-rw-r--r--src/core/or/connection_edge.c2
-rw-r--r--src/core/or/connection_or.c33
-rw-r--r--src/core/or/or_circuit_st.h6
-rw-r--r--src/core/or/sendme.c4
-rw-r--r--src/core/proto/proto_socks.c7
-rw-r--r--src/ext/keccak-tiny/keccak-tiny.h2
-rw-r--r--src/feature/client/bridges.c11
-rw-r--r--src/feature/client/bridges.h1
-rw-r--r--src/feature/client/transports.c22
-rw-r--r--src/feature/client/transports.h1
-rw-r--r--src/feature/hs/hs_cache.c6
-rw-r--r--src/feature/hs_common/shared_random_client.c21
-rw-r--r--src/feature/nodelist/networkstatus.c2
-rw-r--r--src/feature/nodelist/routerlist.c54
-rw-r--r--src/feature/relay/ext_orport.c58
-rw-r--r--src/feature/relay/ext_orport.h7
-rw-r--r--src/feature/relay/relay_config.c17
-rw-r--r--src/feature/relay/relay_find_addr.c68
-rw-r--r--src/feature/relay/relay_find_addr.h2
-rw-r--r--src/feature/relay/router.c49
-rw-r--r--src/feature/relay/router.h1
-rw-r--r--src/feature/relay/selftest.c7
-rw-r--r--src/feature/rend/rendcache.c2
-rw-r--r--src/feature/stats/rephist.c344
-rw-r--r--src/feature/stats/rephist.h53
-rw-r--r--src/lib/crypt_ops/compat_openssl.h3
-rw-r--r--src/lib/crypt_ops/crypto_openssl_mgt.c2
-rw-r--r--src/lib/fs/conffile.c41
-rw-r--r--src/lib/fs/path.c15
-rw-r--r--src/lib/sandbox/sandbox.c39
-rw-r--r--src/lib/sandbox/sandbox.h5
-rw-r--r--src/lib/tls/tortls_openssl.c2
-rw-r--r--src/test/hs_build_address.py14
-rw-r--r--src/test/hs_ntor_ref.py6
-rw-r--r--src/test/hs_test_helpers.c19
-rw-r--r--src/test/hs_test_helpers.h4
-rw-r--r--src/test/test_config.c44
-rw-r--r--src/test/test_extorport.c55
-rwxr-xr-xsrc/test/test_key_expiration.sh2
-rwxr-xr-xsrc/test/test_keygen.sh14
-rw-r--r--src/test/test_stats.c131
-rw-r--r--src/win32/orconfig.h2
49 files changed, 945 insertions, 391 deletions
diff --git a/src/app/config/config.c b/src/app/config/config.c
index 458067af4d..bf2f49ead4 100644
--- a/src/app/config/config.c
+++ b/src/app/config/config.c
@@ -2189,6 +2189,23 @@ options_act,(const or_options_t *old_options))
}
}
+ /* Validate that we actually have a configured transport for a Bridge line
+ * that has one. This is done here because we require the bridge and
+ * transport to be added to the global list before doing the validation.
+ *
+ * In an ideal world, pt_parse_transport_line() would actually return a
+ * transport_t object so we could inspect it and thus do this step at
+ * validation time. */
+ SMARTLIST_FOREACH_BEGIN(bridge_list_get(), const bridge_info_t *, bi) {
+ const char *bi_transport_name = bridget_get_transport_name(bi);
+ if (bi_transport_name && (!transport_get_by_name(bi_transport_name) &&
+ !managed_proxy_has_transport(bi_transport_name))) {
+ log_warn(LD_CONFIG, "Bridge line with transport %s is missing a "
+ "ClientTransportPlugin line", bi_transport_name);
+ return -1;
+ }
+ } SMARTLIST_FOREACH_END(bi);
+
if (options_act_server_transport(old_options) < 0)
return -1;
@@ -2466,6 +2483,7 @@ static const struct {
.command=CMD_DUMP_CONFIG,
.quiet=QUIET_SILENT },
{ .name="--list-fingerprint",
+ .takes_argument=ARGUMENT_OPTIONAL,
.command=CMD_LIST_FINGERPRINT },
{ .name="--keygen",
.command=CMD_KEYGEN },
@@ -2584,8 +2602,11 @@ config_parse_commandline(int argc, char **argv, int ignore_errors)
parsed_cmdline_free(result);
return NULL;
}
- } else if (want_arg == ARGUMENT_OPTIONAL && is_last) {
+ } else if (want_arg == ARGUMENT_OPTIONAL &&
+ /* optional arguments may never start with '-'. */
+ (is_last || argv[i+1][0] == '-')) {
arg = tor_strdup("");
+ want_arg = ARGUMENT_NONE; // prevent skipping the next flag.
} else {
arg = (want_arg != ARGUMENT_NONE) ? tor_strdup(argv[i+1]) :
tor_strdup("");
@@ -3274,7 +3295,7 @@ options_validate_cb(const void *old_options_, void *options_, char **msg)
}
#else /* defined(HAVE_SYS_UN_H) */
if (options->ControlSocketsGroupWritable && !options->ControlSocket) {
- *msg = tor_strdup("Setting ControlSocketGroupWritable without setting "
+ *msg = tor_strdup("Setting ControlSocketsGroupWritable without setting "
"a ControlSocket makes no sense.");
return -1;
}
@@ -4485,6 +4506,25 @@ options_init_from_torrc(int argc, char **argv)
if (config_line_find(cmdline_only_options, "--version")) {
printf("Tor version %s.\n",get_version());
+ printf("Tor is running on %s with Libevent %s, "
+ "%s %s, Zlib %s, Liblzma %s, Libzstd %s and %s %s as libc.\n",
+ 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",
+ tor_libc_get_name() ?
+ tor_libc_get_name() : "Unknown",
+ tor_libc_get_version_str());
+ printf("Tor compiled with %s version %s\n",
+ strcmp(COMPILER_VENDOR, "gnu") == 0?
+ COMPILER:COMPILER_VENDOR, COMPILER_VERSION);
+
return 1;
}
@@ -5934,13 +5974,12 @@ port_parse_config(smartlist_t *out,
char *unix_socket_path = NULL;
port_cfg_t *cfg = NULL;
bool addr_is_explicit = false;
- int family = -1;
-
- /* Parse default address. This can fail for Unix socket for instance so
- * family can be -1 and the default_addr will be made UNSPEC. */
tor_addr_t default_addr = TOR_ADDR_NULL;
+
+ /* Parse default address. This can fail for Unix socket so the default_addr
+ * will simply be made UNSPEC. */
if (defaultaddr) {
- family = tor_addr_parse(&default_addr, defaultaddr);
+ tor_addr_parse(&default_addr, defaultaddr);
}
/* If there's no FooPort, then maybe make a default one. */
@@ -6018,7 +6057,6 @@ port_parse_config(smartlist_t *out,
port = 1;
} else if (!strcasecmp(addrport, "auto")) {
port = CFG_AUTO_PORT;
- tor_assert(family >= 0);
tor_addr_copy(&addr, &default_addr);
} else if (!strcasecmpend(addrport, ":auto")) {
char *addrtmp = tor_strndup(addrport, strlen(addrport)-5);
@@ -6032,21 +6070,15 @@ port_parse_config(smartlist_t *out,
tor_free(addrtmp);
} else {
/* Try parsing integer port before address, because, who knows?
- "9050" might be a valid address. */
+ * "9050" might be a valid address. */
port = (int) tor_parse_long(addrport, 10, 0, 65535, &ok, NULL);
if (ok) {
- tor_assert(family >= 0);
tor_addr_copy(&addr, &default_addr);
} else if (tor_addr_port_lookup(addrport, &addr, &ptmp) == 0) {
if (ptmp == 0) {
log_warn(LD_CONFIG, "%sPort line has address but no port", portname);
goto err;
}
- if (family != -1 && tor_addr_family(&addr) != family) {
- /* This means we are parsing another ORPort family but we are
- * attempting to find the default address' family ORPort. */
- goto ignore;
- }
port = ptmp;
addr_is_explicit = true;
} else {
diff --git a/src/app/include.am b/src/app/include.am
index b81fad0276..2e2180deca 100644
--- a/src/app/include.am
+++ b/src/app/include.am
@@ -14,7 +14,8 @@ src_app_tor_SOURCES = src/app/main/tor_main.c
# This seems to matter nowhere but on windows, but I assure you that it
# matters a lot there, and is quite hard to debug if you forget to do it.
-src_app_tor_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) @TOR_LDFLAGS_libevent@
+src_app_tor_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) \
+ @TOR_LDFLAGS_libevent@ @TOR_STATIC_LDFLAGS@
src_app_tor_LDADD = libtor.a \
$(rust_ldadd) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ $(TOR_LIBS_CRYPTLIB) \
@@ -26,7 +27,8 @@ if COVERAGE_ENABLED
src_app_tor_cov_SOURCES = $(src_app_tor_SOURCES)
src_app_tor_cov_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_app_tor_cov_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
-src_app_tor_cov_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) @TOR_LDFLAGS_libevent@
+src_app_tor_cov_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) \
+ @TOR_LDFLAGS_libevent@ @TOR_STATIC_LDFLAGS@
src_app_tor_cov_LDADD = src/test/libtor-testing.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ $(TOR_LIBS_CRYPTLIB) \
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ \
diff --git a/src/app/main/main.c b/src/app/main/main.c
index 589d365add..e7ffb31b4f 100644
--- a/src/app/main/main.c
+++ b/src/app/main/main.c
@@ -58,6 +58,7 @@
#include "feature/stats/rephist.h"
#include "lib/compress/compress.h"
#include "lib/buf/buffers.h"
+#include "lib/crypt_ops/crypto_format.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_s2k.h"
#include "lib/net/resolve.h"
@@ -735,29 +736,52 @@ tor_remove_file(const char *filename)
static int
do_list_fingerprint(void)
{
- char buf[FINGERPRINT_LEN+1];
+ const or_options_t *options = get_options();
+ const char *arg = options->command_arg;
+ char rsa[FINGERPRINT_LEN + 1];
crypto_pk_t *k;
- const char *nickname = get_options()->Nickname;
+ const ed25519_public_key_t *edkey;
+ const char *nickname = options->Nickname;
sandbox_disable_getaddrinfo_cache();
- if (!server_mode(get_options())) {
+
+ bool show_rsa = !strcmp(arg, "") || !strcmp(arg, "rsa");
+ bool show_ed25519 = !strcmp(arg, "ed25519");
+ if (!show_rsa && !show_ed25519) {
+ log_err(LD_GENERAL,
+ "If you give a key type, you must specify 'rsa' or 'ed25519'. Exiting.");
+ return -1;
+ }
+
+ if (!server_mode(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.");
+ log_err(LD_GENERAL, "Error initializing keys; exiting.");
return -1;
}
if (!(k = get_server_identity_key())) {
- log_err(LD_GENERAL,"Error: missing identity key.");
+ log_err(LD_GENERAL, "Error: missing RSA identity key.");
+ return -1;
+ }
+ if (crypto_pk_get_fingerprint(k, rsa, 1) < 0) {
+ log_err(LD_BUG, "Error computing RSA fingerprint");
return -1;
}
- if (crypto_pk_get_fingerprint(k, buf, 1)<0) {
- log_err(LD_BUG, "Error computing fingerprint");
+ if (!(edkey = get_master_identity_key())) {
+ log_err(LD_GENERAL,"Error: missing ed25519 identity key.");
return -1;
}
- printf("%s %s\n", nickname, buf);
+ if (show_rsa) {
+ printf("%s %s\n", nickname, rsa);
+ }
+ if (show_ed25519) {
+ char ed25519[ED25519_BASE64_LEN + 1];
+ digest256_to_base64(ed25519, (const char *) edkey->pubkey);
+ printf("%s %s\n", nickname, ed25519);
+ }
return 0;
}
@@ -1080,6 +1104,7 @@ sandbox_init_filter(void)
OPEN_DATADIR2_SUFFIX("stats", "buffer-stats", ".tmp");
OPEN_DATADIR2_SUFFIX("stats", "conn-stats", ".tmp");
OPEN_DATADIR2_SUFFIX("stats", "hidserv-stats", ".tmp");
+ OPEN_DATADIR2_SUFFIX("stats", "hidserv-v3-stats", ".tmp");
OPEN_DATADIR("approved-routers");
OPEN_DATADIR_SUFFIX("fingerprint", ".tmp");
@@ -1105,6 +1130,7 @@ sandbox_init_filter(void)
RENAME_SUFFIX2("stats", "buffer-stats", ".tmp");
RENAME_SUFFIX2("stats", "conn-stats", ".tmp");
RENAME_SUFFIX2("stats", "hidserv-stats", ".tmp");
+ RENAME_SUFFIX2("stats", "hidserv-v3-stats", ".tmp");
RENAME_SUFFIX("hashed-fingerprint", ".tmp");
RENAME_SUFFIX("router-stability", ".tmp");
diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c
index 3d551c4ba8..c0c56a8b77 100644
--- a/src/core/mainloop/connection.c
+++ b/src/core/mainloop/connection.c
@@ -948,7 +948,6 @@ connection_free_minimal(connection_t *conn)
connection_or_clear_identity(TO_OR_CONN(conn));
}
if (conn->type == CONN_TYPE_OR || conn->type == CONN_TYPE_EXT_OR) {
- connection_or_remove_from_ext_or_id_map(TO_OR_CONN(conn));
tor_free(TO_OR_CONN(conn)->ext_or_conn_id);
tor_free(TO_OR_CONN(conn)->ext_or_auth_correct_client_hash);
tor_free(TO_OR_CONN(conn)->ext_or_transport);
@@ -1718,13 +1717,6 @@ connection_listener_new(const struct sockaddr *listensockaddr,
}
}
- /* Force IPv4 and IPv6 traffic on for non-SOCKSPorts.
- * Forcing options on isn't a good idea, see #32994 and #33607. */
- if (type != CONN_TYPE_AP_LISTENER) {
- lis_conn->entry_cfg.ipv4_traffic = 1;
- lis_conn->entry_cfg.ipv6_traffic = 1;
- }
-
if (connection_add(conn) < 0) { /* no space, forget it */
log_warn(LD_NET,"connection_add for listener failed. Giving up.");
goto err;
@@ -5824,7 +5816,6 @@ connection_free_all(void)
/* Unlink everything from the identity map. */
connection_or_clear_identity_map();
- connection_or_clear_ext_or_id_map();
/* Clear out our list of broken connections */
clear_broken_connection_map(0);
diff --git a/src/core/mainloop/mainloop.c b/src/core/mainloop/mainloop.c
index 6abfe0ff39..71ab78c51f 100644
--- a/src/core/mainloop/mainloop.c
+++ b/src/core/mainloop/mainloop.c
@@ -1937,7 +1937,11 @@ write_stats_file_callback(time_t now, const or_options_t *options)
next_time_to_write_stats_files = next_write;
}
if (options->HiddenServiceStatistics) {
- time_t next_write = rep_hist_hs_stats_write(now);
+ time_t next_write = rep_hist_hs_stats_write(now, false);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
+
+ next_write = rep_hist_hs_stats_write(now, true);
if (next_write && next_write < next_time_to_write_stats_files)
next_time_to_write_stats_files = next_write;
}
diff --git a/src/core/or/circuitlist.h b/src/core/or/circuitlist.h
index fd7e22e4c0..3178e6cd0d 100644
--- a/src/core/or/circuitlist.h
+++ b/src/core/or/circuitlist.h
@@ -114,7 +114,9 @@
#define CIRCUIT_PURPOSE_S_HSDIR_POST 20
#define CIRCUIT_PURPOSE_S_HS_MAX_ 20
-/** A testing circuit; not meant to be used for actual traffic. */
+/** A testing circuit; not meant to be used for actual traffic. It is used for
+ * bandwidth measurement, reachability test and address discovery from an
+ * authority using the NETINFO cell. */
#define CIRCUIT_PURPOSE_TESTING 21
/** A controller made this circuit and Tor should not use it. */
#define CIRCUIT_PURPOSE_CONTROLLER 22
diff --git a/src/core/or/command.c b/src/core/or/command.c
index 9226309ff7..e2bab87def 100644
--- a/src/core/or/command.c
+++ b/src/core/or/command.c
@@ -331,6 +331,13 @@ command_process_create_cell(cell_t *cell, channel_t *chan)
return;
}
+ /* Mark whether this circuit used TAP in case we need to use this
+ * information for onion service statistics later on. */
+ if (create_cell->handshake_type == ONION_HANDSHAKE_TYPE_FAST ||
+ create_cell->handshake_type == ONION_HANDSHAKE_TYPE_TAP) {
+ circ->used_legacy_circuit_handshake = true;
+ }
+
if (!channel_is_client(chan)) {
/* remember create types we've seen, but don't remember them from
* clients, to be extra conservative about client statistics. */
@@ -587,11 +594,27 @@ command_process_relay_cell(cell_t *cell, channel_t *chan)
}
/* If this is a cell in an RP circuit, count it as part of the
- hidden service stats */
+ onion service stats */
if (options->HiddenServiceStatistics &&
!CIRCUIT_IS_ORIGIN(circ) &&
- TO_OR_CIRCUIT(circ)->circuit_carries_hs_traffic_stats) {
- rep_hist_seen_new_rp_cell();
+ CONST_TO_OR_CIRCUIT(circ)->circuit_carries_hs_traffic_stats) {
+ /** We need to figure out of this is a v2 or v3 RP circuit to count it
+ * appropriately. v2 services always use the TAP legacy handshake to
+ * connect to the RP; we use this feature to distinguish between v2/v3. */
+ bool is_v2 = false;
+ if (CONST_TO_OR_CIRCUIT(circ)->used_legacy_circuit_handshake) {
+ is_v2 = true;
+ } else if (CONST_TO_OR_CIRCUIT(circ)->rend_splice) {
+ /* If this is a client->RP circuit we need to check the spliced circuit
+ * (which is the service->RP circuit) to see if it was using TAP and
+ * hence if it's a v2 circuit. That's because client->RP circuits can
+ * still use ntor even on v2; but service->RP will always use TAP. */
+ const or_circuit_t *splice = CONST_TO_OR_CIRCUIT(circ)->rend_splice;
+ if (splice->used_legacy_circuit_handshake) {
+ is_v2 = true;
+ }
+ }
+ rep_hist_seen_new_rp_cell(is_v2);
}
}
diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c
index 859ad1c6fc..b2390e4f04 100644
--- a/src/core/or/connection_edge.c
+++ b/src/core/or/connection_edge.c
@@ -2209,7 +2209,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
}
/* If this is a .exit hostname, strip off the .name.exit part, and
- * see whether we're willing to connect there, and and otherwise handle the
+ * see whether we're willing to connect there, and otherwise handle the
* .exit address.
*
* We'll set chosen_exit_name and/or close the connection as appropriate.
diff --git a/src/core/or/connection_or.c b/src/core/or/connection_or.c
index 70cb19a3f3..40c4441de6 100644
--- a/src/core/or/connection_or.c
+++ b/src/core/or/connection_or.c
@@ -566,11 +566,6 @@ connection_or_reached_eof(or_connection_t *conn)
int
connection_or_process_inbuf(or_connection_t *conn)
{
- /** Don't let the inbuf of a nonopen OR connection grow beyond this many
- * bytes: it's either a broken client, a non-Tor client, or a DOS
- * attempt. */
-#define MAX_OR_INBUF_WHEN_NONOPEN 0
-
int ret = 0;
tor_assert(conn);
@@ -581,6 +576,15 @@ connection_or_process_inbuf(or_connection_t *conn)
/* start TLS after handshake completion, or deal with error */
if (ret == 1) {
tor_assert(TO_CONN(conn)->proxy_state == PROXY_CONNECTED);
+ if (buf_datalen(conn->base_.inbuf) != 0) {
+ log_fn(LOG_PROTOCOL_WARN, LD_NET, "Found leftover (%d bytes) "
+ "when transitioning from PROXY_HANDSHAKING state on %s: "
+ "closing.",
+ (int)buf_datalen(conn->base_.inbuf),
+ connection_describe(TO_CONN(conn)));
+ connection_or_close_for_error(conn, 0);
+ return -1;
+ }
if (connection_tls_start_handshake(conn, 0) < 0)
ret = -1;
/* Touch the channel's active timestamp if there is one */
@@ -601,14 +605,12 @@ connection_or_process_inbuf(or_connection_t *conn)
break; /* don't do anything */
}
- /* This check was necessary with 0.2.2, when the TLS_SERVER_RENEGOTIATING
- * check would otherwise just let data accumulate. It serves no purpose
- * in 0.2.3.
- *
- * XXXX Remove this check once we verify that the above paragraph is
- * 100% true. */
- if (buf_datalen(conn->base_.inbuf) > MAX_OR_INBUF_WHEN_NONOPEN) {
- log_fn(LOG_PROTOCOL_WARN, LD_NET, "Accumulated too much data (%d bytes) "
+ /* This check makes sure that we don't have any data on the inbuf if we're
+ * doing our TLS handshake: if we did, they were probably put there by a
+ * SOCKS proxy trying to trick us into accepting unauthenticated data.
+ */
+ if (buf_datalen(conn->base_.inbuf) != 0) {
+ log_fn(LOG_PROTOCOL_WARN, LD_NET, "Accumulated data (%d bytes) "
"on non-open %s; closing.",
(int)buf_datalen(conn->base_.inbuf),
connection_describe(TO_CONN(conn)));
@@ -684,6 +686,11 @@ connection_or_finished_flushing(or_connection_t *conn)
/* PROXY_HAPROXY gets connected by receiving an ack. */
if (conn->proxy_type == PROXY_HAPROXY) {
tor_assert(TO_CONN(conn)->proxy_state == PROXY_HAPROXY_WAIT_FOR_FLUSH);
+ IF_BUG_ONCE(buf_datalen(TO_CONN(conn)->inbuf) != 0) {
+ /* This should be impossible; we're not even reading. */
+ connection_or_close_for_error(conn, 0);
+ return -1;
+ }
TO_CONN(conn)->proxy_state = PROXY_CONNECTED;
if (connection_tls_start_handshake(conn, 0) < 0) {
diff --git a/src/core/or/or_circuit_st.h b/src/core/or/or_circuit_st.h
index 4e17b1c143..4da88889ce 100644
--- a/src/core/or/or_circuit_st.h
+++ b/src/core/or/or_circuit_st.h
@@ -63,6 +63,12 @@ struct or_circuit_t {
* statistics. */
unsigned int circuit_carries_hs_traffic_stats : 1;
+ /** True iff this circuit was made with a CREATE_FAST cell, or a CREATE[2]
+ * cell with a TAP handshake. If this is the case and this is a rend circuit,
+ * this is a v2 circuit, otherwise if this is a rend circuit it's a v3
+ * circuit. */
+ bool used_legacy_circuit_handshake;
+
/** Number of cells that were removed from circuit queue; reset every
* time when writing buffer stats to disk. */
uint32_t processed_cells;
diff --git a/src/core/or/sendme.c b/src/core/or/sendme.c
index 7b4595c1ac..9cad245b29 100644
--- a/src/core/or/sendme.c
+++ b/src/core/or/sendme.c
@@ -398,8 +398,8 @@ sendme_connection_edge_consider_sending(edge_connection_t *conn)
conn->deliver_window += STREAMWINDOW_INCREMENT;
if (connection_edge_send_command(conn, RELAY_COMMAND_SENDME,
NULL, 0) < 0) {
- log_warn(LD_BUG, "connection_edge_send_command failed while sending "
- "a SENDME. Circuit probably closed, skipping.");
+ log_debug(LD_CIRC, "connection_edge_send_command failed while sending "
+ "a SENDME. Circuit probably closed, skipping.");
goto end; /* The circuit's closed, don't continue */
}
}
diff --git a/src/core/proto/proto_socks.c b/src/core/proto/proto_socks.c
index ada968c6b4..a7ee190b3f 100644
--- a/src/core/proto/proto_socks.c
+++ b/src/core/proto/proto_socks.c
@@ -550,6 +550,7 @@ parse_socks5_client_request(const uint8_t *raw_data, socks_request_t *req,
if (parsed == -1) {
log_warn(LD_APP, "socks5: parsing failed - invalid client request");
res = SOCKS_RESULT_INVALID;
+ socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR);
goto end;
} else if (parsed == -2) {
res = SOCKS_RESULT_TRUNCATED;
@@ -561,6 +562,7 @@ parse_socks5_client_request(const uint8_t *raw_data, socks_request_t *req,
if (socks5_client_request_get_version(trunnel_req) != 5) {
res = SOCKS_RESULT_INVALID;
+ socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR);
goto end;
}
@@ -594,6 +596,7 @@ parse_socks5_client_request(const uint8_t *raw_data, socks_request_t *req,
tor_addr_to_str(req->address, &destaddr, sizeof(req->address), 1);
} break;
default: {
+ socks_request_set_socks5_error(req, SOCKS5_ADDRESS_TYPE_NOT_SUPPORTED);
res = -1;
} break;
}
@@ -774,8 +777,10 @@ handle_socks_message(const uint8_t *raw_data, size_t datalen,
} else {
res = parse_socks5_client_request(raw_data, req,
datalen, drain_out);
- if (res != SOCKS_RESULT_DONE) {
+ if (BUG(res == SOCKS_RESULT_INVALID && req->replylen == 0)) {
socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR);
+ }
+ if (res != SOCKS_RESULT_DONE) {
goto end;
}
diff --git a/src/ext/keccak-tiny/keccak-tiny.h b/src/ext/keccak-tiny/keccak-tiny.h
index a9c8ed6420..dd26386a9a 100644
--- a/src/ext/keccak-tiny/keccak-tiny.h
+++ b/src/ext/keccak-tiny/keccak-tiny.h
@@ -21,7 +21,7 @@ typedef struct keccak_state {
size_t offset;
uint8_t finalized : 1;
-} keccak_state;
+} __attribute__((aligned(8))) keccak_state;
/* Initialize a Keccak instance suitable for SHA-3 hash functions. */
int keccak_digest_init(keccak_state *s, size_t bits);
diff --git a/src/feature/client/bridges.c b/src/feature/client/bridges.c
index 6e10defa13..8e2bb01661 100644
--- a/src/feature/client/bridges.c
+++ b/src/feature/client/bridges.c
@@ -164,6 +164,17 @@ bridge_get_addr_port(const bridge_info_t *bridge)
return &bridge->addrport_configured;
}
+/**
+ * Given a <b>bridge</b>, return the transport name. If none were configured,
+ * NULL is returned.
+ */
+const char *
+bridget_get_transport_name(const bridge_info_t *bridge)
+{
+ tor_assert(bridge);
+ return bridge->transport_name;
+}
+
/** If we have a bridge configured whose digest matches <b>digest</b>, or a
* bridge with no known digest whose address matches any of the
* tor_addr_port_t's in <b>orports</b>, return that bridge. Else return
diff --git a/src/feature/client/bridges.h b/src/feature/client/bridges.h
index 174149cf97..1b090e8649 100644
--- a/src/feature/client/bridges.h
+++ b/src/feature/client/bridges.h
@@ -23,6 +23,7 @@ void sweep_bridge_list(void);
const smartlist_t *bridge_list_get(void);
const uint8_t *bridge_get_rsa_id_digest(const bridge_info_t *bridge);
const tor_addr_port_t * bridge_get_addr_port(const bridge_info_t *bridge);
+const char *bridget_get_transport_name(const bridge_info_t *bridge);
bridge_info_t *get_configured_bridge_by_addr_port_digest(
const tor_addr_t *addr,
uint16_t port,
diff --git a/src/feature/client/transports.c b/src/feature/client/transports.c
index d1c807189a..4b05d55494 100644
--- a/src/feature/client/transports.c
+++ b/src/feature/client/transports.c
@@ -368,6 +368,28 @@ static int unconfigured_proxies_n = 0;
/** Boolean: True iff we might need to restart some proxies. */
static int check_if_restarts_needed = 0;
+/** Return true iff we have a managed_proxy_t in the global list is for the
+ * given transport name. */
+bool
+managed_proxy_has_transport(const char *transport_name)
+{
+ tor_assert(transport_name);
+
+ if (!managed_proxy_list) {
+ return false;
+ }
+
+ SMARTLIST_FOREACH_BEGIN(managed_proxy_list, const managed_proxy_t *, mp) {
+ SMARTLIST_FOREACH_BEGIN(mp->transports_to_launch, const char *, name) {
+ if (!strcasecmp(name, transport_name)) {
+ return true;
+ }
+ } SMARTLIST_FOREACH_END(name);
+ } SMARTLIST_FOREACH_END(mp);
+
+ return false;
+}
+
/** Return true if there are still unconfigured managed proxies, or proxies
* that need restarting. */
int
diff --git a/src/feature/client/transports.h b/src/feature/client/transports.h
index 3aff1cb248..47b118e77b 100644
--- a/src/feature/client/transports.h
+++ b/src/feature/client/transports.h
@@ -41,6 +41,7 @@ void transport_free_(transport_t *transport);
#define transport_free(tr) FREE_AND_NULL(transport_t, transport_free_, (tr))
MOCK_DECL(transport_t*, transport_get_by_name, (const char *name));
+bool managed_proxy_has_transport(const char *transport_name);
MOCK_DECL(void, pt_kickstart_proxy,
(const smartlist_t *transport_list, char **proxy_argv,
diff --git a/src/feature/hs/hs_cache.c b/src/feature/hs/hs_cache.c
index 03e004c356..0febcfb40e 100644
--- a/src/feature/hs/hs_cache.c
+++ b/src/feature/hs/hs_cache.c
@@ -19,6 +19,7 @@
#include "feature/hs/hs_descriptor.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/rend/rendcache.h"
+#include "feature/stats/rephist.h"
#include "feature/hs/hs_cache.h"
@@ -174,7 +175,10 @@ cache_store_v3_as_dir(hs_cache_dir_descriptor_t *desc)
* old HS protocol cache subsystem for which we are tied with. */
rend_cache_increment_allocation(cache_get_dir_entry_size(desc));
- /* XXX: Update HS statistics. We should have specific stats for v3. */
+ /* Update HSv3 statistics */
+ if (get_options()->HiddenServiceStatistics) {
+ rep_hist_hsdir_stored_maybe_new_v3_onion(desc->key);
+ }
return 0;
diff --git a/src/feature/hs_common/shared_random_client.c b/src/feature/hs_common/shared_random_client.c
index c2ea5afe32..2c7d6c8d90 100644
--- a/src/feature/hs_common/shared_random_client.c
+++ b/src/feature/hs_common/shared_random_client.c
@@ -33,12 +33,11 @@ srv_to_control_string(const sr_srv_t *srv)
}
/**
- * If we have no consensus and we are not an authority, assume that this is
- * the voting interval. We should never actually use this: only authorities
- * should be trying to figure out the schedule when they don't have a
- * consensus.
- **/
+ * If we have no consensus and we are not an authority, assume that this is the
+ * voting interval. This can be used while bootstrapping as a relay and we are
+ * asked to initialize HS stats (see rep_hist_hs_stats_init()) */
#define DEFAULT_NETWORK_VOTING_INTERVAL (3600)
+#define TESTING_DEFAULT_NETWORK_VOTING_INTERVAL (20)
/* This is an unpleasing workaround for tests. Our unit tests assume that we
* are scheduling all of our shared random stuff as if we were a directory
@@ -69,11 +68,13 @@ get_voting_interval(void)
* It's better than falling back to the non-consensus case. */
interval = (int)(consensus->fresh_until - consensus->valid_after);
} else {
- /* We should never be reaching this point, since a client should never
- * call this code unless they have some kind of a consensus. All we can
- * do is hope that this network is using the default voting interval. */
- tor_assert_nonfatal_unreached_once();
- interval = DEFAULT_NETWORK_VOTING_INTERVAL;
+ /* We can reach this as a relay when bootstrapping and we are asked to
+ * initialize HS stats (see rep_hist_hs_stats_init()). */
+ if (get_options()->TestingTorNetwork) {
+ interval = TESTING_DEFAULT_NETWORK_VOTING_INTERVAL;
+ } else {
+ interval = DEFAULT_NETWORK_VOTING_INTERVAL;
+ }
}
tor_assert(interval > 0);
return interval;
diff --git a/src/feature/nodelist/networkstatus.c b/src/feature/nodelist/networkstatus.c
index ece3c9e059..5deec01f82 100644
--- a/src/feature/nodelist/networkstatus.c
+++ b/src/feature/nodelist/networkstatus.c
@@ -240,7 +240,7 @@ networkstatus_get_cache_fname,(int flav,
}
/**
- * Read and and return the cached consensus of type <b>flavorname</b>. If
+ * Read and return the cached consensus of type <b>flavorname</b>. If
* <b>unverified</b> is false, get the one we haven't verified. Return NULL if
* the file isn't there. */
static tor_mmap_t *
diff --git a/src/feature/nodelist/routerlist.c b/src/feature/nodelist/routerlist.c
index 3f6e31bc3a..a1a348edb9 100644
--- a/src/feature/nodelist/routerlist.c
+++ b/src/feature/nodelist/routerlist.c
@@ -65,6 +65,9 @@
#include "app/config/config.h"
#include "core/mainloop/connection.h"
#include "core/mainloop/mainloop.h"
+#include "core/or/circuitlist.h"
+#include "core/or/circuituse.h"
+#include "core/or/extendinfo.h"
#include "core/or/policies.h"
#include "feature/client/bridges.h"
#include "feature/control/control_events.h"
@@ -137,8 +140,6 @@ 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 launch_dummy_descriptor_download_as_needed(time_t now,
- const or_options_t *options);
/****************************************************************************/
@@ -2306,7 +2307,6 @@ update_all_descriptor_downloads(time_t now)
return;
update_router_descriptor_downloads(now);
update_microdesc_downloads(now);
- launch_dummy_descriptor_download_as_needed(now, get_options());
}
/** Clear all our timeouts for fetching v3 directory stuff, and then
@@ -2760,54 +2760,6 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote,
smartlist_free(no_longer_old);
}
-/** How often should we launch a server/authority request to be sure of getting
- * a guess for our IP? */
-/*XXXX+ this info should come from netinfo cells or something, or we should
- * do this only when we aren't seeing incoming data. see bug 652. */
-#define DUMMY_DOWNLOAD_INTERVAL (20*60)
-
-/** As needed, launch a dummy router descriptor fetch to see if our
- * address has changed. */
-static void
-launch_dummy_descriptor_download_as_needed(time_t now,
- const or_options_t *options)
-{
- static time_t last_dummy_download = 0;
- bool have_addr;
- tor_addr_t addr_out;
-
- /* This dummy fetch only matter for relays. */
- if (!server_mode(options)) {
- return;
- }
-
- /* Lookup the address cache to learn if we have a good usable address. We
- * still force relays to have an IPv4 so that alone is enough to learn if we
- * need a lookup. In case we don't have one, we might want to attempt a
- * dummy fetch to learn our address as a suggestion from an authority. */
- have_addr = relay_find_addr_to_publish(options, AF_INET,
- RELAY_FIND_ADDR_CACHE_ONLY,
- &addr_out);
-
- /* XXXX+ we could be smarter here; see notes on bug 652. */
- /* If we're a server that doesn't have an address, we rely on directory
- * fetches to learn when our address changes. So if we haven't tried to get
- * any routerdescs in a long time, try a dummy fetch now. */
- if (!have_addr &&
- last_descriptor_download_attempted + DUMMY_DOWNLOAD_INTERVAL < now &&
- last_dummy_download + DUMMY_DOWNLOAD_INTERVAL < now) {
- last_dummy_download = now;
- /* XX/teor - do we want an authority here, because they are less likely
- * to give us the wrong address? (See #17782)
- * I'm leaving the previous behaviour intact, because I don't like
- * the idea of some relays contacting an authority every 20 minutes. */
- directory_get_from_dirserver(DIR_PURPOSE_FETCH_SERVERDESC,
- ROUTER_PURPOSE_GENERAL, "authority.z",
- PDS_RETRY_IF_NO_SERVERS,
- DL_WANT_ANY_DIRSERVER);
- }
-}
-
/** Launch downloads for router status as needed. */
void
update_router_descriptor_downloads(time_t now)
diff --git a/src/feature/relay/ext_orport.c b/src/feature/relay/ext_orport.c
index 1bb8741e45..c45a0b463f 100644
--- a/src/feature/relay/ext_orport.c
+++ b/src/feature/relay/ext_orport.c
@@ -656,75 +656,17 @@ connection_ext_or_start_auth(or_connection_t *or_conn)
return 0;
}
-/** Global map between Extended ORPort identifiers and OR
- * connections. */
-static digestmap_t *orconn_ext_or_id_map = NULL;
-
-/** Remove the Extended ORPort identifier of <b>conn</b> from the
- * global identifier list. Also, clear the identifier from the
- * connection itself. */
-void
-connection_or_remove_from_ext_or_id_map(or_connection_t *conn)
-{
- or_connection_t *tmp;
- if (!orconn_ext_or_id_map)
- return;
- if (!conn->ext_or_conn_id)
- return;
-
- tmp = digestmap_remove(orconn_ext_or_id_map, conn->ext_or_conn_id);
- if (!tor_digest_is_zero(conn->ext_or_conn_id))
- tor_assert(tmp == conn);
-
- memset(conn->ext_or_conn_id, 0, EXT_OR_CONN_ID_LEN);
-}
-
-#ifdef TOR_UNIT_TESTS
-/** Return the connection whose ext_or_id is <b>id</b>. Return NULL if no such
- * connection is found. */
-or_connection_t *
-connection_or_get_by_ext_or_id(const char *id)
-{
- if (!orconn_ext_or_id_map)
- return NULL;
- return digestmap_get(orconn_ext_or_id_map, id);
-}
-#endif /* defined(TOR_UNIT_TESTS) */
-
-/** Deallocate the global Extended ORPort identifier list */
-void
-connection_or_clear_ext_or_id_map(void)
-{
- digestmap_free(orconn_ext_or_id_map, NULL);
- orconn_ext_or_id_map = NULL;
-}
-
/** Creates an Extended ORPort identifier for <b>conn</b> and deposits
* it into the global list of identifiers. */
void
connection_or_set_ext_or_identifier(or_connection_t *conn)
{
char random_id[EXT_OR_CONN_ID_LEN];
- or_connection_t *tmp;
-
- if (!orconn_ext_or_id_map)
- orconn_ext_or_id_map = digestmap_new();
-
- /* Remove any previous identifiers: */
- if (conn->ext_or_conn_id && !tor_digest_is_zero(conn->ext_or_conn_id))
- connection_or_remove_from_ext_or_id_map(conn);
-
- do {
- crypto_rand(random_id, sizeof(random_id));
- } while (digestmap_get(orconn_ext_or_id_map, random_id));
if (!conn->ext_or_conn_id)
conn->ext_or_conn_id = tor_malloc_zero(EXT_OR_CONN_ID_LEN);
memcpy(conn->ext_or_conn_id, random_id, EXT_OR_CONN_ID_LEN);
-
- tmp = digestmap_set(orconn_ext_or_id_map, random_id, conn);
- tor_assert(!tmp);
}
/** Free any leftover allocated memory of the ext_orport.c subsystem. */
diff --git a/src/feature/relay/ext_orport.h b/src/feature/relay/ext_orport.h
index 416c358397..b149f9eb1c 100644
--- a/src/feature/relay/ext_orport.h
+++ b/src/feature/relay/ext_orport.h
@@ -36,8 +36,6 @@
int connection_ext_or_start_auth(or_connection_t *or_conn);
void connection_or_set_ext_or_identifier(or_connection_t *conn);
-void connection_or_remove_from_ext_or_id_map(or_connection_t *conn);
-void connection_or_clear_ext_or_id_map(void);
int connection_ext_or_finished_flushing(or_connection_t *conn);
int connection_ext_or_process_inbuf(or_connection_t *or_conn);
char *get_ext_or_auth_cookie_file_name(void);
@@ -71,10 +69,6 @@ connection_ext_or_process_inbuf(or_connection_t *conn)
}
#define connection_or_set_ext_or_identifier(conn) \
((void)(conn))
-#define connection_or_remove_from_ext_or_id_map(conn) \
- ((void)(conn))
-#define connection_or_clear_ext_or_id_map() \
- STMT_NIL
#define get_ext_or_auth_cookie_file_name() \
(NULL)
@@ -94,7 +88,6 @@ STATIC int handle_client_auth_nonce(const char *client_nonce,
#ifdef TOR_UNIT_TESTS
extern uint8_t *ext_or_auth_cookie;
extern int ext_or_auth_cookie_is_set;
-or_connection_t *connection_or_get_by_ext_or_id(const char *id);
#endif
#endif /* defined(EXT_ORPORT_PRIVATE) */
diff --git a/src/feature/relay/relay_config.c b/src/feature/relay/relay_config.c
index 6504c680eb..e8c29fa7ed 100644
--- a/src/feature/relay/relay_config.c
+++ b/src/feature/relay/relay_config.c
@@ -227,16 +227,21 @@ remove_duplicate_orports(smartlist_t *ports)
if (removing[j]) {
continue;
}
+ /* Skip non ORPorts. */
+ if (next->type != CONN_TYPE_OR_LISTENER) {
+ continue;
+ }
/* Same address family and same port number, we have a match. */
- if (!current->explicit_addr && next->explicit_addr &&
- tor_addr_family(&current->addr) == tor_addr_family(&next->addr) &&
+ if (tor_addr_family(&current->addr) == tor_addr_family(&next->addr) &&
current->port == next->port) {
/* Remove current because next is explicitly set. */
removing[i] = true;
- char *next_str = tor_strdup(describe_relay_port(next));
- log_warn(LD_CONFIG, "Configuration port %s superseded by %s",
- describe_relay_port(current), next_str);
- tor_free(next_str);
+ if (!current->explicit_addr && next->explicit_addr) {
+ char *next_str = tor_strdup(describe_relay_port(next));
+ log_warn(LD_CONFIG, "Configuration port %s superseded by %s",
+ describe_relay_port(current), next_str);
+ tor_free(next_str);
+ }
}
}
}
diff --git a/src/feature/relay/relay_find_addr.c b/src/feature/relay/relay_find_addr.c
index 43b958d563..9c2c8b281c 100644
--- a/src/feature/relay/relay_find_addr.c
+++ b/src/feature/relay/relay_find_addr.c
@@ -12,10 +12,16 @@
#include "app/config/resolve_addr.h"
#include "core/mainloop/mainloop.h"
+#include "core/or/circuitlist.h"
+#include "core/or/circuituse.h"
+#include "core/or/extendinfo.h"
#include "feature/control/control_events.h"
#include "feature/dircommon/dir_connection_st.h"
#include "feature/nodelist/dirlist.h"
+#include "feature/nodelist/node_select.h"
+#include "feature/nodelist/nodelist.h"
+#include "feature/nodelist/routerstatus_st.h"
#include "feature/relay/relay_find_addr.h"
#include "feature/relay/router.h"
#include "feature/relay/routermode.h"
@@ -151,3 +157,65 @@ relay_has_address_set(int family)
return relay_find_addr_to_publish(get_options(), family,
RELAY_FIND_ADDR_CACHE_ONLY, &addr);
}
+
+/** How often should we launch a circuit to an authority to be sure of getting
+ * a guess for our IP? */
+#define DUMMY_DOWNLOAD_INTERVAL (20*60)
+
+void
+relay_addr_learn_from_dirauth(void)
+{
+ static time_t last_dummy_circuit = 0;
+ const or_options_t *options = get_options();
+ time_t now = time(NULL);
+ bool have_addr;
+ tor_addr_t addr_out;
+
+ /* This dummy circuit only matter for relays. */
+ if (BUG(!server_mode(options))) {
+ return;
+ }
+
+ /* Lookup the address cache to learn if we have a good usable address. We
+ * still force relays to have an IPv4 so that alone is enough to learn if we
+ * need a lookup. In case we don't have one, we might want to attempt a
+ * dummy circuit to learn our address as a suggestion from an authority. */
+ have_addr = relay_find_addr_to_publish(options, AF_INET,
+ RELAY_FIND_ADDR_CACHE_ONLY,
+ &addr_out);
+
+ /* If we're a relay or bridge for which we were unable to discover our
+ * public address, we rely on learning our address from a directory
+ * authority from the NETINFO cell. */
+ if (!have_addr && last_dummy_circuit + DUMMY_DOWNLOAD_INTERVAL < now) {
+ last_dummy_circuit = now;
+
+ const routerstatus_t *rs = router_pick_trusteddirserver(V3_DIRINFO, 0);
+ if (BUG(!rs)) {
+ /* We should really always have trusted directories configured at this
+ * stage. They are loaded early either from default list or the one
+ * given in the configuration file. */
+ return;
+ }
+ const node_t *node = node_get_by_id(rs->identity_digest);
+ if (BUG(!node)) {
+ /* If there is a routerstatus_t, there is a node_t thus this should
+ * never fail. */
+ return;
+ }
+ extend_info_t *ei = extend_info_from_node(node, 1);
+ if (BUG(!ei)) {
+ return;
+ }
+
+ log_debug(LD_GENERAL, "Attempting dummy testing circuit to an authority "
+ "in order to learn our address.");
+
+ /* Launch a one-hop testing circuit to a trusted authority so we can learn
+ * our address through the NETINFO cell. */
+ circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei,
+ CIRCLAUNCH_IS_INTERNAL |
+ CIRCLAUNCH_ONEHOP_TUNNEL);
+ extend_info_free(ei);
+ }
+}
diff --git a/src/feature/relay/relay_find_addr.h b/src/feature/relay/relay_find_addr.h
index 3d30946b05..34890cd34e 100644
--- a/src/feature/relay/relay_find_addr.h
+++ b/src/feature/relay/relay_find_addr.h
@@ -24,6 +24,8 @@ MOCK_DECL(bool, relay_find_addr_to_publish,
bool relay_has_address_set(int family);
+void relay_addr_learn_from_dirauth(void);
+
#ifdef RELAY_FIND_ADDR_PRIVATE
#endif /* RELAY_FIND_ADDR_PRIVATE */
diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c
index 29103ed6c6..04ffe8e48e 100644
--- a/src/feature/relay/router.c
+++ b/src/feature/relay/router.c
@@ -831,6 +831,25 @@ router_initialize_tls_context(void)
(unsigned int)lifetime);
}
+/** Announce URL to bridge status page. */
+STATIC void
+router_announce_bridge_status_page(void)
+{
+ char fingerprint[FINGERPRINT_LEN + 1];
+
+ if (crypto_pk_get_hashed_fingerprint(get_server_identity_key(),
+ fingerprint) < 0) {
+ // LCOV_EXCL_START
+ log_err(LD_GENERAL, "Unable to compute bridge fingerprint");
+ return;
+ // LCOV_EXCL_STOP
+ }
+
+ log_notice(LD_GENERAL, "You can check the status of your bridge relay at "
+ "https://bridges.torproject.org/status?id=%s",
+ fingerprint);
+}
+
/** Compute fingerprint (or hashed fingerprint if hashed is 1) and write
* it to 'fingerprint' (or 'hashed-fingerprint'). Return 0 on success, or
* -1 if Tor should die,
@@ -1133,6 +1152,10 @@ init_keys(void)
return -1;
}
+ /* Display URL to bridge status page. */
+ if (! public_server_mode(options))
+ router_announce_bridge_status_page();
+
if (!authdir_mode(options))
return 0;
/* 6. [authdirserver only] load approved-routers file */
@@ -2071,7 +2094,9 @@ router_build_fresh_unsigned_routerinfo,(routerinfo_t **ri_out))
/* Tor requires a relay to have an IPv4 so bail if we can't find it. */
if (!have_v4) {
- log_warn(LD_CONFIG, "Don't know my address while generating descriptor");
+ log_info(LD_CONFIG, "Don't know my address while generating descriptor. "
+ "Launching circuit to authority to learn it.");
+ relay_addr_learn_from_dirauth();
result = TOR_ROUTERINFO_ERROR_NO_EXT_ADDR;
goto err;
}
@@ -2679,8 +2704,21 @@ check_descriptor_ipaddress_changed(time_t now)
/* Ignore returned value because we want to notice not only an address
* change but also if an address is lost (current == UNSPEC). */
- find_my_address(get_options(), family, LOG_INFO, &current, &method,
- &hostname);
+ bool found = find_my_address(get_options(), family, LOG_INFO, &current,
+ &method, &hostname);
+ if (!found) {
+ /* Address was possibly not found because it is simply not configured or
+ * discoverable. Fallback to our cache, which includes any suggestion
+ * sent by a trusted directory server. */
+ found = relay_find_addr_to_publish(get_options(), family,
+ RELAY_FIND_ADDR_CACHE_ONLY,
+ &current);
+ }
+
+ /* The "current" address might be UNSPEC meaning it was not discovered nor
+ * found in our current cache. If we had an address before and we have
+ * none now, we consider this an IP change since it appears the relay lost
+ * its address. */
if (!tor_addr_eq(previous, &current)) {
char *source;
@@ -3291,6 +3329,11 @@ extrainfo_dump_to_string_stats_helper(smartlist_t *chunks,
"hidserv-stats-end", now, &contents) > 0) {
smartlist_add(chunks, contents);
}
+ if (options->HiddenServiceStatistics &&
+ load_stats_file("stats"PATH_SEPARATOR"hidserv-v3-stats",
+ "hidserv-v3-stats-end", now, &contents) > 0) {
+ smartlist_add(chunks, contents);
+ }
if (options->EntryStatistics &&
load_stats_file("stats"PATH_SEPARATOR"entry-stats",
"entry-stats-end", now, &contents) > 0) {
diff --git a/src/feature/relay/router.h b/src/feature/relay/router.h
index f71ada8eb7..f9df1f5637 100644
--- a/src/feature/relay/router.h
+++ b/src/feature/relay/router.h
@@ -129,6 +129,7 @@ void router_free_all(void);
STATIC void get_platform_str(char *platform, size_t len);
STATIC int router_write_fingerprint(int hashed, int ed25519_identity);
STATIC smartlist_t *get_my_declared_family(const or_options_t *options);
+STATIC void router_announce_bridge_status_page(void);
#ifdef TOR_UNIT_TESTS
extern time_t desc_clean_since;
diff --git a/src/feature/relay/selftest.c b/src/feature/relay/selftest.c
index b711c94eb6..86b1533be1 100644
--- a/src/feature/relay/selftest.c
+++ b/src/feature/relay/selftest.c
@@ -274,7 +274,12 @@ router_do_orport_reachability_checks(const routerinfo_t *me,
!orport_reachable ? "reachability" : "bandwidth",
family_name, fmt_addrport_ap(ap));
- inform_testing_reachability(&ap->addr, ap->port, false);
+ if (!orport_reachable) {
+ /* Only log if we are actually doing a reachability test to learn if our
+ * ORPort is reachable. Else, this prints a log notice if we are simply
+ * opening a bandwidth testing circuit even do we are reachable. */
+ inform_testing_reachability(&ap->addr, ap->port, false);
+ }
circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei,
CIRCLAUNCH_NEED_CAPACITY|
diff --git a/src/feature/rend/rendcache.c b/src/feature/rend/rendcache.c
index 04f6390a7f..a471c8f463 100644
--- a/src/feature/rend/rendcache.c
+++ b/src/feature/rend/rendcache.c
@@ -718,7 +718,7 @@ rend_cache_store_v2_desc_as_dir(const char *desc)
safe_str(desc_id_base32), (int)encoded_size);
/* Statistics: Note down this potentially new HS. */
if (options->HiddenServiceStatistics) {
- rep_hist_stored_maybe_new_hs(e->parsed->pk);
+ rep_hist_hsdir_stored_maybe_new_v2_onion(e->parsed->pk);
}
number_stored++;
diff --git a/src/feature/stats/rephist.c b/src/feature/stats/rephist.c
index 3c22fda3b8..f8d7887e65 100644
--- a/src/feature/stats/rephist.c
+++ b/src/feature/stats/rephist.c
@@ -1710,123 +1710,248 @@ rep_hist_log_circuit_handshake_stats(time_t now)
/** Start of the current hidden service stats interval or 0 if we're
* not collecting hidden service statistics. */
-static time_t start_of_hs_stats_interval;
+static time_t start_of_hs_v2_stats_interval;
-/** Carries the various hidden service statistics, and any other
- * information needed. */
-typedef struct hs_stats_t {
- /** How many relay cells have we seen as rendezvous points? */
- uint64_t rp_relay_cells_seen;
+/** Our v2 statistics structure singleton. */
+static hs_v2_stats_t *hs_v2_stats = NULL;
- /** Set of unique public key digests we've seen this stat period
- * (could also be implemented as sorted smartlist). */
- digestmap_t *onions_seen_this_period;
-} hs_stats_t;
+/** HSv2 stats */
-/** Our statistics structure singleton. */
-static hs_stats_t *hs_stats = NULL;
-
-/** Allocate, initialize and return an hs_stats_t structure. */
-static hs_stats_t *
-hs_stats_new(void)
+/** Allocate, initialize and return an hs_v2_stats_t structure. */
+static hs_v2_stats_t *
+hs_v2_stats_new(void)
{
- hs_stats_t *new_hs_stats = tor_malloc_zero(sizeof(hs_stats_t));
- new_hs_stats->onions_seen_this_period = digestmap_new();
+ hs_v2_stats_t *new_hs_v2_stats = tor_malloc_zero(sizeof(hs_v2_stats_t));
+ new_hs_v2_stats->v2_onions_seen_this_period = digestmap_new();
- return new_hs_stats;
+ return new_hs_v2_stats;
}
-#define hs_stats_free(val) \
- FREE_AND_NULL(hs_stats_t, hs_stats_free_, (val))
+#define hs_v2_stats_free(val) \
+ FREE_AND_NULL(hs_v2_stats_t, hs_v2_stats_free_, (val))
-/** Free an hs_stats_t structure. */
+/** Free an hs_v2_stats_t structure. */
static void
-hs_stats_free_(hs_stats_t *victim_hs_stats)
+hs_v2_stats_free_(hs_v2_stats_t *victim_hs_v2_stats)
{
- if (!victim_hs_stats) {
+ if (!victim_hs_v2_stats) {
return;
}
- digestmap_free(victim_hs_stats->onions_seen_this_period, NULL);
- tor_free(victim_hs_stats);
+ digestmap_free(victim_hs_v2_stats->v2_onions_seen_this_period, NULL);
+ tor_free(victim_hs_v2_stats);
}
-/** Initialize hidden service statistics. */
+/** Clear history of hidden service statistics and set the measurement
+ * interval start to <b>now</b>. */
+static void
+rep_hist_reset_hs_v2_stats(time_t now)
+{
+ if (!hs_v2_stats) {
+ hs_v2_stats = hs_v2_stats_new();
+ }
+
+ hs_v2_stats->rp_v2_relay_cells_seen = 0;
+
+ digestmap_free(hs_v2_stats->v2_onions_seen_this_period, NULL);
+ hs_v2_stats->v2_onions_seen_this_period = digestmap_new();
+
+ start_of_hs_v2_stats_interval = now;
+}
+
+/** As HSDirs, we saw another v2 onion with public key <b>pubkey</b>. Check
+ * whether we have counted it before, if not count it now! */
void
-rep_hist_hs_stats_init(time_t now)
+rep_hist_hsdir_stored_maybe_new_v2_onion(const crypto_pk_t *pubkey)
+{
+ char pubkey_hash[DIGEST_LEN];
+
+ if (!hs_v2_stats) {
+ return; // We're not collecting stats
+ }
+
+ /* Get the digest of the pubkey which will be used to detect whether
+ we've seen this hidden service before or not. */
+ if (crypto_pk_get_digest(pubkey, pubkey_hash) < 0) {
+ /* This fail should not happen; key has been validated by
+ descriptor parsing code first. */
+ return;
+ }
+
+ /* Check if this is the first time we've seen this hidden
+ service. If it is, count it as new. */
+ if (!digestmap_get(hs_v2_stats->v2_onions_seen_this_period,
+ pubkey_hash)) {
+ digestmap_set(hs_v2_stats->v2_onions_seen_this_period,
+ pubkey_hash, (void*)(uintptr_t)1);
+ }
+}
+
+/*** HSv3 stats ******/
+
+/** Start of the current hidden service stats interval or 0 if we're not
+ * collecting hidden service statistics.
+ *
+ * This is particularly important for v3 statistics since this variable
+ * controls the start time of initial v3 stats collection. It's initialized by
+ * rep_hist_hs_stats_init() to the next time period start (i.e. 12:00UTC), and
+ * should_collect_v3_stats() ensures that functions that collect v3 stats do
+ * not do so sooner than that.
+ *
+ * Collecting stats from 12:00UTC to 12:00UTC is extremely important for v3
+ * stats because rep_hist_hsdir_stored_maybe_new_v3_onion() uses the blinded
+ * key of each onion service as its double-counting index. Onion services
+ * rotate their descriptor at around 00:00UTC which means that their blinded
+ * key also changes around that time. However the precise time that onion
+ * services rotate their descriptors is actually when they fetch a new
+ * 00:00UTC consensus and that happens at a random time (e.g. it can even
+ * happen at 02:00UTC). This means that if we started keeping v3 stats at
+ * around 00:00UTC we wouldn't be able to tell when onion services change
+ * their blinded key and hence we would double count an unpredictable amount
+ * of them (for example, if an onion service fetches the 00:00UTC consensus at
+ * 01:00UTC it would upload to its old HSDir at 00:45UTC, and then to a
+ * different HSDir at 01:50UTC).
+ *
+ * For this reason, we start collecting statistics at 12:00UTC. This way we
+ * know that by the time we stop collecting statistics for that time period 24
+ * hours later, all the onion services have switched to their new blinded
+ * key. This way we can predict much better how much double counting has been
+ * performed.
+ */
+static time_t start_of_hs_v3_stats_interval;
+
+/** Our v3 statistics structure singleton. */
+static hs_v3_stats_t *hs_v3_stats = NULL;
+
+/** Allocate, initialize and return an hs_v3_stats_t structure. */
+static hs_v3_stats_t *
+hs_v3_stats_new(void)
+{
+ hs_v3_stats_t *new_hs_v3_stats = tor_malloc_zero(sizeof(hs_v3_stats_t));
+ new_hs_v3_stats->v3_onions_seen_this_period = digest256map_new();
+
+ return new_hs_v3_stats;
+}
+
+#define hs_v3_stats_free(val) \
+ FREE_AND_NULL(hs_v3_stats_t, hs_v3_stats_free_, (val))
+
+/** Free an hs_v3_stats_t structure. */
+static void
+hs_v3_stats_free_(hs_v3_stats_t *victim_hs_v3_stats)
{
- if (!hs_stats) {
- hs_stats = hs_stats_new();
+ if (!victim_hs_v3_stats) {
+ return;
}
- start_of_hs_stats_interval = now;
+ digest256map_free(victim_hs_v3_stats->v3_onions_seen_this_period, NULL);
+ tor_free(victim_hs_v3_stats);
}
/** Clear history of hidden service statistics and set the measurement
* interval start to <b>now</b>. */
static void
-rep_hist_reset_hs_stats(time_t now)
+rep_hist_reset_hs_v3_stats(time_t now)
{
- if (!hs_stats) {
- hs_stats = hs_stats_new();
+ if (!hs_v3_stats) {
+ hs_v3_stats = hs_v3_stats_new();
}
- hs_stats->rp_relay_cells_seen = 0;
+ digest256map_free(hs_v3_stats->v3_onions_seen_this_period, NULL);
+ hs_v3_stats->v3_onions_seen_this_period = digest256map_new();
- digestmap_free(hs_stats->onions_seen_this_period, NULL);
- hs_stats->onions_seen_this_period = digestmap_new();
+ hs_v3_stats->rp_v3_relay_cells_seen = 0;
- start_of_hs_stats_interval = now;
+ start_of_hs_v3_stats_interval = now;
}
-/** Stop collecting hidden service stats in a way that we can re-start
- * doing so in rep_hist_buffer_stats_init(). */
-void
-rep_hist_hs_stats_term(void)
+/** Return true if it's a good time to collect v3 stats.
+ *
+ * v3 stats have a strict stats collection period (from 12:00UTC to 12:00UTC
+ * on the real network). We don't want to collect statistics if (for example)
+ * we just booted and it's 03:00UTC; we will wait until 12:00UTC before we
+ * start collecting statistics to make sure that the final result represents
+ * the whole collection period. This behavior is controlled by
+ * rep_hist_hs_stats_init().
+ */
+MOCK_IMPL(STATIC bool,
+should_collect_v3_stats,(void))
{
- rep_hist_reset_hs_stats(0);
+ return start_of_hs_v3_stats_interval <= approx_time();
}
-/** We saw a new HS relay cell, Count it! */
+/** We just received a new descriptor with <b>blinded_key</b>. See if we've
+ * seen this blinded key before, and if not add it to the stats. */
void
-rep_hist_seen_new_rp_cell(void)
+rep_hist_hsdir_stored_maybe_new_v3_onion(const uint8_t *blinded_key)
{
- if (!hs_stats) {
- return; // We're not collecting stats
+ /* Return early if we don't collect HSv3 stats, or if it's not yet the time
+ * to collect them. */
+ if (!hs_v3_stats || !should_collect_v3_stats()) {
+ return;
}
- hs_stats->rp_relay_cells_seen++;
+ bool seen_before =
+ !!digest256map_get(hs_v3_stats->v3_onions_seen_this_period,
+ blinded_key);
+
+ log_info(LD_GENERAL, "Considering v3 descriptor with %s (%sseen before)",
+ safe_str(hex_str((char*)blinded_key, 32)),
+ seen_before ? "" : "not ");
+
+ /* Count it if we haven't seen it before. */
+ if (!seen_before) {
+ digest256map_set(hs_v3_stats->v3_onions_seen_this_period,
+ blinded_key, (void*)(uintptr_t)1);
+ }
}
-/** As HSDirs, we saw another hidden service with public key
- * <b>pubkey</b>. Check whether we have counted it before, if not
- * count it now! */
+/** We saw a new HS relay cell: count it!
+ * If <b>is_v2</b> is set then it's a v2 RP cell, otherwise it's a v3. */
void
-rep_hist_stored_maybe_new_hs(const crypto_pk_t *pubkey)
+rep_hist_seen_new_rp_cell(bool is_v2)
{
- char pubkey_hash[DIGEST_LEN];
+ log_debug(LD_GENERAL, "New RP cell (%d)", is_v2);
- if (!hs_stats) {
- return; // We're not collecting stats
+ if (is_v2 && hs_v2_stats) {
+ hs_v2_stats->rp_v2_relay_cells_seen++;
+ } else if (!is_v2 && hs_v3_stats && should_collect_v3_stats()) {
+ hs_v3_stats->rp_v3_relay_cells_seen++;
}
+}
- /* Get the digest of the pubkey which will be used to detect whether
- we've seen this hidden service before or not. */
- if (crypto_pk_get_digest(pubkey, pubkey_hash) < 0) {
- /* This fail should not happen; key has been validated by
- descriptor parsing code first. */
- return;
+/** Generic HS stats code */
+
+/** Initialize v2 and v3 hidden service statistics. */
+void
+rep_hist_hs_stats_init(time_t now)
+{
+ if (!hs_v2_stats) {
+ hs_v2_stats = hs_v2_stats_new();
}
- /* Check if this is the first time we've seen this hidden
- service. If it is, count it as new. */
- if (!digestmap_get(hs_stats->onions_seen_this_period,
- pubkey_hash)) {
- digestmap_set(hs_stats->onions_seen_this_period,
- pubkey_hash, (void*)(uintptr_t)1);
+ /* Start collecting v2 stats straight away */
+ start_of_hs_v2_stats_interval = now;
+
+ if (!hs_v3_stats) {
+ hs_v3_stats = hs_v3_stats_new();
}
+
+ /* Start collecting v3 stats at the next 12:00 UTC */
+ start_of_hs_v3_stats_interval = hs_get_start_time_of_next_time_period(now);
+}
+
+/** Stop collecting hidden service stats in a way that we can re-start
+ * doing so in rep_hist_buffer_stats_init(). */
+void
+rep_hist_hs_stats_term(void)
+{
+ rep_hist_reset_hs_v2_stats(0);
+ rep_hist_reset_hs_v3_stats(0);
}
+/** Stats reporting code */
+
/* The number of cells that are supposed to be hidden from the adversary
* by adding noise from the Laplace distribution. This value, divided by
* EPSILON, is Laplace parameter b. It must be greater than 0. */
@@ -1851,58 +1976,69 @@ rep_hist_stored_maybe_new_hs(const crypto_pk_t *pubkey)
#define ONIONS_SEEN_BIN_SIZE 8
/** Allocate and return a string containing hidden service stats that
- * are meant to be placed in the extra-info descriptor. */
-static char *
-rep_hist_format_hs_stats(time_t now)
+ * are meant to be placed in the extra-info descriptor.
+ *
+ * Function works for both v2 and v3 stats depending on <b>is_v3</b>. */
+STATIC char *
+rep_hist_format_hs_stats(time_t now, bool is_v3)
{
char t[ISO_TIME_LEN+1];
char *hs_stats_string;
- int64_t obfuscated_cells_seen;
- int64_t obfuscated_onions_seen;
+ int64_t obfuscated_onions_seen, obfuscated_cells_seen;
+
+ uint64_t rp_cells_seen = is_v3 ?
+ hs_v3_stats->rp_v3_relay_cells_seen : hs_v2_stats->rp_v2_relay_cells_seen;
+ size_t onions_seen = is_v3 ?
+ digest256map_size(hs_v3_stats->v3_onions_seen_this_period) :
+ digestmap_size(hs_v2_stats->v2_onions_seen_this_period);
+ time_t start_of_hs_stats_interval = is_v3 ?
+ start_of_hs_v3_stats_interval : start_of_hs_v2_stats_interval;
uint64_t rounded_cells_seen
- = round_uint64_to_next_multiple_of(hs_stats->rp_relay_cells_seen,
- REND_CELLS_BIN_SIZE);
+ = round_uint64_to_next_multiple_of(rp_cells_seen, REND_CELLS_BIN_SIZE);
rounded_cells_seen = MIN(rounded_cells_seen, INT64_MAX);
obfuscated_cells_seen = add_laplace_noise((int64_t)rounded_cells_seen,
crypto_rand_double(),
REND_CELLS_DELTA_F, REND_CELLS_EPSILON);
uint64_t rounded_onions_seen =
- round_uint64_to_next_multiple_of((size_t)digestmap_size(
- hs_stats->onions_seen_this_period),
- ONIONS_SEEN_BIN_SIZE);
+ round_uint64_to_next_multiple_of(onions_seen, ONIONS_SEEN_BIN_SIZE);
rounded_onions_seen = MIN(rounded_onions_seen, INT64_MAX);
obfuscated_onions_seen = add_laplace_noise((int64_t)rounded_onions_seen,
crypto_rand_double(), ONIONS_SEEN_DELTA_F,
ONIONS_SEEN_EPSILON);
format_iso_time(t, now);
- tor_asprintf(&hs_stats_string, "hidserv-stats-end %s (%d s)\n"
- "hidserv-rend-relayed-cells %"PRId64" delta_f=%d "
- "epsilon=%.2f bin_size=%d\n"
- "hidserv-dir-onions-seen %"PRId64" delta_f=%d "
- "epsilon=%.2f bin_size=%d\n",
+ tor_asprintf(&hs_stats_string, "%s %s (%u s)\n"
+ "%s %"PRId64" delta_f=%d epsilon=%.2f bin_size=%d\n"
+ "%s %"PRId64" delta_f=%d epsilon=%.2f bin_size=%d\n",
+ is_v3 ? "hidserv-v3-stats-end" : "hidserv-stats-end",
t, (unsigned) (now - start_of_hs_stats_interval),
- (obfuscated_cells_seen), REND_CELLS_DELTA_F,
+ is_v3 ?
+ "hidserv-rend-v3-relayed-cells" : "hidserv-rend-relayed-cells",
+ obfuscated_cells_seen, REND_CELLS_DELTA_F,
REND_CELLS_EPSILON, REND_CELLS_BIN_SIZE,
- (obfuscated_onions_seen),
- ONIONS_SEEN_DELTA_F,
+ is_v3 ? "hidserv-dir-v3-onions-seen" :"hidserv-dir-onions-seen",
+ obfuscated_onions_seen, ONIONS_SEEN_DELTA_F,
ONIONS_SEEN_EPSILON, ONIONS_SEEN_BIN_SIZE);
return hs_stats_string;
}
/** If 24 hours have passed since the beginning of the current HS
- * stats period, write buffer stats to $DATADIR/stats/hidserv-stats
+ * stats period, write buffer stats to $DATADIR/stats/hidserv-v3-stats
* (possibly overwriting an existing file) and reset counters. Return
* when we would next want to write buffer stats or 0 if we never want to
- * write. */
+ * write. Function works for both v2 and v3 stats depending on <b>is_v3</b>.
+ */
time_t
-rep_hist_hs_stats_write(time_t now)
+rep_hist_hs_stats_write(time_t now, bool is_v3)
{
char *str = NULL;
+ time_t start_of_hs_stats_interval = is_v3 ?
+ start_of_hs_v3_stats_interval : start_of_hs_v2_stats_interval;
+
if (!start_of_hs_stats_interval) {
return 0; /* Not initialized. */
}
@@ -1912,15 +2048,20 @@ rep_hist_hs_stats_write(time_t now)
}
/* Generate history string. */
- str = rep_hist_format_hs_stats(now);
+ str = rep_hist_format_hs_stats(now, is_v3);
/* Reset HS history. */
- rep_hist_reset_hs_stats(now);
+ if (is_v3) {
+ rep_hist_reset_hs_v3_stats(now);
+ } else {
+ rep_hist_reset_hs_v2_stats(now);
+ }
/* Try to write to disk. */
if (!check_or_create_data_subdir("stats")) {
- write_to_data_subdir("stats", "hidserv-stats", str,
- "hidden service stats");
+ write_to_data_subdir("stats",
+ is_v3 ? "hidserv-v3-stats" : "hidserv-stats",
+ str, "hidden service stats");
}
done:
@@ -2134,7 +2275,8 @@ rep_hist_log_link_protocol_counts(void)
void
rep_hist_free_all(void)
{
- hs_stats_free(hs_stats);
+ hs_v2_stats_free(hs_v2_stats);
+ hs_v3_stats_free(hs_v3_stats);
digestmap_free(history_map, free_or_history);
tor_free(exit_bytes_read);
@@ -2155,3 +2297,19 @@ rep_hist_free_all(void)
tor_assert_nonfatal(rephist_total_alloc == 0);
tor_assert_nonfatal_once(rephist_total_num == 0);
}
+
+#ifdef TOR_UNIT_TESTS
+/* only exists for unit tests: get HSv2 stats object */
+const hs_v2_stats_t *
+rep_hist_get_hs_v2_stats(void)
+{
+ return hs_v2_stats;
+}
+
+/* only exists for unit tests: get HSv2 stats object */
+const hs_v3_stats_t *
+rep_hist_get_hs_v3_stats(void)
+{
+ return hs_v3_stats;
+}
+#endif /* defined(TOR_UNIT_TESTS) */
diff --git a/src/feature/stats/rephist.h b/src/feature/stats/rephist.h
index c9ebc5c328..de27b16ae0 100644
--- a/src/feature/stats/rephist.h
+++ b/src/feature/stats/rephist.h
@@ -65,10 +65,14 @@ MOCK_DECL(int, rep_hist_get_circuit_handshake_assigned, (uint16_t type));
void rep_hist_hs_stats_init(time_t now);
void rep_hist_hs_stats_term(void);
-time_t rep_hist_hs_stats_write(time_t now);
-char *rep_hist_get_hs_stats_string(void);
-void rep_hist_seen_new_rp_cell(void);
-void rep_hist_stored_maybe_new_hs(const crypto_pk_t *pubkey);
+time_t rep_hist_hs_stats_write(time_t now, bool is_v3);
+
+char *rep_hist_get_hs_v2_stats_string(void);
+void rep_hist_seen_new_rp_cell(bool is_v2);
+void rep_hist_hsdir_stored_maybe_new_v2_onion(const crypto_pk_t *pubkey);
+
+char *rep_hist_get_hs_v3_stats_string(void);
+void rep_hist_hsdir_stored_maybe_new_v3_onion(const uint8_t *blinded_key);
void rep_hist_free_all(void);
@@ -83,6 +87,40 @@ extern int onion_handshakes_requested[MAX_ONION_HANDSHAKE_TYPE+1];
extern int onion_handshakes_assigned[MAX_ONION_HANDSHAKE_TYPE+1];
#endif
+#ifdef REPHIST_PRIVATE
+/** Carries the various hidden service statistics, and any other
+ * information needed. */
+typedef struct hs_v2_stats_t {
+ /** How many v2 relay cells have we seen as rendezvous points? */
+ uint64_t rp_v2_relay_cells_seen;
+
+ /** Set of unique public key digests we've seen this stat period
+ * (could also be implemented as sorted smartlist). */
+ digestmap_t *v2_onions_seen_this_period;
+} hs_v2_stats_t;
+
+/** Structure that contains the various statistics we keep about v3
+ * services.
+ *
+ * Because of the time period logic of v3 services, v3 statistics are more
+ * sensitive to time than v2 stats. For this reason, we collect v3
+ * statistics strictly from 12:00UTC to 12:00UTC as dictated by
+ * 'start_of_hs_v3_stats_interval'.
+ **/
+typedef struct hs_v3_stats_t {
+ /** How many v3 relay cells have we seen as a rendezvous point? */
+ uint64_t rp_v3_relay_cells_seen;
+
+ /* The number of unique v3 onion descriptors (actually, unique v3 blind keys)
+ * we've seen during the measurement period */
+ digest256map_t *v3_onions_seen_this_period;
+} hs_v3_stats_t;
+
+MOCK_DECL(STATIC bool, should_collect_v3_stats,(void));
+
+STATIC char *rep_hist_format_hs_stats(time_t now, bool is_v3);
+#endif /* defined(REPHIST_PRIVATE) */
+
/**
* Represents the type of a cell for padding accounting
*/
@@ -108,4 +146,11 @@ void rep_hist_reset_padding_counts(void);
void rep_hist_prep_published_padding_counts(time_t now);
void rep_hist_padding_count_timers(uint64_t num_timers);
+#ifdef TOR_UNIT_TESTS
+struct hs_v2_stats_t;
+const struct hs_v2_stats_t *rep_hist_get_hs_v2_stats(void);
+struct hs_v3_stats_t;
+const struct hs_v3_stats_t *rep_hist_get_hs_v3_stats(void);
+#endif
+
#endif /* !defined(TOR_REPHIST_H) */
diff --git a/src/lib/crypt_ops/compat_openssl.h b/src/lib/crypt_ops/compat_openssl.h
index c2e1459078..aa66e0c3fa 100644
--- a/src/lib/crypt_ops/compat_openssl.h
+++ b/src/lib/crypt_ops/compat_openssl.h
@@ -34,7 +34,7 @@
#ifndef OPENSSL_1_1_API
#define OpenSSL_version(v) SSLeay_version(v)
-#define OpenSSL_version_num() SSLeay()
+#define tor_OpenSSL_version_num() SSLeay()
#define RAND_OpenSSL() RAND_SSLeay()
#define STATE_IS_SW_SERVER_HELLO(st) \
(((st) == SSL3_ST_SW_SRVR_HELLO_A) || \
@@ -42,6 +42,7 @@
#define OSSL_HANDSHAKE_STATE int
#define CONST_IF_OPENSSL_1_1_API
#else /* defined(OPENSSL_1_1_API) */
+#define tor_OpenSSL_version_num() OpenSSL_version_num()
#define STATE_IS_SW_SERVER_HELLO(st) \
((st) == TLS_ST_SW_SRVR_HELLO)
#define CONST_IF_OPENSSL_1_1_API const
diff --git a/src/lib/crypt_ops/crypto_openssl_mgt.c b/src/lib/crypt_ops/crypto_openssl_mgt.c
index 065cbca1cc..e763491a11 100644
--- a/src/lib/crypt_ops/crypto_openssl_mgt.c
+++ b/src/lib/crypt_ops/crypto_openssl_mgt.c
@@ -222,7 +222,7 @@ crypto_openssl_early_init(void)
setup_openssl_threading();
- unsigned long version_num = OpenSSL_version_num();
+ unsigned long version_num = tor_OpenSSL_version_num();
const char *version_str = crypto_openssl_get_version_str();
if (version_num == OPENSSL_VERSION_NUMBER &&
!strcmp(version_str, OPENSSL_VERSION_TEXT)) {
diff --git a/src/lib/fs/conffile.c b/src/lib/fs/conffile.c
index f1f6d8ae5f..0d0bdf09a6 100644
--- a/src/lib/fs/conffile.c
+++ b/src/lib/fs/conffile.c
@@ -19,9 +19,11 @@
#include "lib/fs/path.h"
#include "lib/log/log.h"
#include "lib/malloc/malloc.h"
+#include "lib/sandbox/sandbox.h"
#include "lib/string/printf.h"
#include <stdbool.h>
+#include <errno.h>
static smartlist_t *config_get_file_list(const char *path,
smartlist_t *opened_files);
@@ -52,21 +54,26 @@ config_get_lines_include(const char *string, config_line_t **result,
opened_lst, 1, NULL, config_process_include);
}
-/** Returns a list of paths obtained when expading globs in <b>pattern</b>. If
- * <b>pattern</b> has no globs, returns a list with <b>pattern</b> if it is an
- * existing path or NULL otherwise. If <b>opened_files</b> is provided, adds
- * paths opened by glob to it. Returns NULL on failure. */
+/** Return a list of paths obtained when expading globs in <b>pattern</b>.
+ * If <b>pattern</b> has no globs, return a list with <b>pattern</b> in it.
+ * If <b>opened_files</b> is provided, add paths opened by glob to it.
+ * Return NULL on failure. */
static smartlist_t *
expand_glob(const char *pattern, smartlist_t *opened_files)
{
- smartlist_t *matches = tor_glob(pattern);
- if (!matches) {
- return NULL;
+ if (! has_glob(pattern)) {
+ smartlist_t *matches = smartlist_new();
+ smartlist_add_strdup(matches, pattern);
+ return matches;
}
- // if it is not a glob, return error when the path is missing
- if (!has_glob(pattern) && smartlist_len(matches) == 0) {
- smartlist_free(matches);
+ smartlist_t *matches = tor_glob(pattern);
+ if (!matches) {
+ if (errno == EPERM) {
+ log_err(LD_CONFIG, "Sandbox is active, but the configuration pattern "
+ "\"%s\" listed with %%include would access files or folders not "
+ "allowed by it. Cannot proceed.", pattern);
+ }
return NULL;
}
@@ -107,6 +114,13 @@ config_get_file_list(const char *pattern, smartlist_t *opened_files)
if (opened_files) {
smartlist_add_strdup(opened_files, path);
}
+ if (sandbox_interned_string_is_missing(path)) {
+ log_err(LD_CONFIG, "Sandbox is active, but a new configuration "
+ "file \"%s\" has been listed with %%include. Cannot proceed.",
+ path);
+ error_found = true;
+ break;
+ }
file_status_t file_type = file_status(path);
if (file_type == FN_FILE) {
@@ -201,6 +215,13 @@ config_process_include(const char *pattern, int recursion_level, int extended,
int rv = -1;
SMARTLIST_FOREACH_BEGIN(config_files, const char *, config_file) {
+ if (sandbox_interned_string_is_missing(config_file)) {
+ log_err(LD_CONFIG, "Sandbox is active, but a new configuration "
+ "file \"%s\" has been listed with %%include. Cannot proceed.",
+ config_file);
+ goto done;
+ }
+
log_notice(LD_CONFIG, "Including configuration file \"%s\".", config_file);
config_line_t *included_config = NULL;
config_line_t *included_config_last = NULL;
diff --git a/src/lib/fs/path.c b/src/lib/fs/path.c
index fc759f6169..c2fdddb9db 100644
--- a/src/lib/fs/path.c
+++ b/src/lib/fs/path.c
@@ -537,6 +537,10 @@ unglob_win32(const char *pattern, int prev_sep, int next_sep)
static DIR *
prot_opendir(const char *name)
{
+ if (sandbox_interned_string_is_missing(name)) {
+ errno = EPERM;
+ return NULL;
+ }
return opendir(sandbox_intern_string(name));
}
@@ -544,6 +548,10 @@ prot_opendir(const char *name)
static int
prot_stat(const char *pathname, struct stat *buf)
{
+ if (sandbox_interned_string_is_missing(pathname)) {
+ errno = EPERM;
+ return -1;
+ }
return stat(sandbox_intern_string(pathname), buf);
}
@@ -551,6 +559,10 @@ prot_stat(const char *pathname, struct stat *buf)
static int
prot_lstat(const char *pathname, struct stat *buf)
{
+ if (sandbox_interned_string_is_missing(pathname)) {
+ errno = EPERM;
+ return -1;
+ }
return lstat(sandbox_intern_string(pathname), buf);
}
/** As closedir, but has the right type for gl_closedir */
@@ -563,7 +575,8 @@ wrap_closedir(void *arg)
/** Return a new list containing the paths that match the pattern
* <b>pattern</b>. Return NULL on error. On POSIX systems, errno is set by the
- * glob function.
+ * glob function or is set to EPERM if glob tried to access a file not allowed
+ * by the seccomp sandbox.
*/
struct smartlist_t *
tor_glob(const char *pattern)
diff --git a/src/lib/sandbox/sandbox.c b/src/lib/sandbox/sandbox.c
index 8d467c516e..168dfd943c 100644
--- a/src/lib/sandbox/sandbox.c
+++ b/src/lib/sandbox/sandbox.c
@@ -310,6 +310,8 @@ static int filter_nopar_gen[] = {
#define seccomp_rule_add_4(ctx,act,call,f1,f2,f3,f4) \
seccomp_rule_add((ctx),(act),(call),4,(f1),(f2),(f3),(f4))
+static const char *sandbox_get_interned_string(const char *str);
+
/**
* Function responsible for setting up the rt_sigaction syscall for
* the seccomp filter sandbox.
@@ -1224,9 +1226,42 @@ static sandbox_filter_func_t filter_func[] = {
sb_kill
};
+/**
+ * Return the interned (and hopefully sandbox-permitted) string equal
+ * to @a str.
+ *
+ * Return NULL if `str` is NULL, or `str` is not an interned string.
+ **/
const char *
sandbox_intern_string(const char *str)
{
+ const char *interned = sandbox_get_interned_string(str);
+
+ if (sandbox_active && str != NULL && interned == NULL) {
+ log_warn(LD_BUG, "No interned sandbox parameter found for %s", str);
+ }
+
+ return interned ? interned : str;
+}
+
+/**
+ * Return true if the sandbox is running and we are missing an interned string
+ * equal to @a str.
+ */
+bool
+sandbox_interned_string_is_missing(const char *str)
+{
+ return sandbox_active && sandbox_get_interned_string(str) == NULL;
+}
+
+/**
+ * Try to find and return the interned string equal to @a str.
+ *
+ * If there is no such string, return NULL.
+ **/
+static const char *
+sandbox_get_interned_string(const char *str)
+{
sandbox_cfg_t *elem;
if (str == NULL)
@@ -1245,9 +1280,7 @@ sandbox_intern_string(const char *str)
}
}
- if (sandbox_active)
- log_warn(LD_BUG, "No interned sandbox parameter found for %s", str);
- return str;
+ return NULL;
}
/* DOCDOC */
diff --git a/src/lib/sandbox/sandbox.h b/src/lib/sandbox/sandbox.h
index a2b3227b90..eba99afbde 100644
--- a/src/lib/sandbox/sandbox.h
+++ b/src/lib/sandbox/sandbox.h
@@ -104,12 +104,11 @@ typedef struct {
#endif /* defined(USE_LIBSECCOMP) */
#ifdef USE_LIBSECCOMP
-/** Returns a registered protected string used with the sandbox, given that
- * it matches the parameter.
- */
const char* sandbox_intern_string(const char *param);
+bool sandbox_interned_string_is_missing(const char *s);
#else /* !defined(USE_LIBSECCOMP) */
#define sandbox_intern_string(s) (s)
+#define sandbox_interned_string_is_missing(s) (false)
#endif /* defined(USE_LIBSECCOMP) */
/** Creates an empty sandbox configuration file.*/
diff --git a/src/lib/tls/tortls_openssl.c b/src/lib/tls/tortls_openssl.c
index 2269714141..ad9b49ab4f 100644
--- a/src/lib/tls/tortls_openssl.c
+++ b/src/lib/tls/tortls_openssl.c
@@ -342,7 +342,7 @@ tor_tls_init(void)
#if (SIZEOF_VOID_P >= 8 && \
OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,1))
- long version = OpenSSL_version_num();
+ long version = tor_OpenSSL_version_num();
/* LCOV_EXCL_START : we can't test these lines on the same machine */
if (version >= OPENSSL_V_SERIES(1,0,1)) {
diff --git a/src/test/hs_build_address.py b/src/test/hs_build_address.py
index 91864eabcb..216b7626bf 100644
--- a/src/test/hs_build_address.py
+++ b/src/test/hs_build_address.py
@@ -10,17 +10,21 @@ import base64
# Python 3.6+, the SHA3 is available in hashlib natively. Else this requires
# the pysha3 package (pip install pysha3).
+TEST_INPUT = b"Hello World"
if sys.version_info < (3, 6):
import sha3
+ m = sha3.sha3_256(TEST_INPUT)
+else:
+ m = hashlib.sha3_256(TEST_INPUT)
# Test vector to make sure the right sha3 version will be used. pysha3 < 1.0
# used the old Keccak implementation. During the finalization of SHA3, NIST
# changed the delimiter suffix from 0x01 to 0x06. The Keccak sponge function
# stayed the same. pysha3 1.0 provides the previous Keccak hash, too.
TEST_VALUE = "e167f68d6563d75bb25f3aa49c29ef612d41352dc00606de7cbd630bb2665f51"
-if TEST_VALUE != sha3.sha3_256(b"Hello World").hexdigest():
+if TEST_VALUE != m.hexdigest():
print("pysha3 version is < 1.0. Please install from:")
- print("https://github.com/tiran/pysha3https://github.com/tiran/pysha3")
+ print("https://github.com/tiran/pysha3")
sys.exit(1)
# Checksum is built like so:
@@ -28,7 +32,11 @@ if TEST_VALUE != sha3.sha3_256(b"Hello World").hexdigest():
PREFIX = ".onion checksum".encode()
# 32 bytes ed25519 pubkey from first test vector of
# https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-02#section-6
-PUBKEY = "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a".decode('hex')
+PUBKEY_STRING = "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a"
+if sys.version_info < (3, 0):
+ PUBKEY = PUBKEY_STRING.decode('hex')
+else:
+ PUBKEY = bytes.fromhex(PUBKEY_STRING)
# Version 3 is proposal224
VERSION = 3
diff --git a/src/test/hs_ntor_ref.py b/src/test/hs_ntor_ref.py
index f107cc36ca..98025dd584 100644
--- a/src/test/hs_ntor_ref.py
+++ b/src/test/hs_ntor_ref.py
@@ -70,14 +70,16 @@ except ImportError:
try:
# Pull the sha3 functions in.
from hashlib import sha3_256, shake_256
- shake_squeeze = shake_256.digest
+ def shake_squeeze(obj, n):
+ return obj.digest(n)
except ImportError:
if hasattr(sha3, "SHA3256"):
# If this happens, then we have the old "sha3" module which
# hashlib and pysha3 superseded.
sha3_256 = sha3.SHA3256
shake_256 = sha3.SHAKE256
- shake_squeeze = shake_256.squeeze
+ def shake_squeeze(obj, n):
+ return obj.squeeze(n)
else:
# error code 77 tells automake to skip this test
sys.exit(77)
diff --git a/src/test/hs_test_helpers.c b/src/test/hs_test_helpers.c
index e9aafa4760..e1ecf9fe56 100644
--- a/src/test/hs_test_helpers.c
+++ b/src/test/hs_test_helpers.c
@@ -134,7 +134,8 @@ hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
* points are added. */
static hs_descriptor_t *
hs_helper_build_hs_desc_impl(unsigned int no_ip,
- const ed25519_keypair_t *signing_kp)
+ const ed25519_keypair_t *signing_kp,
+ uint64_t rev_counter)
{
int ret;
int i;
@@ -161,7 +162,7 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip,
&signing_kp->pubkey, now, 3600,
CERT_FLAG_INCLUDE_SIGNING_KEY);
tt_assert(desc->plaintext_data.signing_key_cert);
- desc->plaintext_data.revision_counter = 42;
+ desc->plaintext_data.revision_counter = rev_counter;
desc->plaintext_data.lifetime_sec = 3 * 60 * 60;
hs_get_subcredential(&signing_kp->pubkey, &blinded_kp.pubkey,
@@ -226,18 +227,26 @@ hs_helper_get_subcred_from_identity_keypair(ed25519_keypair_t *signing_kp,
subcred_out);
}
+/* Build a descriptor with a specific rev counter. */
+hs_descriptor_t *
+hs_helper_build_hs_desc_with_rev_counter(const ed25519_keypair_t *signing_kp,
+ uint64_t revision_counter)
+{
+ return hs_helper_build_hs_desc_impl(0, signing_kp, revision_counter);
+}
+
/* Build a descriptor with introduction points. */
hs_descriptor_t *
hs_helper_build_hs_desc_with_ip(const ed25519_keypair_t *signing_kp)
{
- return hs_helper_build_hs_desc_impl(0, signing_kp);
+ return hs_helper_build_hs_desc_impl(0, signing_kp, 42);
}
/* Build a descriptor without any introduction points. */
hs_descriptor_t *
hs_helper_build_hs_desc_no_ip(const ed25519_keypair_t *signing_kp)
{
- return hs_helper_build_hs_desc_impl(1, signing_kp);
+ return hs_helper_build_hs_desc_impl(1, signing_kp, 42);
}
hs_descriptor_t *
@@ -247,7 +256,7 @@ hs_helper_build_hs_desc_with_client_auth(
const ed25519_keypair_t *signing_kp)
{
curve25519_keypair_t auth_ephemeral_kp;
- hs_descriptor_t *desc = hs_helper_build_hs_desc_impl(0, signing_kp);
+ hs_descriptor_t *desc = hs_helper_build_hs_desc_impl(0, signing_kp, 42);
hs_desc_authorized_client_t *desc_client;
/* The number of client authorized auth has tobe a multiple of
diff --git a/src/test/hs_test_helpers.h b/src/test/hs_test_helpers.h
index 23d11f2a4a..e22295b660 100644
--- a/src/test/hs_test_helpers.h
+++ b/src/test/hs_test_helpers.h
@@ -17,6 +17,10 @@ hs_descriptor_t *hs_helper_build_hs_desc_no_ip(
const ed25519_keypair_t *signing_kp);
hs_descriptor_t *hs_helper_build_hs_desc_with_ip(
const ed25519_keypair_t *signing_kp);
+hs_descriptor_t *
+hs_helper_build_hs_desc_with_rev_counter(const ed25519_keypair_t *signing_kp,
+ uint64_t revision_counter);
+
hs_descriptor_t *hs_helper_build_hs_desc_with_client_auth(
const uint8_t *descriptor_cookie,
const curve25519_public_key_t *client_pk,
diff --git a/src/test/test_config.c b/src/test/test_config.c
index a0278f9422..49d7b87410 100644
--- a/src/test/test_config.c
+++ b/src/test/test_config.c
@@ -706,11 +706,13 @@ test_config_parse_tcp_proxy_line(void *arg)
tor_free(msg);
/* Bad TCPProxy line - unparsable address/port. */
- ret = parse_tcp_proxy_line("haproxy 95.216.163.36/443", options, &msg);
+ MOCK(tor_addr_lookup, mock_tor_addr_lookup__fail_on_bad_addrs);
+ ret = parse_tcp_proxy_line("haproxy bogus_address!/300", options, &msg);
tt_int_op(ret, OP_EQ, -1);
tt_str_op(msg, OP_EQ, "TCPProxy address/port failed to parse or resolve. "
"Please fix.");
tor_free(msg);
+ UNMOCK(tor_addr_lookup);
/* Good TCPProxy line - ipv4. */
ret = parse_tcp_proxy_line("haproxy 95.216.163.36:443", options, &msg);
@@ -723,7 +725,7 @@ test_config_parse_tcp_proxy_line(void *arg)
tor_free(msg);
done:
- ;
+ UNMOCK(tor_addr_lookup);
}
/**
@@ -6855,9 +6857,15 @@ test_config_duplicate_orports(void *arg)
port_parse_config(ports, config_port, "OR", CONN_TYPE_OR_LISTENER, "[::]",
0, CL_PORT_SERVER_OPTIONS);
- // There should be three ports at this point.
- tt_int_op(smartlist_len(ports), OP_EQ, 3);
+ /* There should be 4 ports at this point that is:
+ * - 0.0.0.0:9050
+ * - [::]:9050
+ * - [::1]:9050
+ * - [::1]:9050
+ */
+ tt_int_op(smartlist_len(ports), OP_EQ, 4);
+ /* This will remove the [::] and the extra [::1]. */
remove_duplicate_orports(ports);
// The explicit IPv6 port should have replaced the implicit IPv6 port.
@@ -6869,6 +6877,33 @@ test_config_duplicate_orports(void *arg)
config_free_lines(config_port);
}
+static void
+test_config_multifamily_port(void *arg)
+{
+ (void) arg;
+
+ config_line_t *config_port = NULL;
+ smartlist_t *ports = smartlist_new();
+
+ config_line_append(&config_port, "SocksPort", "9050");
+ config_line_append(&config_port, "SocksPort", "[::1]:9050");
+
+ // Parse IPv4, then IPv6.
+ port_parse_config(ports, config_port, "SOCKS", CONN_TYPE_AP_LISTENER,
+ "0.0.0.0", 9050, 0);
+
+ /* There should be 2 ports at this point that is:
+ * - 0.0.0.0:9050
+ * - [::1]:9050
+ */
+ tt_int_op(smartlist_len(ports), OP_EQ, 2);
+
+ done:
+ SMARTLIST_FOREACH(ports, port_cfg_t *, cfg, port_cfg_free(cfg));
+ smartlist_free(ports);
+ config_free_lines(config_port);
+}
+
#ifndef COCCI
#define CONFIG_TEST(name, flags) \
{ #name, test_config_ ## name, flags, NULL, NULL }
@@ -6937,5 +6972,6 @@ struct testcase_t config_tests[] = {
CONFIG_TEST(kvline_parse, 0),
CONFIG_TEST(getinfo_config_names, 0),
CONFIG_TEST(duplicate_orports, 0),
+ CONFIG_TEST(multifamily_port, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c
index 7935530653..fd260f06dc 100644
--- a/src/test/test_extorport.c
+++ b/src/test/test_extorport.c
@@ -24,60 +24,6 @@
#include <sys/stat.h>
#endif
-/* Test connection_or_remove_from_ext_or_id_map and
- * connection_or_set_ext_or_identifier */
-static void
-test_ext_or_id_map(void *arg)
-{
- or_connection_t *c1 = NULL, *c2 = NULL, *c3 = NULL;
- char *idp = NULL, *idp2 = NULL;
- (void)arg;
-
- /* pre-initialization */
- tt_ptr_op(NULL, OP_EQ,
- connection_or_get_by_ext_or_id("xxxxxxxxxxxxxxxxxxxx"));
-
- c1 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
- c2 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
- c3 = or_connection_new(CONN_TYPE_OR, AF_INET);
-
- tt_ptr_op(c1->ext_or_conn_id, OP_NE, NULL);
- tt_ptr_op(c2->ext_or_conn_id, OP_NE, NULL);
- tt_ptr_op(c3->ext_or_conn_id, OP_EQ, NULL);
-
- tt_ptr_op(c1, OP_EQ, connection_or_get_by_ext_or_id(c1->ext_or_conn_id));
- tt_ptr_op(c2, OP_EQ, connection_or_get_by_ext_or_id(c2->ext_or_conn_id));
- tt_ptr_op(NULL, OP_EQ,
- connection_or_get_by_ext_or_id("xxxxxxxxxxxxxxxxxxxx"));
-
- idp = tor_memdup(c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN);
-
- /* Give c2 a new ID. */
- connection_or_set_ext_or_identifier(c2);
- tt_mem_op(idp, OP_NE, c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN);
- idp2 = tor_memdup(c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN);
- tt_assert(!tor_digest_is_zero(idp2));
-
- tt_ptr_op(NULL, OP_EQ, connection_or_get_by_ext_or_id(idp));
- tt_ptr_op(c2, OP_EQ, connection_or_get_by_ext_or_id(idp2));
-
- /* Now remove it. */
- connection_or_remove_from_ext_or_id_map(c2);
- tt_ptr_op(NULL, OP_EQ, connection_or_get_by_ext_or_id(idp));
- tt_ptr_op(NULL, OP_EQ, connection_or_get_by_ext_or_id(idp2));
-
- done:
- if (c1)
- connection_free_minimal(TO_CONN(c1));
- if (c2)
- connection_free_minimal(TO_CONN(c2));
- if (c3)
- connection_free_minimal(TO_CONN(c3));
- tor_free(idp);
- tor_free(idp2);
- connection_or_clear_ext_or_id_map();
-}
-
/* Simple connection_write_to_buf_impl_ replacement that unconditionally
* writes to outbuf. */
static void
@@ -581,7 +527,6 @@ test_ext_or_handshake(void *arg)
}
struct testcase_t extorport_tests[] = {
- { "id_map", test_ext_or_id_map, TT_FORK, NULL, NULL },
{ "write_command", test_ext_or_write_command, TT_FORK, NULL, NULL },
{ "init_auth", test_ext_or_init_auth, TT_FORK, NULL, NULL },
{ "cookie_auth", test_ext_or_cookie_auth, TT_FORK, NULL, NULL },
diff --git a/src/test/test_key_expiration.sh b/src/test/test_key_expiration.sh
index 1ba8179aa1..2e2745e0a3 100755
--- a/src/test/test_key_expiration.sh
+++ b/src/test/test_key_expiration.sh
@@ -107,7 +107,7 @@ TOR="${TOR_BINARY} --DisableNetwork 1 --ShutdownWaitLength 0 --ORPort 12345 --Ex
# Step 1: Start Tor with --list-fingerprint --quiet. Make sure everything is there.
echo "Setup step #1"
-${TOR} --list-fingerprint ${SILENTLY} > /dev/null
+${TOR} ${SILENTLY} --list-fingerprint > /dev/null
check_dir "${DATA_DIR}/keys"
check_file "${DATA_DIR}/keys/ed25519_master_id_public_key"
diff --git a/src/test/test_keygen.sh b/src/test/test_keygen.sh
index 6812f8883d..be1fde9e32 100755
--- a/src/test/test_keygen.sh
+++ b/src/test/test_keygen.sh
@@ -120,7 +120,7 @@ TOR="${TOR_BINARY} ${QUIETLY} --DisableNetwork 1 --ShutdownWaitLength 0 --ORPort
# Step 1: Start Tor with --list-fingerprint --quiet. Make sure everything is there.
mkdir "${DATA_DIR}/orig"
-${TOR} --DataDirectory "${DATA_DIR}/orig" --list-fingerprint ${SILENTLY} > /dev/null
+${TOR} --DataDirectory "${DATA_DIR}/orig" ${SILENTLY} --list-fingerprint > /dev/null
check_dir "${DATA_DIR}/orig/keys"
check_file "${DATA_DIR}/orig/keys/ed25519_master_id_public_key"
@@ -206,7 +206,7 @@ SRC="${DATA_DIR}/orig"
mkdir -p "${ME}/keys"
cp "${SRC}/keys/ed25519_master_id_"* "${ME}/keys/"
-${TOR} --DataDirectory "${ME}" --list-fingerprint ${SILENTLY} >/dev/null || die "Tor failed when starting with only master key"
+${TOR} --DataDirectory "${ME}" ${SILENTLY} --list-fingerprint >/dev/null || die "Tor failed when starting with only master key"
check_files_eq "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/ed25519_master_id_public_key"
check_files_eq "${SRC}/keys/ed25519_master_id_secret_key" "${ME}/keys/ed25519_master_id_secret_key"
check_file "${ME}/keys/ed25519_signing_cert"
@@ -264,11 +264,11 @@ SRC="${DATA_DIR}/orig"
mkdir -p "${ME}/keys"
cp "${SRC}/keys/ed25519_master_id_secret_key" "${ME}/keys/"
-${TOR} --DataDirectory "${ME}" --list-fingerprint ${SILENTLY} > "${ME}/fp1" || die "Tor wouldn't start with only unencrypted secret key"
+${TOR} --DataDirectory "${ME}" ${SILENTLY} --list-fingerprint > "${ME}/fp1" || die "Tor wouldn't start with only unencrypted secret key"
check_file "${ME}/keys/ed25519_master_id_public_key"
check_file "${ME}/keys/ed25519_signing_cert"
check_file "${ME}/keys/ed25519_signing_secret_key"
-${TOR} --DataDirectory "${ME}" --list-fingerprint ${SILENTLY} > "${ME}/fp2" || die "Tor wouldn't start again after starting once with only unencrypted secret key."
+${TOR} --DataDirectory "${ME}" ${SILENTLY} --list-fingerprint > "${ME}/fp2" || die "Tor wouldn't start again after starting once with only unencrypted secret key."
check_files_eq "${ME}/fp1" "${ME}/fp2"
@@ -330,7 +330,7 @@ cp "${SRC}/keys/ed25519_master_id_secret_key" "${ME}/keys/"
cp "${SRC}/keys/ed25519_signing_cert" "${ME}/keys/"
cp "${SRC}/keys/ed25519_signing_secret_key" "${ME}/keys/"
-${TOR} --DataDirectory "${ME}" --list-fingerprint ${SILENTLY} >/dev/null || die "Failed when starting with missing public key"
+${TOR} --DataDirectory "${ME}" ${SILENTLY} --list-fingerprint >/dev/null || die "Failed when starting with missing public key"
check_keys_eq ed25519_master_id_secret_key
check_keys_eq ed25519_master_id_public_key
check_keys_eq ed25519_signing_secret_key
@@ -352,7 +352,7 @@ cp "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/"
cp "${SRC}/keys/ed25519_signing_cert" "${ME}/keys/"
cp "${SRC}/keys/ed25519_signing_secret_key" "${ME}/keys/"
-${TOR} --DataDirectory "${ME}" --list-fingerprint ${SILENTLY} >/dev/null || die "Failed when starting with offline secret key"
+${TOR} --DataDirectory "${ME}" ${SILENTLY} --list-fingerprint >/dev/null || die "Failed when starting with offline secret key"
check_no_file "${ME}/keys/ed25519_master_id_secret_key"
check_keys_eq ed25519_master_id_public_key
check_keys_eq ed25519_signing_secret_key
@@ -373,7 +373,7 @@ mkdir -p "${ME}/keys"
cp "${SRC}/keys/ed25519_signing_cert" "${ME}/keys/"
cp "${SRC}/keys/ed25519_signing_secret_key" "${ME}/keys/"
-${TOR} --DataDirectory "${ME}" --list-fingerprint ${SILENTLY} >/dev/null || die "Failed when starting with only signing material"
+${TOR} --DataDirectory "${ME}" ${SILENTLY} --list-fingerprint >/dev/null || die "Failed when starting with only signing material"
check_no_file "${ME}/keys/ed25519_master_id_secret_key"
check_file "${ME}/keys/ed25519_master_id_public_key"
check_keys_eq ed25519_signing_secret_key
diff --git a/src/test/test_stats.c b/src/test/test_stats.c
index b6849b0b6d..25ee837f2f 100644
--- a/src/test/test_stats.c
+++ b/src/test/test_stats.c
@@ -12,6 +12,8 @@
#include "lib/crypt_ops/crypto_rand.h"
#include "app/config/or_state_st.h"
#include "test/rng_test_helpers.h"
+#include "feature/hs/hs_cache.h"
+#include "test/hs_test_helpers.h"
#include <stdio.h>
@@ -31,6 +33,7 @@
#define MAINLOOP_PRIVATE
#define STATEFILE_PRIVATE
#define BWHIST_PRIVATE
+#define REPHIST_PRIVATE
#include "core/or/or.h"
#include "lib/err/backtrace.h"
@@ -493,6 +496,133 @@ test_get_bandwidth_lines(void *arg)
bwhist_free_all();
}
+static bool
+mock_should_collect_v3_stats(void)
+{
+ return true;
+}
+
+/* Test v3 metrics */
+static void
+test_rephist_v3_onions(void *arg)
+{
+ int ret;
+
+ char *stats_string = NULL;
+ char *desc1_str = NULL;
+ ed25519_keypair_t signing_kp1;
+ hs_descriptor_t *desc1 = NULL;
+
+ const hs_v3_stats_t *hs_v3_stats = NULL;
+
+ (void) arg;
+
+ MOCK(should_collect_v3_stats, mock_should_collect_v3_stats);
+
+ get_options_mutable()->HiddenServiceStatistics = 1;
+
+ /* Initialize the subsystems */
+ hs_cache_init();
+ rep_hist_hs_stats_init(0);
+
+ /* Change time to 03-01-2002 23:36 UTC */
+ update_approx_time(1010101010);
+
+ /* HS stats should be zero here */
+ hs_v3_stats = rep_hist_get_hs_v3_stats();
+ tt_int_op(digest256map_size(hs_v3_stats->v3_onions_seen_this_period),
+ OP_EQ, 0);
+
+ /* Generate a valid descriptor */
+ ret = ed25519_keypair_generate(&signing_kp1, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ desc1 = hs_helper_build_hs_desc_with_rev_counter(&signing_kp1, 42);
+ tt_assert(desc1);
+ ret = hs_desc_encode_descriptor(desc1, &signing_kp1, NULL, &desc1_str);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* Store descriptor and check that stats got updated */
+ ret = hs_cache_store_as_dir(desc1_str);
+ tt_int_op(ret, OP_EQ, 0);
+ hs_v3_stats = rep_hist_get_hs_v3_stats();
+ tt_int_op(digest256map_size(hs_v3_stats->v3_onions_seen_this_period),
+ OP_EQ, 1);
+
+ /* cleanup */
+ hs_descriptor_free(desc1);
+ tor_free(desc1_str);
+
+ /* Generate another valid descriptor */
+ ret = ed25519_keypair_generate(&signing_kp1, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ desc1 = hs_helper_build_hs_desc_with_rev_counter(&signing_kp1, 42);
+ tt_assert(desc1);
+ ret = hs_desc_encode_descriptor(desc1, &signing_kp1, NULL, &desc1_str);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* Store descriptor and check that stats are updated */
+ ret = hs_cache_store_as_dir(desc1_str);
+ tt_int_op(ret, OP_EQ, 0);
+ hs_v3_stats = rep_hist_get_hs_v3_stats();
+ tt_int_op(digest256map_size(hs_v3_stats->v3_onions_seen_this_period),
+ OP_EQ, 2);
+
+ /* Check that storing the same descriptor twice does not work */
+ ret = hs_cache_store_as_dir(desc1_str);
+ tt_int_op(ret, OP_EQ, -1);
+
+ /* cleanup */
+ hs_descriptor_free(desc1);
+ tor_free(desc1_str);
+
+ /* Create a descriptor with the same identity key but diff rev counter and
+ same blinded key */
+ desc1 = hs_helper_build_hs_desc_with_rev_counter(&signing_kp1, 43);
+ tt_assert(desc1);
+ ret = hs_desc_encode_descriptor(desc1, &signing_kp1, NULL, &desc1_str);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* Store descriptor and check that stats are updated */
+ ret = hs_cache_store_as_dir(desc1_str);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(digest256map_size(hs_v3_stats->v3_onions_seen_this_period),
+ OP_EQ, 2);
+
+ /* cleanup */
+ hs_descriptor_free(desc1);
+ tor_free(desc1_str);
+
+ /* Now let's skip to four days forward so that the blinded key rolls
+ forward */
+ update_approx_time(approx_time() + 345600);
+
+ /* Now create a descriptor with the same identity key but diff rev counter
+ and different blinded key */
+ desc1 = hs_helper_build_hs_desc_with_rev_counter(&signing_kp1, 44);
+ tt_assert(desc1);
+ ret = hs_desc_encode_descriptor(desc1, &signing_kp1, NULL, &desc1_str);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* Store descriptor and check that stats are updated */
+ ret = hs_cache_store_as_dir(desc1_str);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(digest256map_size(hs_v3_stats->v3_onions_seen_this_period),
+ OP_EQ, 3);
+
+ /* cleanup */
+ hs_descriptor_free(desc1);
+ tor_free(desc1_str);
+
+ /* Because of differential privacy we can't actually check the stat value,
+ but let's just check that it's formatted correctly. */
+ stats_string = rep_hist_format_hs_stats(approx_time(), true);
+ tt_assert(strstr(stats_string, "hidserv-dir-v3-onions-seen"));
+
+ done:
+ UNMOCK(should_collect_v3_stats);
+ tor_free(stats_string);
+}
+
#define ENT(name) \
{ #name, test_ ## name , 0, NULL, NULL }
#define FORK(name) \
@@ -506,6 +636,7 @@ struct testcase_t stats_tests[] = {
FORK(add_obs),
FORK(fill_bandwidth_history),
FORK(get_bandwidth_lines),
+ FORK(rephist_v3_onions),
END_OF_TESTCASES
};
diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h
index 57ec53cf34..908ea08d3a 100644
--- a/src/win32/orconfig.h
+++ b/src/win32/orconfig.h
@@ -217,7 +217,7 @@
#define USING_TWOS_COMPLEMENT
/* Version number of package */
-#define VERSION "0.4.5.1-alpha-dev"
+#define VERSION "0.4.6.0-alpha-dev"
#define HAVE_STRUCT_SOCKADDR_IN6
#define HAVE_STRUCT_IN6_ADDR