summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/address.c22
-rw-r--r--src/common/address.h13
-rw-r--r--src/common/address_set.c6
-rw-r--r--src/common/address_set.h2
-rw-r--r--src/common/aes.c7
-rw-r--r--src/common/aes.h4
-rw-r--r--src/common/buffers.c77
-rw-r--r--src/common/buffers.h13
-rw-r--r--src/common/compat.c33
-rw-r--r--src/common/compat.h6
-rw-r--r--src/common/compat_libevent.c17
-rw-r--r--src/common/compat_libevent.h10
-rw-r--r--src/common/compat_openssl.h8
-rw-r--r--src/common/compat_rust.c39
-rw-r--r--src/common/compat_rust.h28
-rw-r--r--src/common/compat_threads.c24
-rw-r--r--src/common/compat_threads.h79
-rw-r--r--src/common/compat_time.c187
-rw-r--r--src/common/compat_time.h42
-rw-r--r--src/common/compat_winthreads.c4
-rw-r--r--src/common/compress.c2
-rw-r--r--src/common/compress.h4
-rw-r--r--src/common/compress_lzma.c2
-rw-r--r--src/common/compress_lzma.h5
-rw-r--r--src/common/compress_zlib.c2
-rw-r--r--src/common/compress_zlib.h5
-rw-r--r--src/common/compress_zstd.c2
-rw-r--r--src/common/compress_zstd.h5
-rw-r--r--src/common/confline.c96
-rw-r--r--src/common/confline.h12
-rw-r--r--src/common/container.c19
-rw-r--r--src/common/container.h27
-rw-r--r--src/common/crypto.c1174
-rw-r--r--src/common/crypto.h120
-rw-r--r--src/common/crypto_curve25519.h1
-rw-r--r--src/common/crypto_ed25519.c14
-rw-r--r--src/common/crypto_ed25519.h5
-rw-r--r--src/common/crypto_openssl_mgt.c161
-rw-r--r--src/common/crypto_openssl_mgt.h85
-rw-r--r--src/common/crypto_pwbox.h3
-rw-r--r--src/common/crypto_rsa.c923
-rw-r--r--src/common/crypto_rsa.h104
-rw-r--r--src/common/di_ops.c2
-rw-r--r--src/common/di_ops.h7
-rw-r--r--src/common/handles.h4
-rw-r--r--src/common/include.am10
-rw-r--r--src/common/log.c108
-rw-r--r--src/common/memarea.c4
-rw-r--r--src/common/memarea.h7
-rw-r--r--src/common/procmon.c2
-rw-r--r--src/common/procmon.h4
-rw-r--r--src/common/sandbox.c15
-rw-r--r--src/common/storagedir.c2
-rw-r--r--src/common/storagedir.h5
-rw-r--r--src/common/timers.c4
-rw-r--r--src/common/timers.h3
-rw-r--r--src/common/torint.h10
-rw-r--r--src/common/torlog.h6
-rw-r--r--src/common/tortls.c6
-rw-r--r--src/common/tortls.h7
-rw-r--r--src/common/util.c104
-rw-r--r--src/common/util.h33
-rw-r--r--src/common/workqueue.c5
-rw-r--r--src/ext/csiphash.c6
-rw-r--r--src/ext/ed25519/donna/ed25519_tor.c2
m---------src/ext/rust0
-rw-r--r--src/ext/siphash.h1
-rw-r--r--src/or/addressmap.c38
-rw-r--r--src/or/bridges.c72
-rw-r--r--src/or/channel.c1974
-rw-r--r--src/or/channel.h112
-rw-r--r--src/or/channelpadding.c56
-rw-r--r--src/or/channeltls.c124
-rw-r--r--src/or/circpathbias.c4
-rw-r--r--src/or/circuitbuild.c460
-rw-r--r--src/or/circuitbuild.h8
-rw-r--r--src/or/circuitlist.c263
-rw-r--r--src/or/circuitlist.h8
-rw-r--r--src/or/circuitmux.c2
-rw-r--r--src/or/circuitmux.h4
-rw-r--r--src/or/circuitstats.c238
-rw-r--r--src/or/circuitstats.h12
-rw-r--r--src/or/circuituse.c356
-rw-r--r--src/or/circuituse.h3
-rw-r--r--src/or/command.c2
-rw-r--r--src/or/config.c957
-rw-r--r--src/or/config.h94
-rw-r--r--src/or/confparse.c2
-rw-r--r--src/or/confparse.h21
-rw-r--r--src/or/connection.c207
-rw-r--r--src/or/connection.h11
-rw-r--r--src/or/connection_edge.c28
-rw-r--r--src/or/connection_or.c292
-rw-r--r--src/or/connection_or.h15
-rw-r--r--src/or/conscache.c4
-rw-r--r--src/or/conscache.h7
-rw-r--r--src/or/consdiffmgr.c17
-rw-r--r--src/or/control.c662
-rw-r--r--src/or/control.h69
-rw-r--r--src/or/cpuworker.c21
-rw-r--r--src/or/dircollate.c7
-rw-r--r--src/or/dircollate.h4
-rw-r--r--src/or/directory.c228
-rw-r--r--src/or/directory.h32
-rw-r--r--src/or/dirserv.c55
-rw-r--r--src/or/dirserv.h7
-rw-r--r--src/or/dirvote.c44
-rw-r--r--src/or/dirvote.h18
-rw-r--r--src/or/dns.c35
-rw-r--r--src/or/dnsserv.c5
-rw-r--r--src/or/dos.c15
-rw-r--r--src/or/entrynodes.c43
-rw-r--r--src/or/entrynodes.h34
-rw-r--r--src/or/ext_orport.c2
-rw-r--r--src/or/ext_orport.h6
-rw-r--r--src/or/fp_pair.c2
-rw-r--r--src/or/fp_pair.h7
-rw-r--r--src/or/geoip.c27
-rw-r--r--src/or/git_revision.c17
-rw-r--r--src/or/git_revision.h12
-rw-r--r--src/or/hibernate.c21
-rw-r--r--src/or/hs_cache.c66
-rw-r--r--src/or/hs_cache.h2
-rw-r--r--src/or/hs_circuit.c116
-rw-r--r--src/or/hs_circuit.h2
-rw-r--r--src/or/hs_circuitmap.c41
-rw-r--r--src/or/hs_circuitmap.h2
-rw-r--r--src/or/hs_client.c10
-rw-r--r--src/or/hs_common.c19
-rw-r--r--src/or/hs_common.h15
-rw-r--r--src/or/hs_config.c2
-rw-r--r--src/or/hs_control.c258
-rw-r--r--src/or/hs_control.h52
-rw-r--r--src/or/hs_descriptor.c20
-rw-r--r--src/or/hs_descriptor.h21
-rw-r--r--src/or/hs_ident.c6
-rw-r--r--src/or/hs_ident.h12
-rw-r--r--src/or/hs_intropoint.c2
-rw-r--r--src/or/hs_service.c343
-rw-r--r--src/or/hs_service.h43
-rw-r--r--src/or/hs_stats.c58
-rw-r--r--src/or/hs_stats.h14
-rw-r--r--src/or/include.am18
-rw-r--r--src/or/main.c589
-rw-r--r--src/or/main.h15
-rw-r--r--src/or/microdesc.c7
-rw-r--r--src/or/microdesc.h6
-rw-r--r--src/or/networkstatus.c107
-rw-r--r--src/or/networkstatus.h17
-rw-r--r--src/or/nodelist.c205
-rw-r--r--src/or/nodelist.h13
-rw-r--r--src/or/ntmain.c4
-rw-r--r--src/or/onion.c2
-rw-r--r--src/or/onion.h4
-rw-r--r--src/or/onion_fast.c4
-rw-r--r--src/or/onion_fast.h4
-rw-r--r--src/or/onion_ntor.c2
-rw-r--r--src/or/onion_ntor.h4
-rw-r--r--src/or/or.h225
-rw-r--r--src/or/policies.c82
-rw-r--r--src/or/policies.h15
-rw-r--r--src/or/proto_socks.c6
-rw-r--r--src/or/proto_socks.h4
-rw-r--r--src/or/protover.c161
-rw-r--r--src/or/protover.h18
-rw-r--r--src/or/protover_rust.c34
-rw-r--r--src/or/reasons.c2
-rw-r--r--src/or/relay.c210
-rw-r--r--src/or/relay.h12
-rw-r--r--src/or/rendcache.c36
-rw-r--r--src/or/rendcache.h20
-rw-r--r--src/or/rendclient.c26
-rw-r--r--src/or/rendcommon.c6
-rw-r--r--src/or/rendcommon.h14
-rw-r--r--src/or/rendservice.c43
-rw-r--r--src/or/rendservice.h37
-rw-r--r--src/or/rephist.c303
-rw-r--r--src/or/rephist.h7
-rw-r--r--src/or/replaycache.c13
-rw-r--r--src/or/replaycache.h4
-rw-r--r--src/or/router.c46
-rw-r--r--src/or/router.h4
-rw-r--r--src/or/routerkeys.c31
-rw-r--r--src/or/routerlist.c124
-rw-r--r--src/or/routerlist.h14
-rw-r--r--src/or/routerparse.c113
-rw-r--r--src/or/routerparse.h4
-rw-r--r--src/or/routerset.c2
-rw-r--r--src/or/routerset.h3
-rw-r--r--src/or/scheduler.c74
-rw-r--r--src/or/scheduler.h3
-rw-r--r--src/or/scheduler_kist.c28
-rw-r--r--src/or/scheduler_vanilla.c32
-rw-r--r--src/or/shared_random.c6
-rw-r--r--src/or/shared_random.h3
-rw-r--r--src/or/shared_random_state.c20
-rw-r--r--src/or/shared_random_state.h2
-rw-r--r--src/or/statefile.c2
-rw-r--r--src/or/statefile.h3
-rw-r--r--src/or/status.c41
-rw-r--r--src/or/tor_api.c88
-rw-r--r--src/or/tor_api.h101
-rw-r--r--src/or/tor_api_internal.h20
-rw-r--r--src/or/tor_main.c26
-rw-r--r--src/or/torcert.c4
-rw-r--r--src/or/torcert.h7
-rw-r--r--src/or/transports.c4
-rw-r--r--src/or/transports.h3
-rw-r--r--src/rust/.cargo/config.in2
-rw-r--r--src/rust/Cargo.lock47
-rw-r--r--src/rust/Cargo.toml2
-rw-r--r--src/rust/external/Cargo.toml13
-rw-r--r--src/rust/external/external.rs33
-rw-r--r--src/rust/external/lib.rs14
-rw-r--r--src/rust/include.am27
-rw-r--r--src/rust/protover/Cargo.toml25
-rw-r--r--src/rust/protover/errors.rs43
-rw-r--r--src/rust/protover/ffi.rs296
-rw-r--r--src/rust/protover/lib.rs38
-rw-r--r--src/rust/protover/protoset.rs651
-rw-r--r--src/rust/protover/protover.rs899
-rw-r--r--src/rust/protover/tests/protover.rs394
-rw-r--r--src/rust/smartlist/Cargo.toml13
-rw-r--r--src/rust/smartlist/lib.rs8
-rw-r--r--src/rust/smartlist/smartlist.rs115
-rw-r--r--src/rust/tor_allocate/Cargo.toml13
-rw-r--r--src/rust/tor_allocate/lib.rs15
-rw-r--r--src/rust/tor_allocate/tor_allocate.rs97
-rw-r--r--src/rust/tor_rust/Cargo.toml16
-rw-r--r--src/rust/tor_rust/include.am28
-rw-r--r--src/rust/tor_rust/lib.rs5
-rw-r--r--src/rust/tor_util/Cargo.toml5
-rw-r--r--src/rust/tor_util/ffi.rs62
-rw-r--r--src/rust/tor_util/include.am12
-rw-r--r--src/rust/tor_util/lib.rs16
-rw-r--r--src/rust/tor_util/rust_string.rs101
-rw-r--r--src/rust/tor_util/strings.rs82
-rw-r--r--src/rust/tor_util/tests/rust_string.rs37
-rw-r--r--src/test/Makefile.nmake1
-rw-r--r--src/test/bench.c9
-rw-r--r--src/test/fakechans.h1
-rw-r--r--src/test/fuzz/fuzzing_common.c6
-rw-r--r--src/test/include.am8
-rw-r--r--src/test/test-memwipe.c3
-rw-r--r--src/test/test-timers.c2
-rw-r--r--src/test/test.c14
-rw-r--r--src/test/test.h5
-rw-r--r--src/test/test_accounting.c3
-rw-r--r--src/test/test_address.c16
-rw-r--r--src/test/test_buffers.c24
-rw-r--r--src/test/test_cell_formats.c2
-rw-r--r--src/test/test_cell_queue.c4
-rw-r--r--src/test/test_channel.c1765
-rw-r--r--src/test/test_channelpadding.c123
-rw-r--r--src/test/test_circuitlist.c28
-rw-r--r--src/test/test_circuitstats.c201
-rw-r--r--src/test/test_config.c211
-rw-r--r--src/test/test_connection.c99
-rw-r--r--src/test/test_conscache.c12
-rw-r--r--src/test/test_consdiffmgr.c8
-rw-r--r--src/test/test_controller.c194
-rw-r--r--src/test/test_dir.c582
-rw-r--r--src/test/test_dir_handle_get.c97
-rw-r--r--src/test/test_dns.c9
-rw-r--r--src/test/test_entryconn.c34
-rw-r--r--src/test/test_entrynodes.c6
-rw-r--r--src/test/test_extorport.c10
-rw-r--r--src/test/test_handles.c2
-rw-r--r--src/test/test_hs.c18
-rw-r--r--src/test/test_hs_cache.c2
-rw-r--r--src/test/test_hs_cell.c4
-rw-r--r--src/test/test_hs_client.c12
-rw-r--r--src/test/test_hs_common.c5
-rw-r--r--src/test/test_hs_control.c199
-rw-r--r--src/test/test_hs_descriptor.c2
-rw-r--r--src/test/test_hs_intropoint.c47
-rw-r--r--src/test/test_hs_service.c39
-rwxr-xr-xsrc/test/test_keygen.sh109
-rw-r--r--src/test/test_link_handshake.c14
-rw-r--r--src/test/test_microdesc.c36
-rw-r--r--src/test/test_oom.c38
-rw-r--r--src/test/test_options.c29
-rw-r--r--src/test/test_policy.c8
-rw-r--r--src/test/test_protover.c292
-rw-r--r--src/test/test_relay.c4
-rw-r--r--src/test/test_rendcache.c8
-rw-r--r--src/test/test_replay.c2
-rw-r--r--src/test/test_routerkeys.c9
-rw-r--r--src/test/test_routerlist.c116
-rw-r--r--src/test/test_routerset.c59
-rw-r--r--src/test/test_rust.c31
-rwxr-xr-xsrc/test/test_rust.sh4
-rw-r--r--src/test/test_scheduler.c279
-rw-r--r--src/test/test_shared_random.c9
-rw-r--r--src/test/test_socks.c24
-rw-r--r--src/test/test_status.c15
-rw-r--r--src/test/test_tortls.c3
-rw-r--r--src/test/test_util.c154
-rw-r--r--src/test/testing_common.c10
-rw-r--r--src/tools/include.am5
-rw-r--r--src/tools/tor-resolve.c10
-rw-r--r--src/tools/tor_runner.c101
-rw-r--r--src/trunnel/hs/cell_introduce1.trunnel2
-rw-r--r--src/win32/orconfig.h2
304 files changed, 14692 insertions, 8301 deletions
diff --git a/src/common/address.c b/src/common/address.c
index ef3117aefd..5074c1ccf0 100644
--- a/src/common/address.c
+++ b/src/common/address.c
@@ -1125,7 +1125,7 @@ tor_addr_compare_masked(const tor_addr_t *addr1, const tor_addr_t *addr2,
case AF_UNIX:
/* HACKHACKHACKHACKHACK:
* tor_addr_t doesn't contain a copy of sun_path, so it's not
- * possible to comapre this at all.
+ * possible to compare this at all.
*
* Since the only time we currently actually should be comparing
* 2 AF_UNIX addresses is when dealing with ISO_CLIENTADDR (which
@@ -1543,6 +1543,18 @@ get_interface_addresses_win32(int severity, sa_family_t family)
#define _SIZEOF_ADDR_IFREQ sizeof
#endif
+/* Free ifc->ifc_buf safely. */
+static void
+ifconf_free_ifc_buf(struct ifconf *ifc)
+{
+ /* On macOS, tor_free() takes the address of ifc.ifc_buf, which leads to
+ * undefined behaviour, because pointer-to-pointers are expected to be
+ * aligned at 8-bytes, but the ifconf structure is packed. So we use
+ * raw_free() instead. */
+ raw_free(ifc->ifc_buf);
+ ifc->ifc_buf = NULL;
+}
+
/** Convert <b>*buf</b>, an ifreq structure array of size <b>buflen</b>,
* into smartlist of <b>tor_addr_t</b> structures.
*/
@@ -1629,7 +1641,7 @@ get_interface_addresses_ioctl(int severity, sa_family_t family)
done:
if (fd >= 0)
close(fd);
- tor_free(ifc.ifc_buf);
+ ifconf_free_ifc_buf(&ifc);
return result;
}
#endif /* defined(HAVE_IFCONF_TO_SMARTLIST) */
@@ -1787,14 +1799,14 @@ get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr))
break;
} SMARTLIST_FOREACH_END(a);
- free_interface_address6_list(addrs);
+ interface_address6_list_free(addrs);
return rv;
}
/** Free a smartlist of IP addresses returned by get_interface_address6_list.
*/
void
-free_interface_address6_list(smartlist_t *addrs)
+interface_address6_list_free_(smartlist_t *addrs)
{
if (addrs != NULL) {
SMARTLIST_FOREACH(addrs, tor_addr_t *, a, tor_free(a));
@@ -1809,7 +1821,7 @@ free_interface_address6_list(smartlist_t *addrs)
* An empty smartlist means that there are no addresses of the selected type
* matching these criteria.
* Returns NULL on failure.
- * Use free_interface_address6_list to free the returned list.
+ * Use interface_address6_list_free to free the returned list.
*/
MOCK_IMPL(smartlist_t *,
get_interface_address6_list,(int severity,
diff --git a/src/common/address.h b/src/common/address.h
index 2b9546782e..c9d9543dee 100644
--- a/src/common/address.h
+++ b/src/common/address.h
@@ -206,7 +206,9 @@ const char * fmt_addr32(uint32_t addr);
MOCK_DECL(int,get_interface_address6,(int severity, sa_family_t family,
tor_addr_t *addr));
-void free_interface_address6_list(smartlist_t * addrs);
+void interface_address6_list_free_(smartlist_t * addrs);// XXXX
+#define interface_address6_list_free(addrs) \
+ FREE_AND_NULL(smartlist_t, interface_address6_list_free_, (addrs))
MOCK_DECL(smartlist_t *,get_interface_address6_list,(int severity,
sa_family_t family,
int include_internal));
@@ -323,13 +325,8 @@ int addr_mask_get_bits(uint32_t mask);
int tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len);
char *tor_dup_ip(uint32_t addr) ATTR_MALLOC;
MOCK_DECL(int,get_interface_address,(int severity, uint32_t *addr));
-/** Free a smartlist of IP addresses returned by get_interface_address_list.
- */
-static inline void
-free_interface_address_list(smartlist_t *addrs)
-{
- free_interface_address6_list(addrs);
-}
+#define interface_address_list_free(lst)\
+ interface_address6_list_free(lst)
/** Return a smartlist of the IPv4 addresses of all interfaces on the server.
* Excludes loopback and multicast addresses. Only includes internal addresses
* if include_internal is true. (Note that a relay behind NAT may use an
diff --git a/src/common/address_set.c b/src/common/address_set.c
index 4924cb65c2..f61fa294e0 100644
--- a/src/common/address_set.c
+++ b/src/common/address_set.c
@@ -34,7 +34,7 @@
* independent siphashes rather than messing around with bit-shifts. The
* approach here is probably more sound, and we should prefer it if&when we
* unify the implementations.
- **/
+ */
struct address_set_t {
/** siphash keys to make N_HASHES independent hashes for each address. */
@@ -63,7 +63,7 @@ address_set_new(int max_addresses_guess)
}
/**
- * Release all storage associated with <b>set</b>
+ * Release all storage associated with <b>set</b>.
*/
void
address_set_free(address_set_t *set)
@@ -107,7 +107,7 @@ address_set_add_ipv4h(address_set_t *set, uint32_t addr)
}
/**
- * Return true if <b>addr</b> if a member of <b>set</b>. (And probably,
+ * Return true if <b>addr</b> is a member of <b>set</b>. (And probably,
* return false if <b>addr</b> is not a member of set.)
*/
int
diff --git a/src/common/address_set.h b/src/common/address_set.h
index aedf17fc66..28d29f3fdf 100644
--- a/src/common/address_set.h
+++ b/src/common/address_set.h
@@ -2,7 +2,7 @@
/* See LICENSE for licensing information */
/**
- * \file addressset.h
+ * \file address_set.h
* \brief Types to handle sets of addresses.
*
* This module was first written on a semi-emergency basis to improve the
diff --git a/src/common/aes.c b/src/common/aes.c
index 20b51a6758..5d0841dfa3 100644
--- a/src/common/aes.c
+++ b/src/common/aes.c
@@ -18,6 +18,7 @@
#include <openssl/opensslv.h>
#include "crypto.h"
+#include "crypto_openssl_mgt.h"
#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0)
#error "We require OpenSSL >= 1.0.0"
@@ -110,7 +111,7 @@ aes_new_cipher(const uint8_t *key, const uint8_t *iv, int key_bits)
return (aes_cnt_cipher_t *) cipher;
}
void
-aes_cipher_free(aes_cnt_cipher_t *cipher_)
+aes_cipher_free_(aes_cnt_cipher_t *cipher_)
{
if (!cipher_)
return;
@@ -254,7 +255,7 @@ evaluate_ctr_for_aes(void)
/* LCOV_EXCL_START */
log_err(LD_CRYPTO, "This OpenSSL has a buggy version of counter mode; "
"quitting tor.");
- exit(1);
+ exit(1); // exit ok: openssl is broken.
/* LCOV_EXCL_STOP */
}
return 0;
@@ -324,7 +325,7 @@ aes_set_key(aes_cnt_cipher_t *cipher, const uint8_t *key, int key_bits)
/** Release storage held by <b>cipher</b>
*/
void
-aes_cipher_free(aes_cnt_cipher_t *cipher)
+aes_cipher_free_(aes_cnt_cipher_t *cipher)
{
if (!cipher)
return;
diff --git a/src/common/aes.h b/src/common/aes.h
index 1e400a56e0..0b17cd55a4 100644
--- a/src/common/aes.h
+++ b/src/common/aes.h
@@ -17,7 +17,9 @@ typedef struct aes_cnt_cipher aes_cnt_cipher_t;
aes_cnt_cipher_t* aes_new_cipher(const uint8_t *key, const uint8_t *iv,
int key_bits);
-void aes_cipher_free(aes_cnt_cipher_t *cipher);
+void aes_cipher_free_(aes_cnt_cipher_t *cipher);
+#define aes_cipher_free(cipher) \
+ FREE_AND_NULL(aes_cnt_cipher_t, aes_cipher_free_, (cipher))
void aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len);
int evaluate_evp_for_aes(int force_value);
diff --git a/src/common/buffers.c b/src/common/buffers.c
index c45e13d551..a01add9bef 100644
--- a/src/common/buffers.c
+++ b/src/common/buffers.c
@@ -409,7 +409,7 @@ buf_slack(const buf_t *buf)
/** Release storage held by <b>buf</b>. */
void
-buf_free(buf_t *buf)
+buf_free_(buf_t *buf)
{
if (!buf)
return;
@@ -472,7 +472,7 @@ buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped)
chunk = chunk_new_with_alloc_size(buf_preferred_chunk_size(capacity));
}
- chunk->inserted_time = (uint32_t)monotime_coarse_absolute_msec();
+ chunk->inserted_time = monotime_coarse_get_stamp();
if (buf->tail) {
tor_assert(buf->head);
@@ -487,8 +487,8 @@ buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped)
}
/** Return the age of the oldest chunk in the buffer <b>buf</b>, in
- * milliseconds. Requires the current monotonic time, in truncated msec,
- * as its input <b>now</b>.
+ * timestamp units. Requires the current monotonic timestamp as its
+ * input <b>now</b>.
*/
uint32_t
buf_get_oldest_chunk_timestamp(const buf_t *buf, uint32_t now)
@@ -714,6 +714,53 @@ buf_add(buf_t *buf, const char *string, size_t string_len)
return (int)buf->datalen;
}
+/** Add a nul-terminated <b>string</b> to <b>buf</b>, not including the
+ * terminating NUL. */
+void
+buf_add_string(buf_t *buf, const char *string)
+{
+ buf_add(buf, string, strlen(string));
+}
+
+/** As tor_snprintf, but write the results into a buf_t */
+void
+buf_add_printf(buf_t *buf, const char *format, ...)
+{
+ va_list ap;
+ va_start(ap,format);
+ buf_add_vprintf(buf, format, ap);
+ va_end(ap);
+}
+
+/** As tor_vsnprintf, but write the results into a buf_t. */
+void
+buf_add_vprintf(buf_t *buf, const char *format, va_list args)
+{
+ /* XXXX Faster implementations are easy enough, but let's optimize later */
+ char *tmp;
+ tor_vasprintf(&tmp, format, args);
+ buf_add(buf, tmp, strlen(tmp));
+ tor_free(tmp);
+}
+
+/** Return a heap-allocated string containing the contents of <b>buf</b>, plus
+ * a NUL byte. If <b>sz_out</b> is provided, set *<b>sz_out</b> to the length
+ * of the returned string, not including the terminating NUL. */
+char *
+buf_extract(buf_t *buf, size_t *sz_out)
+{
+ tor_assert(buf);
+
+ size_t sz = buf_datalen(buf);
+ char *result;
+ result = tor_malloc(sz+1);
+ buf_peek(buf, result, sz);
+ result[sz] = 0;
+ if (sz_out)
+ *sz_out = sz;
+ return result;
+}
+
/** Helper: copy the first <b>string_len</b> bytes from <b>buf</b>
* onto <b>string</b>.
*/
@@ -795,6 +842,28 @@ buf_move_to_buf(buf_t *buf_out, buf_t *buf_in, size_t *buf_flushlen)
return (int)cp;
}
+/** Moves all data from <b>buf_in</b> to <b>buf_out</b>, without copying.
+ */
+void
+buf_move_all(buf_t *buf_out, buf_t *buf_in)
+{
+ tor_assert(buf_out);
+ if (!buf_in)
+ return;
+
+ if (buf_out->head == NULL) {
+ buf_out->head = buf_in->head;
+ buf_out->tail = buf_in->tail;
+ } else {
+ buf_out->tail->next = buf_in->head;
+ buf_out->tail = buf_in->tail;
+ }
+
+ buf_out->datalen += buf_in->datalen;
+ buf_in->head = buf_in->tail = NULL;
+ buf_in->datalen = 0;
+}
+
/** Internal structure: represents a position in a buffer. */
typedef struct buf_pos_t {
const chunk_t *chunk; /**< Which chunk are we pointing to? */
diff --git a/src/common/buffers.h b/src/common/buffers.h
index 1eaa5f2d04..22a5f7bfa3 100644
--- a/src/common/buffers.h
+++ b/src/common/buffers.h
@@ -24,7 +24,8 @@ struct tor_compress_state_t;
buf_t *buf_new(void);
buf_t *buf_new_with_capacity(size_t size);
size_t buf_get_default_chunk_size(const buf_t *buf);
-void buf_free(buf_t *buf);
+void buf_free_(buf_t *buf);
+#define buf_free(b) FREE_AND_NULL(buf_t, buf_free_, (b))
void buf_clear(buf_t *buf);
buf_t *buf_copy(const buf_t *buf);
@@ -43,9 +44,15 @@ int buf_flush_to_socket(buf_t *buf, tor_socket_t s, size_t sz,
size_t *buf_flushlen);
int buf_add(buf_t *buf, const char *string, size_t string_len);
+void buf_add_string(buf_t *buf, const char *string);
+void buf_add_printf(buf_t *buf, const char *format, ...)
+ CHECK_PRINTF(2, 3);
+void buf_add_vprintf(buf_t *buf, const char *format, va_list args)
+ CHECK_PRINTF(2, 0);
int buf_add_compress(buf_t *buf, struct tor_compress_state_t *state,
const char *data, size_t data_len, int done);
int buf_move_to_buf(buf_t *buf_out, buf_t *buf_in, size_t *buf_flushlen);
+void buf_move_all(buf_t *buf_out, buf_t *buf_in);
void buf_peek(const buf_t *buf, char *string, size_t string_len);
void buf_drain(buf_t *buf, size_t n);
int buf_get_bytes(buf_t *buf, char *string, size_t string_len);
@@ -62,6 +69,7 @@ void buf_assert_ok(buf_t *buf);
int buf_find_string_offset(const buf_t *buf, const char *s, size_t n);
void buf_pullup(buf_t *buf, size_t bytes,
const char **head_out, size_t *len_out);
+char *buf_extract(buf_t *buf, size_t *sz_out);
#ifdef BUFFERS_PRIVATE
#ifdef TOR_UNIT_TESTS
@@ -79,8 +87,7 @@ typedef struct chunk_t {
size_t DBG_alloc;
#endif
char *data; /**< A pointer to the first byte of data stored in <b>mem</b>. */
- uint32_t inserted_time; /**< Timestamp in truncated ms since epoch
- * when this chunk was inserted. */
+ uint32_t inserted_time; /**< Timestamp when this chunk was inserted. */
char mem[FLEXIBLE_ARRAY_MEMBER]; /**< The actual memory used for storage in
* this chunk. */
} chunk_t;
diff --git a/src/common/compat.c b/src/common/compat.c
index 83bb707e17..7d9add50b2 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -1186,7 +1186,7 @@ mark_socket_open(tor_socket_t s)
bitarray_set(open_sockets, s);
}
#else /* !(defined(DEBUG_SOCKET_COUNTING)) */
-#define mark_socket_open(s) STMT_NIL
+#define mark_socket_open(s) ((void) (s))
#endif /* defined(DEBUG_SOCKET_COUNTING) */
/** @} */
@@ -1273,11 +1273,22 @@ tor_open_socket_with_extensions(int domain, int type, int protocol,
goto socket_ok; /* So that socket_ok will not be unused. */
socket_ok:
+ tor_take_socket_ownership(s);
+ return s;
+}
+
+/**
+ * For socket accounting: remember that we are the owner of the socket
+ * <b>s</b>. This will prevent us from overallocating sockets, and prevent us
+ * from asserting later when we close the socket <b>s</b>.
+ */
+void
+tor_take_socket_ownership(tor_socket_t s)
+{
socket_accounting_lock();
++n_sockets_open;
mark_socket_open(s);
socket_accounting_unlock();
- return s;
}
/** As accept(), but counts the number of open sockets. */
@@ -1358,10 +1369,7 @@ tor_accept_socket_with_extensions(tor_socket_t sockfd, struct sockaddr *addr,
goto socket_ok; /* So that socket_ok will not be unused. */
socket_ok:
- socket_accounting_lock();
- ++n_sockets_open;
- mark_socket_open(s);
- socket_accounting_unlock();
+ tor_take_socket_ownership(s);
return s;
}
@@ -1897,9 +1905,12 @@ tor_passwd_dup(const struct passwd *pw)
return new_pw;
}
+#define tor_passwd_free(pw) \
+ FREE_AND_NULL(struct passwd, tor_passwd_free_, (pw))
+
/** Helper: free one of our cached 'struct passwd' values. */
static void
-tor_passwd_free(struct passwd *pw)
+tor_passwd_free_(struct passwd *pw)
{
if (!pw)
return;
@@ -2444,7 +2455,7 @@ get_environment(void)
/** Get name of current host and write it to <b>name</b> array, whose
* length is specified by <b>namelen</b> argument. Return 0 upon
- * successfull completion; otherwise return return -1. (Currently,
+ * successful completion; otherwise return return -1. (Currently,
* this function is merely a mockable wrapper for POSIX gethostname().)
*/
MOCK_IMPL(int,
@@ -2879,7 +2890,7 @@ compute_num_cpus(void)
/** Helper: Deal with confused or out-of-bounds values from localtime_r and
* friends. (On some platforms, they can give out-of-bounds values or can
* return NULL.) If <b>islocal</b>, this is a localtime result; otherwise
- * it's from gmtime. The function returned <b>r</b>, when given <b>timep</b>
+ * it's from gmtime. The function returns <b>r</b>, when given <b>timep</b>
* as its input. If we need to store new results, store them in
* <b>resultbuf</b>. */
static struct tm *
@@ -3398,8 +3409,8 @@ get_total_system_memory_impl(void)
* Try to find out how much physical memory the system has. On success,
* return 0 and set *<b>mem_out</b> to that value. On failure, return -1.
*/
-int
-get_total_system_memory(size_t *mem_out)
+MOCK_IMPL(int,
+get_total_system_memory, (size_t *mem_out))
{
static size_t mem_cached=0;
uint64_t m = get_total_system_memory_impl();
diff --git a/src/common/compat.h b/src/common/compat.h
index fee9e6587d..3088e68355 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -10,6 +10,9 @@
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
+#ifndef SIO_IDEAL_SEND_BACKLOG_QUERY
+#define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747b
+#endif
#endif
#include "torint.h"
#include "testsupport.h"
@@ -483,6 +486,7 @@ typedef int socklen_t;
int tor_close_socket_simple(tor_socket_t s);
MOCK_DECL(int, tor_close_socket, (tor_socket_t s));
+void tor_take_socket_ownership(tor_socket_t s);
tor_socket_t tor_open_socket_with_extensions(
int domain, int type, int protocol,
int cloexec, int nonblock);
@@ -695,7 +699,7 @@ char *make_path_absolute(char *fname);
char **get_environment(void);
-int get_total_system_memory(size_t *mem_out);
+MOCK_DECL(int, get_total_system_memory, (size_t *mem_out));
int compute_num_cpus(void);
diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c
index 740cc2a11d..735385557c 100644
--- a/src/common/compat_libevent.c
+++ b/src/common/compat_libevent.c
@@ -69,7 +69,7 @@ suppress_libevent_log_msg(const char *msg)
/* Wrapper for event_free() that tolerates tor_event_free(NULL) */
void
-tor_event_free(struct event *ev)
+tor_event_free_(struct event *ev)
{
if (ev == NULL)
return;
@@ -126,7 +126,7 @@ tor_libevent_initialize(tor_libevent_cfg *torcfg)
if (!the_event_base) {
/* LCOV_EXCL_START */
log_err(LD_GENERAL, "Unable to initialize Libevent: cannot continue.");
- exit(1);
+ exit(1); // exit ok: libevent is broken.
/* LCOV_EXCL_STOP */
}
@@ -213,7 +213,7 @@ periodic_timer_new(struct event_base *base,
/** Stop and free a periodic timer */
void
-periodic_timer_free(periodic_timer_t *timer)
+periodic_timer_free_(periodic_timer_t *timer)
{
if (!timer)
return;
@@ -237,6 +237,17 @@ tor_init_libevent_rng(void)
return rv;
}
+/**
+ * Un-initialize libevent in preparation for an exit
+ */
+void
+tor_libevent_free_all(void)
+{
+ if (the_event_base)
+ event_base_free(the_event_base);
+ the_event_base = NULL;
+}
+
#if defined(LIBEVENT_VERSION_NUMBER) && \
LIBEVENT_VERSION_NUMBER >= V(2,1,1) && \
!defined(TOR_UNIT_TESTS)
diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h
index 834354c405..1853e50917 100644
--- a/src/common/compat_libevent.h
+++ b/src/common/compat_libevent.h
@@ -19,7 +19,9 @@ void suppress_libevent_log_msg(const char *msg);
evdns_add_server_port_with_base(tor_libevent_get_base(), \
(sock),(tcp),(cb),(data));
-void tor_event_free(struct event *ev);
+void tor_event_free_(struct event *ev);
+#define tor_event_free(ev) \
+ FREE_AND_NULL(struct event, tor_event_free_, (ev))
typedef struct periodic_timer_t periodic_timer_t;
@@ -27,9 +29,12 @@ periodic_timer_t *periodic_timer_new(struct event_base *base,
const struct timeval *tv,
void (*cb)(periodic_timer_t *timer, void *data),
void *data);
-void periodic_timer_free(periodic_timer_t *);
+void periodic_timer_free_(periodic_timer_t *);
+#define periodic_timer_free(t) \
+ FREE_AND_NULL(periodic_timer_t, periodic_timer_free_, (t))
#define tor_event_base_loopexit event_base_loopexit
+#define tor_event_base_loopbreak event_base_loopbreak
/** Defines a configuration for using libevent with Tor: passed as an argument
* to tor_libevent_initialize() to describe how we want to set up. */
@@ -47,6 +52,7 @@ const char *tor_libevent_get_method(void);
void tor_check_libevent_header_compatibility(void);
const char *tor_libevent_get_version_str(void);
const char *tor_libevent_get_header_version_str(void);
+void tor_libevent_free_all(void);
int tor_init_libevent_rng(void);
diff --git a/src/common/compat_openssl.h b/src/common/compat_openssl.h
index c695f1e9df..d1481fb46c 100644
--- a/src/common/compat_openssl.h
+++ b/src/common/compat_openssl.h
@@ -8,11 +8,12 @@
#define TOR_COMPAT_OPENSSL_H
#include <openssl/opensslv.h>
+#include "crypto_openssl_mgt.h"
/**
* \file compat_openssl.h
*
- * \brief compatability definitions for working with different openssl forks
+ * \brief compatibility definitions for working with different openssl forks
**/
#if !defined(LIBRESSL_VERSION_NUMBER) && \
@@ -27,8 +28,11 @@
#define OPENSSL_1_1_API
#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) && ... */
-#ifndef OPENSSL_1_1_API
+#ifndef OPENSSL_VERSION
#define OPENSSL_VERSION SSLEAY_VERSION
+#endif
+
+#ifndef OPENSSL_1_1_API
#define OpenSSL_version(v) SSLeay_version(v)
#define OpenSSL_version_num() SSLeay()
#define RAND_OpenSSL() RAND_SSLeay()
diff --git a/src/common/compat_rust.c b/src/common/compat_rust.c
deleted file mode 100644
index 366fd4037b..0000000000
--- a/src/common/compat_rust.c
+++ /dev/null
@@ -1,39 +0,0 @@
-/* Copyright (c) 2017, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-/**
- * \file rust_compat.c
- * \brief Rust FFI compatibility functions and helpers. This file is only built
- * if Rust is not used.
- **/
-
-#include "compat_rust.h"
-#include "util.h"
-
-/**
- * Free storage pointed to by <b>str</b>, and itself.
- */
-void
-rust_str_free(rust_str_t str)
-{
- char *s = (char *)str;
- tor_free(s);
-}
-
-/**
- * Return zero-terminated contained string.
- */
-const char *
-rust_str_get(const rust_str_t str)
-{
- return (const char *)str;
-}
-
-/* If we were using Rust, we'd say so on startup. */
-rust_str_t
-rust_welcome_string(void)
-{
- char *s = tor_malloc_zero(1);
- return (rust_str_t)s;
-}
-
diff --git a/src/common/compat_rust.h b/src/common/compat_rust.h
deleted file mode 100644
index 72fde39296..0000000000
--- a/src/common/compat_rust.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright (c) 2017, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-/**
- * \file rust_compat.h
- * \brief Headers for rust_compat.c
- **/
-
-#ifndef TOR_RUST_COMPAT_H
-#define TOR_RUST_COMPAT_H
-
-#include "torint.h"
-
-/**
- * Strings allocated in Rust must be freed from Rust code again. Let's make
- * it less likely to accidentally mess up and call tor_free() on it, because
- * currently it'll just work but might break at any time.
- */
-typedef uintptr_t rust_str_t;
-
-void rust_str_free(rust_str_t);
-
-const char *rust_str_get(const rust_str_t);
-
-rust_str_t rust_welcome_string(void);
-
-#endif /* !defined(TOR_RUST_COMPAT_H) */
-
diff --git a/src/common/compat_threads.c b/src/common/compat_threads.c
index 208d3138d9..9f64c06342 100644
--- a/src/common/compat_threads.c
+++ b/src/common/compat_threads.c
@@ -48,7 +48,7 @@ tor_mutex_new_nonrecursive(void)
}
/** Release all storage and system resources held by <b>m</b>. */
void
-tor_mutex_free(tor_mutex_t *m)
+tor_mutex_free_(tor_mutex_t *m)
{
if (!m)
return;
@@ -68,7 +68,7 @@ tor_cond_new(void)
/** Free all storage held in <b>c</b>. */
void
-tor_cond_free(tor_cond_t *c)
+tor_cond_free_(tor_cond_t *c)
{
if (!c)
return;
@@ -352,12 +352,7 @@ alert_sockets_close(alert_sockets_t *socks)
socks->read_fd = socks->write_fd = -1;
}
-/*
- * XXXX We might be smart to move to compiler intrinsics or real atomic
- * XXXX operations at some point. But not yet.
- *
- */
-
+#ifndef HAVE_WORKING_STDATOMIC
/** Initialize a new atomic counter with the value 0 */
void
atomic_counter_init(atomic_counter_t *counter)
@@ -397,4 +392,15 @@ atomic_counter_get(atomic_counter_t *counter)
tor_mutex_release(&counter->mutex);
return val;
}
-
+/** Replace the value of an atomic counter; return the old one. */
+size_t
+atomic_counter_exchange(atomic_counter_t *counter, size_t newval)
+{
+ size_t oldval;
+ tor_mutex_acquire(&counter->mutex);
+ oldval = counter->val;
+ counter->val = newval;
+ tor_mutex_release(&counter->mutex);
+ return oldval;
+}
+#endif /* !defined(HAVE_WORKING_STDATOMIC) */
diff --git a/src/common/compat_threads.h b/src/common/compat_threads.h
index 42f14eab2a..8bf8225689 100644
--- a/src/common/compat_threads.h
+++ b/src/common/compat_threads.h
@@ -14,6 +14,14 @@
#include <pthread.h>
#endif
+#if defined(HAVE_STDATOMIC_H) && defined(STDATOMIC_WORKS)
+#define HAVE_WORKING_STDATOMIC
+#endif
+
+#ifdef HAVE_WORKING_STDATOMIC
+#include <stdatomic.h>
+#endif
+
#if defined(_WIN32)
#define USE_WIN32_THREADS
#elif defined(HAVE_PTHREAD_H) && defined(HAVE_PTHREAD_CREATE)
@@ -50,7 +58,8 @@ void tor_mutex_init(tor_mutex_t *m);
void tor_mutex_init_nonrecursive(tor_mutex_t *m);
void tor_mutex_acquire(tor_mutex_t *m);
void tor_mutex_release(tor_mutex_t *m);
-void tor_mutex_free(tor_mutex_t *m);
+void tor_mutex_free_(tor_mutex_t *m);
+#define tor_mutex_free(m) FREE_AND_NULL(tor_mutex_t, tor_mutex_free_, (m))
void tor_mutex_uninit(tor_mutex_t *m);
unsigned long tor_get_thread_id(void);
void tor_threads_init(void);
@@ -77,7 +86,8 @@ typedef struct tor_cond_t {
} tor_cond_t;
tor_cond_t *tor_cond_new(void);
-void tor_cond_free(tor_cond_t *cond);
+void tor_cond_free_(tor_cond_t *cond);
+#define tor_cond_free(c) FREE_AND_NULL(tor_cond_t, tor_cond_free_, (c))
int tor_cond_init(tor_cond_t *cond);
void tor_cond_uninit(tor_cond_t *cond);
int tor_cond_wait(tor_cond_t *cond, tor_mutex_t *mutex,
@@ -150,16 +160,67 @@ void tor_threadlocal_set(tor_threadlocal_t *threadlocal, void *value);
/**
* Atomic counter type; holds a size_t value.
*/
+#ifdef HAVE_WORKING_STDATOMIC
+typedef struct atomic_counter_t {
+ atomic_size_t val;
+} atomic_counter_t;
+#define ATOMIC_LINKAGE static
+#else /* !(defined(HAVE_WORKING_STDATOMIC)) */
typedef struct atomic_counter_t {
tor_mutex_t mutex;
size_t val;
} atomic_counter_t;
-
-void atomic_counter_init(atomic_counter_t *counter);
-void atomic_counter_destroy(atomic_counter_t *counter);
-void atomic_counter_add(atomic_counter_t *counter, size_t add);
-void atomic_counter_sub(atomic_counter_t *counter, size_t sub);
-size_t atomic_counter_get(atomic_counter_t *counter);
+#define ATOMIC_LINKAGE
+#endif /* defined(HAVE_WORKING_STDATOMIC) */
+
+ATOMIC_LINKAGE void atomic_counter_init(atomic_counter_t *counter);
+ATOMIC_LINKAGE void atomic_counter_destroy(atomic_counter_t *counter);
+ATOMIC_LINKAGE void atomic_counter_add(atomic_counter_t *counter, size_t add);
+ATOMIC_LINKAGE void atomic_counter_sub(atomic_counter_t *counter, size_t sub);
+ATOMIC_LINKAGE size_t atomic_counter_get(atomic_counter_t *counter);
+ATOMIC_LINKAGE size_t atomic_counter_exchange(atomic_counter_t *counter,
+ size_t newval);
+#undef ATOMIC_LINKAGE
+
+#ifdef HAVE_WORKING_STDATOMIC
+/** Initialize a new atomic counter with the value 0 */
+static inline void
+atomic_counter_init(atomic_counter_t *counter)
+{
+ atomic_init(&counter->val, 0);
+}
+/** Clean up all resources held by an atomic counter. */
+static inline void
+atomic_counter_destroy(atomic_counter_t *counter)
+{
+ (void)counter;
+}
+/** Add a value to an atomic counter. */
+static inline void
+atomic_counter_add(atomic_counter_t *counter, size_t add)
+{
+ (void) atomic_fetch_add(&counter->val, add);
+}
+/** Subtract a value from an atomic counter. */
+static inline void
+atomic_counter_sub(atomic_counter_t *counter, size_t sub)
+{
+ (void) atomic_fetch_sub(&counter->val, sub);
+}
+/** Return the current value of an atomic counter */
+static inline size_t
+atomic_counter_get(atomic_counter_t *counter)
+{
+ return atomic_load(&counter->val);
+}
+/** Replace the value of an atomic counter; return the old one. */
+static inline size_t
+atomic_counter_exchange(atomic_counter_t *counter, size_t newval)
+{
+ return atomic_exchange(&counter->val, newval);
+}
+
+#else /* !(defined(HAVE_WORKING_STDATOMIC)) */
+#endif /* defined(HAVE_WORKING_STDATOMIC) */
#endif /* !defined(TOR_COMPAT_THREADS_H) */
-
diff --git a/src/common/compat_time.c b/src/common/compat_time.c
index 1ce6f5ce4e..183a60a480 100644
--- a/src/common/compat_time.c
+++ b/src/common/compat_time.c
@@ -90,7 +90,7 @@ tor_gettimeofday(struct timeval *timeval)
if (ft.ft_64 < EPOCH_BIAS) {
/* LCOV_EXCL_START */
log_err(LD_GENERAL,"System time is before 1970; failing.");
- exit(1);
+ exit(1); // exit ok: system clock is broken.
/* LCOV_EXCL_STOP */
}
ft.ft_64 -= EPOCH_BIAS;
@@ -102,7 +102,7 @@ tor_gettimeofday(struct timeval *timeval)
log_err(LD_GENERAL,"gettimeofday failed.");
/* If gettimeofday dies, we have either given a bad timezone (we didn't),
or segfaulted.*/
- exit(1);
+ exit(1); // exit ok: gettimeofday failed.
/* LCOV_EXCL_STOP */
}
#elif defined(HAVE_FTIME)
@@ -279,6 +279,7 @@ monotime_reset_ratchets_for_testing(void)
* nanoseconds.
*/
static struct mach_timebase_info mach_time_info;
+static int monotime_shift = 0;
static void
monotime_init_internal(void)
@@ -287,6 +288,14 @@ monotime_init_internal(void)
int r = mach_timebase_info(&mach_time_info);
tor_assert(r == 0);
tor_assert(mach_time_info.denom != 0);
+
+ {
+ // approximate only.
+ uint64_t ns_per_tick = mach_time_info.numer / mach_time_info.denom;
+ uint64_t ms_per_tick = ns_per_tick * ONE_MILLION;
+ // requires that tor_log2(0) == 0.
+ monotime_shift = tor_log2(ms_per_tick);
+ }
}
/**
@@ -305,6 +314,21 @@ monotime_get(monotime_t *out)
out->abstime_ = mach_absolute_time();
}
+#if defined(HAVE_MACH_APPROXIMATE_TIME)
+void
+monotime_coarse_get(monotime_coarse_t *out)
+{
+#ifdef TOR_UNIT_TESTS
+ if (monotime_mocking_enabled) {
+ out->abstime_ = (mock_time_nsec_coarse * mach_time_info.denom)
+ / mach_time_info.numer;
+ return;
+ }
+#endif /* defined(TOR_UNIT_TESTS) */
+ out->abstime_ = mach_approximate_time();
+}
+#endif
+
/**
* Return the number of nanoseconds between <b>start</b> and <b>end</b>.
*/
@@ -321,6 +345,26 @@ monotime_diff_nsec(const monotime_t *start,
return diff_nsec;
}
+uint32_t
+monotime_coarse_to_stamp(const monotime_coarse_t *t)
+{
+ return (uint32_t)(t->abstime_ >> monotime_shift);
+}
+
+int
+monotime_is_zero(const monotime_t *val)
+{
+ return val->abstime_ == 0;
+}
+
+void
+monotime_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec)
+{
+ const uint64_t nsec = msec * ONE_MILLION;
+ const uint64_t ticks = (nsec * mach_time_info.denom) / mach_time_info.numer;
+ out->abstime_ = val->abstime_ + ticks;
+}
+
/* end of "__APPLE__" */
#elif defined(HAVE_CLOCK_GETTIME)
@@ -399,6 +443,37 @@ monotime_diff_nsec(const monotime_t *start,
return diff_nsec;
}
+/* This value is ONE_BILLION >> 20. */
+static const uint32_t STAMP_TICKS_PER_SECOND = 953;
+
+uint32_t
+monotime_coarse_to_stamp(const monotime_coarse_t *t)
+{
+ uint32_t nsec = (uint32_t)t->ts_.tv_nsec;
+ uint32_t sec = (uint32_t)t->ts_.tv_sec;
+
+ return (sec * STAMP_TICKS_PER_SECOND) + (nsec >> 20);
+}
+
+int
+monotime_is_zero(const monotime_t *val)
+{
+ return val->ts_.tv_sec == 0 && val->ts_.tv_nsec == 0;
+}
+
+void
+monotime_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec)
+{
+ const uint32_t sec = msec / 1000;
+ const uint32_t msec_remainder = msec % 1000;
+ out->ts_.tv_sec = val->ts_.tv_sec + sec;
+ out->ts_.tv_nsec = val->ts_.tv_nsec + (msec_remainder * ONE_MILLION);
+ if (out->ts_.tv_nsec > ONE_BILLION) {
+ out->ts_.tv_nsec -= ONE_BILLION;
+ out->ts_.tv_sec += 1;
+ }
+}
+
/* end of "HAVE_CLOCK_GETTIME" */
#elif defined (_WIN32)
@@ -531,6 +606,41 @@ monotime_coarse_diff_nsec(const monotime_coarse_t *start,
return monotime_coarse_diff_msec(start, end) * ONE_MILLION;
}
+static const uint32_t STAMP_TICKS_PER_SECOND = 1000;
+
+uint32_t
+monotime_coarse_to_stamp(const monotime_coarse_t *t)
+{
+ return (uint32_t) t->tick_count_;
+}
+
+int
+monotime_is_zero(const monotime_t *val)
+{
+ return val->pcount_ == 0;
+}
+
+int
+monotime_coarse_is_zero(const monotime_coarse_t *val)
+{
+ return val->tick_count_ == 0;
+}
+
+void
+monotime_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec)
+{
+ const uint64_t nsec = msec * ONE_MILLION;
+ const uint64_t ticks = (nsec * nsec_per_tick_denom) / nsec_per_tick_numer;
+ out->pcount_ = val->pcount_ + ticks;
+}
+
+void
+monotime_coarse_add_msec(monotime_coarse_t *out, const monotime_coarse_t *val,
+ uint32_t msec)
+{
+ out->tick_count_ = val->tick_count_ + msec;
+}
+
/* end of "_WIN32" */
#elif defined(MONOTIME_USING_GETTIMEOFDAY)
@@ -567,6 +677,36 @@ monotime_diff_nsec(const monotime_t *start,
return (diff.tv_sec * ONE_BILLION + diff.tv_usec * 1000);
}
+/* This value is ONE_MILLION >> 10. */
+static const uint32_t STAMP_TICKS_PER_SECOND = 976;
+
+uint32_t
+monotime_coarse_to_stamp(const monotime_coarse_t *t)
+{
+ const uint32_t usec = (uint32_t)t->tv_.tv_usec;
+ const uint32_t sec = (uint32_t)t->tv_.tv_sec;
+ return (sec * STAMP_TICKS_PER_SECOND) | (nsec >> 10);
+}
+
+int
+monotime_is_zero(const monotime_t *val)
+{
+ return val->tv_.tv_sec == 0 && val->tv_.tv_usec == 0;
+}
+
+void
+monotime_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec)
+{
+ const uint32_t sec = msec / 1000;
+ const uint32_t msec_remainder = msec % 1000;
+ out->tv_.tv_sec = val->tv_.tv_sec + sec;
+ out->tv_.tv_usec = val->tv_.tv_nsec + (msec_remainder * 1000);
+ if (out->tv_.tv_usec > ONE_MILLION) {
+ out->tv_.tv_usec -= ONE_MILLION;
+ out->tv_.tv_sec += 1;
+ }
+}
+
/* end of "MONOTIME_USING_GETTIMEOFDAY" */
#else
#error "No way to implement monotonic timers."
@@ -589,6 +729,19 @@ monotime_init(void)
}
}
+void
+monotime_zero(monotime_t *out)
+{
+ memset(out, 0, sizeof(*out));
+}
+#ifdef MONOTIME_COARSE_TYPE_IS_DIFFERENT
+void
+monotime_coarse_zero(monotime_coarse_t *out)
+{
+ memset(out, 0, sizeof(*out));
+}
+#endif
+
int64_t
monotime_diff_usec(const monotime_t *start,
const monotime_t *end)
@@ -653,5 +806,35 @@ monotime_coarse_absolute_msec(void)
{
return monotime_coarse_absolute_nsec() / ONE_MILLION;
}
+#else
+#define initialized_at_coarse initialized_at
#endif /* defined(MONOTIME_COARSE_FN_IS_DIFFERENT) */
+/**
+ * Return the current time "stamp" as described by monotime_coarse_to_stamp.
+ */
+uint32_t
+monotime_coarse_get_stamp(void)
+{
+ monotime_coarse_t now;
+ monotime_coarse_get(&now);
+ return monotime_coarse_to_stamp(&now);
+}
+
+#ifdef __APPLE__
+uint64_t
+monotime_coarse_stamp_units_to_approx_msec(uint64_t units)
+{
+ /* Recover as much precision as we can. */
+ uint64_t abstime_diff = (units << monotime_shift);
+ return (abstime_diff * mach_time_info.numer) /
+ (mach_time_info.denom * ONE_MILLION);
+}
+#else
+uint64_t
+monotime_coarse_stamp_units_to_approx_msec(uint64_t units)
+{
+ return (units * 1000) / STAMP_TICKS_PER_SECOND;
+}
+#endif
+
diff --git a/src/common/compat_time.h b/src/common/compat_time.h
index 5ea4aae42b..6ddd11883d 100644
--- a/src/common/compat_time.h
+++ b/src/common/compat_time.h
@@ -65,6 +65,9 @@ typedef struct monotime_t {
typedef struct monotime_coarse_t {
uint64_t tick_count_;
} monotime_coarse_t;
+#elif defined(__APPLE__) && defined(HAVE_MACH_APPROXIMATE_TIME)
+#define MONOTIME_COARSE_FN_IS_DIFFERENT
+#define monotime_coarse_t monotime_t
#else
#define monotime_coarse_t monotime_t
#endif /* defined(CLOCK_MONOTONIC_COARSE) && ... || ... */
@@ -102,6 +105,21 @@ uint64_t monotime_absolute_usec(void);
*/
uint64_t monotime_absolute_msec(void);
+/**
+ * Set <b>out</b> to zero.
+ */
+void monotime_zero(monotime_t *out);
+/**
+ * Return true iff <b>out</b> is zero
+ */
+int monotime_is_zero(const monotime_t *out);
+
+/**
+ * Set <b>out</b> to N milliseconds after <b>val</b>.
+ */
+/* XXXX We should add a more generic function here if we ever need to */
+void monotime_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec);
+
#if defined(MONOTIME_COARSE_FN_IS_DIFFERENT)
/**
* Set <b>out</b> to the current coarse time.
@@ -117,6 +135,23 @@ uint64_t monotime_coarse_absolute_msec(void);
#define monotime_coarse_absolute_msec monotime_absolute_msec
#endif /* defined(MONOTIME_COARSE_FN_IS_DIFFERENT) */
+/**
+ * Return a "timestamp" approximation for a coarse monotonic timer.
+ * This timestamp is meant to be fast to calculate and easy to
+ * compare, and have a unit of something roughly around 1 msec.
+ *
+ * It will wrap over from time to time.
+ *
+ * It has no defined zero point.
+ */
+uint32_t monotime_coarse_to_stamp(const monotime_coarse_t *t);
+/**
+ * Convert a difference, expressed in the units of monotime_coarse_to_stamp,
+ * into an approximate number of milliseconds.
+ */
+uint64_t monotime_coarse_stamp_units_to_approx_msec(uint64_t units);
+uint32_t monotime_coarse_get_stamp(void);
+
#if defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT)
int64_t monotime_coarse_diff_nsec(const monotime_coarse_t *start,
const monotime_coarse_t *end);
@@ -124,10 +159,17 @@ int64_t monotime_coarse_diff_usec(const monotime_coarse_t *start,
const monotime_coarse_t *end);
int64_t monotime_coarse_diff_msec(const monotime_coarse_t *start,
const monotime_coarse_t *end);
+void monotime_coarse_zero(monotime_coarse_t *out);
+int monotime_coarse_is_zero(const monotime_coarse_t *val);
+void monotime_coarse_add_msec(monotime_coarse_t *out,
+ const monotime_coarse_t *val, uint32_t msec);
#else /* !(defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT)) */
#define monotime_coarse_diff_nsec monotime_diff_nsec
#define monotime_coarse_diff_usec monotime_diff_usec
#define monotime_coarse_diff_msec monotime_diff_msec
+#define monotime_coarse_zero monotime_zero
+#define monotime_coarse_is_zero monotime_is_zero
+#define monotime_coarse_add_msec monotime_add_msec
#endif /* defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT) */
void tor_gettimeofday(struct timeval *timeval);
diff --git a/src/common/compat_winthreads.c b/src/common/compat_winthreads.c
index 50a3c498ca..5f7ec94c23 100644
--- a/src/common/compat_winthreads.c
+++ b/src/common/compat_winthreads.c
@@ -48,10 +48,12 @@ void
spawn_exit(void)
{
_endthread();
+ // LCOV_EXCL_START
//we should never get here. my compiler thinks that _endthread returns, this
//is an attempt to fool it.
tor_assert(0);
- _exit(0);
+ _exit(0); // exit ok: unreachable.
+ // LCOV_EXCL_STOP
}
void
diff --git a/src/common/compress.c b/src/common/compress.c
index bc12a58ad6..47c93cf6a9 100644
--- a/src/common/compress.c
+++ b/src/common/compress.c
@@ -598,7 +598,7 @@ tor_compress_process(tor_compress_state_t *state,
/** Deallocate <b>state</b>. */
void
-tor_compress_free(tor_compress_state_t *state)
+tor_compress_free_(tor_compress_state_t *state)
{
if (state == NULL)
return;
diff --git a/src/common/compress.h b/src/common/compress.h
index 23a9817479..952102bf97 100644
--- a/src/common/compress.h
+++ b/src/common/compress.h
@@ -80,7 +80,9 @@ tor_compress_output_t tor_compress_process(tor_compress_state_t *state,
char **out, size_t *out_len,
const char **in, size_t *in_len,
int finish);
-void tor_compress_free(tor_compress_state_t *state);
+void tor_compress_free_(tor_compress_state_t *state);
+#define tor_compress_free(st) \
+ FREE_AND_NULL(tor_compress_state_t, tor_compress_free_, (st))
size_t tor_compress_state_size(const tor_compress_state_t *state);
diff --git a/src/common/compress_lzma.c b/src/common/compress_lzma.c
index 6426ede4fd..051c59ba2d 100644
--- a/src/common/compress_lzma.c
+++ b/src/common/compress_lzma.c
@@ -323,7 +323,7 @@ tor_lzma_compress_process(tor_lzma_compress_state_t *state,
/** Deallocate <b>state</b>. */
void
-tor_lzma_compress_free(tor_lzma_compress_state_t *state)
+tor_lzma_compress_free_(tor_lzma_compress_state_t *state)
{
if (state == NULL)
return;
diff --git a/src/common/compress_lzma.h b/src/common/compress_lzma.h
index 7639d98a70..38a447c1f3 100644
--- a/src/common/compress_lzma.h
+++ b/src/common/compress_lzma.h
@@ -31,7 +31,10 @@ tor_lzma_compress_process(tor_lzma_compress_state_t *state,
const char **in, size_t *in_len,
int finish);
-void tor_lzma_compress_free(tor_lzma_compress_state_t *state);
+void tor_lzma_compress_free_(tor_lzma_compress_state_t *state);
+#define tor_lzma_compress_free(st) \
+ FREE_AND_NULL(tor_lzma_compress_state_t, \
+ tor_lzma_compress_free_, (st))
size_t tor_lzma_compress_state_size(const tor_lzma_compress_state_t *state);
diff --git a/src/common/compress_zlib.c b/src/common/compress_zlib.c
index 284542e885..23d71d27be 100644
--- a/src/common/compress_zlib.c
+++ b/src/common/compress_zlib.c
@@ -265,7 +265,7 @@ tor_zlib_compress_process(tor_zlib_compress_state_t *state,
/** Deallocate <b>state</b>. */
void
-tor_zlib_compress_free(tor_zlib_compress_state_t *state)
+tor_zlib_compress_free_(tor_zlib_compress_state_t *state)
{
if (state == NULL)
return;
diff --git a/src/common/compress_zlib.h b/src/common/compress_zlib.h
index 8ace467bf0..e3c1a2b339 100644
--- a/src/common/compress_zlib.h
+++ b/src/common/compress_zlib.h
@@ -31,7 +31,10 @@ tor_zlib_compress_process(tor_zlib_compress_state_t *state,
const char **in, size_t *in_len,
int finish);
-void tor_zlib_compress_free(tor_zlib_compress_state_t *state);
+void tor_zlib_compress_free_(tor_zlib_compress_state_t *state);
+#define tor_zlib_compress_free(st) \
+ FREE_AND_NULL(tor_zlib_compress_state_t, \
+ tor_zlib_compress_free_, (st))
size_t tor_zlib_compress_state_size(const tor_zlib_compress_state_t *state);
diff --git a/src/common/compress_zstd.c b/src/common/compress_zstd.c
index 1a7d21d486..b9f9f1f076 100644
--- a/src/common/compress_zstd.c
+++ b/src/common/compress_zstd.c
@@ -405,7 +405,7 @@ tor_zstd_compress_process(tor_zstd_compress_state_t *state,
/** Deallocate <b>state</b>. */
void
-tor_zstd_compress_free(tor_zstd_compress_state_t *state)
+tor_zstd_compress_free_(tor_zstd_compress_state_t *state)
{
if (state == NULL)
return;
diff --git a/src/common/compress_zstd.h b/src/common/compress_zstd.h
index 02466010ff..9bca24ded7 100644
--- a/src/common/compress_zstd.h
+++ b/src/common/compress_zstd.h
@@ -31,7 +31,10 @@ tor_zstd_compress_process(tor_zstd_compress_state_t *state,
const char **in, size_t *in_len,
int finish);
-void tor_zstd_compress_free(tor_zstd_compress_state_t *state);
+void tor_zstd_compress_free_(tor_zstd_compress_state_t *state);
+#define tor_zstd_compress_free(st) \
+ FREE_AND_NULL(tor_zstd_compress_state_t, \
+ tor_zstd_compress_free_, (st))
size_t tor_zstd_compress_state_size(const tor_zstd_compress_state_t *state);
diff --git a/src/common/confline.c b/src/common/confline.c
index 04545bc2c3..bf613ab742 100644
--- a/src/common/confline.c
+++ b/src/common/confline.c
@@ -12,15 +12,18 @@
static int config_get_lines_aux(const char *string, config_line_t **result,
int extended, int allow_include,
- int *has_include, int recursion_level,
- config_line_t **last);
-static smartlist_t *config_get_file_list(const char *path);
-static int config_get_included_list(const char *path, int recursion_level,
- int extended, config_line_t **list,
- config_line_t **list_last);
+ int *has_include, smartlist_t *opened_lst,
+ int recursion_level, config_line_t **last);
+static smartlist_t *config_get_file_list(const char *path,
+ smartlist_t *opened_files);
+static int config_get_included_config(const char *path, int recursion_level,
+ int extended, config_line_t **config,
+ config_line_t **config_last,
+ smartlist_t *opened_lst);
static int config_process_include(const char *path, int recursion_level,
int extended, config_line_t **list,
- config_line_t **list_last);
+ config_line_t **list_last,
+ smartlist_t *opened_lst);
/** Helper: allocate a new configuration option mapping 'key' to 'val',
* append it to *<b>lst</b>. */
@@ -80,11 +83,13 @@ config_line_find(const config_line_t *lines,
/** Auxiliary function that does all the work of config_get_lines.
* <b>recursion_level</b> is the count of how many nested %includes we have.
+ * <b>opened_lst</b> will have a list of opened files if provided.
* Returns the a pointer to the last element of the <b>result</b> in
* <b>last</b>. */
static int
config_get_lines_aux(const char *string, config_line_t **result, int extended,
- int allow_include, int *has_include, int recursion_level,
+ int allow_include, int *has_include,
+ smartlist_t *opened_lst, int recursion_level,
config_line_t **last)
{
config_line_t *list = NULL, **next, *list_last = NULL;
@@ -134,7 +139,7 @@ config_get_lines_aux(const char *string, config_line_t **result, int extended,
config_line_t *include_list;
if (config_process_include(v, recursion_level, extended, &include_list,
- &list_last) < 0) {
+ &list_last, opened_lst) < 0) {
log_warn(LD_CONFIG, "Error reading included configuration "
"file or directory: \"%s\".", v);
config_free_lines(list);
@@ -176,24 +181,27 @@ config_get_lines_aux(const char *string, config_line_t **result, int extended,
/** Helper: parse the config string and strdup into key/value
* strings. Set *result to the list, or NULL if parsing the string
* failed. Set *has_include to 1 if <b>result</b> has values from
- * %included files. Return 0 on success, -1 on failure. Warn and ignore any
+ * %included files. <b>opened_lst</b> will have a list of opened files if
+ * provided. Return 0 on success, -1 on failure. Warn and ignore any
* misformatted lines.
*
* If <b>extended</b> is set, then treat keys beginning with / and with + as
* indicating "clear" and "append" respectively. */
int
config_get_lines_include(const char *string, config_line_t **result,
- int extended, int *has_include)
+ int extended, int *has_include,
+ smartlist_t *opened_lst)
{
- return config_get_lines_aux(string, result, extended, 1, has_include, 1,
- NULL);
+ return config_get_lines_aux(string, result, extended, 1, has_include,
+ opened_lst, 1, NULL);
}
/** Same as config_get_lines_include but does not allow %include */
int
config_get_lines(const char *string, config_line_t **result, int extended)
{
- return config_get_lines_aux(string, result, extended, 0, NULL, 1, NULL);
+ return config_get_lines_aux(string, result, extended, 0, NULL, NULL, 1,
+ NULL);
}
/** Adds a list of configuration files present on <b>path</b> to
@@ -201,12 +209,18 @@ config_get_lines(const char *string, config_line_t **result, int extended)
* only that file will be added to <b>file_list</b>. If it is a directory,
* all paths for files on that directory root (no recursion) except for files
* whose name starts with a dot will be added to <b>file_list</b>.
- * Return 0 on success, -1 on failure. Ignores empty files.
+ * <b>opened_files</b> will have a list of files opened by this function
+ * if provided. Return 0 on success, -1 on failure. Ignores empty files.
*/
static smartlist_t *
-config_get_file_list(const char *path)
+config_get_file_list(const char *path, smartlist_t *opened_files)
{
smartlist_t *file_list = smartlist_new();
+
+ if (opened_files) {
+ smartlist_add_strdup(opened_files, path);
+ }
+
file_status_t file_type = file_status(path);
if (file_type == FN_FILE) {
smartlist_add_strdup(file_list, path);
@@ -228,6 +242,10 @@ config_get_file_list(const char *path)
tor_asprintf(&fullname, "%s"PATH_SEPARATOR"%s", path, f);
tor_free(f);
+ if (opened_files) {
+ smartlist_add_strdup(opened_files, fullname);
+ }
+
if (file_status(fullname) != FN_FILE) {
tor_free(fullname);
continue;
@@ -245,19 +263,21 @@ config_get_file_list(const char *path)
}
/** Creates a list of config lines present on included <b>path</b>.
- * Set <b>list</b> to the list and <b>list_last</b> to the last element of
- * <b>list</b>. Return 0 on success, -1 on failure. */
+ * Set <b>config</b> to the list and <b>config_last</b> to the last element of
+ * <b>config</b>. <b>opened_lst</b> will have a list of opened files if
+ * provided. Return 0 on success, -1 on failure. */
static int
-config_get_included_list(const char *path, int recursion_level, int extended,
- config_line_t **list, config_line_t **list_last)
+config_get_included_config(const char *path, int recursion_level, int extended,
+ config_line_t **config, config_line_t **config_last,
+ smartlist_t *opened_lst)
{
char *included_conf = read_file_to_str(path, 0, NULL);
if (!included_conf) {
return -1;
}
- if (config_get_lines_aux(included_conf, list, extended, 1, NULL,
- recursion_level+1, list_last) < 0) {
+ if (config_get_lines_aux(included_conf, config, extended, 1, NULL,
+ opened_lst, recursion_level+1, config_last) < 0) {
tor_free(included_conf);
return -1;
}
@@ -268,41 +288,31 @@ config_get_included_list(const char *path, int recursion_level, int extended,
/** Process an %include <b>path</b> in a config file. Set <b>list</b> to the
* list of configuration settings obtained and <b>list_last</b> to the last
- * element of the same list. Return 0 on success, -1 on failure. */
+ * element of the same list. <b>opened_lst</b> will have a list of opened
+ * files if provided. Return 0 on success, -1 on failure. */
static int
config_process_include(const char *path, int recursion_level, int extended,
- config_line_t **list, config_line_t **list_last)
+ config_line_t **list, config_line_t **list_last,
+ smartlist_t *opened_lst)
{
config_line_t *ret_list = NULL;
config_line_t **next = &ret_list;
-#if 0
- // Disabled -- we already unescape_string() on the result. */
- char *unquoted_path = get_unquoted_path(path);
- if (!unquoted_path) {
- return -1;
- }
- smartlist_t *config_files = config_get_file_list(unquoted_path);
- if (!config_files) {
- tor_free(unquoted_path);
- return -1;
- }
- tor_free(unquoted_path);
-#endif /* 0 */
- smartlist_t *config_files = config_get_file_list(path);
+ smartlist_t *config_files = config_get_file_list(path, opened_lst);
if (!config_files) {
return -1;
}
int rv = -1;
SMARTLIST_FOREACH_BEGIN(config_files, const char *, config_file) {
- config_line_t *included_list = NULL;
- if (config_get_included_list(config_file, recursion_level, extended,
- &included_list, list_last) < 0) {
+ config_line_t *included_config = NULL;
+ if (config_get_included_config(config_file, recursion_level, extended,
+ &included_config, list_last,
+ opened_lst) < 0) {
goto done;
}
- *next = included_list;
+ *next = included_config;
if (*list_last)
next = &(*list_last)->next;
@@ -320,7 +330,7 @@ config_process_include(const char *path, int recursion_level, int extended,
* Free all the configuration lines on the linked list <b>front</b>.
*/
void
-config_free_lines(config_line_t *front)
+config_free_lines_(config_line_t *front)
{
config_line_t *tmp;
diff --git a/src/common/confline.h b/src/common/confline.h
index 8256326f2d..772a9bbbdc 100644
--- a/src/common/confline.h
+++ b/src/common/confline.h
@@ -7,6 +7,8 @@
#ifndef TOR_CONFLINE_H
#define TOR_CONFLINE_H
+#include "container.h"
+
/** Ordinary configuration line. */
#define CONFIG_LINE_NORMAL 0
/** Appends to previous configuration for the same option, even if we
@@ -44,8 +46,14 @@ int config_lines_eq(config_line_t *a, config_line_t *b);
int config_count_key(const config_line_t *a, const char *key);
int config_get_lines(const char *string, config_line_t **result, int extended);
int config_get_lines_include(const char *string, config_line_t **result,
- int extended, int *has_include);
-void config_free_lines(config_line_t *front);
+ int extended, int *has_include,
+ smartlist_t *opened_lst);
+void config_free_lines_(config_line_t *front);
+#define config_free_lines(front) \
+ do { \
+ config_free_lines_(front); \
+ (front) = NULL; \
+ } while (0)
const char *parse_config_line_from_str_verbose(const char *line,
char **key_out, char **value_out,
const char **err_out);
diff --git a/src/common/container.c b/src/common/container.c
index 8645cb4826..54b0b2028f 100644
--- a/src/common/container.c
+++ b/src/common/container.c
@@ -42,7 +42,7 @@ smartlist_new,(void))
* list's elements.
*/
MOCK_IMPL(void,
-smartlist_free,(smartlist_t *sl))
+smartlist_free_,(smartlist_t *sl))
{
if (!sl)
return;
@@ -1163,19 +1163,26 @@ HT_GENERATE2(digest256map_impl, digest256map_entry_t, node,
digest256map_entry_hash,
digest256map_entries_eq, 0.6, tor_reallocarray_, tor_free_)
+#define strmap_entry_free(ent) \
+ FREE_AND_NULL(strmap_entry_t, strmap_entry_free_, (ent))
+#define digestmap_entry_free(ent) \
+ FREE_AND_NULL(digestmap_entry_t, digestmap_entry_free_, (ent))
+#define digest256map_entry_free(ent) \
+ FREE_AND_NULL(digest256map_entry_t, digest256map_entry_free_, (ent))
+
static inline void
-strmap_entry_free(strmap_entry_t *ent)
+strmap_entry_free_(strmap_entry_t *ent)
{
tor_free(ent->key);
tor_free(ent);
}
static inline void
-digestmap_entry_free(digestmap_entry_t *ent)
+digestmap_entry_free_(digestmap_entry_t *ent)
{
tor_free(ent);
}
static inline void
-digest256map_entry_free(digest256map_entry_t *ent)
+digest256map_entry_free_(digest256map_entry_t *ent)
{
tor_free(ent);
}
@@ -1335,7 +1342,7 @@ digest256map_assign_key(digest256map_entry_t *ent, const uint8_t *key)
* those entries. If free_val is provided, invoked it every value in \
* <b>map</b>. */ \
MOCK_IMPL(void, \
- prefix##_free, (maptype *map, void (*free_val)(void*))) \
+ prefix##_free_, (maptype *map, void (*free_val)(void*))) \
{ \
prefix##_entry_t **ent, **next, *this; \
if (!map) \
@@ -1525,7 +1532,7 @@ digestset_new(int max_elements)
/** Free all storage held in <b>set</b>. */
void
-digestset_free(digestset_t *set)
+digestset_free_(digestset_t *set)
{
if (!set)
return;
diff --git a/src/common/container.h b/src/common/container.h
index f6affd3bc6..5d2dce5416 100644
--- a/src/common/container.h
+++ b/src/common/container.h
@@ -28,7 +28,9 @@ typedef struct smartlist_t {
} smartlist_t;
MOCK_DECL(smartlist_t *, smartlist_new, (void));
-MOCK_DECL(void, smartlist_free, (smartlist_t *sl));
+MOCK_DECL(void, smartlist_free_, (smartlist_t *sl));
+#define smartlist_free(sl) FREE_AND_NULL(smartlist_t, smartlist_free_, (sl))
+
void smartlist_clear(smartlist_t *sl);
void smartlist_add(smartlist_t *sl, void *element);
void smartlist_add_all(smartlist_t *sl, const smartlist_t *s2);
@@ -350,7 +352,7 @@ char *smartlist_join_strings2(smartlist_t *sl, const char *join,
void* prefix##set(maptype *map, keytype key, void *val); \
void* prefix##get(const maptype *map, keytype key); \
void* prefix##remove(maptype *map, keytype key); \
- MOCK_DECL(void, prefix##free, (maptype *map, void (*free_val)(void*))); \
+ MOCK_DECL(void, prefix##free_, (maptype *map, void (*free_val)(void*))); \
int prefix##isempty(const maptype *map); \
int prefix##size(const maptype *map); \
prefix##iter_t *prefix##iter_init(maptype *map); \
@@ -368,6 +370,16 @@ DECLARE_MAP_FNS(digestmap_t, const char *, digestmap_);
* table. */
DECLARE_MAP_FNS(digest256map_t, const uint8_t *, digest256map_);
+#define MAP_FREE_AND_NULL(maptype, map, fn) \
+ do { \
+ maptype ## _free_((map), (fn)); \
+ (map) = NULL; \
+ } while (0)
+
+#define strmap_free(map, fn) MAP_FREE_AND_NULL(strmap, (map), (fn))
+#define digestmap_free(map, fn) MAP_FREE_AND_NULL(digestmap, (map), (fn))
+#define digest256map_free(map, fn) MAP_FREE_AND_NULL(digest256map, (map), (fn))
+
#undef DECLARE_MAP_FNS
/** Iterates over the key-value pairs in a map <b>map</b> in order.
@@ -528,9 +540,9 @@ void* strmap_remove_lc(strmap_t *map, const char *key);
return (valtype*)digestmap_remove((digestmap_t*)map, key); \
} \
ATTR_UNUSED static inline void \
- prefix##f##ree(maptype *map, void (*free_val)(void*)) \
+ prefix##f##ree_(maptype *map, void (*free_val)(void*)) \
{ \
- digestmap_free((digestmap_t*)map, free_val); \
+ digestmap_free_((digestmap_t*)map, free_val); \
} \
ATTR_UNUSED static inline int \
prefix##isempty(maptype *map) \
@@ -614,10 +626,12 @@ bitarray_expand(bitarray_t *ba,
}
/** Free the bit array <b>ba</b>. */
static inline void
-bitarray_free(bitarray_t *ba)
+bitarray_free_(bitarray_t *ba)
{
tor_free(ba);
}
+#define bitarray_free(ba) FREE_AND_NULL(bitarray_t, bitarray_free_, (ba))
+
/** Set the <b>bit</b>th bit in <b>b</b> to 1. */
static inline void
bitarray_set(bitarray_t *b, int bit)
@@ -679,7 +693,8 @@ digestset_contains(const digestset_t *set, const char *digest)
#undef BIT
digestset_t *digestset_new(int max_elements);
-void digestset_free(digestset_t* set);
+void digestset_free_(digestset_t* set);
+#define digestset_free(set) FREE_AND_NULL(digestset_t, digestset_free_, (set))
/* These functions, given an <b>array</b> of <b>n_elements</b>, return the
* <b>nth</b> lowest element. <b>nth</b>=0 gives the lowest element;
diff --git a/src/common/crypto.c b/src/common/crypto.c
index 16536f3716..d85aca4004 100644
--- a/src/common/crypto.c
+++ b/src/common/crypto.c
@@ -28,6 +28,7 @@
#include "crypto_curve25519.h"
#include "crypto_ed25519.h"
#include "crypto_format.h"
+#include "crypto_rsa.h"
DISABLE_GCC_WARNING(redundant-decls)
@@ -82,80 +83,20 @@ ENABLE_GCC_WARNING(redundant-decls)
#include "keccak-tiny/keccak-tiny.h"
-#ifdef ANDROID
-/* Android's OpenSSL seems to have removed all of its Engine support. */
-#define DISABLE_ENGINES
-#endif
-
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) && \
- !defined(LIBRESSL_VERSION_NUMBER)
-/* OpenSSL as of 1.1.0pre4 has an "new" thread API, which doesn't require
- * seting up various callbacks.
- *
- * OpenSSL 1.1.0pre4 has a messed up `ERR_remove_thread_state()` prototype,
- * while the previous one was restored in pre5, and the function made a no-op
- * (along with a deprecated annotation, which produces a compiler warning).
- *
- * While it is possible to support all three versions of the thread API,
- * a version that existed only for one snapshot pre-release is kind of
- * pointless, so let's not.
- */
-#define NEW_THREAD_API
-#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) && ... */
-
/** Longest recognized */
#define MAX_DNS_LABEL_SIZE 63
/** Largest strong entropy request */
#define MAX_STRONGEST_RAND_SIZE 256
-#ifndef NEW_THREAD_API
-/** A number of preallocated mutexes for use by OpenSSL. */
-static tor_mutex_t **openssl_mutexes_ = NULL;
-/** How many mutexes have we allocated for use by OpenSSL? */
-static int n_openssl_mutexes_ = 0;
-#endif /* !defined(NEW_THREAD_API) */
-
-/** A public key, or a public/private key-pair. */
-struct crypto_pk_t
-{
- int refs; /**< reference count, so we don't have to copy keys */
- RSA *key; /**< The key itself */
-};
-
/** A structure to hold the first half (x, g^x) of a Diffie-Hellman handshake
* while we're waiting for the second.*/
struct crypto_dh_t {
DH *dh; /**< The openssl DH object */
};
-static int setup_openssl_threading(void);
static int tor_check_dh_key(int severity, const BIGNUM *bn);
-/** Return the number of bytes added by padding method <b>padding</b>.
- */
-static inline int
-crypto_get_rsa_padding_overhead(int padding)
-{
- switch (padding)
- {
- case RSA_PKCS1_OAEP_PADDING: return PKCS1_OAEP_PADDING_OVERHEAD;
- default: tor_assert(0); return -1; // LCOV_EXCL_LINE
- }
-}
-
-/** Given a padding method <b>padding</b>, return the correct OpenSSL constant.
- */
-static inline int
-crypto_get_rsa_padding(int padding)
-{
- switch (padding)
- {
- case PK_PKCS1_OAEP_PADDING: return RSA_PKCS1_OAEP_PADDING;
- default: tor_assert(0); return -1; // LCOV_EXCL_LINE
- }
-}
-
/** Boolean: has OpenSSL's crypto been initialized? */
static int crypto_early_initialized_ = 0;
@@ -220,52 +161,6 @@ try_load_engine(const char *path, const char *engine)
}
#endif /* !defined(DISABLE_ENGINES) */
-/* Returns a trimmed and human-readable version of an openssl version string
-* <b>raw_version</b>. They are usually in the form of 'OpenSSL 1.0.0b 10
-* May 2012' and this will parse them into a form similar to '1.0.0b' */
-static char *
-parse_openssl_version_str(const char *raw_version)
-{
- const char *end_of_version = NULL;
- /* The output should be something like "OpenSSL 1.0.0b 10 May 2012. Let's
- trim that down. */
- if (!strcmpstart(raw_version, "OpenSSL ")) {
- raw_version += strlen("OpenSSL ");
- end_of_version = strchr(raw_version, ' ');
- }
-
- if (end_of_version)
- return tor_strndup(raw_version,
- end_of_version-raw_version);
- else
- return tor_strdup(raw_version);
-}
-
-static char *crypto_openssl_version_str = NULL;
-/* Return a human-readable version of the run-time openssl version number. */
-const char *
-crypto_openssl_get_version_str(void)
-{
- if (crypto_openssl_version_str == NULL) {
- const char *raw_version = OpenSSL_version(OPENSSL_VERSION);
- crypto_openssl_version_str = parse_openssl_version_str(raw_version);
- }
- return crypto_openssl_version_str;
-}
-
-static char *crypto_openssl_header_version_str = NULL;
-/* Return a human-readable version of the compile-time openssl version
-* number. */
-const char *
-crypto_openssl_get_header_version_str(void)
-{
- if (crypto_openssl_header_version_str == NULL) {
- crypto_openssl_header_version_str =
- parse_openssl_version_str(OPENSSL_VERSION_TEXT);
- }
- return crypto_openssl_header_version_str;
-}
-
/** Make sure that openssl is using its default PRNG. Return 1 if we had to
* adjust it; 0 otherwise. */
STATIC int
@@ -283,11 +178,12 @@ crypto_force_rand_ssleay(void)
return 0;
}
+static int have_seeded_siphash = 0;
+
/** Set up the siphash key if we haven't already done so. */
int
crypto_init_siphash_key(void)
{
- static int have_seeded_siphash = 0;
struct sipkey key;
if (have_seeded_siphash)
return 0;
@@ -437,73 +333,6 @@ crypto_thread_cleanup(void)
#endif
}
-/** used internally: quicly validate a crypto_pk_t object as a private key.
- * Return 1 iff the public key is valid, 0 if obviously invalid.
- */
-static int
-crypto_pk_private_ok(const crypto_pk_t *k)
-{
-#ifdef OPENSSL_1_1_API
- if (!k || !k->key)
- return 0;
-
- const BIGNUM *p, *q;
- RSA_get0_factors(k->key, &p, &q);
- return p != NULL; /* XXX/yawning: Should we check q? */
-#else /* !(defined(OPENSSL_1_1_API)) */
- return k && k->key && k->key->p;
-#endif /* defined(OPENSSL_1_1_API) */
-}
-
-/** used by tortls.c: wrap an RSA* in a crypto_pk_t. */
-crypto_pk_t *
-crypto_new_pk_from_rsa_(RSA *rsa)
-{
- crypto_pk_t *env;
- tor_assert(rsa);
- env = tor_malloc(sizeof(crypto_pk_t));
- env->refs = 1;
- env->key = rsa;
- return env;
-}
-
-/** Helper, used by tor-gencert.c. Return the RSA from a
- * crypto_pk_t. */
-RSA *
-crypto_pk_get_rsa_(crypto_pk_t *env)
-{
- return env->key;
-}
-
-/** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t. Iff
- * private is set, include the private-key portion of the key. Return a valid
- * pointer on success, and NULL on failure. */
-MOCK_IMPL(EVP_PKEY *,
-crypto_pk_get_evp_pkey_,(crypto_pk_t *env, int private))
-{
- RSA *key = NULL;
- EVP_PKEY *pkey = NULL;
- tor_assert(env->key);
- if (private) {
- if (!(key = RSAPrivateKey_dup(env->key)))
- goto error;
- } else {
- if (!(key = RSAPublicKey_dup(env->key)))
- goto error;
- }
- if (!(pkey = EVP_PKEY_new()))
- goto error;
- if (!(EVP_PKEY_assign_RSA(pkey, key)))
- goto error;
- return pkey;
- error:
- if (pkey)
- EVP_PKEY_free(pkey);
- if (key)
- RSA_free(key);
- return NULL;
-}
-
/** Used by tortls.c: Get the DH* from a crypto_dh_t.
*/
DH *
@@ -512,38 +341,6 @@ crypto_dh_get_dh_(crypto_dh_t *dh)
return dh->dh;
}
-/** Allocate and return storage for a public key. The key itself will not yet
- * be set.
- */
-MOCK_IMPL(crypto_pk_t *,
-crypto_pk_new,(void))
-{
- RSA *rsa;
-
- rsa = RSA_new();
- tor_assert(rsa);
- return crypto_new_pk_from_rsa_(rsa);
-}
-
-/** Release a reference to an asymmetric key; when all the references
- * are released, free the key.
- */
-void
-crypto_pk_free(crypto_pk_t *env)
-{
- if (!env)
- return;
-
- if (--env->refs > 0)
- return;
- tor_assert(env->refs == 0);
-
- if (env->key)
- RSA_free(env->key);
-
- tor_free(env);
-}
-
/** Allocate and return a new symmetric cipher using the provided key and iv.
* The key is <b>bits</b> bits long; the IV is CIPHER_IV_LEN bytes. Both
* must be provided. Key length must be 128, 192, or 256 */
@@ -592,7 +389,7 @@ crypto_cipher_new(const char *key)
/** Free a symmetric cipher.
*/
void
-crypto_cipher_free(crypto_cipher_t *env)
+crypto_cipher_free_(crypto_cipher_t *env)
{
if (!env)
return;
@@ -602,548 +399,6 @@ crypto_cipher_free(crypto_cipher_t *env)
/* public key crypto */
-/** Generate a <b>bits</b>-bit new public/private keypair in <b>env</b>.
- * Return 0 on success, -1 on failure.
- */
-MOCK_IMPL(int,
-crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits))
-{
- tor_assert(env);
-
- if (env->key) {
- RSA_free(env->key);
- env->key = NULL;
- }
-
- {
- BIGNUM *e = BN_new();
- RSA *r = NULL;
- if (!e)
- goto done;
- if (! BN_set_word(e, 65537))
- goto done;
- r = RSA_new();
- if (!r)
- goto done;
- if (RSA_generate_key_ex(r, bits, e, NULL) == -1)
- goto done;
-
- env->key = r;
- r = NULL;
- done:
- if (e)
- BN_clear_free(e);
- if (r)
- RSA_free(r);
- }
-
- if (!env->key) {
- crypto_log_errors(LOG_WARN, "generating RSA key");
- return -1;
- }
-
- return 0;
-}
-
-/** A PEM callback that always reports a failure to get a password */
-static int
-pem_no_password_cb(char *buf, int size, int rwflag, void *u)
-{
- (void)buf;
- (void)size;
- (void)rwflag;
- (void)u;
- /* The openssl documentation says that a callback "must" return 0 if an
- * error occurred. But during the 1.1.1 series (commit c82c3462267afdbbaa5
- * they changed the interpretation so that 0 indicates an empty password and
- * -1 indicates an error. We want to reject any encrypted PEM buffers, so we
- * return -1. This will work on older OpenSSL versions and LibreSSL too. */
- return -1;
-}
-
-/** Read a PEM-encoded private key from the <b>len</b>-byte string <b>s</b>
- * into <b>env</b>. Return 0 on success, -1 on failure. If len is -1,
- * the string is nul-terminated.
- */
-int
-crypto_pk_read_private_key_from_string(crypto_pk_t *env,
- const char *s, ssize_t len)
-{
- BIO *b;
-
- tor_assert(env);
- tor_assert(s);
- tor_assert(len < INT_MAX && len < SSIZE_T_CEILING);
-
- /* Create a read-only memory BIO, backed by the string 's' */
- b = BIO_new_mem_buf((char*)s, (int)len);
- if (!b)
- return -1;
-
- if (env->key)
- RSA_free(env->key);
-
- env->key = PEM_read_bio_RSAPrivateKey(b,NULL,pem_no_password_cb,NULL);
-
- BIO_free(b);
-
- if (!env->key) {
- crypto_log_errors(LOG_WARN, "Error parsing private key");
- return -1;
- }
- return 0;
-}
-
-/** Read a PEM-encoded private key from the file named by
- * <b>keyfile</b> into <b>env</b>. Return 0 on success, -1 on failure.
- */
-int
-crypto_pk_read_private_key_from_filename(crypto_pk_t *env,
- const char *keyfile)
-{
- char *contents;
- int r;
-
- /* Read the file into a string. */
- contents = read_file_to_str(keyfile, 0, NULL);
- if (!contents) {
- log_warn(LD_CRYPTO, "Error reading private key from \"%s\"", keyfile);
- return -1;
- }
-
- /* Try to parse it. */
- r = crypto_pk_read_private_key_from_string(env, contents, -1);
- memwipe(contents, 0, strlen(contents));
- tor_free(contents);
- if (r)
- return -1; /* read_private_key_from_string already warned, so we don't.*/
-
- /* Make sure it's valid. */
- if (crypto_pk_check_key(env) <= 0)
- return -1;
-
- return 0;
-}
-
-/** Helper function to implement crypto_pk_write_*_key_to_string. Return 0 on
- * success, -1 on failure. */
-static int
-crypto_pk_write_key_to_string_impl(crypto_pk_t *env, char **dest,
- size_t *len, int is_public)
-{
- BUF_MEM *buf;
- BIO *b;
- int r;
-
- tor_assert(env);
- tor_assert(env->key);
- tor_assert(dest);
-
- b = BIO_new(BIO_s_mem()); /* Create a memory BIO */
- if (!b)
- return -1;
-
- /* Now you can treat b as if it were a file. Just use the
- * PEM_*_bio_* functions instead of the non-bio variants.
- */
- if (is_public)
- r = PEM_write_bio_RSAPublicKey(b, env->key);
- else
- r = PEM_write_bio_RSAPrivateKey(b, env->key, NULL,NULL,0,NULL,NULL);
-
- if (!r) {
- crypto_log_errors(LOG_WARN, "writing RSA key to string");
- BIO_free(b);
- return -1;
- }
-
- BIO_get_mem_ptr(b, &buf);
-
- *dest = tor_malloc(buf->length+1);
- memcpy(*dest, buf->data, buf->length);
- (*dest)[buf->length] = 0; /* nul terminate it */
- *len = buf->length;
-
- BIO_free(b);
-
- return 0;
-}
-
-/** PEM-encode the public key portion of <b>env</b> and write it to a
- * newly allocated string. On success, set *<b>dest</b> to the new
- * string, *<b>len</b> to the string's length, and return 0. On
- * failure, return -1.
- */
-int
-crypto_pk_write_public_key_to_string(crypto_pk_t *env, char **dest,
- size_t *len)
-{
- return crypto_pk_write_key_to_string_impl(env, dest, len, 1);
-}
-
-/** PEM-encode the private key portion of <b>env</b> and write it to a
- * newly allocated string. On success, set *<b>dest</b> to the new
- * string, *<b>len</b> to the string's length, and return 0. On
- * failure, return -1.
- */
-int
-crypto_pk_write_private_key_to_string(crypto_pk_t *env, char **dest,
- size_t *len)
-{
- return crypto_pk_write_key_to_string_impl(env, dest, len, 0);
-}
-
-/** Read a PEM-encoded public key from the first <b>len</b> characters of
- * <b>src</b>, and store the result in <b>env</b>. Return 0 on success, -1 on
- * failure.
- */
-int
-crypto_pk_read_public_key_from_string(crypto_pk_t *env, const char *src,
- size_t len)
-{
- BIO *b;
-
- tor_assert(env);
- tor_assert(src);
- tor_assert(len<INT_MAX);
-
- b = BIO_new(BIO_s_mem()); /* Create a memory BIO */
- if (!b)
- return -1;
-
- BIO_write(b, src, (int)len);
-
- if (env->key)
- RSA_free(env->key);
- env->key = PEM_read_bio_RSAPublicKey(b, NULL, pem_no_password_cb, NULL);
- BIO_free(b);
- if (!env->key) {
- crypto_log_errors(LOG_WARN, "reading public key from string");
- return -1;
- }
-
- return 0;
-}
-
-/** Write the private key from <b>env</b> into the file named by <b>fname</b>,
- * PEM-encoded. Return 0 on success, -1 on failure.
- */
-int
-crypto_pk_write_private_key_to_filename(crypto_pk_t *env,
- const char *fname)
-{
- BIO *bio;
- char *cp;
- long len;
- char *s;
- int r;
-
- tor_assert(crypto_pk_private_ok(env));
-
- if (!(bio = BIO_new(BIO_s_mem())))
- return -1;
- if (PEM_write_bio_RSAPrivateKey(bio, env->key, NULL,NULL,0,NULL,NULL)
- == 0) {
- crypto_log_errors(LOG_WARN, "writing private key");
- BIO_free(bio);
- return -1;
- }
- len = BIO_get_mem_data(bio, &cp);
- tor_assert(len >= 0);
- s = tor_malloc(len+1);
- memcpy(s, cp, len);
- s[len]='\0';
- r = write_str_to_file(fname, s, 0);
- BIO_free(bio);
- memwipe(s, 0, strlen(s));
- tor_free(s);
- return r;
-}
-
-/** Return true iff <b>env</b> has a valid key.
- */
-int
-crypto_pk_check_key(crypto_pk_t *env)
-{
- int r;
- tor_assert(env);
-
- r = RSA_check_key(env->key);
- if (r <= 0)
- crypto_log_errors(LOG_WARN,"checking RSA key");
- return r;
-}
-
-/** Return true iff <b>key</b> contains the private-key portion of the RSA
- * key. */
-int
-crypto_pk_key_is_private(const crypto_pk_t *key)
-{
- tor_assert(key);
- return crypto_pk_private_ok(key);
-}
-
-/** Return true iff <b>env</b> contains a public key whose public exponent
- * equals 65537.
- */
-int
-crypto_pk_public_exponent_ok(crypto_pk_t *env)
-{
- tor_assert(env);
- tor_assert(env->key);
-
- const BIGNUM *e;
-
-#ifdef OPENSSL_1_1_API
- const BIGNUM *n, *d;
- RSA_get0_key(env->key, &n, &e, &d);
-#else
- e = env->key->e;
-#endif /* defined(OPENSSL_1_1_API) */
- return BN_is_word(e, 65537);
-}
-
-/** Compare the public-key components of a and b. Return less than 0
- * if a\<b, 0 if a==b, and greater than 0 if a\>b. A NULL key is
- * considered to be less than all non-NULL keys, and equal to itself.
- *
- * Note that this may leak information about the keys through timing.
- */
-int
-crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b)
-{
- int result;
- char a_is_non_null = (a != NULL) && (a->key != NULL);
- char b_is_non_null = (b != NULL) && (b->key != NULL);
- char an_argument_is_null = !a_is_non_null | !b_is_non_null;
-
- result = tor_memcmp(&a_is_non_null, &b_is_non_null, sizeof(a_is_non_null));
- if (an_argument_is_null)
- return result;
-
- const BIGNUM *a_n, *a_e;
- const BIGNUM *b_n, *b_e;
-
-#ifdef OPENSSL_1_1_API
- const BIGNUM *a_d, *b_d;
- RSA_get0_key(a->key, &a_n, &a_e, &a_d);
- RSA_get0_key(b->key, &b_n, &b_e, &b_d);
-#else
- a_n = a->key->n;
- a_e = a->key->e;
- b_n = b->key->n;
- b_e = b->key->e;
-#endif /* defined(OPENSSL_1_1_API) */
-
- tor_assert(a_n != NULL && a_e != NULL);
- tor_assert(b_n != NULL && b_e != NULL);
-
- result = BN_cmp(a_n, b_n);
- if (result)
- return result;
- return BN_cmp(a_e, b_e);
-}
-
-/** Compare the public-key components of a and b. Return non-zero iff
- * a==b. A NULL key is considered to be distinct from all non-NULL
- * keys, and equal to itself.
- *
- * Note that this may leak information about the keys through timing.
- */
-int
-crypto_pk_eq_keys(const crypto_pk_t *a, const crypto_pk_t *b)
-{
- return (crypto_pk_cmp_keys(a, b) == 0);
-}
-
-/** Return the size of the public key modulus in <b>env</b>, in bytes. */
-size_t
-crypto_pk_keysize(const crypto_pk_t *env)
-{
- tor_assert(env);
- tor_assert(env->key);
-
- return (size_t) RSA_size((RSA*)env->key);
-}
-
-/** Return the size of the public key modulus of <b>env</b>, in bits. */
-int
-crypto_pk_num_bits(crypto_pk_t *env)
-{
- tor_assert(env);
- tor_assert(env->key);
-
-#ifdef OPENSSL_1_1_API
- /* It's so stupid that there's no other way to check that n is valid
- * before calling RSA_bits().
- */
- const BIGNUM *n, *e, *d;
- RSA_get0_key(env->key, &n, &e, &d);
- tor_assert(n != NULL);
-
- return RSA_bits(env->key);
-#else /* !(defined(OPENSSL_1_1_API)) */
- tor_assert(env->key->n);
- return BN_num_bits(env->key->n);
-#endif /* defined(OPENSSL_1_1_API) */
-}
-
-/** Increase the reference count of <b>env</b>, and return it.
- */
-crypto_pk_t *
-crypto_pk_dup_key(crypto_pk_t *env)
-{
- tor_assert(env);
- tor_assert(env->key);
-
- env->refs++;
- return env;
-}
-
-#ifdef TOR_UNIT_TESTS
-/** For testing: replace dest with src. (Dest must have a refcount
- * of 1) */
-void
-crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src)
-{
- tor_assert(dest);
- tor_assert(dest->refs == 1);
- tor_assert(src);
- RSA_free(dest->key);
- dest->key = RSAPrivateKey_dup(src->key);
-}
-#endif /* defined(TOR_UNIT_TESTS) */
-
-/** Make a real honest-to-goodness copy of <b>env</b>, and return it.
- * Returns NULL on failure. */
-crypto_pk_t *
-crypto_pk_copy_full(crypto_pk_t *env)
-{
- RSA *new_key;
- int privatekey = 0;
- tor_assert(env);
- tor_assert(env->key);
-
- if (crypto_pk_private_ok(env)) {
- new_key = RSAPrivateKey_dup(env->key);
- privatekey = 1;
- } else {
- new_key = RSAPublicKey_dup(env->key);
- }
- if (!new_key) {
- /* LCOV_EXCL_START
- *
- * We can't cause RSA*Key_dup() to fail, so we can't really test this.
- */
- log_err(LD_CRYPTO, "Unable to duplicate a %s key: openssl failed.",
- privatekey?"private":"public");
- crypto_log_errors(LOG_ERR,
- privatekey ? "Duplicating a private key" :
- "Duplicating a public key");
- tor_fragile_assert();
- return NULL;
- /* LCOV_EXCL_STOP */
- }
-
- return crypto_new_pk_from_rsa_(new_key);
-}
-
-/** Encrypt <b>fromlen</b> bytes from <b>from</b> with the public key
- * in <b>env</b>, using the padding method <b>padding</b>. On success,
- * write the result to <b>to</b>, and return the number of bytes
- * written. On failure, return -1.
- *
- * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
- * at least the length of the modulus of <b>env</b>.
- */
-int
-crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen,
- const char *from, size_t fromlen, int padding)
-{
- int r;
- tor_assert(env);
- tor_assert(from);
- tor_assert(to);
- tor_assert(fromlen<INT_MAX);
- tor_assert(tolen >= crypto_pk_keysize(env));
-
- r = RSA_public_encrypt((int)fromlen,
- (unsigned char*)from, (unsigned char*)to,
- env->key, crypto_get_rsa_padding(padding));
- if (r<0) {
- crypto_log_errors(LOG_WARN, "performing RSA encryption");
- return -1;
- }
- return r;
-}
-
-/** Decrypt <b>fromlen</b> bytes from <b>from</b> with the private key
- * in <b>env</b>, using the padding method <b>padding</b>. On success,
- * write the result to <b>to</b>, and return the number of bytes
- * written. On failure, return -1.
- *
- * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
- * at least the length of the modulus of <b>env</b>.
- */
-int
-crypto_pk_private_decrypt(crypto_pk_t *env, char *to,
- size_t tolen,
- const char *from, size_t fromlen,
- int padding, int warnOnFailure)
-{
- int r;
- tor_assert(env);
- tor_assert(from);
- tor_assert(to);
- tor_assert(env->key);
- tor_assert(fromlen<INT_MAX);
- tor_assert(tolen >= crypto_pk_keysize(env));
- if (!crypto_pk_key_is_private(env))
- /* Not a private key */
- return -1;
-
- r = RSA_private_decrypt((int)fromlen,
- (unsigned char*)from, (unsigned char*)to,
- env->key, crypto_get_rsa_padding(padding));
-
- if (r<0) {
- crypto_log_errors(warnOnFailure?LOG_WARN:LOG_DEBUG,
- "performing RSA decryption");
- return -1;
- }
- return r;
-}
-
-/** Check the signature in <b>from</b> (<b>fromlen</b> bytes long) with the
- * public key in <b>env</b>, using PKCS1 padding. On success, write the
- * signed data to <b>to</b>, and return the number of bytes written.
- * On failure, return -1.
- *
- * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
- * at least the length of the modulus of <b>env</b>.
- */
-MOCK_IMPL(int,
-crypto_pk_public_checksig,(const crypto_pk_t *env, char *to,
- size_t tolen,
- const char *from, size_t fromlen))
-{
- int r;
- tor_assert(env);
- tor_assert(from);
- tor_assert(to);
- tor_assert(fromlen < INT_MAX);
- tor_assert(tolen >= crypto_pk_keysize(env));
- r = RSA_public_decrypt((int)fromlen,
- (unsigned char*)from, (unsigned char*)to,
- env->key, RSA_PKCS1_PADDING);
-
- if (r<0) {
- crypto_log_errors(LOG_INFO, "checking RSA signature");
- return -1;
- }
- return r;
-}
-
/** Check a siglen-byte long signature at <b>sig</b> against
* <b>datalen</b> bytes of data at <b>data</b>, using the public key
* in <b>env</b>. Return 0 if <b>sig</b> is a correct signature for
@@ -1187,38 +442,6 @@ crypto_pk_public_checksig_digest,(crypto_pk_t *env, const char *data,
return 0;
}
-/** Sign <b>fromlen</b> bytes of data from <b>from</b> with the private key in
- * <b>env</b>, using PKCS1 padding. On success, write the signature to
- * <b>to</b>, and return the number of bytes written. On failure, return
- * -1.
- *
- * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
- * at least the length of the modulus of <b>env</b>.
- */
-int
-crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen,
- const char *from, size_t fromlen)
-{
- int r;
- tor_assert(env);
- tor_assert(from);
- tor_assert(to);
- tor_assert(fromlen < INT_MAX);
- tor_assert(tolen >= crypto_pk_keysize(env));
- if (!crypto_pk_key_is_private(env))
- /* Not a private key */
- return -1;
-
- r = RSA_private_encrypt((int)fromlen,
- (unsigned char*)from, (unsigned char*)to,
- (RSA*)env->key, RSA_PKCS1_PADDING);
- if (r<0) {
- crypto_log_errors(LOG_WARN, "generating RSA signature");
- return -1;
- }
- return r;
-}
-
/** Compute a SHA1 digest of <b>fromlen</b> bytes of data stored at
* <b>from</b>; sign the data with the private key in <b>env</b>, and
* store it in <b>to</b>. Return the number of bytes written on
@@ -1382,51 +605,6 @@ crypto_pk_obsolete_private_hybrid_decrypt(crypto_pk_t *env,
return -1;
}
-/** ASN.1-encode the public portion of <b>pk</b> into <b>dest</b>.
- * Return -1 on error, or the number of characters used on success.
- */
-int
-crypto_pk_asn1_encode(crypto_pk_t *pk, char *dest, size_t dest_len)
-{
- int len;
- unsigned char *buf = NULL;
-
- len = i2d_RSAPublicKey(pk->key, &buf);
- if (len < 0 || buf == NULL)
- return -1;
-
- if ((size_t)len > dest_len || dest_len > SIZE_T_CEILING) {
- OPENSSL_free(buf);
- return -1;
- }
- /* We don't encode directly into 'dest', because that would be illegal
- * type-punning. (C99 is smarter than me, C99 is smarter than me...)
- */
- memcpy(dest,buf,len);
- OPENSSL_free(buf);
- return len;
-}
-
-/** Decode an ASN.1-encoded public key from <b>str</b>; return the result on
- * success and NULL on failure.
- */
-crypto_pk_t *
-crypto_pk_asn1_decode(const char *str, size_t len)
-{
- RSA *rsa;
- unsigned char *buf;
- const unsigned char *cp;
- cp = buf = tor_malloc(len);
- memcpy(buf,str,len);
- rsa = d2i_RSAPublicKey(NULL, &cp, len);
- tor_free(buf);
- if (!rsa) {
- crypto_log_errors(LOG_WARN,"decoding public key");
- return NULL;
- }
- return crypto_new_pk_from_rsa_(rsa);
-}
-
/** Given a private or public key <b>pk</b>, put a SHA1 hash of the
* public key into <b>digest_out</b> (must have DIGEST_LEN bytes of space).
* Return 0 on success, -1 on failure.
@@ -1434,18 +612,24 @@ crypto_pk_asn1_decode(const char *str, size_t len)
int
crypto_pk_get_digest(const crypto_pk_t *pk, char *digest_out)
{
- unsigned char *buf = NULL;
+ char *buf;
+ size_t buflen;
int len;
+ int rv = -1;
- len = i2d_RSAPublicKey((RSA*)pk->key, &buf);
- if (len < 0 || buf == NULL)
- return -1;
- if (crypto_digest(digest_out, (char*)buf, len) < 0) {
- OPENSSL_free(buf);
- return -1;
- }
- OPENSSL_free(buf);
- return 0;
+ buflen = crypto_pk_keysize(pk)*2;
+ buf = tor_malloc(buflen);
+ len = crypto_pk_asn1_encode(pk, buf, buflen);
+ if (len < 0)
+ goto done;
+
+ if (crypto_digest(digest_out, buf, len) < 0)
+ goto done;
+
+ rv = 0;
+ done:
+ tor_free(buf);
+ return rv;
}
/** Compute all digests of the DER encoding of <b>pk</b>, and store them
@@ -1453,18 +637,24 @@ crypto_pk_get_digest(const crypto_pk_t *pk, char *digest_out)
int
crypto_pk_get_common_digests(crypto_pk_t *pk, common_digests_t *digests_out)
{
- unsigned char *buf = NULL;
+ char *buf;
+ size_t buflen;
int len;
+ int rv = -1;
- len = i2d_RSAPublicKey(pk->key, &buf);
- if (len < 0 || buf == NULL)
- return -1;
- if (crypto_common_digests(digests_out, (char*)buf, len) < 0) {
- OPENSSL_free(buf);
- return -1;
- }
- OPENSSL_free(buf);
- return 0;
+ buflen = crypto_pk_keysize(pk)*2;
+ buf = tor_malloc(buflen);
+ len = crypto_pk_asn1_encode(pk, buf, buflen);
+ if (len < 0)
+ goto done;
+
+ if (crypto_common_digests(digests_out, (char*)buf, len) < 0)
+ goto done;
+
+ rv = 0;
+ done:
+ tor_free(buf);
+ return rv;
}
/** Copy <b>in</b> to the <b>outlen</b>-byte buffer <b>out</b>, adding spaces
@@ -1487,127 +677,6 @@ crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in)
*out = '\0';
}
-/** Given a private or public key <b>pk</b>, put a fingerprint of the
- * public key into <b>fp_out</b> (must have at least FINGERPRINT_LEN+1 bytes of
- * space). Return 0 on success, -1 on failure.
- *
- * Fingerprints are computed as the SHA1 digest of the ASN.1 encoding
- * of the public key, converted to hexadecimal, in upper case, with a
- * space after every four digits.
- *
- * If <b>add_space</b> is false, omit the spaces.
- */
-int
-crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out, int add_space)
-{
- char digest[DIGEST_LEN];
- char hexdigest[HEX_DIGEST_LEN+1];
- if (crypto_pk_get_digest(pk, digest)) {
- return -1;
- }
- base16_encode(hexdigest,sizeof(hexdigest),digest,DIGEST_LEN);
- if (add_space) {
- crypto_add_spaces_to_fp(fp_out, FINGERPRINT_LEN+1, hexdigest);
- } else {
- strncpy(fp_out, hexdigest, HEX_DIGEST_LEN+1);
- }
- return 0;
-}
-
-/** Given a private or public key <b>pk</b>, put a hashed fingerprint of
- * the public key into <b>fp_out</b> (must have at least FINGERPRINT_LEN+1
- * bytes of space). Return 0 on success, -1 on failure.
- *
- * Hashed fingerprints are computed as the SHA1 digest of the SHA1 digest
- * of the ASN.1 encoding of the public key, converted to hexadecimal, in
- * upper case.
- */
-int
-crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out)
-{
- char digest[DIGEST_LEN], hashed_digest[DIGEST_LEN];
- if (crypto_pk_get_digest(pk, digest)) {
- return -1;
- }
- if (crypto_digest(hashed_digest, digest, DIGEST_LEN) < 0) {
- return -1;
- }
- base16_encode(fp_out, FINGERPRINT_LEN + 1, hashed_digest, DIGEST_LEN);
- return 0;
-}
-
-/** Given a crypto_pk_t <b>pk</b>, allocate a new buffer containing the
- * Base64 encoding of the DER representation of the private key as a NUL
- * terminated string, and return it via <b>priv_out</b>. Return 0 on
- * sucess, -1 on failure.
- *
- * It is the caller's responsibility to sanitize and free the resulting buffer.
- */
-int
-crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out)
-{
- unsigned char *der = NULL;
- int der_len;
- int ret = -1;
-
- *priv_out = NULL;
-
- der_len = i2d_RSAPrivateKey(pk->key, &der);
- if (der_len < 0 || der == NULL)
- return ret;
-
- size_t priv_len = base64_encode_size(der_len, 0) + 1;
- char *priv = tor_malloc_zero(priv_len);
- if (base64_encode(priv, priv_len, (char *)der, der_len, 0) >= 0) {
- *priv_out = priv;
- ret = 0;
- } else {
- tor_free(priv);
- }
-
- memwipe(der, 0, der_len);
- OPENSSL_free(der);
- return ret;
-}
-
-/** Given a string containing the Base64 encoded DER representation of the
- * private key <b>str</b>, decode and return the result on success, or NULL
- * on failure.
- */
-crypto_pk_t *
-crypto_pk_base64_decode(const char *str, size_t len)
-{
- crypto_pk_t *pk = NULL;
-
- char *der = tor_malloc_zero(len + 1);
- int der_len = base64_decode(der, len, str, len);
- if (der_len <= 0) {
- log_warn(LD_CRYPTO, "Stored RSA private key seems corrupted (base64).");
- goto out;
- }
-
- const unsigned char *dp = (unsigned char*)der; /* Shut the compiler up. */
- RSA *rsa = d2i_RSAPrivateKey(NULL, &dp, der_len);
- if (!rsa) {
- crypto_log_errors(LOG_WARN, "decoding private key");
- goto out;
- }
-
- pk = crypto_new_pk_from_rsa_(rsa);
-
- /* Make sure it's valid. */
- if (crypto_pk_check_key(pk) <= 0) {
- crypto_pk_free(pk);
- pk = NULL;
- goto out;
- }
-
- out:
- memwipe(der, 0, len + 1);
- tor_free(der);
- return pk;
-}
-
/* symmetric crypto */
/** Encrypt <b>fromlen</b> bytes from <b>from</b> using the cipher
@@ -1982,7 +1051,7 @@ crypto_digest512_new(digest_algorithm_t algorithm)
/** Deallocate a digest object.
*/
void
-crypto_digest_free(crypto_digest_t *digest)
+crypto_digest_free_(crypto_digest_t *digest)
{
if (!digest)
return;
@@ -2229,7 +1298,7 @@ crypto_xof_squeeze_bytes(crypto_xof_t *xof, uint8_t *out, size_t len)
/** Cleanse and deallocate a XOF object. */
void
-crypto_xof_free(crypto_xof_t *xof)
+crypto_xof_free_(crypto_xof_t *xof)
{
if (!xof)
return;
@@ -2782,7 +1851,7 @@ crypto_expand_key_material_rfc5869_sha256(
/** Free a DH key exchange object.
*/
void
-crypto_dh_free(crypto_dh_t *dh)
+crypto_dh_free_(crypto_dh_t *dh)
{
if (!dh)
return;
@@ -2822,6 +1891,12 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len)
{
tor_assert(out_len <= MAX_STRONGEST_RAND_SIZE);
+ /* We only log at notice-level here because in the case that this function
+ * fails the crypto_strongest_rand_raw() caller will log with a warning-level
+ * message and let crypto_strongest_rand() error out and finally terminating
+ * Tor with an assertion error.
+ */
+
#ifdef TOR_UNIT_TESTS
if (break_strongest_rng_syscall)
return -1;
@@ -2834,21 +1909,21 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len)
if (!provider_set) {
if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT)) {
- log_warn(LD_CRYPTO, "Can't get CryptoAPI provider [1]");
+ log_notice(LD_CRYPTO, "Unable to set Windows CryptoAPI provider [1].");
return -1;
}
provider_set = 1;
}
if (!CryptGenRandom(provider, out_len, out)) {
- log_warn(LD_CRYPTO, "Can't get entropy from CryptoAPI.");
+ log_notice(LD_CRYPTO, "Unable get entropy from the Windows CryptoAPI.");
return -1;
}
return 0;
#elif defined(__linux__) && defined(SYS_getrandom)
- static int getrandom_works = 1; /* Be optimitic about our chances... */
+ static int getrandom_works = 1; /* Be optimistic about our chances... */
- /* getrandom() isn't as straight foward as getentropy(), and has
+ /* getrandom() isn't as straightforward as getentropy(), and has
* no glibc wrapper.
*
* As far as I can tell from getrandom(2) and the source code, the
@@ -2861,7 +1936,7 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len)
*
* We optimistically assume that getrandom() is available and functional
* because it is the way of the future, and 2 branch mispredicts pale in
- * comparision to the overheads involved with failing to open
+ * comparison to the overheads involved with failing to open
* /dev/srandom followed by opening and reading from /dev/urandom.
*/
if (PREDICT_LIKELY(getrandom_works)) {
@@ -2882,13 +1957,15 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len)
/* Useful log message for errno. */
if (errno == ENOSYS) {
- log_warn(LD_CRYPTO, "Can't get entropy from getrandom()."
- " You are running a version of Tor built to support"
- " getrandom(), but the kernel doesn't implement this"
- " function--probably because it is too old?");
+ log_notice(LD_CRYPTO, "Can't get entropy from getrandom()."
+ " You are running a version of Tor built to support"
+ " getrandom(), but the kernel doesn't implement this"
+ " function--probably because it is too old?"
+ " Trying fallback method instead.");
} else {
- log_warn(LD_CRYPTO, "Can't get entropy from getrandom(): %s.",
- strerror(errno));
+ log_notice(LD_CRYPTO, "Can't get entropy from getrandom(): %s."
+ " Trying fallback method instead.",
+ strerror(errno));
}
getrandom_works = 0; /* Don't bother trying again. */
@@ -2940,7 +2017,7 @@ crypto_strongest_rand_fallback(uint8_t *out, size_t out_len)
size_t n;
for (i = 0; filenames[i]; ++i) {
- log_debug(LD_FS, "Considering %s for entropy", filenames[i]);
+ log_debug(LD_FS, "Considering %s as entropy source", filenames[i]);
fd = open(sandbox_intern_string(filenames[i]), O_RDONLY, 0);
if (fd<0) continue;
log_info(LD_CRYPTO, "Reading entropy from \"%s\"", filenames[i]);
@@ -2949,9 +2026,10 @@ crypto_strongest_rand_fallback(uint8_t *out, size_t out_len)
if (n != out_len) {
/* LCOV_EXCL_START
* We can't make /dev/foorandom actually fail. */
- log_warn(LD_CRYPTO,
- "Error reading from entropy source (read only %lu bytes).",
- (unsigned long)n);
+ log_notice(LD_CRYPTO,
+ "Error reading from entropy source %s (read only %lu bytes).",
+ filenames[i],
+ (unsigned long)n);
return -1;
/* LCOV_EXCL_STOP */
}
@@ -3352,110 +2430,7 @@ memwipe(void *mem, uint8_t byte, size_t sz)
memset(mem, byte, sz);
}
-#ifndef OPENSSL_THREADS
-#error OpenSSL has been built without thread support. Tor requires an \
- OpenSSL library with thread support enabled.
-#endif
-
-#ifndef NEW_THREAD_API
-/** Helper: OpenSSL uses this callback to manipulate mutexes. */
-static void
-openssl_locking_cb_(int mode, int n, const char *file, int line)
-{
- (void)file;
- (void)line;
- if (!openssl_mutexes_)
- /* This is not a really good fix for the
- * "release-freed-lock-from-separate-thread-on-shutdown" problem, but
- * it can't hurt. */
- return;
- if (mode & CRYPTO_LOCK)
- tor_mutex_acquire(openssl_mutexes_[n]);
- else
- tor_mutex_release(openssl_mutexes_[n]);
-}
-
-static void
-tor_set_openssl_thread_id(CRYPTO_THREADID *threadid)
-{
- CRYPTO_THREADID_set_numeric(threadid, tor_get_thread_id());
-}
-#endif /* !defined(NEW_THREAD_API) */
-
-#if 0
-/* This code is disabled, because OpenSSL never actually uses these callbacks.
- */
-
-/** OpenSSL helper type: wraps a Tor mutex so that OpenSSL can use it
- * as a lock. */
-struct CRYPTO_dynlock_value {
- tor_mutex_t *lock;
-};
-
-/** OpenSSL callback function to allocate a lock: see CRYPTO_set_dynlock_*
- * documentation in OpenSSL's docs for more info. */
-static struct CRYPTO_dynlock_value *
-openssl_dynlock_create_cb_(const char *file, int line)
-{
- struct CRYPTO_dynlock_value *v;
- (void)file;
- (void)line;
- v = tor_malloc(sizeof(struct CRYPTO_dynlock_value));
- v->lock = tor_mutex_new();
- return v;
-}
-
-/** OpenSSL callback function to acquire or release a lock: see
- * CRYPTO_set_dynlock_* documentation in OpenSSL's docs for more info. */
-static void
-openssl_dynlock_lock_cb_(int mode, struct CRYPTO_dynlock_value *v,
- const char *file, int line)
-{
- (void)file;
- (void)line;
- if (mode & CRYPTO_LOCK)
- tor_mutex_acquire(v->lock);
- else
- tor_mutex_release(v->lock);
-}
-
-/** OpenSSL callback function to free a lock: see CRYPTO_set_dynlock_*
- * documentation in OpenSSL's docs for more info. */
-static void
-openssl_dynlock_destroy_cb_(struct CRYPTO_dynlock_value *v,
- const char *file, int line)
-{
- (void)file;
- (void)line;
- tor_mutex_free(v->lock);
- tor_free(v);
-}
-#endif /* 0 */
-
/** @{ */
-/** Helper: Construct mutexes, and set callbacks to help OpenSSL handle being
- * multithreaded. Returns 0. */
-static int
-setup_openssl_threading(void)
-{
-#ifndef NEW_THREAD_API
- int i;
- int n = CRYPTO_num_locks();
- n_openssl_mutexes_ = n;
- openssl_mutexes_ = tor_calloc(n, sizeof(tor_mutex_t *));
- for (i=0; i < n; ++i)
- openssl_mutexes_[i] = tor_mutex_new();
- CRYPTO_set_locking_callback(openssl_locking_cb_);
- CRYPTO_THREADID_set_callback(tor_set_openssl_thread_id);
-#endif /* !defined(NEW_THREAD_API) */
-#if 0
- CRYPTO_set_dynlock_create_callback(openssl_dynlock_create_cb_);
- CRYPTO_set_dynlock_lock_callback(openssl_dynlock_lock_cb_);
- CRYPTO_set_dynlock_destroy_callback(openssl_dynlock_destroy_cb_);
-#endif
- return 0;
-}
-
/** Uninitialize the crypto library. Return 0 on success. Does not detect
* failure.
*/
@@ -3475,6 +2450,8 @@ crypto_global_cleanup(void)
if (dh_param_g)
BN_clear_free(dh_param_g);
+ dh_param_p = dh_param_p_tls = dh_param_g = NULL;
+
#ifndef DISABLE_ENGINES
ENGINE_cleanup();
#endif
@@ -3482,22 +2459,13 @@ crypto_global_cleanup(void)
CONF_modules_unload(1);
CRYPTO_cleanup_all_ex_data();
-#ifndef NEW_THREAD_API
- if (n_openssl_mutexes_) {
- int n = n_openssl_mutexes_;
- tor_mutex_t **ms = openssl_mutexes_;
- int i;
- openssl_mutexes_ = NULL;
- n_openssl_mutexes_ = 0;
- for (i=0;i<n;++i) {
- tor_mutex_free(ms[i]);
- }
- tor_free(ms);
- }
-#endif /* !defined(NEW_THREAD_API) */
+ crypto_openssl_free_all();
+
+ crypto_early_initialized_ = 0;
+ crypto_global_initialized_ = 0;
+ have_seeded_siphash = 0;
+ siphash_unset_global_key();
- tor_free(crypto_openssl_version_str);
- tor_free(crypto_openssl_header_version_str);
return 0;
}
diff --git a/src/common/crypto.h b/src/common/crypto.h
index f9aeeee2c0..a9c8837b9e 100644
--- a/src/common/crypto.h
+++ b/src/common/crypto.h
@@ -19,42 +19,11 @@
#include "torint.h"
#include "testsupport.h"
#include "compat.h"
+#include "util.h"
+#include "crypto_rsa.h"
-#include <openssl/engine.h>
#include "keccak-tiny/keccak-tiny.h"
-/*
- Macro to create an arbitrary OpenSSL version number as used by
- OPENSSL_VERSION_NUMBER or SSLeay(), since the actual numbers are a bit hard
- to read.
-
- Don't use this directly, instead use one of the other OPENSSL_V macros
- below.
-
- The format is: 4 bits major, 8 bits minor, 8 bits fix, 8 bits patch, 4 bit
- status.
- */
-#define OPENSSL_VER(a,b,c,d,e) \
- (((a)<<28) | \
- ((b)<<20) | \
- ((c)<<12) | \
- ((d)<< 4) | \
- (e))
-/** An openssl release number. For example, OPENSSL_V(0,9,8,'j') is the
- * version for the released version of 0.9.8j */
-#define OPENSSL_V(a,b,c,d) \
- OPENSSL_VER((a),(b),(c),(d)-'a'+1,0xf)
-/** An openssl release number for the first release in the series. For
- * example, OPENSSL_V_NOPATCH(1,0,0) is the first released version of OpenSSL
- * 1.0.0. */
-#define OPENSSL_V_NOPATCH(a,b,c) \
- OPENSSL_VER((a),(b),(c),0,0xf)
-/** The first version that would occur for any alpha or beta in an openssl
- * series. For example, OPENSSL_V_SERIES(0,9,8) is greater than any released
- * 0.9.7, and less than any released 0.9.8. */
-#define OPENSSL_V_SERIES(a,b,c) \
- OPENSSL_VER((a),(b),(c),0,0)
-
/** Length of the output of our message digest. */
#define DIGEST_LEN 20
/** Length of the output of our second (improved) message digests. (For now
@@ -68,8 +37,6 @@
#define CIPHER_IV_LEN 16
/** Length of our symmetric cipher's keys of 256-bit. */
#define CIPHER256_KEY_LEN 32
-/** Length of our public keys. */
-#define PK_BYTES (1024/8)
/** Length of our DH keys. */
#define DH_BYTES (1024/8)
@@ -86,12 +53,6 @@
* signs removed. */
#define BASE64_DIGEST512_LEN 86
-/** Constant used to indicate OAEP padding for public-key encryption */
-#define PK_PKCS1_OAEP_PADDING 60002
-
-/** Number of bytes added for PKCS1-OAEP padding. */
-#define PKCS1_OAEP_PADDING_OVERHEAD 42
-
/** Length of encoded public key fingerprints, including space; but not
* including terminating NUL. */
#define FINGERPRINT_LEN 49
@@ -124,15 +85,12 @@ typedef struct {
char d[N_COMMON_DIGEST_ALGORITHMS][DIGEST256_LEN];
} common_digests_t;
-typedef struct crypto_pk_t crypto_pk_t;
typedef struct aes_cnt_cipher crypto_cipher_t;
typedef struct crypto_digest_t crypto_digest_t;
typedef struct crypto_xof_t crypto_xof_t;
typedef struct crypto_dh_t crypto_dh_t;
/* global state */
-const char * crypto_openssl_get_version_str(void);
-const char * crypto_openssl_get_header_version_str(void);
int crypto_early_init(void) ATTR_WUR;
int crypto_global_init(int hardwareAccel,
const char *accelName,
@@ -145,9 +103,6 @@ void crypto_thread_cleanup(void);
int crypto_global_cleanup(void);
/* environment setup */
-MOCK_DECL(crypto_pk_t *,crypto_pk_new,(void));
-void crypto_pk_free(crypto_pk_t *env);
-
void crypto_set_tls_dh_prime(void);
crypto_cipher_t *crypto_cipher_new(const char *key);
crypto_cipher_t *crypto_cipher_new_with_bits(const char *key, int bits);
@@ -155,49 +110,14 @@ crypto_cipher_t *crypto_cipher_new_with_iv(const char *key, const char *iv);
crypto_cipher_t *crypto_cipher_new_with_iv_and_bits(const uint8_t *key,
const uint8_t *iv,
int bits);
-void crypto_cipher_free(crypto_cipher_t *env);
-
-/* public key crypto */
-MOCK_DECL(int, crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits));
-#define crypto_pk_generate_key(env) \
- crypto_pk_generate_key_with_bits((env), (PK_BYTES*8))
-
-int crypto_pk_read_private_key_from_filename(crypto_pk_t *env,
- const char *keyfile);
-int crypto_pk_write_public_key_to_string(crypto_pk_t *env,
- char **dest, size_t *len);
-int crypto_pk_write_private_key_to_string(crypto_pk_t *env,
- char **dest, size_t *len);
-int crypto_pk_read_public_key_from_string(crypto_pk_t *env,
- const char *src, size_t len);
-int crypto_pk_read_private_key_from_string(crypto_pk_t *env,
- const char *s, ssize_t len);
-int crypto_pk_write_private_key_to_filename(crypto_pk_t *env,
- const char *fname);
-
-int crypto_pk_check_key(crypto_pk_t *env);
-int crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b);
-int crypto_pk_eq_keys(const crypto_pk_t *a, const crypto_pk_t *b);
-size_t crypto_pk_keysize(const crypto_pk_t *env);
-int crypto_pk_num_bits(crypto_pk_t *env);
-crypto_pk_t *crypto_pk_dup_key(crypto_pk_t *orig);
-crypto_pk_t *crypto_pk_copy_full(crypto_pk_t *orig);
-int crypto_pk_key_is_private(const crypto_pk_t *key);
-int crypto_pk_public_exponent_ok(crypto_pk_t *env);
-
-int crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen,
- const char *from, size_t fromlen, int padding);
-int crypto_pk_private_decrypt(crypto_pk_t *env, char *to, size_t tolen,
- const char *from, size_t fromlen,
- int padding, int warnOnFailure);
-MOCK_DECL(int, crypto_pk_public_checksig,(const crypto_pk_t *env,
- char *to, size_t tolen,
- const char *from, size_t fromlen));
+void crypto_cipher_free_(crypto_cipher_t *env);
+#define crypto_cipher_free(c) \
+ FREE_AND_NULL(crypto_cipher_t, crypto_cipher_free_, (c))
+
+/* public key crypto */
MOCK_DECL(int, crypto_pk_public_checksig_digest,(crypto_pk_t *env,
const char *data, size_t datalen,
const char *sig, size_t siglen));
-int crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen,
- const char *from, size_t fromlen);
int crypto_pk_private_sign_digest(crypto_pk_t *env, char *to, size_t tolen,
const char *from, size_t fromlen);
int crypto_pk_obsolete_public_hybrid_encrypt(crypto_pk_t *env, char *to,
@@ -208,17 +128,9 @@ int crypto_pk_obsolete_private_hybrid_decrypt(crypto_pk_t *env, char *to,
size_t tolen,
const char *from, size_t fromlen,
int padding, int warnOnFailure);
-
-int crypto_pk_asn1_encode(crypto_pk_t *pk, char *dest, size_t dest_len);
-crypto_pk_t *crypto_pk_asn1_decode(const char *str, size_t len);
int crypto_pk_get_digest(const crypto_pk_t *pk, char *digest_out);
int crypto_pk_get_common_digests(crypto_pk_t *pk,
common_digests_t *digests_out);
-int crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out,int add_space);
-int crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out);
-
-int crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out);
-crypto_pk_t *crypto_pk_base64_decode(const char *str, size_t len);
/* symmetric crypto */
const char *crypto_cipher_get_key(crypto_cipher_t *env);
@@ -258,7 +170,9 @@ int crypto_digest_algorithm_parse_name(const char *name);
crypto_digest_t *crypto_digest_new(void);
crypto_digest_t *crypto_digest256_new(digest_algorithm_t algorithm);
crypto_digest_t *crypto_digest512_new(digest_algorithm_t algorithm);
-void crypto_digest_free(crypto_digest_t *digest);
+void crypto_digest_free_(crypto_digest_t *digest);
+#define crypto_digest_free(d) \
+ FREE_AND_NULL(crypto_digest_t, crypto_digest_free_, (d))
void crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
size_t len);
void crypto_digest_get_digest(crypto_digest_t *digest,
@@ -276,7 +190,9 @@ void crypto_mac_sha3_256(uint8_t *mac_out, size_t len_out,
crypto_xof_t *crypto_xof_new(void);
void crypto_xof_add_bytes(crypto_xof_t *xof, const uint8_t *data, size_t len);
void crypto_xof_squeeze_bytes(crypto_xof_t *xof, uint8_t *out, size_t len);
-void crypto_xof_free(crypto_xof_t *xof);
+void crypto_xof_free_(crypto_xof_t *xof);
+#define crypto_xof_free(xof) \
+ FREE_AND_NULL(crypto_xof_t, crypto_xof_free_, (xof))
/* Key negotiation */
#define DH_TYPE_CIRCUIT 1
@@ -291,7 +207,8 @@ int crypto_dh_get_public(crypto_dh_t *dh, char *pubkey_out,
ssize_t crypto_dh_compute_secret(int severity, crypto_dh_t *dh,
const char *pubkey, size_t pubkey_len,
char *secret_out, size_t secret_out_len);
-void crypto_dh_free(crypto_dh_t *dh);
+void crypto_dh_free_(crypto_dh_t *dh);
+#define crypto_dh_free(dh) FREE_AND_NULL(crypto_dh_t, crypto_dh_free_, (dh))
int crypto_expand_key_material_TAP(const uint8_t *key_in,
size_t key_in_len,
@@ -329,13 +246,7 @@ void memwipe(void *mem, uint8_t byte, size_t sz);
/* Prototypes for private functions only used by tortls.c, crypto.c, and the
* unit tests. */
-struct rsa_st;
-struct evp_pkey_st;
struct dh_st;
-struct rsa_st *crypto_pk_get_rsa_(crypto_pk_t *env);
-crypto_pk_t *crypto_new_pk_from_rsa_(struct rsa_st *rsa);
-MOCK_DECL(struct evp_pkey_st *, crypto_pk_get_evp_pkey_,(crypto_pk_t *env,
- int private));
struct dh_st *crypto_dh_get_dh_(crypto_dh_t *dh);
void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in);
@@ -352,7 +263,6 @@ extern int break_strongest_rng_fallback;
#endif /* defined(CRYPTO_PRIVATE) */
#ifdef TOR_UNIT_TESTS
-void crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src);
digest_algorithm_t crypto_digest_get_algorithm(crypto_digest_t *digest);
#endif
diff --git a/src/common/crypto_curve25519.h b/src/common/crypto_curve25519.h
index d024ab79f5..11f7423b07 100644
--- a/src/common/crypto_curve25519.h
+++ b/src/common/crypto_curve25519.h
@@ -6,6 +6,7 @@
#include "testsupport.h"
#include "torint.h"
+#include "crypto_openssl_mgt.h"
/** Length of a curve25519 public key when encoded. */
#define CURVE25519_PUBKEY_LEN 32
diff --git a/src/common/crypto_ed25519.c b/src/common/crypto_ed25519.c
index 94b23e31b9..b962a59de1 100644
--- a/src/common/crypto_ed25519.c
+++ b/src/common/crypto_ed25519.c
@@ -225,8 +225,8 @@ ed25519_public_key_is_zero(const ed25519_public_key_t *pubkey)
/* Return a heap-allocated array that contains <b>msg</b> prefixed by the
* string <b>prefix_str</b>. Set <b>final_msg_len_out</b> to the size of the
- * final array. If an error occured, return NULL. It's the resonsibility of the
- * caller to free the returned array. */
+ * final array. If an error occurred, return NULL. It's the responsibility of
+ * the caller to free the returned array. */
static uint8_t *
get_prefixed_msg(const uint8_t *msg, size_t msg_len,
const char *prefix_str,
@@ -259,7 +259,7 @@ get_prefixed_msg(const uint8_t *msg, size_t msg_len,
* Set <b>signature_out</b> to a signature of the <b>len</b>-byte message
* <b>msg</b>, using the secret and public key in <b>keypair</b>.
*
- * Return 0 if we successfuly signed the message, otherwise return -1.
+ * Return 0 if we successfully signed the message, otherwise return -1.
*/
int
ed25519_sign(ed25519_signature_t *signature_out,
@@ -529,7 +529,7 @@ ed25519_keypair_blind(ed25519_keypair_t *out,
/**
* Given an ed25519 public key in <b>inp</b>, generate a corresponding blinded
* public key in <b>out</b>, blinded with the 32-byte parameter in
- * <b>param</b>. Return 0 on sucess, -1 on railure.
+ * <b>param</b>. Return 0 on success, -1 on railure.
*/
int
ed25519_public_blind(ed25519_public_key_t *out,
@@ -622,7 +622,7 @@ ed25519_pubkey_read_from_file(ed25519_public_key_t *pubkey_out,
/** Release all storage held for <b>kp</b>. */
void
-ed25519_keypair_free(ed25519_keypair_t *kp)
+ed25519_keypair_free_(ed25519_keypair_t *kp)
{
if (! kp)
return;
@@ -719,7 +719,7 @@ ed25519_impl_spot_check,(void))
return -1;
/* XXX/yawning: Someone that's more paranoid than I am, can write "Assume
- * ref0 is cannonical, and fuzz impl against it" if they want, but I doubt
+ * ref0 is canonical, and fuzz impl against it" if they want, but I doubt
* that will catch anything that the known answer tests won't.
*/
goto end;
@@ -762,7 +762,7 @@ pick_ed25519_impl(void)
/* LCOV_EXCL_STOP */
}
-/* Initialize the Ed25519 implementation. This is neccessary if you're
+/* Initialize the Ed25519 implementation. This is necessary if you're
* going to use them in a multithreaded setting, and not otherwise. */
void
ed25519_init(void)
diff --git a/src/common/crypto_ed25519.h b/src/common/crypto_ed25519.h
index 8d13a487d6..74269ccffd 100644
--- a/src/common/crypto_ed25519.h
+++ b/src/common/crypto_ed25519.h
@@ -7,6 +7,7 @@
#include "testsupport.h"
#include "torint.h"
#include "crypto_curve25519.h"
+#include "util.h"
#define ED25519_PUBKEY_LEN 32
#define ED25519_SECKEY_LEN 64
@@ -117,7 +118,9 @@ int ed25519_pubkey_read_from_file(ed25519_public_key_t *pubkey_out,
char **tag_out,
const char *filename);
-void ed25519_keypair_free(ed25519_keypair_t *kp);
+void ed25519_keypair_free_(ed25519_keypair_t *kp);
+#define ed25519_keypair_free(kp) \
+ FREE_AND_NULL(ed25519_keypair_t, ed25519_keypair_free_, (kp))
int ed25519_pubkey_eq(const ed25519_public_key_t *key1,
const ed25519_public_key_t *key2);
diff --git a/src/common/crypto_openssl_mgt.c b/src/common/crypto_openssl_mgt.c
new file mode 100644
index 0000000000..ea3519efa2
--- /dev/null
+++ b/src/common/crypto_openssl_mgt.c
@@ -0,0 +1,161 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file crypto_openssl.c
+ *
+ * \brief Block of functions related to operations from OpenSSL.
+ **/
+
+#include "compat_openssl.h"
+#include "crypto_openssl_mgt.h"
+
+DISABLE_GCC_WARNING(redundant-decls)
+
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+#include <openssl/engine.h>
+#include <openssl/rand.h>
+#include <openssl/bn.h>
+#include <openssl/dh.h>
+#include <openssl/conf.h>
+#include <openssl/hmac.h>
+#include <openssl/crypto.h>
+
+ENABLE_GCC_WARNING(redundant-decls)
+
+#ifndef NEW_THREAD_API
+/** A number of preallocated mutexes for use by OpenSSL. */
+static tor_mutex_t **openssl_mutexes_ = NULL;
+/** How many mutexes have we allocated for use by OpenSSL? */
+static int n_openssl_mutexes_ = 0;
+#endif /* !defined(NEW_THREAD_API) */
+
+/** Declare STATIC functions */
+STATIC char * parse_openssl_version_str(const char *raw_version);
+#ifndef NEW_THREAD_API
+STATIC void openssl_locking_cb_(int mode, int n, const char *file, int line);
+STATIC void tor_set_openssl_thread_id(CRYPTO_THREADID *threadid);
+#endif
+
+/* Returns a trimmed and human-readable version of an openssl version string
+* <b>raw_version</b>. They are usually in the form of 'OpenSSL 1.0.0b 10
+* May 2012' and this will parse them into a form similar to '1.0.0b' */
+STATIC char *
+parse_openssl_version_str(const char *raw_version)
+{
+ const char *end_of_version = NULL;
+ /* The output should be something like "OpenSSL 1.0.0b 10 May 2012. Let's
+ trim that down. */
+ if (!strcmpstart(raw_version, "OpenSSL ")) {
+ raw_version += strlen("OpenSSL ");
+ end_of_version = strchr(raw_version, ' ');
+ }
+
+ if (end_of_version)
+ return tor_strndup(raw_version,
+ end_of_version-raw_version);
+ else
+ return tor_strdup(raw_version);
+}
+
+static char *crypto_openssl_version_str = NULL;
+/* Return a human-readable version of the run-time openssl version number. */
+const char *
+crypto_openssl_get_version_str(void)
+{
+ if (crypto_openssl_version_str == NULL) {
+ const char *raw_version = OpenSSL_version(OPENSSL_VERSION);
+ crypto_openssl_version_str = parse_openssl_version_str(raw_version);
+ }
+ return crypto_openssl_version_str;
+}
+
+static char *crypto_openssl_header_version_str = NULL;
+/* Return a human-readable version of the compile-time openssl version
+* number. */
+const char *
+crypto_openssl_get_header_version_str(void)
+{
+ if (crypto_openssl_header_version_str == NULL) {
+ crypto_openssl_header_version_str =
+ parse_openssl_version_str(OPENSSL_VERSION_TEXT);
+ }
+ return crypto_openssl_header_version_str;
+}
+
+#ifndef OPENSSL_THREADS
+#error OpenSSL has been built without thread support. Tor requires an \
+ OpenSSL library with thread support enabled.
+#endif
+
+#ifndef NEW_THREAD_API
+/** Helper: OpenSSL uses this callback to manipulate mutexes. */
+STATIC void
+openssl_locking_cb_(int mode, int n, const char *file, int line)
+{
+ (void)file;
+ (void)line;
+ if (!openssl_mutexes_)
+ /* This is not a really good fix for the
+ * "release-freed-lock-from-separate-thread-on-shutdown" problem, but
+ * it can't hurt. */
+ return;
+ if (mode & CRYPTO_LOCK)
+ tor_mutex_acquire(openssl_mutexes_[n]);
+ else
+ tor_mutex_release(openssl_mutexes_[n]);
+}
+
+STATIC void
+tor_set_openssl_thread_id(CRYPTO_THREADID *threadid)
+{
+ CRYPTO_THREADID_set_numeric(threadid, tor_get_thread_id());
+}
+#endif /* !defined(NEW_THREAD_API) */
+
+/** Helper: Construct mutexes, and set callbacks to help OpenSSL handle being
+ * multithreaded. Returns 0. */
+int
+setup_openssl_threading(void)
+{
+#ifndef NEW_THREAD_API
+ int i;
+ int n = CRYPTO_num_locks();
+ n_openssl_mutexes_ = n;
+ openssl_mutexes_ = tor_calloc(n, sizeof(tor_mutex_t *));
+ for (i=0; i < n; ++i)
+ openssl_mutexes_[i] = tor_mutex_new();
+ CRYPTO_set_locking_callback(openssl_locking_cb_);
+ CRYPTO_THREADID_set_callback(tor_set_openssl_thread_id);
+#endif /* !defined(NEW_THREAD_API) */
+ return 0;
+}
+
+/** free OpenSSL variables */
+void
+crypto_openssl_free_all(void)
+{
+ tor_free(crypto_openssl_version_str);
+ tor_free(crypto_openssl_header_version_str);
+
+#ifndef NEW_THREAD_API
+ if (n_openssl_mutexes_) {
+ int n = n_openssl_mutexes_;
+ tor_mutex_t **ms = openssl_mutexes_;
+ int i;
+ openssl_mutexes_ = NULL;
+ n_openssl_mutexes_ = 0;
+ for (i=0;i<n;++i) {
+ tor_mutex_free(ms[i]);
+ }
+ tor_free(ms);
+ }
+#endif /* !defined(NEW_THREAD_API) */
+}
+
diff --git a/src/common/crypto_openssl_mgt.h b/src/common/crypto_openssl_mgt.h
new file mode 100644
index 0000000000..09b6737962
--- /dev/null
+++ b/src/common/crypto_openssl_mgt.h
@@ -0,0 +1,85 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file crypto_openssl.h
+ *
+ * \brief Headers for crypto_openssl.c
+ **/
+
+#ifndef TOR_CRYPTO_OPENSSL_H
+#define TOR_CRYPTO_OPENSSL_H
+
+#include <stdio.h>
+#include "util.h"
+
+#include <openssl/engine.h>
+
+/*
+ Macro to create an arbitrary OpenSSL version number as used by
+ OPENSSL_VERSION_NUMBER or SSLeay(), since the actual numbers are a bit hard
+ to read.
+
+ Don't use this directly, instead use one of the other OPENSSL_V macros
+ below.
+
+ The format is: 4 bits major, 8 bits minor, 8 bits fix, 8 bits patch, 4 bit
+ status.
+ */
+#define OPENSSL_VER(a,b,c,d,e) \
+ (((a)<<28) | \
+ ((b)<<20) | \
+ ((c)<<12) | \
+ ((d)<< 4) | \
+ (e))
+/** An openssl release number. For example, OPENSSL_V(0,9,8,'j') is the
+ * version for the released version of 0.9.8j */
+#define OPENSSL_V(a,b,c,d) \
+ OPENSSL_VER((a),(b),(c),(d)-'a'+1,0xf)
+/** An openssl release number for the first release in the series. For
+ * example, OPENSSL_V_NOPATCH(1,0,0) is the first released version of OpenSSL
+ * 1.0.0. */
+#define OPENSSL_V_NOPATCH(a,b,c) \
+ OPENSSL_VER((a),(b),(c),0,0xf)
+/** The first version that would occur for any alpha or beta in an openssl
+ * series. For example, OPENSSL_V_SERIES(0,9,8) is greater than any released
+ * 0.9.7, and less than any released 0.9.8. */
+#define OPENSSL_V_SERIES(a,b,c) \
+ OPENSSL_VER((a),(b),(c),0,0)
+
+#ifdef ANDROID
+/* Android's OpenSSL seems to have removed all of its Engine support. */
+#define DISABLE_ENGINES
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) && \
+ !defined(LIBRESSL_VERSION_NUMBER)
+/* OpenSSL as of 1.1.0pre4 has an "new" thread API, which doesn't require
+ * seting up various callbacks.
+ *
+ * OpenSSL 1.1.0pre4 has a messed up `ERR_remove_thread_state()` prototype,
+ * while the previous one was restored in pre5, and the function made a no-op
+ * (along with a deprecated annotation, which produces a compiler warning).
+ *
+ * While it is possible to support all three versions of the thread API,
+ * a version that existed only for one snapshot pre-release is kind of
+ * pointless, so let's not.
+ */
+#define NEW_THREAD_API
+#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) && ... */
+
+/* global openssl state */
+const char * crypto_openssl_get_version_str(void);
+const char * crypto_openssl_get_header_version_str(void);
+
+/* OpenSSL threading setup function */
+int setup_openssl_threading(void);
+
+/* Tor OpenSSL utility functions */
+void crypto_openssl_free_all(void);
+
+#endif /* !defined(TOR_CRYPTO_OPENSSL_H) */
+
diff --git a/src/common/crypto_pwbox.h b/src/common/crypto_pwbox.h
index cee8653587..a26b6d2c17 100644
--- a/src/common/crypto_pwbox.h
+++ b/src/common/crypto_pwbox.h
@@ -1,3 +1,6 @@
+/* Copyright (c) 2014-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
#ifndef CRYPTO_PWBOX_H_INCLUDED_
#define CRYPTO_PWBOX_H_INCLUDED_
diff --git a/src/common/crypto_rsa.c b/src/common/crypto_rsa.c
new file mode 100644
index 0000000000..259656810b
--- /dev/null
+++ b/src/common/crypto_rsa.c
@@ -0,0 +1,923 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file crypto_rsa.c
+ * \brief Block of functions related with RSA utilities and operations.
+ **/
+
+#include "crypto_rsa.h"
+#include "crypto.h"
+#include "compat_openssl.h"
+#include "crypto_curve25519.h"
+#include "crypto_ed25519.h"
+#include "crypto_format.h"
+
+DISABLE_GCC_WARNING(redundant-decls)
+
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+#include <openssl/engine.h>
+#include <openssl/rand.h>
+#include <openssl/bn.h>
+#include <openssl/dh.h>
+#include <openssl/conf.h>
+#include <openssl/hmac.h>
+
+ENABLE_GCC_WARNING(redundant-decls)
+
+#include "torlog.h"
+#include "util.h"
+#include "util_format.h"
+
+/** Declaration for crypto_pk_t structure. */
+struct crypto_pk_t
+{
+ int refs; /**< reference count, so we don't have to copy keys */
+ RSA *key; /**< The key itself */
+};
+
+/** Log all pending crypto errors at level <b>severity</b>. Use
+ * <b>doing</b> to describe our current activities.
+ */
+static void
+crypto_log_errors(int severity, const char *doing)
+{
+ unsigned long err;
+ const char *msg, *lib, *func;
+ while ((err = ERR_get_error()) != 0) {
+ msg = (const char*)ERR_reason_error_string(err);
+ lib = (const char*)ERR_lib_error_string(err);
+ func = (const char*)ERR_func_error_string(err);
+ if (!msg) msg = "(null)";
+ if (!lib) lib = "(null)";
+ if (!func) func = "(null)";
+ if (BUG(!doing)) doing = "(null)";
+ tor_log(severity, LD_CRYPTO, "crypto error while %s: %s (in %s:%s)",
+ doing, msg, lib, func);
+ }
+}
+
+/** Return the number of bytes added by padding method <b>padding</b>.
+ */
+int
+crypto_get_rsa_padding_overhead(int padding)
+{
+ switch (padding)
+ {
+ case RSA_PKCS1_OAEP_PADDING: return PKCS1_OAEP_PADDING_OVERHEAD;
+ default: tor_assert(0); return -1; // LCOV_EXCL_LINE
+ }
+}
+
+/** Given a padding method <b>padding</b>, return the correct OpenSSL constant.
+ */
+int
+crypto_get_rsa_padding(int padding)
+{
+ switch (padding)
+ {
+ case PK_PKCS1_OAEP_PADDING: return RSA_PKCS1_OAEP_PADDING;
+ default: tor_assert(0); return -1; // LCOV_EXCL_LINE
+ }
+}
+
+/** used internally: quicly validate a crypto_pk_t object as a private key.
+ * Return 1 iff the public key is valid, 0 if obviously invalid.
+ */
+static int
+crypto_pk_private_ok(const crypto_pk_t *k)
+{
+#ifdef OPENSSL_1_1_API
+ if (!k || !k->key)
+ return 0;
+
+ const BIGNUM *p, *q;
+ RSA_get0_factors(k->key, &p, &q);
+ return p != NULL; /* XXX/yawning: Should we check q? */
+#else /* !(defined(OPENSSL_1_1_API)) */
+ return k && k->key && k->key->p;
+#endif /* defined(OPENSSL_1_1_API) */
+}
+
+/** used by tortls.c: wrap an RSA* in a crypto_pk_t. */
+crypto_pk_t *
+crypto_new_pk_from_rsa_(RSA *rsa)
+{
+ crypto_pk_t *env;
+ tor_assert(rsa);
+ env = tor_malloc(sizeof(crypto_pk_t));
+ env->refs = 1;
+ env->key = rsa;
+ return env;
+}
+
+/** Helper, used by tor-gencert.c. Return the RSA from a
+ * crypto_pk_t. */
+RSA *
+crypto_pk_get_rsa_(crypto_pk_t *env)
+{
+ return env->key;
+}
+
+/** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t. Iff
+ * private is set, include the private-key portion of the key. Return a valid
+ * pointer on success, and NULL on failure. */
+MOCK_IMPL(EVP_PKEY *,
+crypto_pk_get_evp_pkey_,(crypto_pk_t *env, int private))
+{
+ RSA *key = NULL;
+ EVP_PKEY *pkey = NULL;
+ tor_assert(env->key);
+ if (private) {
+ if (!(key = RSAPrivateKey_dup(env->key)))
+ goto error;
+ } else {
+ if (!(key = RSAPublicKey_dup(env->key)))
+ goto error;
+ }
+ if (!(pkey = EVP_PKEY_new()))
+ goto error;
+ if (!(EVP_PKEY_assign_RSA(pkey, key)))
+ goto error;
+ return pkey;
+ error:
+ if (pkey)
+ EVP_PKEY_free(pkey);
+ if (key)
+ RSA_free(key);
+ return NULL;
+}
+
+/** Allocate and return storage for a public key. The key itself will not yet
+ * be set.
+ */
+MOCK_IMPL(crypto_pk_t *,
+crypto_pk_new,(void))
+{
+ RSA *rsa;
+
+ rsa = RSA_new();
+ tor_assert(rsa);
+ return crypto_new_pk_from_rsa_(rsa);
+}
+
+/** Release a reference to an asymmetric key; when all the references
+ * are released, free the key.
+ */
+void
+crypto_pk_free_(crypto_pk_t *env)
+{
+ if (!env)
+ return;
+
+ if (--env->refs > 0)
+ return;
+ tor_assert(env->refs == 0);
+
+ if (env->key)
+ RSA_free(env->key);
+
+ tor_free(env);
+}
+
+/** Generate a <b>bits</b>-bit new public/private keypair in <b>env</b>.
+ * Return 0 on success, -1 on failure.
+ */
+MOCK_IMPL(int,
+crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits))
+{
+ tor_assert(env);
+
+ if (env->key) {
+ RSA_free(env->key);
+ env->key = NULL;
+ }
+
+ {
+ BIGNUM *e = BN_new();
+ RSA *r = NULL;
+ if (!e)
+ goto done;
+ if (! BN_set_word(e, 65537))
+ goto done;
+ r = RSA_new();
+ if (!r)
+ goto done;
+ if (RSA_generate_key_ex(r, bits, e, NULL) == -1)
+ goto done;
+
+ env->key = r;
+ r = NULL;
+ done:
+ if (e)
+ BN_clear_free(e);
+ if (r)
+ RSA_free(r);
+ }
+
+ if (!env->key) {
+ crypto_log_errors(LOG_WARN, "generating RSA key");
+ return -1;
+ }
+
+ return 0;
+}
+
+/** A PEM callback that always reports a failure to get a password */
+static int
+pem_no_password_cb(char *buf, int size, int rwflag, void *u)
+{
+ (void)buf;
+ (void)size;
+ (void)rwflag;
+ (void)u;
+ return -1;
+}
+
+/** Read a PEM-encoded private key from the <b>len</b>-byte string <b>s</b>
+ * into <b>env</b>. Return 0 on success, -1 on failure. If len is -1,
+ * the string is nul-terminated.
+ */
+int
+crypto_pk_read_private_key_from_string(crypto_pk_t *env,
+ const char *s, ssize_t len)
+{
+ BIO *b;
+
+ tor_assert(env);
+ tor_assert(s);
+ tor_assert(len < INT_MAX && len < SSIZE_T_CEILING);
+
+ /* Create a read-only memory BIO, backed by the string 's' */
+ b = BIO_new_mem_buf((char*)s, (int)len);
+ if (!b)
+ return -1;
+
+ if (env->key)
+ RSA_free(env->key);
+
+ env->key = PEM_read_bio_RSAPrivateKey(b,NULL,pem_no_password_cb,NULL);
+
+ BIO_free(b);
+
+ if (!env->key) {
+ crypto_log_errors(LOG_WARN, "Error parsing private key");
+ return -1;
+ }
+ return 0;
+}
+
+/** Read a PEM-encoded private key from the file named by
+ * <b>keyfile</b> into <b>env</b>. Return 0 on success, -1 on failure.
+ */
+int
+crypto_pk_read_private_key_from_filename(crypto_pk_t *env,
+ const char *keyfile)
+{
+ char *contents;
+ int r;
+
+ /* Read the file into a string. */
+ contents = read_file_to_str(keyfile, 0, NULL);
+ if (!contents) {
+ log_warn(LD_CRYPTO, "Error reading private key from \"%s\"", keyfile);
+ return -1;
+ }
+
+ /* Try to parse it. */
+ r = crypto_pk_read_private_key_from_string(env, contents, -1);
+ memwipe(contents, 0, strlen(contents));
+ tor_free(contents);
+ if (r)
+ return -1; /* read_private_key_from_string already warned, so we don't.*/
+
+ /* Make sure it's valid. */
+ if (crypto_pk_check_key(env) <= 0)
+ return -1;
+
+ return 0;
+}
+
+/** Helper function to implement crypto_pk_write_*_key_to_string. Return 0 on
+ * success, -1 on failure. */
+static int
+crypto_pk_write_key_to_string_impl(crypto_pk_t *env, char **dest,
+ size_t *len, int is_public)
+{
+ BUF_MEM *buf;
+ BIO *b;
+ int r;
+
+ tor_assert(env);
+ tor_assert(env->key);
+ tor_assert(dest);
+
+ b = BIO_new(BIO_s_mem()); /* Create a memory BIO */
+ if (!b)
+ return -1;
+
+ /* Now you can treat b as if it were a file. Just use the
+ * PEM_*_bio_* functions instead of the non-bio variants.
+ */
+ if (is_public)
+ r = PEM_write_bio_RSAPublicKey(b, env->key);
+ else
+ r = PEM_write_bio_RSAPrivateKey(b, env->key, NULL,NULL,0,NULL,NULL);
+
+ if (!r) {
+ crypto_log_errors(LOG_WARN, "writing RSA key to string");
+ BIO_free(b);
+ return -1;
+ }
+
+ BIO_get_mem_ptr(b, &buf);
+
+ *dest = tor_malloc(buf->length+1);
+ memcpy(*dest, buf->data, buf->length);
+ (*dest)[buf->length] = 0; /* nul terminate it */
+ *len = buf->length;
+
+ BIO_free(b);
+
+ return 0;
+}
+
+/** PEM-encode the public key portion of <b>env</b> and write it to a
+ * newly allocated string. On success, set *<b>dest</b> to the new
+ * string, *<b>len</b> to the string's length, and return 0. On
+ * failure, return -1.
+ */
+int
+crypto_pk_write_public_key_to_string(crypto_pk_t *env, char **dest,
+ size_t *len)
+{
+ return crypto_pk_write_key_to_string_impl(env, dest, len, 1);
+}
+
+/** PEM-encode the private key portion of <b>env</b> and write it to a
+ * newly allocated string. On success, set *<b>dest</b> to the new
+ * string, *<b>len</b> to the string's length, and return 0. On
+ * failure, return -1.
+ */
+int
+crypto_pk_write_private_key_to_string(crypto_pk_t *env, char **dest,
+ size_t *len)
+{
+ return crypto_pk_write_key_to_string_impl(env, dest, len, 0);
+}
+
+/** Read a PEM-encoded public key from the first <b>len</b> characters of
+ * <b>src</b>, and store the result in <b>env</b>. Return 0 on success, -1 on
+ * failure.
+ */
+int
+crypto_pk_read_public_key_from_string(crypto_pk_t *env, const char *src,
+ size_t len)
+{
+ BIO *b;
+
+ tor_assert(env);
+ tor_assert(src);
+ tor_assert(len<INT_MAX);
+
+ b = BIO_new(BIO_s_mem()); /* Create a memory BIO */
+ if (!b)
+ return -1;
+
+ BIO_write(b, src, (int)len);
+
+ if (env->key)
+ RSA_free(env->key);
+ env->key = PEM_read_bio_RSAPublicKey(b, NULL, pem_no_password_cb, NULL);
+ BIO_free(b);
+ if (!env->key) {
+ crypto_log_errors(LOG_WARN, "reading public key from string");
+ return -1;
+ }
+
+ return 0;
+}
+
+/** Write the private key from <b>env</b> into the file named by <b>fname</b>,
+ * PEM-encoded. Return 0 on success, -1 on failure.
+ */
+int
+crypto_pk_write_private_key_to_filename(crypto_pk_t *env,
+ const char *fname)
+{
+ BIO *bio;
+ char *cp;
+ long len;
+ char *s;
+ int r;
+
+ tor_assert(crypto_pk_private_ok(env));
+
+ if (!(bio = BIO_new(BIO_s_mem())))
+ return -1;
+ if (PEM_write_bio_RSAPrivateKey(bio, env->key, NULL,NULL,0,NULL,NULL)
+ == 0) {
+ crypto_log_errors(LOG_WARN, "writing private key");
+ BIO_free(bio);
+ return -1;
+ }
+ len = BIO_get_mem_data(bio, &cp);
+ tor_assert(len >= 0);
+ s = tor_malloc(len+1);
+ memcpy(s, cp, len);
+ s[len]='\0';
+ r = write_str_to_file(fname, s, 0);
+ BIO_free(bio);
+ memwipe(s, 0, strlen(s));
+ tor_free(s);
+ return r;
+}
+
+/** Return true iff <b>env</b> has a valid key.
+ */
+int
+crypto_pk_check_key(crypto_pk_t *env)
+{
+ int r;
+ tor_assert(env);
+
+ r = RSA_check_key(env->key);
+ if (r <= 0)
+ crypto_log_errors(LOG_WARN,"checking RSA key");
+ return r;
+}
+
+/** Return true iff <b>key</b> contains the private-key portion of the RSA
+ * key. */
+int
+crypto_pk_key_is_private(const crypto_pk_t *key)
+{
+ tor_assert(key);
+ return crypto_pk_private_ok(key);
+}
+
+/** Return true iff <b>env</b> contains a public key whose public exponent
+ * equals 65537.
+ */
+int
+crypto_pk_public_exponent_ok(crypto_pk_t *env)
+{
+ tor_assert(env);
+ tor_assert(env->key);
+
+ const BIGNUM *e;
+
+#ifdef OPENSSL_1_1_API
+ const BIGNUM *n, *d;
+ RSA_get0_key(env->key, &n, &e, &d);
+#else
+ e = env->key->e;
+#endif /* defined(OPENSSL_1_1_API) */
+ return BN_is_word(e, 65537);
+}
+
+/** Compare the public-key components of a and b. Return less than 0
+ * if a\<b, 0 if a==b, and greater than 0 if a\>b. A NULL key is
+ * considered to be less than all non-NULL keys, and equal to itself.
+ *
+ * Note that this may leak information about the keys through timing.
+ */
+int
+crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b)
+{
+ int result;
+ char a_is_non_null = (a != NULL) && (a->key != NULL);
+ char b_is_non_null = (b != NULL) && (b->key != NULL);
+ char an_argument_is_null = !a_is_non_null | !b_is_non_null;
+
+ result = tor_memcmp(&a_is_non_null, &b_is_non_null, sizeof(a_is_non_null));
+ if (an_argument_is_null)
+ return result;
+
+ const BIGNUM *a_n, *a_e;
+ const BIGNUM *b_n, *b_e;
+
+#ifdef OPENSSL_1_1_API
+ const BIGNUM *a_d, *b_d;
+ RSA_get0_key(a->key, &a_n, &a_e, &a_d);
+ RSA_get0_key(b->key, &b_n, &b_e, &b_d);
+#else
+ a_n = a->key->n;
+ a_e = a->key->e;
+ b_n = b->key->n;
+ b_e = b->key->e;
+#endif /* defined(OPENSSL_1_1_API) */
+
+ tor_assert(a_n != NULL && a_e != NULL);
+ tor_assert(b_n != NULL && b_e != NULL);
+
+ result = BN_cmp(a_n, b_n);
+ if (result)
+ return result;
+ return BN_cmp(a_e, b_e);
+}
+
+/** Compare the public-key components of a and b. Return non-zero iff
+ * a==b. A NULL key is considered to be distinct from all non-NULL
+ * keys, and equal to itself.
+ *
+ * Note that this may leak information about the keys through timing.
+ */
+int
+crypto_pk_eq_keys(const crypto_pk_t *a, const crypto_pk_t *b)
+{
+ return (crypto_pk_cmp_keys(a, b) == 0);
+}
+
+/** Return the size of the public key modulus in <b>env</b>, in bytes. */
+size_t
+crypto_pk_keysize(const crypto_pk_t *env)
+{
+ tor_assert(env);
+ tor_assert(env->key);
+
+ return (size_t) RSA_size((RSA*)env->key);
+}
+
+/** Return the size of the public key modulus of <b>env</b>, in bits. */
+int
+crypto_pk_num_bits(crypto_pk_t *env)
+{
+ tor_assert(env);
+ tor_assert(env->key);
+
+#ifdef OPENSSL_1_1_API
+ /* It's so stupid that there's no other way to check that n is valid
+ * before calling RSA_bits().
+ */
+ const BIGNUM *n, *e, *d;
+ RSA_get0_key(env->key, &n, &e, &d);
+ tor_assert(n != NULL);
+
+ return RSA_bits(env->key);
+#else /* !(defined(OPENSSL_1_1_API)) */
+ tor_assert(env->key->n);
+ return BN_num_bits(env->key->n);
+#endif /* defined(OPENSSL_1_1_API) */
+}
+
+/** Increase the reference count of <b>env</b>, and return it.
+ */
+crypto_pk_t *
+crypto_pk_dup_key(crypto_pk_t *env)
+{
+ tor_assert(env);
+ tor_assert(env->key);
+
+ env->refs++;
+ return env;
+}
+
+#ifdef TOR_UNIT_TESTS
+/** For testing: replace dest with src. (Dest must have a refcount
+ * of 1) */
+void
+crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src)
+{
+ tor_assert(dest);
+ tor_assert(dest->refs == 1);
+ tor_assert(src);
+ RSA_free(dest->key);
+ dest->key = RSAPrivateKey_dup(src->key);
+}
+#endif /* defined(TOR_UNIT_TESTS) */
+
+/** Make a real honest-to-goodness copy of <b>env</b>, and return it.
+ * Returns NULL on failure. */
+crypto_pk_t *
+crypto_pk_copy_full(crypto_pk_t *env)
+{
+ RSA *new_key;
+ int privatekey = 0;
+ tor_assert(env);
+ tor_assert(env->key);
+
+ if (crypto_pk_private_ok(env)) {
+ new_key = RSAPrivateKey_dup(env->key);
+ privatekey = 1;
+ } else {
+ new_key = RSAPublicKey_dup(env->key);
+ }
+ if (!new_key) {
+ /* LCOV_EXCL_START
+ *
+ * We can't cause RSA*Key_dup() to fail, so we can't really test this.
+ */
+ log_err(LD_CRYPTO, "Unable to duplicate a %s key: openssl failed.",
+ privatekey?"private":"public");
+ crypto_log_errors(LOG_ERR,
+ privatekey ? "Duplicating a private key" :
+ "Duplicating a public key");
+ tor_fragile_assert();
+ return NULL;
+ /* LCOV_EXCL_STOP */
+ }
+
+ return crypto_new_pk_from_rsa_(new_key);
+}
+
+/** Encrypt <b>fromlen</b> bytes from <b>from</b> with the public key
+ * in <b>env</b>, using the padding method <b>padding</b>. On success,
+ * write the result to <b>to</b>, and return the number of bytes
+ * written. On failure, return -1.
+ *
+ * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
+ * at least the length of the modulus of <b>env</b>.
+ */
+int
+crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen,
+ const char *from, size_t fromlen, int padding)
+{
+ int r;
+ tor_assert(env);
+ tor_assert(from);
+ tor_assert(to);
+ tor_assert(fromlen<INT_MAX);
+ tor_assert(tolen >= crypto_pk_keysize(env));
+
+ r = RSA_public_encrypt((int)fromlen,
+ (unsigned char*)from, (unsigned char*)to,
+ env->key, crypto_get_rsa_padding(padding));
+ if (r<0) {
+ crypto_log_errors(LOG_WARN, "performing RSA encryption");
+ return -1;
+ }
+ return r;
+}
+
+/** Decrypt <b>fromlen</b> bytes from <b>from</b> with the private key
+ * in <b>env</b>, using the padding method <b>padding</b>. On success,
+ * write the result to <b>to</b>, and return the number of bytes
+ * written. On failure, return -1.
+ *
+ * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
+ * at least the length of the modulus of <b>env</b>.
+ */
+int
+crypto_pk_private_decrypt(crypto_pk_t *env, char *to,
+ size_t tolen,
+ const char *from, size_t fromlen,
+ int padding, int warnOnFailure)
+{
+ int r;
+ tor_assert(env);
+ tor_assert(from);
+ tor_assert(to);
+ tor_assert(env->key);
+ tor_assert(fromlen<INT_MAX);
+ tor_assert(tolen >= crypto_pk_keysize(env));
+ if (!crypto_pk_key_is_private(env))
+ /* Not a private key */
+ return -1;
+
+ r = RSA_private_decrypt((int)fromlen,
+ (unsigned char*)from, (unsigned char*)to,
+ env->key, crypto_get_rsa_padding(padding));
+
+ if (r<0) {
+ crypto_log_errors(warnOnFailure?LOG_WARN:LOG_DEBUG,
+ "performing RSA decryption");
+ return -1;
+ }
+ return r;
+}
+
+/** Check the signature in <b>from</b> (<b>fromlen</b> bytes long) with the
+ * public key in <b>env</b>, using PKCS1 padding. On success, write the
+ * signed data to <b>to</b>, and return the number of bytes written.
+ * On failure, return -1.
+ *
+ * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
+ * at least the length of the modulus of <b>env</b>.
+ */
+MOCK_IMPL(int,
+crypto_pk_public_checksig,(const crypto_pk_t *env, char *to,
+ size_t tolen,
+ const char *from, size_t fromlen))
+{
+ int r;
+ tor_assert(env);
+ tor_assert(from);
+ tor_assert(to);
+ tor_assert(fromlen < INT_MAX);
+ tor_assert(tolen >= crypto_pk_keysize(env));
+ r = RSA_public_decrypt((int)fromlen,
+ (unsigned char*)from, (unsigned char*)to,
+ env->key, RSA_PKCS1_PADDING);
+
+ if (r<0) {
+ crypto_log_errors(LOG_INFO, "checking RSA signature");
+ return -1;
+ }
+ return r;
+}
+
+/** Sign <b>fromlen</b> bytes of data from <b>from</b> with the private key in
+ * <b>env</b>, using PKCS1 padding. On success, write the signature to
+ * <b>to</b>, and return the number of bytes written. On failure, return
+ * -1.
+ *
+ * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
+ * at least the length of the modulus of <b>env</b>.
+ */
+int
+crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen,
+ const char *from, size_t fromlen)
+{
+ int r;
+ tor_assert(env);
+ tor_assert(from);
+ tor_assert(to);
+ tor_assert(fromlen < INT_MAX);
+ tor_assert(tolen >= crypto_pk_keysize(env));
+ if (!crypto_pk_key_is_private(env))
+ /* Not a private key */
+ return -1;
+
+ r = RSA_private_encrypt((int)fromlen,
+ (unsigned char*)from, (unsigned char*)to,
+ (RSA*)env->key, RSA_PKCS1_PADDING);
+ if (r<0) {
+ crypto_log_errors(LOG_WARN, "generating RSA signature");
+ return -1;
+ }
+ return r;
+}
+
+/** ASN.1-encode the public portion of <b>pk</b> into <b>dest</b>.
+ * Return -1 on error, or the number of characters used on success.
+ */
+int
+crypto_pk_asn1_encode(const crypto_pk_t *pk, char *dest, size_t dest_len)
+{
+ int len;
+ unsigned char *buf = NULL;
+
+ len = i2d_RSAPublicKey(pk->key, &buf);
+ if (len < 0 || buf == NULL)
+ return -1;
+
+ if ((size_t)len > dest_len || dest_len > SIZE_T_CEILING) {
+ OPENSSL_free(buf);
+ return -1;
+ }
+ /* We don't encode directly into 'dest', because that would be illegal
+ * type-punning. (C99 is smarter than me, C99 is smarter than me...)
+ */
+ memcpy(dest,buf,len);
+ OPENSSL_free(buf);
+ return len;
+}
+
+/** Decode an ASN.1-encoded public key from <b>str</b>; return the result on
+ * success and NULL on failure.
+ */
+crypto_pk_t *
+crypto_pk_asn1_decode(const char *str, size_t len)
+{
+ RSA *rsa;
+ unsigned char *buf;
+ const unsigned char *cp;
+ cp = buf = tor_malloc(len);
+ memcpy(buf,str,len);
+ rsa = d2i_RSAPublicKey(NULL, &cp, len);
+ tor_free(buf);
+ if (!rsa) {
+ crypto_log_errors(LOG_WARN,"decoding public key");
+ return NULL;
+ }
+ return crypto_new_pk_from_rsa_(rsa);
+}
+
+/** Given a private or public key <b>pk</b>, put a fingerprint of the
+ * public key into <b>fp_out</b> (must have at least FINGERPRINT_LEN+1 bytes of
+ * space). Return 0 on success, -1 on failure.
+ *
+ * Fingerprints are computed as the SHA1 digest of the ASN.1 encoding
+ * of the public key, converted to hexadecimal, in upper case, with a
+ * space after every four digits.
+ *
+ * If <b>add_space</b> is false, omit the spaces.
+ */
+int
+crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out, int add_space)
+{
+ char digest[DIGEST_LEN];
+ char hexdigest[HEX_DIGEST_LEN+1];
+ if (crypto_pk_get_digest(pk, digest)) {
+ return -1;
+ }
+ base16_encode(hexdigest,sizeof(hexdigest),digest,DIGEST_LEN);
+ if (add_space) {
+ crypto_add_spaces_to_fp(fp_out, FINGERPRINT_LEN+1, hexdigest);
+ } else {
+ strncpy(fp_out, hexdigest, HEX_DIGEST_LEN+1);
+ }
+ return 0;
+}
+
+/** Given a private or public key <b>pk</b>, put a hashed fingerprint of
+ * the public key into <b>fp_out</b> (must have at least FINGERPRINT_LEN+1
+ * bytes of space). Return 0 on success, -1 on failure.
+ *
+ * Hashed fingerprints are computed as the SHA1 digest of the SHA1 digest
+ * of the ASN.1 encoding of the public key, converted to hexadecimal, in
+ * upper case.
+ */
+int
+crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out)
+{
+ char digest[DIGEST_LEN], hashed_digest[DIGEST_LEN];
+ if (crypto_pk_get_digest(pk, digest)) {
+ return -1;
+ }
+ if (crypto_digest(hashed_digest, digest, DIGEST_LEN) < 0) {
+ return -1;
+ }
+ base16_encode(fp_out, FINGERPRINT_LEN + 1, hashed_digest, DIGEST_LEN);
+ return 0;
+}
+
+/** Given a crypto_pk_t <b>pk</b>, allocate a new buffer containing the
+ * Base64 encoding of the DER representation of the private key as a NUL
+ * terminated string, and return it via <b>priv_out</b>. Return 0 on
+ * success, -1 on failure.
+ *
+ * It is the caller's responsibility to sanitize and free the resulting buffer.
+ */
+int
+crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out)
+{
+ unsigned char *der = NULL;
+ int der_len;
+ int ret = -1;
+
+ *priv_out = NULL;
+
+ der_len = i2d_RSAPrivateKey(pk->key, &der);
+ if (der_len < 0 || der == NULL)
+ return ret;
+
+ size_t priv_len = base64_encode_size(der_len, 0) + 1;
+ char *priv = tor_malloc_zero(priv_len);
+ if (base64_encode(priv, priv_len, (char *)der, der_len, 0) >= 0) {
+ *priv_out = priv;
+ ret = 0;
+ } else {
+ tor_free(priv);
+ }
+
+ memwipe(der, 0, der_len);
+ OPENSSL_free(der);
+ return ret;
+}
+
+/** Given a string containing the Base64 encoded DER representation of the
+ * private key <b>str</b>, decode and return the result on success, or NULL
+ * on failure.
+ */
+crypto_pk_t *
+crypto_pk_base64_decode(const char *str, size_t len)
+{
+ crypto_pk_t *pk = NULL;
+
+ char *der = tor_malloc_zero(len + 1);
+ int der_len = base64_decode(der, len, str, len);
+ if (der_len <= 0) {
+ log_warn(LD_CRYPTO, "Stored RSA private key seems corrupted (base64).");
+ goto out;
+ }
+
+ const unsigned char *dp = (unsigned char*)der; /* Shut the compiler up. */
+ RSA *rsa = d2i_RSAPrivateKey(NULL, &dp, der_len);
+ if (!rsa) {
+ crypto_log_errors(LOG_WARN, "decoding private key");
+ goto out;
+ }
+
+ pk = crypto_new_pk_from_rsa_(rsa);
+
+ /* Make sure it's valid. */
+ if (crypto_pk_check_key(pk) <= 0) {
+ crypto_pk_free(pk);
+ pk = NULL;
+ goto out;
+ }
+
+ out:
+ memwipe(der, 0, len + 1);
+ tor_free(der);
+ return pk;
+}
+
diff --git a/src/common/crypto_rsa.h b/src/common/crypto_rsa.h
new file mode 100644
index 0000000000..5b9025c629
--- /dev/null
+++ b/src/common/crypto_rsa.h
@@ -0,0 +1,104 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file crypto_rsa.h
+ *
+ * \brief Headers for crypto_rsa.c
+ **/
+
+#ifndef TOR_CRYPTO_RSA_H
+#define TOR_CRYPTO_RSA_H
+
+#include "orconfig.h"
+
+#include <stdio.h>
+#include "torint.h"
+#include "testsupport.h"
+#include "compat.h"
+#include "util.h"
+#include "torlog.h"
+#include "crypto_curve25519.h"
+
+/** Length of our public keys. */
+#define PK_BYTES (1024/8)
+
+/** Constant used to indicate OAEP padding for public-key encryption */
+#define PK_PKCS1_OAEP_PADDING 60002
+
+/** Number of bytes added for PKCS1-OAEP padding. */
+#define PKCS1_OAEP_PADDING_OVERHEAD 42
+
+/** A public key, or a public/private key-pair. */
+typedef struct crypto_pk_t crypto_pk_t;
+
+/* RSA enviroment setup */
+MOCK_DECL(crypto_pk_t *,crypto_pk_new,(void));
+void crypto_pk_free_(crypto_pk_t *env);
+#define crypto_pk_free(pk) FREE_AND_NULL(crypto_pk_t, crypto_pk_free_, (pk))
+int crypto_get_rsa_padding_overhead(int padding);
+int crypto_get_rsa_padding(int padding);
+
+/* public key crypto */
+MOCK_DECL(int, crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits));
+#define crypto_pk_generate_key(env) \
+ crypto_pk_generate_key_with_bits((env), (PK_BYTES*8))
+
+int crypto_pk_read_private_key_from_filename(crypto_pk_t *env,
+ const char *keyfile);
+int crypto_pk_write_public_key_to_string(crypto_pk_t *env,
+ char **dest, size_t *len);
+int crypto_pk_write_private_key_to_string(crypto_pk_t *env,
+ char **dest, size_t *len);
+int crypto_pk_read_public_key_from_string(crypto_pk_t *env,
+ const char *src, size_t len);
+int crypto_pk_read_private_key_from_string(crypto_pk_t *env,
+ const char *s, ssize_t len);
+int crypto_pk_write_private_key_to_filename(crypto_pk_t *env,
+ const char *fname);
+
+int crypto_pk_check_key(crypto_pk_t *env);
+int crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b);
+int crypto_pk_eq_keys(const crypto_pk_t *a, const crypto_pk_t *b);
+size_t crypto_pk_keysize(const crypto_pk_t *env);
+int crypto_pk_num_bits(crypto_pk_t *env);
+crypto_pk_t *crypto_pk_dup_key(crypto_pk_t *orig);
+crypto_pk_t *crypto_pk_copy_full(crypto_pk_t *orig);
+int crypto_pk_key_is_private(const crypto_pk_t *key);
+int crypto_pk_public_exponent_ok(crypto_pk_t *env);
+int crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen,
+ const char *from, size_t fromlen, int padding);
+int crypto_pk_private_decrypt(crypto_pk_t *env, char *to, size_t tolen,
+ const char *from, size_t fromlen,
+ int padding, int warnOnFailure);
+MOCK_DECL(int, crypto_pk_public_checksig,(const crypto_pk_t *env,
+ char *to, size_t tolen,
+ const char *from, size_t fromlen));
+int crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen,
+ const char *from, size_t fromlen);
+int crypto_pk_asn1_encode(const crypto_pk_t *pk, char *dest, size_t dest_len);
+crypto_pk_t *crypto_pk_asn1_decode(const char *str, size_t len);
+int crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out,int add_space);
+int crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out);
+
+int crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out);
+crypto_pk_t *crypto_pk_base64_decode(const char *str, size_t len);
+
+/* Prototypes for private functions only used by tortls.c, crypto.c, and the
+ * unit tests. */
+struct rsa_st;
+struct rsa_st *crypto_pk_get_rsa_(crypto_pk_t *env);
+crypto_pk_t *crypto_new_pk_from_rsa_(struct rsa_st *rsa);
+MOCK_DECL(struct evp_pkey_st *, crypto_pk_get_evp_pkey_,(crypto_pk_t *env,
+ int private));
+struct evp_pkey_st;
+
+#ifdef TOR_UNIT_TESTS
+void crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src);
+#endif
+
+#endif
+
diff --git a/src/common/di_ops.c b/src/common/di_ops.c
index 7c0b4e7630..90e9357c8e 100644
--- a/src/common/di_ops.c
+++ b/src/common/di_ops.c
@@ -148,7 +148,7 @@ struct di_digest256_map_t {
/** Release all storage held in <b>map</b>, calling free_fn on each value
* as we go. */
void
-dimap_free(di_digest256_map_t *map, dimap_free_fn free_fn)
+dimap_free_(di_digest256_map_t *map, dimap_free_fn free_fn)
{
while (map) {
di_digest256_map_t *victim = map;
diff --git a/src/common/di_ops.h b/src/common/di_ops.h
index e79973ba52..67d9c9f0df 100644
--- a/src/common/di_ops.h
+++ b/src/common/di_ops.h
@@ -37,7 +37,12 @@ int safe_mem_is_zero(const void *mem, size_t sz);
typedef struct di_digest256_map_t di_digest256_map_t;
typedef void (*dimap_free_fn)(void *);
-void dimap_free(di_digest256_map_t *map, dimap_free_fn free_fn);
+void dimap_free_(di_digest256_map_t *map, dimap_free_fn free_fn);
+#define dimap_free(map, free_fn) \
+ do { \
+ dimap_free_((map), (free_fn)); \
+ (map) = NULL; \
+ } while (0)
void dimap_add_entry(di_digest256_map_t **map,
const uint8_t *key, void *val);
void *dimap_search(const di_digest256_map_t *map, const uint8_t *key,
diff --git a/src/common/handles.h b/src/common/handles.h
index a610753a1c..aef8cd89ef 100644
--- a/src/common/handles.h
+++ b/src/common/handles.h
@@ -59,7 +59,7 @@
#define HANDLE_DECL(name, structname, linkage) \
typedef struct name ## _handle_t name ## _handle_t; \
linkage name ## _handle_t *name ## _handle_new(struct structname *object); \
- linkage void name ## _handle_free(name ## _handle_t *); \
+ linkage void name ## _handle_free_(name ## _handle_t *); \
linkage struct structname *name ## _handle_get(name ## _handle_t *); \
linkage void name ## _handles_clear(struct structname *object);
@@ -113,7 +113,7 @@
} \
\
linkage void \
- name ## _handle_free(struct name ## _handle_t *ref) \
+ name ## _handle_free_(struct name ## _handle_t *ref) \
{ \
if (! ref) return; \
name ## _handle_head_t *head = ref->head; \
diff --git a/src/common/include.am b/src/common/include.am
index 715ec0264c..6945285108 100644
--- a/src/common/include.am
+++ b/src/common/include.am
@@ -102,11 +102,6 @@ LIBOR_A_SRC = \
$(threads_impl_source) \
$(readpassphrase_source)
-if USE_RUST
-else
-LIBOR_A_SRC += src/common/compat_rust.c
-endif
-
src/common/src_common_libor_testing_a-log.$(OBJEXT) \
src/common/log.$(OBJEXT): micro-revision.i
@@ -119,6 +114,8 @@ LIBOR_CRYPTO_A_SRC = \
src/common/compress_zlib.c \
src/common/compress_zstd.c \
src/common/crypto.c \
+ src/common/crypto_rsa.c \
+ src/common/crypto_openssl_mgt.c \
src/common/crypto_pwbox.c \
src/common/crypto_s2k.c \
src/common/crypto_format.c \
@@ -158,7 +155,6 @@ COMMONHEADERS = \
src/common/compat.h \
src/common/compat_libevent.h \
src/common/compat_openssl.h \
- src/common/compat_rust.h \
src/common/compat_threads.h \
src/common/compat_time.h \
src/common/compress.h \
@@ -172,6 +168,8 @@ COMMONHEADERS = \
src/common/crypto_curve25519.h \
src/common/crypto_ed25519.h \
src/common/crypto_format.h \
+ src/common/crypto_openssl_mgt.h \
+ src/common/crypto_rsa.h \
src/common/crypto_pwbox.h \
src/common/crypto_s2k.h \
src/common/di_ops.h \
diff --git a/src/common/log.c b/src/common/log.c
index e4d5cd8fd8..9f4a8b2bc2 100644
--- a/src/common/log.c
+++ b/src/common/log.c
@@ -35,6 +35,9 @@
#define LOG_PRIVATE
#include "torlog.h"
#include "container.h"
+#ifdef HAVE_ANDROID_LOG_H
+#include <android/log.h>
+#endif // HAVE_ANDROID_LOG_H.
/** Given a severity, yields an index into log_severity_list_t.masks to use
* for that severity. */
@@ -58,12 +61,16 @@ typedef struct logfile_t {
int needs_close; /**< Boolean: true if the stream gets closed on shutdown. */
int is_temporary; /**< Boolean: close after initializing logging subsystem.*/
int is_syslog; /**< Boolean: send messages to syslog. */
+ int is_android; /**< Boolean: send messages to Android's log subsystem. */
+ char *android_tag; /**< Identity Tag used in Android's log subsystem. */
log_callback callback; /**< If not NULL, send messages to this function. */
log_severity_list_t *severities; /**< Which severity of messages should we
* log for each log domain? */
} logfile_t;
-static void log_free(logfile_t *victim);
+static void log_free_(logfile_t *victim);
+#define log_free(lg) \
+ FREE_AND_NULL(logfile_t, log_free_, (lg))
/** Helper: map a log severity to descriptive string. */
static inline const char *
@@ -101,6 +108,33 @@ should_log_function_name(log_domain_mask_t domain, int severity)
}
}
+#ifdef HAVE_ANDROID_LOG_H
+/** Helper function to convert Tor's log severity into the matching
+ * Android log priority.
+ */
+static int
+severity_to_android_log_priority(int severity)
+{
+ switch (severity) {
+ case LOG_DEBUG:
+ return ANDROID_LOG_VERBOSE;
+ case LOG_INFO:
+ return ANDROID_LOG_DEBUG;
+ case LOG_NOTICE:
+ return ANDROID_LOG_INFO;
+ case LOG_WARN:
+ return ANDROID_LOG_WARN;
+ case LOG_ERR:
+ return ANDROID_LOG_ERROR;
+ default:
+ // LCOV_EXCL_START
+ raw_assert(0);
+ return 0;
+ // LCOV_EXCL_STOP
+ }
+}
+#endif // HAVE_ANDROID_LOG_H.
+
/** A mutex to guard changes to logfiles and logging. */
static tor_mutex_t log_mutex;
/** True iff we have initialized log_mutex */
@@ -385,9 +419,12 @@ pending_log_message_new(int severity, log_domain_mask_t domain,
return m;
}
+#define pending_log_message_free(msg) \
+ FREE_AND_NULL(pending_log_message_t, pending_log_message_free_, (msg))
+
/** Release all storage held by <b>msg</b>. */
static void
-pending_log_message_free(pending_log_message_t *msg)
+pending_log_message_free_(pending_log_message_t *msg)
{
if (!msg)
return;
@@ -396,6 +433,16 @@ pending_log_message_free(pending_log_message_t *msg)
tor_free(msg);
}
+/** Helper function: returns true iff the log file, given in <b>lf</b>, is
+ * handled externally via the system log API, the Android logging API, or is an
+ * external callback function. */
+static inline int
+logfile_is_external(const logfile_t *lf)
+{
+ raw_assert(lf);
+ return lf->is_syslog || lf->is_android || lf->callback;
+}
+
/** Return true iff <b>lf</b> would like to receive a message with the
* specified <b>severity</b> in the specified <b>domain</b>.
*/
@@ -406,7 +453,7 @@ logfile_wants_message(const logfile_t *lf, int severity,
if (! (lf->severities->masks[SEVERITY_MASK_IDX(severity)] & domain)) {
return 0;
}
- if (! (lf->fd >= 0 || lf->is_syslog || lf->callback)) {
+ if (! (lf->fd >= 0 || logfile_is_external(lf))) {
return 0;
}
if (lf->seems_dead) {
@@ -449,6 +496,11 @@ logfile_deliver(logfile_t *lf, const char *buf, size_t msg_len,
syslog(severity, "%s", msg_after_prefix);
#endif /* defined(MAXLINE) */
#endif /* defined(HAVE_SYSLOG_H) */
+ } else if (lf->is_android) {
+#ifdef HAVE_ANDROID_LOG_H
+ int priority = severity_to_android_log_priority(severity);
+ __android_log_write(priority, lf->android_tag, msg_after_prefix);
+#endif // HAVE_ANDROID_LOG_H.
} else if (lf->callback) {
if (domain & LD_NOCB) {
if (!*callbacks_deferred && pending_cb_messages) {
@@ -641,8 +693,8 @@ tor_log_update_sigsafe_err_fds(void)
/* Don't try callback to the control port, or syslogs: We can't
* do them from a signal handler. Don't try stdout: we always do stderr.
*/
- if (lf->is_temporary || lf->is_syslog ||
- lf->callback || lf->seems_dead || lf->fd < 0)
+ if (lf->is_temporary || logfile_is_external(lf)
+ || lf->seems_dead || lf->fd < 0)
continue;
if (lf->severities->masks[SEVERITY_MASK_IDX(LOG_ERR)] &
(LD_BUG|LD_GENERAL)) {
@@ -678,7 +730,7 @@ tor_log_get_logfile_names(smartlist_t *out)
LOCK_LOGS();
for (lf = logfiles; lf; lf = lf->next) {
- if (lf->is_temporary || lf->is_syslog || lf->callback)
+ if (lf->is_temporary || logfile_is_external(lf))
continue;
if (lf->filename == NULL)
continue;
@@ -721,12 +773,13 @@ log_fn_ratelim_(ratelim_t *ratelim, int severity, log_domain_mask_t domain,
/** Free all storage held by <b>victim</b>. */
static void
-log_free(logfile_t *victim)
+log_free_(logfile_t *victim)
{
if (!victim)
return;
tor_free(victim->severities);
tor_free(victim->filename);
+ tor_free(victim->android_tag);
tor_free(victim);
}
@@ -1146,6 +1199,39 @@ add_syslog_log(const log_severity_list_t *severity,
}
#endif /* defined(HAVE_SYSLOG_H) */
+#ifdef HAVE_ANDROID_LOG_H
+/**
+ * Add a log handler to send messages to the Android platform log facility.
+ */
+int
+add_android_log(const log_severity_list_t *severity,
+ const char *android_tag)
+{
+ logfile_t *lf = NULL;
+
+ lf = tor_malloc_zero(sizeof(logfile_t));
+ lf->fd = -1;
+ lf->severities = tor_memdup(severity, sizeof(log_severity_list_t));
+ lf->filename = tor_strdup("<android>");
+ lf->is_android = 1;
+
+ if (android_tag == NULL)
+ lf->android_tag = tor_strdup("Tor");
+ else {
+ char buf[256];
+ tor_snprintf(buf, sizeof(buf), "Tor-%s", android_tag);
+ lf->android_tag = tor_strdup(buf);
+ }
+
+ LOCK_LOGS();
+ lf->next = logfiles;
+ logfiles = lf;
+ log_global_min_severity_ = get_min_log_level();
+ UNLOCK_LOGS();
+ return 0;
+}
+#endif // HAVE_ANDROID_LOG_H.
+
/** If <b>level</b> is a valid log severity, return the corresponding
* numeric value. Otherwise, return -1. */
int
@@ -1172,7 +1258,10 @@ log_level_to_string(int level)
}
/** NULL-terminated array of names for log domains such that domain_list[dom]
- * is a description of <b>dom</b>. */
+ * is a description of <b>dom</b>.
+ *
+ * Remember to update doc/tor.1.txt if you modify this list.
+ * */
static const char *domain_list[] = {
"GENERAL", "CRYPTO", "NET", "CONFIG", "FS", "PROTOCOL", "MM",
"HTTP", "APP", "CONTROL", "CIRC", "REND", "BUG", "DIR", "DIRSERV",
@@ -1313,7 +1402,8 @@ parse_log_severity_config(const char **cfg_ptr,
if (!strcasecmpstart(cfg, "file") ||
!strcasecmpstart(cfg, "stderr") ||
!strcasecmpstart(cfg, "stdout") ||
- !strcasecmpstart(cfg, "syslog")) {
+ !strcasecmpstart(cfg, "syslog") ||
+ !strcasecmpstart(cfg, "android")) {
goto done;
}
if (got_an_unqualified_range > 1)
diff --git a/src/common/memarea.c b/src/common/memarea.c
index b059987e0e..68c1625fe4 100644
--- a/src/common/memarea.c
+++ b/src/common/memarea.c
@@ -153,7 +153,7 @@ memarea_new(void)
/** Free <b>area</b>, invalidating all pointers returned from memarea_alloc()
* and friends for this area */
void
-memarea_drop_all(memarea_t *area)
+memarea_drop_all_(memarea_t *area)
{
memarea_chunk_t *chunk, *next;
for (chunk = area->first; chunk; chunk = next) {
@@ -322,7 +322,7 @@ memarea_new(void)
return ma;
}
void
-memarea_drop_all(memarea_t *area)
+memarea_drop_all_(memarea_t *area)
{
memarea_clear(area);
smartlist_free(area->pieces);
diff --git a/src/common/memarea.h b/src/common/memarea.h
index c3d954e1ce..5207e8a5bd 100644
--- a/src/common/memarea.h
+++ b/src/common/memarea.h
@@ -8,7 +8,12 @@
typedef struct memarea_t memarea_t;
memarea_t *memarea_new(void);
-void memarea_drop_all(memarea_t *area);
+void memarea_drop_all_(memarea_t *area);
+#define memarea_drop_all(area) \
+ do { \
+ memarea_drop_all_(area); \
+ (area) = NULL; \
+ } while (0)
void memarea_clear(memarea_t *area);
int memarea_owns_ptr(const memarea_t *area, const void *ptr);
void *memarea_alloc(memarea_t *area, size_t sz);
diff --git a/src/common/procmon.c b/src/common/procmon.c
index 26c11823e8..abcbbeaa21 100644
--- a/src/common/procmon.c
+++ b/src/common/procmon.c
@@ -325,7 +325,7 @@ tor_process_monitor_poll_cb(evutil_socket_t unused1, short unused2,
/** Free the process-termination monitor <b>procmon</b>. */
void
-tor_process_monitor_free(tor_process_monitor_t *procmon)
+tor_process_monitor_free_(tor_process_monitor_t *procmon)
{
if (procmon == NULL)
return;
diff --git a/src/common/procmon.h b/src/common/procmon.h
index 10ead11ba8..63777e4111 100644
--- a/src/common/procmon.h
+++ b/src/common/procmon.h
@@ -27,7 +27,9 @@ tor_process_monitor_t *tor_process_monitor_new(struct event_base *base,
tor_procmon_callback_t cb,
void *cb_arg,
const char **msg);
-void tor_process_monitor_free(tor_process_monitor_t *procmon);
+void tor_process_monitor_free_(tor_process_monitor_t *procmon);
+#define tor_process_monitor_free(procmon) \
+ FREE_AND_NULL(tor_process_monitor_t, tor_process_monitor_free_, (procmon))
#endif /* !defined(TOR_PROCMON_H) */
diff --git a/src/common/sandbox.c b/src/common/sandbox.c
index e311ffaa86..ca7803ac9c 100644
--- a/src/common/sandbox.c
+++ b/src/common/sandbox.c
@@ -162,6 +162,7 @@ static int filter_nopar_gen[] = {
SCMP_SYS(fstat64),
#endif
SCMP_SYS(futex),
+ SCMP_SYS(getdents),
SCMP_SYS(getdents64),
SCMP_SYS(getegid),
#ifdef __NR_getegid32
@@ -437,9 +438,9 @@ libc_uses_openat_for_everything(void)
return 1;
else
return 0;
-#else
+#else /* !(defined(CHECK_LIBC_VERSION)) */
return 0;
-#endif
+#endif /* defined(CHECK_LIBC_VERSION) */
}
/** Allow a single file to be opened. If <b>use_openat</b> is true,
@@ -1115,7 +1116,7 @@ sb_kill(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
SCMP_CMP(1, SCMP_CMP_EQ, 0));
#else
return 0;
-#endif
+#endif /* defined(__NR_kill) */
}
/**
@@ -1484,8 +1485,12 @@ cached_getaddrinfo_items_eq(const cached_getaddrinfo_item_t *a,
return (a->family == b->family) && 0 == strcmp(a->name, b->name);
}
+#define cached_getaddrinfo_item_free(item) \
+ FREE_AND_NULL(cached_getaddrinfo_item_t, \
+ cached_getaddrinfo_item_free_, (item))
+
static void
-cached_getaddrinfo_item_free(cached_getaddrinfo_item_t *item)
+cached_getaddrinfo_item_free_(cached_getaddrinfo_item_t *item)
{
if (item == NULL)
return;
@@ -1796,7 +1801,7 @@ sigsys_debugging(int nr, siginfo_t *info, void *void_context)
#endif
#if defined(DEBUGGING_CLOSE)
- _exit(1);
+ _exit(1); // exit ok: programming error has led to sandbox failure.
#endif // DEBUGGING_CLOSE
}
diff --git a/src/common/storagedir.c b/src/common/storagedir.c
index c471ea911f..e2c7b4bb87 100644
--- a/src/common/storagedir.c
+++ b/src/common/storagedir.c
@@ -59,7 +59,7 @@ storage_dir_new(const char *dirname, int max_files)
* Drop all in-RAM storage for <b>d</b>. Does not delete any files.
*/
void
-storage_dir_free(storage_dir_t *d)
+storage_dir_free_(storage_dir_t *d)
{
if (d == NULL)
return;
diff --git a/src/common/storagedir.h b/src/common/storagedir.h
index 3de0afc361..d99bd7ec52 100644
--- a/src/common/storagedir.h
+++ b/src/common/storagedir.h
@@ -9,7 +9,10 @@ struct config_line_t;
struct sandbox_cfg_elem;
storage_dir_t * storage_dir_new(const char *dirname, int n_files);
-void storage_dir_free(storage_dir_t *d);
+void storage_dir_free_(storage_dir_t *d);
+#define storage_dir_free(d) \
+ FREE_AND_NULL(storage_dir_t, storage_dir_free_, (d))
+
int storage_dir_register_with_sandbox(storage_dir_t *d,
struct sandbox_cfg_elem **cfg);
const smartlist_t *storage_dir_list(storage_dir_t *d);
diff --git a/src/common/timers.c b/src/common/timers.c
index c8e09414f4..552080b11e 100644
--- a/src/common/timers.c
+++ b/src/common/timers.c
@@ -63,7 +63,7 @@ struct timeout_cb {
#define TIMEOUT_CB_OVERRIDE
/* We're going to support timers that are pretty far out in advance. Making
* this big can be inefficient, but having a significant number of timers
- * above TIMEOUT_MAX can also be super-inefficent. Choosing 5 here sets
+ * above TIMEOUT_MAX can also be super-inefficient. Choosing 5 here sets
* timeout_max to 2^30 ticks, or 29 hours with our value for USEC_PER_TICK */
#define WHEEL_NUM 5
#include "src/ext/timeouts/timeout.c"
@@ -245,7 +245,7 @@ timer_new(timer_cb_fn_t cb, void *arg)
* scheduled.
*/
void
-timer_free(tor_timer_t *t)
+timer_free_(tor_timer_t *t)
{
if (! t)
return;
diff --git a/src/common/timers.h b/src/common/timers.h
index d4d4fb00a9..6d27f3e01e 100644
--- a/src/common/timers.h
+++ b/src/common/timers.h
@@ -17,7 +17,8 @@ void timer_get_cb(const tor_timer_t *t,
timer_cb_fn_t *cb_out, void **arg_out);
void timer_schedule(tor_timer_t *t, const struct timeval *delay);
void timer_disable(tor_timer_t *t);
-void timer_free(tor_timer_t *t);
+void timer_free_(tor_timer_t *t);
+#define timer_free(t) FREE_AND_NULL(tor_timer_t, timer_free_, (t))
void timers_initialize(void);
void timers_shutdown(void);
diff --git a/src/common/torint.h b/src/common/torint.h
index bc81c114f8..0b8061d24f 100644
--- a/src/common/torint.h
+++ b/src/common/torint.h
@@ -348,6 +348,16 @@ typedef uint32_t uintptr_t;
#endif /* (SIZEOF_SIZE_T == 4) || ... */
#endif /* !defined(SIZE_MAX) */
+#ifdef _WIN32
+# ifdef _WIN64
+# define TOR_PRIuSZ PRIu64
+# else
+# define TOR_PRIuSZ PRIu32
+# endif
+#else
+# define TOR_PRIuSZ "zu"
+#endif
+
#ifndef SSIZE_MAX
#if (SIZEOF_SIZE_T == 4)
#define SSIZE_MAX INT32_MAX
diff --git a/src/common/torlog.h b/src/common/torlog.h
index be24b2b908..cadfe3b879 100644
--- a/src/common/torlog.h
+++ b/src/common/torlog.h
@@ -148,7 +148,11 @@ int add_file_log(const log_severity_list_t *severity, const char *filename,
#ifdef HAVE_SYSLOG_H
int add_syslog_log(const log_severity_list_t *severity,
const char* syslog_identity_tag);
-#endif
+#endif // HAVE_SYSLOG_H.
+#ifdef HAVE_ANDROID_LOG_H
+int add_android_log(const log_severity_list_t *severity,
+ const char *android_identity_tag);
+#endif // HAVE_ANDROID_LOG_H.
int add_callback_log(const log_severity_list_t *severity, log_callback cb);
void logs_set_domain_logging(int enabled);
int get_min_log_level(void);
diff --git a/src/common/tortls.c b/src/common/tortls.c
index 03c0be1cd0..388c97e3a3 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -669,7 +669,7 @@ static const char CLIENT_CIPHER_LIST[] =
/** Free all storage held in <b>cert</b> */
void
-tor_x509_cert_free(tor_x509_cert_t *cert)
+tor_x509_cert_free_(tor_x509_cert_t *cert)
{
if (! cert)
return;
@@ -1819,7 +1819,7 @@ tor_tls_is_server(tor_tls_t *tls)
* underlying file descriptor.
*/
void
-tor_tls_free(tor_tls_t *tls)
+tor_tls_free_(tor_tls_t *tls)
{
if (!tls)
return;
@@ -1966,7 +1966,7 @@ tor_tls_handshake(tor_tls_t *tls)
return r;
}
-/** Perform the final part of the intial TLS handshake on <b>tls</b>. This
+/** Perform the final part of the initial TLS handshake on <b>tls</b>. This
* should be called for the first handshake only: it determines whether the v1
* or the v2 handshake was used, and adjusts things for the renegotiation
* handshake as appropriate.
diff --git a/src/common/tortls.h b/src/common/tortls.h
index 6145f7dbc9..1dbf0b332f 100644
--- a/src/common/tortls.h
+++ b/src/common/tortls.h
@@ -216,7 +216,8 @@ void tor_tls_set_renegotiate_callback(tor_tls_t *tls,
void (*cb)(tor_tls_t *, void *arg),
void *arg);
int tor_tls_is_server(tor_tls_t *tls);
-void tor_tls_free(tor_tls_t *tls);
+void tor_tls_free_(tor_tls_t *tls);
+#define tor_tls_free(tls) FREE_AND_NULL(tor_tls_t, tor_tls_free_, (tls))
int tor_tls_peer_has_cert(tor_tls_t *tls);
MOCK_DECL(tor_x509_cert_t *,tor_tls_get_peer_cert,(tor_tls_t *tls));
MOCK_DECL(tor_x509_cert_t *,tor_tls_get_own_cert,(tor_tls_t *tls));
@@ -263,7 +264,9 @@ void check_no_tls_errors_(const char *fname, int line);
void tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
int severity, int domain, const char *doing);
-void tor_x509_cert_free(tor_x509_cert_t *cert);
+void tor_x509_cert_free_(tor_x509_cert_t *cert);
+#define tor_x509_cert_free(c) \
+ FREE_AND_NULL(tor_x509_cert_t, tor_x509_cert_free_, (c))
tor_x509_cert_t *tor_x509_cert_decode(const uint8_t *certificate,
size_t certificate_len);
void tor_x509_cert_get_der(const tor_x509_cert_t *cert,
diff --git a/src/common/util.c b/src/common/util.c
index 5ff7e104d6..a68fd30d09 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -156,7 +156,7 @@ tor_malloc_(size_t size DMALLOC_PARAMS)
/* If these functions die within a worker process, they won't call
* spawn_exit, but that's ok, since the parent will run out of memory soon
* anyway. */
- exit(1);
+ exit(1); // exit ok: alloc failed.
/* LCOV_EXCL_STOP */
}
return result;
@@ -244,7 +244,7 @@ tor_realloc_(void *ptr, size_t size DMALLOC_PARAMS)
if (PREDICT_UNLIKELY(result == NULL)) {
/* LCOV_EXCL_START */
log_err(LD_MM,"Out of memory on realloc(). Dying.");
- exit(1);
+ exit(1); // exit ok: alloc failed.
/* LCOV_EXCL_STOP */
}
return result;
@@ -282,7 +282,7 @@ tor_strdup_(const char *s DMALLOC_PARAMS)
if (PREDICT_UNLIKELY(duplicate == NULL)) {
/* LCOV_EXCL_START */
log_err(LD_MM,"Out of memory on strdup(). Dying.");
- exit(1);
+ exit(1); // exit ok: alloc failed.
/* LCOV_EXCL_STOP */
}
return duplicate;
@@ -1071,6 +1071,36 @@ string_is_valid_ipv6_address(const char *string)
return (tor_inet_pton(AF_INET6,string,&addr) == 1);
}
+/** Return true iff <b>string</b> is a valid destination address,
+ * i.e. either a DNS hostname or IPv4/IPv6 address string.
+ */
+int
+string_is_valid_dest(const char *string)
+{
+ char *tmp = NULL;
+ int retval;
+ size_t len;
+
+ if (string == NULL)
+ return 0;
+
+ len = strlen(string);
+
+ if (len == 0)
+ return 0;
+
+ if (string[0] == '[' && string[len - 1] == ']')
+ string = tmp = tor_strndup(string + 1, len - 2);
+
+ retval = string_is_valid_ipv4_address(string) ||
+ string_is_valid_ipv6_address(string) ||
+ string_is_valid_nonrfc_hostname(string);
+
+ tor_free(tmp);
+
+ return retval;
+}
+
/** Return true iff <b>string</b> matches a pattern of DNS names
* that we allow Tor clients to connect to.
*
@@ -1078,37 +1108,51 @@ string_is_valid_ipv6_address(const char *string)
* with misconfigured zones that have been encountered in the wild.
*/
int
-string_is_valid_hostname(const char *string)
+string_is_valid_nonrfc_hostname(const char *string)
{
int result = 1;
+ int has_trailing_dot;
+ char *last_label;
smartlist_t *components;
+ if (!string || strlen(string) == 0)
+ return 0;
+
+ if (string_is_valid_ipv4_address(string))
+ return 0;
+
components = smartlist_new();
smartlist_split_string(components,string,".",0,0);
+ if (BUG(smartlist_len(components) == 0))
+ return 0; // LCOV_EXCL_LINE should be impossible given the earlier checks.
+
+ /* Allow a single terminating '.' used rarely to indicate domains
+ * are FQDNs rather than relative. */
+ last_label = (char *)smartlist_get(components,
+ smartlist_len(components) - 1);
+ has_trailing_dot = (last_label[0] == '\0');
+ if (has_trailing_dot) {
+ smartlist_pop_last(components);
+ tor_free(last_label);
+ last_label = NULL;
+ }
+
SMARTLIST_FOREACH_BEGIN(components, char *, c) {
if ((c[0] == '-') || (*c == '_')) {
result = 0;
break;
}
- /* Allow a single terminating '.' used rarely to indicate domains
- * are FQDNs rather than relative. */
- if ((c_sl_idx > 0) && (c_sl_idx + 1 == c_sl_len) && !*c) {
- continue;
- }
-
do {
- if ((*c >= 'a' && *c <= 'z') ||
- (*c >= 'A' && *c <= 'Z') ||
- (*c >= '0' && *c <= '9') ||
- (*c == '-') || (*c == '_'))
- c++;
- else
- result = 0;
+ result = (TOR_ISALNUM(*c) || (*c == '-') || (*c == '_'));
+ c++;
} while (result && *c);
+ if (result == 0) {
+ break;
+ }
} SMARTLIST_FOREACH_END(c);
SMARTLIST_FOREACH_BEGIN(components, char *, c) {
@@ -3047,7 +3091,7 @@ unescape_string(const char *s, char **result, size_t *size_out)
/** Removes enclosing quotes from <b>path</b> and unescapes quotes between the
* enclosing quotes. Backslashes are not unescaped. Return the unquoted
- * <b>path</b> on sucess or 0 if <b>path</b> is not quoted correctly. */
+ * <b>path</b> on success or 0 if <b>path</b> is not quoted correctly. */
char *
get_unquoted_path(const char *path)
{
@@ -3590,14 +3634,14 @@ start_daemon(void)
if (pipe(daemon_filedes)) {
/* LCOV_EXCL_START */
log_err(LD_GENERAL,"pipe failed; exiting. Error was %s", strerror(errno));
- exit(1);
+ exit(1); // exit ok: during daemonize, pipe failed.
/* LCOV_EXCL_STOP */
}
pid = fork();
if (pid < 0) {
/* LCOV_EXCL_START */
log_err(LD_GENERAL,"fork failed. Exiting.");
- exit(1);
+ exit(1); // exit ok: during daemonize, fork failed
/* LCOV_EXCL_STOP */
}
if (pid) { /* Parent */
@@ -3612,9 +3656,9 @@ start_daemon(void)
}
fflush(stdout);
if (ok == 1)
- exit(0);
+ exit(0); // exit ok: during daemonize, daemonizing.
else
- exit(1); /* child reported error */
+ exit(1); /* child reported error. exit ok: daemonize failed. */
} else { /* Child */
close(daemon_filedes[0]); /* we only write */
@@ -3626,7 +3670,7 @@ start_daemon(void)
* _Advanced Programming in the Unix Environment_.
*/
if (fork() != 0) {
- exit(0);
+ exit(0); // exit ok: during daemonize, fork failed (2)
}
set_main_thread(); /* We are now the main thread. */
@@ -3655,14 +3699,14 @@ finish_daemon(const char *desired_cwd)
/* Don't hold the wrong FS mounted */
if (chdir(desired_cwd) < 0) {
log_err(LD_GENERAL,"chdir to \"%s\" failed. Exiting.",desired_cwd);
- exit(1);
+ exit(1); // exit ok: during daemonize, chdir failed.
}
nullfd = tor_open_cloexec("/dev/null", O_RDWR, 0);
if (nullfd < 0) {
/* LCOV_EXCL_START */
log_err(LD_GENERAL,"/dev/null can't be opened. Exiting.");
- exit(1);
+ exit(1); // exit ok: during daemonize, couldn't open /dev/null
/* LCOV_EXCL_STOP */
}
/* close fds linking to invoking terminal, but
@@ -3674,7 +3718,7 @@ finish_daemon(const char *desired_cwd)
dup2(nullfd,2) < 0) {
/* LCOV_EXCL_START */
log_err(LD_GENERAL,"dup2 failed. Exiting.");
- exit(1);
+ exit(1); // exit ok: during daemonize, dup2 failed.
/* LCOV_EXCL_STOP */
}
if (nullfd > 2)
@@ -3898,7 +3942,7 @@ format_number_sigsafe(unsigned long x, char *buf, int buf_len,
* call it with a signed int and an unsigned char, and since the C standard
* does not guarantee that an int is wider than a char (an int must be at
* least 16 bits but it is permitted for a char to be that wide as well), we
- * can't assume a signed int is sufficient to accomodate an unsigned char.
+ * can't assume a signed int is sufficient to accommodate an unsigned char.
* Thus, format_helper_exit_status() will still need to emit any require '-'
* on its own.
*
@@ -3928,7 +3972,7 @@ format_dec_number_sigsafe(unsigned long x, char *buf, int buf_len)
*
* The format of <b>hex_errno</b> is: "CHILD_STATE/ERRNO\n", left-padded
* with spaces. CHILD_STATE indicates where
- * in the processs of starting the child process did the failure occur (see
+ * in the process of starting the child process did the failure occur (see
* CHILD_STATE_* macros for definition), and SAVED_ERRNO is the value of
* errno when the failure occurred.
*
@@ -4474,7 +4518,7 @@ tor_spawn_background(const char *const filename, const char **argv,
err += (nbytes < 0);
}
- _exit(err?254:255);
+ _exit(err?254:255); // exit ok: in child.
}
/* Never reached, but avoids compiler warning */
@@ -4713,7 +4757,7 @@ environment_variable_names_equal(const char *s1, const char *s2)
/** Free <b>env</b> (assuming it was produced by
* process_environment_make). */
void
-process_environment_free(process_environment_t *env)
+process_environment_free_(process_environment_t *env)
{
if (env == NULL) return;
diff --git a/src/common/util.h b/src/common/util.h
index 6bc853da26..9380789128 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -79,13 +79,28 @@ extern int dmalloc_free(const char *file, const int line, void *pnt,
*
* This is a macro. If you need a function pointer to release memory from
* tor_malloc(), use tor_free_().
+ *
+ * Note that this macro takes the address of the pointer it is going to
+ * free and clear. If that pointer is stored with a nonstandard
+ * alignment (eg because of a "packed" pragma) it is not correct to use
+ * tor_free().
*/
+#ifdef __GNUC__
+#define tor_free(p) STMT_BEGIN \
+ typeof(&(p)) tor_free__tmpvar = &(p); \
+ if (PREDICT_LIKELY((*tor_free__tmpvar)!=NULL)) { \
+ raw_free(*tor_free__tmpvar); \
+ *tor_free__tmpvar=NULL; \
+ } \
+ STMT_END
+#else
#define tor_free(p) STMT_BEGIN \
if (PREDICT_LIKELY((p)!=NULL)) { \
raw_free(p); \
(p)=NULL; \
} \
STMT_END
+#endif
#endif /* defined(USE_DMALLOC) */
#define tor_malloc(size) tor_malloc_(size DMALLOC_ARGS)
@@ -109,6 +124,17 @@ extern int dmalloc_free(const char *file, const int line, void *pnt,
void tor_log_mallinfo(int severity);
+/* Helper macro: free a variable of type 'typename' using freefn, and
+ * set the variable to NULL.
+ */
+#define FREE_AND_NULL(typename, freefn, var) \
+ do { \
+ /* only evaluate (var) once. */ \
+ typename **tmp__free__ptr ## freefn = &(var); \
+ freefn(*tmp__free__ptr ## freefn); \
+ (*tmp__free__ptr ## freefn) = NULL; \
+ } while (0)
+
/** Macro: yield a pointer to the field at position <b>off</b> within the
* structure <b>st</b>. Example:
* <pre>
@@ -207,7 +233,8 @@ const char *find_str_at_start_of_line(const char *haystack,
const char *needle);
int string_is_C_identifier(const char *string);
int string_is_key_value(int severity, const char *string);
-int string_is_valid_hostname(const char *string);
+int string_is_valid_dest(const char *string);
+int string_is_valid_nonrfc_hostname(const char *string);
int string_is_valid_ipv4_address(const char *string);
int string_is_valid_ipv6_address(const char *string);
@@ -423,7 +450,9 @@ struct process_environment_t {
};
process_environment_t *process_environment_make(struct smartlist_t *env_vars);
-void process_environment_free(process_environment_t *env);
+void process_environment_free_(process_environment_t *env);
+#define process_environment_free(env) \
+ FREE_AND_NULL(process_environment_t, process_environment_free_, (env))
struct smartlist_t *get_current_process_environment_variables(void);
diff --git a/src/common/workqueue.c b/src/common/workqueue.c
index 42723224d3..ec96959b7d 100644
--- a/src/common/workqueue.c
+++ b/src/common/workqueue.c
@@ -148,12 +148,15 @@ workqueue_entry_new(workqueue_reply_t (*fn)(void*, void*),
return ent;
}
+#define workqueue_entry_free(ent) \
+ FREE_AND_NULL(workqueue_entry_t, workqueue_entry_free_, (ent))
+
/**
* Release all storage held in <b>ent</b>. Call only when <b>ent</b> is not on
* any queue.
*/
static void
-workqueue_entry_free(workqueue_entry_t *ent)
+workqueue_entry_free_(workqueue_entry_t *ent)
{
if (!ent)
return;
diff --git a/src/ext/csiphash.c b/src/ext/csiphash.c
index 508e4f6ceb..0427c87950 100644
--- a/src/ext/csiphash.c
+++ b/src/ext/csiphash.c
@@ -123,3 +123,9 @@ void siphash_set_global_key(const struct sipkey *key)
the_siphash_key.k1 = key->k1;
the_siphash_key_is_set = 1;
}
+
+void siphash_unset_global_key(void)
+{
+ the_siphash_key_is_set = 0;
+ memset(&the_siphash_key, 0, sizeof(the_siphash_key));
+}
diff --git a/src/ext/ed25519/donna/ed25519_tor.c b/src/ext/ed25519/donna/ed25519_tor.c
index 44ec562f02..84fc3850a2 100644
--- a/src/ext/ed25519/donna/ed25519_tor.c
+++ b/src/ext/ed25519/donna/ed25519_tor.c
@@ -132,7 +132,7 @@ ED25519_FN(curved25519_scalarmult_basepoint) (curved25519_key pk, const curved25
}
/*
- Tor has a specific idea of how an Ed25519 implementaion should behave.
+ Tor has a specific idea of how an Ed25519 implementation should behave.
Implement such a beast using the ed25519-donna primitives/internals.
* Private key generation using Tor's CSPRNG.
diff --git a/src/ext/rust b/src/ext/rust
-Subproject 240296800824e40b10cb8c16da0e71156335394
+Subproject fbc0c25785696a25b9cbc09ed645cc8d404ee0f
diff --git a/src/ext/siphash.h b/src/ext/siphash.h
index d9b34b8980..730e49937d 100644
--- a/src/ext/siphash.h
+++ b/src/ext/siphash.h
@@ -9,5 +9,6 @@ uint64_t siphash24(const void *src, unsigned long src_sz, const struct sipkey *k
void siphash_set_global_key(const struct sipkey *key);
uint64_t siphash24g(const void *src, unsigned long src_sz);
+void siphash_unset_global_key(void);
#endif
diff --git a/src/or/addressmap.c b/src/or/addressmap.c
index 7e92633601..96ce275578 100644
--- a/src/or/addressmap.c
+++ b/src/or/addressmap.c
@@ -90,34 +90,47 @@ addressmap_init(void)
virtaddress_reversemap = strmap_new();
}
+#define addressmap_ent_free(ent) \
+ FREE_AND_NULL(addressmap_entry_t, addressmap_ent_free_, (ent))
+
/** Free the memory associated with the addressmap entry <b>_ent</b>. */
static void
-addressmap_ent_free(void *_ent)
+addressmap_ent_free_(addressmap_entry_t *ent)
{
- addressmap_entry_t *ent;
- if (!_ent)
+ if (!ent)
return;
- ent = _ent;
tor_free(ent->new_address);
tor_free(ent);
}
+static void
+addressmap_ent_free_void(void *ent)
+{
+ addressmap_ent_free_(ent);
+}
+
+#define addressmap_virtaddress_ent_free(ent) \
+ FREE_AND_NULL(virtaddress_entry_t, addressmap_virtaddress_ent_free_, (ent))
+
/** Free storage held by a virtaddress_entry_t* entry in <b>_ent</b>. */
static void
-addressmap_virtaddress_ent_free(void *_ent)
+addressmap_virtaddress_ent_free_(virtaddress_entry_t *ent)
{
- virtaddress_entry_t *ent;
- if (!_ent)
+ if (!ent)
return;
-
- ent = _ent;
tor_free(ent->ipv4_address);
tor_free(ent->ipv6_address);
tor_free(ent->hostname_address);
tor_free(ent);
}
+static void
+addressmap_virtaddress_ent_free_void(void *ent)
+{
+ addressmap_virtaddress_ent_free_(ent);
+}
+
/** Remove <b>address</b> (which must map to <b>ent</b>) from the
* virtual address map. */
static void
@@ -311,10 +324,10 @@ addressmap_clean(time_t now)
void
addressmap_free_all(void)
{
- strmap_free(addressmap, addressmap_ent_free);
+ strmap_free(addressmap, addressmap_ent_free_void);
addressmap = NULL;
- strmap_free(virtaddress_reversemap, addressmap_virtaddress_ent_free);
+ strmap_free(virtaddress_reversemap, addressmap_virtaddress_ent_free_void);
virtaddress_reversemap = NULL;
}
@@ -541,7 +554,7 @@ addressmap_have_mapping(const char *address, int update_expiry)
* (virtual address mapping) from the controller.)
*
* <b>new_address</b> should be a newly dup'ed string, which we'll use or
- * free as appropriate. We will leave address alone.
+ * free as appropriate. We will leave <b>address</b> alone.
*
* If <b>wildcard_addr</b> is true, then the mapping will match any address
* equal to <b>address</b>, or any address ending with a period followed by
@@ -554,7 +567,6 @@ addressmap_have_mapping(const char *address, int update_expiry)
* <b>wildcard_new_addr</b>, remove any mappings that exist from
* <b>address</b>.
*
- *
* It is an error to set <b>wildcard_new_addr</b> if <b>wildcard_addr</b> is
* not set. */
void
diff --git a/src/or/bridges.c b/src/or/bridges.c
index 0b1bbbd158..29d00f37ba 100644
--- a/src/or/bridges.c
+++ b/src/or/bridges.c
@@ -53,7 +53,10 @@ struct bridge_info_t {
smartlist_t *socks_args;
};
-static void bridge_free(bridge_info_t *bridge);
+#define bridge_free(bridge) \
+ FREE_AND_NULL(bridge_info_t, bridge_free_, (bridge))
+
+static void bridge_free_(bridge_info_t *bridge);
static void rewrite_node_address_for_bridge(const bridge_info_t *bridge,
node_t *node);
@@ -101,7 +104,7 @@ clear_bridge_list(void)
/** Free the bridge <b>bridge</b>. */
static void
-bridge_free(bridge_info_t *bridge)
+bridge_free_(bridge_info_t *bridge)
{
if (!bridge)
return;
@@ -353,7 +356,7 @@ bridge_resolve_conflicts(const tor_addr_t *addr, uint16_t port,
{
/* Iterate the already-registered bridge list:
- If you find a bridge with the same adress and port, mark it for
+ If you find a bridge with the same address and port, mark it for
removal. It doesn't make sense to have two active bridges with
the same IP:PORT. If the bridge in question has a different
digest or transport than <b>digest</b>/<b>transport_name</b>,
@@ -455,7 +458,6 @@ bridge_add_from_config(bridge_line_t *bridge_line)
if (bridge_line->transport_name)
b->transport_name = bridge_line->transport_name;
b->fetch_status.schedule = DL_SCHED_BRIDGE;
- b->fetch_status.backoff = DL_SCHED_RANDOM_EXPONENTIAL;
b->fetch_status.increment_on = DL_SCHED_INCREMENT_ATTEMPT;
/* We can't reset the bridge's download status here, because UseBridges
* might be 0 now, and it might be changed to 1 much later. */
@@ -634,8 +636,7 @@ fetch_bridge_descriptors(const or_options_t *options, time_t now)
SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge)
{
/* This resets the download status on first use */
- if (!download_status_is_ready(&bridge->fetch_status, now,
- IMPOSSIBLE_TO_DOWNLOAD))
+ if (!download_status_is_ready(&bridge->fetch_status, now))
continue; /* don't bother, no need to retry yet */
if (routerset_contains_bridge(options->ExcludeNodes, bridge)) {
download_status_mark_impossible(&bridge->fetch_status);
@@ -716,7 +717,6 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node)
if (node->ri) {
routerinfo_t *ri = node->ri;
tor_addr_from_ipv4h(&addr, ri->addr);
-
if ((!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) &&
bridge->port == ri->or_port) ||
(!tor_addr_compare(&bridge->addr, &ri->ipv6_addr, CMP_EXACT) &&
@@ -774,16 +774,58 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node)
routerstatus_t *rs = node->rs;
tor_addr_from_ipv4h(&addr, rs->addr);
- if (!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) &&
- bridge->port == rs->or_port) {
+ if ((!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) &&
+ bridge->port == rs->or_port) ||
+ (!tor_addr_compare(&bridge->addr, &rs->ipv6_addr, CMP_EXACT) &&
+ bridge->port == rs->ipv6_orport)) {
/* they match, so no need to do anything */
} else {
- rs->addr = tor_addr_to_ipv4h(&bridge->addr);
- rs->or_port = bridge->port;
- log_info(LD_DIR,
- "Adjusted bridge routerstatus for '%s' to match "
- "configured address %s.",
- rs->nickname, fmt_addrport(&bridge->addr, rs->or_port));
+ if (tor_addr_family(&bridge->addr) == AF_INET) {
+ rs->addr = tor_addr_to_ipv4h(&bridge->addr);
+ rs->or_port = bridge->port;
+ log_info(LD_DIR,
+ "Adjusted bridge routerstatus for '%s' to match "
+ "configured address %s.",
+ rs->nickname, fmt_addrport(&bridge->addr, rs->or_port));
+ /* set IPv6 preferences even if there is no ri */
+ } else if (tor_addr_family(&bridge->addr) == AF_INET6) {
+ tor_addr_copy(&rs->ipv6_addr, &bridge->addr);
+ rs->ipv6_orport = bridge->port;
+ log_info(LD_DIR,
+ "Adjusted bridge routerstatus for '%s' to match configured"
+ " address %s.",
+ rs->nickname, fmt_addrport(&rs->ipv6_addr, rs->ipv6_orport));
+ } else {
+ log_err(LD_BUG, "Address family not supported: %d.",
+ tor_addr_family(&bridge->addr));
+ return;
+ }
+ }
+
+ if (options->ClientPreferIPv6ORPort == -1) {
+ /* Mark which address to use based on which bridge_t we got. */
+ node->ipv6_preferred = (tor_addr_family(&bridge->addr) == AF_INET6 &&
+ !tor_addr_is_null(&node->rs->ipv6_addr));
+ } else {
+ /* Mark which address to use based on user preference */
+ node->ipv6_preferred = (fascist_firewall_prefer_ipv6_orport(options) &&
+ !tor_addr_is_null(&node->rs->ipv6_addr));
+ }
+
+ /* XXXipv6 we lack support for falling back to another address for
+ the same relay, warn the user */
+ if (!tor_addr_is_null(&rs->ipv6_addr)) {
+ tor_addr_port_t ap;
+ node_get_pref_orport(node, &ap);
+ log_notice(LD_CONFIG,
+ "Bridge '%s' has both an IPv4 and an IPv6 address. "
+ "Will prefer using its %s address (%s) based on %s.",
+ rs->nickname,
+ node->ipv6_preferred ? "IPv6" : "IPv4",
+ fmt_addrport(&ap.addr, ap.port),
+ options->ClientPreferIPv6ORPort == -1 ?
+ "the configured Bridge address" :
+ "ClientPreferIPv6ORPort");
}
}
}
diff --git a/src/or/channel.c b/src/or/channel.c
index 3c0025aff6..a4740dd752 100644
--- a/src/or/channel.c
+++ b/src/or/channel.c
@@ -1,3 +1,4 @@
+
/* * Copyright (c) 2012-2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
@@ -5,9 +6,8 @@
* \file channel.c
*
* \brief OR/OP-to-OR channel abstraction layer. A channel's job is to
- * transfer cells from Tor instance to Tor instance.
- * Currently, there is only one implementation of the channel abstraction: in
- * channeltls.c.
+ * transfer cells from Tor instance to Tor instance. Currently, there is only
+ * one implementation of the channel abstraction: in channeltls.c.
*
* Channels are a higher-level abstraction than or_connection_t: In general,
* any means that two Tor relays use to exchange cells, or any means that a
@@ -24,23 +24,34 @@
* connection.
*
* Every channel implementation is responsible for being able to transmit
- * cells that are added to it with channel_write_cell() and related functions,
- * and to receive incoming cells with the channel_queue_cell() and related
- * functions. See the channel_t documentation for more information.
- *
- * When new cells arrive on a channel, they are passed to cell handler
- * functions, which can be set by channel_set_cell_handlers()
- * functions. (Tor's cell handlers are in command.c.)
- *
- * Tor flushes cells to channels from relay.c in
- * channel_flush_from_first_active_circuit().
+ * cells that are passed to it
+ *
+ * For *inbound* cells, the entry point is: channel_process_cell(). It takes a
+ * cell and will pass it to the cell handler set by
+ * channel_set_cell_handlers(). Currently, this is passed back to the command
+ * subsystem which is command_process_cell().
+ *
+ * NOTE: For now, the separation between channels and specialized channels
+ * (like channeltls) is not that well defined. So the channeltls layer calls
+ * channel_process_cell() which originally comes from the connection subsytem.
+ * This should be hopefully be fixed with #23993.
+ *
+ * For *outbound* cells, the entry point is: channel_write_packed_cell().
+ * Only packed cells are dequeued from the circuit queue by the scheduler
+ * which uses channel_flush_from_first_active_circuit() to decide which cells
+ * to flush from which circuit on the channel. They are then passed down to
+ * the channel subsystem. This calls the low layer with the function pointer
+ * .write_packed_cell().
+ *
+ * Each specialized channel (currently only channeltls_t) MUST implement a
+ * series of function found in channel_t. See channel.h for more
+ * documentation.
**/
/*
* Define this so channel.h gives us things only channel_t subclasses
* should touch.
*/
-
#define TOR_CHANNEL_INTERNAL_
/* This one's for stuff only channel.c and the test suite should see */
@@ -112,59 +123,6 @@ HANDLE_IMPL(channel, channel_s,)
/* Counter for ID numbers */
static uint64_t n_channels_allocated = 0;
-/*
- * Channel global byte/cell counters, for statistics and for scheduler high
- * /low-water marks.
- */
-
-/*
- * Total number of cells ever given to any channel with the
- * channel_write_*_cell() functions.
- */
-
-static uint64_t n_channel_cells_queued = 0;
-
-/*
- * Total number of cells ever passed to a channel lower layer with the
- * write_*_cell() methods.
- */
-
-static uint64_t n_channel_cells_passed_to_lower_layer = 0;
-
-/*
- * Current number of cells in all channel queues; should be
- * n_channel_cells_queued - n_channel_cells_passed_to_lower_layer.
- */
-
-static uint64_t n_channel_cells_in_queues = 0;
-
-/*
- * Total number of bytes for all cells ever queued to a channel and
- * counted in n_channel_cells_queued.
- */
-
-static uint64_t n_channel_bytes_queued = 0;
-
-/*
- * Total number of bytes for all cells ever passed to a channel lower layer
- * and counted in n_channel_cells_passed_to_lower_layer.
- */
-
-static uint64_t n_channel_bytes_passed_to_lower_layer = 0;
-
-/*
- * Current number of bytes in all channel queues; should be
- * n_channel_bytes_queued - n_channel_bytes_passed_to_lower_layer.
- */
-
-static uint64_t n_channel_bytes_in_queues = 0;
-
-/*
- * Current total estimated queue size *including lower layer queues and
- * transmit overhead*
- */
-
-STATIC uint64_t estimated_total_queue_size = 0;
/* Digest->channel map
*
@@ -201,49 +159,23 @@ HT_PROTOTYPE(channel_idmap, channel_idmap_entry_s, node, channel_idmap_hash,
HT_GENERATE2(channel_idmap, channel_idmap_entry_s, node, channel_idmap_hash,
channel_idmap_eq, 0.5, tor_reallocarray_, tor_free_)
-static cell_queue_entry_t * cell_queue_entry_dup(cell_queue_entry_t *q);
-#if 0
-static int cell_queue_entry_is_padding(cell_queue_entry_t *q);
-#endif
-static cell_queue_entry_t *
-cell_queue_entry_new_fixed(cell_t *cell);
-static cell_queue_entry_t *
-cell_queue_entry_new_var(var_cell_t *var_cell);
-static int is_destroy_cell(channel_t *chan,
- const cell_queue_entry_t *q, circid_t *circid_out);
-
-static void channel_assert_counter_consistency(void);
-
/* Functions to maintain the digest map */
-static void channel_add_to_digest_map(channel_t *chan);
static void channel_remove_from_digest_map(channel_t *chan);
-/*
- * Flush cells from just the outgoing queue without trying to get them
- * from circuits; used internall by channel_flush_some_cells().
- */
-static ssize_t
-channel_flush_some_cells_from_outgoing_queue(channel_t *chan,
- ssize_t num_cells);
-static void channel_force_free(channel_t *chan);
-static void
-channel_free_list(smartlist_t *channels, int mark_for_close);
-static void
-channel_listener_free_list(smartlist_t *channels, int mark_for_close);
-static void channel_listener_force_free(channel_listener_t *chan_l);
-static size_t channel_get_cell_queue_entry_size(channel_t *chan,
- cell_queue_entry_t *q);
-static void
-channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q);
+static void channel_force_xfree(channel_t *chan);
+static void channel_free_list(smartlist_t *channels,
+ int mark_for_close);
+static void channel_listener_free_list(smartlist_t *channels,
+ int mark_for_close);
+static void channel_listener_force_xfree(channel_listener_t *chan_l);
/***********************************
* Channel state utility functions *
**********************************/
/**
- * Indicate whether a given channel state is valid
+ * Indicate whether a given channel state is valid.
*/
-
int
channel_state_is_valid(channel_state_t state)
{
@@ -267,9 +199,8 @@ channel_state_is_valid(channel_state_t state)
}
/**
- * Indicate whether a given channel listener state is valid
+ * Indicate whether a given channel listener state is valid.
*/
-
int
channel_listener_state_is_valid(channel_listener_state_t state)
{
@@ -291,13 +222,12 @@ channel_listener_state_is_valid(channel_listener_state_t state)
}
/**
- * Indicate whether a channel state transition is valid
+ * Indicate whether a channel state transition is valid.
*
* This function takes two channel states and indicates whether a
* transition between them is permitted (see the state definitions and
* transition table in or.h at the channel_state_t typedef).
*/
-
int
channel_state_can_transition(channel_state_t from, channel_state_t to)
{
@@ -338,13 +268,12 @@ channel_state_can_transition(channel_state_t from, channel_state_t to)
}
/**
- * Indicate whether a channel listener state transition is valid
+ * Indicate whether a channel listener state transition is valid.
*
* This function takes two channel listener states and indicates whether a
* transition between them is permitted (see the state definitions and
* transition table in or.h at the channel_listener_state_t typedef).
*/
-
int
channel_listener_state_can_transition(channel_listener_state_t from,
channel_listener_state_t to)
@@ -375,9 +304,8 @@ channel_listener_state_can_transition(channel_listener_state_t from,
}
/**
- * Return a human-readable description for a channel state
+ * Return a human-readable description for a channel state.
*/
-
const char *
channel_state_to_string(channel_state_t state)
{
@@ -411,9 +339,8 @@ channel_state_to_string(channel_state_t state)
}
/**
- * Return a human-readable description for a channel listenier state
+ * Return a human-readable description for a channel listener state.
*/
-
const char *
channel_listener_state_to_string(channel_listener_state_t state)
{
@@ -445,12 +372,11 @@ channel_listener_state_to_string(channel_listener_state_t state)
***************************************/
/**
- * Register a channel
+ * Register a channel.
*
* This function registers a newly created channel in the global lists/maps
* of active channels.
*/
-
void
channel_register(channel_t *chan)
{
@@ -503,12 +429,11 @@ channel_register(channel_t *chan)
}
/**
- * Unregister a channel
+ * Unregister a channel.
*
* This function removes a channel from the global lists and maps and is used
* when freeing a closed/errored channel.
*/
-
void
channel_unregister(channel_t *chan)
{
@@ -543,12 +468,11 @@ channel_unregister(channel_t *chan)
}
/**
- * Register a channel listener
+ * Register a channel listener.
*
- * This function registers a newly created channel listner in the global
+ * This function registers a newly created channel listener in the global
* lists/maps of active channel listeners.
*/
-
void
channel_listener_register(channel_listener_t *chan_l)
{
@@ -585,12 +509,11 @@ channel_listener_register(channel_listener_t *chan_l)
}
/**
- * Unregister a channel listener
+ * Unregister a channel listener.
*
* This function removes a channel listener from the global lists and maps
* and is used when freeing a closed/errored channel listener.
*/
-
void
channel_listener_unregister(channel_listener_t *chan_l)
{
@@ -621,14 +544,13 @@ channel_listener_unregister(channel_listener_t *chan_l)
*********************************/
/**
- * Add a channel to the digest map
+ * Add a channel to the digest map.
*
* This function adds a channel to the digest map and inserts it into the
* correct linked list if channels with that remote endpoint identity digest
* already exist.
*/
-
-static void
+STATIC void
channel_add_to_digest_map(channel_t *chan)
{
channel_idmap_entry_t *ent, search;
@@ -660,12 +582,11 @@ channel_add_to_digest_map(channel_t *chan)
}
/**
- * Remove a channel from the digest map
+ * Remove a channel from the digest map.
*
* This function removes a channel from the digest map and the linked list of
* channels for that digest if more than one exists.
*/
-
static void
channel_remove_from_digest_map(channel_t *chan)
{
@@ -676,33 +597,6 @@ channel_remove_from_digest_map(channel_t *chan)
/* Assert that there is a digest */
tor_assert(!tor_digest_is_zero(chan->identity_digest));
-#if 0
- /* Make sure we have a map */
- if (!channel_identity_map) {
- /*
- * No identity map, so we can't find it by definition. This
- * case is similar to digestmap_get() failing below.
- */
- log_warn(LD_BUG,
- "Trying to remove channel %p (global ID " U64_FORMAT ") "
- "with digest %s from identity map, but didn't have any identity "
- "map",
- chan, U64_PRINTF_ARG(chan->global_identifier),
- hex_str(chan->identity_digest, DIGEST_LEN));
- /* Clear out its next/prev pointers */
- if (chan->next_with_same_id) {
- chan->next_with_same_id->prev_with_same_id = chan->prev_with_same_id;
- }
- if (chan->prev_with_same_id) {
- chan->prev_with_same_id->next_with_same_id = chan->next_with_same_id;
- }
- chan->next_with_same_id = NULL;
- chan->prev_with_same_id = NULL;
-
- return;
- }
-#endif /* 0 */
-
/* Pull it out of its list, wherever that list is */
TOR_LIST_REMOVE(chan, next_with_same_id);
@@ -740,13 +634,12 @@ channel_remove_from_digest_map(channel_t *chan)
***************************/
/**
- * Find channel by global ID
+ * Find channel by global ID.
*
* This function searches for a channel by the global_identifier assigned
* at initialization time. This identifier is unique for the lifetime of the
* Tor process.
*/
-
channel_t *
channel_find_by_global_id(uint64_t global_identifier)
{
@@ -784,7 +677,7 @@ channel_remote_identity_matches(const channel_t *chan,
}
/**
- * Find channel by RSA/Ed25519 identity of of the remote endpoint
+ * Find channel by RSA/Ed25519 identity of of the remote endpoint.
*
* This function looks up a channel by the digest of its remote endpoint's RSA
* identity key. If <b>ed_id</b> is provided and nonzero, only a channel
@@ -823,12 +716,11 @@ channel_find_by_remote_identity(const char *rsa_id_digest,
}
/**
- * Get next channel with digest
+ * Get next channel with digest.
*
* This function takes a channel and finds the next channel in the list
* with the same digest.
*/
-
channel_t *
channel_next_with_rsa_identity(channel_t *chan)
{
@@ -915,13 +807,12 @@ channel_check_for_duplicates(void)
}
/**
- * Initialize a channel
+ * Initialize a channel.
*
* This function should be called by subclasses to set up some per-channel
* variables. I.e., this is the superclass constructor. Before this, the
* channel should be allocated with tor_malloc_zero().
*/
-
void
channel_init(channel_t *chan)
{
@@ -936,10 +827,6 @@ channel_init(channel_t *chan)
/* Warn about exhausted circuit IDs no more than hourly. */
chan->last_warned_circ_ids_exhausted.rate = 3600;
- /* Initialize queues. */
- TOR_SIMPLEQ_INIT(&chan->incoming_queue);
- TOR_SIMPLEQ_INIT(&chan->outgoing_queue);
-
/* Initialize list entries. */
memset(&chan->next_with_same_id, 0, sizeof(chan->next_with_same_id));
@@ -957,13 +844,12 @@ channel_init(channel_t *chan)
}
/**
- * Initialize a channel listener
+ * Initialize a channel listener.
*
* This function should be called by subclasses to set up some per-channel
* variables. I.e., this is the superclass constructor. Before this, the
* channel listener should be allocated with tor_malloc_zero().
*/
-
void
channel_init_listener(channel_listener_t *chan_l)
{
@@ -980,9 +866,8 @@ channel_init_listener(channel_listener_t *chan_l)
* Free a channel; nothing outside of channel.c and subclasses should call
* this - it frees channels after they have closed and been unregistered.
*/
-
void
-channel_free(channel_t *chan)
+channel_free_(channel_t *chan)
{
if (!chan) return;
@@ -1025,8 +910,6 @@ channel_free(channel_t *chan)
chan->cmux = NULL;
}
- /* We're in CLOSED or ERROR, so the cell queue is already empty */
-
tor_free(chan);
}
@@ -1035,9 +918,8 @@ channel_free(channel_t *chan)
* should call this - it frees channel listeners after they have closed and
* been unregistered.
*/
-
void
-channel_listener_free(channel_listener_t *chan_l)
+channel_listener_free_(channel_listener_t *chan_l)
{
if (!chan_l) return;
@@ -1055,11 +937,6 @@ channel_listener_free(channel_listener_t *chan_l)
/* Call a free method if there is one */
if (chan_l->free_fn) chan_l->free_fn(chan_l);
- /*
- * We're in CLOSED or ERROR, so the incoming channel queue is already
- * empty.
- */
-
tor_free(chan_l);
}
@@ -1068,11 +945,9 @@ channel_listener_free(channel_listener_t *chan_l)
* use-only function should be called only from channel_free_all() when
* shutting down the Tor process.
*/
-
static void
-channel_force_free(channel_t *chan)
+channel_force_xfree(channel_t *chan)
{
- cell_queue_entry_t *cell, *cell_tmp;
tor_assert(chan);
log_debug(LD_CHANNEL,
@@ -1106,29 +981,16 @@ channel_force_free(channel_t *chan)
chan->cmux = NULL;
}
- /* We might still have a cell queue; kill it */
- TOR_SIMPLEQ_FOREACH_SAFE(cell, &chan->incoming_queue, next, cell_tmp) {
- cell_queue_entry_free(cell, 0);
- }
- TOR_SIMPLEQ_INIT(&chan->incoming_queue);
-
- /* Outgoing cell queue is similar, but we can have to free packed cells */
- TOR_SIMPLEQ_FOREACH_SAFE(cell, &chan->outgoing_queue, next, cell_tmp) {
- cell_queue_entry_free(cell, 0);
- }
- TOR_SIMPLEQ_INIT(&chan->outgoing_queue);
-
tor_free(chan);
}
/**
- * Free a channel listener and skip the state/reigstration asserts; this
+ * Free a channel listener and skip the state/registration asserts; this
* internal-use-only function should be called only from channel_free_all()
* when shutting down the Tor process.
*/
-
static void
-channel_listener_force_free(channel_listener_t *chan_l)
+channel_listener_force_xfree(channel_listener_t *chan_l)
{
tor_assert(chan_l);
@@ -1159,30 +1021,11 @@ channel_listener_force_free(channel_listener_t *chan_l)
}
/**
- * Return the current registered listener for a channel listener
- *
- * This function returns a function pointer to the current registered
- * handler for new incoming channels on a channel listener.
- */
-
-channel_listener_fn_ptr
-channel_listener_get_listener_fn(channel_listener_t *chan_l)
-{
- tor_assert(chan_l);
-
- if (chan_l->state == CHANNEL_LISTENER_STATE_LISTENING)
- return chan_l->listener;
-
- return NULL;
-}
-
-/**
- * Set the listener for a channel listener
+ * Set the listener for a channel listener.
*
* This function sets the handler for new incoming channels on a channel
* listener.
*/
-
void
channel_listener_set_listener_fn(channel_listener_t *chan_l,
channel_listener_fn_ptr listener)
@@ -1201,12 +1044,11 @@ channel_listener_set_listener_fn(channel_listener_t *chan_l,
}
/**
- * Return the fixed-length cell handler for a channel
+ * Return the fixed-length cell handler for a channel.
*
* This function gets the handler for incoming fixed-length cells installed
* on a channel.
*/
-
channel_cell_handler_fn_ptr
channel_get_cell_handler(channel_t *chan)
{
@@ -1219,12 +1061,11 @@ channel_get_cell_handler(channel_t *chan)
}
/**
- * Return the variable-length cell handler for a channel
+ * Return the variable-length cell handler for a channel.
*
* This function gets the handler for incoming variable-length cells
* installed on a channel.
*/
-
channel_var_cell_handler_fn_ptr
channel_get_var_cell_handler(channel_t *chan)
{
@@ -1237,21 +1078,17 @@ channel_get_var_cell_handler(channel_t *chan)
}
/**
- * Set both cell handlers for a channel
+ * Set both cell handlers for a channel.
*
* This function sets both the fixed-length and variable length cell handlers
- * for a channel and processes any incoming cells that had been blocked in the
- * queue because none were available.
+ * for a channel.
*/
-
void
channel_set_cell_handlers(channel_t *chan,
channel_cell_handler_fn_ptr cell_handler,
channel_var_cell_handler_fn_ptr
var_cell_handler)
{
- int try_again = 0;
-
tor_assert(chan);
tor_assert(CHANNEL_CAN_HANDLE_CELLS(chan));
@@ -1262,21 +1099,9 @@ channel_set_cell_handlers(channel_t *chan,
"Setting var_cell_handler callback for channel %p to %p",
chan, var_cell_handler);
- /* Should we try the queue? */
- if (cell_handler &&
- cell_handler != chan->cell_handler) try_again = 1;
- if (var_cell_handler &&
- var_cell_handler != chan->var_cell_handler) try_again = 1;
-
/* Change them */
chan->cell_handler = cell_handler;
chan->var_cell_handler = var_cell_handler;
-
- /* Re-run the queue if we have one and there's any reason to */
- if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue) &&
- try_again &&
- (chan->cell_handler ||
- chan->var_cell_handler)) channel_process_cells(chan);
}
/*
@@ -1293,13 +1118,12 @@ channel_set_cell_handlers(channel_t *chan,
*/
/**
- * Mark a channel for closure
+ * Mark a channel for closure.
*
* This function tries to close a channel_t; it will go into the CLOSING
* state, and eventually the lower layer should put it into the CLOSED or
* ERROR state. Then, channel_run_cleanup() will eventually free it.
*/
-
void
channel_mark_for_close(channel_t *chan)
{
@@ -1333,13 +1157,12 @@ channel_mark_for_close(channel_t *chan)
}
/**
- * Mark a channel listener for closure
+ * Mark a channel listener for closure.
*
* This function tries to close a channel_listener_t; it will go into the
* CLOSING state, and eventually the lower layer should put it into the CLOSED
* or ERROR state. Then, channel_run_cleanup() will eventually free it.
*/
-
void
channel_listener_mark_for_close(channel_listener_t *chan_l)
{
@@ -1374,13 +1197,12 @@ channel_listener_mark_for_close(channel_listener_t *chan_l)
}
/**
- * Close a channel from the lower layer
+ * Close a channel from the lower layer.
*
* Notify the channel code that the channel is being closed due to a non-error
* condition in the lower layer. This does not call the close() method, since
* the lower layer already knows.
*/
-
void
channel_close_from_lower_layer(channel_t *chan)
{
@@ -1403,43 +1225,12 @@ channel_close_from_lower_layer(channel_t *chan)
}
/**
- * Close a channel listener from the lower layer
- *
- * Notify the channel code that the channel listener is being closed due to a
- * non-error condition in the lower layer. This does not call the close()
- * method, since the lower layer already knows.
- */
-
-void
-channel_listener_close_from_lower_layer(channel_listener_t *chan_l)
-{
- tor_assert(chan_l != NULL);
-
- /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */
- if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSING ||
- chan_l->state == CHANNEL_LISTENER_STATE_CLOSED ||
- chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return;
-
- log_debug(LD_CHANNEL,
- "Closing channel listener %p (global ID " U64_FORMAT ") "
- "due to lower-layer event",
- chan_l, U64_PRINTF_ARG(chan_l->global_identifier));
-
- /* Note closing by event from below */
- chan_l->reason_for_closing = CHANNEL_LISTENER_CLOSE_FROM_BELOW;
-
- /* Change state to CLOSING */
- channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSING);
-}
-
-/**
- * Notify that the channel is being closed due to an error condition
+ * Notify that the channel is being closed due to an error condition.
*
* This function is called by the lower layer implementing the transport
* when a channel must be closed due to an error condition. This does not
* call the channel's close method, since the lower layer already knows.
*/
-
void
channel_close_for_error(channel_t *chan)
{
@@ -1461,44 +1252,12 @@ channel_close_for_error(channel_t *chan)
}
/**
- * Notify that the channel listener is being closed due to an error condition
- *
- * This function is called by the lower layer implementing the transport
- * when a channel listener must be closed due to an error condition. This
- * does not call the channel listener's close method, since the lower layer
- * already knows.
- */
-
-void
-channel_listener_close_for_error(channel_listener_t *chan_l)
-{
- tor_assert(chan_l != NULL);
-
- /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */
- if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSING ||
- chan_l->state == CHANNEL_LISTENER_STATE_CLOSED ||
- chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return;
-
- log_debug(LD_CHANNEL,
- "Closing channel listener %p (global ID " U64_FORMAT ") "
- "due to lower-layer error",
- chan_l, U64_PRINTF_ARG(chan_l->global_identifier));
-
- /* Note closing by event from below */
- chan_l->reason_for_closing = CHANNEL_LISTENER_CLOSE_FOR_ERROR;
-
- /* Change state to CLOSING */
- channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSING);
-}
-
-/**
- * Notify that the lower layer is finished closing the channel
+ * Notify that the lower layer is finished closing the channel.
*
* This function should be called by the lower layer when a channel
* is finished closing and it should be regarded as inactive and
* freed by the channel code.
*/
-
void
channel_closed(channel_t *chan)
{
@@ -1525,39 +1284,11 @@ channel_closed(channel_t *chan)
}
/**
- * Notify that the lower layer is finished closing the channel listener
- *
- * This function should be called by the lower layer when a channel listener
- * is finished closing and it should be regarded as inactive and
- * freed by the channel code.
- */
-
-void
-channel_listener_closed(channel_listener_t *chan_l)
-{
- tor_assert(chan_l);
- tor_assert(chan_l->state == CHANNEL_LISTENER_STATE_CLOSING ||
- chan_l->state == CHANNEL_LISTENER_STATE_CLOSED ||
- chan_l->state == CHANNEL_LISTENER_STATE_ERROR);
-
- /* No-op if already inactive */
- if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSED ||
- chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return;
-
- if (chan_l->reason_for_closing != CHANNEL_LISTENER_CLOSE_FOR_ERROR) {
- channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSED);
- } else {
- channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_ERROR);
- }
-}
-
-/**
- * Clear the identity_digest of a channel
+ * Clear the identity_digest of a channel.
*
* This function clears the identity digest of the remote endpoint for a
* channel; this is intended for use by the lower layer.
*/
-
void
channel_clear_identity_digest(channel_t *chan)
{
@@ -1582,7 +1313,7 @@ channel_clear_identity_digest(channel_t *chan)
}
/**
- * Set the identity_digest of a channel
+ * Set the identity_digest of a channel.
*
* This function sets the identity digest of the remote endpoint for a
* channel; this is intended for use by the lower layer.
@@ -1641,12 +1372,11 @@ channel_set_identity_digest(channel_t *chan,
}
/**
- * Clear the remote end metadata (identity_digest/nickname) of a channel
+ * Clear the remote end metadata (identity_digest) of a channel.
*
* This function clears all the remote end info from a channel; this is
* intended for use by the lower layer.
*/
-
void
channel_clear_remote_end(channel_t *chan)
{
@@ -1668,429 +1398,103 @@ channel_clear_remote_end(channel_t *chan)
memset(chan->identity_digest, 0,
sizeof(chan->identity_digest));
- tor_free(chan->nickname);
}
/**
- * Set the remote end metadata (identity_digest/nickname) of a channel
+ * Write to a channel the given packed cell.
*
- * This function sets new remote end info on a channel; this is intended
- * for use by the lower layer.
- */
-
-void
-channel_set_remote_end(channel_t *chan,
- const char *identity_digest,
- const char *nickname)
-{
- int was_in_digest_map, should_be_in_digest_map, state_not_in_map;
-
- tor_assert(chan);
-
- log_debug(LD_CHANNEL,
- "Setting remote endpoint identity on channel %p with "
- "global ID " U64_FORMAT " to nickname %s, digest %s",
- chan, U64_PRINTF_ARG(chan->global_identifier),
- nickname ? nickname : "(null)",
- identity_digest ?
- hex_str(identity_digest, DIGEST_LEN) : "(null)");
-
- state_not_in_map = CHANNEL_CONDEMNED(chan);
-
- was_in_digest_map =
- !state_not_in_map &&
- chan->registered &&
- !tor_digest_is_zero(chan->identity_digest);
- should_be_in_digest_map =
- !state_not_in_map &&
- chan->registered &&
- (identity_digest &&
- !tor_digest_is_zero(identity_digest));
-
- if (was_in_digest_map)
- /* We should always remove it; we'll add it back if we're writing
- * in a new digest.
- */
- channel_remove_from_digest_map(chan);
-
- if (identity_digest) {
- memcpy(chan->identity_digest,
- identity_digest,
- sizeof(chan->identity_digest));
-
- } else {
- memset(chan->identity_digest, 0,
- sizeof(chan->identity_digest));
- }
-
- tor_free(chan->nickname);
- if (nickname)
- chan->nickname = tor_strdup(nickname);
-
- /* Put it in the digest map if we should */
- if (should_be_in_digest_map)
- channel_add_to_digest_map(chan);
-}
-
-/**
- * Duplicate a cell queue entry; this is a shallow copy intended for use
- * in channel_write_cell_queue_entry().
- */
-
-static cell_queue_entry_t *
-cell_queue_entry_dup(cell_queue_entry_t *q)
-{
- cell_queue_entry_t *rv = NULL;
-
- tor_assert(q);
-
- rv = tor_malloc(sizeof(*rv));
- memcpy(rv, q, sizeof(*rv));
-
- return rv;
-}
-
-/**
- * Free a cell_queue_entry_t; the handed_off parameter indicates whether
- * the contents were passed to the lower layer (it is responsible for
- * them) or not (we should free).
+ * Two possible errors can happen. Either the channel is not opened or the
+ * lower layer (specialized channel) failed to write it. In both cases, it is
+ * the caller responsibility to free the cell.
*/
-
-STATIC void
-cell_queue_entry_free(cell_queue_entry_t *q, int handed_off)
-{
- if (!q) return;
-
- if (!handed_off) {
- /*
- * If we handed it off, the recipient becomes responsible (or
- * with packed cells the channel_t subclass calls packed_cell
- * free after writing out its contents; see, e.g.,
- * channel_tls_write_packed_cell_method(). Otherwise, we have
- * to take care of it here if possible.
- */
- switch (q->type) {
- case CELL_QUEUE_FIXED:
- if (q->u.fixed.cell) {
- /*
- * There doesn't seem to be a cell_free() function anywhere in the
- * pre-channel code; just use tor_free()
- */
- tor_free(q->u.fixed.cell);
- }
- break;
- case CELL_QUEUE_PACKED:
- if (q->u.packed.packed_cell) {
- packed_cell_free(q->u.packed.packed_cell);
- }
- break;
- case CELL_QUEUE_VAR:
- if (q->u.var.var_cell) {
- /*
- * This one's in connection_or.c; it'd be nice to figure out the
- * whole flow of cells from one end to the other and factor the
- * cell memory management functions like this out of the specific
- * TLS lower layer.
- */
- var_cell_free(q->u.var.var_cell);
- }
- break;
- default:
- /*
- * Nothing we can do if we don't know the type; this will
- * have been warned about elsewhere.
- */
- break;
- }
- }
- tor_free(q);
-}
-
-#if 0
-/**
- * Check whether a cell queue entry is padding; this is a helper function
- * for channel_write_cell_queue_entry()
- */
-
static int
-cell_queue_entry_is_padding(cell_queue_entry_t *q)
-{
- tor_assert(q);
-
- if (q->type == CELL_QUEUE_FIXED) {
- if (q->u.fixed.cell) {
- if (q->u.fixed.cell->command == CELL_PADDING ||
- q->u.fixed.cell->command == CELL_VPADDING) {
- return 1;
- }
- }
- } else if (q->type == CELL_QUEUE_VAR) {
- if (q->u.var.var_cell) {
- if (q->u.var.var_cell->command == CELL_PADDING ||
- q->u.var.var_cell->command == CELL_VPADDING) {
- return 1;
- }
- }
- }
-
- return 0;
-}
-#endif /* 0 */
-
-/**
- * Allocate a new cell queue entry for a fixed-size cell
- */
-
-static cell_queue_entry_t *
-cell_queue_entry_new_fixed(cell_t *cell)
+write_packed_cell(channel_t *chan, packed_cell_t *cell)
{
- cell_queue_entry_t *q = NULL;
-
- tor_assert(cell);
-
- q = tor_malloc(sizeof(*q));
- q->type = CELL_QUEUE_FIXED;
- q->u.fixed.cell = cell;
-
- return q;
-}
-
-/**
- * Allocate a new cell queue entry for a variable-size cell
- */
-
-static cell_queue_entry_t *
-cell_queue_entry_new_var(var_cell_t *var_cell)
-{
- cell_queue_entry_t *q = NULL;
-
- tor_assert(var_cell);
-
- q = tor_malloc(sizeof(*q));
- q->type = CELL_QUEUE_VAR;
- q->u.var.var_cell = var_cell;
-
- return q;
-}
-
-/**
- * Ask how big the cell contained in a cell_queue_entry_t is
- */
-
-static size_t
-channel_get_cell_queue_entry_size(channel_t *chan, cell_queue_entry_t *q)
-{
- size_t rv = 0;
-
- tor_assert(chan);
- tor_assert(q);
-
- switch (q->type) {
- case CELL_QUEUE_FIXED:
- rv = get_cell_network_size(chan->wide_circ_ids);
- break;
- case CELL_QUEUE_VAR:
- rv = get_var_cell_header_size(chan->wide_circ_ids) +
- (q->u.var.var_cell ? q->u.var.var_cell->payload_len : 0);
- break;
- case CELL_QUEUE_PACKED:
- rv = get_cell_network_size(chan->wide_circ_ids);
- break;
- default:
- tor_assert_nonfatal_unreached_once();
- }
-
- return rv;
-}
-
-/**
- * Write to a channel based on a cell_queue_entry_t
- *
- * Given a cell_queue_entry_t filled out by the caller, try to send the cell
- * and queue it if we can't.
- */
-
-static void
-channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q)
-{
- int result = 0, sent = 0;
- cell_queue_entry_t *tmp = NULL;
+ int ret = -1;
size_t cell_bytes;
tor_assert(chan);
- tor_assert(q);
+ tor_assert(cell);
/* Assert that the state makes sense for a cell write */
tor_assert(CHANNEL_CAN_HANDLE_CELLS(chan));
{
circid_t circ_id;
- if (is_destroy_cell(chan, q, &circ_id)) {
+ if (packed_cell_is_destroy(chan, cell, &circ_id)) {
channel_note_destroy_not_pending(chan, circ_id);
}
}
/* For statistical purposes, figure out how big this cell is */
- cell_bytes = channel_get_cell_queue_entry_size(chan, q);
+ cell_bytes = get_cell_network_size(chan->wide_circ_ids);
/* Can we send it right out? If so, try */
- if (TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue) &&
- CHANNEL_IS_OPEN(chan)) {
- /* Pick the right write function for this cell type and save the result */
- switch (q->type) {
- case CELL_QUEUE_FIXED:
- tor_assert(chan->write_cell);
- tor_assert(q->u.fixed.cell);
- result = chan->write_cell(chan, q->u.fixed.cell);
- break;
- case CELL_QUEUE_PACKED:
- tor_assert(chan->write_packed_cell);
- tor_assert(q->u.packed.packed_cell);
- result = chan->write_packed_cell(chan, q->u.packed.packed_cell);
- break;
- case CELL_QUEUE_VAR:
- tor_assert(chan->write_var_cell);
- tor_assert(q->u.var.var_cell);
- result = chan->write_var_cell(chan, q->u.var.var_cell);
- break;
- default:
- tor_assert(1);
- }
-
- /* Check if we got it out */
- if (result > 0) {
- sent = 1;
- /* Timestamp for transmission */
- channel_timestamp_xmit(chan);
- /* If we're here the queue is empty, so it's drained too */
- channel_timestamp_drained(chan);
- /* Update the counter */
- ++(chan->n_cells_xmitted);
- chan->n_bytes_xmitted += cell_bytes;
- /* Update global counters */
- ++n_channel_cells_queued;
- ++n_channel_cells_passed_to_lower_layer;
- n_channel_bytes_queued += cell_bytes;
- n_channel_bytes_passed_to_lower_layer += cell_bytes;
- channel_assert_counter_consistency();
- }
+ if (!CHANNEL_IS_OPEN(chan)) {
+ goto done;
}
- if (!sent) {
- /* Not sent, queue it */
- /*
- * We have to copy the queue entry passed in, since the caller probably
- * used the stack.
- */
- tmp = cell_queue_entry_dup(q);
- TOR_SIMPLEQ_INSERT_TAIL(&chan->outgoing_queue, tmp, next);
- /* Update global counters */
- ++n_channel_cells_queued;
- ++n_channel_cells_in_queues;
- n_channel_bytes_queued += cell_bytes;
- n_channel_bytes_in_queues += cell_bytes;
- channel_assert_counter_consistency();
- /* Update channel queue size */
- chan->bytes_in_queue += cell_bytes;
- /* Try to process the queue? */
- if (CHANNEL_IS_OPEN(chan)) channel_flush_cells(chan);
+ /* Write the cell on the connection's outbuf. */
+ if (chan->write_packed_cell(chan, cell) < 0) {
+ goto done;
}
+ /* Timestamp for transmission */
+ channel_timestamp_xmit(chan);
+ /* Update the counter */
+ ++(chan->n_cells_xmitted);
+ chan->n_bytes_xmitted += cell_bytes;
+ /* Successfully sent the cell. */
+ ret = 0;
+
+ done:
+ return ret;
}
-/** Write a generic cell type to a channel
+/**
+ * Write a packed cell to a channel.
+ *
+ * Write a packed cell to a channel using the write_cell() method. This is
+ * called by the transport-independent code to deliver a packed cell to a
+ * channel for transmission.
*
- * Write a generic cell to a channel. It is called by channel_write_cell(),
- * channel_write_var_cell() and channel_write_packed_cell() in order to reduce
- * code duplication. Notice that it takes cell as pointer of type void,
- * this can be dangerous because no type check is performed.
+ * Return 0 on success else a negative value. In both cases, the caller should
+ * not access the cell anymore, it is freed both on success and error.
*/
-
-void
-channel_write_cell_generic_(channel_t *chan, const char *cell_type,
- void *cell, cell_queue_entry_t *q)
+int
+channel_write_packed_cell(channel_t *chan, packed_cell_t *cell)
{
+ int ret = -1;
tor_assert(chan);
tor_assert(cell);
if (CHANNEL_IS_CLOSING(chan)) {
- log_debug(LD_CHANNEL, "Discarding %c %p on closing channel %p with "
- "global ID "U64_FORMAT, *cell_type, cell, chan,
+ log_debug(LD_CHANNEL, "Discarding %p on closing channel %p with "
+ "global ID "U64_FORMAT, cell, chan,
U64_PRINTF_ARG(chan->global_identifier));
- tor_free(cell);
- return;
+ goto end;
}
log_debug(LD_CHANNEL,
- "Writing %c %p to channel %p with global ID "
- U64_FORMAT, *cell_type,
- cell, chan, U64_PRINTF_ARG(chan->global_identifier));
-
- channel_write_cell_queue_entry(chan, q);
- /* Update the queue size estimate */
- channel_update_xmit_queue_size(chan);
-}
-
-/**
- * Write a cell to a channel
- *
- * Write a fixed-length cell to a channel using the write_cell() method.
- * This is equivalent to the pre-channels connection_or_write_cell_to_buf();
- * it is called by the transport-independent code to deliver a cell to a
- * channel for transmission.
- */
-
-void
-channel_write_cell(channel_t *chan, cell_t *cell)
-{
- cell_queue_entry_t q;
- q.type = CELL_QUEUE_FIXED;
- q.u.fixed.cell = cell;
- channel_write_cell_generic_(chan, "cell_t", cell, &q);
-}
+ "Writing %p to channel %p with global ID "
+ U64_FORMAT, cell, chan, U64_PRINTF_ARG(chan->global_identifier));
-/**
- * Write a packed cell to a channel
- *
- * Write a packed cell to a channel using the write_cell() method. This is
- * called by the transport-independent code to deliver a packed cell to a
- * channel for transmission.
- */
+ ret = write_packed_cell(chan, cell);
-void
-channel_write_packed_cell(channel_t *chan, packed_cell_t *packed_cell)
-{
- cell_queue_entry_t q;
- q.type = CELL_QUEUE_PACKED;
- q.u.packed.packed_cell = packed_cell;
- channel_write_cell_generic_(chan, "packed_cell_t", packed_cell, &q);
+ end:
+ /* Whatever happens, we free the cell. Either an error occurred or the cell
+ * was put on the connection outbuf, both cases we have ownership of the
+ * cell and we free it. */
+ packed_cell_free(cell);
+ return ret;
}
/**
- * Write a variable-length cell to a channel
- *
- * Write a variable-length cell to a channel using the write_cell() method.
- * This is equivalent to the pre-channels
- * connection_or_write_var_cell_to_buf(); it's called by the transport-
- * independent code to deliver a var_cell to a channel for transmission.
- */
-
-void
-channel_write_var_cell(channel_t *chan, var_cell_t *var_cell)
-{
- cell_queue_entry_t q;
- q.type = CELL_QUEUE_VAR;
- q.u.var.var_cell = var_cell;
- channel_write_cell_generic_(chan, "var_cell_t", var_cell, &q);
-}
-
-/**
- * Change channel state
+ * Change channel state.
*
* This internal and subclass use only function is used to change channel
* state, performing all transition validity checks and whatever actions
* are appropriate to the state transition in question.
*/
-
static void
channel_change_state_(channel_t *chan, channel_state_t to_state)
{
@@ -2122,15 +1526,6 @@ channel_change_state_(channel_t *chan, channel_state_t to_state)
tor_assert(chan->reason_for_closing != CHANNEL_NOT_CLOSING);
}
- /*
- * We need to maintain the queues here for some transitions:
- * when we enter CHANNEL_STATE_OPEN (especially from CHANNEL_STATE_MAINT)
- * we may have a backlog of cells to transmit, so drain the queues in
- * that case, and when going to CHANNEL_STATE_CLOSED the subclass
- * should have made sure to finish sending things (or gone to
- * CHANNEL_STATE_ERROR if not possible), so we assert for that here.
- */
-
log_debug(LD_CHANNEL,
"Changing state of channel %p (global ID " U64_FORMAT
") from \"%s\" to \"%s\"",
@@ -2187,36 +1582,6 @@ channel_change_state_(channel_t *chan, channel_state_t to_state)
} else if (to_state == CHANNEL_STATE_MAINT) {
scheduler_channel_doesnt_want_writes(chan);
}
-
- /*
- * If we're closing, this channel no longer counts toward the global
- * estimated queue size; if we're open, it now does.
- */
- if ((to_state == CHANNEL_STATE_CLOSING ||
- to_state == CHANNEL_STATE_CLOSED ||
- to_state == CHANNEL_STATE_ERROR) &&
- (from_state == CHANNEL_STATE_OPEN ||
- from_state == CHANNEL_STATE_MAINT)) {
- estimated_total_queue_size -= chan->bytes_in_queue;
- }
-
- /*
- * If we're opening, this channel now does count toward the global
- * estimated queue size.
- */
- if ((to_state == CHANNEL_STATE_OPEN ||
- to_state == CHANNEL_STATE_MAINT) &&
- !(from_state == CHANNEL_STATE_OPEN ||
- from_state == CHANNEL_STATE_MAINT)) {
- estimated_total_queue_size += chan->bytes_in_queue;
- }
-
- if (to_state == CHANNEL_STATE_CLOSED ||
- to_state == CHANNEL_STATE_ERROR) {
- /* Assert that all queues are empty */
- tor_assert(TOR_SIMPLEQ_EMPTY(&chan->incoming_queue));
- tor_assert(TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue));
- }
}
/**
@@ -2240,22 +1605,15 @@ channel_change_state_open(channel_t *chan)
/* Tell circuits if we opened and stuff */
channel_do_open_actions(chan);
chan->has_been_open = 1;
-
- /* Check for queued cells to process */
- if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue))
- channel_process_cells(chan);
- if (! TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue))
- channel_flush_cells(chan);
}
/**
- * Change channel listener state
+ * Change channel listener state.
*
* This internal and subclass use only function is used to change channel
* listener state, performing all transition validity checks and whatever
* actions are appropriate to the state transition in question.
*/
-
void
channel_listener_change_state(channel_listener_t *chan_l,
channel_listener_state_t to_state)
@@ -2287,15 +1645,6 @@ channel_listener_change_state(channel_listener_t *chan_l,
tor_assert(chan_l->reason_for_closing != CHANNEL_LISTENER_NOT_CLOSING);
}
- /*
- * We need to maintain the queues here for some transitions:
- * when we enter CHANNEL_STATE_OPEN (especially from CHANNEL_STATE_MAINT)
- * we may have a backlog of cells to transmit, so drain the queues in
- * that case, and when going to CHANNEL_STATE_CLOSED the subclass
- * should have made sure to finish sending things (or gone to
- * CHANNEL_STATE_ERROR if not possible), so we assert for that here.
- */
-
log_debug(LD_CHANNEL,
"Changing state of channel listener %p (global ID " U64_FORMAT
"from \"%s\" to \"%s\"",
@@ -2328,30 +1677,39 @@ channel_listener_change_state(channel_listener_t *chan_l,
if (to_state == CHANNEL_LISTENER_STATE_CLOSED ||
to_state == CHANNEL_LISTENER_STATE_ERROR) {
- /* Assert that the queue is empty */
tor_assert(!(chan_l->incoming_list) ||
smartlist_len(chan_l->incoming_list) == 0);
}
}
-/**
- * Try to flush cells to the lower layer
- *
- * this is called by the lower layer to indicate that it wants more cells;
- * it will try to write up to num_cells cells from the channel's cell queue or
- * from circuits active on that channel, or as many as it has available if
- * num_cells == -1.
- */
-
+/* Maximum number of cells that is allowed to flush at once within
+ * channel_flush_some_cells(). */
#define MAX_CELLS_TO_GET_FROM_CIRCUITS_FOR_UNLIMITED 256
+/**
+ * Try to flush cells of the given channel chan up to a maximum of num_cells.
+ *
+ * This is called by the scheduler when it wants to flush cells from the
+ * channel's circuit queue(s) to the connection outbuf (not yet on the wire).
+ *
+ * If the channel is not in state CHANNEL_STATE_OPEN, this does nothing and
+ * will return 0 meaning no cells were flushed.
+ *
+ * If num_cells is -1, we'll try to flush up to the maximum cells allowed
+ * defined in MAX_CELLS_TO_GET_FROM_CIRCUITS_FOR_UNLIMITED.
+ *
+ * On success, the number of flushed cells are returned and it can never be
+ * above num_cells. If 0 is returned, no cells were flushed either because the
+ * channel was not opened or we had no cells on the channel. A negative number
+ * can NOT be sent back.
+ *
+ * This function is part of the fast path. */
MOCK_IMPL(ssize_t,
channel_flush_some_cells, (channel_t *chan, ssize_t num_cells))
{
unsigned int unlimited = 0;
ssize_t flushed = 0;
- int num_cells_from_circs, clamped_num_cells;
- int q_len_before, q_len_after;
+ int clamped_num_cells;
tor_assert(chan);
@@ -2360,11 +1718,6 @@ channel_flush_some_cells, (channel_t *chan, ssize_t num_cells))
/* If we aren't in CHANNEL_STATE_OPEN, nothing goes through */
if (CHANNEL_IS_OPEN(chan)) {
- /* Try to flush as much as we can that's already queued */
- flushed += channel_flush_some_cells_from_outgoing_queue(chan,
- (unlimited ? -1 : num_cells - flushed));
- if (!unlimited && num_cells <= flushed) goto done;
-
if (circuitmux_num_cells(chan->cmux) > 0) {
/* Calculate number of cells, including clamp */
if (unlimited) {
@@ -2378,45 +1731,9 @@ channel_flush_some_cells, (channel_t *chan, ssize_t num_cells))
}
}
- /*
- * Keep track of the change in queue size; we have to count cells
- * channel_flush_from_first_active_circuit() writes out directly,
- * but not double-count ones we might get later in
- * channel_flush_some_cells_from_outgoing_queue()
- */
- q_len_before = chan_cell_queue_len(&(chan->outgoing_queue));
-
/* Try to get more cells from any active circuits */
- num_cells_from_circs = channel_flush_from_first_active_circuit(
+ flushed = channel_flush_from_first_active_circuit(
chan, clamped_num_cells);
-
- q_len_after = chan_cell_queue_len(&(chan->outgoing_queue));
-
- /*
- * If it claims we got some, adjust the flushed counter and consider
- * processing the queue again
- */
- if (num_cells_from_circs > 0) {
- /*
- * Adjust flushed by the number of cells counted in
- * num_cells_from_circs that didn't go to the cell queue.
- */
-
- if (q_len_after > q_len_before) {
- num_cells_from_circs -= (q_len_after - q_len_before);
- if (num_cells_from_circs < 0) num_cells_from_circs = 0;
- }
-
- flushed += num_cells_from_circs;
-
- /* Now process the queue if necessary */
-
- if ((q_len_after > q_len_before) &&
- (unlimited || (flushed < num_cells))) {
- flushed += channel_flush_some_cells_from_outgoing_queue(chan,
- (unlimited ? -1 : num_cells - flushed));
- }
- }
}
}
@@ -2425,197 +1742,16 @@ channel_flush_some_cells, (channel_t *chan, ssize_t num_cells))
}
/**
- * Flush cells from just the channel's outgoing cell queue
+ * Check if any cells are available.
*
- * This gets called from channel_flush_some_cells() above to flush cells
- * just from the queue without trying for active_circuits.
+ * This is used by the scheduler to know if the channel has more to flush
+ * after a scheduling round.
*/
-
-static ssize_t
-channel_flush_some_cells_from_outgoing_queue(channel_t *chan,
- ssize_t num_cells)
-{
- unsigned int unlimited = 0;
- ssize_t flushed = 0;
- cell_queue_entry_t *q = NULL;
- size_t cell_size;
- int free_q = 0, handed_off = 0;
-
- tor_assert(chan);
- tor_assert(chan->write_cell);
- tor_assert(chan->write_packed_cell);
- tor_assert(chan->write_var_cell);
-
- if (num_cells < 0) unlimited = 1;
- if (!unlimited && num_cells <= flushed) return 0;
-
- /* If we aren't in CHANNEL_STATE_OPEN, nothing goes through */
- if (CHANNEL_IS_OPEN(chan)) {
- while ((unlimited || num_cells > flushed) &&
- NULL != (q = TOR_SIMPLEQ_FIRST(&chan->outgoing_queue))) {
- free_q = 0;
- handed_off = 0;
-
- /* Figure out how big it is for statistical purposes */
- cell_size = channel_get_cell_queue_entry_size(chan, q);
- /*
- * Okay, we have a good queue entry, try to give it to the lower
- * layer.
- */
- switch (q->type) {
- case CELL_QUEUE_FIXED:
- if (q->u.fixed.cell) {
- if (chan->write_cell(chan,
- q->u.fixed.cell)) {
- ++flushed;
- channel_timestamp_xmit(chan);
- ++(chan->n_cells_xmitted);
- chan->n_bytes_xmitted += cell_size;
- free_q = 1;
- handed_off = 1;
- }
- /* Else couldn't write it; leave it on the queue */
- } else {
- /* This shouldn't happen */
- log_info(LD_CHANNEL,
- "Saw broken cell queue entry of type CELL_QUEUE_FIXED "
- "with no cell on channel %p "
- "(global ID " U64_FORMAT ").",
- chan, U64_PRINTF_ARG(chan->global_identifier));
- /* Throw it away */
- free_q = 1;
- handed_off = 0;
- }
- break;
- case CELL_QUEUE_PACKED:
- if (q->u.packed.packed_cell) {
- if (chan->write_packed_cell(chan,
- q->u.packed.packed_cell)) {
- ++flushed;
- channel_timestamp_xmit(chan);
- ++(chan->n_cells_xmitted);
- chan->n_bytes_xmitted += cell_size;
- free_q = 1;
- handed_off = 1;
- }
- /* Else couldn't write it; leave it on the queue */
- } else {
- /* This shouldn't happen */
- log_info(LD_CHANNEL,
- "Saw broken cell queue entry of type CELL_QUEUE_PACKED "
- "with no cell on channel %p "
- "(global ID " U64_FORMAT ").",
- chan, U64_PRINTF_ARG(chan->global_identifier));
- /* Throw it away */
- free_q = 1;
- handed_off = 0;
- }
- break;
- case CELL_QUEUE_VAR:
- if (q->u.var.var_cell) {
- if (chan->write_var_cell(chan,
- q->u.var.var_cell)) {
- ++flushed;
- channel_timestamp_xmit(chan);
- ++(chan->n_cells_xmitted);
- chan->n_bytes_xmitted += cell_size;
- free_q = 1;
- handed_off = 1;
- }
- /* Else couldn't write it; leave it on the queue */
- } else {
- /* This shouldn't happen */
- log_info(LD_CHANNEL,
- "Saw broken cell queue entry of type CELL_QUEUE_VAR "
- "with no cell on channel %p "
- "(global ID " U64_FORMAT ").",
- chan, U64_PRINTF_ARG(chan->global_identifier));
- /* Throw it away */
- free_q = 1;
- handed_off = 0;
- }
- break;
- default:
- /* Unknown type, log and free it */
- log_info(LD_CHANNEL,
- "Saw an unknown cell queue entry type %d on channel %p "
- "(global ID " U64_FORMAT "; ignoring it."
- " Someone should fix this.",
- q->type, chan, U64_PRINTF_ARG(chan->global_identifier));
- free_q = 1;
- handed_off = 0;
- }
-
- /*
- * if free_q is set, we used it and should remove the queue entry;
- * we have to do the free down here so TOR_SIMPLEQ_REMOVE_HEAD isn't
- * accessing freed memory
- */
- if (free_q) {
- TOR_SIMPLEQ_REMOVE_HEAD(&chan->outgoing_queue, next);
- /*
- * ...and we handed a cell off to the lower layer, so we should
- * update the counters.
- */
- ++n_channel_cells_passed_to_lower_layer;
- --n_channel_cells_in_queues;
- n_channel_bytes_passed_to_lower_layer += cell_size;
- n_channel_bytes_in_queues -= cell_size;
- channel_assert_counter_consistency();
- /* Update the channel's queue size too */
- chan->bytes_in_queue -= cell_size;
- /* Finally, free q */
- cell_queue_entry_free(q, handed_off);
- q = NULL;
- } else {
- /* No cell removed from list, so we can't go on any further */
- break;
- }
- }
- }
-
- /* Did we drain the queue? */
- if (TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue)) {
- channel_timestamp_drained(chan);
- }
-
- /* Update the estimate queue size */
- channel_update_xmit_queue_size(chan);
-
- return flushed;
-}
-
-/**
- * Flush as many cells as we possibly can from the queue
- *
- * This tries to flush as many cells from the queue as the lower layer
- * will take. It just calls channel_flush_some_cells_from_outgoing_queue()
- * in unlimited mode.
- */
-
-void
-channel_flush_cells(channel_t *chan)
-{
- channel_flush_some_cells_from_outgoing_queue(chan, -1);
-}
-
-/**
- * Check if any cells are available
- *
- * This gets used from the lower layer to check if any more cells are
- * available.
- */
-
MOCK_IMPL(int,
channel_more_to_flush, (channel_t *chan))
{
tor_assert(chan);
- /* Check if we have any queued */
- if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue))
- return 1;
-
- /* Check if any circuits would like to queue some */
if (circuitmux_num_cells(chan->cmux) > 0) return 1;
/* Else no */
@@ -2623,12 +1759,11 @@ channel_more_to_flush, (channel_t *chan))
}
/**
- * Notify the channel we're done flushing the output in the lower layer
+ * Notify the channel we're done flushing the output in the lower layer.
*
* Connection.c will call this when we've flushed the output; there's some
* dirreq-related maintenance to do.
*/
-
void
channel_notify_flushed(channel_t *chan)
{
@@ -2641,12 +1776,11 @@ channel_notify_flushed(channel_t *chan)
}
/**
- * Process the queue of incoming channels on a listener
+ * Process the queue of incoming channels on a listener.
*
* Use a listener's registered callback to process as many entries in the
* queue of incoming channels as possible.
*/
-
void
channel_listener_process_incoming(channel_listener_t *listener)
{
@@ -2688,18 +1822,17 @@ channel_listener_process_incoming(channel_listener_t *listener)
}
/**
- * Take actions required when a channel becomes open
+ * Take actions required when a channel becomes open.
*
* Handle actions we should do when we know a channel is open; a lot of
* this comes from the old connection_or_set_state_open() of connection_or.c.
*
* Because of this mechanism, future channel_t subclasses should take care
- * not to change a channel to from CHANNEL_STATE_OPENING to CHANNEL_STATE_OPEN
+ * not to change a channel from CHANNEL_STATE_OPENING to CHANNEL_STATE_OPEN
* until there is positive confirmation that the network is operational.
* In particular, anything UDP-based should not make this transition until a
* packet is received from the other side.
*/
-
void
channel_do_open_actions(channel_t *chan)
{
@@ -2714,11 +1847,10 @@ channel_do_open_actions(channel_t *chan)
if (started_here) {
circuit_build_times_network_is_live(get_circuit_build_times_mutable());
- rep_hist_note_connect_succeeded(chan->identity_digest, now);
router_set_status(chan->identity_digest, 1);
} else {
- /* only report it to the geoip module if it's not a known router */
- if (!connection_or_digest_is_known_relay(chan->identity_digest)) {
+ /* only report it to the geoip module if it's a client */
+ if (channel_is_client(chan)) {
if (channel_get_addr_if_possible(chan, &remote_addr)) {
char *transport_name = NULL;
channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan);
@@ -2755,7 +1887,7 @@ channel_do_open_actions(channel_t *chan)
CHANNELPADDING_SOS_PARAM,
CHANNELPADDING_SOS_DEFAULT, 0, 1)) {
/* Disable if we're using RSOS and the consensus disabled padding
- * for RSOS*/
+ * for RSOS */
channelpadding_disable_padding_on_channel(chan);
} else if (get_options()->ReducedConnectionPadding) {
/* Padding can be forced and/or reduced by clients, regardless of if
@@ -2768,13 +1900,12 @@ channel_do_open_actions(channel_t *chan)
}
/**
- * Queue an incoming channel on a listener
+ * Queue an incoming channel on a listener.
*
* Internal and subclass use only function to queue an incoming channel from
* a listener. A subclass of channel_listener_t should call this when a new
* incoming channel is created.
*/
-
void
channel_listener_queue_incoming(channel_listener_t *listener,
channel_t *incoming)
@@ -2824,207 +1955,31 @@ channel_listener_queue_incoming(channel_listener_t *listener,
}
/**
- * Process queued incoming cells
- *
- * Process as many queued cells as we can from the incoming
- * cell queue.
+ * Process a cell from the given channel.
*/
-
void
-channel_process_cells(channel_t *chan)
+channel_process_cell(channel_t *chan, cell_t *cell)
{
- cell_queue_entry_t *q;
tor_assert(chan);
tor_assert(CHANNEL_IS_CLOSING(chan) || CHANNEL_IS_MAINT(chan) ||
CHANNEL_IS_OPEN(chan));
-
- log_debug(LD_CHANNEL,
- "Processing as many incoming cells as we can for channel %p",
- chan);
-
- /* Nothing we can do if we have no registered cell handlers */
- if (!(chan->cell_handler ||
- chan->var_cell_handler)) return;
- /* Nothing we can do if we have no cells */
- if (TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) return;
-
- /*
- * Process cells until we're done or find one we have no current handler
- * for.
- *
- * We must free the cells here after calling the handler, since custody
- * of the buffer was given to the channel layer when they were queued;
- * see comments on memory management in channel_queue_cell() and in
- * channel_queue_var_cell() below.
- */
- while (NULL != (q = TOR_SIMPLEQ_FIRST(&chan->incoming_queue))) {
- tor_assert(q);
- tor_assert(q->type == CELL_QUEUE_FIXED ||
- q->type == CELL_QUEUE_VAR);
-
- if (q->type == CELL_QUEUE_FIXED &&
- chan->cell_handler) {
- /* Handle a fixed-length cell */
- TOR_SIMPLEQ_REMOVE_HEAD(&chan->incoming_queue, next);
- tor_assert(q->u.fixed.cell);
- log_debug(LD_CHANNEL,
- "Processing incoming cell_t %p for channel %p (global ID "
- U64_FORMAT ")",
- q->u.fixed.cell, chan,
- U64_PRINTF_ARG(chan->global_identifier));
- chan->cell_handler(chan, q->u.fixed.cell);
- tor_free(q->u.fixed.cell);
- tor_free(q);
- } else if (q->type == CELL_QUEUE_VAR &&
- chan->var_cell_handler) {
- /* Handle a variable-length cell */
- TOR_SIMPLEQ_REMOVE_HEAD(&chan->incoming_queue, next);
- tor_assert(q->u.var.var_cell);
- log_debug(LD_CHANNEL,
- "Processing incoming var_cell_t %p for channel %p (global ID "
- U64_FORMAT ")",
- q->u.var.var_cell, chan,
- U64_PRINTF_ARG(chan->global_identifier));
- chan->var_cell_handler(chan, q->u.var.var_cell);
- tor_free(q->u.var.var_cell);
- tor_free(q);
- } else {
- /* Can't handle this one */
- break;
- }
- }
-}
-
-/**
- * Queue incoming cell
- *
- * This should be called by a channel_t subclass to queue an incoming fixed-
- * length cell for processing, and process it if possible.
- */
-
-void
-channel_queue_cell(channel_t *chan, cell_t *cell)
-{
- int need_to_queue = 0;
- cell_queue_entry_t *q;
- cell_t *cell_copy = NULL;
-
- tor_assert(chan);
tor_assert(cell);
- tor_assert(CHANNEL_IS_OPEN(chan));
- /* Do we need to queue it, or can we just call the handler right away? */
- if (!(chan->cell_handler)) need_to_queue = 1;
- if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue))
- need_to_queue = 1;
+ /* Nothing we can do if we have no registered cell handlers */
+ if (!chan->cell_handler)
+ return;
/* Timestamp for receiving */
channel_timestamp_recv(chan);
-
- /* Update the counters */
+ /* Update received counter. */
++(chan->n_cells_recved);
chan->n_bytes_recved += get_cell_network_size(chan->wide_circ_ids);
- /* If we don't need to queue we can just call cell_handler */
- if (!need_to_queue) {
- tor_assert(chan->cell_handler);
- log_debug(LD_CHANNEL,
- "Directly handling incoming cell_t %p for channel %p "
- "(global ID " U64_FORMAT ")",
- cell, chan,
- U64_PRINTF_ARG(chan->global_identifier));
- chan->cell_handler(chan, cell);
- } else {
- /*
- * Otherwise queue it and then process the queue if possible.
- *
- * We queue a copy, not the original pointer - it might have been on the
- * stack in connection_or_process_cells_from_inbuf() (or another caller
- * if we ever have a subclass other than channel_tls_t), or be freed
- * there after we return. This is the uncommon case; the non-copying
- * fast path occurs in the if (!need_to_queue) case above when the
- * upper layer has installed cell handlers.
- */
- cell_copy = tor_malloc_zero(sizeof(cell_t));
- memcpy(cell_copy, cell, sizeof(cell_t));
- q = cell_queue_entry_new_fixed(cell_copy);
- log_debug(LD_CHANNEL,
- "Queueing incoming cell_t %p for channel %p "
- "(global ID " U64_FORMAT ")",
- cell, chan,
- U64_PRINTF_ARG(chan->global_identifier));
- TOR_SIMPLEQ_INSERT_TAIL(&chan->incoming_queue, q, next);
- if (chan->cell_handler ||
- chan->var_cell_handler) {
- channel_process_cells(chan);
- }
- }
-}
-
-/**
- * Queue incoming variable-length cell
- *
- * This should be called by a channel_t subclass to queue an incoming
- * variable-length cell for processing, and process it if possible.
- */
-
-void
-channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell)
-{
- int need_to_queue = 0;
- cell_queue_entry_t *q;
- var_cell_t *cell_copy = NULL;
-
- tor_assert(chan);
- tor_assert(var_cell);
- tor_assert(CHANNEL_IS_OPEN(chan));
-
- /* Do we need to queue it, or can we just call the handler right away? */
- if (!(chan->var_cell_handler)) need_to_queue = 1;
- if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue))
- need_to_queue = 1;
-
- /* Timestamp for receiving */
- channel_timestamp_recv(chan);
-
- /* Update the counter */
- ++(chan->n_cells_recved);
- chan->n_bytes_recved += get_var_cell_header_size(chan->wide_circ_ids) +
- var_cell->payload_len;
-
- /* If we don't need to queue we can just call cell_handler */
- if (!need_to_queue) {
- tor_assert(chan->var_cell_handler);
- log_debug(LD_CHANNEL,
- "Directly handling incoming var_cell_t %p for channel %p "
- "(global ID " U64_FORMAT ")",
- var_cell, chan,
- U64_PRINTF_ARG(chan->global_identifier));
- chan->var_cell_handler(chan, var_cell);
- } else {
- /*
- * Otherwise queue it and then process the queue if possible.
- *
- * We queue a copy, not the original pointer - it might have been on the
- * stack in connection_or_process_cells_from_inbuf() (or another caller
- * if we ever have a subclass other than channel_tls_t), or be freed
- * there after we return. This is the uncommon case; the non-copying
- * fast path occurs in the if (!need_to_queue) case above when the
- * upper layer has installed cell handlers.
- */
- cell_copy = var_cell_copy(var_cell);
- q = cell_queue_entry_new_var(cell_copy);
- log_debug(LD_CHANNEL,
- "Queueing incoming var_cell_t %p for channel %p "
- "(global ID " U64_FORMAT ")",
- var_cell, chan,
- U64_PRINTF_ARG(chan->global_identifier));
- TOR_SIMPLEQ_INSERT_TAIL(&chan->incoming_queue, q, next);
- if (chan->cell_handler ||
- chan->var_cell_handler) {
- channel_process_cells(chan);
- }
- }
+ log_debug(LD_CHANNEL,
+ "Processing incoming cell_t %p for channel %p (global ID "
+ U64_FORMAT ")", cell, chan,
+ U64_PRINTF_ARG(chan->global_identifier));
+ chan->cell_handler(chan, cell);
}
/** If <b>packed_cell</b> on <b>chan</b> is a destroy cell, then set
@@ -3051,51 +2006,12 @@ packed_cell_is_destroy(channel_t *chan,
}
/**
- * Assert that the global channel stats counters are internally consistent
- */
-
-static void
-channel_assert_counter_consistency(void)
-{
- tor_assert(n_channel_cells_queued ==
- (n_channel_cells_in_queues + n_channel_cells_passed_to_lower_layer));
- tor_assert(n_channel_bytes_queued ==
- (n_channel_bytes_in_queues + n_channel_bytes_passed_to_lower_layer));
-}
-
-/* DOCDOC */
-static int
-is_destroy_cell(channel_t *chan,
- const cell_queue_entry_t *q, circid_t *circid_out)
-{
- *circid_out = 0;
- switch (q->type) {
- case CELL_QUEUE_FIXED:
- if (q->u.fixed.cell->command == CELL_DESTROY) {
- *circid_out = q->u.fixed.cell->circ_id;
- return 1;
- }
- break;
- case CELL_QUEUE_VAR:
- if (q->u.var.var_cell->command == CELL_DESTROY) {
- *circid_out = q->u.var.var_cell->circ_id;
- return 1;
- }
- break;
- case CELL_QUEUE_PACKED:
- return packed_cell_is_destroy(chan, q->u.packed.packed_cell, circid_out);
- }
- return 0;
-}
-
-/**
- * Send destroy cell on a channel
+ * Send destroy cell on a channel.
*
* Write a destroy cell with circ ID <b>circ_id</b> and reason <b>reason</b>
* onto channel <b>chan</b>. Don't perform range-checking on reason:
* we may want to propagate reasons from other cells.
*/
-
int
channel_send_destroy(circid_t circ_id, channel_t *chan, int reason)
{
@@ -3131,30 +2047,16 @@ channel_send_destroy(circid_t circ_id, channel_t *chan, int reason)
}
/**
- * Dump channel statistics to the log
+ * Dump channel statistics to the log.
*
* This is called from dumpstats() in main.c and spams the log with
* statistics on channels.
*/
-
void
channel_dumpstats(int severity)
{
if (all_channels && smartlist_len(all_channels) > 0) {
tor_log(severity, LD_GENERAL,
- "Channels have queued " U64_FORMAT " bytes in " U64_FORMAT " cells, "
- "and handed " U64_FORMAT " bytes in " U64_FORMAT " cells to the lower"
- " layer.",
- U64_PRINTF_ARG(n_channel_bytes_queued),
- U64_PRINTF_ARG(n_channel_cells_queued),
- U64_PRINTF_ARG(n_channel_bytes_passed_to_lower_layer),
- U64_PRINTF_ARG(n_channel_cells_passed_to_lower_layer));
- tor_log(severity, LD_GENERAL,
- "There are currently " U64_FORMAT " bytes in " U64_FORMAT " cells "
- "in channel queues.",
- U64_PRINTF_ARG(n_channel_bytes_in_queues),
- U64_PRINTF_ARG(n_channel_cells_in_queues));
- tor_log(severity, LD_GENERAL,
"Dumping statistics about %d channels:",
smartlist_len(all_channels));
tor_log(severity, LD_GENERAL,
@@ -3176,12 +2078,11 @@ channel_dumpstats(int severity)
}
/**
- * Dump channel listener statistics to the log
+ * Dump channel listener statistics to the log.
*
* This is called from dumpstats() in main.c and spams the log with
* statistics on channel listeners.
*/
-
void
channel_listener_dumpstats(int severity)
{
@@ -3208,9 +2109,8 @@ channel_listener_dumpstats(int severity)
}
/**
- * Set the cmux policy on all active channels
+ * Set the cmux policy on all active channels.
*/
-
void
channel_set_cmux_policy_everywhere(circuitmux_policy_t *pol)
{
@@ -3224,12 +2124,11 @@ channel_set_cmux_policy_everywhere(circuitmux_policy_t *pol)
}
/**
- * Clean up channels
+ * Clean up channels.
*
* This gets called periodically from run_scheduled_events() in main.c;
* it cleans up after closed channels.
*/
-
void
channel_run_cleanup(void)
{
@@ -3251,12 +2150,11 @@ channel_run_cleanup(void)
}
/**
- * Clean up channel listeners
+ * Clean up channel listeners.
*
* This gets called periodically from run_scheduled_events() in main.c;
* it cleans up after closed channel listeners.
*/
-
void
channel_listener_run_cleanup(void)
{
@@ -3278,9 +2176,8 @@ channel_listener_run_cleanup(void)
}
/**
- * Free a list of channels for channel_free_all()
+ * Free a list of channels for channel_free_all().
*/
-
static void
channel_free_list(smartlist_t *channels, int mark_for_close)
{
@@ -3304,15 +2201,14 @@ channel_free_list(smartlist_t *channels, int mark_for_close)
if (!CHANNEL_CONDEMNED(curr)) {
channel_mark_for_close(curr);
}
- channel_force_free(curr);
+ channel_force_xfree(curr);
} else channel_free(curr);
} SMARTLIST_FOREACH_END(curr);
}
/**
- * Free a list of channel listeners for channel_free_all()
+ * Free a list of channel listeners for channel_free_all().
*/
-
static void
channel_listener_free_list(smartlist_t *listeners, int mark_for_close)
{
@@ -3333,20 +2229,19 @@ channel_listener_free_list(smartlist_t *listeners, int mark_for_close)
curr->state == CHANNEL_LISTENER_STATE_ERROR)) {
channel_listener_mark_for_close(curr);
}
- channel_listener_force_free(curr);
+ channel_listener_force_xfree(curr);
} else channel_listener_free(curr);
} SMARTLIST_FOREACH_END(curr);
}
/**
- * Close all channels and free everything
+ * Close all channels and free everything.
*
* This gets called from tor_free_all() in main.c to clean up on exit.
* It will close all registered channels and free associated storage,
* then free the all_channels, active_channels, listening_channels and
* finished_channels lists and also channel_identity_map.
*/
-
void
channel_free_all(void)
{
@@ -3411,7 +2306,7 @@ channel_free_all(void)
}
/**
- * Connect to a given addr/port/digest
+ * Connect to a given addr/port/digest.
*
* This sets up a new outgoing channel; in the future if multiple
* channel_t subclasses are available, this is where the selection policy
@@ -3420,7 +2315,6 @@ channel_free_all(void)
* single abstract object encapsulating all the protocol details of
* how to contact an OR.
*/
-
channel_t *
channel_connect(const tor_addr_t *addr, uint16_t port,
const char *id_digest,
@@ -3430,7 +2324,7 @@ channel_connect(const tor_addr_t *addr, uint16_t port,
}
/**
- * Decide which of two channels to prefer for extending a circuit
+ * Decide which of two channels to prefer for extending a circuit.
*
* This function is called while extending a circuit and returns true iff
* a is 'better' than b. The most important criterion here is that a
@@ -3439,7 +2333,6 @@ channel_connect(const tor_addr_t *addr, uint16_t port,
*
* This is based on the former connection_or_is_better() of connection_or.c
*/
-
int
channel_is_better(channel_t *a, channel_t *b)
{
@@ -3491,7 +2384,7 @@ channel_is_better(channel_t *a, channel_t *b)
}
/**
- * Get a channel to extend a circuit
+ * Get a channel to extend a circuit.
*
* Pick a suitable channel to extend a circuit to given the desired digest
* the address we believe is correct for that digest; this tries to see
@@ -3500,7 +2393,6 @@ channel_is_better(channel_t *a, channel_t *b)
* and our next action, and set *launch_out to a boolean indicated whether
* the caller should try to launch a new channel with channel_connect().
*/
-
channel_t *
channel_get_for_extend(const char *rsa_id_digest,
const ed25519_public_key_t *ed_id,
@@ -3605,12 +2497,11 @@ channel_get_for_extend(const char *rsa_id_digest,
}
/**
- * Describe the transport subclass for a channel
+ * Describe the transport subclass for a channel.
*
* Invoke a method to get a string description of the lower-layer
* transport for this channel.
*/
-
const char *
channel_describe_transport(channel_t *chan)
{
@@ -3621,12 +2512,11 @@ channel_describe_transport(channel_t *chan)
}
/**
- * Describe the transport subclass for a channel listener
+ * Describe the transport subclass for a channel listener.
*
* Invoke a method to get a string description of the lower-layer
* transport for this channel listener.
*/
-
const char *
channel_listener_describe_transport(channel_listener_t *chan_l)
{
@@ -3637,24 +2527,10 @@ channel_listener_describe_transport(channel_listener_t *chan_l)
}
/**
- * Return the number of entries in <b>queue</b>
- */
-STATIC int
-chan_cell_queue_len(const chan_cell_queue_t *queue)
-{
- int r = 0;
- cell_queue_entry_t *cell;
- TOR_SIMPLEQ_FOREACH(cell, queue, next)
- ++r;
- return r;
-}
-
-/**
- * Dump channel statistics
+ * Dump channel statistics.
*
- * Dump statistics for one channel to the log
+ * Dump statistics for one channel to the log.
*/
-
MOCK_IMPL(void,
channel_dump_statistics, (channel_t *chan, int severity))
{
@@ -3684,35 +2560,18 @@ channel_dump_statistics, (channel_t *chan, int severity))
U64_PRINTF_ARG(chan->timestamp_active),
U64_PRINTF_ARG(now - chan->timestamp_active));
- /* Handle digest and nickname */
+ /* Handle digest. */
if (!tor_digest_is_zero(chan->identity_digest)) {
- if (chan->nickname) {
- tor_log(severity, LD_GENERAL,
- " * Channel " U64_FORMAT " says it is connected "
- "to an OR with digest %s and nickname %s",
- U64_PRINTF_ARG(chan->global_identifier),
- hex_str(chan->identity_digest, DIGEST_LEN),
- chan->nickname);
- } else {
- tor_log(severity, LD_GENERAL,
- " * Channel " U64_FORMAT " says it is connected "
- "to an OR with digest %s and no known nickname",
- U64_PRINTF_ARG(chan->global_identifier),
- hex_str(chan->identity_digest, DIGEST_LEN));
- }
+ tor_log(severity, LD_GENERAL,
+ " * Channel " U64_FORMAT " says it is connected "
+ "to an OR with digest %s",
+ U64_PRINTF_ARG(chan->global_identifier),
+ hex_str(chan->identity_digest, DIGEST_LEN));
} else {
- if (chan->nickname) {
- tor_log(severity, LD_GENERAL,
- " * Channel " U64_FORMAT " does not know the digest"
- " of the OR it is connected to, but reports its nickname is %s",
- U64_PRINTF_ARG(chan->global_identifier),
- chan->nickname);
- } else {
- tor_log(severity, LD_GENERAL,
- " * Channel " U64_FORMAT " does not know the digest"
- " or the nickname of the OR it is connected to",
- U64_PRINTF_ARG(chan->global_identifier));
- }
+ tor_log(severity, LD_GENERAL,
+ " * Channel " U64_FORMAT " does not know the digest"
+ " of the OR it is connected to",
+ U64_PRINTF_ARG(chan->global_identifier));
}
/* Handle remote address and descriptions */
@@ -3761,14 +2620,6 @@ channel_dump_statistics, (channel_t *chan, int severity))
channel_is_incoming(chan) ?
"incoming" : "outgoing");
- /* Describe queues */
- tor_log(severity, LD_GENERAL,
- " * Channel " U64_FORMAT " has %d queued incoming cells"
- " and %d queued outgoing cells",
- U64_PRINTF_ARG(chan->global_identifier),
- chan_cell_queue_len(&chan->incoming_queue),
- chan_cell_queue_len(&chan->outgoing_queue));
-
/* Describe circuits */
tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " has %d active circuits out of"
@@ -3787,12 +2638,6 @@ channel_dump_statistics, (channel_t *chan, int severity))
U64_PRINTF_ARG(chan->timestamp_client),
U64_PRINTF_ARG(now - chan->timestamp_client));
tor_log(severity, LD_GENERAL,
- " * Channel " U64_FORMAT " was last drained at "
- U64_FORMAT " (" U64_FORMAT " seconds ago)",
- U64_PRINTF_ARG(chan->global_identifier),
- U64_PRINTF_ARG(chan->timestamp_drained),
- U64_PRINTF_ARG(now - chan->timestamp_drained));
- tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " last received a cell "
"at " U64_FORMAT " (" U64_FORMAT " seconds ago)",
U64_PRINTF_ARG(chan->global_identifier),
@@ -3868,11 +2713,10 @@ channel_dump_statistics, (channel_t *chan, int severity))
}
/**
- * Dump channel listener statistics
+ * Dump channel listener statistics.
*
- * Dump statistics for one channel listener to the log
+ * Dump statistics for one channel listener to the log.
*/
-
void
channel_listener_dump_statistics(channel_listener_t *chan_l, int severity)
{
@@ -3935,11 +2779,10 @@ channel_listener_dump_statistics(channel_listener_t *chan_l, int severity)
}
/**
- * Invoke transport-specific stats dump for channel
+ * Invoke transport-specific stats dump for channel.
*
- * If there is a lower-layer statistics dump method, invoke it
+ * If there is a lower-layer statistics dump method, invoke it.
*/
-
void
channel_dump_transport_statistics(channel_t *chan, int severity)
{
@@ -3949,11 +2792,10 @@ channel_dump_transport_statistics(channel_t *chan, int severity)
}
/**
- * Invoke transport-specific stats dump for channel listener
+ * Invoke transport-specific stats dump for channel listener.
*
- * If there is a lower-layer statistics dump method, invoke it
+ * If there is a lower-layer statistics dump method, invoke it.
*/
-
void
channel_listener_dump_transport_statistics(channel_listener_t *chan_l,
int severity)
@@ -3964,7 +2806,7 @@ channel_listener_dump_transport_statistics(channel_listener_t *chan_l,
}
/**
- * Return text description of the remote endpoint
+ * Return text description of the remote endpoint.
*
* This function return a test provided by the lower layer of the remote
* endpoint for this channel; it should specify the actual address connected
@@ -3997,7 +2839,7 @@ channel_get_actual_remote_address(channel_t *chan)
}
/**
- * Return text description of the remote endpoint canonical address
+ * Return text description of the remote endpoint canonical address.
*
* This function return a test provided by the lower layer of the remote
* endpoint for this channel; it should use the known canonical address for
@@ -4036,37 +2878,25 @@ channel_get_addr_if_possible,(channel_t *chan, tor_addr_t *addr_out))
}
/**
- * Check if there are outgoing queue writes on this channel
- *
- * Indicate if either we have queued cells, or if not, whether the underlying
- * lower-layer transport thinks it has an output queue.
+ * Return true iff the channel has any cells on the connection outbuf waiting
+ * to be sent onto the network.
*/
-
int
channel_has_queued_writes(channel_t *chan)
{
- int has_writes = 0;
-
tor_assert(chan);
tor_assert(chan->has_queued_writes);
- if (! TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue)) {
- has_writes = 1;
- } else {
- /* Check with the lower layer */
- has_writes = chan->has_queued_writes(chan);
- }
-
- return has_writes;
+ /* Check with the lower layer */
+ return chan->has_queued_writes(chan);
}
/**
- * Check the is_bad_for_new_circs flag
+ * Check the is_bad_for_new_circs flag.
*
* This function returns the is_bad_for_new_circs flag of the specified
* channel.
*/
-
int
channel_is_bad_for_new_circs(channel_t *chan)
{
@@ -4076,11 +2906,10 @@ channel_is_bad_for_new_circs(channel_t *chan)
}
/**
- * Mark a channel as bad for new circuits
+ * Mark a channel as bad for new circuits.
*
* Set the is_bad_for_new_circs_flag on chan.
*/
-
void
channel_mark_bad_for_new_circs(channel_t *chan)
{
@@ -4090,13 +2919,12 @@ channel_mark_bad_for_new_circs(channel_t *chan)
}
/**
- * Get the client flag
+ * Get the client flag.
*
* This returns the client flag of a channel, which will be set if
* command_process_create_cell() in command.c thinks this is a connection
* from a client.
*/
-
int
channel_is_client(const channel_t *chan)
{
@@ -4106,11 +2934,10 @@ channel_is_client(const channel_t *chan)
}
/**
- * Set the client flag
+ * Set the client flag.
*
- * Mark a channel as being from a client
+ * Mark a channel as being from a client.
*/
-
void
channel_mark_client(channel_t *chan)
{
@@ -4120,11 +2947,10 @@ channel_mark_client(channel_t *chan)
}
/**
- * Clear the client flag
+ * Clear the client flag.
*
- * Mark a channel as being _not_ from a client
+ * Mark a channel as being _not_ from a client.
*/
-
void
channel_clear_client(channel_t *chan)
{
@@ -4134,12 +2960,11 @@ channel_clear_client(channel_t *chan)
}
/**
- * Get the canonical flag for a channel
+ * Get the canonical flag for a channel.
*
* This returns the is_canonical for a channel; this flag is determined by
* the lower layer and can't be set in a transport-independent way.
*/
-
int
channel_is_canonical(channel_t *chan)
{
@@ -4150,12 +2975,11 @@ channel_is_canonical(channel_t *chan)
}
/**
- * Test if the canonical flag is reliable
+ * Test if the canonical flag is reliable.
*
* This function asks if the lower layer thinks it's safe to trust the
- * result of channel_is_canonical()
+ * result of channel_is_canonical().
*/
-
int
channel_is_canonical_is_reliable(channel_t *chan)
{
@@ -4166,12 +2990,11 @@ channel_is_canonical_is_reliable(channel_t *chan)
}
/**
- * Test incoming flag
+ * Test incoming flag.
*
* This function gets the incoming flag; this is set when a listener spawns
* a channel. If this returns true the channel was remotely initiated.
*/
-
int
channel_is_incoming(channel_t *chan)
{
@@ -4181,12 +3004,11 @@ channel_is_incoming(channel_t *chan)
}
/**
- * Set the incoming flag
+ * Set the incoming flag.
*
* This function is called when a channel arrives on a listening channel
* to mark it as incoming.
*/
-
void
channel_mark_incoming(channel_t *chan)
{
@@ -4196,7 +3018,7 @@ channel_mark_incoming(channel_t *chan)
}
/**
- * Test local flag
+ * Test local flag.
*
* This function gets the local flag; the lower layer should set this when
* setting up the channel if is_local_addr() is true for all of the
@@ -4204,7 +3026,6 @@ channel_mark_incoming(channel_t *chan)
* used to decide whether to declare the network reachable when seeing incoming
* traffic on the channel.
*/
-
int
channel_is_local(channel_t *chan)
{
@@ -4214,13 +3035,12 @@ channel_is_local(channel_t *chan)
}
/**
- * Set the local flag
+ * Set the local flag.
*
* This internal-only function should be called by the lower layer if the
* channel is to a local address. See channel_is_local() above or the
- * description of the is_local bit in channel.h
+ * description of the is_local bit in channel.h.
*/
-
void
channel_mark_local(channel_t *chan)
{
@@ -4230,14 +3050,13 @@ channel_mark_local(channel_t *chan)
}
/**
- * Mark a channel as remote
+ * Mark a channel as remote.
*
* This internal-only function should be called by the lower layer if the
* channel is not to a local address but has previously been marked local.
* See channel_is_local() above or the description of the is_local bit in
* channel.h
*/
-
void
channel_mark_remote(channel_t *chan)
{
@@ -4247,13 +3066,12 @@ channel_mark_remote(channel_t *chan)
}
/**
- * Test outgoing flag
+ * Test outgoing flag.
*
* This function gets the outgoing flag; this is the inverse of the incoming
* bit set when a listener spawns a channel. If this returns true the channel
* was locally initiated.
*/
-
int
channel_is_outgoing(channel_t *chan)
{
@@ -4263,12 +3081,11 @@ channel_is_outgoing(channel_t *chan)
}
/**
- * Mark a channel as outgoing
+ * Mark a channel as outgoing.
*
* This function clears the incoming flag and thus marks a channel as
* outgoing.
*/
-
void
channel_mark_outgoing(channel_t *chan)
{
@@ -4281,24 +3098,11 @@ channel_mark_outgoing(channel_t *chan)
* Flow control queries *
***********************/
-/*
- * Get the latest estimate for the total queue size of all open channels
- */
-
-uint64_t
-channel_get_global_queue_estimate(void)
-{
- return estimated_total_queue_size;
-}
-
-/*
- * Estimate the number of writeable cells
+/**
+ * Estimate the number of writeable cells.
*
- * Ask the lower layer for an estimate of how many cells it can accept, and
- * then subtract the length of our outgoing_queue, if any, to produce an
- * estimate of the number of cells this channel can accept for writes.
+ * Ask the lower layer for an estimate of how many cells it can accept.
*/
-
int
channel_num_cells_writeable(channel_t *chan)
{
@@ -4310,8 +3114,6 @@ channel_num_cells_writeable(channel_t *chan)
if (chan->state == CHANNEL_STATE_OPEN) {
/* Query lower layer */
result = chan->num_cells_writeable(chan);
- /* Subtract cell queue length, if any */
- result -= chan_cell_queue_len(&chan->outgoing_queue);
if (result < 0) result = 0;
} else {
/* No cells are writeable in any other state */
@@ -4326,12 +3128,11 @@ channel_num_cells_writeable(channel_t *chan)
********************/
/**
- * Update the created timestamp for a channel
+ * Update the created timestamp for a channel.
*
* This updates the channel's created timestamp and should only be called
* from channel_init().
*/
-
void
channel_timestamp_created(channel_t *chan)
{
@@ -4343,12 +3144,11 @@ channel_timestamp_created(channel_t *chan)
}
/**
- * Update the created timestamp for a channel listener
+ * Update the created timestamp for a channel listener.
*
* This updates the channel listener's created timestamp and should only be
* called from channel_init_listener().
*/
-
void
channel_listener_timestamp_created(channel_listener_t *chan_l)
{
@@ -4360,7 +3160,7 @@ channel_listener_timestamp_created(channel_listener_t *chan_l)
}
/**
- * Update the last active timestamp for a channel
+ * Update the last active timestamp for a channel.
*
* This function updates the channel's last active timestamp; it should be
* called by the lower layer whenever there is activity on the channel which
@@ -4369,25 +3169,23 @@ channel_listener_timestamp_created(channel_listener_t *chan_l)
* but it should be updated for things like the v3 handshake and stuff that
* produce activity only visible to the lower layer.
*/
-
void
channel_timestamp_active(channel_t *chan)
{
time_t now = time(NULL);
tor_assert(chan);
- chan->timestamp_xfer_ms = monotime_coarse_absolute_msec();
+ monotime_coarse_get(&chan->timestamp_xfer);
chan->timestamp_active = now;
/* Clear any potential netflow padding timer. We're active */
- chan->next_padding_time_ms = 0;
+ monotime_coarse_zero(&chan->next_padding_time);
}
/**
- * Update the last active timestamp for a channel listener
+ * Update the last active timestamp for a channel listener.
*/
-
void
channel_listener_timestamp_active(channel_listener_t *chan_l)
{
@@ -4405,7 +3203,6 @@ channel_listener_timestamp_active(channel_listener_t *chan_l)
* should be called whenever a new incoming channel is accepted on a
* listener.
*/
-
void
channel_listener_timestamp_accepted(channel_listener_t *chan_l)
{
@@ -4418,12 +3215,11 @@ channel_listener_timestamp_accepted(channel_listener_t *chan_l)
}
/**
- * Update client timestamp
+ * Update client timestamp.
*
* This function is called by relay.c to timestamp a channel that appears to
* be used as a client.
*/
-
void
channel_timestamp_client(channel_t *chan)
{
@@ -4435,64 +3231,44 @@ channel_timestamp_client(channel_t *chan)
}
/**
- * Update the last drained timestamp
- *
- * This is called whenever we transmit a cell which leaves the outgoing cell
- * queue completely empty. It also updates the xmit time and the active time.
- */
-
-void
-channel_timestamp_drained(channel_t *chan)
-{
- time_t now = time(NULL);
-
- tor_assert(chan);
-
- chan->timestamp_active = now;
- chan->timestamp_drained = now;
- chan->timestamp_xmit = now;
-}
-
-/**
- * Update the recv timestamp
+ * Update the recv timestamp.
*
* This is called whenever we get an incoming cell from the lower layer.
* This also updates the active timestamp.
*/
-
void
channel_timestamp_recv(channel_t *chan)
{
time_t now = time(NULL);
tor_assert(chan);
- chan->timestamp_xfer_ms = monotime_coarse_absolute_msec();
+ monotime_coarse_get(&chan->timestamp_xfer);
chan->timestamp_active = now;
chan->timestamp_recv = now;
/* Clear any potential netflow padding timer. We're active */
- chan->next_padding_time_ms = 0;
+ monotime_coarse_zero(&chan->next_padding_time);
}
/**
- * Update the xmit timestamp
+ * Update the xmit timestamp.
+ *
* This is called whenever we pass an outgoing cell to the lower layer. This
* also updates the active timestamp.
*/
-
void
channel_timestamp_xmit(channel_t *chan)
{
time_t now = time(NULL);
tor_assert(chan);
- chan->timestamp_xfer_ms = monotime_coarse_absolute_msec();
+ monotime_coarse_get(&chan->timestamp_xfer);
chan->timestamp_active = now;
chan->timestamp_xmit = now;
/* Clear any potential netflow padding timer. We're active */
- chan->next_padding_time_ms = 0;
+ monotime_coarse_zero(&chan->next_padding_time);
}
/***************************************************************
@@ -4500,9 +3276,8 @@ channel_timestamp_xmit(channel_t *chan)
**************************************************************/
/**
- * Query created timestamp for a channel
+ * Query created timestamp for a channel.
*/
-
time_t
channel_when_created(channel_t *chan)
{
@@ -4512,57 +3287,8 @@ channel_when_created(channel_t *chan)
}
/**
- * Query created timestamp for a channel listener
- */
-
-time_t
-channel_listener_when_created(channel_listener_t *chan_l)
-{
- tor_assert(chan_l);
-
- return chan_l->timestamp_created;
-}
-
-/**
- * Query last active timestamp for a channel
- */
-
-time_t
-channel_when_last_active(channel_t *chan)
-{
- tor_assert(chan);
-
- return chan->timestamp_active;
-}
-
-/**
- * Query last active timestamp for a channel listener
- */
-
-time_t
-channel_listener_when_last_active(channel_listener_t *chan_l)
-{
- tor_assert(chan_l);
-
- return chan_l->timestamp_active;
-}
-
-/**
- * Query last accepted timestamp for a channel listener
- */
-
-time_t
-channel_listener_when_last_accepted(channel_listener_t *chan_l)
-{
- tor_assert(chan_l);
-
- return chan_l->timestamp_accepted;
-}
-
-/**
- * Query client timestamp
+ * Query client timestamp.
*/
-
time_t
channel_when_last_client(channel_t *chan)
{
@@ -4572,33 +3298,8 @@ channel_when_last_client(channel_t *chan)
}
/**
- * Query drained timestamp
- */
-
-time_t
-channel_when_last_drained(channel_t *chan)
-{
- tor_assert(chan);
-
- return chan->timestamp_drained;
-}
-
-/**
- * Query recv timestamp
- */
-
-time_t
-channel_when_last_recv(channel_t *chan)
-{
- tor_assert(chan);
-
- return chan->timestamp_recv;
-}
-
-/**
- * Query xmit timestamp
+ * Query xmit timestamp.
*/
-
time_t
channel_when_last_xmit(channel_t *chan)
{
@@ -4608,48 +3309,11 @@ channel_when_last_xmit(channel_t *chan)
}
/**
- * Query accepted counter
- */
-
-uint64_t
-channel_listener_count_accepted(channel_listener_t *chan_l)
-{
- tor_assert(chan_l);
-
- return chan_l->n_accepted;
-}
-
-/**
- * Query received cell counter
- */
-
-uint64_t
-channel_count_recved(channel_t *chan)
-{
- tor_assert(chan);
-
- return chan->n_cells_recved;
-}
-
-/**
- * Query transmitted cell counter
- */
-
-uint64_t
-channel_count_xmitted(channel_t *chan)
-{
- tor_assert(chan);
-
- return chan->n_cells_xmitted;
-}
-
-/**
- * Check if a channel matches an extend_info_t
+ * Check if a channel matches an extend_info_t.
*
* This function calls the lower layer and asks if this channel matches a
* given extend_info_t.
*/
-
int
channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info)
{
@@ -4666,7 +3330,6 @@ channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info)
* This function calls into the lower layer and asks if this channel thinks
* it matches a given target address for circuit extension purposes.
*/
-
int
channel_matches_target_addr_for_extend(channel_t *chan,
const tor_addr_t *target)
@@ -4679,12 +3342,11 @@ channel_matches_target_addr_for_extend(channel_t *chan,
}
/**
- * Return the total number of circuits used by a channel
+ * Return the total number of circuits used by a channel.
*
* @param chan Channel to query
* @return Number of circuits using this as n_chan or p_chan
*/
-
unsigned int
channel_num_circuits(channel_t *chan)
{
@@ -4695,10 +3357,10 @@ channel_num_circuits(channel_t *chan)
}
/**
- * Set up circuit ID generation
+ * Set up circuit ID generation.
*
* This is called when setting up a channel and replaces the old
- * connection_or_set_circid_type()
+ * connection_or_set_circid_type().
*/
MOCK_IMPL(void,
channel_set_circid_type,(channel_t *chan,
@@ -4734,6 +3396,16 @@ channel_set_circid_type,(channel_t *chan,
}
}
+static int
+channel_sort_by_ed25519_identity(const void **a_, const void **b_)
+{
+ const channel_t *a = *a_,
+ *b = *b_;
+ return fast_memcmp(&a->ed25519_identity.pubkey,
+ &b->ed25519_identity.pubkey,
+ sizeof(a->ed25519_identity.pubkey));
+}
+
/** Helper for channel_update_bad_for_new_circs(): Perform the
* channel_update_bad_for_new_circs operation on all channels in <b>lst</b>,
* all of which MUST have the same RSA ID. (They MAY have different
@@ -4742,44 +3414,52 @@ static void
channel_rsa_id_group_set_badness(struct channel_list_s *lst, int force)
{
/*XXXX This function should really be about channels. 15056 */
- channel_t *chan;
+ channel_t *chan = TOR_LIST_FIRST(lst);
+
+ if (!chan)
+ return;
+
+ /* if there is only one channel, don't bother looping */
+ if (PREDICT_LIKELY(!TOR_LIST_NEXT(chan, next_with_same_id))) {
+ connection_or_single_set_badness_(
+ time(NULL), BASE_CHAN_TO_TLS(chan)->conn, force);
+ return;
+ }
+
+ smartlist_t *channels = smartlist_new();
- /* First, get a minimal list of the ed25519 identites */
- smartlist_t *ed_identities = smartlist_new();
TOR_LIST_FOREACH(chan, lst, next_with_same_id) {
- uint8_t *id_copy =
- tor_memdup(&chan->ed25519_identity.pubkey, DIGEST256_LEN);
- smartlist_add(ed_identities, id_copy);
+ if (BASE_CHAN_TO_TLS(chan)->conn) {
+ smartlist_add(channels, chan);
+ }
}
- smartlist_sort_digests256(ed_identities);
- smartlist_uniq_digests256(ed_identities);
- /* Now, for each Ed identity, build a smartlist and find the best entry on
- * it. */
+ smartlist_sort(channels, channel_sort_by_ed25519_identity);
+
+ const ed25519_public_key_t *common_ed25519_identity = NULL;
+ /* it would be more efficient to do a slice, but this case is rare */
smartlist_t *or_conns = smartlist_new();
- SMARTLIST_FOREACH_BEGIN(ed_identities, const uint8_t *, ed_id) {
- TOR_LIST_FOREACH(chan, lst, next_with_same_id) {
- channel_tls_t *chantls = BASE_CHAN_TO_TLS(chan);
- if (tor_memneq(ed_id, &chan->ed25519_identity.pubkey, DIGEST256_LEN))
- continue;
- or_connection_t *orconn = chantls->conn;
- if (orconn) {
- tor_assert(orconn->chan == chantls);
- smartlist_add(or_conns, orconn);
- }
+ SMARTLIST_FOREACH_BEGIN(channels, channel_t *, channel) {
+ if (!common_ed25519_identity)
+ common_ed25519_identity = &channel->ed25519_identity;
+
+ if (! ed25519_pubkey_eq(&channel->ed25519_identity,
+ common_ed25519_identity)) {
+ connection_or_group_set_badness_(or_conns, force);
+ smartlist_clear(or_conns);
+ common_ed25519_identity = &channel->ed25519_identity;
}
- connection_or_group_set_badness_(or_conns, force);
- smartlist_clear(or_conns);
- } SMARTLIST_FOREACH_END(ed_id);
+ smartlist_add(or_conns, BASE_CHAN_TO_TLS(channel)->conn);
+ } SMARTLIST_FOREACH_END(channel);
+
+ connection_or_group_set_badness_(or_conns, force);
/* XXXX 15056 we may want to do something special with connections that have
* no set Ed25519 identity! */
smartlist_free(or_conns);
-
- SMARTLIST_FOREACH(ed_identities, uint8_t *, ed_id, tor_free(ed_id));
- smartlist_free(ed_identities);
+ smartlist_free(channels);
}
/** Go through all the channels (or if <b>digest</b> is non-NULL, just
@@ -4809,83 +3489,3 @@ channel_update_bad_for_new_circs(const char *digest, int force)
}
}
-/**
- * Update the estimated number of bytes queued to transmit for this channel,
- * and notify the scheduler. The estimate includes both the channel queue and
- * the queue size reported by the lower layer, and an overhead estimate
- * optionally provided by the lower layer.
- */
-
-void
-channel_update_xmit_queue_size(channel_t *chan)
-{
- uint64_t queued, adj;
- double overhead;
-
- tor_assert(chan);
- tor_assert(chan->num_bytes_queued);
-
- /*
- * First, get the number of bytes we have queued without factoring in
- * lower-layer overhead.
- */
- queued = chan->num_bytes_queued(chan) + chan->bytes_in_queue;
- /* Next, adjust by the overhead factor, if any is available */
- if (chan->get_overhead_estimate) {
- overhead = chan->get_overhead_estimate(chan);
- if (overhead >= 1.0) {
- queued = (uint64_t)(queued * overhead);
- } else {
- /* Ignore silly overhead factors */
- log_notice(LD_CHANNEL, "Ignoring silly overhead factor %f", overhead);
- }
- }
-
- /* Now, compare to the previous estimate */
- if (queued > chan->bytes_queued_for_xmit) {
- adj = queued - chan->bytes_queued_for_xmit;
- log_debug(LD_CHANNEL,
- "Increasing queue size for channel " U64_FORMAT " by " U64_FORMAT
- " from " U64_FORMAT " to " U64_FORMAT,
- U64_PRINTF_ARG(chan->global_identifier),
- U64_PRINTF_ARG(adj),
- U64_PRINTF_ARG(chan->bytes_queued_for_xmit),
- U64_PRINTF_ARG(queued));
- /* Update the channel's estimate */
- chan->bytes_queued_for_xmit = queued;
-
- /* Update the global queue size estimate if appropriate */
- if (chan->state == CHANNEL_STATE_OPEN ||
- chan->state == CHANNEL_STATE_MAINT) {
- estimated_total_queue_size += adj;
- log_debug(LD_CHANNEL,
- "Increasing global queue size by " U64_FORMAT " for channel "
- U64_FORMAT ", new size is " U64_FORMAT,
- U64_PRINTF_ARG(adj), U64_PRINTF_ARG(chan->global_identifier),
- U64_PRINTF_ARG(estimated_total_queue_size));
- }
- } else if (queued < chan->bytes_queued_for_xmit) {
- adj = chan->bytes_queued_for_xmit - queued;
- log_debug(LD_CHANNEL,
- "Decreasing queue size for channel " U64_FORMAT " by " U64_FORMAT
- " from " U64_FORMAT " to " U64_FORMAT,
- U64_PRINTF_ARG(chan->global_identifier),
- U64_PRINTF_ARG(adj),
- U64_PRINTF_ARG(chan->bytes_queued_for_xmit),
- U64_PRINTF_ARG(queued));
- /* Update the channel's estimate */
- chan->bytes_queued_for_xmit = queued;
-
- /* Update the global queue size estimate if appropriate */
- if (chan->state == CHANNEL_STATE_OPEN ||
- chan->state == CHANNEL_STATE_MAINT) {
- estimated_total_queue_size -= adj;
- log_debug(LD_CHANNEL,
- "Decreasing global queue size by " U64_FORMAT " for channel "
- U64_FORMAT ", new size is " U64_FORMAT,
- U64_PRINTF_ARG(adj), U64_PRINTF_ARG(chan->global_identifier),
- U64_PRINTF_ARG(estimated_total_queue_size));
- }
- }
-}
-
diff --git a/src/or/channel.h b/src/or/channel.h
index 074cc86fe7..0af5aed414 100644
--- a/src/or/channel.h
+++ b/src/or/channel.h
@@ -19,10 +19,6 @@ typedef void (*channel_listener_fn_ptr)(channel_listener_t *, channel_t *);
typedef void (*channel_cell_handler_fn_ptr)(channel_t *, cell_t *);
typedef void (*channel_var_cell_handler_fn_ptr)(channel_t *, var_cell_t *);
-struct cell_queue_entry_s;
-TOR_SIMPLEQ_HEAD(chan_cell_queue, cell_queue_entry_s);
-typedef struct chan_cell_queue chan_cell_queue_t;
-
/**
* This enum is used by channelpadding to decide when to pad channels.
* Don't add values to it without updating the checks in
@@ -42,7 +38,6 @@ typedef enum {
* to a particular node, and once constructed support the abstract operations
* defined below.
*/
-
struct channel_s {
/** Magic number for type-checking cast macros */
uint32_t magic;
@@ -92,10 +87,9 @@ struct channel_s {
* Used to decide what channels to pad, and when. */
channel_usage_info_t channel_usage;
- /** When should we send a cell for netflow padding, in absolute
- * milliseconds since monotime system start. 0 means no padding
- * is scheduled. */
- uint64_t next_padding_time_ms;
+ /** When should we send a cell for netflow padding? 0 means no padding is
+ * scheduled. */
+ monotime_coarse_t next_padding_time;
/** The callback pointer for the padding callbacks */
tor_timer_t *padding_timer;
@@ -162,7 +156,7 @@ struct channel_s {
time_t timestamp_active; /* Any activity */
/**
- * This is a high-resolution monotonic timestamp that marks when we
+ * This is a monotonic timestamp that marks when we
* believe the channel has actually sent or received data to/from
* the wire. Right now, it is used to determine when we should send
* a padding cell for channelpadding.
@@ -171,7 +165,7 @@ struct channel_s {
* accurately reflect actual network data transfer? Or might this be
* very wrong wrt when bytes actually go on the wire?
*/
- uint64_t timestamp_xfer_ms;
+ monotime_coarse_t timestamp_xfer;
/* Methods implemented by the lower layer */
@@ -259,21 +253,12 @@ struct channel_s {
*/
ed25519_public_key_t ed25519_identity;
- /** Nickname of the OR on the other side, or NULL if none. */
- char *nickname;
-
/**
* Linked list of channels with the same RSA identity digest, for use with
* the digest->channel map
*/
TOR_LIST_ENTRY(channel_s) next_with_same_id;
- /** List of incoming cells to handle */
- chan_cell_queue_t incoming_queue;
-
- /** List of queued outgoing cells */
- chan_cell_queue_t outgoing_queue;
-
/** Circuit mux for circuits sending on this channel */
circuitmux_t *cmux;
@@ -320,7 +305,6 @@ struct channel_s {
/** Channel timestamps for cell channels */
time_t timestamp_client; /* Client used this, according to relay.c */
- time_t timestamp_drained; /* Output queue empty */
time_t timestamp_recv; /* Cell received from lower layer */
time_t timestamp_xmit; /* Cell sent to lower layer */
@@ -337,14 +321,6 @@ struct channel_s {
/** Channel counters for cell channels */
uint64_t n_cells_recved, n_bytes_recved;
uint64_t n_cells_xmitted, n_bytes_xmitted;
-
- /** Our current contribution to the scheduler's total xmit queue */
- uint64_t bytes_queued_for_xmit;
-
- /** Number of bytes in this channel's cell queue; does not include
- * lower-layer queueing.
- */
- uint64_t bytes_in_queue;
};
struct channel_listener_s {
@@ -412,18 +388,13 @@ channel_listener_state_to_string(channel_listener_state_t state);
/* Abstract channel operations */
void channel_mark_for_close(channel_t *chan);
-void channel_write_cell(channel_t *chan, cell_t *cell);
-void channel_write_packed_cell(channel_t *chan, packed_cell_t *cell);
-void channel_write_var_cell(channel_t *chan, var_cell_t *cell);
+int channel_write_packed_cell(channel_t *chan, packed_cell_t *cell);
void channel_listener_mark_for_close(channel_listener_t *chan_l);
/* Channel callback registrations */
/* Listener callback */
-channel_listener_fn_ptr
-channel_listener_get_listener_fn(channel_listener_t *chan);
-
void channel_listener_set_listener_fn(channel_listener_t *chan,
channel_listener_fn_ptr listener);
@@ -457,36 +428,9 @@ void channel_set_cmux_policy_everywhere(circuitmux_policy_t *pol);
#ifdef TOR_CHANNEL_INTERNAL_
#ifdef CHANNEL_PRIVATE_
-/* Cell queue structure (here rather than channel.c for test suite use) */
-typedef struct cell_queue_entry_s cell_queue_entry_t;
-struct cell_queue_entry_s {
- TOR_SIMPLEQ_ENTRY(cell_queue_entry_s) next;
- enum {
- CELL_QUEUE_FIXED,
- CELL_QUEUE_VAR,
- CELL_QUEUE_PACKED
- } type;
- union {
- struct {
- cell_t *cell;
- } fixed;
- struct {
- var_cell_t *var_cell;
- } var;
- struct {
- packed_cell_t *packed_cell;
- } packed;
- } u;
-};
-
-/* Cell queue functions for benefit of test suite */
-STATIC int chan_cell_queue_len(const chan_cell_queue_t *queue);
-
-STATIC void cell_queue_entry_free(cell_queue_entry_t *q, int handed_off);
+STATIC void channel_add_to_digest_map(channel_t *chan);
-void channel_write_cell_generic_(channel_t *chan, const char *cell_type,
- void *cell, cell_queue_entry_t *q);
#endif /* defined(CHANNEL_PRIVATE_) */
/* Channel operations for subclasses and internal use only */
@@ -511,13 +455,12 @@ void channel_close_from_lower_layer(channel_t *chan);
void channel_close_for_error(channel_t *chan);
void channel_closed(channel_t *chan);
-void channel_listener_close_from_lower_layer(channel_listener_t *chan_l);
-void channel_listener_close_for_error(channel_listener_t *chan_l);
-void channel_listener_closed(channel_listener_t *chan_l);
-
/* Free a channel */
-void channel_free(channel_t *chan);
-void channel_listener_free(channel_listener_t *chan_l);
+void channel_free_(channel_t *chan);
+#define channel_free(chan) FREE_AND_NULL(channel_t, channel_free_, (chan))
+void channel_listener_free_(channel_listener_t *chan_l);
+#define channel_listener_free(chan_l) \
+ FREE_AND_NULL(channel_listener_t, channel_listener_free_, (chan_l))
/* State/metadata setters */
@@ -532,9 +475,6 @@ void channel_mark_remote(channel_t *chan);
void channel_set_identity_digest(channel_t *chan,
const char *identity_digest,
const ed25519_public_key_t *ed_identity);
-void channel_set_remote_end(channel_t *chan,
- const char *identity_digest,
- const char *nickname);
void channel_listener_change_state(channel_listener_t *chan_l,
channel_listener_state_t to_state);
@@ -542,7 +482,6 @@ void channel_listener_change_state(channel_listener_t *chan_l,
/* Timestamp updates */
void channel_timestamp_created(channel_t *chan);
void channel_timestamp_active(channel_t *chan);
-void channel_timestamp_drained(channel_t *chan);
void channel_timestamp_recv(channel_t *chan);
void channel_timestamp_xmit(channel_t *chan);
@@ -556,12 +495,7 @@ void channel_listener_queue_incoming(channel_listener_t *listener,
channel_t *incoming);
/* Incoming cell handling */
-void channel_process_cells(channel_t *chan);
-void channel_queue_cell(channel_t *chan, cell_t *cell);
-void channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell);
-
-/* Outgoing cell handling */
-void channel_flush_cells(channel_t *chan);
+void channel_process_cell(channel_t *chan, cell_t *cell);
/* Request from lower layer for more cells if available */
MOCK_DECL(ssize_t, channel_flush_some_cells,
@@ -576,10 +510,6 @@ void channel_notify_flushed(channel_t *chan);
/* Handle stuff we need to do on open like notifying circuits */
void channel_do_open_actions(channel_t *chan);
-#ifdef TOR_UNIT_TESTS
-extern uint64_t estimated_total_queue_size;
-#endif
-
#endif /* defined(TOR_CHANNEL_INTERNAL_) */
/* Helper functions to perform operations on channels */
@@ -681,7 +611,6 @@ MOCK_DECL(void,channel_set_circid_type,(channel_t *chan,
crypto_pk_t *identity_rcvd,
int consider_identity));
void channel_timestamp_client(channel_t *chan);
-void channel_update_xmit_queue_size(channel_t *chan);
const char * channel_listener_describe_transport(channel_listener_t *chan_l);
void channel_listener_dump_statistics(channel_listener_t *chan_l,
@@ -693,33 +622,22 @@ void channel_check_for_duplicates(void);
void channel_update_bad_for_new_circs(const char *digest, int force);
/* Flow control queries */
-uint64_t channel_get_global_queue_estimate(void);
int channel_num_cells_writeable(channel_t *chan);
/* Timestamp queries */
time_t channel_when_created(channel_t *chan);
-time_t channel_when_last_active(channel_t *chan);
time_t channel_when_last_client(channel_t *chan);
-time_t channel_when_last_drained(channel_t *chan);
-time_t channel_when_last_recv(channel_t *chan);
time_t channel_when_last_xmit(channel_t *chan);
-time_t channel_listener_when_created(channel_listener_t *chan_l);
-time_t channel_listener_when_last_active(channel_listener_t *chan_l);
-time_t channel_listener_when_last_accepted(channel_listener_t *chan_l);
-
/* Counter queries */
-uint64_t channel_count_recved(channel_t *chan);
-uint64_t channel_count_xmitted(channel_t *chan);
-
-uint64_t channel_listener_count_accepted(channel_listener_t *chan_l);
-
int packed_cell_is_destroy(channel_t *chan,
const packed_cell_t *packed_cell,
circid_t *circid_out);
/* Declare the handle helpers */
HANDLE_DECL(channel, channel_s,)
+#define channel_handle_free(h) \
+ FREE_AND_NULL(channel_handle_t, channel_handle_free_, (h))
#endif /* !defined(TOR_CHANNEL_H) */
diff --git a/src/or/channelpadding.c b/src/or/channelpadding.c
index 435436c45c..5da3009e67 100644
--- a/src/or/channelpadding.c
+++ b/src/or/channelpadding.c
@@ -23,7 +23,8 @@
#include <event2/event.h>
#include "rendservice.h"
-STATIC int channelpadding_get_netflow_inactive_timeout_ms(const channel_t *);
+STATIC int32_t channelpadding_get_netflow_inactive_timeout_ms(
+ const channel_t *);
STATIC int channelpadding_send_disable_command(channel_t *);
STATIC int64_t channelpadding_compute_time_until_pad_for_netflow(channel_t *);
@@ -68,7 +69,7 @@ static int consensus_nf_pad_single_onion;
/**
* This macro tells us if either end of the channel is connected to a client.
* (If we're not a server, we're definitely a client. If the channel thinks
- * its a client, use that. Then finally verify in the consensus).
+ * it's a client, use that. Then finally verify in the consensus).
*/
#define CHANNEL_IS_CLIENT(chan, options) \
(!public_server_mode((options)) || channel_is_client(chan) || \
@@ -165,7 +166,7 @@ channelpadding_new_consensus_params(networkstatus_t *ns)
* Returns the next timeout period (in milliseconds) after which we should
* send a padding packet, or 0 if padding is disabled.
*/
-STATIC int
+STATIC int32_t
channelpadding_get_netflow_inactive_timeout_ms(const channel_t *chan)
{
int low_timeout = consensus_nf_ito_low;
@@ -377,15 +378,16 @@ channelpadding_send_padding_cell_for_callback(channel_t *chan)
chan->pending_padding_callback = 0;
- if (!chan->next_padding_time_ms ||
+ if (monotime_coarse_is_zero(&chan->next_padding_time) ||
chan->has_queued_writes(chan)) {
/* We must have been active before the timer fired */
- chan->next_padding_time_ms = 0;
+ monotime_coarse_zero(&chan->next_padding_time);
return;
}
{
- uint64_t now = monotime_coarse_absolute_msec();
+ monotime_coarse_t now;
+ monotime_coarse_get(&now);
log_fn(LOG_INFO,LD_OR,
"Sending netflow keepalive on "U64_FORMAT" to %s (%s) after "
@@ -393,12 +395,13 @@ channelpadding_send_padding_cell_for_callback(channel_t *chan)
U64_PRINTF_ARG(chan->global_identifier),
safe_str_client(chan->get_remote_descr(chan, 0)),
safe_str_client(hex_str(chan->identity_digest, DIGEST_LEN)),
- U64_PRINTF_ARG(now - chan->timestamp_xfer_ms),
- U64_PRINTF_ARG(now - chan->next_padding_time_ms));
+ I64_PRINTF_ARG(monotime_coarse_diff_msec(&chan->timestamp_xfer,&now)),
+ I64_PRINTF_ARG(
+ monotime_coarse_diff_msec(&chan->next_padding_time,&now)));
}
/* Clear the timer */
- chan->next_padding_time_ms = 0;
+ monotime_coarse_zero(&chan->next_padding_time);
/* Send the padding cell. This will cause the channel to get a
* fresh timestamp_active */
@@ -500,38 +503,42 @@ channelpadding_schedule_padding(channel_t *chan, int in_ms)
STATIC int64_t
channelpadding_compute_time_until_pad_for_netflow(channel_t *chan)
{
- uint64_t long_now = monotime_coarse_absolute_msec();
+ monotime_coarse_t now;
+ monotime_coarse_get(&now);
- if (!chan->next_padding_time_ms) {
+ if (monotime_coarse_is_zero(&chan->next_padding_time)) {
/* If the below line or crypto_rand_int() shows up on a profile,
* we can avoid getting a timeout until we're at least nf_ito_lo
* from a timeout window. That will prevent us from setting timers
* on connections that were active up to 1.5 seconds ago.
* Idle connections should only call this once every 5.5s on average
* though, so that might be a micro-optimization for little gain. */
- int64_t padding_timeout =
+ int32_t padding_timeout =
channelpadding_get_netflow_inactive_timeout_ms(chan);
if (!padding_timeout)
return CHANNELPADDING_TIME_DISABLED;
- chan->next_padding_time_ms = padding_timeout
- + chan->timestamp_xfer_ms;
+ monotime_coarse_add_msec(&chan->next_padding_time,
+ &chan->timestamp_xfer,
+ padding_timeout);
}
+ const int64_t ms_till_pad =
+ monotime_coarse_diff_msec(&now, &chan->next_padding_time);
+
/* If the next padding time is beyond the maximum possible consensus value,
* then this indicates a clock jump, so just send padding now. This is
* better than using monotonic time because we want to avoid the situation
* where we wait around forever for monotonic time to move forward after
* a clock jump far into the past.
*/
- if (chan->next_padding_time_ms > long_now +
- DFLT_NETFLOW_INACTIVE_KEEPALIVE_MAX) {
+ if (ms_till_pad > DFLT_NETFLOW_INACTIVE_KEEPALIVE_MAX) {
tor_fragile_assert();
log_warn(LD_BUG,
"Channel padding timeout scheduled "I64_FORMAT"ms in the future. "
"Did the monotonic clock just jump?",
- I64_PRINTF_ARG(chan->next_padding_time_ms - long_now));
+ I64_PRINTF_ARG(ms_till_pad));
return 0; /* Clock jumped: Send padding now */
}
@@ -540,11 +547,8 @@ channelpadding_compute_time_until_pad_for_netflow(channel_t *chan)
from now which we should send padding, so we can schedule a callback
then.
*/
- if (long_now +
- (TOR_HOUSEKEEPING_CALLBACK_MSEC + TOR_HOUSEKEEPING_CALLBACK_SLACK_MSEC)
- >= chan->next_padding_time_ms) {
- int64_t ms_until_pad_for_netflow = chan->next_padding_time_ms -
- long_now;
+ if (ms_till_pad < (TOR_HOUSEKEEPING_CALLBACK_MSEC +
+ TOR_HOUSEKEEPING_CALLBACK_SLACK_MSEC)) {
/* If the padding time is in the past, that means that libevent delayed
* calling the once-per-second callback due to other work taking too long.
* See https://bugs.torproject.org/22212 and
@@ -554,16 +558,16 @@ channelpadding_compute_time_until_pad_for_netflow(channel_t *chan)
* and allowed a router to emit a netflow frame, just so we don't forget
* about it entirely.. */
#define NETFLOW_MISSED_WINDOW (150000 - DFLT_NETFLOW_INACTIVE_KEEPALIVE_HIGH)
- if (ms_until_pad_for_netflow < 0) {
- int severity = (ms_until_pad_for_netflow < -NETFLOW_MISSED_WINDOW)
+ if (ms_till_pad < 0) {
+ int severity = (ms_till_pad < -NETFLOW_MISSED_WINDOW)
? LOG_NOTICE : LOG_INFO;
log_fn(severity, LD_OR,
"Channel padding timeout scheduled "I64_FORMAT"ms in the past. ",
- I64_PRINTF_ARG(-ms_until_pad_for_netflow));
+ I64_PRINTF_ARG(-ms_till_pad));
return 0; /* Clock jumped: Send padding now */
}
- return ms_until_pad_for_netflow;
+ return ms_till_pad;
}
return CHANNELPADDING_TIME_LATER;
}
diff --git a/src/or/channeltls.c b/src/or/channeltls.c
index 0244871548..9000703b01 100644
--- a/src/or/channeltls.c
+++ b/src/or/channeltls.c
@@ -34,7 +34,6 @@
* Define this so channel.h gives us things only channel_t subclasses
* should touch.
*/
-
#define TOR_CHANNEL_INTERNAL_
#define CHANNELTLS_PRIVATE
@@ -132,7 +131,6 @@ static void channel_tls_process_padding_negotiate_cell(cell_t *cell,
* Do parts of channel_tls_t initialization common to channel_tls_connect()
* and channel_tls_handle_incoming().
*/
-
STATIC void
channel_tls_common_init(channel_tls_t *tlschan)
{
@@ -168,13 +166,12 @@ channel_tls_common_init(channel_tls_t *tlschan)
}
/**
- * Start a new TLS channel
+ * Start a new TLS channel.
*
* Launch a new OR connection to <b>addr</b>:<b>port</b> and expect to
* handshake with an OR with identity digest <b>id_digest</b>, and wrap
* it in a channel_tls_t.
*/
-
channel_t *
channel_tls_connect(const tor_addr_t *addr, uint16_t port,
const char *id_digest,
@@ -233,12 +230,11 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port,
}
/**
- * Return the current channel_tls_t listener
+ * Return the current channel_tls_t listener.
*
* Returns the current channel listener for incoming TLS connections, or
* NULL if none has been established
*/
-
channel_listener_t *
channel_tls_get_listener(void)
{
@@ -246,12 +242,11 @@ channel_tls_get_listener(void)
}
/**
- * Start a channel_tls_t listener if necessary
+ * Start a channel_tls_t listener if necessary.
*
* Return the current channel_tls_t listener, or start one if we haven't yet,
* and return that.
*/
-
channel_listener_t *
channel_tls_start_listener(void)
{
@@ -278,12 +273,11 @@ channel_tls_start_listener(void)
}
/**
- * Free everything on shutdown
+ * Free everything on shutdown.
*
* Not much to do here, since channel_free_all() takes care of a lot, but let's
* get rid of the listener.
*/
-
void
channel_tls_free_all(void)
{
@@ -314,9 +308,8 @@ channel_tls_free_all(void)
}
/**
- * Create a new channel around an incoming or_connection_t
+ * Create a new channel around an incoming or_connection_t.
*/
-
channel_t *
channel_tls_handle_incoming(or_connection_t *orconn)
{
@@ -359,7 +352,6 @@ channel_tls_handle_incoming(or_connection_t *orconn)
/**
* Cast a channel_tls_t to a channel_t.
*/
-
channel_t *
channel_tls_to_base(channel_tls_t *tlschan)
{
@@ -372,7 +364,6 @@ channel_tls_to_base(channel_tls_t *tlschan)
* Cast a channel_t to a channel_tls_t, with appropriate type-checking
* asserts.
*/
-
channel_tls_t *
channel_tls_from_base(channel_t *chan)
{
@@ -388,11 +379,10 @@ channel_tls_from_base(channel_t *chan)
*******************************************/
/**
- * Close a channel_tls_t
+ * Close a channel_tls_t.
*
- * This implements the close method for channel_tls_t
+ * This implements the close method for channel_tls_t.
*/
-
static void
channel_tls_close_method(channel_t *chan)
{
@@ -411,12 +401,11 @@ channel_tls_close_method(channel_t *chan)
}
/**
- * Describe the transport for a channel_tls_t
+ * Describe the transport for a channel_tls_t.
*
* This returns the string "TLS channel on connection <id>" to the upper
* layer.
*/
-
static const char *
channel_tls_describe_transport_method(channel_t *chan)
{
@@ -446,7 +435,7 @@ channel_tls_describe_transport_method(channel_t *chan)
}
/**
- * Free a channel_tls_t
+ * Free a channel_tls_t.
*
* This is called by the generic channel layer when freeing a channel_tls_t;
* this happens either on a channel which has already reached
@@ -455,7 +444,6 @@ channel_tls_describe_transport_method(channel_t *chan)
* have an orconn active (which connection_free_all() will get to later),
* so we should null out its channel pointer now.
*/
-
static void
channel_tls_free_method(channel_t *chan)
{
@@ -470,9 +458,8 @@ channel_tls_free_method(channel_t *chan)
}
/**
- * Get an estimate of the average TLS overhead for the upper layer
+ * Get an estimate of the average TLS overhead for the upper layer.
*/
-
static double
channel_tls_get_overhead_estimate_method(channel_t *chan)
{
@@ -505,13 +492,12 @@ channel_tls_get_overhead_estimate_method(channel_t *chan)
}
/**
- * Get the remote address of a channel_tls_t
+ * Get the remote address of a channel_tls_t.
*
* This implements the get_remote_addr method for channel_tls_t; copy the
* remote endpoint of the channel to addr_out and return 1 (always
* succeeds for this transport).
*/
-
static int
channel_tls_get_remote_addr_method(channel_t *chan, tor_addr_t *addr_out)
{
@@ -535,8 +521,8 @@ channel_tls_get_remote_addr_method(channel_t *chan, tor_addr_t *addr_out)
* This implements the get_transport_name for channel_tls_t. If the
* channel uses a pluggable transport, copy its name to
* <b>transport_out</b> and return 0. If the channel did not use a
- * pluggable transport, return -1. */
-
+ * pluggable transport, return -1.
+ */
static int
channel_tls_get_transport_name_method(channel_t *chan, char **transport_out)
{
@@ -554,14 +540,13 @@ channel_tls_get_transport_name_method(channel_t *chan, char **transport_out)
}
/**
- * Get endpoint description of a channel_tls_t
+ * Get endpoint description of a channel_tls_t.
*
* This implements the get_remote_descr method for channel_tls_t; it returns
* a text description of the remote endpoint of the channel suitable for use
- * in log messages. The req parameter is 0 for the canonical address or 1 for
+ * in log messages. The req parameter is 0 for the canonical address or 1 for
* the actual address seen.
*/
-
static const char *
channel_tls_get_remote_descr_method(channel_t *chan, int flags)
{
@@ -617,12 +602,11 @@ channel_tls_get_remote_descr_method(channel_t *chan, int flags)
}
/**
- * Tell the upper layer if we have queued writes
+ * Tell the upper layer if we have queued writes.
*
* This implements the has_queued_writes method for channel_tls t_; it returns
* 1 iff we have queued writes on the outbuf of the underlying or_connection_t.
*/
-
static int
channel_tls_has_queued_writes_method(channel_t *chan)
{
@@ -645,13 +629,12 @@ channel_tls_has_queued_writes_method(channel_t *chan)
}
/**
- * Tell the upper layer if we're canonical
+ * Tell the upper layer if we're canonical.
*
* This implements the is_canonical method for channel_tls_t; if req is zero,
* it returns whether this is a canonical channel, and if it is one it returns
* whether that can be relied upon.
*/
-
static int
channel_tls_is_canonical_method(channel_t *chan, int req)
{
@@ -684,12 +667,11 @@ channel_tls_is_canonical_method(channel_t *chan, int req)
}
/**
- * Check if we match an extend_info_t
+ * Check if we match an extend_info_t.
*
* This implements the matches_extend_info method for channel_tls_t; the upper
* layer wants to know if this channel matches an extend_info_t.
*/
-
static int
channel_tls_matches_extend_info_method(channel_t *chan,
extend_info_t *extend_info)
@@ -720,7 +702,6 @@ channel_tls_matches_extend_info_method(channel_t *chan,
* layer wants to know if this channel matches a target address when extending
* a circuit.
*/
-
static int
channel_tls_matches_target_method(channel_t *chan,
const tor_addr_t *target)
@@ -755,7 +736,6 @@ channel_tls_matches_target_method(channel_t *chan,
* Tell the upper layer how many bytes we have queued and not yet
* sent.
*/
-
static size_t
channel_tls_num_bytes_queued_method(channel_t *chan)
{
@@ -768,13 +748,12 @@ channel_tls_num_bytes_queued_method(channel_t *chan)
}
/**
- * Tell the upper layer how many cells we can accept to write
+ * Tell the upper layer how many cells we can accept to write.
*
* This implements the num_cells_writeable method for channel_tls_t; it
* returns an estimate of the number of cells we can accept with
* channel_tls_write_*_cell().
*/
-
static int
channel_tls_num_cells_writeable_method(channel_t *chan)
{
@@ -799,12 +778,11 @@ channel_tls_num_cells_writeable_method(channel_t *chan)
}
/**
- * Write a cell to a channel_tls_t
+ * Write a cell to a channel_tls_t.
*
* This implements the write_cell method for channel_tls_t; given a
* channel_tls_t and a cell_t, transmit the cell_t.
*/
-
static int
channel_tls_write_cell_method(channel_t *chan, cell_t *cell)
{
@@ -828,12 +806,14 @@ channel_tls_write_cell_method(channel_t *chan, cell_t *cell)
}
/**
- * Write a packed cell to a channel_tls_t
+ * Write a packed cell to a channel_tls_t.
*
* This implements the write_packed_cell method for channel_tls_t; given a
* channel_tls_t and a packed_cell_t, transmit the packed_cell_t.
+ *
+ * Return 0 on success or negative value on error. The caller must free the
+ * packed cell.
*/
-
static int
channel_tls_write_packed_cell_method(channel_t *chan,
packed_cell_t *packed_cell)
@@ -841,7 +821,6 @@ channel_tls_write_packed_cell_method(channel_t *chan,
tor_assert(chan);
channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan);
size_t cell_network_size = get_cell_network_size(chan->wide_circ_ids);
- int written = 0;
tor_assert(tlschan);
tor_assert(packed_cell);
@@ -849,27 +828,23 @@ channel_tls_write_packed_cell_method(channel_t *chan,
if (tlschan->conn) {
connection_buf_add(packed_cell->body, cell_network_size,
TO_CONN(tlschan->conn));
-
- /* This is where the cell is finished; used to be done from relay.c */
- packed_cell_free(packed_cell);
- ++written;
} else {
log_info(LD_CHANNEL,
"something called write_packed_cell on a tlschan "
"(%p with ID " U64_FORMAT " but no conn",
chan, U64_PRINTF_ARG(chan->global_identifier));
+ return -1;
}
- return written;
+ return 0;
}
/**
- * Write a variable-length cell to a channel_tls_t
+ * Write a variable-length cell to a channel_tls_t.
*
* This implements the write_var_cell method for channel_tls_t; given a
* channel_tls_t and a var_cell_t, transmit the var_cell_t.
*/
-
static int
channel_tls_write_var_cell_method(channel_t *chan, var_cell_t *var_cell)
{
@@ -897,11 +872,10 @@ channel_tls_write_var_cell_method(channel_t *chan, var_cell_t *var_cell)
************************************************/
/**
- * Close a channel_listener_t
+ * Close a channel_listener_t.
*
- * This implements the close method for channel_listener_t
+ * This implements the close method for channel_listener_t.
*/
-
static void
channel_tls_listener_close_method(channel_listener_t *chan_l)
{
@@ -937,12 +911,11 @@ channel_tls_listener_close_method(channel_listener_t *chan_l)
}
/**
- * Describe the transport for a channel_listener_t
+ * Describe the transport for a channel_listener_t.
*
* This returns the string "TLS channel (listening)" to the upper
* layer.
*/
-
static const char *
channel_tls_listener_describe_transport_method(channel_listener_t *chan_l)
{
@@ -956,12 +929,11 @@ channel_tls_listener_describe_transport_method(channel_listener_t *chan_l)
******************************************************/
/**
- * Handle an orconn state change
+ * Handle an orconn state change.
*
* This function will be called by connection_or.c when the or_connection_t
* associated with this channel_tls_t changes state.
*/
-
void
channel_tls_handle_state_change_on_orconn(channel_tls_t *chan,
or_connection_t *conn,
@@ -1012,13 +984,12 @@ channel_tls_handle_state_change_on_orconn(channel_tls_t *chan,
#ifdef KEEP_TIMING_STATS
/**
- * Timing states wrapper
+ * Timing states wrapper.
*
* This is a wrapper function around the actual function that processes the
* <b>cell</b> that just arrived on <b>chan</b>. Increment <b>*time</b>
* by the number of microseconds used by the call to <b>*func(cell, chan)</b>.
*/
-
static void
channel_tls_time_process_cell(cell_t *cell, channel_tls_t *chan, int *time,
void (*func)(cell_t *, channel_tls_t *))
@@ -1047,7 +1018,7 @@ channel_tls_time_process_cell(cell_t *cell, channel_tls_t *chan, int *time,
#endif /* defined(KEEP_TIMING_STATS) */
/**
- * Handle an incoming cell on a channel_tls_t
+ * Handle an incoming cell on a channel_tls_t.
*
* This is called from connection_or.c to handle an arriving cell; it checks
* for cell types specific to the handshake for this transport protocol and
@@ -1059,7 +1030,6 @@ channel_tls_time_process_cell(cell_t *cell, channel_tls_t *chan, int *time,
* for copying in the case that it queues; we merely pass pointers through
* which we get from connection_or_process_cells_from_inbuf().
*/
-
void
channel_tls_handle_cell(cell_t *cell, or_connection_t *conn)
{
@@ -1149,7 +1119,7 @@ channel_tls_handle_cell(cell_t *cell, or_connection_t *conn)
* These are all transport independent and we pass them up through the
* channel_t mechanism. They are ultimately handled in command.c.
*/
- channel_queue_cell(TLS_CHAN_TO_BASE(chan), cell);
+ channel_process_cell(TLS_CHAN_TO_BASE(chan), cell);
break;
default:
log_fn(LOG_INFO, LD_PROTOCOL,
@@ -1161,7 +1131,7 @@ channel_tls_handle_cell(cell_t *cell, or_connection_t *conn)
}
/**
- * Handle an incoming variable-length cell on a channel_tls_t
+ * Handle an incoming variable-length cell on a channel_tls_t.
*
* Process a <b>var_cell</b> that was just received on <b>conn</b>. Keep
* internal statistics about how many of each cell we've processed so far
@@ -1177,7 +1147,6 @@ channel_tls_handle_cell(cell_t *cell, or_connection_t *conn)
* caller always frees them after this function returns, so this function
* should never free var_cell.
*/
-
void
channel_tls_handle_var_cell(var_cell_t *var_cell, or_connection_t *conn)
{
@@ -1342,7 +1311,7 @@ channel_tls_handle_var_cell(var_cell_t *var_cell, or_connection_t *conn)
}
/**
- * Update channel marks after connection_or.c has changed an address
+ * Update channel marks after connection_or.c has changed an address.
*
* This is called from connection_or_init_conn_from_address() after the
* connection's _base.addr or real_addr fields have potentially been changed
@@ -1351,7 +1320,6 @@ channel_tls_handle_var_cell(var_cell_t *var_cell, or_connection_t *conn)
* remote router by looking it up in the consensus after we finish the
* handshake and know an authenticated identity digest.
*/
-
void
channel_tls_update_marks(or_connection_t *conn)
{
@@ -1380,12 +1348,11 @@ channel_tls_update_marks(or_connection_t *conn)
}
/**
- * Check if this cell type is allowed before the handshake is finished
+ * Check if this cell type is allowed before the handshake is finished.
*
* Return true if <b>command</b> is a cell command that's allowed to start a
* V3 handshake.
*/
-
static int
command_allowed_before_handshake(uint8_t command)
{
@@ -1400,14 +1367,13 @@ command_allowed_before_handshake(uint8_t command)
}
/**
- * Start a V3 handshake on an incoming connection
+ * Start a V3 handshake on an incoming connection.
*
* Called when we as a server receive an appropriate cell while waiting
* either for a cell or a TLS handshake. Set the connection's state to
* "handshaking_v3', initializes the or_handshake_state field as needed,
* and add the cell to the hash of incoming cells.)
*/
-
static int
enter_v3_handshake_with_cell(var_cell_t *cell, channel_tls_t *chan)
{
@@ -1448,7 +1414,6 @@ enter_v3_handshake_with_cell(var_cell_t *cell, channel_tls_t *chan)
* we support, pick the highest version we have in common, and continue the
* negotiation from there.
*/
-
static void
channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *chan)
{
@@ -1589,7 +1554,7 @@ channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *chan)
}
}
- /* We set this after sending the verions cell. */
+ /* We set this after sending the versions cell. */
/*XXXXX symbolic const.*/
TLS_CHAN_TO_BASE(chan)->wide_circ_ids =
chan->conn->link_proto >= MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS;
@@ -1623,7 +1588,7 @@ channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *chan)
}
/**
- * Process a 'padding_negotiate' cell
+ * Process a 'padding_negotiate' cell.
*
* This function is called to handle an incoming PADDING_NEGOTIATE cell;
* enable or disable padding accordingly, and read and act on its timeout
@@ -1660,12 +1625,11 @@ channel_tls_process_padding_negotiate_cell(cell_t *cell, channel_tls_t *chan)
}
/**
- * Process a 'netinfo' cell
+ * Process a 'netinfo' cell.
*
* This function is called to handle an incoming NETINFO cell; read and act
* on its contents, and set the connection state to "open".
*/
-
static void
channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
{
@@ -2168,7 +2132,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
}
/**
- * Process an AUTH_CHALLENGE cell from a channel_tls_t
+ * Process an AUTH_CHALLENGE cell from a channel_tls_t.
*
* This function is called to handle an incoming AUTH_CHALLENGE cell on a
* channel_tls_t; if we weren't supposed to get one (for example, because we're
@@ -2177,7 +2141,6 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
* want to authenticate, just drop it. If the cell is well-formed *and* we
* want to authenticate, send an AUTHENTICATE cell and then a NETINFO cell.
*/
-
STATIC void
channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan)
{
@@ -2271,7 +2234,7 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan)
}
/**
- * Process an AUTHENTICATE cell from a channel_tls_t
+ * Process an AUTHENTICATE cell from a channel_tls_t.
*
* If it's ill-formed or we weren't supposed to get one or we're not doing a
* v3 handshake, then mark the connection. If it does not authenticate the
@@ -2279,7 +2242,6 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan)
* we didn't get a CERTS cell, etc) mark the connection. Otherwise, accept
* the identity of the router on the other side of the connection.
*/
-
STATIC void
channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan)
{
diff --git a/src/or/circpathbias.c b/src/or/circpathbias.c
index f4bd5ea2fa..c1c1ca31be 100644
--- a/src/or/circpathbias.c
+++ b/src/or/circpathbias.c
@@ -1467,7 +1467,7 @@ pathbias_measure_close_rate(entry_guard_t *guard)
*
* XXX: The attempt count transfer stuff here might be done
* better by keeping separate pending counters that get
- * transfered at circuit close. See ticket #8160.
+ * transferred at circuit close. See ticket #8160.
*/
static void
pathbias_scale_close_rates(entry_guard_t *guard)
@@ -1527,7 +1527,7 @@ pathbias_scale_close_rates(entry_guard_t *guard)
*
* XXX: The attempt count transfer stuff here might be done
* better by keeping separate pending counters that get
- * transfered at circuit close. See ticket #8160.
+ * transferred at circuit close. See ticket #8160.
*/
void
pathbias_scale_use_rates(entry_guard_t *guard)
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 8c52c58e6d..75307c367f 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -75,11 +75,15 @@ static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit,
int is_hs_v3_rp_circuit);
static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath);
static int onion_extend_cpath(origin_circuit_t *circ);
-static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
+STATIC int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
static int circuit_send_first_onion_skin(origin_circuit_t *circ);
static int circuit_build_no_more_hops(origin_circuit_t *circ);
static int circuit_send_intermediate_onion_skin(origin_circuit_t *circ,
crypt_path_t *hop);
+static const node_t *choose_good_middle_server(uint8_t purpose,
+ cpath_build_state_t *state,
+ crypt_path_t *head,
+ int cur_len);
/** This function tries to get a channel to the specified endpoint,
* and then calls command_setup_channel() to give it the right
@@ -343,45 +347,6 @@ circuit_log_path(int severity, unsigned int domain, origin_circuit_t *circ)
tor_free(s);
}
-/** Tell the rep(utation)hist(ory) module about the status of the links
- * in <b>circ</b>. Hops that have become OPEN are marked as successfully
- * extended; the _first_ hop that isn't open (if any) is marked as
- * unable to extend.
- */
-/* XXXX Someday we should learn from OR circuits too. */
-void
-circuit_rep_hist_note_result(origin_circuit_t *circ)
-{
- crypt_path_t *hop;
- const char *prev_digest = NULL;
- hop = circ->cpath;
- if (!hop) /* circuit hasn't started building yet. */
- return;
- if (server_mode(get_options())) {
- const routerinfo_t *me = router_get_my_routerinfo();
- if (!me)
- return;
- prev_digest = me->cache_info.identity_digest;
- }
- do {
- const node_t *node = node_get_by_id(hop->extend_info->identity_digest);
- if (node) { /* Why do we check this? We know the identity. -NM XXXX */
- if (prev_digest) {
- if (hop->state == CPATH_STATE_OPEN)
- rep_hist_note_extend_succeeded(prev_digest, node->identity);
- else {
- rep_hist_note_extend_failed(prev_digest, node->identity);
- break;
- }
- }
- prev_digest = node->identity;
- } else {
- prev_digest = NULL;
- }
- hop=hop->next;
- } while (hop!=circ->cpath);
-}
-
/** Return 1 iff every node in circ's cpath definitely supports ntor. */
static int
circuit_cpath_supports_ntor(const origin_circuit_t *circ)
@@ -452,7 +417,7 @@ onion_populate_cpath(origin_circuit_t *circ)
circ->cpath->extend_info->identity_digest);
/* If we don't know the node and its descriptor, we must be bootstrapping.
*/
- if (!node || !node_has_descriptor(node)) {
+ if (!node || !node_has_preferred_descriptor(node, 1)) {
return 0;
}
}
@@ -631,8 +596,7 @@ circuit_n_chan_done(channel_t *chan, int status, int close_origin_circuits)
tor_assert(chan);
- log_debug(LD_CIRC,"chan to %s/%s, status=%d",
- chan->nickname ? chan->nickname : "NULL",
+ log_debug(LD_CIRC,"chan to %s, status=%d",
channel_get_canonical_remote_descr(chan), status);
pending_circs = smartlist_new();
@@ -825,16 +789,25 @@ should_use_create_fast_for_circuit(origin_circuit_t *circ)
return networkstatus_get_param(NULL, "usecreatefast", 0, 0, 1);
}
-/** Return true if <b>circ</b> is the type of circuit we want to count
- * timeouts from. In particular, we want it to have not completed yet
- * (already completing indicates we cannibalized it), and we want it to
- * have exactly three hops.
+/**
+ * Return true if <b>circ</b> is the type of circuit we want to count
+ * timeouts from.
+ *
+ * In particular, we want to consider any circuit that plans to build
+ * at least 3 hops (but maybe more), but has 3 or fewer hops built
+ * so far.
+ *
+ * We still want to consider circuits before 3 hops, because we need
+ * to decide if we should convert them to a measurement circuit in
+ * circuit_build_times_handle_completed_hop(), rather than letting
+ * slow circuits get killed right away.
*/
int
-circuit_timeout_want_to_count_circ(origin_circuit_t *circ)
+circuit_timeout_want_to_count_circ(const origin_circuit_t *circ)
{
return !circ->has_opened
- && circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN;
+ && circ->build_state->desired_path_len >= DEFAULT_ROUTE_LEN
+ && circuit_get_cpath_opened_len(circ) <= DEFAULT_ROUTE_LEN;
}
/** Decide whether to use a TAP or ntor handshake for connecting to <b>ei</b>
@@ -938,7 +911,9 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
tor_assert(circ->cpath->state == CPATH_STATE_OPEN);
tor_assert(circ->base_.state == CIRCUIT_STATE_BUILDING);
+
crypt_path_t *hop = onion_next_hop_in_cpath(circ->cpath);
+ circuit_build_times_handle_completed_hop(circ);
if (hop) {
/* Case two: we're on a hop after the first. */
@@ -1053,38 +1028,6 @@ circuit_build_no_more_hops(origin_circuit_t *circ)
* I think I got them right, but more checking would be wise. -NM
*/
- if (circuit_timeout_want_to_count_circ(circ)) {
- struct timeval end;
- long timediff;
- tor_gettimeofday(&end);
- timediff = tv_mdiff(&circ->base_.timestamp_began, &end);
-
- /*
- * If the circuit build time is much greater than we would have cut
- * it off at, we probably had a suspend event along this codepath,
- * and we should discard the value.
- */
- if (timediff < 0 ||
- timediff > 2*get_circuit_build_close_time_ms()+1000) {
- log_notice(LD_CIRC, "Strange value for circuit build time: %ldmsec. "
- "Assuming clock jump. Purpose %d (%s)", timediff,
- circ->base_.purpose,
- circuit_purpose_to_string(circ->base_.purpose));
- } else if (!circuit_build_times_disabled(get_options())) {
- /* Only count circuit times if the network is live */
- if (circuit_build_times_network_check_live(
- get_circuit_build_times())) {
- circuit_build_times_add_time(get_circuit_build_times_mutable(),
- (build_time_t)timediff);
- circuit_build_times_set_timeout(get_circuit_build_times_mutable());
- }
-
- if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
- circuit_build_times_network_circ_success(
- get_circuit_build_times_mutable());
- }
- }
- }
log_info(LD_CIRC,"circuit built!");
circuit_reset_failure_count(0);
@@ -1093,7 +1036,6 @@ circuit_build_no_more_hops(origin_circuit_t *circ)
}
pathbias_count_build_success(circ);
- circuit_rep_hist_note_result(circ);
if (is_usable_for_streams)
circuit_has_opened(circ); /* do other actions as necessary */
@@ -1290,7 +1232,7 @@ circuit_extend(cell_t *cell, circuit_t *circ)
const node_t *node = node_get_by_id((const char*)ec.node_id);
const ed25519_public_key_t *node_ed_id = NULL;
if (node &&
- node_supports_ed25519_link_authentication(node) &&
+ node_supports_ed25519_link_authentication(node, 1) &&
(node_ed_id = node_get_ed25519_id(node))) {
ed25519_pubkey_copy(&ec.ed_pubkey, node_ed_id);
}
@@ -1676,12 +1618,49 @@ onionskin_answer(or_circuit_t *circ,
* new_route_len()) in the one-hop tunnel case, so we don't need to
* handle that.
*/
-static int
+int
route_len_for_purpose(uint8_t purpose, extend_info_t *exit_ei)
{
int routelen = DEFAULT_ROUTE_LEN;
int known_purpose = 0;
+ if (circuit_should_use_vanguards(purpose)) {
+ /* Clients want an extra hop for rends to avoid linkability.
+ * Services want it for intro points to avoid publishing their
+ * layer3 guards. They want it for hsdir posts to use
+ * their full layer3 guard set for those connections.
+ * Ex: C - G - L2 - L3 - R
+ * S - G - L2 - L3 - HSDIR
+ * S - G - L2 - L3 - I
+ */
+ if (purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND ||
+ purpose == CIRCUIT_PURPOSE_S_HSDIR_POST ||
+ purpose == CIRCUIT_PURPOSE_HS_VANGUARDS ||
+ purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO)
+ return routelen+1;
+
+ /* If we only have Layer2 vanguards, then we do not need
+ * the extra hop for linkabilty reasons (see below).
+ * This means all hops can be of the form:
+ * S/C - G - L2 - M - R/HSDir/I
+ */
+ if (get_options()->HSLayer2Nodes && !get_options()->HSLayer3Nodes)
+ return routelen+1;
+
+ /* For connections to hsdirs, clients want two extra hops
+ * when using layer3 guards, to avoid linkability.
+ * Same goes for intro points. Note that the route len
+ * includes the intro point or hsdir, hence the +2.
+ * Ex: C - G - L2 - L3 - M - I
+ * C - G - L2 - L3 - M - HSDIR
+ * S - G - L2 - L3 - M - R
+ */
+ if (purpose == CIRCUIT_PURPOSE_S_CONNECT_REND ||
+ purpose == CIRCUIT_PURPOSE_C_HSDIR_GET ||
+ purpose == CIRCUIT_PURPOSE_C_INTRODUCING)
+ return routelen+2;
+ }
+
if (!exit_ei)
return routelen;
@@ -1698,6 +1677,8 @@ route_len_for_purpose(uint8_t purpose, extend_info_t *exit_ei)
/* These three purposes connect to a router that someone else
* might have chosen, so add an extra hop to protect anonymity. */
case CIRCUIT_PURPOSE_C_GENERAL:
+ case CIRCUIT_PURPOSE_C_HSDIR_GET:
+ case CIRCUIT_PURPOSE_S_HSDIR_POST:
/* connecting to hidden service directory */
case CIRCUIT_PURPOSE_C_INTRODUCING:
/* client connecting to introduction point */
@@ -1847,7 +1828,7 @@ ap_stream_wants_exit_attention(connection_t *conn)
* Return NULL if we can't find any suitable routers.
*/
static const node_t *
-choose_good_exit_server_general(int need_uptime, int need_capacity)
+choose_good_exit_server_general(router_crn_flags_t flags)
{
int *n_supported;
int n_pending_connections = 0;
@@ -1857,6 +1838,9 @@ choose_good_exit_server_general(int need_uptime, int need_capacity)
const or_options_t *options = get_options();
const smartlist_t *the_nodes;
const node_t *selected_node=NULL;
+ const int need_uptime = (flags & CRN_NEED_UPTIME) != 0;
+ const int need_capacity = (flags & CRN_NEED_CAPACITY) != 0;
+ const int direct_conn = (flags & CRN_DIRECT_CONN) != 0;
connections = get_connection_array();
@@ -1889,7 +1873,7 @@ choose_good_exit_server_general(int need_uptime, int need_capacity)
*/
continue;
}
- if (!node_has_descriptor(node)) {
+ if (!node_has_preferred_descriptor(node, direct_conn)) {
n_supported[i] = -1;
continue;
}
@@ -2002,7 +1986,8 @@ choose_good_exit_server_general(int need_uptime, int need_capacity)
need_capacity?", fast":"",
need_uptime?", stable":"");
tor_free(n_supported);
- return choose_good_exit_server_general(0, 0);
+ flags &= ~(CRN_NEED_UPTIME|CRN_NEED_CAPACITY);
+ return choose_good_exit_server_general(flags);
}
log_notice(LD_CIRC, "All routers are down or won't exit%s -- "
"choosing a doomed exit at random.",
@@ -2146,6 +2131,98 @@ pick_rendezvous_node(router_crn_flags_t flags)
return router_choose_random_node(NULL, options->ExcludeNodes, flags);
}
+/*
+ * Helper function to pick a configured restricted middle node
+ * (either HSLayer2Nodes or HSLayer3Nodes).
+ *
+ * Make sure that the node we chose is alive, and not excluded,
+ * and return it.
+ *
+ * The exclude_set is a routerset of nodes that the selected node
+ * must not match, and the exclude_list is a simple list of nodes
+ * that the selected node must not be in. Either or both may be
+ * NULL.
+ *
+ * Return NULL if no usable nodes could be found. */
+static const node_t *
+pick_restricted_middle_node(router_crn_flags_t flags,
+ const routerset_t *pick_from,
+ const routerset_t *exclude_set,
+ const smartlist_t *exclude_list,
+ int position_hint)
+{
+ const node_t *middle_node = NULL;
+
+ smartlist_t *whitelisted_live_middles = smartlist_new();
+ smartlist_t *all_live_nodes = smartlist_new();
+
+ tor_assert(pick_from);
+
+ /* Add all running nodes to all_live_nodes */
+ router_add_running_nodes_to_smartlist(all_live_nodes,
+ (flags & CRN_NEED_UPTIME) != 0,
+ (flags & CRN_NEED_CAPACITY) != 0,
+ (flags & CRN_NEED_GUARD) != 0,
+ (flags & CRN_NEED_DESC) != 0,
+ (flags & CRN_PREF_ADDR) != 0,
+ (flags & CRN_DIRECT_CONN) != 0);
+
+ /* Filter all_live_nodes to only add live *and* whitelisted middles
+ * to the list whitelisted_live_middles. */
+ SMARTLIST_FOREACH_BEGIN(all_live_nodes, node_t *, live_node) {
+ if (routerset_contains_node(pick_from, live_node)) {
+ smartlist_add(whitelisted_live_middles, live_node);
+ }
+ } SMARTLIST_FOREACH_END(live_node);
+
+ /* Honor ExcludeNodes */
+ if (exclude_set) {
+ routerset_subtract_nodes(whitelisted_live_middles, exclude_set);
+ }
+
+ if (exclude_list) {
+ smartlist_subtract(whitelisted_live_middles, exclude_list);
+ }
+
+ /**
+ * Max number of restricted nodes before we alert the user and try
+ * to load balance for them.
+ *
+ * The most aggressive vanguard design had 16 nodes at layer3.
+ * Let's give a small ceiling above that. */
+#define MAX_SANE_RESTRICTED_NODES 20
+ /* If the user (or associated tor controller) selected only a few nodes,
+ * assume they took load balancing into account and don't do it for them.
+ *
+ * If there are a lot of nodes in here, assume they did not load balance
+ * and do it for them, but also warn them that they may be Doing It Wrong.
+ */
+ if (smartlist_len(whitelisted_live_middles) <=
+ MAX_SANE_RESTRICTED_NODES) {
+ middle_node = smartlist_choose(whitelisted_live_middles);
+ } else {
+ static ratelim_t pinned_notice_limit = RATELIM_INIT(24*3600);
+ log_fn_ratelim(&pinned_notice_limit, LOG_NOTICE, LD_CIRC,
+ "Your _HSLayer%dNodes setting has resulted "
+ "in %d total nodes. This is a lot of nodes. "
+ "You may want to consider using a Tor controller "
+ "to select and update a smaller set of nodes instead.",
+ position_hint, smartlist_len(whitelisted_live_middles));
+
+ /* NO_WEIGHTING here just means don't take node flags into account
+ * (ie: use consensus measurement only). This is done so that
+ * we don't further surprise the user by not using Exits that they
+ * specified at all */
+ middle_node = node_sl_choose_by_bandwidth(whitelisted_live_middles,
+ NO_WEIGHTING);
+ }
+
+ smartlist_free(whitelisted_live_middles);
+ smartlist_free(all_live_nodes);
+
+ return middle_node;
+}
+
/** Return a pointer to a suitable router to be the exit node for the
* circuit of purpose <b>purpose</b> that we're about to build (or NULL
* if no router is suitable).
@@ -2157,25 +2234,25 @@ pick_rendezvous_node(router_crn_flags_t flags)
* toward the preferences in 'options'.
*/
static const node_t *
-choose_good_exit_server(uint8_t purpose,
- int need_uptime, int need_capacity, int is_internal,
- int need_hs_v3)
+choose_good_exit_server(origin_circuit_t *circ,
+ router_crn_flags_t flags, int is_internal)
{
const or_options_t *options = get_options();
- router_crn_flags_t flags = CRN_NEED_DESC;
- if (need_uptime)
- flags |= CRN_NEED_UPTIME;
- if (need_capacity)
- flags |= CRN_NEED_CAPACITY;
- if (need_hs_v3)
- flags |= CRN_RENDEZVOUS_V3;
-
- switch (purpose) {
+ flags |= CRN_NEED_DESC;
+
+ switch (TO_CIRCUIT(circ)->purpose) {
+ case CIRCUIT_PURPOSE_C_HSDIR_GET:
+ case CIRCUIT_PURPOSE_S_HSDIR_POST:
+ case CIRCUIT_PURPOSE_HS_VANGUARDS:
+ /* For these three, we want to pick the exit like a middle hop,
+ * since it should be random. */
+ tor_assert_nonfatal(is_internal);
+ /* Falls through */
case CIRCUIT_PURPOSE_C_GENERAL:
if (is_internal) /* pick it like a middle hop */
return router_choose_random_node(NULL, options->ExcludeNodes, flags);
else
- return choose_good_exit_server_general(need_uptime,need_capacity);
+ return choose_good_exit_server_general(flags);
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
{
/* Pick a new RP */
@@ -2185,7 +2262,7 @@ choose_good_exit_server(uint8_t purpose,
return rendezvous_node;
}
}
- log_warn(LD_BUG,"Unhandled purpose %d", purpose);
+ log_warn(LD_BUG,"Unhandled purpose %d", TO_CIRCUIT(circ)->purpose);
tor_fragile_assert();
return NULL;
}
@@ -2215,6 +2292,8 @@ warn_if_last_router_excluded(origin_circuit_t *circ,
(int)purpose,
circuit_purpose_to_string(purpose));
return;
+ case CIRCUIT_PURPOSE_S_HSDIR_POST:
+ case CIRCUIT_PURPOSE_C_HSDIR_GET:
case CIRCUIT_PURPOSE_C_GENERAL:
if (circ->build_state->is_internal)
return;
@@ -2298,15 +2377,22 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei,
extend_info_describe(exit_ei));
exit_ei = extend_info_dup(exit_ei);
} else { /* we have to decide one */
+ router_crn_flags_t flags = CRN_NEED_DESC;
+ if (state->need_uptime)
+ flags |= CRN_NEED_UPTIME;
+ if (state->need_capacity)
+ flags |= CRN_NEED_CAPACITY;
+ if (is_hs_v3_rp_circuit)
+ flags |= CRN_RENDEZVOUS_V3;
+ if (state->onehop_tunnel)
+ flags |= CRN_DIRECT_CONN;
const node_t *node =
- choose_good_exit_server(circ->base_.purpose, state->need_uptime,
- state->need_capacity, state->is_internal,
- is_hs_v3_rp_circuit);
+ choose_good_exit_server(circ, flags, state->is_internal);
if (!node) {
log_warn(LD_CIRC,"Failed to choose an exit server");
return -1;
}
- exit_ei = extend_info_from_node(node, 0);
+ exit_ei = extend_info_from_node(node, state->onehop_tunnel);
if (BUG(exit_ei == NULL))
return -1;
}
@@ -2363,6 +2449,10 @@ circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *exit_ei)
/** Return the number of routers in <b>routers</b> that are currently up
* and available for building circuits through.
+ *
+ * (Note that this function may overcount or undercount, if we have
+ * descriptors that are not the type we would prefer to use for some
+ * particular router. See bug #25885.)
*/
MOCK_IMPL(STATIC int,
count_acceptable_nodes, (smartlist_t *nodes))
@@ -2379,7 +2469,7 @@ count_acceptable_nodes, (smartlist_t *nodes))
if (! node->is_valid)
// log_debug(LD_CIRC,"Nope, the directory says %d is not valid.",i);
continue;
- if (! node_has_descriptor(node))
+ if (! node_has_any_descriptor(node))
continue;
/* The node has a descriptor, so we can just check the ntor key directly */
if (!node_has_curve25519_onion_key(node))
@@ -2433,6 +2523,118 @@ cpath_get_n_hops(crypt_path_t **head_ptr)
#endif /* defined(TOR_UNIT_TESTS) */
+/**
+ * Build a list of nodes to exclude from the choice of this middle
+ * hop, based on already chosen nodes.
+ *
+ * XXX: At present, this function does not exclude any nodes from
+ * the vanguard circuits. See
+ * https://trac.torproject.org/projects/tor/ticket/24487
+ */
+static smartlist_t *
+build_middle_exclude_list(uint8_t purpose,
+ cpath_build_state_t *state,
+ crypt_path_t *head,
+ int cur_len)
+{
+ smartlist_t *excluded;
+ const node_t *r;
+ crypt_path_t *cpath;
+ int i;
+
+ excluded = smartlist_new();
+
+ /* Add the exit to the exclude list (note that the exit/last hop is always
+ * chosen first in circuit_establish_circuit()). */
+ if ((r = build_state_get_exit_node(state))) {
+ nodelist_add_node_and_family(excluded, r);
+ }
+
+ /* XXX: We don't apply any other previously selected node restrictions for
+ * vanguards, and allow nodes to be reused for those hop positions in the
+ * same circuit. This is because after many rotations, you get to learn
+ * inner guard nodes through the nodes that are not selected for outer
+ * hops.
+ *
+ * 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
+ * for this.
+ *
+ * (Note further that we can and do still exclude the exit in the block
+ * above, because it is chosen first in circuit_establish_circuit()..) */
+ if (circuit_should_use_vanguards(purpose)) {
+ return excluded;
+ }
+
+ for (i = 0, cpath = head; cpath && i < cur_len; ++i, cpath=cpath->next) {
+ if ((r = node_get_by_id(cpath->extend_info->identity_digest))) {
+ nodelist_add_node_and_family(excluded, r);
+ }
+ }
+
+ return excluded;
+}
+
+/** Return true if we MUST use vanguards for picking this middle node. */
+static int
+middle_node_must_be_vanguard(const or_options_t *options,
+ uint8_t purpose, int cur_len)
+{
+ /* If this is not a hidden service circuit, don't use vanguards */
+ if (!circuit_purpose_is_hidden_service(purpose)) {
+ return 0;
+ }
+
+ /* If we have sticky L2 nodes, and this is an L2 pick, use vanguards */
+ if (options->HSLayer2Nodes && cur_len == 1) {
+ return 1;
+ }
+
+ /* If we have sticky L3 nodes, and this is an L3 pick, use vanguards */
+ if (options->HSLayer3Nodes && cur_len == 2) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/** Pick a sticky vanguard middle node or return NULL if not found.
+ * See doc of pick_restricted_middle_node() for argument details. */
+static const node_t *
+pick_vanguard_middle_node(const or_options_t *options,
+ router_crn_flags_t flags, int cur_len,
+ const smartlist_t *excluded)
+{
+ const routerset_t *vanguard_routerset = NULL;
+ const node_t *node = NULL;
+
+ /* Pick the right routerset based on the current hop */
+ if (cur_len == 1) {
+ vanguard_routerset = options->HSLayer2Nodes;
+ } else if (cur_len == 2) {
+ vanguard_routerset = options->HSLayer3Nodes;
+ } else {
+ /* guaranteed by middle_node_should_be_vanguard() */
+ tor_assert_nonfatal_unreached();
+ return NULL;
+ }
+
+ node = pick_restricted_middle_node(flags, vanguard_routerset,
+ options->ExcludeNodes, excluded,
+ cur_len+1);
+
+ if (!node) {
+ static ratelim_t pinned_warning_limit = RATELIM_INIT(300);
+ log_fn_ratelim(&pinned_warning_limit, LOG_WARN, LD_CIRC,
+ "Could not find a node that matches the configured "
+ "_HSLayer%dNodes set", cur_len+1);
+ }
+
+ return node;
+}
+
/** A helper function used by onion_extend_cpath(). Use <b>purpose</b>
* and <b>state</b> and the cpath <b>head</b> (currently populated only
* to length <b>cur_len</b> to decide a suitable middle hop for a
@@ -2445,9 +2647,7 @@ choose_good_middle_server(uint8_t purpose,
crypt_path_t *head,
int cur_len)
{
- int i;
- const node_t *r, *choice;
- crypt_path_t *cpath;
+ const node_t *choice;
smartlist_t *excluded;
const or_options_t *options = get_options();
router_crn_flags_t flags = CRN_NEED_DESC;
@@ -2456,20 +2656,20 @@ choose_good_middle_server(uint8_t purpose,
log_debug(LD_CIRC, "Contemplating intermediate hop #%d: random choice.",
cur_len+1);
- excluded = smartlist_new();
- if ((r = build_state_get_exit_node(state))) {
- nodelist_add_node_and_family(excluded, r);
- }
- for (i = 0, cpath = head; i < cur_len; ++i, cpath=cpath->next) {
- if ((r = node_get_by_id(cpath->extend_info->identity_digest))) {
- nodelist_add_node_and_family(excluded, r);
- }
- }
+
+ excluded = build_middle_exclude_list(purpose, state, head, cur_len);
if (state->need_uptime)
flags |= CRN_NEED_UPTIME;
if (state->need_capacity)
flags |= CRN_NEED_CAPACITY;
+
+ /** If a hidden service circuit wants a specific middle node, pin it. */
+ if (middle_node_must_be_vanguard(options, purpose, cur_len)) {
+ log_debug(LD_GENERAL, "Picking a sticky node (cur_len = %d)", cur_len);
+ return pick_vanguard_middle_node(options, flags, cur_len, excluded);
+ }
+
choice = router_choose_random_node(excluded, options->ExcludeNodes, flags);
smartlist_free(excluded);
return choice;
@@ -2608,7 +2808,7 @@ onion_extend_cpath(origin_circuit_t *circ)
/** Create a new hop, annotate it with information about its
* corresponding router <b>choice</b>, and append it to the
* end of the cpath <b>head_ptr</b>. */
-static int
+STATIC int
onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice)
{
crypt_path_t *hop = tor_malloc_zero(sizeof(crypt_path_t));
@@ -2657,9 +2857,10 @@ extend_info_new(const char *nickname,
* of the node (i.e. its IPv4 address) unless
* <b>for_direct_connect</b> is true, in which case the preferred
* address is used instead. May return NULL if there is not enough
- * info about <b>node</b> to extend to it--for example, if there is no
- * routerinfo_t or microdesc_t, or if for_direct_connect is true and none of
- * the node's addresses are allowed by tor's firewall and IP version config.
+ * info about <b>node</b> to extend to it--for example, if the preferred
+ * routerinfo_t or microdesc_t is missing, or if for_direct_connect is
+ * true and none of the node's addresses is allowed by tor's firewall
+ * and IP version config.
**/
extend_info_t *
extend_info_from_node(const node_t *node, int for_direct_connect)
@@ -2667,8 +2868,9 @@ extend_info_from_node(const node_t *node, int for_direct_connect)
tor_addr_port_t ap;
int valid_addr = 0;
- if (node->ri == NULL && (node->rs == NULL || node->md == NULL))
+ if (!node_has_preferred_descriptor(node, for_direct_connect)) {
return NULL;
+ }
/* Choose a preferred address first, but fall back to an allowed address.
* choose_address returns 1 on success, but get_prim_orport returns 0. */
@@ -2699,7 +2901,7 @@ extend_info_from_node(const node_t *node, int for_direct_connect)
/* Don't send the ed25519 pubkey unless the target node actually supports
* authenticating with it. */
- if (node_supports_ed25519_link_authentication(node)) {
+ if (node_supports_ed25519_link_authentication(node, 0)) {
log_info(LD_CIRC, "Including Ed25519 ID for %s", node_describe(node));
ed_pubkey = node_get_ed25519_id(node);
} else if (node_get_ed25519_id(node)) {
@@ -2708,12 +2910,16 @@ extend_info_from_node(const node_t *node, int for_direct_connect)
node_describe(node));
}
+ /* Retrieve the curve25519 pubkey. */
+ const curve25519_public_key_t *curve_pubkey =
+ node_get_curve25519_onion_key(node);
+
if (valid_addr && node->ri)
return extend_info_new(node->ri->nickname,
node->identity,
ed_pubkey,
node->ri->onion_pkey,
- node->ri->onion_curve25519_pkey,
+ curve_pubkey,
&ap.addr,
ap.port);
else if (valid_addr && node->rs && node->md)
@@ -2721,7 +2927,7 @@ extend_info_from_node(const node_t *node, int for_direct_connect)
node->identity,
ed_pubkey,
node->md->onion_pkey,
- node->md->onion_curve25519_pkey,
+ curve_pubkey,
&ap.addr,
ap.port);
else
@@ -2730,7 +2936,7 @@ extend_info_from_node(const node_t *node, int for_direct_connect)
/** Release storage held by an extend_info_t struct. */
void
-extend_info_free(extend_info_t *info)
+extend_info_free_(extend_info_t *info)
{
if (!info)
return;
diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h
index b8a651e055..bea31ad0dd 100644
--- a/src/or/circuitbuild.h
+++ b/src/or/circuitbuild.h
@@ -12,11 +12,11 @@
#ifndef TOR_CIRCUITBUILD_H
#define TOR_CIRCUITBUILD_H
+int route_len_for_purpose(uint8_t purpose, extend_info_t *exit_ei);
char *circuit_list_path(origin_circuit_t *circ, int verbose);
char *circuit_list_path_for_controller(origin_circuit_t *circ);
void circuit_log_path(int severity, unsigned int domain,
origin_circuit_t *circ);
-void circuit_rep_hist_note_result(origin_circuit_t *circ);
origin_circuit_t *origin_circuit_init(uint8_t purpose, int flags);
origin_circuit_t *circuit_establish_circuit(uint8_t purpose,
extend_info_t *exit,
@@ -27,7 +27,7 @@ int circuit_handle_first_hop(origin_circuit_t *circ);
void circuit_n_chan_done(channel_t *chan, int status,
int close_origin_circuits);
int inform_testing_reachability(void);
-int circuit_timeout_want_to_count_circ(origin_circuit_t *circ);
+int circuit_timeout_want_to_count_circ(const origin_circuit_t *circ);
int circuit_send_next_onion_skin(origin_circuit_t *circ);
void circuit_note_clock_jumped(int seconds_elapsed);
int circuit_extend(cell_t *cell, circuit_t *circ);
@@ -58,7 +58,9 @@ extend_info_t *extend_info_new(const char *nickname,
const tor_addr_t *addr, uint16_t port);
extend_info_t *extend_info_from_node(const node_t *r, int for_direct_connect);
extend_info_t *extend_info_dup(extend_info_t *info);
-void extend_info_free(extend_info_t *info);
+void extend_info_free_(extend_info_t *info);
+#define extend_info_free(info) \
+ FREE_AND_NULL(extend_info_t, extend_info_free_, (info))
int extend_info_addr_is_allowed(const tor_addr_t *addr);
int extend_info_supports_tap(const extend_info_t* ei);
int extend_info_supports_ntor(const extend_info_t* ei);
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index d442887c9e..7bdef0b878 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -51,6 +51,8 @@
* logic, which was originally circuit-focused.
**/
#define CIRCUITLIST_PRIVATE
+#include "torint.h" /* TOR_PRIuSZ */
+
#include "or.h"
#include "channel.h"
#include "circpathbias.h"
@@ -80,6 +82,9 @@
#include "routerlist.h"
#include "routerset.h"
#include "channelpadding.h"
+#include "compress_lzma.h"
+#include "compress_zlib.h"
+#include "compress_zstd.h"
#include "ht.h"
@@ -108,6 +113,14 @@ static void cpath_ref_decref(crypt_path_reference_t *cpath_ref);
static void circuit_about_to_free_atexit(circuit_t *circ);
static void circuit_about_to_free(circuit_t *circ);
+/**
+ * A cached value of the current state of the origin circuit list. Has the
+ * value 1 if we saw any opened circuits recently (since the last call to
+ * circuit_any_opened_circuits(), which gets called around once a second by
+ * circuit_expire_building). 0 otherwise.
+ */
+static int any_opened_circs_cached_val = 0;
+
/********* END VARIABLES ************/
/** A map from channel and circuit ID to circuit. (Lookup performance is
@@ -504,8 +517,7 @@ circuit_count_pending_on_channel(channel_t *chan)
circuit_get_all_pending_on_channel(sl, chan);
cnt = smartlist_len(sl);
smartlist_free(sl);
- log_debug(LD_CIRC,"or_conn to %s at %s, %d pending circs",
- chan->nickname ? chan->nickname : "NULL",
+ log_debug(LD_CIRC,"or_conn to %s, %d pending circs",
channel_get_canonical_remote_descr(chan),
cnt);
return cnt;
@@ -596,6 +608,56 @@ circuit_get_global_origin_circuit_list(void)
return global_origin_circuit_list;
}
+/**
+ * Return true if we have any opened general-purpose 3 hop
+ * origin circuits.
+ *
+ * The result from this function is cached for use by
+ * circuit_any_opened_circuits_cached().
+ */
+int
+circuit_any_opened_circuits(void)
+{
+ SMARTLIST_FOREACH_BEGIN(circuit_get_global_origin_circuit_list(),
+ const origin_circuit_t *, next_circ) {
+ if (!TO_CIRCUIT(next_circ)->marked_for_close &&
+ next_circ->has_opened &&
+ TO_CIRCUIT(next_circ)->state == CIRCUIT_STATE_OPEN &&
+ TO_CIRCUIT(next_circ)->purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT &&
+ next_circ->build_state &&
+ next_circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN) {
+ circuit_cache_opened_circuit_state(1);
+ return 1;
+ }
+ } SMARTLIST_FOREACH_END(next_circ);
+
+ circuit_cache_opened_circuit_state(0);
+ return 0;
+}
+
+/**
+ * Cache the "any circuits opened" state, as specified in param
+ * circuits_are_opened. This is a helper function to update
+ * the circuit opened status whenever we happen to look at the
+ * circuit list.
+ */
+void
+circuit_cache_opened_circuit_state(int circuits_are_opened)
+{
+ any_opened_circs_cached_val = circuits_are_opened;
+}
+
+/**
+ * Return true if there were any opened circuits since the last call to
+ * circuit_any_opened_circuits(), or since circuit_expire_building() last
+ * ran (it runs roughly once per second).
+ */
+int
+circuit_any_opened_circuits_cached(void)
+{
+ return any_opened_circs_cached_val;
+}
+
/** Function to make circ-\>state human-readable */
const char *
circuit_state_to_string(int state)
@@ -630,6 +692,10 @@ circuit_purpose_to_controller_string(uint8_t purpose)
case CIRCUIT_PURPOSE_C_GENERAL:
return "GENERAL";
+
+ case CIRCUIT_PURPOSE_C_HSDIR_GET:
+ return "HS_CLIENT_HSDIR";
+
case CIRCUIT_PURPOSE_C_INTRODUCING:
case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT:
case CIRCUIT_PURPOSE_C_INTRODUCE_ACKED:
@@ -641,6 +707,9 @@ circuit_purpose_to_controller_string(uint8_t purpose)
case CIRCUIT_PURPOSE_C_REND_JOINED:
return "HS_CLIENT_REND";
+ case CIRCUIT_PURPOSE_S_HSDIR_POST:
+ return "HS_SERVICE_HSDIR";
+
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
case CIRCUIT_PURPOSE_S_INTRO:
return "HS_SERVICE_INTRO";
@@ -657,6 +726,8 @@ circuit_purpose_to_controller_string(uint8_t purpose)
return "CONTROLLER";
case CIRCUIT_PURPOSE_PATH_BIAS_TESTING:
return "PATH_BIAS_TESTING";
+ case CIRCUIT_PURPOSE_HS_VANGUARDS:
+ return "HS_VANGUARDS";
default:
tor_snprintf(buf, sizeof(buf), "UNKNOWN_%d", (int)purpose);
@@ -685,6 +756,7 @@ circuit_purpose_to_controller_hs_state_string(uint8_t purpose)
case CIRCUIT_PURPOSE_TESTING:
case CIRCUIT_PURPOSE_CONTROLLER:
case CIRCUIT_PURPOSE_PATH_BIAS_TESTING:
+ case CIRCUIT_PURPOSE_HS_VANGUARDS:
return NULL;
case CIRCUIT_PURPOSE_INTRO_POINT:
@@ -694,6 +766,7 @@ circuit_purpose_to_controller_hs_state_string(uint8_t purpose)
case CIRCUIT_PURPOSE_REND_ESTABLISHED:
return "OR_HS_R_JOINED";
+ case CIRCUIT_PURPOSE_C_HSDIR_GET:
case CIRCUIT_PURPOSE_C_INTRODUCING:
return "HSCI_CONNECTING";
case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT:
@@ -710,6 +783,7 @@ circuit_purpose_to_controller_hs_state_string(uint8_t purpose)
case CIRCUIT_PURPOSE_C_REND_JOINED:
return "HSCR_JOINED";
+ case CIRCUIT_PURPOSE_S_HSDIR_POST:
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
return "HSSI_CONNECTING";
case CIRCUIT_PURPOSE_S_INTRO:
@@ -735,9 +809,9 @@ circuit_purpose_to_string(uint8_t purpose)
case CIRCUIT_PURPOSE_INTRO_POINT:
return "Acting as intro point";
case CIRCUIT_PURPOSE_REND_POINT_WAITING:
- return "Acting as rendevous (pending)";
+ return "Acting as rendezvous (pending)";
case CIRCUIT_PURPOSE_REND_ESTABLISHED:
- return "Acting as rendevous (established)";
+ return "Acting as rendezvous (established)";
case CIRCUIT_PURPOSE_C_GENERAL:
return "General-purpose client";
case CIRCUIT_PURPOSE_C_INTRODUCING:
@@ -754,6 +828,9 @@ circuit_purpose_to_string(uint8_t purpose)
return "Hidden service client: Pending rendezvous point (ack received)";
case CIRCUIT_PURPOSE_C_REND_JOINED:
return "Hidden service client: Active rendezvous point";
+ case CIRCUIT_PURPOSE_C_HSDIR_GET:
+ return "Hidden service client: Fetching HS descriptor";
+
case CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT:
return "Measuring circuit timeout";
@@ -765,6 +842,8 @@ circuit_purpose_to_string(uint8_t purpose)
return "Hidden service: Connecting to rendezvous point";
case CIRCUIT_PURPOSE_S_REND_JOINED:
return "Hidden service: Active rendezvous point";
+ case CIRCUIT_PURPOSE_S_HSDIR_POST:
+ return "Hidden service: Uploading HS descriptor";
case CIRCUIT_PURPOSE_TESTING:
return "Testing circuit";
@@ -775,6 +854,9 @@ circuit_purpose_to_string(uint8_t purpose)
case CIRCUIT_PURPOSE_PATH_BIAS_TESTING:
return "Path-bias testing circuit";
+ case CIRCUIT_PURPOSE_HS_VANGUARDS:
+ return "Hidden service: Pre-built vanguard circuit";
+
default:
tor_snprintf(buf, sizeof(buf), "UNKNOWN_%d", (int)purpose);
return buf;
@@ -818,8 +900,10 @@ init_circuit_base(circuit_t *circ)
/** If we haven't yet decided on a good timeout value for circuit
* building, we close idle circuits aggressively so we can get more
- * data points. */
-#define IDLE_TIMEOUT_WHILE_LEARNING (1*60)
+ * data points. These are the default, min, and max consensus values */
+#define DFLT_IDLE_TIMEOUT_WHILE_LEARNING (3*60)
+#define MIN_IDLE_TIMEOUT_WHILE_LEARNING (10)
+#define MAX_IDLE_TIMEOUT_WHILE_LEARNING (1000*60)
/** Allocate space for a new circuit, initializing with <b>p_circ_id</b>
* and <b>p_conn</b>. Add it to the global circuit list.
@@ -852,7 +936,11 @@ origin_circuit_new(void)
circuit_build_times_needs_circuits(get_circuit_build_times())) {
/* Circuits should be shorter lived if we need more of them
* for learning a good build timeout */
- circ->circuit_idle_timeout = IDLE_TIMEOUT_WHILE_LEARNING;
+ circ->circuit_idle_timeout =
+ networkstatus_get_param(NULL, "cbtlearntimeout",
+ DFLT_IDLE_TIMEOUT_WHILE_LEARNING,
+ MIN_IDLE_TIMEOUT_WHILE_LEARNING,
+ MAX_IDLE_TIMEOUT_WHILE_LEARNING);
} else {
// This should always be larger than the current port prediction time
// remaining, or else we'll end up with the case where a circuit times out
@@ -872,7 +960,11 @@ origin_circuit_new(void)
"%d seconds of predictive building remaining.",
circ->circuit_idle_timeout,
prediction_time_remaining);
- circ->circuit_idle_timeout = IDLE_TIMEOUT_WHILE_LEARNING;
+ circ->circuit_idle_timeout =
+ networkstatus_get_param(NULL, "cbtlearntimeout",
+ DFLT_IDLE_TIMEOUT_WHILE_LEARNING,
+ MIN_IDLE_TIMEOUT_WHILE_LEARNING,
+ MAX_IDLE_TIMEOUT_WHILE_LEARNING);
}
log_info(LD_CIRC,
@@ -923,7 +1015,7 @@ circuit_clear_testing_cell_stats(circuit_t *circ)
/** Deallocate space associated with circ.
*/
STATIC void
-circuit_free(circuit_t *circ)
+circuit_free_(circuit_t *circ)
{
circid_t n_circ_id = 0;
void *mem;
@@ -1091,7 +1183,7 @@ circuit_free_all(void)
while (or_circ->resolving_streams) {
edge_connection_t *next_conn;
next_conn = or_circ->resolving_streams->next_stream;
- connection_free(TO_CONN(or_circ->resolving_streams));
+ connection_free_(TO_CONN(or_circ->resolving_streams));
or_circ->resolving_streams = next_conn;
}
}
@@ -1646,14 +1738,29 @@ circuit_can_be_cannibalized_for_v3_rp(const origin_circuit_t *circ)
return 0;
}
+/** We are trying to create a circuit of purpose <b>purpose</b> and we are
+ * looking for cannibalizable circuits. Return the circuit purpose we would be
+ * willing to cannibalize. */
+static uint8_t
+get_circuit_purpose_needed_to_cannibalize(uint8_t purpose)
+{
+ if (circuit_should_use_vanguards(purpose)) {
+ /* If we are using vanguards, then we should only cannibalize vanguard
+ * circuits so that we get the same path construction logic. */
+ return CIRCUIT_PURPOSE_HS_VANGUARDS;
+ } else {
+ /* If no vanguards are used just get a general circuit! */
+ return CIRCUIT_PURPOSE_C_GENERAL;
+ }
+}
+
/** Return a circuit that is open, is CIRCUIT_PURPOSE_C_GENERAL,
* has a timestamp_dirty value of 0, has flags matching the CIRCLAUNCH_*
* flags in <b>flags</b>, and if info is defined, does not already use info
* as any of its hops; or NULL if no circuit fits this description.
*
- * The <b>purpose</b> argument (currently ignored) refers to the purpose of
- * the circuit we want to create, not the purpose of the circuit we want to
- * cannibalize.
+ * The <b>purpose</b> argument refers to the purpose of the circuit we want to
+ * create, not the purpose of the circuit we want to cannibalize.
*
* If !CIRCLAUNCH_NEED_UPTIME, prefer returning non-uptime circuits.
*
@@ -1666,7 +1773,7 @@ circuit_can_be_cannibalized_for_v3_rp(const origin_circuit_t *circ)
* a new circuit.)
*/
origin_circuit_t *
-circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
+circuit_find_to_cannibalize(uint8_t purpose_to_produce, extend_info_t *info,
int flags)
{
origin_circuit_t *best=NULL;
@@ -1674,29 +1781,53 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
int need_capacity = (flags & CIRCLAUNCH_NEED_CAPACITY) != 0;
int internal = (flags & CIRCLAUNCH_IS_INTERNAL) != 0;
const or_options_t *options = get_options();
+ /* We want the circuit we are trying to cannibalize to have this purpose */
+ int purpose_to_search_for;
/* Make sure we're not trying to create a onehop circ by
* cannibalization. */
tor_assert(!(flags & CIRCLAUNCH_ONEHOP_TUNNEL));
+ purpose_to_search_for = get_circuit_purpose_needed_to_cannibalize(
+ purpose_to_produce);
+
+ tor_assert_nonfatal(purpose_to_search_for == CIRCUIT_PURPOSE_C_GENERAL ||
+ purpose_to_search_for == CIRCUIT_PURPOSE_HS_VANGUARDS);
+
log_debug(LD_CIRC,
"Hunting for a circ to cannibalize: purpose %d, uptime %d, "
"capacity %d, internal %d",
- purpose, need_uptime, need_capacity, internal);
+ purpose_to_produce, need_uptime, need_capacity, internal);
SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ_) {
if (CIRCUIT_IS_ORIGIN(circ_) &&
circ_->state == CIRCUIT_STATE_OPEN &&
!circ_->marked_for_close &&
- circ_->purpose == CIRCUIT_PURPOSE_C_GENERAL &&
+ circ_->purpose == purpose_to_search_for &&
!circ_->timestamp_dirty) {
origin_circuit_t *circ = TO_ORIGIN_CIRCUIT(circ_);
+
+ /* Only cannibalize from reasonable length circuits. If we
+ * want C_GENERAL, then only choose 3 hop circs. If we want
+ * HS_VANGUARDS, only choose 4 hop circs.
+ */
+ if (circ->build_state->desired_path_len !=
+ route_len_for_purpose(purpose_to_search_for, NULL)) {
+ goto next;
+ }
+
+ /* Ignore any circuits for which we can't use the Guard. It is possible
+ * that the Guard was removed from the samepled set after the circuit
+ * was created so avoid using it. */
+ if (!entry_guard_could_succeed(circ->guard_state)) {
+ goto next;
+ }
+
if ((!need_uptime || circ->build_state->need_uptime) &&
(!need_capacity || circ->build_state->need_capacity) &&
(internal == circ->build_state->is_internal) &&
!circ->unusable_for_new_conns &&
circ->remaining_relay_early_cells &&
- circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN &&
!circ->build_state->onehop_tunnel &&
!circ->isolation_values_set) {
if (info) {
@@ -1793,6 +1924,25 @@ circuit_get_cpath_len(origin_circuit_t *circ)
return n;
}
+/** Return the number of opened hops in circuit's path.
+ * If circ has no entries, or is NULL, returns 0. */
+int
+circuit_get_cpath_opened_len(const origin_circuit_t *circ)
+{
+ int n = 0;
+ if (circ && circ->cpath) {
+ crypt_path_t *cpath, *cpath_next = NULL;
+ for (cpath = circ->cpath;
+ cpath->state == CPATH_STATE_OPEN
+ && cpath_next != circ->cpath;
+ cpath = cpath_next) {
+ cpath_next = cpath->next;
+ ++n;
+ }
+ }
+ return n;
+}
+
/** Return the <b>hopnum</b>th hop in <b>circ</b>->cpath, or NULL if there
* aren't that many hops in the list. <b>hopnum</b> starts at 1.
* Returns NULL if <b>hopnum</b> is 0 or negative. */
@@ -1849,8 +1999,7 @@ circuit_mark_all_dirty_circs_as_unusable(void)
* - If state is onionskin_pending, remove circ from the onion_pending
* list.
* - If circ isn't open yet: call circuit_build_failed() if we're
- * the origin, and in either case call circuit_rep_hist_note_result()
- * to note stats.
+ * the origin.
* - If purpose is C_INTRODUCE_ACK_WAIT, report the intro point
* failure we just had to the hidden service client module.
* - If purpose is C_INTRODUCING and <b>reason</b> isn't TIMEOUT,
@@ -1986,7 +2135,6 @@ circuit_about_to_free(circuit_t *circ)
if (CIRCUIT_IS_ORIGIN(circ)) {
origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
circuit_build_failed(ocirc); /* take actions if necessary */
- circuit_rep_hist_note_result(ocirc);
}
}
if (circ->state == CIRCUIT_STATE_CHAN_WAIT) {
@@ -2176,12 +2324,12 @@ n_cells_in_circ_queues(const circuit_t *c)
}
/**
- * Return the age of the oldest cell queued on <b>c</b>, in milliseconds.
+ * Return the age of the oldest cell queued on <b>c</b>, in timestamp units.
* Return 0 if there are no cells queued on c. Requires that <b>now</b> be
- * the current time in milliseconds since the epoch, truncated.
+ * the current coarse timestamp.
*
* This function will return incorrect results if the oldest cell queued on
- * the circuit is older than 2**32 msec (about 49 days) old.
+ * the circuit is older than about 2**32 msec (about 49 days) old.
*/
STATIC uint32_t
circuit_max_queued_cell_age(const circuit_t *c, uint32_t now)
@@ -2190,12 +2338,12 @@ circuit_max_queued_cell_age(const circuit_t *c, uint32_t now)
packed_cell_t *cell;
if (NULL != (cell = TOR_SIMPLEQ_FIRST(&c->n_chan_cells.head)))
- age = now - cell->inserted_time;
+ age = now - cell->inserted_timestamp;
if (! CIRCUIT_IS_ORIGIN(c)) {
const or_circuit_t *orcirc = CONST_TO_OR_CIRCUIT(c);
if (NULL != (cell = TOR_SIMPLEQ_FIRST(&orcirc->p_chan_cells.head))) {
- uint32_t age2 = now - cell->inserted_time;
+ uint32_t age2 = now - cell->inserted_timestamp;
if (age2 > age)
return age2;
}
@@ -2203,31 +2351,30 @@ circuit_max_queued_cell_age(const circuit_t *c, uint32_t now)
return age;
}
-/** Return the age in milliseconds of the oldest buffer chunk on <b>conn</b>,
- * where age is taken in milliseconds before the time <b>now</b> (in truncated
- * absolute monotonic msec). If the connection has no data, treat
- * it as having age zero.
+/** Return the age of the oldest buffer chunk on <b>conn</b>, where age is
+ * taken in timestamp units before the time <b>now</b>. If the connection has
+ * no data, treat it as having age zero.
**/
static uint32_t
-conn_get_buffer_age(const connection_t *conn, uint32_t now)
+conn_get_buffer_age(const connection_t *conn, uint32_t now_ts)
{
uint32_t age = 0, age2;
if (conn->outbuf) {
- age2 = buf_get_oldest_chunk_timestamp(conn->outbuf, now);
+ age2 = buf_get_oldest_chunk_timestamp(conn->outbuf, now_ts);
if (age2 > age)
age = age2;
}
if (conn->inbuf) {
- age2 = buf_get_oldest_chunk_timestamp(conn->inbuf, now);
+ age2 = buf_get_oldest_chunk_timestamp(conn->inbuf, now_ts);
if (age2 > age)
age = age2;
}
return age;
}
-/** Return the age in milliseconds of the oldest buffer chunk on any stream in
- * the linked list <b>stream</b>, where age is taken in milliseconds before
- * the time <b>now</b> (in truncated milliseconds since the epoch). */
+/** Return the age in timestamp units of the oldest buffer chunk on any stream
+ * in the linked list <b>stream</b>, where age is taken in timestamp units
+ * before the timestamp <b>now</b>. */
static uint32_t
circuit_get_streams_max_data_age(const edge_connection_t *stream, uint32_t now)
{
@@ -2246,9 +2393,9 @@ circuit_get_streams_max_data_age(const edge_connection_t *stream, uint32_t now)
return age;
}
-/** Return the age in milliseconds of the oldest buffer chunk on any stream
- * attached to the circuit <b>c</b>, where age is taken in milliseconds before
- * the time <b>now</b> (in truncated milliseconds since the epoch). */
+/** Return the age in timestamp units of the oldest buffer chunk on any stream
+ * attached to the circuit <b>c</b>, where age is taken before the timestamp
+ * <b>now</b>. */
STATIC uint32_t
circuit_max_queued_data_age(const circuit_t *c, uint32_t now)
{
@@ -2262,8 +2409,8 @@ circuit_max_queued_data_age(const circuit_t *c, uint32_t now)
}
/** Return the age of the oldest cell or stream buffer chunk on the circuit
- * <b>c</b>, where age is taken in milliseconds before the time <b>now</b> (in
- * truncated milliseconds since the epoch). */
+ * <b>c</b>, where age is taken in timestamp units before the timestamp
+ * <b>now</b> */
STATIC uint32_t
circuit_max_queued_item_age(const circuit_t *c, uint32_t now)
{
@@ -2293,7 +2440,7 @@ circuits_compare_by_oldest_queued_item_(const void **a_, const void **b_)
return -1;
}
-static uint32_t now_ms_for_buf_cmp;
+static uint32_t now_ts_for_buf_cmp;
/** Helper to sort a list of circuit_t by age of oldest item, in descending
* order. */
@@ -2302,8 +2449,8 @@ conns_compare_by_buffer_age_(const void **a_, const void **b_)
{
const connection_t *a = *a_;
const connection_t *b = *b_;
- time_t age_a = conn_get_buffer_age(a, now_ms_for_buf_cmp);
- time_t age_b = conn_get_buffer_age(b, now_ms_for_buf_cmp);
+ time_t age_a = conn_get_buffer_age(a, now_ts_for_buf_cmp);
+ time_t age_b = conn_get_buffer_age(b, now_ts_for_buf_cmp);
if (age_a < age_b)
return 1;
@@ -2328,10 +2475,22 @@ circuits_handle_oom(size_t current_allocation)
size_t mem_recovered=0;
int n_circuits_killed=0;
int n_dirconns_killed=0;
- uint32_t now_ms;
- log_notice(LD_GENERAL, "We're low on memory. Killing circuits with "
- "over-long queues. (This behavior is controlled by "
- "MaxMemInQueues.)");
+ uint32_t now_ts;
+ log_notice(LD_GENERAL, "We're low on memory (cell queues total alloc:"
+ " %"TOR_PRIuSZ" buffer total alloc: %" TOR_PRIuSZ ","
+ " tor compress total alloc: %" TOR_PRIuSZ
+ " (zlib: %" TOR_PRIuSZ ", zstd: %" TOR_PRIuSZ ","
+ " lzma: %" TOR_PRIuSZ "),"
+ " rendezvous cache total alloc: %" TOR_PRIuSZ "). Killing"
+ " circuits withover-long queues. (This behavior is controlled by"
+ " MaxMemInQueues.)",
+ cell_queues_get_total_allocation(),
+ buf_get_total_allocation(),
+ tor_compress_get_total_allocation(),
+ tor_zlib_get_total_allocation(),
+ tor_zstd_get_total_allocation(),
+ tor_lzma_get_total_allocation(),
+ rend_cache_get_total_allocation());
{
size_t mem_target = (size_t)(get_options()->MaxMemInQueues *
@@ -2341,11 +2500,11 @@ circuits_handle_oom(size_t current_allocation)
mem_to_recover = current_allocation - mem_target;
}
- now_ms = (uint32_t)monotime_coarse_absolute_msec();
+ now_ts = monotime_coarse_get_stamp();
circlist = circuit_get_global_list();
SMARTLIST_FOREACH_BEGIN(circlist, circuit_t *, circ) {
- circ->age_tmp = circuit_max_queued_item_age(circ, now_ms);
+ circ->age_tmp = circuit_max_queued_item_age(circ, now_ts);
} SMARTLIST_FOREACH_END(circ);
/* This is O(n log n); there are faster algorithms we could use instead.
@@ -2358,9 +2517,9 @@ circuits_handle_oom(size_t current_allocation)
} SMARTLIST_FOREACH_END(circ);
/* Now sort the connection array ... */
- now_ms_for_buf_cmp = now_ms;
+ now_ts_for_buf_cmp = now_ts;
smartlist_sort(connection_array, conns_compare_by_buffer_age_);
- now_ms_for_buf_cmp = 0;
+ now_ts_for_buf_cmp = 0;
/* Fix up the connection array to its new order. */
SMARTLIST_FOREACH_BEGIN(connection_array, connection_t *, conn) {
@@ -2379,7 +2538,7 @@ circuits_handle_oom(size_t current_allocation)
* data older than this circuit. */
while (conn_idx < smartlist_len(connection_array)) {
connection_t *conn = smartlist_get(connection_array, conn_idx);
- uint32_t conn_age = conn_get_buffer_age(conn, now_ms);
+ uint32_t conn_age = conn_get_buffer_age(conn, now_ts);
if (conn_age < circ->age_tmp) {
break;
}
diff --git a/src/or/circuitlist.h b/src/or/circuitlist.h
index 4190c1f82e..246f0c8815 100644
--- a/src/or/circuitlist.h
+++ b/src/or/circuitlist.h
@@ -17,6 +17,10 @@
MOCK_DECL(smartlist_t *, circuit_get_global_list, (void));
smartlist_t *circuit_get_global_origin_circuit_list(void);
+int circuit_any_opened_circuits(void);
+int circuit_any_opened_circuits_cached(void);
+void circuit_cache_opened_circuit_state(int circuits_are_opened);
+
const char *circuit_state_to_string(int state);
const char *circuit_purpose_to_controller_string(uint8_t purpose);
const char *circuit_purpose_to_controller_hs_state_string(uint8_t purpose);
@@ -58,6 +62,7 @@ void circuit_mark_all_dirty_circs_as_unusable(void);
MOCK_DECL(void, circuit_mark_for_close_, (circuit_t *circ, int reason,
int line, const char *file));
int circuit_get_cpath_len(origin_circuit_t *circ);
+int circuit_get_cpath_opened_len(const origin_circuit_t *);
void circuit_clear_cpath(origin_circuit_t *circ);
crypt_path_t *circuit_get_cpath_hop(origin_circuit_t *circ, int hopnum);
void circuit_get_all_pending_on_channel(smartlist_t *out,
@@ -81,7 +86,8 @@ MOCK_DECL(void, channel_note_destroy_not_pending,
smartlist_t *circuit_find_circuits_to_upgrade_from_guard_wait(void);
#ifdef CIRCUITLIST_PRIVATE
-STATIC void circuit_free(circuit_t *circ);
+STATIC void circuit_free_(circuit_t *circ);
+#define circuit_free(circ) FREE_AND_NULL(circuit_t, circuit_free_, (circ))
STATIC size_t n_cells_in_circ_queues(const circuit_t *c);
STATIC uint32_t circuit_max_queued_data_age(const circuit_t *c, uint32_t now);
STATIC uint32_t circuit_max_queued_cell_age(const circuit_t *c, uint32_t now);
diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c
index 2cc0e8fc44..fe3d8f1332 100644
--- a/src/or/circuitmux.c
+++ b/src/or/circuitmux.c
@@ -537,7 +537,7 @@ circuitmux_mark_destroyed_circids_usable(circuitmux_t *cmux, channel_t *chan)
*/
void
-circuitmux_free(circuitmux_t *cmux)
+circuitmux_free_(circuitmux_t *cmux)
{
if (!cmux) return;
diff --git a/src/or/circuitmux.h b/src/or/circuitmux.h
index a95edb99d8..336e128c76 100644
--- a/src/or/circuitmux.h
+++ b/src/or/circuitmux.h
@@ -104,7 +104,9 @@ void circuitmux_assert_okay(circuitmux_t *cmux);
circuitmux_t * circuitmux_alloc(void);
void circuitmux_detach_all_circuits(circuitmux_t *cmux,
smartlist_t *detached_out);
-void circuitmux_free(circuitmux_t *cmux);
+void circuitmux_free_(circuitmux_t *cmux);
+#define circuitmux_free(cmux) \
+ FREE_AND_NULL(circuitmux_t, circuitmux_free_, (cmux))
/* Policy control */
void circuitmux_clear_policy(circuitmux_t *cmux);
diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c
index b8421a3c7e..f06c2e5e38 100644
--- a/src/or/circuitstats.c
+++ b/src/or/circuitstats.c
@@ -36,6 +36,8 @@
#include "rendclient.h"
#include "rendservice.h"
#include "statefile.h"
+#include "circuitlist.h"
+#include "circuituse.h"
#undef log
#include <math.h>
@@ -43,6 +45,7 @@
static void cbt_control_event_buildtimeout_set(
const circuit_build_times_t *cbt,
buildtimeout_set_event_t type);
+static void circuit_build_times_scale_circ_counts(circuit_build_times_t *cbt);
#define CBT_BIN_TO_MS(bin) ((bin)*CBT_BIN_WIDTH + (CBT_BIN_WIDTH/2))
@@ -164,7 +167,7 @@ circuit_build_times_disabled_(const or_options_t *options,
}
/**
- * Retrieve and bounds-check the cbtmaxtimeouts consensus paramter.
+ * Retrieve and bounds-check the cbtmaxtimeouts consensus parameter.
*
* Effect: When this many timeouts happen in the last 'cbtrecentcount'
* circuit attempts, the client should discard all of its history and
@@ -191,7 +194,7 @@ circuit_build_times_max_timeouts(void)
}
/**
- * Retrieve and bounds-check the cbtnummodes consensus paramter.
+ * Retrieve and bounds-check the cbtnummodes consensus parameter.
*
* Effect: This value governs how many modes to use in the weighted
* average calculation of Pareto parameter Xm. A value of 3 introduces
@@ -218,7 +221,7 @@ circuit_build_times_default_num_xm_modes(void)
}
/**
- * Retrieve and bounds-check the cbtmincircs consensus paramter.
+ * Retrieve and bounds-check the cbtmincircs consensus parameter.
*
* Effect: This is the minimum number of circuits to build before
* computing a timeout.
@@ -250,7 +253,7 @@ circuit_build_times_enough_to_compute(const circuit_build_times_t *cbt)
}
/**
- * Retrieve and bounds-check the cbtquantile consensus paramter.
+ * Retrieve and bounds-check the cbtquantile consensus parameter.
*
* Effect: This is the position on the quantile curve to use to set the
* timeout value. It is a percent (10-99).
@@ -274,7 +277,7 @@ circuit_build_times_quantile_cutoff(void)
}
/**
- * Retrieve and bounds-check the cbtclosequantile consensus paramter.
+ * Retrieve and bounds-check the cbtclosequantile consensus parameter.
*
* Effect: This is the position on the quantile curve to use to set the
* timeout value to use to actually close circuits. It is a percent
@@ -306,7 +309,7 @@ circuit_build_times_close_quantile(void)
}
/**
- * Retrieve and bounds-check the cbttestfreq consensus paramter.
+ * Retrieve and bounds-check the cbttestfreq consensus parameter.
*
* Effect: Describes how often in seconds to build a test circuit to
* gather timeout values. Only applies if less than 'cbtmincircs'
@@ -353,7 +356,7 @@ circuit_build_times_min_timeout(void)
}
/**
- * Retrieve and bounds-check the cbtinitialtimeout consensus paramter.
+ * Retrieve and bounds-check the cbtinitialtimeout consensus parameter.
*
* Effect: This is the timeout value to use before computing a timeout,
* in milliseconds.
@@ -383,7 +386,7 @@ circuit_build_times_initial_timeout(void)
}
/**
- * Retrieve and bounds-check the cbtrecentcount consensus paramter.
+ * Retrieve and bounds-check the cbtrecentcount consensus parameter.
*
* Effect: This is the number of circuit build times to keep track of
* for deciding if we hit cbtmaxtimeouts and need to reset our state
@@ -540,6 +543,11 @@ circuit_build_times_reset(circuit_build_times_t *cbt)
cbt->total_build_times = 0;
cbt->build_times_idx = 0;
cbt->have_computed_timeout = 0;
+
+ // Reset timeout and close counts
+ cbt->num_circ_succeeded = 0;
+ cbt->num_circ_closed = 0;
+ cbt->num_circ_timeouts = 0;
}
/**
@@ -616,6 +624,123 @@ circuit_build_times_rewind_history(circuit_build_times_t *cbt, int n)
#endif /* 0 */
/**
+ * Mark this circuit as timed out, but change its purpose
+ * so that it continues to build, allowing us to measure
+ * its full build time.
+ */
+void
+circuit_build_times_mark_circ_as_measurement_only(origin_circuit_t *circ)
+{
+ control_event_circuit_status(circ,
+ CIRC_EVENT_FAILED,
+ END_CIRC_REASON_TIMEOUT);
+ circuit_change_purpose(TO_CIRCUIT(circ),
+ CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT);
+ /* Record this event to check for too many timeouts
+ * in a row. This function does not record a time value yet
+ * (we do that later); it only counts the fact that we did
+ * have a timeout. We also want to avoid double-counting
+ * already "relaxed" circuits, which are counted in
+ * circuit_expire_building(). */
+ if (!circ->relaxed_timeout) {
+ int first_hop_succeeded = circ->cpath &&
+ circ->cpath->state == CPATH_STATE_OPEN;
+
+ circuit_build_times_count_timeout(
+ get_circuit_build_times_mutable(),
+ first_hop_succeeded);
+ }
+}
+
+/**
+ * Perform the build time work that needs to be done when a circuit
+ * completes a hop.
+ *
+ * This function decides if we should record a circuit's build time
+ * in our histogram data and other statistics, and if so, records it.
+ * It also will mark circuits that have already timed out as
+ * measurement-only circuits, so they can continue to build but
+ * not get used.
+ *
+ * For this, we want to consider circuits that will eventually make
+ * it to the third hop. For circuits longer than 3 hops, we want to
+ * record their build time when they reach the third hop, but let
+ * them continue (and not count them later). For circuits that are
+ * exactly 3 hops, this will count them when they are completed. We
+ * do this so that CBT is always gathering statistics on circuits
+ * of the same length, regardless of their type.
+ */
+void
+circuit_build_times_handle_completed_hop(origin_circuit_t *circ)
+{
+ struct timeval end;
+ long timediff;
+
+ /* If circuit build times are disabled, let circuit_expire_building()
+ * handle it.. */
+ if (circuit_build_times_disabled(get_options())) {
+ return;
+ }
+
+ /* Is this a circuit for which the timeout applies in a straight-forward
+ * way? If so, handle it below. If not, just return (and let
+ * circuit_expire_building() eventually take care of it).
+ */
+ if (!circuit_timeout_want_to_count_circ(circ)) {
+ return;
+ }
+
+ tor_gettimeofday(&end);
+ timediff = tv_mdiff(&circ->base_.timestamp_began, &end);
+
+ /* Check if we would have timed out already. If so, change the
+ * purpose here. But don't do any timeout handling here if there
+ * are no circuits opened yet. Save it for circuit_expire_building()
+ * (to allow it to handle timeout "relaxing" over there). */
+ if (timediff > get_circuit_build_timeout_ms() &&
+ circuit_any_opened_circuits_cached()) {
+
+ /* Circuits are allowed to last longer for measurement.
+ * Switch their purpose and wait. */
+ if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
+ log_info(LD_CIRC,
+ "Deciding to timeout circuit "U64_FORMAT"\n",
+ U64_PRINTF_ARG(circ->global_identifier));
+ circuit_build_times_mark_circ_as_measurement_only(circ);
+ }
+ }
+
+ /* If the circuit is built to exactly the DEFAULT_ROUTE_LEN,
+ * add it to our buildtimes. */
+ if (circuit_get_cpath_opened_len(circ) == DEFAULT_ROUTE_LEN) {
+ /* If the circuit build time is much greater than we would have cut
+ * it off at, we probably had a suspend event along this codepath,
+ * and we should discard the value.
+ */
+ if (timediff < 0 ||
+ timediff > 2*get_circuit_build_close_time_ms()+1000) {
+ log_notice(LD_CIRC, "Strange value for circuit build time: %ldmsec. "
+ "Assuming clock jump. Purpose %d (%s)", timediff,
+ circ->base_.purpose,
+ circuit_purpose_to_string(circ->base_.purpose));
+ } else {
+ /* Only count circuit times if the network is live */
+ if (circuit_build_times_network_check_live(
+ get_circuit_build_times())) {
+ circuit_build_times_add_time(get_circuit_build_times_mutable(),
+ (build_time_t)timediff);
+ circuit_build_times_set_timeout(get_circuit_build_times_mutable());
+ }
+
+ if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
+ circuit_build_times_network_circ_success(
+ get_circuit_build_times_mutable());
+ }
+ }
+ }
+}
+
+/**
* Add a new build time value <b>time</b> to the set of build times. Time
* units are milliseconds.
*
@@ -767,11 +892,23 @@ circuit_build_times_get_xm(circuit_build_times_t *cbt)
histogram[nth_max_bin[n]]);
}
- /* The following assert is safe, because we don't get called when we
- * haven't observed at least CBT_MIN_MIN_CIRCUITS_TO_OBSERVE circuits. */
+ /* bin_counts can become zero if all of our last CBT_NCIRCUITS_TO_OBSERVE
+ * circuits were abandoned before they completed. This shouldn't happen,
+ * though. We should have reset/re-learned a lower timeout first. */
+ if (bin_counts == 0) {
+ ret = 0;
+ log_warn(LD_CIRC,
+ "No valid circuit build time data out of %d times, %u modes, "
+ "have_timeout=%d, %lfms", cbt->total_build_times, num_modes,
+ cbt->have_computed_timeout, cbt->timeout_ms);
+ goto done;
+ }
+
tor_assert(bin_counts > 0);
ret /= bin_counts;
+
+ done:
tor_free(histogram);
tor_free(nth_max_bin);
@@ -1057,6 +1194,10 @@ circuit_build_times_update_alpha(circuit_build_times_t *cbt)
* and less frechet-like. */
cbt->Xm = circuit_build_times_get_xm(cbt);
+ /* If Xm came back 0, then too many circuits were abandoned. */
+ if (cbt->Xm == 0)
+ return 0;
+
tor_assert(cbt->Xm > 0);
for (i=0; i< CBT_NCIRCUITS_TO_OBSERVE; i++) {
@@ -1290,9 +1431,32 @@ circuit_build_times_network_is_live(circuit_build_times_t *cbt)
}
/**
- * Called to indicate that we completed a circuit. Because this circuit
+ * Non-destructively scale all of our circuit success, timeout, and close
+ * counts down by a factor of two. Scaling in this way preserves the
+ * ratios between succeeded vs timed out vs closed circuits, so that
+ * our statistics don't change when we scale.
+ *
+ * This is used only in the rare event that we build more than
+ * INT32_MAX circuits. Since the num_circ_* variables are
+ * uint32_t, we won't even be close to overflowing them.
+ */
+void
+circuit_build_times_scale_circ_counts(circuit_build_times_t *cbt)
+{
+ cbt->num_circ_succeeded /= 2;
+ cbt->num_circ_timeouts /= 2;
+ cbt->num_circ_closed /= 2;
+}
+
+/**
+ * Called to indicate that we "completed" a circuit. Because this circuit
* succeeded, it doesn't count as a timeout-after-the-first-hop.
*
+ * (For the purposes of the cbt code, we consider a circuit "completed" if
+ * it has 3 hops, regardless of its final hop count. We do this because
+ * we're trying to answer the question, "how long should a circuit take to
+ * reach the 3-hop count".)
+ *
* This is used by circuit_build_times_network_check_changed() to determine
* if we had too many recent timeouts and need to reset our learned timeout
* to something higher.
@@ -1300,6 +1464,14 @@ circuit_build_times_network_is_live(circuit_build_times_t *cbt)
void
circuit_build_times_network_circ_success(circuit_build_times_t *cbt)
{
+ // Count circuit success
+ cbt->num_circ_succeeded++;
+
+ // If we're going to wrap int32, scale everything
+ if (cbt->num_circ_succeeded >= INT32_MAX) {
+ circuit_build_times_scale_circ_counts(cbt);
+ }
+
/* Check for NULLness because we might not be using adaptive timeouts */
if (cbt->liveness.timeouts_after_firsthop &&
cbt->liveness.num_recent_circs > 0) {
@@ -1322,6 +1494,14 @@ static void
circuit_build_times_network_timeout(circuit_build_times_t *cbt,
int did_onehop)
{
+ // Count circuit timeout
+ cbt->num_circ_timeouts++;
+
+ // If we're going to wrap int32, scale everything
+ if (cbt->num_circ_timeouts >= INT32_MAX) {
+ circuit_build_times_scale_circ_counts(cbt);
+ }
+
/* Check for NULLness because we might not be using adaptive timeouts */
if (cbt->liveness.timeouts_after_firsthop &&
cbt->liveness.num_recent_circs > 0) {
@@ -1347,6 +1527,15 @@ circuit_build_times_network_close(circuit_build_times_t *cbt,
int did_onehop, time_t start_time)
{
time_t now = time(NULL);
+
+ // Count circuit close
+ cbt->num_circ_closed++;
+
+ // If we're going to wrap int32, scale everything
+ if (cbt->num_circ_closed >= INT32_MAX) {
+ circuit_build_times_scale_circ_counts(cbt);
+ }
+
/*
* Check if this is a timeout that was for a circuit that spent its
* entire existence during a time where we have had no network activity.
@@ -1701,6 +1890,8 @@ cbt_control_event_buildtimeout_set(const circuit_build_times_t *cbt,
{
char *args = NULL;
double qnt;
+ double timeout_rate = 0.0;
+ double close_rate = 0.0;
switch (type) {
case BUILDTIMEOUT_SET_EVENT_RESET:
@@ -1715,15 +1906,36 @@ cbt_control_event_buildtimeout_set(const circuit_build_times_t *cbt,
break;
}
+ /* The timeout rate is the ratio of the timeout count over
+ * the total number of circuits attempted. The total number of
+ * circuits is (timeouts+succeeded), since every circuit
+ * either succeeds, or times out. "Closed" circuits are
+ * MEASURE_TIMEOUT circuits whose measurement period expired.
+ * All MEASURE_TIMEOUT circuits are counted in the timeouts stat
+ * before transitioning to MEASURE_TIMEOUT (in
+ * circuit_build_times_mark_circ_as_measurement_only()).
+ * MEASURE_TIMEOUT circuits that succeed are *not* counted as
+ * "succeeded". See circuit_build_times_handle_completed_hop().
+ *
+ * We cast the denominator
+ * to promote it to double before the addition, to avoid int32
+ * overflow. */
+ const double total_circuits =
+ ((double)cbt->num_circ_timeouts) + cbt->num_circ_succeeded;
+ if (total_circuits >= 1.0) {
+ timeout_rate = cbt->num_circ_timeouts / total_circuits;
+ close_rate = cbt->num_circ_closed / total_circuits;
+ }
+
tor_asprintf(&args, "TOTAL_TIMES=%lu "
"TIMEOUT_MS=%lu XM=%lu ALPHA=%f CUTOFF_QUANTILE=%f "
"TIMEOUT_RATE=%f CLOSE_MS=%lu CLOSE_RATE=%f",
(unsigned long)cbt->total_build_times,
(unsigned long)cbt->timeout_ms,
(unsigned long)cbt->Xm, cbt->alpha, qnt,
- circuit_build_times_timeout_rate(cbt),
+ timeout_rate,
(unsigned long)cbt->close_ms,
- circuit_build_times_close_rate(cbt));
+ close_rate);
control_event_buildtimeout_set(type, args);
diff --git a/src/or/circuitstats.h b/src/or/circuitstats.h
index 92dc6405ba..86116cb7f8 100644
--- a/src/or/circuitstats.h
+++ b/src/or/circuitstats.h
@@ -34,6 +34,7 @@ void circuit_build_times_set_timeout(circuit_build_times_t *cbt);
int circuit_build_times_add_time(circuit_build_times_t *cbt,
build_time_t time);
int circuit_build_times_needs_circuits(const circuit_build_times_t *cbt);
+void circuit_build_times_handle_completed_hop(origin_circuit_t *circ);
int circuit_build_times_needs_circuits_now(const circuit_build_times_t *cbt);
void circuit_build_times_init(circuit_build_times_t *cbt);
@@ -44,6 +45,7 @@ double circuit_build_times_timeout_rate(const circuit_build_times_t *cbt);
double circuit_build_times_close_rate(const circuit_build_times_t *cbt);
void circuit_build_times_update_last_circ(circuit_build_times_t *cbt);
+void circuit_build_times_mark_circ_as_measurement_only(origin_circuit_t *circ);
#ifdef CIRCUITSTATS_PRIVATE
STATIC double circuit_build_times_calculate_timeout(circuit_build_times_t *cbt,
@@ -94,6 +96,16 @@ struct circuit_build_times_s {
double timeout_ms;
/** How long we wait before actually closing the circuit. */
double close_ms;
+ /** Total succeeded counts. Old measurements may be scaled downward if
+ * we've seen a lot of circuits. */
+ uint32_t num_circ_succeeded;
+ /** Total timeout counts. Old measurements may be scaled downward if
+ * we've seen a lot of circuits. */
+ uint32_t num_circ_timeouts;
+ /** Total closed counts. Old measurements may be scaled downward if
+ * we've seen a lot of circuits.*/
+ uint32_t num_circ_closed;
+
};
#endif /* defined(CIRCUITSTATS_PRIVATE) */
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index 7baa035696..dc62e4d09c 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -45,6 +45,7 @@
#include "hs_client.h"
#include "hs_circuit.h"
#include "hs_ident.h"
+#include "hs_stats.h"
#include "nodelist.h"
#include "networkstatus.h"
#include "policies.h"
@@ -54,6 +55,7 @@
#include "rephist.h"
#include "router.h"
#include "routerlist.h"
+#include "config.h"
static void circuit_expire_old_circuits_clientside(void);
static void circuit_increment_failure_count(void);
@@ -133,6 +135,9 @@ circuit_is_acceptable(const origin_circuit_t *origin_circ,
}
if (purpose == CIRCUIT_PURPOSE_C_GENERAL ||
+ purpose == CIRCUIT_PURPOSE_C_HSDIR_GET ||
+ purpose == CIRCUIT_PURPOSE_S_HSDIR_POST ||
+ purpose == CIRCUIT_PURPOSE_HS_VANGUARDS ||
purpose == CIRCUIT_PURPOSE_C_REND_JOINED) {
if (circ->timestamp_dirty &&
circ->timestamp_dirty+get_options()->MaxCircuitDirtiness <= now)
@@ -156,7 +161,9 @@ circuit_is_acceptable(const origin_circuit_t *origin_circ,
if (need_internal != build_state->is_internal)
return 0;
- if (purpose == CIRCUIT_PURPOSE_C_GENERAL) {
+ if (purpose == CIRCUIT_PURPOSE_C_GENERAL ||
+ purpose == CIRCUIT_PURPOSE_S_HSDIR_POST ||
+ purpose == CIRCUIT_PURPOSE_C_HSDIR_GET) {
tor_addr_t addr;
const int family = tor_addr_parse(&addr, conn->socks_request->address);
if (!exitnode && !build_state->onehop_tunnel) {
@@ -238,6 +245,8 @@ circuit_is_better(const origin_circuit_t *oa, const origin_circuit_t *ob,
return 1; /* oa is better. It's not relaxed. */
switch (purpose) {
+ case CIRCUIT_PURPOSE_S_HSDIR_POST:
+ case CIRCUIT_PURPOSE_C_HSDIR_GET:
case CIRCUIT_PURPOSE_C_GENERAL:
/* if it's used but less dirty it's best;
* else if it's more recently created it's best
@@ -323,6 +332,9 @@ circuit_get_best(const entry_connection_t *conn,
tor_assert(conn);
tor_assert(purpose == CIRCUIT_PURPOSE_C_GENERAL ||
+ purpose == CIRCUIT_PURPOSE_HS_VANGUARDS ||
+ purpose == CIRCUIT_PURPOSE_C_HSDIR_GET ||
+ purpose == CIRCUIT_PURPOSE_S_HSDIR_POST ||
purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT ||
purpose == CIRCUIT_PURPOSE_C_REND_JOINED);
@@ -372,7 +384,7 @@ count_pending_general_client_circuits(void)
SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) {
if (circ->marked_for_close ||
circ->state == CIRCUIT_STATE_OPEN ||
- circ->purpose != CIRCUIT_PURPOSE_C_GENERAL ||
+ !CIRCUIT_PURPOSE_COUNTS_TOWARDS_MAXPENDING(circ->purpose) ||
!CIRCUIT_IS_ORIGIN(circ))
continue;
@@ -412,8 +424,17 @@ circuit_conforms_to_options(const origin_circuit_t *circ,
}
#endif /* 0 */
-/** Close all circuits that start at us, aren't open, and were born
+/**
+ * Close all circuits that start at us, aren't open, and were born
* at least CircuitBuildTimeout seconds ago.
+ *
+ * TODO: This function is now partially redundant to
+ * circuit_build_times_handle_completed_hop(), but that function only
+ * covers circuits up to and including 3 hops that are still actually
+ * completing hops. However, circuit_expire_building() also handles longer
+ * circuits, as well as circuits that are completely stalled.
+ * In the future (after prop247/other path selection revamping), we probably
+ * want to eliminate this rats nest in favor of a simpler approach.
*/
void
circuit_expire_building(void)
@@ -435,21 +456,7 @@ circuit_expire_building(void)
* we want to be more lenient with timeouts, in case the
* user has relocated and/or changed network connections.
* See bug #3443. */
- SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, next_circ) {
- if (!CIRCUIT_IS_ORIGIN(next_circ) || /* didn't originate here */
- next_circ->marked_for_close) { /* don't mess with marked circs */
- continue;
- }
-
- if (TO_ORIGIN_CIRCUIT(next_circ)->has_opened &&
- next_circ->state == CIRCUIT_STATE_OPEN &&
- TO_ORIGIN_CIRCUIT(next_circ)->build_state &&
- TO_ORIGIN_CIRCUIT(next_circ)->build_state->desired_path_len
- == DEFAULT_ROUTE_LEN) {
- any_opened_circs = 1;
- break;
- }
- } SMARTLIST_FOREACH_END(next_circ);
+ any_opened_circs = circuit_any_opened_circuits();
#define SET_CUTOFF(target, msec) do { \
long ms = tor_lround(msec); \
@@ -493,6 +500,10 @@ circuit_expire_building(void)
SET_CUTOFF(general_cutoff, get_circuit_build_timeout_ms());
SET_CUTOFF(begindir_cutoff, get_circuit_build_timeout_ms());
+ // TODO: We should probably use route_len_for_purpose() here instead,
+ // except that does not count the extra round trip for things like server
+ // intros and rends.
+
/* > 3hop circs seem to have a 1.0 second delay on their cannibalized
* 4th hop. */
SET_CUTOFF(fourhop_cutoff, get_circuit_build_timeout_ms() * (10/6.0) + 1000);
@@ -585,7 +596,8 @@ circuit_expire_building(void)
TO_ORIGIN_CIRCUIT(victim)->build_state->desired_path_len :
-1,
circuit_state_to_string(victim->state),
- channel_state_to_string(victim->n_chan->state));
+ victim->n_chan ?
+ channel_state_to_string(victim->n_chan->state) : "none");
/* We count the timeout here for CBT, because technically this
* was a timeout, and the timeout value needs to reset if we
@@ -610,7 +622,8 @@ circuit_expire_building(void)
TO_ORIGIN_CIRCUIT(victim)->build_state->desired_path_len :
-1,
circuit_state_to_string(victim->state),
- channel_state_to_string(victim->n_chan->state),
+ victim->n_chan ?
+ channel_state_to_string(victim->n_chan->state) : "none",
(long)build_close_ms);
}
}
@@ -661,7 +674,7 @@ circuit_expire_building(void)
break;
case CIRCUIT_PURPOSE_C_INTRODUCING:
/* That purpose means that the intro point circuit has been opened
- * succesfully but the INTRODUCE1 cell hasn't been sent yet because
+ * successfully but the INTRODUCE1 cell hasn't been sent yet because
* the client is waiting for the rendezvous point circuit to open.
* Keep this circuit open while waiting for the rendezvous circuit.
* We let the circuit idle timeout take care of cleaning this
@@ -694,23 +707,17 @@ circuit_expire_building(void)
if (circuit_timeout_want_to_count_circ(TO_ORIGIN_CIRCUIT(victim)) &&
circuit_build_times_enough_to_compute(get_circuit_build_times())) {
+
+ log_info(LD_CIRC,
+ "Deciding to count the timeout for circuit "U64_FORMAT"\n",
+ U64_PRINTF_ARG(
+ TO_ORIGIN_CIRCUIT(victim)->global_identifier));
+
/* Circuits are allowed to last longer for measurement.
* Switch their purpose and wait. */
if (victim->purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
- control_event_circuit_status(TO_ORIGIN_CIRCUIT(victim),
- CIRC_EVENT_FAILED,
- END_CIRC_REASON_TIMEOUT);
- circuit_change_purpose(victim, CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT);
- /* Record this failure to check for too many timeouts
- * in a row. This function does not record a time value yet
- * (we do that later); it only counts the fact that we did
- * have a timeout. We also want to avoid double-counting
- * already "relaxed" circuits, which are counted above. */
- if (!TO_ORIGIN_CIRCUIT(victim)->relaxed_timeout) {
- circuit_build_times_count_timeout(
- get_circuit_build_times_mutable(),
- first_hop_succeeded);
- }
+ circuit_build_times_mark_circ_as_measurement_only(TO_ORIGIN_CIRCUIT(
+ victim));
continue;
}
@@ -868,10 +875,10 @@ circuit_log_ancient_one_hop_circuits(int age)
if (circ->timestamp_created.tv_sec >= cutoff)
continue;
/* Single Onion Services deliberately make long term one-hop intro
- * connections. We only ignore active intro point connections, if we take
- * a long time establishing, that's worth logging. */
+ * and rendezvous connections. Don't log the established ones. */
if (rend_service_allow_non_anonymous_connection(options) &&
- circ->purpose == CIRCUIT_PURPOSE_S_INTRO)
+ (circ->purpose == CIRCUIT_PURPOSE_S_INTRO ||
+ circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED))
continue;
/* Tor2web deliberately makes long term one-hop rend connections,
* particularly when Tor2webRendezvousPoints is used. We only ignore
@@ -1085,7 +1092,8 @@ circuit_is_available_for_use(const circuit_t *circ)
return 0; /* Don't mess with marked circs */
if (circ->timestamp_dirty)
return 0; /* Only count clean circs */
- if (circ->purpose != CIRCUIT_PURPOSE_C_GENERAL)
+ if (circ->purpose != CIRCUIT_PURPOSE_C_GENERAL &&
+ circ->purpose != CIRCUIT_PURPOSE_HS_VANGUARDS)
return 0; /* We only pay attention to general purpose circuits.
General purpose circuits are always origin circuits. */
@@ -1171,15 +1179,11 @@ needs_hs_client_circuits(time_t now, int *needs_uptime, int *needs_capacity,
router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN);
}
-/* The minimum number of open slots we should keep in order to preemptively
- * build circuits. */
-#define CBT_MIN_REMAINING_PREEMPTIVE_CIRCUITS 2
-
-/* Check to see if we need more circuits to have a good build timeout. However,
- * leave a couple slots open so that we can still build circuits preemptively
- * as needed. */
-#define CBT_MAX_UNUSED_OPEN_CIRCUITS (MAX_UNUSED_OPEN_CIRCUITS - \
- CBT_MIN_REMAINING_PREEMPTIVE_CIRCUITS)
+/* This is how many circuits can be opened concurrently during the cbt learning
+ * phase. This number cannot exceed the tor-wide MAX_UNUSED_OPEN_CIRCUITS. */
+#define DFLT_CBT_UNUSED_OPEN_CIRCS (10)
+#define MIN_CBT_UNUSED_OPEN_CIRCS 0
+#define MAX_CBT_UNUSED_OPEN_CIRCS MAX_UNUSED_OPEN_CIRCUITS
/* Return true if we need more circuits for a good build timeout.
* XXXX make the assumption that build timeout streams should be
@@ -1188,7 +1192,10 @@ STATIC int
needs_circuits_for_build(int num)
{
if (router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN) {
- if (num < CBT_MAX_UNUSED_OPEN_CIRCUITS &&
+ if (num < networkstatus_get_param(NULL, "cbtmaxopencircs",
+ DFLT_CBT_UNUSED_OPEN_CIRCS,
+ MIN_CBT_UNUSED_OPEN_CIRCS,
+ MAX_CBT_UNUSED_OPEN_CIRCS) &&
!circuit_build_times_disabled(get_options()) &&
circuit_build_times_needs_circuits_now(get_circuit_build_times())) {
return 1;
@@ -1197,6 +1204,25 @@ needs_circuits_for_build(int num)
return 0;
}
+/**
+ * Launch the appropriate type of predicted circuit for hidden
+ * services, depending on our options.
+ */
+static void
+circuit_launch_predicted_hs_circ(int flags)
+{
+ /* K.I.S.S. implementation of bug #23101: If we are using
+ * vanguards or pinned middles, pre-build a specific purpose
+ * for HS circs. */
+ if (circuit_should_use_vanguards(CIRCUIT_PURPOSE_HS_VANGUARDS)) {
+ circuit_launch(CIRCUIT_PURPOSE_HS_VANGUARDS, flags);
+ } else {
+ /* If no vanguards, then no HS-specific prebuilt circuits are needed.
+ * Normal GENERAL circs are fine */
+ circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags);
+ }
+}
+
/** Determine how many circuits we have open that are clean,
* Make sure it's enough for all the upcoming behaviors we predict we'll have.
* But put an upper bound on the total number of circuits.
@@ -1250,7 +1276,7 @@ circuit_predict_and_launch_new(void)
"Have %d clean circs (%d internal), need another internal "
"circ for my hidden service.",
num, num_internal);
- circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags);
+ circuit_launch_predicted_hs_circ(flags);
return;
}
@@ -1268,7 +1294,8 @@ circuit_predict_and_launch_new(void)
"Have %d clean circs (%d uptime-internal, %d internal), need"
" another hidden service circ.",
num, num_uptime_internal, num_internal);
- circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags);
+
+ circuit_launch_predicted_hs_circ(flags);
return;
}
@@ -1463,6 +1490,9 @@ circuit_expire_old_circuits_clientside(void)
} else if (!circ->timestamp_dirty && circ->state == CIRCUIT_STATE_OPEN) {
if (timercmp(&circ->timestamp_began, &cutoff, OP_LT)) {
if (circ->purpose == CIRCUIT_PURPOSE_C_GENERAL ||
+ circ->purpose == CIRCUIT_PURPOSE_C_HSDIR_GET ||
+ circ->purpose == CIRCUIT_PURPOSE_S_HSDIR_POST ||
+ circ->purpose == CIRCUIT_PURPOSE_HS_VANGUARDS ||
circ->purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT ||
circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
circ->purpose == CIRCUIT_PURPOSE_TESTING ||
@@ -1655,6 +1685,8 @@ circuit_has_opened(origin_circuit_t *circ)
hs_client_circuit_has_opened(circ);
break;
case CIRCUIT_PURPOSE_C_GENERAL:
+ case CIRCUIT_PURPOSE_C_HSDIR_GET:
+ case CIRCUIT_PURPOSE_S_HSDIR_POST:
/* Tell any AP connections that have been waiting for a new
* circuit that one is ready. */
circuit_try_attaching_streams(circ);
@@ -1725,6 +1757,39 @@ circuit_build_failed(origin_circuit_t *circ)
* the last hop or an earlier hop. then use this info below.
*/
int failed_at_last_hop = 0;
+
+ /* First, check to see if this was a path failure, rather than build
+ * failure.
+ *
+ * Note that we deliberately use circuit_get_cpath_len() (and not
+ * circuit_get_cpath_opened_len()) because we only want to ensure
+ * that a full path is *chosen*. This is different than a full path
+ * being *built*. We only want to count *build* failures below.
+ *
+ * Path selection failures can happen spuriously for a number
+ * of reasons (such as aggressive/invalid user-specified path
+ * restrictions in the torrc, insufficient microdescriptors, and
+ * non-user reasons like exitpolicy issues), and so should not be
+ * counted as failures below.
+ */
+ if (circuit_get_cpath_len(circ) < circ->build_state->desired_path_len) {
+ static ratelim_t pathfail_limit = RATELIM_INIT(3600);
+ log_fn_ratelim(&pathfail_limit, LOG_NOTICE, LD_CIRC,
+ "Our circuit %u (id: %" PRIu32 ") died due to an invalid "
+ "selected path, purpose %s. This may be a torrc "
+ "configuration issue, or a bug.",
+ TO_CIRCUIT(circ)->n_circ_id, circ->global_identifier,
+ circuit_purpose_to_string(TO_CIRCUIT(circ)->purpose));
+
+ /* If the path failed on an RP, retry it. */
+ if (TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND)
+ hs_circ_retry_service_rendezvous_point(circ);
+
+ /* In all other cases, just bail. The rest is just failure accounting
+ * that we don't want to do */
+ return;
+ }
+
/* If the last hop isn't open, and the second-to-last is, we failed
* at the last hop. */
if (circ->cpath &&
@@ -1732,13 +1797,16 @@ circuit_build_failed(origin_circuit_t *circ)
circ->cpath->prev->prev->state == CPATH_STATE_OPEN) {
failed_at_last_hop = 1;
}
+
+ /* Check if we failed at first hop */
if (circ->cpath &&
circ->cpath->state != CPATH_STATE_OPEN &&
! circ->base_.received_destroy) {
/* We failed at the first hop for some reason other than a DESTROY cell.
* If there's an OR connection to blame, blame it. Also, avoid this relay
* for a while, and fail any one-hop directory fetches destined for it. */
- const char *n_chan_id = circ->cpath->extend_info->identity_digest;
+ const char *n_chan_ident = circ->cpath->extend_info->identity_digest;
+ tor_assert(n_chan_ident);
int already_marked = 0;
if (circ->base_.n_chan) {
n_chan = circ->base_.n_chan;
@@ -1766,17 +1834,23 @@ circuit_build_failed(origin_circuit_t *circ)
"with no connection",
TO_CIRCUIT(circ)->n_circ_id, circ->global_identifier);
}
- if (n_chan_id && !already_marked) {
- /* New guard API: we failed. */
+ if (!already_marked) {
+ /*
+ * If we have guard state (new guard API) and our path selection
+ * code actually chose a full path, then blame the failure of this
+ * circuit on the guard.
+ */
if (circ->guard_state)
entry_guard_failed(&circ->guard_state);
/* if there are any one-hop streams waiting on this circuit, fail
* them now so they can retry elsewhere. */
- connection_ap_fail_onehop(n_chan_id, circ->build_state);
+ connection_ap_fail_onehop(n_chan_ident, circ->build_state);
}
}
switch (circ->base_.purpose) {
+ case CIRCUIT_PURPOSE_C_HSDIR_GET:
+ case CIRCUIT_PURPOSE_S_HSDIR_POST:
case CIRCUIT_PURPOSE_C_GENERAL:
/* If we never built the circuit, note it as a failure. */
circuit_increment_failure_count();
@@ -1861,6 +1935,106 @@ have_enough_path_info(int need_exit)
return router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN;
}
+/**
+ * Tell us if a circuit is a hidden service circuit.
+ */
+int
+circuit_purpose_is_hidden_service(uint8_t purpose)
+{
+ if (purpose == CIRCUIT_PURPOSE_HS_VANGUARDS) {
+ return 1;
+ }
+
+ /* Client-side purpose */
+ if (purpose >= CIRCUIT_PURPOSE_C_HS_MIN_ &&
+ purpose <= CIRCUIT_PURPOSE_C_HS_MAX_) {
+ return 1;
+ }
+
+ /* Service-side purpose */
+ if (purpose >= CIRCUIT_PURPOSE_S_HS_MIN_ &&
+ purpose <= CIRCUIT_PURPOSE_S_HS_MAX_) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Return true if this circuit purpose should use vanguards
+ * or pinned Layer2 or Layer3 guards.
+ *
+ * This function takes both the circuit purpose and the
+ * torrc options for pinned middles/vanguards into account
+ * (ie: the circuit must be a hidden service circuit and
+ * vanguards/pinned middles must be enabled for it to return
+ * true).
+ */
+int
+circuit_should_use_vanguards(uint8_t purpose)
+{
+ const or_options_t *options = get_options();
+
+ /* Only hidden service circuits use vanguards */
+ if (!circuit_purpose_is_hidden_service(purpose))
+ return 0;
+
+ /* Pinned middles are effectively vanguards */
+ if (options->HSLayer2Nodes || options->HSLayer3Nodes)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * Return true for the set of conditions for which it is OK to use
+ * a cannibalized circuit.
+ *
+ * Don't cannibalize for onehops, or tor2web, or certain purposes.
+ */
+static int
+circuit_should_cannibalize_to_build(uint8_t purpose_to_build,
+ int has_extend_info,
+ int onehop_tunnel,
+ int need_specific_rp)
+{
+
+ /* Do not try to cannibalize if this is a one hop circuit, or
+ * is a tor2web/special rp. */
+ if (onehop_tunnel || need_specific_rp) {
+ return 0;
+ }
+
+ /* Don't try to cannibalize for general purpose circuits that do not
+ * specify a custom exit. */
+ if (purpose_to_build == CIRCUIT_PURPOSE_C_GENERAL && !has_extend_info) {
+ return 0;
+ }
+
+ /* Don't cannibalize for testing circuits. We want to see if they
+ * complete normally. Also don't cannibalize for vanguard-purpose
+ * circuits, since those are specially pre-built for later
+ * cannibalization by the actual specific circuit types that need
+ * vanguards.
+ */
+ if (purpose_to_build == CIRCUIT_PURPOSE_TESTING ||
+ purpose_to_build == CIRCUIT_PURPOSE_HS_VANGUARDS) {
+ return 0;
+ }
+
+ /* For vanguards, the server-side intro circ is not cannibalized
+ * because we pre-build 4 hop HS circuits, and it only needs a 3 hop
+ * circuit. It is also long-lived, so it is more important that
+ * it have lower latency than get built fast.
+ */
+ if (circuit_should_use_vanguards(purpose_to_build) &&
+ purpose_to_build == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) {
+ return 0;
+ }
+
+ return 1;
+}
+
/** Launch a new circuit with purpose <b>purpose</b> and exit node
* <b>extend_info</b> (or NULL to select a random exit node). If flags
* contains CIRCLAUNCH_NEED_UPTIME, choose among routers with high uptime. If
@@ -1878,6 +2052,11 @@ circuit_launch_by_extend_info(uint8_t purpose,
int have_path = have_enough_path_info(! (flags & CIRCLAUNCH_IS_INTERNAL) );
int need_specific_rp = 0;
+ /* Keep some stats about our attempts to launch HS rendezvous circuits */
+ if (purpose == CIRCUIT_PURPOSE_S_CONNECT_REND) {
+ hs_stats_note_service_rendezvous_launch();
+ }
+
if (!onehop_tunnel && (!router_have_minimum_dir_info() || !have_path)) {
log_debug(LD_CIRC,"Haven't %s yet; canceling "
"circuit launch.",
@@ -1895,9 +2074,12 @@ circuit_launch_by_extend_info(uint8_t purpose,
need_specific_rp = 1;
}
- if ((extend_info || purpose != CIRCUIT_PURPOSE_C_GENERAL) &&
- purpose != CIRCUIT_PURPOSE_TESTING &&
- !onehop_tunnel && !need_specific_rp) {
+ /* If we can/should cannibalize another circuit to build this one,
+ * then do so. */
+ if (circuit_should_cannibalize_to_build(purpose,
+ extend_info != NULL,
+ onehop_tunnel,
+ need_specific_rp)) {
/* see if there are appropriate circs available to cannibalize. */
/* XXX if we're planning to add a hop, perhaps we want to look for
* internal circs rather than exit circs? -RD */
@@ -1952,6 +2134,8 @@ circuit_launch_by_extend_info(uint8_t purpose,
case CIRCUIT_PURPOSE_C_INTRODUCING:
case CIRCUIT_PURPOSE_S_CONNECT_REND:
case CIRCUIT_PURPOSE_C_GENERAL:
+ case CIRCUIT_PURPOSE_S_HSDIR_POST:
+ case CIRCUIT_PURPOSE_C_HSDIR_GET:
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
/* need to add a new hop */
tor_assert(extend_info);
@@ -2216,12 +2400,14 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
/* If we have specified a particular exit node for our
* connection, then be sure to open a circuit to that exit node.
*/
- if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL) {
+ if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL ||
+ desired_circuit_purpose == CIRCUIT_PURPOSE_S_HSDIR_POST ||
+ desired_circuit_purpose == CIRCUIT_PURPOSE_C_HSDIR_GET) {
if (conn->chosen_exit_name) {
const node_t *r;
int opt = conn->chosen_exit_optional;
r = node_get_by_nickname(conn->chosen_exit_name, 0);
- if (r && node_has_descriptor(r)) {
+ if (r && node_has_preferred_descriptor(r, conn->want_onehop ? 1 : 0)) {
/* We might want to connect to an IPv6 bridge for loading
descriptors so we use the preferred address rather than
the primary. */
@@ -2231,7 +2417,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
"Discarding this circuit.", conn->chosen_exit_name);
return -1;
}
- } else { /* ! (r && node_has_descriptor(r)) */
+ } else { /* ! (r && node_has_preferred_descriptor(...)) */
log_debug(LD_DIR, "considering %d, %s",
want_onehop, conn->chosen_exit_name);
if (want_onehop && conn->chosen_exit_name[0] == '$') {
@@ -2324,7 +2510,9 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
/* Now trigger things that need to happen when we launch circuits */
- if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL) {
+ if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL ||
+ desired_circuit_purpose == CIRCUIT_PURPOSE_C_HSDIR_GET ||
+ desired_circuit_purpose == CIRCUIT_PURPOSE_S_HSDIR_POST) {
/* We just caused a circuit to get built because of this stream.
* If this stream has caused a _lot_ of circuits to be built, that's
* a bad sign: we should tell the user. */
@@ -2453,6 +2641,8 @@ link_apconn_to_circ(entry_connection_t *apconn, origin_circuit_t *circ,
/* See if we can use optimistic data on this circuit */
if (optimistic_data_enabled() &&
(circ->base_.purpose == CIRCUIT_PURPOSE_C_GENERAL ||
+ circ->base_.purpose == CIRCUIT_PURPOSE_C_HSDIR_GET ||
+ circ->base_.purpose == CIRCUIT_PURPOSE_S_HSDIR_POST ||
circ->base_.purpose == CIRCUIT_PURPOSE_C_REND_JOINED))
apconn->may_use_optimistic_data = 1;
else
@@ -2573,6 +2763,39 @@ connection_ap_handshake_attach_chosen_circuit(entry_connection_t *conn,
return 1;
}
+/**
+ * Return an appropriate circuit purpose for non-rend streams.
+ * We don't handle rends here because a rend stream triggers two
+ * circuit builds with different purposes, so it is handled elsewhere.
+ *
+ * This function just figures out what type of hsdir activity this is,
+ * and tells us. Everything else is general.
+ */
+static int
+connection_ap_get_nonrend_circ_purpose(const entry_connection_t *conn)
+{
+ const connection_t *base_conn = ENTRY_TO_CONN(conn);
+ tor_assert_nonfatal(!connection_edge_is_rendezvous_stream(
+ ENTRY_TO_EDGE_CONN(conn)));
+
+ if (base_conn->linked_conn &&
+ base_conn->linked_conn->type == CONN_TYPE_DIR) {
+ /* Set a custom purpose for hsdir activity */
+ if (base_conn->linked_conn->purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2 ||
+ base_conn->linked_conn->purpose == DIR_PURPOSE_UPLOAD_HSDESC) {
+ return CIRCUIT_PURPOSE_S_HSDIR_POST;
+ } else if (base_conn->linked_conn->purpose
+ == DIR_PURPOSE_FETCH_RENDDESC_V2 ||
+ base_conn->linked_conn->purpose
+ == DIR_PURPOSE_FETCH_HSDESC) {
+ return CIRCUIT_PURPOSE_C_HSDIR_GET;
+ }
+ }
+
+ /* All other purposes are general for now */
+ return CIRCUIT_PURPOSE_C_GENERAL;
+}
+
/** Try to find a safe live circuit for stream <b>conn</b>. If we find one,
* attach the stream, send appropriate cells, and return 1. Otherwise,
* try to launch new circuit(s) for the stream. If we can launch
@@ -2671,9 +2894,12 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
}
/* Find the circuit that we should use, if there is one. Otherwise
- * launch it. */
- retval = circuit_get_open_circ_or_launch(
- conn, CIRCUIT_PURPOSE_C_GENERAL, &circ);
+ * launch it
+ */
+ retval = circuit_get_open_circ_or_launch(conn,
+ connection_ap_get_nonrend_circ_purpose(conn),
+ &circ);
+
if (retval < 1) {
/* We were either told "-1" (complete failure) or 0 (circuit in
* progress); we can't attach this stream yet. */
diff --git a/src/or/circuituse.h b/src/or/circuituse.h
index 2b0f983f1a..71c818b978 100644
--- a/src/or/circuituse.h
+++ b/src/or/circuituse.h
@@ -63,6 +63,9 @@ int hostname_in_track_host_exits(const or_options_t *options,
const char *address);
void mark_circuit_unusable_for_new_conns(origin_circuit_t *circ);
+int circuit_purpose_is_hidden_service(uint8_t);
+int circuit_should_use_vanguards(uint8_t);
+
#ifdef TOR_UNIT_TESTS
/* Used only by circuituse.c and test_circuituse.c */
diff --git a/src/or/command.c b/src/or/command.c
index 185596a65a..7280be1396 100644
--- a/src/or/command.c
+++ b/src/or/command.c
@@ -286,7 +286,7 @@ command_process_create_cell(cell_t *cell, channel_t *chan)
"Received create cell but we're shutting down. Sending back "
"destroy.");
channel_send_destroy(cell->circ_id, chan,
- END_CIRC_REASON_HIBERNATING);
+ END_CIRC_REASON_HIBERNATING);
return;
}
diff --git a/src/or/config.c b/src/or/config.c
index 8f4b5ab5a1..ac37324a2a 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -18,7 +18,7 @@
* inspecting values that are calculated as a result of the
* configured options.
*
- * <h3>How to add new options</h3>
+ * <h3>How to add new options</h3>
*
* To add new items to the torrc, there are a minimum of three places to edit:
* <ul>
@@ -83,6 +83,7 @@
#include "dns.h"
#include "dos.h"
#include "entrynodes.h"
+#include "git_revision.h"
#include "geoip.h"
#include "hibernate.h"
#include "main.h"
@@ -170,6 +171,10 @@ static config_abbrev_t option_abbrevs_[] = {
{ "BridgeAuthoritativeDirectory", "BridgeAuthoritativeDir", 0, 0},
{ "HashedControlPassword", "__HashedControlSessionPassword", 1, 0},
{ "VirtualAddrNetwork", "VirtualAddrNetworkIPv4", 0, 0},
+ { "SocksSocketsGroupWritable", "UnixSocksGroupWritable", 0, 1},
+ { "_HSLayer2Nodes", "HSLayer2Nodes", 0, 1 },
+ { "_HSLayer3Nodes", "HSLayer3Nodes", 0, 1 },
+
{ NULL, NULL, 0, 0},
};
@@ -253,6 +258,8 @@ static config_var_t option_vars_[] = {
V(BridgeRecordUsageByCountry, BOOL, "1"),
V(BridgeRelay, BOOL, "0"),
V(BridgeDistribution, STRING, NULL),
+ VAR("CacheDirectory", FILENAME, CacheDirectory_option, NULL),
+ V(CacheDirectoryGroupReadable, BOOL, "0"),
V(CellStatistics, BOOL, "0"),
V(PaddingStatistics, BOOL, "1"),
V(LearnCircuitBuildTimeout, BOOL, "1"),
@@ -281,12 +288,12 @@ static config_var_t option_vars_[] = {
V(ControlPortWriteToFile, FILENAME, NULL),
V(ControlSocket, LINELIST, NULL),
V(ControlSocketsGroupWritable, BOOL, "0"),
- V(SocksSocketsGroupWritable, BOOL, "0"),
+ V(UnixSocksGroupWritable, BOOL, "0"),
V(CookieAuthentication, BOOL, "0"),
V(CookieAuthFileGroupReadable, BOOL, "0"),
V(CookieAuthFile, STRING, NULL),
V(CountPrivateBandwidth, BOOL, "0"),
- V(DataDirectory, FILENAME, NULL),
+ VAR("DataDirectory", FILENAME, DataDirectory_option, NULL),
V(DataDirectoryGroupReadable, BOOL, "0"),
V(DisableOOSCheck, BOOL, "1"),
V(DisableNetwork, BOOL, "0"),
@@ -378,6 +385,7 @@ static config_var_t option_vars_[] = {
V(GuardLifetime, INTERVAL, "0 minutes"),
V(HardwareAccel, BOOL, "0"),
V(HeartbeatPeriod, INTERVAL, "6 hours"),
+ V(MainloopStats, BOOL, "0"),
V(AccelName, STRING, NULL),
V(AccelDir, FILENAME, NULL),
V(HashedControlPassword, LINELIST, NULL),
@@ -412,6 +420,10 @@ static config_var_t option_vars_[] = {
V(Socks5Proxy, STRING, NULL),
V(Socks5ProxyUsername, STRING, NULL),
V(Socks5ProxyPassword, STRING, NULL),
+ VAR("KeyDirectory", FILENAME, KeyDirectory_option, NULL),
+ V(KeyDirectoryGroupReadable, BOOL, "0"),
+ VAR("HSLayer2Nodes", ROUTERSET, HSLayer2Nodes, NULL),
+ VAR("HSLayer3Nodes", ROUTERSET, HSLayer3Nodes, NULL),
V(KeepalivePeriod, INTERVAL, "5 minutes"),
V(KeepBindCapabilities, AUTOBOOL, "auto"),
VAR("Log", LINELIST, Logs, NULL),
@@ -419,6 +431,7 @@ static config_var_t option_vars_[] = {
V(LogTimeGranularity, MSEC_INTERVAL, "1 second"),
V(TruncateLogFile, BOOL, "0"),
V(SyslogIdentityTag, STRING, NULL),
+ V(AndroidIdentityTag, STRING, NULL),
V(LongLivedPorts, CSV,
"21,22,706,1863,5050,5190,5222,5223,6523,6667,6697,8300"),
VAR("MapAddress", LINELIST, AddressMap, NULL),
@@ -504,6 +517,7 @@ static config_var_t option_vars_[] = {
V(RendPostPeriod, INTERVAL, "1 hour"),
V(RephistTrackTime, INTERVAL, "24 hours"),
V(RunAsDaemon, BOOL, "0"),
+ V(ReducedExitPolicy, BOOL, "0"),
OBSOLETE("RunTesting"), // currently unused
V(Sandbox, BOOL, "0"),
V(SafeLogging, STRING, "1"),
@@ -578,10 +592,12 @@ static config_var_t option_vars_[] = {
VAR("__ReloadTorrcOnSIGHUP", BOOL, ReloadTorrcOnSIGHUP, "1"),
VAR("__AllDirActionsPrivate", BOOL, AllDirActionsPrivate, "0"),
VAR("__DisablePredictedCircuits",BOOL,DisablePredictedCircuits, "0"),
+ VAR("__DisableSignalHandlers", BOOL, DisableSignalHandlers, "0"),
VAR("__LeaveStreamsUnattached",BOOL, LeaveStreamsUnattached, "0"),
VAR("__HashedControlSessionPassword", LINELIST, HashedControlSessionPassword,
NULL),
VAR("__OwningControllerProcess",STRING,OwningControllerProcess, NULL),
+ VAR("__OwningControllerFD",INT,OwningControllerFD, "-1"),
V(MinUptimeHidServDirectoryV2, INTERVAL, "96 hours"),
V(TestingServerDownloadSchedule, CSV_INTERVAL, "0, 0, 0, 60, 60, 120, "
"300, 900, 2147483647"),
@@ -632,15 +648,12 @@ static config_var_t option_vars_[] = {
"0, 30, 90, 600, 3600, 10800, 25200, 54000, 111600, 262800"),
V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "10 minutes"),
V(TestingDirConnectionMaxStall, INTERVAL, "5 minutes"),
- V(TestingConsensusMaxDownloadTries, UINT, "8"),
- /* Since we try connections rapidly and simultaneously, we can afford
- * to give up earlier. (This protects against overloading directories.) */
- V(ClientBootstrapConsensusMaxDownloadTries, UINT, "7"),
- /* We want to give up much earlier if we're only using authorities. */
- V(ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries, UINT, "4"),
- V(TestingDescriptorMaxDownloadTries, UINT, "8"),
- V(TestingMicrodescMaxDownloadTries, UINT, "8"),
- V(TestingCertMaxDownloadTries, UINT, "8"),
+ OBSOLETE("TestingConsensusMaxDownloadTries"),
+ OBSOLETE("ClientBootstrapConsensusMaxDownloadTries"),
+ OBSOLETE("ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries"),
+ OBSOLETE("TestingDescriptorMaxDownloadTries"),
+ OBSOLETE("TestingMicrodescMaxDownloadTries"),
+ OBSOLETE("TestingCertMaxDownloadTries"),
V(TestingDirAuthVoteExit, ROUTERSET, NULL),
V(TestingDirAuthVoteExitIsStrict, BOOL, "0"),
V(TestingDirAuthVoteGuard, ROUTERSET, NULL),
@@ -665,8 +678,6 @@ static const config_var_t testing_tor_network_defaults[] = {
"0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"),
V(ClientBootstrapConsensusAuthorityOnlyDownloadSchedule, CSV_INTERVAL,
"0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"),
- V(ClientBootstrapConsensusMaxDownloadTries, UINT, "80"),
- V(ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries, UINT, "80"),
V(ClientDNSRejectInternalAddresses, BOOL,"0"),
V(ClientRejectInternalAddresses, BOOL, "0"),
V(CountPrivateBandwidth, BOOL, "1"),
@@ -694,10 +705,6 @@ static const config_var_t testing_tor_network_defaults[] = {
"15, 20, 30, 60"),
V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "5 seconds"),
V(TestingDirConnectionMaxStall, INTERVAL, "30 seconds"),
- V(TestingConsensusMaxDownloadTries, UINT, "80"),
- V(TestingDescriptorMaxDownloadTries, UINT, "80"),
- V(TestingMicrodescMaxDownloadTries, UINT, "80"),
- V(TestingCertMaxDownloadTries, UINT, "80"),
V(TestingEnableConnBwEvent, BOOL, "1"),
V(TestingEnableCellStatsEvent, BOOL, "1"),
V(TestingEnableTbEmptyEvent, BOOL, "1"),
@@ -751,7 +758,7 @@ static int parse_ports(or_options_t *options, int validate_only,
static int check_server_ports(const smartlist_t *ports,
const or_options_t *options,
int *num_low_ports_out);
-static int validate_data_directory(or_options_t *options);
+static int validate_data_directories(or_options_t *options);
static int write_configuration_file(const char *fname,
const or_options_t *options);
static int options_init_logs(const or_options_t *old_options,
@@ -766,8 +773,8 @@ static void config_maybe_load_geoip_files_(const or_options_t *options,
static int options_validate_cb(void *old_options, void *options,
void *default_options,
int from_setconf, char **msg);
-static uint64_t compute_real_max_mem_in_queues(const uint64_t val,
- int log_guess);
+static void cleanup_protocol_warning_severity_level(void);
+static void set_protocol_warning_severity_level(int warning_severity);
/** Magic value for or_options_t. */
#define OR_OPTIONS_MAGIC 9090909
@@ -796,7 +803,7 @@ static or_options_t *global_default_options = NULL;
/** Name of most recently read torrc file. */
static char *torrc_fname = NULL;
/** Name of the most recently read torrc-defaults file.*/
-static char *torrc_defaults_fname;
+static char *torrc_defaults_fname = NULL;
/** Configuration options set by command line. */
static config_line_t *global_cmdline_options = NULL;
/** Non-configuration options set by the command line */
@@ -810,6 +817,8 @@ static smartlist_t *configured_ports = NULL;
/** True iff we're currently validating options, and any calls to
* get_options() are likely to be bugs. */
static int in_option_validation = 0;
+/* True iff we've initialized libevent */
+static int libevent_initialized = 0;
/** Return the contents of our frontpage string, or NULL if not configured. */
MOCK_IMPL(const char*,
@@ -854,9 +863,12 @@ set_options(or_options_t *new_val, char **msg)
return -1;
}
if (options_act(old_options) < 0) { /* acting on the options failed. die. */
- log_err(LD_BUG,
- "Acting on config options left us in a broken state. Dying.");
- exit(1);
+ if (! tor_event_loop_shutdown_is_pending()) {
+ log_err(LD_BUG,
+ "Acting on config options left us in a broken state. Dying.");
+ tor_shutdown_event_loop_and_exit(1);
+ }
+ return -1;
}
/* Issues a CONF_CHANGED event to notify controller of the change. If Tor is
* just starting up then the old_options will be undefined. */
@@ -898,8 +910,6 @@ set_options(or_options_t *new_val, char **msg)
return 0;
}
-extern const char tor_git_revision[]; /* from tor_main.c */
-
/** The version of this Tor process, as parsed. */
static char *the_tor_version = NULL;
/** A shorter version of this Tor process's version, for export in our router
@@ -939,7 +949,7 @@ get_short_version(void)
/** Release additional memory allocated in options
*/
STATIC void
-or_options_free(or_options_t *options)
+or_options_free_(or_options_t *options)
{
if (!options)
return;
@@ -954,6 +964,13 @@ or_options_free(or_options_t *options)
SMARTLIST_FOREACH(options->SchedulerTypes_, int *, i, tor_free(i));
smartlist_free(options->SchedulerTypes_);
}
+ if (options->FilesOpenedByIncludes) {
+ SMARTLIST_FOREACH(options->FilesOpenedByIncludes, char *, f, tor_free(f));
+ smartlist_free(options->FilesOpenedByIncludes);
+ }
+ tor_free(options->DataDirectory);
+ tor_free(options->CacheDirectory);
+ tor_free(options->KeyDirectory);
tor_free(options->BridgePassword_AuthDigest_);
tor_free(options->command_arg);
tor_free(options->master_key_fname);
@@ -990,6 +1007,11 @@ config_free_all(void)
tor_free(the_short_tor_version);
tor_free(the_tor_version);
+
+ cleanup_protocol_warning_severity_level();
+
+ have_parsed_cmdline = 0;
+ libevent_initialized = 0;
}
/** Make <b>address</b> -- a piece of information related to our operation as
@@ -1053,17 +1075,46 @@ escaped_safe_str(const char *address)
* The severity level that should be used for warnings of severity
* LOG_PROTOCOL_WARN.
*
- * We keep this outside the options, in case somebody needs to use
- * LOG_PROTOCOL_WARN while an option transition is happening.
+ * We keep this outside the options, and we use an atomic_counter_t, in case
+ * one thread needs to use LOG_PROTOCOL_WARN while an option transition is
+ * happening in the main thread.
*/
-static int protocol_warning_severity_level = LOG_WARN;
+static atomic_counter_t protocol_warning_severity_level;
/** Return the severity level that should be used for warnings of severity
* LOG_PROTOCOL_WARN. */
int
get_protocol_warning_severity_level(void)
{
- return protocol_warning_severity_level;
+ return (int) atomic_counter_get(&protocol_warning_severity_level);
+}
+
+/** Set the protocol warning severity level to <b>severity</b>. */
+static void
+set_protocol_warning_severity_level(int warning_severity)
+{
+ atomic_counter_exchange(&protocol_warning_severity_level,
+ warning_severity);
+}
+
+/**
+ * Initialize the log warning severity level for protocol warnings. Call
+ * only once at startup.
+ */
+void
+init_protocol_warning_severity_level(void)
+{
+ atomic_counter_init(&protocol_warning_severity_level);
+ set_protocol_warning_severity_level(LOG_WARN);
+}
+
+/**
+ * Tear down protocol_warning_severity_level.
+ */
+static void
+cleanup_protocol_warning_severity_level(void)
+{
+ atomic_counter_destroy(&protocol_warning_severity_level);
}
/** List of default directory authorities */
@@ -1231,6 +1282,69 @@ consider_adding_dir_servers(const or_options_t *options,
return 0;
}
+/**
+ * Make sure that <b>directory</b> exists, with appropriate ownership and
+ * permissions (as modified by <b>group_readable</b>). If <b>create</b>,
+ * create the directory if it is missing. Return 0 on success.
+ * On failure, return -1 and set *<b>msg_out</b>.
+ */
+static int
+check_and_create_data_directory(int create,
+ const char *directory,
+ int group_readable,
+ const char *owner,
+ char **msg_out)
+{
+ cpd_check_t cpd_opts = create ? CPD_CREATE : CPD_CHECK;
+ if (group_readable)
+ cpd_opts |= CPD_GROUP_READ;
+ if (check_private_dir(directory,
+ cpd_opts,
+ owner) < 0) {
+ tor_asprintf(msg_out,
+ "Couldn't %s private data directory \"%s\"",
+ create ? "create" : "access",
+ directory);
+ return -1;
+ }
+
+#ifndef _WIN32
+ if (group_readable) {
+ /* Only new dirs created get new opts, also enforce group read. */
+ if (chmod(directory, 0750)) {
+ log_warn(LD_FS,"Unable to make %s group-readable: %s",
+ directory, strerror(errno));
+ }
+ }
+#endif /* !defined(_WIN32) */
+
+ return 0;
+}
+
+/**
+ * Ensure that our keys directory exists, with appropriate permissions.
+ * Return 0 on success, -1 on failure.
+ */
+int
+create_keys_directory(const or_options_t *options)
+{
+ /* Make sure DataDirectory exists, and is private. */
+ cpd_check_t cpd_opts = CPD_CREATE;
+ if (options->DataDirectoryGroupReadable)
+ cpd_opts |= CPD_GROUP_READ;
+ if (check_private_dir(options->DataDirectory, cpd_opts, options->User)) {
+ log_err(LD_OR, "Can't create/check datadirectory %s",
+ options->DataDirectory);
+ return -1;
+ }
+
+ /* Check the key directory. */
+ if (check_private_dir(options->KeyDirectory, CPD_CREATE, options->User)) {
+ return -1;
+ }
+ return 0;
+}
+
/* Helps determine flags to pass to switch_id. */
static int have_low_ports = -1;
@@ -1245,7 +1359,6 @@ options_act_reversible(const or_options_t *old_options, char **msg)
{
smartlist_t *new_listeners = smartlist_new();
smartlist_t *replaced_listeners = smartlist_new();
- static int libevent_initialized = 0;
or_options_t *options = get_options_mutable();
int running_tor = options->command == CMD_RUN_TOR;
int set_conn_limit = 0;
@@ -1385,29 +1498,30 @@ options_act_reversible(const or_options_t *old_options, char **msg)
}
/* Ensure data directory is private; create if possible. */
- cpd_check_t cpd_opts = running_tor ? CPD_CREATE : CPD_CHECK;
- if (options->DataDirectoryGroupReadable)
- cpd_opts |= CPD_GROUP_READ;
- if (check_private_dir(options->DataDirectory,
- cpd_opts,
- options->User)<0) {
- tor_asprintf(msg,
- "Couldn't access/create private data directory \"%s\"",
- options->DataDirectory);
-
+ /* It's okay to do this in "options_act_reversible()" even though it isn't
+ * actually reversible, since you can't change the DataDirectory while
+ * Tor is running. */
+ if (check_and_create_data_directory(running_tor /* create */,
+ options->DataDirectory,
+ options->DataDirectoryGroupReadable,
+ options->User,
+ msg) < 0) {
goto done;
- /* No need to roll back, since you can't change the value. */
}
-
-#ifndef _WIN32
- if (options->DataDirectoryGroupReadable) {
- /* Only new dirs created get new opts, also enforce group read. */
- if (chmod(options->DataDirectory, 0750)) {
- log_warn(LD_FS,"Unable to make %s group-readable: %s",
- options->DataDirectory, strerror(errno));
- }
+ if (check_and_create_data_directory(running_tor /* create */,
+ options->KeyDirectory,
+ options->KeyDirectoryGroupReadable,
+ options->User,
+ msg) < 0) {
+ goto done;
+ }
+ if (check_and_create_data_directory(running_tor /* create */,
+ options->CacheDirectory,
+ options->CacheDirectoryGroupReadable,
+ options->User,
+ msg) < 0) {
+ goto done;
}
-#endif /* !defined(_WIN32) */
/* Bail out at this point if we're not going to be a client or server:
* we don't run Tor itself. */
@@ -1544,6 +1658,8 @@ options_need_geoip_info(const or_options_t *options, const char **reason_out)
routerset_needs_geoip(options->ExitNodes) ||
routerset_needs_geoip(options->ExcludeExitNodes) ||
routerset_needs_geoip(options->ExcludeNodes) ||
+ routerset_needs_geoip(options->HSLayer2Nodes) ||
+ routerset_needs_geoip(options->HSLayer3Nodes) ||
routerset_needs_geoip(options->Tor2webRendezvousPoints);
if (routerset_usage && reason_out) {
@@ -1584,32 +1700,46 @@ get_effective_bwburst(const or_options_t *options)
return (uint32_t)bw;
}
+/* Used in the various options_transition_affects* functions. */
+#define YES_IF_CHANGED_BOOL(opt) \
+ if (!CFG_EQ_BOOL(old_options, new_options, opt)) return 1;
+#define YES_IF_CHANGED_INT(opt) \
+ if (!CFG_EQ_INT(old_options, new_options, opt)) return 1;
+#define YES_IF_CHANGED_STRING(opt) \
+ if (!CFG_EQ_STRING(old_options, new_options, opt)) return 1;
+#define YES_IF_CHANGED_LINELIST(opt) \
+ if (!CFG_EQ_LINELIST(old_options, new_options, opt)) return 1;
+#define YES_IF_CHANGED_SMARTLIST(opt) \
+ if (!CFG_EQ_SMARTLIST(old_options, new_options, opt)) return 1;
+#define YES_IF_CHANGED_ROUTERSET(opt) \
+ if (!CFG_EQ_ROUTERSET(old_options, new_options, opt)) return 1;
+
/**
* Return true if changing the configuration from <b>old</b> to <b>new</b>
- * affects the guard susbsystem.
+ * affects the guard subsystem.
*/
static int
-options_transition_affects_guards(const or_options_t *old,
- const or_options_t *new)
+options_transition_affects_guards(const or_options_t *old_options,
+ const or_options_t *new_options)
{
/* NOTE: Make sure this function stays in sync with
- * entry_guards_set_filtered_flags */
-
- tor_assert(old);
- tor_assert(new);
-
- return
- (old->UseEntryGuards != new->UseEntryGuards ||
- old->UseBridges != new->UseBridges ||
- old->ClientUseIPv4 != new->ClientUseIPv4 ||
- old->ClientUseIPv6 != new->ClientUseIPv6 ||
- old->FascistFirewall != new->FascistFirewall ||
- !routerset_equal(old->ExcludeNodes, new->ExcludeNodes) ||
- !routerset_equal(old->EntryNodes, new->EntryNodes) ||
- !smartlist_strings_eq(old->FirewallPorts, new->FirewallPorts) ||
- !config_lines_eq(old->Bridges, new->Bridges) ||
- !config_lines_eq(old->ReachableORAddresses, new->ReachableORAddresses) ||
- !config_lines_eq(old->ReachableDirAddresses, new->ReachableDirAddresses));
+ * node_passes_guard_filter */
+ tor_assert(old_options);
+ tor_assert(new_options);
+
+ YES_IF_CHANGED_BOOL(UseEntryGuards);
+ YES_IF_CHANGED_BOOL(UseBridges);
+ YES_IF_CHANGED_BOOL(ClientUseIPv4);
+ YES_IF_CHANGED_BOOL(ClientUseIPv6);
+ YES_IF_CHANGED_BOOL(FascistFirewall);
+ YES_IF_CHANGED_ROUTERSET(ExcludeNodes);
+ YES_IF_CHANGED_ROUTERSET(EntryNodes);
+ YES_IF_CHANGED_SMARTLIST(FirewallPorts);
+ YES_IF_CHANGED_LINELIST(Bridges);
+ YES_IF_CHANGED_LINELIST(ReachableORAddresses);
+ YES_IF_CHANGED_LINELIST(ReachableDirAddresses);
+
+ return 0;
}
/** Fetch the active option list, and take actions based on it. All of the
@@ -1672,13 +1802,16 @@ options_act(const or_options_t *old_options)
return -1;
}
- if (options->ProtocolWarnings)
- protocol_warning_severity_level = LOG_WARN;
- else
- protocol_warning_severity_level = LOG_INFO;
+ {
+ int warning_severity = options->ProtocolWarnings ? LOG_WARN : LOG_INFO;
+ set_protocol_warning_severity_level(warning_severity);
+ }
- if (consider_adding_dir_servers(options, old_options) < 0)
+ if (consider_adding_dir_servers(options, old_options) < 0) {
+ // XXXX This should get validated earlier, and committed here, to
+ // XXXX lower opportunities for reaching an error case.
return -1;
+ }
if (rend_non_anonymous_mode_enabled(options)) {
log_warn(LD_GENERAL, "This copy of Tor was compiled or configured to run "
@@ -1687,6 +1820,7 @@ options_act(const or_options_t *old_options)
#ifdef ENABLE_TOR2WEB_MODE
/* LCOV_EXCL_START */
+ // XXXX This should move into options_validate()
if (!options->Tor2webMode) {
log_err(LD_CONFIG, "This copy of Tor was compiled to run in "
"'tor2web mode'. It can only be run with the Tor2webMode torrc "
@@ -1695,6 +1829,7 @@ options_act(const or_options_t *old_options)
}
/* LCOV_EXCL_STOP */
#else /* !(defined(ENABLE_TOR2WEB_MODE)) */
+ // XXXX This should move into options_validate()
if (options->Tor2webMode) {
log_err(LD_CONFIG, "This copy of Tor was not compiled to run in "
"'tor2web mode'. It cannot be run with the Tor2webMode torrc "
@@ -1722,9 +1857,11 @@ options_act(const or_options_t *old_options)
for (cl = options->Bridges; cl; cl = cl->next) {
bridge_line_t *bridge_line = parse_bridge_line(cl->value);
if (!bridge_line) {
+ // LCOV_EXCL_START
log_warn(LD_BUG,
"Previously validated Bridge line could not be added!");
return -1;
+ // LCOV_EXCL_STOP
}
bridge_add_from_config(bridge_line);
}
@@ -1732,15 +1869,37 @@ options_act(const or_options_t *old_options)
}
if (running_tor && hs_config_service_all(options, 0)<0) {
+ // LCOV_EXCL_START
log_warn(LD_BUG,
"Previously validated hidden services line could not be added!");
return -1;
+ // LCOV_EXCL_STOP
}
if (running_tor && rend_parse_service_authorization(options, 0) < 0) {
+ // LCOV_EXCL_START
log_warn(LD_BUG, "Previously validated client authorization for "
"hidden services could not be added!");
return -1;
+ // LCOV_EXCL_STOP
+ }
+
+ if (running_tor && !old_options && options->OwningControllerFD != -1) {
+#ifdef _WIN32
+ log_warn(LD_CONFIG, "OwningControllerFD is not supported on Windows. "
+ "If you need it, tell the Tor developers.");
+ return -1;
+#else
+ const unsigned ctrl_flags =
+ CC_LOCAL_FD_IS_OWNER |
+ CC_LOCAL_FD_IS_AUTHENTICATED;
+ tor_socket_t ctrl_sock = (tor_socket_t)options->OwningControllerFD;
+ if (control_connection_add_local_fd(ctrl_sock, ctrl_flags) < 0) {
+ log_warn(LD_CONFIG, "Could not add local controller connection with "
+ "given FD.");
+ return -1;
+ }
+#endif /* defined(_WIN32) */
}
/* Load state */
@@ -1763,10 +1922,12 @@ options_act(const or_options_t *old_options)
if (options->ClientTransportPlugin) {
for (cl = options->ClientTransportPlugin; cl; cl = cl->next) {
if (parse_transport_line(options, cl->value, 0, 0) < 0) {
+ // LCOV_EXCL_START
log_warn(LD_BUG,
"Previously validated ClientTransportPlugin line "
"could not be added!");
return -1;
+ // LCOV_EXCL_STOP
}
}
}
@@ -1774,10 +1935,12 @@ options_act(const or_options_t *old_options)
if (options->ServerTransportPlugin && server_mode(options)) {
for (cl = options->ServerTransportPlugin; cl; cl = cl->next) {
if (parse_transport_line(options, cl->value, 0, 1) < 0) {
+ // LCOV_EXCL_START
log_warn(LD_BUG,
"Previously validated ServerTransportPlugin line "
"could not be added!");
return -1;
+ // LCOV_EXCL_STOP
}
}
}
@@ -1863,8 +2026,10 @@ options_act(const or_options_t *old_options)
/* Set up accounting */
if (accounting_parse_options(options, 0)<0) {
- log_warn(LD_CONFIG,"Error in accounting options");
+ // LCOV_EXCL_START
+ log_warn(LD_BUG,"Error in previously validated accounting options");
return -1;
+ // LCOV_EXCL_STOP
}
if (accounting_is_enabled(options))
configure_accounting(time(NULL));
@@ -1887,6 +2052,7 @@ options_act(const or_options_t *old_options)
char *http_authenticator;
http_authenticator = alloc_http_authenticator(options->BridgePassword);
if (!http_authenticator) {
+ // XXXX This should get validated in options_validate().
log_warn(LD_BUG, "Unable to allocate HTTP authenticator. Not setting "
"BridgePassword.");
return -1;
@@ -1899,9 +2065,12 @@ options_act(const or_options_t *old_options)
}
if (parse_outbound_addresses(options, 0, &msg) < 0) {
- log_warn(LD_BUG, "Failed parsing outbound bind addresses: %s", msg);
+ // LCOV_EXCL_START
+ log_warn(LD_BUG, "Failed parsing previously validated outbound "
+ "bind addresses: %s", msg);
tor_free(msg);
return -1;
+ // LCOV_EXCL_STOP
}
config_maybe_load_geoip_files_(options, old_options);
@@ -1932,6 +2101,10 @@ options_act(const or_options_t *old_options)
options->ExcludeExitNodes) ||
!routerset_equal(old_options->EntryNodes, options->EntryNodes) ||
!routerset_equal(old_options->ExitNodes, options->ExitNodes) ||
+ !routerset_equal(old_options->HSLayer2Nodes,
+ options->HSLayer2Nodes) ||
+ !routerset_equal(old_options->HSLayer3Nodes,
+ options->HSLayer3Nodes) ||
!routerset_equal(old_options->Tor2webRendezvousPoints,
options->Tor2webRendezvousPoints) ||
options->StrictNodes != old_options->StrictNodes) {
@@ -2032,6 +2205,10 @@ options_act(const or_options_t *old_options)
if (options->PerConnBWRate != old_options->PerConnBWRate ||
options->PerConnBWBurst != old_options->PerConnBWBurst)
connection_or_update_token_buckets(get_connection_array(), options);
+
+ if (options->MainloopStats != old_options->MainloopStats) {
+ reset_main_loop_counters();
+ }
}
/* Only collect directory-request statistics on relays and bridges. */
@@ -2937,7 +3114,7 @@ warn_if_option_path_is_relative(const char *option,
return 0;
}
-/** Scan <b>options</b> for occurances of relative file/directory
+/** Scan <b>options</b> for occurrences of relative file/directory
* path and log a warning whenever it is found.
*
* Return 1 if there were relative paths; 0 otherwise.
@@ -3148,12 +3325,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
if (parse_outbound_addresses(options, 1, msg) < 0)
return -1;
- if (validate_data_directory(options)<0)
+ if (validate_data_directories(options)<0)
REJECT("Invalid DataDirectory");
if (options->Nickname == NULL) {
if (server_mode(options)) {
- options->Nickname = tor_strdup(UNNAMED_ROUTER_NICKNAME);
+ options->Nickname = tor_strdup(UNNAMED_ROUTER_NICKNAME);
}
} else {
if (!is_legal_nickname(options->Nickname)) {
@@ -3173,9 +3350,9 @@ options_validate(or_options_t *old_options, or_options_t *options,
/* Special case on first boot if no Log options are given. */
if (!options->Logs && !options->RunAsDaemon && !from_setconf) {
if (quiet_level == 0)
- config_line_append(&options->Logs, "Log", "notice stdout");
+ config_line_append(&options->Logs, "Log", "notice stdout");
else if (quiet_level == 1)
- config_line_append(&options->Logs, "Log", "warn stdout");
+ config_line_append(&options->Logs, "Log", "warn stdout");
}
/* Validate the tor_log(s) */
@@ -4216,10 +4393,6 @@ options_validate(or_options_t *old_options, or_options_t *options,
CHECK_DEFAULT(TestingBridgeBootstrapDownloadSchedule);
CHECK_DEFAULT(TestingClientMaxIntervalWithoutRequest);
CHECK_DEFAULT(TestingDirConnectionMaxStall);
- CHECK_DEFAULT(TestingConsensusMaxDownloadTries);
- CHECK_DEFAULT(TestingDescriptorMaxDownloadTries);
- CHECK_DEFAULT(TestingMicrodescMaxDownloadTries);
- CHECK_DEFAULT(TestingCertMaxDownloadTries);
CHECK_DEFAULT(TestingAuthKeyLifetime);
CHECK_DEFAULT(TestingLinkCertLifetime);
CHECK_DEFAULT(TestingSigningKeySlop);
@@ -4294,33 +4467,6 @@ options_validate(or_options_t *old_options, or_options_t *options,
COMPLAIN("TestingDirConnectionMaxStall is insanely high.");
}
- if (options->TestingConsensusMaxDownloadTries < 2) {
- REJECT("TestingConsensusMaxDownloadTries must be greater than 2.");
- } else if (options->TestingConsensusMaxDownloadTries > 800) {
- COMPLAIN("TestingConsensusMaxDownloadTries is insanely high.");
- }
-
- if (options->ClientBootstrapConsensusMaxDownloadTries < 2) {
- REJECT("ClientBootstrapConsensusMaxDownloadTries must be greater "
- "than 2."
- );
- } else if (options->ClientBootstrapConsensusMaxDownloadTries > 800) {
- COMPLAIN("ClientBootstrapConsensusMaxDownloadTries is insanely "
- "high.");
- }
-
- if (options->ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries
- < 2) {
- REJECT("ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries must "
- "be greater than 2."
- );
- } else if (
- options->ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries
- > 800) {
- COMPLAIN("ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries is "
- "insanely high.");
- }
-
if (options->ClientBootstrapConsensusMaxInProgressTries < 1) {
REJECT("ClientBootstrapConsensusMaxInProgressTries must be greater "
"than 0.");
@@ -4330,24 +4476,6 @@ options_validate(or_options_t *old_options, or_options_t *options,
"high.");
}
- if (options->TestingDescriptorMaxDownloadTries < 2) {
- REJECT("TestingDescriptorMaxDownloadTries must be greater than 1.");
- } else if (options->TestingDescriptorMaxDownloadTries > 800) {
- COMPLAIN("TestingDescriptorMaxDownloadTries is insanely high.");
- }
-
- if (options->TestingMicrodescMaxDownloadTries < 2) {
- REJECT("TestingMicrodescMaxDownloadTries must be greater than 1.");
- } else if (options->TestingMicrodescMaxDownloadTries > 800) {
- COMPLAIN("TestingMicrodescMaxDownloadTries is insanely high.");
- }
-
- if (options->TestingCertMaxDownloadTries < 2) {
- REJECT("TestingCertMaxDownloadTries must be greater than 1.");
- } else if (options->TestingCertMaxDownloadTries > 800) {
- COMPLAIN("TestingCertMaxDownloadTries is insanely high.");
- }
-
if (options->TestingEnableConnBwEvent &&
!options->TestingTorNetwork && !options->UsingTestNetworkDefaults_) {
REJECT("TestingEnableConnBwEvent may only be changed in testing "
@@ -4407,7 +4535,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
/* Given the value that the user has set for MaxMemInQueues, compute the
* actual maximum value. We clip this value if it's too low, and autodetect
* it if it's set to 0. */
-static uint64_t
+STATIC uint64_t
compute_real_max_mem_in_queues(const uint64_t val, int log_guess)
{
uint64_t result;
@@ -4415,11 +4543,6 @@ compute_real_max_mem_in_queues(const uint64_t val, int log_guess)
if (val == 0) {
#define ONE_GIGABYTE (U64_LITERAL(1) << 30)
#define ONE_MEGABYTE (U64_LITERAL(1) << 20)
-#if SIZEOF_VOID_P >= 8
-#define MAX_DEFAULT_MAXMEM (8*ONE_GIGABYTE)
-#else
-#define MAX_DEFAULT_MAXMEM (2*ONE_GIGABYTE)
-#endif
/* The user didn't pick a memory limit. Choose a very large one
* that is still smaller than the system memory */
static int notice_sent = 0;
@@ -4434,14 +4557,38 @@ compute_real_max_mem_in_queues(const uint64_t val, int log_guess)
result = ONE_GIGABYTE;
#endif /* SIZEOF_VOID_P >= 8 */
} else {
- /* We detected it, so let's pick 3/4 of the total RAM as our limit. */
- const uint64_t avail = (ram / 4) * 3;
+ /* We detected the amount of memory available. */
+ uint64_t avail = 0;
+
+#if SIZEOF_SIZE_T > 4
+/* On a 64-bit platform, we consider 8GB "very large". */
+#define RAM_IS_VERY_LARGE(x) ((x) >= (8 * ONE_GIGABYTE))
+#else
+/* On a 32-bit platform, we can't have 8GB of ram. */
+#define RAM_IS_VERY_LARGE(x) (0)
+#endif
+
+ if (RAM_IS_VERY_LARGE(ram)) {
+ /* If we have 8 GB, or more, RAM available, we set the MaxMemInQueues
+ * to 0.4 * RAM. The idea behind this value is that the amount of RAM
+ * is more than enough for a single relay and should allow the relay
+ * operator to run two relays if they have additional bandwidth
+ * available.
+ */
+ avail = (ram / 5) * 2;
+ } else {
+ /* If we have less than 8 GB of RAM available, we use the "old" default
+ * for MaxMemInQueues of 0.75 * RAM.
+ */
+ avail = (ram / 4) * 3;
+ }
- /* Make sure it's in range from 0.25 GB to 8 GB. */
- if (avail > MAX_DEFAULT_MAXMEM) {
+ /* Make sure it's in range from 0.25 GB to 8 GB for 64-bit and 0.25 to 2
+ * GB for 32-bit. */
+ if (avail > MAX_DEFAULT_MEMORY_QUEUE_SIZE) {
/* If you want to use more than this much RAM, you need to configure
it yourself */
- result = MAX_DEFAULT_MAXMEM;
+ result = MAX_DEFAULT_MEMORY_QUEUE_SIZE;
} else if (avail < ONE_GIGABYTE / 4) {
result = ONE_GIGABYTE / 4;
} else {
@@ -4529,125 +4676,61 @@ options_transition_allowed(const or_options_t *old,
if (!old)
return 0;
- if (!opt_streq(old->PidFile, new_val->PidFile)) {
- *msg = tor_strdup("PidFile is not allowed to change.");
- return -1;
- }
-
- if (old->RunAsDaemon != new_val->RunAsDaemon) {
- *msg = tor_strdup("While Tor is running, changing RunAsDaemon "
- "is not allowed.");
- return -1;
- }
-
- if (old->Sandbox != new_val->Sandbox) {
- *msg = tor_strdup("While Tor is running, changing Sandbox "
- "is not allowed.");
- return -1;
- }
-
- if (strcmp(old->DataDirectory,new_val->DataDirectory)!=0) {
- tor_asprintf(msg,
- "While Tor is running, changing DataDirectory "
- "(\"%s\"->\"%s\") is not allowed.",
- old->DataDirectory, new_val->DataDirectory);
- return -1;
- }
-
- if (!opt_streq(old->User, new_val->User)) {
- *msg = tor_strdup("While Tor is running, changing User is not allowed.");
- return -1;
- }
-
- if (old->KeepBindCapabilities != new_val->KeepBindCapabilities) {
- *msg = tor_strdup("While Tor is running, changing KeepBindCapabilities is "
- "not allowed.");
- return -1;
- }
-
- if (!opt_streq(old->SyslogIdentityTag, new_val->SyslogIdentityTag)) {
- *msg = tor_strdup("While Tor is running, changing "
- "SyslogIdentityTag is not allowed.");
- return -1;
- }
-
- if ((old->HardwareAccel != new_val->HardwareAccel)
- || !opt_streq(old->AccelName, new_val->AccelName)
- || !opt_streq(old->AccelDir, new_val->AccelDir)) {
- *msg = tor_strdup("While Tor is running, changing OpenSSL hardware "
- "acceleration engine is not allowed.");
- return -1;
- }
-
- if (old->TestingTorNetwork != new_val->TestingTorNetwork) {
- *msg = tor_strdup("While Tor is running, changing TestingTorNetwork "
- "is not allowed.");
- return -1;
- }
-
- if (old->DisableAllSwap != new_val->DisableAllSwap) {
- *msg = tor_strdup("While Tor is running, changing DisableAllSwap "
- "is not allowed.");
- return -1;
- }
-
- if (old->TokenBucketRefillInterval != new_val->TokenBucketRefillInterval) {
- *msg = tor_strdup("While Tor is running, changing TokenBucketRefill"
- "Interval is not allowed");
- return -1;
- }
-
- if (old->HiddenServiceSingleHopMode != new_val->HiddenServiceSingleHopMode) {
- *msg = tor_strdup("While Tor is running, changing "
- "HiddenServiceSingleHopMode is not allowed.");
- return -1;
- }
-
- if (old->HiddenServiceNonAnonymousMode !=
- new_val->HiddenServiceNonAnonymousMode) {
- *msg = tor_strdup("While Tor is running, changing "
- "HiddenServiceNonAnonymousMode is not allowed.");
- return -1;
- }
-
- if (old->DisableDebuggerAttachment &&
- !new_val->DisableDebuggerAttachment) {
- *msg = tor_strdup("While Tor is running, disabling "
- "DisableDebuggerAttachment is not allowed.");
- return -1;
- }
-
- if (old->NoExec && !new_val->NoExec) {
- *msg = tor_strdup("While Tor is running, disabling "
- "NoExec is not allowed.");
- return -1;
- }
+#define BAD_CHANGE_TO(opt, how) do { \
+ *msg = tor_strdup("While Tor is running"how", changing " #opt \
+ " is not allowed"); \
+ return -1; \
+ } while (0)
+
+#define NO_CHANGE_BOOL(opt) \
+ if (! CFG_EQ_BOOL(old, new_val, opt)) BAD_CHANGE_TO(opt,"")
+#define NO_CHANGE_INT(opt) \
+ if (! CFG_EQ_INT(old, new_val, opt)) BAD_CHANGE_TO(opt,"")
+#define NO_CHANGE_STRING(opt) \
+ if (! CFG_EQ_STRING(old, new_val, opt)) BAD_CHANGE_TO(opt,"")
+
+ NO_CHANGE_STRING(PidFile);
+ NO_CHANGE_BOOL(RunAsDaemon);
+ NO_CHANGE_BOOL(Sandbox);
+ NO_CHANGE_STRING(DataDirectory);
+ NO_CHANGE_STRING(KeyDirectory);
+ NO_CHANGE_STRING(CacheDirectory);
+ NO_CHANGE_STRING(User);
+ NO_CHANGE_BOOL(KeepBindCapabilities);
+ NO_CHANGE_STRING(SyslogIdentityTag);
+ NO_CHANGE_STRING(AndroidIdentityTag);
+ NO_CHANGE_BOOL(HardwareAccel);
+ NO_CHANGE_STRING(AccelName);
+ NO_CHANGE_STRING(AccelDir);
+ NO_CHANGE_BOOL(TestingTorNetwork);
+ NO_CHANGE_BOOL(DisableAllSwap);
+ NO_CHANGE_INT(TokenBucketRefillInterval);
+ NO_CHANGE_BOOL(HiddenServiceSingleHopMode);
+ NO_CHANGE_BOOL(HiddenServiceNonAnonymousMode);
+ NO_CHANGE_BOOL(DisableDebuggerAttachment);
+ NO_CHANGE_BOOL(NoExec);
+ NO_CHANGE_INT(OwningControllerFD);
+ NO_CHANGE_BOOL(DisableSignalHandlers);
if (sandbox_is_active()) {
-#define SB_NOCHANGE_STR(opt) \
- do { \
- if (! opt_streq(old->opt, new_val->opt)) { \
- *msg = tor_strdup("Can't change " #opt " while Sandbox is active"); \
- return -1; \
- } \
- } while (0)
+#define SB_NOCHANGE_STR(opt) \
+ if (! CFG_EQ_STRING(old, new_val, opt)) \
+ BAD_CHANGE_TO(opt," with Sandbox active")
+#define SB_NOCHANGE_LINELIST(opt) \
+ if (! CFG_EQ_LINELIST(old, new_val, opt)) \
+ BAD_CHANGE_TO(opt," with Sandbox active")
+#define SB_NOCHANGE_INT(opt) \
+ if (! CFG_EQ_INT(old, new_val, opt)) \
+ BAD_CHANGE_TO(opt," with Sandbox active")
SB_NOCHANGE_STR(Address);
SB_NOCHANGE_STR(ServerDNSResolvConfFile);
SB_NOCHANGE_STR(DirPortFrontPage);
SB_NOCHANGE_STR(CookieAuthFile);
SB_NOCHANGE_STR(ExtORPortCookieAuthFile);
+ SB_NOCHANGE_LINELIST(Logs);
+ SB_NOCHANGE_INT(ConnLimit);
-#undef SB_NOCHANGE_STR
-
- if (! config_lines_eq(old->Logs, new_val->Logs)) {
- *msg = tor_strdup("Can't change Logs while Sandbox is active");
- return -1;
- }
- if (old->ConnLimit != new_val->ConnLimit) {
- *msg = tor_strdup("Can't change ConnLimit while Sandbox is active");
- return -1;
- }
if (server_mode(old) != server_mode(new_val)) {
*msg = tor_strdup("Can't start/stop being a server while "
"Sandbox is active");
@@ -4655,6 +4738,13 @@ options_transition_allowed(const or_options_t *old,
}
}
+#undef SB_NOCHANGE_LINELIST
+#undef SB_NOCHANGE_STR
+#undef SB_NOCHANGE_INT
+#undef BAD_CHANGE_TO
+#undef NO_CHANGE_BOOL
+#undef NO_CHANGE_INT
+#undef NO_CHANGE_STRING
return 0;
}
@@ -4664,22 +4754,20 @@ static int
options_transition_affects_workers(const or_options_t *old_options,
const or_options_t *new_options)
{
- if (!opt_streq(old_options->DataDirectory, new_options->DataDirectory) ||
- old_options->NumCPUs != new_options->NumCPUs ||
- !config_lines_eq(old_options->ORPort_lines, new_options->ORPort_lines) ||
- old_options->ServerDNSSearchDomains !=
- new_options->ServerDNSSearchDomains ||
- old_options->SafeLogging_ != new_options->SafeLogging_ ||
- old_options->ClientOnly != new_options->ClientOnly ||
- server_mode(old_options) != server_mode(new_options) ||
- dir_server_mode(old_options) != dir_server_mode(new_options) ||
+ YES_IF_CHANGED_STRING(DataDirectory);
+ YES_IF_CHANGED_INT(NumCPUs);
+ YES_IF_CHANGED_LINELIST(ORPort_lines);
+ YES_IF_CHANGED_BOOL(ServerDNSSearchDomains);
+ YES_IF_CHANGED_BOOL(SafeLogging_);
+ YES_IF_CHANGED_BOOL(ClientOnly);
+ YES_IF_CHANGED_BOOL(LogMessageDomains);
+ YES_IF_CHANGED_LINELIST(Logs);
+
+ if (server_mode(old_options) != server_mode(new_options) ||
public_server_mode(old_options) != public_server_mode(new_options) ||
- !config_lines_eq(old_options->Logs, new_options->Logs) ||
- old_options->LogMessageDomains != new_options->LogMessageDomains)
+ dir_server_mode(old_options) != dir_server_mode(new_options))
return 1;
- /* Check whether log options match. */
-
/* Nothing that changed matters. */
return 0;
}
@@ -4692,37 +4780,34 @@ options_transition_affects_descriptor(const or_options_t *old_options,
{
/* XXX We can be smarter here. If your DirPort isn't being
* published and you just turned it off, no need to republish. Etc. */
- if (!opt_streq(old_options->DataDirectory, new_options->DataDirectory) ||
- !opt_streq(old_options->Nickname,new_options->Nickname) ||
- !opt_streq(old_options->Address,new_options->Address) ||
- !config_lines_eq(old_options->ExitPolicy,new_options->ExitPolicy) ||
- old_options->ExitRelay != new_options->ExitRelay ||
- old_options->ExitPolicyRejectPrivate !=
- new_options->ExitPolicyRejectPrivate ||
- old_options->ExitPolicyRejectLocalInterfaces !=
- new_options->ExitPolicyRejectLocalInterfaces ||
- old_options->IPv6Exit != new_options->IPv6Exit ||
- !config_lines_eq(old_options->ORPort_lines,
- new_options->ORPort_lines) ||
- !config_lines_eq(old_options->DirPort_lines,
- new_options->DirPort_lines) ||
- old_options->ClientOnly != new_options->ClientOnly ||
- old_options->DisableNetwork != new_options->DisableNetwork ||
- old_options->PublishServerDescriptor_ !=
- new_options->PublishServerDescriptor_ ||
- get_effective_bwrate(old_options) != get_effective_bwrate(new_options) ||
+
+ YES_IF_CHANGED_STRING(DataDirectory);
+ YES_IF_CHANGED_STRING(Nickname);
+ YES_IF_CHANGED_STRING(Address);
+ YES_IF_CHANGED_LINELIST(ExitPolicy);
+ YES_IF_CHANGED_BOOL(ExitRelay);
+ YES_IF_CHANGED_BOOL(ExitPolicyRejectPrivate);
+ YES_IF_CHANGED_BOOL(ExitPolicyRejectLocalInterfaces);
+ YES_IF_CHANGED_BOOL(IPv6Exit);
+ YES_IF_CHANGED_LINELIST(ORPort_lines);
+ YES_IF_CHANGED_LINELIST(DirPort_lines);
+ YES_IF_CHANGED_LINELIST(DirPort_lines);
+ YES_IF_CHANGED_BOOL(ClientOnly);
+ YES_IF_CHANGED_BOOL(DisableNetwork);
+ YES_IF_CHANGED_BOOL(PublishServerDescriptor_);
+ YES_IF_CHANGED_STRING(ContactInfo);
+ YES_IF_CHANGED_STRING(BridgeDistribution);
+ YES_IF_CHANGED_LINELIST(MyFamily);
+ YES_IF_CHANGED_STRING(AccountingStart);
+ YES_IF_CHANGED_INT(AccountingMax);
+ YES_IF_CHANGED_INT(AccountingRule);
+ YES_IF_CHANGED_BOOL(DirCache);
+ YES_IF_CHANGED_BOOL(AssumeReachable);
+
+ if (get_effective_bwrate(old_options) != get_effective_bwrate(new_options) ||
get_effective_bwburst(old_options) !=
get_effective_bwburst(new_options) ||
- !opt_streq(old_options->ContactInfo, new_options->ContactInfo) ||
- !opt_streq(old_options->BridgeDistribution,
- new_options->BridgeDistribution) ||
- !config_lines_eq(old_options->MyFamily, new_options->MyFamily) ||
- !opt_streq(old_options->AccountingStart, new_options->AccountingStart) ||
- old_options->AccountingMax != new_options->AccountingMax ||
- old_options->AccountingRule != new_options->AccountingRule ||
- public_server_mode(old_options) != public_server_mode(new_options) ||
- old_options->DirCache != new_options->DirCache ||
- old_options->AssumeReachable != new_options->AssumeReachable)
+ public_server_mode(old_options) != public_server_mode(new_options))
return 1;
return 0;
@@ -5036,7 +5121,8 @@ load_torrc_from_disk(config_line_t *cmd_arg, int defaults_file)
/** Read a configuration file into <b>options</b>, finding the configuration
* file location based on the command line. After loading the file
* call options_init_from_string() to load the config.
- * Return 0 if success, -1 if failure. */
+ * Return 0 if success, -1 if failure, and 1 if we succeeded but should exit
+ * anyway. */
int
options_init_from_torrc(int argc, char **argv)
{
@@ -5063,22 +5149,22 @@ options_init_from_torrc(int argc, char **argv)
if (config_line_find(cmdline_only_options, "-h") ||
config_line_find(cmdline_only_options, "--help")) {
print_usage();
- exit(0);
+ return 1;
}
if (config_line_find(cmdline_only_options, "--list-torrc-options")) {
/* For validating whether we've documented everything. */
list_torrc_options();
- exit(0);
+ return 1;
}
if (config_line_find(cmdline_only_options, "--list-deprecated-options")) {
/* For validating whether what we have deprecated really exists. */
list_deprecated_options();
- exit(0);
+ return 1;
}
if (config_line_find(cmdline_only_options, "--version")) {
printf("Tor version %s.\n",get_version());
- exit(0);
+ return 1;
}
if (config_line_find(cmdline_only_options, "--library-versions")) {
@@ -5106,7 +5192,7 @@ options_init_from_torrc(int argc, char **argv)
tor_compress_header_version_str(ZSTD_METHOD));
}
//TODO: Hex versions?
- exit(0);
+ return 1;
}
command = CMD_RUN_TOR;
@@ -5167,7 +5253,8 @@ options_init_from_torrc(int argc, char **argv)
get_options_mutable()->keygen_force_passphrase = FORCE_PASSPHRASE_OFF;
} else {
log_err(LD_CONFIG, "--no-passphrase specified without --keygen!");
- exit(1);
+ retval = -1;
+ goto err;
}
}
@@ -5176,7 +5263,8 @@ options_init_from_torrc(int argc, char **argv)
get_options_mutable()->change_key_passphrase = 1;
} else {
log_err(LD_CONFIG, "--newpass specified without --keygen!");
- exit(1);
+ retval = -1;
+ goto err;
}
}
@@ -5186,17 +5274,20 @@ options_init_from_torrc(int argc, char **argv)
if (fd_line) {
if (get_options()->keygen_force_passphrase == FORCE_PASSPHRASE_OFF) {
log_err(LD_CONFIG, "--no-passphrase specified with --passphrase-fd!");
- exit(1);
+ retval = -1;
+ goto err;
} else if (command != CMD_KEYGEN) {
log_err(LD_CONFIG, "--passphrase-fd specified without --keygen!");
- exit(1);
+ retval = -1;
+ goto err;
} else {
const char *v = fd_line->value;
int ok = 1;
long fd = tor_parse_long(v, 10, 0, INT_MAX, &ok, NULL);
if (fd < 0 || ok == 0) {
log_err(LD_CONFIG, "Invalid --passphrase-fd value %s", escaped(v));
- exit(1);
+ retval = -1;
+ goto err;
}
get_options_mutable()->keygen_passphrase_fd = (int)fd;
get_options_mutable()->use_keygen_passphrase_fd = 1;
@@ -5211,7 +5302,8 @@ options_init_from_torrc(int argc, char **argv)
if (key_line) {
if (command != CMD_KEYGEN) {
log_err(LD_CONFIG, "--master-key without --keygen!");
- exit(1);
+ retval = -1;
+ goto err;
} else {
get_options_mutable()->master_key_fname = tor_strdup(key_line->value);
}
@@ -5259,13 +5351,16 @@ options_init_from_string(const char *cf_defaults, const char *cf,
newoptions->command = command;
newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL;
+ smartlist_t *opened_files = smartlist_new();
for (int i = 0; i < 2; ++i) {
const char *body = i==0 ? cf_defaults : cf;
if (!body)
continue;
+
/* get config lines, assign them */
retval = config_get_lines_include(body, &cl, 1,
- body == cf ? &cf_has_include : NULL);
+ body == cf ? &cf_has_include : NULL,
+ opened_files);
if (retval < 0) {
err = SETOPT_ERR_PARSE;
goto err;
@@ -5294,6 +5389,7 @@ options_init_from_string(const char *cf_defaults, const char *cf,
}
newoptions->IncludeUsed = cf_has_include;
+ newoptions->FilesOpenedByIncludes = opened_files;
/* If this is a testing network configuration, change defaults
* for a list of dependent config options, re-initialize newoptions
@@ -5333,13 +5429,16 @@ options_init_from_string(const char *cf_defaults, const char *cf,
newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL;
/* Assign all options a second time. */
+ opened_files = smartlist_new();
for (int i = 0; i < 2; ++i) {
const char *body = i==0 ? cf_defaults : cf;
if (!body)
continue;
+
/* get config lines, assign them */
retval = config_get_lines_include(body, &cl, 1,
- body == cf ? &cf_has_include : NULL);
+ body == cf ? &cf_has_include : NULL,
+ opened_files);
if (retval < 0) {
err = SETOPT_ERR_PARSE;
goto err;
@@ -5364,6 +5463,7 @@ options_init_from_string(const char *cf_defaults, const char *cf,
newoptions->IncludeUsed = cf_has_include;
in_option_validation = 1;
+ newoptions->FilesOpenedByIncludes = opened_files;
/* Validate newoptions */
if (options_validate(oldoptions, newoptions, newdefaultoptions,
@@ -5390,6 +5490,12 @@ options_init_from_string(const char *cf_defaults, const char *cf,
err:
in_option_validation = 0;
+ if (opened_files) {
+ SMARTLIST_FOREACH(opened_files, char *, f, tor_free(f));
+ smartlist_free(opened_files);
+ }
+ // may have been set to opened_files, avoid double free
+ newoptions->FilesOpenedByIncludes = NULL;
or_options_free(newoptions);
or_options_free(newdefaultoptions);
if (*msg) {
@@ -5585,16 +5691,29 @@ options_init_logs(const or_options_t *old_options, or_options_t *options,
}
goto cleanup;
}
- if (smartlist_len(elts) == 1 &&
- !strcasecmp(smartlist_get(elts,0), "syslog")) {
+ if (smartlist_len(elts) == 1) {
+ if (!strcasecmp(smartlist_get(elts,0), "syslog")) {
#ifdef HAVE_SYSLOG_H
- if (!validate_only) {
- add_syslog_log(severity, options->SyslogIdentityTag);
- }
+ if (!validate_only) {
+ add_syslog_log(severity, options->SyslogIdentityTag);
+ }
#else
- log_warn(LD_CONFIG, "Syslog is not supported on this system. Sorry.");
+ log_warn(LD_CONFIG, "Syslog is not supported on this system. Sorry.");
#endif /* defined(HAVE_SYSLOG_H) */
- goto cleanup;
+ goto cleanup;
+ }
+
+ if (!strcasecmp(smartlist_get(elts, 0), "android")) {
+#ifdef HAVE_ANDROID_LOG_H
+ if (!validate_only) {
+ add_android_log(severity, options->AndroidIdentityTag);
+ }
+#else
+ log_warn(LD_CONFIG, "Android logging is not supported"
+ " on this system. Sorry.");
+#endif // HAVE_ANDROID_LOG_H.
+ goto cleanup;
+ }
}
if (smartlist_len(elts) == 2 &&
@@ -5680,7 +5799,7 @@ validate_transport_socks_arguments(const smartlist_t *args)
/** Deallocate a bridge_line_t structure. */
/* private */ void
-bridge_line_free(bridge_line_t *bridge_line)
+bridge_line_free_(bridge_line_t *bridge_line)
{
if (!bridge_line)
return;
@@ -6446,7 +6565,7 @@ port_cfg_new(size_t namelen)
/** Free all storage held in <b>port</b> */
STATIC void
-port_cfg_free(port_cfg_t *port)
+port_cfg_free_(port_cfg_t *port)
{
tor_free(port);
}
@@ -7184,7 +7303,7 @@ parse_ports(or_options_t *options, int validate_only,
*n_ports_out = 0;
- const unsigned gw_flag = options->SocksSocketsGroupWritable ?
+ const unsigned gw_flag = options->UnixSocksGroupWritable ?
CL_PORT_DFLT_GROUP_WRITABLE : 0;
if (parse_port_config(ports,
options->SocksPort_lines,
@@ -7624,60 +7743,81 @@ port_exists_by_type_addr32h_port(int listener_type, uint32_t addr_ipv4h,
check_wildcard);
}
-/** Adjust the value of options->DataDirectory, or fill it in if it's
- * absent. Return 0 on success, -1 on failure. */
-static int
-normalize_data_directory(or_options_t *options)
+/** Allocate and return a good value for the DataDirectory based on
+ * <b>val</b>, which may be NULL. Return NULL on failure. */
+static char *
+get_data_directory(const char *val)
{
#ifdef _WIN32
- char *p;
- if (options->DataDirectory)
- return 0; /* all set */
- p = tor_malloc(MAX_PATH);
- strlcpy(p,get_windows_conf_root(),MAX_PATH);
- options->DataDirectory = p;
- return 0;
+ if (val) {
+ return tor_strdup(val);
+ } else {
+ return tor_strdup(get_windows_conf_root());
+ }
#else /* !(defined(_WIN32)) */
- const char *d = options->DataDirectory;
+ const char *d = val;
if (!d)
d = "~/.tor";
- if (strncmp(d,"~/",2) == 0) {
- char *fn = expand_filename(d);
- if (!fn) {
- log_warn(LD_CONFIG,"Failed to expand filename \"%s\".", d);
- return -1;
- }
- if (!options->DataDirectory && !strcmp(fn,"/.tor")) {
- /* If our homedir is /, we probably don't want to use it. */
- /* Default to LOCALSTATEDIR/tor which is probably closer to what we
- * want. */
- log_warn(LD_CONFIG,
- "Default DataDirectory is \"~/.tor\". This expands to "
- "\"%s\", which is probably not what you want. Using "
- "\"%s"PATH_SEPARATOR"tor\" instead", fn, LOCALSTATEDIR);
- tor_free(fn);
- fn = tor_strdup(LOCALSTATEDIR PATH_SEPARATOR "tor");
- }
- tor_free(options->DataDirectory);
- options->DataDirectory = fn;
- }
- return 0;
+ if (!strcmpstart(d, "~/")) {
+ char *fn = expand_filename(d);
+ if (!fn) {
+ log_warn(LD_CONFIG,"Failed to expand filename \"%s\".", d);
+ return NULL;
+ }
+ if (!val && !strcmp(fn,"/.tor")) {
+ /* If our homedir is /, we probably don't want to use it. */
+ /* Default to LOCALSTATEDIR/tor which is probably closer to what we
+ * want. */
+ log_warn(LD_CONFIG,
+ "Default DataDirectory is \"~/.tor\". This expands to "
+ "\"%s\", which is probably not what you want. Using "
+ "\"%s"PATH_SEPARATOR"tor\" instead", fn, LOCALSTATEDIR);
+ tor_free(fn);
+ fn = tor_strdup(LOCALSTATEDIR PATH_SEPARATOR "tor");
+ }
+ return fn;
+ }
+ return tor_strdup(d);
#endif /* defined(_WIN32) */
}
-/** Check and normalize the value of options->DataDirectory; return 0 if it
- * is sane, -1 otherwise. */
+/** Check and normalize the values of options->{Key,Data,Cache}Directory;
+ * return 0 if it is sane, -1 otherwise. */
static int
-validate_data_directory(or_options_t *options)
+validate_data_directories(or_options_t *options)
{
- if (normalize_data_directory(options) < 0)
+ tor_free(options->DataDirectory);
+ options->DataDirectory = get_data_directory(options->DataDirectory_option);
+ if (!options->DataDirectory)
return -1;
- tor_assert(options->DataDirectory);
if (strlen(options->DataDirectory) > (512-128)) {
log_warn(LD_CONFIG, "DataDirectory is too long.");
return -1;
}
+
+ tor_free(options->KeyDirectory);
+ if (options->KeyDirectory_option) {
+ options->KeyDirectory = get_data_directory(options->KeyDirectory_option);
+ if (!options->KeyDirectory)
+ return -1;
+ } else {
+ /* Default to the data directory's keys subdir */
+ tor_asprintf(&options->KeyDirectory, "%s"PATH_SEPARATOR"keys",
+ options->DataDirectory);
+ }
+
+ tor_free(options->CacheDirectory);
+ if (options->CacheDirectory_option) {
+ options->CacheDirectory = get_data_directory(
+ options->CacheDirectory_option);
+ if (!options->CacheDirectory)
+ return -1;
+ } else {
+ /* Default to the data directory. */
+ options->CacheDirectory = tor_strdup(options->DataDirectory);
+ }
+
return 0;
}
@@ -7818,53 +7958,56 @@ init_libevent(const or_options_t *options)
suppress_libevent_log_msg(NULL);
}
-/** Return a newly allocated string holding a filename relative to the data
- * directory. If <b>sub1</b> is present, it is the first path component after
+/** Return a newly allocated string holding a filename relative to the
+ * directory in <b>options</b> specified by <b>roottype</b>.
+ * If <b>sub1</b> is present, it is the first path component after
* the data directory. If <b>sub2</b> is also present, it is the second path
* component after the data directory. If <b>suffix</b> is present, it
* is appended to the filename.
*
- * Examples:
- * get_datadir_fname2_suffix("a", NULL, NULL) -> $DATADIR/a
- * get_datadir_fname2_suffix("a", NULL, ".tmp") -> $DATADIR/a.tmp
- * get_datadir_fname2_suffix("a", "b", ".tmp") -> $DATADIR/a/b/.tmp
- * get_datadir_fname2_suffix("a", "b", NULL) -> $DATADIR/a/b
- *
- * Note: Consider using the get_datadir_fname* macros in or.h.
+ * Note: Consider using macros in config.h that wrap this function;
+ * you should probably never need to call it as-is.
*/
MOCK_IMPL(char *,
-options_get_datadir_fname2_suffix,(const or_options_t *options,
- const char *sub1, const char *sub2,
- const char *suffix))
+options_get_dir_fname2_suffix,(const or_options_t *options,
+ directory_root_t roottype,
+ const char *sub1, const char *sub2,
+ const char *suffix))
{
- char *fname = NULL;
- size_t len;
tor_assert(options);
- tor_assert(options->DataDirectory);
- tor_assert(sub1 || !sub2); /* If sub2 is present, sub1 must be present. */
- len = strlen(options->DataDirectory);
- if (sub1) {
- len += strlen(sub1)+1;
- if (sub2)
- len += strlen(sub2)+1;
- }
- if (suffix)
- len += strlen(suffix);
- len++;
- fname = tor_malloc(len);
- if (sub1) {
- if (sub2) {
- tor_snprintf(fname, len, "%s"PATH_SEPARATOR"%s"PATH_SEPARATOR"%s",
- options->DataDirectory, sub1, sub2);
- } else {
- tor_snprintf(fname, len, "%s"PATH_SEPARATOR"%s",
- options->DataDirectory, sub1);
- }
+
+ const char *rootdir = NULL;
+ switch (roottype) {
+ case DIRROOT_DATADIR:
+ rootdir = options->DataDirectory;
+ break;
+ case DIRROOT_CACHEDIR:
+ rootdir = options->CacheDirectory;
+ break;
+ case DIRROOT_KEYDIR:
+ rootdir = options->KeyDirectory;
+ break;
+ default:
+ tor_assert_unreached();
+ break;
+ }
+ tor_assert(rootdir);
+
+ if (!suffix)
+ suffix = "";
+
+ char *fname = NULL;
+
+ if (sub1 == NULL) {
+ tor_asprintf(&fname, "%s%s", rootdir, suffix);
+ tor_assert(!sub2); /* If sub2 is present, sub1 must be present. */
+ } else if (sub2 == NULL) {
+ tor_asprintf(&fname, "%s"PATH_SEPARATOR"%s%s", rootdir, sub1, suffix);
} else {
- strlcpy(fname, options->DataDirectory, len);
+ tor_asprintf(&fname, "%s"PATH_SEPARATOR"%s"PATH_SEPARATOR"%s%s",
+ rootdir, sub1, sub2, suffix);
}
- if (suffix)
- strlcat(fname, suffix, len);
+
return fname;
}
@@ -7905,28 +8048,6 @@ write_to_data_subdir(const char* subdir, const char* fname,
return return_val;
}
-/** Given a file name check to see whether the file exists but has not been
- * modified for a very long time. If so, remove it. */
-void
-remove_file_if_very_old(const char *fname, time_t now)
-{
-#define VERY_OLD_FILE_AGE (28*24*60*60)
- struct stat st;
-
- log_debug(LD_FS, "stat()ing %s", fname);
- if (stat(sandbox_intern_string(fname), &st)==0 &&
- st.st_mtime < now-VERY_OLD_FILE_AGE) {
- char buf[ISO_TIME_LEN+1];
- format_local_iso_time(buf, st.st_mtime);
- log_notice(LD_GENERAL, "Obsolete file %s hasn't been modified since %s. "
- "Removing it.", fname, buf);
- if (unlink(fname) != 0) {
- log_warn(LD_FS, "Failed to unlink %s: %s",
- fname, strerror(errno));
- }
- }
-}
-
/** Return a smartlist of ports that must be forwarded by
* tor-fw-helper. The smartlist contains the ports in a string format
* that is understandable by tor-fw-helper. */
diff --git a/src/or/config.h b/src/or/config.h
index efdd8c59b0..1d3c27217e 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -22,6 +22,13 @@
* expose more information than we're comfortable with. */
#define MIN_HEARTBEAT_PERIOD (30*60)
+/** Maximum default value for MaxMemInQueues, in bytes. */
+#if SIZEOF_VOID_P >= 8
+#define MAX_DEFAULT_MEMORY_QUEUE_SIZE (U64_LITERAL(8) << 30)
+#else
+#define MAX_DEFAULT_MEMORY_QUEUE_SIZE (U64_LITERAL(2) << 30)
+#endif
+
MOCK_DECL(const char*, get_dirportfrontpage, (void));
MOCK_DECL(const or_options_t *, get_options, (void));
MOCK_DECL(or_options_t *, get_options_mutable, (void));
@@ -31,6 +38,7 @@ const char *safe_str_client(const char *address);
const char *safe_str(const char *address);
const char *escaped_safe_str_client(const char *address);
const char *escaped_safe_str(const char *address);
+void init_protocol_warning_severity_level(void);
int get_protocol_warning_severity_level(void);
const char *get_version(void);
const char *get_short_version(void);
@@ -58,31 +66,77 @@ config_line_t *option_get_assignment(const or_options_t *options,
const char *key);
int options_save_current(void);
const char *get_torrc_fname(int defaults_fname);
+typedef enum {
+ DIRROOT_DATADIR,
+ DIRROOT_CACHEDIR,
+ DIRROOT_KEYDIR
+} directory_root_t;
+
MOCK_DECL(char *,
- options_get_datadir_fname2_suffix,
+ options_get_dir_fname2_suffix,
(const or_options_t *options,
+ directory_root_t roottype,
const char *sub1, const char *sub2,
const char *suffix));
+
+/* These macros wrap options_get_dir_fname2_suffix to provide a more
+ * convenient API for finding filenames that Tor uses inside its storage
+ * They are named according to a pattern:
+ * (options_)?get_(cache|key|data)dir_fname(2)?(_suffix)?
+ *
+ * Macros that begin with options_ take an options argument; the others
+ * work with respect to the global options.
+ *
+ * Each macro works relative to the data directory, the key directory,
+ * or the cache directory, as determined by which one is mentioned.
+ *
+ * Macro variants with "2" in their name take two path components; others
+ * take one.
+ *
+ * Macro variants with "_suffix" at the end take an additional suffix
+ * that gets appended to the end of the file
+ */
+#define options_get_datadir_fname2_suffix(options, sub1, sub2, suffix) \
+ options_get_dir_fname2_suffix((options), DIRROOT_DATADIR, \
+ (sub1), (sub2), (suffix))
+#define options_get_cachedir_fname2_suffix(options, sub1, sub2, suffix) \
+ options_get_dir_fname2_suffix((options), DIRROOT_CACHEDIR, \
+ (sub1), (sub2), (suffix))
+#define options_get_keydir_fname2_suffix(options, sub1, sub2, suffix) \
+ options_get_dir_fname2_suffix((options), DIRROOT_KEYDIR, \
+ (sub1), (sub2), (suffix))
+
+#define options_get_datadir_fname(opts,sub1) \
+ options_get_datadir_fname2_suffix((opts),(sub1), NULL, NULL)
+#define options_get_datadir_fname2(opts,sub1,sub2) \
+ options_get_datadir_fname2_suffix((opts),(sub1), (sub2), NULL)
+
#define get_datadir_fname2_suffix(sub1, sub2, suffix) \
options_get_datadir_fname2_suffix(get_options(), (sub1), (sub2), (suffix))
-/** Return a newly allocated string containing datadir/sub1. See
- * get_datadir_fname2_suffix. */
-#define get_datadir_fname(sub1) get_datadir_fname2_suffix((sub1), NULL, NULL)
-/** Return a newly allocated string containing datadir/sub1/sub2. See
- * get_datadir_fname2_suffix. */
+#define get_datadir_fname(sub1) \
+ get_datadir_fname2_suffix((sub1), NULL, NULL)
#define get_datadir_fname2(sub1,sub2) \
get_datadir_fname2_suffix((sub1), (sub2), NULL)
-/** Return a newly allocated string containing datadir/sub1/sub2 relative to
- * opts. See get_datadir_fname2_suffix. */
-#define options_get_datadir_fname2(opts,sub1,sub2) \
- options_get_datadir_fname2_suffix((opts),(sub1), (sub2), NULL)
-/** Return a newly allocated string containing datadir/sub1suffix. See
- * get_datadir_fname2_suffix. */
#define get_datadir_fname_suffix(sub1, suffix) \
get_datadir_fname2_suffix((sub1), NULL, (suffix))
+/** DOCDOC */
+#define options_get_keydir_fname(options, sub1) \
+ options_get_keydir_fname2_suffix((options), (sub1), NULL, NULL)
+#define get_keydir_fname_suffix(sub1, suffix) \
+ options_get_keydir_fname2_suffix(get_options(), (sub1), NULL, suffix)
+#define get_keydir_fname(sub1) \
+ options_get_keydir_fname2_suffix(get_options(), (sub1), NULL, NULL)
+
+#define get_cachedir_fname(sub1) \
+ options_get_cachedir_fname2_suffix(get_options(), (sub1), NULL, NULL)
+#define get_cachedir_fname_suffix(sub1, suffix) \
+ options_get_cachedir_fname2_suffix(get_options(), (sub1), NULL, (suffix))
+
int using_default_dir_authorities(const or_options_t *options);
+int create_keys_directory(const or_options_t *options);
+
int check_or_create_data_subdir(const char *subdir);
int write_to_data_subdir(const char* subdir, const char* fname,
const char* str, const char* descr);
@@ -152,7 +206,9 @@ typedef struct bridge_line_t {
transport proxy. */
} bridge_line_t;
-void bridge_line_free(bridge_line_t *bridge_line);
+void bridge_line_free_(bridge_line_t *bridge_line);
+#define bridge_line_free(line) \
+ FREE_AND_NULL(bridge_line_t, bridge_line_free_, (line))
bridge_line_t *parse_bridge_line(const char *line);
smartlist_t *get_options_from_transport_options_line(const char *line,
const char *transport);
@@ -175,8 +231,12 @@ extern struct config_format_t options_format;
#endif
STATIC port_cfg_t *port_cfg_new(size_t namelen);
-STATIC void port_cfg_free(port_cfg_t *port);
-STATIC void or_options_free(or_options_t *options);
+#define port_cfg_free(port) \
+ FREE_AND_NULL(port_cfg_t, port_cfg_free_, (port))
+STATIC void port_cfg_free_(port_cfg_t *port);
+#define or_options_free(opt) \
+ FREE_AND_NULL(or_options_t, or_options_free_, (opt))
+STATIC void or_options_free_(or_options_t *options);
STATIC int options_validate_single_onion(or_options_t *options,
char **msg);
STATIC int options_validate(or_options_t *old_options,
@@ -205,6 +265,10 @@ STATIC int parse_port_config(smartlist_t *out,
const unsigned flags);
STATIC int check_bridge_distribution_setting(const char *bd);
+
+STATIC uint64_t compute_real_max_mem_in_queues(const uint64_t val,
+ int log_guess);
+
#endif /* defined(CONFIG_PRIVATE) */
#endif /* !defined(TOR_CONFIG_H) */
diff --git a/src/or/confparse.c b/src/or/confparse.c
index abae7e33dc..64ed9ee6bb 100644
--- a/src/or/confparse.c
+++ b/src/or/confparse.c
@@ -863,7 +863,7 @@ config_reset(const config_format_t *fmt, void *options,
/** Release storage held by <b>options</b>. */
void
-config_free(const config_format_t *fmt, void *options)
+config_free_(const config_format_t *fmt, void *options)
{
int i;
diff --git a/src/or/confparse.h b/src/or/confparse.h
index 6f0b3b325c..f1f2030343 100644
--- a/src/or/confparse.h
+++ b/src/or/confparse.h
@@ -68,7 +68,7 @@ typedef union {
config_line_t **LINELIST_V;
routerset_t **ROUTERSET;
} confparse_dummy_values_t;
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
/** An abbreviation for a configuration option allowed on the command line. */
typedef struct config_abbrev_t {
@@ -132,13 +132,13 @@ typedef struct config_var_t {
{ NULL, CONFIG_TYPE_OBSOLETE, 0, NULL, { .INT=NULL } }
#define DUMMY_TYPECHECK_INSTANCE(tp) \
static tp tp ## _dummy
-#else
+#else /* !(defined(TOR_UNIT_TESTS)) */
#define CONF_TEST_MEMBERS(tp, conftype, member)
#define END_OF_CONFIG_VARS { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
/* Repeatedly declarable incomplete struct to absorb redundant semicolons */
#define DUMMY_TYPECHECK_INSTANCE(tp) \
struct tor_semicolon_eater
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
/** Type of a callback to validate whether a given configuration is
* well-formed and consistent. See options_trial_assign() for documentation
@@ -177,7 +177,12 @@ typedef struct config_format_t {
#define CAL_WARN_DEPRECATIONS (1u<<2)
void *config_new(const config_format_t *fmt);
-void config_free(const config_format_t *fmt, void *options);
+void config_free_(const config_format_t *fmt, void *options);
+#define config_free(fmt, options) do { \
+ config_free_((fmt), (options)); \
+ (options) = NULL; \
+ } while (0)
+
config_line_t *config_get_assigned_option(const config_format_t *fmt,
const void *options, const char *key,
int escape_val);
@@ -203,5 +208,13 @@ const char *config_expand_abbrev(const config_format_t *fmt,
int command_line, int warn_obsolete);
void warn_deprecated_option(const char *what, const char *why);
+/* Helper macros to compare an option across two configuration objects */
+#define CFG_EQ_BOOL(a,b,opt) ((a)->opt == (b)->opt)
+#define CFG_EQ_INT(a,b,opt) ((a)->opt == (b)->opt)
+#define CFG_EQ_STRING(a,b,opt) (!strcmp_opt((a)->opt, (b)->opt))
+#define CFG_EQ_SMARTLIST(a,b,opt) smartlist_strings_eq((a)->opt, (b)->opt)
+#define CFG_EQ_LINELIST(a,b,opt) config_lines_eq((a)->opt, (b)->opt)
+#define CFG_EQ_ROUTERSET(a,b,opt) routerset_equal((a)->opt, (b)->opt)
+
#endif /* !defined(TOR_CONFPARSE_H) */
diff --git a/src/or/connection.c b/src/or/connection.c
index ed8de05d78..2a6b10763e 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2001 Matej Pfajfar.
+ /* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2017, The Tor Project, Inc. */
@@ -119,8 +119,6 @@ static connection_t *connection_listener_new(
const port_cfg_t *portcfg);
static void connection_init(time_t now, connection_t *conn, int type,
int socket_family);
-static int connection_init_accepted_conn(connection_t *conn,
- const listener_connection_t *listener);
static int connection_handle_listener_read(connection_t *conn, int new_type);
static int connection_bucket_should_increase(int bucket,
or_connection_t *conn);
@@ -338,8 +336,6 @@ entry_connection_new(int type, int socket_family)
entry_conn->entry_cfg.ipv4_traffic = 1;
else if (socket_family == AF_INET6)
entry_conn->entry_cfg.ipv6_traffic = 1;
- else if (socket_family == AF_UNIX)
- entry_conn->is_socks_socket = 1;
return entry_conn;
}
@@ -502,7 +498,7 @@ conn_listener_type_supports_af_unix(int type)
* if <b>conn</b> is an OR or OP connection.
*/
STATIC void
-connection_free_(connection_t *conn)
+connection_free_minimal(connection_t *conn)
{
void *mem;
size_t memlen;
@@ -678,7 +674,7 @@ connection_free_(connection_t *conn)
/** Make sure <b>conn</b> isn't in any of the global conn lists; then free it.
*/
MOCK_IMPL(void,
-connection_free,(connection_t *conn))
+connection_free_,(connection_t *conn))
{
if (!conn)
return;
@@ -714,7 +710,7 @@ connection_free,(connection_t *conn))
}
connection_unregister_events(conn);
- connection_free_(conn);
+ connection_free_minimal(conn);
}
/**
@@ -1682,11 +1678,15 @@ connection_handle_listener_read(connection_t *conn, int new_type)
}
/** Initialize states for newly accepted connection <b>conn</b>.
+ *
* If conn is an OR, start the TLS handshake.
+ *
* If conn is a transparent AP, get its original destination
* and place it in circuit_wait.
+ *
+ * The <b>listener</b> parameter is only used for AP connections.
*/
-static int
+int
connection_init_accepted_conn(connection_t *conn,
const listener_connection_t *listener)
{
@@ -1766,7 +1766,11 @@ connection_connect_sockaddr,(connection_t *conn,
if (get_options()->DisableNetwork) {
/* We should never even try to connect anyplace if DisableNetwork is set.
- * Warn if we do, and refuse to make the connection. */
+ * Warn if we do, and refuse to make the connection.
+ *
+ * We only check DisableNetwork here, not we_are_hibernating(), since
+ * we'll still try to fulfill client requests sometimes in the latter case
+ * (if it is soft hibernation) */
static ratelim_t disablenet_violated = RATELIM_INIT(30*60);
*socket_error = SOCK_ERRNO(ENETUNREACH);
log_fn_ratelim(&disablenet_violated, LOG_WARN, LD_BUG,
@@ -3771,6 +3775,44 @@ connection_outbuf_too_full(connection_t *conn)
return (conn->outbuf_flushlen > 10*CELL_PAYLOAD_SIZE);
}
+/**
+ * On Windows Vista and Windows 7, tune the send buffer size according to a
+ * hint from the OS.
+ *
+ * This should help fix slow upload rates.
+ */
+static void
+
+update_send_buffer_size(tor_socket_t sock)
+{
+#ifdef _WIN32
+ /* We only do this on Vista and 7, because earlier versions of Windows
+ * don't have the SIO_IDEAL_SEND_BACKLOG_QUERY functionality, and on
+ * later versions it isn't necessary. */
+ static int isVistaOr7 = -1;
+ if (isVistaOr7 == -1) {
+ isVistaOr7 = 0;
+ OSVERSIONINFO osvi = { 0 };
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&osvi);
+ if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion < 2)
+ isVistaOr7 = 1;
+ }
+ if (!isVistaOr7)
+ return;
+ if (get_options()->ConstrainedSockets)
+ return;
+ ULONG isb = 0;
+ DWORD bytesReturned = 0;
+ if (!WSAIoctl(sock, SIO_IDEAL_SEND_BACKLOG_QUERY, NULL, 0,
+ &isb, sizeof(isb), &bytesReturned, NULL, NULL)) {
+ setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (const char*)&isb, sizeof(isb));
+ }
+#else
+ (void) sock;
+#endif
+}
+
/** Try to flush more bytes onto <b>conn</b>-\>s.
*
* This function gets called either from conn_write_callback() in main.c
@@ -3889,6 +3931,9 @@ connection_handle_write_impl(connection_t *conn, int force)
result = buf_flush_to_tls(conn->outbuf, or_conn->tls,
max_to_write, &conn->outbuf_flushlen);
+ if (result >= 0)
+ update_send_buffer_size(conn->s);
+
/* If we just flushed the last bytes, tell the channel on the
* or_conn to check if it needs to geoip_change_dirreq_state() */
/* XXXX move this to flushed_some or finished_flushing -NM */
@@ -3963,6 +4008,7 @@ connection_handle_write_impl(connection_t *conn, int force)
connection_mark_for_close(conn);
return -1;
}
+ update_send_buffer_size(conn->s);
n_written = (size_t) result;
}
@@ -4065,6 +4111,68 @@ connection_flush(connection_t *conn)
return connection_handle_write(conn, 1);
}
+/** Helper for connection_write_to_buf_impl and connection_write_buf_to_buf:
+ *
+ * Return true iff it is okay to queue bytes on <b>conn</b>'s outbuf for
+ * writing.
+ */
+static int
+connection_may_write_to_buf(connection_t *conn)
+{
+ /* if it's marked for close, only allow write if we mean to flush it */
+ if (conn->marked_for_close && !conn->hold_open_until_flushed)
+ return 0;
+
+ return 1;
+}
+
+/** Helper for connection_write_to_buf_impl and connection_write_buf_to_buf:
+ *
+ * Called when an attempt to add bytes on <b>conn</b>'s outbuf has failed;
+ * mark the connection and warn as appropriate.
+ */
+static void
+connection_write_to_buf_failed(connection_t *conn)
+{
+ if (CONN_IS_EDGE(conn)) {
+ /* if it failed, it means we have our package/delivery windows set
+ wrong compared to our max outbuf size. close the whole circuit. */
+ log_warn(LD_NET,
+ "write_to_buf failed. Closing circuit (fd %d).", (int)conn->s);
+ circuit_mark_for_close(circuit_get_by_edge_conn(TO_EDGE_CONN(conn)),
+ END_CIRC_REASON_INTERNAL);
+ } else if (conn->type == CONN_TYPE_OR) {
+ or_connection_t *orconn = TO_OR_CONN(conn);
+ log_warn(LD_NET,
+ "write_to_buf failed on an orconn; notifying of error "
+ "(fd %d)", (int)(conn->s));
+ connection_or_close_for_error(orconn, 0);
+ } else {
+ log_warn(LD_NET,
+ "write_to_buf failed. Closing connection (fd %d).",
+ (int)conn->s);
+ connection_mark_for_close(conn);
+ }
+}
+
+/** Helper for connection_write_to_buf_impl and connection_write_buf_to_buf:
+ *
+ * Called when an attempt to add bytes on <b>conn</b>'s outbuf has succeeded:
+ * record the number of bytes added.
+ */
+static void
+connection_write_to_buf_commit(connection_t *conn, size_t len)
+{
+ /* If we receive optimistic data in the EXIT_CONN_STATE_RESOLVING
+ * state, we don't want to try to write it right away, since
+ * conn->write_event won't be set yet. Otherwise, write data from
+ * this conn as the socket is available. */
+ if (conn->write_event) {
+ connection_start_writing(conn);
+ }
+ conn->outbuf_flushlen += len;
+}
+
/** Append <b>len</b> bytes of <b>string</b> onto <b>conn</b>'s
* outbuf, and ask it to start writing.
*
@@ -4079,58 +4187,52 @@ connection_write_to_buf_impl_,(const char *string, size_t len,
{
/* XXXX This function really needs to return -1 on failure. */
int r;
- size_t old_datalen;
if (!len && !(zlib<0))
return;
- /* if it's marked for close, only allow write if we mean to flush it */
- if (conn->marked_for_close && !conn->hold_open_until_flushed)
+
+ if (!connection_may_write_to_buf(conn))
return;
- old_datalen = buf_datalen(conn->outbuf);
+ size_t written;
+
if (zlib) {
+ size_t old_datalen = buf_datalen(conn->outbuf);
dir_connection_t *dir_conn = TO_DIR_CONN(conn);
int done = zlib < 0;
CONN_LOG_PROTECT(conn, r = buf_add_compress(conn->outbuf,
- dir_conn->compress_state,
- string, len, done));
+ dir_conn->compress_state,
+ string, len, done));
+ written = buf_datalen(conn->outbuf) - old_datalen;
} else {
CONN_LOG_PROTECT(conn, r = buf_add(conn->outbuf, string, len));
+ written = len;
}
if (r < 0) {
- if (CONN_IS_EDGE(conn)) {
- /* if it failed, it means we have our package/delivery windows set
- wrong compared to our max outbuf size. close the whole circuit. */
- log_warn(LD_NET,
- "write_to_buf failed. Closing circuit (fd %d).", (int)conn->s);
- circuit_mark_for_close(circuit_get_by_edge_conn(TO_EDGE_CONN(conn)),
- END_CIRC_REASON_INTERNAL);
- } else if (conn->type == CONN_TYPE_OR) {
- or_connection_t *orconn = TO_OR_CONN(conn);
- log_warn(LD_NET,
- "write_to_buf failed on an orconn; notifying of error "
- "(fd %d)", (int)(conn->s));
- connection_or_close_for_error(orconn, 0);
- } else {
- log_warn(LD_NET,
- "write_to_buf failed. Closing connection (fd %d).",
- (int)conn->s);
- connection_mark_for_close(conn);
- }
+ connection_write_to_buf_failed(conn);
return;
}
+ connection_write_to_buf_commit(conn, written);
+}
- /* If we receive optimistic data in the EXIT_CONN_STATE_RESOLVING
- * state, we don't want to try to write it right away, since
- * conn->write_event won't be set yet. Otherwise, write data from
- * this conn as the socket is available. */
- if (conn->write_event) {
- connection_start_writing(conn);
- }
- if (zlib) {
- conn->outbuf_flushlen += buf_datalen(conn->outbuf) - old_datalen;
- } else {
- conn->outbuf_flushlen += len;
- }
+/**
+ * Add all bytes from <b>buf</b> to <b>conn</b>'s outbuf, draining them
+ * from <b>buf</b>. (If the connection is marked and will soon be closed,
+ * nothing is drained.)
+ */
+void
+connection_buf_add_buf(connection_t *conn, buf_t *buf)
+{
+ tor_assert(conn);
+ tor_assert(buf);
+ size_t len = buf_datalen(buf);
+ if (len == 0)
+ return;
+
+ if (!connection_may_write_to_buf(conn))
+ return;
+
+ buf_move_all(conn->outbuf, buf);
+ connection_write_to_buf_commit(conn, len);
}
#define CONN_GET_ALL_TEMPLATE(var, test) \
@@ -4146,7 +4248,7 @@ connection_write_to_buf_impl_,(const char *string, size_t len,
/* Return a list of connections that aren't close and matches the given type
* and state. The returned list can be empty and must be freed using
- * smartlist_free(). The caller does NOT have owernship of the objects in the
+ * smartlist_free(). The caller does NOT have ownership of the objects in the
* list so it must not free them nor reference them as they can disappear. */
smartlist_t *
connection_list_by_type_state(int type, int state)
@@ -4156,7 +4258,7 @@ connection_list_by_type_state(int type, int state)
/* Return a list of connections that aren't close and matches the given type
* and purpose. The returned list can be empty and must be freed using
- * smartlist_free(). The caller does NOT have owernship of the objects in the
+ * smartlist_free(). The caller does NOT have ownership of the objects in the
* list so it must not free them nor reference them as they can disappear. */
smartlist_t *
connection_list_by_type_purpose(int type, int purpose)
@@ -5193,8 +5295,8 @@ proxy_type_to_string(int proxy_type)
return NULL; /*Unreached*/
}
-/** Call connection_free_() on every connection in our array, and release all
- * storage held by connection.c.
+/** Call connection_free_minimal() on every connection in our array, and
+ * release all storage held by connection.c.
*
* Don't do the checks in connection_free(), because they will
* fail.
@@ -5218,7 +5320,8 @@ connection_free_all(void)
/* Clear out our list of broken connections */
clear_broken_connection_map(0);
- SMARTLIST_FOREACH(conns, connection_t *, conn, connection_free_(conn));
+ SMARTLIST_FOREACH(conns, connection_t *, conn,
+ connection_free_minimal(conn));
if (outgoing_addrs) {
SMARTLIST_FOREACH(outgoing_addrs, tor_addr_t *, addr, tor_free(addr));
diff --git a/src/or/connection.h b/src/or/connection.h
index 4a5bd6971b..6bc5a7cfd0 100644
--- a/src/or/connection.h
+++ b/src/or/connection.h
@@ -26,9 +26,12 @@ entry_connection_t *entry_connection_new(int type, int socket_family);
control_connection_t *control_connection_new(int socket_family);
listener_connection_t *listener_connection_new(int type, int socket_family);
connection_t *connection_new(int type, int socket_family);
-
+int connection_init_accepted_conn(connection_t *conn,
+ const listener_connection_t *listener);
void connection_link_connections(connection_t *conn_a, connection_t *conn_b);
-MOCK_DECL(void,connection_free,(connection_t *conn));
+MOCK_DECL(void,connection_free_,(connection_t *conn));
+#define connection_free(conn) \
+ FREE_AND_NULL(connection_t, connection_free_, (conn))
void connection_free_all(void);
void connection_about_to_close_connection(connection_t *conn);
void connection_close_immediate(connection_t *conn);
@@ -155,6 +158,7 @@ connection_buf_add_compress(const char *string, size_t len,
{
connection_write_to_buf_impl_(string, len, TO_CONN(conn), done ? -1 : 1);
}
+void connection_buf_add_buf(connection_t *conn, buf_t *buf);
/* DOCDOC connection_get_inbuf_len */
static size_t connection_get_inbuf_len(connection_t *conn);
@@ -243,7 +247,6 @@ char *alloc_http_authenticator(const char *authenticator);
void assert_connection_ok(connection_t *conn, time_t now);
int connection_or_nonopen_was_started_here(or_connection_t *conn);
void connection_dump_buffer_mem_stats(int severity);
-void remove_file_if_very_old(const char *fname, time_t now);
void clock_skew_warning(const connection_t *conn, long apparent_skew,
int trusted, log_domain_mask_t domain,
@@ -266,7 +269,7 @@ connection_is_moribund(connection_t *conn)
void connection_check_oos(int n_socks, int failed);
#ifdef CONNECTION_PRIVATE
-STATIC void connection_free_(connection_t *conn);
+STATIC void connection_free_minimal(connection_t *conn);
/* Used only by connection.c and test*.c */
uint32_t bucket_millis_empty(int tokens_before, uint32_t last_empty_time,
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 07c6ac88b4..003f84adb3 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -339,6 +339,10 @@ relay_send_end_cell_from_edge(streamid_t stream_id, circuit_t *circ,
payload[0] = (char) reason;
+ /* Note: we have to use relay_send_command_from_edge here, not
+ * connection_edge_end or connection_edge_send_command, since those require
+ * that we have a stream connected to a circuit, and we don't connect to a
+ * circuit until we have a pending/successful resolve. */
return relay_send_command_from_edge(stream_id, circ, RELAY_COMMAND_END,
payload, 1, cpath_layer);
}
@@ -788,7 +792,10 @@ connection_ap_expire_beginning(void)
}
continue;
}
+
if (circ->purpose != CIRCUIT_PURPOSE_C_GENERAL &&
+ circ->purpose != CIRCUIT_PURPOSE_C_HSDIR_GET &&
+ circ->purpose != CIRCUIT_PURPOSE_S_HSDIR_POST &&
circ->purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT &&
circ->purpose != CIRCUIT_PURPOSE_PATH_BIAS_TESTING) {
log_warn(LD_BUG, "circuit->purpose == CIRCUIT_PURPOSE_C_GENERAL failed. "
@@ -999,7 +1006,7 @@ connection_ap_mark_as_pending_circuit_(entry_connection_t *entry_conn,
* So the fix is to tell it right now that it ought to finish its loop at
* its next available opportunity.
*/
- tell_event_loop_to_finish();
+ tell_event_loop_to_run_external_code();
}
/** Mark <b>entry_conn</b> as no longer waiting for a circuit. */
@@ -2588,6 +2595,8 @@ connection_ap_supports_optimistic_data(const entry_connection_t *conn)
if (edge_conn->on_circuit == NULL ||
edge_conn->on_circuit->state != CIRCUIT_STATE_OPEN ||
(edge_conn->on_circuit->purpose != CIRCUIT_PURPOSE_C_GENERAL &&
+ edge_conn->on_circuit->purpose != CIRCUIT_PURPOSE_C_HSDIR_GET &&
+ edge_conn->on_circuit->purpose != CIRCUIT_PURPOSE_S_HSDIR_POST &&
edge_conn->on_circuit->purpose != CIRCUIT_PURPOSE_C_REND_JOINED))
return 0;
@@ -3327,7 +3336,7 @@ handle_hs_exit_conn(circuit_t *circ, edge_connection_t *conn)
relay_send_end_cell_from_edge(conn->stream_id, circ,
END_STREAM_REASON_DONE,
origin_circ->cpath->prev);
- connection_free(TO_CONN(conn));
+ connection_free_(TO_CONN(conn));
/* Drop the circuit here since it might be someone deliberately
* scanning the hidden service ports. Note that this mitigates port
@@ -3405,11 +3414,6 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
if (rh.length > RELAY_PAYLOAD_SIZE)
return -END_CIRC_REASON_TORPROTOCOL;
- /* Note: we have to use relay_send_command_from_edge here, not
- * connection_edge_end or connection_edge_send_command, since those require
- * that we have a stream connected to a circuit, and we don't connect to a
- * circuit until we have a pending/successful resolve. */
-
if (!server_mode(options) &&
circ->purpose != CIRCUIT_PURPOSE_S_REND_JOINED) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
@@ -3524,7 +3528,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
if (we_are_hibernating()) {
relay_send_end_cell_from_edge(rh.stream_id, circ,
END_STREAM_REASON_HIBERNATING, NULL);
- connection_free(TO_CONN(n_stream));
+ connection_free_(TO_CONN(n_stream));
return 0;
}
@@ -3602,7 +3606,7 @@ connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ)
return 0;
case 1: /* The result was cached; a resolved cell was sent. */
if (!dummy_conn->base_.marked_for_close)
- connection_free(TO_CONN(dummy_conn));
+ connection_free_(TO_CONN(dummy_conn));
return 0;
case 0: /* resolve added to pending list */
assert_circuit_ok(TO_CIRCUIT(circ));
@@ -3775,8 +3779,8 @@ connection_exit_connect_dir(edge_connection_t *exitconn)
if (connection_add(TO_CONN(exitconn))<0) {
connection_edge_end(exitconn, END_STREAM_REASON_RESOURCELIMIT);
- connection_free(TO_CONN(exitconn));
- connection_free(TO_CONN(dirconn));
+ connection_free_(TO_CONN(exitconn));
+ connection_free_(TO_CONN(dirconn));
return 0;
}
@@ -3788,7 +3792,7 @@ connection_exit_connect_dir(edge_connection_t *exitconn)
connection_edge_end(exitconn, END_STREAM_REASON_RESOURCELIMIT);
connection_close_immediate(TO_CONN(exitconn));
connection_mark_for_close(TO_CONN(exitconn));
- connection_free(TO_CONN(dirconn));
+ connection_free_(TO_CONN(dirconn));
return 0;
}
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index fe2cea4f46..221b8dc8ad 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -28,6 +28,7 @@
* part of a subclass (channel_tls_t).
*/
#define TOR_CHANNEL_INTERNAL_
+#define CONNECTION_OR_PRIVATE
#include "channel.h"
#include "channeltls.h"
#include "circuitbuild.h"
@@ -506,7 +507,7 @@ var_cell_copy(const var_cell_t *src)
/** Release all space held by <b>cell</b>. */
void
-var_cell_free(var_cell_t *cell)
+var_cell_free_(var_cell_t *cell)
{
tor_free(cell);
}
@@ -593,8 +594,9 @@ connection_or_flushed_some(or_connection_t *conn)
{
size_t datalen;
- /* The channel will want to update its estimated queue size */
- channel_update_xmit_queue_size(TLS_CHAN_TO_BASE(conn->chan));
+ /* Update the channel's active timestamp if there is one */
+ if (conn->chan)
+ channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan));
/* If we're under the low water mark, add cells until we're just over the
* high water mark. */
@@ -656,6 +658,11 @@ connection_or_finished_flushing(or_connection_t *conn)
tor_fragile_assert();
return -1;
}
+
+ /* Update the channel's active timestamp if there is one */
+ if (conn->chan)
+ channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan));
+
return 0;
}
@@ -700,7 +707,6 @@ connection_or_finished_connecting(or_connection_t *or_conn)
void
connection_or_about_to_close(or_connection_t *or_conn)
{
- time_t now = time(NULL);
connection_t *conn = TO_CONN(or_conn);
/* Tell the controlling channel we're closed */
@@ -720,7 +726,6 @@ connection_or_about_to_close(or_connection_t *or_conn)
if (connection_or_nonopen_was_started_here(or_conn)) {
const or_options_t *options = get_options();
connection_or_note_state_when_broken(or_conn);
- rep_hist_note_connect_failed(or_conn->identity_digest, now);
/* Tell the new guard API about the channel failure */
entry_guard_chan_failed(TLS_CHAN_TO_BASE(or_conn->chan));
if (conn->state >= OR_CONN_STATE_TLS_HANDSHAKING) {
@@ -736,11 +741,9 @@ connection_or_about_to_close(or_connection_t *or_conn)
} else if (conn->hold_open_until_flushed) {
/* We only set hold_open_until_flushed when we're intentionally
* closing a connection. */
- rep_hist_note_disconnect(or_conn->identity_digest, now);
control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED,
tls_error_to_orconn_end_reason(or_conn->tls_error));
} else if (!tor_digest_is_zero(or_conn->identity_digest)) {
- rep_hist_note_connection_died(or_conn->identity_digest, now);
control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED,
tls_error_to_orconn_end_reason(or_conn->tls_error));
}
@@ -887,7 +890,7 @@ connection_or_check_canonicity(or_connection_t *conn, int started_here)
const node_t *r = node_get_by_id(id_digest);
if (r &&
- node_supports_ed25519_link_authentication(r) &&
+ node_supports_ed25519_link_authentication(r, 1) &&
! node_ed25519_id_matches(r, ed_id)) {
/* If this node is capable of proving an ed25519 ID,
* we can't call this a canonical connection unless both IDs match. */
@@ -966,6 +969,36 @@ connection_or_mark_bad_for_new_circs(or_connection_t *or_conn)
* too old for new circuits? */
#define TIME_BEFORE_OR_CONN_IS_TOO_OLD (60*60*24*7)
+/** Expire an or_connection if it is too old. Helper for
+ * connection_or_group_set_badness_ and fast path for
+ * channel_rsa_id_group_set_badness.
+ *
+ * Returns 1 if the connection was already expired, else 0.
+ */
+int
+connection_or_single_set_badness_(time_t now,
+ or_connection_t *or_conn,
+ int force)
+{
+ /* XXXX this function should also be about channels? */
+ if (or_conn->base_.marked_for_close ||
+ connection_or_is_bad_for_new_circs(or_conn))
+ return 1;
+
+ if (force ||
+ or_conn->base_.timestamp_created + TIME_BEFORE_OR_CONN_IS_TOO_OLD
+ < now) {
+ log_info(LD_OR,
+ "Marking OR conn to %s:%d as too old for new circuits "
+ "(fd "TOR_SOCKET_T_FORMAT", %d secs old).",
+ or_conn->base_.address, or_conn->base_.port, or_conn->base_.s,
+ (int)(now - or_conn->base_.timestamp_created));
+ connection_or_mark_bad_for_new_circs(or_conn);
+ }
+
+ return 0;
+}
+
/** Given a list of all the or_connections with a given
* identity, set elements of that list as is_bad_for_new_circs as
* appropriate. Helper for connection_or_set_bad_connections().
@@ -996,19 +1029,8 @@ connection_or_group_set_badness_(smartlist_t *group, int force)
/* Pass 1: expire everything that's old, and see what the status of
* everything else is. */
SMARTLIST_FOREACH_BEGIN(group, or_connection_t *, or_conn) {
- if (or_conn->base_.marked_for_close ||
- connection_or_is_bad_for_new_circs(or_conn))
+ if (connection_or_single_set_badness_(now, or_conn, force))
continue;
- if (force ||
- or_conn->base_.timestamp_created + TIME_BEFORE_OR_CONN_IS_TOO_OLD
- < now) {
- log_info(LD_OR,
- "Marking OR conn to %s:%d as too old for new circuits "
- "(fd "TOR_SOCKET_T_FORMAT", %d secs old).",
- or_conn->base_.address, or_conn->base_.port, or_conn->base_.s,
- (int)(now - or_conn->base_.timestamp_created));
- connection_or_mark_bad_for_new_circs(or_conn);
- }
if (connection_or_is_bad_for_new_circs(or_conn)) {
++n_old;
@@ -1102,6 +1124,216 @@ connection_or_group_set_badness_(smartlist_t *group, int force)
} SMARTLIST_FOREACH_END(or_conn);
}
+/* Lifetime of a connection failure. After that, we'll retry. This is in
+ * seconds. */
+#define OR_CONNECT_FAILURE_LIFETIME 60
+/* The interval to use with when to clean up the failure cache. */
+#define OR_CONNECT_FAILURE_CLEANUP_INTERVAL 60
+
+/* When is the next time we have to cleanup the failure map. We keep this
+ * because we clean it opportunistically. */
+static time_t or_connect_failure_map_next_cleanup_ts = 0;
+
+/* OR connection failure entry data structure. It is kept in the connection
+ * failure map defined below and indexed by OR identity digest, address and
+ * port.
+ *
+ * We need to identify a connection failure with these three values because we
+ * want to avoid to wrongfully blacklist a relay if someone is trying to
+ * extend to a known identity digest but with the wrong IP/port. For instance,
+ * it can happen if a relay changed its port but the client still has an old
+ * descriptor with the old port. We want to stop connecting to that
+ * IP/port/identity all together, not only the relay identity. */
+typedef struct or_connect_failure_entry_t {
+ HT_ENTRY(or_connect_failure_entry_t) node;
+ /* Identity digest of the connection where it is connecting to. */
+ uint8_t identity_digest[DIGEST_LEN];
+ /* This is the connection address from the base connection_t. After the
+ * connection is checked for canonicity, the base address should represent
+ * what we know instead of where we are connecting to. This is what we need
+ * so we can correlate known relays within the consensus. */
+ tor_addr_t addr;
+ uint16_t port;
+ /* Last time we were unable to connect. */
+ time_t last_failed_connect_ts;
+} or_connect_failure_entry_t;
+
+/* Map where we keep connection failure entries. They are indexed by addr,
+ * port and identity digest. */
+static HT_HEAD(or_connect_failure_ht, or_connect_failure_entry_t)
+ or_connect_failures_map = HT_INITIALIZER();
+
+/* Helper: Hashtable equal function. Return 1 if equal else 0. */
+static int
+or_connect_failure_ht_eq(const or_connect_failure_entry_t *a,
+ const or_connect_failure_entry_t *b)
+{
+ return fast_memeq(a->identity_digest, b->identity_digest, DIGEST_LEN) &&
+ tor_addr_eq(&a->addr, &b->addr) &&
+ a->port == b->port;
+}
+
+/* Helper: Return the hash for the hashtable of the given entry. For this
+ * table, it is a combination of address, port and identity digest. */
+static unsigned int
+or_connect_failure_ht_hash(const or_connect_failure_entry_t *entry)
+{
+ size_t offset = 0, addr_size;
+ const void *addr_ptr;
+ /* Largest size is IPv6 and IPv4 is smaller so it is fine. */
+ uint8_t data[16 + sizeof(uint16_t) + DIGEST_LEN];
+
+ /* Get the right address bytes depending on the family. */
+ switch (tor_addr_family(&entry->addr)) {
+ case AF_INET:
+ addr_size = 4;
+ addr_ptr = &entry->addr.addr.in_addr.s_addr;
+ break;
+ case AF_INET6:
+ addr_size = 16;
+ addr_ptr = &entry->addr.addr.in6_addr.s6_addr;
+ break;
+ default:
+ tor_assert_nonfatal_unreached();
+ return 0;
+ }
+
+ memcpy(data, addr_ptr, addr_size);
+ offset += addr_size;
+ memcpy(data + offset, entry->identity_digest, DIGEST_LEN);
+ offset += DIGEST_LEN;
+ set_uint16(data + offset, entry->port);
+ offset += sizeof(uint16_t);
+
+ return (unsigned int) siphash24g(data, offset);
+}
+
+HT_PROTOTYPE(or_connect_failure_ht, or_connect_failure_entry_t, node,
+ or_connect_failure_ht_hash, or_connect_failure_ht_eq)
+
+HT_GENERATE2(or_connect_failure_ht, or_connect_failure_entry_t, node,
+ or_connect_failure_ht_hash, or_connect_failure_ht_eq,
+ 0.6, tor_reallocarray_, tor_free_)
+
+/* Initialize a given connect failure entry with the given identity_digest,
+ * addr and port. All field are optional except ocf. */
+static void
+or_connect_failure_init(const char *identity_digest, const tor_addr_t *addr,
+ uint16_t port, or_connect_failure_entry_t *ocf)
+{
+ tor_assert(ocf);
+ if (identity_digest) {
+ memcpy(ocf->identity_digest, identity_digest,
+ sizeof(ocf->identity_digest));
+ }
+ if (addr) {
+ tor_addr_copy(&ocf->addr, addr);
+ }
+ ocf->port = port;
+}
+
+/* Return a newly allocated connection failure entry. It is initialized with
+ * the given or_conn data. This can't fail. */
+static or_connect_failure_entry_t *
+or_connect_failure_new(const or_connection_t *or_conn)
+{
+ or_connect_failure_entry_t *ocf = tor_malloc_zero(sizeof(*ocf));
+ or_connect_failure_init(or_conn->identity_digest, &or_conn->real_addr,
+ TO_CONN(or_conn)->port, ocf);
+ return ocf;
+}
+
+/* Return a connection failure entry matching the given or_conn. NULL is
+ * returned if not found. */
+static or_connect_failure_entry_t *
+or_connect_failure_find(const or_connection_t *or_conn)
+{
+ or_connect_failure_entry_t lookup;
+ tor_assert(or_conn);
+ or_connect_failure_init(or_conn->identity_digest, &TO_CONN(or_conn)->addr,
+ TO_CONN(or_conn)->port, &lookup);
+ return HT_FIND(or_connect_failure_ht, &or_connect_failures_map, &lookup);
+}
+
+/* Note down in the connection failure cache that a failure occurred on the
+ * given or_conn. */
+STATIC void
+note_or_connect_failed(const or_connection_t *or_conn)
+{
+ or_connect_failure_entry_t *ocf = NULL;
+
+ tor_assert(or_conn);
+
+ ocf = or_connect_failure_find(or_conn);
+ if (ocf == NULL) {
+ ocf = or_connect_failure_new(or_conn);
+ HT_INSERT(or_connect_failure_ht, &or_connect_failures_map, ocf);
+ }
+ ocf->last_failed_connect_ts = approx_time();
+}
+
+/* Cleanup the connection failure cache and remove all entries below the
+ * given cutoff. */
+static void
+or_connect_failure_map_cleanup(time_t cutoff)
+{
+ or_connect_failure_entry_t **ptr, **next, *entry;
+
+ for (ptr = HT_START(or_connect_failure_ht, &or_connect_failures_map);
+ ptr != NULL; ptr = next) {
+ entry = *ptr;
+ if (entry->last_failed_connect_ts <= cutoff) {
+ next = HT_NEXT_RMV(or_connect_failure_ht, &or_connect_failures_map, ptr);
+ tor_free(entry);
+ } else {
+ next = HT_NEXT(or_connect_failure_ht, &or_connect_failures_map, ptr);
+ }
+ }
+}
+
+/* Return true iff the given OR connection can connect to its destination that
+ * is the triplet identity_digest, address and port.
+ *
+ * The or_conn MUST have gone through connection_or_check_canonicity() so the
+ * base address is properly set to what we know or doesn't know. */
+STATIC int
+should_connect_to_relay(const or_connection_t *or_conn)
+{
+ time_t now, cutoff;
+ time_t connect_failed_since_ts = 0;
+ or_connect_failure_entry_t *ocf;
+
+ tor_assert(or_conn);
+
+ now = approx_time();
+ cutoff = now - OR_CONNECT_FAILURE_LIFETIME;
+
+ /* Opportunistically try to cleanup the failure cache. We do that at regular
+ * interval so it doesn't grow too big. */
+ if (or_connect_failure_map_next_cleanup_ts <= now) {
+ or_connect_failure_map_cleanup(cutoff);
+ or_connect_failure_map_next_cleanup_ts =
+ now + OR_CONNECT_FAILURE_CLEANUP_INTERVAL;
+ }
+
+ /* Look if we have failed previously to the same destination as this
+ * OR connection. */
+ ocf = or_connect_failure_find(or_conn);
+ if (ocf) {
+ connect_failed_since_ts = ocf->last_failed_connect_ts;
+ }
+ /* If we do have an unable to connect timestamp and it is below cutoff, we
+ * can connect. Or we have never failed before so let it connect. */
+ if (connect_failed_since_ts > cutoff) {
+ goto no_connect;
+ }
+
+ /* Ok we can connect! */
+ return 1;
+ no_connect:
+ return 0;
+}
+
/** <b>conn</b> is in the 'connecting' state, and it failed to complete
* a TCP connection. Send notifications appropriately.
*
@@ -1115,6 +1347,7 @@ connection_or_connect_failed(or_connection_t *conn,
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED, reason);
if (!authdir_mode_tests_reachability(get_options()))
control_event_bootstrap_prob_or(msg, reason, conn);
+ note_or_connect_failed(conn);
}
/** <b>conn</b> got an error in connection_handle_read_impl() or
@@ -1205,6 +1438,19 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port,
conn->chan = chan;
chan->conn = conn;
connection_or_init_conn_from_address(conn, &addr, port, id_digest, ed_id, 1);
+
+ /* We have a proper OR connection setup, now check if we can connect to it
+ * that is we haven't had a failure earlier. This is to avoid to try to
+ * constantly connect to relays that we think are not reachable. */
+ if (!should_connect_to_relay(conn)) {
+ log_info(LD_GENERAL, "Can't connect to identity %s at %s:%u because we "
+ "failed earlier. Refusing.",
+ hex_str(id_digest, DIGEST_LEN), fmt_addr(&TO_CONN(conn)->addr),
+ TO_CONN(conn)->port);
+ connection_free_(TO_CONN(conn));
+ return NULL;
+ }
+
connection_or_change_state(conn, OR_CONN_STATE_CONNECTING);
control_event_or_conn_status(conn, OR_CONN_EVENT_LAUNCHED, 0);
@@ -1248,7 +1494,7 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port,
fmt_addrport(&TO_CONN(conn)->addr, TO_CONN(conn)->port));
}
- connection_free(TO_CONN(conn));
+ connection_free_(TO_CONN(conn));
return NULL;
}
@@ -1261,7 +1507,7 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port,
connection_or_connect_failed(conn,
errno_to_orconn_end_reason(socket_error),
tor_socket_strerror(socket_error));
- connection_free(TO_CONN(conn));
+ connection_free_(TO_CONN(conn));
return NULL;
case 0:
connection_watch_events(TO_CONN(conn), READ_EVENT | WRITE_EVENT);
@@ -1858,7 +2104,7 @@ connection_init_or_handshake_state(or_connection_t *conn, int started_here)
/** Free all storage held by <b>state</b>. */
void
-or_handshake_state_free(or_handshake_state_t *state)
+or_handshake_state_free_(or_handshake_state_t *state)
{
if (!state)
return;
diff --git a/src/or/connection_or.h b/src/or/connection_or.h
index ee66b7c528..158eb1fdad 100644
--- a/src/or/connection_or.h
+++ b/src/or/connection_or.h
@@ -68,7 +68,9 @@ int connection_or_client_learned_peer_id(or_connection_t *conn,
const ed25519_public_key_t *ed_peer_id);
time_t connection_or_client_used(or_connection_t *conn);
MOCK_DECL(int, connection_or_get_num_circuits, (or_connection_t *conn));
-void or_handshake_state_free(or_handshake_state_t *state);
+void or_handshake_state_free_(or_handshake_state_t *state);
+#define or_handshake_state_free(state) \
+ FREE_AND_NULL(or_handshake_state_t, or_handshake_state_free_, (state))
void or_handshake_state_record_cell(or_connection_t *conn,
or_handshake_state_t *state,
const cell_t *cell,
@@ -105,15 +107,24 @@ int var_cell_pack_header(const var_cell_t *cell, char *hdr_out,
int wide_circ_ids);
var_cell_t *var_cell_new(uint16_t payload_len);
var_cell_t *var_cell_copy(const var_cell_t *src);
-void var_cell_free(var_cell_t *cell);
+void var_cell_free_(var_cell_t *cell);
+#define var_cell_free(cell) FREE_AND_NULL(var_cell_t, var_cell_free_, (cell))
/* DOCDOC */
#define MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS 4
#define MIN_LINK_PROTO_FOR_CHANNEL_PADDING 5
#define MAX_LINK_PROTO MIN_LINK_PROTO_FOR_CHANNEL_PADDING
+int connection_or_single_set_badness_(time_t now,
+ or_connection_t *or_conn,
+ int force);
void connection_or_group_set_badness_(smartlist_t *group, int force);
+#ifdef CONNECTION_OR_PRIVATE
+STATIC int should_connect_to_relay(const or_connection_t *or_conn);
+STATIC void note_or_connect_failed(const or_connection_t *or_conn);
+#endif
+
#ifdef TOR_UNIT_TESTS
extern int certs_cell_ed25519_disabled_for_testing;
#endif
diff --git a/src/or/conscache.c b/src/or/conscache.c
index 1a15126f97..e25ac5f40b 100644
--- a/src/or/conscache.c
+++ b/src/or/conscache.c
@@ -79,7 +79,7 @@ consensus_cache_open(const char *subdir, int max_entries)
{
int storagedir_max_entries;
consensus_cache_t *cache = tor_malloc_zero(sizeof(consensus_cache_t));
- char *directory = get_datadir_fname(subdir);
+ char *directory = get_cachedir_fname(subdir);
cache->max_entries = max_entries;
#ifdef MUST_UNMAP_TO_UNLINK
@@ -170,7 +170,7 @@ consensus_cache_clear(consensus_cache_t *cache)
* Drop all storage held by <b>cache</b>.
*/
void
-consensus_cache_free(consensus_cache_t *cache)
+consensus_cache_free_(consensus_cache_t *cache)
{
if (! cache)
return;
diff --git a/src/or/conscache.h b/src/or/conscache.h
index 3c89dedf43..08a5c5a37b 100644
--- a/src/or/conscache.h
+++ b/src/or/conscache.h
@@ -10,9 +10,14 @@ typedef struct consensus_cache_entry_t consensus_cache_entry_t;
typedef struct consensus_cache_t consensus_cache_t;
HANDLE_DECL(consensus_cache_entry, consensus_cache_entry_t, )
+#define consensus_cache_entry_handle_free(h) \
+ FREE_AND_NULL(consensus_cache_entry_handle_t, \
+ consensus_cache_entry_handle_free_, (h))
consensus_cache_t *consensus_cache_open(const char *subdir, int max_entries);
-void consensus_cache_free(consensus_cache_t *cache);
+void consensus_cache_free_(consensus_cache_t *cache);
+#define consensus_cache_free(cache) \
+ FREE_AND_NULL(consensus_cache_t, consensus_cache_free_, (cache))
struct sandbox_cfg_elem;
int consensus_cache_may_overallocate(consensus_cache_t *cache);
int consensus_cache_register_with_sandbox(consensus_cache_t *cache,
diff --git a/src/or/consdiffmgr.c b/src/or/consdiffmgr.c
index 8349f257de..02b905a520 100644
--- a/src/or/consdiffmgr.c
+++ b/src/or/consdiffmgr.c
@@ -207,9 +207,12 @@ HT_PROTOTYPE(cdm_diff_ht, cdm_diff_t, node, cdm_diff_hash, cdm_diff_eq)
HT_GENERATE2(cdm_diff_ht, cdm_diff_t, node, cdm_diff_hash, cdm_diff_eq,
0.6, tor_reallocarray, tor_free_)
+#define cdm_diff_free(diff) \
+ FREE_AND_NULL(cdm_diff_t, cdm_diff_free_, (diff))
+
/** Release all storage held in <b>diff</b>. */
static void
-cdm_diff_free(cdm_diff_t *diff)
+cdm_diff_free_(cdm_diff_t *diff)
{
if (!diff)
return;
@@ -1511,11 +1514,15 @@ consensus_diff_worker_threadfn(void *state_, void *work_)
return WQ_RPL_REPLY;
}
+#define consensus_diff_worker_job_free(job) \
+ FREE_AND_NULL(consensus_diff_worker_job_t, \
+ consensus_diff_worker_job_free_, (job))
+
/**
* Helper: release all storage held in <b>job</b>.
*/
static void
-consensus_diff_worker_job_free(consensus_diff_worker_job_t *job)
+consensus_diff_worker_job_free_(consensus_diff_worker_job_t *job)
{
if (!job)
return;
@@ -1663,11 +1670,15 @@ typedef struct consensus_compress_worker_job_t {
compressed_result_t out[ARRAY_LENGTH(compress_consensus_with)];
} consensus_compress_worker_job_t;
+#define consensus_compress_worker_job_free(job) \
+ FREE_AND_NULL(consensus_compress_worker_job_t, \
+ consensus_compress_worker_job_free_, (job))
+
/**
* Free all resources held in <b>job</b>
*/
static void
-consensus_compress_worker_job_free(consensus_compress_worker_job_t *job)
+consensus_compress_worker_job_free_(consensus_compress_worker_job_t *job)
{
if (!job)
return;
diff --git a/src/or/control.c b/src/or/control.c
index 3609210b95..3f677ba348 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -58,7 +58,9 @@
#include "entrynodes.h"
#include "geoip.h"
#include "hibernate.h"
+#include "hs_cache.h"
#include "hs_common.h"
+#include "hs_control.h"
#include "main.h"
#include "microdesc.h"
#include "networkstatus.h"
@@ -549,6 +551,49 @@ decode_escaped_string(const char *start, size_t in_len_max,
return end+1;
}
+/** Create and add a new controller connection on <b>sock</b>. If
+ * <b>CC_LOCAL_FD_IS_OWNER</b> is set in <b>flags</b>, this Tor process should
+ * exit when the connection closes. If <b>CC_LOCAL_FD_IS_AUTHENTICATED</b>
+ * is set, then the connection does not need to authenticate.
+ */
+int
+control_connection_add_local_fd(tor_socket_t sock, unsigned flags)
+{
+ if (BUG(! SOCKET_OK(sock)))
+ return -1;
+ const int is_owner = !!(flags & CC_LOCAL_FD_IS_OWNER);
+ const int is_authenticated = !!(flags & CC_LOCAL_FD_IS_AUTHENTICATED);
+ control_connection_t *control_conn = control_connection_new(AF_UNSPEC);
+ connection_t *conn = TO_CONN(control_conn);
+ conn->s = sock;
+ tor_addr_make_unspec(&conn->addr);
+ conn->port = 1;
+ conn->address = tor_strdup("<local socket>");
+
+ /* We take ownership of this socket so that later, when we close it,
+ * we don't freak out. */
+ tor_take_socket_ownership(sock);
+
+ if (set_socket_nonblocking(sock) < 0 ||
+ connection_add(conn) < 0) {
+ connection_free(conn);
+ return -1;
+ }
+
+ control_conn->is_owning_control_connection = is_owner;
+
+ if (connection_init_accepted_conn(conn, NULL) < 0) {
+ connection_mark_for_close(conn);
+ return -1;
+ }
+
+ if (is_authenticated) {
+ conn->state = CONTROL_CONN_STATE_OPEN;
+ }
+
+ return 0;
+}
+
/** Acts like sprintf, but writes its formatted string to the end of
* <b>conn</b>-\>outbuf. */
static void
@@ -740,9 +785,12 @@ queue_control_event_string,(uint16_t event, char *msg))
}
}
+#define queued_event_free(ev) \
+ FREE_AND_NULL(queued_event_t, queued_event_free_, (ev))
+
/** Release all storage held by <b>ev</b>. */
static void
-queued_event_free(queued_event_t *ev)
+queued_event_free_(queued_event_t *ev)
{
if (ev == NULL)
return;
@@ -1975,36 +2023,89 @@ getinfo_helper_dir(control_connection_t *control_conn,
SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
smartlist_free(sl);
} else if (!strcmpstart(question, "hs/client/desc/id/")) {
- rend_cache_entry_t *e = NULL;
+ hostname_type_t addr_type;
question += strlen("hs/client/desc/id/");
- if (strlen(question) != REND_SERVICE_ID_LEN_BASE32) {
+ if (rend_valid_v2_service_id(question)) {
+ addr_type = ONION_V2_HOSTNAME;
+ } else if (hs_address_is_valid(question)) {
+ addr_type = ONION_V3_HOSTNAME;
+ } else {
*errmsg = "Invalid address";
return -1;
}
- if (!rend_cache_lookup_entry(question, -1, &e)) {
- /* Descriptor found in cache */
- *answer = tor_strdup(e->desc);
+ if (addr_type == ONION_V2_HOSTNAME) {
+ rend_cache_entry_t *e = NULL;
+ if (!rend_cache_lookup_entry(question, -1, &e)) {
+ /* Descriptor found in cache */
+ *answer = tor_strdup(e->desc);
+ } else {
+ *errmsg = "Not found in cache";
+ return -1;
+ }
} else {
- *errmsg = "Not found in cache";
- return -1;
+ ed25519_public_key_t service_pk;
+ const char *desc;
+
+ /* The check before this if/else makes sure of this. */
+ tor_assert(addr_type == ONION_V3_HOSTNAME);
+
+ if (hs_parse_address(question, &service_pk, NULL, NULL) < 0) {
+ *errmsg = "Invalid v3 address";
+ return -1;
+ }
+
+ desc = hs_cache_lookup_encoded_as_client(&service_pk);
+ if (desc) {
+ *answer = tor_strdup(desc);
+ } else {
+ *errmsg = "Not found in cache";
+ return -1;
+ }
}
} else if (!strcmpstart(question, "hs/service/desc/id/")) {
- rend_cache_entry_t *e = NULL;
+ hostname_type_t addr_type;
question += strlen("hs/service/desc/id/");
- if (strlen(question) != REND_SERVICE_ID_LEN_BASE32) {
+ if (rend_valid_v2_service_id(question)) {
+ addr_type = ONION_V2_HOSTNAME;
+ } else if (hs_address_is_valid(question)) {
+ addr_type = ONION_V3_HOSTNAME;
+ } else {
*errmsg = "Invalid address";
return -1;
}
+ rend_cache_entry_t *e = NULL;
- if (!rend_cache_lookup_v2_desc_as_service(question, &e)) {
- /* Descriptor found in cache */
- *answer = tor_strdup(e->desc);
+ if (addr_type == ONION_V2_HOSTNAME) {
+ if (!rend_cache_lookup_v2_desc_as_service(question, &e)) {
+ /* Descriptor found in cache */
+ *answer = tor_strdup(e->desc);
+ } else {
+ *errmsg = "Not found in cache";
+ return -1;
+ }
} else {
- *errmsg = "Not found in cache";
- return -1;
+ ed25519_public_key_t service_pk;
+ char *desc;
+
+ /* The check before this if/else makes sure of this. */
+ tor_assert(addr_type == ONION_V3_HOSTNAME);
+
+ if (hs_parse_address(question, &service_pk, NULL, NULL) < 0) {
+ *errmsg = "Invalid v3 address";
+ return -1;
+ }
+
+ desc = hs_service_lookup_current_desc(&service_pk);
+ if (desc) {
+ /* Newly allocated string, we have ownership. */
+ *answer = desc;
+ } else {
+ *errmsg = "Not found in cache";
+ return -1;
+ }
}
} else if (!strcmpstart(question, "md/id/")) {
const node_t *node = node_get_by_hex_id(question+strlen("md/id/"), 0);
@@ -2076,7 +2177,7 @@ getinfo_helper_dir(control_connection_t *control_conn,
*answer = tor_strdup(consensus->dir);
}
if (!*answer) { /* try loading it from disk */
- char *filename = get_datadir_fname("cached-consensus");
+ char *filename = get_cachedir_fname("cached-consensus");
*answer = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL);
tor_free(filename);
if (!*answer) { /* generate an error */
@@ -2155,7 +2256,7 @@ digest_list_to_string(const smartlist_t *sl)
static char *
download_status_to_string(const download_status_t *dl)
{
- char *rv = NULL, *tmp;
+ char *rv = NULL;
char tbuf[ISO_TIME_LEN+1];
const char *schedule_str, *want_authority_str;
const char *increment_on_str, *backoff_str;
@@ -2203,49 +2304,28 @@ download_status_to_string(const download_status_t *dl)
break;
}
- switch (dl->backoff) {
- case DL_SCHED_DETERMINISTIC:
- backoff_str = "DL_SCHED_DETERMINISTIC";
- break;
- case DL_SCHED_RANDOM_EXPONENTIAL:
- backoff_str = "DL_SCHED_RANDOM_EXPONENTIAL";
- break;
- default:
- backoff_str = "unknown";
- break;
- }
+ backoff_str = "DL_SCHED_RANDOM_EXPONENTIAL";
/* Now assemble them */
- tor_asprintf(&tmp,
+ tor_asprintf(&rv,
"next-attempt-at %s\n"
"n-download-failures %u\n"
"n-download-attempts %u\n"
"schedule %s\n"
"want-authority %s\n"
"increment-on %s\n"
- "backoff %s\n",
+ "backoff %s\n"
+ "last-backoff-position %u\n"
+ "last-delay-used %d\n",
tbuf,
dl->n_download_failures,
dl->n_download_attempts,
schedule_str,
want_authority_str,
increment_on_str,
- backoff_str);
-
- if (dl->backoff == DL_SCHED_RANDOM_EXPONENTIAL) {
- /* Additional fields become relevant in random-exponential mode */
- tor_asprintf(&rv,
- "%s"
- "last-backoff-position %u\n"
- "last-delay-used %d\n",
- tmp,
- dl->last_backoff_position,
- dl->last_delay_used);
- tor_free(tmp);
- } else {
- /* That was it */
- rv = tmp;
- }
+ backoff_str,
+ dl->last_backoff_position,
+ dl->last_delay_used);
}
return rv;
@@ -2585,9 +2665,16 @@ circuit_describe_status_for_controller(origin_circuit_t *circ)
}
}
- if (circ->rend_data != NULL) {
- smartlist_add_asprintf(descparts, "REND_QUERY=%s",
- rend_data_get_address(circ->rend_data));
+ if (circ->rend_data != NULL || circ->hs_ident != NULL) {
+ char addr[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+ const char *onion_address;
+ if (circ->rend_data) {
+ onion_address = rend_data_get_address(circ->rend_data);
+ } else {
+ hs_build_address(&circ->hs_ident->identity_pk, HS_VERSION_THREE, addr);
+ onion_address = addr;
+ }
+ smartlist_add_asprintf(descparts, "REND_QUERY=%s", onion_address);
}
{
@@ -3409,17 +3496,19 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
smartlist_free(args);
nodes = smartlist_new();
+ int first_node = zero_circ;
SMARTLIST_FOREACH_BEGIN(router_nicknames, const char *, n) {
const node_t *node = node_get_by_nickname(n, 0);
if (!node) {
connection_printf_to_buf(conn, "552 No such router \"%s\"\r\n", n);
goto done;
}
- if (!node_has_descriptor(node)) {
+ if (!node_has_preferred_descriptor(node, first_node)) {
connection_printf_to_buf(conn, "552 No descriptor for \"%s\"\r\n", n);
goto done;
}
smartlist_add(nodes, (void*)node);
+ first_node = 0;
} SMARTLIST_FOREACH_END(n);
if (!smartlist_len(nodes)) {
connection_write_str_to_buf("512 No router names provided\r\n", conn);
@@ -3432,14 +3521,15 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
}
/* now circ refers to something that is ready to be extended */
- int first_node = zero_circ;
+ first_node = zero_circ;
SMARTLIST_FOREACH(nodes, const node_t *, node,
{
extend_info_t *info = extend_info_from_node(node, first_node);
if (!info) {
tor_assert_nonfatal(first_node);
log_warn(LD_CONTROL,
- "controller tried to connect to a node that doesn't have any "
+ "controller tried to connect to a node that lacks a suitable "
+ "descriptor, or which doesn't have any "
"addresses that are allowed by the firewall configuration; "
"circuit marked for closing.");
circuit_mark_for_close(TO_CIRCUIT(circ), -END_CIRC_REASON_CONNECTFAILED);
@@ -3447,6 +3537,9 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
goto done;
}
circuit_append_new_exit(circ, info);
+ if (circ->build_state->desired_path_len > 1) {
+ circ->build_state->onehop_tunnel = 0;
+ }
extend_info_free(info);
first_node = 0;
});
@@ -4238,9 +4331,11 @@ handle_control_hspost(control_connection_t *conn,
const char *body)
{
static const char *opt_server = "SERVER=";
+ static const char *opt_hsaddress = "HSADDRESS=";
smartlist_t *hs_dirs = NULL;
const char *encoded_desc = body;
size_t encoded_desc_len = len;
+ const char *onion_address = NULL;
char *cp = memchr(body, '\n', len);
if (cp == NULL) {
@@ -4251,8 +4346,10 @@ handle_control_hspost(control_connection_t *conn,
smartlist_t *args = smartlist_new();
- /* If any SERVER= options were specified, try parse the options line */
- if (!strcasecmpstart(argline, opt_server)) {
+ /* If any SERVER= or HSADDRESS= options were specified, try to parse
+ * the options line. */
+ if (!strcasecmpstart(argline, opt_server) ||
+ !strcasecmpstart(argline, opt_hsaddress)) {
/* encoded_desc begins after a newline character */
cp = cp + 1;
encoded_desc = cp;
@@ -4270,15 +4367,17 @@ handle_control_hspost(control_connection_t *conn,
server);
goto done;
}
- if (!node->rs->is_hs_dir) {
- connection_printf_to_buf(conn, "552 Server \"%s\" is not a HSDir"
- "\r\n", server);
- goto done;
- }
/* Valid server, add it to our local list. */
if (!hs_dirs)
hs_dirs = smartlist_new();
smartlist_add(hs_dirs, node->rs);
+ } else if (!strcasecmpstart(arg, opt_hsaddress)) {
+ const char *address = arg + strlen(opt_hsaddress);
+ if (!hs_address_is_valid(address)) {
+ connection_printf_to_buf(conn, "512 Malformed onion address\r\n");
+ goto done;
+ }
+ onion_address = address;
} else {
connection_printf_to_buf(conn, "512 Unexpected argument \"%s\"\r\n",
arg);
@@ -4287,6 +4386,21 @@ handle_control_hspost(control_connection_t *conn,
} SMARTLIST_FOREACH_END(arg);
}
+ /* Handle the v3 case. */
+ if (onion_address) {
+ char *desc_str = NULL;
+ read_escaped_data(encoded_desc, encoded_desc_len, &desc_str);
+ if (hs_control_hspost_command(desc_str, onion_address, hs_dirs) < 0) {
+ connection_printf_to_buf(conn, "554 Invalid descriptor\r\n");
+ } else {
+ send_control_done(conn);
+ }
+ tor_free(desc_str);
+ goto done;
+ }
+
+ /* From this point on, it is only v2. */
+
/* Read the dot encoded descriptor, and parse it. */
rend_encoded_v2_service_descriptor_t *desc =
tor_malloc_zero(sizeof(rend_encoded_v2_service_descriptor_t));
@@ -4331,6 +4445,52 @@ handle_control_hspost(control_connection_t *conn,
return 0;
}
+/* Helper function for ADD_ONION that adds an ephemeral service depending on
+ * the given hs_version.
+ *
+ * The secret key in pk depends on the hs_version. The ownership of the key
+ * used in pk is given to the HS subsystem so the caller must stop accessing
+ * it after.
+ *
+ * The port_cfgs is a list of service port. Ownership transferred to service.
+ * The max_streams refers to the MaxStreams= key.
+ * The max_streams_close_circuit refers to the MaxStreamsCloseCircuit key.
+ * The auth_type is the authentication type of the clients in auth_clients.
+ * The ownership of that list is transferred to the service.
+ *
+ * On success (RSAE_OKAY), the address_out points to a newly allocated string
+ * containing the onion address without the .onion part. On error, address_out
+ * is untouched. */
+static hs_service_add_ephemeral_status_t
+add_onion_helper_add_service(int hs_version,
+ add_onion_secret_key_t *pk,
+ smartlist_t *port_cfgs, int max_streams,
+ int max_streams_close_circuit, int auth_type,
+ smartlist_t *auth_clients, char **address_out)
+{
+ hs_service_add_ephemeral_status_t ret;
+
+ tor_assert(pk);
+ tor_assert(port_cfgs);
+ tor_assert(address_out);
+
+ switch (hs_version) {
+ case HS_VERSION_TWO:
+ ret = rend_service_add_ephemeral(pk->v2, port_cfgs, max_streams,
+ max_streams_close_circuit, auth_type,
+ auth_clients, address_out);
+ break;
+ case HS_VERSION_THREE:
+ ret = hs_service_add_ephemeral(pk->v3, port_cfgs, max_streams,
+ max_streams_close_circuit, address_out);
+ break;
+ default:
+ tor_assert_unreached();
+ }
+
+ return ret;
+}
+
/** Called when we get a ADD_ONION command; parse the body, and set up
* the new ephemeral Onion Service. */
static int
@@ -4512,15 +4672,15 @@ handle_control_add_onion(control_connection_t *conn,
}
/* Parse the "keytype:keyblob" argument. */
- crypto_pk_t *pk = NULL;
+ int hs_version = 0;
+ add_onion_secret_key_t pk = { NULL };
const char *key_new_alg = NULL;
char *key_new_blob = NULL;
char *err_msg = NULL;
- pk = add_onion_helper_keyarg(smartlist_get(args, 0), discard_pk,
- &key_new_alg, &key_new_blob,
- &err_msg);
- if (!pk) {
+ if (add_onion_helper_keyarg(smartlist_get(args, 0), discard_pk,
+ &key_new_alg, &key_new_blob, &pk, &hs_version,
+ &err_msg) < 0) {
if (err_msg) {
connection_write_str_to_buf(err_msg, conn);
tor_free(err_msg);
@@ -4529,16 +4689,23 @@ handle_control_add_onion(control_connection_t *conn,
}
tor_assert(!err_msg);
+ /* Hidden service version 3 don't have client authentication support so if
+ * ClientAuth was given, send back an error. */
+ if (hs_version == HS_VERSION_THREE && auth_clients) {
+ connection_printf_to_buf(conn, "513 ClientAuth not supported\r\n");
+ goto out;
+ }
+
/* Create the HS, using private key pk, client authentication auth_type,
* the list of auth_clients, and port config port_cfg.
* rend_service_add_ephemeral() will take ownership of pk and port_cfg,
* regardless of success/failure.
*/
char *service_id = NULL;
- int ret = rend_service_add_ephemeral(pk, port_cfgs, max_streams,
- max_streams_close_circuit,
- auth_type, auth_clients,
- &service_id);
+ int ret = add_onion_helper_add_service(hs_version, &pk, port_cfgs,
+ max_streams,
+ max_streams_close_circuit, auth_type,
+ auth_clients, &service_id);
port_cfgs = NULL; /* port_cfgs is now owned by the rendservice code. */
auth_clients = NULL; /* so is auth_clients */
switch (ret) {
@@ -4631,9 +4798,10 @@ handle_control_add_onion(control_connection_t *conn,
* Note: The error messages returned are deliberately vague to avoid echoing
* key material.
*/
-STATIC crypto_pk_t *
+STATIC int
add_onion_helper_keyarg(const char *arg, int discard_pk,
const char **key_new_alg_out, char **key_new_blob_out,
+ add_onion_secret_key_t *decoded_key, int *hs_version,
char **err_msg_out)
{
smartlist_t *key_args = smartlist_new();
@@ -4641,7 +4809,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
const char *key_new_alg = NULL;
char *key_new_blob = NULL;
char *err_msg = NULL;
- int ok = 0;
+ int ret = -1;
smartlist_split_string(key_args, arg, ":", SPLIT_IGNORE_BLANK, 0);
if (smartlist_len(key_args) != 2) {
@@ -4653,6 +4821,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
static const char *key_type_new = "NEW";
static const char *key_type_best = "BEST";
static const char *key_type_rsa1024 = "RSA1024";
+ static const char *key_type_ed25519_v3 = "ED25519-V3";
const char *key_type = smartlist_get(key_args, 0);
const char *key_blob = smartlist_get(key_args, 1);
@@ -4665,9 +4834,23 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
goto err;
}
if (crypto_pk_num_bits(pk) != PK_BYTES*8) {
+ crypto_pk_free(pk);
err_msg = tor_strdup("512 Invalid RSA key size\r\n");
goto err;
}
+ decoded_key->v2 = pk;
+ *hs_version = HS_VERSION_TWO;
+ } else if (!strcasecmp(key_type_ed25519_v3, key_type)) {
+ /* "ED25519-V3:<Base64 Blob>" - Loading a pre-existing ed25519 key. */
+ ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk));
+ if (base64_decode((char *) sk->seckey, sizeof(sk->seckey), key_blob,
+ strlen(key_blob)) != sizeof(sk->seckey)) {
+ tor_free(sk);
+ err_msg = tor_strdup("512 Failed to decode ED25519-V3 key\r\n");
+ goto err;
+ }
+ decoded_key->v3 = sk;
+ *hs_version = HS_VERSION_THREE;
} else if (!strcasecmp(key_type_new, key_type)) {
/* "NEW:<Algorithm>" - Generating a new key, blob as algorithm. */
if (!strcasecmp(key_type_rsa1024, key_blob) ||
@@ -4681,12 +4864,38 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
}
if (!discard_pk) {
if (crypto_pk_base64_encode(pk, &key_new_blob)) {
+ crypto_pk_free(pk);
tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n",
key_type_rsa1024);
goto err;
}
key_new_alg = key_type_rsa1024;
}
+ decoded_key->v2 = pk;
+ *hs_version = HS_VERSION_TWO;
+ } else if (!strcasecmp(key_type_ed25519_v3, key_blob)) {
+ ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk));
+ if (ed25519_secret_key_generate(sk, 1) < 0) {
+ tor_free(sk);
+ tor_asprintf(&err_msg, "551 Failed to generate %s key\r\n",
+ key_type_ed25519_v3);
+ goto err;
+ }
+ if (!discard_pk) {
+ ssize_t len = base64_encode_size(sizeof(sk->seckey), 0) + 1;
+ key_new_blob = tor_malloc_zero(len);
+ if (base64_encode(key_new_blob, len, (const char *) sk->seckey,
+ sizeof(sk->seckey), 0) != (len - 1)) {
+ tor_free(sk);
+ tor_free(key_new_blob);
+ tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n",
+ key_type_ed25519_v3);
+ goto err;
+ }
+ key_new_alg = key_type_ed25519_v3;
+ }
+ decoded_key->v3 = sk;
+ *hs_version = HS_VERSION_THREE;
} else {
err_msg = tor_strdup("513 Invalid key type\r\n");
goto err;
@@ -4696,9 +4905,8 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
goto err;
}
- /* Succeded in loading or generating a private key. */
- tor_assert(pk);
- ok = 1;
+ /* Succeeded in loading or generating a private key. */
+ ret = 0;
err:
SMARTLIST_FOREACH(key_args, char *, cp, {
@@ -4707,10 +4915,6 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
});
smartlist_free(key_args);
- if (!ok) {
- crypto_pk_free(pk);
- pk = NULL;
- }
if (err_msg_out) {
*err_msg_out = err_msg;
} else {
@@ -4719,7 +4923,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
*key_new_alg_out = key_new_alg;
*key_new_blob_out = key_new_blob;
- return pk;
+ return ret;
}
/** Helper function to handle parsing a ClientAuth argument to the
@@ -4788,6 +4992,7 @@ handle_control_del_onion(control_connection_t *conn,
uint32_t len,
const char *body)
{
+ int hs_version = 0;
smartlist_t *args;
(void) len; /* body is nul-terminated; it's safe to ignore the length */
args = getargs_helper("DEL_ONION", conn, body, 1, 1);
@@ -4795,7 +5000,11 @@ handle_control_del_onion(control_connection_t *conn,
return 0;
const char *service_id = smartlist_get(args, 0);
- if (!rend_valid_v2_service_id(service_id)) {
+ if (rend_valid_v2_service_id(service_id)) {
+ hs_version = HS_VERSION_TWO;
+ } else if (hs_address_is_valid(service_id)) {
+ hs_version = HS_VERSION_THREE;
+ } else {
connection_printf_to_buf(conn, "512 Malformed Onion Service id\r\n");
goto out;
}
@@ -4822,8 +5031,20 @@ handle_control_del_onion(control_connection_t *conn,
if (onion_services == NULL) {
connection_printf_to_buf(conn, "552 Unknown Onion Service id\r\n");
} else {
- int ret = rend_service_del_ephemeral(service_id);
- if (ret) {
+ int ret = -1;
+ switch (hs_version) {
+ case HS_VERSION_TWO:
+ ret = rend_service_del_ephemeral(service_id);
+ break;
+ case HS_VERSION_THREE:
+ ret = hs_service_del_ephemeral(service_id);
+ break;
+ default:
+ /* The ret value will be -1 thus hitting the warning below. This should
+ * never happen because of the check at the start of the function. */
+ break;
+ }
+ if (ret < 0) {
/* This should *NEVER* fail, since the service is on either the
* per-control connection list, or the global one.
*/
@@ -4893,9 +5114,16 @@ connection_control_closed(control_connection_t *conn)
* The list and it's contents are scrubbed/freed in connection_free_.
*/
if (conn->ephemeral_onion_services) {
- SMARTLIST_FOREACH(conn->ephemeral_onion_services, char *, cp, {
- rend_service_del_ephemeral(cp);
- });
+ SMARTLIST_FOREACH_BEGIN(conn->ephemeral_onion_services, char *, cp) {
+ if (rend_valid_v2_service_id(cp)) {
+ rend_service_del_ephemeral(cp);
+ } else if (hs_address_is_valid(cp)) {
+ hs_service_del_ephemeral(cp);
+ } else {
+ /* An invalid .onion in our list should NEVER happen */
+ tor_fragile_assert();
+ }
+ } SMARTLIST_FOREACH_END(cp);
}
if (conn->is_owning_control_connection) {
@@ -6575,8 +6803,7 @@ monitor_owning_controller_process(const char *process_spec)
"owning controller: %s. Exiting.",
msg);
owning_controller_process_spec = NULL;
- tor_cleanup();
- exit(1);
+ tor_shutdown_event_loop_and_exit(1);
}
}
@@ -6974,27 +7201,33 @@ rend_hsaddress_str_or_unknown(const char *onion_address)
* <b>rend_query</b> is used to fetch requested onion address and auth type.
* <b>hs_dir</b> is the description of contacting hs directory.
* <b>desc_id_base32</b> is the ID of requested hs descriptor.
+ * <b>hsdir_index</b> is the HSDir fetch index value for v3, an hex string.
*/
void
-control_event_hs_descriptor_requested(const rend_data_t *rend_query,
+control_event_hs_descriptor_requested(const char *onion_address,
+ rend_auth_type_t auth_type,
const char *id_digest,
- const char *desc_id_base32)
+ const char *desc_id,
+ const char *hsdir_index)
{
- if (!id_digest || !rend_query || !desc_id_base32) {
- log_warn(LD_BUG, "Called with rend_query==%p, "
- "id_digest==%p, desc_id_base32==%p",
- rend_query, id_digest, desc_id_base32);
+ char *hsdir_index_field = NULL;
+
+ if (BUG(!id_digest || !desc_id)) {
return;
}
+ if (hsdir_index) {
+ tor_asprintf(&hsdir_index_field, " HSDIR_INDEX=%s", hsdir_index);
+ }
+
send_control_event(EVENT_HS_DESC,
- "650 HS_DESC REQUESTED %s %s %s %s\r\n",
- rend_hsaddress_str_or_unknown(
- rend_data_get_address(rend_query)),
- rend_auth_type_to_string(
- TO_REND_DATA_V2(rend_query)->auth_type),
+ "650 HS_DESC REQUESTED %s %s %s %s%s\r\n",
+ rend_hsaddress_str_or_unknown(onion_address),
+ rend_auth_type_to_string(auth_type),
node_describe_longname_by_id(id_digest),
- desc_id_base32);
+ desc_id,
+ hsdir_index_field ? hsdir_index_field : "");
+ tor_free(hsdir_index_field);
}
/** For an HS descriptor query <b>rend_data</b>, using the
@@ -7043,89 +7276,87 @@ get_desc_id_from_query(const rend_data_t *rend_data, const char *hsdir_fp)
/** send HS_DESC CREATED event when a local service generates a descriptor.
*
- * <b>service_id</b> is the descriptor onion address.
- * <b>desc_id_base32</b> is the descriptor ID.
- * <b>replica</b> is the the descriptor replica number.
+ * <b>onion_address</b> is service address.
+ * <b>desc_id</b> is the descriptor ID.
+ * <b>replica</b> is the the descriptor replica number. If it is negative, it
+ * is ignored.
*/
void
-control_event_hs_descriptor_created(const char *service_id,
- const char *desc_id_base32,
+control_event_hs_descriptor_created(const char *onion_address,
+ const char *desc_id,
int replica)
{
- if (!service_id || !desc_id_base32) {
- log_warn(LD_BUG, "Called with service_digest==%p, "
- "desc_id_base32==%p", service_id, desc_id_base32);
+ char *replica_field = NULL;
+
+ if (BUG(!onion_address || !desc_id)) {
return;
}
+ if (replica >= 0) {
+ tor_asprintf(&replica_field, " REPLICA=%d", replica);
+ }
+
send_control_event(EVENT_HS_DESC,
- "650 HS_DESC CREATED %s UNKNOWN UNKNOWN %s "
- "REPLICA=%d\r\n",
- service_id,
- desc_id_base32,
- replica);
+ "650 HS_DESC CREATED %s UNKNOWN UNKNOWN %s%s\r\n",
+ onion_address, desc_id,
+ replica_field ? replica_field : "");
+ tor_free(replica_field);
}
/** send HS_DESC upload event.
*
- * <b>service_id</b> is the descriptor onion address.
+ * <b>onion_address</b> is service address.
* <b>hs_dir</b> is the description of contacting hs directory.
- * <b>desc_id_base32</b> is the ID of requested hs descriptor.
+ * <b>desc_id</b> is the ID of requested hs descriptor.
*/
void
-control_event_hs_descriptor_upload(const char *service_id,
+control_event_hs_descriptor_upload(const char *onion_address,
const char *id_digest,
- const char *desc_id_base32)
+ const char *desc_id,
+ const char *hsdir_index)
{
- if (!service_id || !id_digest || !desc_id_base32) {
- log_warn(LD_BUG, "Called with service_digest==%p, "
- "desc_id_base32==%p, id_digest==%p", service_id,
- desc_id_base32, id_digest);
+ char *hsdir_index_field = NULL;
+
+ if (BUG(!onion_address || !id_digest || !desc_id)) {
return;
}
+ if (hsdir_index) {
+ tor_asprintf(&hsdir_index_field, " HSDIR_INDEX=%s", hsdir_index);
+ }
+
send_control_event(EVENT_HS_DESC,
- "650 HS_DESC UPLOAD %s UNKNOWN %s %s\r\n",
- service_id,
+ "650 HS_DESC UPLOAD %s UNKNOWN %s %s%s\r\n",
+ onion_address,
node_describe_longname_by_id(id_digest),
- desc_id_base32);
+ desc_id,
+ hsdir_index_field ? hsdir_index_field : "");
+ tor_free(hsdir_index_field);
}
/** send HS_DESC event after got response from hs directory.
*
* NOTE: this is an internal function used by following functions:
- * control_event_hs_descriptor_received
- * control_event_hs_descriptor_failed
+ * control_event_hsv2_descriptor_received
+ * control_event_hsv2_descriptor_failed
+ * control_event_hsv3_descriptor_failed
*
* So do not call this function directly.
*/
-void
-control_event_hs_descriptor_receive_end(const char *action,
- const char *onion_address,
- const rend_data_t *rend_data,
- const char *id_digest,
- const char *reason)
+static void
+event_hs_descriptor_receive_end(const char *action,
+ const char *onion_address,
+ const char *desc_id,
+ rend_auth_type_t auth_type,
+ const char *hsdir_id_digest,
+ const char *reason)
{
- char *desc_id_field = NULL;
char *reason_field = NULL;
- char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
- const char *desc_id = NULL;
- if (!action || !rend_data || !onion_address) {
- log_warn(LD_BUG, "Called with action==%p, rend_data==%p, "
- "onion_address==%p", action, rend_data, onion_address);
+ if (BUG(!action || !onion_address)) {
return;
}
- desc_id = get_desc_id_from_query(rend_data, id_digest);
- if (desc_id != NULL) {
- /* Set the descriptor ID digest to base32 so we can send it. */
- base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
- DIGEST_LEN);
- /* Extra whitespace is needed before the value. */
- tor_asprintf(&desc_id_field, " %s", desc_id_base32);
- }
-
if (reason) {
tor_asprintf(&reason_field, " REASON=%s", reason);
}
@@ -7134,14 +7365,13 @@ control_event_hs_descriptor_receive_end(const char *action,
"650 HS_DESC %s %s %s %s%s%s\r\n",
action,
rend_hsaddress_str_or_unknown(onion_address),
- rend_auth_type_to_string(
- TO_REND_DATA_V2(rend_data)->auth_type),
- id_digest ?
- node_describe_longname_by_id(id_digest) : "UNKNOWN",
- desc_id_field ? desc_id_field : "",
+ rend_auth_type_to_string(auth_type),
+ hsdir_id_digest ?
+ node_describe_longname_by_id(hsdir_id_digest) :
+ "UNKNOWN",
+ desc_id ? desc_id : "",
reason_field ? reason_field : "");
- tor_free(desc_id_field);
tor_free(reason_field);
}
@@ -7161,9 +7391,7 @@ control_event_hs_descriptor_upload_end(const char *action,
{
char *reason_field = NULL;
- if (!action || !id_digest) {
- log_warn(LD_BUG, "Called with action==%p, id_digest==%p", action,
- id_digest);
+ if (BUG(!action || !id_digest)) {
return;
}
@@ -7186,17 +7414,54 @@ control_event_hs_descriptor_upload_end(const char *action,
* called when we successfully received a hidden service descriptor.
*/
void
-control_event_hs_descriptor_received(const char *onion_address,
- const rend_data_t *rend_data,
- const char *id_digest)
+control_event_hsv2_descriptor_received(const char *onion_address,
+ const rend_data_t *rend_data,
+ const char *hsdir_id_digest)
+{
+ char *desc_id_field = NULL;
+ const char *desc_id;
+
+ if (BUG(!rend_data || !hsdir_id_digest || !onion_address)) {
+ return;
+ }
+
+ desc_id = get_desc_id_from_query(rend_data, hsdir_id_digest);
+ if (desc_id != NULL) {
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ /* Set the descriptor ID digest to base32 so we can send it. */
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
+ DIGEST_LEN);
+ /* Extra whitespace is needed before the value. */
+ tor_asprintf(&desc_id_field, " %s", desc_id_base32);
+ }
+
+ event_hs_descriptor_receive_end("RECEIVED", onion_address, desc_id_field,
+ TO_REND_DATA_V2(rend_data)->auth_type,
+ hsdir_id_digest, NULL);
+ tor_free(desc_id_field);
+}
+
+/* Send HS_DESC RECEIVED event
+ *
+ * Called when we successfully received a hidden service descriptor. */
+void
+control_event_hsv3_descriptor_received(const char *onion_address,
+ const char *desc_id,
+ const char *hsdir_id_digest)
{
- if (!rend_data || !id_digest || !onion_address) {
- log_warn(LD_BUG, "Called with rend_data==%p, id_digest==%p, "
- "onion_address==%p", rend_data, id_digest, onion_address);
+ char *desc_id_field = NULL;
+
+ if (BUG(!onion_address || !desc_id || !hsdir_id_digest)) {
return;
}
- control_event_hs_descriptor_receive_end("RECEIVED", onion_address,
- rend_data, id_digest, NULL);
+
+ /* Because DescriptorID is an optional positional value, we need to add a
+ * whitespace before in order to not be next to the HsDir value. */
+ tor_asprintf(&desc_id_field, " %s", desc_id);
+
+ event_hs_descriptor_receive_end("RECEIVED", onion_address, desc_id_field,
+ REND_NO_AUTH, hsdir_id_digest, NULL);
+ tor_free(desc_id_field);
}
/** send HS_DESC UPLOADED event
@@ -7207,9 +7472,7 @@ void
control_event_hs_descriptor_uploaded(const char *id_digest,
const char *onion_address)
{
- if (!id_digest) {
- log_warn(LD_BUG, "Called with id_digest==%p",
- id_digest);
+ if (BUG(!id_digest)) {
return;
}
@@ -7223,17 +7486,58 @@ control_event_hs_descriptor_uploaded(const char *id_digest,
* add it to REASON= field.
*/
void
-control_event_hs_descriptor_failed(const rend_data_t *rend_data,
- const char *id_digest,
- const char *reason)
+control_event_hsv2_descriptor_failed(const rend_data_t *rend_data,
+ const char *hsdir_id_digest,
+ const char *reason)
+{
+ char *desc_id_field = NULL;
+ const char *desc_id;
+
+ if (BUG(!rend_data)) {
+ return;
+ }
+
+ desc_id = get_desc_id_from_query(rend_data, hsdir_id_digest);
+ if (desc_id != NULL) {
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ /* Set the descriptor ID digest to base32 so we can send it. */
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
+ DIGEST_LEN);
+ /* Extra whitespace is needed before the value. */
+ tor_asprintf(&desc_id_field, " %s", desc_id_base32);
+ }
+
+ event_hs_descriptor_receive_end("FAILED", rend_data_get_address(rend_data),
+ desc_id_field,
+ TO_REND_DATA_V2(rend_data)->auth_type,
+ hsdir_id_digest, reason);
+ tor_free(desc_id_field);
+}
+
+/** Send HS_DESC event to inform controller that the query to
+ * <b>onion_address</b> failed to retrieve hidden service descriptor
+ * <b>desc_id</b> from directory identified by <b>hsdir_id_digest</b>. If
+ * NULL, "UNKNOWN" is used. If <b>reason</b> is not NULL, add it to REASON=
+ * field. */
+void
+control_event_hsv3_descriptor_failed(const char *onion_address,
+ const char *desc_id,
+ const char *hsdir_id_digest,
+ const char *reason)
{
- if (!rend_data) {
- log_warn(LD_BUG, "Called with rend_data==%p", rend_data);
+ char *desc_id_field = NULL;
+
+ if (BUG(!onion_address || !desc_id || !reason)) {
return;
}
- control_event_hs_descriptor_receive_end("FAILED",
- rend_data_get_address(rend_data),
- rend_data, id_digest, reason);
+
+ /* Because DescriptorID is an optional positional value, we need to add a
+ * whitespace before in order to not be next to the HsDir value. */
+ tor_asprintf(&desc_id_field, " %s", desc_id);
+
+ event_hs_descriptor_receive_end("FAILED", onion_address, desc_id_field,
+ REND_NO_AUTH, hsdir_id_digest, reason);
+ tor_free(desc_id_field);
}
/** Send HS_DESC_CONTENT event after completion of a successful fetch from hs
@@ -7283,9 +7587,7 @@ control_event_hs_descriptor_upload_failed(const char *id_digest,
const char *onion_address,
const char *reason)
{
- if (!id_digest) {
- log_warn(LD_BUG, "Called with id_digest==%p",
- id_digest);
+ if (BUG(!id_digest)) {
return;
}
control_event_hs_descriptor_upload_end("FAILED", onion_address,
@@ -7312,6 +7614,14 @@ control_free_all(void)
tor_event_free(flush_queued_events_event);
flush_queued_events_event = NULL;
}
+ bootstrap_percent = BOOTSTRAP_STATUS_UNDEF;
+ notice_bootstrap_percent = 0;
+ bootstrap_problems = 0;
+ authentication_cookie_is_set = 0;
+ global_event_mask = 0;
+ disable_log_messages = 0;
+ memset(last_sent_bootstrap_message, 0, sizeof(last_sent_bootstrap_message));
+ flush_queued_event_pending = 0;
}
#ifdef TOR_UNIT_TESTS
diff --git a/src/or/control.h b/src/or/control.h
index e957b593a6..28ffeaed86 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -27,6 +27,10 @@ void control_ports_write_to_file(void);
#define LOG_FN_CONN(conn, args) \
CONN_LOG_PROTECT(conn, log_fn args)
+#define CC_LOCAL_FD_IS_OWNER (1u<<0)
+#define CC_LOCAL_FD_IS_AUTHENTICATED (1u<<1)
+int control_connection_add_local_fd(tor_socket_t sock, unsigned flags);
+
int connection_control_finished_flushing(control_connection_t *conn);
int connection_control_reached_eof(control_connection_t *conn);
void connection_control_closed(control_connection_t *conn);
@@ -111,32 +115,39 @@ void control_event_transport_launched(const char *mode,
tor_addr_t *addr, uint16_t port);
const char *rend_auth_type_to_string(rend_auth_type_t auth_type);
MOCK_DECL(const char *, node_describe_longname_by_id,(const char *id_digest));
-void control_event_hs_descriptor_requested(const rend_data_t *rend_query,
- const char *desc_id_base32,
- const char *hs_dir);
-void control_event_hs_descriptor_created(const char *service_id,
- const char *desc_id_base32,
+void control_event_hs_descriptor_requested(const char *onion_address,
+ rend_auth_type_t auth_type,
+ const char *id_digest,
+ const char *desc_id,
+ const char *hsdir_index);
+void control_event_hs_descriptor_created(const char *onion_address,
+ const char *desc_id,
int replica);
-void control_event_hs_descriptor_upload(const char *service_id,
- const char *desc_id_base32,
- const char *hs_dir);
-void control_event_hs_descriptor_receive_end(const char *action,
- const char *onion_address,
- const rend_data_t *rend_data,
- const char *id_digest,
- const char *reason);
+void control_event_hs_descriptor_upload(const char *onion_address,
+ const char *desc_id,
+ const char *hs_dir,
+ const char *hsdir_index);
void control_event_hs_descriptor_upload_end(const char *action,
const char *onion_address,
const char *hs_dir,
const char *reason);
-void control_event_hs_descriptor_received(const char *onion_address,
- const rend_data_t *rend_data,
- const char *id_digest);
void control_event_hs_descriptor_uploaded(const char *hs_dir,
const char *onion_address);
-void control_event_hs_descriptor_failed(const rend_data_t *rend_data,
- const char *id_digest,
- const char *reason);
+/* Hidden service v2 HS_DESC specific. */
+void control_event_hsv2_descriptor_failed(const rend_data_t *rend_data,
+ const char *id_digest,
+ const char *reason);
+void control_event_hsv2_descriptor_received(const char *onion_address,
+ const rend_data_t *rend_data,
+ const char *id_digest);
+/* Hidden service v3 HS_DESC specific. */
+void control_event_hsv3_descriptor_failed(const char *onion_address,
+ const char *desc_id,
+ const char *hsdir_id_digest,
+ const char *reason);
+void control_event_hsv3_descriptor_received(const char *onion_address,
+ const char *desc_id,
+ const char *hsdir_id_digest);
void control_event_hs_descriptor_upload_failed(const char *hs_dir,
const char *onion_address,
const char *reason);
@@ -252,10 +263,22 @@ void format_cell_stats(char **event_string, circuit_t *circ,
cell_stats_t *cell_stats);
STATIC char *get_bw_samples(void);
-STATIC crypto_pk_t *add_onion_helper_keyarg(const char *arg, int discard_pk,
- const char **key_new_alg_out,
- char **key_new_blob_out,
- char **err_msg_out);
+/* ADD_ONION secret key to create an ephemeral service. The command supports
+ * multiple versions so this union stores the key and passes it to the HS
+ * subsystem depending on the requested version. */
+typedef union add_onion_secret_key_t {
+ /* Hidden service v2 secret key. */
+ crypto_pk_t *v2;
+ /* Hidden service v3 secret key. */
+ ed25519_secret_key_t *v3;
+} add_onion_secret_key_t;
+
+STATIC int add_onion_helper_keyarg(const char *arg, int discard_pk,
+ const char **key_new_alg_out,
+ char **key_new_blob_out,
+ add_onion_secret_key_t *decoded_key,
+ int *hs_version, char **err_msg_out);
+
STATIC rend_authorized_client_t *
add_onion_helper_clientauth(const char *arg, int *created, char **err_msg_out);
diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c
index d9371b3446..50761dd4d3 100644
--- a/src/or/cpuworker.c
+++ b/src/or/cpuworker.c
@@ -48,14 +48,25 @@ worker_state_new(void *arg)
ws->onion_keys = server_onion_keys_new();
return ws;
}
+
+#define worker_state_free(ws) \
+ FREE_AND_NULL(worker_state_t, worker_state_free_, (ws))
+
static void
-worker_state_free(void *arg)
+worker_state_free_(worker_state_t *ws)
{
- worker_state_t *ws = arg;
+ if (!ws)
+ return;
server_onion_keys_free(ws->onion_keys);
tor_free(ws);
}
+static void
+worker_state_free_void(void *arg)
+{
+ worker_state_free_(arg);
+}
+
static replyqueue_t *replyqueue = NULL;
static threadpool_t *threadpool = NULL;
static struct event *reply_event = NULL;
@@ -102,7 +113,7 @@ cpu_init(void)
threadpool = threadpool_new(n_threads,
replyqueue,
worker_state_new,
- worker_state_free,
+ worker_state_free_void,
NULL);
}
/* Total voodoo. Can we make this more sensible? */
@@ -198,7 +209,7 @@ cpuworkers_rotate_keyinfo(void)
if (threadpool_queue_update(threadpool,
worker_state_new,
update_state_threadfn,
- worker_state_free,
+ worker_state_free_void,
NULL)) {
log_warn(LD_OR, "Failed to queue key update for worker threads.");
}
@@ -239,7 +250,7 @@ should_time_request(uint16_t onionskin_type)
}
/** Return an estimate of how many microseconds we will need for a single
- * cpuworker to to process <b>n_requests</b> onionskins of type
+ * cpuworker to process <b>n_requests</b> onionskins of type
* <b>onionskin_type</b>. */
uint64_t
estimated_usec_for_onionskins(uint32_t n_requests, uint16_t onionskin_type)
diff --git a/src/or/dircollate.c b/src/or/dircollate.c
index d34ebe8af5..ce4534ff6c 100644
--- a/src/or/dircollate.c
+++ b/src/or/dircollate.c
@@ -41,9 +41,12 @@ typedef struct ddmap_entry_s {
vote_routerstatus_t *vrs_lst[FLEXIBLE_ARRAY_MEMBER];
} ddmap_entry_t;
+#define ddmap_entry_free(e) \
+ FREE_AND_NULL(ddmap_entry_t, ddmap_entry_free_, (e))
+
/** Release all storage held by e. */
static void
-ddmap_entry_free(ddmap_entry_t *e)
+ddmap_entry_free_(ddmap_entry_t *e)
{
tor_free(e);
}
@@ -158,7 +161,7 @@ dircollator_new(int n_votes, int n_authorities)
/** Release all storage held by <b>dc</b>. */
void
-dircollator_free(dircollator_t *dc)
+dircollator_free_(dircollator_t *dc)
{
if (!dc)
return;
diff --git a/src/or/dircollate.h b/src/or/dircollate.h
index 7932e18998..0584b2fe06 100644
--- a/src/or/dircollate.h
+++ b/src/or/dircollate.h
@@ -18,7 +18,9 @@
typedef struct dircollator_s dircollator_t;
dircollator_t *dircollator_new(int n_votes, int n_authorities);
-void dircollator_free(dircollator_t *obj);
+void dircollator_free_(dircollator_t *obj);
+#define dircollator_free(c) \
+ FREE_AND_NULL(dircollator_t, dircollator_free_, (c))
void dircollator_add_vote(dircollator_t *dc, networkstatus_t *v);
void dircollator_collate(dircollator_t *dc, int consensus_method);
diff --git a/src/or/directory.c b/src/or/directory.c
index 7f47d71ad6..8636f68410 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -25,6 +25,7 @@
#include "geoip.h"
#include "hs_cache.h"
#include "hs_common.h"
+#include "hs_control.h"
#include "hs_client.h"
#include "main.h"
#include "microdesc.h"
@@ -1101,7 +1102,7 @@ directory_request_new(uint8_t dir_purpose)
* Release all resources held by <b>req</b>.
*/
void
-directory_request_free(directory_request_t *req)
+directory_request_free_(directory_request_t *req)
{
if (req == NULL)
return;
@@ -1111,7 +1112,7 @@ directory_request_free(directory_request_t *req)
/**
* Set the address and OR port to use for this directory request. If there is
* no OR port, we'll have to connect over the dirport. (If there are both,
- * the indirection setting determins which to use.)
+ * the indirection setting determines which to use.)
*/
void
directory_request_set_or_addr_port(directory_request_t *req,
@@ -1122,7 +1123,7 @@ directory_request_set_or_addr_port(directory_request_t *req,
/**
* Set the address and dirport to use for this directory request. If there
* is no dirport, we'll have to connect over the OR port. (If there are both,
- * the indirection setting determins which to use.)
+ * the indirection setting determines which to use.)
*/
void
directory_request_set_dir_addr_port(directory_request_t *req,
@@ -2197,8 +2198,6 @@ load_downloaded_routers(const char *body, smartlist_t *which,
return added;
}
-static int handle_response_fetch_consensus(dir_connection_t *,
- const response_handler_args_t *);
static int handle_response_fetch_certificate(dir_connection_t *,
const response_handler_args_t *);
static int handle_response_fetch_status_vote(dir_connection_t *,
@@ -2543,7 +2542,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
* consensus document by checking the consensus, storing it, and marking
* router requests as reachable.
**/
-static int
+STATIC int
handle_response_fetch_consensus(dir_connection_t *conn,
const response_handler_args_t *args)
{
@@ -3093,10 +3092,19 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn,
/* We got something: Try storing it in the cache. */
if (hs_cache_store_as_client(body, &conn->hs_ident->identity_pk) < 0) {
log_warn(LD_REND, "Failed to store hidden service descriptor");
+ /* Fire control port FAILED event. */
+ hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
+ "BAD_DESC");
+ hs_control_desc_event_content(conn->hs_ident, conn->identity_digest,
+ NULL);
} else {
log_info(LD_REND, "Stored hidden service descriptor successfully.");
TO_CONN(conn)->purpose = DIR_PURPOSE_HAS_FETCHED_HSDESC;
hs_client_desc_has_arrived(conn->hs_ident);
+ /* Fire control port RECEIVED event. */
+ hs_control_desc_event_received(conn->hs_ident, conn->identity_digest);
+ hs_control_desc_event_content(conn->hs_ident, conn->identity_digest,
+ body);
}
break;
case 404:
@@ -3104,13 +3112,22 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn,
* tries to clean this conn up. */
log_info(LD_REND, "Fetching hidden service v3 descriptor not found: "
"Retrying at another directory.");
- /* TODO: Inform the control port */
+ /* Fire control port FAILED event. */
+ hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
+ "NOT_FOUND");
+ hs_control_desc_event_content(conn->hs_ident, conn->identity_digest,
+ NULL);
break;
case 400:
log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: "
"http status 400 (%s). Dirserver didn't like our "
"query? Retrying at another directory.",
escaped(reason));
+ /* Fire control port FAILED event. */
+ hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
+ "QUERY_REJECTED");
+ hs_control_desc_event_content(conn->hs_ident, conn->identity_digest,
+ NULL);
break;
default:
log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: "
@@ -3118,6 +3135,11 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn,
"'%s:%d'. Retrying at another directory.",
status_code, escaped(reason), TO_CONN(conn)->address,
TO_CONN(conn)->port);
+ /* Fire control port FAILED event. */
+ hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
+ "UNEXPECTED");
+ hs_control_desc_event_content(conn->hs_ident, conn->identity_digest,
+ NULL);
break;
}
@@ -3139,9 +3161,9 @@ handle_response_fetch_renddesc_v2(dir_connection_t *conn,
const size_t body_len = args->body_len;
#define SEND_HS_DESC_FAILED_EVENT(reason) \
- (control_event_hs_descriptor_failed(conn->rend_data, \
- conn->identity_digest, \
- reason))
+ (control_event_hsv2_descriptor_failed(conn->rend_data, \
+ conn->identity_digest, \
+ reason))
#define SEND_HS_DESC_FAILED_CONTENT() \
(control_event_hs_descriptor_content( \
rend_data_get_address(conn->rend_data), \
@@ -3176,9 +3198,9 @@ handle_response_fetch_renddesc_v2(dir_connection_t *conn,
/* success. notify pending connections about this. */
log_info(LD_REND, "Successfully fetched v2 rendezvous "
"descriptor.");
- control_event_hs_descriptor_received(service_id,
- conn->rend_data,
- conn->identity_digest);
+ control_event_hsv2_descriptor_received(service_id,
+ conn->rend_data,
+ conn->identity_digest);
control_event_hs_descriptor_content(service_id,
conn->requested_resource,
conn->identity_digest,
@@ -3295,7 +3317,7 @@ handle_response_upload_hsdesc(dir_connection_t *conn,
case 200:
log_info(LD_REND, "Uploading hidden service descriptor: "
"finished with status 200 (%s)", escaped(reason));
- /* XXX: Trigger control event. */
+ hs_control_desc_event_uploaded(conn->hs_ident, conn->identity_digest);
break;
case 400:
log_fn(LOG_PROTOCOL_WARN, LD_REND,
@@ -3303,7 +3325,8 @@ handle_response_upload_hsdesc(dir_connection_t *conn,
"status 400 (%s) response from dirserver "
"'%s:%d'. Malformed hidden service descriptor?",
escaped(reason), conn->base_.address, conn->base_.port);
- /* XXX: Trigger control event. */
+ hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
+ "UPLOAD_REJECTED");
break;
default:
log_warn(LD_REND, "Uploading hidden service descriptor: http "
@@ -3311,7 +3334,8 @@ handle_response_upload_hsdesc(dir_connection_t *conn,
"'%s:%d').",
status_code, escaped(reason), conn->base_.address,
conn->base_.port);
- /* XXX: Trigger control event. */
+ hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
+ "UNEXPECTED");
break;
}
@@ -3390,7 +3414,7 @@ connection_dir_process_inbuf(dir_connection_t *conn)
}
/** We are closing a dir connection: If <b>dir_conn</b> is a dir connection
- * that tried to fetch an HS descriptor, check if it successfuly fetched it,
+ * that tried to fetch an HS descriptor, check if it successfully fetched it,
* or if we need to try again. */
static void
refetch_hsdesc_if_needed(dir_connection_t *dir_conn)
@@ -3477,63 +3501,47 @@ write_http_response_header_impl(dir_connection_t *conn, ssize_t length,
long cache_lifetime)
{
char date[RFC1123_TIME_LEN+1];
- char tmp[1024];
- char *cp;
time_t now = time(NULL);
+ buf_t *buf = buf_new_with_capacity(1024);
tor_assert(conn);
format_rfc1123_time(date, now);
- cp = tmp;
- tor_snprintf(cp, sizeof(tmp),
- "HTTP/1.0 200 OK\r\nDate: %s\r\n",
- date);
- cp += strlen(tmp);
+
+ buf_add_printf(buf, "HTTP/1.0 200 OK\r\nDate: %s\r\n", date);
if (type) {
- tor_snprintf(cp, sizeof(tmp)-(cp-tmp), "Content-Type: %s\r\n", type);
- cp += strlen(cp);
+ buf_add_printf(buf, "Content-Type: %s\r\n", type);
}
if (!is_local_addr(&conn->base_.addr)) {
/* Don't report the source address for a nearby/private connection.
* Otherwise we tend to mis-report in cases where incoming ports are
* being forwarded to a Tor server running behind the firewall. */
- tor_snprintf(cp, sizeof(tmp)-(cp-tmp),
- X_ADDRESS_HEADER "%s\r\n", conn->base_.address);
- cp += strlen(cp);
+ buf_add_printf(buf, X_ADDRESS_HEADER "%s\r\n", conn->base_.address);
}
if (encoding) {
- tor_snprintf(cp, sizeof(tmp)-(cp-tmp),
- "Content-Encoding: %s\r\n", encoding);
- cp += strlen(cp);
+ buf_add_printf(buf, "Content-Encoding: %s\r\n", encoding);
}
if (length >= 0) {
- tor_snprintf(cp, sizeof(tmp)-(cp-tmp),
- "Content-Length: %ld\r\n", (long)length);
- cp += strlen(cp);
+ buf_add_printf(buf, "Content-Length: %ld\r\n", (long)length);
}
if (cache_lifetime > 0) {
char expbuf[RFC1123_TIME_LEN+1];
format_rfc1123_time(expbuf, (time_t)(now + cache_lifetime));
/* We could say 'Cache-control: max-age=%d' here if we start doing
* http/1.1 */
- tor_snprintf(cp, sizeof(tmp)-(cp-tmp),
- "Expires: %s\r\n", expbuf);
- cp += strlen(cp);
+ buf_add_printf(buf, "Expires: %s\r\n", expbuf);
} else if (cache_lifetime == 0) {
/* We could say 'Cache-control: no-cache' here if we start doing
* http/1.1 */
- strlcpy(cp, "Pragma: no-cache\r\n", sizeof(tmp)-(cp-tmp));
- cp += strlen(cp);
+ buf_add_string(buf, "Pragma: no-cache\r\n");
}
if (extra_headers) {
- strlcpy(cp, extra_headers, sizeof(tmp)-(cp-tmp));
- cp += strlen(cp);
+ buf_add_string(buf, extra_headers);
}
- if (sizeof(tmp)-(cp-tmp) > 3)
- memcpy(cp, "\r\n", 3);
- else
- tor_assert(0);
- connection_buf_add(tmp, strlen(tmp), TO_CONN(conn));
+ buf_add_string(buf, "\r\n");
+
+ connection_buf_add_buf(TO_CONN(conn), buf);
+ buf_free(buf);
}
/** As write_http_response_header_impl, but sets encoding and content-typed
@@ -4030,7 +4038,7 @@ find_best_diff(const smartlist_t *digests, int flav,
}
/** Lookup the cached consensus document by the flavor found in <b>flav</b>.
- * The prefered set of compression methods should be listed in the
+ * The preferred set of compression methods should be listed in the
* <b>compression_methods</b> bitfield. The compression method chosen (if any)
* is stored in <b>compression_used_out</b>. */
static struct consensus_cache_entry_t *
@@ -4942,7 +4950,7 @@ handle_get_robots(dir_connection_t *conn, const get_handler_args_t *args)
/* Given the <b>url</b> from a POST request, try to extract the version number
* using the provided <b>prefix</b>. The version should be after the prefix and
- * ending with the seperator "/". For instance:
+ * ending with the separator "/". For instance:
* /tor/hs/3/publish
*
* On success, <b>end_pos</b> points to the position right after the version
@@ -5356,17 +5364,14 @@ find_dl_schedule(const download_status_t *dls, const or_options_t *options)
return NULL;
}
-/** Decide which minimum and maximum delay step we want to use based on
+/** Decide which minimum delay step we want to use based on
* descriptor type in <b>dls</b> and <b>options</b>.
* Helper function for download_status_schedule_get_delay(). */
-STATIC void
-find_dl_min_and_max_delay(download_status_t *dls, const or_options_t *options,
- int *min, int *max)
+STATIC int
+find_dl_min_delay(download_status_t *dls, const or_options_t *options)
{
tor_assert(dls);
tor_assert(options);
- tor_assert(min);
- tor_assert(max);
/*
* For now, just use the existing schedule config stuff and pick the
@@ -5374,13 +5379,7 @@ find_dl_min_and_max_delay(download_status_t *dls, const or_options_t *options,
*/
const smartlist_t *schedule = find_dl_schedule(dls, options);
tor_assert(schedule != NULL && smartlist_len(schedule) >= 2);
- *min = *((int *)(smartlist_get(schedule, 0)));
- /* Increment on failure schedules always use exponential backoff, but they
- * have a smaller limit when they're deterministic */
- if (dls->backoff == DL_SCHED_DETERMINISTIC)
- *max = *((int *)((smartlist_get(schedule, smartlist_len(schedule) - 1))));
- else
- *max = INT_MAX;
+ return *(int *)(smartlist_get(schedule, 0));
}
/** As next_random_exponential_delay() below, but does not compute a random
@@ -5418,55 +5417,39 @@ next_random_exponential_delay_range(int *low_bound_out,
* zero); the <b>backoff_position</b> parameter is the number of times we've
* generated a delay; and the <b>delay</b> argument is the most recently used
* delay.
- *
- * Requires that delay is less than INT_MAX, and delay is in [0,max_delay].
*/
STATIC int
next_random_exponential_delay(int delay,
- int base_delay,
- int max_delay)
+ int base_delay)
{
/* Check preconditions */
- if (BUG(max_delay < 0))
- max_delay = 0;
- if (BUG(delay > max_delay))
- delay = max_delay;
if (BUG(delay < 0))
delay = 0;
if (base_delay < 1)
base_delay = 1;
- int low_bound=0, high_bound=max_delay;
+ int low_bound=0, high_bound=INT_MAX;
next_random_exponential_delay_range(&low_bound, &high_bound,
delay, base_delay);
- int rand_delay = crypto_rand_int_range(low_bound, high_bound);
-
- return MIN(rand_delay, max_delay);
+ return crypto_rand_int_range(low_bound, high_bound);
}
-/** Find the current delay for dls based on schedule or min_delay/
- * max_delay if we're using exponential backoff. If dls->backoff is
- * DL_SCHED_RANDOM_EXPONENTIAL, we must have 0 <= min_delay <= max_delay <=
- * INT_MAX, but schedule may be set to NULL; otherwise schedule is required.
+/** Find the current delay for dls based on min_delay.
+ *
* This function sets dls->next_attempt_at based on now, and returns the delay.
* Helper for download_status_increment_failure and
* download_status_increment_attempt. */
STATIC int
download_status_schedule_get_delay(download_status_t *dls,
- const smartlist_t *schedule,
- int min_delay, int max_delay,
+ int min_delay,
time_t now)
{
tor_assert(dls);
- /* We don't need a schedule if we're using random exponential backoff */
- tor_assert(dls->backoff == DL_SCHED_RANDOM_EXPONENTIAL ||
- schedule != NULL);
/* If we're using random exponential backoff, we do need min/max delay */
- tor_assert(dls->backoff != DL_SCHED_RANDOM_EXPONENTIAL ||
- (min_delay >= 0 && max_delay >= min_delay));
+ tor_assert(min_delay >= 0);
int delay = INT_MAX;
uint8_t dls_schedule_position = (dls->increment_on
@@ -5474,42 +5457,32 @@ download_status_schedule_get_delay(download_status_t *dls,
? dls->n_download_attempts
: dls->n_download_failures);
- if (dls->backoff == DL_SCHED_DETERMINISTIC) {
- if (dls_schedule_position < smartlist_len(schedule))
- delay = *(int *)smartlist_get(schedule, dls_schedule_position);
- else if (dls_schedule_position == IMPOSSIBLE_TO_DOWNLOAD)
- delay = INT_MAX;
- else
- delay = *(int *)smartlist_get(schedule, smartlist_len(schedule) - 1);
- } else if (dls->backoff == DL_SCHED_RANDOM_EXPONENTIAL) {
- /* Check if we missed a reset somehow */
- IF_BUG_ONCE(dls->last_backoff_position > dls_schedule_position) {
- dls->last_backoff_position = 0;
- dls->last_delay_used = 0;
- }
+ /* Check if we missed a reset somehow */
+ IF_BUG_ONCE(dls->last_backoff_position > dls_schedule_position) {
+ dls->last_backoff_position = 0;
+ dls->last_delay_used = 0;
+ }
- if (dls_schedule_position > 0) {
- delay = dls->last_delay_used;
+ if (dls_schedule_position > 0) {
+ delay = dls->last_delay_used;
- while (dls->last_backoff_position < dls_schedule_position) {
- /* Do one increment step */
- delay = next_random_exponential_delay(delay, min_delay, max_delay);
- /* Update our position */
- ++(dls->last_backoff_position);
- }
- } else {
- /* If we're just starting out, use the minimum delay */
- delay = min_delay;
+ while (dls->last_backoff_position < dls_schedule_position) {
+ /* Do one increment step */
+ delay = next_random_exponential_delay(delay, min_delay);
+ /* Update our position */
+ ++(dls->last_backoff_position);
}
+ } else {
+ /* If we're just starting out, use the minimum delay */
+ delay = min_delay;
+ }
- /* Clamp it within min/max if we have them */
- if (min_delay >= 0 && delay < min_delay) delay = min_delay;
- if (max_delay != INT_MAX && delay > max_delay) delay = max_delay;
+ /* Clamp it within min/max if we have them */
+ if (min_delay >= 0 && delay < min_delay) delay = min_delay;
- /* Store it for next time */
- dls->last_backoff_position = dls_schedule_position;
- dls->last_delay_used = delay;
- }
+ /* Store it for next time */
+ dls->last_backoff_position = dls_schedule_position;
+ dls->last_delay_used = delay;
/* A negative delay makes no sense. Knowing that delay is
* non-negative allows us to safely do the wrapping check below. */
@@ -5572,7 +5545,7 @@ download_status_increment_failure(download_status_t *dls, int status_code,
(void) status_code; // XXXX no longer used.
(void) server; // XXXX no longer used.
int increment = -1;
- int min_delay = 0, max_delay = INT_MAX;
+ int min_delay = 0;
tor_assert(dls);
@@ -5597,10 +5570,8 @@ download_status_increment_failure(download_status_t *dls, int status_code,
/* only return a failure retry time if this schedule increments on failures
*/
- const smartlist_t *schedule = find_dl_schedule(dls, get_options());
- find_dl_min_and_max_delay(dls, get_options(), &min_delay, &max_delay);
- increment = download_status_schedule_get_delay(dls, schedule,
- min_delay, max_delay, now);
+ min_delay = find_dl_min_delay(dls, get_options());
+ increment = download_status_schedule_get_delay(dls, min_delay, now);
}
download_status_log_helper(item, !dls->increment_on, "failed",
@@ -5631,7 +5602,7 @@ download_status_increment_attempt(download_status_t *dls, const char *item,
time_t now)
{
int delay = -1;
- int min_delay = 0, max_delay = INT_MAX;
+ int min_delay = 0;
tor_assert(dls);
@@ -5651,10 +5622,8 @@ download_status_increment_attempt(download_status_t *dls, const char *item,
if (dls->n_download_attempts < IMPOSSIBLE_TO_DOWNLOAD-1)
++dls->n_download_attempts;
- const smartlist_t *schedule = find_dl_schedule(dls, get_options());
- find_dl_min_and_max_delay(dls, get_options(), &min_delay, &max_delay);
- delay = download_status_schedule_get_delay(dls, schedule,
- min_delay, max_delay, now);
+ min_delay = find_dl_min_delay(dls, get_options());
+ delay = download_status_schedule_get_delay(dls, min_delay, now);
download_status_log_helper(item, dls->increment_on, "attempted",
"on failure", dls->n_download_attempts,
@@ -5763,8 +5732,7 @@ dir_routerdesc_download_failed(smartlist_t *failed, int status_code,
} else {
dls = router_get_dl_status_by_descriptor_digest(digest);
}
- if (!dls || dls->n_download_failures >=
- get_options()->TestingDescriptorMaxDownloadTries)
+ if (!dls)
continue;
download_status_increment_failure(dls, status_code, cp, server, now);
} SMARTLIST_FOREACH_END(cp);
@@ -5801,10 +5769,6 @@ dir_microdesc_download_failed(smartlist_t *failed,
if (!rs)
continue;
dls = &rs->dl_status;
- if (dls->n_download_failures >=
- get_options()->TestingMicrodescMaxDownloadTries) {
- continue;
- }
{ /* Increment the failure count for this md fetch */
char buf[BASE64_DIGEST256_LEN+1];
diff --git a/src/or/directory.h b/src/or/directory.h
index 5e6a91d3e7..aa4d29a5bb 100644
--- a/src/or/directory.h
+++ b/src/or/directory.h
@@ -51,7 +51,9 @@ int directory_must_use_begindir(const or_options_t *options);
*/
typedef struct directory_request_t directory_request_t;
directory_request_t *directory_request_new(uint8_t dir_purpose);
-void directory_request_free(directory_request_t *req);
+void directory_request_free_(directory_request_t *req);
+#define directory_request_free(req) \
+ FREE_AND_NULL(directory_request_t, directory_request_free_, (req))
void directory_request_set_or_addr_port(directory_request_t *req,
const tor_addr_port_t *p);
void directory_request_set_dir_addr_port(directory_request_t *req,
@@ -130,29 +132,19 @@ time_t download_status_increment_attempt(download_status_t *dls,
time(NULL))
void download_status_reset(download_status_t *dls);
-static int download_status_is_ready(download_status_t *dls, time_t now,
- int max_failures);
+static int download_status_is_ready(download_status_t *dls, time_t now);
time_t download_status_get_next_attempt_at(const download_status_t *dls);
/** Return true iff, as of <b>now</b>, the resource tracked by <b>dls</b> is
* ready to get its download reattempted. */
static inline int
-download_status_is_ready(download_status_t *dls, time_t now,
- int max_failures)
+download_status_is_ready(download_status_t *dls, time_t now)
{
/* dls wasn't reset before it was used */
if (dls->next_attempt_at == 0) {
download_status_reset(dls);
}
- if (dls->backoff == DL_SCHED_DETERMINISTIC) {
- /* Deterministic schedules can hit an endpoint; exponential backoff
- * schedules just wait longer and longer. */
- int under_failure_limit = (dls->n_download_failures <= max_failures
- && dls->n_download_attempts <= max_failures);
- if (!under_failure_limit)
- return 0;
- }
return download_status_get_next_attempt_at(dls) <= now;
}
@@ -238,6 +230,9 @@ STATIC int handle_response_fetch_hsdesc_v3(dir_connection_t *conn,
STATIC int handle_response_fetch_microdesc(dir_connection_t *conn,
const response_handler_args_t *args);
+STATIC int handle_response_fetch_consensus(dir_connection_t *conn,
+ const response_handler_args_t *args);
+
#endif /* defined(DIRECTORY_PRIVATE) */
#ifdef TOR_UNIT_TESTS
@@ -255,8 +250,7 @@ MOCK_DECL(STATIC int, directory_handle_command_post,(dir_connection_t *conn,
const char *body,
size_t body_len));
STATIC int download_status_schedule_get_delay(download_status_t *dls,
- const smartlist_t *schedule,
- int min_delay, int max_delay,
+ int min_delay,
time_t now);
STATIC int handle_post_hs_descriptor(const char *url, const char *body);
@@ -267,13 +261,11 @@ STATIC int should_use_directory_guards(const or_options_t *options);
STATIC compression_level_t choose_compression_level(ssize_t n_bytes);
STATIC const smartlist_t *find_dl_schedule(const download_status_t *dls,
const or_options_t *options);
-STATIC void find_dl_min_and_max_delay(download_status_t *dls,
- const or_options_t *options,
- int *min, int *max);
+STATIC int find_dl_min_delay(download_status_t *dls,
+ const or_options_t *options);
STATIC int next_random_exponential_delay(int delay,
- int base_delay,
- int max_delay);
+ int base_delay);
STATIC void next_random_exponential_delay_range(int *low_bound_out,
int *high_bound_out,
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index a1ccf03e91..ced40899d0 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -43,7 +43,7 @@
* the directory authority functionality. The directory.c module delegates
* here in order to handle incoming requests from clients, via
* connection_dirserv_flushed_some() and its kin. In order to save RAM, this
- * module is reponsible for spooling directory objects (in whole or in part)
+ * module is responsible for spooling directory objects (in whole or in part)
* onto buf_t instances, and then closing the dir_connection_t once the
* objects are totally flushed.
*
@@ -1122,7 +1122,7 @@ router_is_active(const routerinfo_t *ri, const node_t *node, time_t now)
if (!node->is_running || !node->is_valid || ri->is_hibernating) {
return 0;
}
- /* Only require bandwith capacity in non-test networks, or
+ /* Only require bandwidth capacity in non-test networks, or
* if TestingTorNetwork, and TestingMinExitFlagThreshold is non-zero */
if (!ri->bandwidthcapacity) {
if (get_options()->TestingTorNetwork) {
@@ -1555,15 +1555,21 @@ dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil)
node->ri &&
node->ri->purpose != ROUTER_PURPOSE_BRIDGE)
continue;
+
+ routerinfo_t *ri = node->ri;
+ if (ri) {
+ node->is_exit = (!router_exit_policy_rejects_all(ri) &&
+ exit_policy_is_general_exit(ri->exit_policy));
+ }
+
if (router_counts_toward_thresholds(node, now, omit_as_sybil,
require_mbw)) {
- routerinfo_t *ri = node->ri;
const char *id = node->identity;
uint32_t bw_kb;
+
/* resolve spurious clang shallow analysis null pointer errors */
tor_assert(ri);
- node->is_exit = (!router_exit_policy_rejects_all(ri) &&
- exit_policy_is_general_exit(ri->exit_policy));
+
uptimes[n_active] = (uint32_t)real_uptime(ri, now);
mtbfs[n_active] = rep_hist_get_stability(id, now);
tks [n_active] = rep_hist_get_weighted_time_known(id, now);
@@ -1933,21 +1939,28 @@ version_from_platform(const char *platform)
/** Helper: write the router-status information in <b>rs</b> into a newly
* allocated character buffer. Use the same format as in network-status
* documents. If <b>version</b> is non-NULL, add a "v" line for the platform.
+ *
+ * consensus_method is the current consensus method when format is
+ * NS_V3_CONSENSUS or NS_V3_CONSENSUS_MICRODESC. It is ignored for other
+ * formats: pass ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD.
+ *
* Return 0 on success, -1 on failure.
*
* The format argument has one of the following values:
* NS_V2 - Output an entry suitable for a V2 NS opinion document
* NS_V3_CONSENSUS - Output the first portion of a V3 NS consensus entry
+ * for consensus_method.
* NS_V3_CONSENSUS_MICRODESC - Output the first portion of a V3 microdesc
- * consensus entry.
+ * consensus entry for consensus_method.
* NS_V3_VOTE - Output a complete V3 NS vote. If <b>vrs</b> is present,
* it contains additional information for the vote.
- * NS_CONTROL_PORT - Output a NS document for the control port
+ * NS_CONTROL_PORT - Output a NS document for the control port.
*/
char *
routerstatus_format_entry(const routerstatus_t *rs, const char *version,
const char *protocols,
routerstatus_format_type_t format,
+ int consensus_method,
const vote_routerstatus_t *vrs)
{
char *summary;
@@ -1978,8 +1991,10 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version,
* networkstatus_type_t values, with an additional control port value
* added -MP */
- /* V3 microdesc consensuses don't have "a" lines. */
- if (format == NS_V3_CONSENSUS_MICRODESC)
+ /* V3 microdesc consensuses only have "a" lines in later consensus methods
+ */
+ if (format == NS_V3_CONSENSUS_MICRODESC &&
+ consensus_method < MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS)
goto done;
/* Possible "a" line. At most one for now. */
@@ -1988,7 +2003,7 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version,
fmt_addrport(&rs->ipv6_addr, rs->ipv6_orport));
}
- if (format == NS_V3_CONSENSUS)
+ if (format == NS_V3_CONSENSUS || format == NS_V3_CONSENSUS_MICRODESC)
goto done;
smartlist_add_asprintf(chunks,
@@ -2249,7 +2264,8 @@ routers_make_ed_keys_unique(smartlist_t *routers)
}
/** Extract status information from <b>ri</b> and from other authority
- * functions and store it in <b>rs</b>>.
+ * functions and store it in <b>rs</b>. <b>rs</b> is zeroed out before it is
+ * set.
*
* We assume that ri-\>is_running has already been set, e.g. by
* dirserv_set_router_is_running(ri, now);
@@ -2280,6 +2296,7 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs,
rs->is_valid = node->is_valid;
if (node->is_fast && node->is_stable &&
+ ri->supports_tunnelled_dir_requests &&
((options->AuthDirGuardBWGuarantee &&
routerbw_kb >= options->AuthDirGuardBWGuarantee/1000) ||
routerbw_kb >= MIN(guard_bandwidth_including_exits_kb,
@@ -2315,6 +2332,9 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs,
OR port and it's reachable so copy it to the routerstatus. */
tor_addr_copy(&rs->ipv6_addr, &ri->ipv6_addr);
rs->ipv6_orport = ri->ipv6_orport;
+ } else {
+ tor_addr_make_null(&rs->ipv6_addr, AF_INET6);
+ rs->ipv6_orport = 0;
}
if (options->TestingTorNetwork) {
@@ -2975,6 +2995,12 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
microdescriptors = smartlist_new();
SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) {
+ /* If it has a protover list and contains a protocol name greater than
+ * MAX_PROTOCOL_NAME_LENGTH, skip it. */
+ if (ri->protocol_list &&
+ protover_contains_long_protocol_names(ri->protocol_list)) {
+ continue;
+ }
if (ri->cache_info.published_on >= cutoff) {
routerstatus_t *rs;
vote_routerstatus_t *vrs;
@@ -3331,7 +3357,7 @@ dirserv_orconn_tls_done(const tor_addr_t *addr,
ri = node->ri;
if (get_options()->AuthDirTestEd25519LinkKeys &&
- node_supports_ed25519_link_authentication(node) &&
+ node_supports_ed25519_link_authentication(node, 1) &&
ri->cache_info.signing_key_cert) {
/* We allow the node to have an ed25519 key if we haven't been told one in
* the routerinfo, but if we *HAVE* been told one in the routerinfo, it
@@ -3414,7 +3440,8 @@ dirserv_single_reachability_test(time_t now, routerinfo_t *router)
tor_assert(node);
if (options->AuthDirTestEd25519LinkKeys &&
- node_supports_ed25519_link_authentication(node)) {
+ node_supports_ed25519_link_authentication(node, 1) &&
+ router->cache_info.signing_key_cert) {
ed_id_key = &router->cache_info.signing_key_cert->signing_key;
} else {
ed_id_key = NULL;
@@ -3543,7 +3570,7 @@ spooled_resource_new_from_cache_entry(consensus_cache_entry_t *entry)
/** Release all storage held by <b>spooled</b>. */
void
-spooled_resource_free(spooled_resource_t *spooled)
+spooled_resource_free_(spooled_resource_t *spooled)
{
if (spooled == NULL)
return;
diff --git a/src/or/dirserv.h b/src/or/dirserv.h
index 5f195442ce..cb7c628387 100644
--- a/src/or/dirserv.h
+++ b/src/or/dirserv.h
@@ -123,7 +123,7 @@ void dirserv_set_cached_consensus_networkstatus(const char *consensus,
void dirserv_clear_old_networkstatuses(time_t cutoff);
int dirserv_get_routerdesc_spool(smartlist_t *spools_out, const char *key,
dir_spool_source_t source,
- int conn_is_encrytped,
+ int conn_is_encrypted,
const char **msg_out);
int dirserv_get_routerdescs(smartlist_t *descs_out, const char *key,
const char **msg);
@@ -150,6 +150,7 @@ char *routerstatus_format_entry(
const char *version,
const char *protocols,
routerstatus_format_type_t format,
+ int consensus_method,
const vote_routerstatus_t *vrs);
void dirserv_free_all(void);
void cached_dir_decref(cached_dir_t *d);
@@ -195,7 +196,9 @@ spooled_resource_t *spooled_resource_new(dir_spool_source_t source,
size_t digestlen);
spooled_resource_t *spooled_resource_new_from_cache_entry(
struct consensus_cache_entry_t *entry);
-void spooled_resource_free(spooled_resource_t *spooled);
+void spooled_resource_free_(spooled_resource_t *spooled);
+#define spooled_resource_free(sp) \
+ FREE_AND_NULL(spooled_resource_t, spooled_resource_free_, (sp))
void dirserv_spool_remove_missing_and_guess_size(dir_connection_t *conn,
time_t cutoff,
int compression,
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index fbd112d488..7023d4f951 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -278,7 +278,9 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
vote_microdesc_hash_t *h;
rsf = routerstatus_format_entry(&vrs->status,
vrs->version, vrs->protocols,
- NS_V3_VOTE, vrs);
+ NS_V3_VOTE,
+ ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD,
+ vrs);
if (rsf)
smartlist_add(chunks, rsf);
@@ -519,7 +521,7 @@ compute_routerstatus_consensus(smartlist_t *votes, int consensus_method,
/* compare_vote_rs_() sorts the items by identity digest (all the same),
* then by SD digest. That way, if we have a tie that the published_on
- * date cannot tie, we use the descriptor with the smaller digest.
+ * date cannot break, we use the descriptor with the smaller digest.
*/
smartlist_sort(votes, compare_vote_rs_);
SMARTLIST_FOREACH_BEGIN(votes, vote_routerstatus_t *, rs) {
@@ -663,7 +665,7 @@ static int
consensus_method_is_supported(int method)
{
if (method == MIN_METHOD_FOR_ED25519_ID_IN_MD) {
- /* This method was broken due to buggy code accidently left in
+ /* This method was broken due to buggy code accidentally left in
* dircollate.c; do not actually use it.
*/
return 0;
@@ -803,6 +805,9 @@ dirvote_compute_params(smartlist_t *votes, int method, int total_authorities)
output = smartlist_new();
SMARTLIST_FOREACH_BEGIN(param_list, const char *, param) {
+ /* resolve spurious clang shallow analysis null pointer errors */
+ tor_assert(param);
+
const char *next_param;
int ok=0;
eq = strchr(param, '=');
@@ -815,8 +820,7 @@ dirvote_compute_params(smartlist_t *votes, int method, int total_authorities)
next_param = NULL;
else
next_param = smartlist_get(param_list, param_sl_idx+1);
- /* resolve spurious clang shallow analysis null pointer errors */
- tor_assert(param);
+
if (!next_param || strncmp(next_param, param, cur_param_len)) {
/* We've reached the end of a series. */
/* Make sure enough authorities voted on this param, unless the
@@ -1323,8 +1327,9 @@ compute_nth_protocol_set(int n, int n_voters, const smartlist_t *votes)
/** Given a list of vote networkstatus_t in <b>votes</b>, our public
* authority <b>identity_key</b>, our private authority <b>signing_key</b>,
* and the number of <b>total_authorities</b> that we believe exist in our
- * voting quorum, generate the text of a new v3 consensus vote, and return the
- * value in a newly allocated string.
+ * voting quorum, generate the text of a new v3 consensus or microdescriptor
+ * consensus (depending on <b>flavor</b>), and return the value in a newly
+ * allocated string.
*
* Note: this function DOES NOT check whether the votes are from
* recognized authorities. (dirvote_add_vote does that.)
@@ -2107,7 +2112,8 @@ networkstatus_compute_consensus(smartlist_t *votes,
char *buf;
/* Okay!! Now we can write the descriptor... */
/* First line goes into "buf". */
- buf = routerstatus_format_entry(&rs_out, NULL, NULL, rs_format, NULL);
+ buf = routerstatus_format_entry(&rs_out, NULL, NULL,
+ rs_format, consensus_method, NULL);
if (buf)
smartlist_add(chunks, buf);
}
@@ -2693,7 +2699,7 @@ get_detached_signatures_from_pending_consensuses(pending_consensus_t *pending,
/** Release all storage held in <b>s</b>. */
void
-ns_detached_signatures_free(ns_detached_signatures_t *s)
+ns_detached_signatures_free_(ns_detached_signatures_t *s)
{
if (!s)
return;
@@ -2851,10 +2857,13 @@ get_voting_schedule(const or_options_t *options, time_t now, int severity)
return new_voting_schedule;
}
+#define voting_schedule_free(s) \
+ FREE_AND_NULL(voting_schedule_t, voting_schedule_free_, (s))
+
/** Frees a voting_schedule_t. This should be used instead of the generic
* tor_free. */
static void
-voting_schedule_free(voting_schedule_t *voting_schedule_to_free)
+voting_schedule_free_(voting_schedule_t *voting_schedule_to_free)
{
if (!voting_schedule_to_free)
return;
@@ -3559,7 +3568,13 @@ dirvote_add_signatures_to_pending_consensus(
}
r = networkstatus_add_detached_signatures(pc->consensus, sigs,
source, severity, msg_out);
- log_info(LD_DIR,"Added %d signatures to consensus.", r);
+ if (r >= 0) {
+ log_info(LD_DIR,"Added %d signatures to consensus.", r);
+ } else {
+ log_fn(LOG_PROTOCOL_WARN, LD_DIR,
+ "Unable to add signatures to consensus: %s",
+ *msg_out ? *msg_out : "(unknown)");
+ }
if (r >= 1) {
char *new_signatures =
@@ -3837,7 +3852,10 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf);
}
+ /* We originally put a lines in the micrdescriptors, but then we worked out
+ * that we needed them in the microdesc consensus. See #20916. */
if (consensus_method >= MIN_METHOD_FOR_A_LINES &&
+ consensus_method < MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC &&
!tor_addr_is_null(&ri->ipv6_addr) && ri->ipv6_orport)
smartlist_add_asprintf(chunks, "a %s\n",
fmt_addrport(&ri->ipv6_addr, ri->ipv6_orport));
@@ -3946,7 +3964,9 @@ static const struct consensus_method_range_t {
{MIN_METHOD_FOR_P6_LINES, MIN_METHOD_FOR_NTOR_KEY - 1},
{MIN_METHOD_FOR_NTOR_KEY, MIN_METHOD_FOR_ID_HASH_IN_MD - 1},
{MIN_METHOD_FOR_ID_HASH_IN_MD, MIN_METHOD_FOR_ED25519_ID_IN_MD - 1},
- {MIN_METHOD_FOR_ED25519_ID_IN_MD, MAX_SUPPORTED_CONSENSUS_METHOD},
+ {MIN_METHOD_FOR_ED25519_ID_IN_MD,
+ MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC - 1},
+ {MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC, MAX_SUPPORTED_CONSENSUS_METHOD},
{-1, -1}
};
diff --git a/src/or/dirvote.h b/src/or/dirvote.h
index 72a35fea6d..deeb27bfe1 100644
--- a/src/or/dirvote.h
+++ b/src/or/dirvote.h
@@ -51,11 +51,15 @@
#define MIN_VOTE_INTERVAL_TESTING_INITIAL \
((MIN_VOTE_SECONDS_TESTING)+(MIN_DIST_SECONDS_TESTING)+1)
+/* A placeholder for routerstatus_format_entry() when the consensus method
+ * argument is not applicable. */
+#define ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD 0
+
/** The lowest consensus method that we currently support. */
#define MIN_SUPPORTED_CONSENSUS_METHOD 13
/** The highest consensus method that we currently support. */
-#define MAX_SUPPORTED_CONSENSUS_METHOD 26
+#define MAX_SUPPORTED_CONSENSUS_METHOD 28
/** Lowest consensus method where microdesc consensuses omit any entry
* with no microdesc. */
@@ -115,6 +119,14 @@
* instead of 0. See #14881 */
#define MIN_METHOD_FOR_INIT_BW_WEIGHTS_ONE 26
+/** Lowest consensus method where the microdesc consensus contains relay IPv6
+ * addresses. See #23826 and #20916. */
+#define MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS 27
+
+/** Lowest consensus method where microdescriptors do not contain relay IPv6
+ * addresses. See #23828 and #20916. */
+#define MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC 28
+
/** Default bandwidth to clip unmeasured bandwidths to using method >=
* MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not
* get confused with the above macros.) */
@@ -136,7 +148,9 @@ int networkstatus_add_detached_signatures(networkstatus_t *target,
int severity,
const char **msg_out);
char *networkstatus_get_detached_signatures(smartlist_t *consensuses);
-void ns_detached_signatures_free(ns_detached_signatures_t *s);
+void ns_detached_signatures_free_(ns_detached_signatures_t *s);
+#define ns_detached_signatures_free(s) \
+ FREE_AND_NULL(ns_detached_signatures_t, ns_detached_signatures_free_, (s))
/* cert manipulation */
authority_cert_t *authority_cert_dup(authority_cert_t *cert);
diff --git a/src/or/dns.c b/src/or/dns.c
index 7dc3575f53..411e2d5aa6 100644
--- a/src/or/dns.c
+++ b/src/or/dns.c
@@ -19,7 +19,7 @@
* dns_seems_to_be_broken().
* <li>When a client has asked the relay, in a RELAY_BEGIN cell, to connect
* to a given server by hostname. This happens via dns_resolve().
- * <li>When a client has asked the rela, in a RELAY_RESOLVE cell, to look
+ * <li>When a client has asked the relay, in a RELAY_RESOLVE cell, to look
* up a given server's IP address(es) by hostname. This also happens via
* dns_resolve().
* </ol>
@@ -457,7 +457,7 @@ purge_expired_resolves(time_t now)
if (!pendconn->base_.marked_for_close) {
connection_edge_end(pendconn, END_STREAM_REASON_TIMEOUT);
circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn);
- connection_free(TO_CONN(pendconn));
+ connection_free_(TO_CONN(pendconn));
}
tor_free(pend);
}
@@ -670,7 +670,7 @@ dns_resolve(edge_connection_t *exitconn)
/* If we made the connection pending, then we freed it already in
* dns_cancel_pending_resolve(). If we marked it for close, it'll
* get freed from the main loop. Otherwise, can free it now. */
- connection_free(TO_CONN(exitconn));
+ connection_free_(TO_CONN(exitconn));
}
break;
default:
@@ -1101,7 +1101,7 @@ dns_cancel_pending_resolve,(const char *address))
if (circ)
circuit_detach_stream(circ, pendconn);
if (!pendconn->base_.marked_for_close)
- connection_free(TO_CONN(pendconn));
+ connection_free_(TO_CONN(pendconn));
resolve->pending_connections = pend->next;
tor_free(pend);
}
@@ -1230,7 +1230,7 @@ inform_pending_connections(cached_resolve_t *resolve)
/* This detach must happen after we send the resolved cell. */
circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn);
}
- connection_free(TO_CONN(pendconn));
+ connection_free_(TO_CONN(pendconn));
} else {
circuit_t *circ;
if (pendconn->base_.purpose == EXIT_PURPOSE_CONNECT) {
@@ -1259,7 +1259,7 @@ inform_pending_connections(cached_resolve_t *resolve)
circ = circuit_get_by_edge_conn(pendconn);
tor_assert(circ);
circuit_detach_stream(circ, pendconn);
- connection_free(TO_CONN(pendconn));
+ connection_free_(TO_CONN(pendconn));
}
}
resolve->pending_connections = pend->next;
@@ -1441,27 +1441,30 @@ configure_nameservers(int force)
// If we only have one nameserver, it does not make sense to back off
// from it for a timeout. Unfortunately, the value for max-timeouts is
// currently clamped by libevent to 255, but it does not hurt to set
- // it higher in case libevent gets a patch for this.
- // Reducing attempts in the case of just one name server too, because
- // it is very likely to be a local one where a network connectivity
- // issue should not cause an attempt to fail.
+ // it higher in case libevent gets a patch for this. Higher-than-
+ // default maximum of 3 with multiple nameservers to avoid spuriously
+ // marking one down on bursts of timeouts resulting from scans/attacks
+ // against non-responding authoritative DNS servers.
if (evdns_base_count_nameservers(the_evdns_base) == 1) {
SET("max-timeouts:", "1000000");
- SET("attempts:", "1");
} else {
- SET("max-timeouts:", "3");
+ SET("max-timeouts:", "10");
}
// Elongate the queue of maximum inflight dns requests, so if a bunch
- // time out at the resolver (happens commonly with unbound) we won't
+ // remain pending at the resolver (happens commonly with Unbound) we won't
// stall every other DNS request. This potentially means some wasted
// CPU as there's a walk over a linear queue involved, but this is a
// much better tradeoff compared to just failing DNS requests because
// of a full queue.
SET("max-inflight:", "8192");
- // Time out after 5 seconds if no reply.
+ // Two retries at 5 and 10 seconds for bind9/named which relies on
+ // clients to handle retries. Second retry for retried circuits with
+ // extended 15 second timeout. Superfluous with local-system Unbound
+ // instance--has its own elaborate retry scheme.
SET("timeout:", "5");
+ SET("attempts:","3");
if (options->ServerDNSRandomizeCase)
SET("randomize-case:", "1");
@@ -1666,7 +1669,7 @@ launch_resolve,(cached_resolve_t *resolve))
tor_addr_t a;
int r;
- if (get_options()->DisableNetwork)
+ if (net_is_disabled())
return -1;
/* What? Nameservers not configured? Sounds like a bug. */
@@ -1901,7 +1904,7 @@ launch_test_addresses(evutil_socket_t fd, short event, void *args)
(void)event;
(void)args;
- if (options->DisableNetwork)
+ if (net_is_disabled())
return;
log_info(LD_EXIT, "Launching checks to see whether our nameservers like to "
diff --git a/src/or/dnsserv.c b/src/or/dnsserv.c
index d254717a54..7e344deeab 100644
--- a/src/or/dnsserv.c
+++ b/src/or/dnsserv.c
@@ -172,7 +172,7 @@ evdns_server_callback(struct evdns_server_request *req, void *data_)
if (connection_add(ENTRY_TO_CONN(entry_conn)) < 0) {
log_warn(LD_APP, "Couldn't register dummy connection for DNS request");
evdns_server_request_respond(req, DNS_ERR_SERVERFAILED);
- connection_free(ENTRY_TO_CONN(entry_conn));
+ connection_free_(ENTRY_TO_CONN(entry_conn));
return;
}
@@ -208,6 +208,7 @@ dnsserv_launch_request(const char *name, int reverse,
/* Make a new dummy AP connection, and attach the request to it. */
entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET);
+ entry_conn->entry_cfg.dns_request = 1;
conn = ENTRY_TO_EDGE_CONN(entry_conn);
CONNECTION_AP_EXPECT_NONPENDING(entry_conn);
conn->base_.state = AP_CONN_STATE_RESOLVE_WAIT;
@@ -249,7 +250,7 @@ dnsserv_launch_request(const char *name, int reverse,
if (connection_add(TO_CONN(conn))<0) {
log_warn(LD_APP, "Couldn't register dummy connection for RESOLVE request");
- connection_free(TO_CONN(conn));
+ connection_free_(TO_CONN(conn));
return -1;
}
diff --git a/src/or/dos.c b/src/or/dos.c
index 4d1797eece..2cb3470582 100644
--- a/src/or/dos.c
+++ b/src/or/dos.c
@@ -15,6 +15,7 @@
#include "main.h"
#include "networkstatus.h"
#include "nodelist.h"
+#include "relay.h"
#include "router.h"
#include "dos.h"
@@ -622,10 +623,12 @@ dos_log_heartbeat(void)
char *conn_msg = NULL;
char *cc_msg = NULL;
char *single_hop_client_msg = NULL;
+ char *circ_stats_msg = NULL;
- if (!dos_is_enabled()) {
- goto end;
- }
+ /* Stats number coming from relay.c append_cell_to_circuit_queue(). */
+ tor_asprintf(&circ_stats_msg,
+ " %" PRIu64 " circuits killed with too many cells.",
+ stats_n_circ_max_cell_reached);
if (dos_cc_enabled) {
tor_asprintf(&cc_msg,
@@ -647,7 +650,8 @@ dos_log_heartbeat(void)
}
log_notice(LD_HEARTBEAT,
- "DoS mitigation since startup:%s%s%s",
+ "DoS mitigation since startup:%s%s%s%s",
+ circ_stats_msg,
(cc_msg != NULL) ? cc_msg : " [cc not enabled]",
(conn_msg != NULL) ? conn_msg : " [conn not enabled]",
(single_hop_client_msg != NULL) ? single_hop_client_msg : "");
@@ -655,8 +659,7 @@ dos_log_heartbeat(void)
tor_free(conn_msg);
tor_free(cc_msg);
tor_free(single_hop_client_msg);
-
- end:
+ tor_free(circ_stats_msg);
return;
}
diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c
index 67b0259243..54638810fa 100644
--- a/src/or/entrynodes.c
+++ b/src/or/entrynodes.c
@@ -185,14 +185,14 @@ should_apply_guardfraction(const networkstatus_t *ns)
return options->UseGuardFraction;
}
-/** Return true iff we know a descriptor for <b>guard</b> */
+/** Return true iff we know a preferred descriptor for <b>guard</b> */
static int
guard_has_descriptor(const entry_guard_t *guard)
{
const node_t *node = node_get_by_id(guard->identity);
if (!node)
return 0;
- return node_has_descriptor(node);
+ return node_has_preferred_descriptor(node, 1);
}
/**
@@ -1094,7 +1094,11 @@ select_and_add_guard_item_for_sample(guard_selection_t *gs,
return added_guard;
}
-/** Return true iff we need a consensus to maintain our */
+/**
+ * Return true iff we need a consensus to update our guards, but we don't
+ * have one. (We can return 0 here either if the consensus is _not_ missing,
+ * or if we don't need a consensus because we're using bridges.)
+ */
static int
live_consensus_is_missing(const guard_selection_t *gs)
{
@@ -2196,7 +2200,7 @@ entry_guard_has_higher_priority(entry_guard_t *a, entry_guard_t *b)
/** Release all storage held in <b>restriction</b> */
STATIC void
-entry_guard_restriction_free(entry_guard_restriction_t *rst)
+entry_guard_restriction_free_(entry_guard_restriction_t *rst)
{
tor_free(rst);
}
@@ -2205,7 +2209,7 @@ entry_guard_restriction_free(entry_guard_restriction_t *rst)
* Release all storage held in <b>state</b>.
*/
void
-circuit_guard_state_free(circuit_guard_state_t *state)
+circuit_guard_state_free_(circuit_guard_state_t *state)
{
if (!state)
return;
@@ -2216,9 +2220,9 @@ circuit_guard_state_free(circuit_guard_state_t *state)
/** Allocate and return a new circuit_guard_state_t to track the result
* of using <b>guard</b> for a given operation. */
-static circuit_guard_state_t *
-circuit_guard_state_new(entry_guard_t *guard, unsigned state,
- entry_guard_restriction_t *rst)
+MOCK_IMPL(STATIC circuit_guard_state_t *,
+circuit_guard_state_new,(entry_guard_t *guard, unsigned state,
+ entry_guard_restriction_t *rst))
{
circuit_guard_state_t *result;
@@ -2265,7 +2269,8 @@ entry_guard_pick_for_circuit(guard_selection_t *gs,
// XXXX #20827 check Ed ID.
if (! node)
goto fail;
- if (BUG(usage != GUARD_USAGE_DIRGUARD && !node_has_descriptor(node)))
+ if (BUG(usage != GUARD_USAGE_DIRGUARD &&
+ !node_has_preferred_descriptor(node, 1)))
goto fail;
*chosen_node_out = node;
@@ -3108,7 +3113,7 @@ get_guard_state_for_bridge_desc_fetch(const char *digest)
/** Release all storage held by <b>e</b>. */
STATIC void
-entry_guard_free(entry_guard_t *e)
+entry_guard_free_(entry_guard_t *e)
{
if (!e)
return;
@@ -3303,6 +3308,22 @@ entry_guards_update_state(or_state_t *state)
entry_guards_dirty = 0;
}
+/** Return true iff the circuit's guard can succeed that is can be used. */
+int
+entry_guard_could_succeed(const circuit_guard_state_t *guard_state)
+{
+ if (!guard_state) {
+ return 0;
+ }
+
+ entry_guard_t *guard = entry_guard_handle_get(guard_state->guard);
+ if (!guard || BUG(guard->in_selection == NULL)) {
+ return 0;
+ }
+
+ return 1;
+}
+
/**
* Format a single entry guard in the format expected by the controller.
* Return a newly allocated string.
@@ -3615,7 +3636,7 @@ entry_guards_get_err_str_if_dir_info_missing(int using_mds,
/** Free one guard selection context */
STATIC void
-guard_selection_free(guard_selection_t *gs)
+guard_selection_free_(guard_selection_t *gs)
{
if (!gs) return;
diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h
index d909115b1f..d562498313 100644
--- a/src/or/entrynodes.h
+++ b/src/or/entrynodes.h
@@ -220,9 +220,10 @@ struct guard_selection_s {
guard_selection_type_t type;
/**
- * A value of 1 means that primary_entry_guards is up-to-date; 0
- * means we need to recalculate it before using primary_entry_guards
- * or the is_primary flag on any guard.
+ * A value of 1 means that primary_entry_guards is up-to-date with respect to
+ * the consensus and status info that we currently have; 0 means we need to
+ * recalculate it before using primary_entry_guards or the is_primary flag on
+ * any guard.
*/
int primary_guards_up_to_date;
@@ -356,7 +357,10 @@ typedef enum {
GUARD_USAGE_DIRGUARD = 1
} guard_usage_t;
-void circuit_guard_state_free(circuit_guard_state_t *state);
+#define circuit_guard_state_free(val) \
+ FREE_AND_NULL(circuit_guard_state_t, circuit_guard_state_free_, (val))
+
+void circuit_guard_state_free_(circuit_guard_state_t *state);
int entry_guard_pick_for_circuit(guard_selection_t *gs,
guard_usage_t usage,
entry_guard_restriction_t *rst,
@@ -383,6 +387,8 @@ void entry_guards_note_internet_connectivity(guard_selection_t *gs);
int update_guard_selection_choice(const or_options_t *options);
+int entry_guard_could_succeed(const circuit_guard_state_t *guard_state);
+
MOCK_DECL(int,num_bridges_usable,(int use_maybe_reachable));
#ifdef ENTRYNODES_PRIVATE
@@ -475,6 +481,9 @@ STATIC double get_meaningful_restriction_threshold(void);
STATIC double get_extreme_restriction_threshold(void);
HANDLE_DECL(entry_guard, entry_guard_t, STATIC)
+#define entry_guard_handle_free(h) \
+ FREE_AND_NULL(entry_guard_handle_t, entry_guard_handle_free_, (h))
+
STATIC guard_selection_type_t guard_selection_infer_type(
guard_selection_type_t type_in,
const char *name);
@@ -482,7 +491,9 @@ STATIC guard_selection_t *guard_selection_new(const char *name,
guard_selection_type_t type);
STATIC guard_selection_t *get_guard_selection_by_name(
const char *name, guard_selection_type_t type, int create_if_absent);
-STATIC void guard_selection_free(guard_selection_t *gs);
+STATIC void guard_selection_free_(guard_selection_t *gs);
+#define guard_selection_free(gs) \
+ FREE_AND_NULL(guard_selection_t, guard_selection_free_, (gs))
MOCK_DECL(STATIC int, entry_guard_is_listed,
(guard_selection_t *gs, const entry_guard_t *guard));
STATIC const char *choose_guard_selection(const or_options_t *options,
@@ -494,12 +505,18 @@ STATIC entry_guard_t *get_sampled_guard_with_id(guard_selection_t *gs,
MOCK_DECL(STATIC time_t, randomize_time, (time_t now, time_t max_backdate));
+MOCK_DECL(STATIC circuit_guard_state_t *,
+ circuit_guard_state_new,(entry_guard_t *guard, unsigned state,
+ entry_guard_restriction_t *rst));
+
STATIC entry_guard_t *entry_guard_add_to_sample(guard_selection_t *gs,
const node_t *node);
STATIC entry_guard_t *entry_guards_expand_sample(guard_selection_t *gs);
STATIC char *entry_guard_encode_for_state(entry_guard_t *guard);
STATIC entry_guard_t *entry_guard_parse_from_state(const char *s);
-STATIC void entry_guard_free(entry_guard_t *e);
+#define entry_guard_free(e) \
+ FREE_AND_NULL(entry_guard_t, entry_guard_free_, (e))
+STATIC void entry_guard_free_(entry_guard_t *e);
STATIC void entry_guards_update_filtered_sets(guard_selection_t *gs);
STATIC int entry_guards_all_primary_guards_are_down(guard_selection_t *gs);
/**
@@ -562,7 +579,10 @@ STATIC entry_guard_restriction_t *guard_create_exit_restriction(
STATIC entry_guard_restriction_t *guard_create_dirserver_md_restriction(void);
-STATIC void entry_guard_restriction_free(entry_guard_restriction_t *rst);
+STATIC void entry_guard_restriction_free_(entry_guard_restriction_t *rst);
+#define entry_guard_restriction_free(rst) \
+ FREE_AND_NULL(entry_guard_restriction_t, \
+ entry_guard_restriction_free_, (rst))
#endif /* defined(ENTRYNODES_PRIVATE) */
diff --git a/src/or/ext_orport.c b/src/or/ext_orport.c
index 28377a3f36..16a250fa58 100644
--- a/src/or/ext_orport.c
+++ b/src/or/ext_orport.c
@@ -40,7 +40,7 @@ ext_or_cmd_new(uint16_t len)
/** Deallocate the Extended ORPort message in <b>cmd</b>. */
void
-ext_or_cmd_free(ext_or_cmd_t *cmd)
+ext_or_cmd_free_(ext_or_cmd_t *cmd)
{
tor_free(cmd);
}
diff --git a/src/or/ext_orport.h b/src/or/ext_orport.h
index af2b97e77c..09acbc407e 100644
--- a/src/or/ext_orport.h
+++ b/src/or/ext_orport.h
@@ -10,7 +10,11 @@
int connection_ext_or_start_auth(or_connection_t *or_conn);
ext_or_cmd_t *ext_or_cmd_new(uint16_t len);
-void ext_or_cmd_free(ext_or_cmd_t *cmd);
+
+#define ext_or_cmd_free(cmd) \
+ FREE_AND_NULL(ext_or_cmd_t, ext_or_cmd_free_, (cmd))
+
+void ext_or_cmd_free_(ext_or_cmd_t *cmd);
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);
diff --git a/src/or/fp_pair.c b/src/or/fp_pair.c
index f730106d06..c938e76678 100644
--- a/src/or/fp_pair.c
+++ b/src/or/fp_pair.c
@@ -196,7 +196,7 @@ fp_pair_map_remove(fp_pair_map_t *map, const fp_pair_t *key)
*/
void
-fp_pair_map_free(fp_pair_map_t *map, void (*free_val)(void*))
+fp_pair_map_free_(fp_pair_map_t *map, void (*free_val)(void*))
{
fp_pair_map_entry_t **ent, **next, *this;
diff --git a/src/or/fp_pair.h b/src/or/fp_pair.h
index f7c060b459..4498a16101 100644
--- a/src/or/fp_pair.h
+++ b/src/or/fp_pair.h
@@ -26,7 +26,12 @@ void * fp_pair_map_get(const fp_pair_map_t *map, const fp_pair_t *key);
void * fp_pair_map_get_by_digests(const fp_pair_map_t *map,
const char *first, const char *second);
void * fp_pair_map_remove(fp_pair_map_t *map, const fp_pair_t *key);
-void fp_pair_map_free(fp_pair_map_t *map, void (*free_val)(void*));
+void fp_pair_map_free_(fp_pair_map_t *map, void (*free_val)(void*));
+#define fp_pair_map_free(map, free_val) do { \
+ fp_pair_map_free_((map), (free_val)); \
+ (map) = NULL; \
+ } while (0)
+
int fp_pair_map_isempty(const fp_pair_map_t *map);
int fp_pair_map_size(const fp_pair_map_t *map);
fp_pair_map_iter_t * fp_pair_map_iter_init(fp_pair_map_t *map);
diff --git a/src/or/geoip.c b/src/or/geoip.c
index 14e0b1b6fa..0ff1c6ce0d 100644
--- a/src/or/geoip.c
+++ b/src/or/geoip.c
@@ -30,6 +30,7 @@
#define GEOIP_PRIVATE
#include "or.h"
#include "ht.h"
+#include "buffers.h"
#include "config.h"
#include "control.h"
#include "dnsserv.h"
@@ -541,6 +542,9 @@ HT_PROTOTYPE(clientmap, clientmap_entry_t, node, clientmap_entry_hash,
HT_GENERATE2(clientmap, clientmap_entry_t, node, clientmap_entry_hash,
clientmap_entries_eq, 0.6, tor_reallocarray_, tor_free_)
+#define clientmap_entry_free(ent) \
+ FREE_AND_NULL(clientmap_entry_t, clientmap_entry_free_, ent)
+
/** Return the size of a client map entry. */
static inline size_t
clientmap_entry_size(const clientmap_entry_t *ent)
@@ -552,7 +556,7 @@ clientmap_entry_size(const clientmap_entry_t *ent)
/** Free all storage held by <b>ent</b>. */
static void
-clientmap_entry_free(clientmap_entry_t *ent)
+clientmap_entry_free_(clientmap_entry_t *ent)
{
if (!ent)
return;
@@ -1067,9 +1071,9 @@ static char *
geoip_get_dirreq_history(dirreq_type_t type)
{
char *result = NULL;
+ buf_t *buf = NULL;
smartlist_t *dirreq_completed = NULL;
uint32_t complete = 0, timeouts = 0, running = 0;
- int bufsize = 1024, written;
dirreq_map_entry_t **ptr, **next;
struct timeval now;
@@ -1102,13 +1106,9 @@ geoip_get_dirreq_history(dirreq_type_t type)
DIR_REQ_GRANULARITY);
running = round_uint32_to_next_multiple_of(running,
DIR_REQ_GRANULARITY);
- result = tor_malloc_zero(bufsize);
- written = tor_snprintf(result, bufsize, "complete=%u,timeout=%u,"
- "running=%u", complete, timeouts, running);
- if (written < 0) {
- tor_free(result);
- goto done;
- }
+ buf = buf_new_with_capacity(1024);
+ buf_add_printf(buf, "complete=%u,timeout=%u,"
+ "running=%u", complete, timeouts, running);
#define MIN_DIR_REQ_RESPONSES 16
if (complete >= MIN_DIR_REQ_RESPONSES) {
@@ -1129,7 +1129,7 @@ geoip_get_dirreq_history(dirreq_type_t type)
dltimes[ent_sl_idx] = bytes_per_second;
} SMARTLIST_FOREACH_END(ent);
median_uint32(dltimes, complete); /* sorts as a side effect. */
- written = tor_snprintf(result + written, bufsize - written,
+ buf_add_printf(buf,
",min=%u,d1=%u,d2=%u,q1=%u,d3=%u,d4=%u,md=%u,"
"d6=%u,d7=%u,q3=%u,d8=%u,d9=%u,max=%u",
dltimes[0],
@@ -1145,14 +1145,15 @@ geoip_get_dirreq_history(dirreq_type_t type)
dltimes[8*complete/10-1],
dltimes[9*complete/10-1],
dltimes[complete-1]);
- if (written<0)
- tor_free(result);
tor_free(dltimes);
}
- done:
+
+ result = buf_extract(buf, NULL);
+
SMARTLIST_FOREACH(dirreq_completed, dirreq_map_entry_t *, ent,
tor_free(ent));
smartlist_free(dirreq_completed);
+ buf_free(buf);
return result;
}
diff --git a/src/or/git_revision.c b/src/or/git_revision.c
new file mode 100644
index 0000000000..8f326b8751
--- /dev/null
+++ b/src/or/git_revision.c
@@ -0,0 +1,17 @@
+/* Copyright 2001-2004 Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "git_revision.h"
+
+/** String describing which Tor Git repository version the source was
+ * built from. This string is generated by a bit of shell kludging in
+ * src/or/include.am, and is usually right.
+ */
+const char tor_git_revision[] =
+#ifndef _MSC_VER
+#include "micro-revision.i"
+#endif
+ "";
+
diff --git a/src/or/git_revision.h b/src/or/git_revision.h
new file mode 100644
index 0000000000..5613cb4335
--- /dev/null
+++ b/src/or/git_revision.h
@@ -0,0 +1,12 @@
+/* Copyright 2001-2004 Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_GIT_REVISION_H
+#define TOR_GIT_REVISION_H
+
+extern const char tor_git_revision[];
+
+#endif /* !defined(TOR_GIT_REVISION_H) */
+
diff --git a/src/or/hibernate.c b/src/or/hibernate.c
index 74ab766468..4dc35f68d0 100644
--- a/src/or/hibernate.c
+++ b/src/or/hibernate.c
@@ -34,6 +34,7 @@ hibernating, phase 2:
#include "config.h"
#include "connection.h"
#include "connection_edge.h"
+#include "connection_or.h"
#include "control.h"
#include "hibernate.h"
#include "main.h"
@@ -818,8 +819,8 @@ hibernate_begin(hibernate_state_t new_state, time_t now)
log_notice(LD_GENERAL,"SIGINT received %s; exiting now.",
hibernate_state == HIBERNATE_STATE_EXITING ?
"a second time" : "while hibernating");
- tor_cleanup();
- exit(0);
+ tor_shutdown_event_loop_and_exit(0);
+ return;
}
if (new_state == HIBERNATE_STATE_LOWBANDWIDTH &&
@@ -906,20 +907,23 @@ hibernate_go_dormant(time_t now)
while ((conn = connection_get_by_type(CONN_TYPE_OR)) ||
(conn = connection_get_by_type(CONN_TYPE_AP)) ||
(conn = connection_get_by_type(CONN_TYPE_EXIT))) {
- if (CONN_IS_EDGE(conn))
+ if (CONN_IS_EDGE(conn)) {
connection_edge_end(TO_EDGE_CONN(conn), END_STREAM_REASON_HIBERNATING);
+ }
log_info(LD_NET,"Closing conn type %d", conn->type);
- if (conn->type == CONN_TYPE_AP) /* send socks failure if needed */
+ if (conn->type == CONN_TYPE_AP) {
+ /* send socks failure if needed */
connection_mark_unattached_ap(TO_ENTRY_CONN(conn),
END_STREAM_REASON_HIBERNATING);
- else if (conn->type == CONN_TYPE_OR) {
+ } else if (conn->type == CONN_TYPE_OR) {
if (TO_OR_CONN(conn)->chan) {
- channel_mark_for_close(TLS_CHAN_TO_BASE(TO_OR_CONN(conn)->chan));
+ connection_or_close_normally(TO_OR_CONN(conn), 0);
} else {
connection_mark_for_close(conn);
}
- } else
+ } else {
connection_mark_for_close(conn);
+ }
}
if (now < interval_wakeup_time)
@@ -980,8 +984,7 @@ consider_hibernation(time_t now)
tor_assert(shutdown_time);
if (shutdown_time <= now) {
log_notice(LD_GENERAL, "Clean shutdown finished. Exiting.");
- tor_cleanup();
- exit(0);
+ tor_shutdown_event_loop_and_exit(0);
}
return; /* if exiting soon, don't worry about bandwidth limits */
}
diff --git a/src/or/hs_cache.c b/src/or/hs_cache.c
index 6a5a3895b0..df53efd32d 100644
--- a/src/or/hs_cache.c
+++ b/src/or/hs_cache.c
@@ -52,9 +52,12 @@ lookup_v3_desc_as_dir(const uint8_t *key)
return digest256map_get(hs_cache_v3_dir, key);
}
+#define cache_dir_desc_free(val) \
+ FREE_AND_NULL(hs_cache_dir_descriptor_t, cache_dir_desc_free_, (val))
+
/* Free a directory descriptor object. */
static void
-cache_dir_desc_free(hs_cache_dir_descriptor_t *desc)
+cache_dir_desc_free_(hs_cache_dir_descriptor_t *desc)
{
if (desc == NULL) {
return;
@@ -67,10 +70,9 @@ cache_dir_desc_free(hs_cache_dir_descriptor_t *desc)
/* Helper function: Use by the free all function using the digest256map
* interface to cache entries. */
static void
-cache_dir_desc_free_(void *ptr)
+cache_dir_desc_free_void(void *ptr)
{
- hs_cache_dir_descriptor_t *desc = ptr;
- cache_dir_desc_free(desc);
+ cache_dir_desc_free_(ptr);
}
/* Create a new directory cache descriptor object from a encoded descriptor.
@@ -417,9 +419,12 @@ cache_client_desc_new(const char *desc_str,
return client_desc;
}
+#define cache_client_desc_free(val) \
+ FREE_AND_NULL(hs_cache_client_descriptor_t, cache_client_desc_free_, (val))
+
/** Free memory allocated by <b>desc</b>. */
static void
-cache_client_desc_free(hs_cache_client_descriptor_t *desc)
+cache_client_desc_free_(hs_cache_client_descriptor_t *desc)
{
if (desc == NULL) {
return;
@@ -433,7 +438,7 @@ cache_client_desc_free(hs_cache_client_descriptor_t *desc)
/** Helper function: Use by the free all function to clear the client cache */
static void
-cache_client_desc_free_(void *ptr)
+cache_client_desc_free_void(void *ptr)
{
hs_cache_client_descriptor_t *desc = ptr;
cache_client_desc_free(desc);
@@ -448,18 +453,21 @@ cache_intro_state_new(void)
return state;
}
+#define cache_intro_state_free(val) \
+ FREE_AND_NULL(hs_cache_intro_state_t, cache_intro_state_free_, (val))
+
/* Free an hs_cache_intro_state_t object. */
static void
-cache_intro_state_free(hs_cache_intro_state_t *state)
+cache_intro_state_free_(hs_cache_intro_state_t *state)
{
tor_free(state);
}
/* Helper function: use by the free all function. */
static void
-cache_intro_state_free_(void *state)
+cache_intro_state_free_void(void *state)
{
- cache_intro_state_free(state);
+ cache_intro_state_free_(state);
}
/* Return a newly allocated and initialized hs_cache_client_intro_state_t
@@ -472,22 +480,26 @@ cache_client_intro_state_new(void)
return cache;
}
+#define cache_client_intro_state_free(val) \
+ FREE_AND_NULL(hs_cache_client_intro_state_t, \
+ cache_client_intro_state_free_, (val))
+
/* Free a cache client intro state object. */
static void
-cache_client_intro_state_free(hs_cache_client_intro_state_t *cache)
+cache_client_intro_state_free_(hs_cache_client_intro_state_t *cache)
{
if (cache == NULL) {
return;
}
- digest256map_free(cache->intro_points, cache_intro_state_free_);
+ digest256map_free(cache->intro_points, cache_intro_state_free_void);
tor_free(cache);
}
/* Helper function: use by the free all function. */
static void
-cache_client_intro_state_free_(void *entry)
+cache_client_intro_state_free_void(void *entry)
{
- cache_client_intro_state_free(entry);
+ cache_client_intro_state_free_(entry);
}
/* For the given service identity key service_pk and an introduction
@@ -690,7 +702,7 @@ cache_clean_v3_as_client(time_t now)
/* Entry is not in the cache anymore, destroy it. */
cache_client_desc_free(entry);
/* Update our OOM. We didn't use the remove() function because we are in
- * a loop so we have to explicitely decrement. */
+ * a loop so we have to explicitly decrement. */
rend_cache_decrement_allocation(entry_size);
/* Logging. */
{
@@ -706,6 +718,24 @@ cache_clean_v3_as_client(time_t now)
}
/** Public API: Given the HS ed25519 identity public key in <b>key</b>, return
+ * its HS encoded descriptor if it's stored in our cache, or NULL if not. */
+const char *
+hs_cache_lookup_encoded_as_client(const ed25519_public_key_t *key)
+{
+ hs_cache_client_descriptor_t *cached_desc = NULL;
+
+ tor_assert(key);
+
+ cached_desc = lookup_v3_desc_as_client(key->pubkey);
+ if (cached_desc) {
+ tor_assert(cached_desc->encoded_desc);
+ return cached_desc->encoded_desc;
+ }
+
+ return NULL;
+}
+
+/** Public API: Given the HS ed25519 identity public key in <b>key</b>, return
* its HS descriptor if it's stored in our cache, or NULL if not. */
const hs_descriptor_t *
hs_cache_lookup_as_client(const ed25519_public_key_t *key)
@@ -775,7 +805,7 @@ hs_cache_purge_as_client(void)
MAP_DEL_CURRENT(key);
cache_client_desc_free(entry);
/* Update our OOM. We didn't use the remove() function because we are in
- * a loop so we have to explicitely decrement. */
+ * a loop so we have to explicitly decrement. */
rend_cache_decrement_allocation(entry_size);
} DIGEST256MAP_FOREACH_END;
@@ -933,14 +963,14 @@ hs_cache_init(void)
void
hs_cache_free_all(void)
{
- digest256map_free(hs_cache_v3_dir, cache_dir_desc_free_);
+ digest256map_free(hs_cache_v3_dir, cache_dir_desc_free_void);
hs_cache_v3_dir = NULL;
- digest256map_free(hs_cache_v3_client, cache_client_desc_free_);
+ digest256map_free(hs_cache_v3_client, cache_client_desc_free_void);
hs_cache_v3_client = NULL;
digest256map_free(hs_cache_client_intro_state,
- cache_client_intro_state_free_);
+ cache_client_intro_state_free_void);
hs_cache_client_intro_state = NULL;
}
diff --git a/src/or/hs_cache.h b/src/or/hs_cache.h
index 2dcc518a71..a141634cc4 100644
--- a/src/or/hs_cache.h
+++ b/src/or/hs_cache.h
@@ -81,6 +81,8 @@ int hs_cache_lookup_as_dir(uint32_t version, const char *query,
const hs_descriptor_t *
hs_cache_lookup_as_client(const ed25519_public_key_t *key);
+const char *
+hs_cache_lookup_encoded_as_client(const ed25519_public_key_t *key);
int hs_cache_store_as_client(const char *desc_str,
const ed25519_public_key_t *identity_pk);
void hs_cache_clean_as_client(time_t now);
diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c
index 66c59e0dc7..0a9999a190 100644
--- a/src/or/hs_circuit.c
+++ b/src/or/hs_circuit.c
@@ -13,6 +13,7 @@
#include "circuitlist.h"
#include "circuituse.h"
#include "config.h"
+#include "nodelist.h"
#include "policies.h"
#include "relay.h"
#include "rendservice.h"
@@ -558,76 +559,107 @@ retry_service_rendezvous_point(const origin_circuit_t *circ)
return;
}
-/* Using an extend info object ei, set all possible link specifiers in lspecs.
- * legacy ID is mandatory thus MUST be present in ei. If IPv4 is not present,
- * logs a BUG() warning, and returns an empty smartlist. Clients never make
- * direct connections to rendezvous points, so they should always have an
- * IPv4 address in ei. */
+/* Add all possible link specifiers in node to lspecs:
+ * - legacy ID is mandatory thus MUST be present in node;
+ * - include ed25519 link specifier if present in the node, and the node
+ * supports ed25519 link authentication, even if its link versions are not
+ * compatible with us;
+ * - include IPv4 link specifier, if the primary address is not IPv4, log a
+ * BUG() warning, and return an empty smartlist;
+ * - include IPv6 link specifier if present in the node. */
static void
-get_lspecs_from_extend_info(const extend_info_t *ei, smartlist_t *lspecs)
+get_lspecs_from_node(const node_t *node, smartlist_t *lspecs)
{
link_specifier_t *ls;
+ tor_addr_port_t ap;
- tor_assert(ei);
+ tor_assert(node);
tor_assert(lspecs);
- /* We require IPv4, we will add IPv6 support in a later tor version */
- if (BUG(!tor_addr_is_v4(&ei->addr))) {
+ /* Get the relay's IPv4 address. */
+ node_get_prim_orport(node, &ap);
+
+ /* We expect the node's primary address to be a valid IPv4 address.
+ * This conforms to the protocol, which requires either an IPv4 or IPv6
+ * address (or both). */
+ if (BUG(!tor_addr_is_v4(&ap.addr)) ||
+ BUG(!tor_addr_port_is_valid_ap(&ap, 0))) {
return;
}
ls = link_specifier_new();
link_specifier_set_ls_type(ls, LS_IPV4);
- link_specifier_set_un_ipv4_addr(ls, tor_addr_to_ipv4h(&ei->addr));
- link_specifier_set_un_ipv4_port(ls, ei->port);
+ link_specifier_set_un_ipv4_addr(ls, tor_addr_to_ipv4h(&ap.addr));
+ link_specifier_set_un_ipv4_port(ls, ap.port);
/* Four bytes IPv4 and two bytes port. */
- link_specifier_set_ls_len(ls, sizeof(ei->addr.addr.in_addr) +
- sizeof(ei->port));
+ link_specifier_set_ls_len(ls, sizeof(ap.addr.addr.in_addr) +
+ sizeof(ap.port));
smartlist_add(lspecs, ls);
- /* Legacy ID is mandatory. */
+ /* Legacy ID is mandatory and will always be present in node. */
ls = link_specifier_new();
link_specifier_set_ls_type(ls, LS_LEGACY_ID);
- memcpy(link_specifier_getarray_un_legacy_id(ls), ei->identity_digest,
+ memcpy(link_specifier_getarray_un_legacy_id(ls), node->identity,
link_specifier_getlen_un_legacy_id(ls));
link_specifier_set_ls_len(ls, link_specifier_getlen_un_legacy_id(ls));
smartlist_add(lspecs, ls);
- /* ed25519 ID is only included if the extend_info has it. */
- if (!ed25519_public_key_is_zero(&ei->ed_identity)) {
+ /* ed25519 ID is only included if the node has it, and the node declares a
+ protocol version that supports ed25519 link authentication, even if that
+ link version is not compatible with us. (We are sending the ed25519 key
+ to another tor, which may support different link versions.) */
+ if (!ed25519_public_key_is_zero(&node->ed25519_id) &&
+ node_supports_ed25519_link_authentication(node, 0)) {
ls = link_specifier_new();
link_specifier_set_ls_type(ls, LS_ED25519_ID);
- memcpy(link_specifier_getarray_un_ed25519_id(ls), &ei->ed_identity,
+ memcpy(link_specifier_getarray_un_ed25519_id(ls), &node->ed25519_id,
link_specifier_getlen_un_ed25519_id(ls));
link_specifier_set_ls_len(ls, link_specifier_getlen_un_ed25519_id(ls));
smartlist_add(lspecs, ls);
}
+
+ /* Check for IPv6. If so, include it as well. */
+ if (node_has_ipv6_orport(node)) {
+ ls = link_specifier_new();
+ node_get_pref_ipv6_orport(node, &ap);
+ link_specifier_set_ls_type(ls, LS_IPV6);
+ size_t addr_len = link_specifier_getlen_un_ipv6_addr(ls);
+ const uint8_t *in6_addr = tor_addr_to_in6_addr8(&ap.addr);
+ uint8_t *ipv6_array = link_specifier_getarray_un_ipv6_addr(ls);
+ memcpy(ipv6_array, in6_addr, addr_len);
+ link_specifier_set_un_ipv6_port(ls, ap.port);
+ /* Sixteen bytes IPv6 and two bytes port. */
+ link_specifier_set_ls_len(ls, addr_len + sizeof(ap.port));
+ smartlist_add(lspecs, ls);
+ }
}
-/* Using the given descriptor intro point ip, the extend information of the
- * rendezvous point rp_ei and the service's subcredential, populate the
+/* Using the given descriptor intro point ip, the node of the
+ * rendezvous point rp_node and the service's subcredential, populate the
* already allocated intro1_data object with the needed key material and link
* specifiers.
*
- * This can't fail but the ip MUST be a valid object containing the needed
- * keys and authentication method. */
+ * If rp_node has an invalid primary address, intro1_data->link_specifiers
+ * will be an empty list. Otherwise, this function can't fail. The ip
+ * MUST be a valid object containing the needed keys and authentication
+ * method. */
static void
setup_introduce1_data(const hs_desc_intro_point_t *ip,
- const extend_info_t *rp_ei,
+ const node_t *rp_node,
const uint8_t *subcredential,
hs_cell_introduce1_data_t *intro1_data)
{
smartlist_t *rp_lspecs;
tor_assert(ip);
- tor_assert(rp_ei);
+ tor_assert(rp_node);
tor_assert(subcredential);
tor_assert(intro1_data);
/* Build the link specifiers from the extend information of the rendezvous
* circuit that we've picked previously. */
rp_lspecs = smartlist_new();
- get_lspecs_from_extend_info(rp_ei, rp_lspecs);
+ get_lspecs_from_node(rp_node, rp_lspecs);
/* Populate the introduce1 data object. */
memset(intro1_data, 0, sizeof(hs_cell_introduce1_data_t));
@@ -638,7 +670,7 @@ setup_introduce1_data(const hs_desc_intro_point_t *ip,
intro1_data->auth_pk = &ip->auth_key_cert->signed_key;
intro1_data->enc_pk = &ip->enc_key;
intro1_data->subcredential = subcredential;
- intro1_data->onion_pk = &rp_ei->curve25519_onion_key;
+ intro1_data->onion_pk = node_get_curve25519_onion_key(rp_node);
intro1_data->link_specifiers = rp_lspecs;
}
@@ -675,12 +707,12 @@ hs_circ_service_get_intro_circ(const hs_service_intro_point_t *ip)
*
* We currently relaunch connections to rendezvous points if:
* - A rendezvous circuit timed out before connecting to RP.
- * - The redenzvous circuit failed to connect to the RP.
+ * - The rendezvous circuit failed to connect to the RP.
*
* We avoid relaunching a connection to this rendezvous point if:
- * - We have already tried MAX_REND_FAILURES times to connect to this RP.
+ * - We have already tried MAX_REND_FAILURES times to connect to this RP,
* - We've been trying to connect to this RP for more than MAX_REND_TIMEOUT
- * seconds
+ * seconds, or
* - We've already retried this specific rendezvous circuit.
*/
void
@@ -694,11 +726,11 @@ hs_circ_retry_service_rendezvous_point(origin_circuit_t *circ)
goto done;
}
- /* Flag the circuit that we are relaunching so to avoid to relaunch twice a
+ /* Flag the circuit that we are relaunching, to avoid to relaunch twice a
* circuit to the same rendezvous point at the same time. */
circ->hs_service_side_rend_circ_has_been_relaunched = 1;
- /* Legacy service don't have an hidden service ident. */
+ /* Legacy services don't have a hidden service ident. */
if (circ->hs_ident) {
retry_service_rendezvous_point(circ);
} else {
@@ -800,7 +832,11 @@ hs_circ_service_intro_has_opened(hs_service_t *service,
/* Cleaning up the hidden service identifier and repurpose. */
hs_ident_circuit_free(circ->hs_ident);
circ->hs_ident = NULL;
- circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_GENERAL);
+ if (circuit_should_use_vanguards(TO_CIRCUIT(circ)->purpose))
+ circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_HS_VANGUARDS);
+ else
+ circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_GENERAL);
+
/* Inform that this circuit just opened for this new purpose. */
circuit_has_opened(circ);
/* This return value indicate to the caller that the IP object should be
@@ -1082,11 +1118,21 @@ hs_circ_send_introduce1(origin_circuit_t *intro_circ,
tor_assert(ip);
tor_assert(subcredential);
+ /* It is undefined behavior in hs_cell_introduce1_data_clear() if intro1_data
+ * has been declared on the stack but not initialized. Here, we set it to 0.
+ */
+ memset(&intro1_data, 0, sizeof(hs_cell_introduce1_data_t));
+
/* This takes various objects in order to populate the introduce1 data
* object which is used to build the content of the cell. */
- setup_introduce1_data(ip, rend_circ->build_state->chosen_exit,
- subcredential, &intro1_data);
- /* If we didn't get any link specifiers, it's because our extend info was
+ const node_t *exit_node = build_state_get_exit_node(rend_circ->build_state);
+ if (exit_node == NULL) {
+ log_info(LD_REND, "Unable to get rendezvous point for circuit %u. "
+ "Failing.", TO_CIRCUIT(intro_circ)->n_circ_id);
+ goto done;
+ }
+ setup_introduce1_data(ip, exit_node, subcredential, &intro1_data);
+ /* If we didn't get any link specifiers, it's because our node was
* bad. */
if (BUG(!intro1_data.link_specifiers) ||
!smartlist_len(intro1_data.link_specifiers)) {
diff --git a/src/or/hs_circuit.h b/src/or/hs_circuit.h
index 63ff5e463c..2f5beaa168 100644
--- a/src/or/hs_circuit.h
+++ b/src/or/hs_circuit.h
@@ -70,7 +70,7 @@ create_rp_circuit_identifier(const hs_service_t *service,
const curve25519_public_key_t *server_pk,
const hs_ntor_rend_cell_keys_t *keys);
-#endif
+#endif /* defined(HS_CIRCUIT_PRIVATE) */
#endif /* !defined(TOR_HS_CIRCUIT_H) */
diff --git a/src/or/hs_circuitmap.c b/src/or/hs_circuitmap.c
index 97d6053e9b..112c8bdced 100644
--- a/src/or/hs_circuitmap.c
+++ b/src/or/hs_circuitmap.c
@@ -106,9 +106,12 @@ hs_token_new(hs_token_type_t type, size_t token_len,
return hs_token;
}
+#define hs_token_free(val) \
+ FREE_AND_NULL(hs_token_t, hs_token_free_, (val))
+
/** Free memory allocated by this <b>hs_token</b>. */
static void
-hs_token_free(hs_token_t *hs_token)
+hs_token_free_(hs_token_t *hs_token)
{
if (!hs_token) {
return;
@@ -428,30 +431,54 @@ hs_circuitmap_get_rend_circ_client_side(const uint8_t *cookie)
{
origin_circuit_t *circ = NULL;
- circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE,
- REND_TOKEN_LEN, cookie,
- CIRCUIT_PURPOSE_C_REND_READY);
+ circ = hs_circuitmap_get_established_rend_circ_client_side(cookie);
if (circ) {
return circ;
}
circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE,
REND_TOKEN_LEN, cookie,
- CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED);
+ CIRCUIT_PURPOSE_C_ESTABLISH_REND);
+ return circ;
+}
+
+/* Public function: Return client-side established rendezvous circuit with
+ * rendezvous <b>cookie</b>. It will look for circuits with the following
+ * purposes:
+ *
+ * a) CIRCUIT_PURPOSE_C_REND_READY: Established rend circuit (received
+ * RENDEZVOUS_ESTABLISHED). Waiting for RENDEZVOUS2 from service, and for
+ * INTRODUCE_ACK from intro point.
+ *
+ * b) CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: Established rend circuit and
+ * introduce circuit acked. Waiting for RENDEZVOUS2 from service.
+ *
+ * c) CIRCUIT_PURPOSE_C_REND_JOINED: Established rend circuit and received
+ * RENDEZVOUS2 from service.
+ *
+ * Return NULL if no such circuit is found in the circuitmap. */
+origin_circuit_t *
+hs_circuitmap_get_established_rend_circ_client_side(const uint8_t *cookie)
+{
+ origin_circuit_t *circ = NULL;
+
+ circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE,
+ REND_TOKEN_LEN, cookie,
+ CIRCUIT_PURPOSE_C_REND_READY);
if (circ) {
return circ;
}
circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE,
REND_TOKEN_LEN, cookie,
- CIRCUIT_PURPOSE_C_REND_JOINED);
+ CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED);
if (circ) {
return circ;
}
circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE,
REND_TOKEN_LEN, cookie,
- CIRCUIT_PURPOSE_C_ESTABLISH_REND);
+ CIRCUIT_PURPOSE_C_REND_JOINED);
return circ;
}
diff --git a/src/or/hs_circuitmap.h b/src/or/hs_circuitmap.h
index 43b2947c17..9e653480b5 100644
--- a/src/or/hs_circuitmap.h
+++ b/src/or/hs_circuitmap.h
@@ -45,6 +45,8 @@ struct origin_circuit_t *
hs_circuitmap_get_rend_circ_service_side(const uint8_t *cookie);
struct origin_circuit_t *
hs_circuitmap_get_rend_circ_client_side(const uint8_t *cookie);
+struct origin_circuit_t *
+hs_circuitmap_get_established_rend_circ_client_side(const uint8_t *cookie);
void hs_circuitmap_register_intro_circ_v2_service_side(
struct origin_circuit_t *circ,
diff --git a/src/or/hs_client.c b/src/or/hs_client.c
index 4e2824c13d..636226a1d5 100644
--- a/src/or/hs_client.c
+++ b/src/or/hs_client.c
@@ -21,6 +21,7 @@
#include "config.h"
#include "directory.h"
#include "hs_client.h"
+#include "hs_control.h"
#include "router.h"
#include "routerset.h"
#include "circuitlist.h"
@@ -349,6 +350,10 @@ directory_launch_v3_desc_fetch(const ed25519_public_key_t *onion_identity_pk,
safe_str_client(base64_blinded_pubkey),
safe_str_client(routerstatus_describe(hsdir)));
+ /* Fire a REQUESTED event on the control port. */
+ hs_control_desc_event_requested(onion_identity_pk, base64_blinded_pubkey,
+ hsdir);
+
/* Cleanup memory. */
memwipe(&blinded_pubkey, 0, sizeof(blinded_pubkey));
memwipe(base64_blinded_pubkey, 0, sizeof(base64_blinded_pubkey));
@@ -711,7 +716,7 @@ desc_intro_point_to_extend_info(const hs_desc_intro_point_t *ip)
smartlist_add(lspecs, lspec);
} SMARTLIST_FOREACH_END(desc_lspec);
- /* Explicitely put the direct connection option to 0 because this is client
+ /* Explicitly put the direct connection option to 0 because this is client
* side and there is no such thing as a non anonymous client. */
ei = hs_get_extend_info_from_lspecs(lspecs, &ip->onion_key, 0);
@@ -940,7 +945,8 @@ handle_introduce_ack_success(origin_circuit_t *intro_circ)
/* Get the rendezvous circuit for this rendezvous cookie. */
uint8_t *rendezvous_cookie = intro_circ->hs_ident->rendezvous_cookie;
- rend_circ = hs_circuitmap_get_rend_circ_client_side(rendezvous_cookie);
+ rend_circ =
+ hs_circuitmap_get_established_rend_circ_client_side(rendezvous_cookie);
if (rend_circ == NULL) {
log_warn(LD_REND, "Can't find any rendezvous circuit. Stopping");
goto end;
diff --git a/src/or/hs_common.c b/src/or/hs_common.c
index e9d7323316..10b56c0baa 100644
--- a/src/or/hs_common.c
+++ b/src/or/hs_common.c
@@ -210,7 +210,7 @@ hs_check_service_private_dir(const char *username, const char *path,
return 0;
}
-/* Default, minimum and maximum values for the maximum rendezvous failures
+/* Default, minimum, and maximum values for the maximum rendezvous failures
* consensus parameter. */
#define MAX_REND_FAILURES_DEFAULT 2
#define MAX_REND_FAILURES_MIN 1
@@ -347,7 +347,7 @@ rend_data_alloc(uint32_t version)
/** Free all storage associated with <b>data</b> */
void
-rend_data_free(rend_data_t *data)
+rend_data_free_(rend_data_t *data)
{
if (!data) {
return;
@@ -1280,8 +1280,10 @@ node_has_hsdir_index(const node_t *node)
tor_assert(node_supports_v3_hsdir(node));
/* A node can't have an HSDir index without a descriptor since we need desc
- * to get its ed25519 key */
- if (!node_has_descriptor(node)) {
+ * to get its ed25519 key. for_direct_connect should be zero, since we
+ * always use the consensus-indexed node's keys to build the hash ring, even
+ * if some of the consensus-indexed nodes are also bridges. */
+ if (!node_has_preferred_descriptor(node, 0)) {
return 0;
}
@@ -1612,12 +1614,17 @@ hs_pick_hsdir(smartlist_t *responsible_dirs, const char *req_key_str)
hs_clean_last_hid_serv_requests(now);
/* Only select those hidden service directories to which we did not send a
- * request recently and for which we have a router descriptor here. */
+ * request recently and for which we have a router descriptor here.
+ *
+ * Use for_direct_connect==0 even if we will be connecting to the node
+ * directly, since we always use the key information in the
+ * consensus-indexed node descriptors for building the index.
+ **/
SMARTLIST_FOREACH_BEGIN(responsible_dirs, routerstatus_t *, dir) {
time_t last = hs_lookup_last_hid_serv_request(dir, req_key_str, 0, 0);
const node_t *node = node_get_by_id(dir->identity_digest);
if (last + hs_hsdir_requery_period(options) >= now ||
- !node || !node_has_descriptor(node)) {
+ !node || !node_has_preferred_descriptor(node, 0)) {
SMARTLIST_DEL_CURRENT(responsible_dirs, dir);
continue;
}
diff --git a/src/or/hs_common.h b/src/or/hs_common.h
index 7c5ea4792c..83ba1b8599 100644
--- a/src/or/hs_common.h
+++ b/src/or/hs_common.h
@@ -130,6 +130,17 @@ typedef enum {
HS_AUTH_KEY_TYPE_ED25519 = 2,
} hs_auth_key_type_t;
+/* Return value when adding an ephemeral service through the ADD_ONION
+ * control port command. Both v2 and v3 share these. */
+typedef enum {
+ RSAE_BADAUTH = -5, /**< Invalid auth_type/auth_clients */
+ RSAE_BADVIRTPORT = -4, /**< Invalid VIRTPORT/TARGET(s) */
+ RSAE_ADDREXISTS = -3, /**< Onion address collision */
+ RSAE_BADPRIVKEY = -2, /**< Invalid public key */
+ RSAE_INTERNAL = -1, /**< Internal error */
+ RSAE_OKAY = 0 /**< Service added as expected */
+} hs_service_add_ephemeral_status_t;
+
/* Represents the mapping from a virtual port of a rendezvous service to a
* real port on some IP. */
typedef struct rend_service_port_config_t {
@@ -185,7 +196,9 @@ void hs_build_blinded_keypair(const ed25519_keypair_t *kp,
ed25519_keypair_t *kp_out);
int hs_service_requires_uptime_circ(const smartlist_t *ports);
-void rend_data_free(rend_data_t *data);
+void rend_data_free_(rend_data_t *data);
+#define rend_data_free(data) \
+ FREE_AND_NULL(rend_data_t, rend_data_free_, (data))
rend_data_t *rend_data_dup(const rend_data_t *data);
rend_data_t *rend_data_client_create(const char *onion_address,
const char *desc_id,
diff --git a/src/or/hs_config.c b/src/or/hs_config.c
index fa5c1ab176..be223503a0 100644
--- a/src/or/hs_config.c
+++ b/src/or/hs_config.c
@@ -558,7 +558,7 @@ hs_config_service_all(const or_options_t *options, int validate_only)
}
/* In non validation mode, we'll stage those services we just successfully
- * configured. Service ownership is transfered from the list to the global
+ * configured. Service ownership is transferred from the list to the global
* state. If any service is invalid, it will be removed from the list and
* freed. All versions are handled in that function. */
if (!validate_only) {
diff --git a/src/or/hs_control.c b/src/or/hs_control.c
new file mode 100644
index 0000000000..87b4e3fca8
--- /dev/null
+++ b/src/or/hs_control.c
@@ -0,0 +1,258 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_control.c
+ * \brief Contains control port event related code.
+ **/
+
+#include "or.h"
+#include "control.h"
+#include "hs_common.h"
+#include "hs_control.h"
+#include "hs_descriptor.h"
+#include "hs_service.h"
+#include "nodelist.h"
+
+/* Send on the control port the "HS_DESC REQUESTED [...]" event.
+ *
+ * The onion_pk is the onion service public key, base64_blinded_pk is the
+ * base64 encoded blinded key for the service and hsdir_rs is the routerstatus
+ * object of the HSDir that this request is for. */
+void
+hs_control_desc_event_requested(const ed25519_public_key_t *onion_pk,
+ const char *base64_blinded_pk,
+ const routerstatus_t *hsdir_rs)
+{
+ char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+ const uint8_t *hsdir_index;
+ const node_t *hsdir_node;
+
+ tor_assert(onion_pk);
+ tor_assert(base64_blinded_pk);
+ tor_assert(hsdir_rs);
+
+ hs_build_address(onion_pk, HS_VERSION_THREE, onion_address);
+
+ /* Get the node from the routerstatus object to get the HSDir index used for
+ * this request. We can't have a routerstatus entry without a node and we
+ * can't pick a node without an hsdir_index. */
+ hsdir_node = node_get_by_id(hsdir_rs->identity_digest);
+ tor_assert(hsdir_node);
+ tor_assert(hsdir_node->hsdir_index);
+ /* This is a fetch event. */
+ hsdir_index = hsdir_node->hsdir_index->fetch;
+
+ /* Trigger the event. */
+ control_event_hs_descriptor_requested(onion_address, REND_NO_AUTH,
+ hsdir_rs->identity_digest,
+ base64_blinded_pk,
+ hex_str((const char *) hsdir_index,
+ DIGEST256_LEN));
+ memwipe(onion_address, 0, sizeof(onion_address));
+}
+
+/* Send on the control port the "HS_DESC FAILED [...]" event.
+ *
+ * Using a directory connection identifier, the HSDir identity digest and a
+ * reason for the failure. None can be NULL. */
+void
+hs_control_desc_event_failed(const hs_ident_dir_conn_t *ident,
+ const char *hsdir_id_digest,
+ const char *reason)
+{
+ char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+ char base64_blinded_pk[ED25519_BASE64_LEN + 1];
+
+ tor_assert(ident);
+ tor_assert(hsdir_id_digest);
+ tor_assert(reason);
+
+ /* Build onion address and encoded blinded key. */
+ IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk,
+ &ident->blinded_pk) < 0) {
+ return;
+ }
+ hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address);
+
+ control_event_hsv3_descriptor_failed(onion_address, base64_blinded_pk,
+ hsdir_id_digest, reason);
+}
+
+/* Send on the control port the "HS_DESC RECEIVED [...]" event.
+ *
+ * Using a directory connection identifier and the HSDir identity digest.
+ * None can be NULL. */
+void
+hs_control_desc_event_received(const hs_ident_dir_conn_t *ident,
+ const char *hsdir_id_digest)
+{
+ char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+ char base64_blinded_pk[ED25519_BASE64_LEN + 1];
+
+ tor_assert(ident);
+ tor_assert(hsdir_id_digest);
+
+ /* Build onion address and encoded blinded key. */
+ IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk,
+ &ident->blinded_pk) < 0) {
+ return;
+ }
+ hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address);
+
+ control_event_hsv3_descriptor_received(onion_address, base64_blinded_pk,
+ hsdir_id_digest);
+}
+
+/* Send on the control port the "HS_DESC CREATED [...]" event.
+ *
+ * Using the onion address of the descriptor's service and the blinded public
+ * key of the descriptor as a descriptor ID. None can be NULL. */
+void
+hs_control_desc_event_created(const char *onion_address,
+ const ed25519_public_key_t *blinded_pk)
+{
+ char base64_blinded_pk[ED25519_BASE64_LEN + 1];
+
+ tor_assert(onion_address);
+ tor_assert(blinded_pk);
+
+ /* Build base64 encoded blinded key. */
+ IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, blinded_pk) < 0) {
+ return;
+ }
+
+ /* Version 3 doesn't use the replica number in its descriptor ID computation
+ * so we pass negative value so the control port subsystem can ignore it. */
+ control_event_hs_descriptor_created(onion_address, base64_blinded_pk, -1);
+}
+
+/* Send on the control port the "HS_DESC UPLOAD [...]" event.
+ *
+ * Using the onion address of the descriptor's service, the HSDir identity
+ * digest, the blinded public key of the descriptor as a descriptor ID and the
+ * HSDir index for this particular request. None can be NULL. */
+void
+hs_control_desc_event_upload(const char *onion_address,
+ const char *hsdir_id_digest,
+ const ed25519_public_key_t *blinded_pk,
+ const uint8_t *hsdir_index)
+{
+ char base64_blinded_pk[ED25519_BASE64_LEN + 1];
+
+ tor_assert(onion_address);
+ tor_assert(hsdir_id_digest);
+ tor_assert(blinded_pk);
+ tor_assert(hsdir_index);
+
+ /* Build base64 encoded blinded key. */
+ IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, blinded_pk) < 0) {
+ return;
+ }
+
+ control_event_hs_descriptor_upload(onion_address, hsdir_id_digest,
+ base64_blinded_pk,
+ hex_str((const char *) hsdir_index,
+ DIGEST256_LEN));
+}
+
+/* Send on the control port the "HS_DESC UPLOADED [...]" event.
+ *
+ * Using the directory connection identifier and the HSDir identity digest.
+ * None can be NULL. */
+void
+hs_control_desc_event_uploaded(const hs_ident_dir_conn_t *ident,
+ const char *hsdir_id_digest)
+{
+ char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+
+ tor_assert(ident);
+ tor_assert(hsdir_id_digest);
+
+ hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address);
+
+ control_event_hs_descriptor_uploaded(hsdir_id_digest, onion_address);
+}
+
+/* Send on the control port the "HS_DESC_CONTENT [...]" event.
+ *
+ * Using the directory connection identifier, the HSDir identity digest and
+ * the body of the descriptor (as it was received from the directory). None
+ * can be NULL. */
+void
+hs_control_desc_event_content(const hs_ident_dir_conn_t *ident,
+ const char *hsdir_id_digest,
+ const char *body)
+{
+ char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+ char base64_blinded_pk[ED25519_BASE64_LEN + 1];
+
+ tor_assert(ident);
+ tor_assert(hsdir_id_digest);
+
+ /* Build onion address and encoded blinded key. */
+ IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk,
+ &ident->blinded_pk) < 0) {
+ return;
+ }
+ hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address);
+
+ control_event_hs_descriptor_content(onion_address, base64_blinded_pk,
+ hsdir_id_digest, body);
+}
+
+/* Handle the "HSPOST [...]" command. The body is an encoded descriptor for
+ * the given onion_address. The descriptor will be uploaded to each directory
+ * in hsdirs_rs. If NULL, the responsible directories for the current time
+ * period will be selected.
+ *
+ * Return -1 on if the descriptor plaintext section is not decodable. Else, 0
+ * on success. */
+int
+hs_control_hspost_command(const char *body, const char *onion_address,
+ const smartlist_t *hsdirs_rs)
+{
+ int ret = -1;
+ ed25519_public_key_t identity_pk;
+ hs_desc_plaintext_data_t plaintext;
+ smartlist_t *hsdirs = NULL;
+
+ tor_assert(body);
+ tor_assert(onion_address);
+
+ /* This can't fail because we require the caller to pass us a valid onion
+ * address that has passed hs_address_is_valid(). */
+ if (BUG(hs_parse_address(onion_address, &identity_pk, NULL, NULL) < 0)) {
+ goto done; // LCOV_EXCL_LINE
+ }
+
+ /* Only decode the plaintext part which is what the directory will do to
+ * validate before caching. */
+ if (hs_desc_decode_plaintext(body, &plaintext) < 0) {
+ goto done;
+ }
+
+ /* No HSDir(s) given, we'll compute what the current ones should be. */
+ if (hsdirs_rs == NULL) {
+ hsdirs = smartlist_new();
+ hs_get_responsible_hsdirs(&plaintext.blinded_pubkey,
+ hs_get_time_period_num(0),
+ 0, /* Always the current descriptor which uses
+ * the first hsdir index. */
+ 0, /* It is for storing on a directory. */
+ hsdirs);
+ hsdirs_rs = hsdirs;
+ }
+
+ SMARTLIST_FOREACH_BEGIN(hsdirs_rs, const routerstatus_t *, rs) {
+ hs_service_upload_desc_to_dir(body, plaintext.version, &identity_pk,
+ &plaintext.blinded_pubkey, rs);
+ } SMARTLIST_FOREACH_END(rs);
+ ret = 0;
+
+ done:
+ /* We don't have ownership of the objects in this list. */
+ smartlist_free(hsdirs);
+ return ret;
+}
+
diff --git a/src/or/hs_control.h b/src/or/hs_control.h
new file mode 100644
index 0000000000..95c46e655e
--- /dev/null
+++ b/src/or/hs_control.h
@@ -0,0 +1,52 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_control.h
+ * \brief Header file containing control port event related code.
+ **/
+
+#ifndef TOR_HS_CONTROL_H
+#define TOR_HS_CONTROL_H
+
+#include "hs_ident.h"
+
+/* Event "HS_DESC REQUESTED [...]" */
+void hs_control_desc_event_requested(const ed25519_public_key_t *onion_pk,
+ const char *base64_blinded_pk,
+ const routerstatus_t *hsdir_rs);
+
+/* Event "HS_DESC FAILED [...]" */
+void hs_control_desc_event_failed(const hs_ident_dir_conn_t *ident,
+ const char *hsdir_id_digest,
+ const char *reason);
+
+/* Event "HS_DESC RECEIVED [...]" */
+void hs_control_desc_event_received(const hs_ident_dir_conn_t *ident,
+ const char *hsdir_id_digest);
+
+/* Event "HS_DESC CREATED [...]" */
+void hs_control_desc_event_created(const char *onion_address,
+ const ed25519_public_key_t *blinded_pk);
+
+/* Event "HS_DESC UPLOAD [...]" */
+void hs_control_desc_event_upload(const char *onion_address,
+ const char *hsdir_id_digest,
+ const ed25519_public_key_t *blinded_pk,
+ const uint8_t *hsdir_index);
+
+/* Event "HS_DESC UPLOADED [...]" */
+void hs_control_desc_event_uploaded(const hs_ident_dir_conn_t *ident,
+ const char *hsdir_id_digest);
+
+/* Event "HS_DESC_CONTENT [...]" */
+void hs_control_desc_event_content(const hs_ident_dir_conn_t *ident,
+ const char *hsdir_id_digest,
+ const char *body);
+
+/* Command "HSPOST [...]" */
+int hs_control_hspost_command(const char *body, const char *onion_address,
+ const smartlist_t *hsdirs_rs);
+
+#endif /* !defined(TOR_HS_CONTROL_H) */
+
diff --git a/src/or/hs_descriptor.c b/src/or/hs_descriptor.c
index 582ac9cb7c..0298c37322 100644
--- a/src/or/hs_descriptor.c
+++ b/src/or/hs_descriptor.c
@@ -745,8 +745,8 @@ get_fake_auth_client_lines(void)
/* Create the inner layer of the descriptor (which includes the intro points,
* etc.). Return a newly-allocated string with the layer plaintext, or NULL if
- * an error occured. It's the responsibility of the caller to free the returned
- * string. */
+ * an error occurred. It's the responsibility of the caller to free the
+ * returned string. */
static char *
get_inner_encrypted_layer_plaintext(const hs_descriptor_t *desc)
{
@@ -802,8 +802,8 @@ get_inner_encrypted_layer_plaintext(const hs_descriptor_t *desc)
/* Create the middle layer of the descriptor, which includes the client auth
* data and the encrypted inner layer (provided as a base64 string at
* <b>layer2_b64_ciphertext</b>). Return a newly-allocated string with the
- * layer plaintext, or NULL if an error occured. It's the responsibility of the
- * caller to free the returned string. */
+ * layer plaintext, or NULL if an error occurred. It's the responsibility of
+ * the caller to free the returned string. */
static char *
get_outer_encrypted_layer_plaintext(const hs_descriptor_t *desc,
const char *layer2_b64_ciphertext)
@@ -1610,7 +1610,7 @@ decode_intro_legacy_key(const directory_token_t *tok,
/* The check on the expiration date is for the entire lifetime of a
* certificate which is 24 hours. However, a descriptor has a maximum
* lifetime of 12 hours meaning we have a 12h difference between the two
- * which ultimately accomodate the clock skewed client. */
+ * which ultimately accommodate the clock skewed client. */
if (rsa_ed25519_crosscert_check(ip->legacy.cert.encoded,
ip->legacy.cert.len, ip->legacy.key,
&desc->plaintext_data.signing_pubkey,
@@ -2371,7 +2371,7 @@ hs_desc_encode_descriptor,(const hs_descriptor_t *desc,
/* Free the descriptor plaintext data object. */
void
-hs_desc_plaintext_data_free(hs_desc_plaintext_data_t *desc)
+hs_desc_plaintext_data_free_(hs_desc_plaintext_data_t *desc)
{
desc_plaintext_data_free_contents(desc);
tor_free(desc);
@@ -2379,7 +2379,7 @@ hs_desc_plaintext_data_free(hs_desc_plaintext_data_t *desc)
/* Free the descriptor encrypted data object. */
void
-hs_desc_encrypted_data_free(hs_desc_encrypted_data_t *desc)
+hs_desc_encrypted_data_free_(hs_desc_encrypted_data_t *desc)
{
desc_encrypted_data_free_contents(desc);
tor_free(desc);
@@ -2387,7 +2387,7 @@ hs_desc_encrypted_data_free(hs_desc_encrypted_data_t *desc)
/* Free the given descriptor object. */
void
-hs_descriptor_free(hs_descriptor_t *desc)
+hs_descriptor_free_(hs_descriptor_t *desc)
{
if (!desc) {
return;
@@ -2452,7 +2452,7 @@ hs_desc_intro_point_new(void)
/* Free a descriptor intro point object. */
void
-hs_desc_intro_point_free(hs_desc_intro_point_t *ip)
+hs_desc_intro_point_free_(hs_desc_intro_point_t *ip)
{
if (ip == NULL) {
return;
@@ -2471,7 +2471,7 @@ hs_desc_intro_point_free(hs_desc_intro_point_t *ip)
/* Free the given descriptor link specifier. */
void
-hs_desc_link_specifier_free(hs_desc_link_specifier_t *ls)
+hs_desc_link_specifier_free_(hs_desc_link_specifier_t *ls)
{
if (ls == NULL) {
return;
diff --git a/src/or/hs_descriptor.h b/src/or/hs_descriptor.h
index 52bec8e244..09979410e1 100644
--- a/src/or/hs_descriptor.h
+++ b/src/or/hs_descriptor.h
@@ -208,11 +208,20 @@ hs_desc_is_supported_version(uint32_t version)
/* Public API. */
-void hs_descriptor_free(hs_descriptor_t *desc);
-void hs_desc_plaintext_data_free(hs_desc_plaintext_data_t *desc);
-void hs_desc_encrypted_data_free(hs_desc_encrypted_data_t *desc);
+void hs_descriptor_free_(hs_descriptor_t *desc);
+#define hs_descriptor_free(desc) \
+ FREE_AND_NULL(hs_descriptor_t, hs_descriptor_free_, (desc))
+void hs_desc_plaintext_data_free_(hs_desc_plaintext_data_t *desc);
+#define hs_desc_plaintext_data_free(desc) \
+ FREE_AND_NULL(hs_desc_plaintext_data_t, hs_desc_plaintext_data_free_, (desc))
+void hs_desc_encrypted_data_free_(hs_desc_encrypted_data_t *desc);
+#define hs_desc_encrypted_data_free(desc) \
+ FREE_AND_NULL(hs_desc_encrypted_data_t, hs_desc_encrypted_data_free_, (desc))
+
+void hs_desc_link_specifier_free_(hs_desc_link_specifier_t *ls);
+#define hs_desc_link_specifier_free(ls) \
+ FREE_AND_NULL(hs_desc_link_specifier_t, hs_desc_link_specifier_free_, (ls))
-void hs_desc_link_specifier_free(hs_desc_link_specifier_t *ls);
hs_desc_link_specifier_t *hs_desc_link_specifier_new(
const extend_info_t *info, uint8_t type);
void hs_descriptor_clear_intro_points(hs_descriptor_t *desc);
@@ -234,7 +243,9 @@ size_t hs_desc_obj_size(const hs_descriptor_t *data);
size_t hs_desc_plaintext_obj_size(const hs_desc_plaintext_data_t *data);
hs_desc_intro_point_t *hs_desc_intro_point_new(void);
-void hs_desc_intro_point_free(hs_desc_intro_point_t *ip);
+void hs_desc_intro_point_free_(hs_desc_intro_point_t *ip);
+#define hs_desc_intro_point_free(ip) \
+ FREE_AND_NULL(hs_desc_intro_point_t, hs_desc_intro_point_free_, (ip))
link_specifier_t *hs_desc_lspec_to_trunnel(
const hs_desc_link_specifier_t *spec);
diff --git a/src/or/hs_ident.c b/src/or/hs_ident.c
index b0e4e36a9b..0bce2f625b 100644
--- a/src/or/hs_ident.c
+++ b/src/or/hs_ident.c
@@ -25,7 +25,7 @@ hs_ident_circuit_new(const ed25519_public_key_t *identity_pk,
/* Free the given circuit identifier. */
void
-hs_ident_circuit_free(hs_ident_circuit_t *ident)
+hs_ident_circuit_free_(hs_ident_circuit_t *ident)
{
if (ident == NULL) {
return;
@@ -56,7 +56,7 @@ hs_ident_dir_conn_dup(const hs_ident_dir_conn_t *src)
/* Free the given directory connection identifier. */
void
-hs_ident_dir_conn_free(hs_ident_dir_conn_t *ident)
+hs_ident_dir_conn_free_(hs_ident_dir_conn_t *ident)
{
if (ident == NULL) {
return;
@@ -93,7 +93,7 @@ hs_ident_edge_conn_new(const ed25519_public_key_t *identity_pk)
/* Free the given edge connection identifier. */
void
-hs_ident_edge_conn_free(hs_ident_edge_conn_t *ident)
+hs_ident_edge_conn_free_(hs_ident_edge_conn_t *ident)
{
if (ident == NULL) {
return;
diff --git a/src/or/hs_ident.h b/src/or/hs_ident.h
index 03150d25ea..91ec389aa4 100644
--- a/src/or/hs_ident.h
+++ b/src/or/hs_ident.h
@@ -119,12 +119,16 @@ typedef struct hs_ident_edge_conn_t {
hs_ident_circuit_t *hs_ident_circuit_new(
const ed25519_public_key_t *identity_pk,
hs_ident_circuit_type_t circuit_type);
-void hs_ident_circuit_free(hs_ident_circuit_t *ident);
+void hs_ident_circuit_free_(hs_ident_circuit_t *ident);
+#define hs_ident_circuit_free(id) \
+ FREE_AND_NULL(hs_ident_circuit_t, hs_ident_circuit_free_, (id))
hs_ident_circuit_t *hs_ident_circuit_dup(const hs_ident_circuit_t *src);
/* Directory connection identifier API. */
hs_ident_dir_conn_t *hs_ident_dir_conn_dup(const hs_ident_dir_conn_t *src);
-void hs_ident_dir_conn_free(hs_ident_dir_conn_t *ident);
+void hs_ident_dir_conn_free_(hs_ident_dir_conn_t *ident);
+#define hs_ident_dir_conn_free(id) \
+ FREE_AND_NULL(hs_ident_dir_conn_t, hs_ident_dir_conn_free_, (id))
void hs_ident_dir_conn_init(const ed25519_public_key_t *identity_pk,
const ed25519_public_key_t *blinded_pk,
hs_ident_dir_conn_t *ident);
@@ -132,7 +136,9 @@ void hs_ident_dir_conn_init(const ed25519_public_key_t *identity_pk,
/* Edge connection identifier API. */
hs_ident_edge_conn_t *hs_ident_edge_conn_new(
const ed25519_public_key_t *identity_pk);
-void hs_ident_edge_conn_free(hs_ident_edge_conn_t *ident);
+void hs_ident_edge_conn_free_(hs_ident_edge_conn_t *ident);
+#define hs_ident_edge_conn_free(id) \
+ FREE_AND_NULL(hs_ident_edge_conn_t, hs_ident_edge_conn_free_, (id))
/* Validators */
int hs_ident_intro_circ_is_valid(const hs_ident_circuit_t *ident);
diff --git a/src/or/hs_intropoint.c b/src/or/hs_intropoint.c
index 4a5202f614..8c6453e6fd 100644
--- a/src/or/hs_intropoint.c
+++ b/src/or/hs_intropoint.c
@@ -424,7 +424,7 @@ validate_introduce1_parsed_cell(const trn_cell_introduce1_t *cell)
/* We just received a non legacy INTRODUCE1 cell on <b>client_circ</b> with
* the payload in <b>request</b> of size <b>request_len</b>. Return 0 if
- * everything went well, or -1 if an error occured. This function is in charge
+ * everything went well, or -1 if an error occurred. This function is in charge
* of sending back an INTRODUCE_ACK cell and will close client_circ on error.
*/
STATIC int
diff --git a/src/or/hs_service.c b/src/or/hs_service.c
index 33088480d1..e7abf715ad 100644
--- a/src/or/hs_service.c
+++ b/src/or/hs_service.c
@@ -30,11 +30,13 @@
#include "hs_circuit.h"
#include "hs_common.h"
#include "hs_config.h"
+#include "hs_control.h"
#include "hs_circuit.h"
#include "hs_descriptor.h"
#include "hs_ident.h"
#include "hs_intropoint.h"
#include "hs_service.h"
+#include "hs_stats.h"
/* Trunnel */
#include "ed25519_cert.h"
@@ -70,7 +72,7 @@ static const char address_tld[] = "onion";
/* Staging list of service object. When configuring service, we add them to
* this list considered a staging area and they will get added to our global
- * map once the keys have been loaded. These two steps are seperated because
+ * map once the keys have been loaded. These two steps are separated because
* loading keys requires that we are an actual running tor process. */
static smartlist_t *hs_service_staging_list;
@@ -137,7 +139,7 @@ find_service(hs_service_ht *map, const ed25519_public_key_t *pk)
/* Register the given service in the given map. If the service already exists
* in the map, -1 is returned. On success, 0 is returned and the service
- * ownership has been transfered to the global map. */
+ * ownership has been transferred to the global map. */
STATIC int
register_service(hs_service_ht *map, hs_service_t *service)
{
@@ -251,7 +253,7 @@ describe_intro_point(const hs_service_intro_point_t *ip)
static int32_t
get_intro_point_min_introduce2(void)
{
- /* The [0, 2147483647] range is quite large to accomodate anything we decide
+ /* The [0, 2147483647] range is quite large to accommodate anything we decide
* in the future. */
return networkstatus_get_param(NULL, "hs_intro_min_introduce2",
INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS,
@@ -264,7 +266,7 @@ get_intro_point_min_introduce2(void)
static int32_t
get_intro_point_max_introduce2(void)
{
- /* The [0, 2147483647] range is quite large to accomodate anything we decide
+ /* The [0, 2147483647] range is quite large to accommodate anything we decide
* in the future. */
return networkstatus_get_param(NULL, "hs_intro_max_introduce2",
INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS,
@@ -281,7 +283,7 @@ get_intro_point_min_lifetime(void)
return MIN_INTRO_POINT_LIFETIME_TESTING;
}
- /* The [0, 2147483647] range is quite large to accomodate anything we decide
+ /* The [0, 2147483647] range is quite large to accommodate anything we decide
* in the future. */
return networkstatus_get_param(NULL, "hs_intro_min_lifetime",
INTRO_POINT_LIFETIME_MIN_SECONDS,
@@ -298,7 +300,7 @@ get_intro_point_max_lifetime(void)
return MAX_INTRO_POINT_LIFETIME_TESTING;
}
- /* The [0, 2147483647] range is quite large to accomodate anything we decide
+ /* The [0, 2147483647] range is quite large to accommodate anything we decide
* in the future. */
return networkstatus_get_param(NULL, "hs_intro_max_lifetime",
INTRO_POINT_LIFETIME_MAX_SECONDS,
@@ -352,7 +354,7 @@ service_free_all(void)
/* Free a given service intro point object. */
STATIC void
-service_intro_point_free(hs_service_intro_point_t *ip)
+service_intro_point_free_(hs_service_intro_point_t *ip)
{
if (!ip) {
return;
@@ -368,23 +370,27 @@ service_intro_point_free(hs_service_intro_point_t *ip)
/* Helper: free an hs_service_intro_point_t object. This function is used by
* digest256map_free() which requires a void * pointer. */
static void
-service_intro_point_free_(void *obj)
+service_intro_point_free_void(void *obj)
{
- service_intro_point_free(obj);
+ service_intro_point_free_(obj);
}
/* Return a newly allocated service intro point and fully initialized from the
- * given extend_info_t ei if non NULL. If is_legacy is true, we also generate
- * the legacy key. On error, NULL is returned.
+ * given extend_info_t ei if non NULL.
+ * If is_legacy is true, we also generate the legacy key.
+ * If supports_ed25519_link_handshake_any is true, we add the relay's ed25519
+ * key to the link specifiers.
*
* If ei is NULL, returns a hs_service_intro_point_t with an empty link
* specifier list and no onion key. (This is used for testing.)
+ * On any other error, NULL is returned.
*
* ei must be an extend_info_t containing an IPv4 address. (We will add supoort
* for IPv6 in a later release.) When calling extend_info_from_node(), pass
* 0 in for_direct_connection to make sure ei always has an IPv4 address. */
STATIC hs_service_intro_point_t *
-service_intro_point_new(const extend_info_t *ei, unsigned int is_legacy)
+service_intro_point_new(const extend_info_t *ei, unsigned int is_legacy,
+ unsigned int supports_ed25519_link_handshake_any)
{
hs_desc_link_specifier_t *ls;
hs_service_intro_point_t *ip;
@@ -451,10 +457,13 @@ service_intro_point_new(const extend_info_t *ei, unsigned int is_legacy)
}
smartlist_add(ip->base.link_specifiers, ls);
- /* ed25519 identity key is optional for intro points */
- ls = hs_desc_link_specifier_new(ei, LS_ED25519_ID);
- if (ls) {
- smartlist_add(ip->base.link_specifiers, ls);
+ /* ed25519 identity key is optional for intro points. If the node supports
+ * ed25519 link authentication, we include it. */
+ if (supports_ed25519_link_handshake_any) {
+ ls = hs_desc_link_specifier_new(ei, LS_ED25519_ID);
+ if (ls) {
+ smartlist_add(ip->base.link_specifiers, ls);
+ }
}
/* IPv6 is not supported in this release. */
@@ -1027,7 +1036,7 @@ load_service_keys(hs_service_t *service)
/* Free a given service descriptor object and all key material is wiped. */
STATIC void
-service_descriptor_free(hs_service_descriptor_t *desc)
+service_descriptor_free_(hs_service_descriptor_t *desc)
{
if (!desc) {
return;
@@ -1036,7 +1045,7 @@ service_descriptor_free(hs_service_descriptor_t *desc)
memwipe(&desc->signing_kp, 0, sizeof(desc->signing_kp));
memwipe(&desc->blinded_kp, 0, sizeof(desc->blinded_kp));
/* Cleanup all intro points. */
- digest256map_free(desc->intro_points.map, service_intro_point_free_);
+ digest256map_free(desc->intro_points.map, service_intro_point_free_void);
digestmap_free(desc->intro_points.failed_id, tor_free_);
if (desc->previous_hsdirs) {
SMARTLIST_FOREACH(desc->previous_hsdirs, char *, s, tor_free(s));
@@ -1270,7 +1279,7 @@ build_desc_intro_points(const hs_service_t *service,
} DIGEST256MAP_FOREACH_END;
}
-/* Populate the descriptor encrypted section fomr the given service object.
+/* Populate the descriptor encrypted section from the given service object.
* This will generate a valid list of introduction points that can be used
* after for circuit creation. Return 0 on success else -1 on error. */
static int
@@ -1300,7 +1309,7 @@ build_service_desc_encrypted(const hs_service_t *service,
return 0;
}
-/* Populare the descriptor plaintext section from the given service object.
+/* Populate the descriptor plaintext section from the given service object.
* The caller must make sure that the keys in the descriptors are valid that
* is are non-zero. Return 0 on success else -1 on error. */
static int
@@ -1431,6 +1440,9 @@ build_service_descriptor(hs_service_t *service, time_t now,
/* Assign newly built descriptor to the next slot. */
*desc_out = desc;
+ /* Fire a CREATED control port event. */
+ hs_control_desc_event_created(service->onion_address,
+ &desc->blinded_kp.pubkey);
return;
err:
@@ -1508,7 +1520,9 @@ build_all_descriptors(time_t now)
* empty, we'll try to build it for the next time period. This only
* happens when we rotate meaning that we are guaranteed to have a new SRV
* at that point for the next time period. */
- tor_assert(service->desc_current);
+ if (BUG(service->desc_current == NULL)) {
+ continue;
+ }
if (service->desc_next == NULL) {
build_service_descriptor(service, now, hs_get_next_time_period_num(0),
@@ -1572,7 +1586,7 @@ pick_intro_point(unsigned int direct_conn, smartlist_t *exclude_nodes)
/* Let's do a basic sanity check here so that we don't end up advertising the
* ed25519 identity key of relays that don't actually support the link
* protocol */
- if (!node_supports_ed25519_link_authentication(node)) {
+ if (!node_supports_ed25519_link_authentication(node, 0)) {
tor_assert_nonfatal(ed25519_public_key_is_zero(&info->ed_identity));
} else {
/* Make sure we *do* have an ed key if we support the link authentication.
@@ -1580,8 +1594,12 @@ pick_intro_point(unsigned int direct_conn, smartlist_t *exclude_nodes)
tor_assert_nonfatal(!ed25519_public_key_is_zero(&info->ed_identity));
}
- /* Create our objects and populate them with the node information. */
- ip = service_intro_point_new(info, !node_supports_ed25519_hs_intro(node));
+ /* Create our objects and populate them with the node information.
+ * We don't care if the intro's link auth is compatible with us, because
+ * we are sending the ed25519 key to a remote client via the descriptor. */
+ ip = service_intro_point_new(info, !node_supports_ed25519_hs_intro(node),
+ node_supports_ed25519_link_authentication(node,
+ 0));
if (ip == NULL) {
goto err;
}
@@ -1925,6 +1943,33 @@ should_rotate_descriptors(hs_service_t *service, time_t now)
}
if (ns->valid_after >= service->state.next_rotation_time) {
+ /* In theory, we should never get here with no descriptors. We can never
+ * have a NULL current descriptor except when tor starts up. The next
+ * descriptor can be NULL after a rotation but we build a new one right
+ * after.
+ *
+ * So, when tor starts, the next rotation time is set to the start of the
+ * next SRV period using the consensus valid after time so it should
+ * always be set to a future time value. This means that we should never
+ * reach this point at bootup that is this check safeguards tor in never
+ * allowing a rotation if the valid after time is smaller than the next
+ * rotation time.
+ *
+ * This is all good in theory but we've had a NULL descriptor issue here
+ * so this is why we BUG() on both with extra logging to try to understand
+ * how this can possibly happens. We'll simply ignore and tor should
+ * recover from this by skipping rotation and building the missing
+ * descriptors just after this. */
+ if (BUG(service->desc_current == NULL || service->desc_next == NULL)) {
+ log_warn(LD_BUG, "Service descriptor is NULL (%p/%p). Next rotation "
+ "time is %ld (now: %ld). Valid after time from "
+ "consensus is %ld",
+ service->desc_current, service->desc_next,
+ (long)service->state.next_rotation_time,
+ (long)now,
+ (long)ns->valid_after);
+ goto no_rotation;
+ }
goto rotation;
}
@@ -1977,9 +2022,6 @@ rotate_all_descriptors(time_t now)
continue;
}
- tor_assert(service->desc_current);
- tor_assert(service->desc_next);
-
log_info(LD_REND, "Time to rotate our descriptors (%p / %p) for %s",
service->desc_current, service->desc_next,
safe_str_client(service->onion_address));
@@ -2221,16 +2263,12 @@ static void
upload_descriptor_to_hsdir(const hs_service_t *service,
hs_service_descriptor_t *desc, const node_t *hsdir)
{
- char version_str[4] = {0}, *encoded_desc = NULL;
- directory_request_t *dir_req;
- hs_ident_dir_conn_t ident;
+ char *encoded_desc = NULL;
tor_assert(service);
tor_assert(desc);
tor_assert(hsdir);
- memset(&ident, 0, sizeof(ident));
-
/* Let's avoid doing that if tor is configured to not publish. */
if (!get_options()->PublishHidServDescriptors) {
log_info(LD_REND, "Service %s not publishing descriptor. "
@@ -2246,29 +2284,10 @@ upload_descriptor_to_hsdir(const hs_service_t *service,
goto end;
}
- /* Setup the connection identifier. */
- hs_ident_dir_conn_init(&service->keys.identity_pk, &desc->blinded_kp.pubkey,
- &ident);
-
- /* This is our resource when uploading which is used to construct the URL
- * with the version number: "/tor/hs/<version>/publish". */
- tor_snprintf(version_str, sizeof(version_str), "%u",
- service->config.version);
-
- /* Build the directory request for this HSDir. */
- dir_req = directory_request_new(DIR_PURPOSE_UPLOAD_HSDESC);
- directory_request_set_routerstatus(dir_req, hsdir->rs);
- directory_request_set_indirection(dir_req, DIRIND_ANONYMOUS);
- directory_request_set_resource(dir_req, version_str);
- directory_request_set_payload(dir_req, encoded_desc,
- strlen(encoded_desc));
- /* The ident object is copied over the directory connection object once
- * the directory request is initiated. */
- directory_request_upload_set_hs_ident(dir_req, &ident);
-
- /* Initiate the directory request to the hsdir.*/
- directory_initiate_request(dir_req);
- directory_request_free(dir_req);
+ /* Time to upload the descriptor to the directory. */
+ hs_service_upload_desc_to_dir(encoded_desc, service->config.version,
+ &service->keys.identity_pk,
+ &desc->blinded_kp.pubkey, hsdir->rs);
/* Add this node to previous_hsdirs list */
service_desc_note_upload(desc, hsdir);
@@ -2285,9 +2304,12 @@ upload_descriptor_to_hsdir(const hs_service_t *service,
desc->desc->plaintext_data.revision_counter,
safe_str_client(node_describe(hsdir)),
safe_str_client(hex_str((const char *) idx, 32)));
+
+ /* Fire a UPLOAD control port event. */
+ hs_control_desc_event_upload(service->onion_address, hsdir->identity,
+ &desc->blinded_kp.pubkey, idx);
}
- /* XXX: Inform control port of the upload event (#20699). */
end:
tor_free(encoded_desc);
return;
@@ -2922,6 +2944,205 @@ service_add_fnames_to_list(const hs_service_t *service, smartlist_t *list)
/* Public API */
/* ========== */
+/* Upload an encoded descriptor in encoded_desc of the given version. This
+ * descriptor is for the service identity_pk and blinded_pk used to setup the
+ * directory connection identifier. It is uploaded to the directory hsdir_rs
+ * routerstatus_t object.
+ *
+ * NOTE: This function does NOT check for PublishHidServDescriptors because it
+ * is only used by the control port command HSPOST outside of this subsystem.
+ * Inside this code, upload_descriptor_to_hsdir() should be used. */
+void
+hs_service_upload_desc_to_dir(const char *encoded_desc,
+ const uint8_t version,
+ const ed25519_public_key_t *identity_pk,
+ const ed25519_public_key_t *blinded_pk,
+ const routerstatus_t *hsdir_rs)
+{
+ char version_str[4] = {0};
+ directory_request_t *dir_req;
+ hs_ident_dir_conn_t ident;
+
+ tor_assert(encoded_desc);
+ tor_assert(identity_pk);
+ tor_assert(blinded_pk);
+ tor_assert(hsdir_rs);
+
+ /* Setup the connection identifier. */
+ memset(&ident, 0, sizeof(ident));
+ hs_ident_dir_conn_init(identity_pk, blinded_pk, &ident);
+
+ /* This is our resource when uploading which is used to construct the URL
+ * with the version number: "/tor/hs/<version>/publish". */
+ tor_snprintf(version_str, sizeof(version_str), "%u", version);
+
+ /* Build the directory request for this HSDir. */
+ dir_req = directory_request_new(DIR_PURPOSE_UPLOAD_HSDESC);
+ directory_request_set_routerstatus(dir_req, hsdir_rs);
+ directory_request_set_indirection(dir_req, DIRIND_ANONYMOUS);
+ directory_request_set_resource(dir_req, version_str);
+ directory_request_set_payload(dir_req, encoded_desc,
+ strlen(encoded_desc));
+ /* The ident object is copied over the directory connection object once
+ * the directory request is initiated. */
+ directory_request_upload_set_hs_ident(dir_req, &ident);
+
+ /* Initiate the directory request to the hsdir.*/
+ directory_initiate_request(dir_req);
+ directory_request_free(dir_req);
+}
+
+/* Add the ephemeral service using the secret key sk and ports. Both max
+ * streams parameter will be set in the newly created service.
+ *
+ * Ownership of sk and ports is passed to this routine. Regardless of
+ * success/failure, callers should not touch these values after calling this
+ * routine, and may assume that correct cleanup has been done on failure.
+ *
+ * Return an appropriate hs_service_add_ephemeral_status_t. */
+hs_service_add_ephemeral_status_t
+hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports,
+ int max_streams_per_rdv_circuit,
+ int max_streams_close_circuit, char **address_out)
+{
+ hs_service_add_ephemeral_status_t ret;
+ hs_service_t *service = NULL;
+
+ tor_assert(sk);
+ tor_assert(ports);
+ tor_assert(address_out);
+
+ service = hs_service_new(get_options());
+
+ /* Setup the service configuration with specifics. A default service is
+ * HS_VERSION_TWO so explicitly set it. */
+ service->config.version = HS_VERSION_THREE;
+ service->config.max_streams_per_rdv_circuit = max_streams_per_rdv_circuit;
+ service->config.max_streams_close_circuit = !!max_streams_close_circuit;
+ service->config.is_ephemeral = 1;
+ smartlist_free(service->config.ports);
+ service->config.ports = ports;
+
+ /* Handle the keys. */
+ memcpy(&service->keys.identity_sk, sk, sizeof(service->keys.identity_sk));
+ if (ed25519_public_key_generate(&service->keys.identity_pk,
+ &service->keys.identity_sk) < 0) {
+ log_warn(LD_CONFIG, "Unable to generate ed25519 public key"
+ "for v3 service.");
+ ret = RSAE_BADPRIVKEY;
+ goto err;
+ }
+
+ /* Make sure we have at least one port. */
+ if (smartlist_len(service->config.ports) == 0) {
+ log_warn(LD_CONFIG, "At least one VIRTPORT/TARGET must be specified "
+ "for v3 service.");
+ ret = RSAE_BADVIRTPORT;
+ goto err;
+ }
+
+ /* The only way the registration can fail is if the service public key
+ * already exists. */
+ if (BUG(register_service(hs_service_map, service) < 0)) {
+ log_warn(LD_CONFIG, "Onion Service private key collides with an "
+ "existing v3 service.");
+ ret = RSAE_ADDREXISTS;
+ goto err;
+ }
+
+ /* Last step is to build the onion address. */
+ hs_build_address(&service->keys.identity_pk,
+ (uint8_t) service->config.version,
+ service->onion_address);
+ *address_out = tor_strdup(service->onion_address);
+
+ log_info(LD_CONFIG, "Added ephemeral v3 onion service: %s",
+ safe_str_client(service->onion_address));
+ ret = RSAE_OKAY;
+ goto end;
+
+ err:
+ hs_service_free(service);
+
+ end:
+ memwipe(sk, 0, sizeof(ed25519_secret_key_t));
+ tor_free(sk);
+ return ret;
+}
+
+/* For the given onion address, delete the ephemeral service. Return 0 on
+ * success else -1 on error. */
+int
+hs_service_del_ephemeral(const char *address)
+{
+ uint8_t version;
+ ed25519_public_key_t pk;
+ hs_service_t *service = NULL;
+
+ tor_assert(address);
+
+ if (hs_parse_address(address, &pk, NULL, &version) < 0) {
+ log_warn(LD_CONFIG, "Requested malformed v3 onion address for removal.");
+ goto err;
+ }
+
+ if (version != HS_VERSION_THREE) {
+ log_warn(LD_CONFIG, "Requested version of onion address for removal "
+ "is not supported.");
+ goto err;
+ }
+
+ service = find_service(hs_service_map, &pk);
+ if (service == NULL) {
+ log_warn(LD_CONFIG, "Requested non-existent v3 hidden service for "
+ "removal.");
+ goto err;
+ }
+
+ if (!service->config.is_ephemeral) {
+ log_warn(LD_CONFIG, "Requested non-ephemeral v3 hidden service for "
+ "removal.");
+ goto err;
+ }
+
+ /* Close circuits, remove from map and finally free. */
+ close_service_circuits(service);
+ remove_service(hs_service_map, service);
+ hs_service_free(service);
+
+ log_info(LD_CONFIG, "Removed ephemeral v3 hidden service: %s",
+ safe_str_client(address));
+ return 0;
+
+ err:
+ return -1;
+}
+
+/* Using the ed25519 public key pk, find a service for that key and return the
+ * current encoded descriptor as a newly allocated string or NULL if not
+ * found. This is used by the control port subsystem. */
+char *
+hs_service_lookup_current_desc(const ed25519_public_key_t *pk)
+{
+ const hs_service_t *service;
+
+ tor_assert(pk);
+
+ service = find_service(hs_service_map, pk);
+ if (service && service->desc_current) {
+ char *encoded_desc = NULL;
+ /* No matter what is the result (which should never be a failure), return
+ * the encoded variable, if success it will contain the right thing else
+ * it will be NULL. */
+ hs_desc_encode_descriptor(service->desc_current->desc,
+ &service->desc_current->signing_kp,
+ &encoded_desc);
+ return encoded_desc;
+ }
+
+ return NULL;
+}
+
/* Return the number of service we have configured and usable. */
unsigned int
hs_service_get_num_services(void)
@@ -2950,7 +3171,9 @@ hs_service_intro_circ_has_closed(origin_circuit_t *circ)
get_objects_from_ident(circ->hs_ident, &service, &ip, &desc);
if (service == NULL) {
- log_warn(LD_REND, "Unable to find any hidden service associated "
+ /* This is possible if the circuits are closed and the service is
+ * immediately deleted. */
+ log_info(LD_REND, "Unable to find any hidden service associated "
"identity key %s on intro circuit %u.",
ed25519_fmt(&circ->hs_ident->identity_pk),
TO_CIRCUIT(circ)->n_circ_id);
@@ -3032,7 +3255,7 @@ hs_service_set_conn_addr_port(const origin_circuit_t *circ,
}
/* Find a virtual port of that service mathcing the one in the connection if
- * succesful, set the address in the connection. */
+ * successful, set the address in the connection. */
if (hs_set_conn_addr_port(service->config.ports, conn) < 0) {
log_info(LD_REND, "No virtual port mapping exists for port %d for "
"hidden service %s.",
@@ -3119,8 +3342,10 @@ hs_service_receive_introduce2(origin_circuit_t *circ, const uint8_t *payload,
if (circ->hs_ident) {
ret = service_handle_introduce2(circ, payload, payload_len);
+ hs_stats_note_introduce2_cell(1);
} else {
ret = rend_service_receive_introduction(circ, payload, payload_len);
+ hs_stats_note_introduce2_cell(0);
}
done:
@@ -3269,7 +3494,7 @@ hs_service_new(const or_options_t *options)
* also takes care of wiping service keys from memory. It is safe to pass a
* NULL pointer. */
void
-hs_service_free(hs_service_t *service)
+hs_service_free_(hs_service_t *service)
{
if (service == NULL) {
return;
diff --git a/src/or/hs_service.h b/src/or/hs_service.h
index ed1053d850..f3cd49e073 100644
--- a/src/or/hs_service.h
+++ b/src/or/hs_service.h
@@ -249,7 +249,8 @@ void hs_service_free_all(void);
/* Service new/free functions. */
hs_service_t *hs_service_new(const or_options_t *options);
-void hs_service_free(hs_service_t *service);
+void hs_service_free_(hs_service_t *service);
+#define hs_service_free(s) FREE_AND_NULL(hs_service_t, hs_service_free_, (s))
unsigned int hs_service_get_num_services(void);
void hs_service_stage_services(const smartlist_t *service_list);
@@ -271,15 +272,33 @@ int hs_service_receive_introduce2(origin_circuit_t *circ,
void hs_service_intro_circ_has_closed(origin_circuit_t *circ);
+char *hs_service_lookup_current_desc(const ed25519_public_key_t *pk);
+
+hs_service_add_ephemeral_status_t
+hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports,
+ int max_streams_per_rdv_circuit,
+ int max_streams_close_circuit, char **address_out);
+int hs_service_del_ephemeral(const char *address);
+
+/* Used outside of the HS subsystem by the control port command HSPOST. */
+void hs_service_upload_desc_to_dir(const char *encoded_desc,
+ const uint8_t version,
+ const ed25519_public_key_t *identity_pk,
+ const ed25519_public_key_t *blinded_pk,
+ const routerstatus_t *hsdir_rs);
+
#ifdef HS_SERVICE_PRIVATE
#ifdef TOR_UNIT_TESTS
-
/* Useful getters for unit tests. */
STATIC unsigned int get_hs_service_map_size(void);
STATIC int get_hs_service_staging_list_size(void);
STATIC hs_service_ht *get_hs_service_map(void);
STATIC hs_service_t *get_first_service(void);
+STATIC hs_service_intro_point_t *service_intro_point_find_by_ident(
+ const hs_service_t *service,
+ const hs_ident_circuit_t *ident);
+#endif
/* Service accessors. */
STATIC hs_service_t *find_service(hs_service_ht *map,
@@ -288,9 +307,13 @@ STATIC void remove_service(hs_service_ht *map, hs_service_t *service);
STATIC int register_service(hs_service_ht *map, hs_service_t *service);
/* Service introduction point functions. */
STATIC hs_service_intro_point_t *service_intro_point_new(
- const extend_info_t *ei,
- unsigned int is_legacy);
-STATIC void service_intro_point_free(hs_service_intro_point_t *ip);
+ const extend_info_t *ei,
+ unsigned int is_legacy,
+ unsigned int supports_ed25519_link_handshake_any);
+STATIC void service_intro_point_free_(hs_service_intro_point_t *ip);
+#define service_intro_point_free(ip) \
+ FREE_AND_NULL(hs_service_intro_point_t, \
+ service_intro_point_free_, (ip))
STATIC void service_intro_point_add(digest256map_t *map,
hs_service_intro_point_t *ip);
STATIC void service_intro_point_remove(const hs_service_t *service,
@@ -298,9 +321,6 @@ STATIC void service_intro_point_remove(const hs_service_t *service,
STATIC hs_service_intro_point_t *service_intro_point_find(
const hs_service_t *service,
const ed25519_public_key_t *auth_key);
-STATIC hs_service_intro_point_t *service_intro_point_find_by_ident(
- const hs_service_t *service,
- const hs_ident_circuit_t *ident);
/* Service descriptor functions. */
STATIC hs_service_descriptor_t *service_descriptor_new(void);
STATIC hs_service_descriptor_t *service_desc_find_by_intro(
@@ -326,7 +346,10 @@ STATIC void run_upload_descriptor_event(time_t now);
STATIC char *
encode_desc_rev_counter_for_state(const hs_service_descriptor_t *desc);
-STATIC void service_descriptor_free(hs_service_descriptor_t *desc);
+STATIC void service_descriptor_free_(hs_service_descriptor_t *desc);
+#define service_descriptor_free(d) \
+ FREE_AND_NULL(hs_service_descriptor_t, \
+ service_descriptor_free_, (d))
STATIC uint64_t
check_state_line_for_service_rev_counter(const char *state_line,
@@ -346,8 +369,6 @@ STATIC void service_desc_schedule_upload(hs_service_descriptor_t *desc,
STATIC int service_desc_hsdirs_changed(const hs_service_t *service,
const hs_service_descriptor_t *desc);
-#endif /* defined(TOR_UNIT_TESTS) */
-
#endif /* defined(HS_SERVICE_PRIVATE) */
#endif /* !defined(TOR_HS_SERVICE_H) */
diff --git a/src/or/hs_stats.c b/src/or/hs_stats.c
new file mode 100644
index 0000000000..3e183a5bfc
--- /dev/null
+++ b/src/or/hs_stats.c
@@ -0,0 +1,58 @@
+/* Copyright (c) 2016-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_stats.c
+ * \brief Keeps stats about the activity of our hidden service.
+ **/
+
+#include "or.h"
+#include "hs_stats.h"
+#include "hs_service.h"
+
+/** Number of v3 INTRODUCE2 cells received */
+static uint32_t n_introduce2_v3 = 0;
+/** Number of v2 INTRODUCE2 cells received */
+static uint32_t n_introduce2_v2 = 0;
+/** Number of attempts to make a circuit to a rendezvous point */
+static uint32_t n_rendezvous_launches = 0;
+
+/** Note that we received another INTRODUCE2 cell. */
+void
+hs_stats_note_introduce2_cell(int is_hsv3)
+{
+ if (is_hsv3) {
+ n_introduce2_v3++;
+ } else {
+ n_introduce2_v2++;
+ }
+}
+
+/** Return the number of v3 INTRODUCE2 cells we have received. */
+uint32_t
+hs_stats_get_n_introduce2_v3_cells(void)
+{
+ return n_introduce2_v3;
+}
+
+/** Return the number of v2 INTRODUCE2 cells we have received. */
+uint32_t
+hs_stats_get_n_introduce2_v2_cells(void)
+{
+ return n_introduce2_v2;
+}
+
+/** Note that we attempted to launch another circuit to a rendezvous point */
+void
+hs_stats_note_service_rendezvous_launch(void)
+{
+ n_rendezvous_launches++;
+}
+
+/** Return the number of rendezvous circuits we have attempted to launch */
+uint32_t
+hs_stats_get_n_rendezvous_launches(void)
+{
+ return n_rendezvous_launches;
+}
+
diff --git a/src/or/hs_stats.h b/src/or/hs_stats.h
new file mode 100644
index 0000000000..a946ad75e5
--- /dev/null
+++ b/src/or/hs_stats.h
@@ -0,0 +1,14 @@
+/* Copyright (c) 2016-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_stats.h
+ * \brief Header file for hs_stats.c
+ **/
+
+void hs_stats_note_introduce2_cell(int is_hsv3);
+uint32_t hs_stats_get_n_introduce2_v3_cells(void);
+uint32_t hs_stats_get_n_introduce2_v2_cells(void);
+void hs_stats_note_service_rendezvous_launch(void);
+uint32_t hs_stats_get_n_rendezvous_launches(void);
+
diff --git a/src/or/include.am b/src/or/include.am
index 4938ae8e73..c1e23dd3d9 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -52,6 +52,7 @@ LIBTOR_A_SOURCES = \
src/or/geoip.c \
src/or/entrynodes.c \
src/or/ext_orport.c \
+ src/or/git_revision.c \
src/or/hibernate.c \
src/or/hs_cache.c \
src/or/hs_cell.c \
@@ -60,11 +61,13 @@ LIBTOR_A_SOURCES = \
src/or/hs_client.c \
src/or/hs_common.c \
src/or/hs_config.c \
+ src/or/hs_control.c \
src/or/hs_descriptor.c \
src/or/hs_ident.c \
src/or/hs_intropoint.c \
src/or/hs_ntor.c \
src/or/hs_service.c \
+ src/or/hs_stats.c \
src/or/keypin.c \
src/or/main.c \
src/or/microdesc.c \
@@ -79,6 +82,7 @@ LIBTOR_A_SOURCES = \
src/or/parsecommon.c \
src/or/periodic.c \
src/or/protover.c \
+ src/or/protover_rust.c \
src/or/proto_cell.c \
src/or/proto_control0.c \
src/or/proto_ext_or.c \
@@ -105,6 +109,7 @@ LIBTOR_A_SOURCES = \
src/or/statefile.c \
src/or/status.c \
src/or/torcert.c \
+ src/or/tor_api.c \
src/or/onion_ntor.c \
$(tor_platform_source)
@@ -192,18 +197,21 @@ ORHEADERS = \
src/or/fp_pair.h \
src/or/geoip.h \
src/or/entrynodes.h \
+ src/or/git_revision.h \
src/or/hibernate.h \
src/or/hs_cache.h \
src/or/hs_cell.h \
- src/or/hs_config.h \
src/or/hs_circuit.h \
src/or/hs_circuitmap.h \
src/or/hs_client.h \
src/or/hs_common.h \
+ src/or/hs_config.h \
+ src/or/hs_control.h \
src/or/hs_descriptor.h \
src/or/hs_ident.h \
src/or/hs_intropoint.h \
src/or/hs_ntor.h \
+ src/or/hs_stats.h \
src/or/hs_service.h \
src/or/keypin.h \
src/or/main.h \
@@ -246,9 +254,13 @@ ORHEADERS = \
src/or/scheduler.h \
src/or/statefile.h \
src/or/status.h \
- src/or/torcert.h
+ src/or/torcert.h \
+ src/or/tor_api_internal.h
+
+# This may someday want to be an installed file?
+noinst_HEADERS += src/or/tor_api.h
-noinst_HEADERS+= $(ORHEADERS) micro-revision.i
+noinst_HEADERS += $(ORHEADERS) micro-revision.i
micro-revision.i: FORCE
$(AM_V_at)rm -f micro-revision.tmp; \
diff --git a/src/or/main.c b/src/or/main.c
index 6c32ee5d87..3aa730dcec 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -60,7 +60,6 @@
#include "circuitlist.h"
#include "circuituse.h"
#include "command.h"
-#include "compat_rust.h"
#include "compress.h"
#include "config.h"
#include "confparse.h"
@@ -107,6 +106,8 @@
#include "shared_random.h"
#include "statefile.h"
#include "status.h"
+#include "tor_api.h"
+#include "tor_api_internal.h"
#include "util_process.h"
#include "ext_orport.h"
#ifdef USE_DMALLOC
@@ -129,6 +130,12 @@
void evdns_shutdown(int);
+#ifdef HAVE_RUST
+// helper function defined in Rust to output a log message indicating if tor is
+// running with Rust enabled. See src/rust/tor_util
+char *rust_welcome_string(void);
+#endif
+
/********* PROTOTYPES **********/
static void dumpmemusage(int severity);
@@ -141,6 +148,8 @@ static void connection_start_reading_from_linked_conn(connection_t *conn);
static int connection_should_read_from_linked_conn(connection_t *conn);
static int run_main_loop_until_done(void);
static void process_signal(int sig);
+static void shutdown_did_not_work_callback(evutil_socket_t fd, short event,
+ void *arg) ATTR_NORETURN;
/********* START VARIABLES **********/
int global_read_bucket; /**< Max number of bytes I can read this second. */
@@ -171,6 +180,12 @@ static uint64_t stats_n_bytes_written = 0;
time_t time_of_process_start = 0;
/** How many seconds have we been running? */
long stats_n_seconds_working = 0;
+/** How many times have we returned from the main loop successfully? */
+static uint64_t stats_n_main_loop_successes = 0;
+/** How many times have we received an error from the main loop? */
+static uint64_t stats_n_main_loop_errors = 0;
+/** How many times have we returned from the main loop with no events. */
+static uint64_t stats_n_main_loop_idle = 0;
/** How often will we honor SIGNEWNYM requests? */
#define MAX_SIGNEWNYM_RATE 10
@@ -193,6 +208,14 @@ static smartlist_t *active_linked_connection_lst = NULL;
* <b>loop_once</b>. If so, there's no need to trigger a loopexit in order
* to handle linked connections. */
static int called_loop_once = 0;
+/** Flag: if true, it's time to shut down, so the main loop should exit as
+ * soon as possible.
+ */
+static int main_loop_should_exit = 0;
+/** The return value that the main loop should yield when it exits, if
+ * main_loop_should_exit is true.
+ */
+static int main_loop_exit_value = 0;
/** We set this to 1 when we've opened a circuit, so we can print a log
* entry to inform the user that Tor is working. We set it to 0 when
@@ -319,7 +342,7 @@ connection_remove(connection_t *conn)
smartlist_len(connection_array));
if (conn->type == CONN_TYPE_AP && conn->socket_family == AF_UNIX) {
- log_info(LD_NET, "Closing SOCKS SocksSocket connection");
+ log_info(LD_NET, "Closing SOCKS Unix socket connection");
}
control_event_conn_bandwidth(conn);
@@ -477,6 +500,57 @@ connection_is_reading(connection_t *conn)
(conn->read_event && event_pending(conn->read_event, EV_READ, NULL));
}
+/** Reset our main loop counters. */
+void
+reset_main_loop_counters(void)
+{
+ stats_n_main_loop_successes = 0;
+ stats_n_main_loop_errors = 0;
+ stats_n_main_loop_idle = 0;
+}
+
+/** Increment the main loop success counter. */
+static void
+increment_main_loop_success_count(void)
+{
+ ++stats_n_main_loop_successes;
+}
+
+/** Get the main loop success counter. */
+uint64_t
+get_main_loop_success_count(void)
+{
+ return stats_n_main_loop_successes;
+}
+
+/** Increment the main loop error counter. */
+static void
+increment_main_loop_error_count(void)
+{
+ ++stats_n_main_loop_errors;
+}
+
+/** Get the main loop error counter. */
+uint64_t
+get_main_loop_error_count(void)
+{
+ return stats_n_main_loop_errors;
+}
+
+/** Increment the main loop idle counter. */
+static void
+increment_main_loop_idle_count(void)
+{
+ ++stats_n_main_loop_idle;
+}
+
+/** Get the main loop idle counter. */
+uint64_t
+get_main_loop_idle_count(void)
+{
+ return stats_n_main_loop_idle;
+}
+
/** Check whether <b>conn</b> is correct in having (or not having) a
* read/write event (passed in <b>ev</b>). On success, return 0. On failure,
* log a warning and return -1. */
@@ -638,9 +712,10 @@ connection_should_read_from_linked_conn(connection_t *conn)
/** If we called event_base_loop() and told it to never stop until it
* runs out of events, now we've changed our mind: tell it we want it to
- * finish. */
+ * exit once the current round of callbacks is done, so that we can
+ * run external code, and then return to the main loop. */
void
-tell_event_loop_to_finish(void)
+tell_event_loop_to_run_external_code(void)
{
if (!called_loop_once) {
struct timeval tv = { 0, 0 };
@@ -649,6 +724,72 @@ tell_event_loop_to_finish(void)
}
}
+/** Event to run 'shutdown did not work callback'. */
+static struct event *shutdown_did_not_work_event = NULL;
+
+/** Failsafe measure that should never actually be necessary: If
+ * tor_shutdown_event_loop_and_exit() somehow doesn't successfully exit the
+ * event loop, then this callback will kill Tor with an assertion failure
+ * seconds later
+ */
+static void
+shutdown_did_not_work_callback(evutil_socket_t fd, short event, void *arg)
+{
+ // LCOV_EXCL_START
+ (void) fd;
+ (void) event;
+ (void) arg;
+ tor_assert_unreached();
+ // LCOV_EXCL_STOP
+}
+
+#ifdef ENABLE_RESTART_DEBUGGING
+static struct event *tor_shutdown_event_loop_for_restart_event = NULL;
+static void
+tor_shutdown_event_loop_for_restart_cb(
+ evutil_socket_t fd, short event, void *arg)
+{
+ (void)fd;
+ (void)event;
+ (void)arg;
+ tor_event_free(tor_shutdown_event_loop_for_restart_event);
+ tor_shutdown_event_loop_and_exit(0);
+}
+#endif
+
+/**
+ * After finishing the current callback (if any), shut down the main loop,
+ * clean up the process, and exit with <b>exitcode</b>.
+ */
+void
+tor_shutdown_event_loop_and_exit(int exitcode)
+{
+ if (main_loop_should_exit)
+ return; /* Ignore multiple calls to this function. */
+
+ main_loop_should_exit = 1;
+ main_loop_exit_value = exitcode;
+
+ /* Die with an assertion failure in ten seconds, if for some reason we don't
+ * exit normally. */
+ /* XXXX We should consider this code if it's never used. */
+ struct timeval ten_seconds = { 10, 0 };
+ shutdown_did_not_work_event = tor_evtimer_new(
+ tor_libevent_get_base(),
+ shutdown_did_not_work_callback, NULL);
+ event_add(shutdown_did_not_work_event, &ten_seconds);
+
+ /* Unlike loopexit, loopbreak prevents other callbacks from running. */
+ tor_event_base_loopbreak(tor_libevent_get_base());
+}
+
+/** Return true iff tor_shutdown_event_loop_and_exit() has been called. */
+int
+tor_event_loop_shutdown_is_pending(void)
+{
+ return main_loop_should_exit;
+}
+
/** Helper: Tell the main loop to begin reading bytes into <b>conn</b> from
* its linked connection, if it is not doing so already. Called by
* connection_start_reading and connection_start_writing as appropriate. */
@@ -664,7 +805,7 @@ connection_start_reading_from_linked_conn(connection_t *conn)
/* make sure that the event_base_loop() function exits at
* the end of its run through the current connections, so we can
* activate read events for linked connections. */
- tell_event_loop_to_finish();
+ tell_event_loop_to_run_external_code();
} else {
tor_assert(smartlist_contains(active_linked_connection_lst, conn));
}
@@ -1056,7 +1197,7 @@ run_connection_housekeeping(int i, time_t now)
if (!connection_speaks_cells(conn))
return; /* we're all done here, the rest is just for OR conns */
- /* If we haven't written to an OR connection for a while, then either nuke
+ /* If we haven't flushed to an OR connection for a while, then either nuke
the connection or send a keepalive, depending. */
or_conn = TO_OR_CONN(conn);
@@ -1276,6 +1417,9 @@ find_periodic_event(const char *name)
return NULL;
}
+/** Event to run initialize_periodic_events_cb */
+static struct event *initialize_periodic_events_event = NULL;
+
/** Helper, run one second after setup:
* Initializes all members of periodic_events and starts them running.
*
@@ -1287,6 +1431,7 @@ initialize_periodic_events_cb(evutil_socket_t fd, short events, void *data)
(void) fd;
(void) events;
(void) data;
+ tor_event_free(initialize_periodic_events_event);
int i;
for (i = 0; periodic_events[i].name; ++i) {
periodic_event_launch(&periodic_events[i]);
@@ -1315,9 +1460,10 @@ initialize_periodic_events(void)
NAMED_CALLBACK(check_dns_honesty);
struct timeval one_second = { 1, 0 };
- event_base_once(tor_libevent_get_base(), -1, EV_TIMEOUT,
- initialize_periodic_events_cb, NULL,
- &one_second);
+ initialize_periodic_events_event = tor_evtimer_new(
+ tor_libevent_get_base(),
+ initialize_periodic_events_cb, NULL);
+ event_add(initialize_periodic_events_event, &one_second);
}
STATIC void
@@ -1406,7 +1552,9 @@ run_scheduled_events(time_t now)
/* Maybe enough time elapsed for us to reconsider a circuit. */
circuit_upgrade_circuits_from_guard_wait();
- if (options->UseBridges && !options->DisableNetwork) {
+ if (options->UseBridges && !net_is_disabled()) {
+ /* Note: this check uses net_is_disabled(), not should_delay_dir_fetches()
+ * -- the latter is only for fetching consensus-derived directory info. */
fetch_bridge_descriptors(options, now);
}
@@ -1512,7 +1660,7 @@ rotate_onion_key_callback(time_t now, const or_options_t *options)
if (router_rebuild_descriptor(1)<0) {
log_info(LD_CONFIG, "Couldn't rebuild router descriptor");
}
- if (advertised_server_mode() && !options->DisableNetwork)
+ if (advertised_server_mode() && !net_is_disabled())
router_upload_dir_desc_to_dirservers(0);
return ONION_KEY_CONSENSUS_CHECK_INTERVAL;
}
@@ -1555,8 +1703,7 @@ check_ed_keys_callback(time_t now, const or_options_t *options)
if (new_signing_key < 0 ||
generate_ed_link_cert(options, now, new_signing_key > 0)) {
log_err(LD_OR, "Unable to update Ed25519 keys! Exiting.");
- tor_cleanup();
- exit(1);
+ tor_shutdown_event_loop_and_exit(1);
}
}
return 30;
@@ -1878,9 +2025,11 @@ check_descriptor_callback(time_t now, const or_options_t *options)
* address has changed. */
#define CHECK_DESCRIPTOR_INTERVAL (60)
+ (void)options;
+
/* 2b. Once per minute, regenerate and upload the descriptor if the old
* one is inaccurate. */
- if (!options->DisableNetwork) {
+ if (!net_is_disabled()) {
check_descriptor_bandwidth_changed(now);
check_descriptor_ipaddress_changed(now);
mark_my_descriptor_dirty_if_too_old(now);
@@ -1912,7 +2061,7 @@ check_for_reachability_bw_callback(time_t now, const or_options_t *options)
* 20 minutes of our uptime. */
if (server_mode(options) &&
(have_completed_a_circuit() || !any_predicted_circuits(now)) &&
- !we_are_hibernating()) {
+ !net_is_disabled()) {
if (stats_n_seconds_working < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) {
consider_testing_reachability(1, dirport_reachability_count==0);
if (++dirport_reachability_count > 5)
@@ -2367,10 +2516,18 @@ do_hup(void)
/* first, reload config variables, in case they've changed */
if (options->ReloadTorrcOnSIGHUP) {
/* no need to provide argc/v, they've been cached in init_from_config */
- if (options_init_from_torrc(0, NULL) < 0) {
+ int init_rv = options_init_from_torrc(0, NULL);
+ if (init_rv < 0) {
log_err(LD_CONFIG,"Reading config failed--see warnings above. "
"For usage, try -h.");
return -1;
+ } else if (BUG(init_rv > 0)) {
+ // LCOV_EXCL_START
+ /* This should be impossible: the only "return 1" cases in
+ * options_init_from_torrc are ones caused by command-line arguments;
+ * but they can't change while Tor is running. */
+ return -1;
+ // LCOV_EXCL_STOP
}
options = get_options(); /* they have changed now */
/* Logs are only truncated the first time they are opened, but were
@@ -2406,7 +2563,7 @@ do_hup(void)
/* retry appropriate downloads */
router_reset_status_download_failures();
router_reset_descriptor_download_failures();
- if (!options->DisableNetwork)
+ if (!net_is_disabled())
update_networkstatus_downloads(time(NULL));
/* We'll retry routerstatus downloads in about 10 seconds; no need to
@@ -2454,7 +2611,7 @@ do_main_loop(void)
}
}
- handle_signals(1);
+ handle_signals();
monotime_init();
timers_initialize();
@@ -2596,6 +2753,34 @@ do_main_loop(void)
}
#endif /* defined(HAVE_SYSTEMD) */
+ main_loop_should_exit = 0;
+ main_loop_exit_value = 0;
+
+#ifdef ENABLE_RESTART_DEBUGGING
+ {
+ static int first_time = 1;
+
+ if (first_time && getenv("TOR_DEBUG_RESTART")) {
+ first_time = 0;
+ const char *sec_str = getenv("TOR_DEBUG_RESTART_AFTER_SECONDS");
+ long sec;
+ int sec_ok=0;
+ if (sec_str &&
+ (sec = tor_parse_long(sec_str, 10, 0, INT_MAX, &sec_ok, NULL)) &&
+ sec_ok) {
+ /* Okay, we parsed the seconds. */
+ } else {
+ sec = 5;
+ }
+ struct timeval restart_after = { (time_t) sec, 0 };
+ tor_shutdown_event_loop_for_restart_event =
+ tor_evtimer_new(tor_libevent_get_base(),
+ tor_shutdown_event_loop_for_restart_cb, NULL);
+ event_add(tor_shutdown_event_loop_for_restart_event, &restart_after);
+ }
+ }
+#endif
+
return run_main_loop_until_done();
}
@@ -2611,6 +2796,9 @@ run_main_loop_once(void)
if (nt_service_is_stopping())
return 0;
+ if (main_loop_should_exit)
+ return 0;
+
#ifndef _WIN32
/* Make it easier to tell whether libevent failure is our fault or not. */
errno = 0;
@@ -2620,7 +2808,14 @@ run_main_loop_once(void)
* so that libevent knows to run their callbacks. */
SMARTLIST_FOREACH(active_linked_connection_lst, connection_t *, conn,
event_active(conn->read_event, EV_READ, 1));
- called_loop_once = smartlist_len(active_linked_connection_lst) ? 1 : 0;
+
+ if (get_options()->MainloopStats) {
+ /* We always enforce that EVLOOP_ONCE is passed to event_base_loop() if we
+ * are collecting main loop statistics. */
+ called_loop_once = 1;
+ } else {
+ called_loop_once = smartlist_len(active_linked_connection_lst) ? 1 : 0;
+ }
/* Make sure we know (about) what time it is. */
update_approx_time(time(NULL));
@@ -2632,6 +2827,21 @@ run_main_loop_once(void)
loop_result = event_base_loop(tor_libevent_get_base(),
called_loop_once ? EVLOOP_ONCE : 0);
+ if (get_options()->MainloopStats) {
+ /* Update our main loop counters. */
+ if (loop_result == 0) {
+ // The call was successful.
+ increment_main_loop_success_count();
+ } else if (loop_result == -1) {
+ // The call was erroneous.
+ increment_main_loop_error_count();
+ } else if (loop_result == 1) {
+ // The call didn't have any active or pending events
+ // to handle.
+ increment_main_loop_idle_count();
+ }
+ }
+
/* Oh, the loop failed. That might be an error that we need to
* catch, but more likely, it's just an interrupted poll() call or something,
* and we should try again. */
@@ -2657,6 +2867,9 @@ run_main_loop_once(void)
}
}
+ if (main_loop_should_exit)
+ return 0;
+
/* And here is where we put callbacks that happen "every time the event loop
* runs." They must be very fast, or else the whole Tor process will get
* slowed down.
@@ -2685,7 +2898,11 @@ run_main_loop_until_done(void)
do {
loop_result = run_main_loop_once();
} while (loop_result == 1);
- return loop_result;
+
+ if (main_loop_should_exit)
+ return main_loop_exit_value;
+ else
+ return loop_result;
}
/** Libevent callback: invoked when we get a signal.
@@ -2709,14 +2926,13 @@ process_signal(int sig)
{
case SIGTERM:
log_notice(LD_GENERAL,"Catching signal TERM, exiting cleanly.");
- tor_cleanup();
- exit(0);
+ tor_shutdown_event_loop_and_exit(0);
break;
case SIGINT:
if (!server_mode(get_options())) { /* do it now */
log_notice(LD_GENERAL,"Interrupt: exiting cleanly.");
- tor_cleanup();
- exit(0);
+ tor_shutdown_event_loop_and_exit(0);
+ return;
}
#ifdef HAVE_SYSTEMD
sd_notify(0, "STOPPING=1");
@@ -2745,8 +2961,8 @@ process_signal(int sig)
#endif
if (do_hup() < 0) {
log_warn(LD_CONFIG,"Restart failed (config error?). Exiting.");
- tor_cleanup();
- exit(1);
+ tor_shutdown_event_loop_and_exit(1);
+ return;
}
#ifdef HAVE_SYSTEMD
sd_notify(0, "READY=1");
@@ -2928,9 +3144,15 @@ exit_function(void)
#else
#define UNIX_ONLY 1
#endif
+
static struct {
+ /** A numeric code for this signal. Must match the signal value if
+ * try_to_register is true. */
int signal_value;
+ /** True if we should try to register this signal with libevent and catch
+ * corresponding posix signals. False otherwise. */
int try_to_register;
+ /** Pointer to hold the event object constructed for this signal. */
struct event *signal_event;
} signal_handlers[] = {
#ifdef SIGINT
@@ -2964,50 +3186,40 @@ static struct {
{ -1, -1, NULL }
};
-/** Set up the signal handlers for either parent or child process */
+/** Set up the signal handler events for this process, and register them
+ * with libevent if appropriate. */
void
-handle_signals(int is_parent)
+handle_signals(void)
{
int i;
- if (is_parent) {
- for (i = 0; signal_handlers[i].signal_value >= 0; ++i) {
- if (signal_handlers[i].try_to_register) {
- signal_handlers[i].signal_event =
- tor_evsignal_new(tor_libevent_get_base(),
- signal_handlers[i].signal_value,
- signal_callback,
- &signal_handlers[i].signal_value);
- if (event_add(signal_handlers[i].signal_event, NULL))
- log_warn(LD_BUG, "Error from libevent when adding "
- "event for signal %d",
- signal_handlers[i].signal_value);
- } else {
- signal_handlers[i].signal_event =
- tor_event_new(tor_libevent_get_base(), -1,
- EV_SIGNAL, signal_callback,
- &signal_handlers[i].signal_value);
- }
+ const int enabled = !get_options()->DisableSignalHandlers;
+
+ for (i = 0; signal_handlers[i].signal_value >= 0; ++i) {
+ /* Signal handlers are only registered with libevent if they need to catch
+ * real POSIX signals. We construct these signal handler events in either
+ * case, though, so that controllers can activate them with the SIGNAL
+ * command.
+ */
+ if (enabled && signal_handlers[i].try_to_register) {
+ signal_handlers[i].signal_event =
+ tor_evsignal_new(tor_libevent_get_base(),
+ signal_handlers[i].signal_value,
+ signal_callback,
+ &signal_handlers[i].signal_value);
+ if (event_add(signal_handlers[i].signal_event, NULL))
+ log_warn(LD_BUG, "Error from libevent when adding "
+ "event for signal %d",
+ signal_handlers[i].signal_value);
+ } else {
+ signal_handlers[i].signal_event =
+ tor_event_new(tor_libevent_get_base(), -1,
+ EV_SIGNAL, signal_callback,
+ &signal_handlers[i].signal_value);
}
- } else {
-#ifndef _WIN32
- struct sigaction action;
- action.sa_flags = 0;
- sigemptyset(&action.sa_mask);
- action.sa_handler = SIG_IGN;
- sigaction(SIGINT, &action, NULL);
- sigaction(SIGTERM, &action, NULL);
- sigaction(SIGPIPE, &action, NULL);
- sigaction(SIGUSR1, &action, NULL);
- sigaction(SIGUSR2, &action, NULL);
- sigaction(SIGHUP, &action, NULL);
-#ifdef SIGXFSZ
- sigaction(SIGXFSZ, &action, NULL);
-#endif
-#endif /* !defined(_WIN32) */
}
}
-/* Make sure the signal handler for signal_num will be called. */
+/* Cause the signal handler for signal_num to be called in the event loop. */
void
activate_signal(int signal_num)
{
@@ -3020,7 +3232,8 @@ activate_signal(int signal_num)
}
}
-/** Main entry point for the Tor command-line client.
+/** Main entry point for the Tor command-line client. Return 0 on "success",
+ * negative on "failure", and positive on "success and exit".
*/
int
tor_init(int argc, char *argv[])
@@ -3112,14 +3325,13 @@ tor_init(int argc, char *argv[])
"Expect more bugs than usual.");
}
- {
- rust_str_t rust_str = rust_welcome_string();
- const char *s = rust_str_get(rust_str);
- if (strlen(s) > 0) {
- log_notice(LD_GENERAL, "%s", s);
- }
- rust_str_free(rust_str);
+#ifdef HAVE_RUST
+ char *rust_str = rust_welcome_string();
+ if (rust_str != NULL && strlen(rust_str) > 0) {
+ log_notice(LD_GENERAL, "%s", rust_str);
}
+ tor_free(rust_str);
+#endif /* defined(HAVE_RUST) */
if (network_init()<0) {
log_err(LD_BUG,"Error initializing network; exiting.");
@@ -3127,9 +3339,14 @@ tor_init(int argc, char *argv[])
}
atexit(exit_function);
- if (options_init_from_torrc(argc,argv) < 0) {
+ int init_rv = options_init_from_torrc(argc,argv);
+ if (init_rv < 0) {
log_err(LD_CONFIG,"Reading config failed--see warnings above.");
return -1;
+ } else if (init_rv > 0) {
+ // We succeeded, and should exit anyway -- probably the user just said
+ // "--version" or something like that.
+ return 1;
}
/* The options are now initialised */
@@ -3159,7 +3376,7 @@ tor_init(int argc, char *argv[])
log_warn(LD_NET, "Problem initializing libevent RNG.");
}
- /* Scan/clean unparseable descroptors; after reading config */
+ /* Scan/clean unparseable descriptors; after reading config */
routerparse_init();
return 0;
@@ -3181,7 +3398,7 @@ try_locking(const or_options_t *options, int err_if_locked)
if (lockfile)
return 0;
else {
- char *fname = options_get_datadir_fname2_suffix(options, "lock",NULL,NULL);
+ char *fname = options_get_datadir_fname(options, "lock");
int already_locked = 0;
tor_lockfile_t *lf = tor_lockfile_lock(fname, 0, &already_locked);
tor_free(fname);
@@ -3199,7 +3416,7 @@ try_locking(const or_options_t *options, int err_if_locked)
r = try_locking(options, 0);
if (r<0) {
log_err(LD_GENERAL, "No, it's still there. Exiting.");
- exit(1);
+ return -1;
}
return r;
}
@@ -3292,10 +3509,13 @@ tor_free_all(int postfork)
periodic_timer_free(second_timer);
teardown_periodic_events();
periodic_timer_free(refill_timer);
+ tor_event_free(shutdown_did_not_work_event);
+ tor_event_free(initialize_periodic_events_event);
if (!postfork) {
release_lockfile();
}
+ tor_libevent_free_all();
/* Stuff in util.c and address.c*/
if (!postfork) {
escaped(NULL);
@@ -3305,6 +3525,19 @@ tor_free_all(int postfork)
}
}
+/**
+ * Remove the specified file, and log a warning if the operation fails for
+ * any reason other than the file not existing. Ignores NULL filenames.
+ */
+void
+tor_remove_file(const char *filename)
+{
+ if (filename && tor_unlink(filename) != 0 && errno != ENOENT) {
+ log_warn(LD_FS, "Couldn't unlink %s: %s",
+ filename, strerror(errno));
+ }
+}
+
/** Do whatever cleanup is necessary before shutting Tor down. */
void
tor_cleanup(void)
@@ -3314,18 +3547,20 @@ tor_cleanup(void)
time_t now = time(NULL);
/* Remove our pid file. We don't care if there was an error when we
* unlink, nothing we could do about it anyways. */
- if (options->PidFile) {
- if (unlink(options->PidFile) != 0) {
- log_warn(LD_FS, "Couldn't unlink pid file %s: %s",
- options->PidFile, strerror(errno));
- }
+ tor_remove_file(options->PidFile);
+ /* Remove control port file */
+ tor_remove_file(options->ControlPortWriteToFile);
+ /* Remove cookie authentication file */
+ {
+ char *cookie_fname = get_controller_cookie_file_name();
+ tor_remove_file(cookie_fname);
+ tor_free(cookie_fname);
}
- if (options->ControlPortWriteToFile) {
- if (unlink(options->ControlPortWriteToFile) != 0) {
- log_warn(LD_FS, "Couldn't unlink control port file %s: %s",
- options->ControlPortWriteToFile,
- strerror(errno));
- }
+ /* Remove Extended ORPort cookie authentication file */
+ {
+ char *cookie_fname = get_ext_or_auth_cookie_file_name();
+ tor_remove_file(cookie_fname);
+ tor_free(cookie_fname);
}
if (accounting_is_enabled(options))
accounting_record_bandwidth_usage(now, get_or_state());
@@ -3458,7 +3693,7 @@ sandbox_init_filter(void)
int i;
sandbox_cfg_allow_openat_filename(&cfg,
- get_datadir_fname("cached-status"));
+ get_cachedir_fname("cached-status"));
#define OPEN(name) \
sandbox_cfg_allow_open_filename(&cfg, tor_strdup(name))
@@ -3479,21 +3714,38 @@ sandbox_init_filter(void)
OPEN_DATADIR2(name, name2 suffix); \
} while (0)
+#define OPEN_KEY_DIRECTORY() \
+ sandbox_cfg_allow_open_filename(&cfg, tor_strdup(options->KeyDirectory))
+#define OPEN_CACHEDIR(name) \
+ sandbox_cfg_allow_open_filename(&cfg, get_cachedir_fname(name))
+#define OPEN_CACHEDIR_SUFFIX(name, suffix) do { \
+ OPEN_CACHEDIR(name); \
+ OPEN_CACHEDIR(name suffix); \
+ } while (0)
+#define OPEN_KEYDIR(name) \
+ sandbox_cfg_allow_open_filename(&cfg, get_keydir_fname(name))
+#define OPEN_KEYDIR_SUFFIX(name, suffix) do { \
+ OPEN_KEYDIR(name); \
+ OPEN_KEYDIR(name suffix); \
+ } while (0)
+
OPEN(options->DataDirectory);
- OPEN_DATADIR("keys");
- OPEN_DATADIR_SUFFIX("cached-certs", ".tmp");
- OPEN_DATADIR_SUFFIX("cached-consensus", ".tmp");
- OPEN_DATADIR_SUFFIX("unverified-consensus", ".tmp");
- OPEN_DATADIR_SUFFIX("unverified-microdesc-consensus", ".tmp");
- OPEN_DATADIR_SUFFIX("cached-microdesc-consensus", ".tmp");
- OPEN_DATADIR_SUFFIX("cached-microdescs", ".tmp");
- OPEN_DATADIR_SUFFIX("cached-microdescs.new", ".tmp");
- OPEN_DATADIR_SUFFIX("cached-descriptors", ".tmp");
- OPEN_DATADIR_SUFFIX("cached-descriptors.new", ".tmp");
- OPEN_DATADIR("cached-descriptors.tmp.tmp");
- OPEN_DATADIR_SUFFIX("cached-extrainfo", ".tmp");
- OPEN_DATADIR_SUFFIX("cached-extrainfo.new", ".tmp");
- OPEN_DATADIR("cached-extrainfo.tmp.tmp");
+ OPEN_KEY_DIRECTORY();
+
+ OPEN_CACHEDIR_SUFFIX("cached-certs", ".tmp");
+ OPEN_CACHEDIR_SUFFIX("cached-consensus", ".tmp");
+ OPEN_CACHEDIR_SUFFIX("unverified-consensus", ".tmp");
+ OPEN_CACHEDIR_SUFFIX("unverified-microdesc-consensus", ".tmp");
+ OPEN_CACHEDIR_SUFFIX("cached-microdesc-consensus", ".tmp");
+ OPEN_CACHEDIR_SUFFIX("cached-microdescs", ".tmp");
+ OPEN_CACHEDIR_SUFFIX("cached-microdescs.new", ".tmp");
+ OPEN_CACHEDIR_SUFFIX("cached-descriptors", ".tmp");
+ OPEN_CACHEDIR_SUFFIX("cached-descriptors.new", ".tmp");
+ OPEN_CACHEDIR("cached-descriptors.tmp.tmp");
+ OPEN_CACHEDIR_SUFFIX("cached-extrainfo", ".tmp");
+ OPEN_CACHEDIR_SUFFIX("cached-extrainfo.new", ".tmp");
+ OPEN_CACHEDIR("cached-extrainfo.tmp.tmp");
+
OPEN_DATADIR_SUFFIX("state", ".tmp");
OPEN_DATADIR_SUFFIX("sr-state", ".tmp");
OPEN_DATADIR_SUFFIX("unparseable-desc", ".tmp");
@@ -3523,6 +3775,10 @@ sandbox_init_filter(void)
}
}
+ SMARTLIST_FOREACH(options->FilesOpenedByIncludes, char *, f, {
+ OPEN(f);
+ });
+
#define RENAME_SUFFIX(name, suffix) \
sandbox_cfg_allow_rename(&cfg, \
get_datadir_fname(name suffix), \
@@ -3533,20 +3789,31 @@ sandbox_init_filter(void)
get_datadir_fname2(prefix, name suffix), \
get_datadir_fname2(prefix, name))
- RENAME_SUFFIX("cached-certs", ".tmp");
- RENAME_SUFFIX("cached-consensus", ".tmp");
- RENAME_SUFFIX("unverified-consensus", ".tmp");
- RENAME_SUFFIX("unverified-microdesc-consensus", ".tmp");
- RENAME_SUFFIX("cached-microdesc-consensus", ".tmp");
- RENAME_SUFFIX("cached-microdescs", ".tmp");
- RENAME_SUFFIX("cached-microdescs", ".new");
- RENAME_SUFFIX("cached-microdescs.new", ".tmp");
- RENAME_SUFFIX("cached-descriptors", ".tmp");
- RENAME_SUFFIX("cached-descriptors", ".new");
- RENAME_SUFFIX("cached-descriptors.new", ".tmp");
- RENAME_SUFFIX("cached-extrainfo", ".tmp");
- RENAME_SUFFIX("cached-extrainfo", ".new");
- RENAME_SUFFIX("cached-extrainfo.new", ".tmp");
+#define RENAME_CACHEDIR_SUFFIX(name, suffix) \
+ sandbox_cfg_allow_rename(&cfg, \
+ get_cachedir_fname(name suffix), \
+ get_cachedir_fname(name))
+
+#define RENAME_KEYDIR_SUFFIX(name, suffix) \
+ sandbox_cfg_allow_rename(&cfg, \
+ get_keydir_fname(name suffix), \
+ get_keydir_fname(name))
+
+ RENAME_CACHEDIR_SUFFIX("cached-certs", ".tmp");
+ RENAME_CACHEDIR_SUFFIX("cached-consensus", ".tmp");
+ RENAME_CACHEDIR_SUFFIX("unverified-consensus", ".tmp");
+ RENAME_CACHEDIR_SUFFIX("unverified-microdesc-consensus", ".tmp");
+ RENAME_CACHEDIR_SUFFIX("cached-microdesc-consensus", ".tmp");
+ RENAME_CACHEDIR_SUFFIX("cached-microdescs", ".tmp");
+ RENAME_CACHEDIR_SUFFIX("cached-microdescs", ".new");
+ RENAME_CACHEDIR_SUFFIX("cached-microdescs.new", ".tmp");
+ RENAME_CACHEDIR_SUFFIX("cached-descriptors", ".tmp");
+ RENAME_CACHEDIR_SUFFIX("cached-descriptors", ".new");
+ RENAME_CACHEDIR_SUFFIX("cached-descriptors.new", ".tmp");
+ RENAME_CACHEDIR_SUFFIX("cached-extrainfo", ".tmp");
+ RENAME_CACHEDIR_SUFFIX("cached-extrainfo", ".new");
+ RENAME_CACHEDIR_SUFFIX("cached-extrainfo.new", ".tmp");
+
RENAME_SUFFIX("state", ".tmp");
RENAME_SUFFIX("sr-state", ".tmp");
RENAME_SUFFIX("unparseable-desc", ".tmp");
@@ -3558,14 +3825,21 @@ sandbox_init_filter(void)
#define STAT_DATADIR(name) \
sandbox_cfg_allow_stat_filename(&cfg, get_datadir_fname(name))
+#define STAT_CACHEDIR(name) \
+ sandbox_cfg_allow_stat_filename(&cfg, get_cachedir_fname(name))
+
#define STAT_DATADIR2(name, name2) \
sandbox_cfg_allow_stat_filename(&cfg, get_datadir_fname2((name), (name2)))
+#define STAT_KEY_DIRECTORY() \
+ sandbox_cfg_allow_stat_filename(&cfg, tor_strdup(options->KeyDirectory))
+
STAT_DATADIR(NULL);
STAT_DATADIR("lock");
STAT_DATADIR("state");
STAT_DATADIR("router-stability");
- STAT_DATADIR("cached-extrainfo.new");
+
+ STAT_CACHEDIR("cached-extrainfo.new");
{
smartlist_t *files = smartlist_new();
@@ -3630,22 +3904,20 @@ sandbox_init_filter(void)
// orport
if (server_mode(get_options())) {
- OPEN_DATADIR2_SUFFIX("keys", "secret_id_key", ".tmp");
- OPEN_DATADIR2_SUFFIX("keys", "secret_onion_key", ".tmp");
- OPEN_DATADIR2_SUFFIX("keys", "secret_onion_key_ntor", ".tmp");
- OPEN_DATADIR2("keys", "secret_id_key.old");
- OPEN_DATADIR2("keys", "secret_onion_key.old");
- OPEN_DATADIR2("keys", "secret_onion_key_ntor.old");
-
- OPEN_DATADIR2_SUFFIX("keys", "ed25519_master_id_secret_key", ".tmp");
- OPEN_DATADIR2_SUFFIX("keys", "ed25519_master_id_secret_key_encrypted",
- ".tmp");
- OPEN_DATADIR2_SUFFIX("keys", "ed25519_master_id_public_key", ".tmp");
- OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_secret_key", ".tmp");
- OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_secret_key_encrypted",
- ".tmp");
- OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_public_key", ".tmp");
- OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_cert", ".tmp");
+ OPEN_KEYDIR_SUFFIX("secret_id_key", ".tmp");
+ OPEN_KEYDIR_SUFFIX("secret_onion_key", ".tmp");
+ OPEN_KEYDIR_SUFFIX("secret_onion_key_ntor", ".tmp");
+ OPEN_KEYDIR("secret_id_key.old");
+ OPEN_KEYDIR("secret_onion_key.old");
+ OPEN_KEYDIR("secret_onion_key_ntor.old");
+
+ OPEN_KEYDIR_SUFFIX("ed25519_master_id_secret_key", ".tmp");
+ OPEN_KEYDIR_SUFFIX("ed25519_master_id_secret_key_encrypted", ".tmp");
+ OPEN_KEYDIR_SUFFIX("ed25519_master_id_public_key", ".tmp");
+ OPEN_KEYDIR_SUFFIX("ed25519_signing_secret_key", ".tmp");
+ OPEN_KEYDIR_SUFFIX("ed25519_signing_secret_key_encrypted", ".tmp");
+ OPEN_KEYDIR_SUFFIX("ed25519_signing_public_key", ".tmp");
+ OPEN_KEYDIR_SUFFIX("ed25519_signing_cert", ".tmp");
OPEN_DATADIR2_SUFFIX("stats", "bridge-stats", ".tmp");
OPEN_DATADIR2_SUFFIX("stats", "dirreq-stats", ".tmp");
@@ -3664,11 +3936,13 @@ sandbox_init_filter(void)
OPEN("/etc/resolv.conf");
RENAME_SUFFIX("fingerprint", ".tmp");
- RENAME_SUFFIX2("keys", "secret_onion_key_ntor", ".tmp");
- RENAME_SUFFIX2("keys", "secret_id_key", ".tmp");
- RENAME_SUFFIX2("keys", "secret_id_key.old", ".tmp");
- RENAME_SUFFIX2("keys", "secret_onion_key", ".tmp");
- RENAME_SUFFIX2("keys", "secret_onion_key.old", ".tmp");
+ RENAME_KEYDIR_SUFFIX("secret_onion_key_ntor", ".tmp");
+
+ RENAME_KEYDIR_SUFFIX("secret_id_key", ".tmp");
+ RENAME_KEYDIR_SUFFIX("secret_id_key.old", ".tmp");
+ RENAME_KEYDIR_SUFFIX("secret_onion_key", ".tmp");
+ RENAME_KEYDIR_SUFFIX("secret_onion_key.old", ".tmp");
+
RENAME_SUFFIX2("stats", "bridge-stats", ".tmp");
RENAME_SUFFIX2("stats", "dirreq-stats", ".tmp");
RENAME_SUFFIX2("stats", "entry-stats", ".tmp");
@@ -3679,20 +3953,20 @@ sandbox_init_filter(void)
RENAME_SUFFIX("hashed-fingerprint", ".tmp");
RENAME_SUFFIX("router-stability", ".tmp");
- RENAME_SUFFIX2("keys", "ed25519_master_id_secret_key", ".tmp");
- RENAME_SUFFIX2("keys", "ed25519_master_id_secret_key_encrypted", ".tmp");
- RENAME_SUFFIX2("keys", "ed25519_master_id_public_key", ".tmp");
- RENAME_SUFFIX2("keys", "ed25519_signing_secret_key", ".tmp");
- RENAME_SUFFIX2("keys", "ed25519_signing_cert", ".tmp");
+ RENAME_KEYDIR_SUFFIX("ed25519_master_id_secret_key", ".tmp");
+ RENAME_KEYDIR_SUFFIX("ed25519_master_id_secret_key_encrypted", ".tmp");
+ RENAME_KEYDIR_SUFFIX("ed25519_master_id_public_key", ".tmp");
+ RENAME_KEYDIR_SUFFIX("ed25519_signing_secret_key", ".tmp");
+ RENAME_KEYDIR_SUFFIX("ed25519_signing_cert", ".tmp");
sandbox_cfg_allow_rename(&cfg,
- get_datadir_fname2("keys", "secret_onion_key"),
- get_datadir_fname2("keys", "secret_onion_key.old"));
+ get_keydir_fname("secret_onion_key"),
+ get_keydir_fname("secret_onion_key.old"));
sandbox_cfg_allow_rename(&cfg,
- get_datadir_fname2("keys", "secret_onion_key_ntor"),
- get_datadir_fname2("keys", "secret_onion_key_ntor.old"));
+ get_keydir_fname("secret_onion_key_ntor"),
+ get_keydir_fname("secret_onion_key_ntor.old"));
- STAT_DATADIR("keys");
+ STAT_KEY_DIRECTORY();
OPEN_DATADIR("stats");
STAT_DATADIR("stats");
STAT_DATADIR2("stats", "dirreq-stats");
@@ -3705,14 +3979,16 @@ sandbox_init_filter(void)
return cfg;
}
-/** Main entry point for the Tor process. Called from main(). */
-/* This function is distinct from main() only so we can link main.c into
- * the unittest binary without conflicting with the unittests' main. */
+/* Main entry point for the Tor process. Called from tor_main(), and by
+ * anybody embedding Tor. */
int
-tor_main(int argc, char *argv[])
+tor_run_main(const tor_main_configuration_t *tor_cfg)
{
int result = 0;
+ int argc = tor_cfg->argc;
+ char **argv = tor_cfg->argv;
+
#ifdef _WIN32
#ifndef HeapEnableTerminationOnCorruption
#define HeapEnableTerminationOnCorruption 1
@@ -3743,6 +4019,7 @@ tor_main(int argc, char *argv[])
#endif /* defined(_WIN32) */
configure_backtrace_handler(get_version());
+ init_protocol_warning_severity_level();
update_approx_time(time(NULL));
tor_threads_init();
@@ -3764,14 +4041,20 @@ tor_main(int argc, char *argv[])
if (done) return result;
}
#endif /* defined(NT_SERVICE) */
- if (tor_init(argc, argv)<0)
- return -1;
+ {
+ int init_rv = tor_init(argc, argv);
+ if (init_rv) {
+ tor_free_all(0);
+ return (init_rv < 0) ? -1 : 0;
+ }
+ }
if (get_options()->Sandbox && get_options()->command == CMD_RUN_TOR) {
sandbox_cfg_t* cfg = sandbox_init_filter();
if (sandbox_init(cfg)) {
log_err(LD_BUG,"Failed to create syscall sandbox filter");
+ tor_free_all(0);
return -1;
}
diff --git a/src/or/main.h b/src/or/main.h
index 132ab12bbb..c49d216f4e 100644
--- a/src/or/main.h
+++ b/src/or/main.h
@@ -45,7 +45,9 @@ int connection_is_writing(connection_t *conn);
MOCK_DECL(void,connection_stop_writing,(connection_t *conn));
MOCK_DECL(void,connection_start_writing,(connection_t *conn));
-void tell_event_loop_to_finish(void);
+void tell_event_loop_to_run_external_code(void);
+void tor_shutdown_event_loop_and_exit(int exitcode);
+int tor_event_loop_shutdown_is_pending(void);
void connection_stop_reading_from_linked_conn(connection_t *conn);
@@ -64,21 +66,26 @@ MOCK_DECL(long,get_uptime,(void));
unsigned get_signewnym_epoch(void);
-void handle_signals(int is_parent);
+void handle_signals(void);
void activate_signal(int signal_num);
int try_locking(const or_options_t *options, int err_if_locked);
int have_lockfile(void);
void release_lockfile(void);
+void tor_remove_file(const char *filename);
+
void tor_cleanup(void);
void tor_free_all(int postfork);
-int tor_main(int argc, char *argv[]);
-
int do_main_loop(void);
int tor_init(int argc, char **argv);
+void reset_main_loop_counters(void);
+uint64_t get_main_loop_success_count(void);
+uint64_t get_main_loop_error_count(void);
+uint64_t get_main_loop_idle_count(void);
+
extern time_t time_of_process_start;
extern long stats_n_seconds_working;
extern int quiet_level;
diff --git a/src/or/microdesc.c b/src/or/microdesc.c
index fe327c6c82..b4a934e095 100644
--- a/src/or/microdesc.c
+++ b/src/or/microdesc.c
@@ -238,8 +238,8 @@ get_microdesc_cache_noload(void)
if (PREDICT_UNLIKELY(the_microdesc_cache==NULL)) {
microdesc_cache_t *cache = tor_malloc_zero(sizeof(*cache));
HT_INIT(microdesc_map, &cache->map);
- cache->cache_fname = get_datadir_fname("cached-microdescs");
- cache->journal_fname = get_datadir_fname("cached-microdescs.new");
+ cache->cache_fname = get_cachedir_fname("cached-microdescs");
+ cache->journal_fname = get_cachedir_fname("cached-microdescs.new");
the_microdesc_cache = cache;
}
return the_microdesc_cache;
@@ -920,8 +920,7 @@ microdesc_list_missing_digest256(networkstatus_t *ns, microdesc_cache_t *cache,
if (microdesc_cache_lookup_by_digest256(cache, rs->descriptor_digest))
continue;
if (downloadable_only &&
- !download_status_is_ready(&rs->dl_status, now,
- get_options()->TestingMicrodescMaxDownloadTries))
+ !download_status_is_ready(&rs->dl_status, now))
continue;
if (skip && digest256map_get(skip, (const uint8_t*)rs->descriptor_digest))
continue;
diff --git a/src/or/microdesc.h b/src/or/microdesc.h
index 1be12156a4..83a90bd8ff 100644
--- a/src/or/microdesc.h
+++ b/src/or/microdesc.h
@@ -38,8 +38,10 @@ smartlist_t *microdesc_list_missing_digest256(networkstatus_t *ns,
digest256map_t *skip);
void microdesc_free_(microdesc_t *md, const char *fname, int line);
-#define microdesc_free(md) \
- microdesc_free_((md), __FILE__, __LINE__)
+#define microdesc_free(md) do { \
+ microdesc_free_((md), __FILE__, __LINE__); \
+ (md) = NULL; \
+ } while (0)
void microdesc_free_all(void);
void update_microdesc_downloads(time_t now);
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index 3d99dd9eec..040405555c 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -53,6 +53,7 @@
#include "dirvote.h"
#include "dos.h"
#include "entrynodes.h"
+#include "hibernate.h"
#include "main.h"
#include "microdesc.h"
#include "networkstatus.h"
@@ -106,9 +107,9 @@ static time_t time_to_download_next_consensus[N_CONSENSUS_FLAVORS];
static download_status_t consensus_dl_status[N_CONSENSUS_FLAVORS] =
{
{ 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER,
- DL_SCHED_INCREMENT_FAILURE, DL_SCHED_RANDOM_EXPONENTIAL, 0, 0 },
+ DL_SCHED_INCREMENT_FAILURE, 0, 0 },
{ 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER,
- DL_SCHED_INCREMENT_FAILURE, DL_SCHED_RANDOM_EXPONENTIAL, 0, 0 },
+ DL_SCHED_INCREMENT_FAILURE, 0, 0 },
};
#define N_CONSENSUS_BOOTSTRAP_SCHEDULES 2
@@ -125,10 +126,10 @@ static download_status_t
consensus_bootstrap_dl_status[N_CONSENSUS_BOOTSTRAP_SCHEDULES] =
{
{ 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_AUTHORITY,
- DL_SCHED_INCREMENT_ATTEMPT, DL_SCHED_RANDOM_EXPONENTIAL, 0, 0 },
+ DL_SCHED_INCREMENT_ATTEMPT, 0, 0 },
/* During bootstrap, DL_WANT_ANY_DIRSERVER means "use fallbacks". */
{ 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER,
- DL_SCHED_INCREMENT_ATTEMPT, DL_SCHED_RANDOM_EXPONENTIAL, 0, 0 },
+ DL_SCHED_INCREMENT_ATTEMPT, 0, 0 },
};
/** True iff we have logged a warning about this OR's version being older than
@@ -197,7 +198,7 @@ networkstatus_read_cached_consensus_impl(int flav,
tor_snprintf(buf, sizeof(buf), "%s-%s-consensus", prefix, flavorname);
}
- char *filename = get_datadir_fname(buf);
+ char *filename = get_cachedir_fname(buf);
char *result = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL);
tor_free(filename);
return result;
@@ -255,7 +256,7 @@ router_reload_consensus_networkstatus(void)
/** Free all storage held by the vote_routerstatus object <b>rs</b>. */
void
-vote_routerstatus_free(vote_routerstatus_t *rs)
+vote_routerstatus_free_(vote_routerstatus_t *rs)
{
vote_microdesc_hash_t *h, *next;
if (!rs)
@@ -273,7 +274,7 @@ vote_routerstatus_free(vote_routerstatus_t *rs)
/** Free all storage held by the routerstatus object <b>rs</b>. */
void
-routerstatus_free(routerstatus_t *rs)
+routerstatus_free_(routerstatus_t *rs)
{
if (!rs)
return;
@@ -283,7 +284,7 @@ routerstatus_free(routerstatus_t *rs)
/** Free all storage held in <b>sig</b> */
void
-document_signature_free(document_signature_t *sig)
+document_signature_free_(document_signature_t *sig)
{
tor_free(sig->signature);
tor_free(sig);
@@ -301,7 +302,7 @@ document_signature_dup(const document_signature_t *sig)
/** Free all storage held in <b>ns</b>. */
void
-networkstatus_vote_free(networkstatus_t *ns)
+networkstatus_vote_free_(networkstatus_t *ns)
{
if (!ns)
return;
@@ -942,14 +943,11 @@ update_consensus_networkstatus_downloads(time_t now)
update_consensus_bootstrap_multiple_downloads(now, options);
} else {
/* Check if we failed downloading a consensus too recently */
- int max_dl_tries = options->TestingConsensusMaxDownloadTries;
/* Let's make sure we remembered to update consensus_dl_status */
tor_assert(consensus_dl_status[i].schedule == DL_SCHED_CONSENSUS);
- if (!download_status_is_ready(&consensus_dl_status[i],
- now,
- max_dl_tries)) {
+ if (!download_status_is_ready(&consensus_dl_status[i], now)) {
continue;
}
@@ -976,17 +974,9 @@ update_consensus_networkstatus_downloads(time_t now)
static void
update_consensus_bootstrap_attempt_downloads(
time_t now,
- const or_options_t *options,
download_status_t *dls,
download_want_authority_t want_authority)
{
- int use_fallbacks = networkstatus_consensus_can_use_extra_fallbacks(options);
- int max_dl_tries = options->ClientBootstrapConsensusMaxDownloadTries;
- if (!use_fallbacks) {
- max_dl_tries =
- options->ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries;
- }
-
const char *resource = networkstatus_get_flavor_name(
usable_consensus_flavor());
@@ -995,7 +985,7 @@ update_consensus_bootstrap_attempt_downloads(
/* Allow for multiple connections in the same second, if the schedule value
* is 0. */
- while (download_status_is_ready(dls, now, max_dl_tries)) {
+ while (download_status_is_ready(dls, now)) {
log_info(LD_DIR, "Launching %s bootstrap %s networkstatus consensus "
"download.", resource, (want_authority == DL_WANT_AUTHORITY
? "authority"
@@ -1046,7 +1036,7 @@ update_consensus_bootstrap_multiple_downloads(time_t now,
if (!check_consensus_waiting_for_certs(usable_flavor, now, dls_f)) {
/* During bootstrap, DL_WANT_ANY_DIRSERVER means "use fallbacks". */
- update_consensus_bootstrap_attempt_downloads(now, options, dls_f,
+ update_consensus_bootstrap_attempt_downloads(now, dls_f,
DL_WANT_ANY_DIRSERVER);
}
}
@@ -1056,7 +1046,7 @@ update_consensus_bootstrap_multiple_downloads(time_t now,
&consensus_bootstrap_dl_status[CONSENSUS_BOOTSTRAP_SOURCE_AUTHORITY];
if (!check_consensus_waiting_for_certs(usable_flavor, now, dls_a)) {
- update_consensus_bootstrap_attempt_downloads(now, options, dls_a,
+ update_consensus_bootstrap_attempt_downloads(now, dls_a,
DL_WANT_AUTHORITY);
}
}
@@ -1209,6 +1199,14 @@ should_delay_dir_fetches(const or_options_t *options, const char **msg_out)
return 1;
}
+ if (we_are_hibernating()) {
+ if (msg_out) {
+ *msg_out = "We are hibernating or shutting down.";
+ }
+ log_info(LD_DIR, "Delaying dir fetches (Hibernating or shutting down)");
+ return 1;
+ }
+
if (options->UseBridges) {
/* If we know that none of our bridges can possibly work, avoid fetching
* directory documents. But if some of them might work, try again. */
@@ -1501,6 +1499,32 @@ networkstatus_consensus_is_already_downloading(const char *resource)
return answer;
}
+/* Does the current, reasonably live consensus have IPv6 addresses?
+ * Returns 1 if there is a reasonably live consensus and its consensus method
+ * includes IPv6 addresses in the consensus.
+ * Otherwise, if there is no consensus, or the method does not include IPv6
+ * addresses, returns 0. */
+int
+networkstatus_consensus_has_ipv6(const or_options_t* options)
+{
+ const networkstatus_t *cons = networkstatus_get_reasonably_live_consensus(
+ approx_time(),
+ usable_consensus_flavor());
+
+ /* If we have no consensus, we have no IPv6 in it */
+ if (!cons) {
+ return 0;
+ }
+
+ /* Different flavours of consensus gained IPv6 at different times */
+ if (we_use_microdescriptors_for_circuits(options)) {
+ return
+ cons->consensus_method >= MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS;
+ } else {
+ return cons->consensus_method >= MIN_METHOD_FOR_A_LINES;
+ }
+}
+
/** Given two router status entries for the same router identity, return 1 if
* if the contents have changed between them. Otherwise, return 0. */
static int
@@ -1572,6 +1596,7 @@ notify_before_networkstatus_changes(const networkstatus_t *old_c,
{
notify_control_networkstatus_changed(old_c, new_c);
dos_consensus_has_changed(new_c);
+ relay_consensus_has_changed(new_c);
}
/* Called after a new consensus has been put in the global state. It is safe
@@ -1681,7 +1706,7 @@ handle_missing_protocol_warning_impl(const networkstatus_t *c,
}
tor_free(protocol_warning);
if (should_exit)
- exit(1);
+ exit(1); // XXXX bad exit: should return from main.
}
/** Called when we have received a networkstatus <b>c</b>. If there are
@@ -1730,7 +1755,7 @@ networkstatus_set_current_consensus(const char *consensus,
{
networkstatus_t *c=NULL;
int r, result = -1;
- time_t now = time(NULL);
+ time_t now = approx_time();
const or_options_t *options = get_options();
char *unverified_fname = NULL, *consensus_fname = NULL;
int flav = networkstatus_parse_flavor_name(flavor);
@@ -1795,15 +1820,15 @@ networkstatus_set_current_consensus(const char *consensus,
}
if (!strcmp(flavor, "ns")) {
- consensus_fname = get_datadir_fname("cached-consensus");
- unverified_fname = get_datadir_fname("unverified-consensus");
+ consensus_fname = get_cachedir_fname("cached-consensus");
+ unverified_fname = get_cachedir_fname("unverified-consensus");
if (current_ns_consensus) {
current_digests = &current_ns_consensus->digests;
current_valid_after = current_ns_consensus->valid_after;
}
} else if (!strcmp(flavor, "microdesc")) {
- consensus_fname = get_datadir_fname("cached-microdesc-consensus");
- unverified_fname = get_datadir_fname("unverified-microdesc-consensus");
+ consensus_fname = get_cachedir_fname("cached-microdesc-consensus");
+ unverified_fname = get_cachedir_fname("unverified-microdesc-consensus");
if (current_md_consensus) {
current_digests = &current_md_consensus->digests;
current_valid_after = current_md_consensus->valid_after;
@@ -1812,9 +1837,9 @@ networkstatus_set_current_consensus(const char *consensus,
cached_dir_t *cur;
char buf[128];
tor_snprintf(buf, sizeof(buf), "cached-%s-consensus", flavor);
- consensus_fname = get_datadir_fname(buf);
+ consensus_fname = get_cachedir_fname(buf);
tor_snprintf(buf, sizeof(buf), "unverified-%s-consensus", flavor);
- unverified_fname = get_datadir_fname(buf);
+ unverified_fname = get_cachedir_fname(buf);
cur = dirserv_get_consensus(flavor);
if (cur) {
current_digests = &cur->digests;
@@ -2057,6 +2082,7 @@ networkstatus_note_certs_arrived(const char *source_dir)
{
int i;
for (i=0; i<N_CONSENSUS_FLAVORS; ++i) {
+ const char *flavor_name = networkstatus_get_flavor_name(i);
consensus_waiting_for_certs_t *waiting = &consensus_waiting_for_certs[i];
if (!waiting->consensus)
continue;
@@ -2064,7 +2090,7 @@ networkstatus_note_certs_arrived(const char *source_dir)
char *waiting_body = waiting->body;
if (!networkstatus_set_current_consensus(
waiting_body,
- networkstatus_get_flavor_name(i),
+ flavor_name,
NSSET_WAS_WAITING_FOR_CERTS,
source_dir)) {
tor_free(waiting_body);
@@ -2217,7 +2243,9 @@ signed_descs_update_status_from_consensus_networkstatus(smartlist_t *descs)
char *
networkstatus_getinfo_helper_single(const routerstatus_t *rs)
{
- return routerstatus_format_entry(rs, NULL, NULL, NS_CONTROL_PORT, NULL);
+ return routerstatus_format_entry(rs, NULL, NULL, NS_CONTROL_PORT,
+ ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD,
+ NULL);
}
/** Alloc and return a string describing routerstatuses for the most
@@ -2230,13 +2258,13 @@ networkstatus_getinfo_helper_single(const routerstatus_t *rs)
char *
networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now)
{
- time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH;
+ const time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH;
char *answer;
routerlist_t *rl = router_get_routerlist();
smartlist_t *statuses;
- uint8_t purpose = router_purpose_from_string(purpose_string);
+ const uint8_t purpose = router_purpose_from_string(purpose_string);
routerstatus_t rs;
- int bridge_auth = authdir_mode_bridge(get_options());
+ const int bridge_auth = authdir_mode_bridge(get_options());
if (purpose == ROUTER_PURPOSE_UNKNOWN) {
log_info(LD_DIR, "Unrecognized purpose '%s' when listing router statuses.",
@@ -2253,6 +2281,7 @@ networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now)
continue;
if (ri->purpose != purpose)
continue;
+ /* TODO: modifying the running flag in a getinfo is a bad idea */
if (bridge_auth && ri->purpose == ROUTER_PURPOSE_BRIDGE)
dirserv_set_router_is_running(ri, now);
/* then generate and write out status lines for each of them */
@@ -2271,7 +2300,6 @@ void
networkstatus_dump_bridge_status_to_file(time_t now)
{
char *status = networkstatus_getinfo_by_purpose("bridge", now);
- const or_options_t *options = get_options();
char *fname = NULL;
char *thresholds = NULL;
char *published_thresholds_and_status = NULL;
@@ -2293,8 +2321,7 @@ networkstatus_dump_bridge_status_to_file(time_t now)
"published %s\nflag-thresholds %s\n%s%s",
published, thresholds, fingerprint_line ? fingerprint_line : "",
status);
- tor_asprintf(&fname, "%s"PATH_SEPARATOR"networkstatus-bridges",
- options->DataDirectory);
+ fname = get_datadir_fname("networkstatus-bridges");
write_str_to_file(fname,published_thresholds_and_status,0);
tor_free(thresholds);
tor_free(published_thresholds_and_status);
diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h
index 39a0f753d8..1851a55e82 100644
--- a/src/or/networkstatus.h
+++ b/src/or/networkstatus.h
@@ -18,8 +18,12 @@ void networkstatus_reset_warnings(void);
void networkstatus_reset_download_failures(void);
char *networkstatus_read_cached_consensus(const char *flavorname);
int router_reload_consensus_networkstatus(void);
-void routerstatus_free(routerstatus_t *rs);
-void networkstatus_vote_free(networkstatus_t *ns);
+void routerstatus_free_(routerstatus_t *rs);
+#define routerstatus_free(rs) \
+ FREE_AND_NULL(routerstatus_t, routerstatus_free_, (rs))
+void networkstatus_vote_free_(networkstatus_t *ns);
+#define networkstatus_vote_free(ns) \
+ FREE_AND_NULL(networkstatus_t, networkstatus_vote_free_, (ns))
networkstatus_voter_info_t *networkstatus_get_voter_by_id(
networkstatus_t *vote,
const char *identity);
@@ -89,6 +93,7 @@ int networkstatus_consensus_can_use_multiple_directories(
MOCK_DECL(int, networkstatus_consensus_can_use_extra_fallbacks,(
const or_options_t *options));
int networkstatus_consensus_is_already_downloading(const char *resource);
+int networkstatus_consensus_has_ipv6(const or_options_t* options);
#define NSSET_FROM_CACHE 1
#define NSSET_WAS_WAITING_FOR_CERTS 2
@@ -124,12 +129,16 @@ int32_t networkstatus_get_bw_weight(networkstatus_t *ns, const char *weight,
int32_t default_val);
const char *networkstatus_get_flavor_name(consensus_flavor_t flav);
int networkstatus_parse_flavor_name(const char *flavname);
-void document_signature_free(document_signature_t *sig);
+void document_signature_free_(document_signature_t *sig);
+#define document_signature_free(sig) \
+ FREE_AND_NULL(document_signature_t, document_signature_free_, (sig))
document_signature_t *document_signature_dup(const document_signature_t *sig);
void networkstatus_free_all(void);
int networkstatus_get_weight_scale_param(networkstatus_t *ns);
-void vote_routerstatus_free(vote_routerstatus_t *rs);
+void vote_routerstatus_free_(vote_routerstatus_t *rs);
+#define vote_routerstatus_free(rs) \
+ FREE_AND_NULL(vote_routerstatus_t, vote_routerstatus_free_, (rs))
#ifdef NETWORKSTATUS_PRIVATE
#ifdef TOR_UNIT_TESTS
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index ac94498558..212606d2f7 100644
--- a/src/or/nodelist.c
+++ b/src/or/nodelist.c
@@ -43,6 +43,7 @@
#include "or.h"
#include "address.h"
#include "address_set.h"
+#include "bridges.h"
#include "config.h"
#include "control.h"
#include "dirserv.h"
@@ -66,7 +67,9 @@
#include <string.h>
static void nodelist_drop_node(node_t *node, int remove_from_ht);
-static void node_free(node_t *node);
+#define node_free(val) \
+ FREE_AND_NULL(node_t, node_free_, (val))
+static void node_free_(node_t *node);
/** count_usable_descriptors counts descriptors with these flag(s)
*/
@@ -105,6 +108,7 @@ typedef struct nodelist_t {
* you should add it to this map with node_add_to_ed25519_map().
*/
HT_HEAD(nodelist_ed_map, node_t) nodes_by_ed_id;
+
/* Set of addresses that belong to nodes we believe in. */
address_set_t *node_addrs;
} nodelist_t;
@@ -158,8 +162,8 @@ init_nodelist(void)
}
/** As node_get_by_id, but returns a non-const pointer */
-node_t *
-node_get_mutable_by_id(const char *identity_digest)
+MOCK_IMPL(node_t *,
+node_get_mutable_by_id,(const char *identity_digest))
{
node_t search, *node;
if (PREDICT_UNLIKELY(the_nodelist == NULL))
@@ -375,27 +379,6 @@ node_set_hsdir_index(node_t *node, const networkstatus_t *ns)
return;
}
-/** Recompute all node hsdir indices. */
-void
-nodelist_recompute_all_hsdir_indices(void)
-{
- networkstatus_t *consensus;
- if (!the_nodelist) {
- return;
- }
-
- /* Get a live consensus. Abort if not found */
- consensus = networkstatus_get_live_consensus(approx_time());
- if (!consensus) {
- return;
- }
-
- /* Recompute all hsdir indices */
- SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
- node_set_hsdir_index(node, consensus);
- } SMARTLIST_FOREACH_END(node);
-}
-
/** Called when a node's address changes. */
static void
node_addrs_changed(node_t *node)
@@ -493,7 +476,7 @@ nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out)
/* Setting the HSDir index requires the ed25519 identity key which can
* only be found either in the ri or md. This is why this is called here.
* Only nodes supporting HSDir=2 protocol version needs this index. */
- if (node->rs && node->rs->supports_v3_hsdir) {
+ if (node->rs && node->rs->pv.supports_v3_hsdir) {
node_set_hsdir_index(node,
networkstatus_get_latest_consensus());
}
@@ -537,7 +520,7 @@ nodelist_add_microdesc(microdesc_t *md)
/* Setting the HSDir index requires the ed25519 identity key which can
* only be found either in the ri or md. This is why this is called here.
* Only nodes supporting HSDir=2 protocol version needs this index. */
- if (rs->supports_v3_hsdir) {
+ if (rs->pv.supports_v3_hsdir) {
node_set_hsdir_index(node, ns);
}
node_add_to_ed25519_map(node);
@@ -598,7 +581,7 @@ nodelist_set_consensus(networkstatus_t *ns)
}
}
- if (rs->supports_v3_hsdir) {
+ if (rs->pv.supports_v3_hsdir) {
node_set_hsdir_index(node, ns);
}
node_set_country(node);
@@ -730,7 +713,7 @@ nodelist_find_nodes_with_microdesc(const microdesc_t *md)
/** Release storage held by <b>node</b> */
static void
-node_free(node_t *node)
+node_free_(node_t *node)
{
if (!node)
return;
@@ -983,9 +966,12 @@ node_get_ed25519_id(const node_t *node)
{
const ed25519_public_key_t *ri_pk = NULL;
const ed25519_public_key_t *md_pk = NULL;
+
if (node->ri) {
if (node->ri->cache_info.signing_key_cert) {
ri_pk = &node->ri->cache_info.signing_key_cert->signing_key;
+ /* Checking whether routerinfo ed25519 is all zero.
+ * Our descriptor parser should make sure this never happens. */
if (BUG(ed25519_public_key_is_zero(ri_pk)))
ri_pk = NULL;
}
@@ -994,6 +980,10 @@ node_get_ed25519_id(const node_t *node)
if (node->md) {
if (node->md->ed25519_identity_pkey) {
md_pk = node->md->ed25519_identity_pkey;
+ /* Checking whether microdesc ed25519 is all zero.
+ * Our descriptor parser should make sure this never happens. */
+ if (BUG(ed25519_public_key_is_zero(md_pk)))
+ md_pk = NULL;
}
}
@@ -1027,27 +1017,47 @@ node_ed25519_id_matches(const node_t *node, const ed25519_public_key_t *id)
}
}
+/** Dummy object that should be unreturnable. Used to ensure that
+ * node_get_protover_summary_flags() always returns non-NULL. */
+static const protover_summary_flags_t zero_protover_flags = {
+ 0,0,0,0,0,0,0
+};
+
+/** Return the protover_summary_flags for a given node. */
+static const protover_summary_flags_t *
+node_get_protover_summary_flags(const node_t *node)
+{
+ if (node->rs) {
+ return &node->rs->pv;
+ } else if (node->ri) {
+ return &node->ri->pv;
+ } else {
+ /* This should be impossible: every node should have a routerstatus or a
+ * router descriptor or both. But just in case we've messed up somehow,
+ * return a nice empty set of flags to indicate "this node supports
+ * nothing." */
+ tor_assert_nonfatal_unreached_once();
+ return &zero_protover_flags;
+ }
+}
+
/** Return true iff <b>node</b> supports authenticating itself
- * by ed25519 ID during the link handshake in a way that we can understand
- * when we probe it. */
+ * by ed25519 ID during the link handshake. If <b>compatible_with_us</b>,
+ * it needs to be using a link authentication method that we understand.
+ * If not, any plausible link authentication method will do. */
int
-node_supports_ed25519_link_authentication(const node_t *node)
+node_supports_ed25519_link_authentication(const node_t *node,
+ int compatible_with_us)
{
- /* XXXX Oh hm. What if some day in the future there are link handshake
- * versions that aren't 3 but which are ed25519 */
if (! node_get_ed25519_id(node))
return 0;
- if (node->ri) {
- const char *protos = node->ri->protocol_list;
- if (protos == NULL)
- return 0;
- return protocol_list_supports_protocol(protos, PRT_LINKAUTH, 3);
- }
- if (node->rs) {
- return node->rs->supports_ed25519_link_handshake;
- }
- tor_assert_nonfatal_unreached_once();
- return 0;
+
+ const protover_summary_flags_t *pv = node_get_protover_summary_flags(node);
+
+ if (compatible_with_us)
+ return pv->supports_ed25519_link_handshake_compat;
+ else
+ return pv->supports_ed25519_link_handshake_any;
}
/** Return true iff <b>node</b> supports the hidden service directory version
@@ -1057,27 +1067,7 @@ node_supports_v3_hsdir(const node_t *node)
{
tor_assert(node);
- if (node->rs) {
- return node->rs->supports_v3_hsdir;
- }
- if (node->ri) {
- if (node->ri->protocol_list == NULL) {
- return 0;
- }
- /* Bug #22447 forces us to filter on tor version:
- * If platform is a Tor version, and older than 0.3.0.8, return False.
- * Else, obey the protocol list. */
- if (node->ri->platform) {
- if (!strcmpstart(node->ri->platform, "Tor ") &&
- !tor_version_as_new_as(node->ri->platform, "0.3.0.8")) {
- return 0;
- }
- }
- return protocol_list_supports_protocol(node->ri->protocol_list,
- PRT_HSDIR, PROTOVER_HSDIR_V3);
- }
- tor_assert_nonfatal_unreached_once();
- return 0;
+ return node_get_protover_summary_flags(node)->supports_v3_hsdir;
}
/** Return true iff <b>node</b> supports ed25519 authentication as an hidden
@@ -1087,18 +1077,7 @@ node_supports_ed25519_hs_intro(const node_t *node)
{
tor_assert(node);
- if (node->rs) {
- return node->rs->supports_ed25519_hs_intro;
- }
- if (node->ri) {
- if (node->ri->protocol_list == NULL) {
- return 0;
- }
- return protocol_list_supports_protocol(node->ri->protocol_list,
- PRT_HSINTRO, PROTOVER_HS_INTRO_V3);
- }
- tor_assert_nonfatal_unreached_once();
- return 0;
+ return node_get_protover_summary_flags(node)->supports_ed25519_hs_intro;
}
/** Return true iff <b>node</b> supports to be a rendezvous point for hidden
@@ -1108,19 +1087,7 @@ node_supports_v3_rendezvous_point(const node_t *node)
{
tor_assert(node);
- if (node->rs) {
- return node->rs->supports_v3_rendezvous_point;
- }
- if (node->ri) {
- if (node->ri->protocol_list == NULL) {
- return 0;
- }
- return protocol_list_supports_protocol(node->ri->protocol_list,
- PRT_HSREND,
- PROTOVER_HS_RENDEZVOUS_POINT_V3);
- }
- tor_assert_nonfatal_unreached_once();
- return 0;
+ return node_get_protover_summary_flags(node)->supports_v3_rendezvous_point;
}
/** Return the RSA ID key's SHA1 digest for the provided node. */
@@ -1164,15 +1131,44 @@ node_is_dir(const node_t *node)
}
}
-/** Return true iff <b>node</b> has either kind of usable descriptor -- that
- * is, a routerdescriptor or a microdescriptor. */
+/** Return true iff <b>node</b> has either kind of descriptor -- that
+ * is, a routerdescriptor or a microdescriptor.
+ *
+ * You should probably use node_has_preferred_descriptor() instead.
+ **/
int
-node_has_descriptor(const node_t *node)
+node_has_any_descriptor(const node_t *node)
{
return (node->ri ||
(node->rs && node->md));
}
+/** Return true iff <b>node</b> has the kind of descriptor we would prefer to
+ * use for it, given our configuration and how we intend to use the node.
+ *
+ * If <b>for_direct_connect</b> is true, we intend to connect to the node
+ * directly, as the first hop of a circuit; otherwise, we intend to connect to
+ * it indirectly, or use it as if we were connecting to it indirectly. */
+int
+node_has_preferred_descriptor(const node_t *node,
+ int for_direct_connect)
+{
+ const int is_bridge = node_is_a_configured_bridge(node);
+ const int we_use_mds = we_use_microdescriptors_for_circuits(get_options());
+
+ if ((is_bridge && for_direct_connect) || !we_use_mds) {
+ /* We need an ri in this case. */
+ if (!node->ri)
+ return 0;
+ } else {
+ /* Otherwise we need an rs and an md. */
+ if (node->rs == NULL || node->md == NULL)
+ return 0;
+ }
+
+ return 1;
+}
+
/** Return the router_purpose of <b>node</b>. */
int
node_get_purpose(const node_t *node)
@@ -1699,15 +1695,21 @@ microdesc_has_curve25519_onion_key(const microdesc_t *md)
int
node_has_curve25519_onion_key(const node_t *node)
{
- if (!node)
- return 0;
+ return node_get_curve25519_onion_key(node) != NULL;
+}
- if (node->ri)
- return routerinfo_has_curve25519_onion_key(node->ri);
- else if (node->md)
- return microdesc_has_curve25519_onion_key(node->md);
+/** Return the curve25519 key of <b>node</b>, or NULL if none. */
+const curve25519_public_key_t *
+node_get_curve25519_onion_key(const node_t *node)
+{
+ if (!node)
+ return NULL;
+ if (routerinfo_has_curve25519_onion_key(node->ri))
+ return node->ri->onion_curve25519_pkey;
+ else if (microdesc_has_curve25519_onion_key(node->md))
+ return node->md->onion_curve25519_pkey;
else
- return 0;
+ return NULL;
}
/** Refresh the country code of <b>ri</b>. This function MUST be called on
@@ -2249,7 +2251,8 @@ compute_frac_paths_available(const networkstatus_t *consensus,
nu);
SMARTLIST_FOREACH_BEGIN(myexits_unflagged, const node_t *, node) {
- if (node_has_descriptor(node) && node_exit_policy_rejects_all(node)) {
+ if (node_has_preferred_descriptor(node, 0) &&
+ node_exit_policy_rejects_all(node)) {
SMARTLIST_DEL_CURRENT(myexits_unflagged, node);
/* this node is not actually an exit */
np--;
diff --git a/src/or/nodelist.h b/src/or/nodelist.h
index 8a0c79f86d..00f12ca1e4 100644
--- a/src/or/nodelist.h
+++ b/src/or/nodelist.h
@@ -16,7 +16,7 @@
tor_assert((n)->ri || (n)->rs); \
} STMT_END
-node_t *node_get_mutable_by_id(const char *identity_digest);
+MOCK_DECL(node_t *, node_get_mutable_by_id,(const char *identity_digest));
MOCK_DECL(const node_t *, node_get_by_id, (const char *identity_digest));
node_t *node_get_mutable_by_ed25519_id(const ed25519_public_key_t *ed_id);
MOCK_DECL(const node_t *, node_get_by_ed25519_id,
@@ -36,8 +36,6 @@ void nodelist_remove_routerinfo(routerinfo_t *ri);
void nodelist_purge(void);
smartlist_t *nodelist_find_nodes_with_microdesc(const microdesc_t *md);
-void nodelist_recompute_all_hsdir_indices(void);
-
void nodelist_free_all(void);
void nodelist_assert_ok(void);
@@ -48,7 +46,9 @@ void node_get_verbose_nickname(const node_t *node,
void node_get_verbose_nickname_by_id(const char *id_digest,
char *verbose_name_out);
int node_is_dir(const node_t *node);
-int node_has_descriptor(const node_t *node);
+int node_has_any_descriptor(const node_t *node);
+int node_has_preferred_descriptor(const node_t *node,
+ int for_direct_connect);
int node_get_purpose(const node_t *node);
#define node_is_bridge(node) \
(node_get_purpose((node)) == ROUTER_PURPOSE_BRIDGE)
@@ -66,7 +66,8 @@ const smartlist_t *node_get_declared_family(const node_t *node);
const ed25519_public_key_t *node_get_ed25519_id(const node_t *node);
int node_ed25519_id_matches(const node_t *node,
const ed25519_public_key_t *id);
-int node_supports_ed25519_link_authentication(const node_t *node);
+int node_supports_ed25519_link_authentication(const node_t *node,
+ int compatible_with_us);
int node_supports_v3_hsdir(const node_t *node);
int node_supports_ed25519_hs_intro(const node_t *node);
int node_supports_v3_rendezvous_point(const node_t *node);
@@ -86,6 +87,8 @@ int node_get_prim_dirport(const node_t *node, tor_addr_port_t *ap_out);
void node_get_pref_dirport(const node_t *node, tor_addr_port_t *ap_out);
void node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out);
int node_has_curve25519_onion_key(const node_t *node);
+const curve25519_public_key_t *node_get_curve25519_onion_key(
+ const node_t *node);
MOCK_DECL(smartlist_t *, nodelist_get_list, (void));
diff --git a/src/or/ntmain.c b/src/or/ntmain.c
index 508e5844eb..ebbe0018bd 100644
--- a/src/or/ntmain.c
+++ b/src/or/ntmain.c
@@ -195,7 +195,7 @@ nt_service_loadlibrary(void)
return;
err:
printf("Unable to load library support for NT services: exiting.\n");
- exit(1);
+ exit(1); // exit ok: ntmain can't read libraries
}
/** If we're compiled to run as an NT service, and the service wants to
@@ -318,7 +318,7 @@ nt_service_main(void)
printf("Service error %d : %s\n", (int) result, errmsg);
tor_free(errmsg);
if (result == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
- if (tor_init(backup_argc, backup_argv) < 0)
+ if (tor_init(backup_argc, backup_argv))
return;
switch (get_options()->command) {
case CMD_RUN_TOR:
diff --git a/src/or/onion.c b/src/or/onion.c
index 7e1e89df1b..bd80c2f503 100644
--- a/src/or/onion.c
+++ b/src/or/onion.c
@@ -423,7 +423,7 @@ server_onion_keys_new(void)
/** Release all storage held in <b>keys</b>. */
void
-server_onion_keys_free(server_onion_keys_t *keys)
+server_onion_keys_free_(server_onion_keys_t *keys)
{
if (! keys)
return;
diff --git a/src/or/onion.h b/src/or/onion.h
index 95544dfac1..3b738debeb 100644
--- a/src/or/onion.h
+++ b/src/or/onion.h
@@ -31,7 +31,9 @@ typedef struct server_onion_keys_t {
#define MAX_ONIONSKIN_REPLY_LEN 255
server_onion_keys_t *server_onion_keys_new(void);
-void server_onion_keys_free(server_onion_keys_t *keys);
+void server_onion_keys_free_(server_onion_keys_t *keys);
+#define server_onion_keys_free(keys) \
+ FREE_AND_NULL(server_onion_keys_t, server_onion_keys_free_, (keys))
void onion_handshake_state_release(onion_handshake_state_t *state);
diff --git a/src/or/onion_fast.c b/src/or/onion_fast.c
index 146943a273..de9103b1f5 100644
--- a/src/or/onion_fast.c
+++ b/src/or/onion_fast.c
@@ -23,7 +23,7 @@
* [*]Actually, it's possible that TAP _was_ a little better than TLS with
* RSA1024 certificates and EDH1024 for forward secrecy, if you
* hypothesize an adversary who can compute discrete logarithms on a
- * small number of targetted DH1024 fields, but who can't break all that
+ * small number of targeted DH1024 fields, but who can't break all that
* many RSA1024 keys.
**/
@@ -32,7 +32,7 @@
/** Release all state held in <b>victim</b>. */
void
-fast_handshake_state_free(fast_handshake_state_t *victim)
+fast_handshake_state_free_(fast_handshake_state_t *victim)
{
if (! victim)
return;
diff --git a/src/or/onion_fast.h b/src/or/onion_fast.h
index 3a5aefea3f..c56712e2c2 100644
--- a/src/or/onion_fast.h
+++ b/src/or/onion_fast.h
@@ -19,7 +19,9 @@ typedef struct fast_handshake_state_t {
uint8_t state[DIGEST_LEN];
} fast_handshake_state_t;
-void fast_handshake_state_free(fast_handshake_state_t *victim);
+void fast_handshake_state_free_(fast_handshake_state_t *victim);
+#define fast_handshake_state_free(st) \
+ FREE_AND_NULL(fast_handshake_state_t, fast_handshake_state_free_, (st))
int fast_onionskin_create(fast_handshake_state_t **handshake_state_out,
uint8_t *handshake_out);
diff --git a/src/or/onion_ntor.c b/src/or/onion_ntor.c
index 902260b54b..b167cb61fb 100644
--- a/src/or/onion_ntor.c
+++ b/src/or/onion_ntor.c
@@ -28,7 +28,7 @@
/** Free storage held in an ntor handshake state. */
void
-ntor_handshake_state_free(ntor_handshake_state_t *state)
+ntor_handshake_state_free_(ntor_handshake_state_t *state)
{
if (!state)
return;
diff --git a/src/or/onion_ntor.h b/src/or/onion_ntor.h
index 02dea2dfc1..f7c962b7d0 100644
--- a/src/or/onion_ntor.h
+++ b/src/or/onion_ntor.h
@@ -17,7 +17,9 @@ typedef struct ntor_handshake_state_t ntor_handshake_state_t;
/** Length of an ntor reply, as sent from server to client. */
#define NTOR_REPLY_LEN 64
-void ntor_handshake_state_free(ntor_handshake_state_t *state);
+void ntor_handshake_state_free_(ntor_handshake_state_t *state);
+#define ntor_handshake_state_free(state) \
+ FREE_AND_NULL(ntor_handshake_state_t, ntor_handshake_state_free_, (state))
int onion_skin_ntor_create(const uint8_t *router_id,
const curve25519_public_key_t *router_key,
diff --git a/src/or/or.h b/src/or/or.h
index 5128fd2197..3c0c2ad613 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -506,6 +506,7 @@ typedef enum {
*/
/** Client-side circuit purpose: Normal circuit, with cpath. */
#define CIRCUIT_PURPOSE_C_GENERAL 5
+#define CIRCUIT_PURPOSE_C_HS_MIN_ 6
/** Client-side circuit purpose: at the client, connecting to intro point. */
#define CIRCUIT_PURPOSE_C_INTRODUCING 6
/** Client-side circuit purpose: at the client, sent INTRODUCE1 to intro point,
@@ -523,28 +524,46 @@ typedef enum {
#define CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED 11
/** Client-side circuit purpose: at the client, rendezvous established. */
#define CIRCUIT_PURPOSE_C_REND_JOINED 12
+/** This circuit is used for getting hsdirs */
+#define CIRCUIT_PURPOSE_C_HSDIR_GET 13
+#define CIRCUIT_PURPOSE_C_HS_MAX_ 13
/** This circuit is used for build time measurement only */
-#define CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT 13
-#define CIRCUIT_PURPOSE_C_MAX_ 13
+#define CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT 14
+#define CIRCUIT_PURPOSE_C_MAX_ 14
+
+#define CIRCUIT_PURPOSE_S_HS_MIN_ 15
/** Hidden-service-side circuit purpose: at the service, waiting for
* introductions. */
-#define CIRCUIT_PURPOSE_S_ESTABLISH_INTRO 14
+#define CIRCUIT_PURPOSE_S_ESTABLISH_INTRO 15
/** Hidden-service-side circuit purpose: at the service, successfully
* established intro. */
-#define CIRCUIT_PURPOSE_S_INTRO 15
+#define CIRCUIT_PURPOSE_S_INTRO 16
/** Hidden-service-side circuit purpose: at the service, connecting to rend
* point. */
-#define CIRCUIT_PURPOSE_S_CONNECT_REND 16
+#define CIRCUIT_PURPOSE_S_CONNECT_REND 17
/** Hidden-service-side circuit purpose: at the service, rendezvous
* established. */
-#define CIRCUIT_PURPOSE_S_REND_JOINED 17
+#define CIRCUIT_PURPOSE_S_REND_JOINED 18
+/** This circuit is used for uploading hsdirs */
+#define CIRCUIT_PURPOSE_S_HSDIR_POST 19
+#define CIRCUIT_PURPOSE_S_HS_MAX_ 19
+
/** A testing circuit; not meant to be used for actual traffic. */
-#define CIRCUIT_PURPOSE_TESTING 18
+#define CIRCUIT_PURPOSE_TESTING 20
/** A controller made this circuit and Tor should not use it. */
-#define CIRCUIT_PURPOSE_CONTROLLER 19
+#define CIRCUIT_PURPOSE_CONTROLLER 21
/** This circuit is used for path bias probing only */
-#define CIRCUIT_PURPOSE_PATH_BIAS_TESTING 20
-#define CIRCUIT_PURPOSE_MAX_ 20
+#define CIRCUIT_PURPOSE_PATH_BIAS_TESTING 22
+
+/** This circuit is used for vanguards/restricted paths.
+ *
+ * This type of circuit is *only* created preemptively and never
+ * on-demand. When an HS operation needs to take place (e.g. connect to an
+ * intro point), these circuits are then cannibalized and repurposed to the
+ * actual needed HS purpose. */
+#define CIRCUIT_PURPOSE_HS_VANGUARDS 23
+
+#define CIRCUIT_PURPOSE_MAX_ 23
/** A catch-all for unrecognized purposes. Currently we don't expect
* to make or see any circuits with this purpose. */
#define CIRCUIT_PURPOSE_UNKNOWN 255
@@ -567,6 +586,14 @@ typedef enum {
/** True iff the circuit_t c is actually an or_circuit_t */
#define CIRCUIT_IS_ORCIRC(c) (((circuit_t *)(c))->magic == OR_CIRCUIT_MAGIC)
+/** True iff this circuit purpose should count towards the global
+ * pending rate limit (set by MaxClientCircuitsPending). We count all
+ * general purpose circuits, as well as the first step of client onion
+ * service connections (HSDir gets). */
+#define CIRCUIT_PURPOSE_COUNTS_TOWARDS_MAXPENDING(p) \
+ ((p) == CIRCUIT_PURPOSE_C_GENERAL || \
+ (p) == CIRCUIT_PURPOSE_C_HSDIR_GET)
+
/** How many circuits do we want simultaneously in-progress to handle
* a given stream? */
#define MIN_CIRCUITS_HANDLING_STREAM 2
@@ -1166,8 +1193,8 @@ typedef struct packed_cell_t {
/** Next cell queued on this circuit. */
TOR_SIMPLEQ_ENTRY(packed_cell_t) next;
char body[CELL_MAX_NETWORK_SIZE]; /**< Cell as packed for network. */
- uint32_t inserted_time; /**< Time (in milliseconds since epoch, with high
- * bits truncated) when this cell was inserted. */
+ uint32_t inserted_timestamp; /**< Time (in timestamp units) when this cell
+ * was inserted */
} packed_cell_t;
/** A queue of cells on a circuit, waiting to be added to the
@@ -1182,7 +1209,8 @@ typedef struct cell_queue_t {
typedef struct destroy_cell_t {
TOR_SIMPLEQ_ENTRY(destroy_cell_t) next;
circid_t circid;
- uint32_t inserted_time; /** Timestamp when this was queued. */
+ uint32_t inserted_timestamp; /**< Time (in timestamp units) when this cell
+ * was inserted */
uint8_t reason;
} destroy_cell_t;
@@ -1798,9 +1826,6 @@ typedef struct entry_connection_t {
* the exit has sent a CONNECTED cell) and we have chosen to use it.
*/
unsigned int may_use_optimistic_data : 1;
-
- /** Are we a socks SocksSocket listener? */
- unsigned int is_socks_socket:1;
} entry_connection_t;
/** Subtype of connection_t for an "directory connection" -- that is, an HTTP
@@ -2053,15 +2078,6 @@ typedef enum {
#define download_schedule_increment_bitfield_t \
ENUM_BF(download_schedule_increment_t)
-/** Enumeration: do we want to use the random exponential backoff
- * mechanism? */
-typedef enum {
- DL_SCHED_DETERMINISTIC = 0,
- DL_SCHED_RANDOM_EXPONENTIAL = 1,
-} download_schedule_backoff_t;
-#define download_schedule_backoff_bitfield_t \
- ENUM_BF(download_schedule_backoff_t)
-
/** Information about our plans for retrying downloads for a downloadable
* directory object.
* Each type of downloadable directory object has a corresponding retry
@@ -2108,11 +2124,6 @@ typedef struct download_status_t {
download_schedule_increment_bitfield_t increment_on : 1; /**< does this
* schedule increment on each attempt,
* or after each failure? */
- download_schedule_backoff_bitfield_t backoff : 1; /**< do we use the
- * deterministic schedule, or random
- * exponential backoffs?
- * Increment on failure schedules
- * always use exponential backoff. */
uint8_t last_backoff_position; /**< number of attempts/failures, depending
* on increment_on, when we last recalculated
* the delay. Only updated if backoff
@@ -2180,6 +2191,43 @@ typedef struct signed_descriptor_t {
/** A signed integer representing a country code. */
typedef int16_t country_t;
+/** Flags used to summarize the declared protocol versions of a relay,
+ * so we don't need to parse them again and again. */
+typedef struct protover_summary_flags_t {
+ /** True iff we have a proto line for this router, or a versions line
+ * from which we could infer the protocols. */
+ unsigned int protocols_known:1;
+
+ /** True iff this router has a version or protocol list that allows it to
+ * accept EXTEND2 cells. This requires Relay=2. */
+ unsigned int supports_extend2_cells:1;
+
+ /** True iff this router has a protocol list that allows it to negotiate
+ * ed25519 identity keys on a link handshake with us. This
+ * requires LinkAuth=3. */
+ unsigned int supports_ed25519_link_handshake_compat:1;
+
+ /** True iff this router has a protocol list that allows it to negotiate
+ * ed25519 identity keys on a link handshake, at all. This requires some
+ * LinkAuth=X for X >= 3. */
+ unsigned int supports_ed25519_link_handshake_any:1;
+
+ /** True iff this router has a protocol list that allows it to be an
+ * introduction point supporting ed25519 authentication key which is part of
+ * the v3 protocol detailed in proposal 224. This requires HSIntro=4. */
+ unsigned int supports_ed25519_hs_intro : 1;
+
+ /** True iff this router has a protocol list that allows it to be an hidden
+ * service directory supporting version 3 as seen in proposal 224. This
+ * requires HSDir=2. */
+ unsigned int supports_v3_hsdir : 1;
+
+ /** True iff this router has a protocol list that allows it to be an hidden
+ * service rendezvous point supporting version 3 as seen in proposal 224.
+ * This requires HSRend=2. */
+ unsigned int supports_v3_rendezvous_point: 1;
+} protover_summary_flags_t;
+
/** Information about another onion router in the network. */
typedef struct {
signed_descriptor_t cache_info;
@@ -2248,23 +2296,26 @@ typedef struct {
* this routerinfo. Used only during voting. */
unsigned int omit_from_vote:1;
+ /** Flags to summarize the protocol versions for this routerinfo_t. */
+ protover_summary_flags_t pv;
+
/** Tor can use this router for general positions in circuits; we got it
* from a directory server as usual, or we're an authority and a server
* uploaded it. */
#define ROUTER_PURPOSE_GENERAL 0
/** Tor should avoid using this router for circuit-building: we got it
- * from a crontroller. If the controller wants to use it, it'll have to
+ * from a controller. If the controller wants to use it, it'll have to
* ask for it by identity. */
#define ROUTER_PURPOSE_CONTROLLER 1
/** Tor should use this router only for bridge positions in circuits: we got
* it via a directory request from the bridge itself, or a bridge
- * authority. x*/
+ * authority. */
#define ROUTER_PURPOSE_BRIDGE 2
/** Tor should not use this router; it was marked in cached-descriptors with
* a purpose we didn't recognize. */
#define ROUTER_PURPOSE_UNKNOWN 255
- /* In what way did we find out about this router? One of ROUTER_PURPOSE_*.
+ /** In what way did we find out about this router? One of ROUTER_PURPOSE_*.
* Routers of different purposes are kept segregated and used for different
* things; see notes on ROUTER_PURPOSE_* macros above.
*/
@@ -2326,38 +2377,15 @@ typedef struct routerstatus_t {
unsigned int is_v2_dir:1; /** True iff this router publishes an open DirPort
* or it claims to accept tunnelled dir requests.
*/
- /** True iff we have a proto line for this router, or a versions line
- * from which we could infer the protocols. */
- unsigned int protocols_known:1;
-
- /** True iff this router has a version or protocol list that allows it to
- * accept EXTEND2 cells */
- unsigned int supports_extend2_cells:1;
-
- /** True iff this router has a protocol list that allows it to negotiate
- * ed25519 identity keys on a link handshake. */
- unsigned int supports_ed25519_link_handshake:1;
-
- /** True iff this router has a protocol list that allows it to be an
- * introduction point supporting ed25519 authentication key which is part of
- * the v3 protocol detailed in proposal 224. This requires HSIntro=4. */
- unsigned int supports_ed25519_hs_intro : 1;
-
- /** True iff this router has a protocol list that allows it to be an hidden
- * service directory supporting version 3 as seen in proposal 224. This
- * requires HSDir=2. */
- unsigned int supports_v3_hsdir : 1;
-
- /** True iff this router has a protocol list that allows it to be an hidden
- * service rendezvous point supporting version 3 as seen in proposal 224.
- * This requires HSRend=2. */
- unsigned int supports_v3_rendezvous_point: 1;
unsigned int has_bandwidth:1; /**< The vote/consensus had bw info */
unsigned int has_exitsummary:1; /**< The vote/consensus had exit summaries */
unsigned int bw_is_unmeasured:1; /**< This is a consensus entry, with
* the Unmeasured flag set. */
+ /** Flags to summarize the protocol versions for this routerstatus_t. */
+ protover_summary_flags_t pv;
+
uint32_t bandwidth_kb; /**< Bandwidth (capacity) of the router as reported in
* the vote/consensus, in kilobytes/sec. */
@@ -3105,7 +3133,7 @@ typedef struct circuit_t {
/** When the circuit was first used, or 0 if the circuit is clean.
*
- * XXXX Note that some code will artifically adjust this value backward
+ * XXXX Note that some code will artificially adjust this value backward
* in time in order to indicate that a circuit shouldn't be used for new
* streams, but that it can stay alive as long as it has streams on it.
* That's a kludge we should fix.
@@ -3363,7 +3391,7 @@ typedef struct origin_circuit_t {
uint32_t global_identifier;
/** True if we have associated one stream to this circuit, thereby setting
- * the isolation paramaters for this circuit. Note that this doesn't
+ * the isolation parameters for this circuit. Note that this doesn't
* necessarily mean that we've <em>attached</em> any streams to the circuit:
* we may only have marked up this circuit during the launch process.
*/
@@ -3504,12 +3532,6 @@ typedef struct or_circuit_t {
* exit-ward queues of this circuit; reset every time when writing
* buffer stats to disk. */
uint64_t total_cell_waiting_time;
-
- /** Maximum cell queue size for a middle relay; this is stored per circuit
- * so append_cell_to_circuit_queue() can adjust it if it changes. If set
- * to zero, it is initialized to the default value.
- */
- uint32_t max_middle_cells;
} or_circuit_t;
#if REND_COOKIE_LEN != DIGEST_LEN
@@ -3654,10 +3676,24 @@ typedef struct {
int TruncateLogFile; /**< Boolean: Should we truncate the log file
before we start writing? */
char *SyslogIdentityTag; /**< Identity tag to add for syslog logging. */
+ char *AndroidIdentityTag; /**< Identity tag to add for Android logging. */
char *DebugLogFile; /**< Where to send verbose log messages. */
- char *DataDirectory; /**< OR only: where to store long-term data. */
+ char *DataDirectory_option; /**< Where to store long-term data, as
+ * configured by the user. */
+ char *DataDirectory; /**< Where to store long-term data, as modified. */
int DataDirectoryGroupReadable; /**< Boolean: Is the DataDirectory g+r? */
+
+ char *KeyDirectory_option; /**< Where to store keys, as
+ * configured by the user. */
+ char *KeyDirectory; /**< Where to store keys data, as modified. */
+ int KeyDirectoryGroupReadable; /**< Boolean: Is the KeyDirectory g+r? */
+
+ char *CacheDirectory_option; /**< Where to store cached data, as
+ * configured by the user. */
+ char *CacheDirectory; /**< Where to store cached data, as modified. */
+ int CacheDirectoryGroupReadable; /**< Boolean: Is the CacheDirectory g+r? */
+
char *Nickname; /**< OR only: nickname of this onion router. */
char *Address; /**< OR only: configured address for this onion router. */
char *PidFile; /**< Where to store PID of Tor process. */
@@ -3693,6 +3729,7 @@ typedef struct {
* interface addresses?
* Includes OutboundBindAddresses and
* configured ports. */
+ int ReducedExitPolicy; /**<Should we use the Reduced Exit Policy? */
config_line_t *SocksPolicy; /**< Lists of socks policy components */
config_line_t *DirPolicy; /**< Lists of dir policy components */
/** Local address to bind outbound sockets */
@@ -3742,7 +3779,7 @@ typedef struct {
* for control connections. */
int ControlSocketsGroupWritable; /**< Boolean: Are control sockets g+rw? */
- int SocksSocketsGroupWritable; /**< Boolean: Are SOCKS sockets g+rw? */
+ int UnixSocksGroupWritable; /**< Boolean: Are SOCKS Unix sockets g+rw? */
/** Ports to listen on for directory connections. */
config_line_t *DirPort_lines;
config_line_t *DNSPort_lines; /**< Ports to listen on for DNS requests. */
@@ -3860,6 +3897,14 @@ typedef struct {
/** A routerset that should be used when picking RPs for HS circuits. */
routerset_t *Tor2webRendezvousPoints;
+ /** A routerset that should be used when picking middle nodes for HS
+ * circuits. */
+ routerset_t *HSLayer2Nodes;
+
+ /** A routerset that should be used when picking third-hop nodes for HS
+ * circuits. */
+ routerset_t *HSLayer3Nodes;
+
/** Onion Services in HiddenServiceSingleHopMode make one-hop (direct)
* circuits between the onion service server, and the introduction and
* rendezvous points. (Onion service descriptors are still posted using
@@ -3972,6 +4017,8 @@ typedef struct {
int HeartbeatPeriod; /**< Log heartbeat messages after this many seconds
* have passed. */
+ int MainloopStats; /**< Log main loop statistics as part of the
+ * heartbeat messages. */
char *HTTPProxy; /**< hostname[:port] to use as http proxy, if any. */
tor_addr_t HTTPProxyAddr; /**< Parsed IPv4 addr for http proxy, if any. */
@@ -4089,6 +4136,8 @@ typedef struct {
/** Process specifier for a controller that ‘owns’ this Tor
* instance. Tor will terminate if its owning controller does. */
char *OwningControllerProcess;
+ /** FD specifier for a controller that owns this Tor instance. */
+ int OwningControllerFD;
int ShutdownWaitLength; /**< When we get a SIGINT and we're a server, how
* long do we wait before exiting? */
@@ -4118,7 +4167,7 @@ typedef struct {
int UseEntryGuards_option;
/** Internal variable to remember whether we're actually acting on
* UseEntryGuards_option -- when we're a non-anonymous Tor2web client or
- * Single Onion Service, it is alwasy false, otherwise we use the value of
+ * Single Onion Service, it is always false, otherwise we use the value of
* UseEntryGuards_option. */
int UseEntryGuards;
@@ -4364,37 +4413,11 @@ typedef struct {
* it? Only altered on testing networks. */
int TestingDirConnectionMaxStall;
- /** How many times will we try to fetch a consensus before we give
- * up? Only altered on testing networks. */
- int TestingConsensusMaxDownloadTries;
-
- /** How many times will a client try to fetch a consensus while
- * bootstrapping using a list of fallback directories, before it gives up?
- * Only altered on testing networks. */
- int ClientBootstrapConsensusMaxDownloadTries;
-
- /** How many times will a client try to fetch a consensus while
- * bootstrapping using only a list of authorities, before it gives up?
- * Only altered on testing networks. */
- int ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries;
-
/** How many simultaneous in-progress connections will we make when trying
* to fetch a consensus before we wait for one to complete, timeout, or
* error out? Only altered on testing networks. */
int ClientBootstrapConsensusMaxInProgressTries;
- /** How many times will we try to download a router's descriptor before
- * giving up? Only altered on testing networks. */
- int TestingDescriptorMaxDownloadTries;
-
- /** How many times will we try to download a microdescriptor before
- * giving up? Only altered on testing networks. */
- int TestingMicrodescMaxDownloadTries;
-
- /** How many times will we try to fetch a certificate before giving
- * up? Only altered on testing networks. */
- int TestingCertMaxDownloadTries;
-
/** If true, we take part in a testing network. Change the defaults of a
* couple of other configuration options and allow to change the values
* of certain configuration options. */
@@ -4647,6 +4670,14 @@ typedef struct {
/* An ordered list of scheduler_types mapped from Schedulers. */
smartlist_t *SchedulerTypes_;
+ /** List of files that were opened by %include in torrc and torrc-defaults */
+ smartlist_t *FilesOpenedByIncludes;
+
+ /** If true, Tor shouldn't install any posix signal handlers, since it is
+ * running embedded inside another process.
+ */
+ int DisableSignalHandlers;
+
/** Autobool: Is the circuit creation DoS mitigation subsystem enabled? */
int DoSCircuitCreationEnabled;
/** Minimum concurrent connection needed from one single address before any
@@ -5308,7 +5339,7 @@ typedef struct rend_intro_point_t {
*/
int accepted_introduce2_count;
- /** (Service side only) Number of maximum INTRODUCE2 cells that this IP
+ /** (Service side only) Maximum number of INTRODUCE2 cells that this IP
* will accept. This is a random value between
* INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS and
* INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS. */
diff --git a/src/or/policies.c b/src/or/policies.c
index 3bfea3a57c..f718ded326 100644
--- a/src/or/policies.c
+++ b/src/or/policies.c
@@ -18,6 +18,7 @@
#define POLICIES_PRIVATE
#include "or.h"
+#include "bridges.h"
#include "config.h"
#include "dirserv.h"
#include "microdesc.h"
@@ -81,7 +82,8 @@ static int policies_parse_exit_policy_internal(
const smartlist_t *configured_addresses,
int reject_interface_addresses,
int reject_configured_port_addresses,
- int add_default_policy);
+ int add_default_policy,
+ int add_reduced_policy);
/** Replace all "private" entries in *<b>policy</b> with their expanded
* equivalents. */
@@ -894,9 +896,10 @@ fascist_firewall_choose_address_ipv4h(uint32_t ipv4h_addr,
pref_ipv6, ap);
}
-/* The microdescriptor consensus has no IPv6 addresses in rs: they are in
- * the microdescriptors. This means we can't rely on the node's IPv6 address
- * until its microdescriptor is available (when using microdescs).
+/* Some microdescriptor consensus methods have no IPv6 addresses in rs: they
+ * are in the microdescriptors. For these consensus methods, we can't rely on
+ * the node's IPv6 address until its microdescriptor is available (when using
+ * microdescs).
* But for bridges, rewrite_node_address_for_bridge() updates node->ri with
* the configured address, so we can trust bridge addresses.
* (Bridges could gain an IPv6 address if their microdescriptor arrives, but
@@ -914,11 +917,26 @@ node_awaiting_ipv6(const or_options_t* options, const node_t *node)
return 0;
}
+ /* If the node has an IPv6 address, we're not waiting */
+ if (node_has_ipv6_addr(node)) {
+ return 0;
+ }
+
+ /* If the current consensus method and flavour has IPv6 addresses, we're not
+ * waiting */
+ if (networkstatus_consensus_has_ipv6(options)) {
+ return 0;
+ }
+
+ /* Bridge clients never use the address from a bridge's md, so there's no
+ * need to wait for it. */
+ if (node_is_a_configured_bridge(node)) {
+ return 0;
+ }
+
/* We are waiting if we_use_microdescriptors_for_circuits() and we have no
- * md. Bridges have a ri based on their config. They would never use the
- * address from their md, so there's no need to wait for it. */
- return (!node->md && we_use_microdescriptors_for_circuits(options) &&
- !node->ri);
+ * md. */
+ return (!node->md && we_use_microdescriptors_for_circuits(options));
}
/** Like fascist_firewall_choose_address_base(), but takes <b>rs</b>.
@@ -1146,7 +1164,7 @@ validate_addr_policies(const or_options_t *options, char **msg)
"to 1 to disable this warning, and for forward compatibility.",
options->ExitPolicy == NULL ?
" with the default exit policy" : "");
- if (options->ExitPolicy == NULL) {
+ if (options->ExitPolicy == NULL && options->ReducedExitPolicy == 0) {
log_warn(LD_CONFIG,
"In a future version of Tor, ExitRelay 0 may become the "
"default when no ExitPolicy is given.");
@@ -1793,14 +1811,14 @@ policies_parse_exit_policy_reject_private(
/* Reject public IPv4 addresses on any interface */
public_addresses = get_interface_address6_list(LOG_INFO, AF_INET, 0);
addr_policy_append_reject_addr_list_filter(dest, public_addresses, 1, 0);
- free_interface_address6_list(public_addresses);
+ interface_address6_list_free(public_addresses);
/* Don't look for IPv6 addresses if we're configured as IPv4-only */
if (ipv6_exit) {
/* Reject public IPv6 addresses on any interface */
public_addresses = get_interface_address6_list(LOG_INFO, AF_INET6, 0);
addr_policy_append_reject_addr_list_filter(dest, public_addresses, 0, 1);
- free_interface_address6_list(public_addresses);
+ interface_address6_list_free(public_addresses);
}
}
@@ -1879,6 +1897,24 @@ policies_log_first_redundant_entry(const smartlist_t *policy)
"reject *:563,reject *:1214,reject *:4661-4666," \
"reject *:6346-6429,reject *:6699,reject *:6881-6999,accept *:*"
+#define REDUCED_EXIT_POLICY \
+ "accept *:20-23,accept *:43,accept *:53,accept *:79-81,accept *:88," \
+ "accept *:110,accept *:143,accept *:194,accept *:220,accept *:389," \
+ "accept *:443,accept *:464,accept *:465,accept *:531,accept *:543-544," \
+ "accept *:554,accept *:563,accept *:587,accept *:636,accept *:706," \
+ "accept *:749,accept *:873,accept *:902-904,accept *:981,accept *:989-995," \
+ "accept *:1194,accept *:1220,accept *:1293,accept *:1500,accept *:1533," \
+ "accept *:1677,accept *:1723,accept *:1755,accept *:1863," \
+ "accept *:2082-2083,accept *:2086-2087,accept *:2095-2096," \
+ "accept *:2102-2104,accept *:3128,accept *:3389,accept *:3690," \
+ "accept *:4321,accept *:4643,accept *:5050,accept *:5190," \
+ "accept *:5222-5223,accept *:5228,accept *:5900,accept *:6660-6669," \
+ "accept *:6679,accept *:6697,accept *:8000,accept *:8008,accept *:8074," \
+ "accept *:8080,accept *:8082,accept *:8087-8088,accept *:8232-8233," \
+ "accept *:8332-8333,accept *:8443,accept *:8888,accept *:9418," \
+ "accept *:9999,accept *:10000,accept *:11371,accept *:19294," \
+ "accept *:19638,accept *:50002,accept *:64738,reject *:*"
+
/** Parse the exit policy <b>cfg</b> into the linked list *<b>dest</b>.
*
* If <b>ipv6_exit</b> is false, prepend "reject *6:*" to the policy.
@@ -1914,7 +1950,8 @@ policies_parse_exit_policy_internal(config_line_t *cfg,
const smartlist_t *configured_addresses,
int reject_interface_addresses,
int reject_configured_port_addresses,
- int add_default_policy)
+ int add_default_policy,
+ int add_reduced_policy)
{
if (!ipv6_exit) {
append_exit_policy_string(dest, "reject *6:*");
@@ -1940,7 +1977,9 @@ policies_parse_exit_policy_internal(config_line_t *cfg,
* effect, and are most likely an error. */
policies_log_first_redundant_entry(*dest);
- if (add_default_policy) {
+ if (add_reduced_policy) {
+ append_exit_policy_string(dest, REDUCED_EXIT_POLICY);
+ } else if (add_default_policy) {
append_exit_policy_string(dest, DEFAULT_EXIT_POLICY);
} else {
append_exit_policy_string(dest, "reject *4:*");
@@ -1981,13 +2020,15 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
int add_default = (options & EXIT_POLICY_ADD_DEFAULT) ? 1 : 0;
int reject_local_interfaces = (options &
EXIT_POLICY_REJECT_LOCAL_INTERFACES) ? 1 : 0;
+ int add_reduced = (options & EXIT_POLICY_ADD_REDUCED) ? 1 : 0;
return policies_parse_exit_policy_internal(cfg,dest,ipv6_enabled,
reject_private,
configured_addresses,
reject_local_interfaces,
reject_local_interfaces,
- add_default);
+ add_default,
+ add_reduced);
}
/** Helper function that adds a copy of addr to a smartlist as long as it is
@@ -2097,7 +2138,10 @@ policies_parse_exit_policy_from_options(const or_options_t *or_options,
}
if (!or_options->BridgeRelay) {
- parser_cfg |= EXIT_POLICY_ADD_DEFAULT;
+ if (or_options->ReducedExitPolicy)
+ parser_cfg |= EXIT_POLICY_ADD_REDUCED;
+ else
+ parser_cfg |= EXIT_POLICY_ADD_DEFAULT;
}
if (or_options->ExitPolicyRejectLocalInterfaces) {
@@ -2363,7 +2407,7 @@ policy_summary_item_split(policy_summary_item_t* old, uint16_t new_starts)
#define REJECT_CUTOFF_SCALE_IPV6 (64)
/* Ports are rejected in an IPv6 summary if they are rejected in more than one
* IPv6 /16 address block.
- * This is rougly equivalent to the IPv4 cutoff, as only five IPv6 /12s (and
+ * This is roughly equivalent to the IPv4 cutoff, as only five IPv6 /12s (and
* some scattered smaller blocks) have been allocated to the RIRs.
* Network providers are typically allocated one or more IPv6 /32s.
*/
@@ -2769,7 +2813,7 @@ write_short_policy(const short_policy_t *policy)
/** Release all storage held in <b>policy</b>. */
void
-short_policy_free(short_policy_t *policy)
+short_policy_free_(short_policy_t *policy)
{
tor_free(policy);
}
@@ -3019,7 +3063,7 @@ getinfo_helper_policies(control_connection_t *conn,
/** Release all storage held by <b>p</b>. */
void
-addr_policy_list_free(smartlist_t *lst)
+addr_policy_list_free_(smartlist_t *lst)
{
if (!lst)
return;
@@ -3029,7 +3073,7 @@ addr_policy_list_free(smartlist_t *lst)
/** Release all storage held by <b>p</b>. */
void
-addr_policy_free(addr_policy_t *p)
+addr_policy_free_(addr_policy_t *p)
{
if (!p)
return;
diff --git a/src/or/policies.h b/src/or/policies.h
index 52ff4e2f99..35220a812f 100644
--- a/src/or/policies.h
+++ b/src/or/policies.h
@@ -22,7 +22,8 @@
#define EXIT_POLICY_REJECT_PRIVATE (1 << 1)
#define EXIT_POLICY_ADD_DEFAULT (1 << 2)
#define EXIT_POLICY_REJECT_LOCAL_INTERFACES (1 << 3)
-#define EXIT_POLICY_OPTION_MAX EXIT_POLICY_REJECT_LOCAL_INTERFACES
+#define EXIT_POLICY_ADD_REDUCED (1 << 4)
+#define EXIT_POLICY_OPTION_MAX EXIT_POLICY_ADD_REDUCED
/* All options set: used for unit testing */
#define EXIT_POLICY_OPTION_ALL ((EXIT_POLICY_OPTION_MAX << 1) - 1)
@@ -114,15 +115,21 @@ int getinfo_helper_policies(control_connection_t *conn,
int policy_write_item(char *buf, size_t buflen, const addr_policy_t *item,
int format_for_desc);
-void addr_policy_list_free(smartlist_t *p);
-void addr_policy_free(addr_policy_t *p);
+void addr_policy_list_free_(smartlist_t *p);
+#define addr_policy_list_free(lst) \
+ FREE_AND_NULL(smartlist_t, addr_policy_list_free_, (lst))
+void addr_policy_free_(addr_policy_t *p);
+#define addr_policy_free(p) \
+ FREE_AND_NULL(addr_policy_t, addr_policy_free_, (p))
void policies_free_all(void);
char *policy_summarize(smartlist_t *policy, sa_family_t family);
short_policy_t *parse_short_policy(const char *summary);
char *write_short_policy(const short_policy_t *policy);
-void short_policy_free(short_policy_t *policy);
+void short_policy_free_(short_policy_t *policy);
+#define short_policy_free(p) \
+ FREE_AND_NULL(short_policy_t, short_policy_free_, (p))
int short_policy_is_reject_star(const short_policy_t *policy);
addr_policy_result_t compare_tor_addr_to_short_policy(
const tor_addr_t *addr, uint16_t port,
diff --git a/src/or/proto_socks.c b/src/or/proto_socks.c
index 7649fcc4be..8700fe1269 100644
--- a/src/or/proto_socks.c
+++ b/src/or/proto_socks.c
@@ -66,7 +66,7 @@ socks_request_new(void)
/** Free all storage held in the socks_request_t <b>req</b>. */
void
-socks_request_free(socks_request_t *req)
+socks_request_free_(socks_request_t *req)
{
if (!req)
return;
@@ -393,7 +393,7 @@ parse_socks(const char *data, size_t datalen, socks_request_t *req,
req->port = ntohs(get_uint16(data+5+len));
*drain_out = 5+len+2;
- if (!string_is_valid_hostname(req->address)) {
+ if (!string_is_valid_dest(req->address)) {
socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR);
log_warn(LD_PROTOCOL,
@@ -518,7 +518,7 @@ parse_socks(const char *data, size_t datalen, socks_request_t *req,
log_debug(LD_APP,"socks4: Everything is here. Success.");
strlcpy(req->address, startaddr ? startaddr : tmpbuf,
sizeof(req->address));
- if (!string_is_valid_hostname(req->address)) {
+ if (!string_is_valid_dest(req->address)) {
log_warn(LD_PROTOCOL,
"Your application (using socks4 to port %d) gave Tor "
"a malformed hostname: %s. Rejecting the connection.",
diff --git a/src/or/proto_socks.h b/src/or/proto_socks.h
index a714151414..02e0aca7e9 100644
--- a/src/or/proto_socks.h
+++ b/src/or/proto_socks.h
@@ -11,7 +11,9 @@ struct socks_request_t;
struct buf_t;
struct socks_request_t *socks_request_new(void);
-void socks_request_free(struct socks_request_t *req);
+void socks_request_free_(struct socks_request_t *req);
+#define socks_request_free(req) \
+ FREE_AND_NULL(socks_request_t, socks_request_free_, (req))
int fetch_from_buf_socks(struct buf_t *buf, socks_request_t *req,
int log_sockstype, int safe_socks);
int fetch_from_buf_socks_client(buf_t *buf, int state, char **reason);
diff --git a/src/or/protover.c b/src/or/protover.c
index e8524a25b5..5145881ba9 100644
--- a/src/or/protover.c
+++ b/src/or/protover.c
@@ -27,11 +27,14 @@
#include "protover.h"
#include "routerparse.h"
+#ifndef HAVE_RUST
+
static const smartlist_t *get_supported_protocol_list(void);
static int protocol_list_contains(const smartlist_t *protos,
protocol_type_t pr, uint32_t ver);
/** Mapping between protocol type string and protocol type. */
+/// C_RUST_COUPLED: src/rust/protover/protover.rs `PROTOCOL_NAMES`
static const struct {
protocol_type_t protover_type;
const char *name;
@@ -50,6 +53,11 @@ static const struct {
#define N_PROTOCOL_NAMES ARRAY_LENGTH(PROTOCOL_NAMES)
+/* Maximum allowed length of any single subprotocol name. */
+// C_RUST_COUPLED: src/rust/protover/protover.rs
+// `MAX_PROTOCOL_NAME_LENGTH`
+static const unsigned MAX_PROTOCOL_NAME_LENGTH = 100;
+
/**
* Given a protocol_type_t, return the corresponding string used in
* descriptors.
@@ -93,7 +101,7 @@ str_to_protocol_type(const char *s, protocol_type_t *pr_out)
* Release all space held by a single proto_entry_t structure
*/
STATIC void
-proto_entry_free(proto_entry_t *entry)
+proto_entry_free_(proto_entry_t *entry)
{
if (!entry)
return;
@@ -195,6 +203,15 @@ parse_single_entry(const char *s, const char *end_of_entry)
if (equals == s)
goto error;
+ /* The name must not be longer than MAX_PROTOCOL_NAME_LENGTH. */
+ if (equals - s > (int)MAX_PROTOCOL_NAME_LENGTH) {
+ log_warn(LD_NET, "When parsing a protocol entry, I got a very large "
+ "protocol name. This is possibly an attack or a bug, unless "
+ "the Tor network truly supports protocol names larger than "
+ "%ud characters. The offending string was: %s",
+ MAX_PROTOCOL_NAME_LENGTH, escaped(out->name));
+ goto error;
+ }
out->name = tor_strndup(s, equals-s);
tor_assert(equals < end_of_entry);
@@ -260,6 +277,21 @@ parse_protocol_list(const char *s)
}
/**
+ * Return true if the unparsed protover in <b>s</b> would contain a protocol
+ * name longer than MAX_PROTOCOL_NAME_LENGTH, and false otherwise.
+ */
+bool
+protover_contains_long_protocol_names(const char *s)
+{
+ smartlist_t *list = parse_protocol_list(s);
+ if (!list)
+ return true; /* yes, has a dangerous name */
+ SMARTLIST_FOREACH(list, proto_entry_t *, ent, proto_entry_free(ent));
+ smartlist_free(list);
+ return false; /* no, looks fine */
+}
+
+/**
* Given a protocol type and version number, return true iff we know
* how to speak that protocol.
*/
@@ -292,8 +324,45 @@ protocol_list_supports_protocol(const char *list, protocol_type_t tp,
return contains;
}
+/**
+ * Return true iff "list" encodes a protocol list that includes support for
+ * the indicated protocol and version, or some later version.
+ */
+int
+protocol_list_supports_protocol_or_later(const char *list,
+ protocol_type_t tp,
+ uint32_t version)
+{
+ /* NOTE: This is a pretty inefficient implementation. If it ever shows
+ * up in profiles, we should memoize it.
+ */
+ smartlist_t *protocols = parse_protocol_list(list);
+ if (!protocols) {
+ return 0;
+ }
+ const char *pr_name = protocol_type_to_str(tp);
+
+ int contains = 0;
+ SMARTLIST_FOREACH_BEGIN(protocols, proto_entry_t *, proto) {
+ if (strcasecmp(proto->name, pr_name))
+ continue;
+ SMARTLIST_FOREACH_BEGIN(proto->ranges, const proto_range_t *, range) {
+ if (range->high >= version) {
+ contains = 1;
+ goto found;
+ }
+ } SMARTLIST_FOREACH_END(range);
+ } SMARTLIST_FOREACH_END(proto);
+
+ found:
+ SMARTLIST_FOREACH(protocols, proto_entry_t *, ent, proto_entry_free(ent));
+ smartlist_free(protocols);
+ return contains;
+}
+
/** Return the canonical string containing the list of protocols
* that we support. */
+/// C_RUST_COUPLED: src/rust/protover/protover.rs `SUPPORTED_PROTOCOLS`
const char *
protover_get_supported_protocols(void)
{
@@ -377,6 +446,8 @@ encode_protocol_list(const smartlist_t *sl)
/* We treat any protocol list with more than this many subprotocols in it
* as a DoS attempt. */
+/// C_RUST_COUPLED: src/rust/protover/protover.rs
+/// `MAX_PROTOCOLS_TO_EXPAND`
static const int MAX_PROTOCOLS_TO_EXPAND = (1<<16);
/** Voting helper: Given a list of proto_entry_t, return a newly allocated
@@ -397,6 +468,14 @@ expand_protocol_list(const smartlist_t *protos)
SMARTLIST_FOREACH_BEGIN(protos, const proto_entry_t *, ent) {
const char *name = ent->name;
+ if (strlen(name) > MAX_PROTOCOL_NAME_LENGTH) {
+ log_warn(LD_NET, "When expanding a protocol entry, I got a very large "
+ "protocol name. This is possibly an attack or a bug, unless "
+ "the Tor network truly supports protocol names larger than "
+ "%ud characters. The offending string was: %s",
+ MAX_PROTOCOL_NAME_LENGTH, escaped(name));
+ continue;
+ }
SMARTLIST_FOREACH_BEGIN(ent->ranges, const proto_range_t *, range) {
uint32_t u;
for (u = range->low; u <= range->high; ++u) {
@@ -642,7 +721,9 @@ int
protover_all_supported(const char *s, char **missing_out)
{
int all_supported = 1;
- smartlist_t *missing;
+ smartlist_t *missing_some;
+ smartlist_t *missing_completely;
+ smartlist_t *missing_all;
if (!s) {
return 1;
@@ -655,7 +736,8 @@ protover_all_supported(const char *s, char **missing_out)
return 1;
}
- missing = smartlist_new();
+ missing_some = smartlist_new();
+ missing_completely = smartlist_new();
SMARTLIST_FOREACH_BEGIN(entries, const proto_entry_t *, ent) {
protocol_type_t tp;
@@ -667,26 +749,86 @@ protover_all_supported(const char *s, char **missing_out)
}
SMARTLIST_FOREACH_BEGIN(ent->ranges, const proto_range_t *, range) {
+ proto_entry_t *unsupported = tor_malloc_zero(sizeof(proto_entry_t));
+ proto_range_t *versions = tor_malloc_zero(sizeof(proto_range_t));
uint32_t i;
+
+ unsupported->name = tor_strdup(ent->name);
+ unsupported->ranges = smartlist_new();
+
for (i = range->low; i <= range->high; ++i) {
if (!protover_is_supported_here(tp, i)) {
- goto unsupported;
+ if (versions->low == 0 && versions->high == 0) {
+ versions->low = i;
+ /* Pre-emptively add the high now, just in case we're in a single
+ * version range (e.g. "Link=999"). */
+ versions->high = i;
+ }
+ /* If the last one to be unsupported is one less than the current
+ * one, we're in a continous range, so set the high field. */
+ if ((versions->high && versions->high == i - 1) ||
+ /* Similarly, if the last high wasn't set and we're currently
+ * one higher than the low, add current index as the highest
+ * known high. */
+ (!versions->high && versions->low == i - 1)) {
+ versions->high = i;
+ continue;
+ }
+ } else {
+ /* If we hit a supported version, and we previously had a range,
+ * we've hit a non-continuity. Copy the previous range and add it to
+ * the unsupported->ranges list and zero-out the previous range for
+ * the next iteration. */
+ if (versions->low != 0 && versions->high != 0) {
+ proto_range_t *versions_to_add = tor_malloc(sizeof(proto_range_t));
+
+ versions_to_add->low = versions->low;
+ versions_to_add->high = versions->high;
+ smartlist_add(unsupported->ranges, versions_to_add);
+
+ versions->low = 0;
+ versions->high = 0;
+ }
}
}
+ /* Once we've run out of versions to check, see if we had any unsupported
+ * ones and, if so, add them to unsupported->ranges. */
+ if (versions->low != 0 && versions->high != 0) {
+ smartlist_add(unsupported->ranges, versions);
+ }
+ /* Finally, if we had something unsupported, add it to the list of
+ * missing_some things and mark that there was something missing. */
+ if (smartlist_len(unsupported->ranges) != 0) {
+ smartlist_add(missing_some, (void*) unsupported);
+ all_supported = 0;
+ } else {
+ proto_entry_free(unsupported);
+ tor_free(versions);
+ }
} SMARTLIST_FOREACH_END(range);
continue;
unsupported:
all_supported = 0;
- smartlist_add(missing, (void*) ent);
+ smartlist_add(missing_completely, (void*) ent);
} SMARTLIST_FOREACH_END(ent);
+ /* We keep the two smartlists separate so that we can free the proto_entry_t
+ * we created and put in missing_some, so here we add them together to build
+ * the string. */
+ missing_all = smartlist_new();
+ smartlist_add_all(missing_all, missing_some);
+ smartlist_add_all(missing_all, missing_completely);
+
if (missing_out && !all_supported) {
- tor_assert(0 != smartlist_len(missing));
- *missing_out = encode_protocol_list(missing);
+ tor_assert(smartlist_len(missing_all) != 0);
+ *missing_out = encode_protocol_list(missing_all);
}
- smartlist_free(missing);
+ SMARTLIST_FOREACH(missing_some, proto_entry_t *, ent, proto_entry_free(ent));
+ smartlist_free(missing_some);
+ smartlist_free(missing_completely);
+ smartlist_free(missing_all);
SMARTLIST_FOREACH(entries, proto_entry_t *, ent, proto_entry_free(ent));
smartlist_free(entries);
@@ -727,6 +869,7 @@ protocol_list_contains(const smartlist_t *protos,
* Note that this is only used to infer protocols for Tor versions that
* can't declare their own.
**/
+/// C_RUST_COUPLED: src/rust/protover/protover.rs `compute_for_old_tor`
const char *
protover_compute_for_old_tor(const char *version)
{
@@ -776,3 +919,5 @@ protover_free_all(void)
}
}
+#endif /* !defined(HAVE_RUST) */
+
diff --git a/src/or/protover.h b/src/or/protover.h
index 657977279e..b94ebab15b 100644
--- a/src/or/protover.h
+++ b/src/or/protover.h
@@ -10,11 +10,13 @@
#define TOR_PROTOVER_H
#include "container.h"
-
+#include <stdbool.h>
/** The first version of Tor that included "proto" entries in its
* descriptors. Authorities should use this to decide whether to
* guess proto lines. */
/* This is a guess. */
+/// C_RUST_COUPLED: src/rust/protover/protover.rs
+/// `FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS`
#define FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS "0.2.9.3-alpha"
/** The protover version number that signifies HSDir support for HSv3 */
@@ -25,6 +27,8 @@
#define PROTOVER_HS_RENDEZVOUS_POINT_V3 2
/** List of recognized subprotocols. */
+/// C_RUST_COUPLED: src/rust/protover/ffi.rs `translate_to_rust`
+/// C_RUST_COUPLED: src/rust/protover/protover.rs `Proto`
typedef enum protocol_type_t {
PRT_LINK,
PRT_LINKAUTH,
@@ -38,6 +42,7 @@ typedef enum protocol_type_t {
PRT_CONS,
} protocol_type_t;
+bool protover_contains_long_protocol_names(const char *s);
int protover_all_supported(const char *s, char **missing);
int protover_is_supported_here(protocol_type_t pr, uint32_t ver);
const char *protover_get_supported_protocols(void);
@@ -47,6 +52,9 @@ char *protover_compute_vote(const smartlist_t *list_of_proto_strings,
const char *protover_compute_for_old_tor(const char *version);
int protocol_list_supports_protocol(const char *list, protocol_type_t tp,
uint32_t version);
+int protocol_list_supports_protocol_or_later(const char *list,
+ protocol_type_t tp,
+ uint32_t version);
void protover_free_all(void);
@@ -70,11 +78,17 @@ typedef struct proto_entry_t {
smartlist_t *ranges;
} proto_entry_t;
+#if !defined(HAVE_RUST) && defined(TOR_UNIT_TESTS)
STATIC smartlist_t *parse_protocol_list(const char *s);
-STATIC void proto_entry_free(proto_entry_t *entry);
STATIC char *encode_protocol_list(const smartlist_t *sl);
STATIC const char *protocol_type_to_str(protocol_type_t pr);
STATIC int str_to_protocol_type(const char *s, protocol_type_t *pr_out);
+STATIC void proto_entry_free_(proto_entry_t *entry);
+#endif /* !defined(HAVE_RUST) && defined(TOR_UNIT_TESTS) */
+
+#define proto_entry_free(entry) \
+ FREE_AND_NULL(proto_entry_t, proto_entry_free_, (entry))
+
#endif /* defined(PROTOVER_PRIVATE) */
#endif /* !defined(TOR_PROTOVER_H) */
diff --git a/src/or/protover_rust.c b/src/or/protover_rust.c
new file mode 100644
index 0000000000..99304f8b51
--- /dev/null
+++ b/src/or/protover_rust.c
@@ -0,0 +1,34 @@
+/* Copyright (c) 2016-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/*
+ * \file protover_rust.c
+ * \brief Provide a C wrapper for functions exposed in /src/rust/protover,
+ * and safe translation/handling between the Rust/C boundary.
+ */
+
+#include "or.h"
+#include "protover.h"
+
+#ifdef HAVE_RUST
+
+/* Define for compatibility, used in main.c */
+void
+protover_free_all(void)
+{
+}
+
+int protover_contains_long_protocol_names_(const char *s);
+
+/**
+ * Return true if the unparsed protover in <b>s</b> would contain a protocol
+ * name longer than MAX_PROTOCOL_NAME_LENGTH, and false otherwise.
+ */
+bool
+protover_contains_long_protocol_names(const char *s)
+{
+ return protover_contains_long_protocol_names_(s) != 0;
+}
+
+#endif /* defined(HAVE_RUST) */
+
diff --git a/src/or/reasons.c b/src/or/reasons.c
index 03d49418da..ce1259b8f3 100644
--- a/src/or/reasons.c
+++ b/src/or/reasons.c
@@ -428,7 +428,7 @@ socks5_response_code_to_string(uint8_t code)
}
}
-/** Return a string corresponding to a bandwidht_weight_rule_t */
+/** Return a string corresponding to a bandwidth_weight_rule_t */
const char *
bandwidth_weight_rule_to_string(bandwidth_weight_rule_t rule)
{
diff --git a/src/or/relay.c b/src/or/relay.c
index defbf63b79..4c1a8ed96d 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -99,9 +99,6 @@ static void adjust_exit_policy_from_exitpolicy_failure(origin_circuit_t *circ,
entry_connection_t *conn,
node_t *node,
const tor_addr_t *addr);
-#if 0
-static int get_max_middle_cells(void);
-#endif
/** Stop reading on edge connections when we have this many cells
* waiting on the appropriate queue. */
@@ -118,6 +115,9 @@ uint64_t stats_n_relay_cells_relayed = 0;
* hop?
*/
uint64_t stats_n_relay_cells_delivered = 0;
+/** Stats: how many circuits have we closed due to the cell queue limit being
+ * reached (see append_cell_to_circuit_queue()) */
+uint64_t stats_n_circ_max_cell_reached = 0;
/** Used to tell which stream to read from first on a circuit. */
static tor_weak_rng_t stream_choice_rng = TOR_WEAK_RNG_INIT;
@@ -1158,7 +1158,7 @@ connected_cell_parse(const relay_header_t *rh, const cell_t *cell,
/** Drop all storage held by <b>addr</b>. */
STATIC void
-address_ttl_free(address_ttl_t *addr)
+address_ttl_free_(address_ttl_t *addr)
{
if (!addr)
return;
@@ -2426,7 +2426,7 @@ packed_cell_new(void)
/** Return a packed cell used outside by channel_t lower layer */
void
-packed_cell_free(packed_cell_t *cell)
+packed_cell_free_(packed_cell_t *cell)
{
if (!cell)
return;
@@ -2483,7 +2483,7 @@ cell_queue_append_packed_copy(circuit_t *circ, cell_queue_t *queue,
(void)exitward;
(void)use_stats;
- copy->inserted_time = (uint32_t) monotime_coarse_absolute_msec();
+ copy->inserted_timestamp = monotime_coarse_get_stamp();
cell_queue_append(queue, copy);
}
@@ -2566,7 +2566,7 @@ destroy_cell_queue_append(destroy_cell_queue_t *queue,
cell->circid = circid;
cell->reason = reason;
/* Not yet used, but will be required for OOM handling. */
- cell->inserted_time = (uint32_t) monotime_coarse_absolute_msec();
+ cell->inserted_timestamp = monotime_coarse_get_stamp();
TOR_SIMPLEQ_INSERT_TAIL(&queue->head, cell, next);
++queue->n;
@@ -2597,7 +2597,7 @@ packed_cell_mem_cost(void)
}
/* DOCDOC */
-STATIC size_t
+size_t
cell_queues_get_total_allocation(void)
{
return total_cells_allocated * packed_cell_mem_cost();
@@ -2632,7 +2632,7 @@ cell_queues_check_size(void)
if (rend_cache_total > get_options()->MaxMemInQueues / 5) {
const size_t bytes_to_remove =
rend_cache_total - (size_t)(get_options()->MaxMemInQueues / 10);
- alloc -= hs_cache_handle_oom(time(NULL), bytes_to_remove);
+ alloc -= hs_cache_handle_oom(now, bytes_to_remove);
}
if (geoip_client_cache_total > get_options()->MaxMemInQueues / 5) {
const size_t bytes_to_remove =
@@ -2831,8 +2831,13 @@ channel_flush_from_first_active_circuit, (channel_t *chan, int max))
tor_assert(dcell);
/* frees dcell */
cell = destroy_cell_to_packed_cell(dcell, chan->wide_circ_ids);
- /* frees cell */
- channel_write_packed_cell(chan, cell);
+ /* Send the DESTROY cell. It is very unlikely that this fails but just
+ * in case, get rid of the channel. */
+ if (channel_write_packed_cell(chan, cell) < 0) {
+ /* The cell has been freed. */
+ channel_mark_for_close(chan);
+ continue;
+ }
/* Update the cmux destroy counter */
circuitmux_notify_xmit_destroy(cmux);
cell = NULL;
@@ -2875,9 +2880,10 @@ channel_flush_from_first_active_circuit, (channel_t *chan, int max))
/* Calculate the exact time that this cell has spent in the queue. */
if (get_options()->CellStatistics ||
get_options()->TestingEnableCellStatsEvent) {
- uint32_t msec_waiting;
- uint32_t msec_now = (uint32_t)monotime_coarse_absolute_msec();
- msec_waiting = msec_now - cell->inserted_time;
+ uint32_t timestamp_now = monotime_coarse_get_stamp();
+ uint32_t msec_waiting =
+ (uint32_t) monotime_coarse_stamp_units_to_approx_msec(
+ timestamp_now - cell->inserted_timestamp);
if (get_options()->CellStatistics && !CIRCUIT_IS_ORIGIN(circ)) {
or_circ = TO_OR_CIRCUIT(circ);
@@ -2908,8 +2914,13 @@ channel_flush_from_first_active_circuit, (channel_t *chan, int max))
DIRREQ_TUNNELED,
DIRREQ_CIRC_QUEUE_FLUSHED);
- /* Now send the cell */
- channel_write_packed_cell(chan, cell);
+ /* Now send the cell. It is very unlikely that this fails but just in
+ * case, get rid of the channel. */
+ if (channel_write_packed_cell(chan, cell) < 0) {
+ /* The cell has been freed at this point. */
+ channel_mark_for_close(chan);
+ continue;
+ }
cell = NULL;
/*
@@ -2944,22 +2955,67 @@ channel_flush_from_first_active_circuit, (channel_t *chan, int max))
return n_flushed;
}
-#if 0
-/** Indicate the current preferred cap for middle circuits; zero disables
- * the cap. Right now it's just a constant, ORCIRC_MAX_MIDDLE_CELLS, but
- * the logic in append_cell_to_circuit_queue() is written to be correct
- * if we want to base it on a consensus param or something that might change
- * in the future.
- */
-static int
-get_max_middle_cells(void)
+/* Minimum value is the maximum circuit window size.
+ *
+ * SENDME cells makes it that we can control how many cells can be inflight on
+ * a circuit from end to end. This logic makes it that on any circuit cell
+ * queue, we have a maximum of cells possible.
+ *
+ * Because the Tor protocol allows for a client to exit at any hop in a
+ * circuit and a circuit can be of a maximum of 8 hops, so in theory the
+ * normal worst case will be the circuit window start value times the maximum
+ * number of hops (8). Having more cells then that means something is wrong.
+ *
+ * However, because padding cells aren't counted in the package window, we set
+ * the maximum size to a reasonably large size for which we expect that we'll
+ * never reach in theory. And if we ever do because of future changes, we'll
+ * be able to control it with a consensus parameter.
+ *
+ * XXX: Unfortunately, END cells aren't accounted for in the circuit window
+ * which means that for instance if a client opens 8001 streams, the 8001
+ * following END cells will queue up in the circuit which will get closed if
+ * the max limit is 8000. Which is sad because it is allowed by the Tor
+ * protocol. But, we need an upper bound on circuit queue in order to avoid
+ * DoS memory pressure so the default size is a middle ground between not
+ * having any limit and having a very restricted one. This is why we can also
+ * control it through a consensus parameter. */
+#define RELAY_CIRC_CELL_QUEUE_SIZE_MIN CIRCWINDOW_START_MAX
+/* We can't have a consensus parameter above this value. */
+#define RELAY_CIRC_CELL_QUEUE_SIZE_MAX INT32_MAX
+/* Default value is set to a large value so we can handle padding cells
+ * properly which aren't accounted for in the SENDME window. Default is 50000
+ * allowed cells in the queue resulting in ~25MB. */
+#define RELAY_CIRC_CELL_QUEUE_SIZE_DEFAULT \
+ (50 * RELAY_CIRC_CELL_QUEUE_SIZE_MIN)
+
+/* The maximum number of cell a circuit queue can contain. This is updated at
+ * every new consensus and controlled by a parameter. */
+static int32_t max_circuit_cell_queue_size =
+ RELAY_CIRC_CELL_QUEUE_SIZE_DEFAULT;
+
+/* Called when the consensus has changed. At this stage, the global consensus
+ * object has NOT been updated. It is called from
+ * notify_before_networkstatus_changes(). */
+void
+relay_consensus_has_changed(const networkstatus_t *ns)
{
- return ORCIRC_MAX_MIDDLE_CELLS;
+ tor_assert(ns);
+
+ /* Update the circuit max cell queue size from the consensus. */
+ max_circuit_cell_queue_size =
+ networkstatus_get_param(ns, "circ_max_cell_queue_size",
+ RELAY_CIRC_CELL_QUEUE_SIZE_DEFAULT,
+ RELAY_CIRC_CELL_QUEUE_SIZE_MIN,
+ RELAY_CIRC_CELL_QUEUE_SIZE_MAX);
}
-#endif /* 0 */
/** Add <b>cell</b> to the queue of <b>circ</b> writing to <b>chan</b>
- * transmitting in <b>direction</b>. */
+ * transmitting in <b>direction</b>.
+ *
+ * The given <b>cell</b> is copied over the circuit queue so the caller must
+ * cleanup the memory.
+ *
+ * This function is part of the fast path. */
void
append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan,
cell_t *cell, cell_direction_t direction,
@@ -2968,10 +3024,6 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan,
or_circuit_t *orcirc = NULL;
cell_queue_t *queue;
int streams_blocked;
-#if 0
- uint32_t tgt_max_middle_cells, p_len, n_len, tmp, hard_max_middle_cells;
-#endif
-
int exitward;
if (circ->marked_for_close)
return;
@@ -2986,93 +3038,25 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan,
streams_blocked = circ->streams_blocked_on_p_chan;
}
- /*
- * Disabling this for now because of a possible guard discovery attack
- */
-#if 0
- /* Are we a middle circuit about to exceed ORCIRC_MAX_MIDDLE_CELLS? */
- if ((circ->n_chan != NULL) && CIRCUIT_IS_ORCIRC(circ)) {
- orcirc = TO_OR_CIRCUIT(circ);
- if (orcirc->p_chan) {
- /* We are a middle circuit if we have both n_chan and p_chan */
- /* We'll need to know the current preferred maximum */
- tgt_max_middle_cells = get_max_middle_cells();
- if (tgt_max_middle_cells > 0) {
- /* Do we need to initialize middle_max_cells? */
- if (orcirc->max_middle_cells == 0) {
- orcirc->max_middle_cells = tgt_max_middle_cells;
- } else {
- if (tgt_max_middle_cells > orcirc->max_middle_cells) {
- /* If we want to increase the cap, we can do so right away */
- orcirc->max_middle_cells = tgt_max_middle_cells;
- } else if (tgt_max_middle_cells < orcirc->max_middle_cells) {
- /*
- * If we're shrinking the cap, we can't shrink past either queue;
- * compare tgt_max_middle_cells rather than tgt_max_middle_cells *
- * ORCIRC_MAX_MIDDLE_KILL_THRESH so the queues don't shrink enough
- * to generate spurious warnings, either.
- */
- n_len = circ->n_chan_cells.n;
- p_len = orcirc->p_chan_cells.n;
- tmp = tgt_max_middle_cells;
- if (tmp < n_len) tmp = n_len;
- if (tmp < p_len) tmp = p_len;
- orcirc->max_middle_cells = tmp;
- }
- /* else no change */
- }
- } else {
- /* tgt_max_middle_cells == 0 indicates we should disable the cap */
- orcirc->max_middle_cells = 0;
- }
-
- /* Now we know orcirc->max_middle_cells is set correctly */
- if (orcirc->max_middle_cells > 0) {
- hard_max_middle_cells =
- (uint32_t)(((double)orcirc->max_middle_cells) *
- ORCIRC_MAX_MIDDLE_KILL_THRESH);
-
- if ((unsigned)queue->n + 1 >= hard_max_middle_cells) {
- /* Queueing this cell would put queue over the kill theshold */
- log_warn(LD_CIRC,
- "Got a cell exceeding the hard cap of %u in the "
- "%s direction on middle circ ID %u on chan ID "
- U64_FORMAT "; killing the circuit.",
- hard_max_middle_cells,
- (direction == CELL_DIRECTION_OUT) ? "n" : "p",
- (direction == CELL_DIRECTION_OUT) ?
- circ->n_circ_id : orcirc->p_circ_id,
- U64_PRINTF_ARG(
- (direction == CELL_DIRECTION_OUT) ?
- circ->n_chan->global_identifier :
- orcirc->p_chan->global_identifier));
- circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
- return;
- } else if ((unsigned)queue->n + 1 == orcirc->max_middle_cells) {
- /* Only use ==, not >= for this test so we don't spam the log */
- log_warn(LD_CIRC,
- "While trying to queue a cell, reached the soft cap of %u "
- "in the %s direction on middle circ ID %u "
- "on chan ID " U64_FORMAT ".",
- orcirc->max_middle_cells,
- (direction == CELL_DIRECTION_OUT) ? "n" : "p",
- (direction == CELL_DIRECTION_OUT) ?
- circ->n_circ_id : orcirc->p_circ_id,
- U64_PRINTF_ARG(
- (direction == CELL_DIRECTION_OUT) ?
- circ->n_chan->global_identifier :
- orcirc->p_chan->global_identifier));
- }
- }
- }
+ if (PREDICT_UNLIKELY(queue->n >= max_circuit_cell_queue_size)) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "%s circuit has %d cells in its queue, maximum allowed is %d. "
+ "Closing circuit for safety reasons.",
+ (exitward) ? "Outbound" : "Inbound", queue->n,
+ max_circuit_cell_queue_size);
+ circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
+ stats_n_circ_max_cell_reached++;
+ return;
}
-#endif /* 0 */
+ /* Very important that we copy to the circuit queue because all calls to
+ * this function use the stack for the cell memory. */
cell_queue_append_packed_copy(circ, queue, exitward, cell,
chan->wide_circ_ids, 1);
+ /* Check and run the OOM if needed. */
if (PREDICT_UNLIKELY(cell_queues_check_size())) {
- /* We ran the OOM handler */
+ /* We ran the OOM handler which might have closed this circuit. */
if (circ->marked_for_close)
return;
}
diff --git a/src/or/relay.h b/src/or/relay.h
index 4cc1a0fbdb..e96639170c 100644
--- a/src/or/relay.h
+++ b/src/or/relay.h
@@ -14,9 +14,12 @@
extern uint64_t stats_n_relay_cells_relayed;
extern uint64_t stats_n_relay_cells_delivered;
+extern uint64_t stats_n_circ_max_cell_reached;
+void relay_consensus_has_changed(const networkstatus_t *ns);
int circuit_receive_relay_cell(cell_t *cell, circuit_t *circ,
cell_direction_t cell_direction);
+size_t cell_queues_get_total_allocation(void);
void relay_header_pack(uint8_t *dest, const relay_header_t *src);
void relay_header_unpack(relay_header_t *dest, const uint8_t *src);
@@ -51,7 +54,9 @@ size_t packed_cell_mem_cost(void);
int have_been_under_memory_pressure(void);
/* For channeltls.c */
-void packed_cell_free(packed_cell_t *cell);
+void packed_cell_free_(packed_cell_t *cell);
+#define packed_cell_free(cell) \
+ FREE_AND_NULL(packed_cell_t, packed_cell_free_, (cell))
void cell_queue_init(cell_queue_t *queue);
void cell_queue_clear(cell_queue_t *queue);
@@ -101,7 +106,9 @@ typedef struct address_ttl_s {
char *hostname;
int ttl;
} address_ttl_t;
-STATIC void address_ttl_free(address_ttl_t *addr);
+STATIC void address_ttl_free_(address_ttl_t *addr);
+#define address_ttl_free(addr) \
+ FREE_AND_NULL(address_ttl_t, address_ttl_free_, (addr))
STATIC int resolved_cell_parse(const cell_t *cell, const relay_header_t *rh,
smartlist_t *addresses_out, int *errcode_out);
STATIC int connection_edge_process_resolved_cell(edge_connection_t *conn,
@@ -110,7 +117,6 @@ STATIC int connection_edge_process_resolved_cell(edge_connection_t *conn,
STATIC packed_cell_t *packed_cell_new(void);
STATIC packed_cell_t *cell_queue_pop(cell_queue_t *queue);
STATIC destroy_cell_t *destroy_cell_queue_pop(destroy_cell_queue_t *queue);
-STATIC size_t cell_queues_get_total_allocation(void);
STATIC int cell_queues_check_size(void);
#endif /* defined(RELAY_PRIVATE) */
diff --git a/src/or/rendcache.c b/src/or/rendcache.c
index b98b2bccfa..d27e1c293f 100644
--- a/src/or/rendcache.c
+++ b/src/or/rendcache.c
@@ -43,12 +43,12 @@ STATIC digestmap_t *rend_cache_v2_dir = NULL;
* ID, that were NOT present in the descriptor are removed from this cache.
* Which means that if at least one IP was not in this cache, thus usuable,
* it's considered a new descriptor so we keep it. Else, if all IPs were in
- * this cache, we discard the descriptor as it's considered unsuable.
+ * this cache, we discard the descriptor as it's considered unusable.
*
* Once a descriptor is removed from the rend cache or expires, the entry
* in this cache is also removed for the service ID.
*
- * This scheme allows us to not realy on the descriptor's timestamp (which
+ * This scheme allows us to not rely on the descriptor's timestamp (which
* is rounded down to the hour) to know if we have a newer descriptor. We
* only rely on the usability of intro points from an internal state. */
STATIC strmap_t *rend_cache_failure = NULL;
@@ -120,7 +120,7 @@ rend_cache_increment_allocation(size_t n)
/** Helper: free a rend cache failure intro object. */
STATIC void
-rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t *entry)
+rend_cache_failure_intro_entry_free_(rend_cache_failure_intro_t *entry)
{
if (entry == NULL) {
return;
@@ -129,9 +129,9 @@ rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t *entry)
}
static void
-rend_cache_failure_intro_entry_free_(void *entry)
+rend_cache_failure_intro_entry_free_void(void *entry)
{
- rend_cache_failure_intro_entry_free(entry);
+ rend_cache_failure_intro_entry_free_(entry);
}
/** Allocate a rend cache failure intro object and return it. <b>failure</b>
@@ -147,7 +147,7 @@ rend_cache_failure_intro_entry_new(rend_intro_point_failure_t failure)
/** Helper: free a rend cache failure object. */
STATIC void
-rend_cache_failure_entry_free(rend_cache_failure_t *entry)
+rend_cache_failure_entry_free_(rend_cache_failure_t *entry)
{
if (entry == NULL) {
return;
@@ -155,7 +155,7 @@ rend_cache_failure_entry_free(rend_cache_failure_t *entry)
/* Free and remove every intro failure object. */
digestmap_free(entry->intro_failures,
- rend_cache_failure_intro_entry_free_);
+ rend_cache_failure_intro_entry_free_void);
tor_free(entry);
}
@@ -163,9 +163,9 @@ rend_cache_failure_entry_free(rend_cache_failure_t *entry)
/** Helper: deallocate a rend_cache_failure_t. (Used with strmap_free(),
* which requires a function pointer whose argument is void*). */
STATIC void
-rend_cache_failure_entry_free_(void *entry)
+rend_cache_failure_entry_free_void(void *entry)
{
- rend_cache_failure_entry_free(entry);
+ rend_cache_failure_entry_free_(entry);
}
/** Allocate a rend cache failure object and return it. This function can
@@ -201,7 +201,7 @@ rend_cache_failure_remove(rend_service_descriptor_t *desc)
/** Helper: free storage held by a single service descriptor cache entry. */
STATIC void
-rend_cache_entry_free(rend_cache_entry_t *e)
+rend_cache_entry_free_(rend_cache_entry_t *e)
{
if (!e)
return;
@@ -217,19 +217,19 @@ rend_cache_entry_free(rend_cache_entry_t *e)
/** Helper: deallocate a rend_cache_entry_t. (Used with strmap_free(), which
* requires a function pointer whose argument is void*). */
static void
-rend_cache_entry_free_(void *p)
+rend_cache_entry_free_void(void *p)
{
- rend_cache_entry_free(p);
+ rend_cache_entry_free_(p);
}
/** Free all storage held by the service descriptor cache. */
void
rend_cache_free_all(void)
{
- strmap_free(rend_cache, rend_cache_entry_free_);
- digestmap_free(rend_cache_v2_dir, rend_cache_entry_free_);
- strmap_free(rend_cache_local_service, rend_cache_entry_free_);
- strmap_free(rend_cache_failure, rend_cache_failure_entry_free_);
+ strmap_free(rend_cache, rend_cache_entry_free_void);
+ digestmap_free(rend_cache_v2_dir, rend_cache_entry_free_void);
+ strmap_free(rend_cache_local_service, rend_cache_entry_free_void);
+ strmap_free(rend_cache_failure, rend_cache_failure_entry_free_void);
rend_cache = NULL;
rend_cache_v2_dir = NULL;
rend_cache_local_service = NULL;
@@ -304,7 +304,7 @@ rend_cache_purge(void)
{
if (rend_cache) {
log_info(LD_REND, "Purging HS v2 descriptor cache");
- strmap_free(rend_cache, rend_cache_entry_free_);
+ strmap_free(rend_cache, rend_cache_entry_free_void);
}
rend_cache = strmap_new();
}
@@ -316,7 +316,7 @@ rend_cache_failure_purge(void)
{
if (rend_cache_failure) {
log_info(LD_REND, "Purging HS v2 failure cache");
- strmap_free(rend_cache_failure, rend_cache_failure_entry_free_);
+ strmap_free(rend_cache_failure, rend_cache_failure_entry_free_void);
}
rend_cache_failure = strmap_new();
}
diff --git a/src/or/rendcache.h b/src/or/rendcache.h
index 5b13eadfa1..8b6fd5b671 100644
--- a/src/or/rendcache.h
+++ b/src/or/rendcache.h
@@ -36,7 +36,7 @@ typedef struct rend_cache_entry_t {
/* Introduction point failure type. */
typedef struct rend_cache_failure_intro_t {
- /* When this intro point failure occured thus we allocated this object and
+ /* When this intro point failure occurred thus we allocated this object and
* cache it. */
time_t created_ts;
rend_intro_point_failure_t failure_type;
@@ -90,10 +90,18 @@ void rend_cache_increment_allocation(size_t n);
#ifdef RENDCACHE_PRIVATE
STATIC size_t rend_cache_entry_allocation(const rend_cache_entry_t *e);
-STATIC void rend_cache_entry_free(rend_cache_entry_t *e);
-STATIC void rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t
- *entry);
-STATIC void rend_cache_failure_entry_free(rend_cache_failure_t *entry);
+STATIC void rend_cache_entry_free_(rend_cache_entry_t *e);
+#define rend_cache_entry_free(e) \
+ FREE_AND_NULL(rend_cache_entry_t, rend_cache_entry_free_, (e))
+STATIC void rend_cache_failure_intro_entry_free_(rend_cache_failure_intro_t
+ *entry);
+#define rend_cache_failure_intro_entry_free(e) \
+ FREE_AND_NULL(rend_cache_failure_intro_t, \
+ rend_cache_failure_intro_entry_free_, (e))
+STATIC void rend_cache_failure_entry_free_(rend_cache_failure_t *entry);
+#define rend_cache_failure_entry_free(e) \
+ FREE_AND_NULL(rend_cache_failure_t, \
+ rend_cache_failure_entry_free_, (e))
STATIC int cache_failure_intro_lookup(const uint8_t *identity,
const char *service_id,
rend_cache_failure_intro_t
@@ -108,7 +116,7 @@ STATIC void cache_failure_intro_add(const uint8_t *identity,
STATIC void validate_intro_point_failure(const rend_service_descriptor_t *desc,
const char *service_id);
-STATIC void rend_cache_failure_entry_free_(void *entry);
+STATIC void rend_cache_failure_entry_free_void(void *entry);
#ifdef TOR_UNIT_TESTS
extern strmap_t *rend_cache;
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index ac2ac5be21..e61207f2ee 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -468,7 +468,8 @@ directory_get_from_hs_dir(const char *desc_id,
hs_dir = hs_pick_hsdir(responsible_dirs, desc_id_base32);
if (!hs_dir) {
/* No suitable hs dir can be found, stop right now. */
- control_event_hs_descriptor_failed(rend_query, NULL, "QUERY_NO_HSDIR");
+ control_event_hsv2_descriptor_failed(rend_query, NULL,
+ "QUERY_NO_HSDIR");
control_event_hs_descriptor_content(rend_data_get_address(rend_query),
desc_id_base32, NULL, NULL);
return 0;
@@ -491,7 +492,7 @@ directory_get_from_hs_dir(const char *desc_id,
REND_DESC_COOKIE_LEN,
0)<0) {
log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
- control_event_hs_descriptor_failed(rend_query, hsdir_fp, "BAD_DESC");
+ control_event_hsv2_descriptor_failed(rend_query, hsdir_fp, "BAD_DESC");
control_event_hs_descriptor_content(rend_data_get_address(rend_query),
desc_id_base32, hsdir_fp, NULL);
return 0;
@@ -524,9 +525,10 @@ directory_get_from_hs_dir(const char *desc_id,
(rend_data->auth_type == REND_NO_AUTH ? "[none]" :
escaped_safe_str_client(descriptor_cookie_base64)),
routerstatus_describe(hs_dir));
- control_event_hs_descriptor_requested(rend_query,
+ control_event_hs_descriptor_requested(rend_data->onion_address,
+ rend_data->auth_type,
hs_dir->identity_digest,
- desc_id_base32);
+ desc_id_base32, NULL);
return 1;
}
@@ -578,7 +580,7 @@ fetch_v2_desc_by_descid(const char *desc_id,
/** Fetch a v2 descriptor using the onion address in the given query object.
* This will compute the descriptor id for each replicas and fetch it on the
- * given hsdir(s) if any or the responsible ones that are choosen
+ * given hsdir(s) if any or the responsible ones that are chosen
* automatically.
*
* On success, 1 is returned. If no hidden service is left to ask, return 0.
@@ -1111,18 +1113,22 @@ rend_client_lookup_service_authorization(const char *onion_address)
return strmap_get(auth_hid_servs, onion_address);
}
+#define rend_service_authorization_free(val) \
+ FREE_AND_NULL(rend_service_authorization_t, \
+ rend_service_authorization_free_, (val))
+
/** Helper: Free storage held by rend_service_authorization_t. */
static void
-rend_service_authorization_free(rend_service_authorization_t *auth)
+rend_service_authorization_free_(rend_service_authorization_t *auth)
{
tor_free(auth);
}
/** Helper for strmap_free. */
static void
-rend_service_authorization_strmap_item_free(void *service_auth)
+rend_service_authorization_free_void(void *service_auth)
{
- rend_service_authorization_free(service_auth);
+ rend_service_authorization_free_(service_auth);
}
/** Release all the storage held in auth_hid_servs.
@@ -1133,7 +1139,7 @@ rend_service_authorization_free_all(void)
if (!auth_hid_servs) {
return;
}
- strmap_free(auth_hid_servs, rend_service_authorization_strmap_item_free);
+ strmap_free(auth_hid_servs, rend_service_authorization_free_void);
auth_hid_servs = NULL;
}
@@ -1208,7 +1214,7 @@ rend_parse_service_authorization(const or_options_t *options,
rend_service_authorization_free_all();
auth_hid_servs = parsed;
} else {
- strmap_free(parsed, rend_service_authorization_strmap_item_free);
+ strmap_free(parsed, rend_service_authorization_free_void);
}
return res;
}
diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c
index 458a90058f..230da4be5c 100644
--- a/src/or/rendcommon.c
+++ b/src/or/rendcommon.c
@@ -37,7 +37,7 @@ rend_cmp_service_ids(const char *one, const char *two)
/** Free the storage held by the service descriptor <b>desc</b>.
*/
void
-rend_service_descriptor_free(rend_service_descriptor_t *desc)
+rend_service_descriptor_free_(rend_service_descriptor_t *desc)
{
if (!desc)
return;
@@ -419,7 +419,7 @@ rend_desc_v2_is_parsable(rend_encoded_v2_service_descriptor_t *desc)
/** Free the storage held by an encoded v2 service descriptor. */
void
-rend_encoded_v2_service_descriptor_free(
+rend_encoded_v2_service_descriptor_free_(
rend_encoded_v2_service_descriptor_t *desc)
{
if (!desc)
@@ -430,7 +430,7 @@ rend_encoded_v2_service_descriptor_free(
/** Free the storage held by an introduction point info. */
void
-rend_intro_point_free(rend_intro_point_t *intro)
+rend_intro_point_free_(rend_intro_point_t *intro)
{
if (!intro)
return;
diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h
index c35d7272fd..1ed0f62609 100644
--- a/src/or/rendcommon.h
+++ b/src/or/rendcommon.h
@@ -24,11 +24,19 @@ void rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint,
int command, size_t length,
const uint8_t *payload);
-void rend_service_descriptor_free(rend_service_descriptor_t *desc);
+void rend_service_descriptor_free_(rend_service_descriptor_t *desc);
+#define rend_service_descriptor_free(desc) \
+ FREE_AND_NULL(rend_service_descriptor_t, rend_service_descriptor_free_, \
+ (desc))
int rend_get_service_id(crypto_pk_t *pk, char *out);
-void rend_encoded_v2_service_descriptor_free(
+void rend_encoded_v2_service_descriptor_free_(
rend_encoded_v2_service_descriptor_t *desc);
-void rend_intro_point_free(rend_intro_point_t *intro);
+#define rend_encoded_v2_service_descriptor_free(desc) \
+ FREE_AND_NULL(rend_encoded_v2_service_descriptor_t, \
+ rend_encoded_v2_service_descriptor_free_, (desc))
+void rend_intro_point_free_(rend_intro_point_t *intro);
+#define rend_intro_point_free(intro) \
+ FREE_AND_NULL(rend_intro_point_t, rend_intro_point_free_, (intro))
int rend_valid_v2_service_id(const char *query);
int rend_valid_descriptor_id(const char *query);
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index fed8c97ebb..ac86c143d1 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -157,7 +157,7 @@ rend_num_services(void)
/** Helper: free storage held by a single service authorized client entry. */
void
-rend_authorized_client_free(rend_authorized_client_t *client)
+rend_authorized_client_free_(rend_authorized_client_t *client)
{
if (!client)
return;
@@ -172,15 +172,15 @@ rend_authorized_client_free(rend_authorized_client_t *client)
/** Helper for strmap_free. */
static void
-rend_authorized_client_strmap_item_free(void *authorized_client)
+rend_authorized_client_free_void(void *authorized_client)
{
- rend_authorized_client_free(authorized_client);
+ rend_authorized_client_free_(authorized_client);
}
/** Release the storage held by <b>service</b>.
*/
STATIC void
-rend_service_free(rend_service_t *service)
+rend_service_free_(rend_service_t *service)
{
if (!service)
return;
@@ -470,7 +470,7 @@ rend_service_parse_port_config(const char *string, const char *sep,
/** Release all storage held in a rend_service_port_config_t. */
void
-rend_service_port_config_free(rend_service_port_config_t *p)
+rend_service_port_config_free_(rend_service_port_config_t *p)
{
tor_free(p);
}
@@ -848,9 +848,9 @@ rend_config_service(const config_line_t *line_,
* after calling this routine, and may assume that correct cleanup has
* been done on failure.
*
- * Return an appropriate rend_service_add_ephemeral_status_t.
+ * Return an appropriate hs_service_add_ephemeral_status_t.
*/
-rend_service_add_ephemeral_status_t
+hs_service_add_ephemeral_status_t
rend_service_add_ephemeral(crypto_pk_t *pk,
smartlist_t *ports,
int max_streams_per_circuit,
@@ -1361,7 +1361,7 @@ rend_services_add_filenames_to_lists(smartlist_t *open_lst,
}
/** Derive all rend_service_t internal material based on the service's key.
- * Returns 0 on sucess, -1 on failure.
+ * Returns 0 on success, -1 on failure.
*/
static int
rend_service_derive_key_digests(struct rend_service_t *s)
@@ -1676,7 +1676,7 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname)
memwipe(client_keys_str, 0, strlen(client_keys_str));
tor_free(client_keys_str);
}
- strmap_free(parsed_clients, rend_authorized_client_strmap_item_free);
+ strmap_free(parsed_clients, rend_authorized_client_free_void);
if (cfname) {
memwipe(cfname, 0, strlen(cfname));
@@ -2225,7 +2225,7 @@ find_rp_for_intro(const rend_intro_cell_t *intro,
* rend_service_parse_intro().
*/
void
-rend_service_free_intro(rend_intro_cell_t *request)
+rend_service_free_intro_(rend_intro_cell_t *request)
{
if (!request) {
return;
@@ -3197,7 +3197,7 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
/* If we already have enough introduction circuits for this service,
* redefine this one as a general circuit or close it, depending.
- * Substract the amount of expiring nodes here because the circuits are
+ * Subtract the amount of expiring nodes here because the circuits are
* still opened. */
if (valid_ip_circuits > service->n_intro_points_wanted) {
const or_options_t *options = get_options();
@@ -3223,7 +3223,12 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
"circuit, but we already have enough. Redefining purpose to "
"general; leaving as internal.");
- circuit_change_purpose(TO_CIRCUIT(circuit), CIRCUIT_PURPOSE_C_GENERAL);
+ if (circuit_should_use_vanguards(TO_CIRCUIT(circuit)->purpose)) {
+ circuit_change_purpose(TO_CIRCUIT(circuit),
+ CIRCUIT_PURPOSE_HS_VANGUARDS);
+ } else {
+ circuit_change_purpose(TO_CIRCUIT(circuit), CIRCUIT_PURPOSE_C_GENERAL);
+ }
{
rend_data_free(circuit->rend_data);
@@ -3577,7 +3582,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
"directories to post descriptors to.");
control_event_hs_descriptor_upload(service_id,
"UNKNOWN",
- "UNKNOWN");
+ "UNKNOWN", NULL);
goto done;
}
}
@@ -3592,7 +3597,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
/* Don't upload descriptor if we succeeded in doing so last time. */
continue;
node = node_get_by_id(hs_dir->identity_digest);
- if (!node || !node_has_descriptor(node)) {
+ if (!node || !node_has_preferred_descriptor(node,0)) {
log_info(LD_REND, "Not launching upload for for v2 descriptor to "
"hidden service directory %s; we don't have its "
"router descriptor. Queuing for later upload.",
@@ -3632,7 +3637,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
hs_dir->or_port);
control_event_hs_descriptor_upload(service_id,
hs_dir->identity_digest,
- desc_id_base32);
+ desc_id_base32, NULL);
tor_free(hs_dir_ip);
/* Remember successful upload to this router for next time. */
if (!smartlist_contains_digest(successful_uploads,
@@ -3729,7 +3734,7 @@ upload_service_descriptor(rend_service_t *service)
}
/* Free memory for descriptors. */
for (i = 0; i < smartlist_len(descs); i++)
- rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
+ rend_encoded_v2_service_descriptor_free_(smartlist_get(descs, i));
smartlist_clear(descs);
/* Update next upload time. */
if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS
@@ -3760,7 +3765,7 @@ upload_service_descriptor(rend_service_t *service)
}
/* Free memory for descriptors. */
for (i = 0; i < smartlist_len(descs); i++)
- rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
+ rend_encoded_v2_service_descriptor_free_(smartlist_get(descs, i));
smartlist_clear(descs);
}
}
@@ -4117,7 +4122,7 @@ rend_consider_services_intro_points(time_t now)
n_intro_points_to_open);
break;
}
- /* Add the choosen node to the exclusion list in order to avoid picking
+ /* Add the chosen node to the exclusion list in order to avoid picking
* it again in the next iteration. */
smartlist_add(exclude_nodes, (void*)node);
intro = tor_malloc_zero(sizeof(rend_intro_point_t));
@@ -4282,7 +4287,7 @@ rend_service_dump_stats(int severity)
}
/** Given <b>conn</b>, a rendezvous exit stream, look up the hidden service for
- * 'circ', and look up the port and address based on conn-\>port.
+ * <b>circ</b>, and look up the port and address based on conn-\>port.
* Assign the actual conn-\>addr and conn-\>port. Return -2 on failure
* for which the circuit should be closed, -1 on other failure,
* or 0 for success.
diff --git a/src/or/rendservice.h b/src/or/rendservice.h
index 5946e31861..cc872ab575 100644
--- a/src/or/rendservice.h
+++ b/src/or/rendservice.h
@@ -108,7 +108,7 @@ typedef struct rend_service_t {
/** If true, we don't close circuits for making requests to unsupported
* ports. */
int allow_unknown_ports;
- /** The maximum number of simultanious streams-per-circuit that are allowed
+ /** The maximum number of simultaneous streams-per-circuit that are allowed
* to be established, or 0 if no limit is set.
*/
int max_streams_per_circuit;
@@ -117,7 +117,9 @@ typedef struct rend_service_t {
int max_streams_close_circuit;
} rend_service_t;
-STATIC void rend_service_free(rend_service_t *service);
+STATIC void rend_service_free_(rend_service_t *service);
+#define rend_service_free(s) \
+ FREE_AND_NULL(rend_service_t, rend_service_free_, (s))
STATIC char *rend_service_sos_poison_path(const rend_service_t *service);
STATIC int rend_service_verify_single_onion_poison(
const rend_service_t *s,
@@ -160,7 +162,11 @@ int rend_service_receive_introduction(origin_circuit_t *circuit,
int rend_service_decrypt_intro(rend_intro_cell_t *request,
crypto_pk_t *key,
char **err_msg_out);
-void rend_service_free_intro(rend_intro_cell_t *request);
+void rend_service_free_intro_(rend_intro_cell_t *request);
+#define rend_service_free_intro(req) do { \
+ rend_service_free_intro_(req); \
+ (req) = NULL; \
+ } while (0)
rend_intro_cell_t * rend_service_begin_parse_intro(const uint8_t *request,
size_t request_len,
uint8_t type,
@@ -183,20 +189,17 @@ void rend_service_init(void);
rend_service_port_config_t *rend_service_parse_port_config(const char *string,
const char *sep,
char **err_msg_out);
-void rend_service_port_config_free(rend_service_port_config_t *p);
-
-void rend_authorized_client_free(rend_authorized_client_t *client);
-
-/** Return value from rend_service_add_ephemeral. */
-typedef enum {
- RSAE_BADAUTH = -5, /**< Invalid auth_type/auth_clients */
- RSAE_BADVIRTPORT = -4, /**< Invalid VIRTPORT/TARGET(s) */
- RSAE_ADDREXISTS = -3, /**< Onion address collision */
- RSAE_BADPRIVKEY = -2, /**< Invalid public key */
- RSAE_INTERNAL = -1, /**< Internal error */
- RSAE_OKAY = 0 /**< Service added as expected */
-} rend_service_add_ephemeral_status_t;
-rend_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk,
+void rend_service_port_config_free_(rend_service_port_config_t *p);
+#define rend_service_port_config_free(p) \
+ FREE_AND_NULL(rend_service_port_config_t, rend_service_port_config_free_, \
+ (p))
+
+void rend_authorized_client_free_(rend_authorized_client_t *client);
+#define rend_authorized_client_free(client) \
+ FREE_AND_NULL(rend_authorized_client_t, rend_authorized_client_free_, \
+ (client))
+
+hs_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk,
smartlist_t *ports,
int max_streams_per_circuit,
int max_streams_close_circuit,
diff --git a/src/or/rephist.c b/src/or/rephist.c
index 345722d8ce..43494692cb 100644
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@ -110,38 +110,12 @@ uint32_t rephist_total_num=0;
* 20X as much as one that ended a month ago, and routers that have had no
* uptime data for about half a year will get forgotten.) */
-/** History of an OR-\>OR link. */
-typedef struct link_history_t {
- /** When did we start tracking this list? */
- time_t since;
- /** When did we most recently note a change to this link */
- time_t changed;
- /** How many times did extending from OR1 to OR2 succeed? */
- unsigned long n_extend_ok;
- /** How many times did extending from OR1 to OR2 fail? */
- unsigned long n_extend_fail;
-} link_history_t;
-
/** History of an OR. */
typedef struct or_history_t {
/** When did we start tracking this OR? */
time_t since;
/** When did we most recently note a change to this OR? */
time_t changed;
- /** How many times did we successfully connect? */
- unsigned long n_conn_ok;
- /** How many times did we try to connect and fail?*/
- unsigned long n_conn_fail;
- /** How many seconds have we been connected to this OR before
- * 'up_since'? */
- unsigned long uptime;
- /** How many seconds have we been unable to connect to this OR before
- * 'down_since'? */
- unsigned long downtime;
- /** If nonzero, we have been connected since this time. */
- time_t up_since;
- /** If nonzero, we have been unable to connect since this time. */
- time_t down_since;
/** The address at which we most recently connected to this OR
* successfully. */
@@ -163,10 +137,6 @@ typedef struct or_history_t {
time_t start_of_downtime;
unsigned long weighted_uptime;
unsigned long total_weighted_time;
-
- /** Map from hex OR2 identity digest to a link_history_t for the link
- * from this OR to OR2. */
- digestmap_t *link_history_map;
} or_history_t;
/**
@@ -232,7 +202,6 @@ get_or_history(const char* id)
hist = tor_malloc_zero(sizeof(or_history_t));
rephist_total_alloc += sizeof(or_history_t);
rephist_total_num++;
- hist->link_history_map = digestmap_new();
hist->since = hist->changed = time(NULL);
tor_addr_make_unspec(&hist->last_reached_addr);
digestmap_set(history_map, id, hist);
@@ -240,66 +209,16 @@ get_or_history(const char* id)
return hist;
}
-/** Return the link_history_t for the link from the first named OR to
- * the second, creating it if necessary. (ORs are identified by
- * identity digest.)
- */
-static link_history_t *
-get_link_history(const char *from_id, const char *to_id)
-{
- or_history_t *orhist;
- link_history_t *lhist;
- orhist = get_or_history(from_id);
- if (!orhist)
- return NULL;
- if (tor_digest_is_zero(to_id))
- return NULL;
- lhist = digestmap_get(orhist->link_history_map, to_id);
- if (!lhist) {
- lhist = tor_malloc_zero(sizeof(link_history_t));
- rephist_total_alloc += sizeof(link_history_t);
- lhist->since = lhist->changed = time(NULL);
- digestmap_set(orhist->link_history_map, to_id, lhist);
- }
- return lhist;
-}
-
-/** Helper: free storage held by a single link history entry. */
-static void
-free_link_history_(void *val)
-{
- rephist_total_alloc -= sizeof(link_history_t);
- tor_free(val);
-}
-
/** Helper: free storage held by a single OR history entry. */
static void
free_or_history(void *_hist)
{
or_history_t *hist = _hist;
- digestmap_free(hist->link_history_map, free_link_history_);
rephist_total_alloc -= sizeof(or_history_t);
rephist_total_num--;
tor_free(hist);
}
-/** Update an or_history_t object <b>hist</b> so that its uptime/downtime
- * count is up-to-date as of <b>when</b>.
- */
-static void
-update_or_history(or_history_t *hist, time_t when)
-{
- tor_assert(hist);
- if (hist->up_since) {
- tor_assert(!hist->down_since);
- hist->uptime += (when - hist->up_since);
- hist->up_since = when;
- } else if (hist->down_since) {
- hist->downtime += (when - hist->down_since);
- hist->down_since = when;
- }
-}
-
/** Initialize the static data structures for tracking history. */
void
rep_hist_init(void)
@@ -309,99 +228,6 @@ rep_hist_init(void)
predicted_ports_alloc();
}
-/** Helper: note that we are no longer connected to the router with history
- * <b>hist</b>. If <b>failed</b>, the connection failed; otherwise, it was
- * closed correctly. */
-static void
-mark_or_down(or_history_t *hist, time_t when, int failed)
-{
- if (hist->up_since) {
- hist->uptime += (when - hist->up_since);
- hist->up_since = 0;
- }
- if (failed && !hist->down_since) {
- hist->down_since = when;
- }
-}
-
-/** Helper: note that we are connected to the router with history
- * <b>hist</b>. */
-static void
-mark_or_up(or_history_t *hist, time_t when)
-{
- if (hist->down_since) {
- hist->downtime += (when - hist->down_since);
- hist->down_since = 0;
- }
- if (!hist->up_since) {
- hist->up_since = when;
- }
-}
-
-/** Remember that an attempt to connect to the OR with identity digest
- * <b>id</b> failed at <b>when</b>.
- */
-void
-rep_hist_note_connect_failed(const char* id, time_t when)
-{
- or_history_t *hist;
- hist = get_or_history(id);
- if (!hist)
- return;
- ++hist->n_conn_fail;
- mark_or_down(hist, when, 1);
- hist->changed = when;
-}
-
-/** Remember that an attempt to connect to the OR with identity digest
- * <b>id</b> succeeded at <b>when</b>.
- */
-void
-rep_hist_note_connect_succeeded(const char* id, time_t when)
-{
- or_history_t *hist;
- hist = get_or_history(id);
- if (!hist)
- return;
- ++hist->n_conn_ok;
- mark_or_up(hist, when);
- hist->changed = when;
-}
-
-/** Remember that we intentionally closed our connection to the OR
- * with identity digest <b>id</b> at <b>when</b>.
- */
-void
-rep_hist_note_disconnect(const char* id, time_t when)
-{
- or_history_t *hist;
- hist = get_or_history(id);
- if (!hist)
- return;
- mark_or_down(hist, when, 0);
- hist->changed = when;
-}
-
-/** Remember that our connection to the OR with identity digest
- * <b>id</b> had an error and stopped working at <b>when</b>.
- */
-void
-rep_hist_note_connection_died(const char* id, time_t when)
-{
- or_history_t *hist;
- if (!id) {
- /* If conn has no identity, it didn't complete its handshake, or something
- * went wrong. Ignore it.
- */
- return;
- }
- hist = get_or_history(id);
- if (!hist)
- return;
- mark_or_down(hist, when, 1);
- hist->changed = when;
-}
-
/** We have just decided that this router with identity digest <b>id</b> is
* reachable, meaning we will give it a "Running" flag for the next while. */
void
@@ -538,7 +364,6 @@ rep_hist_make_router_pessimal(const char *id, time_t when)
tor_assert(hist);
rep_hist_note_router_unreachable(id, when);
- mark_or_down(hist, when, 1);
hist->weighted_run_length = 0;
hist->weighted_uptime = 0;
@@ -715,57 +540,17 @@ rep_hist_have_measured_enough_stability(void)
return started_tracking_stability < time(NULL) - 4*60*60;
}
-/** Remember that we successfully extended from the OR with identity
- * digest <b>from_id</b> to the OR with identity digest
- * <b>to_name</b>.
- */
-void
-rep_hist_note_extend_succeeded(const char *from_id, const char *to_id)
-{
- link_history_t *hist;
- /* log_fn(LOG_WARN, "EXTEND SUCCEEDED: %s->%s",from_name,to_name); */
- hist = get_link_history(from_id, to_id);
- if (!hist)
- return;
- ++hist->n_extend_ok;
- hist->changed = time(NULL);
-}
-
-/** Remember that we tried to extend from the OR with identity digest
- * <b>from_id</b> to the OR with identity digest <b>to_name</b>, but
- * failed.
- */
-void
-rep_hist_note_extend_failed(const char *from_id, const char *to_id)
-{
- link_history_t *hist;
- /* log_fn(LOG_WARN, "EXTEND FAILED: %s->%s",from_name,to_name); */
- hist = get_link_history(from_id, to_id);
- if (!hist)
- return;
- ++hist->n_extend_fail;
- hist->changed = time(NULL);
-}
-
/** Log all the reliability data we have remembered, with the chosen
* severity.
*/
void
rep_hist_dump_stats(time_t now, int severity)
{
- digestmap_iter_t *lhist_it;
digestmap_iter_t *orhist_it;
- const char *name1, *name2, *digest1, *digest2;
+ const char *name1, *digest1;
char hexdigest1[HEX_DIGEST_LEN+1];
- char hexdigest2[HEX_DIGEST_LEN+1];
or_history_t *or_history;
- link_history_t *link_history;
- void *or_history_p, *link_history_p;
- double uptime;
- char buffer[2048];
- size_t len;
- int ret;
- unsigned long upt, downt;
+ void *or_history_p;
const node_t *node;
rep_history_clean(now - get_options()->RephistTrackTime);
@@ -785,52 +570,12 @@ rep_hist_dump_stats(time_t now, int severity)
else
name1 = "(unknown)";
base16_encode(hexdigest1, sizeof(hexdigest1), digest1, DIGEST_LEN);
- update_or_history(or_history, now);
- upt = or_history->uptime;
- downt = or_history->downtime;
s = get_stability(or_history, now);
stability = (long)s;
- if (upt+downt) {
- uptime = ((double)upt) / (upt+downt);
- } else {
- uptime=1.0;
- }
tor_log(severity, LD_HIST,
- "OR %s [%s]: %ld/%ld good connections; uptime %ld/%ld sec (%.2f%%); "
- "wmtbf %lu:%02lu:%02lu",
+ "OR %s [%s]: wmtbf %lu:%02lu:%02lu",
name1, hexdigest1,
- or_history->n_conn_ok, or_history->n_conn_fail+or_history->n_conn_ok,
- upt, upt+downt, uptime*100.0,
stability/3600, (stability/60)%60, stability%60);
-
- if (!digestmap_isempty(or_history->link_history_map)) {
- strlcpy(buffer, " Extend attempts: ", sizeof(buffer));
- len = strlen(buffer);
- for (lhist_it = digestmap_iter_init(or_history->link_history_map);
- !digestmap_iter_done(lhist_it);
- lhist_it = digestmap_iter_next(or_history->link_history_map,
- lhist_it)) {
- digestmap_iter_get(lhist_it, &digest2, &link_history_p);
- if ((node = node_get_by_id(digest2)) && node_get_nickname(node))
- name2 = node_get_nickname(node);
- else
- name2 = "(unknown)";
-
- link_history = (link_history_t*) link_history_p;
-
- base16_encode(hexdigest2, sizeof(hexdigest2), digest2, DIGEST_LEN);
- ret = tor_snprintf(buffer+len, 2048-len, "%s [%s](%ld/%ld); ",
- name2,
- hexdigest2,
- link_history->n_extend_ok,
- link_history->n_extend_ok+link_history->n_extend_fail);
- if (ret<0)
- break;
- else
- len += ret;
- }
- tor_log(severity, LD_HIST, "%s", buffer);
- }
}
}
@@ -842,10 +587,9 @@ rep_history_clean(time_t before)
{
int authority = authdir_mode(get_options());
or_history_t *or_history;
- link_history_t *link_history;
- void *or_history_p, *link_history_p;
- digestmap_iter_t *orhist_it, *lhist_it;
- const char *d1, *d2;
+ void *or_history_p;
+ digestmap_iter_t *orhist_it;
+ const char *d1;
orhist_it = digestmap_iter_init(history_map);
while (!digestmap_iter_done(orhist_it)) {
@@ -862,19 +606,6 @@ rep_history_clean(time_t before)
free_or_history(or_history);
continue;
}
- for (lhist_it = digestmap_iter_init(or_history->link_history_map);
- !digestmap_iter_done(lhist_it); ) {
- digestmap_iter_get(lhist_it, &d2, &link_history_p);
- link_history = link_history_p;
- if (link_history->changed < before) {
- lhist_it = digestmap_iter_next_rmv(or_history->link_history_map,
- lhist_it);
- rephist_total_alloc -= sizeof(link_history_t);
- tor_free(link_history);
- continue;
- }
- lhist_it = digestmap_iter_next(or_history->link_history_map,lhist_it);
- }
orhist_it = digestmap_iter_next(history_map, orhist_it);
}
}
@@ -1358,9 +1089,12 @@ bw_array_new(void)
return b;
}
+#define bw_array_free(val) \
+ FREE_AND_NULL(bw_array_t, bw_array_free_, (val))
+
/** Free storage held by bandwidth array <b>b</b>. */
static void
-bw_array_free(bw_array_t *b)
+bw_array_free_(bw_array_t *b)
{
if (!b) {
return;
@@ -1885,7 +1619,7 @@ predicted_ports_init(void)
* be used.
*/
static void
-predicted_ports_free(void)
+predicted_ports_free_all(void)
{
rephist_total_alloc -=
smartlist_len(predicted_ports_list)*sizeof(predicted_port_t);
@@ -2733,7 +2467,7 @@ rep_hist_desc_stats_write(time_t now)
}
/** Called to note that we've served a given descriptor (by
- * digest). Incrememnts the count of descriptors served, and the number
+ * digest). Increments the count of descriptors served, and the number
* of times we've served this descriptor. */
void
rep_hist_note_desc_served(const char * desc)
@@ -2830,7 +2564,7 @@ HT_GENERATE2(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
/* DOCDOC bidi_map_free */
static void
-bidi_map_free(void)
+bidi_map_free_all(void)
{
bidi_map_entry_t **ptr, **next, *ent;
for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
@@ -2850,7 +2584,7 @@ rep_hist_reset_conn_stats(time_t now)
mostly_read = 0;
mostly_written = 0;
both_read_and_written = 0;
- bidi_map_free();
+ bidi_map_free_all();
}
/** Stop collecting connection stats in a way that we can re-start doing
@@ -3039,9 +2773,12 @@ hs_stats_new(void)
return new_hs_stats;
}
+#define hs_stats_free(val) \
+ FREE_AND_NULL(hs_stats_t, hs_stats_free_, (val))
+
/** Free an hs_stats_t structure. */
static void
-hs_stats_free(hs_stats_t *victim_hs_stats)
+hs_stats_free_(hs_stats_t *victim_hs_stats)
{
if (!victim_hs_stats) {
return;
@@ -3453,8 +3190,8 @@ rep_hist_free_all(void)
tor_free(exit_bytes_read);
tor_free(exit_bytes_written);
tor_free(exit_streams);
- predicted_ports_free();
- bidi_map_free();
+ predicted_ports_free_all();
+ bidi_map_free_all();
if (circuits_for_buffer_stats) {
SMARTLIST_FOREACH(circuits_for_buffer_stats, circ_buffer_stats_t *, s,
diff --git a/src/or/rephist.h b/src/or/rephist.h
index 496e366865..5072721592 100644
--- a/src/or/rephist.h
+++ b/src/or/rephist.h
@@ -13,13 +13,6 @@
#define TOR_REPHIST_H
void rep_hist_init(void);
-void rep_hist_note_connect_failed(const char* nickname, time_t when);
-void rep_hist_note_connect_succeeded(const char* nickname, time_t when);
-void rep_hist_note_disconnect(const char* nickname, time_t when);
-void rep_hist_note_connection_died(const char* nickname, time_t when);
-void rep_hist_note_extend_succeeded(const char *from_name,
- const char *to_name);
-void rep_hist_note_extend_failed(const char *from_name, const char *to_name);
void rep_hist_dump_stats(time_t now, int severity);
void rep_hist_note_bytes_read(size_t num_bytes, time_t when);
void rep_hist_note_bytes_written(size_t num_bytes, time_t when);
diff --git a/src/or/replaycache.c b/src/or/replaycache.c
index 3d42deb90a..a9a6709937 100644
--- a/src/or/replaycache.c
+++ b/src/or/replaycache.c
@@ -26,9 +26,8 @@
/** Free the replaycache r and all of its entries.
*/
-
void
-replaycache_free(replaycache_t *r)
+replaycache_free_(replaycache_t *r)
{
if (!r) {
log_info(LD_BUG, "replaycache_free() called on NULL");
@@ -44,7 +43,6 @@ replaycache_free(replaycache_t *r)
* for entries to age out and interval is the time after which the cache
* should be scrubbed for old entries.
*/
-
replaycache_t *
replaycache_new(time_t horizon, time_t interval)
{
@@ -72,9 +70,8 @@ replaycache_new(time_t horizon, time_t interval)
return r;
}
-/** See documentation for replaycache_add_and_test()
+/** See documentation for replaycache_add_and_test().
*/
-
STATIC int
replaycache_add_and_test_internal(
time_t present, replaycache_t *r, const void *data, size_t len,
@@ -136,9 +133,8 @@ replaycache_add_and_test_internal(
return rv;
}
-/** See documentation for replaycache_scrub_if_needed()
+/** See documentation for replaycache_scrub_if_needed().
*/
-
STATIC void
replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r)
{
@@ -186,7 +182,6 @@ replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r)
* and the function will return 1 if it was already seen within the cache's
* horizon, or 0 otherwise.
*/
-
int
replaycache_add_and_test(replaycache_t *r, const void *data, size_t len)
{
@@ -196,7 +191,6 @@ replaycache_add_and_test(replaycache_t *r, const void *data, size_t len)
/** Like replaycache_add_and_test(), but if it's a hit also return the time
* elapsed since this digest was last seen.
*/
-
int
replaycache_add_test_and_elapsed(
replaycache_t *r, const void *data, size_t len, time_t *elapsed)
@@ -207,7 +201,6 @@ replaycache_add_test_and_elapsed(
/** Scrub aged entries out of r if sufficiently long has elapsed since r was
* last scrubbed.
*/
-
void
replaycache_scrub_if_needed(replaycache_t *r)
{
diff --git a/src/or/replaycache.h b/src/or/replaycache.h
index 1cae3497ae..81a8d907fd 100644
--- a/src/or/replaycache.h
+++ b/src/or/replaycache.h
@@ -33,7 +33,9 @@ struct replaycache_s {
/* replaycache_t free/new */
-void replaycache_free(replaycache_t *r);
+void replaycache_free_(replaycache_t *r);
+#define replaycache_free(r) \
+ FREE_AND_NULL(replaycache_t, replaycache_free_, (r))
replaycache_t * replaycache_new(time_t horizon, time_t interval);
#ifdef REPLAYCACHE_PRIVATE
diff --git a/src/or/router.c b/src/or/router.c
index 854eaa71a9..a3d7cd373c 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -177,7 +177,7 @@ expire_old_onion_keys(void)
tor_mutex_release(key_lock);
- fname = get_datadir_fname2("keys", "secret_onion_key.old");
+ fname = get_keydir_fname("secret_onion_key.old");
if (file_status(fname) == FN_FILE) {
if (tor_unlink(fname) != 0) {
log_warn(LD_FS, "Couldn't unlink old onion key file %s: %s",
@@ -186,7 +186,7 @@ expire_old_onion_keys(void)
}
tor_free(fname);
- fname = get_datadir_fname2("keys", "secret_onion_key_ntor.old");
+ fname = get_keydir_fname("secret_onion_key_ntor.old");
if (file_status(fname) == FN_FILE) {
if (tor_unlink(fname) != 0) {
log_warn(LD_FS, "Couldn't unlink old ntor onion key file %s: %s",
@@ -240,7 +240,7 @@ ntor_key_map_free_helper(void *arg)
}
/** Release all storage from a keymap returned by construct_ntor_key_map. */
void
-ntor_key_map_free(di_digest256_map_t *map)
+ntor_key_map_free_(di_digest256_map_t *map)
{
if (!map)
return;
@@ -385,8 +385,8 @@ rotate_onion_key(void)
or_state_t *state = get_or_state();
curve25519_keypair_t new_curve25519_keypair;
time_t now;
- fname = get_datadir_fname2("keys", "secret_onion_key");
- fname_prev = get_datadir_fname2("keys", "secret_onion_key.old");
+ fname = get_keydir_fname("secret_onion_key");
+ fname_prev = get_keydir_fname("secret_onion_key.old");
/* There isn't much point replacing an old key with an empty file */
if (file_status(fname) == FN_FILE) {
if (replace_file(fname, fname_prev))
@@ -406,8 +406,8 @@ rotate_onion_key(void)
}
tor_free(fname);
tor_free(fname_prev);
- fname = get_datadir_fname2("keys", "secret_onion_key_ntor");
- fname_prev = get_datadir_fname2("keys", "secret_onion_key_ntor.old");
+ fname = get_keydir_fname("secret_onion_key_ntor");
+ fname_prev = get_keydir_fname("secret_onion_key_ntor.old");
if (curve25519_keypair_generate(&new_curve25519_keypair, 1) < 0)
goto error;
/* There isn't much point replacing an old key with an empty file */
@@ -631,7 +631,7 @@ load_authority_keyset(int legacy, crypto_pk_t **key_out,
crypto_pk_t *signing_key = NULL;
authority_cert_t *parsed = NULL;
- fname = get_datadir_fname2("keys",
+ fname = get_keydir_fname(
legacy ? "legacy_signing_key" : "authority_signing_key");
signing_key = init_key_from_file(fname, 0, LOG_ERR, 0);
if (!signing_key) {
@@ -639,7 +639,7 @@ load_authority_keyset(int legacy, crypto_pk_t **key_out,
goto done;
}
tor_free(fname);
- fname = get_datadir_fname2("keys",
+ fname = get_keydir_fname(
legacy ? "legacy_certificate" : "authority_certificate");
cert = read_file_to_str(fname, 0, NULL);
if (!cert) {
@@ -939,22 +939,9 @@ init_keys(void)
}
if (init_keys_common() < 0)
return -1;
- /* Make sure DataDirectory exists, and is private. */
- cpd_check_t cpd_opts = CPD_CREATE;
- if (options->DataDirectoryGroupReadable)
- cpd_opts |= CPD_GROUP_READ;
- if (check_private_dir(options->DataDirectory, cpd_opts, options->User)) {
- log_err(LD_OR, "Can't create/check datadirectory %s",
- options->DataDirectory);
- return -1;
- }
- /* Check the key directory. */
- keydir = get_datadir_fname("keys");
- if (check_private_dir(keydir, CPD_CREATE, options->User)) {
- tor_free(keydir);
+
+ if (create_keys_directory(options) < 0)
return -1;
- }
- tor_free(keydir);
/* 1a. Read v3 directory authority key/cert information. */
memset(v3_digest, 0, sizeof(v3_digest));
@@ -978,7 +965,7 @@ init_keys(void)
}
/* 1b. Read identity key. Make it if none is found. */
- keydir = get_datadir_fname2("keys", "secret_id_key");
+ keydir = get_keydir_fname("secret_id_key");
log_info(LD_GENERAL,"Reading/making identity key \"%s\"...",keydir);
prkey = init_key_from_file(keydir, 1, LOG_ERR, 1);
tor_free(keydir);
@@ -1006,7 +993,7 @@ init_keys(void)
return -1;
/* 2. Read onion key. Make it if none is found. */
- keydir = get_datadir_fname2("keys", "secret_onion_key");
+ keydir = get_keydir_fname("secret_onion_key");
log_info(LD_GENERAL,"Reading/making onion key \"%s\"...",keydir);
prkey = init_key_from_file(keydir, 1, LOG_ERR, 1);
tor_free(keydir);
@@ -1031,7 +1018,7 @@ init_keys(void)
}
}
- keydir = get_datadir_fname2("keys", "secret_onion_key.old");
+ keydir = get_keydir_fname("secret_onion_key.old");
if (!lastonionkey && file_status(keydir) == FN_FILE) {
/* Load keys from non-empty files only.
* Missing old keys won't be replaced with freshly generated keys. */
@@ -1044,14 +1031,14 @@ init_keys(void)
{
/* 2b. Load curve25519 onion keys. */
int r;
- keydir = get_datadir_fname2("keys", "secret_onion_key_ntor");
+ keydir = get_keydir_fname("secret_onion_key_ntor");
r = init_curve25519_keypair_from_file(&curve25519_onion_key,
keydir, 1, LOG_ERR, "onion");
tor_free(keydir);
if (r<0)
return -1;
- keydir = get_datadir_fname2("keys", "secret_onion_key_ntor.old");
+ keydir = get_keydir_fname("secret_onion_key_ntor.old");
if (tor_mem_is_zero((const char *)
last_curve25519_onion_key.pubkey.public_key,
CURVE25519_PUBKEY_LEN) &&
@@ -3714,6 +3701,7 @@ router_free_all(void)
crypto_pk_free(lastonionkey);
crypto_pk_free(server_identitykey);
crypto_pk_free(client_identitykey);
+
tor_mutex_free(key_lock);
routerinfo_free(desc_routerinfo);
extrainfo_free(desc_extrainfo);
diff --git a/src/or/router.h b/src/or/router.h
index 3351400911..696e983662 100644
--- a/src/or/router.h
+++ b/src/or/router.h
@@ -36,7 +36,9 @@ int get_onion_key_lifetime(void);
int get_onion_key_grace_period(void);
di_digest256_map_t *construct_ntor_key_map(void);
-void ntor_key_map_free(di_digest256_map_t *map);
+void ntor_key_map_free_(di_digest256_map_t *map);
+#define ntor_key_map_free(map) \
+ FREE_AND_NULL(di_digest256_map_t, ntor_key_map_free_, (map))
int router_initialize_tls_context(void);
int init_keys(void);
diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c
index f0973044b5..1933aaf4b6 100644
--- a/src/or/routerkeys.c
+++ b/src/or/routerkeys.c
@@ -719,7 +719,7 @@ load_ed_keys(const or_options_t *options, time_t now)
/* First try to get the signing key to see how it is. */
{
char *fname =
- options_get_datadir_fname2(options, "keys", "ed25519_signing");
+ options_get_keydir_fname(options, "ed25519_signing");
sign = ed_key_init_from_file(
fname,
INIT_ED_KEY_NEEDCERT|
@@ -814,26 +814,15 @@ load_ed_keys(const or_options_t *options, time_t now)
flags |= INIT_ED_KEY_TRY_ENCRYPTED;
/* Check/Create the key directory */
- cpd_check_t cpd_opts = CPD_CREATE;
- if (options->DataDirectoryGroupReadable)
- cpd_opts |= CPD_GROUP_READ;
- if (check_private_dir(options->DataDirectory, cpd_opts, options->User)) {
- log_err(LD_OR, "Can't create/check datadirectory %s",
- options->DataDirectory);
- goto err;
- }
- char *fname = get_datadir_fname("keys");
- if (check_private_dir(fname, CPD_CREATE, options->User) < 0) {
- log_err(LD_OR, "Problem creating/checking key directory %s", fname);
- tor_free(fname);
- goto err;
- }
- tor_free(fname);
+ if (create_keys_directory(options) < 0)
+ return -1;
+
+ char *fname;
if (options->master_key_fname) {
fname = tor_strdup(options->master_key_fname);
flags |= INIT_ED_KEY_EXPLICIT_FNAME;
} else {
- fname = options_get_datadir_fname2(options, "keys", "ed25519_master_id");
+ fname = options_get_keydir_fname(options, "ed25519_master_id");
}
id = ed_key_init_from_file(
fname,
@@ -853,8 +842,8 @@ load_ed_keys(const or_options_t *options, time_t now)
id = tor_malloc_zero(sizeof(*id));
memcpy(&id->pubkey, &check_signing_cert->signing_key,
sizeof(ed25519_public_key_t));
- fname = options_get_datadir_fname2(options, "keys",
- "ed25519_master_id_public_key");
+ fname = options_get_keydir_fname(options,
+ "ed25519_master_id_public_key");
if (ed25519_pubkey_write_to_file(&id->pubkey, fname, "type0") < 0) {
log_warn(LD_OR, "Error while attempting to write master public key "
"to disk");
@@ -899,7 +888,7 @@ load_ed_keys(const or_options_t *options, time_t now)
INIT_ED_KEY_NEEDCERT|
INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT);
char *fname =
- options_get_datadir_fname2(options, "keys", "ed25519_signing");
+ options_get_keydir_fname(options, "ed25519_signing");
ed25519_keypair_free(sign);
tor_cert_free(sign_cert);
sign = ed_key_init_from_file(fname,
@@ -1190,7 +1179,7 @@ log_master_signing_key_cert_expiration(const or_options_t *options)
int failed = 0;
time_t now = approx_time();
- fn = options_get_datadir_fname2(options, "keys", "ed25519_signing_cert");
+ fn = options_get_keydir_fname(options, "ed25519_signing_cert");
/* Try to grab our cached copy of the key. */
signing_key = get_master_signing_key_cert();
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index 95b39d3571..203895c867 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -143,6 +143,10 @@ DECLARE_TYPED_DIGESTMAP_FNS(dsmap_, digest_ds_map_t, download_status_t)
#define DSMAP_FOREACH(map, keyvar, valvar) \
DIGESTMAP_FOREACH(dsmap_to_digestmap(map), keyvar, download_status_t *, \
valvar)
+#define eimap_free(map, fn) MAP_FREE_AND_NULL(eimap, (map), (fn))
+#define rimap_free(map, fn) MAP_FREE_AND_NULL(rimap, (map), (fn))
+#define dsmap_free(map, fn) MAP_FREE_AND_NULL(dsmap, (map), (fn))
+#define sdmap_free(map, fn) MAP_FREE_AND_NULL(sdmap, (map), (fn))
/* Forward declaration for cert_list_t */
typedef struct cert_list_t cert_list_t;
@@ -159,7 +163,6 @@ static const routerstatus_t *router_pick_dirserver_generic(
smartlist_t *sourcelist,
dirinfo_type_t type, int flags);
static void mark_all_dirservers_up(smartlist_t *server_list);
-static void dir_server_free(dir_server_t *ds);
static int signed_desc_digest_is_recognized(signed_descriptor_t *desc);
static const char *signed_descriptor_get_body_impl(
const signed_descriptor_t *desc,
@@ -174,7 +177,7 @@ static void download_status_reset_by_sk_in_cl(cert_list_t *cl,
const char *digest);
static int download_status_is_ready_by_sk_in_cl(cert_list_t *cl,
const char *digest,
- time_t now, int max_failures);
+ time_t now);
/****************************************************************************/
@@ -232,7 +235,7 @@ get_n_authorities(dirinfo_type_t type)
return n;
}
-/** Initialise schedule, want_authority, and increment on in the download
+/** Initialise schedule, want_authority, and increment_on in the download
* status dlstatus, then call download_status_reset() on it.
* It is safe to call this function or download_status_reset() multiple times
* on a new dlstatus. But it should *not* be called after a dlstatus has been
@@ -243,7 +246,6 @@ download_status_cert_init(download_status_t *dlstatus)
dlstatus->schedule = DL_SCHED_CONSENSUS;
dlstatus->want_authority = DL_WANT_ANY_DIRSERVER;
dlstatus->increment_on = DL_SCHED_INCREMENT_FAILURE;
- dlstatus->backoff = DL_SCHED_RANDOM_EXPONENTIAL;
dlstatus->last_backoff_position = 0;
dlstatus->last_delay_used = 0;
@@ -285,7 +287,7 @@ download_status_reset_by_sk_in_cl(cert_list_t *cl, const char *digest)
static int
download_status_is_ready_by_sk_in_cl(cert_list_t *cl,
const char *digest,
- time_t now, int max_failures)
+ time_t now)
{
int rv = 0;
download_status_t *dlstatus = NULL;
@@ -302,7 +304,7 @@ download_status_is_ready_by_sk_in_cl(cert_list_t *cl,
/* Got one? */
if (dlstatus) {
/* Use download_status_is_ready() */
- rv = download_status_is_ready(dlstatus, now, max_failures);
+ rv = download_status_is_ready(dlstatus, now);
} else {
/*
* If we don't know anything about it, return 1, since we haven't
@@ -364,7 +366,7 @@ list_authority_ids_with_downloads, (void))
smartlist_add(ids, tmp);
}
}
- /* else definitely no downlaods going since nothing even has a cert list */
+ /* else definitely no downloads going since nothing even has a cert list */
return ids;
}
@@ -443,9 +445,12 @@ download_status_for_authority_id_and_sk,(const char *id_digest,
return dl;
}
+#define cert_list_free(val) \
+ FREE_AND_NULL(cert_list_t, cert_list_free_, (val))
+
/** Release all space held by a cert_list_t */
static void
-cert_list_free(cert_list_t *cl)
+cert_list_free_(cert_list_t *cl)
{
if (!cl)
return;
@@ -459,9 +464,9 @@ cert_list_free(cert_list_t *cl)
/** Wrapper for cert_list_free so we can pass it to digestmap_free */
static void
-cert_list_free_(void *cl)
+cert_list_free_void(void *cl)
{
- cert_list_free(cl);
+ cert_list_free_(cl);
}
/** Reload the cached v3 key certificates from the cached-certs file in
@@ -473,7 +478,7 @@ trusted_dirs_reload_certs(void)
char *contents;
int r;
- filename = get_datadir_fname("cached-certs");
+ filename = get_cachedir_fname("cached-certs");
contents = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL);
tor_free(filename);
if (!contents)
@@ -662,7 +667,7 @@ trusted_dirs_flush_certs_to_disk(void)
});
} DIGESTMAP_FOREACH_END;
- filename = get_datadir_fname("cached-certs");
+ filename = get_cachedir_fname("cached-certs");
if (write_chunks_to_file(filename, chunks, 0, 0)) {
log_warn(LD_FS, "Error writing certificates to disk.");
}
@@ -1062,8 +1067,7 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now,
}
} SMARTLIST_FOREACH_END(cert);
if (!found &&
- download_status_is_ready(&(cl->dl_status_by_id), now,
- options->TestingCertMaxDownloadTries) &&
+ download_status_is_ready(&(cl->dl_status_by_id), now) &&
!digestmap_get(pending_id, ds->v3_identity_digest)) {
log_info(LD_DIR,
"No current certificate known for authority %s "
@@ -1126,8 +1130,7 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now,
continue;
}
if (download_status_is_ready_by_sk_in_cl(
- cl, sig->signing_key_digest,
- now, options->TestingCertMaxDownloadTries) &&
+ cl, sig->signing_key_digest, now) &&
!fp_pair_map_get_by_digests(pending_cert,
voter->identity_digest,
sig->signing_key_digest)) {
@@ -1164,7 +1167,7 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now,
} SMARTLIST_FOREACH_END(voter);
}
- /* Bridge clients look up the node for the dir_hint */
+ /* Bridge clients look up the node for the dir_hint */
const node_t *node = NULL;
/* All clients, including bridge clients, look up the routerstatus for the
* dir_hint */
@@ -1339,7 +1342,7 @@ static int
signed_desc_append_to_journal(signed_descriptor_t *desc,
desc_store_t *store)
{
- char *fname = get_datadir_fname_suffix(store->fname_base, ".new");
+ char *fname = get_cachedir_fname_suffix(store->fname_base, ".new");
const char *body = signed_descriptor_get_body_impl(desc,1);
size_t len = desc->signed_descriptor_len + desc->annotations_len;
@@ -1410,8 +1413,8 @@ router_rebuild_store(int flags, desc_store_t *store)
log_info(LD_DIR, "Rebuilding %s cache", store->description);
- fname = get_datadir_fname(store->fname_base);
- fname_tmp = get_datadir_fname_suffix(store->fname_base, ".tmp");
+ fname = get_cachedir_fname(store->fname_base);
+ fname_tmp = get_cachedir_fname_suffix(store->fname_base, ".tmp");
chunk_list = smartlist_new();
@@ -1508,7 +1511,7 @@ router_rebuild_store(int flags, desc_store_t *store)
} SMARTLIST_FOREACH_END(sd);
tor_free(fname);
- fname = get_datadir_fname_suffix(store->fname_base, ".new");
+ fname = get_cachedir_fname_suffix(store->fname_base, ".new");
write_str_to_file(fname, "", 1);
r = 0;
@@ -1538,7 +1541,7 @@ router_reload_router_list_impl(desc_store_t *store)
int extrainfo = (store->type == EXTRAINFO_STORE);
store->journal_len = store->store_len = 0;
- fname = get_datadir_fname(store->fname_base);
+ fname = get_cachedir_fname(store->fname_base);
if (store->mmap) {
/* get rid of it first */
@@ -1565,7 +1568,7 @@ router_reload_router_list_impl(desc_store_t *store)
}
tor_free(fname);
- fname = get_datadir_fname_suffix(store->fname_base, ".new");
+ fname = get_cachedir_fname_suffix(store->fname_base, ".new");
/* don't load empty files - we wouldn't get any data, even if we tried */
if (file_status(fname) == FN_FILE)
contents = read_file_to_str(fname, RFTS_BIN|RFTS_IGNORE_MISSING, &st);
@@ -2332,7 +2335,7 @@ router_add_running_nodes_to_smartlist(smartlist_t *sl, int need_uptime,
SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) {
if (!node->is_running || !node->is_valid)
continue;
- if (need_desc && !(node->ri || (node->rs && node->md)))
+ if (need_desc && !node_has_preferred_descriptor(node, direct_conn))
continue;
if (node->ri && node->ri->purpose != ROUTER_PURPOSE_GENERAL)
continue;
@@ -2646,7 +2649,7 @@ compute_weighted_bandwidths(const smartlist_t *sl,
is_dir = node_is_dir(node);
if (node->rs) {
if (!node->rs->has_bandwidth) {
- /* This should never happen, unless all the authorites downgrade
+ /* This should never happen, unless all the authorities downgrade
* to 0.2.0 or rogue routerstatuses get inserted into our consensus. */
if (! warned_missing_bw) {
log_warn(LD_BUG,
@@ -2755,7 +2758,7 @@ frac_nodes_with_descriptors(const smartlist_t *sl,
total <= 0.0) {
int n_with_descs = 0;
SMARTLIST_FOREACH(sl, const node_t *, node, {
- if (node_has_descriptor(node))
+ if (node_has_any_descriptor(node))
n_with_descs++;
});
@@ -2765,7 +2768,7 @@ frac_nodes_with_descriptors(const smartlist_t *sl,
present = 0.0;
SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) {
- if (node_has_descriptor(node))
+ if (node_has_any_descriptor(node))
present += bandwidths[node_sl_idx];
} SMARTLIST_FOREACH_END(node);
@@ -3101,7 +3104,7 @@ signed_descriptor_get_body_impl(const signed_descriptor_t *desc,
log_err(LD_DIR, "We couldn't read a descriptor that is supposedly "
"mmaped in our cache. Is another process running in our data "
"directory? Exiting.");
- exit(1);
+ exit(1); // XXXX bad exit: should recover.
}
}
if (!r) /* no mmap, or not in cache. */
@@ -3115,7 +3118,7 @@ signed_descriptor_get_body_impl(const signed_descriptor_t *desc,
log_err(LD_DIR, "descriptor at %p begins with unexpected string %s. "
"Is another process running in our data directory? Exiting.",
desc, escaped(cp));
- exit(1);
+ exit(1); // XXXX bad exit: should recover.
}
}
@@ -3169,7 +3172,7 @@ router_get_routerlist(void)
/** Free all storage held by <b>router</b>. */
void
-routerinfo_free(routerinfo_t *router)
+routerinfo_free_(routerinfo_t *router)
{
if (!router)
return;
@@ -3199,7 +3202,7 @@ routerinfo_free(routerinfo_t *router)
/** Release all storage held by <b>extrainfo</b> */
void
-extrainfo_free(extrainfo_t *extrainfo)
+extrainfo_free_(extrainfo_t *extrainfo)
{
if (!extrainfo)
return;
@@ -3211,9 +3214,12 @@ extrainfo_free(extrainfo_t *extrainfo)
tor_free(extrainfo);
}
+#define signed_descriptor_free(val) \
+ FREE_AND_NULL(signed_descriptor_t, signed_descriptor_free_, (val))
+
/** Release storage held by <b>sd</b>. */
static void
-signed_descriptor_free(signed_descriptor_t *sd)
+signed_descriptor_free_(signed_descriptor_t *sd)
{
if (!sd)
return;
@@ -3267,21 +3273,21 @@ signed_descriptor_from_routerinfo(routerinfo_t *ri)
/** Helper: free the storage held by the extrainfo_t in <b>e</b>. */
static void
-extrainfo_free_(void *e)
+extrainfo_free_void(void *e)
{
- extrainfo_free(e);
+ extrainfo_free_(e);
}
/** Free all storage held by a routerlist <b>rl</b>. */
void
-routerlist_free(routerlist_t *rl)
+routerlist_free_(routerlist_t *rl)
{
if (!rl)
return;
rimap_free(rl->identity_map, NULL);
sdmap_free(rl->desc_digest_map, NULL);
sdmap_free(rl->desc_by_eid_map, NULL);
- eimap_free(rl->extra_info_map, extrainfo_free_);
+ eimap_free(rl->extra_info_map, extrainfo_free_void);
SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r,
routerinfo_free(r));
SMARTLIST_FOREACH(rl->old_routers, signed_descriptor_t *, sd,
@@ -3773,7 +3779,7 @@ routerlist_free_all(void)
smartlist_free(fallback_dir_servers);
trusted_dir_servers = fallback_dir_servers = NULL;
if (trusted_dir_certs) {
- digestmap_free(trusted_dir_certs, cert_list_free_);
+ digestmap_free(trusted_dir_certs, cert_list_free_void);
trusted_dir_certs = NULL;
}
}
@@ -4550,7 +4556,7 @@ signed_desc_digest_is_recognized(signed_descriptor_t *desc)
void
update_all_descriptor_downloads(time_t now)
{
- if (get_options()->DisableNetwork)
+ if (should_delay_dir_fetches(get_options(), NULL))
return;
update_router_descriptor_downloads(now);
update_microdesc_downloads(now);
@@ -4741,7 +4747,7 @@ dir_server_add(dir_server_t *ent)
/** Free storage held in <b>cert</b>. */
void
-authority_cert_free(authority_cert_t *cert)
+authority_cert_free_(authority_cert_t *cert)
{
if (!cert)
return;
@@ -4753,9 +4759,12 @@ authority_cert_free(authority_cert_t *cert)
tor_free(cert);
}
+#define dir_server_free(val) \
+ FREE_AND_NULL(dir_server_t, dir_server_free_, (val))
+
/** Free storage held in <b>ds</b>. */
static void
-dir_server_free(dir_server_t *ds)
+dir_server_free_(dir_server_t *ds)
{
if (!ds)
return;
@@ -5157,8 +5166,7 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote,
++n_inprogress;
continue; /* We have an in-progress download. */
}
- if (!download_status_is_ready(&rs->dl_status, now,
- options->TestingDescriptorMaxDownloadTries)) {
+ if (!download_status_is_ready(&rs->dl_status, now)) {
++n_delayed; /* Not ready for retry. */
continue;
}
@@ -5197,15 +5205,30 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote,
SMARTLIST_FOREACH_BEGIN(no_longer_old, signed_descriptor_t *, sd) {
const char *msg;
was_router_added_t r;
+ time_t tmp_cert_expiration_time;
routerinfo_t *ri = routerlist_reparse_old(rl, sd);
if (!ri) {
log_warn(LD_BUG, "Failed to re-parse a router.");
continue;
}
+ /* need to remember for below, since add_to_routerlist may free. */
+ tmp_cert_expiration_time = ri->cert_expiration_time;
+
r = router_add_to_routerlist(ri, &msg, 1, 0);
if (WRA_WAS_OUTDATED(r)) {
- log_warn(LD_DIR, "Couldn't add re-parsed router: %s",
+ log_warn(LD_DIR, "Couldn't add re-parsed router: %s. This isn't "
+ "usually a big deal, but you should make sure that your "
+ "clock and timezone are set correctly.",
msg?msg:"???");
+ if (r == ROUTER_CERTS_EXPIRED) {
+ char time_cons[ISO_TIME_LEN+1];
+ char time_cert_expires[ISO_TIME_LEN+1];
+ format_iso_time(time_cons, consensus->valid_after);
+ format_iso_time(time_cert_expires, tmp_cert_expiration_time);
+ log_warn(LD_DIR, " (I'm looking at a consensus from %s; This "
+ "router's certificates began expiring at %s.)",
+ time_cons, time_cert_expires);
+ }
}
} SMARTLIST_FOREACH_END(sd);
routerlist_assert_ok(rl);
@@ -5319,8 +5342,7 @@ update_extrainfo_downloads(time_t now)
++n_have;
continue;
}
- if (!download_status_is_ready(&sd->ei_dl_status, now,
- options->TestingDescriptorMaxDownloadTries)) {
+ if (!download_status_is_ready(&sd->ei_dl_status, now)) {
++n_delay;
continue;
}
@@ -5382,8 +5404,10 @@ update_extrainfo_downloads(time_t now)
smartlist_free(wanted);
}
-/** Reset the descriptor download failure count on all routers, so that we
- * can retry any long-failed routers immediately.
+/** Reset the consensus and extra-info download failure count on all routers.
+ * When we get a new consensus,
+ * routers_update_status_from_consensus_networkstatus() will reset the
+ * download statuses on the descriptors in that consensus.
*/
void
router_reset_descriptor_download_failures(void)
@@ -5395,6 +5419,8 @@ router_reset_descriptor_download_failures(void)
last_descriptor_download_attempted = 0;
if (!routerlist)
return;
+ /* We want to download *all* extra-info descriptors, not just those in
+ * the consensus we currently have (or are about to have) */
SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, ri,
{
download_status_reset(&ri->cache_info.ei_dl_status);
@@ -5636,11 +5662,11 @@ routerstatus_version_supports_extend2_cells(const routerstatus_t *rs,
return allow_unknown_versions;
}
- if (!rs->protocols_known) {
+ if (!rs->pv.protocols_known) {
return allow_unknown_versions;
}
- return rs->supports_extend2_cells;
+ return rs->pv.supports_extend2_cells;
}
/** Assert that the internal representation of <b>rl</b> is
diff --git a/src/or/routerlist.h b/src/or/routerlist.h
index 8384c7eb8c..83f4d1002f 100644
--- a/src/or/routerlist.h
+++ b/src/or/routerlist.h
@@ -96,9 +96,13 @@ MOCK_DECL(signed_descriptor_t *,extrainfo_get_by_descriptor_digest,
const char *signed_descriptor_get_body(const signed_descriptor_t *desc);
const char *signed_descriptor_get_annotations(const signed_descriptor_t *desc);
routerlist_t *router_get_routerlist(void);
-void routerinfo_free(routerinfo_t *router);
-void extrainfo_free(extrainfo_t *extrainfo);
-void routerlist_free(routerlist_t *rl);
+void routerinfo_free_(routerinfo_t *router);
+#define routerinfo_free(router) \
+ FREE_AND_NULL(routerinfo_t, routerinfo_free_, (router))
+void extrainfo_free_(extrainfo_t *extrainfo);
+#define extrainfo_free(ei) FREE_AND_NULL(extrainfo_t, extrainfo_free_, (ei))
+void routerlist_free_(routerlist_t *rl);
+#define routerlist_free(rl) FREE_AND_NULL(routerlist_t, routerlist_free_, (rl))
void dump_routerlist_mem_usage(int severity);
void routerlist_remove(routerlist_t *rl, routerinfo_t *ri, int make_old,
time_t now);
@@ -191,7 +195,9 @@ dir_server_t *fallback_dir_server_new(const tor_addr_t *addr,
const char *id_digest, double weight);
void dir_server_add(dir_server_t *ent);
-void authority_cert_free(authority_cert_t *cert);
+void authority_cert_free_(authority_cert_t *cert);
+#define authority_cert_free(cert) \
+ FREE_AND_NULL(authority_cert_t, authority_cert_free_, (cert))
void clear_dir_servers(void);
void update_consensus_router_descriptor_downloads(time_t now, int is_vote,
networkstatus_t *consensus);
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 3eda024f0f..79499f2e6f 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -1895,12 +1895,19 @@ router_parse_entry_from_string(const char *s, const char *end,
}
}
- if ((tok = find_opt_by_keyword(tokens, K_PLATFORM))) {
- router->platform = tor_strdup(tok->args[0]);
- }
+ {
+ const char *version = NULL, *protocols = NULL;
+ if ((tok = find_opt_by_keyword(tokens, K_PLATFORM))) {
+ router->platform = tor_strdup(tok->args[0]);
+ version = tok->args[0];
+ }
+
+ if ((tok = find_opt_by_keyword(tokens, K_PROTO))) {
+ router->protocol_list = tor_strdup(tok->args[0]);
+ protocols = tok->args[0];
+ }
- if ((tok = find_opt_by_keyword(tokens, K_PROTO))) {
- router->protocol_list = tor_strdup(tok->args[0]);
+ summarize_protover_flags(&router->pv, protocols, version);
}
if ((tok = find_opt_by_keyword(tokens, K_CONTACT))) {
@@ -2530,6 +2537,50 @@ routerstatus_parse_guardfraction(const char *guardfraction_str,
return 0;
}
+/** Summarize the protocols listed in <b>protocols</b> into <b>out</b>,
+ * falling back or correcting them based on <b>version</b> as appropriate.
+ */
+STATIC void
+summarize_protover_flags(protover_summary_flags_t *out,
+ const char *protocols,
+ const char *version)
+{
+ tor_assert(out);
+ memset(out, 0, sizeof(*out));
+ if (protocols) {
+ out->protocols_known = 1;
+ out->supports_extend2_cells =
+ protocol_list_supports_protocol(protocols, PRT_RELAY, 2);
+ out->supports_ed25519_link_handshake_compat =
+ protocol_list_supports_protocol(protocols, PRT_LINKAUTH, 3);
+ out->supports_ed25519_link_handshake_any =
+ protocol_list_supports_protocol_or_later(protocols, PRT_LINKAUTH, 3);
+ out->supports_ed25519_hs_intro =
+ protocol_list_supports_protocol(protocols, PRT_HSINTRO, 4);
+ out->supports_v3_hsdir =
+ protocol_list_supports_protocol(protocols, PRT_HSDIR,
+ PROTOVER_HSDIR_V3);
+ out->supports_v3_rendezvous_point =
+ protocol_list_supports_protocol(protocols, PRT_HSREND,
+ PROTOVER_HS_RENDEZVOUS_POINT_V3);
+ }
+ if (version && !strcmpstart(version, "Tor ")) {
+ if (!out->protocols_known) {
+ /* The version is a "Tor" version, and where there is no
+ * list of protocol versions that we should be looking at instead. */
+
+ out->supports_extend2_cells =
+ tor_version_as_new_as(version, "0.2.4.8-alpha");
+ out->protocols_known = 1;
+ } else {
+ /* Bug #22447 forces us to filter on this version. */
+ if (!tor_version_as_new_as(version, "0.3.0.8")) {
+ out->supports_v3_hsdir = 0;
+ }
+ }
+ }
+}
+
/** Given a string at *<b>s</b>, containing a routerstatus object, and an
* empty smartlist at <b>tokens</b>, parse and return the first router status
* object in the string, and advance *<b>s</b> to just after the end of the
@@ -2695,42 +2746,21 @@ routerstatus_parse_entry_from_string(memarea_t *area,
if (consensus_method >= MIN_METHOD_FOR_EXCLUDING_INVALID_NODES)
rs->is_valid = 1;
}
- int found_protocol_list = 0;
- if ((tok = find_opt_by_keyword(tokens, K_PROTO))) {
- found_protocol_list = 1;
- rs->protocols_known = 1;
- rs->supports_extend2_cells =
- protocol_list_supports_protocol(tok->args[0], PRT_RELAY, 2);
- rs->supports_ed25519_link_handshake =
- protocol_list_supports_protocol(tok->args[0], PRT_LINKAUTH, 3);
- rs->supports_ed25519_hs_intro =
- protocol_list_supports_protocol(tok->args[0], PRT_HSINTRO, 4);
- rs->supports_v3_hsdir =
- protocol_list_supports_protocol(tok->args[0], PRT_HSDIR,
- PROTOVER_HSDIR_V3);
- rs->supports_v3_rendezvous_point =
- protocol_list_supports_protocol(tok->args[0], PRT_HSREND,
- PROTOVER_HS_RENDEZVOUS_POINT_V3);
- }
- if ((tok = find_opt_by_keyword(tokens, K_V))) {
- tor_assert(tok->n_args == 1);
- if (!strcmpstart(tok->args[0], "Tor ") && !found_protocol_list) {
- /* We only do version checks like this in the case where
- * the version is a "Tor" version, and where there is no
- * list of protocol versions that we should be looking at instead. */
- rs->supports_extend2_cells =
- tor_version_as_new_as(tok->args[0], "0.2.4.8-alpha");
- rs->protocols_known = 1;
- }
- if (!strcmpstart(tok->args[0], "Tor ") && found_protocol_list) {
- /* Bug #22447 forces us to filter on this version. */
- if (!tor_version_as_new_as(tok->args[0], "0.3.0.8")) {
- rs->supports_v3_hsdir = 0;
+ {
+ const char *protocols = NULL, *version = NULL;
+ if ((tok = find_opt_by_keyword(tokens, K_PROTO))) {
+ tor_assert(tok->n_args == 1);
+ protocols = tok->args[0];
+ }
+ if ((tok = find_opt_by_keyword(tokens, K_V))) {
+ tor_assert(tok->n_args == 1);
+ version = tok->args[0];
+ if (vote_rs) {
+ vote_rs->version = tor_strdup(tok->args[0]);
}
}
- if (vote_rs) {
- vote_rs->version = tor_strdup(tok->args[0]);
- }
+
+ summarize_protover_flags(&rs->pv, protocols, version);
}
/* handle weighting/bandwidth info */
@@ -3779,7 +3809,6 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
ns->consensus_method,
flav))) {
/* Use exponential-backoff scheduling when downloading microdescs */
- rs->dl_status.backoff = DL_SCHED_RANDOM_EXPONENTIAL;
smartlist_add(ns->routerstatus_list, rs);
}
}
@@ -4027,7 +4056,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
/** Return the common_digests_t that holds the digests of the
* <b>flavor_name</b>-flavored networkstatus according to the detached
* signatures document <b>sigs</b>, allocating a new common_digests_t as
- * neeeded. */
+ * needed. */
static common_digests_t *
detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name)
{
@@ -4041,7 +4070,7 @@ detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name)
/** Return the list of signatures of the <b>flavor_name</b>-flavored
* networkstatus according to the detached signatures document <b>sigs</b>,
- * allocating a new common_digests_t as neeeded. */
+ * allocating a new common_digests_t as needed. */
static smartlist_t *
detached_get_signatures(ns_detached_signatures_t *sigs,
const char *flavor_name)
diff --git a/src/or/routerparse.h b/src/or/routerparse.h
index c4c8635f4b..418fd3acdb 100644
--- a/src/or/routerparse.h
+++ b/src/or/routerparse.h
@@ -133,6 +133,10 @@ MOCK_DECL(STATIC int, router_compute_hash_final,(char *digest,
digest_algorithm_t alg));
MOCK_DECL(STATIC int, signed_digest_equals,
(const uint8_t *d1, const uint8_t *d2, size_t len));
+
+STATIC void summarize_protover_flags(protover_summary_flags_t *out,
+ const char *protocols,
+ const char *version);
#endif /* defined(ROUTERPARSE_PRIVATE) */
#define ED_DESC_SIGNATURE_PREFIX "Tor router descriptor signature v1"
diff --git a/src/or/routerset.c b/src/or/routerset.c
index 54e26ef943..a2599b316c 100644
--- a/src/or/routerset.c
+++ b/src/or/routerset.c
@@ -437,7 +437,7 @@ routerset_equal(const routerset_t *old, const routerset_t *new)
/** Free all storage held in <b>routerset</b>. */
void
-routerset_free(routerset_t *routerset)
+routerset_free_(routerset_t *routerset)
{
if (!routerset)
return;
diff --git a/src/or/routerset.h b/src/or/routerset.h
index d8819ef3fd..53e8c66c5e 100644
--- a/src/or/routerset.h
+++ b/src/or/routerset.h
@@ -40,7 +40,8 @@ void routerset_subtract_nodes(smartlist_t *out,
char *routerset_to_string(const routerset_t *routerset);
int routerset_equal(const routerset_t *old, const routerset_t *new);
-void routerset_free(routerset_t *routerset);
+void routerset_free_(routerset_t *routerset);
+#define routerset_free(rs) FREE_AND_NULL(routerset_t, routerset_free_, (rs))
int routerset_len(const routerset_t *set);
#ifdef ROUTERSET_PRIVATE
diff --git a/src/or/scheduler.c b/src/or/scheduler.c
index 1984084feb..382b3e3ca9 100644
--- a/src/or/scheduler.c
+++ b/src/or/scheduler.c
@@ -305,7 +305,7 @@ select_scheduler(void)
* wishes of using what it has been configured and don't do a sneaky
* fallback. Because this can be changed at runtime, we have to stop tor
* right now. */
- exit(1);
+ exit(1); // XXXX bad exit
}
/* Set the chosen scheduler. */
@@ -362,6 +362,36 @@ set_scheduler(void)
* Functions that can only be accessed from scheduler*.c
*****************************************************************************/
+/** Returns human readable string for the given channel scheduler state. */
+const char *
+get_scheduler_state_string(int scheduler_state)
+{
+ switch (scheduler_state) {
+ case SCHED_CHAN_IDLE:
+ return "IDLE";
+ case SCHED_CHAN_WAITING_FOR_CELLS:
+ return "WAITING_FOR_CELLS";
+ case SCHED_CHAN_WAITING_TO_WRITE:
+ return "WAITING_TO_WRITE";
+ case SCHED_CHAN_PENDING:
+ return "PENDING";
+ default:
+ return "(invalid)";
+ }
+}
+
+/** Helper that logs channel scheduler_state changes. Use this instead of
+ * setting scheduler_state directly. */
+void
+scheduler_set_channel_state(channel_t *chan, int new_state)
+{
+ log_debug(LD_SCHED, "chan %" PRIu64 " changed from scheduler state %s to %s",
+ chan->global_identifier,
+ get_scheduler_state_string(chan->scheduler_state),
+ get_scheduler_state_string(new_state));
+ chan->scheduler_state = new_state;
+}
+
/** Return the pending channel list. */
smartlist_t *
get_channels_pending(void)
@@ -498,11 +528,7 @@ scheduler_channel_doesnt_want_writes,(channel_t *chan))
scheduler_compare_channels,
offsetof(channel_t, sched_heap_idx),
chan);
- chan->scheduler_state = SCHED_CHAN_WAITING_TO_WRITE;
- log_debug(LD_SCHED,
- "Channel " U64_FORMAT " at %p went from pending "
- "to waiting_to_write",
- U64_PRINTF_ARG(chan->global_identifier), chan);
+ scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE);
} else {
/*
* It's not in pending, so it can't become waiting_to_write; it's
@@ -510,10 +536,7 @@ scheduler_channel_doesnt_want_writes,(channel_t *chan))
* waiting_for_cells (remove it, can't write any more).
*/
if (chan->scheduler_state == SCHED_CHAN_WAITING_FOR_CELLS) {
- chan->scheduler_state = SCHED_CHAN_IDLE;
- log_debug(LD_SCHED,
- "Channel " U64_FORMAT " at %p left waiting_for_cells",
- U64_PRINTF_ARG(chan->global_identifier), chan);
+ scheduler_set_channel_state(chan, SCHED_CHAN_IDLE);
}
}
}
@@ -536,17 +559,13 @@ scheduler_channel_has_waiting_cells,(channel_t *chan))
* the other lists. It has waiting cells now, so it goes to
* channels_pending.
*/
- chan->scheduler_state = SCHED_CHAN_PENDING;
+ scheduler_set_channel_state(chan, SCHED_CHAN_PENDING);
if (!SCHED_BUG(chan->sched_heap_idx != -1, chan)) {
smartlist_pqueue_add(channels_pending,
scheduler_compare_channels,
offsetof(channel_t, sched_heap_idx),
chan);
}
- log_debug(LD_SCHED,
- "Channel " U64_FORMAT " at %p went from waiting_for_cells "
- "to pending",
- U64_PRINTF_ARG(chan->global_identifier), chan);
/* If we made a channel pending, we potentially have scheduling work to
* do. */
the_scheduler->schedule();
@@ -558,10 +577,7 @@ scheduler_channel_has_waiting_cells,(channel_t *chan))
*/
if (!(chan->scheduler_state == SCHED_CHAN_WAITING_TO_WRITE ||
chan->scheduler_state == SCHED_CHAN_PENDING)) {
- chan->scheduler_state = SCHED_CHAN_WAITING_TO_WRITE;
- log_debug(LD_SCHED,
- "Channel " U64_FORMAT " at %p entered waiting_to_write",
- U64_PRINTF_ARG(chan->global_identifier), chan);
+ scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE);
}
}
}
@@ -648,7 +664,7 @@ scheduler_release_channel,(channel_t *chan))
if (the_scheduler->on_channel_free) {
the_scheduler->on_channel_free(chan);
}
- chan->scheduler_state = SCHED_CHAN_IDLE;
+ scheduler_set_channel_state(chan, SCHED_CHAN_IDLE);
}
/** Mark a channel as ready to accept writes */
@@ -668,19 +684,13 @@ scheduler_channel_wants_writes(channel_t *chan)
/*
* It can write now, so it goes to channels_pending.
*/
- log_debug(LD_SCHED, "chan=%" PRIu64 " became pending",
- chan->global_identifier);
+ scheduler_set_channel_state(chan, SCHED_CHAN_PENDING);
if (!SCHED_BUG(chan->sched_heap_idx != -1, chan)) {
smartlist_pqueue_add(channels_pending,
scheduler_compare_channels,
offsetof(channel_t, sched_heap_idx),
chan);
}
- chan->scheduler_state = SCHED_CHAN_PENDING;
- log_debug(LD_SCHED,
- "Channel " U64_FORMAT " at %p went from waiting_to_write "
- "to pending",
- U64_PRINTF_ARG(chan->global_identifier), chan);
/* We just made a channel pending, we have scheduling work to do. */
the_scheduler->schedule();
} else {
@@ -690,10 +700,7 @@ scheduler_channel_wants_writes(channel_t *chan)
*/
if (!(chan->scheduler_state == SCHED_CHAN_WAITING_FOR_CELLS ||
chan->scheduler_state == SCHED_CHAN_PENDING)) {
- chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS;
- log_debug(LD_SCHED,
- "Channel " U64_FORMAT " at %p entered waiting_for_cells",
- U64_PRINTF_ARG(chan->global_identifier), chan);
+ scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS);
}
}
}
@@ -710,11 +717,12 @@ scheduler_bug_occurred(const channel_t *chan)
const size_t outbuf_len =
buf_datalen(TO_CONN(BASE_CHAN_TO_TLS((channel_t *) chan)->conn)->outbuf);
tor_snprintf(buf, sizeof(buf),
- "Channel %" PRIu64 " in state %s and scheduler state %d."
+ "Channel %" PRIu64 " in state %s and scheduler state %s."
" Num cells on cmux: %d. Connection outbuf len: %lu.",
chan->global_identifier,
channel_state_to_string(chan->state),
- chan->scheduler_state, circuitmux_num_cells(chan->cmux),
+ get_scheduler_state_string(chan->scheduler_state),
+ circuitmux_num_cells(chan->cmux),
(unsigned long)outbuf_len);
}
diff --git a/src/or/scheduler.h b/src/or/scheduler.h
index 559f1c8afc..aeba9e2b75 100644
--- a/src/or/scheduler.h
+++ b/src/or/scheduler.h
@@ -142,6 +142,9 @@ MOCK_DECL(void, scheduler_channel_has_waiting_cells, (channel_t *chan));
* Defined in scheduler.c
*********************************/
+void scheduler_set_channel_state(channel_t *chan, int new_state);
+const char *get_scheduler_state_string(int scheduler_state);
+
/* Triggers a BUG() and extra information with chan if available. */
#define SCHED_BUG(cond, chan) \
(PREDICT_UNLIKELY(cond) ? \
diff --git a/src/or/scheduler_kist.c b/src/or/scheduler_kist.c
index c79b413b88..6d6490077d 100644
--- a/src/or/scheduler_kist.c
+++ b/src/or/scheduler_kist.c
@@ -373,7 +373,7 @@ set_scheduler_run_interval(void)
}
}
-/* Return true iff the channel hasn’t hit its kist-imposed write limit yet */
+/* Return true iff the channel hasn't hit its kist-imposed write limit yet */
static int
socket_can_write(socket_table_t *table, const channel_t *chan)
{
@@ -474,7 +474,7 @@ kist_free_all(void)
/* Function of the scheduler interface: on_channel_free() */
static void
-kist_on_channel_free(const channel_t *chan)
+kist_on_channel_free_fn(const channel_t *chan)
{
free_socket_info_by_chan(&socket_table, chan);
}
@@ -620,7 +620,7 @@ kist_scheduler_run(void)
if (!CHANNEL_IS_OPEN(chan)) {
/* Channel isn't open so we put it back in IDLE mode. It is either
* renegotiating its TLS session or about to be released. */
- chan->scheduler_state = SCHED_CHAN_IDLE;
+ scheduler_set_channel_state(chan, SCHED_CHAN_IDLE);
continue;
}
/* flush_result has the # cells flushed */
@@ -636,12 +636,12 @@ kist_scheduler_run(void)
log_debug(LD_SCHED,
"We didn't flush anything on a chan that we think "
"can write and wants to write. The channel's state is '%s' "
- "and in scheduler state %d. We're going to mark it as "
+ "and in scheduler state '%s'. We're going to mark it as "
"waiting_for_cells (as that's most likely the issue) and "
"stop scheduling it this round.",
channel_state_to_string(chan->state),
- chan->scheduler_state);
- chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS;
+ get_scheduler_state_string(chan->scheduler_state));
+ scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS);
continue;
}
}
@@ -668,16 +668,12 @@ kist_scheduler_run(void)
* SCHED_CHAN_WAITING_FOR_CELLS to SCHED_CHAN_IDLE and seeing if Tor
* starts having serious throughput issues. Best done in shadow/chutney.
*/
- chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS;
- log_debug(LD_SCHED, "chan=%" PRIu64 " now waiting_for_cells",
- chan->global_identifier);
+ scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS);
} else if (!channel_more_to_flush(chan)) {
/* Case 2: no more cells to send, but still open for writes */
- chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS;
- log_debug(LD_SCHED, "chan=%" PRIu64 " now waiting_for_cells",
- chan->global_identifier);
+ scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS);
} else if (!socket_can_write(&socket_table, chan)) {
/* Case 3: cells to send, but cannot write */
@@ -693,13 +689,11 @@ kist_scheduler_run(void)
to_readd = smartlist_new();
}
smartlist_add(to_readd, chan);
- log_debug(LD_SCHED, "chan=%" PRIu64 " now waiting_to_write",
- chan->global_identifier);
} else {
/* Case 4: cells to send, and still open for writes */
- chan->scheduler_state = SCHED_CHAN_PENDING;
+ scheduler_set_channel_state(chan, SCHED_CHAN_PENDING);
if (!SCHED_BUG(chan->sched_heap_idx != -1, chan)) {
smartlist_pqueue_add(cp, scheduler_compare_channels,
offsetof(channel_t, sched_heap_idx), chan);
@@ -721,7 +715,7 @@ kist_scheduler_run(void)
/* Re-add any channels we need to */
if (to_readd) {
SMARTLIST_FOREACH_BEGIN(to_readd, channel_t *, readd_chan) {
- readd_chan->scheduler_state = SCHED_CHAN_PENDING;
+ scheduler_set_channel_state(readd_chan, SCHED_CHAN_PENDING);
if (!smartlist_contains(cp, readd_chan)) {
if (!SCHED_BUG(chan->sched_heap_idx != -1, chan)) {
/* XXXX Note that the check above is in theory redundant with
@@ -746,7 +740,7 @@ kist_scheduler_run(void)
static scheduler_t kist_scheduler = {
.type = SCHEDULER_KIST,
.free_all = kist_free_all,
- .on_channel_free = kist_on_channel_free,
+ .on_channel_free = kist_on_channel_free_fn,
.init = kist_scheduler_init,
.on_new_consensus = kist_scheduler_on_new_consensus,
.schedule = kist_scheduler_schedule,
diff --git a/src/or/scheduler_vanilla.c b/src/or/scheduler_vanilla.c
index 303b3dbba8..7a83b9da18 100644
--- a/src/or/scheduler_vanilla.c
+++ b/src/or/scheduler_vanilla.c
@@ -89,12 +89,7 @@ vanilla_scheduler_run(void)
if (flushed < n_cells) {
/* We ran out of cells to flush */
- chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS;
- log_debug(LD_SCHED,
- "Channel " U64_FORMAT " at %p "
- "entered waiting_for_cells from pending",
- U64_PRINTF_ARG(chan->global_identifier),
- chan);
+ scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS);
} else {
/* The channel may still have some cells */
if (channel_more_to_flush(chan)) {
@@ -110,12 +105,7 @@ vanilla_scheduler_run(void)
chan);
} else {
/* It's waiting to be able to write more */
- chan->scheduler_state = SCHED_CHAN_WAITING_TO_WRITE;
- log_debug(LD_SCHED,
- "Channel " U64_FORMAT " at %p "
- "entered waiting_to_write from pending",
- U64_PRINTF_ARG(chan->global_identifier),
- chan);
+ scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE);
}
} else {
/* No cells left; it can go to idle or waiting_for_cells */
@@ -124,23 +114,13 @@ vanilla_scheduler_run(void)
* It can still accept writes, so it goes to
* waiting_for_cells
*/
- chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS;
- log_debug(LD_SCHED,
- "Channel " U64_FORMAT " at %p "
- "entered waiting_for_cells from pending",
- U64_PRINTF_ARG(chan->global_identifier),
- chan);
+ scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS);
} else {
/*
* We exactly filled up the output queue with all available
* cells; go to idle.
*/
- chan->scheduler_state = SCHED_CHAN_IDLE;
- log_debug(LD_SCHED,
- "Channel " U64_FORMAT " at %p "
- "become idle from pending",
- U64_PRINTF_ARG(chan->global_identifier),
- chan);
+ scheduler_set_channel_state(chan, SCHED_CHAN_IDLE);
}
}
}
@@ -156,14 +136,14 @@ vanilla_scheduler_run(void)
"no cells writeable",
U64_PRINTF_ARG(chan->global_identifier), chan);
/* Put it back to WAITING_TO_WRITE */
- chan->scheduler_state = SCHED_CHAN_WAITING_TO_WRITE;
+ scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE);
}
}
/* Readd any channels we need to */
if (to_readd) {
SMARTLIST_FOREACH_BEGIN(to_readd, channel_t *, readd_chan) {
- readd_chan->scheduler_state = SCHED_CHAN_PENDING;
+ scheduler_set_channel_state(readd_chan, SCHED_CHAN_PENDING);
smartlist_pqueue_add(cp,
scheduler_compare_channels,
offsetof(channel_t, sched_heap_idx),
diff --git a/src/or/shared_random.c b/src/or/shared_random.c
index b3f62a8fd8..13416d6bc7 100644
--- a/src/or/shared_random.c
+++ b/src/or/shared_random.c
@@ -384,7 +384,7 @@ commit_encode(const sr_commit_t *commit, char *dst, size_t len)
static void
sr_cleanup(void)
{
- sr_state_free();
+ sr_state_free_all();
}
/* Using <b>commit</b>, return a newly allocated string containing the commit
@@ -897,7 +897,7 @@ sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv)
/* Free a commit object. */
void
-sr_commit_free(sr_commit_t *commit)
+sr_commit_free_(sr_commit_t *commit)
{
if (commit == NULL) {
return;
@@ -1071,7 +1071,7 @@ sr_parse_srv(const smartlist_t *args)
srv = tor_malloc_zero(sizeof(*srv));
srv->num_reveals = num_reveals;
- /* We substract one byte from the srclen because the function ignores the
+ /* We subtract one byte from the srclen because the function ignores the
* '=' character in the given buffer. This is broken but it's a documented
* behavior of the implementation. */
ret = base64_decode((char *) srv->value, sizeof(srv->value), value,
diff --git a/src/or/shared_random.h b/src/or/shared_random.h
index c0992489cb..675a8d8b06 100644
--- a/src/or/shared_random.h
+++ b/src/or/shared_random.h
@@ -113,7 +113,8 @@ sr_srv_t *sr_parse_srv(const smartlist_t *args);
char *sr_get_string_for_vote(void);
char *sr_get_string_for_consensus(const smartlist_t *votes,
int32_t num_srv_agreements);
-void sr_commit_free(sr_commit_t *commit);
+void sr_commit_free_(sr_commit_t *commit);
+#define sr_commit_free(sr) FREE_AND_NULL(sr_commit_t, sr_commit_free_, (sr))
void sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv);
/* Private methods (only used by shared_random_state.c): */
diff --git a/src/or/shared_random_state.c b/src/or/shared_random_state.c
index 812c6e4e00..7bac8e9482 100644
--- a/src/or/shared_random_state.c
+++ b/src/or/shared_random_state.c
@@ -271,12 +271,15 @@ commit_add_to_state(sr_commit_t *commit, sr_state_t *state)
static void
commit_free_(void *p)
{
- sr_commit_free(p);
+ sr_commit_free_(p);
}
+#define state_free(val) \
+ FREE_AND_NULL(sr_state_t, state_free_, (val))
+
/* Free a state that was allocated with state_new(). */
static void
-state_free(sr_state_t *state)
+state_free_(sr_state_t *state)
{
if (state == NULL) {
return;
@@ -318,9 +321,12 @@ state_set(sr_state_t *state)
sr_state = state;
}
+#define disk_state_free(val) \
+ FREE_AND_NULL(sr_disk_state_t, disk_state_free_, (val))
+
/* Free an allocated disk state. */
static void
-disk_state_free(sr_disk_state_t *state)
+disk_state_free_(sr_disk_state_t *state)
{
if (state == NULL) {
return;
@@ -1096,7 +1102,7 @@ sr_state_get_previous_srv(void)
}
/* Set the current SRV value from our state. Value CAN be NULL. The srv
- * object ownership is transfered to the state object. */
+ * object ownership is transferred to the state object. */
void
sr_state_set_previous_srv(const sr_srv_t *srv)
{
@@ -1115,7 +1121,7 @@ sr_state_get_current_srv(void)
}
/* Set the current SRV value from our state. Value CAN be NULL. The srv
- * object ownership is transfered to the state object. */
+ * object ownership is transferred to the state object. */
void
sr_state_set_current_srv(const sr_srv_t *srv)
{
@@ -1220,7 +1226,7 @@ sr_state_get_commit(const char *rsa_identity)
}
/* Add <b>commit</b> to the permanent state. The commit object ownership is
- * transfered to the state so the caller MUST not free it. */
+ * transferred to the state so the caller MUST not free it. */
void
sr_state_add_commit(sr_commit_t *commit)
{
@@ -1287,7 +1293,7 @@ sr_state_srv_is_fresh(void)
/* Cleanup and free our disk and memory state. */
void
-sr_state_free(void)
+sr_state_free_all(void)
{
state_free(sr_state);
disk_state_free(sr_disk_state);
diff --git a/src/or/shared_random_state.h b/src/or/shared_random_state.h
index 866725c435..fdbbf4919a 100644
--- a/src/or/shared_random_state.h
+++ b/src/or/shared_random_state.h
@@ -119,7 +119,7 @@ void sr_state_unset_fresh_srv(void);
int sr_state_init(int save_to_disk, int read_from_disk);
int sr_state_is_initialized(void);
void sr_state_save(void);
-void sr_state_free(void);
+void sr_state_free_all(void);
time_t sr_state_get_start_time_of_current_protocol_run(time_t now);
unsigned int sr_state_get_phase_duration(void);
diff --git a/src/or/statefile.c b/src/or/statefile.c
index 97bd9cac36..cc114f0a2b 100644
--- a/src/or/statefile.c
+++ b/src/or/statefile.c
@@ -681,7 +681,7 @@ save_transport_to_state(const char *transport,
}
STATIC void
-or_state_free(or_state_t *state)
+or_state_free_(or_state_t *state)
{
if (!state)
return;
diff --git a/src/or/statefile.h b/src/or/statefile.h
index 574afb3622..b4cc4d1dc6 100644
--- a/src/or/statefile.h
+++ b/src/or/statefile.h
@@ -20,7 +20,8 @@ void or_state_free_all(void);
#ifdef STATEFILE_PRIVATE
STATIC config_line_t *get_transport_in_state_by_name(const char *transport);
-STATIC void or_state_free(or_state_t *state);
+STATIC void or_state_free_(or_state_t *state);
+#define or_state_free(st) FREE_AND_NULL(or_state_t, or_state_free_, (st))
STATIC or_state_t *or_state_new(void);
#endif
diff --git a/src/or/status.c b/src/or/status.c
index 52763a7042..4f7be164b1 100644
--- a/src/or/status.c
+++ b/src/or/status.c
@@ -27,6 +27,8 @@
#include "hibernate.h"
#include "rephist.h"
#include "statefile.h"
+#include "hs_stats.h"
+#include "hs_service.h"
#include "dos.h"
static void log_accounting(const time_t now, const or_options_t *options);
@@ -40,7 +42,7 @@ count_circuits(void)
}
/** Take seconds <b>secs</b> and return a newly allocated human-readable
- * uptime string */
+ * uptime string. */
STATIC char *
secs_to_uptime(long secs)
{
@@ -86,6 +88,26 @@ bytes_to_usage(uint64_t bytes)
return bw_string;
}
+/** Log some usage info about our hidden service */
+static void
+log_onion_service_stats(void)
+{
+ unsigned int num_services = hs_service_get_num_services();
+
+ /* If there are no active hidden services, no need to print logs */
+ if (num_services == 0) {
+ return;
+ }
+
+ log_notice(LD_HEARTBEAT,
+ "Our hidden service%s received %u v2 and %u v3 INTRODUCE2 cells "
+ "and attempted to launch %d rendezvous circuits.",
+ num_services == 1 ? "" : "s",
+ hs_stats_get_n_introduce2_v2_cells(),
+ hs_stats_get_n_introduce2_v3_cells(),
+ hs_stats_get_n_rendezvous_launches());
+}
+
/** 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. */
@@ -159,6 +181,23 @@ log_heartbeat(time_t now)
tor_free(msg);
}
+ if (options->MainloopStats) {
+ const uint64_t main_loop_success_count = get_main_loop_success_count();
+ const uint64_t main_loop_error_count = get_main_loop_error_count();
+ const uint64_t main_loop_idle_count = get_main_loop_idle_count();
+
+ log_fn(LOG_NOTICE, LD_HEARTBEAT, "Main event loop statistics: "
+ U64_FORMAT " successful returns, "
+ U64_FORMAT " erroneous returns, and "
+ U64_FORMAT " idle returns.",
+ U64_PRINTF_ARG(main_loop_success_count),
+ U64_PRINTF_ARG(main_loop_error_count),
+ U64_PRINTF_ARG(main_loop_idle_count));
+ }
+
+ /** Now, if we are an HS service, log some stats about our usage */
+ log_onion_service_stats();
+
tor_free(uptime);
tor_free(bw_sent);
tor_free(bw_rcvd);
diff --git a/src/or/tor_api.c b/src/or/tor_api.c
new file mode 100644
index 0000000000..4260cc88f4
--- /dev/null
+++ b/src/or/tor_api.c
@@ -0,0 +1,88 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file tor_api.c
+ **/
+
+#include "tor_api.h"
+#include "tor_api_internal.h"
+
+// Include this after the above headers, to insure that they don't
+// depend on anything else.
+#include "orconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// We don't want to use tor_malloc and tor_free here, since this needs
+// to run before anything is initialized at all, and ought to run when
+// we're not linked to anything at all.
+
+#define raw_malloc malloc
+#define raw_free free
+
+tor_main_configuration_t *
+tor_main_configuration_new(void)
+{
+ static const char *fake_argv[] = { "tor" };
+ tor_main_configuration_t *cfg = raw_malloc(sizeof(*cfg));
+ if (cfg == NULL)
+ return NULL;
+
+ memset(cfg, 0, sizeof(*cfg));
+
+ cfg->argc = 1;
+ cfg->argv = (char **) fake_argv;
+
+ return cfg;
+}
+
+int
+tor_main_configuration_set_command_line(tor_main_configuration_t *cfg,
+ int argc, char *argv[])
+{
+ if (cfg == NULL)
+ return -1;
+ cfg->argc = argc;
+ cfg->argv = argv;
+ return 0;
+}
+
+void
+tor_main_configuration_free(tor_main_configuration_t *cfg)
+{
+ if (cfg == NULL)
+ return;
+ raw_free(cfg);
+}
+
+/* Main entry point for the Tor process. Called from main().
+ *
+ * This function is distinct from main() only so we can link main.c into
+ * the unittest binary without conflicting with the unittests' main.
+ *
+ * Some embedders have historically called this function; but that usage is
+ * deprecated: they should use tor_run_main() instead.
+ */
+int
+tor_main(int argc, char *argv[])
+{
+ tor_main_configuration_t *cfg = tor_main_configuration_new();
+ if (!cfg) {
+ puts("INTERNAL ERROR: Allocation failure. Cannot proceed");
+ return 1;
+ }
+ if (tor_main_configuration_set_command_line(cfg, argc, argv) < 0) {
+ puts("INTERNAL ERROR: Can't set command line. Cannot proceed.");
+ return 1;
+ }
+ int rv = tor_run_main(cfg);
+ tor_main_configuration_free(cfg);
+ return rv;
+}
+
diff --git a/src/or/tor_api.h b/src/or/tor_api.h
new file mode 100644
index 0000000000..6d4a9518e0
--- /dev/null
+++ b/src/or/tor_api.h
@@ -0,0 +1,101 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file tor_api.h
+ * \brief Public C API for the Tor network service.
+ *
+ * This interface is intended for use by programs that need to link Tor as
+ * a library, and launch it in a separate thread. If you have the ability
+ * to run Tor as a separate executable, you should probably do that instead
+ * of embedding it as a library.
+ *
+ * To use this API, first construct a tor_main_configuration_t object using
+ * tor_main_configuration_new(). Then, you use one or more other function
+ * calls (such as tor_main_configuration_set_command_line() to configure how
+ * Tor should be run. Finally, you pass the configuration object to
+ * tor_run_main().
+ *
+ * At this point, tor_run_main() will block its thread to run a Tor daemon;
+ * when the Tor daemon exits, it will return. See notes on bugs and
+ * limitations below.
+ *
+ * There is no other public C API to Tor: calling any C Tor function not
+ * documented in this file is not guaranteed to be stable.
+ **/
+
+#ifndef TOR_API_H
+#define TOR_API_H
+
+typedef struct tor_main_configuration_t tor_main_configuration_t;
+
+/**
+ * Create and return a new tor_main_configuration().
+ */
+tor_main_configuration_t *tor_main_configuration_new(void);
+
+/**
+ * Set the command-line arguments in <b>cfg</b>.
+ *
+ * The <b>argc</b> and <b>argv</b> values here are as for main(). The
+ * contents of the argv pointer must remain unchanged until tor_run_main() has
+ * finished and you call tor_main_configuration_free().
+ *
+ * Return 0 on success, -1 on failure.
+ */
+int tor_main_configuration_set_command_line(tor_main_configuration_t *cfg,
+ int argc, char *argv[]);
+
+/**
+ * Release all storage held in <b>cfg</b>.
+ *
+ * Once you have passed a tor_main_configuration_t to tor_run_main(), you
+ * must not free it until tor_run_main() has finished.
+ */
+void tor_main_configuration_free(tor_main_configuration_t *cfg);
+
+/**
+ * Run the tor process, as if from the command line.
+ *
+ * The command line arguments from tor_main_configuration_set_command_line()
+ * are taken as if they had been passed to main().
+ *
+ * This function will not return until Tor is done running. It returns zero
+ * on success, and nonzero on failure.
+ *
+ * If you want to control when Tor exits, make sure to configure a control
+ * socket. The OwningControllerFD option may be helpful there.
+ *
+ * BUG 23847: Sometimes, if you call tor_main a second time (after it has
+ * returned), Tor may crash or behave strangely. We have fixed all issues of
+ * this type that we could find, but more may remain.
+ *
+ * LIMITATION: You cannot run more than one instance of Tor in the same
+ * process at the same time. Concurrent calls will cause undefined behavior.
+ * We do not currently have plans to change this.
+ *
+ * LIMITATION: While we will try to fix any problems found here, you
+ * should be aware that Tor was originally written to run as its own
+ * process, and that the functionality of this file was added later. If
+ * you find any bugs or strange behavior, please report them, and we'll
+ * try to straighten them out.
+ */
+int tor_run_main(const tor_main_configuration_t *);
+
+/**
+ * Run the tor process, as if from the command line.
+ *
+ * @deprecated Using this function from outside Tor is deprecated; you should
+ * use tor_run_main() instead.
+ *
+ * BUGS: This function has all the same bugs as tor_run_main().
+ *
+ * LIMITATIONS: This function has all the limitations of tor_run_main().
+ */
+int tor_main(int argc, char **argv);
+
+#endif /* !defined(TOR_API_H) */
+
diff --git a/src/or/tor_api_internal.h b/src/or/tor_api_internal.h
new file mode 100644
index 0000000000..10b6278b7b
--- /dev/null
+++ b/src/or/tor_api_internal.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_API_INTERNAL_H
+#define TOR_API_INTERNAL_H
+
+/* The contents of this type are private; don't mess with them from outside
+ * Tor. */
+struct tor_main_configuration_t {
+ /** As in main() */
+ int argc;
+ /** As in main(). This pointer is owned by the caller */
+ char **argv;
+};
+
+#endif /* !defined(TOR_API_INTERNAL_H) */
+
diff --git a/src/or/tor_main.c b/src/or/tor_main.c
index a3a8838602..703669ac99 100644
--- a/src/or/tor_main.c
+++ b/src/or/tor_main.c
@@ -3,17 +3,10 @@
* Copyright (c) 2007-2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
-extern const char tor_git_revision[];
-
-/** String describing which Tor Git repository version the source was
- * built from. This string is generated by a bit of shell kludging in
- * src/or/include.am, and is usually right.
- */
-const char tor_git_revision[] =
-#ifndef _MSC_VER
-#include "micro-revision.i"
+#include "orconfig.h"
+#ifdef ENABLE_RESTART_DEBUGGING
+#include <stdlib.h>
#endif
- "";
/**
* \file tor_main.c
@@ -26,14 +19,23 @@ const char tor_git_revision[] =
int tor_main(int argc, char *argv[]);
/** We keep main() in a separate file so that our unit tests can use
- * functions from main.c)
+ * functions from main.c.
*/
int
main(int argc, char *argv[])
{
- int r = tor_main(argc, argv);
+ int r;
+#ifdef ENABLE_RESTART_DEBUGGING
+ int restart_count = getenv("TOR_DEBUG_RESTART") ? 1 : 0;
+ again:
+#endif
+ r = tor_main(argc, argv);
if (r < 0 || r > 255)
return 1;
+#ifdef ENABLE_RESTART_DEBUGGING
+ else if (r == 0 && restart_count--)
+ goto again;
+#endif
else
return r;
}
diff --git a/src/or/torcert.c b/src/or/torcert.c
index 212534d311..51935ddf72 100644
--- a/src/or/torcert.c
+++ b/src/or/torcert.c
@@ -138,7 +138,7 @@ tor_cert_create(const ed25519_keypair_t *signing_key,
/** Release all storage held for <b>cert</b>. */
void
-tor_cert_free(tor_cert_t *cert)
+tor_cert_free_(tor_cert_t *cert)
{
if (! cert)
return;
@@ -472,7 +472,7 @@ or_handshake_certs_new(void)
/** Release all storage held in <b>certs</b> */
void
-or_handshake_certs_free(or_handshake_certs_t *certs)
+or_handshake_certs_free_(or_handshake_certs_t *certs)
{
if (!certs)
return;
diff --git a/src/or/torcert.h b/src/or/torcert.h
index ac227db209..18ca60b5a8 100644
--- a/src/or/torcert.h
+++ b/src/or/torcert.h
@@ -57,7 +57,8 @@ tor_cert_t *tor_cert_create(const ed25519_keypair_t *signing_key,
tor_cert_t *tor_cert_parse(const uint8_t *cert, size_t certlen);
-void tor_cert_free(tor_cert_t *cert);
+void tor_cert_free_(tor_cert_t *cert);
+#define tor_cert_free(cert) FREE_AND_NULL(tor_cert_t, tor_cert_free_, (cert))
int tor_cert_get_checkable_sig(ed25519_checkable_t *checkable_out,
const tor_cert_t *out,
@@ -84,7 +85,9 @@ rsa_ed25519_crosscert_check, (const uint8_t *crosscert,
const time_t reject_if_expired_before));
or_handshake_certs_t *or_handshake_certs_new(void);
-void or_handshake_certs_free(or_handshake_certs_t *certs);
+void or_handshake_certs_free_(or_handshake_certs_t *certs);
+#define or_handshake_certs_free(certs) \
+ FREE_AND_NULL(or_handshake_certs_t, or_handshake_certs_free_, (certs))
int or_handshake_certs_rsa_ok(int severity,
or_handshake_certs_t *certs,
tor_tls_t *tls,
diff --git a/src/or/transports.c b/src/or/transports.c
index 5fb24e11a0..b08dcd1613 100644
--- a/src/or/transports.c
+++ b/src/or/transports.c
@@ -154,7 +154,7 @@ transport_new(const tor_addr_t *addr, uint16_t port,
/** Free the pluggable transport struct <b>transport</b>. */
void
-transport_free(transport_t *transport)
+transport_free_(transport_t *transport)
{
if (!transport)
return;
@@ -590,7 +590,7 @@ pt_configure_remaining_proxies(void)
}
/* If the proxy is not fully configured, try to configure it
- futher. */
+ further. */
if (!proxy_configuration_finished(mp))
if (configure_proxy(mp) == 1)
at_least_a_proxy_config_finished = 1;
diff --git a/src/or/transports.h b/src/or/transports.h
index e368e447c3..1b2786472c 100644
--- a/src/or/transports.h
+++ b/src/or/transports.h
@@ -35,7 +35,8 @@ void sweep_transport_list(void);
MOCK_DECL(int, transport_add_from_config,
(const tor_addr_t *addr, uint16_t port,
const char *name, int socks_ver));
-void transport_free(transport_t *transport);
+void transport_free_(transport_t *transport);
+#define transport_free(tr) FREE_AND_NULL(transport_t, transport_free_, (tr))
transport_t *transport_get_by_name(const char *name);
diff --git a/src/rust/.cargo/config.in b/src/rust/.cargo/config.in
index 414b253a57..301e7fdbe7 100644
--- a/src/rust/.cargo/config.in
+++ b/src/rust/.cargo/config.in
@@ -5,4 +5,4 @@
@RUST_DL@ replace-with = 'vendored-sources'
@RUST_DL@ [source.vendored-sources]
-@RUST_DL@ directory = '@RUST_DEPENDENCIES@'
+@RUST_DL@ directory = '@TOR_RUST_DEPENDENCIES@'
diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock
index e208e14320..4f918c0221 100644
--- a/src/rust/Cargo.lock
+++ b/src/rust/Cargo.lock
@@ -1,14 +1,55 @@
[[package]]
+name = "external"
+version = "0.0.1"
+dependencies = [
+ "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "libc"
-version = "0.2.22"
+version = "0.2.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "protover"
+version = "0.0.1"
+dependencies = [
+ "external 0.0.1",
+ "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
+ "smartlist 0.0.1",
+ "tor_allocate 0.0.1",
+ "tor_util 0.0.1",
+]
+
+[[package]]
+name = "smartlist"
+version = "0.0.1"
+dependencies = [
+ "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tor_allocate"
+version = "0.0.1"
+dependencies = [
+ "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tor_rust"
+version = "0.1.0"
+dependencies = [
+ "protover 0.0.1",
+ "tor_util 0.0.1",
+]
+
+[[package]]
name = "tor_util"
version = "0.0.1"
dependencies = [
- "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tor_allocate 0.0.1",
]
[metadata]
-"checksum libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)" = "babb8281da88cba992fa1f4ddec7d63ed96280a1a53ec9b919fd37b53d71e502"
+"checksum libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "f54263ad99207254cf58b5f701ecb432c717445ea2ee8af387334bdd1a03fdff"
diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml
index fc4377e8b4..953c9b96b7 100644
--- a/src/rust/Cargo.toml
+++ b/src/rust/Cargo.toml
@@ -1,5 +1,5 @@
[workspace]
-members = ["tor_util"]
+members = ["tor_util", "protover", "smartlist", "external", "tor_allocate", "tor_rust"]
[profile.release]
debug = true
diff --git a/src/rust/external/Cargo.toml b/src/rust/external/Cargo.toml
new file mode 100644
index 0000000000..b5957b1079
--- /dev/null
+++ b/src/rust/external/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+authors = ["The Tor Project"]
+version = "0.0.1"
+name = "external"
+
+[dependencies]
+libc = "=0.2.39"
+
+[lib]
+name = "external"
+path = "lib.rs"
+crate_type = ["rlib", "staticlib"]
+
diff --git a/src/rust/external/external.rs b/src/rust/external/external.rs
new file mode 100644
index 0000000000..b9e17f021d
--- /dev/null
+++ b/src/rust/external/external.rs
@@ -0,0 +1,33 @@
+// Copyright (c) 2016-2017, The Tor Project, Inc. */
+// See LICENSE for licensing information */
+
+use libc::{c_char, c_int};
+use std::ffi::CString;
+
+extern "C" {
+ fn tor_version_as_new_as(
+ platform: *const c_char,
+ cutoff: *const c_char,
+ ) -> c_int;
+}
+
+/// Wrap calls to tor_version_as_new_as, defined in src/or/routerparse.c
+pub fn c_tor_version_as_new_as(platform: &str, cutoff: &str) -> bool {
+ // CHK: These functions should log a warning if an error occurs. This
+ // can be added when integration with tor's logger is added to rust
+ let c_platform = match CString::new(platform) {
+ Ok(n) => n,
+ Err(_) => return false,
+ };
+
+ let c_cutoff = match CString::new(cutoff) {
+ Ok(n) => n,
+ Err(_) => return false,
+ };
+
+ let result: c_int = unsafe {
+ tor_version_as_new_as(c_platform.as_ptr(), c_cutoff.as_ptr())
+ };
+
+ result == 1
+}
diff --git a/src/rust/external/lib.rs b/src/rust/external/lib.rs
new file mode 100644
index 0000000000..0af0d6452d
--- /dev/null
+++ b/src/rust/external/lib.rs
@@ -0,0 +1,14 @@
+//! Copyright (c) 2016-2017, The Tor Project, Inc. */
+//! See LICENSE for licensing information */
+
+//! Interface for external calls to tor C ABI
+//!
+//! The purpose of this module is to provide a clean interface for when Rust
+//! modules need to interact with functionality in tor C code rather than each
+//! module implementing this functionality repeatedly.
+
+extern crate libc;
+
+mod external;
+
+pub use external::*;
diff --git a/src/rust/include.am b/src/rust/include.am
index 20afc6c4db..7a0181e373 100644
--- a/src/rust/include.am
+++ b/src/rust/include.am
@@ -1,6 +1,29 @@
-include src/rust/tor_util/include.am
+include src/rust/tor_rust/include.am
EXTRA_DIST +=\
src/rust/Cargo.toml \
src/rust/Cargo.lock \
- src/rust/.cargo/config.in
+ src/rust/.cargo/config.in \
+ src/rust/external/Cargo.toml \
+ src/rust/external/external.rs \
+ src/rust/external/lib.rs \
+ src/rust/protover/Cargo.toml \
+ src/rust/protover/errors.rs \
+ src/rust/protover/protoset.rs \
+ src/rust/protover/ffi.rs \
+ src/rust/protover/lib.rs \
+ src/rust/protover/protover.rs \
+ src/rust/protover/tests/protover.rs \
+ src/rust/smartlist/Cargo.toml \
+ src/rust/smartlist/lib.rs \
+ src/rust/smartlist/smartlist.rs \
+ src/rust/tor_allocate/Cargo.toml \
+ src/rust/tor_allocate/lib.rs \
+ src/rust/tor_allocate/tor_allocate.rs \
+ src/rust/tor_rust/Cargo.toml \
+ src/rust/tor_rust/include.am \
+ src/rust/tor_rust/lib.rs \
+ src/rust/tor_util/Cargo.toml \
+ src/rust/tor_util/ffi.rs \
+ src/rust/tor_util/lib.rs \
+ src/rust/tor_util/strings.rs
diff --git a/src/rust/protover/Cargo.toml b/src/rust/protover/Cargo.toml
new file mode 100644
index 0000000000..86301b8787
--- /dev/null
+++ b/src/rust/protover/Cargo.toml
@@ -0,0 +1,25 @@
+[package]
+authors = ["The Tor Project"]
+version = "0.0.1"
+name = "protover"
+
+[dependencies]
+libc = "=0.2.39"
+
+[dependencies.smartlist]
+path = "../smartlist"
+
+[dependencies.external]
+path = "../external"
+
+[dependencies.tor_util]
+path = "../tor_util"
+
+[dependencies.tor_allocate]
+path = "../tor_allocate"
+
+[lib]
+name = "protover"
+path = "lib.rs"
+crate_type = ["rlib", "staticlib"]
+
diff --git a/src/rust/protover/errors.rs b/src/rust/protover/errors.rs
new file mode 100644
index 0000000000..56473d12e6
--- /dev/null
+++ b/src/rust/protover/errors.rs
@@ -0,0 +1,43 @@
+// Copyright (c) 2018, The Tor Project, Inc.
+// Copyright (c) 2018, isis agora lovecruft
+// See LICENSE for licensing information
+
+//! Various errors which may occur during protocol version parsing.
+
+use std::fmt;
+use std::fmt::Display;
+
+/// All errors which may occur during protover parsing routines.
+#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
+#[allow(missing_docs)] // See Display impl for error descriptions
+pub enum ProtoverError {
+ Overlap,
+ LowGreaterThanHigh,
+ Unparseable,
+ ExceedsMax,
+ ExceedsExpansionLimit,
+ UnknownProtocol,
+ ExceedsNameLimit,
+}
+
+/// Descriptive error messages for `ProtoverError` variants.
+impl Display for ProtoverError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ ProtoverError::Overlap
+ => write!(f, "Two or more (low, high) protover ranges would overlap once expanded."),
+ ProtoverError::LowGreaterThanHigh
+ => write!(f, "The low in a (low, high) protover range was greater than high."),
+ ProtoverError::Unparseable
+ => write!(f, "The protover string was unparseable."),
+ ProtoverError::ExceedsMax
+ => write!(f, "The high in a (low, high) protover range exceeds u32::MAX."),
+ ProtoverError::ExceedsExpansionLimit
+ => write!(f, "The protover string would exceed the maximum expansion limit."),
+ ProtoverError::UnknownProtocol
+ => write!(f, "A protocol in the protover string we attempted to parse is unknown."),
+ ProtoverError::ExceedsNameLimit
+ => write!(f, "An unrecognised protocol name was too long."),
+ }
+ }
+}
diff --git a/src/rust/protover/ffi.rs b/src/rust/protover/ffi.rs
new file mode 100644
index 0000000000..9656e8c318
--- /dev/null
+++ b/src/rust/protover/ffi.rs
@@ -0,0 +1,296 @@
+// Copyright (c) 2016-2017, The Tor Project, Inc. */
+// See LICENSE for licensing information */
+
+//! FFI functions, only to be called from C.
+//!
+//! Equivalent C versions of this api are in `src/or/protover.c`
+
+use libc::{c_char, c_int, uint32_t};
+use std::ffi::CStr;
+use std::ffi::CString;
+
+use smartlist::*;
+use tor_allocate::allocate_and_copy_string;
+use tor_util::strings::byte_slice_is_c_like;
+use tor_util::strings::empty_static_cstr;
+
+
+use errors::ProtoverError;
+use protover::*;
+
+/// Translate C enums to Rust Proto enums, using the integer value of the C
+/// enum to map to its associated Rust enum.
+///
+/// C_RUST_COUPLED: src/or/protover.h `protocol_type_t`
+fn translate_to_rust(c_proto: uint32_t) -> Result<Protocol, ProtoverError> {
+ match c_proto {
+ 0 => Ok(Protocol::Link),
+ 1 => Ok(Protocol::LinkAuth),
+ 2 => Ok(Protocol::Relay),
+ 3 => Ok(Protocol::DirCache),
+ 4 => Ok(Protocol::HSDir),
+ 5 => Ok(Protocol::HSIntro),
+ 6 => Ok(Protocol::HSRend),
+ 7 => Ok(Protocol::Desc),
+ 8 => Ok(Protocol::Microdesc),
+ 9 => Ok(Protocol::Cons),
+ _ => Err(ProtoverError::UnknownProtocol),
+ }
+}
+
+/// Provide an interface for C to translate arguments and return types for
+/// protover::all_supported
+#[no_mangle]
+pub extern "C" fn protover_all_supported(
+ c_relay_version: *const c_char,
+ missing_out: *mut *mut c_char,
+) -> c_int {
+
+ if c_relay_version.is_null() {
+ return 1;
+ }
+
+ // Require an unsafe block to read the version from a C string. The pointer
+ // is checked above to ensure it is not null.
+ let c_str: &CStr = unsafe { CStr::from_ptr(c_relay_version) };
+
+ let relay_version = match c_str.to_str() {
+ Ok(n) => n,
+ Err(_) => return 1,
+ };
+
+ let relay_proto_entry: UnvalidatedProtoEntry =
+ match UnvalidatedProtoEntry::from_str_any_len(relay_version) {
+ Ok(n) => n,
+ Err(_) => return 1,
+ };
+ let maybe_unsupported: Option<UnvalidatedProtoEntry> = relay_proto_entry.all_supported();
+
+ if maybe_unsupported.is_some() {
+ let unsupported: UnvalidatedProtoEntry = maybe_unsupported.unwrap();
+ let c_unsupported: CString = match CString::new(unsupported.to_string()) {
+ Ok(n) => n,
+ Err(_) => return 1,
+ };
+
+ let ptr = c_unsupported.into_raw();
+ unsafe { *missing_out = ptr };
+
+ return 0;
+ }
+
+ 1
+}
+
+/// Provide an interface for C to translate arguments and return types for
+/// protover::list_supports_protocol
+#[no_mangle]
+pub extern "C" fn protocol_list_supports_protocol(
+ c_protocol_list: *const c_char,
+ c_protocol: uint32_t,
+ version: uint32_t,
+) -> c_int {
+ if c_protocol_list.is_null() {
+ return 1;
+ }
+
+ // Require an unsafe block to read the version from a C string. The pointer
+ // is checked above to ensure it is not null.
+ let c_str: &CStr = unsafe { CStr::from_ptr(c_protocol_list) };
+
+ let protocol_list = match c_str.to_str() {
+ Ok(n) => n,
+ Err(_) => return 1,
+ };
+ let proto_entry: UnvalidatedProtoEntry = match protocol_list.parse() {
+ Ok(n) => n,
+ Err(_) => return 0,
+ };
+ let protocol: UnknownProtocol = match translate_to_rust(c_protocol) {
+ Ok(n) => n.into(),
+ Err(_) => return 0,
+ };
+ match proto_entry.supports_protocol(&protocol, &version) {
+ false => return 0,
+ true => return 1,
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn protover_contains_long_protocol_names_(
+ c_protocol_list: *const c_char
+) -> c_int {
+ if c_protocol_list.is_null() {
+ return 1;
+ }
+
+ // Require an unsafe block to read the version from a C string. The pointer
+ // is checked above to ensure it is not null.
+ let c_str: &CStr = unsafe { CStr::from_ptr(c_protocol_list) };
+
+ let protocol_list = match c_str.to_str() {
+ Ok(n) => n,
+ Err(_) => return 1
+ };
+
+ let protocol_entry : Result<UnvalidatedProtoEntry,_> =
+ protocol_list.parse();
+
+ match protocol_entry {
+ Ok(_) => 0,
+ Err(_) => 1,
+ }
+}
+
+/// Provide an interface for C to translate arguments and return types for
+/// protover::list_supports_protocol_or_later
+#[no_mangle]
+pub extern "C" fn protocol_list_supports_protocol_or_later(
+ c_protocol_list: *const c_char,
+ c_protocol: uint32_t,
+ version: uint32_t,
+) -> c_int {
+ if c_protocol_list.is_null() {
+ return 1;
+ }
+
+ // Require an unsafe block to read the version from a C string. The pointer
+ // is checked above to ensure it is not null.
+ let c_str: &CStr = unsafe { CStr::from_ptr(c_protocol_list) };
+
+ let protocol_list = match c_str.to_str() {
+ Ok(n) => n,
+ Err(_) => return 1,
+ };
+
+ let protocol = match translate_to_rust(c_protocol) {
+ Ok(n) => n,
+ Err(_) => return 0,
+ };
+
+ let proto_entry: UnvalidatedProtoEntry = match protocol_list.parse() {
+ Ok(n) => n,
+ Err(_) => return 1,
+ };
+
+ if proto_entry.supports_protocol_or_later(&protocol.into(), &version) {
+ return 1;
+ }
+ 0
+}
+
+/// Provide an interface for C to translate arguments and return types for
+/// protover::get_supported_protocols
+#[no_mangle]
+pub extern "C" fn protover_get_supported_protocols() -> *const c_char {
+ let supported: &'static CStr;
+
+ // If we're going to pass it to C, there cannot be any intermediate NUL
+ // bytes. An assert is okay here, since changing the const byte slice
+ // in protover.rs to contain a NUL byte somewhere in the middle would be a
+ // programming error.
+ assert!(byte_slice_is_c_like(SUPPORTED_PROTOCOLS));
+
+ // It's okay to unwrap the result of this function because
+ // we can see that the bytes we're passing into it 1) are valid UTF-8,
+ // 2) have no intermediate NUL bytes, and 3) are terminated with a NUL
+ // byte.
+ supported = CStr::from_bytes_with_nul(SUPPORTED_PROTOCOLS).unwrap();
+
+ supported.as_ptr()
+}
+
+/// Provide an interface for C to translate arguments and return types for
+/// protover::compute_vote
+//
+// Why is the threshold a signed integer? —isis
+#[no_mangle]
+pub extern "C" fn protover_compute_vote(
+ list: *const Stringlist,
+ threshold: c_int,
+ allow_long_proto_names: bool,
+) -> *mut c_char {
+
+ if list.is_null() {
+ let empty = String::new();
+ return allocate_and_copy_string(&empty);
+ }
+
+ // Dereference of raw pointer requires an unsafe block. The pointer is
+ // checked above to ensure it is not null.
+ let data: Vec<String> = unsafe { (*list).get_list() };
+ let hold: usize = threshold as usize;
+ let mut proto_entries: Vec<UnvalidatedProtoEntry> = Vec::new();
+
+ for datum in data {
+ let entry: UnvalidatedProtoEntry = match allow_long_proto_names {
+ true => match UnvalidatedProtoEntry::from_str_any_len(datum.as_str()) {
+ Ok(n) => n,
+ Err(_) => continue},
+ false => match datum.parse() {
+ Ok(n) => n,
+ Err(_) => continue},
+ };
+ proto_entries.push(entry);
+ }
+ let vote: UnvalidatedProtoEntry = ProtoverVote::compute(&proto_entries, &hold);
+
+ allocate_and_copy_string(&vote.to_string())
+}
+
+/// Provide an interface for C to translate arguments and return types for
+/// protover::is_supported_here
+#[no_mangle]
+pub extern "C" fn protover_is_supported_here(
+ c_protocol: uint32_t,
+ version: uint32_t,
+) -> c_int {
+ let protocol = match translate_to_rust(c_protocol) {
+ Ok(n) => n,
+ Err(_) => return 0,
+ };
+
+ let is_supported = is_supported_here(&protocol, &version);
+
+ return if is_supported { 1 } else { 0 };
+}
+
+/// Provide an interface for C to translate arguments and return types for
+/// protover::compute_for_old_tor
+#[no_mangle]
+pub extern "C" fn protover_compute_for_old_tor(version: *const c_char) -> *const c_char {
+ let supported: &'static CStr;
+ let elder_protocols: &'static [u8];
+ let empty: &'static CStr;
+
+ empty = empty_static_cstr();
+
+ if version.is_null() {
+ return empty.as_ptr();
+ }
+
+ // Require an unsafe block to read the version from a C string. The pointer
+ // is checked above to ensure it is not null.
+ let c_str: &CStr = unsafe { CStr::from_ptr(version) };
+
+ let version = match c_str.to_str() {
+ Ok(n) => n,
+ Err(_) => return empty.as_ptr(),
+ };
+
+ elder_protocols = compute_for_old_tor_cstr(&version);
+
+ // If we're going to pass it to C, there cannot be any intermediate NUL
+ // bytes. An assert is okay here, since changing the const byte slice
+ // in protover.rs to contain a NUL byte somewhere in the middle would be a
+ // programming error.
+ assert!(byte_slice_is_c_like(elder_protocols));
+
+ // It's okay to unwrap the result of this function because
+ // we can see that the bytes we're passing into it 1) are valid UTF-8,
+ // 2) have no intermediate NUL bytes, and 3) are terminated with a NUL
+ // byte.
+ supported = CStr::from_bytes_with_nul(elder_protocols).unwrap_or(empty);
+
+ supported.as_ptr()
+}
diff --git a/src/rust/protover/lib.rs b/src/rust/protover/lib.rs
new file mode 100644
index 0000000000..483260bca8
--- /dev/null
+++ b/src/rust/protover/lib.rs
@@ -0,0 +1,38 @@
+//! Copyright (c) 2016-2017, The Tor Project, Inc. */
+//! See LICENSE for licensing information */
+
+//! Versioning information for different pieces of the Tor protocol.
+//!
+//! The below description is taken from src/rust/protover.c, which is currently
+//! enabled by default. We are in the process of experimenting with Rust in
+//! tor, and this protover module is implemented to help achieve this goal.
+//!
+//! Starting in version 0.2.9.3-alpha, Tor places separate version numbers on
+//! each of the different components of its protocol. Relays use these numbers
+//! to advertise what versions of the protocols they can support, and clients
+//! use them to find what they can ask a given relay to do. Authorities vote
+//! on the supported protocol versions for each relay, and also vote on the
+//! which protocols you should have to support in order to be on the Tor
+//! network. All Tor instances use these required/recommended protocol versions
+//! to tell what level of support for recent protocols each relay has, and
+//! to decide whether they should be running given their current protocols.
+//!
+//! The main advantage of these protocol versions numbers over using Tor
+//! version numbers is that they allow different implementations of the Tor
+//! protocols to develop independently, without having to claim compatibility
+//! with specific versions of Tor.
+
+#[deny(missing_docs)]
+
+extern crate libc;
+extern crate smartlist;
+extern crate external;
+extern crate tor_allocate;
+extern crate tor_util;
+
+pub mod errors;
+pub mod protoset;
+mod protover;
+pub mod ffi;
+
+pub use protover::*;
diff --git a/src/rust/protover/protoset.rs b/src/rust/protover/protoset.rs
new file mode 100644
index 0000000000..027dfba203
--- /dev/null
+++ b/src/rust/protover/protoset.rs
@@ -0,0 +1,651 @@
+// Copyright (c) 2018, The Tor Project, Inc.
+// Copyright (c) 2018, isis agora lovecruft
+// See LICENSE for licensing information
+
+//! Sets for lazily storing ordered, non-overlapping ranges of integers.
+
+use std::slice;
+use std::str::FromStr;
+use std::u32;
+
+use errors::ProtoverError;
+
+/// A single version number.
+pub type Version = u32;
+
+/// A `ProtoSet` stores an ordered `Vec<T>` of `(low, high)` pairs of ranges of
+/// non-overlapping protocol versions.
+///
+/// # Examples
+///
+/// ```
+/// use std::str::FromStr;
+///
+/// use protover::errors::ProtoverError;
+/// use protover::protoset::ProtoSet;
+/// use protover::protoset::Version;
+///
+/// # fn do_test() -> Result<ProtoSet, ProtoverError> {
+/// let protoset: ProtoSet = ProtoSet::from_str("3-5,8")?;
+///
+/// // We could also equivalently call:
+/// let protoset: ProtoSet = "3-5,8".parse()?;
+///
+/// assert!(protoset.contains(&4));
+/// assert!(!protoset.contains(&7));
+///
+/// let expanded: Vec<Version> = protoset.clone().into();
+///
+/// assert_eq!(&expanded[..], &[3, 4, 5, 8]);
+///
+/// let contracted: String = protoset.clone().to_string();
+///
+/// assert_eq!(contracted, "3-5,8".to_string());
+/// # Ok(protoset)
+/// # }
+/// # fn main() { do_test(); } // wrap the test so we can use the ? operator
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub struct ProtoSet {
+ pub(crate) pairs: Vec<(Version, Version)>,
+}
+
+impl Default for ProtoSet {
+ fn default() -> Self {
+ let pairs: Vec<(Version, Version)> = Vec::new();
+
+ ProtoSet{ pairs }
+ }
+}
+
+impl<'a> ProtoSet {
+ /// Create a new `ProtoSet` from a slice of `(low, high)` pairs.
+ ///
+ /// # Inputs
+ ///
+ /// We do not assume the input pairs are deduplicated or ordered.
+ pub fn from_slice(low_high_pairs: &'a [(Version, Version)]) -> Result<Self, ProtoverError> {
+ let mut pairs: Vec<(Version, Version)> = Vec::with_capacity(low_high_pairs.len());
+
+ for &(low, high) in low_high_pairs {
+ pairs.push((low, high));
+ }
+ // Sort the pairs without reallocation and remove all duplicate pairs.
+ pairs.sort_unstable();
+ pairs.dedup();
+
+ ProtoSet{ pairs }.is_ok()
+ }
+}
+
+/// Expand this `ProtoSet` to a `Vec` of all its `Version`s.
+///
+/// # Examples
+///
+/// ```
+/// use std::str::FromStr;
+/// use protover::protoset::ProtoSet;
+/// use protover::protoset::Version;
+/// # use protover::errors::ProtoverError;
+///
+/// # fn do_test() -> Result<Vec<Version>, ProtoverError> {
+/// let protoset: ProtoSet = ProtoSet::from_str("3-5,21")?;
+/// let versions: Vec<Version> = protoset.into();
+///
+/// assert_eq!(&versions[..], &[3, 4, 5, 21]);
+/// #
+/// # Ok(versions)
+/// # }
+/// # fn main() { do_test(); } // wrap the test so we can use the ? operator
+/// ```
+impl Into<Vec<Version>> for ProtoSet {
+ fn into(self) -> Vec<Version> {
+ let mut versions: Vec<Version> = Vec::new();
+
+ for &(low, high) in self.iter() {
+ versions.extend(low..high + 1);
+ }
+ versions
+ }
+}
+
+impl ProtoSet {
+ /// Get an iterator over the `(low, high)` `pairs` in this `ProtoSet`.
+ pub fn iter(&self) -> slice::Iter<(Version, Version)> {
+ self.pairs.iter()
+ }
+
+ /// Expand this `ProtoSet` into a `Vec` of all its `Version`s.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use protover::errors::ProtoverError;
+ /// use protover::protoset::ProtoSet;
+ ///
+ /// # fn do_test() -> Result<bool, ProtoverError> {
+ /// let protoset: ProtoSet = "3-5,9".parse()?;
+ ///
+ /// assert_eq!(protoset.expand(), vec![3, 4, 5, 9]);
+ ///
+ /// let protoset: ProtoSet = "1,3,5-7".parse()?;
+ ///
+ /// assert_eq!(protoset.expand(), vec![1, 3, 5, 6, 7]);
+ /// #
+ /// # Ok(true)
+ /// # }
+ /// # fn main() { do_test(); } // wrap the test so we can use the ? operator
+ /// ```
+ pub fn expand(self) -> Vec<Version> {
+ self.into()
+ }
+
+ pub fn len(&self) -> usize {
+ let mut length: usize = 0;
+
+ for &(low, high) in self.iter() {
+ length += (high as usize - low as usize) + 1;
+ }
+
+ length
+ }
+
+ /// Check that this `ProtoSet` is well-formed.
+ ///
+ /// This is automatically called in `ProtoSet::from_str()`.
+ ///
+ /// # Errors
+ ///
+ /// * `ProtoverError::LowGreaterThanHigh`: if its `pairs` were not
+ /// well-formed, i.e. a `low` in a `(low, high)` was higher than the
+ /// previous `high`,
+ /// * `ProtoverError::Overlap`: if one or more of the `pairs` are
+ /// overlapping,
+ /// * `ProtoverError::ExceedsMax`: if the number of versions when expanded
+ /// would exceed `MAX_PROTOCOLS_TO_EXPAND`, and
+ ///
+ /// # Returns
+ ///
+ /// A `Result` whose `Ok` is this `Protoset`, and whose `Err` is one of the
+ /// errors enumerated in the Errors section above.
+ fn is_ok(self) -> Result<ProtoSet, ProtoverError> {
+ let mut last_high: Version = 0;
+
+ for &(low, high) in self.iter() {
+ if low == u32::MAX || high == u32::MAX {
+ return Err(ProtoverError::ExceedsMax);
+ }
+ if low < last_high {
+ return Err(ProtoverError::Overlap);
+ } else if low > high {
+ return Err(ProtoverError::LowGreaterThanHigh);
+ }
+ last_high = high;
+ }
+
+ Ok(self)
+ }
+
+ /// Determine if this `ProtoSet` contains no `Version`s.
+ ///
+ /// # Returns
+ ///
+ /// * `true` if this `ProtoSet`'s length is zero, and
+ /// * `false` otherwise.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use protover::protoset::ProtoSet;
+ ///
+ /// let protoset: ProtoSet = ProtoSet::default();
+ ///
+ /// assert!(protoset.is_empty());
+ /// ```
+ pub fn is_empty(&self) -> bool {
+ self.pairs.len() == 0
+ }
+
+ /// Determine if `version` is included within this `ProtoSet`.
+ ///
+ /// # Inputs
+ ///
+ /// * `version`: a `Version`.
+ ///
+ /// # Returns
+ ///
+ /// `true` if the `version` is contained within this set; `false` otherwise.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use protover::errors::ProtoverError;
+ /// use protover::protoset::ProtoSet;
+ ///
+ /// # fn do_test() -> Result<ProtoSet, ProtoverError> {
+ /// let protoset: ProtoSet = ProtoSet::from_slice(&[(0, 5), (7, 9), (13, 14)])?;
+ ///
+ /// assert!(protoset.contains(&5));
+ /// assert!(!protoset.contains(&10));
+ /// #
+ /// # Ok(protoset)
+ /// # }
+ /// # fn main() { do_test(); } // wrap the test so we can use the ? operator
+ /// ```
+ pub fn contains(&self, version: &Version) -> bool {
+ for &(low, high) in self.iter() {
+ if low <= *version && *version <= high {
+ return true;
+ }
+ }
+ false
+ }
+
+ /// Retain only the `Version`s in this `ProtoSet` for which the predicate
+ /// `F` returns `true`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use protover::errors::ProtoverError;
+ /// use protover::protoset::ProtoSet;
+ ///
+ /// # fn do_test() -> Result<bool, ProtoverError> {
+ /// let mut protoset: ProtoSet = "1,3-5,9".parse()?;
+ ///
+ /// // Keep only versions less than or equal to 8:
+ /// protoset.retain(|x| x <= &8);
+ ///
+ /// assert_eq!(protoset.expand(), vec![1, 3, 4, 5]);
+ /// #
+ /// # Ok(true)
+ /// # }
+ /// # fn main() { do_test(); } // wrap the test so we can use the ? operator
+ /// ```
+ // XXX we could probably do something more efficient here. —isis
+ pub fn retain<F>(&mut self, f: F)
+ where F: FnMut(&Version) -> bool
+ {
+ let mut expanded: Vec<Version> = self.clone().expand();
+ expanded.retain(f);
+ *self = expanded.into();
+ }
+}
+
+impl FromStr for ProtoSet {
+ type Err = ProtoverError;
+
+ /// Parse the unique version numbers supported by a subprotocol from a string.
+ ///
+ /// # Inputs
+ ///
+ /// * `version_string`, a string comprised of "[0-9,-]"
+ ///
+ /// # Returns
+ ///
+ /// A `Result` whose `Ok` value is a `ProtoSet` holding all of the unique
+ /// version numbers.
+ ///
+ /// The returned `Result`'s `Err` value is an `ProtoverError` appropriate to
+ /// the error.
+ ///
+ /// # Errors
+ ///
+ /// This function will error if:
+ ///
+ /// * the `version_string` is an equals (`"="`) sign,
+ /// * the expansion of a version range produces an error (see
+ /// `expand_version_range`),
+ /// * any single version number is not parseable as an `u32` in radix 10, or
+ /// * there are greater than 2^16 version numbers to expand.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::str::FromStr;
+ ///
+ /// use protover::errors::ProtoverError;
+ /// use protover::protoset::ProtoSet;
+ ///
+ /// # fn do_test() -> Result<ProtoSet, ProtoverError> {
+ /// let protoset: ProtoSet = ProtoSet::from_str("2-5,8")?;
+ ///
+ /// assert!(protoset.contains(&5));
+ /// assert!(!protoset.contains(&10));
+ ///
+ /// // We can also equivalently call `ProtoSet::from_str` by doing (all
+ /// // implementations of `FromStr` can be called this way, this one isn't
+ /// // special):
+ /// let protoset: ProtoSet = "4-6,12".parse()?;
+ ///
+ /// // Calling it (either way) can take really large ranges (up to `u32::MAX`):
+ /// let protoset: ProtoSet = "1-70000".parse()?;
+ /// let protoset: ProtoSet = "1-4294967296".parse()?;
+ ///
+ /// // There are lots of ways to get an `Err` from this function. Here are
+ /// // a few:
+ /// assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("="));
+ /// assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("-"));
+ /// assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("not_an_int"));
+ /// assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("3-"));
+ /// assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("1-,4"));
+ ///
+ /// // Things which would get parsed into an _empty_ `ProtoSet` are,
+ /// // however, legal, and result in an empty `ProtoSet`:
+ /// assert_eq!(Ok(ProtoSet::default()), ProtoSet::from_str(""));
+ /// assert_eq!(Ok(ProtoSet::default()), ProtoSet::from_str(",,,"));
+ /// #
+ /// # Ok(protoset)
+ /// # }
+ /// # fn main() { do_test(); } // wrap the test so we can use the ? operator
+ /// ```
+ fn from_str(version_string: &str) -> Result<Self, Self::Err> {
+ let mut pairs: Vec<(Version, Version)> = Vec::new();
+ let pieces: ::std::str::Split<char> = version_string.split(',');
+
+ for p in pieces {
+ if p.is_empty() {
+ continue;
+ } else if p.contains('-') {
+ let mut pair = p.splitn(2, '-');
+
+ let low = pair.next().ok_or(ProtoverError::Unparseable)?;
+ let high = pair.next().ok_or(ProtoverError::Unparseable)?;
+
+ let lo: Version = low.parse().or(Err(ProtoverError::Unparseable))?;
+ let hi: Version = high.parse().or(Err(ProtoverError::Unparseable))?;
+
+ if lo == u32::MAX || hi == u32::MAX {
+ return Err(ProtoverError::ExceedsMax);
+ }
+ pairs.push((lo, hi));
+ } else {
+ let v: u32 = p.parse().or(Err(ProtoverError::Unparseable))?;
+
+ if v == u32::MAX {
+ return Err(ProtoverError::ExceedsMax);
+ }
+ pairs.push((v, v));
+ }
+ }
+ // If we were passed in an empty string, or
+ // simply a comma, or a pile of commas, then return an empty ProtoSet.
+ if pairs.len() == 0 {
+ return Ok(ProtoSet::default());
+ }
+ ProtoSet::from_slice(&pairs[..])
+ }
+}
+
+impl ToString for ProtoSet {
+ /// Contracts a `ProtoSet` of versions into a string.
+ ///
+ /// # Returns
+ ///
+ /// A `String` representation of this `ProtoSet` in ascending order.
+ fn to_string(&self) -> String {
+ let mut final_output: Vec<String> = Vec::new();
+
+ for &(lo, hi) in self.iter() {
+ if lo != hi {
+ debug_assert!(lo < hi);
+ final_output.push(format!("{}-{}", lo, hi));
+ } else {
+ final_output.push(format!("{}", lo));
+ }
+ }
+ final_output.join(",")
+ }
+}
+
+/// Checks to see if there is a continuous range of integers, starting at the
+/// first in the list. Returns the last integer in the range if a range exists.
+///
+/// # Inputs
+///
+/// `list`, an ordered vector of `u32` integers of "[0-9,-]" representing the
+/// supported versions for a single protocol.
+///
+/// # Returns
+///
+/// A `bool` indicating whether the list contains a range, starting at the first
+/// in the list, a`Version` of the last integer in the range, and a `usize` of
+/// the index of that version.
+///
+/// For example, if given vec![1, 2, 3, 5], find_range will return true,
+/// as there is a continuous range, and 3, which is the last number in the
+/// continuous range, and 2 which is the index of 3.
+fn find_range(list: &Vec<Version>) -> (bool, Version, usize) {
+ if list.len() == 0 {
+ return (false, 0, 0);
+ }
+
+ let mut index: usize = 0;
+ let mut iterable = list.iter().peekable();
+ let mut range_end = match iterable.next() {
+ Some(n) => *n,
+ None => return (false, 0, 0),
+ };
+
+ let mut has_range = false;
+
+ while iterable.peek().is_some() {
+ let n = *iterable.next().unwrap();
+ if n != range_end + 1 {
+ break;
+ }
+
+ has_range = true;
+ range_end = n;
+ index += 1;
+ }
+
+ (has_range, range_end, index)
+}
+
+impl From<Vec<Version>> for ProtoSet {
+ fn from(mut v: Vec<Version>) -> ProtoSet {
+ let mut version_pairs: Vec<(Version, Version)> = Vec::new();
+
+ v.sort_unstable();
+ v.dedup();
+
+ 'vector: while !v.is_empty() {
+ let (has_range, end, index): (bool, Version, usize) = find_range(&v);
+
+ if has_range {
+ let first: Version = match v.first() {
+ Some(x) => *x,
+ None => continue,
+ };
+ let last: Version = match v.get(index) {
+ Some(x) => *x,
+ None => continue,
+ };
+ debug_assert!(last == end, format!("last = {}, end = {}", last, end));
+
+ version_pairs.push((first, last));
+ v = v.split_off(index + 1);
+
+ if v.len() == 0 {
+ break 'vector;
+ }
+ } else {
+ let last: Version = match v.get(index) {
+ Some(x) => *x,
+ None => continue,
+ };
+ version_pairs.push((last, last));
+ v.remove(index);
+ }
+ }
+ ProtoSet::from_slice(&version_pairs[..]).unwrap_or(ProtoSet::default())
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_find_range() {
+ assert_eq!((false, 0, 0), find_range(&vec![]));
+ assert_eq!((false, 1, 0), find_range(&vec![1]));
+ assert_eq!((true, 2, 1), find_range(&vec![1, 2]));
+ assert_eq!((true, 3, 2), find_range(&vec![1, 2, 3]));
+ assert_eq!((true, 3, 2), find_range(&vec![1, 2, 3, 5]));
+ }
+
+ macro_rules! assert_contains_each {
+ ($protoset:expr, $versions:expr) => (
+ for version in $versions {
+ assert!($protoset.contains(version));
+ }
+ )
+ }
+
+ macro_rules! test_protoset_contains_versions {
+ ($list:expr, $str:expr) => (
+ let versions: &[Version] = $list;
+ let protoset: Result<ProtoSet, ProtoverError> = ProtoSet::from_str($str);
+
+ assert!(protoset.is_ok());
+ let p = protoset.unwrap();
+ assert_contains_each!(p, versions);
+ )
+ }
+
+ #[test]
+ fn test_versions_from_str() {
+ test_protoset_contains_versions!(&[], "");
+ test_protoset_contains_versions!(&[1], "1");
+ test_protoset_contains_versions!(&[1, 2], "1,2");
+ test_protoset_contains_versions!(&[1, 2, 3], "1-3");
+ test_protoset_contains_versions!(&[0, 1], "0-1");
+ test_protoset_contains_versions!(&[1, 2, 5], "1-2,5");
+ test_protoset_contains_versions!(&[1, 3, 4, 5], "1,3-5");
+ test_protoset_contains_versions!(&[42, 55, 56, 57, 58], "42,55-58");
+ }
+
+ #[test]
+ fn test_versions_from_str_ab() {
+ assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("a,b"));
+ }
+
+ #[test]
+ fn test_versions_from_str_negative_1() {
+ assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("-1"));
+ }
+
+ #[test]
+ fn test_versions_from_str_hyphens() {
+ assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("--1"));
+ assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("-1-2"));
+ assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("1--2"));
+ }
+
+ #[test]
+ fn test_versions_from_str_triple() {
+ assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("1-2-3"));
+ }
+
+ #[test]
+ fn test_versions_from_str_1exclam() {
+ assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("1,!"));
+ }
+
+ #[test]
+ fn test_versions_from_str_percent_equal() {
+ assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("%="));
+ }
+
+ #[test]
+ fn test_versions_from_str_whitespace() {
+ assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("1,2\n"));
+ assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("1\r,2"));
+ assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("1,\t2"));
+ }
+
+ #[test]
+ fn test_versions_from_str_overlap() {
+ assert_eq!(Err(ProtoverError::Overlap), ProtoSet::from_str("1-3,2-4"));
+ }
+
+ #[test]
+ fn test_versions_from_slice_overlap() {
+ assert_eq!(Err(ProtoverError::Overlap), ProtoSet::from_slice(&[(1, 3), (2, 4)]));
+ }
+
+ #[test]
+ fn test_versions_from_str_max() {
+ assert_eq!(Err(ProtoverError::ExceedsMax), ProtoSet::from_str("4294967295"));
+ }
+
+ #[test]
+ fn test_versions_from_slice_max() {
+ assert_eq!(Err(ProtoverError::ExceedsMax), ProtoSet::from_slice(&[(4294967295, 4294967295)]));
+ }
+
+ #[test]
+ fn test_protoset_contains() {
+ let protoset: ProtoSet = ProtoSet::from_slice(&[(0, 5), (7, 9), (13, 14)]).unwrap();
+
+ for x in 0..6 { assert!(protoset.contains(&x), format!("should contain {}", x)); }
+ for x in 7..10 { assert!(protoset.contains(&x), format!("should contain {}", x)); }
+ for x in 13..15 { assert!(protoset.contains(&x), format!("should contain {}", x)); }
+
+ for x in [6, 10, 11, 12, 15, 42, 43, 44, 45, 1234584].iter() {
+ assert!(!protoset.contains(&x), format!("should not contain {}", x));
+ }
+ }
+
+ #[test]
+ fn test_protoset_contains_0_3() {
+ let protoset: ProtoSet = ProtoSet::from_slice(&[(0, 3)]).unwrap();
+
+ for x in 0..4 { assert!(protoset.contains(&x), format!("should contain {}", x)); }
+ }
+
+ macro_rules! assert_protoset_from_vec_contains_all {
+ ($($x:expr),*) => (
+ let vec: Vec<Version> = vec!($($x),*);
+ let protoset: ProtoSet = vec.clone().into();
+
+ for x in vec.iter() {
+ assert!(protoset.contains(&x));
+ }
+ )
+ }
+
+ #[test]
+ fn test_protoset_from_vec_123() {
+ assert_protoset_from_vec_contains_all!(1, 2, 3);
+ }
+
+ #[test]
+ fn test_protoset_from_vec_0_315() {
+ assert_protoset_from_vec_contains_all!(0, 1, 2, 3, 15);
+ }
+
+ #[test]
+ fn test_protoset_from_vec_unordered() {
+ let v: Vec<Version> = vec!(2, 3, 8, 4, 3, 9, 7, 2);
+ let ps: ProtoSet = v.into();
+
+ assert_eq!(ps.to_string(), "2-4,7-9");
+ }
+
+ #[test]
+ fn test_protoset_into_vec() {
+ let ps: ProtoSet = "1-13,42,9001,4294967294".parse().unwrap();
+ let v: Vec<Version> = ps.into();
+
+ assert!(v.contains(&7));
+ assert!(v.contains(&9001));
+ assert!(v.contains(&4294967294));
+ }
+}
+
+#[cfg(all(test, feature = "bench"))]
+mod bench {
+ use super::*;
+}
diff --git a/src/rust/protover/protover.rs b/src/rust/protover/protover.rs
new file mode 100644
index 0000000000..17a8d60ec6
--- /dev/null
+++ b/src/rust/protover/protover.rs
@@ -0,0 +1,899 @@
+// Copyright (c) 2016-2017, The Tor Project, Inc. */
+// See LICENSE for licensing information */
+
+use std::collections::HashMap;
+use std::collections::hash_map;
+use std::fmt;
+use std::str;
+use std::str::FromStr;
+use std::string::String;
+
+use tor_util::strings::NUL_BYTE;
+use external::c_tor_version_as_new_as;
+
+use errors::ProtoverError;
+use protoset::Version;
+use protoset::ProtoSet;
+
+/// The first version of Tor that included "proto" entries in its descriptors.
+/// Authorities should use this to decide whether to guess proto lines.
+///
+/// C_RUST_COUPLED:
+/// src/or/protover.h `FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS`
+const FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS: &'static str = "0.2.9.3-alpha";
+
+/// The maximum number of subprotocol version numbers we will attempt to expand
+/// before concluding that someone is trying to DoS us
+///
+/// C_RUST_COUPLED: src/or/protover.c `MAX_PROTOCOLS_TO_EXPAND`
+const MAX_PROTOCOLS_TO_EXPAND: usize = (1<<16);
+
+/// The maximum size an `UnknownProtocol`'s name may be.
+pub(crate) const MAX_PROTOCOL_NAME_LENGTH: usize = 100;
+
+/// Currently supported protocols and their versions, as a byte-slice.
+///
+/// # Warning
+///
+/// This byte-slice ends in a NUL byte. This is so that we can directly convert
+/// it to an `&'static CStr` in the FFI code, in order to hand the static string
+/// to C in a way that is compatible with C static strings.
+///
+/// Rust code which wishes to accesses this string should use
+/// `protover::get_supported_protocols()` instead.
+///
+/// C_RUST_COUPLED: src/or/protover.c `protover_get_supported_protocols`
+pub(crate) const SUPPORTED_PROTOCOLS: &'static [u8] =
+ b"Cons=1-2 \
+ Desc=1-2 \
+ DirCache=1-2 \
+ HSDir=1-2 \
+ HSIntro=3-4 \
+ HSRend=1-2 \
+ Link=1-5 \
+ LinkAuth=1,3 \
+ Microdesc=1-2 \
+ Relay=1-2\0";
+
+/// Known subprotocols in Tor. Indicates which subprotocol a relay supports.
+///
+/// C_RUST_COUPLED: src/or/protover.h `protocol_type_t`
+#[derive(Clone, Hash, Eq, PartialEq, Debug)]
+pub enum Protocol {
+ Cons,
+ Desc,
+ DirCache,
+ HSDir,
+ HSIntro,
+ HSRend,
+ Link,
+ LinkAuth,
+ Microdesc,
+ Relay,
+}
+
+impl fmt::Display for Protocol {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{:?}", self)
+ }
+}
+
+/// Translates a string representation of a protocol into a Proto type.
+/// Error if the string is an unrecognized protocol name.
+///
+/// C_RUST_COUPLED: src/or/protover.c `PROTOCOL_NAMES`
+impl FromStr for Protocol {
+ type Err = ProtoverError;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s {
+ "Cons" => Ok(Protocol::Cons),
+ "Desc" => Ok(Protocol::Desc),
+ "DirCache" => Ok(Protocol::DirCache),
+ "HSDir" => Ok(Protocol::HSDir),
+ "HSIntro" => Ok(Protocol::HSIntro),
+ "HSRend" => Ok(Protocol::HSRend),
+ "Link" => Ok(Protocol::Link),
+ "LinkAuth" => Ok(Protocol::LinkAuth),
+ "Microdesc" => Ok(Protocol::Microdesc),
+ "Relay" => Ok(Protocol::Relay),
+ _ => Err(ProtoverError::UnknownProtocol),
+ }
+ }
+}
+
+/// A protocol string which is not one of the `Protocols` we currently know
+/// about.
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
+pub struct UnknownProtocol(String);
+
+impl fmt::Display for UnknownProtocol {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.0)
+ }
+}
+
+impl FromStr for UnknownProtocol {
+ type Err = ProtoverError;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ if s.len() <= MAX_PROTOCOL_NAME_LENGTH {
+ Ok(UnknownProtocol(s.to_string()))
+ } else {
+ Err(ProtoverError::ExceedsNameLimit)
+ }
+ }
+}
+
+impl UnknownProtocol {
+ /// Create an `UnknownProtocol`, ignoring whether or not it
+ /// exceeds MAX_PROTOCOL_NAME_LENGTH.
+ fn from_str_any_len(s: &str) -> Result<Self, ProtoverError> {
+ Ok(UnknownProtocol(s.to_string()))
+ }
+}
+
+impl From<Protocol> for UnknownProtocol {
+ fn from(p: Protocol) -> UnknownProtocol {
+ UnknownProtocol(p.to_string())
+ }
+}
+
+/// Get the string representation of current supported protocols
+///
+/// # Returns
+///
+/// A `String` whose value is the existing protocols supported by tor.
+/// Returned data is in the format as follows:
+///
+/// "HSDir=1-1 LinkAuth=1"
+///
+pub fn get_supported_protocols() -> &'static str {
+ // The `len() - 1` is to remove the NUL byte.
+ // The `unwrap` is safe becauase we SUPPORTED_PROTOCOLS is under
+ // our control.
+ str::from_utf8(&SUPPORTED_PROTOCOLS[..SUPPORTED_PROTOCOLS.len() - 1])
+ .unwrap_or("")
+}
+
+/// A map of protocol names to the versions of them which are supported.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct ProtoEntry(HashMap<Protocol, ProtoSet>);
+
+impl Default for ProtoEntry {
+ fn default() -> ProtoEntry {
+ ProtoEntry( HashMap::new() )
+ }
+}
+
+impl ProtoEntry {
+ /// Get an iterator over the `Protocol`s and their `ProtoSet`s in this `ProtoEntry`.
+ pub fn iter(&self) -> hash_map::Iter<Protocol, ProtoSet> {
+ self.0.iter()
+ }
+
+ /// Translate the supported tor versions from a string into a
+ /// ProtoEntry, which is useful when looking up a specific
+ /// subprotocol.
+ pub fn supported() -> Result<Self, ProtoverError> {
+ let supported: &'static str = get_supported_protocols();
+
+ supported.parse()
+ }
+
+ pub fn len(&self) -> usize {
+ self.0.len()
+ }
+
+ pub fn get(&self, protocol: &Protocol) -> Option<&ProtoSet> {
+ self.0.get(protocol)
+ }
+
+ pub fn insert(&mut self, key: Protocol, value: ProtoSet) {
+ self.0.insert(key, value);
+ }
+
+ pub fn remove(&mut self, key: &Protocol) -> Option<ProtoSet> {
+ self.0.remove(key)
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.0.is_empty()
+ }
+}
+
+impl FromStr for ProtoEntry {
+ type Err = ProtoverError;
+
+ /// Parse a string of subprotocol types and their version numbers.
+ ///
+ /// # Inputs
+ ///
+ /// * A `protocol_entry` string, comprised of a keywords, an "=" sign, and
+ /// one or more version numbers, each separated by a space. For example,
+ /// `"Cons=3-4 HSDir=1"`.
+ ///
+ /// # Returns
+ ///
+ /// A `Result` whose `Ok` value is a `ProtoEntry`, where the
+ /// first element is the subprotocol type (see `protover::Protocol`) and the last
+ /// element is an ordered set of `(low, high)` unique version numbers which are supported.
+ /// Otherwise, the `Err` value of this `Result` is a `ProtoverError`.
+ fn from_str(protocol_entry: &str) -> Result<ProtoEntry, ProtoverError> {
+ let mut proto_entry: ProtoEntry = ProtoEntry::default();
+ let entries = protocol_entry.split(' ');
+
+ for entry in entries {
+ let mut parts = entry.splitn(2, '=');
+
+ let proto = match parts.next() {
+ Some(n) => n,
+ None => return Err(ProtoverError::Unparseable),
+ };
+
+ let vers = match parts.next() {
+ Some(n) => n,
+ None => return Err(ProtoverError::Unparseable),
+ };
+ let versions: ProtoSet = vers.parse()?;
+ let proto_name: Protocol = proto.parse()?;
+
+ proto_entry.insert(proto_name, versions);
+
+ if proto_entry.len() > MAX_PROTOCOLS_TO_EXPAND {
+ return Err(ProtoverError::ExceedsMax);
+ }
+ }
+ Ok(proto_entry)
+ }
+}
+
+/// Generate an implementation of `ToString` for either a `ProtoEntry` or an
+/// `UnvalidatedProtoEntry`.
+macro_rules! impl_to_string_for_proto_entry {
+ ($t:ty) => (
+ impl ToString for $t {
+ fn to_string(&self) -> String {
+ let mut parts: Vec<String> = Vec::new();
+
+ for (protocol, versions) in self.iter() {
+ parts.push(format!("{}={}", protocol.to_string(), versions.to_string()));
+ }
+ parts.sort_unstable();
+ parts.join(" ")
+ }
+ }
+ )
+}
+
+impl_to_string_for_proto_entry!(ProtoEntry);
+impl_to_string_for_proto_entry!(UnvalidatedProtoEntry);
+
+/// A `ProtoEntry`, but whose `Protocols` can be any `UnknownProtocol`, not just
+/// the supported ones enumerated in `Protocols`. The protocol versions are
+/// validated, however.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct UnvalidatedProtoEntry(HashMap<UnknownProtocol, ProtoSet>);
+
+impl Default for UnvalidatedProtoEntry {
+ fn default() -> UnvalidatedProtoEntry {
+ UnvalidatedProtoEntry( HashMap::new() )
+ }
+}
+
+impl UnvalidatedProtoEntry {
+ /// Get an iterator over the `Protocol`s and their `ProtoSet`s in this `ProtoEntry`.
+ pub fn iter(&self) -> hash_map::Iter<UnknownProtocol, ProtoSet> {
+ self.0.iter()
+ }
+
+ pub fn get(&self, protocol: &UnknownProtocol) -> Option<&ProtoSet> {
+ self.0.get(protocol)
+ }
+
+ pub fn insert(&mut self, key: UnknownProtocol, value: ProtoSet) {
+ self.0.insert(key, value);
+ }
+
+ pub fn remove(&mut self, key: &UnknownProtocol) -> Option<ProtoSet> {
+ self.0.remove(key)
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.0.is_empty()
+ }
+
+ pub fn len(&self) -> usize {
+ let mut total: usize = 0;
+
+ for (_, versions) in self.iter() {
+ total += versions.len();
+ }
+ total
+ }
+
+ /// Determine if we support every protocol a client supports, and if not,
+ /// determine which protocols we do not have support for.
+ ///
+ /// # Returns
+ ///
+ /// Optionally, return parameters which the client supports but which we do not.
+ ///
+ /// # Examples
+ /// ```
+ /// use protover::UnvalidatedProtoEntry;
+ ///
+ /// let protocols: UnvalidatedProtoEntry = "LinkAuth=1 Microdesc=1-2 Relay=2".parse().unwrap();
+ /// let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
+ /// assert_eq!(true, unsupported.is_none());
+ ///
+ /// let protocols: UnvalidatedProtoEntry = "Link=1-2 Wombat=9".parse().unwrap();
+ /// let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
+ /// assert_eq!(true, unsupported.is_some());
+ /// assert_eq!("Wombat=9", &unsupported.unwrap().to_string());
+ /// ```
+ pub fn all_supported(&self) -> Option<UnvalidatedProtoEntry> {
+ let mut unsupported: UnvalidatedProtoEntry = UnvalidatedProtoEntry::default();
+ let supported: ProtoEntry = match ProtoEntry::supported() {
+ Ok(x) => x,
+ Err(_) => return None,
+ };
+
+ for (protocol, versions) in self.iter() {
+ let is_supported: Result<Protocol, ProtoverError> = protocol.0.parse();
+ let supported_protocol: Protocol;
+
+ // If the protocol wasn't even in the enum, then we definitely don't
+ // know about it and don't support any of its versions.
+ if is_supported.is_err() {
+ if !versions.is_empty() {
+ unsupported.insert(protocol.clone(), versions.clone());
+ }
+ continue;
+ } else {
+ supported_protocol = is_supported.unwrap();
+ }
+
+ let maybe_supported_versions: Option<&ProtoSet> = supported.get(&supported_protocol);
+ let supported_versions: &ProtoSet;
+ let mut unsupported_versions: ProtoSet;
+
+ // If the protocol wasn't in the map, then we don't know about it
+ // and don't support any of its versions. Add its versions to the
+ // map (if it has versions).
+ if maybe_supported_versions.is_none() {
+ if !versions.is_empty() {
+ unsupported.insert(protocol.clone(), versions.clone());
+ }
+ continue;
+ } else {
+ supported_versions = maybe_supported_versions.unwrap();
+ }
+ unsupported_versions = versions.clone();
+ unsupported_versions.retain(|x| !supported_versions.contains(x));
+
+ if !unsupported_versions.is_empty() {
+ unsupported.insert(protocol.clone(), unsupported_versions);
+ }
+ }
+
+ if unsupported.is_empty() {
+ return None;
+ }
+ Some(unsupported)
+ }
+
+ /// Determine if we have support for some protocol and version.
+ ///
+ /// # Inputs
+ ///
+ /// * `proto`, an `UnknownProtocol` to test support for
+ /// * `vers`, a `Version` which we will go on to determine whether the
+ /// specified protocol supports.
+ ///
+ /// # Return
+ ///
+ /// Returns `true` iff this `UnvalidatedProtoEntry` includes support for the
+ /// indicated protocol and version, and `false` otherwise.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use std::str::FromStr;
+ /// use protover::*;
+ /// # use protover::errors::ProtoverError;
+ ///
+ /// # fn do_test () -> Result<UnvalidatedProtoEntry, ProtoverError> {
+ /// let proto: UnvalidatedProtoEntry = "Link=3-4 Cons=1 Doggo=3-5".parse()?;
+ /// assert_eq!(true, proto.supports_protocol(&Protocol::Cons.into(), &1));
+ /// assert_eq!(false, proto.supports_protocol(&Protocol::Cons.into(), &5));
+ /// assert_eq!(true, proto.supports_protocol(&UnknownProtocol::from_str("Doggo")?, &4));
+ /// # Ok(proto)
+ /// # } fn main () { do_test(); }
+ /// ```
+ pub fn supports_protocol(&self, proto: &UnknownProtocol, vers: &Version) -> bool {
+ let supported_versions: &ProtoSet = match self.get(proto) {
+ Some(n) => n,
+ None => return false,
+ };
+ supported_versions.contains(&vers)
+ }
+
+ /// As `UnvalidatedProtoEntry::supports_protocol()`, but also returns `true`
+ /// if any later version of the protocol is supported.
+ ///
+ /// # Examples
+ /// ```
+ /// use protover::*;
+ /// # use protover::errors::ProtoverError;
+ ///
+ /// # fn do_test () -> Result<UnvalidatedProtoEntry, ProtoverError> {
+ /// let proto: UnvalidatedProtoEntry = "Link=3-4 Cons=5".parse()?;
+ ///
+ /// assert_eq!(true, proto.supports_protocol_or_later(&Protocol::Cons.into(), &5));
+ /// assert_eq!(true, proto.supports_protocol_or_later(&Protocol::Cons.into(), &4));
+ /// assert_eq!(false, proto.supports_protocol_or_later(&Protocol::Cons.into(), &6));
+ /// # Ok(proto)
+ /// # } fn main () { do_test(); }
+ /// ```
+ pub fn supports_protocol_or_later(&self, proto: &UnknownProtocol, vers: &Version) -> bool {
+ let supported_versions: &ProtoSet = match self.get(&proto) {
+ Some(n) => n,
+ None => return false,
+ };
+ supported_versions.iter().any(|v| v.1 >= *vers)
+ }
+
+ /// Split a string containing (potentially) several protocols and their
+ /// versions into a `Vec` of tuples of string in `(protocol, versions)`
+ /// form.
+ ///
+ /// # Inputs
+ ///
+ /// A &str in the form `"Link=3-4 Cons=5"`.
+ ///
+ /// # Returns
+ ///
+ /// A `Result` whose `Ok` variant is a `Vec<(&str, &str)>` of `(protocol,
+ /// versions)`, or whose `Err` variant is a `ProtoverError`.
+ ///
+ /// # Errors
+ ///
+ /// This will error with a `ProtoverError::Unparseable` if any of the
+ /// following are true:
+ ///
+ /// * If a protocol name is an empty string, e.g. `"Cons=1,3 =3-5"`.
+ /// * If a protocol name cannot be parsed as utf-8.
+ /// * If the version numbers are an empty string, e.g. `"Cons="`.
+ fn parse_protocol_and_version_str<'a>(protocol_string: &'a str)
+ -> Result<Vec<(&'a str, &'a str)>, ProtoverError>
+ {
+ let mut protovers: Vec<(&str, &str)> = Vec::new();
+
+ for subproto in protocol_string.split(' ') {
+ let mut parts = subproto.splitn(2, '=');
+
+ let name = match parts.next() {
+ Some("") => return Err(ProtoverError::Unparseable),
+ Some(n) => n,
+ None => return Err(ProtoverError::Unparseable),
+ };
+ let vers = match parts.next() {
+ Some(n) => n,
+ None => return Err(ProtoverError::Unparseable),
+ };
+ protovers.push((name, vers));
+ }
+ Ok(protovers)
+ }
+}
+
+impl FromStr for UnvalidatedProtoEntry {
+ type Err = ProtoverError;
+
+ /// Parses a protocol list without validating the protocol names.
+ ///
+ /// # Inputs
+ ///
+ /// * `protocol_string`, a string comprised of keys and values, both which are
+ /// strings. The keys are the protocol names while values are a string
+ /// representation of the supported versions.
+ ///
+ /// The input is _not_ expected to be a subset of the Protocol types
+ ///
+ /// # Returns
+ ///
+ /// A `Result` whose `Ok` value is a `ProtoSet` holding all of the
+ /// unique version numbers.
+ ///
+ /// The returned `Result`'s `Err` value is an `ProtoverError` whose `Display`
+ /// impl has a description of the error.
+ ///
+ /// # Errors
+ ///
+ /// This function will error if:
+ ///
+ /// * The protocol string does not follow the "protocol_name=version_list"
+ /// expected format, or
+ /// * If the version string is malformed. See `impl FromStr for ProtoSet`.
+ fn from_str(protocol_string: &str) -> Result<UnvalidatedProtoEntry, ProtoverError> {
+ let mut parsed: UnvalidatedProtoEntry = UnvalidatedProtoEntry::default();
+ let parts: Vec<(&str, &str)> =
+ UnvalidatedProtoEntry::parse_protocol_and_version_str(protocol_string)?;
+
+ for &(name, vers) in parts.iter() {
+ let versions = ProtoSet::from_str(vers)?;
+ let protocol = UnknownProtocol::from_str(name)?;
+
+ parsed.insert(protocol, versions);
+ }
+ Ok(parsed)
+ }
+}
+
+impl UnvalidatedProtoEntry {
+ /// Create an `UnknownProtocol`, ignoring whether or not it
+ /// exceeds MAX_PROTOCOL_NAME_LENGTH.
+ pub(crate) fn from_str_any_len(protocol_string: &str)
+ -> Result<UnvalidatedProtoEntry, ProtoverError>
+ {
+ let mut parsed: UnvalidatedProtoEntry = UnvalidatedProtoEntry::default();
+ let parts: Vec<(&str, &str)> =
+ UnvalidatedProtoEntry::parse_protocol_and_version_str(protocol_string)?;
+
+ for &(name, vers) in parts.iter() {
+ let versions = ProtoSet::from_str(vers)?;
+ let protocol = UnknownProtocol::from_str_any_len(name)?;
+
+ parsed.insert(protocol, versions);
+ }
+ Ok(parsed)
+ }
+}
+
+/// Pretend a `ProtoEntry` is actually an `UnvalidatedProtoEntry`.
+impl From<ProtoEntry> for UnvalidatedProtoEntry {
+ fn from(proto_entry: ProtoEntry) -> UnvalidatedProtoEntry {
+ let mut unvalidated: UnvalidatedProtoEntry = UnvalidatedProtoEntry::default();
+
+ for (protocol, versions) in proto_entry.iter() {
+ unvalidated.insert(UnknownProtocol::from(protocol.clone()), versions.clone());
+ }
+ unvalidated
+ }
+}
+
+/// A mapping of protocols to a count of how many times each of their `Version`s
+/// were voted for or supported.
+///
+/// # Warning
+///
+/// The "protocols" are *not* guaranteed to be known/supported `Protocol`s, in
+/// order to allow new subprotocols to be introduced even if Directory
+/// Authorities don't yet know of them.
+pub struct ProtoverVote( HashMap<UnknownProtocol, HashMap<Version, usize>> );
+
+impl Default for ProtoverVote {
+ fn default() -> ProtoverVote {
+ ProtoverVote( HashMap::new() )
+ }
+}
+
+impl IntoIterator for ProtoverVote {
+ type Item = (UnknownProtocol, HashMap<Version, usize>);
+ type IntoIter = hash_map::IntoIter<UnknownProtocol, HashMap<Version, usize>>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.0.into_iter()
+ }
+}
+
+impl ProtoverVote {
+ pub fn entry(&mut self, key: UnknownProtocol)
+ -> hash_map::Entry<UnknownProtocol, HashMap<Version, usize>>
+ {
+ self.0.entry(key)
+ }
+
+ /// Protocol voting implementation.
+ ///
+ /// Given a slice of `UnvalidatedProtoEntry`s and a vote `threshold`, return
+ /// a new `UnvalidatedProtoEntry` encoding all of the protocols that are
+ /// listed by at least `threshold` of the inputs.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use protover::ProtoverVote;
+ /// use protover::UnvalidatedProtoEntry;
+ ///
+ /// let protos: &[UnvalidatedProtoEntry] = &["Link=3-4".parse().unwrap(),
+ /// "Link=3".parse().unwrap()];
+ /// let vote = ProtoverVote::compute(protos, &2);
+ /// assert_eq!("Link=3", vote.to_string());
+ /// ```
+ // C_RUST_COUPLED: /src/or/protover.c protover_compute_vote
+ pub fn compute(proto_entries: &[UnvalidatedProtoEntry], threshold: &usize) -> UnvalidatedProtoEntry {
+ let mut all_count: ProtoverVote = ProtoverVote::default();
+ let mut final_output: UnvalidatedProtoEntry = UnvalidatedProtoEntry::default();
+
+ if proto_entries.is_empty() {
+ return final_output;
+ }
+
+ // parse and collect all of the protos and their versions and collect them
+ for vote in proto_entries {
+ // C_RUST_DIFFERS: This doesn't actually differ, bu this check on
+ // the total is here to make it match. Because the C version calls
+ // expand_protocol_list() which checks if there would be too many
+ // subprotocols *or* individual version numbers, i.e. more than
+ // MAX_PROTOCOLS_TO_EXPAND, and does this *per vote*, we need to
+ // match it's behaviour and ensure we're not allowing more than it
+ // would.
+ if vote.len() > MAX_PROTOCOLS_TO_EXPAND {
+ continue;
+ }
+
+ for (protocol, versions) in vote.iter() {
+ let supported_vers: &mut HashMap<Version, usize> =
+ all_count.entry(protocol.clone()).or_insert(HashMap::new());
+
+ for version in versions.clone().expand() {
+ let counter: &mut usize =
+ supported_vers.entry(version).or_insert(0);
+ *counter += 1;
+ }
+ }
+ }
+
+ for (protocol, mut versions) in all_count {
+ // Go through and remove versions that are less than the threshold
+ versions.retain(|_, count| *count as usize >= *threshold);
+
+ if versions.len() > 0 {
+ let voted_versions: Vec<Version> = versions.keys().cloned().collect();
+ let voted_protoset: ProtoSet = ProtoSet::from(voted_versions);
+
+ final_output.insert(protocol, voted_protoset);
+ }
+ }
+ final_output
+ }
+}
+
+/// Returns a boolean indicating whether the given protocol and version is
+/// supported in any of the existing Tor protocols
+///
+/// # Examples
+/// ```
+/// use protover::is_supported_here;
+/// use protover::Protocol;
+///
+/// let is_supported = is_supported_here(&Protocol::Link, &10);
+/// assert_eq!(false, is_supported);
+///
+/// let is_supported = is_supported_here(&Protocol::Link, &1);
+/// assert_eq!(true, is_supported);
+/// ```
+pub fn is_supported_here(proto: &Protocol, vers: &Version) -> bool {
+ let currently_supported: ProtoEntry = match ProtoEntry::supported() {
+ Ok(result) => result,
+ Err(_) => return false,
+ };
+ let supported_versions = match currently_supported.get(proto) {
+ Some(n) => n,
+ None => return false,
+ };
+ supported_versions.contains(vers)
+}
+
+/// Since older versions of Tor cannot infer their own subprotocols,
+/// determine which subprotocols are supported by older Tor versions.
+///
+/// # Inputs
+///
+/// * `version`, a string comprised of "[0-9a-z.-]"
+///
+/// # Returns
+///
+/// A `&'static [u8]` encoding a list of protocol names and supported
+/// versions. The string takes the following format:
+///
+/// "HSDir=1-1 LinkAuth=1"
+///
+/// This function returns the protocols that are supported by the version input,
+/// only for tor versions older than `FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS`
+/// (but not older than 0.2.4.19). For newer tors (or older than 0.2.4.19), it
+/// returns an empty string.
+///
+/// # Note
+///
+/// This function is meant to be called for/within FFI code. If you'd
+/// like to use this code in Rust, please see `compute_for_old_tor()`.
+//
+// C_RUST_COUPLED: src/rust/protover.c `compute_for_old_tor`
+pub(crate) fn compute_for_old_tor_cstr(version: &str) -> &'static [u8] {
+ if c_tor_version_as_new_as(version, FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS) {
+ return NUL_BYTE;
+ }
+ if c_tor_version_as_new_as(version, "0.2.9.1-alpha") {
+ return b"Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1-2 \
+ Link=1-4 LinkAuth=1 Microdesc=1-2 Relay=1-2\0";
+ }
+ if c_tor_version_as_new_as(version, "0.2.7.5") {
+ return b"Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 \
+ Link=1-4 LinkAuth=1 Microdesc=1-2 Relay=1-2\0";
+ }
+ if c_tor_version_as_new_as(version, "0.2.4.19") {
+ return b"Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 \
+ Link=1-4 LinkAuth=1 Microdesc=1 Relay=1-2\0";
+ }
+
+ NUL_BYTE
+}
+
+/// Since older versions of Tor cannot infer their own subprotocols,
+/// determine which subprotocols are supported by older Tor versions.
+///
+/// # Inputs
+///
+/// * `version`, a string comprised of "[0-9a-z.-]"
+///
+/// # Returns
+///
+/// A `Result` whose `Ok` value is an `&'static str` encoding a list of protocol
+/// names and supported versions. The string takes the following format:
+///
+/// "HSDir=1-1 LinkAuth=1"
+///
+/// This function returns the protocols that are supported by the version input,
+/// only for tor versions older than `FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS`.
+/// (but not older than 0.2.4.19). For newer tors (or older than 0.2.4.19), its
+/// `Ok` `Result` contains an empty string.
+///
+/// Otherwise, its `Err` contains a `ProtoverError::Unparseable` if the
+/// `version` string was invalid utf-8.
+///
+/// # Note
+///
+/// This function is meant to be called for/within non-FFI Rust code.
+//
+// C_RUST_COUPLED: src/rust/protover.c `compute_for_old_tor`
+pub fn compute_for_old_tor(version: &str) -> Result<&'static str, ProtoverError> {
+ let mut computed: &'static [u8] = compute_for_old_tor_cstr(version);
+
+ // Remove the NULL byte at the end.
+ computed = &computed[..computed.len() - 1];
+
+ // .from_utf8() fails with a Utf8Error if it couldn't validate the
+ // utf-8, so convert that here into an Unparseable ProtoverError.
+ str::from_utf8(computed).or(Err(ProtoverError::Unparseable))
+}
+
+#[cfg(test)]
+mod test {
+ use std::str::FromStr;
+ use std::string::ToString;
+
+ use super::*;
+
+ macro_rules! assert_protoentry_is_parseable {
+ ($e:expr) => (
+ let protoentry: Result<ProtoEntry, ProtoverError> = $e.parse();
+
+ assert!(protoentry.is_ok(), format!("{:?}", protoentry.err()));
+ )
+ }
+
+ macro_rules! assert_protoentry_is_unparseable {
+ ($e:expr) => (
+ let protoentry: Result<ProtoEntry, ProtoverError> = $e.parse();
+
+ assert!(protoentry.is_err());
+ )
+ }
+
+ #[test]
+ fn test_protoentry_from_str_multiple_protocols_multiple_versions() {
+ assert_protoentry_is_parseable!("Cons=3-4 Link=1,3-5");
+ }
+
+ #[test]
+ fn test_protoentry_from_str_empty() {
+ assert_protoentry_is_unparseable!("");
+ }
+
+ #[test]
+ fn test_protoentry_from_str_single_protocol_single_version() {
+ assert_protoentry_is_parseable!("HSDir=1");
+ }
+
+ #[test]
+ fn test_protoentry_from_str_unknown_protocol() {
+ assert_protoentry_is_unparseable!("Ducks=5-7,8");
+ }
+
+ #[test]
+ fn test_protoentry_from_str_allowed_number_of_versions() {
+ assert_protoentry_is_parseable!("Desc=1-4294967294");
+ }
+
+ #[test]
+ fn test_protoentry_from_str_too_many_versions() {
+ assert_protoentry_is_unparseable!("Desc=1-4294967295");
+ }
+
+ #[test]
+ fn test_protoentry_from_str_() {
+ assert_protoentry_is_unparseable!("");
+ }
+
+ #[test]
+ fn test_protoentry_all_supported_single_protocol_single_version() {
+ let protocol: UnvalidatedProtoEntry = "Cons=1".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocol.all_supported();
+ assert_eq!(true, unsupported.is_none());
+ }
+
+ #[test]
+ fn test_protoentry_all_supported_multiple_protocol_multiple_versions() {
+ let protocols: UnvalidatedProtoEntry = "Link=3-4 Desc=2".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
+ assert_eq!(true, unsupported.is_none());
+ }
+
+ #[test]
+ fn test_protoentry_all_supported_three_values() {
+ let protocols: UnvalidatedProtoEntry = "LinkAuth=1 Microdesc=1-2 Relay=2".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
+ assert_eq!(true, unsupported.is_none());
+ }
+
+ #[test]
+ fn test_protoentry_all_supported_unknown_protocol() {
+ let protocols: UnvalidatedProtoEntry = "Wombat=9".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
+ assert_eq!(true, unsupported.is_some());
+ assert_eq!("Wombat=9", &unsupported.unwrap().to_string());
+ }
+
+ #[test]
+ fn test_protoentry_all_supported_unsupported_high_version() {
+ let protocols: UnvalidatedProtoEntry = "HSDir=12-100".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
+ assert_eq!(true, unsupported.is_some());
+ assert_eq!("HSDir=12-100", &unsupported.unwrap().to_string());
+ }
+
+ #[test]
+ fn test_protoentry_all_supported_unsupported_low_version() {
+ let protocols: UnvalidatedProtoEntry = "Cons=0-1".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
+ assert_eq!(true, unsupported.is_some());
+ assert_eq!("Cons=0", &unsupported.unwrap().to_string());
+ }
+
+ #[test]
+ fn test_contract_protocol_list() {
+ let mut versions = "";
+ assert_eq!(String::from(versions), ProtoSet::from_str(&versions).unwrap().to_string());
+
+ versions = "1";
+ assert_eq!(String::from(versions), ProtoSet::from_str(&versions).unwrap().to_string());
+
+ versions = "1-2";
+ assert_eq!(String::from(versions), ProtoSet::from_str(&versions).unwrap().to_string());
+
+ versions = "1,3";
+ assert_eq!(String::from(versions), ProtoSet::from_str(&versions).unwrap().to_string());
+
+ versions = "1-4";
+ assert_eq!(String::from(versions), ProtoSet::from_str(&versions).unwrap().to_string());
+
+ versions = "1,3,5-7";
+ assert_eq!(String::from(versions), ProtoSet::from_str(&versions).unwrap().to_string());
+
+ versions = "1-3,500";
+ assert_eq!(String::from(versions), ProtoSet::from_str(&versions).unwrap().to_string());
+ }
+}
diff --git a/src/rust/protover/tests/protover.rs b/src/rust/protover/tests/protover.rs
new file mode 100644
index 0000000000..2db01a1634
--- /dev/null
+++ b/src/rust/protover/tests/protover.rs
@@ -0,0 +1,394 @@
+// Copyright (c) 2016-2017, The Tor Project, Inc. */
+// See LICENSE for licensing information */
+
+extern crate protover;
+
+use protover::ProtoEntry;
+use protover::ProtoverVote;
+use protover::UnvalidatedProtoEntry;
+use protover::errors::ProtoverError;
+
+#[test]
+fn parse_protocol_with_single_proto_and_single_version() {
+ let _: ProtoEntry = "Cons=1".parse().unwrap();
+}
+
+#[test]
+fn parse_protocol_with_single_protocol_and_multiple_versions() {
+ let _: ProtoEntry = "Cons=1-2".parse().unwrap();
+}
+
+#[test]
+fn parse_protocol_with_different_single_protocol_and_single_version() {
+ let _: ProtoEntry = "HSDir=1".parse().unwrap();
+}
+
+#[test]
+fn parse_protocol_with_single_protocol_and_supported_version() {
+ let _: ProtoEntry = "Desc=2".parse().unwrap();
+}
+
+#[test]
+fn parse_protocol_with_two_protocols_and_single_version() {
+ let _: ProtoEntry = "Cons=1 HSDir=1".parse().unwrap();
+}
+
+#[test]
+fn parse_protocol_with_single_protocol_and_two_sequential_versions() {
+ let _: ProtoEntry = "Desc=1-2".parse().unwrap();
+}
+
+#[test]
+fn parse_protocol_with_single_protocol_and_protocol_range() {
+ let _: ProtoEntry = "Link=1-4".parse().unwrap();
+}
+
+#[test]
+fn parse_protocol_with_single_protocol_and_protocol_set() {
+ let _: ProtoEntry = "Link=3-4 Desc=2".parse().unwrap();
+}
+
+#[test]
+fn protocol_all_supported_with_single_protocol_and_protocol_set() {
+ let protocols: UnvalidatedProtoEntry = "Link=3-4 Desc=2".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
+ assert_eq!(true, unsupported.is_none());
+}
+
+#[test]
+fn protocol_all_supported_with_two_values() {
+ let protocols: UnvalidatedProtoEntry = "Microdesc=1-2 Relay=2".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
+ assert_eq!(true, unsupported.is_none());
+}
+
+#[test]
+fn protocol_all_supported_with_one_value() {
+ let protocols: UnvalidatedProtoEntry = "Microdesc=1-2".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
+ assert_eq!(true, unsupported.is_none());
+}
+
+#[test]
+#[should_panic]
+fn parse_protocol_unvalidated_with_empty() {
+ let _: UnvalidatedProtoEntry = "".parse().unwrap();
+}
+
+#[test]
+#[should_panic]
+fn parse_protocol_validated_with_empty() {
+ let _: UnvalidatedProtoEntry = "".parse().unwrap();
+}
+
+#[test]
+fn protocol_all_supported_with_three_values() {
+ let protocols: UnvalidatedProtoEntry = "LinkAuth=1 Microdesc=1-2 Relay=2".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
+ assert_eq!(true, unsupported.is_none());
+}
+
+#[test]
+fn protocol_all_supported_with_unsupported_protocol() {
+ let protocols: UnvalidatedProtoEntry = "Wombat=9".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
+ assert_eq!(true, unsupported.is_some());
+ assert_eq!("Wombat=9", &unsupported.unwrap().to_string());
+}
+
+#[test]
+fn protocol_all_supported_with_unsupported_versions() {
+ let protocols: UnvalidatedProtoEntry = "Link=3-999".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
+ assert_eq!(true, unsupported.is_some());
+ assert_eq!("Link=6-999", &unsupported.unwrap().to_string());
+}
+
+#[test]
+fn protocol_all_supported_with_unsupported_low_version() {
+ let protocols: UnvalidatedProtoEntry = "Cons=0-1".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
+ assert_eq!(true, unsupported.is_some());
+ assert_eq!("Cons=0", &unsupported.unwrap().to_string());
+}
+
+#[test]
+fn protocol_all_supported_with_unsupported_high_version() {
+ let protocols: UnvalidatedProtoEntry = "Cons=1-2,999".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
+ assert_eq!(true, unsupported.is_some());
+ assert_eq!("Cons=999", &unsupported.unwrap().to_string());
+}
+
+#[test]
+fn protocol_all_supported_with_mix_of_supported_and_unsupproted() {
+ let protocols: UnvalidatedProtoEntry = "Link=3-4 Wombat=9".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
+ assert_eq!(true, unsupported.is_some());
+ assert_eq!("Wombat=9", &unsupported.unwrap().to_string());
+}
+
+#[test]
+fn protover_string_supports_protocol_returns_true_for_single_supported() {
+ let protocols: UnvalidatedProtoEntry = "Link=3-4 Cons=1".parse().unwrap();
+ let is_supported = protocols.supports_protocol(&protover::Protocol::Cons.into(), &1);
+ assert_eq!(true, is_supported);
+}
+
+#[test]
+fn protover_string_supports_protocol_returns_false_for_single_unsupported() {
+ let protocols: UnvalidatedProtoEntry = "Link=3-4 Cons=1".parse().unwrap();
+ let is_supported = protocols.supports_protocol(&protover::Protocol::Cons.into(), &2);
+ assert_eq!(false, is_supported);
+}
+
+#[test]
+fn protover_string_supports_protocol_returns_false_for_unsupported() {
+ let protocols: UnvalidatedProtoEntry = "Link=3-4".parse().unwrap();
+ let is_supported = protocols.supports_protocol(&protover::Protocol::Cons.into(), &2);
+ assert_eq!(false, is_supported);
+}
+
+#[test]
+#[should_panic]
+fn parse_protocol_with_unexpected_characters() {
+ let _: UnvalidatedProtoEntry = "Cons=*-%".parse().unwrap();
+}
+
+#[test]
+#[should_panic]
+fn protover_compute_vote_returns_empty_for_empty_string() {
+ let protocols: &[UnvalidatedProtoEntry] = &["".parse().unwrap()];
+ let listed = ProtoverVote::compute(protocols, &1);
+ assert_eq!("", listed.to_string());
+}
+
+#[test]
+fn protover_compute_vote_returns_single_protocol_for_matching() {
+ let protocols: &[UnvalidatedProtoEntry] = &["Cons=1".parse().unwrap()];
+ let listed = ProtoverVote::compute(protocols, &1);
+ assert_eq!("Cons=1", listed.to_string());
+}
+
+#[test]
+fn protover_compute_vote_returns_two_protocols_for_two_matching() {
+ let protocols: &[UnvalidatedProtoEntry] = &["Link=1 Cons=1".parse().unwrap()];
+ let listed = ProtoverVote::compute(protocols, &1);
+ assert_eq!("Cons=1 Link=1", listed.to_string());
+}
+
+#[test]
+fn protover_compute_vote_returns_one_protocol_when_one_out_of_two_matches() {
+ let protocols: &[UnvalidatedProtoEntry] = &["Cons=1 Link=2".parse().unwrap(), "Cons=1".parse().unwrap()];
+ let listed = ProtoverVote::compute(protocols, &2);
+ assert_eq!("Cons=1", listed.to_string());
+}
+
+#[test]
+fn protover_compute_vote_returns_protocols_that_it_doesnt_currently_support() {
+ let protocols: &[UnvalidatedProtoEntry] = &["Foo=1 Cons=2".parse().unwrap(), "Bar=1".parse().unwrap()];
+ let listed = ProtoverVote::compute(protocols, &1);
+ assert_eq!("Bar=1 Cons=2 Foo=1", listed.to_string());
+}
+
+#[test]
+fn protover_compute_vote_returns_matching_for_mix() {
+ let protocols: &[UnvalidatedProtoEntry] = &["Link=1-10,500 Cons=1,3-7,8".parse().unwrap()];
+ let listed = ProtoverVote::compute(protocols, &1);
+ assert_eq!("Cons=1,3-8 Link=1-10,500", listed.to_string());
+}
+
+#[test]
+fn protover_compute_vote_returns_matching_for_longer_mix() {
+ let protocols: &[UnvalidatedProtoEntry] = &[
+ "Desc=1-10,500 Cons=1,3-7,8".parse().unwrap(),
+ "Link=123-456,78 Cons=2-6,8 Desc=9".parse().unwrap(),
+ ];
+
+ let listed = ProtoverVote::compute(protocols, &1);
+ assert_eq!("Cons=1-8 Desc=1-10,500 Link=78,123-456", listed.to_string());
+}
+
+#[test]
+fn protover_compute_vote_returns_matching_for_longer_mix_with_threshold_two() {
+ let protocols: &[UnvalidatedProtoEntry] = &[
+ "Desc=1-10,500 Cons=1,3-7,8".parse().unwrap(),
+ "Link=123-456,78 Cons=2-6,8 Desc=9".parse().unwrap(),
+ ];
+
+ let listed = ProtoverVote::compute(protocols, &2);
+ assert_eq!("Cons=3-6,8 Desc=9", listed.to_string());
+}
+
+#[test]
+fn protover_compute_vote_handles_duplicated_versions() {
+ let protocols: &[UnvalidatedProtoEntry] = &["Cons=1".parse().unwrap(), "Cons=1".parse().unwrap()];
+ assert_eq!("Cons=1", ProtoverVote::compute(protocols, &2).to_string());
+
+ let protocols: &[UnvalidatedProtoEntry] = &["Cons=1-2".parse().unwrap(), "Cons=1-2".parse().unwrap()];
+ assert_eq!("Cons=1-2", ProtoverVote::compute(protocols, &2).to_string());
+}
+
+#[test]
+fn protover_compute_vote_handles_invalid_proto_entries() {
+ let protocols: &[UnvalidatedProtoEntry] = &[
+ "Cons=1".parse().unwrap(),
+ "Cons=1".parse().unwrap(),
+ "Dinosaur=1".parse().unwrap(),
+ ];
+ assert_eq!("Cons=1", ProtoverVote::compute(protocols, &2).to_string());
+}
+
+#[test]
+fn parse_protocol_with_single_protocol_and_two_nonsequential_versions() {
+ let _: ProtoEntry = "Desc=1,2".parse().unwrap();
+}
+
+#[test]
+fn protover_is_supported_here_returns_true_for_supported_protocol() {
+ assert_eq!(true, protover::is_supported_here(&protover::Protocol::Cons, &1));
+}
+
+#[test]
+fn protover_is_supported_here_returns_false_for_unsupported_protocol() {
+ assert_eq!(false, protover::is_supported_here(&protover::Protocol::Cons, &5));
+}
+
+#[test]
+fn protocol_all_supported_with_single_proto_and_single_version() {
+ let protocol: UnvalidatedProtoEntry = "Cons=1".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocol.all_supported();
+ assert_eq!(true, unsupported.is_none());
+}
+
+#[test]
+fn protocol_all_supported_with_single_protocol_and_multiple_versions() {
+ let protocol: UnvalidatedProtoEntry = "Cons=1-2".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocol.all_supported();
+ assert_eq!(true, unsupported.is_none());
+}
+
+#[test]
+fn protocol_all_supported_with_different_single_protocol_and_single_version() {
+ let protocol: UnvalidatedProtoEntry = "HSDir=1".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocol.all_supported();
+ assert_eq!(true, unsupported.is_none());
+}
+
+#[test]
+fn protocol_all_supported_with_single_protocol_and_supported_version() {
+ let protocol: UnvalidatedProtoEntry = "Desc=2".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocol.all_supported();
+ assert_eq!(true, unsupported.is_none());
+}
+
+#[test]
+fn protocol_all_supported_with_two_protocols_and_single_version() {
+ let protocols: UnvalidatedProtoEntry = "Cons=1 HSDir=1".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
+ assert_eq!(true, unsupported.is_none());
+}
+
+#[test]
+fn protocol_all_supported_with_single_protocol_and_two_nonsequential_versions() {
+ let protocol: UnvalidatedProtoEntry = "Desc=1,2".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocol.all_supported();
+ assert_eq!(true, unsupported.is_none());
+}
+
+#[test]
+fn protocol_all_supported_with_single_protocol_and_two_sequential_versions() {
+ let protocol: UnvalidatedProtoEntry = "Desc=1-2".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocol.all_supported();
+ assert_eq!(true, unsupported.is_none());
+}
+
+#[test]
+fn protocol_all_supported_with_single_protocol_and_protocol_range() {
+ let protocol: UnvalidatedProtoEntry = "Link=1-4".parse().unwrap();
+ let unsupported: Option<UnvalidatedProtoEntry> = protocol.all_supported();
+ assert_eq!(true, unsupported.is_none());
+}
+
+// By allowing us to add to votes, the C implementation allows us to
+// exceed the limit.
+#[test]
+fn protover_compute_vote_may_exceed_limit() {
+ let proto1: UnvalidatedProtoEntry = "Sleen=1-65535".parse().unwrap();
+ let proto2: UnvalidatedProtoEntry = "Sleen=100000".parse().unwrap();
+
+ let _result: UnvalidatedProtoEntry = ProtoverVote::compute(&[proto1, proto2], &1);
+}
+
+#[test]
+fn protover_all_supported_should_exclude_versions_we_actually_do_support() {
+ let proto: UnvalidatedProtoEntry = "Link=3-999".parse().unwrap();
+ let result: String = proto.all_supported().unwrap().to_string();
+
+ assert_eq!(result, "Link=6-999".to_string());
+}
+
+#[test]
+fn protover_all_supported_should_exclude_versions_we_actually_do_support_complex1() {
+ let proto: UnvalidatedProtoEntry = "Link=1-3,345-666".parse().unwrap();
+ let result: String = proto.all_supported().unwrap().to_string();
+
+ assert_eq!(result, "Link=345-666".to_string());
+}
+
+#[test]
+fn protover_all_supported_should_exclude_versions_we_actually_do_support_complex2() {
+ let proto: UnvalidatedProtoEntry = "Link=1-3,5-12".parse().unwrap();
+ let result: String = proto.all_supported().unwrap().to_string();
+
+ assert_eq!(result, "Link=6-12".to_string());
+}
+
+#[test]
+fn protover_all_supported_should_exclude_some_versions_and_entire_protocols() {
+ let proto: UnvalidatedProtoEntry = "Link=1-3,5-12 Quokka=9000-9001".parse().unwrap();
+ let result: String = proto.all_supported().unwrap().to_string();
+
+ assert_eq!(result, "Link=6-12 Quokka=9000-9001".to_string());
+}
+
+#[test]
+fn protover_all_supported_should_not_dos_anyones_computer() {
+ let proto: UnvalidatedProtoEntry = "Sleen=0-2147483648".parse().unwrap();
+ let result: String = proto.all_supported().unwrap().to_string();
+
+ assert_eq!(result, "Sleen=0-2147483648".to_string());
+}
+
+#[test]
+fn protover_all_supported_should_not_dos_anyones_computer_max_versions() {
+ let proto: UnvalidatedProtoEntry = "Sleen=0-4294967294".parse().unwrap();
+ let result: String = proto.all_supported().unwrap().to_string();
+
+ assert_eq!(result, "Sleen=0-4294967294".to_string());
+}
+
+#[test]
+// C_RUST_DIFFERS: The C will return true (e.g. saying "yes, that's supported")
+// but set the msg to NULL (??? seems maybe potentially bad). The Rust will
+// simply return a None.
+fn protover_all_supported_should_return_empty_string_for_weird_thing() {
+ let proto: UnvalidatedProtoEntry = "Fribble=".parse().unwrap();
+ let result: Option<UnvalidatedProtoEntry> = proto.all_supported();
+
+ assert!(result.is_none());
+}
+
+#[test]
+fn protover_unvalidatedprotoentry_should_err_entirely_unparseable_things() {
+ let proto: Result<UnvalidatedProtoEntry, ProtoverError> = "Fribble".parse();
+
+ assert_eq!(Err(ProtoverError::Unparseable), proto);
+}
+
+#[test]
+fn protover_all_supported_over_maximum_limit() {
+ let proto: Result<UnvalidatedProtoEntry, ProtoverError> = "Sleen=0-4294967295".parse();
+
+ assert_eq!(Err(ProtoverError::ExceedsMax), proto);
+}
diff --git a/src/rust/smartlist/Cargo.toml b/src/rust/smartlist/Cargo.toml
new file mode 100644
index 0000000000..6ddcbee8e9
--- /dev/null
+++ b/src/rust/smartlist/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+authors = ["The Tor Project"]
+version = "0.0.1"
+name = "smartlist"
+
+[dependencies]
+libc = "0.2.39"
+
+[lib]
+name = "smartlist"
+path = "lib.rs"
+crate_type = ["rlib", "staticlib"]
+
diff --git a/src/rust/smartlist/lib.rs b/src/rust/smartlist/lib.rs
new file mode 100644
index 0000000000..14a8148315
--- /dev/null
+++ b/src/rust/smartlist/lib.rs
@@ -0,0 +1,8 @@
+// Copyright (c) 2016-2017, The Tor Project, Inc. */
+// See LICENSE for licensing information */
+
+extern crate libc;
+
+mod smartlist;
+
+pub use smartlist::*;
diff --git a/src/rust/smartlist/smartlist.rs b/src/rust/smartlist/smartlist.rs
new file mode 100644
index 0000000000..2a822d89f4
--- /dev/null
+++ b/src/rust/smartlist/smartlist.rs
@@ -0,0 +1,115 @@
+// Copyright (c) 2016-2017, The Tor Project, Inc. */
+// See LICENSE for licensing information */
+
+use std::slice;
+use libc::{c_char, c_int};
+use std::ffi::CStr;
+
+/// Smartlists are a type used in C code in tor to define a collection of a
+/// generic type, which has a capacity and a number used. Each Smartlist
+/// defines how to extract the list of values from the underlying C structure
+///
+/// Implementations are required to have a C representation, as this module
+/// serves purely to translate smartlists as defined in tor to vectors in Rust.
+pub trait Smartlist<T> {
+ fn get_list(&self) -> Vec<T>;
+}
+
+#[repr(C)]
+pub struct Stringlist {
+ pub list: *const *const c_char,
+ pub num_used: c_int,
+ pub capacity: c_int,
+}
+
+impl Smartlist<String> for Stringlist {
+ fn get_list(&self) -> Vec<String> {
+ let empty: Vec<String> = Vec::new();
+ let mut rust_list: Vec<String> = Vec::new();
+
+ if self.list.is_null() || self.num_used == 0 {
+ return empty;
+ }
+
+ // unsafe, as we need to extract the smartlist list into a vector of
+ // pointers, and then transform each element into a Rust string.
+ let elems: &[*const c_char] =
+ unsafe { slice::from_raw_parts(self.list, self.num_used as usize) };
+
+ for elem in elems.iter() {
+ if elem.is_null() {
+ continue;
+ }
+
+ // unsafe, as we need to create a cstring from the referenced
+ // element
+ let c_string = unsafe { CStr::from_ptr(*elem) };
+
+ let r_string = match c_string.to_str() {
+ Ok(n) => n,
+ Err(_) => return empty,
+ };
+
+ rust_list.push(String::from(r_string));
+ }
+
+ rust_list
+ }
+}
+
+// TODO: CHK: this module maybe should be tested from a test in C with a
+// smartlist as defined in tor.
+#[cfg(test)]
+mod test {
+ #[test]
+ fn test_get_list_of_strings() {
+ extern crate libc;
+
+ use std::ffi::CString;
+ use libc::c_char;
+
+ use super::Smartlist;
+ use super::Stringlist;
+
+ {
+ // test to verify that null pointers are gracefully handled
+ use std::ptr;
+
+ let sl = Stringlist {
+ list: ptr::null(),
+ num_used: 0,
+ capacity: 0,
+ };
+
+ let data = sl.get_list();
+ assert_eq!(0, data.len());
+ }
+
+ {
+ let args = vec![String::from("a"), String::from("b")];
+
+ // for each string, transform it into a CString
+ let c_strings: Vec<_> = args.iter()
+ .map(|arg| CString::new(arg.as_str()).unwrap())
+ .collect();
+
+ // then, collect a pointer for each CString
+ let p_args: Vec<_> =
+ c_strings.iter().map(|arg| arg.as_ptr()).collect();
+
+ let p: *const *const c_char = p_args.as_ptr();
+
+ // This is the representation that we expect when receiving a
+ // smartlist at the Rust/C FFI layer.
+ let sl = Stringlist {
+ list: p,
+ num_used: 2,
+ capacity: 2,
+ };
+
+ let data = sl.get_list();
+ assert_eq!("a", &data[0]);
+ assert_eq!("b", &data[1]);
+ }
+ }
+}
diff --git a/src/rust/tor_allocate/Cargo.toml b/src/rust/tor_allocate/Cargo.toml
new file mode 100644
index 0000000000..468425f115
--- /dev/null
+++ b/src/rust/tor_allocate/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+authors = ["The Tor Project"]
+version = "0.0.1"
+name = "tor_allocate"
+
+[dependencies]
+libc = "=0.2.39"
+
+[lib]
+name = "tor_allocate"
+path = "lib.rs"
+crate_type = ["rlib", "staticlib"]
+
diff --git a/src/rust/tor_allocate/lib.rs b/src/rust/tor_allocate/lib.rs
new file mode 100644
index 0000000000..937a5dcf63
--- /dev/null
+++ b/src/rust/tor_allocate/lib.rs
@@ -0,0 +1,15 @@
+// Copyright (c) 2016-2017, The Tor Project, Inc. */
+// See LICENSE for licensing information */
+
+//! Allocation helper functions that allow data to be allocated in Rust
+//! using tor's specified allocator. In doing so, this can be later freed
+//! from C.
+//!
+//! This is currently a temporary solution, we will later use tor's allocator
+//! by default for any allocation that occurs in Rust. However, as this will
+//! stabalize in 2018, we can use this as a temporary measure.
+
+extern crate libc;
+
+mod tor_allocate;
+pub use tor_allocate::*;
diff --git a/src/rust/tor_allocate/tor_allocate.rs b/src/rust/tor_allocate/tor_allocate.rs
new file mode 100644
index 0000000000..359df1cd7a
--- /dev/null
+++ b/src/rust/tor_allocate/tor_allocate.rs
@@ -0,0 +1,97 @@
+// Copyright (c) 2016-2017, The Tor Project, Inc. */
+// See LICENSE for licensing information */
+
+use libc::{c_char, c_void};
+use std::{ptr, slice, mem};
+
+#[cfg(not(test))]
+extern "C" {
+ fn tor_malloc_(size: usize) -> *mut c_void;
+}
+
+// Defined only for tests, used for testing purposes, so that we don't need
+// to link to tor C files. Uses the system allocator
+#[cfg(test)]
+unsafe extern "C" fn tor_malloc_(size: usize) -> *mut c_void {
+ use libc::malloc;
+ malloc(size)
+}
+
+/// Allocate memory using tor_malloc_ and copy an existing string into the
+/// allocated buffer, returning a pointer that can later be called in C.
+///
+/// # Inputs
+///
+/// * `src`, a reference to a String.
+///
+/// # Returns
+///
+/// A `*mut c_char` that should be freed by tor_free in C
+///
+pub fn allocate_and_copy_string(src: &String) -> *mut c_char {
+ let bytes: &[u8] = src.as_bytes();
+
+ let size = mem::size_of_val::<[u8]>(bytes);
+ let size_one_byte = mem::size_of::<u8>();
+
+ // handle integer overflow when adding one to the calculated length
+ let size_with_null_byte = match size.checked_add(size_one_byte) {
+ Some(n) => n,
+ None => return ptr::null_mut(),
+ };
+
+ let dest = unsafe { tor_malloc_(size_with_null_byte) as *mut u8 };
+
+ if dest.is_null() {
+ return ptr::null_mut();
+ }
+
+ unsafe { ptr::copy_nonoverlapping(bytes.as_ptr(), dest, size) };
+
+ // set the last byte as null, using the ability to index into a slice
+ // rather than doing pointer arithmatic
+ let slice = unsafe { slice::from_raw_parts_mut(dest, size_with_null_byte) };
+ slice[size] = 0; // add a null terminator
+
+ dest as *mut c_char
+}
+
+#[cfg(test)]
+mod test {
+
+ #[test]
+ fn test_allocate_and_copy_string_with_empty() {
+ use std::ffi::CStr;
+ use libc::{free, c_void};
+
+ use tor_allocate::allocate_and_copy_string;
+
+ let empty = String::new();
+ let allocated_empty = allocate_and_copy_string(&empty);
+
+ let allocated_empty_rust =
+ unsafe { CStr::from_ptr(allocated_empty).to_str().unwrap() };
+
+ assert_eq!("", allocated_empty_rust);
+
+ unsafe { free(allocated_empty as *mut c_void) };
+ }
+
+ #[test]
+ fn test_allocate_and_copy_string_with_not_empty_string() {
+ use std::ffi::CStr;
+ use libc::{free, c_void};
+
+ use tor_allocate::allocate_and_copy_string;
+
+ let empty = String::from("foo bar biz");
+ let allocated_empty = allocate_and_copy_string(&empty);
+
+ let allocated_empty_rust =
+ unsafe { CStr::from_ptr(allocated_empty).to_str().unwrap() };
+
+ assert_eq!("foo bar biz", allocated_empty_rust);
+
+ unsafe { free(allocated_empty as *mut c_void) };
+ }
+}
diff --git a/src/rust/tor_rust/Cargo.toml b/src/rust/tor_rust/Cargo.toml
new file mode 100644
index 0000000000..86fad3ee76
--- /dev/null
+++ b/src/rust/tor_rust/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+authors = ["The Tor Project"]
+name = "tor_rust"
+version = "0.1.0"
+
+[lib]
+name = "tor_rust"
+path = "lib.rs"
+crate_type = ["rlib", "staticlib"]
+
+[dependencies.tor_util]
+path = "../tor_util"
+
+[dependencies.protover]
+path = "../protover"
+
diff --git a/src/rust/tor_rust/include.am b/src/rust/tor_rust/include.am
new file mode 100644
index 0000000000..40511bf9f2
--- /dev/null
+++ b/src/rust/tor_rust/include.am
@@ -0,0 +1,28 @@
+EXTRA_DIST +=\
+ src/rust/tor_rust/Cargo.toml \
+ src/rust/tor_rust/lib.rs
+
+EXTRA_CARGO_OPTIONS=
+
+src/rust/target/release/@TOR_RUST_STATIC_NAME@: FORCE
+ ( cd "$(abs_top_builddir)/src/rust" ; \
+ CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \
+ $(CARGO) build --release $(EXTRA_CARGO_OPTIONS) \
+ $(CARGO_ONLINE) \
+ --manifest-path "$(abs_top_srcdir)/src/rust/tor_rust/Cargo.toml" )
+
+distclean-rust:
+ ( cd "$(abs_top_builddir)/src/rust" ; \
+ CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \
+ $(CARGO) clean $(EXTRA_CARGO_OPTIONS) \
+ $(CARGO_ONLINE) \
+ --manifest-path "$(abs_top_srcdir)/src/rust/tor_rust/Cargo.toml" )
+ rm -rf "$(abs_top_builddir)/src/rust/registry"
+
+if USE_RUST
+build-rust: src/rust/target/release/@TOR_RUST_STATIC_NAME@
+else
+build-rust:
+endif
+
+FORCE:
diff --git a/src/rust/tor_rust/lib.rs b/src/rust/tor_rust/lib.rs
new file mode 100644
index 0000000000..c1585c0480
--- /dev/null
+++ b/src/rust/tor_rust/lib.rs
@@ -0,0 +1,5 @@
+extern crate tor_util;
+extern crate protover;
+
+pub use tor_util::*;
+pub use protover::*;
diff --git a/src/rust/tor_util/Cargo.toml b/src/rust/tor_util/Cargo.toml
index f175fbdfb0..b540d8c847 100644
--- a/src/rust/tor_util/Cargo.toml
+++ b/src/rust/tor_util/Cargo.toml
@@ -8,6 +8,9 @@ name = "tor_util"
path = "lib.rs"
crate_type = ["rlib", "staticlib"]
+[dependencies.tor_allocate]
+path = "../tor_allocate"
+
[dependencies]
-libc = "*"
+libc = "=0.2.39"
diff --git a/src/rust/tor_util/ffi.rs b/src/rust/tor_util/ffi.rs
index af4bfc41af..5c3cdba4be 100644
--- a/src/rust/tor_util/ffi.rs
+++ b/src/rust/tor_util/ffi.rs
@@ -1,56 +1,26 @@
-//! FFI functions, only to be called from C.
-//!
-//! Equivalent C versions of these live in `src/common/compat_rust.c`
-
-use std::mem::forget;
-use std::ffi::CString;
+// Copyright (c) 2016-2017, The Tor Project, Inc. */
+// See LICENSE for licensing information */
-use libc;
-use rust_string::RustString;
-
-/// Free the passed `RustString` (`rust_str_t` in C), to be used in place of
-/// `tor_free`().
-///
-/// # Examples
-/// ```c
-/// rust_str_t r_s = rust_welcome_string();
-/// rust_str_free(r_s);
-/// ```
-#[no_mangle]
-#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
-pub unsafe extern "C" fn rust_str_free(_str: RustString) {
- // Empty body: Just drop _str and we're done (Drop takes care of it).
-}
+//! FFI functions to announce Rust support during tor startup, only to be
+//! called from C.
+//!
-/// Lends an immutable, NUL-terminated C String.
-///
-/// # Examples
-/// ```c
-/// rust_str_t r_s = rust_welcome_string();
-/// const char *s = rust_str_get(r_s);
-/// printf("%s", s);
-/// rust_str_free(r_s);
-/// ```
-#[no_mangle]
-pub unsafe extern "C" fn rust_str_get(str: RustString) -> *const libc::c_char {
- let res = str.as_ptr();
- forget(str);
- res
-}
+use libc::c_char;
+use tor_allocate::allocate_and_copy_string;
/// Returns a short string to announce Rust support during startup.
///
/// # Examples
/// ```c
-/// rust_str_t r_s = rust_welcome_string();
-/// const char *s = rust_str_get(r_s);
-/// printf("%s", s);
-/// rust_str_free(r_s);
+/// char *rust_str = rust_welcome_string();
+/// printf("%s", rust_str);
+/// tor_free(rust_str);
/// ```
#[no_mangle]
-pub extern "C" fn rust_welcome_string() -> RustString {
- let s = CString::new("Tor is running with Rust integration. Please report \
- any bugs you encouter.")
- .unwrap();
- RustString::from(s)
+pub extern "C" fn rust_welcome_string() -> *mut c_char {
+ let rust_welcome = String::from(
+ "Tor is running with Rust integration. Please report \
+ any bugs you encounter.",
+ );
+ allocate_and_copy_string(&rust_welcome)
}
diff --git a/src/rust/tor_util/include.am b/src/rust/tor_util/include.am
deleted file mode 100644
index c110602895..0000000000
--- a/src/rust/tor_util/include.am
+++ /dev/null
@@ -1,12 +0,0 @@
-EXTRA_DIST +=\
- src/rust/tor_util/Cargo.toml \
- src/rust/tor_util/lib.rs \
- src/rust/tor_util/ffi.rs \
- src/rust/tor_util/rust_string.rs
-
-src/rust/target/release/@TOR_RUST_UTIL_STATIC_NAME@: FORCE
- ( cd "$(abs_top_srcdir)/src/rust/tor_util" ; \
- CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \
- $(CARGO) build --release --quiet $(CARGO_ONLINE) )
-
-FORCE:
diff --git a/src/rust/tor_util/lib.rs b/src/rust/tor_util/lib.rs
index 79d583d1ae..12cb3896b6 100644
--- a/src/rust/tor_util/lib.rs
+++ b/src/rust/tor_util/lib.rs
@@ -1,13 +1,11 @@
-//! C <-> Rust compatibility helpers and types.
-//!
-//! Generically useful, small scale helpers should go here. This goes for both
-//! the C side (in the form of the ffi module) as well as the Rust side
-//! (individual modules per functionality). The corresponding C stuff lives in
-//! `src/common/compat_rust.{c,h}`.
+// Copyright (c) 2016-2017, The Tor Project, Inc. */
+// See LICENSE for licensing information */
+
+//! Small module to announce Rust support during startup for demonstration
+//! purposes.
extern crate libc;
+extern crate tor_allocate;
-mod rust_string;
pub mod ffi;
-
-pub use rust_string::*;
+pub mod strings;
diff --git a/src/rust/tor_util/rust_string.rs b/src/rust/tor_util/rust_string.rs
deleted file mode 100644
index 46ec3fd7a8..0000000000
--- a/src/rust/tor_util/rust_string.rs
+++ /dev/null
@@ -1,101 +0,0 @@
-use std::ffi::CString;
-use std::mem::forget;
-use libc;
-
-/// Compatibility wrapper for strings allocated in Rust and passed to C.
-///
-/// Rust doesn't ensure the safety of freeing memory across an FFI boundary, so
-/// we need to take special care to ensure we're not accidentally calling
-/// `tor_free`() on any string allocated in Rust. To more easily differentiate
-/// between strings that possibly (if Rust support is enabled) were allocated
-/// in Rust, C has the `rust_str_t` helper type. The equivalent on the Rust
-/// side is `RustString`.
-///
-/// Note: This type must not be used for strings allocated in C.
-#[repr(C)]
-#[derive(Debug)]
-pub struct RustString(*mut libc::c_char);
-
-impl RustString {
- /// Returns a pointer to the underlying NUL-terminated byte array.
- ///
- /// Note that this function is not typically useful for Rust callers,
- /// except in a direct FFI context.
- ///
- /// # Examples
- /// ```
- /// # use tor_util::RustString;
- /// use std::ffi::CString;
- ///
- /// let r = RustString::from(CString::new("asdf").unwrap());
- /// let c_str = r.as_ptr();
- /// assert_eq!(b'a', unsafe { *c_str as u8});
- /// ```
- pub fn as_ptr(&self) -> *const libc::c_char {
- self.0 as *const libc::c_char
- }
-}
-
-impl From<CString> for RustString {
- /// Constructs a new `RustString`
- ///
- /// # Examples
- /// ```
- /// # use tor_util::RustString;
- /// use std::ffi::CString;
- ///
- /// let r = RustString::from(CString::new("asdf").unwrap());
- /// ```
- fn from(str: CString) -> RustString {
- RustString(str.into_raw())
- }
-}
-
-impl Into<CString> for RustString {
- /// Reconstructs a `CString` from this `RustString`.
- ///
- /// Useful to take ownership back from a `RustString` that was given to C
- /// code.
- ///
- /// # Examples
- /// ```
- /// # use tor_util::RustString;
- /// use std::ffi::CString;
- ///
- /// let cs = CString::new("asdf").unwrap();
- /// let r = RustString::from(cs.clone());
- /// let cs2 = r.into();
- /// assert_eq!(cs, cs2);
- /// ```
- fn into(self) -> CString {
- // Calling from_raw is always OK here: We only construct self using
- // valid CStrings and don't expose anything that could mutate it
- let ret = unsafe { CString::from_raw(self.0) };
- forget(self);
- ret
- }
-}
-
-impl Drop for RustString {
- fn drop(&mut self) {
- // Don't use into() here, because we would need to move out of
- // self. Same safety consideration. Immediately drop the created
- // CString, which takes care of freeing the wrapped string.
- unsafe { CString::from_raw(self.0) };
- }
-}
-
-#[cfg(test)]
-mod test {
- use std::mem;
- use super::*;
-
- use libc;
-
- /// Ensures we're not adding overhead by using RustString.
- #[test]
- fn size_of() {
- assert_eq!(mem::size_of::<*mut libc::c_char>(),
- mem::size_of::<RustString>())
- }
-}
diff --git a/src/rust/tor_util/strings.rs b/src/rust/tor_util/strings.rs
new file mode 100644
index 0000000000..9321ce4f85
--- /dev/null
+++ b/src/rust/tor_util/strings.rs
@@ -0,0 +1,82 @@
+// Copyright (c) 2016-2017, The Tor Project, Inc. */
+// See LICENSE for licensing information */
+
+//! Utilities for working with static strings.
+
+use std::ffi::CStr;
+
+/// A byte-array containing a single NUL byte (`b"\0"`).
+pub const NUL_BYTE: &'static [u8] = b"\0";
+
+/// Determine if a byte slice is a C-like string.
+///
+/// These checks guarantee that:
+///
+/// 1. there are no intermediate NUL bytes
+/// 2. the last byte *is* a NUL byte
+///
+/// # Warning
+///
+/// This function does _not_ guarantee that the bytes represent any valid
+/// encoding such as ASCII or UTF-8.
+///
+/// # Examples
+///
+/// ```
+/// # use tor_util::strings::byte_slice_is_c_like;
+/// #
+/// let bytes: &[u8] = b"foo bar baz";
+///
+/// assert!(byte_slice_is_c_like(&bytes) == false);
+///
+/// let bytes: &[u8] = b"foo\0bar baz";
+///
+/// assert!(byte_slice_is_c_like(&bytes) == false);
+///
+/// let bytes: &[u8] = b"foo bar baz\0";
+///
+/// assert!(byte_slice_is_c_like(&bytes) == true);
+/// ```
+pub fn byte_slice_is_c_like(bytes: &[u8]) -> bool {
+ if !bytes[..bytes.len() - 1].contains(&0x00) && bytes[bytes.len() - 1] == 0x00 {
+ return true;
+ }
+ false
+}
+
+/// Get a static `CStr` containing a single `NUL_BYTE`.
+///
+/// # Examples
+///
+/// When used as follows in a Rust FFI function, which could be called
+/// from C:
+///
+/// ```
+/// # extern crate libc;
+/// # extern crate tor_util;
+/// #
+/// # use tor_util::strings::empty_static_cstr;
+/// use libc::c_char;
+/// use std::ffi::CStr;
+///
+/// pub extern "C" fn give_c_code_an_empty_static_string() -> *const c_char {
+/// let empty: &'static CStr = empty_static_cstr();
+///
+/// empty.as_ptr()
+/// }
+///
+/// # fn main() {
+/// # give_c_code_an_empty_static_string();
+/// # }
+/// ```
+///
+/// This equates to an "empty" `const char*` static string in C.
+pub fn empty_static_cstr() -> &'static CStr {
+ let empty: &'static CStr;
+
+ unsafe {
+ empty = CStr::from_bytes_with_nul_unchecked(NUL_BYTE);
+ }
+
+ empty
+}
diff --git a/src/rust/tor_util/tests/rust_string.rs b/src/rust/tor_util/tests/rust_string.rs
deleted file mode 100644
index 1ff605a43c..0000000000
--- a/src/rust/tor_util/tests/rust_string.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-extern crate tor_util;
-extern crate libc;
-
-use std::ffi::CString;
-use tor_util::RustString;
-
-#[test]
-fn rust_string_conversions_preserve_c_string() {
- let s = CString::new("asdf foo").unwrap();
- let r = RustString::from(s.clone());
- let r2 = RustString::from(s.clone());
- let c = r2.as_ptr();
- assert_eq!(unsafe { libc::strlen(c) }, 8);
- let c_str = r.into();
- assert_eq!(s, c_str);
-}
-
-#[test]
-fn empty_string() {
- let s = CString::new("").unwrap();
- let r = RustString::from(s.clone());
- let c = r.as_ptr();
- assert_eq!(unsafe { libc::strlen(c) }, 0);
- let c_str = r.into();
- assert_eq!(s, c_str);
-}
-
-#[test]
-fn c_string_with_unicode() {
- // The euro sign is three bytes
- let s = CString::new("asd€asd").unwrap();
- let r = RustString::from(s.clone());
- let c = r.as_ptr();
- assert_eq!(unsafe { libc::strlen(c) }, 9);
- let c_str = r.into();
- assert_eq!(s, c_str);
-}
diff --git a/src/test/Makefile.nmake b/src/test/Makefile.nmake
index 605f1a92c3..cfbe281b94 100644
--- a/src/test/Makefile.nmake
+++ b/src/test/Makefile.nmake
@@ -18,6 +18,7 @@ TEST_OBJECTS = test.obj test_addr.obj test_channel.obj test_channeltls.obj \
test_config.obj test_connection.obj \
test_cell_formats.obj test_relay.obj test_replay.obj \
test_channelpadding.obj \
+ test_circuitstats.obj \
test_scheduler.obj test_introduce.obj test_hs.obj tinytest.obj
tinytest.obj: ..\ext\tinytest.c
diff --git a/src/test/bench.c b/src/test/bench.c
index b7b123eee2..92d7a244f7 100644
--- a/src/test/bench.c
+++ b/src/test/bench.c
@@ -3,11 +3,6 @@
* Copyright (c) 2007-2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
-extern const char tor_git_revision[];
-/* Ordinarily defined in tor_main.c; this bit is just here to provide one
- * since we're not linking to tor_main.c */
-const char tor_git_revision[] = "";
-
/**
* \file bench.c
* \brief Benchmarks for lower level Tor modules.
@@ -718,11 +713,15 @@ main(int argc, const char **argv)
printf("Couldn't seed RNG; exiting.\n");
return 1;
}
+
+ init_protocol_warning_severity_level();
crypto_init_siphash_key();
options = options_new();
init_logging(1);
options->command = CMD_RUN_UNITTESTS;
options->DataDirectory = tor_strdup("");
+ options->KeyDirectory = tor_strdup("");
+ options->CacheDirectory = tor_strdup("");
options_init(options);
if (set_options(options, &errmsg) < 0) {
printf("Failed to set initial options: %s\n", errmsg);
diff --git a/src/test/fakechans.h b/src/test/fakechans.h
index c0de430e3d..ab5d8461b6 100644
--- a/src/test/fakechans.h
+++ b/src/test/fakechans.h
@@ -20,7 +20,6 @@ void scheduler_release_channel_mock(channel_t *ch);
/* Query some counters used by the exposed mocks */
int get_mock_scheduler_has_waiting_cells_count(void);
-int get_mock_scheduler_release_channel_count(void);
#endif /* !defined(TOR_FAKECHANS_H) */
diff --git a/src/test/fuzz/fuzzing_common.c b/src/test/fuzz/fuzzing_common.c
index 1e98eb6c85..a96552f0fc 100644
--- a/src/test/fuzz/fuzzing_common.c
+++ b/src/test/fuzz/fuzzing_common.c
@@ -9,9 +9,6 @@
#include "crypto.h"
#include "crypto_ed25519.h"
-extern const char tor_git_revision[];
-const char tor_git_revision[] = "";
-
static or_options_t *mock_options = NULL;
static const or_options_t *
mock_get_options(void)
@@ -113,6 +110,9 @@ global_init(void)
/* Make BUG() and nonfatal asserts crash */
tor_set_failed_assertion_callback(abort);
+
+ /* Make protocol warnings handled correctly. */
+ init_protocol_warning_severity_level();
}
#ifdef LLVM_FUZZ
diff --git a/src/test/include.am b/src/test/include.am
index 2f6789c58d..93fc008419 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -9,6 +9,7 @@ TESTS_ENVIRONMENT = \
export builddir="$(builddir)"; \
export TESTING_TOR_BINARY="$(TESTING_TOR_BINARY)"; \
export CARGO="$(CARGO)"; \
+ export EXTRA_CARGO_OPTIONS="$(EXTRA_CARGO_OPTIONS)"; \
export CARGO_ONLINE="$(CARGO_ONLINE)";
TESTSCRIPTS = \
@@ -99,6 +100,7 @@ src_test_test_SOURCES = \
src/test/test_circuitmux.c \
src/test/test_circuitbuild.c \
src/test/test_circuituse.c \
+ src/test/test_circuitstats.c \
src/test/test_compat_libevent.c \
src/test/test_config.c \
src/test/test_connection.c \
@@ -110,11 +112,11 @@ src_test_test_SOURCES = \
src/test/test_controller_events.c \
src/test/test_crypto.c \
src/test/test_crypto_openssl.c \
- src/test/test_dos.c \
src/test/test_data.c \
src/test/test_dir.c \
src/test/test_dir_common.c \
src/test/test_dir_handle_get.c \
+ src/test/test_dos.c \
src/test/test_entryconn.c \
src/test/test_entrynodes.c \
src/test/test_guardfraction.c \
@@ -127,6 +129,7 @@ src_test_test_SOURCES = \
src/test/test_hs_service.c \
src/test/test_hs_client.c \
src/test/test_hs_intropoint.c \
+ src/test/test_hs_control.c \
src/test/test_handles.c \
src/test/test_hs_cache.c \
src/test/test_hs_descriptor.c \
@@ -154,7 +157,6 @@ src_test_test_SOURCES = \
src/test/test_routerkeys.c \
src/test/test_routerlist.c \
src/test/test_routerset.c \
- src/test/test_rust.c \
src/test/test_scheduler.c \
src/test/test_shared_random.c \
src/test/test_socks.c \
@@ -358,3 +360,5 @@ EXTRA_DIST += \
src/test/test_workqueue_pipe2.sh \
src/test/test_workqueue_socketpair.sh
+test-rust:
+ $(TESTS_ENVIRONMENT) "$(abs_top_srcdir)/src/test/test_rust.sh"
diff --git a/src/test/test-memwipe.c b/src/test/test-memwipe.c
index 484f13dd05..89d946d506 100644
--- a/src/test/test-memwipe.c
+++ b/src/test/test-memwipe.c
@@ -1,3 +1,6 @@
+/* Copyright (c) 2015-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
#include "orconfig.h"
#include <string.h>
#include <stdio.h>
diff --git a/src/test/test-timers.c b/src/test/test-timers.c
index 99715f4333..a0b5b535c2 100644
--- a/src/test/test-timers.c
+++ b/src/test/test-timers.c
@@ -133,7 +133,7 @@ main(int argc, char **argv)
ret = 0;
}
- timer_free(NULL);
+ timer_free_(NULL);
for (i = 0; i < N_TIMERS; ++i) {
timer_free(timers[i]);
diff --git a/src/test/test.c b/src/test/test.c
index a935ac36a6..2e7d399322 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -56,7 +56,6 @@
#include "routerparse.h"
#include "statefile.h"
#include "crypto_curve25519.h"
-#include "onion_ntor.h"
/** Run unit tests for the onion handshake code. */
static void
@@ -168,7 +167,7 @@ test_bad_onion_handshake(void *arg)
s_buf, s_keys, 40));
c_buf[64] ^= 33;
- /* (Let the server procede) */
+ /* (Let the server proceed) */
tt_int_op(0, OP_EQ,
onion_skin_TAP_server_handshake(c_buf, pk, NULL,
s_buf, s_keys, 40));
@@ -339,8 +338,8 @@ test_onion_queues(void *arg)
tt_int_op(0,OP_EQ, onion_num_pending(ONION_HANDSHAKE_TYPE_NTOR));
done:
- circuit_free(TO_CIRCUIT(circ1));
- circuit_free(TO_CIRCUIT(circ2));
+ circuit_free_(TO_CIRCUIT(circ1));
+ circuit_free_(TO_CIRCUIT(circ2));
tor_free(create1);
tor_free(create2);
tor_free(onionskin);
@@ -610,7 +609,7 @@ test_rend_fns(void *arg)
done:
if (descs) {
for (i = 0; i < smartlist_len(descs); i++)
- rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
+ rend_encoded_v2_service_descriptor_free_(smartlist_get(descs, i));
smartlist_free(descs);
}
if (parsed)
@@ -1191,6 +1190,7 @@ struct testgroup_t testgroups[] = {
{ "circuitlist/", circuitlist_tests },
{ "circuitmux/", circuitmux_tests },
{ "circuituse/", circuituse_tests },
+ { "circuitstats/", circuitstats_tests },
{ "compat/libevent/", compat_libevent_tests },
{ "config/", config_tests },
{ "connection/", connection_tests },
@@ -1202,10 +1202,10 @@ struct testgroup_t testgroups[] = {
{ "control/event/", controller_event_tests },
{ "crypto/", crypto_tests },
{ "crypto/openssl/", crypto_openssl_tests },
- { "dos/", dos_tests },
{ "dir/", dir_tests },
{ "dir_handle_get/", dir_handle_get_tests },
{ "dir/md/", microdesc_tests },
+ { "dos/", dos_tests },
{ "entryconn/", entryconn_tests },
{ "entrynodes/", entrynodes_tests },
{ "guardfraction/", guardfraction_tests },
@@ -1215,6 +1215,7 @@ struct testgroup_t testgroups[] = {
{ "hs_cell/", hs_cell_tests },
{ "hs_common/", hs_common_tests },
{ "hs_config/", hs_config_tests },
+ { "hs_control/", hs_control_tests },
{ "hs_descriptor/", hs_descriptor },
{ "hs_ntor/", hs_ntor_tests },
{ "hs_service/", hs_service_tests },
@@ -1241,7 +1242,6 @@ struct testgroup_t testgroups[] = {
{ "routerkeys/", routerkeys_tests },
{ "routerlist/", routerlist_tests },
{ "routerset/" , routerset_tests },
- { "rust/", rust_tests },
{ "scheduler/", scheduler_tests },
{ "socks/", socks_tests },
{ "shared-random/", sr_tests },
diff --git a/src/test/test.h b/src/test/test.h
index 3c12d2b673..26139fc5fe 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -189,6 +189,7 @@ extern struct testcase_t circuitbuild_tests[];
extern struct testcase_t circuitlist_tests[];
extern struct testcase_t circuitmux_tests[];
extern struct testcase_t circuituse_tests[];
+extern struct testcase_t circuitstats_tests[];
extern struct testcase_t compat_libevent_tests[];
extern struct testcase_t config_tests[];
extern struct testcase_t connection_tests[];
@@ -200,9 +201,9 @@ extern struct testcase_t controller_tests[];
extern struct testcase_t controller_event_tests[];
extern struct testcase_t crypto_tests[];
extern struct testcase_t crypto_openssl_tests[];
-extern struct testcase_t dos_tests[];
extern struct testcase_t dir_tests[];
extern struct testcase_t dir_handle_get_tests[];
+extern struct testcase_t dos_tests[];
extern struct testcase_t entryconn_tests[];
extern struct testcase_t entrynodes_tests[];
extern struct testcase_t guardfraction_tests[];
@@ -212,6 +213,7 @@ extern struct testcase_t hs_cache[];
extern struct testcase_t hs_cell_tests[];
extern struct testcase_t hs_common_tests[];
extern struct testcase_t hs_config_tests[];
+extern struct testcase_t hs_control_tests[];
extern struct testcase_t hs_descriptor[];
extern struct testcase_t hs_ntor_tests[];
extern struct testcase_t hs_service_tests[];
@@ -241,7 +243,6 @@ extern struct testcase_t router_tests[];
extern struct testcase_t routerkeys_tests[];
extern struct testcase_t routerlist_tests[];
extern struct testcase_t routerset_tests[];
-extern struct testcase_t rust_tests[];
extern struct testcase_t scheduler_tests[];
extern struct testcase_t storagedir_tests[];
extern struct testcase_t socks_tests[];
diff --git a/src/test/test_accounting.c b/src/test/test_accounting.c
index 7edba988a6..b0d37b2989 100644
--- a/src/test/test_accounting.c
+++ b/src/test/test_accounting.c
@@ -1,3 +1,6 @@
+/* Copyright (c) 2014-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
#include "or.h"
#include "test.h"
#define HIBERNATE_PRIVATE
diff --git a/src/test/test_address.c b/src/test/test_address.c
index f36ff6998b..9c88d37a41 100644
--- a/src/test/test_address.c
+++ b/src/test/test_address.c
@@ -763,7 +763,7 @@ test_address_get_if_addrs_list_internal(void *arg)
tt_assert(!smartlist_contains_ipv6_tor_addr(results));
done:
- free_interface_address_list(results);
+ interface_address_list_free(results);
return;
}
@@ -792,7 +792,7 @@ test_address_get_if_addrs_list_no_internal(void *arg)
tt_assert(!smartlist_contains_ipv6_tor_addr(results));
done:
- free_interface_address_list(results);
+ interface_address_list_free(results);
return;
}
@@ -834,7 +834,7 @@ test_address_get_if_addrs6_list_internal(void *arg)
}
done:
- free_interface_address6_list(results);
+ interface_address6_list_free(results);
teardown_capture_of_logs();
return;
}
@@ -878,7 +878,7 @@ test_address_get_if_addrs6_list_no_internal(void *arg)
done:
teardown_capture_of_logs();
- free_interface_address6_list(results);
+ interface_address6_list_free(results);
return;
}
@@ -943,8 +943,8 @@ test_address_get_if_addrs_internal_fail(void *arg)
done:
UNMOCK(get_interface_addresses_raw);
UNMOCK(get_interface_address6_via_udp_socket_hack);
- free_interface_address6_list(results1);
- free_interface_address6_list(results2);
+ interface_address6_list_free(results1);
+ interface_address6_list_free(results2);
return;
}
@@ -971,8 +971,8 @@ test_address_get_if_addrs_no_internal_fail(void *arg)
done:
UNMOCK(get_interface_addresses_raw);
UNMOCK(get_interface_address6_via_udp_socket_hack);
- free_interface_address6_list(results1);
- free_interface_address6_list(results2);
+ interface_address6_list_free(results1);
+ interface_address6_list_free(results2);
return;
}
diff --git a/src/test/test_buffers.c b/src/test/test_buffers.c
index 9f4e19bc5f..057d9fa2dc 100644
--- a/src/test/test_buffers.c
+++ b/src/test/test_buffers.c
@@ -450,52 +450,54 @@ test_buffer_time_tracking(void *arg)
tt_assert(buf);
monotime_coarse_set_mock_time_nsec(START_NSEC);
- const uint32_t START_MSEC = (uint32_t)monotime_coarse_absolute_msec();
+ const uint32_t START_TS = monotime_coarse_get_stamp();
/* Empty buffer means the timestamp is 0. */
- tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC));
- tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+1000));
+ tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS));
+ tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS+1000));
buf_add(buf, "ABCDEFG", 7);
- tt_int_op(1000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+1000));
+ tt_int_op(1000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS+1000));
buf2 = buf_copy(buf);
tt_assert(buf2);
tt_int_op(1234, OP_EQ,
- buf_get_oldest_chunk_timestamp(buf2, START_MSEC+1234));
+ buf_get_oldest_chunk_timestamp(buf2, START_TS+1234));
/* Now add more bytes; enough to overflow the first chunk. */
monotime_coarse_set_mock_time_nsec(START_NSEC + 123 * (uint64_t)1000000);
+ const uint32_t TS2 = monotime_coarse_get_stamp();
for (i = 0; i < 600; ++i)
buf_add(buf, "ABCDEFG", 7);
tt_int_op(4207, OP_EQ, buf_datalen(buf));
/* The oldest bytes are still in the front. */
- tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2000));
+ tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS+2000));
/* Once those bytes are dropped, the chunk is still on the first
* timestamp. */
buf_get_bytes(buf, tmp, 100);
- tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2000));
+ tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS+2000));
/* But once we discard the whole first chunk, we get the data in the second
* chunk. */
buf_get_bytes(buf, tmp, 4000);
tt_int_op(107, OP_EQ, buf_datalen(buf));
- tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2123));
+ tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, TS2+2000));
/* This time we'll be grabbing a chunk from the freelist, and making sure
its time gets updated */
monotime_coarse_set_mock_time_nsec(START_NSEC + 5617 * (uint64_t)1000000);
+ const uint32_t TS3 = monotime_coarse_get_stamp();
for (i = 0; i < 600; ++i)
buf_add(buf, "ABCDEFG", 7);
tt_int_op(4307, OP_EQ, buf_datalen(buf));
- tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2123));
+ tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, TS2+2000));
buf_get_bytes(buf, tmp, 4000);
buf_get_bytes(buf, tmp, 306);
- tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+5617));
- tt_int_op(383, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+6000));
+ tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, TS3));
+ tt_int_op(383, OP_EQ, buf_get_oldest_chunk_timestamp(buf, TS3+383));
done:
buf_free(buf);
diff --git a/src/test/test_cell_formats.c b/src/test/test_cell_formats.c
index 007f7e3d3e..88cdef383f 100644
--- a/src/test/test_cell_formats.c
+++ b/src/test/test_cell_formats.c
@@ -477,7 +477,7 @@ test_cfmt_create_cells(void *arg)
cell.command = CELL_CREATED;
tt_int_op(-1, OP_EQ, create_cell_parse(&cc, &cell));
- /* You can't acutally make an unparseable CREATE or CREATE_FAST cell. */
+ /* You can't actually make an unparseable CREATE or CREATE_FAST cell. */
/* Try some CREATE2 cells. First with a bad type. */
cell.command = CELL_CREATE2;
diff --git a/src/test/test_cell_queue.c b/src/test/test_cell_queue.c
index 69e89b69b0..df987f82ce 100644
--- a/src/test/test_cell_queue.c
+++ b/src/test/test_cell_queue.c
@@ -130,8 +130,8 @@ test_circuit_n_cells(void *arg)
tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(origin_c)), OP_EQ, 2);
done:
- circuit_free(TO_CIRCUIT(or_c));
- circuit_free(TO_CIRCUIT(origin_c));
+ circuit_free_(TO_CIRCUIT(or_c));
+ circuit_free_(TO_CIRCUIT(origin_c));
}
struct testcase_t cell_queue_tests[] = {
diff --git a/src/test/test_channel.c b/src/test/test_channel.c
index 023c2950c9..bdc9d32f78 100644
--- a/src/test/test_channel.c
+++ b/src/test/test_channel.c
@@ -6,8 +6,10 @@
#include "or.h"
#include "channel.h"
/* For channel_note_destroy_not_pending */
+#define CIRCUITLIST_PRIVATE
#include "circuitlist.h"
#include "circuitmux.h"
+#include "circuitmux_ewma.h"
/* For var_cell_free */
#include "connection_or.h"
/* For packed_cell stuff */
@@ -15,8 +17,10 @@
#include "relay.h"
/* For init/free stuff */
#include "scheduler.h"
+#include "networkstatus.h"
/* Test suite stuff */
+#include "log_test_helpers.h"
#include "test.h"
#include "fakechans.h"
@@ -26,62 +30,18 @@ static cell_t * test_chan_last_seen_fixed_cell_ptr = NULL;
static int test_chan_var_cells_recved = 0;
static var_cell_t * test_chan_last_seen_var_cell_ptr = NULL;
static int test_cells_written = 0;
-static int test_destroy_not_pending_calls = 0;
static int test_doesnt_want_writes_count = 0;
static int test_dumpstats_calls = 0;
static int test_has_waiting_cells_count = 0;
-static double test_overhead_estimate = 1.0;
static int test_releases_count = 0;
-static circuitmux_t *test_target_cmux = NULL;
-static unsigned int test_cmux_cells = 0;
static channel_t *dump_statistics_mock_target = NULL;
static int dump_statistics_mock_matches = 0;
-
-static void chan_test_channel_dump_statistics_mock(
- channel_t *chan, int severity);
-static int chan_test_channel_flush_from_first_active_circuit_mock(
- channel_t *chan, int max);
-static unsigned int chan_test_circuitmux_num_cells_mock(circuitmux_t *cmux);
-static void channel_note_destroy_not_pending_mock(channel_t *ch,
- circid_t circid);
-static void chan_test_cell_handler(channel_t *ch,
- cell_t *cell);
-static const char * chan_test_describe_transport(channel_t *ch);
-static void chan_test_dumpstats(channel_t *ch, int severity);
-static void chan_test_var_cell_handler(channel_t *ch,
- var_cell_t *var_cell);
-static void chan_test_close(channel_t *ch);
-static void chan_test_error(channel_t *ch);
-static void chan_test_finish_close(channel_t *ch);
-static const char * chan_test_get_remote_descr(channel_t *ch, int flags);
-static int chan_test_is_canonical(channel_t *ch, int req);
-static size_t chan_test_num_bytes_queued(channel_t *ch);
-static int chan_test_num_cells_writeable(channel_t *ch);
-static int chan_test_write_cell(channel_t *ch, cell_t *cell);
-static int chan_test_write_packed_cell(channel_t *ch,
- packed_cell_t *packed_cell);
-static int chan_test_write_var_cell(channel_t *ch, var_cell_t *var_cell);
-static void scheduler_channel_doesnt_want_writes_mock(channel_t *ch);
-
-static void test_channel_dumpstats(void *arg);
-static void test_channel_flush(void *arg);
-static void test_channel_flushmux(void *arg);
-static void test_channel_incoming(void *arg);
-static void test_channel_lifecycle(void *arg);
-static void test_channel_multi(void *arg);
-static void test_channel_queue_incoming(void *arg);
-static void test_channel_queue_size(void *arg);
-static void test_channel_write(void *arg);
-
-static void
-channel_note_destroy_not_pending_mock(channel_t *ch,
- circid_t circid)
-{
- (void)ch;
- (void)circid;
-
- ++test_destroy_not_pending_calls;
-}
+static int test_close_called = 0;
+static int test_chan_should_be_canonical = 0;
+static int test_chan_should_match_target = 0;
+static int test_chan_canonical_should_be_reliable = 0;
+static int test_chan_listener_close_fn_called = 0;
+static int test_chan_listener_fn_called = 0;
static const char *
chan_test_describe_transport(channel_t *ch)
@@ -112,71 +72,14 @@ chan_test_channel_dump_statistics_mock(channel_t *chan, int severity)
return;
}
-/**
- * If the target cmux is the cmux for chan, make fake cells up to the
- * target number of cells and write them to chan. Otherwise, invoke
- * the real channel_flush_from_first_active_circuit().
- */
-
-static int
-chan_test_channel_flush_from_first_active_circuit_mock(channel_t *chan,
- int max)
-{
- int result = 0, c = 0;
- packed_cell_t *cell = NULL;
-
- tt_ptr_op(chan, OP_NE, NULL);
- if (test_target_cmux != NULL &&
- test_target_cmux == chan->cmux) {
- while (c <= max && test_cmux_cells > 0) {
- cell = packed_cell_new();
- channel_write_packed_cell(chan, cell);
- ++c;
- --test_cmux_cells;
- }
- result = c;
- } else {
- result = channel_flush_from_first_active_circuit__real(chan, max);
- }
-
- done:
- return result;
-}
-
-/**
- * If we have a target cmux set and this matches it, lie about how
- * many cells we have according to the number indicated; otherwise
- * pass to the real circuitmux_num_cells().
- */
-
-static unsigned int
-chan_test_circuitmux_num_cells_mock(circuitmux_t *cmux)
-{
- unsigned int result = 0;
-
- tt_ptr_op(cmux, OP_NE, NULL);
- if (cmux != NULL) {
- if (cmux == test_target_cmux) {
- result = test_cmux_cells;
- } else {
- result = circuitmux_num_cells__real(cmux);
- }
- }
-
- done:
-
- return result;
-}
-
/*
* Handle an incoming fixed-size cell for unit tests
*/
static void
-chan_test_cell_handler(channel_t *ch,
- cell_t *cell)
+chan_test_cell_handler(channel_t *chan, cell_t *cell)
{
- tt_assert(ch);
+ tt_assert(chan);
tt_assert(cell);
test_chan_last_seen_fixed_cell_ptr = cell;
@@ -226,6 +129,8 @@ chan_test_close(channel_t *ch)
{
tt_assert(ch);
+ ++test_close_called;
+
done:
return;
}
@@ -274,35 +179,6 @@ chan_test_get_remote_descr(channel_t *ch, int flags)
return "Fake channel for unit tests; no real endpoint";
}
-static double
-chan_test_get_overhead_estimate(channel_t *ch)
-{
- tt_assert(ch);
-
- done:
- return test_overhead_estimate;
-}
-
-static int
-chan_test_is_canonical(channel_t *ch, int req)
-{
- tt_ptr_op(ch, OP_NE, NULL);
- tt_assert(req == 0 || req == 1);
-
- done:
- /* Fake channels are always canonical */
- return 1;
-}
-
-static size_t
-chan_test_num_bytes_queued(channel_t *ch)
-{
- tt_assert(ch);
-
- done:
- return 0;
-}
-
static int
chan_test_num_cells_writeable(channel_t *ch)
{
@@ -313,26 +189,6 @@ chan_test_num_cells_writeable(channel_t *ch)
}
static int
-chan_test_write_cell(channel_t *ch, cell_t *cell)
-{
- int rv = 0;
-
- tt_assert(ch);
- tt_assert(cell);
-
- if (test_chan_accept_cells) {
- /* Free the cell and bump the counter */
- tor_free(cell);
- ++test_cells_written;
- rv = 1;
- }
- /* else return 0, we didn't accept it */
-
- done:
- return rv;
-}
-
-static int
chan_test_write_packed_cell(channel_t *ch,
packed_cell_t *packed_cell)
{
@@ -343,7 +199,6 @@ chan_test_write_packed_cell(channel_t *ch,
if (test_chan_accept_cells) {
/* Free the cell and bump the counter */
- packed_cell_free(packed_cell);
++test_cells_written;
rv = 1;
}
@@ -419,36 +274,26 @@ new_fake_channel(void)
channel_init(chan);
chan->close = chan_test_close;
- chan->get_overhead_estimate = chan_test_get_overhead_estimate;
- chan->get_remote_descr = chan_test_get_remote_descr;
- chan->num_bytes_queued = chan_test_num_bytes_queued;
chan->num_cells_writeable = chan_test_num_cells_writeable;
- chan->write_cell = chan_test_write_cell;
+ chan->get_remote_descr = chan_test_get_remote_descr;
chan->write_packed_cell = chan_test_write_packed_cell;
chan->write_var_cell = chan_test_write_var_cell;
chan->state = CHANNEL_STATE_OPEN;
+ chan->cmux = circuitmux_alloc();
+
return chan;
}
void
free_fake_channel(channel_t *chan)
{
- cell_queue_entry_t *cell, *cell_tmp;
-
if (! chan)
return;
if (chan->cmux)
circuitmux_free(chan->cmux);
- TOR_SIMPLEQ_FOREACH_SAFE(cell, &chan->incoming_queue, next, cell_tmp) {
- cell_queue_entry_free(cell, 0);
- }
- TOR_SIMPLEQ_FOREACH_SAFE(cell, &chan->outgoing_queue, next, cell_tmp) {
- cell_queue_entry_free(cell, 0);
- }
-
tor_free(chan);
}
@@ -489,16 +334,6 @@ scheduler_channel_doesnt_want_writes_mock(channel_t *ch)
}
/**
- * Counter query for scheduler_release_channel_mock()
- */
-
-int
-get_mock_scheduler_release_channel_count(void)
-{
- return test_releases_count;
-}
-
-/**
* Mock for scheduler_release_channel()
*/
@@ -513,6 +348,58 @@ scheduler_release_channel_mock(channel_t *ch)
return;
}
+static int
+test_chan_is_canonical(channel_t *chan, int req)
+{
+ tor_assert(chan);
+
+ if (req && test_chan_canonical_should_be_reliable) {
+ return 1;
+ }
+
+ if (test_chan_should_be_canonical) {
+ return 1;
+ }
+ return 0;
+}
+
+static int
+test_chan_matches_target(channel_t *chan, const tor_addr_t *target)
+{
+ (void) chan;
+ (void) target;
+
+ if (test_chan_should_match_target) {
+ return 1;
+ }
+ return 0;
+}
+
+static void
+test_chan_listener_close(channel_listener_t *chan)
+{
+ (void) chan;
+ ++test_chan_listener_close_fn_called;
+ return;
+}
+
+static void
+test_chan_listener_fn(channel_listener_t *listener, channel_t *chan)
+{
+ (void) listener;
+ (void) chan;
+
+ ++test_chan_listener_fn_called;
+ return;
+}
+
+static const char *
+test_chan_listener_describe_transport(channel_listener_t *chan)
+{
+ (void) chan;
+ return "Fake listener channel.";
+}
+
/**
* Test for channel_dumpstats() and limited test for
* channel_dump_statistics()
@@ -523,6 +410,7 @@ test_channel_dumpstats(void *arg)
{
channel_t *ch = NULL;
cell_t *cell = NULL;
+ packed_cell_t *p_cell = NULL;
int old_count;
(void)arg;
@@ -536,7 +424,6 @@ test_channel_dumpstats(void *arg)
/* Set up a new fake channel */
ch = new_fake_channel();
tt_assert(ch);
- ch->cmux = circuitmux_alloc();
/* Try to register it */
channel_register(ch);
@@ -579,24 +466,21 @@ test_channel_dumpstats(void *arg)
/* Now make another channel */
ch = new_fake_channel();
tt_assert(ch);
- ch->cmux = circuitmux_alloc();
channel_register(ch);
- tt_assert(ch->registered);
+ tt_int_op(ch->registered, OP_EQ, 1);
/* Lie about its age so dumpstats gets coverage for rate calculations */
ch->timestamp_created = time(NULL) - 30;
- tt_assert(ch->timestamp_created > 0);
- tt_assert(time(NULL) > ch->timestamp_created);
+ tt_int_op(ch->timestamp_created, OP_GT, 0);
+ tt_int_op(time(NULL), OP_GT, ch->timestamp_created);
/* Put cells through it both ways to make the counters non-zero */
- cell = tor_malloc_zero(sizeof(*cell));
- make_fake_cell(cell);
+ p_cell = packed_cell_new();
test_chan_accept_cells = 1;
old_count = test_cells_written;
- channel_write_cell(ch, cell);
- cell = NULL;
+ channel_write_packed_cell(ch, p_cell);
tt_int_op(test_cells_written, OP_EQ, old_count + 1);
- tt_assert(ch->n_bytes_xmitted > 0);
- tt_assert(ch->n_cells_xmitted > 0);
+ tt_u64_op(ch->n_bytes_xmitted, OP_GT, 0);
+ tt_u64_op(ch->n_cells_xmitted, OP_GT, 0);
/* Receive path */
channel_set_cell_handlers(ch,
@@ -605,19 +489,18 @@ test_channel_dumpstats(void *arg)
tt_ptr_op(channel_get_cell_handler(ch), OP_EQ, chan_test_cell_handler);
tt_ptr_op(channel_get_var_cell_handler(ch), OP_EQ,
chan_test_var_cell_handler);
- cell = tor_malloc_zero(sizeof(cell_t));
- make_fake_cell(cell);
+ cell = tor_malloc_zero(sizeof(*cell));
old_count = test_chan_fixed_cells_recved;
- channel_queue_cell(ch, cell);
- tor_free(cell);
+ channel_process_cell(ch, cell);
tt_int_op(test_chan_fixed_cells_recved, OP_EQ, old_count + 1);
- tt_assert(ch->n_bytes_recved > 0);
- tt_assert(ch->n_cells_recved > 0);
+ tt_u64_op(ch->n_bytes_recved, OP_GT, 0);
+ tt_u64_op(ch->n_cells_recved, OP_GT, 0);
/* Test channel_dump_statistics */
ch->describe_transport = chan_test_describe_transport;
ch->dumpstats = chan_test_dumpstats;
- ch->is_canonical = chan_test_is_canonical;
+ test_chan_should_be_canonical = 1;
+ ch->is_canonical = test_chan_is_canonical;
old_count = test_dumpstats_calls;
channel_dump_statistics(ch, LOG_DEBUG);
tt_int_op(test_dumpstats_calls, OP_EQ, old_count + 1);
@@ -631,8 +514,8 @@ test_channel_dumpstats(void *arg)
ch = NULL;
done:
- tor_free(cell);
free_fake_channel(ch);
+ tor_free(cell);
UNMOCK(scheduler_channel_doesnt_want_writes);
UNMOCK(scheduler_release_channel);
@@ -640,215 +523,229 @@ test_channel_dumpstats(void *arg)
return;
}
+/* Test outbound cell. The callstack is:
+ * channel_flush_some_cells()
+ * -> channel_flush_from_first_active_circuit()
+ * -> channel_write_packed_cell()
+ * -> write_packed_cell()
+ * -> chan->write_packed_cell() fct ptr.
+ *
+ * This test goes from a cell in a circuit up to the channel write handler
+ * that should put them on the connection outbuf. */
static void
-test_channel_flush(void *arg)
+test_channel_outbound_cell(void *arg)
{
- channel_t *ch = NULL;
- cell_t *cell = NULL;
- packed_cell_t *p_cell = NULL;
- var_cell_t *v_cell = NULL;
- int init_count;
-
- (void)arg;
+ int old_count;
+ channel_t *chan = NULL;
+ packed_cell_t *p_cell = NULL, *p_cell2 = NULL;
+ origin_circuit_t *circ = NULL;
+ cell_queue_t *queue;
- ch = new_fake_channel();
- tt_assert(ch);
+ (void) arg;
- /* Cache the original count */
- init_count = test_cells_written;
+ /* The channel will be freed so we need to hijack this so the scheduler
+ * doesn't get confused. */
+ MOCK(scheduler_release_channel, scheduler_release_channel_mock);
- /* Stop accepting so we can queue some */
- test_chan_accept_cells = 0;
+ /* Accept cells to lower layer */
+ test_chan_accept_cells = 1;
- /* Queue a regular cell */
- cell = tor_malloc_zero(sizeof(cell_t));
- make_fake_cell(cell);
- channel_write_cell(ch, cell);
- /* It should be queued, so assert that we didn't write it */
- tt_int_op(test_cells_written, OP_EQ, init_count);
-
- /* Queue a var cell */
- v_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE);
- make_fake_var_cell(v_cell);
- channel_write_var_cell(ch, v_cell);
- /* It should be queued, so assert that we didn't write it */
- tt_int_op(test_cells_written, OP_EQ, init_count);
-
- /* Try a packed cell now */
+ /* Setup a valid circuit to queue a cell. */
+ circ = origin_circuit_new();
+ tt_assert(circ);
+ /* Circuit needs an origin purpose to be considered origin. */
+ TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+ TO_CIRCUIT(circ)->n_circ_id = 42;
+ /* This is the outbound test so use the next channel queue. */
+ queue = &TO_CIRCUIT(circ)->n_chan_cells;
+ /* Setup packed cell to queue on the circuit. */
p_cell = packed_cell_new();
tt_assert(p_cell);
- channel_write_packed_cell(ch, p_cell);
- /* It should be queued, so assert that we didn't write it */
- tt_int_op(test_cells_written, OP_EQ, init_count);
-
- /* Now allow writes through again */
- test_chan_accept_cells = 1;
-
- /* ...and flush */
- channel_flush_cells(ch);
-
- /* All three should have gone through */
- tt_int_op(test_cells_written, OP_EQ, init_count + 3);
-
- done:
- tor_free(ch);
-
- return;
-}
-
-/**
- * Channel flush tests that require cmux mocking
- */
-
-static void
-test_channel_flushmux(void *arg)
-{
- channel_t *ch = NULL;
- int old_count, q_len_before, q_len_after;
- ssize_t result;
-
- (void)arg;
-
- /* Install mocks we need for this test */
- MOCK(channel_flush_from_first_active_circuit,
- chan_test_channel_flush_from_first_active_circuit_mock);
- MOCK(circuitmux_num_cells,
- chan_test_circuitmux_num_cells_mock);
-
- ch = new_fake_channel();
- tt_assert(ch);
- ch->cmux = circuitmux_alloc();
-
+ p_cell2 = packed_cell_new();
+ tt_assert(p_cell2);
+ /* Setup a channel to put the circuit on. */
+ chan = new_fake_channel();
+ tt_assert(chan);
+ chan->state = CHANNEL_STATE_OPENING;
+ channel_change_state_open(chan);
+ /* Outbound channel. */
+ channel_mark_outgoing(chan);
+ /* Try to register it so we can clean it through the channel cleanup
+ * process. */
+ channel_register(chan);
+ tt_int_op(chan->registered, OP_EQ, 1);
+ /* Set EWMA policy so we can pick it when flushing. */
+ channel_set_cmux_policy_everywhere(&ewma_policy);
+ tt_ptr_op(circuitmux_get_policy(chan->cmux), OP_EQ, &ewma_policy);
+
+ /* Register circuit to the channel circid map which will attach the circuit
+ * to the channel's cmux as well. */
+ circuit_set_n_circid_chan(TO_CIRCUIT(circ), 42, chan);
+ tt_int_op(channel_num_circuits(chan), OP_EQ, 1);
+ tt_assert(!TO_CIRCUIT(circ)->next_active_on_n_chan);
+ tt_assert(!TO_CIRCUIT(circ)->prev_active_on_n_chan);
+ /* Test the cmux state. */
+ tt_ptr_op(TO_CIRCUIT(circ)->n_mux, OP_EQ, chan->cmux);
+ tt_int_op(circuitmux_is_circuit_attached(chan->cmux, TO_CIRCUIT(circ)),
+ OP_EQ, 1);
+
+ /* Flush the channel without any cell on it. */
old_count = test_cells_written;
-
- test_target_cmux = ch->cmux;
- test_cmux_cells = 1;
-
- /* Enable cell acceptance */
- test_chan_accept_cells = 1;
-
- result = channel_flush_some_cells(ch, 1);
-
- tt_int_op(result, OP_EQ, 1);
+ ssize_t flushed = channel_flush_some_cells(chan, 1);
+ tt_i64_op(flushed, OP_EQ, 0);
+ tt_int_op(test_cells_written, OP_EQ, old_count);
+ tt_int_op(channel_more_to_flush(chan), OP_EQ, 0);
+ tt_int_op(circuitmux_num_active_circuits(chan->cmux), OP_EQ, 0);
+ tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 0);
+ tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)),
+ OP_EQ, 0);
+ tt_u64_op(chan->n_cells_xmitted, OP_EQ, 0);
+ tt_u64_op(chan->n_bytes_xmitted, OP_EQ, 0);
+
+ /* Queue cell onto the next queue that is the outbound direction. Than
+ * update its cmux so the circuit can be picked when flushing cells. */
+ cell_queue_append(queue, p_cell);
+ p_cell = NULL;
+ tt_int_op(queue->n, OP_EQ, 1);
+ cell_queue_append(queue, p_cell2);
+ p_cell2 = NULL;
+ tt_int_op(queue->n, OP_EQ, 2);
+
+ update_circuit_on_cmux(TO_CIRCUIT(circ), CELL_DIRECTION_OUT);
+ tt_int_op(circuitmux_num_active_circuits(chan->cmux), OP_EQ, 1);
+ tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 2);
+ tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)),
+ OP_EQ, 1);
+
+ /* From this point on, we have a queued cell on an active circuit attached
+ * to the channel's cmux. */
+
+ /* Flush the first cell. This is going to go down the call stack. */
+ old_count = test_cells_written;
+ flushed = channel_flush_some_cells(chan, 1);
+ tt_i64_op(flushed, OP_EQ, 1);
tt_int_op(test_cells_written, OP_EQ, old_count + 1);
- tt_int_op(test_cmux_cells, OP_EQ, 0);
-
- /* Now try it without accepting to force them into the queue */
- test_chan_accept_cells = 0;
- test_cmux_cells = 1;
- q_len_before = chan_cell_queue_len(&(ch->outgoing_queue));
-
- result = channel_flush_some_cells(ch, 1);
-
- /* We should not have actually flushed any */
- tt_int_op(result, OP_EQ, 0);
+ tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 1);
+ tt_int_op(channel_more_to_flush(chan), OP_EQ, 1);
+ /* Circuit should remain active because there is a second cell queued. */
+ tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)),
+ OP_EQ, 1);
+ /* Should still be attached. */
+ tt_int_op(circuitmux_is_circuit_attached(chan->cmux, TO_CIRCUIT(circ)),
+ OP_EQ, 1);
+ tt_u64_op(chan->n_cells_xmitted, OP_EQ, 1);
+ tt_u64_op(chan->n_bytes_xmitted, OP_EQ, get_cell_network_size(0));
+
+ /* Flush second cell. This is going to go down the call stack. */
+ old_count = test_cells_written;
+ flushed = channel_flush_some_cells(chan, 1);
+ tt_i64_op(flushed, OP_EQ, 1);
tt_int_op(test_cells_written, OP_EQ, old_count + 1);
- /* But we should have gotten to the fake cellgen loop */
- tt_int_op(test_cmux_cells, OP_EQ, 0);
- /* ...and we should have a queued cell */
- q_len_after = chan_cell_queue_len(&(ch->outgoing_queue));
- tt_int_op(q_len_after, OP_EQ, q_len_before + 1);
-
- /* Now accept cells again and drain the queue */
- test_chan_accept_cells = 1;
- channel_flush_cells(ch);
- tt_int_op(test_cells_written, OP_EQ, old_count + 2);
- tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0);
-
- test_target_cmux = NULL;
- test_cmux_cells = 0;
+ tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 0);
+ tt_int_op(channel_more_to_flush(chan), OP_EQ, 0);
+ /* No more cells should make the circuit inactive. */
+ tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)),
+ OP_EQ, 0);
+ /* Should still be attached. */
+ tt_int_op(circuitmux_is_circuit_attached(chan->cmux, TO_CIRCUIT(circ)),
+ OP_EQ, 1);
+ tt_u64_op(chan->n_cells_xmitted, OP_EQ, 2);
+ tt_u64_op(chan->n_bytes_xmitted, OP_EQ, get_cell_network_size(0) * 2);
done:
- if (ch)
- circuitmux_free(ch->cmux);
- tor_free(ch);
-
- UNMOCK(channel_flush_from_first_active_circuit);
- UNMOCK(circuitmux_num_cells);
-
- test_chan_accept_cells = 0;
-
- return;
+ if (circ) {
+ circuit_free_(TO_CIRCUIT(circ));
+ }
+ tor_free(p_cell);
+ channel_free_all();
+ UNMOCK(scheduler_release_channel);
}
+/* Test inbound cell. The callstack is:
+ * channel_process_cell()
+ * -> chan->cell_handler()
+ *
+ * This test is about checking if we can process an inbound cell down to the
+ * channel handler. */
static void
-test_channel_incoming(void *arg)
+test_channel_inbound_cell(void *arg)
{
- channel_t *ch = NULL;
+ channel_t *chan = NULL;
cell_t *cell = NULL;
- var_cell_t *var_cell = NULL;
int old_count;
- (void)arg;
+ (void) arg;
- /* Mock these for duration of the test */
- MOCK(scheduler_channel_doesnt_want_writes,
- scheduler_channel_doesnt_want_writes_mock);
- MOCK(scheduler_release_channel,
- scheduler_release_channel_mock);
+ /* The channel will be freed so we need to hijack this so the scheduler
+ * doesn't get confused. */
+ MOCK(scheduler_release_channel, scheduler_release_channel_mock);
/* Accept cells to lower layer */
test_chan_accept_cells = 1;
- /* Use default overhead factor */
- test_overhead_estimate = 1.0;
- ch = new_fake_channel();
- tt_assert(ch);
+ chan = new_fake_channel();
+ tt_assert(chan);
/* Start it off in OPENING */
- ch->state = CHANNEL_STATE_OPENING;
- /* We'll need a cmux */
- ch->cmux = circuitmux_alloc();
-
- /* Install incoming cell handlers */
- channel_set_cell_handlers(ch,
- chan_test_cell_handler,
- chan_test_var_cell_handler);
- /* Test cell handler getters */
- tt_ptr_op(channel_get_cell_handler(ch), OP_EQ, chan_test_cell_handler);
- tt_ptr_op(channel_get_var_cell_handler(ch), OP_EQ,
- chan_test_var_cell_handler);
+ chan->state = CHANNEL_STATE_OPENING;
/* Try to register it */
- channel_register(ch);
- tt_assert(ch->registered);
+ channel_register(chan);
+ tt_int_op(chan->registered, OP_EQ, 1);
/* Open it */
- channel_change_state_open(ch);
- tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_OPEN);
+ channel_change_state_open(chan);
+ tt_int_op(chan->state, OP_EQ, CHANNEL_STATE_OPEN);
+ tt_int_op(chan->has_been_open, OP_EQ, 1);
- /* Receive a fixed cell */
- cell = tor_malloc_zero(sizeof(cell_t));
+ /* Receive a cell now. */
+ cell = tor_malloc_zero(sizeof(*cell));
make_fake_cell(cell);
old_count = test_chan_fixed_cells_recved;
- channel_queue_cell(ch, cell);
- tor_free(cell);
+ channel_process_cell(chan, cell);
+ tt_int_op(test_chan_fixed_cells_recved, OP_EQ, old_count);
+ tt_assert(monotime_coarse_is_zero(&chan->timestamp_xfer));
+ tt_u64_op(chan->timestamp_active, OP_EQ, 0);
+ tt_u64_op(chan->timestamp_recv, OP_EQ, 0);
+
+ /* Setup incoming cell handlers. We don't care about var cell, the channel
+ * layers is not handling those. */
+ channel_set_cell_handlers(chan, chan_test_cell_handler, NULL);
+ tt_ptr_op(chan->cell_handler, OP_EQ, chan_test_cell_handler);
+ /* Now process the cell, we should see it. */
+ old_count = test_chan_fixed_cells_recved;
+ channel_process_cell(chan, cell);
tt_int_op(test_chan_fixed_cells_recved, OP_EQ, old_count + 1);
-
- /* Receive a variable-size cell */
- var_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE);
- make_fake_var_cell(var_cell);
- old_count = test_chan_var_cells_recved;
- channel_queue_var_cell(ch, var_cell);
- tor_free(cell);
- tt_int_op(test_chan_var_cells_recved, OP_EQ, old_count + 1);
+ /* We should have a series of timestamp set. */
+ tt_assert(!monotime_coarse_is_zero(&chan->timestamp_xfer));
+ tt_u64_op(chan->timestamp_active, OP_NE, 0);
+ tt_u64_op(chan->timestamp_recv, OP_NE, 0);
+ tt_assert(monotime_coarse_is_zero(&chan->next_padding_time));
+ tt_u64_op(chan->n_cells_recved, OP_EQ, 1);
+ tt_u64_op(chan->n_bytes_recved, OP_EQ, get_cell_network_size(0));
/* Close it */
- channel_mark_for_close(ch);
- tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING);
- chan_test_finish_close(ch);
- tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSED);
+ old_count = test_close_called;
+ channel_mark_for_close(chan);
+ tt_int_op(chan->state, OP_EQ, CHANNEL_STATE_CLOSING);
+ tt_int_op(chan->reason_for_closing, OP_EQ, CHANNEL_CLOSE_REQUESTED);
+ tt_int_op(test_close_called, OP_EQ, old_count + 1);
+
+ /* This closes the channe so it calls in the scheduler, make sure of it. */
+ old_count = test_releases_count;
+ chan_test_finish_close(chan);
+ tt_int_op(test_releases_count, OP_EQ, old_count + 1);
+ tt_int_op(chan->state, OP_EQ, CHANNEL_STATE_CLOSED);
+
+ /* The channel will be free, lets make sure it is not accessible. */
+ uint64_t chan_id = chan->global_identifier;
+ tt_ptr_op(channel_find_by_global_id(chan_id), OP_EQ, chan);
channel_run_cleanup();
- ch = NULL;
+ chan = channel_find_by_global_id(chan_id);
+ tt_assert(chan == NULL);
done:
- free_fake_channel(ch);
tor_free(cell);
- tor_free(var_cell);
-
- UNMOCK(scheduler_channel_doesnt_want_writes);
UNMOCK(scheduler_release_channel);
-
- return;
}
/**
@@ -861,7 +758,7 @@ static void
test_channel_lifecycle(void *arg)
{
channel_t *ch1 = NULL, *ch2 = NULL;
- cell_t *cell = NULL;
+ packed_cell_t *p_cell = NULL;
int old_count, init_doesnt_want_writes_count;
int init_releases_count;
@@ -879,38 +776,29 @@ test_channel_lifecycle(void *arg)
/* Accept cells to lower layer */
test_chan_accept_cells = 1;
- /* Use default overhead factor */
- test_overhead_estimate = 1.0;
ch1 = new_fake_channel();
tt_assert(ch1);
/* Start it off in OPENING */
ch1->state = CHANNEL_STATE_OPENING;
- /* We'll need a cmux */
- ch1->cmux = circuitmux_alloc();
/* Try to register it */
channel_register(ch1);
tt_assert(ch1->registered);
/* Try to write a cell through (should queue) */
- cell = tor_malloc_zero(sizeof(cell_t));
- make_fake_cell(cell);
+ p_cell = packed_cell_new();
old_count = test_cells_written;
- channel_write_cell(ch1, cell);
+ channel_write_packed_cell(ch1, p_cell);
tt_int_op(old_count, OP_EQ, test_cells_written);
/* Move it to OPEN and flush */
channel_change_state_open(ch1);
- /* Queue should drain */
- tt_int_op(old_count + 1, OP_EQ, test_cells_written);
-
- /* Get another one */
+/* Get another one */
ch2 = new_fake_channel();
tt_assert(ch2);
ch2->state = CHANNEL_STATE_OPENING;
- ch2->cmux = circuitmux_alloc();
/* Register */
channel_register(ch2);
@@ -960,8 +848,6 @@ test_channel_lifecycle(void *arg)
UNMOCK(scheduler_channel_doesnt_want_writes);
UNMOCK(scheduler_release_channel);
-
- return;
}
/**
@@ -988,15 +874,11 @@ test_channel_lifecycle_2(void *arg)
/* Accept cells to lower layer */
test_chan_accept_cells = 1;
- /* Use default overhead factor */
- test_overhead_estimate = 1.0;
ch = new_fake_channel();
tt_assert(ch);
/* Start it off in OPENING */
ch->state = CHANNEL_STATE_OPENING;
- /* The full lifecycle test needs a cmux */
- ch->cmux = circuitmux_alloc();
/* Try to register it */
channel_register(ch);
@@ -1016,7 +898,6 @@ test_channel_lifecycle_2(void *arg)
ch = new_fake_channel();
tt_assert(ch);
ch->state = CHANNEL_STATE_OPENING;
- ch->cmux = circuitmux_alloc();
channel_register(ch);
tt_assert(ch->registered);
@@ -1035,7 +916,6 @@ test_channel_lifecycle_2(void *arg)
ch = new_fake_channel();
tt_assert(ch);
ch->state = CHANNEL_STATE_OPENING;
- ch->cmux = circuitmux_alloc();
channel_register(ch);
tt_assert(ch->registered);
@@ -1064,7 +944,6 @@ test_channel_lifecycle_2(void *arg)
ch = new_fake_channel();
tt_assert(ch);
ch->state = CHANNEL_STATE_OPENING;
- ch->cmux = circuitmux_alloc();
channel_register(ch);
tt_assert(ch->registered);
@@ -1090,7 +969,6 @@ test_channel_lifecycle_2(void *arg)
ch = new_fake_channel();
tt_assert(ch);
ch->state = CHANNEL_STATE_OPENING;
- ch->cmux = circuitmux_alloc();
channel_register(ch);
tt_assert(ch->registered);
@@ -1125,655 +1003,6 @@ test_channel_lifecycle_2(void *arg)
}
static void
-test_channel_multi(void *arg)
-{
- channel_t *ch1 = NULL, *ch2 = NULL;
- uint64_t global_queue_estimate;
- cell_t *cell = NULL;
-
- (void)arg;
-
- /* Accept cells to lower layer */
- test_chan_accept_cells = 1;
- /* Use default overhead factor */
- test_overhead_estimate = 1.0;
-
- ch1 = new_fake_channel();
- tt_assert(ch1);
- ch2 = new_fake_channel();
- tt_assert(ch2);
-
- /* Initial queue size update */
- channel_update_xmit_queue_size(ch1);
- tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 0);
- channel_update_xmit_queue_size(ch2);
- tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 0);
- global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, OP_EQ, 0);
-
- /* Queue some cells, check queue estimates */
- cell = tor_malloc_zero(sizeof(cell_t));
- make_fake_cell(cell);
- channel_write_cell(ch1, cell);
-
- cell = tor_malloc_zero(sizeof(cell_t));
- make_fake_cell(cell);
- channel_write_cell(ch2, cell);
-
- channel_update_xmit_queue_size(ch1);
- channel_update_xmit_queue_size(ch2);
- tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 0);
- tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 0);
- global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, OP_EQ, 0);
-
- /* Stop accepting cells at lower layer */
- test_chan_accept_cells = 0;
-
- /* Queue some cells and check queue estimates */
- cell = tor_malloc_zero(sizeof(cell_t));
- make_fake_cell(cell);
- channel_write_cell(ch1, cell);
-
- channel_update_xmit_queue_size(ch1);
- tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 512);
- global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, OP_EQ, 512);
-
- cell = tor_malloc_zero(sizeof(cell_t));
- make_fake_cell(cell);
- channel_write_cell(ch2, cell);
-
- channel_update_xmit_queue_size(ch2);
- tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 512);
- global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, OP_EQ, 1024);
-
- /* Allow cells through again */
- test_chan_accept_cells = 1;
-
- /* Flush chan 2 */
- channel_flush_cells(ch2);
-
- /* Update and check queue sizes */
- channel_update_xmit_queue_size(ch1);
- channel_update_xmit_queue_size(ch2);
- tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 512);
- tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 0);
- global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, OP_EQ, 512);
-
- /* Flush chan 1 */
- channel_flush_cells(ch1);
-
- /* Update and check queue sizes */
- channel_update_xmit_queue_size(ch1);
- channel_update_xmit_queue_size(ch2);
- tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 0);
- tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 0);
- global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, OP_EQ, 0);
-
- /* Now block again */
- test_chan_accept_cells = 0;
-
- /* Queue some cells */
- cell = tor_malloc_zero(sizeof(cell_t));
- make_fake_cell(cell);
- channel_write_cell(ch1, cell);
-
- cell = tor_malloc_zero(sizeof(cell_t));
- make_fake_cell(cell);
- channel_write_cell(ch2, cell);
- cell = NULL;
-
- /* Check the estimates */
- channel_update_xmit_queue_size(ch1);
- channel_update_xmit_queue_size(ch2);
- tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 512);
- tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 512);
- global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, OP_EQ, 1024);
-
- /* Now close channel 2; it should be subtracted from the global queue */
- MOCK(scheduler_release_channel, scheduler_release_channel_mock);
- channel_mark_for_close(ch2);
- UNMOCK(scheduler_release_channel);
-
- global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, OP_EQ, 512);
-
- /*
- * Since the fake channels aren't registered, channel_free_all() can't
- * see them properly.
- */
- MOCK(scheduler_release_channel, scheduler_release_channel_mock);
- channel_mark_for_close(ch1);
- UNMOCK(scheduler_release_channel);
-
- global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, OP_EQ, 0);
-
- /* Now free everything */
- MOCK(scheduler_release_channel, scheduler_release_channel_mock);
- channel_free_all();
- UNMOCK(scheduler_release_channel);
-
- done:
- free_fake_channel(ch1);
- free_fake_channel(ch2);
-
- return;
-}
-
-/**
- * Check some hopefully-impossible edge cases in the channel queue we
- * can only trigger by doing evil things to the queue directly.
- */
-
-static void
-test_channel_queue_impossible(void *arg)
-{
- channel_t *ch = NULL;
- cell_t *cell = NULL;
- packed_cell_t *packed_cell = NULL;
- var_cell_t *var_cell = NULL;
- int old_count;
- cell_queue_entry_t *q = NULL;
- uint64_t global_queue_estimate;
- uintptr_t cellintptr;
-
- /* Cache the global queue size (see below) */
- global_queue_estimate = channel_get_global_queue_estimate();
-
- (void)arg;
-
- ch = new_fake_channel();
- tt_assert(ch);
-
- /* We test queueing here; tell it not to accept cells */
- test_chan_accept_cells = 0;
- /* ...and keep it from trying to flush the queue */
- ch->state = CHANNEL_STATE_MAINT;
-
- /* Cache the cell written count */
- old_count = test_cells_written;
-
- /* Assert that the queue is initially empty */
- tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0);
-
- /* Get a fresh cell and write it to the channel*/
- cell = tor_malloc_zero(sizeof(cell_t));
- make_fake_cell(cell);
- cellintptr = (uintptr_t)(void*)cell;
- channel_write_cell(ch, cell);
-
- /* Now it should be queued */
- tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 1);
- q = TOR_SIMPLEQ_FIRST(&(ch->outgoing_queue));
- tt_assert(q);
- if (q) {
- tt_int_op(q->type, OP_EQ, CELL_QUEUE_FIXED);
- tt_assert((uintptr_t)q->u.fixed.cell == cellintptr);
- }
- /* Do perverse things to it */
- tor_free(q->u.fixed.cell);
- q->u.fixed.cell = NULL;
-
- /*
- * Now change back to open with channel_change_state() and assert that it
- * gets thrown away properly.
- */
- test_chan_accept_cells = 1;
- channel_change_state_open(ch);
- tt_assert(test_cells_written == old_count);
- tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0);
-
- /* Same thing but for a var_cell */
-
- test_chan_accept_cells = 0;
- ch->state = CHANNEL_STATE_MAINT;
- var_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE);
- make_fake_var_cell(var_cell);
- cellintptr = (uintptr_t)(void*)var_cell;
- channel_write_var_cell(ch, var_cell);
-
- /* Check that it's queued */
- tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 1);
- q = TOR_SIMPLEQ_FIRST(&(ch->outgoing_queue));
- tt_assert(q);
- if (q) {
- tt_int_op(q->type, OP_EQ, CELL_QUEUE_VAR);
- tt_assert((uintptr_t)q->u.var.var_cell == cellintptr);
- }
-
- /* Remove the cell from the queue entry */
- tor_free(q->u.var.var_cell);
- q->u.var.var_cell = NULL;
-
- /* Let it drain and check that the bad entry is discarded */
- test_chan_accept_cells = 1;
- channel_change_state_open(ch);
- tt_assert(test_cells_written == old_count);
- tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0);
-
- /* Same thing with a packed_cell */
-
- test_chan_accept_cells = 0;
- ch->state = CHANNEL_STATE_MAINT;
- packed_cell = packed_cell_new();
- tt_assert(packed_cell);
- cellintptr = (uintptr_t)(void*)packed_cell;
- channel_write_packed_cell(ch, packed_cell);
-
- /* Check that it's queued */
- tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 1);
- q = TOR_SIMPLEQ_FIRST(&(ch->outgoing_queue));
- tt_assert(q);
- if (q) {
- tt_int_op(q->type, OP_EQ, CELL_QUEUE_PACKED);
- tt_assert((uintptr_t)q->u.packed.packed_cell == cellintptr);
- }
-
- /* Remove the cell from the queue entry */
- packed_cell_free(q->u.packed.packed_cell);
- q->u.packed.packed_cell = NULL;
-
- /* Let it drain and check that the bad entry is discarded */
- test_chan_accept_cells = 1;
- channel_change_state_open(ch);
- tt_assert(test_cells_written == old_count);
- tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0);
-
- /* Unknown cell type case */
- test_chan_accept_cells = 0;
- ch->state = CHANNEL_STATE_MAINT;
- cell = tor_malloc_zero(sizeof(cell_t));
- make_fake_cell(cell);
- cellintptr = (uintptr_t)(void*)cell;
- channel_write_cell(ch, cell);
-
- /* Check that it's queued */
- tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 1);
- q = TOR_SIMPLEQ_FIRST(&(ch->outgoing_queue));
- tt_assert(q);
- if (q) {
- tt_int_op(q->type, OP_EQ, CELL_QUEUE_FIXED);
- tt_assert((uintptr_t)q->u.fixed.cell == cellintptr);
- }
- /* Clobber it, including the queue entry type */
- tor_free(q->u.fixed.cell);
- q->u.fixed.cell = NULL;
- q->type = CELL_QUEUE_PACKED + 1;
-
- /* Let it drain and check that the bad entry is discarded */
- test_chan_accept_cells = 1;
- tor_capture_bugs_(1);
- channel_change_state_open(ch);
- tt_assert(test_cells_written == old_count);
- tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0);
-
- tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1);
- tor_end_capture_bugs_();
-
- done:
- free_fake_channel(ch);
-
- /*
- * Doing that meant that we couldn't correctly adjust the queue size
- * for the var cell, so manually reset the global queue size estimate
- * so the next test doesn't break if we run with --no-fork.
- */
- estimated_total_queue_size = global_queue_estimate;
-
- return;
-}
-
-static void
-test_channel_queue_incoming(void *arg)
-{
- channel_t *ch = NULL;
- cell_t *cell = NULL;
- var_cell_t *var_cell = NULL;
- int old_fixed_count, old_var_count;
-
- (void)arg;
-
- /* Mock these for duration of the test */
- MOCK(scheduler_channel_doesnt_want_writes,
- scheduler_channel_doesnt_want_writes_mock);
- MOCK(scheduler_release_channel,
- scheduler_release_channel_mock);
-
- /* Accept cells to lower layer */
- test_chan_accept_cells = 1;
- /* Use default overhead factor */
- test_overhead_estimate = 1.0;
-
- ch = new_fake_channel();
- tt_assert(ch);
- /* Start it off in OPENING */
- ch->state = CHANNEL_STATE_OPENING;
- /* We'll need a cmux */
- ch->cmux = circuitmux_alloc();
-
- /* Test cell handler getters */
- tt_ptr_op(channel_get_cell_handler(ch), OP_EQ, NULL);
- tt_ptr_op(channel_get_var_cell_handler(ch), OP_EQ, NULL);
-
- /* Try to register it */
- channel_register(ch);
- tt_assert(ch->registered);
-
- /* Open it */
- channel_change_state_open(ch);
- tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_OPEN);
-
- /* Assert that the incoming queue is empty */
- tt_assert(TOR_SIMPLEQ_EMPTY(&(ch->incoming_queue)));
-
- /* Queue an incoming fixed-length cell */
- cell = tor_malloc_zero(sizeof(cell_t));
- make_fake_cell(cell);
- channel_queue_cell(ch, cell);
-
- /* Assert that the incoming queue has one entry */
- tt_int_op(chan_cell_queue_len(&(ch->incoming_queue)), OP_EQ, 1);
-
- /* Queue an incoming var cell */
- var_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE);
- make_fake_var_cell(var_cell);
- channel_queue_var_cell(ch, var_cell);
-
- /* Assert that the incoming queue has two entries */
- tt_int_op(chan_cell_queue_len(&(ch->incoming_queue)), OP_EQ, 2);
-
- /*
- * Install cell handlers; this will drain the queue, so save the old
- * cell counters first
- */
- old_fixed_count = test_chan_fixed_cells_recved;
- old_var_count = test_chan_var_cells_recved;
- channel_set_cell_handlers(ch,
- chan_test_cell_handler,
- chan_test_var_cell_handler);
- tt_ptr_op(channel_get_cell_handler(ch), OP_EQ, chan_test_cell_handler);
- tt_ptr_op(channel_get_var_cell_handler(ch), OP_EQ,
- chan_test_var_cell_handler);
-
- /* Assert cells were received */
- tt_int_op(test_chan_fixed_cells_recved, OP_EQ, old_fixed_count + 1);
- tt_int_op(test_chan_var_cells_recved, OP_EQ, old_var_count + 1);
-
- /*
- * Assert that the pointers are different from the cells we allocated;
- * when queueing cells with no incoming cell handlers installed, the
- * channel layer should copy them to a new buffer, and free them after
- * delivery. These pointers will have already been freed by the time
- * we get here, so don't dereference them.
- */
- tt_ptr_op(test_chan_last_seen_fixed_cell_ptr, OP_NE, cell);
- tt_ptr_op(test_chan_last_seen_var_cell_ptr, OP_NE, var_cell);
-
- /* Assert queue is now empty */
- tt_assert(TOR_SIMPLEQ_EMPTY(&(ch->incoming_queue)));
-
- /* Close it; this contains an assertion that the incoming queue is empty */
- channel_mark_for_close(ch);
- tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING);
- chan_test_finish_close(ch);
- tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSED);
- channel_run_cleanup();
- ch = NULL;
-
- done:
- free_fake_channel(ch);
- tor_free(cell);
- tor_free(var_cell);
-
- UNMOCK(scheduler_channel_doesnt_want_writes);
- UNMOCK(scheduler_release_channel);
-
- return;
-}
-
-static void
-test_channel_queue_size(void *arg)
-{
- channel_t *ch = NULL;
- cell_t *cell = NULL;
- int n, old_count;
- uint64_t global_queue_estimate;
-
- (void)arg;
-
- ch = new_fake_channel();
- tt_assert(ch);
-
- /* Initial queue size update */
- channel_update_xmit_queue_size(ch);
- tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 0);
- global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, OP_EQ, 0);
-
- /* Test the call-through to our fake lower layer */
- n = channel_num_cells_writeable(ch);
- /* chan_test_num_cells_writeable() always returns 32 */
- tt_int_op(n, OP_EQ, 32);
-
- /*
- * Now we queue some cells and check that channel_num_cells_writeable()
- * adjusts properly
- */
-
- /* tell it not to accept cells */
- test_chan_accept_cells = 0;
- /* ...and keep it from trying to flush the queue */
- ch->state = CHANNEL_STATE_MAINT;
-
- /* Get a fresh cell */
- cell = tor_malloc_zero(sizeof(cell_t));
- make_fake_cell(cell);
-
- old_count = test_cells_written;
- channel_write_cell(ch, cell);
- /* Assert that it got queued, not written through, correctly */
- tt_int_op(test_cells_written, OP_EQ, old_count);
-
- /* Now check chan_test_num_cells_writeable() again */
- n = channel_num_cells_writeable(ch);
- /* Should return 0 since we're in CHANNEL_STATE_MAINT */
- tt_int_op(n, OP_EQ, 0);
-
- /* Update queue size estimates */
- channel_update_xmit_queue_size(ch);
- /* One cell, times an overhead factor of 1.0 */
- tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 512);
- /* Try a different overhead factor */
- test_overhead_estimate = 0.5;
- /* This one should be ignored since it's below 1.0 */
- channel_update_xmit_queue_size(ch);
- tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 512);
- /* Now try a larger one */
- test_overhead_estimate = 2.0;
- channel_update_xmit_queue_size(ch);
- tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 1024);
- /* Go back to 1.0 */
- test_overhead_estimate = 1.0;
- channel_update_xmit_queue_size(ch);
- tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 512);
- /* Check the global estimate too */
- global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, OP_EQ, 512);
-
- /* Go to open */
- old_count = test_cells_written;
- channel_change_state_open(ch);
-
- /*
- * It should try to write, but we aren't accepting cells right now, so
- * it'll requeue
- */
- tt_int_op(test_cells_written, OP_EQ, old_count);
-
- /* Check the queue size again */
- channel_update_xmit_queue_size(ch);
- tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 512);
- global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, OP_EQ, 512);
-
- /*
- * Now the cell is in the queue, and we're open, so we should get 31
- * writeable cells.
- */
- n = channel_num_cells_writeable(ch);
- tt_int_op(n, OP_EQ, 31);
-
- /* Accept cells again */
- test_chan_accept_cells = 1;
- /* ...and re-process the queue */
- old_count = test_cells_written;
- channel_flush_cells(ch);
- tt_int_op(test_cells_written, OP_EQ, old_count + 1);
-
- /* Should have 32 writeable now */
- n = channel_num_cells_writeable(ch);
- tt_int_op(n, OP_EQ, 32);
-
- /* Should have queue size estimate of zero */
- channel_update_xmit_queue_size(ch);
- tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 0);
- global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, OP_EQ, 0);
-
- /* Okay, now we're done with this one */
- MOCK(scheduler_release_channel, scheduler_release_channel_mock);
- channel_mark_for_close(ch);
- UNMOCK(scheduler_release_channel);
-
- done:
- free_fake_channel(ch);
-
- return;
-}
-
-static void
-test_channel_write(void *arg)
-{
- channel_t *ch = NULL;
- cell_t *cell = tor_malloc_zero(sizeof(cell_t));
- packed_cell_t *packed_cell = NULL;
- var_cell_t *var_cell =
- tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE);
- int old_count;
-
- (void)arg;
-
- packed_cell = packed_cell_new();
- tt_assert(packed_cell);
-
- ch = new_fake_channel();
- tt_assert(ch);
- make_fake_cell(cell);
- make_fake_var_cell(var_cell);
-
- /* Tell it to accept cells */
- test_chan_accept_cells = 1;
-
- old_count = test_cells_written;
- channel_write_cell(ch, cell);
- cell = NULL;
- tt_assert(test_cells_written == old_count + 1);
-
- channel_write_var_cell(ch, var_cell);
- var_cell = NULL;
- tt_assert(test_cells_written == old_count + 2);
-
- channel_write_packed_cell(ch, packed_cell);
- packed_cell = NULL;
- tt_assert(test_cells_written == old_count + 3);
-
- /* Now we test queueing; tell it not to accept cells */
- test_chan_accept_cells = 0;
- /* ...and keep it from trying to flush the queue */
- ch->state = CHANNEL_STATE_MAINT;
-
- /* Get a fresh cell */
- cell = tor_malloc_zero(sizeof(cell_t));
- make_fake_cell(cell);
-
- old_count = test_cells_written;
- channel_write_cell(ch, cell);
- tt_assert(test_cells_written == old_count);
-
- /*
- * Now change back to open with channel_change_state() and assert that it
- * gets drained from the queue.
- */
- test_chan_accept_cells = 1;
- channel_change_state_open(ch);
- tt_assert(test_cells_written == old_count + 1);
-
- /*
- * Check the note destroy case
- */
- cell = tor_malloc_zero(sizeof(cell_t));
- make_fake_cell(cell);
- cell->command = CELL_DESTROY;
-
- /* Set up the mock */
- MOCK(channel_note_destroy_not_pending,
- channel_note_destroy_not_pending_mock);
-
- old_count = test_destroy_not_pending_calls;
- channel_write_cell(ch, cell);
- tt_assert(test_destroy_not_pending_calls == old_count + 1);
-
- /* Now send a non-destroy and check we don't call it */
- cell = tor_malloc_zero(sizeof(cell_t));
- make_fake_cell(cell);
- channel_write_cell(ch, cell);
- tt_assert(test_destroy_not_pending_calls == old_count + 1);
-
- UNMOCK(channel_note_destroy_not_pending);
-
- /*
- * Now switch it to CLOSING so we can test the discard-cells case
- * in the channel_write_*() functions.
- */
- MOCK(scheduler_release_channel, scheduler_release_channel_mock);
- channel_mark_for_close(ch);
- UNMOCK(scheduler_release_channel);
-
- /* Send cells that will drop in the closing state */
- old_count = test_cells_written;
-
- cell = tor_malloc_zero(sizeof(cell_t));
- make_fake_cell(cell);
- channel_write_cell(ch, cell);
- cell = NULL;
- tt_assert(test_cells_written == old_count);
-
- var_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE);
- make_fake_var_cell(var_cell);
- channel_write_var_cell(ch, var_cell);
- var_cell = NULL;
- tt_assert(test_cells_written == old_count);
-
- packed_cell = packed_cell_new();
- channel_write_packed_cell(ch, packed_cell);
- packed_cell = NULL;
- tt_assert(test_cells_written == old_count);
-
- done:
- free_fake_channel(ch);
- tor_free(var_cell);
- tor_free(cell);
- packed_cell_free(packed_cell);
- return;
-}
-
-static void
test_channel_id_map(void *arg)
{
(void)arg;
@@ -1879,19 +1108,449 @@ test_channel_id_map(void *arg)
#undef N_CHAN
}
+static void
+test_channel_state(void *arg)
+{
+ (void) arg;
+
+ /* Test state validity. */
+ tt_int_op(channel_state_is_valid(CHANNEL_STATE_CLOSED), OP_EQ, 1);
+ tt_int_op(channel_state_is_valid(CHANNEL_STATE_CLOSING), OP_EQ, 1);
+ tt_int_op(channel_state_is_valid(CHANNEL_STATE_ERROR), OP_EQ, 1);
+ tt_int_op(channel_state_is_valid(CHANNEL_STATE_OPEN), OP_EQ, 1);
+ tt_int_op(channel_state_is_valid(CHANNEL_STATE_OPENING), OP_EQ, 1);
+ tt_int_op(channel_state_is_valid(CHANNEL_STATE_MAINT), OP_EQ, 1);
+ tt_int_op(channel_state_is_valid(CHANNEL_STATE_LAST), OP_EQ, 0);
+ tt_int_op(channel_state_is_valid(INT_MAX), OP_EQ, 0);
+
+ /* Test listener state validity. */
+ tt_int_op(channel_listener_state_is_valid(CHANNEL_LISTENER_STATE_CLOSED),
+ OP_EQ, 1);
+ tt_int_op(channel_listener_state_is_valid(CHANNEL_LISTENER_STATE_LISTENING),
+ OP_EQ, 1);
+ tt_int_op(channel_listener_state_is_valid(CHANNEL_LISTENER_STATE_CLOSING),
+ OP_EQ, 1);
+ tt_int_op(channel_listener_state_is_valid(CHANNEL_LISTENER_STATE_ERROR),
+ OP_EQ, 1);
+ tt_int_op(channel_listener_state_is_valid(CHANNEL_LISTENER_STATE_LAST),
+ OP_EQ, 0);
+ tt_int_op(channel_listener_state_is_valid(INT_MAX), OP_EQ, 0);
+
+ /* Test state transition. */
+ tt_int_op(channel_state_can_transition(CHANNEL_STATE_CLOSED,
+ CHANNEL_STATE_OPENING), OP_EQ, 1);
+ tt_int_op(channel_state_can_transition(CHANNEL_STATE_CLOSED,
+ CHANNEL_STATE_ERROR), OP_EQ, 0);
+ tt_int_op(channel_state_can_transition(CHANNEL_STATE_CLOSING,
+ CHANNEL_STATE_ERROR), OP_EQ, 1);
+ tt_int_op(channel_state_can_transition(CHANNEL_STATE_CLOSING,
+ CHANNEL_STATE_CLOSED), OP_EQ, 1);
+ tt_int_op(channel_state_can_transition(CHANNEL_STATE_CLOSING,
+ CHANNEL_STATE_OPEN), OP_EQ, 0);
+ tt_int_op(channel_state_can_transition(CHANNEL_STATE_MAINT,
+ CHANNEL_STATE_CLOSING), OP_EQ, 1);
+ tt_int_op(channel_state_can_transition(CHANNEL_STATE_MAINT,
+ CHANNEL_STATE_ERROR), OP_EQ, 1);
+ tt_int_op(channel_state_can_transition(CHANNEL_STATE_MAINT,
+ CHANNEL_STATE_OPEN), OP_EQ, 1);
+ tt_int_op(channel_state_can_transition(CHANNEL_STATE_MAINT,
+ CHANNEL_STATE_OPENING), OP_EQ, 0);
+ tt_int_op(channel_state_can_transition(CHANNEL_STATE_OPENING,
+ CHANNEL_STATE_OPEN), OP_EQ, 1);
+ tt_int_op(channel_state_can_transition(CHANNEL_STATE_OPENING,
+ CHANNEL_STATE_CLOSING), OP_EQ, 1);
+ tt_int_op(channel_state_can_transition(CHANNEL_STATE_OPENING,
+ CHANNEL_STATE_ERROR), OP_EQ, 1);
+ tt_int_op(channel_state_can_transition(CHANNEL_STATE_OPEN,
+ CHANNEL_STATE_ERROR), OP_EQ, 1);
+ tt_int_op(channel_state_can_transition(CHANNEL_STATE_OPEN,
+ CHANNEL_STATE_CLOSING), OP_EQ, 1);
+ tt_int_op(channel_state_can_transition(CHANNEL_STATE_OPEN,
+ CHANNEL_STATE_ERROR), OP_EQ, 1);
+ tt_int_op(channel_state_can_transition(CHANNEL_STATE_OPEN,
+ CHANNEL_STATE_MAINT), OP_EQ, 1);
+ tt_int_op(channel_state_can_transition(CHANNEL_STATE_LAST,
+ CHANNEL_STATE_MAINT), OP_EQ, 0);
+ tt_int_op(channel_state_can_transition(CHANNEL_STATE_LAST, INT_MAX),
+ OP_EQ, 0);
+
+ /* Test listener state transition. */
+ tt_int_op(channel_listener_state_can_transition(
+ CHANNEL_LISTENER_STATE_CLOSED,
+ CHANNEL_LISTENER_STATE_LISTENING),
+ OP_EQ, 1);
+ tt_int_op(channel_listener_state_can_transition(
+ CHANNEL_LISTENER_STATE_CLOSED,
+ CHANNEL_LISTENER_STATE_ERROR),
+ OP_EQ, 0);
+
+ tt_int_op(channel_listener_state_can_transition(
+ CHANNEL_LISTENER_STATE_CLOSING,
+ CHANNEL_LISTENER_STATE_CLOSED),
+ OP_EQ, 1);
+
+ tt_int_op(channel_listener_state_can_transition(
+ CHANNEL_LISTENER_STATE_CLOSING,
+ CHANNEL_LISTENER_STATE_ERROR),
+ OP_EQ, 1);
+ tt_int_op(channel_listener_state_can_transition(
+ CHANNEL_LISTENER_STATE_ERROR,
+ CHANNEL_LISTENER_STATE_CLOSING),
+ OP_EQ, 0);
+
+ tt_int_op(channel_listener_state_can_transition(
+ CHANNEL_LISTENER_STATE_LISTENING,
+ CHANNEL_LISTENER_STATE_CLOSING),
+ OP_EQ, 1);
+ tt_int_op(channel_listener_state_can_transition(
+ CHANNEL_LISTENER_STATE_LISTENING,
+ CHANNEL_LISTENER_STATE_ERROR),
+ OP_EQ, 1);
+ tt_int_op(channel_listener_state_can_transition(
+ CHANNEL_LISTENER_STATE_LAST,
+ INT_MAX),
+ OP_EQ, 0);
+
+ /* Test state string. */
+ tt_str_op(channel_state_to_string(CHANNEL_STATE_CLOSING), OP_EQ,
+ "closing");
+ tt_str_op(channel_state_to_string(CHANNEL_STATE_ERROR), OP_EQ,
+ "channel error");
+ tt_str_op(channel_state_to_string(CHANNEL_STATE_CLOSED), OP_EQ,
+ "closed");
+ tt_str_op(channel_state_to_string(CHANNEL_STATE_OPEN), OP_EQ,
+ "open");
+ tt_str_op(channel_state_to_string(CHANNEL_STATE_OPENING), OP_EQ,
+ "opening");
+ tt_str_op(channel_state_to_string(CHANNEL_STATE_MAINT), OP_EQ,
+ "temporarily suspended for maintenance");
+ tt_str_op(channel_state_to_string(CHANNEL_STATE_LAST), OP_EQ,
+ "unknown or invalid channel state");
+ tt_str_op(channel_state_to_string(INT_MAX), OP_EQ,
+ "unknown or invalid channel state");
+
+ /* Test listener state string. */
+ tt_str_op(channel_listener_state_to_string(CHANNEL_LISTENER_STATE_CLOSING),
+ OP_EQ, "closing");
+ tt_str_op(channel_listener_state_to_string(CHANNEL_LISTENER_STATE_ERROR),
+ OP_EQ, "channel listener error");
+ tt_str_op(channel_listener_state_to_string(CHANNEL_LISTENER_STATE_LISTENING),
+ OP_EQ, "listening");
+ tt_str_op(channel_listener_state_to_string(CHANNEL_LISTENER_STATE_LAST),
+ OP_EQ, "unknown or invalid channel listener state");
+ tt_str_op(channel_listener_state_to_string(INT_MAX),
+ OP_EQ, "unknown or invalid channel listener state");
+
+ done:
+ ;
+}
+
+static networkstatus_t *mock_ns = NULL;
+
+static networkstatus_t *
+mock_networkstatus_get_latest_consensus(void)
+{
+ return mock_ns;
+}
+
+static void
+test_channel_duplicates(void *arg)
+{
+ channel_t *chan = NULL;
+ routerstatus_t rs;
+
+ (void) arg;
+
+ setup_full_capture_of_logs(LOG_INFO);
+ /* Try a flat call with channel nor connections. */
+ channel_check_for_duplicates();
+ expect_log_msg_containing(
+ "Found 0 connections to 0 relays. Found 0 current canonical "
+ "connections, in 0 of which we were a non-canonical peer. "
+ "0 relays had more than 1 connection, 0 had more than 2, and "
+ "0 had more than 4 connections.");
+
+ mock_ns = tor_malloc_zero(sizeof(*mock_ns));
+ mock_ns->routerstatus_list = smartlist_new();
+ MOCK(networkstatus_get_latest_consensus,
+ mock_networkstatus_get_latest_consensus);
+
+ chan = new_fake_channel();
+ tt_assert(chan);
+ chan->is_canonical = test_chan_is_canonical;
+ memset(chan->identity_digest, 'A', sizeof(chan->identity_digest));
+ channel_add_to_digest_map(chan);
+ tt_ptr_op(channel_find_by_remote_identity(chan->identity_digest, NULL),
+ OP_EQ, chan);
+
+ /* No relay has been associated with this channel. */
+ channel_check_for_duplicates();
+ expect_log_msg_containing(
+ "Found 0 connections to 0 relays. Found 0 current canonical "
+ "connections, in 0 of which we were a non-canonical peer. "
+ "0 relays had more than 1 connection, 0 had more than 2, and "
+ "0 had more than 4 connections.");
+
+ /* Associate relay to this connection in the consensus. */
+ memset(&rs, 0, sizeof(rs));
+ memset(rs.identity_digest, 'A', sizeof(rs.identity_digest));
+ smartlist_add(mock_ns->routerstatus_list, &rs);
+
+ /* Non opened channel. */
+ chan->state = CHANNEL_STATE_CLOSING;
+ channel_check_for_duplicates();
+ expect_log_msg_containing(
+ "Found 0 connections to 0 relays. Found 0 current canonical "
+ "connections, in 0 of which we were a non-canonical peer. "
+ "0 relays had more than 1 connection, 0 had more than 2, and "
+ "0 had more than 4 connections.");
+ chan->state = CHANNEL_STATE_OPEN;
+
+ channel_check_for_duplicates();
+ expect_log_msg_containing(
+ "Found 1 connections to 1 relays. Found 0 current canonical "
+ "connections, in 0 of which we were a non-canonical peer. "
+ "0 relays had more than 1 connection, 0 had more than 2, and "
+ "0 had more than 4 connections.");
+
+ test_chan_should_be_canonical = 1;
+ channel_check_for_duplicates();
+ expect_log_msg_containing(
+ "Found 1 connections to 1 relays. Found 1 current canonical "
+ "connections, in 1 of which we were a non-canonical peer. "
+ "0 relays had more than 1 connection, 0 had more than 2, and "
+ "0 had more than 4 connections.");
+ teardown_capture_of_logs();
+
+ done:
+ free_fake_channel(chan);
+ smartlist_clear(mock_ns->routerstatus_list);
+ networkstatus_vote_free(mock_ns);
+ UNMOCK(networkstatus_get_latest_consensus);
+}
+
+static void
+test_channel_for_extend(void *arg)
+{
+ channel_t *chan1 = NULL, *chan2 = NULL;
+ channel_t *ret_chan = NULL;
+ char digest[DIGEST_LEN];
+ ed25519_public_key_t ed_id;
+ tor_addr_t addr;
+ const char *msg;
+ int launch;
+ time_t now = time(NULL);
+
+ (void) arg;
+
+ memset(digest, 'A', sizeof(digest));
+ memset(&ed_id, 'B', sizeof(ed_id));
+
+ chan1 = new_fake_channel();
+ tt_assert(chan1);
+ /* Need to be registered to get added to the id map. */
+ channel_register(chan1);
+ tt_int_op(chan1->registered, OP_EQ, 1);
+ /* We need those for the test. */
+ chan1->is_canonical = test_chan_is_canonical;
+ chan1->matches_target = test_chan_matches_target;
+ chan1->timestamp_created = now - 9;
+
+ chan2 = new_fake_channel();
+ tt_assert(chan2);
+ /* Need to be registered to get added to the id map. */
+ channel_register(chan2);
+ tt_int_op(chan2->registered, OP_EQ, 1);
+ /* We need those for the test. */
+ chan2->is_canonical = test_chan_is_canonical;
+ chan2->matches_target = test_chan_matches_target;
+ /* Make it older than chan1. */
+ chan2->timestamp_created = chan1->timestamp_created - 1;
+
+ /* Set channel identities and add it to the channel map. The last one to be
+ * added is made the first one in the list so the lookup will always return
+ * that one first. */
+ channel_set_identity_digest(chan2, digest, &ed_id);
+ channel_set_identity_digest(chan1, digest, &ed_id);
+ tt_ptr_op(channel_find_by_remote_identity(digest, NULL), OP_EQ, chan1);
+ tt_ptr_op(channel_find_by_remote_identity(digest, &ed_id), OP_EQ, chan1);
+
+ /* The expected result is chan2 because it is older than chan1. */
+ ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch);
+ tt_assert(ret_chan);
+ tt_ptr_op(ret_chan, OP_EQ, chan2);
+ tt_int_op(launch, OP_EQ, 0);
+ tt_str_op(msg, OP_EQ, "Connection is fine; using it.");
+
+ /* Switch that around from previous test. */
+ chan2->timestamp_created = chan1->timestamp_created + 1;
+ ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch);
+ tt_assert(ret_chan);
+ tt_ptr_op(ret_chan, OP_EQ, chan1);
+ tt_int_op(launch, OP_EQ, 0);
+ tt_str_op(msg, OP_EQ, "Connection is fine; using it.");
+
+ /* Same creation time, num circuits will be used and they both have 0 so the
+ * channel 2 should be picked due to how channel_is_better() work. */
+ chan2->timestamp_created = chan1->timestamp_created;
+ ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch);
+ tt_assert(ret_chan);
+ tt_ptr_op(ret_chan, OP_EQ, chan1);
+ tt_int_op(launch, OP_EQ, 0);
+ tt_str_op(msg, OP_EQ, "Connection is fine; using it.");
+
+ /* For the rest of the tests, we need channel 1 to be the older. */
+ chan2->timestamp_created = chan1->timestamp_created + 1;
+
+ /* Condemned the older channel. */
+ chan1->state = CHANNEL_STATE_CLOSING;
+ ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch);
+ tt_assert(ret_chan);
+ tt_ptr_op(ret_chan, OP_EQ, chan2);
+ tt_int_op(launch, OP_EQ, 0);
+ tt_str_op(msg, OP_EQ, "Connection is fine; using it.");
+ chan1->state = CHANNEL_STATE_OPEN;
+
+ /* Make the older channel a client one. */
+ channel_mark_client(chan1);
+ ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch);
+ tt_assert(ret_chan);
+ tt_ptr_op(ret_chan, OP_EQ, chan2);
+ tt_int_op(launch, OP_EQ, 0);
+ tt_str_op(msg, OP_EQ, "Connection is fine; using it.");
+ channel_clear_client(chan1);
+
+ /* Non matching ed identity with valid digest. */
+ ed25519_public_key_t dumb_ed_id;
+ memset(&dumb_ed_id, 0, sizeof(dumb_ed_id));
+ ret_chan = channel_get_for_extend(digest, &dumb_ed_id, &addr, &msg,
+ &launch);
+ tt_assert(!ret_chan);
+ tt_str_op(msg, OP_EQ, "Not connected. Connecting.");
+ tt_int_op(launch, OP_EQ, 1);
+
+ /* Opening channel, we'll check if the target address matches. */
+ test_chan_should_match_target = 1;
+ chan1->state = CHANNEL_STATE_OPENING;
+ chan2->state = CHANNEL_STATE_OPENING;
+ ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch);
+ tt_assert(!ret_chan);
+ tt_str_op(msg, OP_EQ, "Connection in progress; waiting.");
+ tt_int_op(launch, OP_EQ, 0);
+ chan1->state = CHANNEL_STATE_OPEN;
+ chan2->state = CHANNEL_STATE_OPEN;
+
+ /* Mark channel 1 as bad for circuits. */
+ channel_mark_bad_for_new_circs(chan1);
+ ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch);
+ tt_assert(ret_chan);
+ tt_ptr_op(ret_chan, OP_EQ, chan2);
+ tt_int_op(launch, OP_EQ, 0);
+ tt_str_op(msg, OP_EQ, "Connection is fine; using it.");
+ chan1->is_bad_for_new_circs = 0;
+
+ /* Mark both channels as unusable. */
+ channel_mark_bad_for_new_circs(chan1);
+ channel_mark_bad_for_new_circs(chan2);
+ ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch);
+ tt_assert(!ret_chan);
+ tt_str_op(msg, OP_EQ, "Connections all too old, or too non-canonical. "
+ " Launching a new one.");
+ tt_int_op(launch, OP_EQ, 1);
+ chan1->is_bad_for_new_circs = 0;
+ chan2->is_bad_for_new_circs = 0;
+
+ /* Non canonical channels. */
+ test_chan_should_match_target = 0;
+ test_chan_canonical_should_be_reliable = 1;
+ ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch);
+ tt_assert(!ret_chan);
+ tt_str_op(msg, OP_EQ, "Connections all too old, or too non-canonical. "
+ " Launching a new one.");
+ tt_int_op(launch, OP_EQ, 1);
+
+ done:
+ free_fake_channel(chan1);
+ free_fake_channel(chan2);
+}
+
+static void
+test_channel_listener(void *arg)
+{
+ int old_count;
+ time_t now = time(NULL);
+ channel_listener_t *chan = NULL;
+
+ (void) arg;
+
+ chan = tor_malloc_zero(sizeof(*chan));
+ tt_assert(chan);
+ channel_init_listener(chan);
+ tt_u64_op(chan->global_identifier, OP_EQ, 1);
+ tt_int_op(chan->timestamp_created, OP_GE, now);
+ chan->close = test_chan_listener_close;
+
+ /* Register it. At this point, it is not open so it will be put in the
+ * finished list. */
+ channel_listener_register(chan);
+ tt_int_op(chan->registered, OP_EQ, 1);
+ channel_listener_unregister(chan);
+
+ /* Register it as listening now thus active. */
+ chan->state = CHANNEL_LISTENER_STATE_LISTENING;
+ channel_listener_register(chan);
+ tt_int_op(chan->registered, OP_EQ, 1);
+
+ /* Set the listener function. */
+ channel_listener_set_listener_fn(chan, test_chan_listener_fn);
+ tt_ptr_op(chan->listener, OP_EQ, test_chan_listener_fn);
+
+ /* Put a channel in the listener incoming list and queue it.
+ * function. By doing this, the listener() handler will be called. */
+ channel_t *in_chan = new_fake_channel();
+ old_count = test_chan_listener_fn_called;
+ channel_listener_queue_incoming(chan, in_chan);
+ free_fake_channel(in_chan);
+ tt_int_op(test_chan_listener_fn_called, OP_EQ, old_count + 1);
+
+ /* Put listener channel in CLOSING state. */
+ old_count = test_chan_listener_close_fn_called;
+ channel_listener_mark_for_close(chan);
+ tt_int_op(test_chan_listener_close_fn_called, OP_EQ, old_count + 1);
+ channel_listener_change_state(chan, CHANNEL_LISTENER_STATE_CLOSED);
+
+ /* Dump stats so we at least hit the code path. */
+ chan->describe_transport = test_chan_listener_describe_transport;
+ /* There is a check for "now > timestamp_created" when dumping the stats so
+ * make sure we go in. */
+ chan->timestamp_created = now - 10;
+ channel_listener_dump_statistics(chan, LOG_INFO);
+
+ done:
+ channel_free_all();
+}
+
struct testcase_t channel_tests[] = {
- { "dumpstats", test_channel_dumpstats, TT_FORK, NULL, NULL },
- { "flush", test_channel_flush, TT_FORK, NULL, NULL },
- { "flushmux", test_channel_flushmux, TT_FORK, NULL, NULL },
- { "incoming", test_channel_incoming, TT_FORK, NULL, NULL },
- { "lifecycle", test_channel_lifecycle, TT_FORK, NULL, NULL },
- { "lifecycle_2", test_channel_lifecycle_2, TT_FORK, NULL, NULL },
- { "multi", test_channel_multi, TT_FORK, NULL, NULL },
- { "queue_impossible", test_channel_queue_impossible, TT_FORK, NULL, NULL },
- { "queue_incoming", test_channel_queue_incoming, TT_FORK, NULL, NULL },
- { "queue_size", test_channel_queue_size, TT_FORK, NULL, NULL },
- { "write", test_channel_write, TT_FORK, NULL, NULL },
- { "id_map", test_channel_id_map, TT_FORK, NULL, NULL },
+ { "inbound_cell", test_channel_inbound_cell, TT_FORK,
+ NULL, NULL },
+ { "outbound_cell", test_channel_outbound_cell, TT_FORK,
+ NULL, NULL },
+ { "id_map", test_channel_id_map, TT_FORK,
+ NULL, NULL },
+ { "lifecycle", test_channel_lifecycle, TT_FORK,
+ NULL, NULL },
+ { "lifecycle_2", test_channel_lifecycle_2, TT_FORK,
+ NULL, NULL },
+ { "dumpstats", test_channel_dumpstats, TT_FORK,
+ NULL, NULL },
+ { "state", test_channel_state, TT_FORK,
+ NULL, NULL },
+ { "duplicates", test_channel_duplicates, TT_FORK,
+ NULL, NULL },
+ { "get_channel_for_extend", test_channel_for_extend, TT_FORK,
+ NULL, NULL },
+ { "listener", test_channel_listener, TT_FORK,
+ NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_channelpadding.c b/src/test/test_channelpadding.c
index 391ead3c72..90da2163a6 100644
--- a/src/test/test_channelpadding.c
+++ b/src/test/test_channelpadding.c
@@ -1,3 +1,6 @@
+/* Copyright (c) 2016-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
#define TOR_CHANNEL_INTERNAL_
#define MAIN_PRIVATE
#define NETWORKSTATUS_PRIVATE
@@ -276,7 +279,6 @@ test_channelpadding_timers(void *arg)
{
channelpadding_decision_t decision;
channel_t *chans[CHANNELS_TO_TEST];
- int64_t new_time;
(void)arg;
tor_libevent_postfork();
@@ -286,8 +288,9 @@ test_channelpadding_timers(void *arg)
monotime_init();
monotime_enable_test_mocking();
- monotime_set_mock_time_nsec(1);
- monotime_coarse_set_mock_time_nsec(1);
+ uint64_t nsec_mock = 1;
+ monotime_set_mock_time_nsec(nsec_mock);
+ monotime_coarse_set_mock_time_nsec(nsec_mock);
timers_initialize();
channelpadding_new_consensus_params(NULL);
@@ -301,11 +304,14 @@ test_channelpadding_timers(void *arg)
tried_to_write_cell = 0;
int i = 0;
+ monotime_coarse_t now;
+ monotime_coarse_get(&now);
+
/* This loop fills our timerslot array with timers of increasing time
* until they fire */
for (; i < CHANNELPADDING_MAX_TIMERS; i++) {
- chans[i]->next_padding_time_ms = monotime_coarse_absolute_msec()
- + 10 + i*4;
+ monotime_coarse_add_msec(&chans[i]->next_padding_time,
+ &now, 10 + i*4);
decision = channelpadding_decide_to_pad_channel(chans[i]);
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
tt_assert(chans[i]->pending_padding_callback);
@@ -315,7 +321,8 @@ test_channelpadding_timers(void *arg)
/* This loop should add timers to the first position in the timerslot
* array, since its timeout is before all other timers. */
for (; i < CHANNELS_TO_TEST/3; i++) {
- chans[i]->next_padding_time_ms = monotime_coarse_absolute_msec() + 1;
+ monotime_coarse_add_msec(&chans[i]->next_padding_time,
+ &now, 1);
decision = channelpadding_decide_to_pad_channel(chans[i]);
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
tt_assert(chans[i]->pending_padding_callback);
@@ -326,8 +333,8 @@ test_channelpadding_timers(void *arg)
* pseudorandom pattern. It ensures that the lists can grow with multiple
* timers in them. */
for (; i < CHANNELS_TO_TEST/2; i++) {
- chans[i]->next_padding_time_ms = monotime_coarse_absolute_msec() + 10 +
- i*3 % CHANNELPADDING_MAX_TIMERS;
+ monotime_coarse_add_msec(&chans[i]->next_padding_time,
+ &now, 10 + i*3 % CHANNELPADDING_MAX_TIMERS);
decision = channelpadding_decide_to_pad_channel(chans[i]);
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
tt_assert(chans[i]->pending_padding_callback);
@@ -337,8 +344,8 @@ test_channelpadding_timers(void *arg)
/* This loop should add timers to the last position in the timerslot
* array, since its timeout is after all other timers. */
for (; i < CHANNELS_TO_TEST; i++) {
- chans[i]->next_padding_time_ms = monotime_coarse_absolute_msec() + 500 +
- i % CHANNELPADDING_MAX_TIMERS;
+ monotime_coarse_add_msec(&chans[i]->next_padding_time,
+ &now, 500 + i % CHANNELPADDING_MAX_TIMERS);
decision = channelpadding_decide_to_pad_channel(chans[i]);
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
tt_assert(chans[i]->pending_padding_callback);
@@ -346,9 +353,9 @@ test_channelpadding_timers(void *arg)
}
// Wait for the timers and then kill the event loop.
- new_time = (monotime_coarse_absolute_msec()+1001)*NSEC_PER_MSEC;
- monotime_coarse_set_mock_time_nsec(new_time);
- monotime_set_mock_time_nsec(new_time);
+ nsec_mock += 1001 * NSEC_PER_MSEC;
+ monotime_coarse_set_mock_time_nsec(nsec_mock);
+ monotime_set_mock_time_nsec(nsec_mock);
timers_run_pending();
tt_int_op(tried_to_write_cell, OP_EQ, CHANNELS_TO_TEST);
@@ -385,6 +392,7 @@ test_channelpadding_killonehop(void *arg)
monotime_enable_test_mocking();
monotime_set_mock_time_nsec(1);
monotime_coarse_set_mock_time_nsec(1);
+ new_time = 1;
timers_initialize();
setup_mock_consensus();
@@ -398,9 +406,12 @@ test_channelpadding_killonehop(void *arg)
smartlist_clear(current_md_consensus->net_params);
channelpadding_new_consensus_params(current_md_consensus);
+ monotime_coarse_t now;
+ monotime_coarse_get(&now);
+
tried_to_write_cell = 0;
get_options_mutable()->Tor2webMode = 1;
- client_relay3->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+ monotime_coarse_add_msec(&client_relay3->next_padding_time, &now, 100);
decision = channelpadding_decide_to_pad_channel(client_relay3);
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
tt_assert(client_relay3->pending_padding_callback);
@@ -410,9 +421,10 @@ test_channelpadding_killonehop(void *arg)
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_ALREADY_SCHEDULED);
// Wait for the timer
- new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC;
+ new_time += 101*NSEC_PER_MSEC;
monotime_coarse_set_mock_time_nsec(new_time);
monotime_set_mock_time_nsec(new_time);
+ monotime_coarse_get(&now);
timers_run_pending();
tt_int_op(tried_to_write_cell, OP_EQ, 1);
tt_assert(!client_relay3->pending_padding_callback);
@@ -424,7 +436,7 @@ test_channelpadding_killonehop(void *arg)
// Before the client tries to pad, the relay will still pad:
tried_to_write_cell = 0;
- relay3_client->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+ monotime_coarse_add_msec(&relay3_client->next_padding_time, &now, 100);
get_options_mutable()->ORPort_set = 1;
get_options_mutable()->Tor2webMode = 0;
decision = channelpadding_decide_to_pad_channel(relay3_client);
@@ -432,9 +444,10 @@ test_channelpadding_killonehop(void *arg)
tt_assert(relay3_client->pending_padding_callback);
// Wait for the timer
- new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC;
+ new_time += 101*NSEC_PER_MSEC;
monotime_coarse_set_mock_time_nsec(new_time);
monotime_set_mock_time_nsec(new_time);
+ monotime_coarse_get(&now);
timers_run_pending();
tt_int_op(tried_to_write_cell, OP_EQ, 1);
tt_assert(!client_relay3->pending_padding_callback);
@@ -444,7 +457,7 @@ test_channelpadding_killonehop(void *arg)
tt_assert(relay3_client->padding_enabled);
tt_assert(client_relay3->padding_enabled);
get_options_mutable()->Tor2webMode = 1;
- /* For the relay to recieve the negotiate: */
+ /* For the relay to receive the negotiate: */
get_options_mutable()->ORPort_set = 1;
decision = channelpadding_decide_to_pad_channel(client_relay3);
tt_int_op(decision, OP_EQ, CHANNELPADDING_WONTPAD);
@@ -471,7 +484,8 @@ test_channelpadding_killonehop(void *arg)
get_options_mutable()->ORPort_set = 0;
get_options_mutable()->HiddenServiceSingleHopMode = 1;
get_options_mutable()->HiddenServiceNonAnonymousMode = 1;
- client_relay3->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+
+ monotime_coarse_add_msec(&client_relay3->next_padding_time, &now, 100);
decision = channelpadding_decide_to_pad_channel(client_relay3);
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
tt_assert(client_relay3->pending_padding_callback);
@@ -481,9 +495,10 @@ test_channelpadding_killonehop(void *arg)
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_ALREADY_SCHEDULED);
// Wait for the timer
- new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC;
+ new_time += 101 * NSEC_PER_MSEC;
monotime_coarse_set_mock_time_nsec(new_time);
monotime_set_mock_time_nsec(new_time);
+ monotime_coarse_get(&now);
timers_run_pending();
tt_int_op(tried_to_write_cell, OP_EQ, 1);
tt_assert(!client_relay3->pending_padding_callback);
@@ -495,7 +510,7 @@ test_channelpadding_killonehop(void *arg)
// Before the client tries to pad, the relay will still pad:
tried_to_write_cell = 0;
- relay3_client->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+ monotime_coarse_add_msec(&relay3_client->next_padding_time, &now, 100);
get_options_mutable()->ORPort_set = 1;
get_options_mutable()->HiddenServiceSingleHopMode = 0;
get_options_mutable()->HiddenServiceNonAnonymousMode = 0;
@@ -504,9 +519,10 @@ test_channelpadding_killonehop(void *arg)
tt_assert(relay3_client->pending_padding_callback);
// Wait for the timer
- new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC;
+ new_time += 101 * NSEC_PER_MSEC;
monotime_coarse_set_mock_time_nsec(new_time);
monotime_set_mock_time_nsec(new_time);
+ monotime_coarse_get(&now);
timers_run_pending();
tt_int_op(tried_to_write_cell, OP_EQ, 1);
tt_assert(!client_relay3->pending_padding_callback);
@@ -514,7 +530,7 @@ test_channelpadding_killonehop(void *arg)
// Test client side (it should stop immediately)
get_options_mutable()->HiddenServiceSingleHopMode = 1;
get_options_mutable()->HiddenServiceNonAnonymousMode = 1;
- /* For the relay to recieve the negotiate: */
+ /* For the relay to receive the negotiate: */
get_options_mutable()->ORPort_set = 1;
decision = channelpadding_decide_to_pad_channel(client_relay3);
tt_int_op(decision, OP_EQ, CHANNELPADDING_WONTPAD);
@@ -570,6 +586,9 @@ test_channelpadding_consensus(void *arg)
monotime_enable_test_mocking();
monotime_set_mock_time_nsec(1);
monotime_coarse_set_mock_time_nsec(1);
+ new_time = 1;
+ monotime_coarse_t now;
+ monotime_coarse_get(&now);
timers_initialize();
if (!connection_array)
@@ -583,7 +602,7 @@ test_channelpadding_consensus(void *arg)
/* Test 1: Padding can be completely disabled via consensus */
tried_to_write_cell = 0;
- chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+ monotime_coarse_add_msec(&chan->next_padding_time, &now, 100);
decision = channelpadding_decide_to_pad_channel(chan);
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
tt_assert(chan->pending_padding_callback);
@@ -593,9 +612,10 @@ test_channelpadding_consensus(void *arg)
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_ALREADY_SCHEDULED);
// Wait for the timer
- new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC;
+ new_time += 101*NSEC_PER_MSEC;
monotime_coarse_set_mock_time_nsec(new_time);
monotime_set_mock_time_nsec(new_time);
+ monotime_coarse_get(&now);
timers_run_pending();
tt_int_op(tried_to_write_cell, OP_EQ, 1);
tt_assert(!chan->pending_padding_callback);
@@ -625,7 +645,7 @@ test_channelpadding_consensus(void *arg)
tt_i64_op(val, OP_EQ, 0);
val = channelpadding_compute_time_until_pad_for_netflow(chan);
tt_i64_op(val, OP_EQ, -2);
- tt_assert(!chan->next_padding_time_ms);
+ tt_assert(monotime_coarse_is_zero(&chan->next_padding_time));
smartlist_clear(current_md_consensus->net_params);
@@ -638,7 +658,7 @@ test_channelpadding_consensus(void *arg)
channelpadding_new_consensus_params(current_md_consensus);
tried_to_write_cell = 0;
- chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+ monotime_coarse_add_msec(&chan->next_padding_time, &now, 100);
decision = channelpadding_decide_to_pad_channel(chan);
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
tt_assert(chan->pending_padding_callback);
@@ -650,9 +670,10 @@ test_channelpadding_consensus(void *arg)
tt_i64_op(val, OP_LE, 200);
// Wait for the timer
- new_time = (monotime_coarse_absolute_msec()+201)*NSEC_PER_MSEC;
+ new_time += 201*NSEC_PER_MSEC;
monotime_set_mock_time_nsec(new_time);
monotime_coarse_set_mock_time_nsec(new_time);
+ monotime_coarse_get(&now);
timers_run_pending();
tt_int_op(tried_to_write_cell, OP_EQ, 1);
tt_assert(!chan->pending_padding_callback);
@@ -817,7 +838,7 @@ test_channelpadding_negotiation(void *arg)
get_options_mutable()->ORPort_set = 0;
/* Test case #2: Torrc options */
- /* ConnectionPadding auto; Relay doesn't suport us */
+ /* ConnectionPadding auto; Relay doesn't support us */
((channel_tls_t*)relay3_client)->conn->link_proto = 4;
relay3_client->padding_enabled = 0;
tried_to_write_cell = 0;
@@ -828,7 +849,7 @@ test_channelpadding_negotiation(void *arg)
((channel_tls_t*)relay3_client)->conn->link_proto = 5;
relay3_client->padding_enabled = 1;
- /* ConnectionPadding 1; Relay doesn't suport us */
+ /* ConnectionPadding 1; Relay doesn't support us */
get_options_mutable()->ConnectionPadding = 1;
tried_to_write_cell = 0;
decision = channelpadding_decide_to_pad_channel(client_relay3);
@@ -944,6 +965,9 @@ test_channelpadding_decide_to_pad_channel(void *arg)
monotime_enable_test_mocking();
monotime_set_mock_time_nsec(1);
monotime_coarse_set_mock_time_nsec(1);
+ new_time = 1;
+ monotime_coarse_t now;
+ monotime_coarse_get(&now);
timers_initialize();
setup_full_capture_of_logs(LOG_WARN);
channelpadding_new_consensus_params(NULL);
@@ -960,7 +984,7 @@ test_channelpadding_decide_to_pad_channel(void *arg)
/* Test case #2a: > 1.1s until timeout */
tried_to_write_cell = 0;
- chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 1200;
+ monotime_coarse_add_msec(&chan->next_padding_time, &now, 1200);
decision = channelpadding_decide_to_pad_channel(chan);
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADLATER);
tt_assert(!chan->pending_padding_callback);
@@ -968,23 +992,27 @@ test_channelpadding_decide_to_pad_channel(void *arg)
/* Test case #2b: >= 1.0s until timeout */
tried_to_write_cell = 0;
- chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 1000;
+ monotime_coarse_add_msec(&chan->next_padding_time, &now, 1000);
decision = channelpadding_decide_to_pad_channel(chan);
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
tt_assert(chan->pending_padding_callback);
tt_int_op(tried_to_write_cell, OP_EQ, 0);
+ // Set up a timer for the <0 case below.
+ monotime_coarse_t now_minus_100s;
+ monotime_coarse_add_msec(&now_minus_100s, &now, 900);
// Wait for the timer from case #2b
- new_time = (monotime_coarse_absolute_msec() + 1000)*NSEC_PER_MSEC;
+ new_time += 1000*NSEC_PER_MSEC;
monotime_set_mock_time_nsec(new_time);
monotime_coarse_set_mock_time_nsec(new_time);
+ monotime_coarse_get(&now);
timers_run_pending();
tt_int_op(tried_to_write_cell, OP_EQ, 1);
tt_assert(!chan->pending_padding_callback);
/* Test case #2c: > 0.1s until timeout */
tried_to_write_cell = 0;
- chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+ monotime_coarse_add_msec(&chan->next_padding_time, &now, 100);
decision = channelpadding_decide_to_pad_channel(chan);
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
tt_assert(chan->pending_padding_callback);
@@ -995,16 +1023,17 @@ test_channelpadding_decide_to_pad_channel(void *arg)
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_ALREADY_SCHEDULED);
// Wait for the timer
- new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC;
+ new_time += 101*NSEC_PER_MSEC;
monotime_coarse_set_mock_time_nsec(new_time);
monotime_set_mock_time_nsec(new_time);
+ monotime_coarse_get(&now);
timers_run_pending();
tt_int_op(tried_to_write_cell, OP_EQ, 1);
tt_assert(!chan->pending_padding_callback);
/* Test case #2e: 0s until timeout */
tried_to_write_cell = 0;
- chan->next_padding_time_ms = monotime_coarse_absolute_msec();
+ monotime_coarse_add_msec(&chan->next_padding_time, &now, 0);
decision = channelpadding_decide_to_pad_channel(chan);
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SENT);
tt_int_op(tried_to_write_cell, OP_EQ, 1);
@@ -1012,7 +1041,7 @@ test_channelpadding_decide_to_pad_channel(void *arg)
/* Test case #2f: <0s until timeout */
tried_to_write_cell = 0;
- chan->next_padding_time_ms = monotime_coarse_absolute_msec() - 100;
+ monotime_coarse_add_msec(&chan->next_padding_time, &now_minus_100s, 0);
decision = channelpadding_decide_to_pad_channel(chan);
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SENT);
tt_int_op(tried_to_write_cell, OP_EQ, 1);
@@ -1020,7 +1049,7 @@ test_channelpadding_decide_to_pad_channel(void *arg)
/* Test case #3: Channel that sends a packet while timeout is scheduled */
tried_to_write_cell = 0;
- chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+ monotime_coarse_add_msec(&chan->next_padding_time, &now, 100);
decision = channelpadding_decide_to_pad_channel(chan);
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
tt_int_op(tried_to_write_cell, OP_EQ, 0);
@@ -1031,9 +1060,10 @@ test_channelpadding_decide_to_pad_channel(void *arg)
// We don't expect any timer callbacks here. Make a dummy one to be sure.
// Wait for the timer
- new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC;
+ new_time += 101*NSEC_PER_MSEC;
monotime_coarse_set_mock_time_nsec(new_time);
monotime_set_mock_time_nsec(new_time);
+ monotime_coarse_get(&now);
timers_run_pending();
tt_int_op(tried_to_write_cell, OP_EQ, 0);
@@ -1041,7 +1071,7 @@ test_channelpadding_decide_to_pad_channel(void *arg)
/* Test case #4: Channel that closes while a timeout is scheduled */
tried_to_write_cell = 0;
- chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+ monotime_coarse_add_msec(&chan->next_padding_time, &now, 100);
decision = channelpadding_decide_to_pad_channel(chan);
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
tt_int_op(tried_to_write_cell, OP_EQ, 0);
@@ -1051,9 +1081,10 @@ test_channelpadding_decide_to_pad_channel(void *arg)
chan->state = CHANNEL_STATE_MAINT;
// We don't expect any timer callbacks here. Make a dummy one to be sure.
- new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC;
+ new_time += 101*NSEC_PER_MSEC;
monotime_coarse_set_mock_time_nsec(new_time);
monotime_set_mock_time_nsec(new_time);
+ monotime_coarse_get(&now);
timers_run_pending();
tt_int_op(tried_to_write_cell, OP_EQ, 0);
@@ -1062,16 +1093,17 @@ test_channelpadding_decide_to_pad_channel(void *arg)
/* Test case #5: Make sure previous test case didn't break everything */
tried_to_write_cell = 0;
- chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+ monotime_coarse_add_msec(&chan->next_padding_time, &now, 100);
decision = channelpadding_decide_to_pad_channel(chan);
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
tt_assert(chan->pending_padding_callback);
tt_int_op(tried_to_write_cell, OP_EQ, 0);
// Wait for the timer
- new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC;
+ new_time += 101*NSEC_PER_MSEC;
monotime_coarse_set_mock_time_nsec(new_time);
monotime_set_mock_time_nsec(new_time);
+ monotime_coarse_get(&now);
timers_run_pending();
tt_int_op(tried_to_write_cell, OP_EQ, 1);
@@ -1090,7 +1122,7 @@ test_channelpadding_decide_to_pad_channel(void *arg)
* It must be last.
*/
tried_to_write_cell = 0;
- chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100;
+ monotime_coarse_add_msec(&chan->next_padding_time, &now, 100);
decision = channelpadding_decide_to_pad_channel(chan);
tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED);
tt_int_op(tried_to_write_cell, OP_EQ, 0);
@@ -1100,9 +1132,10 @@ test_channelpadding_decide_to_pad_channel(void *arg)
free_fake_channeltls((channel_tls_t*)chan);
// We don't expect any timer callbacks here. Make a dummy one to be sure.
- new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC;
+ new_time = 101*NSEC_PER_MSEC;
monotime_coarse_set_mock_time_nsec(new_time);
monotime_set_mock_time_nsec(new_time);
+ monotime_coarse_get(&now);
timers_run_pending();
tt_int_op(tried_to_write_cell, OP_EQ, 0);
diff --git a/src/test/test_circuitlist.c b/src/test/test_circuitlist.c
index f622704ec5..d170009a9c 100644
--- a/src/test/test_circuitlist.c
+++ b/src/test/test_circuitlist.c
@@ -141,7 +141,7 @@ test_clist_maps(void *arg)
/* Okay, now free ch2 and make sure that the circuit ID is STILL not
* usable, because we haven't declared the destroy to be nonpending */
tt_int_op(cdm.ncalls, OP_EQ, 0);
- circuit_free(TO_CIRCUIT(or_c2));
+ circuit_free_(TO_CIRCUIT(or_c2));
or_c2 = NULL; /* prevent free */
tt_int_op(cdm.ncalls, OP_EQ, 2);
memset(&cdm, 0, sizeof(cdm));
@@ -160,9 +160,9 @@ test_clist_maps(void *arg)
done:
if (or_c1)
- circuit_free(TO_CIRCUIT(or_c1));
+ circuit_free_(TO_CIRCUIT(or_c1));
if (or_c2)
- circuit_free(TO_CIRCUIT(or_c2));
+ circuit_free_(TO_CIRCUIT(or_c2));
if (ch1)
tor_free(ch1->cmux);
if (ch2)
@@ -234,11 +234,11 @@ test_rend_token_maps(void *arg)
/* Marking a circuit makes it not get returned any more */
circuit_mark_for_close(TO_CIRCUIT(c1), END_CIRC_REASON_FINISHED);
tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok1));
- circuit_free(TO_CIRCUIT(c1));
+ circuit_free_(TO_CIRCUIT(c1));
c1 = NULL;
/* Freeing a circuit makes it not get returned any more. */
- circuit_free(TO_CIRCUIT(c2));
+ circuit_free_(TO_CIRCUIT(c2));
c2 = NULL;
tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok2));
@@ -275,15 +275,15 @@ test_rend_token_maps(void *arg)
done:
if (c1)
- circuit_free(TO_CIRCUIT(c1));
+ circuit_free_(TO_CIRCUIT(c1));
if (c2)
- circuit_free(TO_CIRCUIT(c2));
+ circuit_free_(TO_CIRCUIT(c2));
if (c3)
- circuit_free(TO_CIRCUIT(c3));
+ circuit_free_(TO_CIRCUIT(c3));
if (c4)
- circuit_free(TO_CIRCUIT(c4));
+ circuit_free_(TO_CIRCUIT(c4));
if (c5)
- circuit_free(TO_CIRCUIT(c5));
+ circuit_free_(TO_CIRCUIT(c5));
}
static void
@@ -452,10 +452,10 @@ test_hs_circuitmap_isolation(void *arg)
}
done:
- circuit_free(TO_CIRCUIT(circ1));
- circuit_free(TO_CIRCUIT(circ2));
- circuit_free(TO_CIRCUIT(circ3));
- circuit_free(TO_CIRCUIT(circ4));
+ circuit_free_(TO_CIRCUIT(circ1));
+ circuit_free_(TO_CIRCUIT(circ2));
+ circuit_free_(TO_CIRCUIT(circ3));
+ circuit_free_(TO_CIRCUIT(circ4));
}
struct testcase_t circuitlist_tests[] = {
diff --git a/src/test/test_circuitstats.c b/src/test/test_circuitstats.c
new file mode 100644
index 0000000000..8ebef659ca
--- /dev/null
+++ b/src/test/test_circuitstats.c
@@ -0,0 +1,201 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define CIRCUITBUILD_PRIVATE
+#define CIRCUITSTATS_PRIVATE
+#define CIRCUITLIST_PRIVATE
+#define CHANNEL_PRIVATE_
+
+#include "or.h"
+#include "test.h"
+#include "test_helpers.h"
+#include "log_test_helpers.h"
+#include "config.h"
+#include "circuitlist.h"
+#include "circuitbuild.h"
+#include "circuitstats.h"
+#include "circuituse.h"
+#include "channel.h"
+
+void test_circuitstats_timeout(void *arg);
+void test_circuitstats_hoplen(void *arg);
+origin_circuit_t *subtest_fourhop_circuit(struct timeval, int);
+origin_circuit_t *add_opened_threehop(void);
+origin_circuit_t *build_unopened_fourhop(struct timeval);
+
+int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
+
+static int marked_for_close;
+/* Mock function because we are not trying to test the close circuit that does
+ * an awful lot of checks on the circuit object. */
+static void
+mock_circuit_mark_for_close(circuit_t *circ, int reason, int line,
+ const char *file)
+{
+ (void) circ;
+ (void) reason;
+ (void) line;
+ (void) file;
+ marked_for_close = 1;
+ return;
+}
+
+origin_circuit_t *
+add_opened_threehop(void)
+{
+ origin_circuit_t *or_circ = origin_circuit_new();
+ extend_info_t fakehop;
+ memset(&fakehop, 0, sizeof(fakehop));
+
+ TO_CIRCUIT(or_circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+
+ or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
+ or_circ->build_state->desired_path_len = DEFAULT_ROUTE_LEN;
+
+ onion_append_hop(&or_circ->cpath, &fakehop);
+ onion_append_hop(&or_circ->cpath, &fakehop);
+ onion_append_hop(&or_circ->cpath, &fakehop);
+
+ or_circ->has_opened = 1;
+ TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN;
+ TO_CIRCUIT(or_circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+
+ return or_circ;
+}
+
+origin_circuit_t *
+build_unopened_fourhop(struct timeval circ_start_time)
+{
+ origin_circuit_t *or_circ = origin_circuit_new();
+ extend_info_t *fakehop = tor_malloc_zero(sizeof(extend_info_t));
+ memset(fakehop, 0, sizeof(extend_info_t));
+
+ TO_CIRCUIT(or_circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL;
+ TO_CIRCUIT(or_circ)->timestamp_began = circ_start_time;
+ TO_CIRCUIT(or_circ)->timestamp_created = circ_start_time;
+
+ or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
+ or_circ->build_state->desired_path_len = 4;
+
+ onion_append_hop(&or_circ->cpath, fakehop);
+ onion_append_hop(&or_circ->cpath, fakehop);
+ onion_append_hop(&or_circ->cpath, fakehop);
+ onion_append_hop(&or_circ->cpath, fakehop);
+
+ tor_free(fakehop);
+
+ return or_circ;
+}
+
+origin_circuit_t *
+subtest_fourhop_circuit(struct timeval circ_start_time, int should_timeout)
+{
+ origin_circuit_t *or_circ = build_unopened_fourhop(circ_start_time);
+
+ // Now make them open one at a time and call
+ // circuit_build_times_handle_completed_hop();
+ or_circ->cpath->state = CPATH_STATE_OPEN;
+ circuit_build_times_handle_completed_hop(or_circ);
+ tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 0);
+
+ or_circ->cpath->next->state = CPATH_STATE_OPEN;
+ circuit_build_times_handle_completed_hop(or_circ);
+ tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 0);
+
+ // Third hop: We should count it now.
+ or_circ->cpath->next->next->state = CPATH_STATE_OPEN;
+ circuit_build_times_handle_completed_hop(or_circ);
+ tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ,
+ !should_timeout); // 1 if counted, 0 otherwise
+
+ // Fourth hop: Don't double count
+ or_circ->cpath->next->next->next->state = CPATH_STATE_OPEN;
+ circuit_build_times_handle_completed_hop(or_circ);
+ tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ,
+ !should_timeout);
+
+ done:
+ return or_circ;
+}
+
+void
+test_circuitstats_hoplen(void *arg)
+{
+ /* Plan:
+ * 0. Test no other opened circs (relaxed timeout)
+ * 1. Check >3 hop circ building w/o timeout
+ * 2. Check >3 hop circs w/ timeouts..
+ */
+ struct timeval circ_start_time;
+ origin_circuit_t *threehop = NULL;
+ origin_circuit_t *fourhop = NULL;
+ (void)arg;
+ MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close);
+
+ circuit_build_times_init(get_circuit_build_times_mutable());
+
+ // Let's set a close_ms to 2X the initial timeout, so we can
+ // test relaxed functionality (which uses the close_ms timeout)
+ get_circuit_build_times_mutable()->close_ms *= 2;
+
+ tor_gettimeofday(&circ_start_time);
+ circ_start_time.tv_sec -= 119; // make us hit "relaxed" cutoff
+
+ // Test 1: Build a fourhop circuit that should get marked
+ // as relaxed and eventually counted by circuit_expire_building
+ // (but not before)
+ fourhop = subtest_fourhop_circuit(circ_start_time, 0);
+ tt_int_op(fourhop->relaxed_timeout, OP_EQ, 0);
+ tt_int_op(marked_for_close, OP_EQ, 0);
+ circuit_expire_building();
+ tt_int_op(marked_for_close, OP_EQ, 0);
+ tt_int_op(fourhop->relaxed_timeout, OP_EQ, 1);
+ TO_CIRCUIT(fourhop)->timestamp_began.tv_sec -= 119;
+ circuit_expire_building();
+ tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1);
+ tt_int_op(marked_for_close, OP_EQ, 1);
+
+ circuit_free_(TO_CIRCUIT(fourhop));
+ circuit_build_times_reset(get_circuit_build_times_mutable());
+
+ // Test 2: Add a threehop circuit for non-relaxed timeouts
+ threehop = add_opened_threehop();
+
+ /* This circuit should not timeout */
+ tor_gettimeofday(&circ_start_time);
+ circ_start_time.tv_sec -= 59;
+ fourhop = subtest_fourhop_circuit(circ_start_time, 0);
+ circuit_expire_building();
+ tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1);
+ tt_int_op(TO_CIRCUIT(fourhop)->purpose, OP_NE,
+ CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT);
+
+ circuit_free_((circuit_t *)fourhop);
+ circuit_build_times_reset(get_circuit_build_times_mutable());
+
+ /* Test 3: This circuit should now time out and get marked as a
+ * measurement circuit, but still get counted (and counted only once)
+ */
+ circ_start_time.tv_sec -= 2;
+ fourhop = subtest_fourhop_circuit(circ_start_time, 0);
+ tt_int_op(TO_CIRCUIT(fourhop)->purpose, OP_EQ,
+ CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT);
+ tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1);
+ circuit_expire_building();
+ tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1);
+
+ done:
+ UNMOCK(circuit_mark_for_close_);
+ circuit_free_(TO_CIRCUIT(threehop));
+ circuit_free_(TO_CIRCUIT(fourhop));
+ circuit_build_times_free_timeouts(get_circuit_build_times_mutable());
+}
+
+#define TEST_CIRCUITSTATS(name, flags) \
+ { #name, test_##name, (flags), NULL, NULL }
+
+struct testcase_t circuitstats_tests[] = {
+ TEST_CIRCUITSTATS(circuitstats_hoplen, TT_FORK),
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_config.c b/src/test/test_config.c
index e7380c1d14..2bcc0cad7b 100644
--- a/src/test/test_config.c
+++ b/src/test/test_config.c
@@ -323,7 +323,7 @@ test_config_write_to_data_subdir(void *arg)
tt_int_op(mkdir(options->DataDirectory, 0700), OP_EQ, 0);
#endif
- // Write attempt shoudl fail, if subdirectory doesn't exist.
+ // Write attempt should fail, if subdirectory doesn't exist.
tt_assert(write_to_data_subdir(subdir, fname, str, NULL));
tt_assert(! check_or_create_data_subdir(subdir));
@@ -585,6 +585,22 @@ test_config_parse_transport_options_line(void *arg)
}
}
+/* Mocks needed for the compute_max_mem_in_queues test */
+static int get_total_system_memory_mock(size_t *mem_out);
+
+static size_t total_system_memory_output = 0;
+static int total_system_memory_return = 0;
+
+static int
+get_total_system_memory_mock(size_t *mem_out)
+{
+ if (! mem_out)
+ return -1;
+
+ *mem_out = total_system_memory_output;
+ return total_system_memory_return;
+}
+
/* Mocks needed for the transport plugin line test */
static void pt_kickstart_proxy_mock(const smartlist_t *transport_list,
@@ -1391,7 +1407,7 @@ test_config_resolve_my_address(void *arg)
* if running on.
* 3. Hostname from previous step cannot be converted to
* address by using tor_inet_aton() function.
- * 4. However, tor_lookup_hostname() succeds in resolving the
+ * 4. However, tor_lookup_hostname() succeeds in resolving the
* hostname from step 2.
* 5. Unfortunately, tor_addr_is_internal() deems this address
* to be internal.
@@ -4833,7 +4849,7 @@ test_config_include_limit(void *data)
torrc_path);
tt_int_op(write_str_to_file(torrc_path, torrc_contents, 0), OP_EQ, 0);
- tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL),
+ tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL, NULL),
OP_EQ, -1);
done:
@@ -4863,7 +4879,7 @@ test_config_include_does_not_exist(void *data)
tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s",
missing_path);
- tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL),
+ tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL, NULL),
OP_EQ, -1);
done:
@@ -4895,7 +4911,7 @@ test_config_include_error_in_included_file(void *data)
tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s",
invalid_path);
- tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL),
+ tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL, NULL),
OP_EQ, -1);
done:
@@ -4937,8 +4953,8 @@ test_config_include_empty_file_folder(void *data)
folder_path, file_path);
int include_used;
- tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used),
- OP_EQ, 0);
+ tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used,
+ NULL), OP_EQ, 0);
tt_ptr_op(result, OP_EQ, NULL);
tt_int_op(include_used, OP_EQ, 1);
@@ -4975,7 +4991,8 @@ test_config_include_no_permission(void *data)
folder_path);
int include_used;
- tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used),
+ tt_int_op(config_get_lines_include(torrc_contents, &result, 0,
+ &include_used, NULL),
OP_EQ, -1);
tt_ptr_op(result, OP_EQ, NULL);
@@ -5031,8 +5048,8 @@ test_config_include_recursion_before_after(void *data)
}
int include_used;
- tt_int_op(config_get_lines_include(file_contents, &result, 0, &include_used),
- OP_EQ, 0);
+ tt_int_op(config_get_lines_include(file_contents, &result, 0, &include_used,
+ NULL), OP_EQ, 0);
tt_ptr_op(result, OP_NE, NULL);
tt_int_op(include_used, OP_EQ, 1);
@@ -5096,8 +5113,8 @@ test_config_include_recursion_after_only(void *data)
}
int include_used;
- tt_int_op(config_get_lines_include(file_contents, &result, 0, &include_used),
- OP_EQ, 0);
+ tt_int_op(config_get_lines_include(file_contents, &result, 0, &include_used,
+ NULL), OP_EQ, 0);
tt_ptr_op(result, OP_NE, NULL);
tt_int_op(include_used, OP_EQ, 1);
@@ -5185,8 +5202,8 @@ test_config_include_folder_order(void *data)
torrcd);
int include_used;
- tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used),
- OP_EQ, 0);
+ tt_int_op(config_get_lines_include(torrc_contents, &result, 0, &include_used,
+ NULL), OP_EQ, 0);
tt_ptr_op(result, OP_NE, NULL);
tt_int_op(include_used, OP_EQ, 1);
@@ -5239,8 +5256,8 @@ test_config_include_path_syntax(void *data)
esc_dir_with_pathsep);
int include_used;
- tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used),
- OP_EQ, 0);
+ tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used,
+ NULL), OP_EQ, 0);
tt_ptr_op(result, OP_EQ, NULL);
tt_int_op(include_used, OP_EQ, 1);
@@ -5294,14 +5311,14 @@ test_config_include_has_include(void *data)
char torrc_contents[1000] = "Test 1\n";
int include_used;
- tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used),
- OP_EQ, 0);
+ tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used,
+ NULL), OP_EQ, 0);
tt_int_op(include_used, OP_EQ, 0);
config_free_lines(result);
tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s\n", dir);
- tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used),
- OP_EQ, 0);
+ tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used,
+ NULL), OP_EQ, 0);
tt_int_op(include_used, OP_EQ, 1);
done:
@@ -5516,6 +5533,158 @@ test_config_check_bridge_distribution_setting_unrecognised(void *arg)
return;
}
+static void
+test_config_include_opened_file_list(void *data)
+{
+ (void)data;
+
+ config_line_t *result = NULL;
+ smartlist_t *opened_files = smartlist_new();
+ char *torrcd = NULL;
+ char *subfolder = NULL;
+ char *path = NULL;
+ char *empty = NULL;
+ char *file = NULL;
+ char *dot = NULL;
+ char *dir = tor_strdup(get_fname("test_include_opened_file_list"));
+ tt_ptr_op(dir, OP_NE, NULL);
+
+#ifdef _WIN32
+ tt_int_op(mkdir(dir), OP_EQ, 0);
+#else
+ tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
+#endif
+
+ tor_asprintf(&torrcd, "%s"PATH_SEPARATOR"%s", dir, "torrc.d");
+
+#ifdef _WIN32
+ tt_int_op(mkdir(torrcd), OP_EQ, 0);
+#else
+ tt_int_op(mkdir(torrcd, 0700), OP_EQ, 0);
+#endif
+
+ tor_asprintf(&subfolder, "%s"PATH_SEPARATOR"%s", torrcd, "subfolder");
+
+#ifdef _WIN32
+ tt_int_op(mkdir(subfolder), OP_EQ, 0);
+#else
+ tt_int_op(mkdir(subfolder, 0700), OP_EQ, 0);
+#endif
+
+ tor_asprintf(&path, "%s"PATH_SEPARATOR"%s", subfolder,
+ "01_file_in_subfolder");
+ tt_int_op(write_str_to_file(path, "Test 1\n", 0), OP_EQ, 0);
+
+ tor_asprintf(&empty, "%s"PATH_SEPARATOR"%s", torrcd, "empty");
+ tt_int_op(write_str_to_file(empty, "", 0), OP_EQ, 0);
+
+ tor_asprintf(&file, "%s"PATH_SEPARATOR"%s", torrcd, "file");
+ tt_int_op(write_str_to_file(file, "Test 2\n", 0), OP_EQ, 0);
+
+ tor_asprintf(&dot, "%s"PATH_SEPARATOR"%s", torrcd, ".dot");
+ tt_int_op(write_str_to_file(dot, "Test 3\n", 0), OP_EQ, 0);
+
+ char torrc_contents[1000];
+ tor_snprintf(torrc_contents, sizeof(torrc_contents),
+ "%%include %s\n",
+ torrcd);
+
+ int include_used;
+ tt_int_op(config_get_lines_include(torrc_contents, &result, 0, &include_used,
+ opened_files), OP_EQ, 0);
+ tt_ptr_op(result, OP_NE, NULL);
+ tt_int_op(include_used, OP_EQ, 1);
+
+ tt_int_op(smartlist_len(opened_files), OP_EQ, 4);
+ tt_int_op(smartlist_contains_string(opened_files, torrcd), OP_EQ, 1);
+ tt_int_op(smartlist_contains_string(opened_files, subfolder), OP_EQ, 1);
+ // files inside subfolders are not opended, only the subfolder is opened
+ tt_int_op(smartlist_contains_string(opened_files, empty), OP_EQ, 1);
+ tt_int_op(smartlist_contains_string(opened_files, file), OP_EQ, 1);
+ // dot files are not opened as we ignore them when we get their name from
+ // their parent folder
+
+ done:
+ SMARTLIST_FOREACH(opened_files, char *, f, tor_free(f));
+ smartlist_free(opened_files);
+ config_free_lines(result);
+ tor_free(torrcd);
+ tor_free(subfolder);
+ tor_free(path);
+ tor_free(empty);
+ tor_free(file);
+ tor_free(dot);
+ tor_free(dir);
+}
+
+static void
+test_config_compute_max_mem_in_queues(void *data)
+{
+#define GIGABYTE(x) (U64_LITERAL(x) << 30)
+#define MEGABYTE(x) (U64_LITERAL(x) << 20)
+ (void)data;
+ MOCK(get_total_system_memory, get_total_system_memory_mock);
+
+ /* We are unable to detect the amount of memory on the system. Tor will try
+ * to use some sensible default values for 64-bit and 32-bit systems. */
+ total_system_memory_return = -1;
+
+#if SIZEOF_VOID_P >= 8
+ /* We are on a 64-bit system. */
+ tt_u64_op(compute_real_max_mem_in_queues(0, 0), OP_EQ, GIGABYTE(8));
+#else
+ /* We are on a 32-bit system. */
+ tt_u64_op(compute_real_max_mem_in_queues(0, 0), OP_EQ, GIGABYTE(1));
+#endif
+
+ /* We are able to detect the amount of RAM on the system. */
+ total_system_memory_return = 0;
+
+ /* We are running on a system with one gigabyte of RAM. */
+ total_system_memory_output = GIGABYTE(1);
+
+ /* We have 0.75 * RAM available. */
+ tt_u64_op(compute_real_max_mem_in_queues(0, 0), OP_EQ,
+ 3 * (GIGABYTE(1) / 4));
+
+ /* We are running on a tiny machine with 256 MB of RAM. */
+ total_system_memory_output = MEGABYTE(256);
+
+ /* We will now enforce a minimum of 256 MB of RAM available for the
+ * MaxMemInQueues here, even though we should only have had 0.75 * 256 = 192
+ * MB available. */
+ tt_u64_op(compute_real_max_mem_in_queues(0, 0), OP_EQ, MEGABYTE(256));
+
+#if SIZEOF_SIZE_T > 4
+ /* We are running on a machine with 8 GB of RAM. */
+ total_system_memory_output = GIGABYTE(8);
+
+ /* We will have 0.4 * RAM available. */
+ tt_u64_op(compute_real_max_mem_in_queues(0, 0), OP_EQ,
+ 2 * (GIGABYTE(8) / 5));
+
+ /* We are running on a machine with 16 GB of RAM. */
+ total_system_memory_output = GIGABYTE(16);
+
+ /* We will have 0.4 * RAM available. */
+ tt_u64_op(compute_real_max_mem_in_queues(0, 0), OP_EQ,
+ 2 * (GIGABYTE(16) / 5));
+
+ /* We are running on a machine with 32 GB of RAM. */
+ total_system_memory_output = GIGABYTE(32);
+
+ /* We will at maximum get MAX_DEFAULT_MEMORY_QUEUE_SIZE here. */
+ tt_u64_op(compute_real_max_mem_in_queues(0, 0), OP_EQ,
+ MAX_DEFAULT_MEMORY_QUEUE_SIZE);
+#endif
+
+ done:
+ UNMOCK(get_total_system_memory);
+
+#undef GIGABYTE
+#undef MEGABYTE
+}
+
#define CONFIG_TEST(name, flags) \
{ #name, test_config_ ## name, flags, NULL, NULL }
@@ -5563,6 +5732,8 @@ struct testcase_t config_tests[] = {
CONFIG_TEST(check_bridge_distribution_setting_valid, 0),
CONFIG_TEST(check_bridge_distribution_setting_invalid, 0),
CONFIG_TEST(check_bridge_distribution_setting_unrecognised, 0),
+ CONFIG_TEST(include_opened_file_list, 0),
+ CONFIG_TEST(compute_max_mem_in_queues, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_connection.c b/src/test/test_connection.c
index 314b90cfda..dc0f6860d9 100644
--- a/src/test/test_connection.c
+++ b/src/test/test_connection.c
@@ -5,6 +5,7 @@
#define CONNECTION_PRIVATE
#define MAIN_PRIVATE
+#define CONNECTION_OR_PRIVATE
#include "or.h"
#include "test.h"
@@ -13,9 +14,11 @@
#include "hs_common.h"
#include "main.h"
#include "microdesc.h"
+#include "nodelist.h"
#include "networkstatus.h"
#include "rendcache.h"
#include "directory.h"
+#include "connection_or.h"
#include "test_connection.h"
#include "test_helpers.h"
@@ -776,10 +779,103 @@ test_conn_download_status(void *arg)
/* the teardown function removes all the connections in the global list*/;
}
+static node_t test_node;
+
+static node_t *
+mock_node_get_mutable_by_id(const char *digest)
+{
+ (void) digest;
+ static routerinfo_t node_ri;
+ memset(&node_ri, 0, sizeof(node_ri));
+
+ test_node.ri = &node_ri;
+ memset(test_node.identity, 'c', sizeof(test_node.identity));
+
+ tor_addr_t ipv4_addr;
+ tor_addr_parse(&ipv4_addr, "18.0.0.1");
+ node_ri.addr = tor_addr_to_ipv4h(&ipv4_addr);
+ node_ri.or_port = 1;
+
+ return &test_node;
+}
+
+static const node_t *
+mock_node_get_by_id(const char *digest)
+{
+ (void) digest;
+ memset(test_node.identity, 'c', sizeof(test_node.identity));
+ return &test_node;
+}
+
+/* Test whether we correctly track failed connections between relays. */
+static void
+test_failed_orconn_tracker(void *arg)
+{
+ (void) arg;
+
+ int can_connect;
+ time_t now = 1281533250; /* 2010-08-11 13:27:30 UTC */
+ (void) now;
+
+ update_approx_time(now);
+
+ /* Prepare the OR connection that will be used in this test */
+ or_connection_t or_conn;
+ tt_int_op(AF_INET,OP_EQ, tor_addr_parse(&or_conn.real_addr, "18.0.0.1"));
+ tt_int_op(AF_INET,OP_EQ, tor_addr_parse(&or_conn.base_.addr, "18.0.0.1"));
+ or_conn.base_.port = 1;
+ memset(or_conn.identity_digest, 'c', sizeof(or_conn.identity_digest));
+
+ /* Check whether we can connect with an empty failure cache:
+ * this should succeed */
+ can_connect = should_connect_to_relay(&or_conn);
+ tt_int_op(can_connect, OP_EQ, 1);
+
+ /* Now add the destination to the failure cache */
+ note_or_connect_failed(&or_conn);
+
+ /* Check again: now it shouldn't connect */
+ can_connect = should_connect_to_relay(&or_conn);
+ tt_int_op(can_connect, OP_EQ, 0);
+
+ /* Move time forward and check again: the cache should have been cleared and
+ * now it should connect */
+ now += 3600;
+ update_approx_time(now);
+ can_connect = should_connect_to_relay(&or_conn);
+ tt_int_op(can_connect, OP_EQ, 1);
+
+ /* Now mock the node_get_*by_id() functions to start using the node subsystem
+ * optimization. */
+ MOCK(node_get_by_id, mock_node_get_by_id);
+ MOCK(node_get_mutable_by_id, mock_node_get_mutable_by_id);
+
+ /* Since we just started using the node subsystem it will allow connections
+ * now */
+ can_connect = should_connect_to_relay(&or_conn);
+ tt_int_op(can_connect, OP_EQ, 1);
+
+ /* Mark it as failed */
+ note_or_connect_failed(&or_conn);
+
+ /* Check that it shouldn't connect now */
+ can_connect = should_connect_to_relay(&or_conn);
+ tt_int_op(can_connect, OP_EQ, 0);
+
+ /* Move time forward and check again: now it should connect */
+ now += 3600;
+ update_approx_time(now);
+ can_connect = should_connect_to_relay(&or_conn);
+ tt_int_op(can_connect, OP_EQ, 1);
+
+ done:
+ ;
+}
+
#define CONNECTION_TESTCASE(name, fork, setup) \
{ #name, test_conn_##name, fork, &setup, NULL }
-/* where arg is an expression (constant, varaible, compound expression) */
+/* where arg is an expression (constant, variable, compound expression) */
#define CONNECTION_TESTCASE_ARG(name, fork, setup, arg) \
{ #name "_" #arg, test_conn_##name, fork, &setup, (void *)arg }
@@ -792,6 +888,7 @@ struct testcase_t connection_tests[] = {
CONNECTION_TESTCASE_ARG(download_status, TT_FORK,
test_conn_download_status_st, FLAV_NS),
//CONNECTION_TESTCASE(func_suffix, TT_FORK, setup_func_pair),
+ { "failed_orconn_tracker", test_failed_orconn_tracker, TT_FORK, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_conscache.c b/src/test/test_conscache.c
index ddb1bc53c1..ffec3149b0 100644
--- a/src/test/test_conscache.c
+++ b/src/test/test_conscache.c
@@ -31,8 +31,8 @@ test_conscache_simple_usage(void *arg)
/* Make a temporary datadir for these tests */
char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache"));
- tor_free(get_options_mutable()->DataDirectory);
- get_options_mutable()->DataDirectory = tor_strdup(ddir_fname);
+ tor_free(get_options_mutable()->CacheDirectory);
+ get_options_mutable()->CacheDirectory = tor_strdup(ddir_fname);
check_private_dir(ddir_fname, CPD_CREATE, NULL);
consensus_cache_t *cache = consensus_cache_open("cons", 128);
@@ -124,8 +124,8 @@ test_conscache_cleanup(void *arg)
/* Make a temporary datadir for these tests */
char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache"));
- tor_free(get_options_mutable()->DataDirectory);
- get_options_mutable()->DataDirectory = tor_strdup(ddir_fname);
+ tor_free(get_options_mutable()->CacheDirectory);
+ get_options_mutable()->CacheDirectory = tor_strdup(ddir_fname);
check_private_dir(ddir_fname, CPD_CREATE, NULL);
consensus_cache_t *cache = consensus_cache_open("cons", 128);
@@ -267,8 +267,8 @@ test_conscache_filter(void *arg)
/* Make a temporary datadir for these tests */
char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache"));
- tor_free(get_options_mutable()->DataDirectory);
- get_options_mutable()->DataDirectory = tor_strdup(ddir_fname);
+ tor_free(get_options_mutable()->CacheDirectory);
+ get_options_mutable()->CacheDirectory = tor_strdup(ddir_fname);
check_private_dir(ddir_fname, CPD_CREATE, NULL);
consensus_cache_t *cache = consensus_cache_open("cons", 128);
diff --git a/src/test/test_consdiffmgr.c b/src/test/test_consdiffmgr.c
index 80d3f943ab..a9a4b6a98e 100644
--- a/src/test/test_consdiffmgr.c
+++ b/src/test/test_consdiffmgr.c
@@ -24,8 +24,8 @@ consdiffmgr_test_setup(const struct testcase_t *arg)
{
(void)arg;
char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cdm"));
- tor_free(get_options_mutable()->DataDirectory);
- get_options_mutable()->DataDirectory = ddir_fname; // now owns the pointer.
+ tor_free(get_options_mutable()->CacheDirectory);
+ get_options_mutable()->CacheDirectory = ddir_fname; // now owns the pointer.
check_private_dir(ddir_fname, CPD_CREATE, NULL);
consdiff_cfg_t consdiff_cfg = { 300 };
@@ -215,8 +215,8 @@ test_consdiffmgr_init_failure(void *arg)
/* As in ...test_setup, but do not create the datadir. The missing directory
* will cause a failure. */
char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cdm"));
- tor_free(get_options_mutable()->DataDirectory);
- get_options_mutable()->DataDirectory = ddir_fname; // now owns the pointer.
+ tor_free(get_options_mutable()->CacheDirectory);
+ get_options_mutable()->CacheDirectory = ddir_fname; // now owns the pointer.
consdiff_cfg_t consdiff_cfg = { 7200, 300 };
diff --git a/src/test/test_controller.c b/src/test/test_controller.c
index 472fcb8c53..1c285bb3a2 100644
--- a/src/test/test_controller.c
+++ b/src/test/test_controller.c
@@ -6,6 +6,7 @@
#include "bridges.h"
#include "control.h"
#include "entrynodes.h"
+#include "hs_common.h"
#include "networkstatus.h"
#include "rendservice.h"
#include "routerlist.h"
@@ -13,10 +14,87 @@
#include "test_helpers.h"
static void
-test_add_onion_helper_keyarg(void *arg)
+test_add_onion_helper_keyarg_v3(void *arg)
{
- crypto_pk_t *pk = NULL;
- crypto_pk_t *pk2 = NULL;
+ int ret, hs_version;
+ add_onion_secret_key_t pk;
+ char *key_new_blob = NULL;
+ char *err_msg = NULL;
+ const char *key_new_alg = NULL;
+
+ (void) arg;
+
+ memset(&pk, 0, sizeof(pk));
+
+ /* Test explicit ED25519-V3 key generation. */
+ ret = add_onion_helper_keyarg("NEW:ED25519-V3", 0, &key_new_alg,
+ &key_new_blob, &pk, &hs_version,
+ &err_msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE);
+ tt_assert(pk.v3);
+ tt_str_op(key_new_alg, OP_EQ, "ED25519-V3");
+ tt_assert(key_new_blob);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
+ tor_free(pk.v3); pk.v3 = NULL;
+ tor_free(key_new_blob);
+
+ /* Test discarding the private key. */
+ ret = add_onion_helper_keyarg("NEW:ED25519-V3", 1, &key_new_alg,
+ &key_new_blob, &pk, &hs_version,
+ &err_msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE);
+ tt_assert(pk.v3);
+ tt_ptr_op(key_new_alg, OP_EQ, NULL);
+ tt_ptr_op(key_new_blob, OP_EQ, NULL);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
+ tor_free(pk.v3); pk.v3 = NULL;
+ tor_free(key_new_blob);
+
+ /* Test passing a key blob. */
+ {
+ /* The base64 key and hex key are the same. Hex key is 64 bytes long. The
+ * sk has been generated randomly using python3. */
+ const char *base64_sk =
+ "a9bT19PqGC9Y+BmOo1IQvCGjjwxMiaaxEXZ+FKMxpEQW"
+ "6AmSV5roThUGMRCaqQSCnR2jI1vL2QxHORzI4RxMmw==";
+ const char *hex_sk =
+ "\x6b\xd6\xd3\xd7\xd3\xea\x18\x2f\x58\xf8\x19\x8e\xa3\x52\x10\xbc"
+ "\x21\xa3\x8f\x0c\x4c\x89\xa6\xb1\x11\x76\x7e\x14\xa3\x31\xa4\x44"
+ "\x16\xe8\x09\x92\x57\x9a\xe8\x4e\x15\x06\x31\x10\x9a\xa9\x04\x82"
+ "\x9d\x1d\xa3\x23\x5b\xcb\xd9\x0c\x47\x39\x1c\xc8\xe1\x1c\x4c\x9b";
+ char *key_blob = NULL;
+
+ tor_asprintf(&key_blob, "ED25519-V3:%s", base64_sk);
+ tt_assert(key_blob);
+ ret = add_onion_helper_keyarg(key_blob, 1, &key_new_alg,
+ &key_new_blob, &pk, &hs_version,
+ &err_msg);
+ tor_free(key_blob);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE);
+ tt_assert(pk.v3);
+ tt_mem_op(pk.v3, OP_EQ, hex_sk, 64);
+ tt_ptr_op(key_new_alg, OP_EQ, NULL);
+ tt_ptr_op(key_new_blob, OP_EQ, NULL);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
+ tor_free(pk.v3); pk.v3 = NULL;
+ tor_free(key_new_blob);
+ }
+
+ done:
+ tor_free(pk.v3);
+ tor_free(key_new_blob);
+ tor_free(err_msg);
+}
+
+static void
+test_add_onion_helper_keyarg_v2(void *arg)
+{
+ int ret, hs_version;
+ add_onion_secret_key_t pk;
+ crypto_pk_t *pk1 = NULL;
const char *key_new_alg = NULL;
char *key_new_blob = NULL;
char *err_msg = NULL;
@@ -25,83 +103,100 @@ test_add_onion_helper_keyarg(void *arg)
(void) arg;
+ memset(&pk, 0, sizeof(pk));
+
/* Test explicit RSA1024 key generation. */
- pk = add_onion_helper_keyarg("NEW:RSA1024", 0, &key_new_alg, &key_new_blob,
- &err_msg);
- tt_assert(pk);
+ ret = add_onion_helper_keyarg("NEW:RSA1024", 0, &key_new_alg, &key_new_blob,
+ &pk, &hs_version, &err_msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
+ tt_assert(pk.v2);
tt_str_op(key_new_alg, OP_EQ, "RSA1024");
tt_assert(key_new_blob);
tt_ptr_op(err_msg, OP_EQ, NULL);
/* Test "BEST" key generation (Assumes BEST = RSA1024). */
- crypto_pk_free(pk);
+ crypto_pk_free(pk.v2); pk.v2 = NULL;
tor_free(key_new_blob);
- pk = add_onion_helper_keyarg("NEW:BEST", 0, &key_new_alg, &key_new_blob,
- &err_msg);
- tt_assert(pk);
+ ret = add_onion_helper_keyarg("NEW:BEST", 0, &key_new_alg, &key_new_blob,
+ &pk, &hs_version, &err_msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
+ tt_assert(pk.v2);
tt_str_op(key_new_alg, OP_EQ, "RSA1024");
tt_assert(key_new_blob);
tt_ptr_op(err_msg, OP_EQ, NULL);
/* Test discarding the private key. */
- crypto_pk_free(pk);
+ crypto_pk_free(pk.v2); pk.v2 = NULL;
tor_free(key_new_blob);
- pk = add_onion_helper_keyarg("NEW:BEST", 1, &key_new_alg, &key_new_blob,
- &err_msg);
- tt_assert(pk);
+ ret = add_onion_helper_keyarg("NEW:BEST", 1, &key_new_alg, &key_new_blob,
+ &pk, &hs_version, &err_msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
+ tt_assert(pk.v2);
tt_ptr_op(key_new_alg, OP_EQ, NULL);
tt_ptr_op(key_new_blob, OP_EQ, NULL);
tt_ptr_op(err_msg, OP_EQ, NULL);
/* Test generating a invalid key type. */
- crypto_pk_free(pk);
- pk = add_onion_helper_keyarg("NEW:RSA512", 0, &key_new_alg, &key_new_blob,
- &err_msg);
- tt_ptr_op(pk, OP_EQ, NULL);
+ crypto_pk_free(pk.v2); pk.v2 = NULL;
+ ret = add_onion_helper_keyarg("NEW:RSA512", 0, &key_new_alg, &key_new_blob,
+ &pk, &hs_version, &err_msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
+ tt_assert(!pk.v2);
tt_ptr_op(key_new_alg, OP_EQ, NULL);
tt_ptr_op(key_new_blob, OP_EQ, NULL);
tt_assert(err_msg);
/* Test loading a RSA1024 key. */
tor_free(err_msg);
- pk = pk_generate(0);
- tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk, &encoded));
+ pk1 = pk_generate(0);
+ tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk1, &encoded));
tor_asprintf(&arg_str, "RSA1024:%s", encoded);
- pk2 = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
- &err_msg);
- tt_assert(pk2);
+ ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
+ &pk, &hs_version, &err_msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
+ tt_assert(pk.v2);
tt_ptr_op(key_new_alg, OP_EQ, NULL);
tt_ptr_op(key_new_blob, OP_EQ, NULL);
tt_ptr_op(err_msg, OP_EQ, NULL);
- tt_int_op(crypto_pk_cmp_keys(pk, pk2), OP_EQ, 0);
+ tt_int_op(crypto_pk_cmp_keys(pk1, pk.v2), OP_EQ, 0);
/* Test loading a invalid key type. */
tor_free(arg_str);
- crypto_pk_free(pk); pk = NULL;
+ crypto_pk_free(pk1); pk1 = NULL;
+ crypto_pk_free(pk.v2); pk.v2 = NULL;
tor_asprintf(&arg_str, "RSA512:%s", encoded);
- pk = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
- &err_msg);
- tt_ptr_op(pk, OP_EQ, NULL);
+ ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
+ &pk, &hs_version, &err_msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
+ tt_assert(!pk.v2);
tt_ptr_op(key_new_alg, OP_EQ, NULL);
tt_ptr_op(key_new_blob, OP_EQ, NULL);
tt_assert(err_msg);
/* Test loading a invalid key. */
tor_free(arg_str);
- crypto_pk_free(pk); pk = NULL;
+ crypto_pk_free(pk.v2); pk.v2 = NULL;
tor_free(err_msg);
encoded[strlen(encoded)/2] = '\0';
tor_asprintf(&arg_str, "RSA1024:%s", encoded);
- pk = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
- &err_msg);
- tt_ptr_op(pk, OP_EQ, NULL);
+ ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
+ &pk, &hs_version, &err_msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
+ tt_assert(!pk.v2);
tt_ptr_op(key_new_alg, OP_EQ, NULL);
tt_ptr_op(key_new_blob, OP_EQ, NULL);
tt_assert(err_msg);
done:
- crypto_pk_free(pk);
- crypto_pk_free(pk2);
+ crypto_pk_free(pk1);
+ crypto_pk_free(pk.v2);
tor_free(key_new_blob);
tor_free(err_msg);
tor_free(encoded);
@@ -301,7 +396,7 @@ test_add_onion_helper_clientauth(void *arg)
static const download_status_t dl_status_default =
{ 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER,
- DL_SCHED_INCREMENT_FAILURE, DL_SCHED_RANDOM_EXPONENTIAL, 0, 0 };
+ DL_SCHED_INCREMENT_FAILURE, 0, 0 };
static download_status_t ns_dl_status[N_CONSENSUS_FLAVORS];
static download_status_t ns_dl_status_bootstrap[N_CONSENSUS_FLAVORS];
static download_status_t ns_dl_status_running[N_CONSENSUS_FLAVORS];
@@ -312,7 +407,7 @@ static download_status_t ns_dl_status_running[N_CONSENSUS_FLAVORS];
*/
static const download_status_t dls_sample_1 =
{ 1467163900, 0, 0, DL_SCHED_GENERIC, DL_WANT_ANY_DIRSERVER,
- DL_SCHED_INCREMENT_FAILURE, DL_SCHED_DETERMINISTIC, 0, 0 };
+ DL_SCHED_INCREMENT_FAILURE, 0, 0 };
static const char * dls_sample_1_str =
"next-attempt-at 2016-06-29 01:31:40\n"
"n-download-failures 0\n"
@@ -320,10 +415,12 @@ static const char * dls_sample_1_str =
"schedule DL_SCHED_GENERIC\n"
"want-authority DL_WANT_ANY_DIRSERVER\n"
"increment-on DL_SCHED_INCREMENT_FAILURE\n"
- "backoff DL_SCHED_DETERMINISTIC\n";
+ "backoff DL_SCHED_RANDOM_EXPONENTIAL\n"
+ "last-backoff-position 0\n"
+ "last-delay-used 0\n";
static const download_status_t dls_sample_2 =
{ 1467164400, 1, 2, DL_SCHED_CONSENSUS, DL_WANT_AUTHORITY,
- DL_SCHED_INCREMENT_FAILURE, DL_SCHED_DETERMINISTIC, 0, 0 };
+ DL_SCHED_INCREMENT_FAILURE, 0, 0 };
static const char * dls_sample_2_str =
"next-attempt-at 2016-06-29 01:40:00\n"
"n-download-failures 1\n"
@@ -331,10 +428,12 @@ static const char * dls_sample_2_str =
"schedule DL_SCHED_CONSENSUS\n"
"want-authority DL_WANT_AUTHORITY\n"
"increment-on DL_SCHED_INCREMENT_FAILURE\n"
- "backoff DL_SCHED_DETERMINISTIC\n";
+ "backoff DL_SCHED_RANDOM_EXPONENTIAL\n"
+ "last-backoff-position 0\n"
+ "last-delay-used 0\n";
static const download_status_t dls_sample_3 =
{ 1467154400, 12, 25, DL_SCHED_BRIDGE, DL_WANT_ANY_DIRSERVER,
- DL_SCHED_INCREMENT_ATTEMPT, DL_SCHED_DETERMINISTIC, 0, 0 };
+ DL_SCHED_INCREMENT_ATTEMPT, 0, 0 };
static const char * dls_sample_3_str =
"next-attempt-at 2016-06-28 22:53:20\n"
"n-download-failures 12\n"
@@ -342,10 +441,12 @@ static const char * dls_sample_3_str =
"schedule DL_SCHED_BRIDGE\n"
"want-authority DL_WANT_ANY_DIRSERVER\n"
"increment-on DL_SCHED_INCREMENT_ATTEMPT\n"
- "backoff DL_SCHED_DETERMINISTIC\n";
+ "backoff DL_SCHED_RANDOM_EXPONENTIAL\n"
+ "last-backoff-position 0\n"
+ "last-delay-used 0\n";
static const download_status_t dls_sample_4 =
{ 1467166600, 3, 0, DL_SCHED_GENERIC, DL_WANT_ANY_DIRSERVER,
- DL_SCHED_INCREMENT_FAILURE, DL_SCHED_RANDOM_EXPONENTIAL, 0, 0 };
+ DL_SCHED_INCREMENT_FAILURE, 0, 0 };
static const char * dls_sample_4_str =
"next-attempt-at 2016-06-29 02:16:40\n"
"n-download-failures 3\n"
@@ -358,7 +459,7 @@ static const char * dls_sample_4_str =
"last-delay-used 0\n";
static const download_status_t dls_sample_5 =
{ 1467164600, 3, 7, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER,
- DL_SCHED_INCREMENT_FAILURE, DL_SCHED_RANDOM_EXPONENTIAL, 1, 2112, };
+ DL_SCHED_INCREMENT_FAILURE, 1, 2112, };
static const char * dls_sample_5_str =
"next-attempt-at 2016-06-29 01:43:20\n"
"n-download-failures 3\n"
@@ -371,7 +472,7 @@ static const char * dls_sample_5_str =
"last-delay-used 2112\n";
static const download_status_t dls_sample_6 =
{ 1467164200, 4, 9, DL_SCHED_CONSENSUS, DL_WANT_AUTHORITY,
- DL_SCHED_INCREMENT_ATTEMPT, DL_SCHED_RANDOM_EXPONENTIAL, 3, 432 };
+ DL_SCHED_INCREMENT_ATTEMPT, 3, 432 };
static const char * dls_sample_6_str =
"next-attempt-at 2016-06-29 01:36:40\n"
"n-download-failures 4\n"
@@ -1370,7 +1471,10 @@ test_download_status_bridge(void *arg)
}
struct testcase_t controller_tests[] = {
- { "add_onion_helper_keyarg", test_add_onion_helper_keyarg, 0, NULL, NULL },
+ { "add_onion_helper_keyarg_v2", test_add_onion_helper_keyarg_v2, 0,
+ NULL, NULL },
+ { "add_onion_helper_keyarg_v3", test_add_onion_helper_keyarg_v3, 0,
+ NULL, NULL },
{ "getinfo_helper_onion", test_getinfo_helper_onion, 0, NULL, NULL },
{ "rend_service_parse_port_config", test_rend_service_parse_port_config, 0,
NULL, NULL },
diff --git a/src/test/test_dir.c b/src/test/test_dir.c
index 28489d9392..a33b23bcc9 100644
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@ -567,7 +567,7 @@ test_dir_routerinfo_parsing(void *arg)
static void
routerinfo_free_wrapper_(void *arg)
{
- routerinfo_free(arg);
+ routerinfo_free_(arg);
}
static void
@@ -664,7 +664,7 @@ test_dir_extrainfo_parsing(void *arg)
escaped(NULL);
extrainfo_free(ei);
routerinfo_free(ri);
- digestmap_free((digestmap_t*)map, routerinfo_free_wrapper_);
+ digestmap_free_((digestmap_t*)map, routerinfo_free_wrapper_);
}
static void
@@ -760,7 +760,7 @@ test_dir_parse_router_list(void *arg)
smartlist_free(chunks);
routerinfo_free(ri);
if (map) {
- digestmap_free((digestmap_t*)map, routerinfo_free_wrapper_);
+ digestmap_free_((digestmap_t*)map, routerinfo_free_wrapper_);
router_get_routerlist()->identity_map =
(struct digest_ri_map_t*)digestmap_new();
}
@@ -1270,7 +1270,7 @@ test_dir_versions(void *arg)
tt_int_op(0,OP_EQ, tor_version_as_new_as(
"Tor 0.2.9.9 (git-00)",
"Tor 0.2.9.9 (git-01)"));
- /* In #21278, we comapre without integer overflows.
+ /* In #21278, we compare without integer overflows.
* But since #21450 limits version components to [0, INT32_MAX], it is no
* longer possible to cause an integer overflow in tor_version_compare() */
tt_int_op(0,OP_EQ, tor_version_as_new_as(
@@ -3984,164 +3984,11 @@ test_dir_packages(void *arg)
}
static void
-test_dir_download_status_schedule(void *arg)
-{
- (void)arg;
- download_status_t dls_failure = { 0, 0, 0, DL_SCHED_GENERIC,
- DL_WANT_AUTHORITY,
- DL_SCHED_INCREMENT_FAILURE,
- DL_SCHED_DETERMINISTIC, 0, 0 };
- download_status_t dls_attempt = { 0, 0, 0, DL_SCHED_CONSENSUS,
- DL_WANT_ANY_DIRSERVER,
- DL_SCHED_INCREMENT_ATTEMPT,
- DL_SCHED_DETERMINISTIC, 0, 0 };
- download_status_t dls_bridge = { 0, 0, 0, DL_SCHED_BRIDGE,
- DL_WANT_AUTHORITY,
- DL_SCHED_INCREMENT_FAILURE,
- DL_SCHED_DETERMINISTIC, 0, 0 };
- int increment = -1;
- int expected_increment = -1;
- time_t current_time = time(NULL);
- int delay1 = -1;
- int delay2 = -1;
- smartlist_t *schedule = smartlist_new();
-
- /* Make a dummy schedule */
- smartlist_add(schedule, (void *)&delay1);
- smartlist_add(schedule, (void *)&delay2);
-
- /* check a range of values */
- delay1 = 1000;
- increment = download_status_schedule_get_delay(&dls_failure,
- schedule,
- 0, INT_MAX,
- TIME_MIN);
- expected_increment = delay1;
- tt_assert(increment == expected_increment);
- tt_assert(dls_failure.next_attempt_at == TIME_MIN + expected_increment);
-
- delay1 = INT_MAX;
- increment = download_status_schedule_get_delay(&dls_failure,
- schedule,
- 0, INT_MAX,
- -1);
- expected_increment = delay1;
- tt_assert(increment == expected_increment);
- tt_assert(dls_failure.next_attempt_at == TIME_MAX);
-
- delay1 = 0;
- increment = download_status_schedule_get_delay(&dls_attempt,
- schedule,
- 0, INT_MAX,
- 0);
- expected_increment = delay1;
- tt_assert(increment == expected_increment);
- tt_assert(dls_attempt.next_attempt_at == 0 + expected_increment);
-
- delay1 = 1000;
- increment = download_status_schedule_get_delay(&dls_attempt,
- schedule,
- 0, INT_MAX,
- 1);
- expected_increment = delay1;
- tt_assert(increment == expected_increment);
- tt_assert(dls_attempt.next_attempt_at == 1 + expected_increment);
-
- delay1 = INT_MAX;
- increment = download_status_schedule_get_delay(&dls_bridge,
- schedule,
- 0, INT_MAX,
- current_time);
- expected_increment = delay1;
- tt_assert(increment == expected_increment);
- tt_assert(dls_bridge.next_attempt_at == TIME_MAX);
-
- delay1 = 1;
- increment = download_status_schedule_get_delay(&dls_bridge,
- schedule,
- 0, INT_MAX,
- TIME_MAX);
- expected_increment = delay1;
- tt_assert(increment == expected_increment);
- tt_assert(dls_bridge.next_attempt_at == TIME_MAX);
-
- /* see what happens when we reach the end */
- dls_attempt.n_download_attempts++;
- dls_bridge.n_download_failures++;
-
- delay2 = 100;
- increment = download_status_schedule_get_delay(&dls_attempt,
- schedule,
- 0, INT_MAX,
- current_time);
- expected_increment = delay2;
- tt_assert(increment == expected_increment);
- tt_assert(dls_attempt.next_attempt_at == current_time + delay2);
-
- delay2 = 1;
- increment = download_status_schedule_get_delay(&dls_bridge,
- schedule,
- 0, INT_MAX,
- current_time);
- expected_increment = delay2;
- tt_assert(increment == expected_increment);
- tt_assert(dls_bridge.next_attempt_at == current_time + delay2);
-
- /* see what happens when we try to go off the end */
- dls_attempt.n_download_attempts++;
- dls_bridge.n_download_failures++;
-
- delay2 = 5;
- increment = download_status_schedule_get_delay(&dls_attempt,
- schedule,
- 0, INT_MAX,
- current_time);
- expected_increment = delay2;
- tt_assert(increment == expected_increment);
- tt_assert(dls_attempt.next_attempt_at == current_time + delay2);
-
- delay2 = 17;
- increment = download_status_schedule_get_delay(&dls_bridge,
- schedule,
- 0, INT_MAX,
- current_time);
- expected_increment = delay2;
- tt_assert(increment == expected_increment);
- tt_assert(dls_bridge.next_attempt_at == current_time + delay2);
-
- /* see what happens when we reach IMPOSSIBLE_TO_DOWNLOAD */
- dls_attempt.n_download_attempts = IMPOSSIBLE_TO_DOWNLOAD;
- dls_bridge.n_download_failures = IMPOSSIBLE_TO_DOWNLOAD;
-
- delay2 = 35;
- increment = download_status_schedule_get_delay(&dls_attempt,
- schedule,
- 0, INT_MAX,
- current_time);
- expected_increment = INT_MAX;
- tt_assert(increment == expected_increment);
- tt_assert(dls_attempt.next_attempt_at == TIME_MAX);
-
- delay2 = 99;
- increment = download_status_schedule_get_delay(&dls_bridge,
- schedule,
- 0, INT_MAX,
- current_time);
- expected_increment = INT_MAX;
- tt_assert(increment == expected_increment);
- tt_assert(dls_bridge.next_attempt_at == TIME_MAX);
-
- done:
- /* the pointers in schedule are allocated on the stack */
- smartlist_free(schedule);
-}
-
-static void
-download_status_random_backoff_helper(int min_delay, int max_delay)
+download_status_random_backoff_helper(int min_delay)
{
download_status_t dls_random =
{ 0, 0, 0, DL_SCHED_GENERIC, DL_WANT_AUTHORITY,
- DL_SCHED_INCREMENT_FAILURE, DL_SCHED_RANDOM_EXPONENTIAL, 0, 0 };
+ DL_SCHED_INCREMENT_FAILURE, 0, 0 };
int increment = -1;
int old_increment = -1;
time_t current_time = time(NULL);
@@ -4150,23 +3997,21 @@ download_status_random_backoff_helper(int min_delay, int max_delay)
int n_attempts = 0;
do {
increment = download_status_schedule_get_delay(&dls_random,
- NULL,
- min_delay, max_delay,
+ min_delay,
current_time);
- log_debug(LD_DIR, "Min: %d, Max: %d, Inc: %d, Old Inc: %d",
- min_delay, max_delay, increment, old_increment);
+ log_debug(LD_DIR, "Min: %d, Inc: %d, Old Inc: %d",
+ min_delay, increment, old_increment);
/* Regression test for 20534 and friends
* increment must always increase after the first */
- if (dls_random.last_backoff_position > 0 && max_delay > 0) {
+ if (dls_random.last_backoff_position > 0) {
/* Always increment the exponential backoff */
tt_int_op(increment, OP_GE, 1);
}
/* Test */
tt_int_op(increment, OP_GE, min_delay);
- tt_int_op(increment, OP_LE, max_delay);
/* Advance */
if (dls_random.n_download_attempts < IMPOSSIBLE_TO_DOWNLOAD - 1) {
@@ -4176,7 +4021,7 @@ download_status_random_backoff_helper(int min_delay, int max_delay)
/* Try another maybe */
old_increment = increment;
- } while (increment < max_delay && ++n_attempts < 1000);
+ } while (++n_attempts < 1000);
done:
return;
@@ -4188,19 +4033,13 @@ test_dir_download_status_random_backoff(void *arg)
(void)arg;
/* Do a standard test */
- download_status_random_backoff_helper(0, 1000000);
- /* Regression test for 20534 and friends:
- * try tighter bounds */
- download_status_random_backoff_helper(0, 100);
+ download_status_random_backoff_helper(0);
/* regression tests for 17750: initial delay */
- download_status_random_backoff_helper(10, 1000);
- download_status_random_backoff_helper(20, 30);
+ download_status_random_backoff_helper(10);
+ download_status_random_backoff_helper(20);
/* Pathological cases */
- download_status_random_backoff_helper(0, 0);
- download_status_random_backoff_helper(1, 1);
- download_status_random_backoff_helper(0, INT_MAX);
- download_status_random_backoff_helper(INT_MAX/2, INT_MAX);
+ download_status_random_backoff_helper(INT_MAX/2);
}
static void
@@ -4239,18 +4078,10 @@ static void
test_dir_download_status_increment(void *arg)
{
(void)arg;
- download_status_t dls_failure = { 0, 0, 0, DL_SCHED_GENERIC,
- DL_WANT_AUTHORITY,
- DL_SCHED_INCREMENT_FAILURE,
- DL_SCHED_DETERMINISTIC, 0, 0 };
- download_status_t dls_attempt = { 0, 0, 0, DL_SCHED_BRIDGE,
- DL_WANT_ANY_DIRSERVER,
- DL_SCHED_INCREMENT_ATTEMPT,
- DL_SCHED_DETERMINISTIC, 0, 0 };
download_status_t dls_exp = { 0, 0, 0, DL_SCHED_GENERIC,
DL_WANT_ANY_DIRSERVER,
DL_SCHED_INCREMENT_ATTEMPT,
- DL_SCHED_RANDOM_EXPONENTIAL, 0, 0 };
+ 0, 0 };
int no_delay = 0;
int delay0 = -1;
int delay1 = -1;
@@ -4258,7 +4089,6 @@ test_dir_download_status_increment(void *arg)
smartlist_t *schedule = smartlist_new();
smartlist_t *schedule_no_initial_delay = smartlist_new();
or_options_t test_options;
- time_t next_at = TIME_MAX;
time_t current_time = time(NULL);
/* Provide some values for the schedules */
@@ -4291,26 +4121,6 @@ test_dir_download_status_increment(void *arg)
mock_get_options_calls = 0;
/* we really want to test that it's equal to time(NULL) + delay0, but that's
* an unrealiable test, because time(NULL) might change. */
- tt_assert(download_status_get_next_attempt_at(&dls_failure)
- >= current_time + no_delay);
- tt_assert(download_status_get_next_attempt_at(&dls_failure)
- != TIME_MAX);
- tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ, 0);
- tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ, 0);
- tt_int_op(mock_get_options_calls, OP_GE, 1);
-
- /* regression test for 17750: initial delay */
- mock_options->TestingClientDownloadSchedule = schedule;
- mock_get_options_calls = 0;
- /* we really want to test that it's equal to time(NULL) + delay0, but that's
- * an unrealiable test, because time(NULL) might change. */
- tt_assert(download_status_get_next_attempt_at(&dls_failure)
- >= current_time + delay0);
- tt_assert(download_status_get_next_attempt_at(&dls_failure)
- != TIME_MAX);
- tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ, 0);
- tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ, 0);
- tt_int_op(mock_get_options_calls, OP_GE, 1);
/* regression test for 17750: exponential, no initial delay */
mock_options->TestingClientDownloadSchedule = schedule_no_initial_delay;
@@ -4338,258 +4148,6 @@ test_dir_download_status_increment(void *arg)
tt_int_op(download_status_get_n_attempts(&dls_exp), OP_EQ, 0);
tt_int_op(mock_get_options_calls, OP_GE, 1);
- /* Check that a failure reset works */
- mock_get_options_calls = 0;
- download_status_reset(&dls_failure);
- /* we really want to test that it's equal to time(NULL) + delay0, but that's
- * an unrealiable test, because time(NULL) might change. */
- tt_assert(download_status_get_next_attempt_at(&dls_failure)
- >= current_time + delay0);
- tt_assert(download_status_get_next_attempt_at(&dls_failure)
- != TIME_MAX);
- tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ, 0);
- tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ, 0);
- tt_int_op(mock_get_options_calls, OP_GE, 1);
-
- /* avoid timing inconsistencies */
- dls_failure.next_attempt_at = current_time + delay0;
-
- /* check that a reset schedule becomes ready at the right time */
- tt_int_op(download_status_is_ready(&dls_failure,
- current_time + delay0 - 1, 1),
- OP_EQ, 0);
- tt_int_op(download_status_is_ready(&dls_failure,
- current_time + delay0, 1),
- OP_EQ, 1);
- tt_int_op(download_status_is_ready(&dls_failure,
- current_time + delay0 + 1, 1),
- OP_EQ, 1);
-
- /* Check that a failure increment works */
- mock_get_options_calls = 0;
- next_at = download_status_increment_failure(&dls_failure, 404, "test", 0,
- current_time);
- tt_assert(next_at == current_time + delay1);
- tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ, 1);
- tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ, 1);
- tt_int_op(mock_get_options_calls, OP_GE, 1);
-
- /* check that an incremented schedule becomes ready at the right time */
- tt_int_op(download_status_is_ready(&dls_failure,
- current_time + delay1 - 1, 1),
- OP_EQ, 0);
- tt_int_op(download_status_is_ready(&dls_failure,
- current_time + delay1, 1),
- OP_EQ, 1);
- tt_int_op(download_status_is_ready(&dls_failure,
- current_time + delay1 + 1, 1),
- OP_EQ, 1);
-
- /* check that a schedule isn't ready if it's had too many failures */
- tt_int_op(download_status_is_ready(&dls_failure,
- current_time + delay1 + 10, 0),
- OP_EQ, 0);
-
- /* Check that failure increments do happen on 503 for clients, and
- * attempt increments do too. */
- mock_get_options_calls = 0;
- next_at = download_status_increment_failure(&dls_failure, 503, "test", 0,
- current_time);
- tt_i64_op(next_at, OP_EQ, current_time + delay2);
- tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ, 2);
- tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ, 2);
- tt_int_op(mock_get_options_calls, OP_GE, 1);
-
- /* Check that failure increments do happen on 503 for servers */
- mock_get_options_calls = 0;
- next_at = download_status_increment_failure(&dls_failure, 503, "test", 1,
- current_time);
- tt_assert(next_at == current_time + delay2);
- tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ, 3);
- tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ, 3);
- tt_int_op(mock_get_options_calls, OP_GE, 1);
-
- /* Check what happens when we run off the end of the schedule */
- mock_get_options_calls = 0;
- next_at = download_status_increment_failure(&dls_failure, 404, "test", 0,
- current_time);
- tt_assert(next_at == current_time + delay2);
- tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ, 4);
- tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ, 4);
- tt_int_op(mock_get_options_calls, OP_GE, 1);
-
- /* Check what happens when we hit the failure limit */
- mock_get_options_calls = 0;
- download_status_mark_impossible(&dls_failure);
- next_at = download_status_increment_failure(&dls_failure, 404, "test", 0,
- current_time);
- tt_assert(next_at == TIME_MAX);
- tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ,
- IMPOSSIBLE_TO_DOWNLOAD);
- tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ,
- IMPOSSIBLE_TO_DOWNLOAD);
- tt_int_op(mock_get_options_calls, OP_GE, 1);
-
- /* Check that a failure reset doesn't reset at the limit */
- mock_get_options_calls = 0;
- download_status_reset(&dls_failure);
- tt_assert(download_status_get_next_attempt_at(&dls_failure)
- == TIME_MAX);
- tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ,
- IMPOSSIBLE_TO_DOWNLOAD);
- tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ,
- IMPOSSIBLE_TO_DOWNLOAD);
- tt_int_op(mock_get_options_calls, OP_EQ, 0);
-
- /* Check that a failure reset resets just before the limit */
- mock_get_options_calls = 0;
- dls_failure.n_download_failures = IMPOSSIBLE_TO_DOWNLOAD - 1;
- dls_failure.n_download_attempts = IMPOSSIBLE_TO_DOWNLOAD - 1;
- download_status_reset(&dls_failure);
- /* we really want to test that it's equal to time(NULL) + delay0, but that's
- * an unrealiable test, because time(NULL) might change. */
- tt_assert(download_status_get_next_attempt_at(&dls_failure)
- >= current_time + delay0);
- tt_assert(download_status_get_next_attempt_at(&dls_failure)
- != TIME_MAX);
- tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ, 0);
- tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ, 0);
- tt_int_op(mock_get_options_calls, OP_GE, 1);
-
- /* Check that failure increments do happen on attempt-based schedules,
- * but that the retry is set at the end of time */
- mock_options->UseBridges = 1;
- mock_get_options_calls = 0;
- next_at = download_status_increment_failure(&dls_attempt, 404, "test", 0,
- current_time);
- tt_assert(next_at == TIME_MAX);
- tt_int_op(download_status_get_n_failures(&dls_attempt), OP_EQ, 1);
- tt_int_op(download_status_get_n_attempts(&dls_attempt), OP_EQ, 0);
- tt_int_op(mock_get_options_calls, OP_GE, 1);
-
- /* Check that an attempt reset works */
- mock_get_options_calls = 0;
- download_status_reset(&dls_attempt);
- /* we really want to test that it's equal to time(NULL) + delay0, but that's
- * an unrealiable test, because time(NULL) might change. */
- tt_assert(download_status_get_next_attempt_at(&dls_attempt)
- >= current_time + delay0);
- tt_assert(download_status_get_next_attempt_at(&dls_attempt)
- != TIME_MAX);
- tt_int_op(download_status_get_n_failures(&dls_attempt), OP_EQ, 0);
- tt_int_op(download_status_get_n_attempts(&dls_attempt), OP_EQ, 0);
- tt_int_op(mock_get_options_calls, OP_GE, 1);
-
- /* avoid timing inconsistencies */
- dls_attempt.next_attempt_at = current_time + delay0;
-
- /* check that a reset schedule becomes ready at the right time */
- tt_int_op(download_status_is_ready(&dls_attempt,
- current_time + delay0 - 1, 1),
- OP_EQ, 0);
- tt_int_op(download_status_is_ready(&dls_attempt,
- current_time + delay0, 1),
- OP_EQ, 1);
- tt_int_op(download_status_is_ready(&dls_attempt,
- current_time + delay0 + 1, 1),
- OP_EQ, 1);
-
- /* Check that an attempt increment works */
- mock_get_options_calls = 0;
- next_at = download_status_increment_attempt(&dls_attempt, "test",
- current_time);
- tt_assert(next_at == current_time + delay1);
- tt_int_op(download_status_get_n_failures(&dls_attempt), OP_EQ, 0);
- tt_int_op(download_status_get_n_attempts(&dls_attempt), OP_EQ, 1);
- tt_int_op(mock_get_options_calls, OP_GE, 1);
-
- /* check that an incremented schedule becomes ready at the right time */
- tt_int_op(download_status_is_ready(&dls_attempt,
- current_time + delay1 - 1, 1),
- OP_EQ, 0);
- tt_int_op(download_status_is_ready(&dls_attempt,
- current_time + delay1, 1),
- OP_EQ, 1);
- tt_int_op(download_status_is_ready(&dls_attempt,
- current_time + delay1 + 1, 1),
- OP_EQ, 1);
-
- /* check that a schedule isn't ready if it's had too many attempts */
- tt_int_op(download_status_is_ready(&dls_attempt,
- current_time + delay1 + 10, 0),
- OP_EQ, 0);
-
- /* Check what happens when we reach then run off the end of the schedule */
- mock_get_options_calls = 0;
- next_at = download_status_increment_attempt(&dls_attempt, "test",
- current_time);
- tt_assert(next_at == current_time + delay2);
- tt_int_op(download_status_get_n_failures(&dls_attempt), OP_EQ, 0);
- tt_int_op(download_status_get_n_attempts(&dls_attempt), OP_EQ, 2);
- tt_int_op(mock_get_options_calls, OP_GE, 1);
-
- mock_get_options_calls = 0;
- next_at = download_status_increment_attempt(&dls_attempt, "test",
- current_time);
- tt_assert(next_at == current_time + delay2);
- tt_int_op(download_status_get_n_failures(&dls_attempt), OP_EQ, 0);
- tt_int_op(download_status_get_n_attempts(&dls_attempt), OP_EQ, 3);
- tt_int_op(mock_get_options_calls, OP_GE, 1);
-
- /* Check what happens when we hit the attempt limit */
- mock_get_options_calls = 0;
- download_status_mark_impossible(&dls_attempt);
- next_at = download_status_increment_attempt(&dls_attempt, "test",
- current_time);
- tt_assert(next_at == TIME_MAX);
- tt_int_op(download_status_get_n_failures(&dls_attempt), OP_EQ,
- IMPOSSIBLE_TO_DOWNLOAD);
- tt_int_op(download_status_get_n_attempts(&dls_attempt), OP_EQ,
- IMPOSSIBLE_TO_DOWNLOAD);
- tt_int_op(mock_get_options_calls, OP_GE, 1);
-
- /* Check that an attempt reset doesn't reset at the limit */
- mock_get_options_calls = 0;
- download_status_reset(&dls_attempt);
- tt_assert(download_status_get_next_attempt_at(&dls_attempt)
- == TIME_MAX);
- tt_int_op(download_status_get_n_failures(&dls_attempt), OP_EQ,
- IMPOSSIBLE_TO_DOWNLOAD);
- tt_int_op(download_status_get_n_attempts(&dls_attempt), OP_EQ,
- IMPOSSIBLE_TO_DOWNLOAD);
- tt_int_op(mock_get_options_calls, OP_EQ, 0);
-
- /* Check that an attempt reset resets just before the limit */
- mock_get_options_calls = 0;
- dls_attempt.n_download_failures = IMPOSSIBLE_TO_DOWNLOAD - 1;
- dls_attempt.n_download_attempts = IMPOSSIBLE_TO_DOWNLOAD - 1;
- download_status_reset(&dls_attempt);
- /* we really want to test that it's equal to time(NULL) + delay0, but that's
- * an unrealiable test, because time(NULL) might change. */
- tt_assert(download_status_get_next_attempt_at(&dls_attempt)
- >= current_time + delay0);
- tt_assert(download_status_get_next_attempt_at(&dls_attempt)
- != TIME_MAX);
- tt_int_op(download_status_get_n_failures(&dls_attempt), OP_EQ, 0);
- tt_int_op(download_status_get_n_attempts(&dls_attempt), OP_EQ, 0);
- tt_int_op(mock_get_options_calls, OP_GE, 1);
- mock_options->UseBridges = 0;
-
- /* Check that attempt increments don't happen on failure-based schedules,
- * and that the attempt is set at the end of time */
- mock_get_options_calls = 0;
- setup_full_capture_of_logs(LOG_WARN);
- next_at = download_status_increment_attempt(&dls_failure, "test",
- current_time);
- expect_single_log_msg_containing(
- "Tried to launch an attempt-based connection on a failure-based "
- "schedule.");
- teardown_capture_of_logs();
- tt_assert(next_at == TIME_MAX);
- tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ, 0);
- tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ, 0);
- tt_int_op(mock_get_options_calls, OP_EQ, 0);
-
done:
/* the pointers in schedule are allocated on the stack */
smartlist_free(schedule);
@@ -4893,9 +4451,11 @@ mock_check_private_dir(const char *dirname, cpd_check_t check,
static char *
mock_get_datadir_fname(const or_options_t *options,
+ directory_root_t roottype,
const char *sub1, const char *sub2,
const char *suffix)
{
+ (void) roottype;
char *rv = NULL;
/*
@@ -5052,7 +4612,7 @@ test_dir_dump_unparseable_descriptors(void *data)
mock_options->MaxUnparseableDescSizeToLog = 1536;
MOCK(get_options, mock_get_options);
MOCK(check_private_dir, mock_check_private_dir);
- MOCK(options_get_datadir_fname2_suffix,
+ MOCK(options_get_dir_fname2_suffix,
mock_get_datadir_fname);
/*
@@ -5570,7 +5130,7 @@ test_dir_dump_unparseable_descriptors(void *data)
mock_unlink_reset();
UNMOCK(write_str_to_file);
mock_write_str_to_file_reset();
- UNMOCK(options_get_datadir_fname2_suffix);
+ UNMOCK(options_get_dir_fname2_suffix);
UNMOCK(check_private_dir);
UNMOCK(get_options);
tor_free(mock_options);
@@ -6193,6 +5753,106 @@ test_dir_platform_str(void *arg)
;
}
+static networkstatus_t *mock_networkstatus;
+
+static networkstatus_t *
+mock_networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f)
+{
+ (void)f;
+ return mock_networkstatus;
+}
+
+static void
+test_dir_networkstatus_consensus_has_ipv6(void *arg)
+{
+ (void)arg;
+
+ int has_ipv6 = 0;
+
+ /* Init options and networkstatus */
+ or_options_t our_options;
+ mock_options = &our_options;
+ reset_options(mock_options, &mock_get_options_calls);
+ MOCK(get_options, mock_get_options);
+
+ networkstatus_t our_networkstatus;
+ mock_networkstatus = &our_networkstatus;
+ memset(mock_networkstatus, 0, sizeof(*mock_networkstatus));
+ MOCK(networkstatus_get_latest_consensus_by_flavor,
+ mock_networkstatus_get_latest_consensus_by_flavor);
+
+ /* A live consensus */
+ mock_networkstatus->valid_after = time(NULL) - 3600;
+ mock_networkstatus->valid_until = time(NULL) + 3600;
+
+ /* Test the bounds for A lines in the NS consensus */
+ mock_options->UseMicrodescriptors = 0;
+
+ mock_networkstatus->consensus_method = MIN_METHOD_FOR_A_LINES;
+ has_ipv6 = networkstatus_consensus_has_ipv6(get_options());
+ tt_assert(has_ipv6);
+
+ mock_networkstatus->consensus_method = MIN_METHOD_FOR_A_LINES + 1;
+ has_ipv6 = networkstatus_consensus_has_ipv6(get_options());
+ tt_assert(has_ipv6);
+
+ mock_networkstatus->consensus_method = MIN_METHOD_FOR_A_LINES + 20;
+ has_ipv6 = networkstatus_consensus_has_ipv6(get_options());
+ tt_assert(has_ipv6);
+
+ mock_networkstatus->consensus_method = MIN_METHOD_FOR_A_LINES - 1;
+ has_ipv6 = networkstatus_consensus_has_ipv6(get_options());
+ tt_assert(!has_ipv6);
+
+ /* Test the bounds for A lines in the microdesc consensus */
+ mock_options->UseMicrodescriptors = 1;
+
+ mock_networkstatus->consensus_method =
+ MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS;
+ has_ipv6 = networkstatus_consensus_has_ipv6(get_options());
+ tt_assert(has_ipv6);
+
+ mock_networkstatus->consensus_method =
+ MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS + 1;
+ has_ipv6 = networkstatus_consensus_has_ipv6(get_options());
+ tt_assert(has_ipv6);
+
+ mock_networkstatus->consensus_method =
+ MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS + 20;
+ has_ipv6 = networkstatus_consensus_has_ipv6(get_options());
+ tt_assert(has_ipv6);
+
+ mock_networkstatus->consensus_method =
+ MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS - 1;
+ has_ipv6 = networkstatus_consensus_has_ipv6(get_options());
+ tt_assert(!has_ipv6);
+
+ /* Test the edge cases */
+ mock_options->UseMicrodescriptors = 1;
+ mock_networkstatus->consensus_method =
+ MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS;
+
+ /* Reasonably live */
+ mock_networkstatus->valid_until = approx_time() - 60;
+ has_ipv6 = networkstatus_consensus_has_ipv6(get_options());
+ tt_assert(has_ipv6);
+
+ /* Not reasonably live */
+ mock_networkstatus->valid_after = approx_time() - 24*60*60 - 3600;
+ mock_networkstatus->valid_until = approx_time() - 24*60*60 - 60;
+ has_ipv6 = networkstatus_consensus_has_ipv6(get_options());
+ tt_assert(!has_ipv6);
+
+ /* NULL consensus */
+ mock_networkstatus = NULL;
+ has_ipv6 = networkstatus_consensus_has_ipv6(get_options());
+ tt_assert(!has_ipv6);
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(networkstatus_get_latest_consensus_by_flavor);
+}
+
static void
test_dir_format_versions_list(void *arg)
{
@@ -6287,7 +5947,6 @@ struct testcase_t dir_tests[] = {
DIR(post_parsing, 0),
DIR(fetch_type, 0),
DIR(packages, 0),
- DIR(download_status_schedule, 0),
DIR(download_status_random_backoff, 0),
DIR(download_status_random_backoff_ranges, 0),
DIR(download_status_increment, TT_FORK),
@@ -6312,6 +5971,7 @@ struct testcase_t dir_tests[] = {
DIR(assumed_flags, 0),
DIR(networkstatus_compute_bw_weights_v10, 0),
DIR(platform_str, 0),
+ DIR(networkstatus_consensus_has_ipv6, TT_FORK),
DIR(format_versions_list, TT_FORK),
END_OF_TESTCASES
};
diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c
index fe26657ad8..ca64dce5fe 100644
--- a/src/test/test_dir_handle_get.c
+++ b/src/test/test_dir_handle_get.c
@@ -88,7 +88,7 @@ test_dir_handle_get_bad_request(void *data)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
}
@@ -117,7 +117,7 @@ test_dir_handle_get_v1_command_not_found(void *data)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
}
@@ -164,7 +164,7 @@ test_dir_handle_get_v1_command(void *data)
done:
UNMOCK(connection_write_to_buf_impl_);
UNMOCK(get_dirportfrontpage);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
tor_free(body);
}
@@ -190,7 +190,7 @@ test_dir_handle_get_not_found(void *data)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
}
@@ -225,7 +225,7 @@ test_dir_handle_get_robots_txt(void *data)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
tor_free(body);
}
@@ -254,7 +254,7 @@ test_dir_handle_get_rendezvous2_not_found_if_not_encrypted(void *data)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
}
@@ -282,7 +282,7 @@ test_dir_handle_get_rendezvous2_on_encrypted_conn_with_invalid_desc_id(
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
}
@@ -315,7 +315,7 @@ test_dir_handle_get_rendezvous2_on_encrypted_conn_not_well_formed(void *data)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
}
@@ -344,7 +344,7 @@ test_dir_handle_get_rendezvous2_not_found(void *data)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
rend_cache_free_all();
}
@@ -424,7 +424,7 @@ test_dir_handle_get_rendezvous2_on_encrypted_conn_success(void *data)
UNMOCK(connection_write_to_buf_impl_);
NS_UNMOCK(router_get_my_routerinfo);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
tor_free(body);
rend_encoded_v2_service_descriptor_free(desc_holder);
@@ -457,7 +457,7 @@ test_dir_handle_get_micro_d_not_found(void *data)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
}
@@ -469,6 +469,7 @@ init_mock_options(void)
memset(mock_options, 0, sizeof(or_options_t));
mock_options->TestingTorNetwork = 1;
mock_options->DataDirectory = tor_strdup(get_fname_rnd("datadir_tmp"));
+ mock_options->CacheDirectory = tor_strdup(mock_options->DataDirectory);
check_private_dir(mock_options->DataDirectory, CPD_CREATE, NULL);
}
@@ -541,7 +542,7 @@ test_dir_handle_get_micro_d(void *data)
UNMOCK(connection_write_to_buf_impl_);
or_options_free(mock_options); mock_options = NULL;
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
tor_free(body);
smartlist_free(list);
@@ -595,7 +596,7 @@ test_dir_handle_get_micro_d_server_busy(void *data)
UNMOCK(connection_write_to_buf_impl_);
or_options_free(mock_options); mock_options = NULL;
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
smartlist_free(list);
microdesc_free_all();
@@ -632,7 +633,7 @@ test_dir_handle_get_networkstatus_bridges_not_found_without_auth(void *data)
UNMOCK(get_options);
UNMOCK(connection_write_to_buf_impl_);
or_options_free(mock_options); mock_options = NULL;
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
}
@@ -672,7 +673,7 @@ test_dir_handle_get_networkstatus_bridges(void *data)
UNMOCK(get_options);
UNMOCK(connection_write_to_buf_impl_);
or_options_free(mock_options); mock_options = NULL;
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
}
@@ -709,7 +710,7 @@ test_dir_handle_get_networkstatus_bridges_not_found_wrong_auth(void *data)
UNMOCK(get_options);
UNMOCK(connection_write_to_buf_impl_);
or_options_free(mock_options); mock_options = NULL;
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
}
@@ -737,7 +738,7 @@ test_dir_handle_get_server_descriptors_not_found(void* data)
done:
UNMOCK(connection_write_to_buf_impl_);
or_options_free(mock_options); mock_options = NULL;
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
}
@@ -798,7 +799,7 @@ test_dir_handle_get_server_descriptors_all(void* data)
done:
NS_UNMOCK(router_get_my_routerinfo);
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
tor_free(body);
@@ -902,7 +903,7 @@ test_dir_handle_get_server_descriptors_authority(void* data)
UNMOCK(connection_write_to_buf_impl_);
tor_free(mock_routerinfo->cache_info.signed_descriptor_body);
tor_free(mock_routerinfo);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
tor_free(body);
crypto_pk_free(identity_pkey);
@@ -974,7 +975,7 @@ test_dir_handle_get_server_descriptors_fp(void* data)
UNMOCK(connection_write_to_buf_impl_);
tor_free(mock_routerinfo->cache_info.signed_descriptor_body);
tor_free(mock_routerinfo);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
tor_free(body);
crypto_pk_free(identity_pkey);
@@ -1038,7 +1039,7 @@ test_dir_handle_get_server_descriptors_d(void* data)
done:
UNMOCK(connection_write_to_buf_impl_);
tor_free(mock_routerinfo);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
tor_free(body);
crypto_pk_free(identity_pkey);
@@ -1094,7 +1095,7 @@ test_dir_handle_get_server_descriptors_busy(void* data)
UNMOCK(get_options);
UNMOCK(connection_write_to_buf_impl_);
tor_free(mock_routerinfo);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
crypto_pk_free(identity_pkey);
@@ -1125,7 +1126,7 @@ test_dir_handle_get_server_keys_bad_req(void* data)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
}
@@ -1151,7 +1152,7 @@ test_dir_handle_get_server_keys_all_not_found(void* data)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
}
@@ -1210,7 +1211,7 @@ test_dir_handle_get_server_keys_all(void* data)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
tor_free(body);
@@ -1240,7 +1241,7 @@ test_dir_handle_get_server_keys_authority_not_found(void* data)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
}
@@ -1288,7 +1289,7 @@ test_dir_handle_get_server_keys_authority(void* data)
done:
UNMOCK(get_my_v3_authority_cert);
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
tor_free(body);
authority_cert_free(mock_cert); mock_cert = NULL;
@@ -1316,7 +1317,7 @@ test_dir_handle_get_server_keys_fp_not_found(void* data)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
}
@@ -1370,7 +1371,7 @@ test_dir_handle_get_server_keys_fp(void* data)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
tor_free(body);
clear_dir_servers();
@@ -1399,7 +1400,7 @@ test_dir_handle_get_server_keys_sk_not_found(void* data)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
}
@@ -1444,7 +1445,7 @@ test_dir_handle_get_server_keys_sk(void* data)
done:
UNMOCK(get_my_v3_authority_cert);
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
authority_cert_free(mock_cert); mock_cert = NULL;
tor_free(header);
tor_free(body);
@@ -1472,7 +1473,7 @@ test_dir_handle_get_server_keys_fpsk_not_found(void* data)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
}
@@ -1529,7 +1530,7 @@ test_dir_handle_get_server_keys_fpsk(void* data)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
tor_free(body);
@@ -1583,7 +1584,7 @@ test_dir_handle_get_server_keys_busy(void* data)
done:
UNMOCK(get_options);
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
or_options_free(mock_options); mock_options = NULL;
@@ -1649,7 +1650,7 @@ test_dir_handle_get_status_vote_current_consensus_ns_not_enough_sigs(void* d)
UNMOCK(connection_write_to_buf_impl_);
UNMOCK(get_options);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
tor_free(stats);
smartlist_free(mock_ns_val->voters);
@@ -1690,7 +1691,7 @@ test_dir_handle_get_status_vote_current_consensus_ns_not_found(void* data)
done:
UNMOCK(connection_write_to_buf_impl_);
UNMOCK(get_options);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
tor_free(stats);
or_options_free(mock_options); mock_options = NULL;
@@ -1764,7 +1765,7 @@ test_dir_handle_get_status_vote_current_consensus_too_old(void *data)
UNMOCK(networkstatus_get_latest_consensus_by_flavor);
UNMOCK(connection_write_to_buf_impl_);
UNMOCK(get_options);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
tor_free(mock_ns_val);
or_options_free(mock_options); mock_options = NULL;
@@ -1825,7 +1826,7 @@ status_vote_current_consensus_ns_test(char **header, char **body,
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
}
static void
@@ -1948,7 +1949,7 @@ test_dir_handle_get_status_vote_current_not_found(void* data)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
}
@@ -1971,7 +1972,7 @@ status_vote_current_d_test(char **header, char **body, size_t *body_l)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
}
static void
@@ -1991,7 +1992,7 @@ status_vote_next_d_test(char **header, char **body, size_t *body_l)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
}
static void
@@ -2116,7 +2117,7 @@ test_dir_handle_get_status_vote_next_not_found(void* data)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
}
@@ -2135,7 +2136,7 @@ status_vote_next_consensus_test(char **header, char **body, size_t *body_used)
body, body_used, 18, 0);
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
}
static void
@@ -2175,7 +2176,7 @@ test_dir_handle_get_status_vote_current_authority_not_found(void* data)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
}
@@ -2199,7 +2200,7 @@ test_dir_handle_get_status_vote_next_authority_not_found(void* data)
done:
UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
}
@@ -2281,7 +2282,7 @@ status_vote_next_consensus_signatures_test(char **header, char **body,
body, body_used, 22, 0);
done:
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
UNMOCK(connection_write_to_buf_impl_);
}
@@ -2429,7 +2430,7 @@ test_dir_handle_get_status_vote_next_authority(void* data)
done:
UNMOCK(connection_write_to_buf_impl_);
UNMOCK(get_my_v3_authority_cert);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
tor_free(body);
authority_cert_free(mock_cert); mock_cert = NULL;
@@ -2511,7 +2512,7 @@ test_dir_handle_get_status_vote_current_authority(void* data)
done:
UNMOCK(connection_write_to_buf_impl_);
UNMOCK(get_my_v3_authority_cert);
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
tor_free(header);
tor_free(body);
authority_cert_free(mock_cert); mock_cert = NULL;
diff --git a/src/test/test_dns.c b/src/test/test_dns.c
index 19dcb02931..1fee01d2c0 100644
--- a/src/test/test_dns.c
+++ b/src/test/test_dns.c
@@ -1,3 +1,6 @@
+/* Copyright (c) 2015-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
#include "or.h"
#include "test.h"
@@ -121,7 +124,7 @@ static int n_connection_free = 0;
static connection_t *last_freed_conn = NULL;
static void
-NS(connection_free)(connection_t *conn)
+NS(connection_free_)(connection_t *conn)
{
n_connection_free++;
@@ -264,7 +267,7 @@ NS(test_main)(void *arg)
*/
NS_MOCK(dns_cancel_pending_resolve);
- NS_MOCK(connection_free);
+ NS_MOCK(connection_free_);
exitconn->on_circuit = &(on_circuit->base_);
exitconn->base_.purpose = EXIT_PURPOSE_RESOLVE;
@@ -291,7 +294,7 @@ NS(test_main)(void *arg)
NS_UNMOCK(send_resolved_cell);
NS_UNMOCK(send_resolved_hostname_cell);
NS_UNMOCK(dns_cancel_pending_resolve);
- NS_UNMOCK(connection_free);
+ NS_UNMOCK(connection_free_);
tor_free(on_circuit);
tor_free(exitconn);
tor_free(nextconn);
diff --git a/src/test/test_entryconn.c b/src/test/test_entryconn.c
index c29b1a7126..9d8a072c77 100644
--- a/src/test/test_entryconn.c
+++ b/src/test/test_entryconn.c
@@ -34,7 +34,7 @@ entryconn_rewrite_teardown(const struct testcase_t *tc, void *arg)
(void)tc;
entry_connection_t *ec = arg;
if (ec)
- connection_free_(ENTRY_TO_CONN(ec));
+ connection_free_minimal(ENTRY_TO_CONN(ec));
addressmap_free_all();
return 1;
}
@@ -156,8 +156,8 @@ test_entryconn_rewrite_automap_ipv4(void *arg)
ec->socks_request->address);
done:
- connection_free_(ENTRY_TO_CONN(ec2));
- connection_free_(ENTRY_TO_CONN(ec3));
+ connection_free_minimal(ENTRY_TO_CONN(ec2));
+ connection_free_minimal(ENTRY_TO_CONN(ec3));
}
/* Automap on resolve, connect to automapped address, resolve again and get
@@ -230,9 +230,9 @@ test_entryconn_rewrite_automap_ipv6(void *arg)
ec->socks_request->address);
done:
- connection_free_(ENTRY_TO_CONN(ec));
- connection_free_(ENTRY_TO_CONN(ec2));
- connection_free_(ENTRY_TO_CONN(ec3));
+ connection_free_minimal(ENTRY_TO_CONN(ec));
+ connection_free_minimal(ENTRY_TO_CONN(ec2));
+ connection_free_minimal(ENTRY_TO_CONN(ec3));
}
#if 0
@@ -283,7 +283,7 @@ test_entryconn_rewrite_automap_reverse(void *arg)
tt_int_op(rr.exit_source, OP_EQ, ADDRMAPSRC_NONE);
done:
- connection_free_(ENTRY_TO_CONN(ec2));
+ connection_free_minimal(ENTRY_TO_CONN(ec2));
}
#endif /* 0 */
@@ -333,7 +333,7 @@ test_entryconn_rewrite_cached_dns_ipv4(void *arg)
tt_str_op(ec2->socks_request->address, OP_EQ, "240.240.241.241");
done:
- connection_free_(ENTRY_TO_CONN(ec2));
+ connection_free_minimal(ENTRY_TO_CONN(ec2));
}
/* Rewrite because of cached DNS entry. */
@@ -385,8 +385,8 @@ test_entryconn_rewrite_cached_dns_ipv6(void *arg)
tt_str_op(ec2->socks_request->address, OP_EQ, "[::f00f]");
done:
- connection_free_(ENTRY_TO_CONN(ec));
- connection_free_(ENTRY_TO_CONN(ec2));
+ connection_free_minimal(ENTRY_TO_CONN(ec));
+ connection_free_minimal(ENTRY_TO_CONN(ec2));
}
/* Fail to connect to unmapped address in virtual range. */
@@ -426,7 +426,7 @@ test_entryconn_rewrite_unmapped_virtual(void *arg)
tt_int_op(rr.exit_source, OP_EQ, ADDRMAPSRC_NONE);
done:
- connection_free_(ENTRY_TO_CONN(ec2));
+ connection_free_minimal(ENTRY_TO_CONN(ec2));
}
/* Rewrite because of mapaddress option */
@@ -507,7 +507,7 @@ test_entryconn_rewrite_automap_exit(void *arg)
tt_int_op(rr.end_reason, OP_EQ, END_STREAM_REASON_TORPROTOCOL);
done:
- connection_free_(ENTRY_TO_CONN(ec2));
+ connection_free_minimal(ENTRY_TO_CONN(ec2));
}
/* Rewrite into .exit because of mapaddress */
@@ -618,9 +618,9 @@ test_entryconn_rewrite_mapaddress_automap_onion(void *arg)
*/
done:
- connection_free_(ENTRY_TO_CONN(ec2));
- connection_free_(ENTRY_TO_CONN(ec3));
- connection_free_(ENTRY_TO_CONN(ec4));
+ connection_free_minimal(ENTRY_TO_CONN(ec2));
+ connection_free_minimal(ENTRY_TO_CONN(ec3));
+ connection_free_minimal(ENTRY_TO_CONN(ec4));
}
static void
@@ -678,8 +678,8 @@ test_entryconn_rewrite_mapaddress_automap_onion_common(entry_connection_t *ec,
"abcdefghijklmnop.onion"));
done:
- connection_free_(ENTRY_TO_CONN(ec2));
- connection_free_(ENTRY_TO_CONN(ec3));
+ connection_free_minimal(ENTRY_TO_CONN(ec2));
+ connection_free_minimal(ENTRY_TO_CONN(ec3));
}
/* This time is the same, but we start with a mapping from a non-onion
diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c
index 7d85b09e03..505e09e36f 100644
--- a/src/test/test_entrynodes.c
+++ b/src/test/test_entrynodes.c
@@ -2030,7 +2030,7 @@ test_entry_guard_select_for_circuit_highlevel_primary(void *arg)
tt_mem_op(g->identity, OP_NE, g_prev->identity, DIGEST_LEN);
tt_int_op(g->is_primary, OP_EQ, 1);
tt_i64_op(g->last_tried_to_connect, OP_EQ, start+60);
- tt_int_op(g->confirmed_idx, OP_EQ, -1); // not confirmd now.
+ tt_int_op(g->confirmed_idx, OP_EQ, -1); // not confirmed now.
/* Call this one up; watch it get confirmed. */
update_approx_time(start+90);
@@ -2378,8 +2378,8 @@ upgrade_circuits_cleanup(const struct testcase_t *testcase, void *ptr)
// circuit_guard_state_free(data->guard2_state); // held in circ2
guard_selection_free(data->gs);
smartlist_free(data->all_origin_circuits);
- circuit_free(TO_CIRCUIT(data->circ1));
- circuit_free(TO_CIRCUIT(data->circ2));
+ circuit_free_(TO_CIRCUIT(data->circ1));
+ circuit_free_(TO_CIRCUIT(data->circ2));
tor_free(data);
return big_fake_network_cleanup(testcase, NULL);
}
diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c
index e18deb2700..cadef257f1 100644
--- a/src/test/test_extorport.c
+++ b/src/test/test_extorport.c
@@ -58,11 +58,11 @@ test_ext_or_id_map(void *arg)
done:
if (c1)
- connection_free_(TO_CONN(c1));
+ connection_free_minimal(TO_CONN(c1));
if (c2)
- connection_free_(TO_CONN(c2));
+ connection_free_minimal(TO_CONN(c2));
if (c3)
- connection_free_(TO_CONN(c3));
+ connection_free_minimal(TO_CONN(c3));
tor_free(idp);
tor_free(idp2);
connection_or_clear_ext_or_id_map();
@@ -145,7 +145,7 @@ test_ext_or_write_command(void *arg)
done:
if (c1)
- connection_free_(TO_CONN(c1));
+ connection_free_minimal(TO_CONN(c1));
tor_free(cp);
tor_free(buf);
UNMOCK(connection_write_to_buf_impl_);
@@ -591,7 +591,7 @@ test_ext_or_handshake(void *arg)
UNMOCK(connection_write_to_buf_impl_);
UNMOCK(crypto_rand);
if (conn)
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
#undef CONTAINS
#undef WRITE
}
diff --git a/src/test/test_handles.c b/src/test/test_handles.c
index 7ddee6e376..eb1e1f1bbe 100644
--- a/src/test/test_handles.c
+++ b/src/test/test_handles.c
@@ -13,6 +13,8 @@ typedef struct demo_t {
} demo_t;
HANDLE_DECL(demo, demo_t, static)
+#define demo_handle_free(h) \
+ FREE_AND_NULL(demo_handle_t, demo_handle_free_, (h))
HANDLE_IMPL(demo, demo_t, static)
static demo_t *
diff --git a/src/test/test_hs.c b/src/test/test_hs.c
index 09db176df8..07daebc164 100644
--- a/src/test/test_hs.c
+++ b/src/test/test_hs.c
@@ -258,8 +258,9 @@ test_hs_desc_event(void *arg)
sizeof(desc_id_base32));
/* test request event */
- control_event_hs_descriptor_requested(&rend_query.base_, HSDIR_EXIST_ID,
- STR_DESC_ID_BASE32);
+ control_event_hs_descriptor_requested(rend_query.onion_address,
+ rend_query.auth_type, HSDIR_EXIST_ID,
+ STR_DESC_ID_BASE32, NULL);
expected_msg = "650 HS_DESC REQUESTED "STR_HS_ADDR" NO_AUTH "\
STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32 "\r\n";
tt_assert(received_msg);
@@ -268,8 +269,8 @@ test_hs_desc_event(void *arg)
/* test received event */
rend_query.auth_type = REND_BASIC_AUTH;
- control_event_hs_descriptor_received(rend_query.onion_address,
- &rend_query.base_, HSDIR_EXIST_ID);
+ control_event_hsv2_descriptor_received(rend_query.onion_address,
+ &rend_query.base_, HSDIR_EXIST_ID);
expected_msg = "650 HS_DESC RECEIVED "STR_HS_ADDR" BASIC_AUTH "\
STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32"\r\n";
tt_assert(received_msg);
@@ -278,7 +279,7 @@ test_hs_desc_event(void *arg)
/* test failed event */
rend_query.auth_type = REND_STEALTH_AUTH;
- control_event_hs_descriptor_failed(&rend_query.base_,
+ control_event_hsv2_descriptor_failed(&rend_query.base_,
HSDIR_NONE_EXIST_ID,
"QUERY_REJECTED");
expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" STEALTH_AUTH "\
@@ -289,7 +290,7 @@ test_hs_desc_event(void *arg)
/* test invalid auth type */
rend_query.auth_type = 999;
- control_event_hs_descriptor_failed(&rend_query.base_,
+ control_event_hsv2_descriptor_failed(&rend_query.base_,
HSDIR_EXIST_ID,
"QUERY_REJECTED");
expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" UNKNOWN "\
@@ -301,7 +302,7 @@ test_hs_desc_event(void *arg)
/* test no HSDir fingerprint type */
rend_query.auth_type = REND_NO_AUTH;
- control_event_hs_descriptor_failed(&rend_query.base_, NULL,
+ control_event_hsv2_descriptor_failed(&rend_query.base_, NULL,
"QUERY_NO_HSDIR");
expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" NO_AUTH " \
"UNKNOWN REASON=QUERY_NO_HSDIR\r\n";
@@ -360,6 +361,7 @@ test_pick_tor2web_rendezvous_node(void *arg)
/* Parse Tor2webRendezvousPoints as a routerset. */
options->Tor2webRendezvousPoints = routerset_new();
+ options->UseMicrodescriptors = 0;
retval = routerset_parse(options->Tor2webRendezvousPoints,
tor2web_rendezvous_str,
"test_tor2web_rp");
@@ -1012,7 +1014,7 @@ test_prune_services_on_reload(void *arg)
set_rend_service_list(old);
set_rend_rend_service_staging_list(new);
rend_service_prune_list_impl_();
- /* Check if they've all been transfered. */
+ /* Check if they've all been transferred. */
tt_int_op(smartlist_len(old), OP_EQ, 0);
tt_int_op(smartlist_len(new), OP_EQ, 2);
}
diff --git a/src/test/test_hs_cache.c b/src/test/test_hs_cache.c
index 91b13be862..458ce1a92e 100644
--- a/src/test/test_hs_cache.c
+++ b/src/test/test_hs_cache.c
@@ -259,7 +259,7 @@ helper_fetch_desc_from_hsdir(const ed25519_public_key_t *blinded_key)
done:
tor_free(hsdir_query_str);
if (conn)
- connection_free_(TO_CONN(conn));
+ connection_free_minimal(TO_CONN(conn));
return received_desc;
}
diff --git a/src/test/test_hs_cell.c b/src/test/test_hs_cell.c
index 1b3c788a67..aed28d3bd2 100644
--- a/src/test/test_hs_cell.c
+++ b/src/test/test_hs_cell.c
@@ -38,7 +38,7 @@ test_gen_establish_intro_cell(void *arg)
attempt to parse it. */
{
/* We only need the auth key pair here. */
- hs_service_intro_point_t *ip = service_intro_point_new(NULL, 0);
+ hs_service_intro_point_t *ip = service_intro_point_new(NULL, 0, 0);
/* Auth key pair is generated in the constructor so we are all set for
* using this IP object. */
ret = hs_cell_build_establish_intro(circ_nonce, ip, buf);
@@ -106,7 +106,7 @@ test_gen_establish_intro_cell_bad(void *arg)
ed25519_sign_prefixed() function and make it fail. */
cell = trn_cell_establish_intro_new();
tt_assert(cell);
- ip = service_intro_point_new(NULL, 0);
+ ip = service_intro_point_new(NULL, 0, 0);
cell_len = hs_cell_build_establish_intro(circ_nonce, ip, NULL);
service_intro_point_free(ip);
expect_log_msg_containing("Unable to make signature for "
diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c
index 750920fac0..7ee7210bc9 100644
--- a/src/test/test_hs_client.c
+++ b/src/test/test_hs_client.c
@@ -227,10 +227,10 @@ test_e2e_rend_circuit_setup_legacy(void *arg)
tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, TO_CIRCUIT(or_circ));
done:
- connection_free_(conn);
+ connection_free_minimal(conn);
if (or_circ)
tor_free(TO_CIRCUIT(or_circ)->n_chan);
- circuit_free(TO_CIRCUIT(or_circ));
+ circuit_free_(TO_CIRCUIT(or_circ));
}
/* Test: Ensure that setting up v3 rendezvous circuits works correctly. */
@@ -297,10 +297,10 @@ test_e2e_rend_circuit_setup(void *arg)
tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, TO_CIRCUIT(or_circ));
done:
- connection_free_(conn);
+ connection_free_minimal(conn);
if (or_circ)
tor_free(TO_CIRCUIT(or_circ)->n_chan);
- circuit_free(TO_CIRCUIT(or_circ));
+ circuit_free_(TO_CIRCUIT(or_circ));
}
/** Test client logic for picking intro points from a descriptor. Also test how
@@ -560,7 +560,7 @@ test_descriptor_fetch(void *arg)
smartlist_add(get_connection_array(), TO_CONN(dir_conn));
ret = hs_client_refetch_hsdesc(&service_pk);
smartlist_remove(get_connection_array(), TO_CONN(dir_conn));
- connection_free_(TO_CONN(dir_conn));
+ connection_free_minimal(TO_CONN(dir_conn));
tt_int_op(ret, OP_EQ, HS_CLIENT_FETCH_PENDING);
}
@@ -579,7 +579,7 @@ test_descriptor_fetch(void *arg)
tt_int_op(ec->edge_.end_reason, OP_EQ, END_STREAM_REASON_RESOLVEFAILED);
done:
- connection_free_(ENTRY_TO_CONN(ec));
+ connection_free_minimal(ENTRY_TO_CONN(ec));
UNMOCK(networkstatus_get_live_consensus);
UNMOCK(router_have_minimum_dir_info);
hs_free_all();
diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c
index 3ae623ed0a..16803dbd16 100644
--- a/src/test/test_hs_common.c
+++ b/src/test/test_hs_common.c
@@ -290,7 +290,7 @@ helper_add_hsdir_to_networkstatus(networkstatus_t *ns,
memcpy(rs->identity_digest, identity, DIGEST_LEN);
rs->is_hs_dir = is_hsdir;
- rs->supports_v3_hsdir = 1;
+ rs->pv.supports_v3_hsdir = 1;
strlcpy(rs->nickname, nickname, sizeof(rs->nickname));
tor_addr_parse(&ipv4_addr, "1.2.3.4");
ri->addr = tor_addr_to_ipv4h(&ipv4_addr);
@@ -307,7 +307,8 @@ helper_add_hsdir_to_networkstatus(networkstatus_t *ns,
node = node_get_mutable_by_id(ri->cache_info.identity_digest);
tt_assert(node);
node->rs = rs;
- /* We need this to exist for node_has_descriptor() to return true. */
+ /* We need this to exist for node_has_preferred_descriptor() to return
+ * true. */
node->md = tor_malloc_zero(sizeof(microdesc_t));
/* Do this now the nodelist_set_routerinfo() function needs a "rs" to set
* the indexes which it doesn't have when it is called. */
diff --git a/src/test/test_hs_control.c b/src/test/test_hs_control.c
new file mode 100644
index 0000000000..207a55de6d
--- /dev/null
+++ b/src/test/test_hs_control.c
@@ -0,0 +1,199 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_hs_control.c
+ * \brief Unit tests for hidden service control port event and command.
+ **/
+
+#define CONTROL_PRIVATE
+#define CIRCUITBUILD_PRIVATE
+#define RENDCOMMON_PRIVATE
+#define RENDSERVICE_PRIVATE
+#define HS_SERVICE_PRIVATE
+
+#include "or.h"
+#include "test.h"
+#include "control.h"
+#include "config.h"
+#include "hs_common.h"
+#include "hs_control.h"
+#include "nodelist.h"
+//#include "rendcommon.h"
+//#include "rendservice.h"
+//#include "routerset.h"
+//#include "circuitbuild.h"
+#include "test_helpers.h"
+
+/* mock ID digest and longname for node that's in nodelist */
+#define HSDIR_EXIST_ID \
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" \
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+#define STR_HSDIR_EXIST_LONGNAME \
+ "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=TestDir"
+#define STR_HSDIR_NONE_EXIST_LONGNAME \
+ "$BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+
+/* Helper global variable for hidden service descriptor event test.
+ * It's used as a pointer to dynamically created message buffer in
+ * send_control_event_string_replacement function, which mocks
+ * send_control_event_string function.
+ *
+ * Always free it after use! */
+static char *received_msg = NULL;
+
+/** Mock function for send_control_event_string
+ */
+static void
+queue_control_event_string_replacement(uint16_t event, char *msg)
+{
+ (void) event;
+ tor_free(received_msg);
+ received_msg = msg;
+}
+
+/** Mock function for node_describe_longname_by_id, it returns either
+ * STR_HSDIR_EXIST_LONGNAME or STR_HSDIR_NONE_EXIST_LONGNAME
+ */
+static const char *
+node_describe_longname_by_id_replacement(const char *id_digest)
+{
+ if (!strcmp(id_digest, HSDIR_EXIST_ID)) {
+ return STR_HSDIR_EXIST_LONGNAME;
+ } else {
+ return STR_HSDIR_NONE_EXIST_LONGNAME;
+ }
+}
+
+/* HSDir fetch index is a series of 'D' */
+#define HSDIR_INDEX_FETCH_HEX \
+ "4343434343434343434343434343434343434343434343434343434343434343"
+#define HSDIR_INDEX_STORE_HEX \
+ "4444444444444444444444444444444444444444444444444444444444444444"
+
+static const node_t *
+mock_node_get_by_id(const char *digest)
+{
+ static node_t node;
+ memcpy(node.identity, digest, DIGEST_LEN);
+ node.hsdir_index = tor_malloc_zero(sizeof(hsdir_index_t));
+ memset(node.hsdir_index->fetch, 'C', DIGEST256_LEN);
+ memset(node.hsdir_index->store_first, 'D', DIGEST256_LEN);
+ return &node;
+}
+
+static void
+test_hs_desc_event(void *arg)
+{
+ int ret;
+ char *expected_msg = NULL;
+ char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+ ed25519_keypair_t identity_kp;
+ ed25519_public_key_t blinded_pk;
+ char base64_blinded_pk[ED25519_BASE64_LEN + 1];
+ routerstatus_t hsdir_rs;
+ hs_ident_dir_conn_t ident;
+
+ (void) arg;
+ MOCK(queue_control_event_string,
+ queue_control_event_string_replacement);
+ MOCK(node_describe_longname_by_id,
+ node_describe_longname_by_id_replacement);
+ MOCK(node_get_by_id, mock_node_get_by_id);
+
+ /* Setup what we need for this test. */
+ ed25519_keypair_generate(&identity_kp, 0);
+ hs_build_address(&identity_kp.pubkey, HS_VERSION_THREE, onion_address);
+ ret = hs_address_is_valid(onion_address);
+ tt_int_op(ret, OP_EQ, 1);
+ memset(&blinded_pk, 'B', sizeof(blinded_pk));
+ memset(&hsdir_rs, 0, sizeof(hsdir_rs));
+ memcpy(hsdir_rs.identity_digest, HSDIR_EXIST_ID, DIGEST_LEN);
+ ret = ed25519_public_to_base64(base64_blinded_pk, &blinded_pk);
+ tt_int_op(ret, OP_EQ, 0);
+ memcpy(&ident.identity_pk, &identity_kp.pubkey,
+ sizeof(ed25519_public_key_t));
+ memcpy(&ident.blinded_pk, &blinded_pk, sizeof(blinded_pk));
+
+ /* HS_DESC REQUESTED ... */
+ hs_control_desc_event_requested(&identity_kp.pubkey, base64_blinded_pk,
+ &hsdir_rs);
+ tor_asprintf(&expected_msg, "650 HS_DESC REQUESTED %s NO_AUTH "
+ STR_HSDIR_EXIST_LONGNAME " %s HSDIR_INDEX="
+ HSDIR_INDEX_FETCH_HEX "\r\n",
+ onion_address, base64_blinded_pk);
+ tt_assert(received_msg);
+ tt_str_op(received_msg, OP_EQ, expected_msg);
+ tor_free(received_msg);
+ tor_free(expected_msg);
+
+ /* HS_DESC CREATED... */
+ hs_control_desc_event_created(onion_address, &blinded_pk);
+ tor_asprintf(&expected_msg, "650 HS_DESC CREATED %s UNKNOWN "
+ "UNKNOWN %s\r\n",
+ onion_address, base64_blinded_pk);
+ tt_assert(received_msg);
+ tt_str_op(received_msg, OP_EQ, expected_msg);
+ tor_free(received_msg);
+ tor_free(expected_msg);
+
+ /* HS_DESC UPLOAD... */
+ uint8_t hsdir_index_store[DIGEST256_LEN];
+ memset(hsdir_index_store, 'D', sizeof(hsdir_index_store));
+ hs_control_desc_event_upload(onion_address, HSDIR_EXIST_ID,
+ &blinded_pk, hsdir_index_store);
+ tor_asprintf(&expected_msg, "650 HS_DESC UPLOAD %s UNKNOWN "
+ STR_HSDIR_EXIST_LONGNAME " %s "
+ "HSDIR_INDEX=" HSDIR_INDEX_STORE_HEX "\r\n",
+ onion_address, base64_blinded_pk);
+ tt_assert(received_msg);
+ tt_str_op(received_msg, OP_EQ, expected_msg);
+ tor_free(received_msg);
+ tor_free(expected_msg);
+
+ /* HS_DESC FAILED... */
+ hs_control_desc_event_failed(&ident, HSDIR_EXIST_ID, "BAD_DESC");
+ tor_asprintf(&expected_msg, "650 HS_DESC FAILED %s NO_AUTH "
+ STR_HSDIR_EXIST_LONGNAME " %s "
+ "REASON=BAD_DESC\r\n",
+ onion_address, base64_blinded_pk);
+ tt_assert(received_msg);
+ tt_str_op(received_msg, OP_EQ, expected_msg);
+ tor_free(received_msg);
+ tor_free(expected_msg);
+
+ /* HS_DESC RECEIVED... */
+ hs_control_desc_event_received(&ident, HSDIR_EXIST_ID);
+ tor_asprintf(&expected_msg, "650 HS_DESC RECEIVED %s NO_AUTH "
+ STR_HSDIR_EXIST_LONGNAME " %s\r\n",
+ onion_address, base64_blinded_pk);
+ tt_assert(received_msg);
+ tt_str_op(received_msg, OP_EQ, expected_msg);
+ tor_free(received_msg);
+ tor_free(expected_msg);
+
+ /* HS_DESC UPLOADED... */
+ hs_control_desc_event_uploaded(&ident, HSDIR_EXIST_ID);
+ tor_asprintf(&expected_msg, "650 HS_DESC UPLOADED %s UNKNOWN "
+ STR_HSDIR_EXIST_LONGNAME "\r\n",
+ onion_address);
+ tt_assert(received_msg);
+ tt_str_op(received_msg, OP_EQ, expected_msg);
+ tor_free(received_msg);
+ tor_free(expected_msg);
+
+ done:
+ UNMOCK(queue_control_event_string);
+ UNMOCK(node_describe_longname_by_id);
+ UNMOCK(node_get_by_id);
+ tor_free(received_msg);
+ tor_free(expected_msg);
+}
+
+struct testcase_t hs_control_tests[] = {
+ { "hs_desc_event", test_hs_desc_event, TT_FORK,
+ NULL, NULL },
+
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c
index 897b441888..8772461f90 100644
--- a/src/test/test_hs_descriptor.c
+++ b/src/test/test_hs_descriptor.c
@@ -415,7 +415,7 @@ test_decode_invalid_intro_point(void *arg)
(void) arg;
- /* Seperate pieces of a valid encoded introduction point. */
+ /* Separate pieces of a valid encoded introduction point. */
const char *intro_point =
"introduction-point AQIUMDI5OUYyNjhGQ0E5RDU1Q0QxNTc=";
const char *auth_key =
diff --git a/src/test/test_hs_intropoint.c b/src/test/test_hs_intropoint.c
index b2d2700f8b..ec4dcb4705 100644
--- a/src/test/test_hs_intropoint.c
+++ b/src/test/test_hs_intropoint.c
@@ -49,7 +49,7 @@ new_establish_intro_cell(const char *circ_nonce,
/* Auth key pair is generated in the constructor so we are all set for
* using this IP object. */
- ip = service_intro_point_new(NULL, 0);
+ ip = service_intro_point_new(NULL, 0, 0);
tt_assert(ip);
cell_len = hs_cell_build_establish_intro(circ_nonce, ip, buf);
tt_i64_op(cell_len, OP_GT, 0);
@@ -75,7 +75,7 @@ new_establish_intro_encoded_cell(const char *circ_nonce, uint8_t *cell_out)
/* Auth key pair is generated in the constructor so we are all set for
* using this IP object. */
- ip = service_intro_point_new(NULL, 0);
+ ip = service_intro_point_new(NULL, 0, 0);
tt_assert(ip);
cell_len = hs_cell_build_establish_intro(circ_nonce, ip, cell_out);
tt_i64_op(cell_len, OP_GT, 0);
@@ -146,7 +146,7 @@ helper_create_introduce1_cell(void)
memcpy(auth_key_ptr, auth_key_kp.pubkey.pubkey, auth_key_len);
}
- /* Set the cell extentions to none. */
+ /* Set the cell extensions to none. */
{
trn_cell_extension_t *ext = trn_cell_extension_new();
trn_cell_extension_set_num(ext, 0);
@@ -201,7 +201,7 @@ test_establish_intro_wrong_purpose(void *arg)
tt_int_op(retval, OP_EQ, -1);
done:
- circuit_free(TO_CIRCUIT(intro_circ));
+ circuit_free_(TO_CIRCUIT(intro_circ));
}
/* Prepare a circuit for accepting an ESTABLISH_INTRO cell */
@@ -235,7 +235,7 @@ test_establish_intro_wrong_keytype(void *arg)
tt_int_op(retval, OP_EQ, -1);
done:
- circuit_free(TO_CIRCUIT(intro_circ));
+ circuit_free_(TO_CIRCUIT(intro_circ));
}
/* Send an ESTABLISH_INTRO cell with an unknown auth key type. Should fail. */
@@ -270,7 +270,7 @@ test_establish_intro_wrong_keytype2(void *arg)
tt_int_op(retval, OP_EQ, -1);
done:
- circuit_free(TO_CIRCUIT(intro_circ));
+ circuit_free_(TO_CIRCUIT(intro_circ));
}
/* Send a legit ESTABLISH_INTRO cell but with a wrong MAC. Should fail. */
@@ -340,7 +340,7 @@ test_establish_intro_wrong_mac(void *arg)
done:
trn_cell_establish_intro_free(cell);
- circuit_free(TO_CIRCUIT(intro_circ));
+ circuit_free_(TO_CIRCUIT(intro_circ));
}
/* Send a legit ESTABLISH_INTRO cell but with a wrong auth key length. Should
@@ -385,7 +385,7 @@ test_establish_intro_wrong_auth_key_len(void *arg)
done:
trn_cell_establish_intro_free(cell);
- circuit_free(TO_CIRCUIT(intro_circ));
+ circuit_free_(TO_CIRCUIT(intro_circ));
}
/* Send a legit ESTABLISH_INTRO cell but with a wrong sig length. Should
@@ -430,7 +430,7 @@ test_establish_intro_wrong_sig_len(void *arg)
done:
trn_cell_establish_intro_free(cell);
- circuit_free(TO_CIRCUIT(intro_circ));
+ circuit_free_(TO_CIRCUIT(intro_circ));
}
/* Send a legit ESTABLISH_INTRO cell but slightly change the signature. Should
@@ -467,7 +467,7 @@ test_establish_intro_wrong_sig(void *arg)
tt_int_op(retval, OP_EQ, -1);
done:
- circuit_free(TO_CIRCUIT(intro_circ));
+ circuit_free_(TO_CIRCUIT(intro_circ));
}
/* Helper function: Send a well-formed v3 ESTABLISH_INTRO cell to
@@ -559,7 +559,7 @@ test_circuitmap_free_all(void)
;
}
-/** Successfuly register a v2 intro point and a v3 intro point. Ensure that HS
+/** Successfully register a v2 intro point and a v3 intro point. Ensure that HS
* circuitmap is maintained properly. */
static void
test_intro_point_registration(void *arg)
@@ -636,8 +636,8 @@ test_intro_point_registration(void *arg)
done:
crypto_pk_free(legacy_auth_key);
- circuit_free(TO_CIRCUIT(intro_circ));
- circuit_free(TO_CIRCUIT(legacy_intro_circ));
+ circuit_free_(TO_CIRCUIT(intro_circ));
+ circuit_free_(TO_CIRCUIT(legacy_intro_circ));
trn_cell_establish_intro_free(establish_intro_cell);
test_circuitmap_free_all();
@@ -657,7 +657,7 @@ test_introduce1_suitable_circuit(void *arg)
circ = or_circuit_new(0, NULL);
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR);
ret = circuit_is_suitable_for_introduce1(circ);
- circuit_free(TO_CIRCUIT(circ));
+ circuit_free_(TO_CIRCUIT(circ));
tt_int_op(ret, OP_EQ, 1);
}
@@ -666,7 +666,7 @@ test_introduce1_suitable_circuit(void *arg)
circ = or_circuit_new(0, NULL);
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT);
ret = circuit_is_suitable_for_introduce1(circ);
- circuit_free(TO_CIRCUIT(circ));
+ circuit_free_(TO_CIRCUIT(circ));
tt_int_op(ret, OP_EQ, 0);
}
@@ -677,7 +677,7 @@ test_introduce1_suitable_circuit(void *arg)
/* Bogus pointer, the check is against NULL on n_chan. */
circ->base_.n_chan = (channel_t *) circ;
ret = circuit_is_suitable_for_introduce1(circ);
- circuit_free(TO_CIRCUIT(circ));
+ circuit_free_(TO_CIRCUIT(circ));
tt_int_op(ret, OP_EQ, 0);
}
@@ -688,7 +688,7 @@ test_introduce1_suitable_circuit(void *arg)
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR);
circ->already_received_introduce1 = 1;
ret = circuit_is_suitable_for_introduce1(circ);
- circuit_free(TO_CIRCUIT(circ));
+ circuit_free_(TO_CIRCUIT(circ));
tt_int_op(ret, OP_EQ, 0);
}
@@ -732,6 +732,7 @@ test_introduce1_validation(void *arg)
/* Create our decoy cell that we'll modify as we go to test the validation
* function of that parsed cell. */
cell = helper_create_introduce1_cell();
+ tt_assert(cell);
/* It should NOT be a legacy cell which will trigger a BUG(). */
memset(cell->legacy_key_id, 'a', sizeof(cell->legacy_key_id));
@@ -807,7 +808,7 @@ test_received_introduce1_handling(void *arg)
circ = helper_create_intro_circuit();
ret = hs_intro_received_introduce1(circ, buf, DIGEST_LEN - 1);
tt_int_op(ret, OP_EQ, -1);
- circuit_free(TO_CIRCUIT(circ));
+ circuit_free_(TO_CIRCUIT(circ));
}
/* We have a unit test only for the suitability of a circuit to receive an
@@ -820,7 +821,7 @@ test_received_introduce1_handling(void *arg)
memset(test, 0, sizeof(test));
ret = handle_introduce1(circ, test, sizeof(test));
tor_free(circ->p_chan);
- circuit_free(TO_CIRCUIT(circ));
+ circuit_free_(TO_CIRCUIT(circ));
tt_int_op(ret, OP_EQ, -1);
}
@@ -845,8 +846,8 @@ test_received_introduce1_handling(void *arg)
memcpy(auth_key.pubkey, cell_auth_key, ED25519_PUBKEY_LEN);
hs_circuitmap_register_intro_circ_v3_relay_side(service_circ, &auth_key);
ret = hs_intro_received_introduce1(circ, request, request_len);
- circuit_free(TO_CIRCUIT(circ));
- circuit_free(TO_CIRCUIT(service_circ));
+ circuit_free_(TO_CIRCUIT(circ));
+ circuit_free_(TO_CIRCUIT(service_circ));
tt_int_op(ret, OP_EQ, 0);
}
@@ -874,8 +875,8 @@ test_received_introduce1_handling(void *arg)
memcpy(token, legacy_key_id, sizeof(token));
hs_circuitmap_register_intro_circ_v2_relay_side(service_circ, token);
ret = hs_intro_received_introduce1(circ, request, request_len);
- circuit_free(TO_CIRCUIT(circ));
- circuit_free(TO_CIRCUIT(service_circ));
+ circuit_free_(TO_CIRCUIT(circ));
+ circuit_free_(TO_CIRCUIT(service_circ));
tt_int_op(ret, OP_EQ, 0);
}
diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c
index d040f10e0c..c1e9f3ced6 100644
--- a/src/test/test_hs_service.c
+++ b/src/test/test_hs_service.c
@@ -20,6 +20,7 @@
#define STATEFILE_PRIVATE
#define TOR_CHANNEL_INTERNAL_
#define HS_CLIENT_PRIVATE
+#define ROUTERPARSE_PRIVATE
#include "test.h"
#include "test_helpers.h"
@@ -37,6 +38,7 @@
#include "networkstatus.h"
#include "nodelist.h"
#include "relay.h"
+#include "routerparse.h"
#include "hs_common.h"
#include "hs_config.h"
@@ -182,7 +184,7 @@ test_e2e_rend_circuit_setup(void *arg)
tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_S_REND_JOINED);
done:
- circuit_free(TO_CIRCUIT(or_circ));
+ circuit_free_(TO_CIRCUIT(or_circ));
}
/* Helper: Return a newly allocated and initialized origin circuit with
@@ -194,7 +196,7 @@ helper_create_origin_circuit(int purpose, int flags)
origin_circuit_t *circ = NULL;
circ = origin_circuit_init(purpose, flags);
- tt_assert(circ);
+ tor_assert(circ);
circ->cpath = tor_malloc_zero(sizeof(crypt_path_t));
circ->cpath->magic = CRYPT_PATH_MAGIC;
circ->cpath->state = CPATH_STATE_OPEN;
@@ -206,7 +208,6 @@ helper_create_origin_circuit(int purpose, int flags)
/* Create a default HS identifier. */
circ->hs_ident = tor_malloc_zero(sizeof(hs_ident_circuit_t));
- done:
return circ;
}
@@ -219,7 +220,7 @@ helper_create_service(void)
{
/* Set a service for this circuit. */
hs_service_t *service = hs_service_new(get_options());
- tt_assert(service);
+ tor_assert(service);
service->config.version = HS_VERSION_THREE;
ed25519_secret_key_generate(&service->keys.identity_sk, 0);
ed25519_public_key_generate(&service->keys.identity_pk,
@@ -240,8 +241,8 @@ static hs_service_intro_point_t *
helper_create_service_ip(void)
{
hs_desc_link_specifier_t *ls;
- hs_service_intro_point_t *ip = service_intro_point_new(NULL, 0);
- tt_assert(ip);
+ hs_service_intro_point_t *ip = service_intro_point_new(NULL, 0, 0);
+ tor_assert(ip);
/* Add a first unused link specifier. */
ls = tor_malloc_zero(sizeof(*ls));
ls->type = LS_IPV4;
@@ -252,7 +253,6 @@ helper_create_service_ip(void)
memset(ls->u.legacy_id, 'A', sizeof(ls->u.legacy_id));
smartlist_add(ip->base.link_specifiers, ls);
- done:
return ip;
}
@@ -658,13 +658,13 @@ test_intro_circuit_opened(void *arg)
teardown_capture_of_logs();
done:
- circuit_free(TO_CIRCUIT(circ));
+ circuit_free_(TO_CIRCUIT(circ));
hs_free_all();
UNMOCK(circuit_mark_for_close_);
UNMOCK(relay_send_command_from_edge_);
}
-/** Test the operations we do on a circuit after we learn that we successfuly
+/** Test the operations we do on a circuit after we learn that we successfully
* established an intro point on it */
static void
test_intro_established(void *arg)
@@ -733,7 +733,7 @@ test_intro_established(void *arg)
done:
if (circ)
- circuit_free(TO_CIRCUIT(circ));
+ circuit_free_(TO_CIRCUIT(circ));
hs_free_all();
UNMOCK(circuit_mark_for_close_);
}
@@ -775,7 +775,7 @@ test_rdv_circuit_opened(void *arg)
tt_int_op(TO_CIRCUIT(circ)->purpose, OP_EQ, CIRCUIT_PURPOSE_S_REND_JOINED);
done:
- circuit_free(TO_CIRCUIT(circ));
+ circuit_free_(TO_CIRCUIT(circ));
hs_free_all();
UNMOCK(circuit_mark_for_close_);
UNMOCK(relay_send_command_from_edge_);
@@ -828,7 +828,7 @@ test_closing_intro_circs(void *arg)
/* Now pretend we are freeing this intro circuit. We want to see that our
* destructor is not gonna kill our intro point structure since that's the
* job of the cleanup routine. */
- circuit_free(TO_CIRCUIT(intro_circ));
+ circuit_free_(TO_CIRCUIT(intro_circ));
intro_circ = NULL;
entry = service_intro_point_find(service, &ip->auth_key_kp.pubkey);
tt_assert(entry);
@@ -860,7 +860,7 @@ test_closing_intro_circs(void *arg)
done:
if (intro_circ) {
- circuit_free(TO_CIRCUIT(intro_circ));
+ circuit_free_(TO_CIRCUIT(intro_circ));
}
/* Frees the service object. */
hs_free_all();
@@ -941,7 +941,7 @@ test_introduce2(void *arg)
or_state_free(dummy_state);
dummy_state = NULL;
if (circ)
- circuit_free(TO_CIRCUIT(circ));
+ circuit_free_(TO_CIRCUIT(circ));
hs_free_all();
UNMOCK(circuit_mark_for_close_);
}
@@ -1025,7 +1025,7 @@ test_service_event(void *arg)
done:
hs_circuitmap_remove_circuit(TO_CIRCUIT(circ));
- circuit_free(TO_CIRCUIT(circ));
+ circuit_free_(TO_CIRCUIT(circ));
hs_free_all();
UNMOCK(circuit_mark_for_close_);
}
@@ -1215,6 +1215,7 @@ test_build_update_descriptors(void *arg)
/* Ugly yes but we never free the "ri" object so this just makes things
* easier. */
ri.protocol_list = (char *) "HSDir=1-2 LinkAuth=3";
+ summarize_protover_flags(&ri.pv, ri.protocol_list, NULL);
ret = curve25519_secret_key_generate(&curve25519_secret_key, 0);
tt_int_op(ret, OP_EQ, 0);
ri.onion_curve25519_pkey =
@@ -1236,6 +1237,10 @@ test_build_update_descriptors(void *arg)
node->is_running = node->is_valid = node->is_fast = node->is_stable = 1;
}
+ /* We have to set thise, or the lack of microdescriptors for these
+ * nodes will make them unusable. */
+ get_options_mutable()->UseMicrodescriptors = 0;
+
/* We expect to pick only one intro point from the node above. */
setup_full_capture_of_logs(LOG_INFO);
update_all_descriptors(now);
@@ -1579,8 +1584,8 @@ test_rendezvous1_parsing(void *arg)
* would need an extra circuit and some more stuff but it's doable. */
done:
- circuit_free(TO_CIRCUIT(service_circ));
- circuit_free(TO_CIRCUIT(client_circ));
+ circuit_free_(TO_CIRCUIT(service_circ));
+ circuit_free_(TO_CIRCUIT(client_circ));
hs_service_free(service);
hs_free_all();
UNMOCK(relay_send_command_from_edge_);
diff --git a/src/test/test_keygen.sh b/src/test/test_keygen.sh
index 4106425cb3..455f9e7d42 100755
--- a/src/test/test_keygen.sh
+++ b/src/test/test_keygen.sh
@@ -48,6 +48,12 @@ fi
CASE8=$dflt
CASE9=$dflt
CASE10=$dflt
+ CASE11A=$dflt
+ CASE11B=$dflt
+ CASE11C=$dflt
+ CASE11D=$dflt
+ CASE11E=$dflt
+ CASE11F=$dflt
if [ $# -ge 1 ]; then
eval "CASE${1}"=1
@@ -371,6 +377,109 @@ echo "==== Case 10 ok"
fi
+# Case 11a: -passphrase-fd without --keygen
+
+if [ "$CASE11A" = 1 ]; then
+
+ME="${DATA_DIR}/case11a"
+
+mkdir -p "${ME}/keys"
+
+${TOR} --DataDirectory "${ME}" --passphrase-fd 1 > "${ME}/stdout" && die "Successfully started with passphrase-fd but no keygen?" || true
+
+grep "passphrase-fd specified without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
+
+echo "==== Case 11A ok"
+
+fi
+
+# Case 11b: --no-passphrase without --keygen
+
+if [ "$CASE11B" = 1 ]; then
+
+ME="${DATA_DIR}/case11b"
+
+mkdir -p "${ME}/keys"
+
+${TOR} --DataDirectory "${ME}" --no-passphrase > "${ME}/stdout" && die "Successfully started with no-passphrase but no keygen?" || true
+
+grep "no-passphrase specified without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
+
+echo "==== Case 11B ok"
+
+fi
+
+# Case 11c: --newpass without --keygen
+
+if [ "$CASE11C" = 1 ]; then
+
+ME="${DATA_DIR}/case11C"
+
+mkdir -p "${ME}/keys"
+
+${TOR} --DataDirectory "${ME}" --newpass > "${ME}/stdout" && die "Successfully started with newpass but no keygen?" || true
+
+grep "newpass specified without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
+
+echo "==== Case 11C ok"
+
+fi
+
+######## --master-key does not work yet, but this will test the error case
+######## when it does.
+#
+# Case 11d: --master-key without --keygen
+#
+if [ "$CASE11D" = 1 ]; then
+#
+# ME="${DATA_DIR}/case11d"
+#
+# mkdir -p "${ME}/keys"
+#
+# ${TOR} --DataDirectory "${ME}" --master-key "${ME}/foobar" > "${ME}/stdout" && die "Successfully started with master-key but no keygen?" || true
+#
+# cat "${ME}/stdout"
+#
+# grep "master-key without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
+
+ echo "==== Case 11D skipped"
+
+fi
+
+
+# Case 11E: Silly passphrase-fd
+
+if [ "$CASE11E" = 1 ]; then
+
+ME="${DATA_DIR}/case11E"
+
+mkdir -p "${ME}/keys"
+
+${TOR} --DataDirectory "${ME}" --keygen --passphrase-fd ewigeblumenkraft > "${ME}/stdout" && die "Successfully started with bogus passphrase-fd?" || true
+
+grep "Invalid --passphrase-fd value" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
+
+echo "==== Case 11E ok"
+
+fi
+
+
+# Case 11F: --no-passphrase with --passphrase-fd
+
+if [ "$CASE11F" = 1 ]; then
+
+ME="${DATA_DIR}/case11F"
+
+mkdir -p "${ME}/keys"
+
+${TOR} --DataDirectory "${ME}" --keygen --passphrase-fd 1 --no-passphrase > "${ME}/stdout" && die "Successfully started with bogus passphrase-fd combination?" || true
+
+grep "no-passphrase specified with --passphrase-fd" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments."
+
+echo "==== Case 11F ok"
+
+fi
+
# Check cert-only.
diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c
index 422d419078..6840072d76 100644
--- a/src/test/test_link_handshake.c
+++ b/src/test/test_link_handshake.c
@@ -302,8 +302,8 @@ test_link_handshake_certs_ok(void *arg)
mock_own_cert = mock_peer_cert = NULL;
memset(c1->identity_digest, 0, sizeof(c1->identity_digest));
memset(c2->identity_digest, 0, sizeof(c2->identity_digest));
- connection_free_(TO_CONN(c1));
- connection_free_(TO_CONN(c2));
+ connection_free_minimal(TO_CONN(c1));
+ connection_free_minimal(TO_CONN(c2));
tor_free(cell1);
tor_free(cell2);
certs_cell_free(cc1);
@@ -343,7 +343,7 @@ recv_certs_cleanup(const struct testcase_t *test, void *obj)
tor_free(d->cell);
certs_cell_free(d->ccell);
connection_or_clear_identity(d->c);
- connection_free_(TO_CONN(d->c));
+ connection_free_minimal(TO_CONN(d->c));
circuitmux_free(d->chan->base_.cmux);
tor_free(d->chan);
crypto_pk_free(d->key1);
@@ -930,7 +930,7 @@ test_link_handshake_send_authchallenge(void *arg)
done:
UNMOCK(connection_or_write_var_cell_to_buf);
- connection_free_(TO_CONN(c1));
+ connection_free_minimal(TO_CONN(c1));
tor_free(cell1);
tor_free(cell2);
crypto_pk_free(rsa0);
@@ -955,7 +955,7 @@ recv_authchallenge_cleanup(const struct testcase_t *test, void *obj)
if (d) {
tor_free(d->cell);
- connection_free_(TO_CONN(d->c));
+ connection_free_minimal(TO_CONN(d->c));
circuitmux_free(d->chan->base_.cmux);
tor_free(d->chan);
tor_free(d);
@@ -1158,8 +1158,8 @@ authenticate_data_cleanup(const struct testcase_t *test, void *arg)
tor_free(d->cell);
connection_or_clear_identity(d->c1);
connection_or_clear_identity(d->c2);
- connection_free_(TO_CONN(d->c1));
- connection_free_(TO_CONN(d->c2));
+ connection_free_minimal(TO_CONN(d->c1));
+ connection_free_minimal(TO_CONN(d->c2));
circuitmux_free(d->chan2->base_.cmux);
tor_free(d->chan2);
crypto_pk_free(d->key1);
diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c
index 4f0ecd778b..59b28f7580 100644
--- a/src/test/test_microdesc.c
+++ b/src/test/test_microdesc.c
@@ -69,12 +69,12 @@ test_md_cache(void *data)
time3 = time(NULL) - 15*24*60*60;
/* Possibly, turn this into a test setup/cleanup pair */
- tor_free(options->DataDirectory);
- options->DataDirectory = tor_strdup(get_fname("md_datadir_test"));
+ tor_free(options->CacheDirectory);
+ options->CacheDirectory = tor_strdup(get_fname("md_datadir_test"));
#ifdef _WIN32
- tt_int_op(0, OP_EQ, mkdir(options->DataDirectory));
+ tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory));
#else
- tt_int_op(0, OP_EQ, mkdir(options->DataDirectory, 0700));
+ tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory, 0700));
#endif
tt_assert(!strcmpstart(test_md3_noannotation, "onion-key"));
@@ -152,7 +152,7 @@ test_md_cache(void *data)
strlen(test_md3_noannotation));
tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs.new",
- options->DataDirectory);
+ options->CacheDirectory);
s = read_file_to_str(fn, RFTS_BIN, NULL);
tt_assert(s);
tt_mem_op(md1->body, OP_EQ, s + md1->off, md1->bodylen);
@@ -180,7 +180,7 @@ test_md_cache(void *data)
/* read the cache. */
tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs",
- options->DataDirectory);
+ options->CacheDirectory);
s = read_file_to_str(fn, RFTS_BIN, NULL);
tt_mem_op(md1->body, OP_EQ, s + md1->off, strlen(test_md1));
tt_mem_op(md2->body, OP_EQ, s + md2->off, strlen(test_md2));
@@ -234,7 +234,7 @@ test_md_cache(void *data)
done:
if (options)
- tor_free(options->DataDirectory);
+ tor_free(options->CacheDirectory);
microdesc_free_all();
smartlist_free(added);
@@ -266,17 +266,17 @@ test_md_cache_broken(void *data)
options = get_options_mutable();
tt_assert(options);
- tor_free(options->DataDirectory);
- options->DataDirectory = tor_strdup(get_fname("md_datadir_test2"));
+ tor_free(options->CacheDirectory);
+ options->CacheDirectory = tor_strdup(get_fname("md_datadir_test2"));
#ifdef _WIN32
- tt_int_op(0, OP_EQ, mkdir(options->DataDirectory));
+ tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory));
#else
- tt_int_op(0, OP_EQ, mkdir(options->DataDirectory, 0700));
+ tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory, 0700));
#endif
tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs",
- options->DataDirectory);
+ options->CacheDirectory);
write_str_to_file(fn, truncated_md, 1);
@@ -285,7 +285,7 @@ test_md_cache_broken(void *data)
done:
if (options)
- tor_free(options->DataDirectory);
+ tor_free(options->CacheDirectory);
tor_free(fn);
microdesc_free_all();
}
@@ -754,8 +754,8 @@ test_md_reject_cache(void *arg)
or_options_t *options = get_options_mutable();
char buf[DIGEST256_LEN];
- tor_free(options->DataDirectory);
- options->DataDirectory = tor_strdup(get_fname("md_datadir_test_rej"));
+ tor_free(options->CacheDirectory);
+ options->CacheDirectory = tor_strdup(get_fname("md_datadir_test_rej"));
mock_rgsbd_val_a = tor_malloc_zero(sizeof(routerstatus_t));
mock_rgsbd_val_b = tor_malloc_zero(sizeof(routerstatus_t));
mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t));
@@ -765,9 +765,9 @@ test_md_reject_cache(void *arg)
mock_ns_val->flavor = FLAV_MICRODESC;
#ifdef _WIN32
- tt_int_op(0, OP_EQ, mkdir(options->DataDirectory));
+ tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory));
#else
- tt_int_op(0, OP_EQ, mkdir(options->DataDirectory, 0700));
+ tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory, 0700));
#endif
MOCK(router_get_mutable_consensus_status_by_descriptor_digest,
@@ -802,7 +802,7 @@ test_md_reject_cache(void *arg)
done:
UNMOCK(networkstatus_get_latest_consensus_by_flavor);
UNMOCK(router_get_mutable_consensus_status_by_descriptor_digest);
- tor_free(options->DataDirectory);
+ tor_free(options->CacheDirectory);
microdesc_free_all();
smartlist_free(added);
SMARTLIST_FOREACH(wanted, char *, cp, tor_free(cp));
diff --git a/src/test/test_oom.c b/src/test/test_oom.c
index cf28690a28..c172fe60c7 100644
--- a/src/test/test_oom.c
+++ b/src/test/test_oom.c
@@ -202,7 +202,7 @@ test_oom_streambuf(void *arg)
{
or_options_t *options = get_options_mutable();
circuit_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *c4 = NULL, *c5 = NULL;
- uint32_t tvms;
+ uint32_t tvts;
int i;
smartlist_t *edgeconns = smartlist_new();
const uint64_t start_ns = 1389641159 * (uint64_t)1000000000;
@@ -270,22 +270,28 @@ test_oom_streambuf(void *arg)
now_ns -= now_ns % 1000000000;
now_ns += 1000000000;
monotime_coarse_set_mock_time_nsec(now_ns);
- tvms = (uint32_t) monotime_coarse_absolute_msec();
+ tvts = monotime_coarse_get_stamp();
- tt_int_op(circuit_max_queued_cell_age(c1, tvms), OP_EQ, 500);
- tt_int_op(circuit_max_queued_cell_age(c2, tvms), OP_EQ, 490);
- tt_int_op(circuit_max_queued_cell_age(c3, tvms), OP_EQ, 480);
- tt_int_op(circuit_max_queued_cell_age(c4, tvms), OP_EQ, 0);
+#define ts_is_approx(ts, val) do { \
+ uint32_t x_ = (uint32_t) monotime_coarse_stamp_units_to_approx_msec(ts); \
+ tt_int_op(x_, OP_GE, val - 5); \
+ tt_int_op(x_, OP_LE, val + 5); \
+ } while (0)
- tt_int_op(circuit_max_queued_data_age(c1, tvms), OP_EQ, 390);
- tt_int_op(circuit_max_queued_data_age(c2, tvms), OP_EQ, 380);
- tt_int_op(circuit_max_queued_data_age(c3, tvms), OP_EQ, 0);
- tt_int_op(circuit_max_queued_data_age(c4, tvms), OP_EQ, 370);
+ ts_is_approx(circuit_max_queued_cell_age(c1, tvts), 500);
+ ts_is_approx(circuit_max_queued_cell_age(c2, tvts), 490);
+ ts_is_approx(circuit_max_queued_cell_age(c3, tvts), 480);
+ ts_is_approx(circuit_max_queued_cell_age(c4, tvts), 0);
- tt_int_op(circuit_max_queued_item_age(c1, tvms), OP_EQ, 500);
- tt_int_op(circuit_max_queued_item_age(c2, tvms), OP_EQ, 490);
- tt_int_op(circuit_max_queued_item_age(c3, tvms), OP_EQ, 480);
- tt_int_op(circuit_max_queued_item_age(c4, tvms), OP_EQ, 370);
+ ts_is_approx(circuit_max_queued_data_age(c1, tvts), 390);
+ ts_is_approx(circuit_max_queued_data_age(c2, tvts), 380);
+ ts_is_approx(circuit_max_queued_data_age(c3, tvts), 0);
+ ts_is_approx(circuit_max_queued_data_age(c4, tvts), 370);
+
+ ts_is_approx(circuit_max_queued_item_age(c1, tvts), 500);
+ ts_is_approx(circuit_max_queued_item_age(c2, tvts), 490);
+ ts_is_approx(circuit_max_queued_item_age(c3, tvts), 480);
+ ts_is_approx(circuit_max_queued_item_age(c4, tvts), 370);
tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
packed_cell_mem_cost() * 80);
@@ -301,7 +307,7 @@ test_oom_streambuf(void *arg)
smartlist_add(edgeconns, ec);
}
tt_int_op(buf_get_total_allocation(), OP_EQ, 4096*17*2);
- tt_int_op(circuit_max_queued_item_age(c4, tvms), OP_EQ, 1000);
+ ts_is_approx(circuit_max_queued_item_age(c4, tvts), 1000);
tt_int_op(cell_queues_check_size(), OP_EQ, 0);
@@ -335,7 +341,7 @@ test_oom_streambuf(void *arg)
circuit_free(c5);
SMARTLIST_FOREACH(edgeconns, edge_connection_t *, ec,
- connection_free_(TO_CONN(ec)));
+ connection_free_minimal(TO_CONN(ec)));
smartlist_free(edgeconns);
UNMOCK(circuit_mark_for_close_);
diff --git a/src/test/test_options.c b/src/test/test_options.c
index 62732cabf7..eaf5034397 100644
--- a/src/test/test_options.c
+++ b/src/test/test_options.c
@@ -377,17 +377,11 @@ fixed_get_uname(void)
}
#define TEST_OPTIONS_OLD_VALUES "TestingV3AuthInitialVotingInterval 1800\n" \
- "ClientBootstrapConsensusMaxDownloadTries 7\n" \
- "ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries 4\n" \
"ClientBootstrapConsensusMaxInProgressTries 3\n" \
"TestingV3AuthInitialVoteDelay 300\n" \
"TestingV3AuthInitialDistDelay 300\n" \
"TestingClientMaxIntervalWithoutRequest 600\n" \
"TestingDirConnectionMaxStall 600\n" \
- "TestingConsensusMaxDownloadTries 8\n" \
- "TestingDescriptorMaxDownloadTries 8\n" \
- "TestingMicrodescMaxDownloadTries 8\n" \
- "TestingCertMaxDownloadTries 8\n"
#define TEST_OPTIONS_DEFAULT_VALUES TEST_OPTIONS_OLD_VALUES \
"MaxClientCircuitsPending 1\n" \
@@ -906,7 +900,7 @@ test_options_validate__authdir(void *ignored)
"Address 100.200.10.1\n"
"BridgeAuthoritativeDir 1\n"
"ContactInfo hello@hello.com\n"
- "V3BandwidthsFile non-existant-file\n");
+ "V3BandwidthsFile non-existent-file\n");
mock_clean_saved_logs();
options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_str_op(msg, OP_EQ,
@@ -918,7 +912,7 @@ test_options_validate__authdir(void *ignored)
"Address 100.200.10.1\n"
"BridgeAuthoritativeDir 1\n"
"ContactInfo hello@hello.com\n"
- "V3BandwidthsFile non-existant-file\n");
+ "V3BandwidthsFile non-existent-file\n");
mock_clean_saved_logs();
options_validate(NULL, tdata->opt, tdata->def_opt, 0, &msg);
tt_str_op(msg, OP_EQ,
@@ -930,7 +924,7 @@ test_options_validate__authdir(void *ignored)
"Address 100.200.10.1\n"
"BridgeAuthoritativeDir 1\n"
"ContactInfo hello@hello.com\n"
- "GuardfractionFile non-existant-file\n");
+ "GuardfractionFile non-existent-file\n");
mock_clean_saved_logs();
options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_str_op(msg, OP_EQ,
@@ -942,7 +936,7 @@ test_options_validate__authdir(void *ignored)
"Address 100.200.10.1\n"
"BridgeAuthoritativeDir 1\n"
"ContactInfo hello@hello.com\n"
- "GuardfractionFile non-existant-file\n");
+ "GuardfractionFile non-existent-file\n");
mock_clean_saved_logs();
options_validate(NULL, tdata->opt, tdata->def_opt, 0, &msg);
tt_str_op(msg, OP_EQ,
@@ -1112,7 +1106,7 @@ test_options_validate__transproxy(void *ignored)
// Test unknown trans proxy
free_options_test_data(tdata);
- tdata = get_options_test_data("TransProxyType non-existant\n");
+ tdata = get_options_test_data("TransProxyType non-existent\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
tt_str_op(msg, OP_EQ, "Unrecognized value for TransProxyType");
@@ -2081,10 +2075,6 @@ test_options_validate__testing(void *ignored)
ENSURE_DEFAULT(TestingBridgeBootstrapDownloadSchedule, 3000);
ENSURE_DEFAULT(TestingClientMaxIntervalWithoutRequest, 3000);
ENSURE_DEFAULT(TestingDirConnectionMaxStall, 3000);
- ENSURE_DEFAULT(TestingConsensusMaxDownloadTries, 3000);
- ENSURE_DEFAULT(TestingDescriptorMaxDownloadTries, 3000);
- ENSURE_DEFAULT(TestingMicrodescMaxDownloadTries, 3000);
- ENSURE_DEFAULT(TestingCertMaxDownloadTries, 3000);
ENSURE_DEFAULT(TestingAuthKeyLifetime, 3000);
ENSURE_DEFAULT(TestingLinkCertLifetime, 3000);
ENSURE_DEFAULT(TestingSigningKeySlop, 3000);
@@ -4069,15 +4059,6 @@ test_options_validate__testing_options(void *ignored)
"is way too low.");
TEST_TESTING_OPTION(TestingDirConnectionMaxStall, 1, 3601,
"is way too low.");
- // TODO: I think this points to a bug/regression in options_validate
- TEST_TESTING_OPTION(TestingConsensusMaxDownloadTries, 1, 801,
- "must be greater than 2.");
- TEST_TESTING_OPTION(TestingDescriptorMaxDownloadTries, 1, 801,
- "must be greater than 1.");
- TEST_TESTING_OPTION(TestingMicrodescMaxDownloadTries, 1, 801,
- "must be greater than 1.");
- TEST_TESTING_OPTION(TestingCertMaxDownloadTries, 1, 801,
- "must be greater than 1.");
free_options_test_data(tdata);
tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
diff --git a/src/test/test_policy.c b/src/test/test_policy.c
index 83dca2d431..f8aa8ac40b 100644
--- a/src/test/test_policy.c
+++ b/src/test/test_policy.c
@@ -1318,7 +1318,7 @@ mock_get_interface_address6_list(int severity,
return clone_list;
done:
- free_interface_address6_list(clone_list);
+ interface_address6_list_free(clone_list);
return NULL;
}
@@ -1393,11 +1393,11 @@ test_policies_reject_interface_address(void *arg)
done:
addr_policy_list_free(policy);
- free_interface_address6_list(public_ipv4_addrs);
- free_interface_address6_list(public_ipv6_addrs);
+ interface_address6_list_free(public_ipv4_addrs);
+ interface_address6_list_free(public_ipv6_addrs);
UNMOCK(get_interface_address6_list);
- /* we don't use free_interface_address6_list on these lists because their
+ /* we don't use interface_address6_list_free on these lists because their
* address pointers are stack-based */
smartlist_free(mock_ipv4_addrs);
smartlist_free(mock_ipv6_addrs);
diff --git a/src/test/test_protover.c b/src/test/test_protover.c
index 9b94044b91..0948cd5640 100644
--- a/src/test/test_protover.c
+++ b/src/test/test_protover.c
@@ -8,10 +8,20 @@
#include "protover.h"
+#include "or.h"
+#include "connection_or.h"
+
static void
test_protover_parse(void *arg)
{
(void) arg;
+#ifdef HAVE_RUST
+ /** This test is disabled on rust builds, because it only exists to test
+ * internal C functions. */
+ tt_skip();
+ done:
+ ;
+#else
char *re_encoded = NULL;
const char *orig = "Foo=1,3 Bar=3 Baz= Quux=9-12,14,15-16,900";
@@ -78,12 +88,18 @@ test_protover_parse(void *arg)
SMARTLIST_FOREACH(elts, proto_entry_t *, ent, proto_entry_free(ent));
smartlist_free(elts);
tor_free(re_encoded);
+#endif
}
static void
test_protover_parse_fail(void *arg)
{
(void)arg;
+#ifdef HAVE_RUST
+ /** This test is disabled on rust builds, because it only exists to test
+ * internal C functions. */
+ tt_skip();
+#else
smartlist_t *elts;
/* random junk */
@@ -110,6 +126,13 @@ test_protover_parse_fail(void *arg)
elts = parse_protocol_list("Link=1,9-8,3");
tt_ptr_op(elts, OP_EQ, NULL);
+ /* Protocol name too long */
+ elts = parse_protocol_list("DoSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
+ tt_ptr_op(elts, OP_EQ, NULL);
+
+#endif
done:
;
}
@@ -203,6 +226,15 @@ test_protover_vote(void *arg)
tt_str_op(result, OP_EQ, "");
tor_free(result);
+ /* Protocol name too long */
+ smartlist_clear(lst);
+ smartlist_add(lst, (void*) "DoSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
+ result = protover_compute_vote(lst, 1);
+ tt_str_op(result, OP_EQ, "");
+ tor_free(result);
+
done:
tor_free(result);
smartlist_free(lst);
@@ -238,11 +270,26 @@ test_protover_all_supported(void *arg)
tt_assert(! protover_all_supported("Link=3-4 Wombat=9", &msg));
tt_str_op(msg, OP_EQ, "Wombat=9");
tor_free(msg);
+
+ /* Mix of things we support and don't support within a single protocol
+ * which we do support */
tt_assert(! protover_all_supported("Link=3-999", &msg));
- tt_str_op(msg, OP_EQ, "Link=3-999");
+ tt_str_op(msg, OP_EQ, "Link=6-999");
+ tor_free(msg);
+ tt_assert(! protover_all_supported("Link=1-3,345-666", &msg));
+ tt_str_op(msg, OP_EQ, "Link=345-666");
+ tor_free(msg);
+ tt_assert(! protover_all_supported("Link=1-3,5-12", &msg));
+ tt_str_op(msg, OP_EQ, "Link=6-12");
+ tor_free(msg);
+
+ /* Mix of protocols we do support and some we don't, where the protocols
+ * we do support have some versions we don't support. */
+ tt_assert(! protover_all_supported("Link=1-3,5-12 Quokka=9000-9001", &msg));
+ tt_str_op(msg, OP_EQ, "Link=6-12 Quokka=9000-9001");
tor_free(msg);
- /* CPU/RAM DoS loop: Rust only */
+ /* We shouldn't be able to DoS ourselves parsing a large range. */
tt_assert(! protover_all_supported("Sleen=0-2147483648", &msg));
tt_str_op(msg, OP_EQ, "Sleen=0-2147483648");
tor_free(msg);
@@ -252,21 +299,31 @@ test_protover_all_supported(void *arg)
tt_str_op(msg, OP_EQ, "Sleen=0-4294967294");
tor_free(msg);
- /* If we get an unparseable list, we say "yes, that's supported." */
-#ifndef HAVE_RUST
- // XXXX let's make this section unconditional: rust should behave the
- // XXXX same as C here!
+ /* If we get a (barely) valid (but unsupported list, we say "yes, that's
+ * supported." */
+ tt_assert(protover_all_supported("Fribble=", &msg));
+ tt_ptr_op(msg, OP_EQ, NULL);
+
+ /* If we get a completely unparseable list, protover_all_supported should
+ * hit a fatal assertion for BUG(entries == NULL). */
tor_capture_bugs_(1);
tt_assert(protover_all_supported("Fribble", &msg));
- tt_ptr_op(msg, OP_EQ, NULL);
tor_end_capture_bugs_();
- /* This case is forbidden. Since it came from a protover_all_supported,
- * it can trigger a bug message. */
+ /* If we get a completely unparseable list, protover_all_supported should
+ * hit a fatal assertion for BUG(entries == NULL). */
tor_capture_bugs_(1);
tt_assert(protover_all_supported("Sleen=0-4294967295", &msg));
- tt_ptr_op(msg, OP_EQ, NULL);
- tor_free(msg);
+ tor_end_capture_bugs_();
+
+ /* Protocol name too long */
+#ifndef HAVE_RUST // XXXXXX ?????
+ tor_capture_bugs_(1);
+ tt_assert(protover_all_supported(
+ "DoSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaa=1-65536", &msg));
tor_end_capture_bugs_();
#endif
@@ -276,6 +333,209 @@ test_protover_all_supported(void *arg)
}
static void
+test_protover_list_supports_protocol_returns_true(void *arg)
+{
+ (void)arg;
+
+ const char *protocols = "Link=1";
+ int is_supported = protocol_list_supports_protocol(protocols, PRT_LINK, 1);
+ tt_int_op(is_supported, OP_EQ, 1);
+
+ done:
+ ;
+}
+
+static void
+test_protover_list_supports_protocol_for_unsupported_returns_false(void *arg)
+{
+ (void)arg;
+
+ const char *protocols = "Link=1";
+ int is_supported = protocol_list_supports_protocol(protocols, PRT_LINK, 10);
+ tt_int_op(is_supported, OP_EQ, 0);
+
+ done:
+ ;
+}
+
+static void
+test_protover_supports_version(void *arg)
+{
+ (void)arg;
+
+ tt_assert(protocol_list_supports_protocol("Link=3-6", PRT_LINK, 3));
+ tt_assert(protocol_list_supports_protocol("Link=3-6", PRT_LINK, 6));
+ tt_assert(!protocol_list_supports_protocol("Link=3-6", PRT_LINK, 7));
+ tt_assert(!protocol_list_supports_protocol("Link=3-6", PRT_LINKAUTH, 3));
+
+ tt_assert(!protocol_list_supports_protocol("Link=4-6 LinkAuth=3",
+ PRT_LINKAUTH, 2));
+ tt_assert(protocol_list_supports_protocol("Link=4-6 LinkAuth=3",
+ PRT_LINKAUTH, 3));
+ tt_assert(!protocol_list_supports_protocol("Link=4-6 LinkAuth=3",
+ PRT_LINKAUTH, 4));
+ tt_assert(!protocol_list_supports_protocol_or_later("Link=4-6 LinkAuth=3",
+ PRT_LINKAUTH, 4));
+ tt_assert(protocol_list_supports_protocol_or_later("Link=4-6 LinkAuth=3",
+ PRT_LINKAUTH, 3));
+ tt_assert(protocol_list_supports_protocol_or_later("Link=4-6 LinkAuth=3",
+ PRT_LINKAUTH, 2));
+
+ tt_assert(!protocol_list_supports_protocol_or_later("Link=4-6 LinkAuth=3",
+ PRT_DESC, 2));
+ done:
+ ;
+}
+
+/* This could be MAX_PROTOCOLS_TO_EXPAND, but that's not exposed by protover */
+#define MAX_PROTOCOLS_TO_TEST 1024
+
+/* LinkAuth and Relay protocol versions.
+ * Hard-coded here, because they are not in the code, or not exposed in the
+ * headers. */
+#define PROTOVER_LINKAUTH_V1 1
+#define PROTOVER_LINKAUTH_V3 3
+
+#define PROTOVER_RELAY_V1 1
+#define PROTOVER_RELAY_V2 2
+
+/* Highest supported HSv2 introduce protocol version.
+ * Hard-coded here, because it does not appear anywhere in the code.
+ * It's not clear if we actually support version 2, see #25068. */
+#define PROTOVER_HSINTRO_V2 3
+
+/* HSv2 Rend and HSDir protocol versions.
+ * Hard-coded here, because they do not appear anywhere in the code. */
+#define PROTOVER_HS_RENDEZVOUS_POINT_V2 1
+#define PROTOVER_HSDIR_V2 1
+
+/* DirCache, Desc, Microdesc, and Cons protocol versions.
+ * Hard-coded here, because they do not appear anywhere in the code. */
+#define PROTOVER_DIRCACHE_V1 1
+#define PROTOVER_DIRCACHE_V2 2
+
+#define PROTOVER_DESC_V1 1
+#define PROTOVER_DESC_V2 2
+
+#define PROTOVER_MICRODESC_V1 1
+#define PROTOVER_MICRODESC_V2 2
+
+#define PROTOVER_CONS_V1 1
+#define PROTOVER_CONS_V2 2
+
+/* Make sure we haven't forgotten any supported protocols */
+static void
+test_protover_supported_protocols(void *arg)
+{
+ (void)arg;
+
+ const char *supported_protocols = protover_get_supported_protocols();
+
+ /* Test for new Link in the code, that hasn't been added to supported
+ * protocols */
+ tt_assert(protocol_list_supports_protocol(supported_protocols,
+ PRT_LINK,
+ MAX_LINK_PROTO));
+ for (uint16_t i = 0; i < MAX_PROTOCOLS_TO_TEST; i++) {
+ if (is_or_protocol_version_known(i)) {
+ tt_assert(protocol_list_supports_protocol(supported_protocols,
+ PRT_LINK,
+ i));
+ }
+ }
+
+ /* Legacy LinkAuth does not appear anywhere in the code. */
+ tt_assert(protocol_list_supports_protocol(supported_protocols,
+ PRT_LINKAUTH,
+ PROTOVER_LINKAUTH_V1));
+ /* Latest LinkAuth is not exposed in the headers. */
+ tt_assert(protocol_list_supports_protocol(supported_protocols,
+ PRT_LINKAUTH,
+ PROTOVER_LINKAUTH_V3));
+ /* Is there any way to test for new LinkAuth? */
+
+ /* Relay protovers do not appear anywhere in the code. */
+ tt_assert(protocol_list_supports_protocol(supported_protocols,
+ PRT_RELAY,
+ PROTOVER_RELAY_V1));
+ tt_assert(protocol_list_supports_protocol(supported_protocols,
+ PRT_RELAY,
+ PROTOVER_RELAY_V2));
+ /* Is there any way to test for new Relay? */
+
+ /* We could test legacy HSIntro by calling rend_service_update_descriptor(),
+ * and checking the protocols field. But that's unlikely to change, so
+ * we just use a hard-coded value. */
+ tt_assert(protocol_list_supports_protocol(supported_protocols,
+ PRT_HSINTRO,
+ PROTOVER_HSINTRO_V2));
+ /* Test for HSv3 HSIntro */
+ tt_assert(protocol_list_supports_protocol(supported_protocols,
+ PRT_HSINTRO,
+ PROTOVER_HS_INTRO_V3));
+ /* Is there any way to test for new HSIntro? */
+
+ /* Legacy HSRend does not appear anywhere in the code. */
+ tt_assert(protocol_list_supports_protocol(supported_protocols,
+ PRT_HSREND,
+ PROTOVER_HS_RENDEZVOUS_POINT_V2));
+ /* Test for HSv3 HSRend */
+ tt_assert(protocol_list_supports_protocol(supported_protocols,
+ PRT_HSREND,
+ PROTOVER_HS_RENDEZVOUS_POINT_V3));
+ /* Is there any way to test for new HSRend? */
+
+ /* Legacy HSDir does not appear anywhere in the code. */
+ tt_assert(protocol_list_supports_protocol(supported_protocols,
+ PRT_HSDIR,
+ PROTOVER_HSDIR_V2));
+ /* Test for HSv3 HSDir */
+ tt_assert(protocol_list_supports_protocol(supported_protocols,
+ PRT_HSDIR,
+ PROTOVER_HSDIR_V3));
+ /* Is there any way to test for new HSDir? */
+
+ /* No DirCache versions appear anywhere in the code. */
+ tt_assert(protocol_list_supports_protocol(supported_protocols,
+ PRT_DIRCACHE,
+ PROTOVER_DIRCACHE_V1));
+ tt_assert(protocol_list_supports_protocol(supported_protocols,
+ PRT_DIRCACHE,
+ PROTOVER_DIRCACHE_V2));
+ /* Is there any way to test for new DirCache? */
+
+ /* No Desc versions appear anywhere in the code. */
+ tt_assert(protocol_list_supports_protocol(supported_protocols,
+ PRT_DESC,
+ PROTOVER_DESC_V1));
+ tt_assert(protocol_list_supports_protocol(supported_protocols,
+ PRT_DESC,
+ PROTOVER_DESC_V2));
+ /* Is there any way to test for new Desc? */
+
+ /* No Microdesc versions appear anywhere in the code. */
+ tt_assert(protocol_list_supports_protocol(supported_protocols,
+ PRT_MICRODESC,
+ PROTOVER_MICRODESC_V1));
+ tt_assert(protocol_list_supports_protocol(supported_protocols,
+ PRT_MICRODESC,
+ PROTOVER_MICRODESC_V2));
+ /* Is there any way to test for new Microdesc? */
+
+ /* No Cons versions appear anywhere in the code. */
+ tt_assert(protocol_list_supports_protocol(supported_protocols,
+ PRT_CONS,
+ PROTOVER_CONS_V1));
+ tt_assert(protocol_list_supports_protocol(supported_protocols,
+ PRT_CONS,
+ PROTOVER_CONS_V2));
+ /* Is there any way to test for new Cons? */
+
+ done:
+ ;
+}
+
+static void
test_protover_vote_roundtrip(void *args)
{
(void) args;
@@ -312,8 +572,6 @@ test_protover_vote_roundtrip(void *args)
{ "Link=1,9-8,3", NULL },
{ "Faux=-0", NULL },
{ "Faux=0--0", NULL },
- // "These fail at the splitting stage in Rust, but the number parsing
- // stage in C."
{ "Faux=-1", NULL },
{ "Faux=-1-3", NULL },
{ "Faux=1--1", NULL },
@@ -322,9 +580,9 @@ test_protover_vote_roundtrip(void *args)
/* Large range */
{ "Sleen=1-501", "Sleen=1-501" },
{ "Sleen=1-65537", NULL },
- /* CPU/RAM DoS Loop: Rust only. */
+ /* Both C/Rust implementations should be able to handle this mild DoS. */
{ "Sleen=0-2147483648", NULL },
- /* Rust seems to experience an internal error here. */
+ /* Rust tests are built in debug mode, so ints are bounds-checked. */
{ "Sleen=0-4294967295", NULL },
};
unsigned u;
@@ -360,6 +618,10 @@ struct testcase_t protover_tests[] = {
PV_TEST(parse_fail, 0),
PV_TEST(vote, 0),
PV_TEST(all_supported, 0),
+ PV_TEST(list_supports_protocol_for_unsupported_returns_false, 0),
+ PV_TEST(list_supports_protocol_returns_true, 0),
+ PV_TEST(supports_version, 0),
+ PV_TEST(supported_protocols, 0),
PV_TEST(vote_roundtrip, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_relay.c b/src/test/test_relay.c
index e3489627a0..73c0ed5586 100644
--- a/src/test/test_relay.c
+++ b/src/test/test_relay.c
@@ -67,10 +67,6 @@ test_relay_append_cell_to_circuit_queue(void *arg)
pchan = new_fake_channel();
tt_assert(pchan);
- /* We'll need chans with working cmuxes */
- nchan->cmux = circuitmux_alloc();
- pchan->cmux = circuitmux_alloc();
-
/* Make a fake orcirc */
orcirc = new_fake_orcirc(nchan, pchan);
tt_assert(orcirc);
diff --git a/src/test/test_rendcache.c b/src/test/test_rendcache.c
index 9354dd0480..9f6cfc4a22 100644
--- a/src/test/test_rendcache.c
+++ b/src/test/test_rendcache.c
@@ -834,7 +834,7 @@ test_rend_cache_failure_entry_free(void *data)
(void)data;
// Test that it can deal with a NULL argument
- rend_cache_failure_entry_free(NULL);
+ rend_cache_failure_entry_free_(NULL);
/* done: */
/* (void)0; */
@@ -963,7 +963,7 @@ test_rend_cache_entry_free(void *data)
rend_cache_entry_t *e;
// Handles NULL correctly
- rend_cache_entry_free(NULL);
+ rend_cache_entry_free_(NULL);
// Handles NULL descriptor correctly
e = tor_malloc_zero(sizeof(rend_cache_entry_t));
@@ -1135,7 +1135,7 @@ test_rend_cache_failure_intro_entry_free(void *data)
rend_cache_failure_intro_t *entry;
// Handles a null argument
- rend_cache_failure_intro_entry_free(NULL);
+ rend_cache_failure_intro_entry_free_(NULL);
// Handles a non-null argument
entry = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
@@ -1148,7 +1148,7 @@ test_rend_cache_failure_purge(void *data)
(void)data;
// Handles a null failure cache
- strmap_free(rend_cache_failure, rend_cache_failure_entry_free_);
+ strmap_free(rend_cache_failure, rend_cache_failure_entry_free_void);
rend_cache_failure = NULL;
rend_cache_failure_purge();
diff --git a/src/test/test_replay.c b/src/test/test_replay.c
index c379cafa7c..d8dcc7370c 100644
--- a/src/test/test_replay.c
+++ b/src/test/test_replay.c
@@ -74,7 +74,7 @@ static void
test_replaycache_free_null(void *arg)
{
(void)arg;
- replaycache_free(NULL);
+ replaycache_free_(NULL);
/* Assert that we're here without horrible death */
tt_assert(1);
diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c
index d8b72651a0..e4abcdb92d 100644
--- a/src/test/test_routerkeys.c
+++ b/src/test/test_routerkeys.c
@@ -421,6 +421,7 @@ test_routerkeys_ed_keys_init_all(void *arg)
{
(void)arg;
char *dir = tor_strdup(get_fname("test_ed_keys_init_all"));
+ char *keydir = tor_strdup(get_fname("test_ed_keys_init_all/KEYS"));
or_options_t *options = tor_malloc_zero(sizeof(or_options_t));
time_t now = time(NULL);
ed25519_public_key_t id;
@@ -445,13 +446,14 @@ test_routerkeys_ed_keys_init_all(void *arg)
#ifdef _WIN32
mkdir(dir);
- mkdir(get_fname("test_ed_keys_init_all/keys"));
+ mkdir(keydir);
#else
mkdir(dir, 0700);
- mkdir(get_fname("test_ed_keys_init_all/keys"), 0700);
+ mkdir(keydir, 0700);
#endif /* defined(_WIN32) */
options->DataDirectory = dir;
+ options->KeyDirectory = keydir;
tt_int_op(1, OP_EQ, load_ed_keys(options, now));
tt_int_op(0, OP_EQ, generate_ed_link_cert(options, now, 0));
@@ -521,7 +523,7 @@ test_routerkeys_ed_keys_init_all(void *arg)
/* Demonstrate that we can start up with no secret identity key */
routerkeys_free_all();
- unlink(get_fname("test_ed_keys_init_all/keys/"
+ unlink(get_fname("test_ed_keys_init_all/KEYS/"
"ed25519_master_id_secret_key"));
tt_int_op(1, OP_EQ, load_ed_keys(options, now));
tt_int_op(0, OP_EQ, generate_ed_link_cert(options, now, 0));
@@ -542,6 +544,7 @@ test_routerkeys_ed_keys_init_all(void *arg)
done:
tor_free(dir);
+ tor_free(keydir);
tor_free(options);
tor_cert_free(link_cert);
routerkeys_free_all();
diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c
index 3b0e943ce5..c19d66ef9d 100644
--- a/src/test/test_routerlist.c
+++ b/src/test/test_routerlist.c
@@ -5,7 +5,11 @@
#include <math.h>
#include <time.h>
+#define CONNECTION_PRIVATE
+#define DIRECTORY_PRIVATE
#define DIRVOTE_PRIVATE
+#define ENTRYNODES_PRIVATE
+#define HIBERNATE_PRIVATE
#define NETWORKSTATUS_PRIVATE
#define ROUTERLIST_PRIVATE
#define TOR_UNIT_TESTING
@@ -13,19 +17,24 @@
#include "config.h"
#include "connection.h"
#include "container.h"
+#include "control.h"
#include "directory.h"
#include "dirvote.h"
#include "entrynodes.h"
+#include "hibernate.h"
#include "microdesc.h"
#include "networkstatus.h"
#include "nodelist.h"
#include "policies.h"
#include "router.h"
#include "routerlist.h"
+#include "routerset.h"
#include "routerparse.h"
#include "shared_random.h"
+#include "statefile.h"
#include "test.h"
#include "test_dir_common.h"
+#include "log_test_helpers.h"
void construct_consensus(char **consensus_text_md);
@@ -411,6 +420,111 @@ test_router_pick_directory_server_impl(void *arg)
networkstatus_vote_free(con_md);
}
+static or_state_t *dummy_state = NULL;
+static or_state_t *
+get_or_state_replacement(void)
+{
+ return dummy_state;
+}
+
+static void
+mock_directory_initiate_request(directory_request_t *req)
+{
+ (void)req;
+ return;
+}
+
+static circuit_guard_state_t *
+mock_circuit_guard_state_new(entry_guard_t *guard, unsigned state,
+ entry_guard_restriction_t *rst)
+{
+ (void) guard;
+ (void) state;
+ (void) rst;
+ return NULL;
+}
+
+/** Test that we will use our directory guards to fetch mds even if we don't
+ * have any dirinfo (tests bug #23862). */
+static void
+test_directory_guard_fetch_with_no_dirinfo(void *arg)
+{
+ int retval;
+ char *consensus_text_md = NULL;
+ or_options_t *options = get_options_mutable();
+
+ (void) arg;
+
+ hibernate_set_state_for_testing_(HIBERNATE_STATE_LIVE);
+
+ /* Initialize the SRV subsystem */
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+ mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ sr_init(0);
+ UNMOCK(get_my_v3_authority_cert);
+
+ /* Initialize the entry node configuration from the ticket */
+ options->UseEntryGuards = 1;
+ options->StrictNodes = 1;
+ get_options_mutable()->EntryNodes = routerset_new();
+ routerset_parse(get_options_mutable()->EntryNodes,
+ "2121212121212121212121212121212121212121", "foo");
+
+ /* Mock some functions */
+ dummy_state = tor_malloc_zero(sizeof(or_state_t));
+ MOCK(get_or_state, get_or_state_replacement);
+ MOCK(directory_initiate_request, mock_directory_initiate_request);
+ /* we need to mock this one to avoid memleaks */
+ MOCK(circuit_guard_state_new, mock_circuit_guard_state_new);
+
+ /* Call guards_update_all() to simulate loading our state file (see
+ * entry_guards_load_guards_from_state() and ticket #23989). */
+ guards_update_all();
+
+ /* Test logic: Simulate the arrival of a new consensus when we have no
+ * dirinfo at all. Tor will need to fetch the mds from the consensus. Make
+ * sure that Tor will use the specified entry guard instead of relying on the
+ * fallback directories. */
+
+ /* Fixup the dirconn that will deliver the consensus */
+ dir_connection_t *conn = dir_connection_new(AF_INET);
+ tor_addr_from_ipv4h(&conn->base_.addr, 0x7f000001);
+ conn->base_.port = 8800;
+ TO_CONN(conn)->address = tor_strdup("127.0.0.1");
+ conn->base_.purpose = DIR_PURPOSE_FETCH_CONSENSUS;
+ conn->requested_resource = tor_strdup("ns");
+
+ /* Construct a consensus */
+ construct_consensus(&consensus_text_md);
+ tt_assert(consensus_text_md);
+
+ /* Place the consensus in the dirconn */
+ response_handler_args_t args;
+ memset(&args, 0, sizeof(response_handler_args_t));
+ args.status_code = 200;
+ args.body = consensus_text_md;
+ args.body_len = strlen(consensus_text_md);
+
+ /* Update approx time so that the consensus is considered live */
+ update_approx_time(time(NULL)+1010);
+
+ setup_capture_of_logs(LOG_DEBUG);
+
+ /* Now handle the consensus */
+ retval = handle_response_fetch_consensus(conn, &args);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Make sure that our primary guard was chosen */
+ expect_log_msg_containing("Selected primary guard router3");
+
+ done:
+ tor_free(consensus_text_md);
+ tor_free(dummy_state);
+ connection_free_minimal(TO_CONN(conn));
+ entry_guards_free_all();
+ teardown_capture_of_logs();
+}
+
static connection_t *mocked_connection = NULL;
/* Mock connection_get_by_type_addr_port_purpose by returning
@@ -494,6 +608,8 @@ struct testcase_t routerlist_tests[] = {
NODE(launch_descriptor_downloads, 0),
NODE(router_is_already_dir_fetching, TT_FORK),
ROUTER(pick_directory_server_impl, TT_FORK),
+ { "directory_guard_fetch_with_no_dirinfo",
+ test_directory_guard_fetch_with_no_dirinfo, TT_FORK, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_routerset.c b/src/test/test_routerset.c
index c9c69911da..c541324674 100644
--- a/src/test/test_routerset.c
+++ b/src/test/test_routerset.c
@@ -1,3 +1,6 @@
+/* Copyright (c) 2014-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
#define ROUTERSET_PRIVATE
#include "or.h"
@@ -696,7 +699,7 @@ NS(test_main)(void *arg)
static void
NS(test_main)(void *arg)
{
- const routerset_t *set;
+ routerset_t *set;
int needs_geoip;
(void)arg;
@@ -706,14 +709,14 @@ NS(test_main)(void *arg)
set = routerset_new();
needs_geoip = routerset_needs_geoip(set);
- routerset_free((routerset_t *)set);
+ routerset_free(set);
tt_int_op(needs_geoip, OP_EQ, 0);
set = NULL;
set = routerset_new();
smartlist_add(set->country_names, tor_strndup("xx", 2));
needs_geoip = routerset_needs_geoip(set);
- routerset_free((routerset_t *)set);
+ routerset_free(set);
set = NULL;
tt_int_op(needs_geoip, OP_NE, 0);
@@ -1944,7 +1947,7 @@ NS(test_main)(void *arg)
done:
tor_free(s);
- routerset_free((routerset_t *)set);
+ routerset_free(set);
}
#undef NS_SUBMODULE
@@ -2081,28 +2084,28 @@ NS(test_main)(void *arg)
* Structural test for routerset_free, where the routerset is NULL.
*/
-NS_DECL(void, smartlist_free, (smartlist_t *sl));
+NS_DECL(void, smartlist_free_, (smartlist_t *sl));
static void
NS(test_main)(void *arg)
{
(void)arg;
- NS_MOCK(smartlist_free);
+ NS_MOCK(smartlist_free_);
- routerset_free(NULL);
+ routerset_free_(NULL);
- tt_int_op(CALLED(smartlist_free), OP_EQ, 0);
+ tt_int_op(CALLED(smartlist_free_), OP_EQ, 0);
done:
;
}
void
-NS(smartlist_free)(smartlist_t *s)
+NS(smartlist_free_)(smartlist_t *s)
{
(void)s;
- CALLED(smartlist_free)++;
+ CALLED(smartlist_free_)++;
}
#undef NS_SUBMODULE
@@ -2112,9 +2115,9 @@ NS(smartlist_free)(smartlist_t *s)
* Structural test for routerset_free.
*/
-NS_DECL(void, smartlist_free, (smartlist_t *sl));
-NS_DECL(void, strmap_free,(strmap_t *map, void (*free_val)(void*)));
-NS_DECL(void, digestmap_free, (digestmap_t *map, void (*free_val)(void*)));
+NS_DECL(void, smartlist_free_, (smartlist_t *sl));
+NS_DECL(void, strmap_free_,(strmap_t *map, void (*free_val)(void*)));
+NS_DECL(void, digestmap_free_, (digestmap_t *map, void (*free_val)(void*)));
static void
NS(test_main)(void *arg)
@@ -2122,39 +2125,39 @@ NS(test_main)(void *arg)
routerset_t *routerset = routerset_new();
(void)arg;
- NS_MOCK(smartlist_free);
- NS_MOCK(strmap_free);
- NS_MOCK(digestmap_free);
+ NS_MOCK(smartlist_free_);
+ NS_MOCK(strmap_free_);
+ NS_MOCK(digestmap_free_);
routerset_free(routerset);
- tt_int_op(CALLED(smartlist_free), OP_NE, 0);
- tt_int_op(CALLED(strmap_free), OP_NE, 0);
- tt_int_op(CALLED(digestmap_free), OP_NE, 0);
+ tt_int_op(CALLED(smartlist_free_), OP_NE, 0);
+ tt_int_op(CALLED(strmap_free_), OP_NE, 0);
+ tt_int_op(CALLED(digestmap_free_), OP_NE, 0);
done:
;
}
void
-NS(smartlist_free)(smartlist_t *s)
+NS(smartlist_free_)(smartlist_t *s)
{
- CALLED(smartlist_free)++;
- smartlist_free__real(s);
+ CALLED(smartlist_free_)++;
+ smartlist_free___real(s);
}
void
-NS(strmap_free)(strmap_t *map, void (*free_val)(void*))
+NS(strmap_free_)(strmap_t *map, void (*free_val)(void*))
{
- CALLED(strmap_free)++;
- strmap_free__real(map, free_val);
+ CALLED(strmap_free_)++;
+ strmap_free___real(map, free_val);
}
void
-NS(digestmap_free)(digestmap_t *map, void (*free_val)(void*))
+NS(digestmap_free_)(digestmap_t *map, void (*free_val)(void*))
{
- CALLED(digestmap_free)++;
- digestmap_free__real(map, free_val);
+ CALLED(digestmap_free_)++;
+ digestmap_free___real(map, free_val);
}
#undef NS_SUBMODULE
diff --git a/src/test/test_rust.c b/src/test/test_rust.c
deleted file mode 100644
index 6ad57d6fcb..0000000000
--- a/src/test/test_rust.c
+++ /dev/null
@@ -1,31 +0,0 @@
-/* Copyright (c) 2017, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-#include "orconfig.h"
-#include "compat_rust.h"
-#include "test.h"
-#include "util.h"
-
-static void
-test_welcome_string(void *arg)
-{
- (void)arg;
- rust_str_t s = rust_welcome_string();
- const char *c_str = rust_str_get(s);
- tt_assert(c_str);
- size_t len = strlen(c_str);
-#ifdef HAVE_RUST
- tt_assert(len > 0);
-#else
- tt_assert(len == 0);
-#endif
-
- done:
- rust_str_free(s);
-}
-
-struct testcase_t rust_tests[] = {
- { "welcome_string", test_welcome_string, 0, NULL, NULL },
- END_OF_TESTCASES
-};
-
diff --git a/src/test/test_rust.sh b/src/test/test_rust.sh
index 2761b612b5..5405af436b 100755
--- a/src/test/test_rust.sh
+++ b/src/test/test_rust.sh
@@ -7,9 +7,9 @@ export LSAN_OPTIONS=suppressions=${abs_top_srcdir:-../../..}/src/test/rust_supp.
for cargo_toml_dir in "${abs_top_srcdir:-../../..}"/src/rust/*; do
if [ -e "${cargo_toml_dir}/Cargo.toml" ]; then
- cd "${cargo_toml_dir}" && \
+ cd "${abs_top_builddir:-../../..}/src/rust" && \
CARGO_TARGET_DIR="${abs_top_builddir:-../../..}/src/rust/target" \
- "${CARGO:-cargo}" test ${CARGO_ONLINE-"--frozen"} \
+ "${CARGO:-cargo}" test --all-features ${CARGO_ONLINE-"--frozen"} \
${EXTRA_CARGO_OPTIONS} \
--manifest-path "${cargo_toml_dir}/Cargo.toml" || exitcode=1
fi
diff --git a/src/test/test_scheduler.c b/src/test/test_scheduler.c
index 724a6b56b2..ebba71266c 100644
--- a/src/test/test_scheduler.c
+++ b/src/test/test_scheduler.c
@@ -299,10 +299,6 @@ channel_more_to_flush_mock(channel_t *chan)
flush_mock_channel_t *found_mock_ch = NULL;
- /* Check if we have any queued */
- if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue))
- return 1;
-
SMARTLIST_FOREACH_BEGIN(chans_for_flush_mock,
flush_mock_channel_t *,
flush_mock_ch) {
@@ -444,8 +440,6 @@ perform_channel_state_tests(int KISTSchedRunInterval, int sched_type)
/* Start it off in OPENING */
ch1->state = CHANNEL_STATE_OPENING;
- /* We'll need a cmux */
- ch1->cmux = circuitmux_alloc();
/* Try to register it */
channel_register(ch1);
tt_assert(ch1->registered);
@@ -457,7 +451,6 @@ perform_channel_state_tests(int KISTSchedRunInterval, int sched_type)
ch2 = new_fake_channel();
tt_assert(ch2);
ch2->state = CHANNEL_STATE_OPENING;
- ch2->cmux = circuitmux_alloc();
channel_register(ch2);
tt_assert(ch2->registered);
@@ -668,8 +661,6 @@ test_scheduler_loop_vanilla(void *arg)
/* Start it off in OPENING */
ch1->state = CHANNEL_STATE_OPENING;
- /* We'll need a cmux */
- ch1->cmux = circuitmux_alloc();
/* Try to register it */
channel_register(ch1);
tt_assert(ch1->registered);
@@ -684,7 +675,6 @@ test_scheduler_loop_vanilla(void *arg)
ch2->magic = TLS_CHAN_MAGIC;
tt_assert(ch2);
ch2->state = CHANNEL_STATE_OPENING;
- ch2->cmux = circuitmux_alloc();
channel_register(ch2);
tt_assert(ch2->registered);
/*
@@ -836,7 +826,6 @@ test_scheduler_loop_kist(void *arg)
tt_assert(ch1);
ch1->magic = TLS_CHAN_MAGIC;
ch1->state = CHANNEL_STATE_OPENING;
- ch1->cmux = circuitmux_alloc();
channel_register(ch1);
tt_assert(ch1->registered);
channel_change_state_open(ch1);
@@ -847,7 +836,6 @@ test_scheduler_loop_kist(void *arg)
tt_assert(ch2);
ch2->magic = TLS_CHAN_MAGIC;
ch2->state = CHANNEL_STATE_OPENING;
- ch2->cmux = circuitmux_alloc();
channel_register(ch2);
tt_assert(ch2->registered);
channel_change_state_open(ch2);
@@ -1083,6 +1071,271 @@ test_scheduler_ns_changed(void *arg)
return;
}
+/*
+ * Mocked functions for the kist_pending_list test.
+ */
+
+static int mock_flush_some_cells_num = 1;
+static int mock_more_to_flush = 0;
+static int mock_update_socket_info_limit = 0;
+
+static ssize_t
+channel_flush_some_cells_mock_var(channel_t *chan, ssize_t num_cells)
+{
+ (void) chan;
+ (void) num_cells;
+ return mock_flush_some_cells_num;
+}
+
+/* Because when we flush cells, it is possible that the connection outbuf gets
+ * fully drained, the wants to write scheduler event is fired back while we
+ * are in the scheduler loop so this mock function does it for us.
+ * Furthermore, the socket limit is set to 0 so once this is triggered, it
+ * informs the scheduler that it can't write on the socket anymore. */
+static void
+channel_write_to_kernel_mock_trigger_24700(channel_t *chan)
+{
+ static int chan_id_seen[2] = {0};
+ if (++chan_id_seen[chan->global_identifier - 1] > 1) {
+ tt_assert(0);
+ }
+
+ scheduler_channel_wants_writes(chan);
+
+ done:
+ return;
+}
+
+static int
+channel_more_to_flush_mock_var(channel_t *chan)
+{
+ (void) chan;
+ return mock_more_to_flush;
+}
+
+static void
+update_socket_info_impl_mock_var(socket_table_ent_t *ent)
+{
+ ent->cwnd = ent->unacked = ent->mss = ent->notsent = 0;
+ ent->limit = mock_update_socket_info_limit;
+}
+
+static void
+test_scheduler_kist_pending_list(void *arg)
+{
+ (void) arg;
+
+#ifndef HAVE_KIST_SUPPORT
+ return;
+#endif
+
+ /* This is for testing the channel flow with the pending list that is
+ * depending on the channel state, what will be the expected behavior of the
+ * scheduler with that list.
+ *
+ * For instance, we want to catch double channel add or removing a channel
+ * that doesn't exists, or putting a channel in the list in a wrong state.
+ * Essentially, this will articifically test cases of the KIST main loop and
+ * entry point in the channel subsystem.
+ *
+ * In part, this is to also catch things like #24700 and provide a test bed
+ * for more testing in the future like so. */
+
+ /* Mocking a series of scheduler function to control the flow of the
+ * scheduler loop to test every use cases and assess the pending list. */
+ MOCK(get_options, mock_get_options);
+ MOCK(channel_flush_some_cells, channel_flush_some_cells_mock_var);
+ MOCK(channel_more_to_flush, channel_more_to_flush_mock_var);
+ MOCK(update_socket_info_impl, update_socket_info_impl_mock_var);
+ MOCK(channel_write_to_kernel, channel_write_to_kernel_mock);
+ MOCK(channel_should_write_to_kernel, channel_should_write_to_kernel_mock);
+
+ /* Setup options so we're sure about what sched we are running */
+ mocked_options.KISTSchedRunInterval = 10;
+ set_scheduler_options(SCHEDULER_KIST);
+
+ /* Init scheduler. */
+ scheduler_init();
+
+ /* Initialize a channel. We'll need a second channel for the #24700 bug
+ * test. */
+ channel_t *chan1 = new_fake_channel();
+ channel_t *chan2 = new_fake_channel();
+ tt_assert(chan1);
+ tt_assert(chan2);
+ chan1->magic = chan2->magic = TLS_CHAN_MAGIC;
+ channel_register(chan1);
+ channel_register(chan2);
+ tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_IDLE);
+ tt_int_op(chan1->sched_heap_idx, OP_EQ, -1);
+ tt_int_op(chan2->scheduler_state, OP_EQ, SCHED_CHAN_IDLE);
+ tt_int_op(chan2->sched_heap_idx, OP_EQ, -1);
+
+ /* Once a channel becomes OPEN, it always have at least one cell in it so
+ * the scheduler is notified that the channel wants to write so this is the
+ * first step. Might not make sense to you but it is the way it is. */
+ scheduler_channel_wants_writes(chan1);
+ tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
+ tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0);
+
+ /* Signal the scheduler that it has waiting cells which means the channel
+ * will get scheduled. */
+ scheduler_channel_has_waiting_cells(chan1);
+ tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
+ tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1);
+ /* Subsequent call should not add it more times. It is possible we add many
+ * cells in rapid succession before the channel is scheduled. */
+ scheduler_channel_has_waiting_cells(chan1);
+ tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
+ tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1);
+ scheduler_channel_has_waiting_cells(chan1);
+ tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
+ tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1);
+
+ /* We'll flush one cell and make it that the socket can write but no more to
+ * flush else we end up in an infinite loop. We expect the channel to be put
+ * in waiting for cells state and the pending list empty. */
+ mock_update_socket_info_limit = INT_MAX;
+ mock_more_to_flush = 0;
+ the_scheduler->run();
+ tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0);
+ tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
+
+ /* Lets make believe that a cell is now in the channel but this time the
+ * channel can't write so obviously it has more to flush. We expect the
+ * channel to be back in the pending list. */
+ scheduler_channel_has_waiting_cells(chan1);
+ mock_update_socket_info_limit = 0;
+ mock_more_to_flush = 1;
+ the_scheduler->run();
+ tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1);
+ tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
+
+ /* Channel is in the pending list now, during that time, we'll trigger a
+ * wants to write event because maybe the channel buffers were emptied in
+ * the meantime. This is possible because once the connection outbuf is
+ * flushed down the low watermark, the scheduler is notified.
+ *
+ * We expect the channel to NOT be added in the pending list again and stay
+ * in PENDING state. */
+ scheduler_channel_wants_writes(chan1);
+ tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1);
+ tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
+
+ /* Make it that the channel can write now but has nothing else to flush. We
+ * expect that it is removed from the pending list and waiting for cells. */
+ mock_update_socket_info_limit = INT_MAX;
+ mock_more_to_flush = 0;
+ the_scheduler->run();
+ tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0);
+ tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
+
+ /* While waiting for cells, lets say we were able to write more things on
+ * the connection outbuf (unlikely that this can happen but let say it
+ * does). We expect the channel to stay in waiting for cells. */
+ scheduler_channel_wants_writes(chan1);
+ tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0);
+ tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
+
+ /* We'll not put it in the pending list and make the flush cell fail with 0
+ * cell flushed. We expect that it is put back in waiting for cells. */
+ scheduler_channel_has_waiting_cells(chan1);
+ tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1);
+ tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
+ mock_flush_some_cells_num = 0;
+ the_scheduler->run();
+ tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0);
+ tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
+
+ /* Set the channel to a state where it doesn't want to write more. We expect
+ * that the channel becomes idle. */
+ scheduler_channel_doesnt_want_writes(chan1);
+ tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0);
+ tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_IDLE);
+
+ /* Some cells arrive on the channel now. We expect it to go back in waiting
+ * to write. You might wonder why it is not put in the pending list? Because
+ * once the channel becomes OPEN again (the doesn't want to write event only
+ * occurs if the channel goes in MAINT mode), if there are cells in the
+ * channel, the wants to write event is triggered thus putting the channel
+ * in pending mode.
+ *
+ * Else, if no cells, it stays IDLE and then once a cell comes in, it should
+ * go in waiting to write which is a BUG itself because the channel can't be
+ * scheduled until a second cell comes in. Hopefully, #24554 will fix that
+ * for KIST. */
+ scheduler_channel_has_waiting_cells(chan1);
+ tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0);
+ tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_TO_WRITE);
+
+ /* Second cell comes in, unfortunately, it won't get scheduled until a wants
+ * to write event occurs like described above. */
+ scheduler_channel_has_waiting_cells(chan1);
+ tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0);
+ tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_TO_WRITE);
+
+ /* Unblock everything putting the channel in the pending list. */
+ scheduler_channel_wants_writes(chan1);
+ tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1);
+ tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
+
+ /* Testing bug #24700 which is the situation where we have at least two
+ * different channels in the pending list. The first one gets flushed and
+ * bytes are written on the wire which triggers a wants to write event
+ * because the outbuf is below the low watermark. The bug was that this
+ * exact channel was added back in the pending list because its state wasn't
+ * PENDING.
+ *
+ * The following does some ninja-tsu to try to make it happen. We need two
+ * different channels so we create a second one and add it to the pending
+ * list. Then, we have a custom function when we write to kernel that does
+ * two important things:
+ *
+ * 1) Calls scheduler_channel_wants_writes(chan) on the channel.
+ * 2) Keeps track of how many times it sees the channel going through. If
+ * that limit goes > 1, it means we've added the channel twice in the
+ * pending list.
+ *
+ * In the end, we expect both channels to be in the pending list after this
+ * scheduler run. */
+
+ /* Put the second channel in the pending list. */
+ scheduler_channel_wants_writes(chan2);
+ scheduler_channel_has_waiting_cells(chan2);
+ tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 2);
+ tt_int_op(chan2->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
+
+ /* This makes it that the first pass on socket_can_write() will be true but
+ * then when a single cell is flushed (514 + 29 bytes), the second call to
+ * socket_can_write() will be false. If it wasn't sending back false on the
+ * second run, we end up in an infinite loop of the scheduler. */
+ mock_update_socket_info_limit = 600;
+ /* We want to hit "Case 3:" of the scheduler so channel_more_to_flush() is
+ * true but socket_can_write() has to be false on the second check on the
+ * channel. */
+ mock_more_to_flush = 1;
+ mock_flush_some_cells_num = 1;
+ MOCK(channel_write_to_kernel, channel_write_to_kernel_mock_trigger_24700);
+ the_scheduler->run();
+ tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 2);
+ tt_int_op(chan1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
+ tt_int_op(chan2->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
+
+ done:
+ chan1->state = chan2->state = CHANNEL_STATE_CLOSED;
+ chan1->registered = chan2->registered = 0;
+ channel_free(chan1);
+ channel_free(chan2);
+ scheduler_free_all();
+
+ UNMOCK(get_options);
+ UNMOCK(channel_flush_some_cells);
+ UNMOCK(channel_more_to_flush);
+ UNMOCK(update_socket_info_impl);
+ UNMOCK(channel_write_to_kernel);
+ UNMOCK(channel_should_write_to_kernel);
+}
+
struct testcase_t scheduler_tests[] = {
{ "compare_channels", test_scheduler_compare_channels,
TT_FORK, NULL, NULL },
@@ -1092,6 +1345,8 @@ struct testcase_t scheduler_tests[] = {
{ "loop_kist", test_scheduler_loop_kist, TT_FORK, NULL, NULL },
{ "ns_changed", test_scheduler_ns_changed, TT_FORK, NULL, NULL},
{ "should_use_kist", test_scheduler_can_use_kist, TT_FORK, NULL, NULL },
+ { "kist_pending_list", test_scheduler_kist_pending_list, TT_FORK,
+ NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c
index b7cd33715f..437fc38deb 100644
--- a/src/test/test_shared_random.c
+++ b/src/test/test_shared_random.c
@@ -1,3 +1,6 @@
+/* Copyright (c) 2016-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
#define SHARED_RANDOM_PRIVATE
#define SHARED_RANDOM_STATE_PRIVATE
#define CONFIG_PRIVATE
@@ -399,7 +402,7 @@ test_sr_commit(void *arg)
sizeof(our_commit->hashed_reveal)));
/* Do we have a valid encoded commit and reveal. Note the following only
* tests if the generated values are correct. Their could be a bug in
- * the decode function but we test them seperately. */
+ * the decode function but we test them separately. */
tt_int_op(0, OP_EQ, reveal_decode(our_commit->encoded_reveal,
&test_commit));
tt_int_op(0, OP_EQ, commit_decode(our_commit->encoded_commit,
@@ -609,7 +612,7 @@ test_vote(void *arg)
ret = smartlist_split_string(chunks, lines, "\n", SPLIT_IGNORE_BLANK, 0);
tt_int_op(ret, OP_EQ, 4);
tt_str_op(smartlist_get(chunks, 0), OP_EQ, "shared-rand-participate");
- /* Get our commitment line and will validate it agains our commit. The
+ /* Get our commitment line and will validate it against our commit. The
* format is as follow:
* "shared-rand-commitment" SP version SP algname SP identity
* SP COMMIT [SP REVEAL] NL
@@ -1350,7 +1353,7 @@ test_state_update(void *arg)
tt_assert(state->current_srv);
done:
- sr_state_free();
+ sr_state_free_all();
UNMOCK(get_my_v3_authority_cert);
}
diff --git a/src/test/test_socks.c b/src/test/test_socks.c
index 9ae7530e22..8da7191e82 100644
--- a/src/test/test_socks.c
+++ b/src/test/test_socks.c
@@ -347,17 +347,35 @@ test_socks_5_supported_commands(void *ptr)
socks_request_clear(socks);
- /* SOCKS 5 should NOT reject RESOLVE [F0] reject for IPv6 address
+ /* SOCKS 5 should NOT reject RESOLVE [F0] request for IPv6 address
* string if SafeSocks is enabled. */
ADD_DATA(buf, "\x05\x01\x00");
+ ADD_DATA(buf, "\x05\xF0\x00\x03\x29");
+ ADD_DATA(buf, "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]");
+ ADD_DATA(buf, "\x01\x02");
+ tt_int_op(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, 1),
+ OP_EQ, 1);
+
+ tt_str_op("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]", OP_EQ,
+ socks->address);
+ tt_int_op(258, OP_EQ, socks->port);
+
+ tt_int_op(0, OP_EQ, buf_datalen(buf));
+
+ socks_request_clear(socks);
+
+ /* Also allow bracket-less form. */
+
+ ADD_DATA(buf, "\x05\x01\x00");
ADD_DATA(buf, "\x05\xF0\x00\x03\x27");
ADD_DATA(buf, "2001:0db8:85a3:0000:0000:8a2e:0370:7334");
ADD_DATA(buf, "\x01\x02");
tt_int_op(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, 1),
- OP_EQ, -1);
+ OP_EQ, 1);
- tt_str_op("2001:0db8:85a3:0000:0000:8a2e:0370:7334", OP_EQ, socks->address);
+ tt_str_op("2001:0db8:85a3:0000:0000:8a2e:0370:7334", OP_EQ,
+ socks->address);
tt_int_op(258, OP_EQ, socks->port);
tt_int_op(0, OP_EQ, buf_datalen(buf));
diff --git a/src/test/test_status.c b/src/test/test_status.c
index f86f8e3b9e..b4ca17891b 100644
--- a/src/test/test_status.c
+++ b/src/test/test_status.c
@@ -1,3 +1,6 @@
+/* Copyright (c) 2014-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
#define STATUS_PRIVATE
#define HIBERNATE_PRIVATE
#define LOG_PRIVATE
@@ -337,7 +340,7 @@ NS(test_main)(void *arg)
actual = log_heartbeat(0);
tt_int_op(actual, OP_EQ, expected);
- tt_int_op(CALLED(logv), OP_EQ, 5);
+ tt_int_op(CALLED(logv), OP_EQ, 6);
done:
NS_UNMOCK(tls_get_write_overhead_ratio);
@@ -436,6 +439,16 @@ NS(logv)(int severity, log_domain_mask_t domain,
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_int_op(domain, OP_EQ, LD_HEARTBEAT);
+ tt_str_op(format, OP_EQ, "DoS mitigation since startup:%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, "");
+ break;
default:
tt_abort_msg("unexpected call to logv()"); // TODO: prettyprint args
break;
diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c
index 9a6ebeb699..a661eb5c5d 100644
--- a/src/test/test_tortls.c
+++ b/src/test/test_tortls.c
@@ -842,8 +842,10 @@ test_tortls_classify_client_ciphers(void *ignored)
sk_SSL_CIPHER_zero(ciphers);
one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384");
+ tt_assert(one);
one->id = 0x00ff;
two = get_cipher_by_name("ECDHE-RSA-AES128-GCM-SHA256");
+ tt_assert(two);
two->id = 0x0000;
sk_SSL_CIPHER_push(ciphers, one);
tls->client_cipher_list_type = 0;
@@ -914,6 +916,7 @@ test_tortls_client_is_using_v2_ciphers(void *ignored)
ciphers = sk_SSL_CIPHER_new_null();
SSL_CIPHER *one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384");
+ tt_assert(one);
one->id = 0x00ff;
sk_SSL_CIPHER_push(ciphers, one);
sess->ciphers = ciphers;
diff --git a/src/test/test_util.c b/src/test/test_util.c
index 0519a4758f..036f739b89 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -703,7 +703,7 @@ test_util_time(void *arg)
* a "correct" retrospective gregorian negative year value,
* which I'm pretty sure is:
* -1*(2^63)/60/60/24*2000/730485 + 1970 = -292277022657
- * 730485 is the number of days in two millenia, including leap days */
+ * 730485 is the number of days in two millennia, including leap days */
a_time.tm_year = -292277022657-1900;
CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
@@ -739,7 +739,7 @@ test_util_time(void *arg)
* a "correct" proleptic gregorian year value,
* which I'm pretty sure is:
* (2^63-1)/60/60/24*2000/730485 + 1970 = 292277026596
- * 730485 is the number of days in two millenia, including leap days */
+ * 730485 is the number of days in two millennia, including leap days */
a_time.tm_year = 292277026596-1900;
CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
@@ -875,7 +875,7 @@ test_util_time(void *arg)
* a "correct" retrospective gregorian negative year value,
* which I'm pretty sure is:
* -1*(2^63)/60/60/24*2000/730485 + 1970 = -292277022657
- * 730485 is the number of days in two millenia, including leap days
+ * 730485 is the number of days in two millennia, including leap days
* (int64_t)b_time.tm_year == (-292277022657LL-1900LL) without clamping */
t_res = INT64_MIN;
CAPTURE();
@@ -921,7 +921,7 @@ test_util_time(void *arg)
* a "correct" proleptic gregorian year value,
* which I'm pretty sure is:
* (2^63-1)/60/60/24*2000/730485 + 1970 = 292277026596
- * 730485 is the number of days in two millenia, including leap days
+ * 730485 is the number of days in two millennia, including leap days
* (int64_t)b_time.tm_year == (292277026596L-1900L) without clamping */
t_res = INT64_MAX;
CAPTURE();
@@ -5465,7 +5465,7 @@ is_there_a_localhost(int family)
#endif /* 0 */
/* Test for socketpair and ersatz_socketpair(). We test them both, since
- * the latter is a tolerably good way to exersize tor_accept_socket(). */
+ * the latter is a tolerably good way to exercise tor_accept_socket(). */
static void
test_util_socketpair(void *arg)
{
@@ -5542,47 +5542,74 @@ test_util_max_mem(void *arg)
}
static void
+test_util_dest_validation_edgecase(void *arg)
+{
+ (void)arg;
+
+ tt_assert(!string_is_valid_dest(NULL));
+ tt_assert(!string_is_valid_dest(""));
+
+ done:
+ return;
+}
+
+static void
test_util_hostname_validation(void *arg)
{
(void)arg;
// Lets try valid hostnames first.
- tt_assert(string_is_valid_hostname("torproject.org"));
- tt_assert(string_is_valid_hostname("ocw.mit.edu"));
- tt_assert(string_is_valid_hostname("i.4cdn.org"));
- tt_assert(string_is_valid_hostname("stanford.edu"));
- tt_assert(string_is_valid_hostname("multiple-words-with-hypens.jp"));
+ tt_assert(string_is_valid_nonrfc_hostname("torproject.org"));
+ tt_assert(string_is_valid_nonrfc_hostname("ocw.mit.edu"));
+ tt_assert(string_is_valid_nonrfc_hostname("i.4cdn.org"));
+ tt_assert(string_is_valid_nonrfc_hostname("stanford.edu"));
+ tt_assert(string_is_valid_nonrfc_hostname("multiple-words-with-hypens.jp"));
// Subdomain name cannot start with '-' or '_'.
- tt_assert(!string_is_valid_hostname("-torproject.org"));
- tt_assert(!string_is_valid_hostname("subdomain.-domain.org"));
- tt_assert(!string_is_valid_hostname("-subdomain.domain.org"));
- tt_assert(!string_is_valid_hostname("___abc.org"));
+ tt_assert(!string_is_valid_nonrfc_hostname("-torproject.org"));
+ tt_assert(!string_is_valid_nonrfc_hostname("subdomain.-domain.org"));
+ tt_assert(!string_is_valid_nonrfc_hostname("-subdomain.domain.org"));
+ tt_assert(!string_is_valid_nonrfc_hostname("___abc.org"));
// Hostnames cannot contain non-alphanumeric characters.
- tt_assert(!string_is_valid_hostname("%%domain.\\org."));
- tt_assert(!string_is_valid_hostname("***x.net"));
- tt_assert(!string_is_valid_hostname("\xff\xffxyz.org"));
- tt_assert(!string_is_valid_hostname("word1 word2.net"));
+ tt_assert(!string_is_valid_nonrfc_hostname("%%domain.\\org."));
+ tt_assert(!string_is_valid_nonrfc_hostname("***x.net"));
+ tt_assert(!string_is_valid_nonrfc_hostname("\xff\xffxyz.org"));
+ tt_assert(!string_is_valid_nonrfc_hostname("word1 word2.net"));
// Test workaround for nytimes.com stupidity, technically invalid,
// but we allow it since they are big, even though they are failing to
// comply with a ~30 year old standard.
- tt_assert(string_is_valid_hostname("core3_euw1.fabrik.nytimes.com"));
+ tt_assert(string_is_valid_nonrfc_hostname("core3_euw1.fabrik.nytimes.com"));
// Firefox passes FQDNs with trailing '.'s directly to the SOCKS proxy,
// which is redundant since the spec states DOMAINNAME addresses are fully
// qualified. While unusual, this should be tollerated.
- tt_assert(string_is_valid_hostname("core9_euw1.fabrik.nytimes.com."));
- tt_assert(!string_is_valid_hostname("..washingtonpost.is.better.com"));
- tt_assert(!string_is_valid_hostname("so.is..ft.com"));
- tt_assert(!string_is_valid_hostname("..."));
+ tt_assert(string_is_valid_nonrfc_hostname("core9_euw1.fabrik.nytimes.com."));
+ tt_assert(!string_is_valid_nonrfc_hostname(
+ "..washingtonpost.is.better.com"));
+ tt_assert(!string_is_valid_nonrfc_hostname("so.is..ft.com"));
+ tt_assert(!string_is_valid_nonrfc_hostname("..."));
// XXX: do we allow single-label DNS names?
// We shouldn't for SOCKS (spec says "contains a fully-qualified domain name"
// but only test pathologically malformed traling '.' cases for now.
- tt_assert(!string_is_valid_hostname("."));
- tt_assert(!string_is_valid_hostname(".."));
+ tt_assert(!string_is_valid_nonrfc_hostname("."));
+ tt_assert(!string_is_valid_nonrfc_hostname(".."));
+
+ // IP address strings are not hostnames.
+ tt_assert(!string_is_valid_nonrfc_hostname("8.8.8.8"));
+ tt_assert(!string_is_valid_nonrfc_hostname("[2a00:1450:401b:800::200e]"));
+ tt_assert(!string_is_valid_nonrfc_hostname("2a00:1450:401b:800::200e"));
+
+ // We allow alphanumeric TLDs. For discussion, see ticket #25055.
+ tt_assert(string_is_valid_nonrfc_hostname("lucky.13"));
+ tt_assert(string_is_valid_nonrfc_hostname("luck.y13"));
+ tt_assert(string_is_valid_nonrfc_hostname("luck.y13."));
+
+ // We allow punycode TLDs. For examples, see
+ // http://data.iana.org/TLD/tlds-alpha-by-domain.txt
+ tt_assert(string_is_valid_nonrfc_hostname("example.xn--l1acc"));
done:
return;
@@ -5801,6 +5828,7 @@ test_util_monotonic_time(void *arg)
monotime_coarse_t mtc1, mtc2;
uint64_t nsec1, nsec2, usec1, msec1;
uint64_t nsecc1, nsecc2, usecc1, msecc1;
+ uint32_t stamp1, stamp2;
monotime_init();
@@ -5812,6 +5840,7 @@ test_util_monotonic_time(void *arg)
nsecc1 = monotime_coarse_absolute_nsec();
usecc1 = monotime_coarse_absolute_usec();
msecc1 = monotime_coarse_absolute_msec();
+ stamp1 = monotime_coarse_to_stamp(&mtc1);
tor_sleep_msec(200);
@@ -5819,6 +5848,7 @@ test_util_monotonic_time(void *arg)
monotime_coarse_get(&mtc2);
nsec2 = monotime_absolute_nsec();
nsecc2 = monotime_coarse_absolute_nsec();
+ stamp2 = monotime_coarse_to_stamp(&mtc2);
/* We need to be a little careful here since we don't know the system load.
*/
@@ -5835,10 +5865,15 @@ test_util_monotonic_time(void *arg)
tt_u64_op(usec1, OP_GE, nsec1 / 1000);
tt_u64_op(msecc1, OP_GE, nsecc1 / 1000000);
tt_u64_op(usecc1, OP_GE, nsecc1 / 1000);
- tt_u64_op(msec1, OP_LE, nsec1 / 1000000 + 1);
- tt_u64_op(usec1, OP_LE, nsec1 / 1000 + 1000);
- tt_u64_op(msecc1, OP_LE, nsecc1 / 1000000 + 1);
- tt_u64_op(usecc1, OP_LE, nsecc1 / 1000 + 1000);
+ tt_u64_op(msec1, OP_LE, nsec1 / 1000000 + 10);
+ tt_u64_op(usec1, OP_LE, nsec1 / 1000 + 10000);
+ tt_u64_op(msecc1, OP_LE, nsecc1 / 1000000 + 10);
+ tt_u64_op(usecc1, OP_LE, nsecc1 / 1000 + 10000);
+
+ uint64_t coarse_stamp_diff =
+ monotime_coarse_stamp_units_to_approx_msec(stamp2-stamp1);
+ tt_u64_op(coarse_stamp_diff, OP_GE, 120);
+ tt_u64_op(coarse_stamp_diff, OP_LE, 1200);
done:
;
@@ -5918,6 +5953,64 @@ test_util_monotonic_time_ratchet(void *arg)
}
static void
+test_util_monotonic_time_zero(void *arg)
+{
+ (void) arg;
+ monotime_t t1;
+ monotime_coarse_t ct1;
+ monotime_init();
+ /* Check 1: The current time is not zero. */
+ monotime_get(&t1);
+ monotime_coarse_get(&ct1);
+ tt_assert(!monotime_is_zero(&t1));
+ tt_assert(!monotime_coarse_is_zero(&ct1));
+
+ /* Check 2: The _zero() makes the time zero. */
+ monotime_zero(&t1);
+ monotime_coarse_zero(&ct1);
+ tt_assert(monotime_is_zero(&t1));
+ tt_assert(monotime_coarse_is_zero(&ct1));
+ done:
+ ;
+}
+
+static void
+test_util_monotonic_time_add_msec(void *arg)
+{
+ (void) arg;
+ monotime_t t1, t2;
+ monotime_coarse_t ct1, ct2;
+ monotime_init();
+
+ monotime_get(&t1);
+ monotime_coarse_get(&ct1);
+
+ /* adding zero does nothing */
+ monotime_add_msec(&t2, &t1, 0);
+ monotime_coarse_add_msec(&ct2, &ct1, 0);
+ tt_i64_op(monotime_diff_msec(&t1, &t2), OP_EQ, 0);
+ tt_i64_op(monotime_coarse_diff_msec(&ct1, &ct2), OP_EQ, 0);
+
+ /* Add 1337 msec; see if the diff function agree */
+ monotime_add_msec(&t2, &t1, 1337);
+ monotime_coarse_add_msec(&ct2, &ct1, 1337);
+ tt_i64_op(monotime_diff_msec(&t1, &t2), OP_EQ, 1337);
+ tt_i64_op(monotime_coarse_diff_msec(&ct1, &ct2), OP_EQ, 1337);
+
+ /* Add 1337 msec twice more; make sure that any second rollover issues
+ * worked. */
+ monotime_add_msec(&t2, &t2, 1337);
+ monotime_coarse_add_msec(&ct2, &ct2, 1337);
+ monotime_add_msec(&t2, &t2, 1337);
+ monotime_coarse_add_msec(&ct2, &ct2, 1337);
+ tt_i64_op(monotime_diff_msec(&t1, &t2), OP_EQ, 1337*3);
+ tt_i64_op(monotime_coarse_diff_msec(&ct1, &ct2), OP_EQ, 1337*3);
+
+ done:
+ ;
+}
+
+static void
test_util_htonll(void *arg)
{
(void)arg;
@@ -6142,6 +6235,7 @@ struct testcase_t util_tests[] = {
&passthrough_setup, (void*)"1" },
UTIL_TEST(max_mem, 0),
UTIL_TEST(hostname_validation, 0),
+ UTIL_TEST(dest_validation_edgecase, 0),
UTIL_TEST(ipv4_validation, 0),
UTIL_TEST(writepid, 0),
UTIL_TEST(get_avail_disk_space, 0),
@@ -6150,6 +6244,8 @@ struct testcase_t util_tests[] = {
UTIL_TEST(calloc_check, 0),
UTIL_TEST(monotonic_time, 0),
UTIL_TEST(monotonic_time_ratchet, TT_FORK),
+ UTIL_TEST(monotonic_time_zero, 0),
+ UTIL_TEST(monotonic_time_add_msec, 0),
UTIL_TEST(htonll, 0),
UTIL_TEST(get_unquoted_path, 0),
END_OF_TESTCASES
diff --git a/src/test/testing_common.c b/src/test/testing_common.c
index 7e9c47b48d..52729147b2 100644
--- a/src/test/testing_common.c
+++ b/src/test/testing_common.c
@@ -3,12 +3,6 @@
* Copyright (c) 2007-2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
-extern const char tor_git_revision[];
-
-/* Ordinarily defined in tor_main.c; this bit is just here to provide one
- * since we're not linking to tor_main.c */
-const char tor_git_revision[] = "";
-
/**
* \file test_common.c
* \brief Common pieces to implement unit tests.
@@ -284,6 +278,7 @@ main(int c, const char **v)
s.masks[LOG_WARN-LOG_ERR] |= LD_BUG;
add_stream_log(&s, "", fileno(stdout));
}
+ init_protocol_warning_severity_level();
options->command = CMD_RUN_UNITTESTS;
if (crypto_global_init(accel_crypto, NULL, NULL)) {
@@ -299,6 +294,9 @@ main(int c, const char **v)
setup_directory();
options_init(options);
options->DataDirectory = tor_strdup(temp_dir);
+ tor_asprintf(&options->KeyDirectory, "%s"PATH_SEPARATOR"keys",
+ options->DataDirectory);
+ options->CacheDirectory = tor_strdup(temp_dir);
options->EntryStatistics = 1;
if (set_options(options, &errmsg) < 0) {
printf("Failed to set initial options: %s\n", errmsg);
diff --git a/src/tools/include.am b/src/tools/include.am
index 37936c742f..92cc3f10a2 100644
--- a/src/tools/include.am
+++ b/src/tools/include.am
@@ -45,3 +45,8 @@ src_tools_tor_cov_gencert_LDADD = src/common/libor-testing.a \
endif
EXTRA_DIST += src/tools/tor-fw-helper/README
+
+if BUILD_LIBTORRUNNER
+noinst_LIBRARIES += src/tools/libtorrunner.a
+src_tools_libtorrunner_a_SOURCES = src/tools/tor_runner.c src/or/tor_api.c
+endif
diff --git a/src/tools/tor-resolve.c b/src/tools/tor-resolve.c
index 1e2409a131..966b88b3e8 100644
--- a/src/tools/tor-resolve.c
+++ b/src/tools/tor-resolve.c
@@ -347,7 +347,6 @@ main(int argc, char **argv)
int n_args;
tor_addr_t result;
char *result_hostname = NULL;
- log_severity_list_t *s = tor_malloc_zero(sizeof(log_severity_list_t));
init_logging(1);
sandbox_disable_getaddrinfo_cache();
@@ -398,11 +397,14 @@ main(int argc, char **argv)
usage();
}
+ log_severity_list_t *severities =
+ tor_malloc_zero(sizeof(log_severity_list_t));
if (isVerbose)
- set_log_severity_config(LOG_DEBUG, LOG_ERR, s);
+ set_log_severity_config(LOG_DEBUG, LOG_ERR, severities);
else
- set_log_severity_config(LOG_WARN, LOG_ERR, s);
- add_stream_log(s, "<stderr>", fileno(stderr));
+ set_log_severity_config(LOG_WARN, LOG_ERR, severities);
+ add_stream_log(severities, "<stderr>", fileno(stderr));
+ tor_free(severities);
if (n_args == 1) {
log_debug(LD_CONFIG, "defaulting to localhost");
diff --git a/src/tools/tor_runner.c b/src/tools/tor_runner.c
new file mode 100644
index 0000000000..9ed2ee5775
--- /dev/null
+++ b/src/tools/tor_runner.c
@@ -0,0 +1,101 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file tor_runner.c
+ * @brief Experimental module to emulate tor_run_main() API with fork+exec
+ *
+ * The functions here are meant to allow the application developer to
+ * use the tor_run_main() API without having to care whether Tor is
+ * running in-process or out-of-process. For in-process usage, the
+ * developer can link Tor as a library and call tor_run_main(); for
+ * out-of-process usage, the developer can link this library instead.
+ *
+ * This interface is EXPERIMENTAL; please let us know if you would like
+ * to depend on it. We don't know yet whether it will be reliable in
+ * practice.
+ */
+
+/* NOTE: This module is supposed to work without the standard Tor utility
+ * functions. Don't add more dependencies!
+ */
+
+#include "tor_api.h"
+#include "tor_api_internal.h"
+
+#include "orconfig.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef __GNUC__
+#define __attribute__(x)
+#endif
+
+static void child(const tor_main_configuration_t *cfg)
+ __attribute__((noreturn));
+
+int
+tor_run_main(const tor_main_configuration_t *cfg)
+{
+ pid_t pid = fork();
+ if (pid == 0) {
+ child(cfg);
+ exit(0); /* Unreachable */
+ }
+
+ pid_t stopped_pid;
+ int status = 0;
+ do {
+ stopped_pid = waitpid(pid, &status, 0);
+ } while (stopped_pid == -1);
+
+ /* Note: these return values are not documented. No return value is
+ * documented! */
+
+ if (stopped_pid != pid) {
+ return -99999;
+ }
+ if (WIFSTOPPED(status)) {
+ return WEXITSTATUS(status);
+ }
+ if (WIFSIGNALED(status)) {
+ return -WTERMSIG(status);
+ }
+
+ return -999988;
+}
+
+/* circumlocution to avoid getting warned about calling calloc instead of
+ * tor_calloc. */
+#define real_calloc calloc
+
+static void
+child(const tor_main_configuration_t *cfg)
+{
+ /* XXXX Close unused file descriptors. */
+
+ char **args = real_calloc(cfg->argc+1, sizeof(char *));
+ memcpy(args, cfg->argv, cfg->argc * sizeof(char *));
+ args[cfg->argc] = NULL;
+
+ int rv = execv(BINDIR "/tor", args);
+
+ if (rv < 0) {
+ exit(254);
+ } else {
+ abort(); /* Unreachable */
+ }
+}
+
diff --git a/src/trunnel/hs/cell_introduce1.trunnel b/src/trunnel/hs/cell_introduce1.trunnel
index 7577c1526f..574382b163 100644
--- a/src/trunnel/hs/cell_introduce1.trunnel
+++ b/src/trunnel/hs/cell_introduce1.trunnel
@@ -14,7 +14,7 @@ const TRUNNEL_REND_COOKIE_LEN = 20;
/* INTRODUCE1 payload. See details in section 3.2.1. */
struct trn_cell_introduce1 {
- /* Always zeroed. MUST be checked explicitely by the caller. */
+ /* Always zeroed. MUST be checked explicitly by the caller. */
u8 legacy_key_id[TRUNNEL_SHA1_LEN];
/* Authentication key material. */
diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h
index f5186fd242..8837cfce31 100644
--- a/src/win32/orconfig.h
+++ b/src/win32/orconfig.h
@@ -218,7 +218,7 @@
#define USING_TWOS_COMPLEMENT
/* Version number of package */
-#define VERSION "0.3.2.10-dev"
+#define VERSION "0.3.3.8-dev"