summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app/config/app_config.md3
-rw-r--r--src/app/config/config.c33
-rw-r--r--src/app/config/config.h2
-rw-r--r--src/app/config/resolve_addr.c3
-rw-r--r--src/core/mainloop/connection.c6
-rw-r--r--src/core/or/channel.c2
-rw-r--r--src/core/or/channel.h3
-rw-r--r--src/core/or/channeltls.c7
-rw-r--r--src/core/or/circuitbuild.c2
-rw-r--r--src/core/or/circuitlist.c2
-rw-r--r--src/core/or/or_circuit_st.h4
-rw-r--r--src/core/or/status.c41
-rw-r--r--src/core/or/status.h1
-rw-r--r--src/core/proto/proto_socks.c4
-rw-r--r--src/ext/ed25519/donna/ed25519_tor.c1
-rw-r--r--src/feature/client/bridges.c6
-rw-r--r--src/feature/client/entrynodes.c6
-rw-r--r--src/feature/control/control_hs.c11
-rw-r--r--src/feature/hs/hs_client.c2
-rw-r--r--src/feature/hs/hs_client.h3
-rw-r--r--src/feature/hs/hs_dos.c5
-rw-r--r--src/feature/hs/hs_intropoint.c5
-rw-r--r--src/feature/nodelist/routerlist.c26
-rw-r--r--src/feature/relay/circuitbuild_relay.c24
-rw-r--r--src/feature/relay/relay_config.c2
-rw-r--r--src/feature/relay/router.c27
-rw-r--r--src/feature/relay/router.h1
-rw-r--r--src/feature/rend/rendmid.c6
-rw-r--r--src/lib/fs/path.c15
-rw-r--r--src/lib/log/ratelim.c12
-rw-r--r--src/lib/log/ratelim.h8
-rw-r--r--src/lib/string/compat_string.h2
-rw-r--r--src/test/include.am22
-rw-r--r--src/test/test_hs_control.c47
-rw-r--r--src/test/test_logging.c6
-rw-r--r--src/test/test_options.c2
-rw-r--r--src/test/test_status.c341
-rw-r--r--src/test/test_util.c12
-rw-r--r--src/test/testing_common.c42
-rwxr-xr-xsrc/test/unittest_part1.sh3
-rwxr-xr-xsrc/test/unittest_part2.sh3
-rwxr-xr-xsrc/test/unittest_part3.sh3
-rwxr-xr-xsrc/test/unittest_part4.sh3
-rwxr-xr-xsrc/test/unittest_part5.sh3
-rwxr-xr-xsrc/test/unittest_part6.sh3
-rwxr-xr-xsrc/test/unittest_part7.sh3
-rwxr-xr-xsrc/test/unittest_part8.sh3
47 files changed, 438 insertions, 333 deletions
diff --git a/src/app/config/app_config.md b/src/app/config/app_config.md
index b359ce77f6..96a55494ff 100644
--- a/src/app/config/app_config.md
+++ b/src/app/config/app_config.md
@@ -2,5 +2,4 @@
@brief app/config: Top-level configuration code
Refactoring this module is a work in progress, see
-[ticket 29211](https://trac.torproject.org/projects/tor/ticket/29211).
-
+[ticket 29211](https://bugs.torproject.org/tpo/core/tor/29211)
diff --git a/src/app/config/config.c b/src/app/config/config.c
index 26be5d85d0..5c8a3792ee 100644
--- a/src/app/config/config.c
+++ b/src/app/config/config.c
@@ -3592,7 +3592,7 @@ options_validate_cb(const void *old_options_, void *options_, char **msg)
"configured. This is bad because it's very easy to locate your "
"entry guard which can then lead to the deanonymization of your "
"hidden service -- for more details, see "
- "https://trac.torproject.org/projects/tor/ticket/14917. "
+ "https://bugs.torproject.org/tpo/core/tor/14917. "
"For this reason, the use of one EntryNodes with an hidden "
"service is prohibited until a better solution is found.");
return -1;
@@ -3609,7 +3609,7 @@ options_validate_cb(const void *old_options_, void *options_, char **msg)
"be harmful to the service anonymity. Because of this, we "
"recommend you either don't do that or make sure you know what "
"you are doing. For more details, please look at "
- "https://trac.torproject.org/projects/tor/ticket/21155.");
+ "https://bugs.torproject.org/tpo/core/tor/21155.");
}
/* Single Onion Services: non-anonymous hidden services */
@@ -4050,8 +4050,11 @@ options_validate_cb(const void *old_options_, void *options_, char **msg)
* actual maximum value. We clip this value if it's too low, and autodetect
* it if it's set to 0. */
STATIC uint64_t
-compute_real_max_mem_in_queues(const uint64_t val, int log_guess)
+compute_real_max_mem_in_queues(const uint64_t val, bool is_server)
{
+#define MIN_SERVER_MB 64
+#define MIN_UNWARNED_SERVER_MB 256
+#define MIN_UNWARNED_CLIENT_MB 64
uint64_t result;
if (val == 0) {
@@ -4109,7 +4112,7 @@ compute_real_max_mem_in_queues(const uint64_t val, int log_guess)
result = avail;
}
}
- if (log_guess && ! notice_sent) {
+ if (is_server && ! notice_sent) {
log_notice(LD_CONFIG, "%sMaxMemInQueues is set to %"PRIu64" MB. "
"You can override this by setting MaxMemInQueues by hand.",
ram ? "Based on detected system memory, " : "",
@@ -4117,10 +4120,24 @@ compute_real_max_mem_in_queues(const uint64_t val, int log_guess)
notice_sent = 1;
}
return result;
- } else if (val < ONE_GIGABYTE / 4) {
- log_warn(LD_CONFIG, "MaxMemInQueues must be at least 256 MB for now. "
- "Ideally, have it as large as you can afford.");
- return ONE_GIGABYTE / 4;
+ } else if (is_server && val < ONE_MEGABYTE * MIN_SERVER_MB) {
+ /* We can't configure less than this much on a server. */
+ log_warn(LD_CONFIG, "MaxMemInQueues must be at least %d MB on servers "
+ "for now. Ideally, have it as large as you can afford.",
+ MIN_SERVER_MB);
+ return MIN_SERVER_MB * ONE_MEGABYTE;
+ } else if (is_server && val < ONE_MEGABYTE * MIN_UNWARNED_SERVER_MB) {
+ /* On a server, if it's less than this much, we warn that things
+ * may go badly. */
+ log_warn(LD_CONFIG, "MaxMemInQueues is set to a low value; if your "
+ "relay doesn't work, this may be the reason why.");
+ return val;
+ } else if (! is_server && val < ONE_MEGABYTE * MIN_UNWARNED_CLIENT_MB) {
+ /* On a client, if it's less than this much, we warn that things
+ * may go badly. */
+ log_warn(LD_CONFIG, "MaxMemInQueues is set to a low value; if your "
+ "client doesn't work, this may be the reason why.");
+ return val;
} else {
/* The value was fine all along */
return val;
diff --git a/src/app/config/config.h b/src/app/config/config.h
index ee39490072..e95ef4a728 100644
--- a/src/app/config/config.h
+++ b/src/app/config/config.h
@@ -291,7 +291,7 @@ STATIC int parse_dir_authority_line(const char *line,
STATIC int parse_dir_fallback_line(const char *line, int validate_only);
STATIC uint64_t compute_real_max_mem_in_queues(const uint64_t val,
- int log_guess);
+ bool is_server);
STATIC int open_and_add_file_log(const log_severity_list_t *severity,
const char *fname,
int truncate_log);
diff --git a/src/app/config/resolve_addr.c b/src/app/config/resolve_addr.c
index a153d63974..b37707d2da 100644
--- a/src/app/config/resolve_addr.c
+++ b/src/app/config/resolve_addr.c
@@ -787,7 +787,8 @@ MOCK_IMPL(bool,
is_local_to_resolve_addr, (const tor_addr_t *addr))
{
const int family = tor_addr_family(addr);
- const tor_addr_t *last_resolved_addr = &last_resolved_addrs[family];
+ const tor_addr_t *last_resolved_addr =
+ &last_resolved_addrs[af_to_idx(family)];
/* Internal address is always local. */
if (tor_addr_is_internal(addr, 0)) {
diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c
index 7f4c6a5f48..82886ccd0b 100644
--- a/src/core/mainloop/connection.c
+++ b/src/core/mainloop/connection.c
@@ -82,6 +82,7 @@
#include "core/or/policies.h"
#include "core/or/reasons.h"
#include "core/or/relay.h"
+#include "core/or/status.h"
#include "core/or/crypt_path.h"
#include "core/proto/proto_haproxy.h"
#include "core/proto/proto_http.h"
@@ -2042,6 +2043,9 @@ connection_handle_listener_read(connection_t *conn, int new_type)
connection_mark_for_close(newconn);
return 0;
}
+
+ note_connection(true /* inbound */, conn->socket_family);
+
return 0;
}
@@ -2213,6 +2217,8 @@ connection_connect_sockaddr,(connection_t *conn,
}
}
+ note_connection(false /* outbound */, conn->socket_family);
+
/* it succeeded. we're connected. */
log_fn(inprogress ? LOG_DEBUG : LOG_INFO, LD_NET,
"Connection to socket %s (sock "TOR_SOCKET_T_FORMAT").",
diff --git a/src/core/or/channel.c b/src/core/or/channel.c
index 91f083ec00..d082174dc8 100644
--- a/src/core/or/channel.c
+++ b/src/core/or/channel.c
@@ -871,6 +871,8 @@ channel_init(channel_t *chan)
/* Channel is not in the scheduler heap. */
chan->sched_heap_idx = -1;
+
+ tor_addr_make_unspec(&chan->addr_according_to_peer);
}
/**
diff --git a/src/core/or/channel.h b/src/core/or/channel.h
index 10b80aa7d5..606b0730b8 100644
--- a/src/core/or/channel.h
+++ b/src/core/or/channel.h
@@ -236,6 +236,9 @@ struct channel_t {
/** The handle to this channel (to free on canceled timers) */
struct channel_handle_t *timer_handle;
+ /** If not UNSPEC, the address that the peer says we have. */
+ tor_addr_t addr_according_to_peer;
+
/**
* These two fields specify the minimum and maximum negotiated timeout
* values for inactivity (send or receive) before we decide to pad a
diff --git a/src/core/or/channeltls.c b/src/core/or/channeltls.c
index 51b772728e..a0debf8d22 100644
--- a/src/core/or/channeltls.c
+++ b/src/core/or/channeltls.c
@@ -1856,6 +1856,13 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
}
}
+ if (me) {
+ /* We have a descriptor, so we are a relay: record the address that the
+ * other side said we had. */
+ tor_addr_copy(&TLS_CHAN_TO_BASE(chan)->addr_according_to_peer,
+ &my_apparent_addr);
+ }
+
n_other_addrs = netinfo_cell_get_n_my_addrs(netinfo_cell);
for (uint8_t i = 0; i < n_other_addrs; i++) {
/* Consider all the other addresses; if any matches, this connection is
diff --git a/src/core/or/circuitbuild.c b/src/core/or/circuitbuild.c
index 76e9ccf0a5..ab4ce9f784 100644
--- a/src/core/or/circuitbuild.c
+++ b/src/core/or/circuitbuild.c
@@ -2148,7 +2148,7 @@ count_acceptable_nodes, (const smartlist_t *nodes, int direct))
* The alternative is building the circuit in reverse. Reverse calls to
* onion_extend_cpath() (ie: select outer hops first) would then have the
* property that you don't gain information about inner hops by observing
- * outer ones. See https://trac.torproject.org/projects/tor/ticket/24487
+ * outer ones. See https://bugs.torproject.org/tpo/core/tor/24487
* for this.
*
* (Note further that we still exclude the exit to prevent A - B - A
diff --git a/src/core/or/circuitlist.c b/src/core/or/circuitlist.c
index c0c28c9e2d..c31fe00dea 100644
--- a/src/core/or/circuitlist.c
+++ b/src/core/or/circuitlist.c
@@ -2144,7 +2144,7 @@ circuit_mark_all_dirty_circs_as_unusable(void)
* This function is in the critical path of circuit_mark_for_close().
* It must be (and is) O(1)!
*
- * See https://trac.torproject.org/projects/tor/ticket/23512.
+ * See https://bugs.torproject.org/tpo/core/tor/23512
*/
void
circuit_synchronize_written_or_bandwidth(const circuit_t *c,
diff --git a/src/core/or/or_circuit_st.h b/src/core/or/or_circuit_st.h
index 9bfe999728..4e17b1c143 100644
--- a/src/core/or/or_circuit_st.h
+++ b/src/core/or/or_circuit_st.h
@@ -75,6 +75,10 @@ struct or_circuit_t {
/** If set, the DoS defenses are enabled on this circuit meaning that the
* introduce2_bucket is initialized and used. */
unsigned int introduce2_dos_defense_enabled : 1;
+ /** If set, the DoS defenses were explicitly enabled through the
+ * ESTABLISH_INTRO cell extension. If unset, the consensus is used to learn
+ * if the defenses can be enabled or not. */
+ unsigned int introduce2_dos_defense_explicit : 1;
/** INTRODUCE2 cell bucket controlling how much can go on this circuit. Only
* used if this is a service introduction circuit at the intro point
diff --git a/src/core/or/status.c b/src/core/or/status.c
index ed8448883c..00a88a3178 100644
--- a/src/core/or/status.c
+++ b/src/core/or/status.c
@@ -113,6 +113,41 @@ log_onion_service_stats(void)
hs_stats_get_n_rendezvous_launches());
}
+/**
+ * @name connection counts for heartbeat
+ *
+ * Tracks incoming and outgoing connections on IPv4/IPv6, for heartbeat
+ * logs.
+ **/
+/**@{*/
+static unsigned n_incoming_ipv4;
+static unsigned n_incoming_ipv6;
+static unsigned n_outgoing_ipv4;
+static unsigned n_outgoing_ipv6;
+/**@}*/
+
+/**
+ * Note that a connection has arrived or has been made, for use in the
+ * heartbeat message.
+ **/
+void
+note_connection(bool inbound, int family)
+{
+ if (family == AF_INET) {
+ if (inbound) {
+ ++n_incoming_ipv4;
+ } else {
+ ++n_outgoing_ipv4;
+ }
+ } else if (family == AF_INET6) {
+ if (inbound) {
+ ++n_incoming_ipv6;
+ } else {
+ ++n_outgoing_ipv6;
+ }
+ }
+}
+
/** Log a "heartbeat" message describing Tor's status and history so that the
* user can know that there is indeed a running Tor. Return 0 on success and
* -1 on failure. */
@@ -143,8 +178,12 @@ log_heartbeat(time_t now)
bw_sent = bytes_to_usage(get_bytes_written());
log_fn(LOG_NOTICE, LD_HEARTBEAT, "Heartbeat: Tor's uptime is %s, with %d "
- "circuits open. I've sent %s and received %s.%s",
+ "circuits open. I've sent %s and received %s. I've received %u "
+ "connections on IPv4 and %u on IPv6. I've made %u connections "
+ "with IPv4 and %u with IPv6.%s",
uptime, count_circuits(), bw_sent, bw_rcvd,
+ n_incoming_ipv4, n_incoming_ipv6,
+ n_outgoing_ipv4, n_outgoing_ipv6,
hibernating?" We are currently hibernating.":"");
dirclient_dump_total_dls();
diff --git a/src/core/or/status.h b/src/core/or/status.h
index 639f8cdf51..271e0dbc9a 100644
--- a/src/core/or/status.h
+++ b/src/core/or/status.h
@@ -11,6 +11,7 @@
#include "lib/testsupport/testsupport.h"
+void note_connection(bool inbound, int family);
int log_heartbeat(time_t now);
#ifdef STATUS_PRIVATE
diff --git a/src/core/proto/proto_socks.c b/src/core/proto/proto_socks.c
index 198195c0ae..08febee5bb 100644
--- a/src/core/proto/proto_socks.c
+++ b/src/core/proto/proto_socks.c
@@ -67,8 +67,8 @@ log_unsafe_socks_warning(int socks_protocol, const char *address,
"Tor only an IP address. Applications that do DNS resolves "
"themselves may leak information. Consider using Socks4A "
"(e.g. via privoxy or socat) instead. For more information, "
- "please see https://wiki.torproject.org/TheOnionRouter/"
- "TorFAQ#SOCKSAndDNS.%s",
+ "please see https://2019.www.torproject.org/docs/faq.html.en"
+ "#WarningsAboutSOCKSandDNSInformationLeaks.%s",
socks_protocol,
(int)port,
safe_socks ? " Rejecting." : "");
diff --git a/src/ext/ed25519/donna/ed25519_tor.c b/src/ext/ed25519/donna/ed25519_tor.c
index a5bb6f4e21..4b35c9f634 100644
--- a/src/ext/ed25519/donna/ed25519_tor.c
+++ b/src/ext/ed25519/donna/ed25519_tor.c
@@ -43,6 +43,7 @@
#include "ed25519-randombytes.h"
#include "ed25519-hash.h"
+#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_util.h"
typedef unsigned char ed25519_signature[64];
diff --git a/src/feature/client/bridges.c b/src/feature/client/bridges.c
index cc17e8fa67..59f79219cf 100644
--- a/src/feature/client/bridges.c
+++ b/src/feature/client/bridges.c
@@ -249,8 +249,8 @@ get_configured_bridge_by_exact_addr_port_digest(const tor_addr_t *addr,
* address/port matches only. */
int
addr_is_a_configured_bridge(const tor_addr_t *addr,
- uint16_t port,
- const char *digest)
+ uint16_t port,
+ const char *digest)
{
tor_assert(addr);
return get_configured_bridge_by_addr_port_digest(addr, port, digest) ? 1 : 0;
@@ -596,7 +596,7 @@ find_transport_name_by_bridge_addrport(const tor_addr_t *addr, uint16_t port)
*/
int
get_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port,
- const transport_t **transport)
+ const transport_t **transport)
{
*transport = NULL;
if (!bridge_list)
diff --git a/src/feature/client/entrynodes.c b/src/feature/client/entrynodes.c
index c51958acec..2104a622da 100644
--- a/src/feature/client/entrynodes.c
+++ b/src/feature/client/entrynodes.c
@@ -3139,9 +3139,9 @@ entry_guard_parse_from_state(const char *s)
guard->sampled_idx = guard->confirmed_idx;
} else {
- log_warn(LD_GUARD, "The state file seems to be into a status that could"
- " yield to weird entry node selection: we're missing both a"
- " sampled_idx and a confirmed_idx.");
+ log_info(LD_GUARD, "The state file seems to be into a status that could"
+ " yield to weird entry node selection: we're missing both a"
+ " sampled_idx and a confirmed_idx.");
guard->sampled_idx = invalid_sampled_idx++;
}
diff --git a/src/feature/control/control_hs.c b/src/feature/control/control_hs.c
index c8de03b318..54b767cd0d 100644
--- a/src/feature/control/control_hs.c
+++ b/src/feature/control/control_hs.c
@@ -134,6 +134,13 @@ handle_control_onion_client_auth_add(control_connection_t *conn,
}
} SMARTLIST_FOREACH_END(flag);
}
+ if (!strcasecmp(line->key, "ClientName")) {
+ if (strlen(line->value) > REND_CLIENTNAME_MAX_LEN) {
+ control_printf_endreply(conn, 512, "ClientName longer than %d chars",
+ REND_CLIENTNAME_MAX_LEN);
+ }
+ creds->client_name = tor_strdup(line->value);
+ }
}
hs_client_register_auth_status_t register_status;
@@ -255,6 +262,10 @@ encode_client_auth_cred_for_control_port(
}
}
+ if (cred->client_name) {
+ smartlist_add_asprintf(control_line, " ClientName=%s", cred->client_name);
+ }
+
/* Join all the components into a single string */
msg_str = smartlist_join_strings(control_line, "", 0, NULL);
diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c
index cde3560ebf..b67ff237b5 100644
--- a/src/feature/hs/hs_client.c
+++ b/src/feature/hs/hs_client.c
@@ -2169,6 +2169,8 @@ client_service_authorization_free_(hs_client_service_authorization_t *auth)
return;
}
+ tor_free(auth->client_name);
+
memwipe(auth, 0, sizeof(*auth));
tor_free(auth);
}
diff --git a/src/feature/hs/hs_client.h b/src/feature/hs/hs_client.h
index a11caa309f..88dede8126 100644
--- a/src/feature/hs/hs_client.h
+++ b/src/feature/hs/hs_client.h
@@ -71,6 +71,9 @@ typedef struct hs_client_service_authorization_t {
/** An onion address that is used to connect to the onion service. */
char onion_address[HS_SERVICE_ADDR_LEN_BASE32+1];
+ /** An client name used to connect to the onion service. */
+ char *client_name;
+
/* Optional flags for this client. */
int flags;
} hs_client_service_authorization_t;
diff --git a/src/feature/hs/hs_dos.c b/src/feature/hs/hs_dos.c
index 1f7415a280..04c2bfbb89 100644
--- a/src/feature/hs/hs_dos.c
+++ b/src/feature/hs/hs_dos.c
@@ -93,6 +93,11 @@ update_intro_circuits(void)
smartlist_t *intro_circs = hs_circuitmap_get_all_intro_circ_relay_side();
SMARTLIST_FOREACH_BEGIN(intro_circs, circuit_t *, circ) {
+ /* Ignore circuit if the defenses were set explicitly through the
+ * ESTABLISH_INTRO cell DoS extension. */
+ if (TO_OR_CIRCUIT(circ)->introduce2_dos_defense_explicit) {
+ continue;
+ }
/* Defenses might have been enabled or disabled. */
TO_OR_CIRCUIT(circ)->introduce2_dos_defense_enabled =
consensus_param_introduce_defense_enabled;
diff --git a/src/feature/hs/hs_intropoint.c b/src/feature/hs/hs_intropoint.c
index e282d1f1bd..69d60f21c3 100644
--- a/src/feature/hs/hs_intropoint.c
+++ b/src/feature/hs/hs_intropoint.c
@@ -285,6 +285,11 @@ handle_establish_intro_cell_dos_extension(
}
}
+ /* At this point, the extension is valid so any values out of it implies
+ * that it was set explicitly and thus flag the circuit that it should not
+ * look at the consensus for that reason for the defenses' values. */
+ circ->introduce2_dos_defense_explicit = 1;
+
/* A value of 0 is valid in the sense that we accept it but we still disable
* the defenses so return false. */
if (intro2_rate_per_sec == 0 || intro2_burst_per_sec == 0) {
diff --git a/src/feature/nodelist/routerlist.c b/src/feature/nodelist/routerlist.c
index 9def90d8d5..fcaf507efc 100644
--- a/src/feature/nodelist/routerlist.c
+++ b/src/feature/nodelist/routerlist.c
@@ -89,6 +89,7 @@
#include "feature/nodelist/routerset.h"
#include "feature/nodelist/torcert.h"
#include "feature/relay/routermode.h"
+#include "feature/relay/relay_find_addr.h"
#include "feature/stats/rephist.h"
#include "lib/crypt_ops/crypto_format.h"
#include "lib/crypt_ops/crypto_rand.h"
@@ -2772,12 +2773,27 @@ 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 a configured 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 (!options->Address &&
- server_mode(options) &&
+ /* 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;
diff --git a/src/feature/relay/circuitbuild_relay.c b/src/feature/relay/circuitbuild_relay.c
index ad20e143be..64f3c341ae 100644
--- a/src/feature/relay/circuitbuild_relay.c
+++ b/src/feature/relay/circuitbuild_relay.c
@@ -588,13 +588,23 @@ onionskin_answer(struct or_circuit_t *circ,
if ((!channel_is_local(circ->p_chan)
|| get_options()->ExtendAllowPrivateAddresses)
&& !channel_is_outgoing(circ->p_chan)) {
- /* record that we could process create cells from a non-local conn
- * that we didn't initiate; presumably this means that create cells
- * can reach us too. */
- tor_addr_t remote_addr;
- if (channel_get_addr_if_possible(circ->p_chan, &remote_addr)) {
- int family = tor_addr_family(&remote_addr);
- router_orport_found_reachable(family);
+ /* Okay, it's a create cell from a non-local connection
+ * that we didn't initiate. Presumably this means that create cells
+ * can reach us too. But what address can they reach us on? */
+ const tor_addr_t *my_supposed_addr = &circ->p_chan->addr_according_to_peer;
+ if (router_addr_is_my_published_addr(my_supposed_addr)) {
+ /* Great, this create cell came on connection where the peer says
+ * that the our address is an address we're actually advertising!
+ * That should mean that we're reachable. But before we finally
+ * declare ourselves reachable, make sure that the address listed
+ * by the peer is the same family as the peer is actually using.
+ */
+ tor_addr_t remote_addr;
+ int family = tor_addr_family(my_supposed_addr);
+ if (channel_get_addr_if_possible(circ->p_chan, &remote_addr) &&
+ tor_addr_family(&remote_addr) == family) {
+ router_orport_found_reachable(family);
+ }
}
}
diff --git a/src/feature/relay/relay_config.c b/src/feature/relay/relay_config.c
index d3f904d286..6504c680eb 100644
--- a/src/feature/relay/relay_config.c
+++ b/src/feature/relay/relay_config.c
@@ -1037,7 +1037,7 @@ options_validate_relay_mode(const or_options_t *old_options,
"Tor is currently configured as a relay and a hidden service. "
"That's not very secure: you should probably run your hidden service "
"in a separate Tor process, at least -- see "
- "https://trac.torproject.org/8742");
+ "https://bugs.torproject.org/tpo/core/tor/8742.");
if (options->BridgeRelay && options->DirPort_set) {
log_warn(LD_CONFIG, "Can't set a DirPort on a bridge relay; disabling "
diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c
index 675b977ade..11847a2616 100644
--- a/src/feature/relay/router.c
+++ b/src/feature/relay/router.c
@@ -176,7 +176,7 @@ routerinfo_err_is_transient(int err)
/**
* For simplicity, we consider all errors other than
* "not a server" transient - see discussion on
- * https://trac.torproject.org/projects/tor/ticket/27034
+ * https://bugs.torproject.org/tpo/core/tor/27034.
*/
return err != TOR_ROUTERINFO_ERROR_NOT_A_SERVER;
}
@@ -1729,6 +1729,31 @@ router_is_me(const routerinfo_t *router)
return router_digest_is_me(router->cache_info.identity_digest);
}
+/**
+ * Return true if we are a server, and if @a addr is an address we are
+ * currently publishing (or trying to publish) in our descriptor.
+ * Return false otherwise.
+ **/
+bool
+router_addr_is_my_published_addr(const tor_addr_t *addr)
+{
+ IF_BUG_ONCE(!addr)
+ return false;
+
+ const routerinfo_t *me = router_get_my_routerinfo();
+ if (!me)
+ return false;
+
+ switch (tor_addr_family(addr)) {
+ case AF_INET:
+ return tor_addr_eq(addr, &me->ipv4_addr);
+ case AF_INET6:
+ return tor_addr_eq(addr, &me->ipv6_addr);
+ default:
+ return false;
+ }
+}
+
/** Return a routerinfo for this OR, rebuilding a fresh one if
* necessary. Return NULL on error, or if called on an OP. */
MOCK_IMPL(const routerinfo_t *,
diff --git a/src/feature/relay/router.h b/src/feature/relay/router.h
index 89b4a479a4..f71ada8eb7 100644
--- a/src/feature/relay/router.h
+++ b/src/feature/relay/router.h
@@ -100,6 +100,7 @@ int router_digest_is_me(const char *digest);
const uint8_t *router_get_my_id_digest(void);
int router_extrainfo_digest_is_me(const char *digest);
int router_is_me(const routerinfo_t *router);
+bool router_addr_is_my_published_addr(const tor_addr_t *addr);
int router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e);
int router_rebuild_descriptor(int force);
char *router_dump_router_to_string(routerinfo_t *router,
diff --git a/src/feature/rend/rendmid.c b/src/feature/rend/rendmid.c
index a473f0c7e1..b497362857 100644
--- a/src/feature/rend/rendmid.c
+++ b/src/feature/rend/rendmid.c
@@ -343,10 +343,12 @@ rend_mid_rendezvous(or_circuit_t *circ, const uint8_t *request,
goto err;
}
- /* Statistics: Mark this circuit as an RP circuit so that we collect
- stats from it. */
+ /* Statistics: Mark circuits as RP circuits */
if (options->HiddenServiceStatistics) {
+ /* `circ` is the RP <-> service circuit */
circ->circuit_carries_hs_traffic_stats = 1;
+ /* `rend_circ` is the client <-> RP circuit */
+ rend_circ->circuit_carries_hs_traffic_stats = 1;
}
/* Send the RENDEZVOUS2 cell to the client. */
diff --git a/src/lib/fs/path.c b/src/lib/fs/path.c
index f03cecf52d..1a15969419 100644
--- a/src/lib/fs/path.c
+++ b/src/lib/fs/path.c
@@ -532,7 +532,7 @@ unglob_win32(const char *pattern, int prev_sep, int next_sep)
tor_free(path_until_glob);
return result;
}
-#else /* !defined(_WIN32) */
+#elif HAVE_GLOB
/** Same as opendir but calls sandbox_intern_string before */
static DIR *
prot_opendir(const char *name)
@@ -559,7 +559,7 @@ wrap_closedir(void *arg)
{
closedir(arg);
}
-#endif /* defined(_WIN32) */
+#endif /* defined(HAVE_GLOB) */
/** 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
@@ -568,14 +568,15 @@ wrap_closedir(void *arg)
struct smartlist_t *
tor_glob(const char *pattern)
{
- smartlist_t *result;
+ smartlist_t *result = NULL;
+
#ifdef _WIN32
// PathMatchSpec does not support forward slashes, change them to backslashes
char *pattern_normalized = tor_strdup(pattern);
tor_strreplacechar(pattern_normalized, '/', *PATH_SEPARATOR);
result = get_glob_paths(pattern_normalized, unglob_win32, true);
tor_free(pattern_normalized);
-#else /* !(defined(_WIN32)) */
+#elif HAVE_GLOB /* !(defined(_WIN32)) */
glob_t matches;
int flags = GLOB_ERR | GLOB_NOSORT;
#ifdef GLOB_ALTDIRFUNC
@@ -608,7 +609,11 @@ tor_glob(const char *pattern)
smartlist_add(result, match);
}
globfree(&matches);
-#endif /* defined(_WIN32) */
+#else
+ (void)pattern;
+ return result;
+#endif /* !defined(HAVE_GLOB) */
+
return result;
}
diff --git a/src/lib/log/ratelim.c b/src/lib/log/ratelim.c
index ac401fb398..8dfaee3384 100644
--- a/src/lib/log/ratelim.c
+++ b/src/lib/log/ratelim.c
@@ -11,6 +11,7 @@
#include "lib/log/ratelim.h"
#include "lib/malloc/malloc.h"
#include "lib/string/printf.h"
+#include "lib/intmath/muldiv.h"
/** If the rate-limiter <b>lim</b> is ready at <b>now</b>, return the number
* of calls to rate_limit_is_ready (including this one!) since the last time
@@ -42,19 +43,24 @@ rate_limit_log(ratelim_t *lim, time_t now)
{
int n;
if ((n = rate_limit_is_ready(lim, now))) {
+ time_t started_limiting = lim->started_limiting;
+ lim->started_limiting = 0;
if (n == 1) {
return tor_strdup("");
} else {
char *cp=NULL;
const char *opt_over = (n >= RATELIM_TOOMANY) ? "over " : "";
- /* XXXX this is not exactly correct: the messages could have occurred
- * any time between the old value of lim->allowed and now. */
+ unsigned difference = (unsigned)(now - started_limiting);
+ difference = round_to_next_multiple_of(difference, 60);
tor_asprintf(&cp,
" [%s%d similar message(s) suppressed in last %d seconds]",
- opt_over, n-1, lim->rate);
+ opt_over, n-1, (int)difference);
return cp;
}
} else {
+ if (lim->started_limiting == 0) {
+ lim->started_limiting = now;
+ }
return NULL;
}
}
diff --git a/src/lib/log/ratelim.h b/src/lib/log/ratelim.h
index e9b55d40dc..9e202028cf 100644
--- a/src/lib/log/ratelim.h
+++ b/src/lib/log/ratelim.h
@@ -40,13 +40,19 @@
</pre>
*/
typedef struct ratelim_t {
+ /** How many seconds must elapse between log messages? */
int rate;
+ /** When did this limiter last allow a message to appear? */
time_t last_allowed;
+ /** When did this limiter start suppressing messages? */
+ time_t started_limiting;
+ /** How many messages has this limiter suppressed since it last allowed
+ * one to appear? */
int n_calls_since_last_time;
} ratelim_t;
#ifndef COCCI
-#define RATELIM_INIT(r) { (r), 0, 0 }
+#define RATELIM_INIT(r) { (r), 0, 0, 0 }
#endif
#define RATELIM_TOOMANY (16*1000*1000)
diff --git a/src/lib/string/compat_string.h b/src/lib/string/compat_string.h
index f05265bdcc..5c9bf05ebd 100644
--- a/src/lib/string/compat_string.h
+++ b/src/lib/string/compat_string.h
@@ -42,7 +42,7 @@ static inline int strcasecmp(const char *a, const char *b) {
* (If --enable-fragile-hardening is passed to configure, we use the hardened
* variants, which do not suffer from this issue.)
*
- * See https://trac.torproject.org/projects/tor/ticket/15205
+ * See https://bugs.torproject.org/tpo/core/tor/15205.
*/
#undef strlcat
#undef strlcpy
diff --git a/src/test/include.am b/src/test/include.am
index 478547d655..816eba894e 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -25,7 +25,15 @@ TESTSCRIPTS = \
src/test/test_workqueue_socketpair.sh \
src/test/test_switch_id.sh \
src/test/test_cmdline.sh \
- src/test/test_parseconf.sh
+ src/test/test_parseconf.sh \
+ src/test/unittest_part1.sh \
+ src/test/unittest_part2.sh \
+ src/test/unittest_part3.sh \
+ src/test/unittest_part4.sh \
+ src/test/unittest_part5.sh \
+ src/test/unittest_part6.sh \
+ src/test/unittest_part7.sh \
+ src/test/unittest_part8.sh
if USE_RUST
TESTSCRIPTS += \
@@ -55,7 +63,7 @@ TESTSCRIPTS += \
scripts/maint/checkSpaceTest.sh
endif
-TESTS += src/test/test src/test/test-slow src/test/test-memwipe \
+TESTS += src/test/test-slow src/test/test-memwipe \
src/test/test_workqueue \
src/test/test_keygen.sh \
src/test/test_key_expiration.sh \
@@ -451,7 +459,15 @@ EXTRA_DIST += \
src/test/test_workqueue_pipe2.sh \
src/test/test_workqueue_socketpair.sh \
src/test/test_cmdline.sh \
- src/test/test_parseconf.sh
+ src/test/test_parseconf.sh \
+ src/test/unittest_part1.sh \
+ src/test/unittest_part2.sh \
+ src/test/unittest_part3.sh \
+ src/test/unittest_part4.sh \
+ src/test/unittest_part5.sh \
+ src/test/unittest_part6.sh \
+ src/test/unittest_part7.sh \
+ src/test/unittest_part8.sh
test-rust:
$(TESTS_ENVIRONMENT) "$(abs_top_srcdir)/src/test/test_rust.sh"
diff --git a/src/test/test_hs_control.c b/src/test/test_hs_control.c
index 1f574179e9..80bbf547dc 100644
--- a/src/test/test_hs_control.c
+++ b/src/test/test_hs_control.c
@@ -212,7 +212,8 @@ test_hs_control_good_onion_client_auth_add(void *arg)
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
int retval;
- ed25519_public_key_t service_identity_pk_2fv, service_identity_pk_jt4;
+ ed25519_public_key_t service_identity_pk_2fv, service_identity_pk_jt4,
+ service_identity_pk_jam;
control_connection_t conn;
char *args = NULL;
char *cp1 = NULL;
@@ -238,6 +239,12 @@ test_hs_control_good_onion_client_auth_add(void *arg)
&service_identity_pk_jt4,
NULL, NULL);
tt_int_op(retval, OP_EQ, 0);
+
+ retval = hs_parse_address(
+ "jamie3vkiwibfiwucd6vxijskbhpjdyajmzeor4mc4i7yopvpo4p7cyd",
+ &service_identity_pk_jam,
+ NULL, NULL);
+ tt_int_op(retval, OP_EQ, 0);
}
digest256map_t *client_auths = get_hs_client_auths_map();
@@ -268,10 +275,24 @@ test_hs_control_good_onion_client_auth_add(void *arg)
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, "250 OK\r\n");
tor_free(cp1);
+ tor_free(args);
+
+ /* Register second service (even with an unrecognized argument) */
+ args = tor_strdup("jamie3vkiwibfiwucd6vxijskbhpjdyajmzeor4mc4i7yopvpo4p7cyd "
+ "x25519:FCV0c0ELDKKDpSFgVIB8Yow8Evj5iD+GoiTtK878NkQ= "
+ "ClientName=MeganNicole ");
+
+ retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Check contents */
+ cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
+ tt_str_op(cp1, OP_EQ, "250 OK\r\n");
+ tor_free(cp1);
client_auths = get_hs_client_auths_map();
tt_assert(client_auths);
- tt_uint_op(digest256map_size(client_auths), OP_EQ, 2);
+ tt_uint_op(digest256map_size(client_auths), OP_EQ, 3);
hs_client_service_authorization_t *client_2fv =
digest256map_get(client_auths, service_identity_pk_2fv.pubkey);
@@ -283,6 +304,11 @@ test_hs_control_good_onion_client_auth_add(void *arg)
tt_assert(client_jt4);
tt_int_op(client_jt4->flags, OP_EQ, 0);
+ hs_client_service_authorization_t *client_jam =
+ digest256map_get(client_auths, service_identity_pk_jam.pubkey);
+ tt_assert(client_jam);
+ tt_int_op(client_jam->flags, OP_EQ, 0);
+
/* Now let's VIEW the auth credentials */
tor_free(conn.current_cmd);
conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_VIEW");
@@ -294,6 +320,9 @@ test_hs_control_good_onion_client_auth_add(void *arg)
#define VIEW_CORRECT_REPLY_NO_ADDR "250-ONION_CLIENT_AUTH_VIEW\r\n" \
"250-CLIENT 2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd " \
"x25519:iJ1tjKCrMAbiFT2bVrCjhbfMDnE1fpaRbIS5ZHKUvEQ=\r\n" \
+ "250-CLIENT jamie3vkiwibfiwucd6vxijskbhpjdyajmzeor4mc4i7yopvpo4p7cyd " \
+ "x25519:FCV0c0ELDKKDpSFgVIB8Yow8Evj5iD+GoiTtK878NkQ= " \
+ "ClientName=MeganNicole\r\n" \
"250-CLIENT jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd " \
"x25519:eIIdIGoSZwI2Q/lSzpf92akGki5I+PZIDz37MA5BhlA=\r\n" \
"250 OK\r\n"
@@ -364,7 +393,19 @@ test_hs_control_good_onion_client_auth_add(void *arg)
/* Now also remove the other one */
tor_free(args);
- args =tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd");
+ args =
+ tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd");
+
+ retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
+ tt_int_op(retval, OP_EQ, 0);
+ cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
+ tt_str_op(cp1, OP_EQ, "250 OK\r\n");
+ tor_free(cp1);
+
+ /* Now also remove the other one */
+ tor_free(args);
+ args =
+ tor_strdup("jamie3vkiwibfiwucd6vxijskbhpjdyajmzeor4mc4i7yopvpo4p7cyd");
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
diff --git a/src/test/test_logging.c b/src/test/test_logging.c
index e09f7a21cd..58d0f24bd3 100644
--- a/src/test/test_logging.c
+++ b/src/test/test_logging.c
@@ -160,6 +160,7 @@ test_ratelim(void *arg)
tor_free(msg);
int i;
+ time_t first_suppressed_at = now + 60;
for (i = 0; i < 9; ++i) {
now += 60; /* one minute has passed. */
msg = rate_limit_log(&ten_min, now);
@@ -167,12 +168,15 @@ test_ratelim(void *arg)
tt_int_op(ten_min.last_allowed, OP_EQ, start);
tt_int_op(ten_min.n_calls_since_last_time, OP_EQ, i + 1);
}
+ tt_i64_op(ten_min.started_limiting, OP_EQ, first_suppressed_at);
now += 240; /* Okay, we can be done. */
msg = rate_limit_log(&ten_min, now);
tt_ptr_op(msg, OP_NE, NULL);
tt_str_op(msg, OP_EQ,
- " [9 similar message(s) suppressed in last 600 seconds]");
+ " [9 similar message(s) suppressed in last 720 seconds]");
+ tt_i64_op(now, OP_EQ, first_suppressed_at + 720);
+
done:
tor_free(msg);
}
diff --git a/src/test/test_options.c b/src/test/test_options.c
index 8e0d19f126..714ee4767f 100644
--- a/src/test/test_options.c
+++ b/src/test/test_options.c
@@ -1013,7 +1013,7 @@ test_options_validate__relay_with_hidden_services(void *ignored)
"Tor is currently configured as a relay and a hidden service. "
"That's not very secure: you should probably run your hidden servi"
"ce in a separate Tor process, at least -- see "
- "https://trac.torproject.org/8742\n");
+ "https://bugs.torproject.org/tpo/core/tor/8742.\n");
done:
teardown_capture_of_logs();
diff --git a/src/test/test_status.c b/src/test/test_status.c
index 82afe0fd2a..b938b86326 100644
--- a/src/test/test_status.c
+++ b/src/test/test_status.c
@@ -26,6 +26,7 @@
#include "feature/nodelist/nodelist.h"
#include "app/config/statefile.h"
#include "lib/tls/tortls.h"
+#include "test/log_test_helpers.h"
#include "core/or/origin_circuit_st.h"
#include "app/config/or_state_st.h"
@@ -308,10 +309,6 @@ static int status_hb_not_in_consensus_public_server_mode(
static const routerinfo_t *status_hb_not_in_consensus_get_my_routerinfo(void);
static const node_t * status_hb_not_in_consensus_node_get_by_id(
const char *identity_digest);
-static void status_hb_not_in_consensus_logv(
- int severity, log_domain_mask_t domain, const char *funcname,
- const char *suffix, const char *format, va_list ap);
-static int status_hb_not_in_consensus_logv_called = 0;
static int status_hb_not_in_consensus_server_mode(const or_options_t *options);
static routerinfo_t *mock_routerinfo;
@@ -332,8 +329,6 @@ test_status_hb_not_in_consensus(void *arg)
status_hb_not_in_consensus_get_my_routerinfo);
MOCK(node_get_by_id,
status_hb_not_in_consensus_node_get_by_id);
- MOCK(logv,
- status_hb_not_in_consensus_logv);
MOCK(server_mode,
status_hb_not_in_consensus_server_mode);
@@ -344,18 +339,38 @@ test_status_hb_not_in_consensus(void *arg)
onion_handshakes_assigned[ONION_HANDSHAKE_TYPE_NTOR] = 1;
expected = 0;
+ setup_capture_of_logs(LOG_INFO);
actual = log_heartbeat(0);
-
tt_int_op(actual, OP_EQ, expected);
- tt_int_op(status_hb_not_in_consensus_logv_called, OP_EQ, 6);
- done:
+ expect_log_msg("Heartbeat: It seems like we are "
+ "not in the cached consensus.\n");
+ expect_log_msg("Heartbeat: Tor's uptime is 0:00 hours, "
+ "with 0 circuits open. "
+ "I've sent 0 kB and received 0 kB. "
+ "I've received 0 connections on IPv4 and 0 on IPv6. "
+ "I've made 0 connections with IPv4 and 0 with IPv6.\n");
+ expect_log_msg("Average packaged cell fullness: 100.000%. "
+ "TLS write overhead: 0%\n");
+ expect_log_msg("Circuit handshake stats since last time: 1/1 TAP, "
+ "1/1 NTor.\n");
+ expect_log_msg("Since startup we initiated 0 and received 0 v1 "
+ "connections; initiated 0 and received 0 v2 connections; "
+ "initiated 0 and received 0 v3 connections; "
+ "initiated 0 and received 0 v4 connections; "
+ "initiated 0 and received 0 v5 connections.\n");
+ expect_log_msg("DoS mitigation since startup: 0 circuits killed with "
+ "too many cells. [cc not enabled] [conn not enabled] "
+ "0 INTRODUCE2 rejected.\n");
+ tt_int_op(mock_saved_log_n_entries(), OP_EQ, 6);
+
+ done:
+ teardown_capture_of_logs();
UNMOCK(tls_get_write_overhead_ratio);
UNMOCK(we_are_hibernating);
UNMOCK(public_server_mode);
UNMOCK(router_get_my_routerinfo);
UNMOCK(node_get_by_id);
- UNMOCK(logv);
UNMOCK(server_mode);
tor_free(mock_routerinfo);
}
@@ -396,76 +411,6 @@ status_hb_not_in_consensus_node_get_by_id(const char *identity_digest)
return NULL;
}
-static void
-status_hb_not_in_consensus_logv(int severity, log_domain_mask_t domain,
- const char *funcname, const char *suffix, const char *format, va_list ap)
-{
- switch (status_hb_not_in_consensus_logv_called)
- {
- case 0:
- tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
- tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
- tt_ptr_op(suffix, OP_EQ, NULL);
- tt_str_op(format, OP_EQ,
- "Heartbeat: It seems like we are not in the cached consensus.");
- break;
- case 1:
- tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
- tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
- tt_ptr_op(suffix, OP_EQ, NULL);
- tt_str_op(format, OP_EQ,
- "Heartbeat: Tor's uptime is %s, with %d circuits open. "
- "I've sent %s and received %s.%s");
- tt_str_op(va_arg(ap, char *), OP_EQ, "0:00 hours"); /* uptime */
- tt_int_op(va_arg(ap, int), OP_EQ, 0); /* count_circuits() */
- tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* bw_sent */
- tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* bw_rcvd */
- tt_str_op(va_arg(ap, char *), OP_EQ, ""); /* hibernating */
- break;
- case 2:
- tt_int_op(severity, OP_EQ, LOG_INFO);
- break;
- case 3:
- tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
- tt_ptr_op(strstr(funcname, "rep_hist_log_circuit_handshake_stats"),
- OP_NE, NULL);
- tt_ptr_op(suffix, OP_EQ, NULL);
- tt_str_op(format, OP_EQ,
- "Circuit handshake stats since last time: %d/%d TAP, %d/%d NTor.");
- tt_int_op(va_arg(ap, int), OP_EQ, 1); /* handshakes assigned (TAP) */
- tt_int_op(va_arg(ap, int), OP_EQ, 1); /* handshakes requested (TAP) */
- tt_int_op(va_arg(ap, int), OP_EQ, 1); /* handshakes assigned (NTOR) */
- tt_int_op(va_arg(ap, int), OP_EQ, 1); /* handshakes requested (NTOR) */
- break;
- case 4:
- tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
- tt_ptr_op(strstr(funcname, "rep_hist_log_link_protocol_counts"),
- OP_NE, NULL);
- break;
- case 5:
- tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
- tt_str_op(format, OP_EQ, "DoS mitigation since startup:%s%s%s%s%s");
- tt_str_op(va_arg(ap, char *), OP_EQ,
- " 0 circuits killed with too many cells.");
- tt_str_op(va_arg(ap, char *), OP_EQ, " [cc not enabled]");
- tt_str_op(va_arg(ap, char *), OP_EQ, " [conn not enabled]");
- tt_str_op(va_arg(ap, char *), OP_EQ, "");
- tt_str_op(va_arg(ap, char *), OP_EQ, " 0 INTRODUCE2 rejected.");
- break;
- default:
- tt_abort_msg("unexpected call to logv()"); // TODO: prettyprint args
- break;
- }
-
- done:
- status_hb_not_in_consensus_logv_called++;
-}
-
static int
status_hb_not_in_consensus_server_mode(const or_options_t *options)
{
@@ -485,14 +430,8 @@ static int status_hb_simple_public_server_mode(const or_options_t *options);
static long status_hb_simple_get_uptime(void);
static uint64_t status_hb_simple_get_bytes_read(void);
static uint64_t status_hb_simple_get_bytes_written(void);
-static void status_hb_simple_logv(int severity, log_domain_mask_t domain,
- const char *funcname, const char *suffix,
- const char *format, va_list ap);
-ATTR_UNUSED static int status_hb_simple_logv_called = 0;
static int status_hb_simple_server_mode(const or_options_t *options);
-static int status_hb_simple_n_msgs = 0;
-
static void
test_status_hb_simple(void *arg)
{
@@ -511,27 +450,32 @@ test_status_hb_simple(void *arg)
status_hb_simple_get_bytes_read);
MOCK(get_bytes_written,
status_hb_simple_get_bytes_written);
- MOCK(logv,
- status_hb_simple_logv);
MOCK(server_mode,
status_hb_simple_server_mode);
log_global_min_severity_ = LOG_DEBUG;
+ setup_capture_of_logs(LOG_INFO);
expected = 0;
actual = log_heartbeat(0);
tt_int_op(actual, OP_EQ, expected);
- tt_int_op(status_hb_simple_n_msgs, OP_EQ, 1);
+
+ expect_log_msg("Heartbeat: Tor's uptime is 0:00 hours, "
+ "with 0 circuits open. "
+ "I've sent 0 kB and received 0 kB. "
+ "I've received 0 connections on IPv4 and 0 on IPv6. "
+ "I've made 0 connections with IPv4 and 0 with IPv6. "
+ "We are currently hibernating.\n");
done:
+ teardown_capture_of_logs();
UNMOCK(tls_get_write_overhead_ratio);
UNMOCK(we_are_hibernating);
UNMOCK(public_server_mode);
UNMOCK(get_uptime);
UNMOCK(get_bytes_read);
UNMOCK(get_bytes_written);
- UNMOCK(logv);
UNMOCK(server_mode);
}
@@ -573,32 +517,6 @@ status_hb_simple_get_bytes_written(void)
return 0;
}
-static void
-status_hb_simple_logv(int severity, log_domain_mask_t domain,
- const char *funcname,
- const char *suffix, const char *format, va_list ap)
-{
- if (severity == LOG_INFO)
- return;
- ++status_hb_simple_n_msgs;
-
- tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
- tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
- tt_ptr_op(suffix, OP_EQ, NULL);
- tt_str_op(format, OP_EQ,
- "Heartbeat: Tor's uptime is %s, with %d circuits open. "
- "I've sent %s and received %s.%s");
- tt_str_op(va_arg(ap, char *), OP_EQ, "0:00 hours"); /* uptime */
- tt_int_op(va_arg(ap, int), OP_EQ, 0); /* count_circuits() */
- tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* bw_sent */
- tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* bw_rcvd */
- tt_str_op(va_arg(ap, char *), OP_EQ, " We are currently hibernating.");
-
- done:
- ;
-}
-
static int
status_hb_simple_server_mode(const or_options_t *options)
{
@@ -620,11 +538,6 @@ static int status_hb_calls_log_accounting_public_server_mode(
static long status_hb_calls_log_accounting_get_uptime(void);
static uint64_t status_hb_calls_log_accounting_get_bytes_read(void);
static uint64_t status_hb_calls_log_accounting_get_bytes_written(void);
-static void status_hb_calls_log_accounting_logv(
- int severity, log_domain_mask_t domain,
- const char *funcname, const char *suffix,
- const char *format, va_list ap);
-static int status_hb_calls_log_accounting_logv_called = 0;
static int status_hb_calls_log_accounting_server_mode(
const or_options_t *options);
static or_state_t * status_hb_calls_log_accounting_get_or_state(void);
@@ -653,8 +566,6 @@ test_status_hb_calls_log_accounting(void *arg)
status_hb_calls_log_accounting_get_bytes_read);
MOCK(get_bytes_written,
status_hb_calls_log_accounting_get_bytes_written);
- MOCK(logv,
- status_hb_calls_log_accounting_logv);
MOCK(server_mode,
status_hb_calls_log_accounting_server_mode);
MOCK(get_or_state,
@@ -666,20 +577,31 @@ test_status_hb_calls_log_accounting(void *arg)
log_global_min_severity_ = LOG_DEBUG;
+ setup_capture_of_logs(LOG_NOTICE);
expected = 0;
actual = log_heartbeat(0);
tt_int_op(actual, OP_EQ, expected);
- tt_int_op(status_hb_calls_log_accounting_logv_called, OP_EQ, 3);
+
+ expect_log_msg("Heartbeat: Tor's uptime is 0:00 hours, "
+ "with 0 circuits open. "
+ "I've sent 0 kB and received 0 kB. "
+ "I've received 0 connections on IPv4 and 0 on IPv6. "
+ "I've made 0 connections with IPv4 and 0 with IPv6.\n");
+
+ expect_log_msg_containing("Heartbeat: Accounting enabled. Sent: 0 kB, "
+ "Received: 0 kB, Used: 0 kB / 0 kB, Rule: max. "
+ "The current accounting interval ends on ");
+ tt_int_op(mock_saved_log_n_entries(), OP_EQ, 2);
done:
+ teardown_capture_of_logs();
UNMOCK(tls_get_write_overhead_ratio);
UNMOCK(we_are_hibernating);
UNMOCK(public_server_mode);
UNMOCK(get_uptime);
UNMOCK(get_bytes_read);
UNMOCK(get_bytes_written);
- UNMOCK(logv);
UNMOCK(server_mode);
UNMOCK(accounting_is_enabled);
UNMOCK(accounting_get_end_time);
@@ -725,58 +647,6 @@ status_hb_calls_log_accounting_get_bytes_written(void)
return 0;
}
-static void
-status_hb_calls_log_accounting_logv(int severity, log_domain_mask_t domain,
- const char *funcname, const char *suffix, const char *format, va_list ap)
-{
- switch (status_hb_calls_log_accounting_logv_called)
- {
- case 0:
- tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
- tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
- tt_ptr_op(suffix, OP_EQ, NULL);
- tt_str_op(format, OP_EQ,
- "Heartbeat: Tor's uptime is %s, with %d circuits open. "
- "I've sent %s and received %s.%s");
- tt_str_op(va_arg(ap, char *), OP_EQ, "0:00 hours"); /* uptime */
- tt_int_op(va_arg(ap, int), OP_EQ, 0); /* count_circuits() */
- tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* bw_sent */
- tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* bw_rcvd */
- tt_str_op(va_arg(ap, char *), OP_EQ, ""); /* hibernating */
- break;
- case 1:
- tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
- tt_ptr_op(strstr(funcname, "log_accounting"), OP_NE, NULL);
- tt_ptr_op(suffix, OP_EQ, NULL);
- tt_str_op(format, OP_EQ,
- "Heartbeat: Accounting enabled. Sent: %s, Received: %s, Used: %s / "
- "%s, Rule: %s. The current accounting interval ends on %s, in %s.");
- tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* acc_sent */
- tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* acc_rcvd */
- tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* acc_used */
- tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* acc_max */
- tt_str_op(va_arg(ap, char *), OP_EQ, "max"); /* acc_rule */
- /* format_local_iso_time uses local tz, so we can't just compare
- * the string against a constant */
- char datetime[ISO_TIME_LEN+1];
- format_local_iso_time(datetime, 60);
- tt_str_op(va_arg(ap, char *), OP_EQ, datetime); /* end_buf */
- tt_str_op(va_arg(ap, char *), OP_EQ, "0:01 hours"); /* remaining */
- break;
- case 2:
- tt_int_op(severity, OP_EQ, LOG_INFO);
- break;
- default:
- tt_abort_msg("unexpected call to logv()"); // TODO: prettyprint args
- break;
- }
-
- done:
- status_hb_calls_log_accounting_logv_called++;
-}
-
static int
status_hb_calls_log_accounting_server_mode(const or_options_t *options)
{
@@ -826,11 +696,6 @@ static int status_hb_packaged_cell_fullness_public_server_mode(
static long status_hb_packaged_cell_fullness_get_uptime(void);
static uint64_t status_hb_packaged_cell_fullness_get_bytes_read(void);
static uint64_t status_hb_packaged_cell_fullness_get_bytes_written(void);
-static void status_hb_packaged_cell_fullness_logv(
- int severity, log_domain_mask_t domain,
- const char *funcname, const char *suffix,
- const char *format, va_list ap);
-static int status_hb_packaged_cell_fullness_logv_called = 0;
static int status_hb_packaged_cell_fullness_server_mode(
const or_options_t *options);
static int status_hb_packaged_cell_fullness_accounting_is_enabled(
@@ -854,8 +719,6 @@ test_status_hb_packaged_cell_fullness(void *arg)
status_hb_packaged_cell_fullness_get_bytes_read);
MOCK(get_bytes_written,
status_hb_packaged_cell_fullness_get_bytes_written);
- MOCK(logv,
- status_hb_packaged_cell_fullness_logv);
MOCK(server_mode,
status_hb_packaged_cell_fullness_server_mode);
MOCK(accounting_is_enabled,
@@ -865,12 +728,20 @@ test_status_hb_packaged_cell_fullness(void *arg)
stats_n_data_bytes_packaged = RELAY_PAYLOAD_SIZE;
stats_n_data_cells_packaged = 2;
expected = 0;
+ setup_capture_of_logs(LOG_INFO);
actual = log_heartbeat(0);
tt_int_op(actual, OP_EQ, expected);
- tt_int_op(status_hb_packaged_cell_fullness_logv_called, OP_EQ, 2);
+ expect_log_msg("Heartbeat: Tor's uptime is 0:00 hours, "
+ "with 0 circuits open. "
+ "I've sent 0 kB and received 0 kB. "
+ "I've received 0 connections on IPv4 and 0 on IPv6. "
+ "I've made 0 connections with IPv4 and 0 with IPv6.\n");
+ expect_log_msg("Average packaged cell fullness: 50.000%. "
+ "TLS write overhead: 0%\n");
done:
+ teardown_capture_of_logs();
stats_n_data_bytes_packaged = 0;
stats_n_data_cells_packaged = 0;
UNMOCK(tls_get_write_overhead_ratio);
@@ -879,7 +750,6 @@ test_status_hb_packaged_cell_fullness(void *arg)
UNMOCK(get_uptime);
UNMOCK(get_bytes_read);
UNMOCK(get_bytes_written);
- UNMOCK(logv);
UNMOCK(server_mode);
UNMOCK(accounting_is_enabled);
}
@@ -923,47 +793,6 @@ status_hb_packaged_cell_fullness_get_bytes_written(void)
return 0;
}
-static void
-status_hb_packaged_cell_fullness_logv(int severity,
- log_domain_mask_t domain, const char *funcname,
- const char *suffix, const char *format, va_list ap)
-{
- switch (status_hb_packaged_cell_fullness_logv_called)
- {
- case 0:
- tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
- tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
- tt_ptr_op(suffix, OP_EQ, NULL);
- tt_str_op(format, OP_EQ,
- "Heartbeat: Tor's uptime is %s, with %d circuits open. "
- "I've sent %s and received %s.%s");
- tt_str_op(va_arg(ap, char *), OP_EQ, "0:00 hours"); /* uptime */
- tt_int_op(va_arg(ap, int), OP_EQ, 0); /* count_circuits() */
- tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* bw_sent */
- tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* bw_rcvd */
- tt_str_op(va_arg(ap, char *), OP_EQ, ""); /* hibernating */
- break;
- case 1:
- tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
- tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
- tt_ptr_op(suffix, OP_EQ, NULL);
- tt_str_op(format, OP_EQ,
- "Average packaged cell fullness: %2.3f%%. "
- "TLS write overhead: %.f%%");
- tt_double_op(fabs(va_arg(ap, double) - 50.0), OP_LE, DBL_EPSILON);
- tt_double_op(fabs(va_arg(ap, double) - 0.0), OP_LE, DBL_EPSILON);
- break;
- default:
- tt_abort_msg("unexpected call to logv()"); // TODO: prettyprint args
- break;
- }
-
- done:
- status_hb_packaged_cell_fullness_logv_called++;
-}
-
static int
status_hb_packaged_cell_fullness_server_mode(const or_options_t *options)
{
@@ -993,11 +822,6 @@ static int status_hb_tls_write_overhead_public_server_mode(
static long status_hb_tls_write_overhead_get_uptime(void);
static uint64_t status_hb_tls_write_overhead_get_bytes_read(void);
static uint64_t status_hb_tls_write_overhead_get_bytes_written(void);
-static void status_hb_tls_write_overhead_logv(
- int severity, log_domain_mask_t domain,
- const char *funcname, const char *suffix,
- const char *format, va_list ap);
-static int status_hb_tls_write_overhead_logv_called = 0;
static int status_hb_tls_write_overhead_server_mode(
const or_options_t *options);
static int status_hb_tls_write_overhead_accounting_is_enabled(
@@ -1021,8 +845,6 @@ test_status_hb_tls_write_overhead(void *arg)
status_hb_tls_write_overhead_get_bytes_read);
MOCK(get_bytes_written,
status_hb_tls_write_overhead_get_bytes_written);
- MOCK(logv,
- status_hb_tls_write_overhead_logv);
MOCK(server_mode,
status_hb_tls_write_overhead_server_mode);
MOCK(accounting_is_enabled,
@@ -1031,19 +853,26 @@ test_status_hb_tls_write_overhead(void *arg)
log_global_min_severity_ = LOG_DEBUG;
expected = 0;
+ setup_capture_of_logs(LOG_NOTICE);
actual = log_heartbeat(0);
tt_int_op(actual, OP_EQ, expected);
- tt_int_op(status_hb_tls_write_overhead_logv_called, OP_EQ, 2);
+ expect_log_msg("Heartbeat: Tor's uptime is 0:00 hours, "
+ "with 0 circuits open. "
+ "I've sent 0 kB and received 0 kB. "
+ "I've received 0 connections on IPv4 and 0 on IPv6. "
+ "I've made 0 connections with IPv4 and 0 with IPv6.\n");
+ expect_log_msg("Average packaged cell fullness: 100.000%. "
+ "TLS write overhead: 100%\n");
done:
+ teardown_capture_of_logs();
UNMOCK(tls_get_write_overhead_ratio);
UNMOCK(we_are_hibernating);
UNMOCK(public_server_mode);
UNMOCK(get_uptime);
UNMOCK(get_bytes_read);
UNMOCK(get_bytes_written);
- UNMOCK(logv);
UNMOCK(server_mode);
UNMOCK(accounting_is_enabled);
}
@@ -1086,46 +915,6 @@ status_hb_tls_write_overhead_get_bytes_written(void)
return 0;
}
-static void
-status_hb_tls_write_overhead_logv(int severity, log_domain_mask_t domain,
- const char *funcname, const char *suffix, const char *format, va_list ap)
-{
- switch (status_hb_tls_write_overhead_logv_called)
- {
- case 0:
- tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
- tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
- tt_ptr_op(suffix, OP_EQ, NULL);
- tt_str_op(format, OP_EQ,
- "Heartbeat: Tor's uptime is %s, with %d circuits open. "
- "I've sent %s and received %s.%s");
- tt_str_op(va_arg(ap, char *), OP_EQ, "0:00 hours"); /* uptime */
- tt_int_op(va_arg(ap, int), OP_EQ, 0); /* count_circuits() */
- tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* bw_sent */
- tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* bw_rcvd */
- tt_str_op(va_arg(ap, char *), OP_EQ, ""); /* hibernating */
- break;
- case 1:
- tt_int_op(severity, OP_EQ, LOG_NOTICE);
- tt_u64_op(domain, OP_EQ, LD_HEARTBEAT);
- tt_ptr_op(strstr(funcname, "log_heartbeat"), OP_NE, NULL);
- tt_ptr_op(suffix, OP_EQ, NULL);
- tt_str_op(format, OP_EQ,
- "Average packaged cell fullness: %2.3f%%. "
- "TLS write overhead: %.f%%");
- tt_int_op(fabs(va_arg(ap, double) - 100.0) <= DBL_EPSILON, OP_EQ, 1);
- tt_double_op(fabs(va_arg(ap, double) - 100.0), OP_LE, DBL_EPSILON);
- break;
- default:
- tt_abort_msg("unexpected call to logv()"); // TODO: prettyprint args
- break;
- }
-
- done:
- status_hb_tls_write_overhead_logv_called++;
-}
-
static int
status_hb_tls_write_overhead_server_mode(const or_options_t *options)
{
diff --git a/src/test/test_util.c b/src/test/test_util.c
index cab48cd514..1c555e4d06 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -4440,6 +4440,7 @@ test_util_glob(void *ptr)
{
(void)ptr;
+#ifdef HAVE_GLOB
smartlist_t *results = NULL;
int r, i;
char *dir1 = NULL, *dir2 = NULL, *forbidden = NULL, *dirname = NULL;
@@ -4656,6 +4657,11 @@ test_util_glob(void *ptr)
SMARTLIST_FOREACH(results, char *, f, tor_free(f));
smartlist_free(results);
}
+#else
+ tt_skip();
+ done:
+ return;
+#endif
}
static void
@@ -4663,6 +4669,7 @@ test_util_get_glob_opened_files(void *ptr)
{
(void)ptr;
+#ifdef HAVE_GLOB
smartlist_t *results = NULL;
int r, i;
char *dir1 = NULL, *dir2 = NULL, *forbidden = NULL, *dirname = NULL;
@@ -4853,6 +4860,11 @@ test_util_get_glob_opened_files(void *ptr)
SMARTLIST_FOREACH(results, char *, f, tor_free(f));
smartlist_free(results);
}
+#else
+ tt_skip();
+ done:
+ return;
+#endif
}
static void
diff --git a/src/test/testing_common.c b/src/test/testing_common.c
index cd93055dfb..9b50de07a8 100644
--- a/src/test/testing_common.c
+++ b/src/test/testing_common.c
@@ -287,6 +287,8 @@ main(int c, const char **v)
/* Don't add default logs; the tests manage their own. */
quiet_level = QUIET_SILENT;
+ unsigned num=1, den=1;
+
for (i_out = i = 1; i < c; ++i) {
if (!strcmp(v[i], "--warn")) {
loglevel = LOG_WARN;
@@ -298,6 +300,19 @@ main(int c, const char **v)
loglevel = LOG_DEBUG;
} else if (!strcmp(v[i], "--accel")) {
accel_crypto = 1;
+ } else if (!strcmp(v[i], "--fraction")) {
+ if (i+1 == c) {
+ printf("--fraction needs an argument.\n");
+ return 1;
+ }
+ const char *fracstr = v[++i];
+ char ch;
+ if (sscanf(fracstr, "%u/%u%c", &num, &den, &ch) != 2) {
+ printf("--fraction expects a fraction as an input.\n");
+ }
+ if (den == 0 || num == 0 || num > den) {
+ printf("--fraction expects a valid fraction as an input.\n");
+ }
} else {
v[i_out++] = v[i];
}
@@ -375,6 +390,33 @@ main(int c, const char **v)
smartlist_free(skip);
}
+ if (den != 1) {
+ // count the tests. Linear but fast.
+ unsigned n_tests = 0;
+ struct testgroup_t *tg;
+ struct testcase_t *tc;
+ for (tg = testgroups; tg->prefix != NULL; ++tg) {
+ for (tc = tg->cases; tc->name != NULL; ++tc) {
+ ++n_tests;
+ }
+ }
+ // Which tests should we run? This can give iffy results if den is huge
+ // but it doesn't actually matter in practice.
+ unsigned tests_per_chunk = CEIL_DIV(n_tests, den);
+ unsigned start_at = (num-1) * tests_per_chunk;
+
+ // Skip the tests that are outside of the range.
+ unsigned idx = 0;
+ for (tg = testgroups; tg->prefix != NULL; ++tg) {
+ for (tc = tg->cases; tc->name != NULL; ++tc) {
+ if (idx < start_at || idx >= start_at + tests_per_chunk) {
+ tc->flags |= TT_SKIP;
+ }
+ ++idx;
+ }
+ }
+ }
+
int have_failed = (tinytest_main(c, v, testgroups) != 0);
free_pregenerated_keys();
diff --git a/src/test/unittest_part1.sh b/src/test/unittest_part1.sh
new file mode 100755
index 0000000000..5be0f499f9
--- /dev/null
+++ b/src/test/unittest_part1.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+"${abs_top_builddir:-.}/src/test/test" --fraction 1/8
diff --git a/src/test/unittest_part2.sh b/src/test/unittest_part2.sh
new file mode 100755
index 0000000000..9a614eb8c1
--- /dev/null
+++ b/src/test/unittest_part2.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+"${abs_top_builddir:-.}/src/test/test" --fraction 2/8
diff --git a/src/test/unittest_part3.sh b/src/test/unittest_part3.sh
new file mode 100755
index 0000000000..5cbc3fe495
--- /dev/null
+++ b/src/test/unittest_part3.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+"${abs_top_builddir:-.}/src/test/test" --fraction 3/8
diff --git a/src/test/unittest_part4.sh b/src/test/unittest_part4.sh
new file mode 100755
index 0000000000..bc6fe01f68
--- /dev/null
+++ b/src/test/unittest_part4.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+"${abs_top_builddir:-.}/src/test/test" --fraction 4/8
diff --git a/src/test/unittest_part5.sh b/src/test/unittest_part5.sh
new file mode 100755
index 0000000000..9bbff34fb8
--- /dev/null
+++ b/src/test/unittest_part5.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+"${abs_top_builddir:-.}/src/test/test" --fraction 5/8
diff --git a/src/test/unittest_part6.sh b/src/test/unittest_part6.sh
new file mode 100755
index 0000000000..2d5eaa8a28
--- /dev/null
+++ b/src/test/unittest_part6.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+"${abs_top_builddir:-.}/src/test/test" --fraction 6/8
diff --git a/src/test/unittest_part7.sh b/src/test/unittest_part7.sh
new file mode 100755
index 0000000000..5e6ce2aea5
--- /dev/null
+++ b/src/test/unittest_part7.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+"${abs_top_builddir:-.}/src/test/test" --fraction 7/8
diff --git a/src/test/unittest_part8.sh b/src/test/unittest_part8.sh
new file mode 100755
index 0000000000..7fea9c9c7f
--- /dev/null
+++ b/src/test/unittest_part8.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+"${abs_top_builddir:-.}/src/test/test" --fraction 8/8