aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml4
-rw-r--r--.gitignore2
-rw-r--r--ChangeLog67
-rw-r--r--Makefile.am6
-rw-r--r--changes/bug219004
-rw-r--r--changes/bug230824
-rw-r--r--changes/bug243936
-rw-r--r--changes/bug258857
-rw-r--r--changes/bug277073
-rw-r--r--changes/bug279295
-rw-r--r--changes/bug2809613
-rw-r--r--changes/bug285543
-rw-r--r--changes/feature272445
-rw-r--r--changes/feature273674
-rw-r--r--changes/subsystems6
-rw-r--r--changes/ticket272255
-rw-r--r--changes/ticket273593
-rw-r--r--changes/ticket275493
-rw-r--r--changes/ticket276254
-rw-r--r--changes/ticket280063
-rw-r--r--changes/ticket280083
-rw-r--r--changes/ticket280093
-rw-r--r--changes/ticket280103
-rw-r--r--changes/ticket280113
-rw-r--r--changes/ticket280773
-rw-r--r--changes/ticket281003
-rw-r--r--changes/ticket281284
-rw-r--r--changes/ticket28229_diag3
-rw-r--r--changes/ticket283357
-rw-r--r--changes/ticket285744
-rw-r--r--configure.ac15
-rw-r--r--contrib/win32build/tor-mingw.nsi.in2
-rw-r--r--doc/HACKING/Maintaining.md113
-rw-r--r--doc/HACKING/ReleasingTor.md10
-rw-r--r--doc/tor.1.txt17
-rwxr-xr-xscripts/codegen/run_trunnel.sh4
-rwxr-xr-xscripts/maint/run_calltool.sh4
-rwxr-xr-xscripts/test/chutney-git-bisect.sh6
-rwxr-xr-xscripts/test/cov-diff6
-rwxr-xr-xscripts/test/coverage20
-rw-r--r--src/app/config/config.c55
-rw-r--r--src/app/config/config.h2
-rw-r--r--src/app/config/or_options_st.h10
-rw-r--r--src/app/config/statefile.c1
-rw-r--r--src/app/main/main.c99
-rw-r--r--src/app/main/subsysmgr.c202
-rw-r--r--src/app/main/subsysmgr.h24
-rw-r--r--src/app/main/subsystem_list.c40
-rw-r--r--src/config/mmdb-convert.py2
-rw-r--r--src/core/include.am6
-rw-r--r--src/core/mainloop/connection.c13
-rw-r--r--src/core/mainloop/connection.h1
-rw-r--r--src/core/mainloop/mainloop.c351
-rw-r--r--src/core/mainloop/mainloop.h7
-rw-r--r--src/core/mainloop/netstatus.c73
-rw-r--r--src/core/mainloop/netstatus.h7
-rw-r--r--src/core/mainloop/periodic.c22
-rw-r--r--src/core/mainloop/periodic.h15
-rw-r--r--src/core/or/circuitbuild.c21
-rw-r--r--src/core/or/circuitbuild.h3
-rw-r--r--src/core/or/connection_edge.c11
-rw-r--r--src/core/or/or.h2
-rw-r--r--src/core/or/versions.c83
-rw-r--r--src/core/or/versions.h2
-rw-r--r--src/feature/client/dnsserv.c4
-rw-r--r--src/feature/control/control.c11
-rw-r--r--src/feature/dirauth/dirvote.c19
-rw-r--r--src/feature/dirauth/process_descs.c12
-rw-r--r--src/feature/dirauth/process_descs.h3
-rw-r--r--src/feature/dirauth/shared_random_state.c1
-rw-r--r--src/feature/dircache/consdiffmgr.c83
-rw-r--r--src/feature/dircache/consdiffmgr.h11
-rw-r--r--src/feature/dircache/dircache.c18
-rw-r--r--src/feature/dircache/dirserv.c5
-rw-r--r--src/feature/dircache/dirserv.h1
-rw-r--r--src/feature/dirclient/dirclient.c23
-rw-r--r--src/feature/dircommon/consdiff.c42
-rw-r--r--src/feature/dircommon/consdiff.h15
-rw-r--r--src/feature/dirparse/authcert_parse.c16
-rw-r--r--src/feature/dirparse/authcert_parse.h1
-rw-r--r--src/feature/dirparse/microdesc_parse.c16
-rw-r--r--src/feature/dirparse/ns_parse.c55
-rw-r--r--src/feature/dirparse/ns_parse.h10
-rw-r--r--src/feature/hibernate/hibernate.c22
-rw-r--r--src/feature/hs/hs_descriptor.c101
-rw-r--r--src/feature/hs/hs_service.c8
-rw-r--r--src/feature/hs/hs_service.h2
-rw-r--r--src/feature/nodelist/authcert.c3
-rw-r--r--src/feature/nodelist/microdesc.c6
-rw-r--r--src/feature/nodelist/microdesc_st.h5
-rw-r--r--src/feature/nodelist/networkstatus.c100
-rw-r--r--src/feature/nodelist/networkstatus.h4
-rw-r--r--src/feature/nodelist/nodefamily.c373
-rw-r--r--src/feature/nodelist/nodefamily.h47
-rw-r--r--src/feature/nodelist/nodefamily_st.h48
-rw-r--r--src/feature/nodelist/nodelist.c132
-rw-r--r--src/feature/nodelist/nodelist.h11
-rw-r--r--src/feature/relay/dns.c91
-rw-r--r--src/feature/relay/dns.h5
-rw-r--r--src/feature/relay/router.c3
-rw-r--r--src/feature/rend/rendcache.c2
-rw-r--r--src/feature/rend/rendmid.c4
-rw-r--r--src/include.am2
-rw-r--r--src/lib/cc/.may_include1
-rw-r--r--src/lib/cc/ctassert.h53
-rw-r--r--src/lib/cc/include.am1
-rw-r--r--src/lib/compress/.may_include1
-rw-r--r--src/lib/compress/compress.c21
-rw-r--r--src/lib/compress/compress.h2
-rw-r--r--src/lib/compress/compress_sys.h14
-rw-r--r--src/lib/compress/include.am1
-rw-r--r--src/lib/crypt_ops/.may_include1
-rw-r--r--src/lib/crypt_ops/crypto_init.c47
-rw-r--r--src/lib/crypt_ops/crypto_sys.h14
-rw-r--r--src/lib/crypt_ops/include.am1
-rw-r--r--src/lib/err/.may_include2
-rw-r--r--src/lib/err/include.am8
-rw-r--r--src/lib/err/torerr.c10
-rw-r--r--src/lib/err/torerr.h1
-rw-r--r--src/lib/err/torerr_sys.c40
-rw-r--r--src/lib/err/torerr_sys.h14
-rw-r--r--src/lib/evloop/workqueue.c6
-rw-r--r--src/lib/evloop/workqueue.h1
-rw-r--r--src/lib/log/.may_include4
-rw-r--r--src/lib/log/include.am10
-rw-r--r--src/lib/log/log.c3
-rw-r--r--src/lib/log/log_sys.c35
-rw-r--r--src/lib/log/log_sys.h14
-rw-r--r--src/lib/net/.may_include1
-rw-r--r--src/lib/net/address.c14
-rw-r--r--src/lib/net/inaddr.c8
-rw-r--r--src/lib/net/include.am2
-rw-r--r--src/lib/net/network_sys.c44
-rw-r--r--src/lib/net/network_sys.h14
-rw-r--r--src/lib/osinfo/uname.c95
-rw-r--r--src/lib/process/.may_include1
-rw-r--r--src/lib/process/include.am6
-rw-r--r--src/lib/process/winprocess_sys.c64
-rw-r--r--src/lib/process/winprocess_sys.h14
-rw-r--r--src/lib/string/util_string.c13
-rw-r--r--src/lib/string/util_string.h1
-rw-r--r--src/lib/subsys/.may_include1
-rw-r--r--src/lib/subsys/include.am3
-rw-r--r--src/lib/subsys/subsys.h95
-rw-r--r--src/lib/thread/.may_include1
-rw-r--r--src/lib/thread/compat_threads.c16
-rw-r--r--src/lib/thread/include.am5
-rw-r--r--src/lib/thread/thread_sys.h14
-rw-r--r--src/lib/time/.may_include1
-rw-r--r--src/lib/time/include.am2
-rw-r--r--src/lib/time/time_sys.c26
-rw-r--r--src/lib/time/time_sys.h14
-rw-r--r--src/lib/tls/.may_include1
-rw-r--r--src/lib/tls/include.am1
-rw-r--r--src/lib/tls/tortls.c14
-rw-r--r--src/lib/tls/tortls_sys.h14
-rw-r--r--src/lib/version/.may_include3
-rw-r--r--src/lib/version/git_revision.c (renamed from src/lib/log/git_revision.c)2
-rw-r--r--src/lib/version/git_revision.h (renamed from src/lib/log/git_revision.h)0
-rw-r--r--src/lib/version/include.am25
-rw-r--r--src/lib/version/torversion.h12
-rw-r--r--src/lib/version/version.c50
-rw-r--r--src/lib/wallclock/.may_include1
-rw-r--r--src/lib/wallclock/approx_time.c16
-rw-r--r--src/lib/wallclock/include.am3
-rw-r--r--src/lib/wallclock/wallclock_sys.h14
-rw-r--r--src/rust/build.rs1
-rw-r--r--src/rust/tor_util/strings.rs6
-rw-r--r--src/test/bench.c13
-rw-r--r--src/test/fuzz/fuzz_consensus.c6
-rw-r--r--src/test/fuzz/fuzz_diff.c32
-rw-r--r--src/test/fuzz/fuzz_diff_apply.c13
-rw-r--r--src/test/fuzz/fuzz_vrs.c16
-rw-r--r--src/test/fuzz/fuzzing_common.c10
-rw-r--r--src/test/include.am1
-rw-r--r--src/test/test.c19
-rw-r--r--src/test/test.h33
-rw-r--r--src/test/test_addr.c56
-rw-r--r--src/test/test_address.c76
-rw-r--r--src/test/test_circuitbuild.c4
-rw-r--r--src/test/test_compat_libevent.c1
-rw-r--r--src/test/test_consdiff.c94
-rw-r--r--src/test/test_consdiffmgr.c41
-rw-r--r--src/test/test_dir.c44
-rw-r--r--src/test/test_dir_common.c17
-rw-r--r--src/test/test_dir_handle_get.c18
-rw-r--r--src/test/test_dns.c66
-rw-r--r--src/test/test_entrynodes.c5
-rw-r--r--src/test/test_hs_common.c2
-rw-r--r--src/test/test_mainloop.c145
-rw-r--r--src/test/test_microdesc.c8
-rw-r--r--src/test/test_nodelist.c385
-rw-r--r--src/test/test_options.c1
-rw-r--r--src/test/test_parsecommon.c594
-rw-r--r--src/test/test_periodic_event.c61
-rw-r--r--src/test/test_rebind.py26
-rwxr-xr-xsrc/test/test_rebind.sh15
-rw-r--r--src/test/test_routerlist.c19
-rw-r--r--src/test/test_shared_random.c12
-rw-r--r--src/test/test_util.c20
-rw-r--r--src/test/testing_common.c20
-rw-r--r--src/win32/orconfig.h2
202 files changed, 4689 insertions, 866 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 18097f8881..837cded43c 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -40,7 +40,7 @@ install:
#
# Use pacman --debug to show package downloads and install locations
#>
- Execute-Command "C:\msys64\usr\bin\pacman" -Sy --verbose --needed --noconfirm ${env:mingw_prefix}-libevent ${env:mingw_prefix}-openssl ${env:mingw_prefix}-pkg-config ${env:mingw_prefix}-xz ${env:mingw_prefix}-zstd ;
+ Execute-Command "C:\msys64\usr\bin\pacman" -Sy --verbose --needed --noconfirm ${env:mingw_prefix}-libevent ${env:mingw_prefix}-pkg-config ${env:mingw_prefix}-xz ${env:mingw_prefix}-zstd ;
build_script:
- ps: >-
@@ -60,7 +60,7 @@ build_script:
# mingw zstd doesn't come with a pkg-config file, so we manually
# configure its flags. liblzma just works.
#>
- Execute-Bash "ZSTD_CFLAGS='-L/${env:compiler_path}/include' ZSTD_LIBS='-L/${env:compiler_path}/lib -lzstd' ../configure --prefix=/${env:compiler_path} --build=${env:target} --host=${env:target} --disable-asciidoc --enable-fatal-warnings ${env:hardening}"
+ Execute-Bash "ZSTD_CFLAGS='-L/${env:compiler_path}/include' ZSTD_LIBS='-L/${env:compiler_path}/lib -lzstd' ../configure --prefix=/${env:compiler_path} --build=${env:target} --host=${env:target} --with-openssl-dir=/${env:compiler_path} --disable-asciidoc --enable-fatal-warnings ${env:hardening}"
Execute-Bash "V=1 make -j2"
Execute-Bash "V=1 make -j2 install"
}
diff --git a/.gitignore b/.gitignore
index cedff8fb37..ee2de376a6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -210,6 +210,8 @@ uptime-*.json
/src/lib/libtor-tls.a
/src/lib/libtor-tls-testing.a
/src/lib/libtor-trace.a
+/src/lib/libtor-version.a
+/src/lib/libtor-version-testing.a
/src/lib/libtor-wallclock.a
/src/lib/libtor-wallclock-testing.a
diff --git a/ChangeLog b/ChangeLog
index cdf7249059..4c776f7163 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,70 @@
+Changes in version 0.3.5.5-alpha - 2018-11-16
+ Tor 0.3.5.5-alpha includes numerous bugfixes on earlier releases,
+ including several that we hope to backport to older release series in
+ the future.
+
+ o Major bugfixes (OpenSSL, portability):
+ - Fix our usage of named groups when running as a TLS 1.3 client in
+ OpenSSL 1.1.1. Previously, we only initialized EC groups when
+ running as a relay, which caused clients to fail to negotiate TLS
+ 1.3 with relays. Fixes bug 28245; bugfix on 0.2.9.15 (when TLS 1.3
+ support was added).
+
+ o Minor features (geoip):
+ - Update geoip and geoip6 to the November 6 2018 Maxmind GeoLite2
+ Country database. Closes ticket 28395.
+
+ o Minor bugfixes (compilation):
+ - Initialize a variable unconditionally in aes_new_cipher(), since
+ some compilers cannot tell that we always initialize it before
+ use. Fixes bug 28413; bugfix on 0.2.9.3-alpha.
+
+ o Minor bugfixes (connection, relay):
+ - Avoid a logging a BUG() stacktrace when closing connection held
+ open because the write side is rate limited but not the read side.
+ Now, the connection read side is simply shut down until Tor is
+ able to flush the connection and close it. Fixes bug 27750; bugfix
+ on 0.3.4.1-alpha.
+
+ o Minor bugfixes (continuous integration, Windows):
+ - Manually configure the zstd compiler options, when building using
+ mingw on Appveyor Windows CI. The MSYS2 mingw zstd package does
+ not come with a pkg-config file. Fixes bug 28454; bugfix
+ on 0.3.4.1-alpha.
+ - Stop using an external OpenSSL install, and stop installing MSYS2
+ packages, when building using mingw on Appveyor Windows CI. Fixes
+ bug 28399; bugfix on 0.3.4.1-alpha.
+
+ o Minor bugfixes (documentation):
+ - Make Doxygen work again after the code movement in the 0.3.5
+ source tree. Fixes bug 28435; bugfix on 0.3.5.1-alpha.
+
+ o Minor bugfixes (Linux seccomp2 sandbox):
+ - Permit the "shutdown()" system call, which is apparently used by
+ OpenSSL under some circumstances. Fixes bug 28183; bugfix
+ on 0.2.5.1-alpha.
+
+ o Minor bugfixes (logging):
+ - Stop talking about the Named flag in log messages. Clients have
+ ignored the Named flag since 0.3.2. Fixes bug 28441; bugfix
+ on 0.3.2.1-alpha.
+
+ o Minor bugfixes (memory leaks):
+ - Fix a harmless memory leak in libtorrunner.a. Fixes bug 28419;
+ bugfix on 0.3.3.1-alpha. Patch from Martin Kepplinger.
+
+ o Minor bugfixes (onion services):
+ - On an intro point for a version 3 onion service, stop closing
+ introduction circuits on an NACK. This lets the client decide
+ whether to reuse the circuit or discard it. Previously, we closed
+ intro circuits when sending NACKs. Fixes bug 27841; bugfix on
+ 0.3.2.1-alpha. Patch by Neel Chaunan.
+ - When replacing a descriptor in the client cache, make sure to
+ close all client introduction circuits for the old descriptor, so
+ we don't end up with unusable leftover circuits. Fixes bug 27471;
+ bugfix on 0.3.2.1-alpha.
+
+
Changes in version 0.3.5.4-alpha - 2018-11-08
Tor 0.3.5.4-alpha includes numerous bugfixes on earlier versions and
improves our continuous integration support. It continues our attempts
diff --git a/Makefile.am b/Makefile.am
index e5c1be31b5..803e9d00df 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -31,9 +31,7 @@ TESTING_TOR_BINARY=$(top_builddir)/src/app/tor$(EXEEXT)
endif
if USE_RUST
-## this MUST be $(), otherwise am__DEPENDENCIES will not track it
-rust_ldadd=$(top_builddir)/$(TOR_RUST_LIB_PATH) \
- $(TOR_RUST_EXTRA_LIBS)
+rust_ldadd=$(top_builddir)/$(TOR_RUST_LIB_PATH)
else
rust_ldadd=
endif
@@ -62,6 +60,7 @@ TOR_UTIL_LIBS = \
src/lib/libtor-malloc.a \
src/lib/libtor-wallclock.a \
src/lib/libtor-err.a \
+ src/lib/libtor-version.a \
src/lib/libtor-intmath.a \
src/lib/libtor-ctime.a
@@ -91,6 +90,7 @@ TOR_UTIL_TESTING_LIBS = \
src/lib/libtor-malloc-testing.a \
src/lib/libtor-wallclock-testing.a \
src/lib/libtor-err-testing.a \
+ src/lib/libtor-version-testing.a \
src/lib/libtor-intmath.a \
src/lib/libtor-ctime-testing.a
endif
diff --git a/changes/bug21900 b/changes/bug21900
new file mode 100644
index 0000000000..686cb6c584
--- /dev/null
+++ b/changes/bug21900
@@ -0,0 +1,4 @@
+ o Minor bugfixes (DNS):
+ - Gracefully handle empty or absent resolve.conf file by falling
+ back to using localhost DNS service and hoping it works. Fixes
+ bug 21900; bugfix on 0.2.1.10-alpha.
diff --git a/changes/bug23082 b/changes/bug23082
new file mode 100644
index 0000000000..fc4b52c364
--- /dev/null
+++ b/changes/bug23082
@@ -0,0 +1,4 @@
+ o Minor bugfixes (networking):
+ - Introduce additional checks into tor_addr_parse() to
+ reject certain incorrect inputs that previously were
+ not detected. Fixes bug 23082; bugfix on 0.2.0.10-alpha.
diff --git a/changes/bug24393 b/changes/bug24393
new file mode 100644
index 0000000000..e190192319
--- /dev/null
+++ b/changes/bug24393
@@ -0,0 +1,6 @@
+ o Minor features (ipv6):
+ - When using addrs_in_same_network_family(), check IPv6 subnets as well as
+ IPv4 ones where possible when a client chooses circuit paths. Previously,
+ we used this function only for IPv4 subnets. Closes ticket 24393. Patch
+ by Neel Chauhan.
+
diff --git a/changes/bug25885 b/changes/bug25885
new file mode 100644
index 0000000000..1b89acfe06
--- /dev/null
+++ b/changes/bug25885
@@ -0,0 +1,7 @@
+ o Minor bugfixes (guards):
+ - In count_acceptable_nodes(), check if we have at least one bridge
+ or guard node, and two non-guard nodes for a circuit. Previously,
+ we have added up the sum of all nodes with a descriptor, but that
+ could cause us to build circuits that fail if we had either too
+ many bridges, or not enough guard nodes. Fixes bug 25885; bugfix
+ on 0.3.6.1-alpha. Patch by Neel Chauhan.
diff --git a/changes/bug27707 b/changes/bug27707
new file mode 100644
index 0000000000..e114222741
--- /dev/null
+++ b/changes/bug27707
@@ -0,0 +1,3 @@
+ o Minor features (log messages):
+ - Improve log message in HSv3 service that could print out negative
+ revision counters. Closes ticket 27707. Patch by "ffmancera". \ No newline at end of file
diff --git a/changes/bug27929 b/changes/bug27929
new file mode 100644
index 0000000000..dab57a2eca
--- /dev/null
+++ b/changes/bug27929
@@ -0,0 +1,5 @@
+ o Minor bugfixes (periodic events):
+ - Refrain from calling routerlist_remove_old_routers() from
+ check_descriptor_callback(). Instead, create a new periodic
+ event that will run once every hour even if Tor is not configured
+ as onion router. Fixes bug 27929; bugfix on 0.2.8.1-alpha.
diff --git a/changes/bug28096 b/changes/bug28096
new file mode 100644
index 0000000000..6847df9798
--- /dev/null
+++ b/changes/bug28096
@@ -0,0 +1,13 @@
+ o Minor bugfixes (Windows):
+ - Correctly identify Windows 8.1, Windows 10, and Windows Server 2008
+ and later from their NT versions.
+ Fixes bug 28096; bugfix on 0.2.2.34; reported by Keifer Bly.
+ - On recent Windows versions, the GetVersionEx() function may report
+ an earlier Windows version than the running OS. To avoid user
+ confusion, add "[or later]" to Tor's version string on affected
+ versions of Windows.
+ Fixes bug 28096; bugfix on 0.2.2.34; reported by Keifer Bly.
+ - Remove Windows versions that were never supported by the
+ GetVersionEx() function. Stop duplicating the latest Windows
+ version in get_uname().
+ Fixes bug 28096; bugfix on 0.2.2.34; reported by Keifer Bly.
diff --git a/changes/bug28554 b/changes/bug28554
new file mode 100644
index 0000000000..9a0b281406
--- /dev/null
+++ b/changes/bug28554
@@ -0,0 +1,3 @@
+ o Minor bugfixes (unit tests, guard selection):
+ - Stop leaking memory in an entry guard unit test. Fixes bug 28554;
+ bugfix on 0.3.0.1-alpha.
diff --git a/changes/feature27244 b/changes/feature27244
new file mode 100644
index 0000000000..a4debbbe53
--- /dev/null
+++ b/changes/feature27244
@@ -0,0 +1,5 @@
+ o Minor features (memory usage):
+ - Tor clients no longer need to keep the full text of a consensus in
+ memory in order to parse it, or apply a diff to it. Instead, they
+ use mmap() to read the consensus files from disk. Closes ticket
+ 27244.
diff --git a/changes/feature27367 b/changes/feature27367
new file mode 100644
index 0000000000..99c0839621
--- /dev/null
+++ b/changes/feature27367
@@ -0,0 +1,4 @@
+ o Minor features (parsing):
+ - Directory authorities now validate that router descriptors and ExtraInfo
+ documents are in a valid subset of UTF-8, and reject them if not.
+ Closes ticket 27367.
diff --git a/changes/subsystems b/changes/subsystems
new file mode 100644
index 0000000000..a51fb8e2b1
--- /dev/null
+++ b/changes/subsystems
@@ -0,0 +1,6 @@
+ o Major features (refactoring):
+ - Tor now uses an explicit list of its own subsystems when initializing
+ and shutting down. Previously, these systems were managed implicitly
+ though various places throughout the codebase. (There still some
+ subsystems using the old system.)
+ Closes ticket 28330.
diff --git a/changes/ticket27225 b/changes/ticket27225
new file mode 100644
index 0000000000..4c05a269d6
--- /dev/null
+++ b/changes/ticket27225
@@ -0,0 +1,5 @@
+ o Minor features (performance):
+ - Avoid parsing the same protocol-versions string over and over
+ in summarize_protover_flags(). This should save us a huge number
+ of malloc calls on startup, and may reduce memory fragmentation with
+ some allocators. Closes ticket 27225.
diff --git a/changes/ticket27359 b/changes/ticket27359
new file mode 100644
index 0000000000..bddc90634d
--- /dev/null
+++ b/changes/ticket27359
@@ -0,0 +1,3 @@
+ o Minor features (memory usage):
+ - Store microdescriptor family lists with a more compact representation
+ to save memory. Closes ticket 27359.
diff --git a/changes/ticket27549 b/changes/ticket27549
new file mode 100644
index 0000000000..51d0f24757
--- /dev/null
+++ b/changes/ticket27549
@@ -0,0 +1,3 @@
+ o Code simplification and refactoring (hidden service v3):
+ - Consolidate the authorized client descriptor cookie computation code
+ from client and service into one function. Closes ticket 27549.
diff --git a/changes/ticket27625 b/changes/ticket27625
new file mode 100644
index 0000000000..33d40adf34
--- /dev/null
+++ b/changes/ticket27625
@@ -0,0 +1,4 @@
+ o Testing:
+ - Write some unit tests for tokenize_string() and
+ get_next_token() functions. Resolves ticket 27625.
+
diff --git a/changes/ticket28006 b/changes/ticket28006
new file mode 100644
index 0000000000..95a4b2cae4
--- /dev/null
+++ b/changes/ticket28006
@@ -0,0 +1,3 @@
+ o Code simplification and refactoring:
+ - Fix issues that shellcheck found in chutney-git-bisect.sh.
+ Resolves ticket 28006.
diff --git a/changes/ticket28008 b/changes/ticket28008
new file mode 100644
index 0000000000..1f0de1a14d
--- /dev/null
+++ b/changes/ticket28008
@@ -0,0 +1,3 @@
+ o Code simplification and refactoring:
+ - Fix shellcheck warnings in scripts/test/coverage. Resolves issue
+ 28008.
diff --git a/changes/ticket28009 b/changes/ticket28009
new file mode 100644
index 0000000000..1d986d4211
--- /dev/null
+++ b/changes/ticket28009
@@ -0,0 +1,3 @@
+ o Code simplification and refactoring:
+ - Fix shellcheck warnings in cov-diff script. Resolves issue
+ 28009.
diff --git a/changes/ticket28010 b/changes/ticket28010
new file mode 100644
index 0000000000..4fca17d022
--- /dev/null
+++ b/changes/ticket28010
@@ -0,0 +1,3 @@
+ o Code simplification and refactoring:
+ - Fix shellcheck warnings in run_trunnel.sh. Resolves issue
+ 28010.
diff --git a/changes/ticket28011 b/changes/ticket28011
new file mode 100644
index 0000000000..5efc3c917b
--- /dev/null
+++ b/changes/ticket28011
@@ -0,0 +1,3 @@
+ o Code simplification and refactoring:
+ - Fix shellcheck warnings in run_calltool.sh. Resolves
+ ticket 28011.
diff --git a/changes/ticket28077 b/changes/ticket28077
new file mode 100644
index 0000000000..2b5afb1678
--- /dev/null
+++ b/changes/ticket28077
@@ -0,0 +1,3 @@
+ o Code simplification and refactoring:
+ - Remove unnecessarily unsafe code from the rust macro cstr!. Closes
+ ticket 28077.
diff --git a/changes/ticket28100 b/changes/ticket28100
new file mode 100644
index 0000000000..b8e3271013
--- /dev/null
+++ b/changes/ticket28100
@@ -0,0 +1,3 @@
+ o Minor features (HTTP standards compliance):
+ - Don't send Content-Type: application/octet-stream for transparently
+ compressed documents, which confused browsers. Closes ticket 28100.
diff --git a/changes/ticket28128 b/changes/ticket28128
new file mode 100644
index 0000000000..6d08c74242
--- /dev/null
+++ b/changes/ticket28128
@@ -0,0 +1,4 @@
+ o Documentation (hidden service manpage, sandbox):
+ - Document in the man page that changing ClientOnionAuthDir value or
+ adding a new file in the directory will not work at runtime upon sending
+ a HUP if Sandbox 1. Closes ticket 28128.
diff --git a/changes/ticket28229_diag b/changes/ticket28229_diag
new file mode 100644
index 0000000000..cd02b81faa
--- /dev/null
+++ b/changes/ticket28229_diag
@@ -0,0 +1,3 @@
+ o Testing:
+ - Increase logging and tag all log entries with timestamps
+ in test_rebind.py. Provides diagnostics for issue 28229.
diff --git a/changes/ticket28335 b/changes/ticket28335
new file mode 100644
index 0000000000..eecf7c7fd9
--- /dev/null
+++ b/changes/ticket28335
@@ -0,0 +1,7 @@
+ o Major features (client):
+ - When Tor is running as a client, and it is unused for a long time, it
+ can now enter a "dormant" state. When Tor is dormant, it avoids
+ network activity and CPU wakeups until it is reawoken either by a user
+ request or by a controller command. For more information, see
+ the configuration options starting with "Dormant". Implements tickets
+ 2149 and 28335.
diff --git a/changes/ticket28574 b/changes/ticket28574
new file mode 100644
index 0000000000..562810f511
--- /dev/null
+++ b/changes/ticket28574
@@ -0,0 +1,4 @@
+ o Minor bugfixes (continuous integration, Windows):
+ - Explicitly specify the path to the OpenSSL library and do not download
+ OpenSSL from Pacman, but instead use the library that is already provided
+ by AppVeyor. Fixes bug 28574; bugfix on master.
diff --git a/configure.ac b/configure.ac
index c0d58c4faa..31e41c3bbc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4,7 +4,7 @@ dnl Copyright (c) 2007-2018, The Tor Project, Inc.
dnl See LICENSE for licensing information
AC_PREREQ([2.63])
-AC_INIT([tor],[0.3.5.5-alpha-dev])
+AC_INIT([tor],[0.4.0.0-alpha-dev])
AC_CONFIG_SRCDIR([src/app/main/tor_main.c])
AC_CONFIG_MACRO_DIR([m4])
@@ -503,15 +503,6 @@ if test "x$enable_rust" = "xyes"; then
fi
fi
- dnl This is a workaround for #46797
- dnl (a.k.a https://github.com/rust-lang/rust/issues/46797 ). Once the
- dnl upstream bug is fixed, we can remove this workaround.
- case "$host_os" in
- darwin*)
- TOR_RUST_EXTRA_LIBS="-lresolv"
- ;;
- esac
-
dnl For now both MSVC and MinGW rust libraries will output static libs with
dnl the MSVC naming convention.
if test "$bwin32" = "true"; then
@@ -556,8 +547,6 @@ if test "x$enable_rust" = "xyes"; then
AC_MSG_RESULT([$RUSTC_VERSION])
fi
-AC_SUBST(TOR_RUST_EXTRA_LIBS)
-
AC_SEARCH_LIBS(socket, [socket network])
AC_SEARCH_LIBS(gethostbyname, [nsl])
AC_SEARCH_LIBS(dlopen, [dl])
@@ -812,6 +801,8 @@ fi
dnl Now check for particular libevent functions.
AC_CHECK_FUNCS([evutil_secure_rng_set_urandom_device_file \
evutil_secure_rng_add_bytes \
+ evdns_base_get_nameserver_addr \
+
])
LIBS="$save_LIBS"
diff --git a/contrib/win32build/tor-mingw.nsi.in b/contrib/win32build/tor-mingw.nsi.in
index 99685598fa..af01a2b499 100644
--- a/contrib/win32build/tor-mingw.nsi.in
+++ b/contrib/win32build/tor-mingw.nsi.in
@@ -8,7 +8,7 @@
!include "LogicLib.nsh"
!include "FileFunc.nsh"
!insertmacro GetParameters
-!define VERSION "0.3.5.5-alpha-dev"
+!define VERSION "0.4.0.0-alpha-dev"
!define INSTALLER "tor-${VERSION}-win32.exe"
!define WEBSITE "https://www.torproject.org/"
!define LICENSE "LICENSE"
diff --git a/doc/HACKING/Maintaining.md b/doc/HACKING/Maintaining.md
new file mode 100644
index 0000000000..22d62b5471
--- /dev/null
+++ b/doc/HACKING/Maintaining.md
@@ -0,0 +1,113 @@
+# Maintaining Tor
+
+This document details the duties and processes on maintaining the Tor code
+base.
+
+The first section describes who is the current Tor maintainer and what are the
+responsabilities. Tor has one main single maintainer but does have many
+committers and subsystem maintainers.
+
+The second third section describes how the **alpha and master** branches are
+maintained and by whom.
+
+Finally, the last section describes how the **stable** branches are maintained
+and by whom.
+
+This document does not cover how Tor is released, please see
+[ReleasingTor.md](ReleasingTor.md) for that information.
+
+## Tor Maintainer
+
+The current maintainer is Nick Mathewson <nickm@torproject.org>.
+
+The maintainer takes final decisions in terms of engineering, architecture and
+protocol design. Releasing Tor falls under their responsability.
+
+## Alpha and Master Branches
+
+The Tor repository always has at all time a **master** branch which contains
+the upstream ongoing development.
+
+It may also contains a branch for a released feature freezed version which is
+called the **alpha** branch. The git tag and version number is always
+postfixed with `-alpha[-dev]`. For example: `tor-0.3.5.0-alpha-dev` or
+`tor-0.3.5.3-alpha`.
+
+Tor is separated into subsystems and some of those are maintained by other
+developers than the main maintainer. Those people have commit access to the
+code base but only commit (in most cases) into the subsystem they maintain.
+
+Upstream merges are restricted to the alpha and master branches. Subsystem
+maintainers should never push a patch into a stable branch which is the
+responsability of the [stable branch maintainer](#stable-branches).
+
+### Who
+
+In alphabetical order, the following people have upstream commit access and
+maintain the following subsystems:
+
+- David Goulet <dgoulet@torproject.org>
+ * Onion Service (including Shared Random).
+ ***keywords:*** *[tor-hs]*
+ * Channels, Circuitmux, Connection, Scheduler.
+ ***keywords:*** *[tor-chan, tor-cmux, tor-sched, tor-conn]*
+ * Cell Logic (Handling/Parsing).
+ ***keywords:*** *[tor-cell]*
+ * Threading backend.
+ ***keywords:*** *[tor-thread]*
+
+- George Kadianakis <asn@torproject.org>
+ * Onion Service (including Shared Random).
+ ***keywords:*** *[tor-hs]*
+ * Guard.
+ ***keywords:*** *[tor-guard]*
+ * Pluggable Transport (excluding Bridge networking).
+ ***keywords:*** *[tor-pt]*
+
+### Tasks
+
+These are the tasks of a subsystem maintainer:
+
+1. Regurlarly go over `merge_ready` tickets relevant to the related subsystem
+ and for the current alpha or development (master branch) Milestone.
+
+2. A subsystem maintainer is expected to contribute to any design changes
+ (including proposals) or large patch set about the subsystem.
+
+3. Leave their ego at the door. Mistakes will be made but they have to be
+ taking care of seriously. Learn and move on quickly.
+
+### Merging Policy
+
+These are few important items to follow when merging code upstream:
+
+1. To merge code upstream, the patch must have passed our CI (currently
+ github.com/torproject), have a corresponding ticket and reviewed by
+ **at least** one person that is not the original coder.
+
+ Example A: If Alice writes a patch then Bob, a Tor network team member,
+ reviews it and flags it `merge_ready`. Then, the maintainter is required
+ to look at the patch and makes a decision.
+
+ Example B: If the maintainer writes a patch then Bob, a Tor network
+ team member, reviews it and flags it `merge_ready`, then the maintainer
+ can merge the code upstream.
+
+2. Maintainer makes sure the commit message should describe what was fixed
+ and, if it applies, how was it fixed. It should also always refer to
+ the ticket number.
+
+3. Trivial patches such as comment change, documentation, syntax issues or
+ typos can be merged without a ticket or reviewers.
+
+4. Tor uses the "merge forward" method that is if a patch applies to the
+ alpha branch, it has to be merged there first and then merged forward
+ into master.
+
+5. Maintainer should always consult with the network team about any doubts,
+ mis-understandings or unknowns of a patch. Final word will always go to the
+ main Tor maintainer.
+
+## Stable Branches
+
+(Currently being drafted and reviewed by the network team.)
diff --git a/doc/HACKING/ReleasingTor.md b/doc/HACKING/ReleasingTor.md
index 55a40fc89b..b5444afa96 100644
--- a/doc/HACKING/ReleasingTor.md
+++ b/doc/HACKING/ReleasingTor.md
@@ -36,10 +36,10 @@ new Tor release:
What about clang scan-build?
- Does 'make distcheck' complain?
+ Does `make distcheck` complain?
- How about 'make test-stem' and 'make test-network' and
- `make test-network-full`?
+ How about `make test-stem` and `make test-network` and
+ `make test-network-all`?
- Are all those tests still happy with --enable-expensive-hardening ?
@@ -79,7 +79,7 @@ new Tor release:
Present and imperative tense: not past.
- 'Relays', not 'servers' or 'nodes' or 'Tor relays'.
+ "Relays", not "servers" or "nodes" or "Tor relays".
"Stop FOOing", not "Fix a bug where we would FOO".
@@ -100,7 +100,7 @@ new Tor release:
For stable releases that backport things from later, we try to compose
their releases, we try to make sure that we keep the changelog entries
- identical to their original versions, with a 'backport from 0.x.y.z'
+ identical to their original versions, with a "backport from 0.x.y.z"
note added to each section. So in this case, once you have the items
from the changes files copied together, don't use them to build a new
changelog: instead, look up the corrected versions that were merged
diff --git a/doc/tor.1.txt b/doc/tor.1.txt
index 019c8af757..e94382b66b 100644
--- a/doc/tor.1.txt
+++ b/doc/tor.1.txt
@@ -611,6 +611,7 @@ GENERAL OPTIONS
ServerDNSResolvConfFile
Tor must remain in client or server mode (some changes to ClientOnly and
ORPort are not allowed).
+ ClientOnionAuthDir and any files in it won't reload on HUP signal.
(Default: 0)
[[Socks4Proxy]] **Socks4Proxy** __host__[:__port__]::
@@ -1789,6 +1790,19 @@ The following options are useful only for clients (that is, if
Try this many simultaneous connections to download a consensus before
waiting for one to complete, timeout, or error out. (Default: 3)
+[[DormantClientTimeout]] **DormantClientTimeout** __N__ **minutes**|**hours**|**days**|**weeks**::
+ If Tor spends this much time without any client activity,
+ enter a dormant state where automatic circuits are not built, and
+ directory information is not fetched.
+ Does not affect servers or onion services. Must be at least 10 minutes.
+ (Default: 24 hours)
+
+[[DormantTimeoutDisabledByIdleStreams]] **DormantTimeoutDisabledByIdleStreams **0**|**1**::
+ If true, then any open client stream (even one not reading or writing)
+ counts as client activity for the purpose of DormantClientTimeout.
+ If false, then only network activity counts. (Default: 1)
+
+
SERVER OPTIONS
--------------
@@ -2211,7 +2225,8 @@ is non-zero):
__filename__. The file format is the same as the standard Unix
"**resolv.conf**" file (7). This option, like all other ServerDNS options,
only affects name lookups that your server does on behalf of clients.
- (Defaults to use the system DNS configuration.)
+ (Defaults to use the system DNS configuration or a localhost DNS service
+ in case no nameservers are found in a given configuration.)
[[ServerDNSAllowBrokenConfig]] **ServerDNSAllowBrokenConfig** **0**|**1**::
If this option is false, Tor exits immediately if there are problems
diff --git a/scripts/codegen/run_trunnel.sh b/scripts/codegen/run_trunnel.sh
index 428804342a..645b3c2158 100755
--- a/scripts/codegen/run_trunnel.sh
+++ b/scripts/codegen/run_trunnel.sh
@@ -9,9 +9,7 @@ OPTIONS="--require-version=1.5.1"
# Get all .trunnel files recursively from that directory so we can support
# multiple sub-directories.
-for file in `find ./src/trunnel/ -name '*.trunnel'`; do
- python -m trunnel ${OPTIONS} $file
-done
+find ./src/trunnel/ -name '*.trunnel' -exec python -m trunnel ${OPTIONS} {} \;
python -m trunnel ${OPTIONS} --write-c-files --target-dir=./src/ext/trunnel/
diff --git a/scripts/maint/run_calltool.sh b/scripts/maint/run_calltool.sh
index efb8706fea..b0268322f4 100755
--- a/scripts/maint/run_calltool.sh
+++ b/scripts/maint/run_calltool.sh
@@ -15,10 +15,10 @@ SUBITEMS="fn_graph fn_invgraph fn_scc fn_scc_weaklinks module_graph module_invgr
for calculation in $SUBITEMS; do
echo "======== $calculation"
- python -m calltool $calculation > callgraph/$calculation
+ python -m calltool "$calculation" > callgraph/"$calculation"
done
-echo <<EOF > callgraph/README
+cat <<EOF > callgraph/README
This directory holds output from calltool, as run on Tor. For more
information about each of these files, see the NOTES and README files in
the calltool distribution.
diff --git a/scripts/test/chutney-git-bisect.sh b/scripts/test/chutney-git-bisect.sh
index 8a3f2c70c8..dc1319a27a 100755
--- a/scripts/test/chutney-git-bisect.sh
+++ b/scripts/test/chutney-git-bisect.sh
@@ -20,7 +20,7 @@ if [ ! -z "$1" ]; then
fi
if [ ! -z "$2" ]; then
- cd "$2"
+ cd "$2" || exit
fi
CHUTNEY_TEST_CMD="make test-network-all"
@@ -54,9 +54,9 @@ while [ "$i" -le "$CHUTNEY_TRIES" ]; do
echo "test '$CHUTNEY_TEST_CMD' succeeded after $i/$CHUTNEY_TRIES attempts, good"
exit 0
fi
- i=$[$i+1]
+ i=$((i+1))
done
-i=$[$i-1]
+i=$((i-1))
echo "test '$CHUTNEY_TEST_CMD' failed $i/$CHUTNEY_TRIES attempts, bad"
exit 1
diff --git a/scripts/test/cov-diff b/scripts/test/cov-diff
index 6179dff63e..f3ca856888 100755
--- a/scripts/test/cov-diff
+++ b/scripts/test/cov-diff
@@ -7,9 +7,9 @@
DIRA="$1"
DIRB="$2"
-for B in $DIRB/*; do
- A=$DIRA/`basename $B`
- if [ -f $A ]; then
+for B in "$DIRB"/*; do
+ A=$DIRA/$(basename "$B")
+ if [ -f "$A" ]; then
perl -pe 's/^\s*\!*\d+(\*?):/ 1$1:/; s/^([^:]+:)[\d\s]+:/$1/; s/^ *-:(Runs|Programs):.*//;' "$A" > "$A.tmp"
else
cat /dev/null > "$A.tmp"
diff --git a/scripts/test/coverage b/scripts/test/coverage
index 59d468ee1e..7a417cdc89 100755
--- a/scripts/test/coverage
+++ b/scripts/test/coverage
@@ -8,30 +8,30 @@
dst=$1
for fn in src/core/*/*.c src/feature/*/*.c src/app/*/*.c src/lib/*/*.c; do
- BN=`basename $fn`
- DN=`dirname $fn`
- F=`echo $BN | sed -e 's/\.c$//;'`
+ BN=$(basename "$fn")
+ DN=$(dirname "$fn")
+ F=$(echo "$BN" | sed -e 's/\.c$//;')
GC="${BN}.gcov"
# Figure out the object file names
- ONS=`echo ${DN}/src_*-${F}.o`
+ ONS=$(echo "${DN}"/src_*-"${F}".o)
ONS_WILDCARD_LITERAL="${DN}/src_*-${F}.o"
# If the wildcard didn't expand, no files
if [ "$ONS" != "${ONS_WILDCARD_LITERAL}" ]
then
for on in $ONS; do
# We should have a gcno file
- GCNO=`echo $on | sed -e 's/\.o$/\.gcno/;'`
- if [ -e $GCNO ]
+ GCNO=$(echo "$on" | sed -e 's/\.o$/\.gcno/;')
+ if [ -e "$GCNO" ]
then
# No need to test for gcda, since gcov assumes no execution
# if it's absent
- rm -f $GC
- gcov -o $on $fn
- if [ -e $GC ]
+ rm -f "$GC"
+ gcov -o "$on" "$fn"
+ if [ -e "$GC" ]
then
if [ -d "$dst" ]
then
- mv $GC $dst/$GC
+ mv "$GC" "$dst"/"$GC"
fi
else
echo "gcov -o $on $fn didn't make a .gcov file"
diff --git a/src/app/config/config.c b/src/app/config/config.c
index 81cc3e378f..90eae50fdd 100644
--- a/src/app/config/config.c
+++ b/src/app/config/config.c
@@ -64,6 +64,7 @@
#include "app/config/confparse.h"
#include "app/config/statefile.h"
#include "app/main/main.h"
+#include "app/main/subsysmgr.h"
#include "core/mainloop/connection.h"
#include "core/mainloop/cpuworker.h"
#include "core/mainloop/mainloop.h"
@@ -112,9 +113,9 @@
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_util.h"
#include "lib/encoding/confline.h"
-#include "lib/log/git_revision.h"
#include "lib/net/resolve.h"
#include "lib/sandbox/sandbox.h"
+#include "lib/version/torversion.h"
#ifdef ENABLE_NSS
#include "lib/crypt_ops/crypto_nss_mgt.h"
@@ -388,6 +389,8 @@ static config_var_t option_vars_[] = {
OBSOLETE("DynamicDHGroups"),
VPORT(DNSPort),
OBSOLETE("DNSListenAddress"),
+ V(DormantClientTimeout, INTERVAL, "24 hours"),
+ V(DormantTimeoutDisabledByIdleStreams, BOOL, "1"),
/* DoS circuit creation options. */
V(DoSCircuitCreationEnabled, AUTOBOOL, "auto"),
V(DoSCircuitCreationMinConnections, UINT, "0"),
@@ -972,42 +975,6 @@ set_options(or_options_t *new_val, char **msg)
return 0;
}
-/** 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
- * descriptor. (Does not include the git version, if any.) */
-static char *the_short_tor_version = NULL;
-
-/** Return the current Tor version. */
-const char *
-get_version(void)
-{
- if (the_tor_version == NULL) {
- if (strlen(tor_git_revision)) {
- tor_asprintf(&the_tor_version, "%s (git-%s)", get_short_version(),
- tor_git_revision);
- } else {
- the_tor_version = tor_strdup(get_short_version());
- }
- }
- return the_tor_version;
-}
-
-/** Return the current Tor version, without any git tag. */
-const char *
-get_short_version(void)
-{
-
- if (the_short_tor_version == NULL) {
-#ifdef TOR_BUILD_TAG
- tor_asprintf(&the_short_tor_version, "%s (%s)", VERSION, TOR_BUILD_TAG);
-#else
- the_short_tor_version = tor_strdup(VERSION);
-#endif
- }
- return the_short_tor_version;
-}
-
/** Release additional memory allocated in options
*/
STATIC void
@@ -1067,9 +1034,6 @@ config_free_all(void)
tor_free(torrc_defaults_fname);
tor_free(global_dirfrontpagecontents);
- tor_free(the_short_tor_version);
- tor_free(the_tor_version);
-
cleanup_protocol_warning_severity_level();
have_parsed_cmdline = 0;
@@ -1432,10 +1396,10 @@ options_act_reversible(const or_options_t *old_options, char **msg)
* processes. */
if (running_tor && options->RunAsDaemon) {
if (! start_daemon_has_been_called())
- crypto_prefork();
+ subsystems_prefork();
/* No need to roll back, since you can't change the value. */
if (start_daemon())
- crypto_postfork();
+ subsystems_postfork();
}
#ifdef HAVE_SYSTEMD
@@ -2031,9 +1995,6 @@ options_act(const or_options_t *old_options)
finish_daemon(options->DataDirectory);
}
- /* See whether we need to enable/disable our once-a-second timer. */
- reschedule_per_second_timer();
-
/* We want to reinit keys as needed before we do much of anything else:
keys are important, and other things can depend on them. */
if (transition_affects_workers ||
@@ -3877,6 +3838,10 @@ options_validate(or_options_t *old_options, or_options_t *options,
"default.");
}
+ if (options->DormantClientTimeout < 10*60 && !options->TestingTorNetwork) {
+ REJECT("DormantClientTimeout is too low. It must be at least 10 minutes.");
+ }
+
if (options->PathBiasNoticeRate > 1.0) {
tor_asprintf(msg,
"PathBiasNoticeRate is too high. "
diff --git a/src/app/config/config.h b/src/app/config/config.h
index a169cfd451..4c497b83a6 100644
--- a/src/app/config/config.h
+++ b/src/app/config/config.h
@@ -41,8 +41,6 @@ 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);
/** An error from options_trial_assign() or options_init_from_string(). */
typedef enum setopt_err_t {
diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h
index 3524b99b53..6cbc86ec18 100644
--- a/src/app/config/or_options_st.h
+++ b/src/app/config/or_options_st.h
@@ -1072,6 +1072,16 @@ struct or_options_t {
/** Autobool: Do we refuse single hop client rendezvous? */
int DoSRefuseSingleHopClientRendezvous;
+
+ /** Interval: how long without activity does it take for a client
+ * to become dormant?
+ **/
+ int DormantClientTimeout;
+
+ /** Boolean: true if having an idle stream is sufficient to prevent a client
+ * from becoming dormant.
+ **/
+ int DormantTimeoutDisabledByIdleStreams;
};
#endif
diff --git a/src/app/config/statefile.c b/src/app/config/statefile.c
index 8a8b7ced01..4ba7be1519 100644
--- a/src/app/config/statefile.c
+++ b/src/app/config/statefile.c
@@ -45,6 +45,7 @@
#include "app/config/statefile.h"
#include "lib/encoding/confline.h"
#include "lib/net/resolve.h"
+#include "lib/version/torversion.h"
#include "app/config/or_state_st.h"
diff --git a/src/app/main/main.c b/src/app/main/main.c
index ae87add67d..03b3a95d03 100644
--- a/src/app/main/main.c
+++ b/src/app/main/main.c
@@ -15,6 +15,7 @@
#include "app/config/statefile.h"
#include "app/main/main.h"
#include "app/main/ntmain.h"
+#include "app/main/subsysmgr.h"
#include "core/mainloop/connection.h"
#include "core/mainloop/cpuworker.h"
#include "core/mainloop/mainloop.h"
@@ -33,6 +34,7 @@
#include "core/or/relay.h"
#include "core/or/scheduler.h"
#include "core/or/status.h"
+#include "core/or/versions.h"
#include "feature/api/tor_api.h"
#include "feature/api/tor_api_internal.h"
#include "feature/client/addressmap.h"
@@ -68,7 +70,6 @@
#include "lib/container/buffers.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_s2k.h"
-#include "lib/err/backtrace.h"
#include "lib/geoip/geoip.h"
#include "lib/process/waitpid.h"
@@ -83,6 +84,7 @@
#include "lib/encoding/confline.h"
#include "lib/evloop/timers.h"
#include "lib/crypt_ops/crypto_init.h"
+#include "lib/version/torversion.h"
#include <event2/event.h>
@@ -301,6 +303,19 @@ process_signal(int sig)
log_heartbeat(time(NULL));
control_event_signal(sig);
break;
+ case SIGACTIVE:
+ /* "SIGACTIVE" counts as ersatz user activity. */
+ note_user_activity(approx_time());
+ control_event_signal(sig);
+ break;
+ case SIGDORMANT:
+ /* "SIGDORMANT" means to ignore past user activity */
+ log_notice(LD_GENERAL, "Going dormant because of controller request.");
+ reset_user_activity(0);
+ set_network_participation(false);
+ schedule_rescan_periodic_events();
+ control_event_signal(sig);
+ break;
}
}
@@ -426,18 +441,6 @@ dumpstats(int severity)
rend_service_dump_stats(severity);
}
-/** Called by exit() as we shut down the process.
- */
-static void
-exit_function(void)
-{
- /* NOTE: If we ever daemonize, this gets called immediately. That's
- * okay for now, because we only use this on Windows. */
-#ifdef _WIN32
- WSACleanup();
-#endif
-}
-
#ifdef _WIN32
#define UNIX_ONLY 0
#else
@@ -482,6 +485,8 @@ static struct {
{ SIGNEWNYM, 0, NULL },
{ SIGCLEARDNSCACHE, 0, NULL },
{ SIGHEARTBEAT, 0, NULL },
+ { SIGACTIVE, 0, NULL },
+ { SIGDORMANT, 0, NULL },
{ -1, -1, NULL }
};
@@ -546,12 +551,6 @@ tor_init(int argc, char *argv[])
tor_snprintf(progname, sizeof(progname), "Tor %s", get_version());
log_set_application_name(progname);
- /* Set up the crypto nice and early */
- if (crypto_early_init() < 0) {
- log_err(LD_GENERAL, "Unable to initialize the crypto subsystem!");
- return -1;
- }
-
/* Initialize the history structures. */
rep_hist_init();
/* Initialize the service cache. */
@@ -631,12 +630,6 @@ tor_init(int argc, char *argv[])
rust_log_welcome_string();
#endif /* defined(HAVE_RUST) */
- if (network_init()<0) {
- log_err(LD_BUG,"Error initializing network; exiting.");
- return -1;
- }
- atexit(exit_function);
-
int init_rv = options_init_from_torrc(argc,argv);
if (init_rv < 0) {
log_err(LD_CONFIG,"Reading config failed--see warnings above.");
@@ -783,7 +776,6 @@ tor_free_all(int postfork)
routerparse_free_all();
ext_orport_free_all();
control_free_all();
- tor_free_getaddrinfo_cache();
protover_free_all();
bridges_free_all();
consdiffmgr_free_all();
@@ -791,6 +783,7 @@ tor_free_all(int postfork)
dos_free_all();
circuitmux_ewma_free_all();
accounting_free_all();
+ protover_summary_cache_free_all();
if (!postfork) {
config_free_all();
@@ -800,7 +793,6 @@ tor_free_all(int postfork)
policies_free_all();
}
if (!postfork) {
- tor_tls_free_all();
#ifndef _WIN32
tor_getpwnam(NULL);
#endif
@@ -813,12 +805,12 @@ tor_free_all(int postfork)
release_lockfile();
}
tor_libevent_free_all();
+
+ subsystems_shutdown();
+
/* Stuff in util.c and address.c*/
if (!postfork) {
- escaped(NULL);
esc_router_info(NULL);
- clean_up_backtrace_handler();
- logs_free_all(); /* free log strings. do this last so logs keep working. */
}
}
@@ -877,7 +869,6 @@ tor_cleanup(void)
later, if it makes shutdown unacceptably slow. But for
now, leave it here: it's helped us catch bugs in the
past. */
- crypto_global_cleanup();
}
/** Read/create keys as needed, and echo our fingerprint to stdout. */
@@ -1273,7 +1264,6 @@ static int
run_tor_main_loop(void)
{
handle_signals();
- monotime_init();
timers_initialize();
initialize_mainloop_events();
@@ -1385,54 +1375,13 @@ tor_run_main(const tor_main_configuration_t *tor_cfg)
{
int result = 0;
-#ifdef _WIN32
-#ifndef HeapEnableTerminationOnCorruption
-#define HeapEnableTerminationOnCorruption 1
-#endif
- /* On heap corruption, just give up; don't try to play along. */
- HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
-
- /* SetProcessDEPPolicy is only supported on 32-bit Windows.
- * (On 64-bit Windows it always fails, and some compilers don't like the
- * PSETDEP cast.)
- * 32-bit Windows defines _WIN32.
- * 64-bit Windows defines _WIN32 and _WIN64. */
-#ifndef _WIN64
- /* Call SetProcessDEPPolicy to permanently enable DEP.
- The function will not resolve on earlier versions of Windows,
- and failure is not dangerous. */
- HMODULE hMod = GetModuleHandleA("Kernel32.dll");
- if (hMod) {
- typedef BOOL (WINAPI *PSETDEP)(DWORD);
- PSETDEP setdeppolicy = (PSETDEP)GetProcAddress(hMod,
- "SetProcessDEPPolicy");
- if (setdeppolicy) {
- /* PROCESS_DEP_ENABLE | PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION */
- setdeppolicy(3);
- }
- }
-#endif /* !defined(_WIN64) */
-#endif /* defined(_WIN32) */
-
- {
- int bt_err = configure_backtrace_handler(get_version());
- if (bt_err < 0) {
- log_warn(LD_BUG, "Unable to install backtrace handler: %s",
- strerror(-bt_err));
- }
- }
-
#ifdef EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED
event_set_mem_functions(tor_malloc_, tor_realloc_, tor_free_);
#endif
- init_protocol_warning_severity_level();
+ subsystems_init();
- update_approx_time(time(NULL));
- tor_threads_init();
- tor_compress_init();
- init_logging(0);
- monotime_init();
+ init_protocol_warning_severity_level();
int argc = tor_cfg->argc + tor_cfg->argc_owned;
char **argv = tor_calloc(argc, sizeof(char*));
diff --git a/src/app/main/subsysmgr.c b/src/app/main/subsysmgr.c
new file mode 100644
index 0000000000..abd2edd10b
--- /dev/null
+++ b/src/app/main/subsysmgr.c
@@ -0,0 +1,202 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "app/main/subsysmgr.h"
+#include "lib/err/torerr.h"
+
+#include "lib/log/log.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * True iff we have checked tor_subsystems for consistency.
+ **/
+static bool subsystem_array_validated = false;
+
+/**
+ * True if a given subsystem is initialized. Expand this array if there
+ * are more than this number of subsystems. (We'd rather not
+ * dynamically allocate in this module.)
+ **/
+static bool sys_initialized[128];
+
+/**
+ * Exit with a raw assertion if the subsystems list is inconsistent;
+ * initialize the subsystem_initialized array.
+ **/
+static void
+check_and_setup(void)
+{
+ if (subsystem_array_validated)
+ return;
+
+ raw_assert(ARRAY_LENGTH(sys_initialized) >= n_tor_subsystems);
+ memset(sys_initialized, 0, sizeof(sys_initialized));
+
+ int last_level = MIN_SUBSYS_LEVEL;
+
+ for (unsigned i = 0; i < n_tor_subsystems; ++i) {
+ const subsys_fns_t *sys = tor_subsystems[i];
+ if (sys->level < MIN_SUBSYS_LEVEL || sys->level > MAX_SUBSYS_LEVEL) {
+ fprintf(stderr, "BUG: Subsystem %s (at %u) has an invalid level %d. "
+ "It is supposed to be between %d and %d (inclusive).\n",
+ sys->name, i, sys->level, MIN_SUBSYS_LEVEL, MAX_SUBSYS_LEVEL);
+ raw_assert_unreached_msg("There is a bug in subsystem_list.c");
+ }
+ if (sys->level < last_level) {
+ fprintf(stderr, "BUG: Subsystem %s (at #%u) is in the wrong position. "
+ "Its level is %d; but the previous subsystem's level was %d.\n",
+ sys->name, i, sys->level, last_level);
+ raw_assert_unreached_msg("There is a bug in subsystem_list.c");
+ }
+ last_level = sys->level;
+ }
+
+ subsystem_array_validated = true;
+}
+
+/**
+ * Initialize all the subsystems; exit on failure.
+ **/
+int
+subsystems_init(void)
+{
+ return subsystems_init_upto(MAX_SUBSYS_LEVEL);
+}
+
+/**
+ * Initialize all the subsystems whose level is less than or equal to
+ * <b>target_level</b>; exit on failure.
+ **/
+int
+subsystems_init_upto(int target_level)
+{
+ check_and_setup();
+
+ for (unsigned i = 0; i < n_tor_subsystems; ++i) {
+ const subsys_fns_t *sys = tor_subsystems[i];
+ if (!sys->supported)
+ continue;
+ if (sys->level > target_level)
+ break;
+ if (sys_initialized[i])
+ continue;
+ int r = 0;
+ if (sys->initialize) {
+ // Note that the logging subsystem is designed so that it does no harm
+ // to log a message in an uninitialized state. These messages will be
+ // discarded for now, however.
+ log_debug(LD_GENERAL, "Initializing %s", sys->name);
+ r = sys->initialize();
+ }
+ if (r < 0) {
+ fprintf(stderr, "BUG: subsystem %s (at %u) initialization failed.\n",
+ sys->name, i);
+ raw_assert_unreached_msg("A subsystem couldn't be initialized.");
+ }
+ sys_initialized[i] = true;
+ }
+
+ return 0;
+}
+
+/**
+ * Shut down all the subsystems.
+ **/
+void
+subsystems_shutdown(void)
+{
+ subsystems_shutdown_downto(MIN_SUBSYS_LEVEL - 1);
+}
+
+/**
+ * Shut down all the subsystems whose level is above <b>target_level</b>.
+ **/
+void
+subsystems_shutdown_downto(int target_level)
+{
+ check_and_setup();
+
+ for (int i = (int)n_tor_subsystems - 1; i >= 0; --i) {
+ const subsys_fns_t *sys = tor_subsystems[i];
+ if (!sys->supported)
+ continue;
+ if (sys->level <= target_level)
+ break;
+ if (! sys_initialized[i])
+ continue;
+ if (sys->shutdown) {
+ log_debug(LD_GENERAL, "Shutting down %s", sys->name);
+ sys->shutdown();
+ }
+ sys_initialized[i] = false;
+ }
+}
+
+/**
+ * Run pre-fork code on all subsystems that declare any
+ **/
+void
+subsystems_prefork(void)
+{
+ check_and_setup();
+
+ for (int i = (int)n_tor_subsystems - 1; i >= 0; --i) {
+ const subsys_fns_t *sys = tor_subsystems[i];
+ if (!sys->supported)
+ continue;
+ if (! sys_initialized[i])
+ continue;
+ if (sys->prefork) {
+ log_debug(LD_GENERAL, "Pre-fork: %s", sys->name);
+ sys->prefork();
+ }
+ }
+}
+
+/**
+ * Run post-fork code on all subsystems that declare any
+ **/
+void
+subsystems_postfork(void)
+{
+ check_and_setup();
+
+ for (unsigned i = 0; i < n_tor_subsystems; ++i) {
+ const subsys_fns_t *sys = tor_subsystems[i];
+ if (!sys->supported)
+ continue;
+ if (! sys_initialized[i])
+ continue;
+ if (sys->postfork) {
+ log_debug(LD_GENERAL, "Post-fork: %s", sys->name);
+ sys->postfork();
+ }
+ }
+}
+
+/**
+ * Run thread-cleanup code on all subsystems that declare any
+ **/
+void
+subsystems_thread_cleanup(void)
+{
+ check_and_setup();
+
+ for (int i = (int)n_tor_subsystems - 1; i >= 0; --i) {
+ const subsys_fns_t *sys = tor_subsystems[i];
+ if (!sys->supported)
+ continue;
+ if (! sys_initialized[i])
+ continue;
+ if (sys->thread_cleanup) {
+ log_debug(LD_GENERAL, "Thread cleanup: %s", sys->name);
+ sys->thread_cleanup();
+ }
+ }
+}
diff --git a/src/app/main/subsysmgr.h b/src/app/main/subsysmgr.h
new file mode 100644
index 0000000000..4b3cad62ad
--- /dev/null
+++ b/src/app/main/subsysmgr.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_SUBSYSMGR_T
+#define TOR_SUBSYSMGR_T
+
+#include "lib/subsys/subsys.h"
+
+extern const struct subsys_fns_t *tor_subsystems[];
+extern const unsigned n_tor_subsystems;
+
+int subsystems_init(void);
+int subsystems_init_upto(int level);
+
+void subsystems_shutdown(void);
+void subsystems_shutdown_downto(int level);
+
+void subsystems_prefork(void);
+void subsystems_postfork(void);
+void subsystems_thread_cleanup(void);
+
+#endif
diff --git a/src/app/main/subsystem_list.c b/src/app/main/subsystem_list.c
new file mode 100644
index 0000000000..8640329e92
--- /dev/null
+++ b/src/app/main/subsystem_list.c
@@ -0,0 +1,40 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "app/main/subsysmgr.h"
+#include "lib/cc/compat_compiler.h"
+#include "lib/cc/torint.h"
+
+#include "lib/compress/compress_sys.h"
+#include "lib/crypt_ops/crypto_sys.h"
+#include "lib/err/torerr_sys.h"
+#include "lib/log/log_sys.h"
+#include "lib/net/network_sys.h"
+#include "lib/process/winprocess_sys.h"
+#include "lib/thread/thread_sys.h"
+#include "lib/time/time_sys.h"
+#include "lib/tls/tortls_sys.h"
+#include "lib/wallclock/wallclock_sys.h"
+
+#include <stddef.h>
+
+/**
+ * Global list of the subsystems in Tor, in the order of their initialization.
+ **/
+const subsys_fns_t *tor_subsystems[] = {
+ &sys_winprocess, /* -100 */
+ &sys_torerr, /* -100 */
+ &sys_wallclock, /* -99 */
+ &sys_threads, /* -95 */
+ &sys_logging, /* -90 */
+ &sys_time, /* -90 */
+ &sys_network, /* -90 */
+ &sys_compress, /* -70 */
+ &sys_crypto, /* -60 */
+ &sys_tortls, /* -50 */
+};
+
+const unsigned n_tor_subsystems = ARRAY_LENGTH(tor_subsystems);
diff --git a/src/config/mmdb-convert.py b/src/config/mmdb-convert.py
index 3a454a3fc1..706a8b03cc 100644
--- a/src/config/mmdb-convert.py
+++ b/src/config/mmdb-convert.py
@@ -77,7 +77,7 @@ def to_int32(s):
def to_int28(s):
"Parse a pair of big-endian 28-bit integers from bytestring s."
- a, b = unpack("!LL", s + b'\x00')
+ a, b = struct.unpack("!LL", s + b'\x00')
return (((a & 0xf0) << 20) + (a >> 8)), ((a & 0x0f) << 24) + (b >> 8)
class Tree(object):
diff --git a/src/core/include.am b/src/core/include.am
index 1b8ef2ac58..48d75618ad 100644
--- a/src/core/include.am
+++ b/src/core/include.am
@@ -11,6 +11,8 @@ LIBTOR_APP_A_SOURCES = \
src/app/config/confparse.c \
src/app/config/statefile.c \
src/app/main/main.c \
+ src/app/main/subsystem_list.c \
+ src/app/main/subsysmgr.c \
src/core/crypto/hs_ntor.c \
src/core/crypto/onion_crypto.c \
src/core/crypto/onion_fast.c \
@@ -106,6 +108,7 @@ LIBTOR_APP_A_SOURCES = \
src/feature/nodelist/microdesc.c \
src/feature/nodelist/networkstatus.c \
src/feature/nodelist/nickname.c \
+ src/feature/nodelist/nodefamily.c \
src/feature/nodelist/nodelist.c \
src/feature/nodelist/node_select.c \
src/feature/nodelist/routerinfo.c \
@@ -191,6 +194,7 @@ noinst_HEADERS += \
src/app/config/statefile.h \
src/app/main/main.h \
src/app/main/ntmain.h \
+ src/app/main/subsysmgr.h \
src/core/crypto/hs_ntor.h \
src/core/crypto/onion_crypto.h \
src/core/crypto/onion_fast.h \
@@ -340,6 +344,8 @@ noinst_HEADERS += \
src/feature/nodelist/networkstatus_voter_info_st.h \
src/feature/nodelist/nickname.h \
src/feature/nodelist/node_st.h \
+ src/feature/nodelist/nodefamily.h \
+ src/feature/nodelist/nodefamily_st.h \
src/feature/nodelist/nodelist.h \
src/feature/nodelist/node_select.h \
src/feature/nodelist/routerinfo.h \
diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c
index 1198a01ad9..c1c7c3678b 100644
--- a/src/core/mainloop/connection.c
+++ b/src/core/mainloop/connection.c
@@ -1874,6 +1874,9 @@ connection_init_accepted_conn(connection_t *conn,
TO_ENTRY_CONN(conn)->nym_epoch = get_signewnym_epoch();
TO_ENTRY_CONN(conn)->socks_request->listener_type = listener->base_.type;
+ /* Any incoming connection on an entry port counts as user activity. */
+ note_user_activity(approx_time());
+
switch (TO_CONN(listener)->type) {
case CONN_TYPE_AP_LISTENER:
conn->state = AP_CONN_STATE_SOCKS_WAIT;
@@ -4426,6 +4429,16 @@ connection_get_by_type_state(int type, int state)
CONN_GET_TEMPLATE(conn, conn->type == type && conn->state == state);
}
+/**
+ * Return a connection of type <b>type</b> that is not an internally linked
+ * connection, and is not marked for close.
+ **/
+MOCK_IMPL(connection_t *,
+connection_get_by_type_nonlinked,(int type))
+{
+ CONN_GET_TEMPLATE(conn, conn->type == type && !conn->linked);
+}
+
/** Return a connection of type <b>type</b> that has rendquery equal
* to <b>rendquery</b>, and that is not marked for close. If state
* is non-zero, conn must be of that state too.
diff --git a/src/core/mainloop/connection.h b/src/core/mainloop/connection.h
index b569bb038e..07b8df4138 100644
--- a/src/core/mainloop/connection.h
+++ b/src/core/mainloop/connection.h
@@ -240,6 +240,7 @@ size_t connection_get_outbuf_len(connection_t *conn);
connection_t *connection_get_by_global_id(uint64_t id);
connection_t *connection_get_by_type(int type);
+MOCK_DECL(connection_t *,connection_get_by_type_nonlinked,(int type));
MOCK_DECL(connection_t *,connection_get_by_type_addr_port_purpose,(int type,
const tor_addr_t *addr,
uint16_t port, int purpose));
diff --git a/src/core/mainloop/mainloop.c b/src/core/mainloop/mainloop.c
index 7eff82fee4..2e2ae876d4 100644
--- a/src/core/mainloop/mainloop.c
+++ b/src/core/mainloop/mainloop.c
@@ -205,7 +205,6 @@ static void connection_start_reading_from_linked_conn(connection_t *conn);
static int connection_should_read_from_linked_conn(connection_t *conn);
static void conn_read_callback(evutil_socket_t fd, short event, void *_conn);
static void conn_write_callback(evutil_socket_t fd, short event, void *_conn);
-static void second_elapsed_callback(periodic_timer_t *timer, void *args);
static void shutdown_did_not_work_callback(evutil_socket_t fd, short event,
void *arg) ATTR_NORETURN;
@@ -1354,6 +1353,7 @@ CALLBACK(heartbeat);
CALLBACK(hs_service);
CALLBACK(launch_descriptor_fetches);
CALLBACK(launch_reachability_tests);
+CALLBACK(prune_old_routers);
CALLBACK(reachability_warnings);
CALLBACK(record_bridge_stats);
CALLBACK(rend_cache_failure_clean);
@@ -1366,78 +1366,93 @@ CALLBACK(save_stability);
CALLBACK(save_state);
CALLBACK(write_bridge_ns);
CALLBACK(write_stats_file);
+CALLBACK(control_per_second_events);
+CALLBACK(second_elapsed);
#undef CALLBACK
/* Now we declare an array of periodic_event_item_t for each periodic event */
-#define CALLBACK(name, r, f) PERIODIC_EVENT(name, r, f)
+#define CALLBACK(name, r, f) \
+ PERIODIC_EVENT(name, PERIODIC_EVENT_ROLE_ ## r, f)
+#define FL(name) (PERIODIC_EVENT_FLAG_ ## name)
STATIC periodic_event_item_t periodic_events[] = {
- /* Everyone needs to run those. */
- CALLBACK(add_entropy, PERIODIC_EVENT_ROLE_ALL, 0),
- CALLBACK(check_expired_networkstatus, PERIODIC_EVENT_ROLE_ALL, 0),
- CALLBACK(clean_caches, PERIODIC_EVENT_ROLE_ALL, 0),
- CALLBACK(fetch_networkstatus, PERIODIC_EVENT_ROLE_ALL,
- PERIODIC_EVENT_FLAG_NEED_NET),
- CALLBACK(heartbeat, PERIODIC_EVENT_ROLE_ALL, 0),
- CALLBACK(launch_descriptor_fetches, PERIODIC_EVENT_ROLE_ALL,
- PERIODIC_EVENT_FLAG_NEED_NET),
- CALLBACK(reset_padding_counts, PERIODIC_EVENT_ROLE_ALL, 0),
- CALLBACK(retry_listeners, PERIODIC_EVENT_ROLE_ALL,
- PERIODIC_EVENT_FLAG_NEED_NET),
- CALLBACK(save_state, PERIODIC_EVENT_ROLE_ALL, 0),
- CALLBACK(rotate_x509_certificate, PERIODIC_EVENT_ROLE_ALL, 0),
- CALLBACK(write_stats_file, PERIODIC_EVENT_ROLE_ALL, 0),
+
+ /* Everyone needs to run these. They need to have very long timeouts for
+ * that to be safe. */
+ CALLBACK(add_entropy, ALL, 0),
+ CALLBACK(heartbeat, ALL, 0),
+ CALLBACK(reset_padding_counts, ALL, 0),
+
+ /* This is a legacy catch-all callback that runs once per second if
+ * we are online and active. */
+ CALLBACK(second_elapsed, NET_PARTICIPANT,
+ FL(NEED_NET)|FL(RUN_ON_DISABLE)),
+
+ /* XXXX Do we have a reason to do this on a callback? Does it do any good at
+ * all? For now, if we're dormant, we can let our listeners decay. */
+ CALLBACK(retry_listeners, NET_PARTICIPANT, FL(NEED_NET)),
+
+ /* We need to do these if we're participating in the Tor network. */
+ CALLBACK(check_expired_networkstatus, NET_PARTICIPANT, 0),
+ CALLBACK(fetch_networkstatus, NET_PARTICIPANT, 0),
+ CALLBACK(launch_descriptor_fetches, NET_PARTICIPANT, FL(NEED_NET)),
+ CALLBACK(rotate_x509_certificate, NET_PARTICIPANT, 0),
+ CALLBACK(check_network_participation, NET_PARTICIPANT, 0),
+
+ /* We need to do these if we're participating in the Tor network, and
+ * immediately before we stop. */
+ CALLBACK(clean_caches, NET_PARTICIPANT, FL(RUN_ON_DISABLE)),
+ CALLBACK(save_state, NET_PARTICIPANT, FL(RUN_ON_DISABLE)),
+ CALLBACK(write_stats_file, NET_PARTICIPANT, FL(RUN_ON_DISABLE)),
+ CALLBACK(prune_old_routers, NET_PARTICIPANT, FL(RUN_ON_DISABLE)),
/* Routers (bridge and relay) only. */
- CALLBACK(check_descriptor, PERIODIC_EVENT_ROLE_ROUTER,
- PERIODIC_EVENT_FLAG_NEED_NET),
- CALLBACK(check_ed_keys, PERIODIC_EVENT_ROLE_ROUTER, 0),
- CALLBACK(check_for_reachability_bw, PERIODIC_EVENT_ROLE_ROUTER,
- PERIODIC_EVENT_FLAG_NEED_NET),
- CALLBACK(check_onion_keys_expiry_time, PERIODIC_EVENT_ROLE_ROUTER, 0),
- CALLBACK(expire_old_ciruits_serverside, PERIODIC_EVENT_ROLE_ROUTER,
- PERIODIC_EVENT_FLAG_NEED_NET),
- CALLBACK(reachability_warnings, PERIODIC_EVENT_ROLE_ROUTER,
- PERIODIC_EVENT_FLAG_NEED_NET),
- CALLBACK(retry_dns, PERIODIC_EVENT_ROLE_ROUTER, 0),
- CALLBACK(rotate_onion_key, PERIODIC_EVENT_ROLE_ROUTER, 0),
+ CALLBACK(check_descriptor, ROUTER, FL(NEED_NET)),
+ CALLBACK(check_ed_keys, ROUTER, 0),
+ CALLBACK(check_for_reachability_bw, ROUTER, FL(NEED_NET)),
+ CALLBACK(check_onion_keys_expiry_time, ROUTER, 0),
+ CALLBACK(expire_old_ciruits_serverside, ROUTER, FL(NEED_NET)),
+ CALLBACK(reachability_warnings, ROUTER, FL(NEED_NET)),
+ CALLBACK(retry_dns, ROUTER, 0),
+ CALLBACK(rotate_onion_key, ROUTER, 0),
/* Authorities (bridge and directory) only. */
- CALLBACK(downrate_stability, PERIODIC_EVENT_ROLE_AUTHORITIES, 0),
- CALLBACK(launch_reachability_tests, PERIODIC_EVENT_ROLE_AUTHORITIES,
- PERIODIC_EVENT_FLAG_NEED_NET),
- CALLBACK(save_stability, PERIODIC_EVENT_ROLE_AUTHORITIES, 0),
+ CALLBACK(downrate_stability, AUTHORITIES, 0),
+ CALLBACK(launch_reachability_tests, AUTHORITIES, FL(NEED_NET)),
+ CALLBACK(save_stability, AUTHORITIES, 0),
/* Directory authority only. */
- CALLBACK(check_authority_cert, PERIODIC_EVENT_ROLE_DIRAUTH, 0),
- CALLBACK(dirvote, PERIODIC_EVENT_ROLE_DIRAUTH, PERIODIC_EVENT_FLAG_NEED_NET),
+ CALLBACK(check_authority_cert, DIRAUTH, 0),
+ CALLBACK(dirvote, DIRAUTH, FL(NEED_NET)),
/* Relay only. */
- CALLBACK(check_canonical_channels, PERIODIC_EVENT_ROLE_RELAY,
- PERIODIC_EVENT_FLAG_NEED_NET),
- CALLBACK(check_dns_honesty, PERIODIC_EVENT_ROLE_RELAY,
- PERIODIC_EVENT_FLAG_NEED_NET),
+ CALLBACK(check_canonical_channels, RELAY, FL(NEED_NET)),
+ CALLBACK(check_dns_honesty, RELAY, FL(NEED_NET)),
/* Hidden Service service only. */
- CALLBACK(hs_service, PERIODIC_EVENT_ROLE_HS_SERVICE,
- PERIODIC_EVENT_FLAG_NEED_NET),
+ CALLBACK(hs_service, HS_SERVICE, FL(NEED_NET)), // XXXX break this down more
/* Bridge only. */
- CALLBACK(record_bridge_stats, PERIODIC_EVENT_ROLE_BRIDGE, 0),
+ CALLBACK(record_bridge_stats, BRIDGE, 0),
/* Client only. */
- CALLBACK(rend_cache_failure_clean, PERIODIC_EVENT_ROLE_CLIENT, 0),
+ /* XXXX this could be restricted to CLIENT+NET_PARTICIPANT */
+ CALLBACK(rend_cache_failure_clean, NET_PARTICIPANT, FL(RUN_ON_DISABLE)),
/* Bridge Authority only. */
- CALLBACK(write_bridge_ns, PERIODIC_EVENT_ROLE_BRIDGEAUTH, 0),
+ CALLBACK(write_bridge_ns, BRIDGEAUTH, 0),
/* Directory server only. */
- CALLBACK(clean_consdiffmgr, PERIODIC_EVENT_ROLE_DIRSERVER, 0),
+ CALLBACK(clean_consdiffmgr, DIRSERVER, 0),
+
+ /* Controller with per-second events only. */
+ CALLBACK(control_per_second_events, CONTROLEV, 0),
END_OF_PERIODIC_EVENTS
};
#undef CALLBACK
+#undef FL
/* These are pointers to members of periodic_events[] that are used to
* implement particular callbacks. We keep them separate here so that we
@@ -1449,6 +1464,7 @@ static periodic_event_item_t *fetch_networkstatus_event=NULL;
static periodic_event_item_t *launch_descriptor_fetches_event=NULL;
static periodic_event_item_t *check_dns_honesty_event=NULL;
static periodic_event_item_t *save_state_event=NULL;
+static periodic_event_item_t *prune_old_routers_event=NULL;
/** Reset all the periodic events so we'll do all our actions again as if we
* just started up.
@@ -1485,7 +1501,7 @@ get_my_roles(const or_options_t *options)
{
tor_assert(options);
- int roles = 0;
+ int roles = PERIODIC_EVENT_ROLE_ALL;
int is_bridge = options->BridgeRelay;
int is_relay = server_mode(options);
int is_dirauth = authdir_mode_v3(options);
@@ -1493,6 +1509,8 @@ get_my_roles(const or_options_t *options)
int is_hidden_service = !!hs_service_get_num_services() ||
!!rend_num_services();
int is_dirserver = dir_server_mode(options);
+ int sending_control_events = control_any_per_second_event_enabled();
+
/* We also consider tor to have the role of a client if the ControlPort is
* set because a lot of things can be done over the control port which
* requires tor to have basic functionnalities. */
@@ -1500,6 +1518,9 @@ get_my_roles(const or_options_t *options)
options->ControlPort_set ||
options->OwningControllerFD != UINT64_MAX;
+ int is_net_participant = is_participating_on_network() ||
+ is_relay || is_hidden_service;
+
if (is_bridge) roles |= PERIODIC_EVENT_ROLE_BRIDGE;
if (is_client) roles |= PERIODIC_EVENT_ROLE_CLIENT;
if (is_relay) roles |= PERIODIC_EVENT_ROLE_RELAY;
@@ -1507,6 +1528,8 @@ get_my_roles(const or_options_t *options)
if (is_bridgeauth) roles |= PERIODIC_EVENT_ROLE_BRIDGEAUTH;
if (is_hidden_service) roles |= PERIODIC_EVENT_ROLE_HS_SERVICE;
if (is_dirserver) roles |= PERIODIC_EVENT_ROLE_DIRSERVER;
+ if (is_net_participant) roles |= PERIODIC_EVENT_ROLE_NET_PARTICIPANT;
+ if (sending_control_events) roles |= PERIODIC_EVENT_ROLE_CONTROLEV;
return roles;
}
@@ -1551,6 +1574,7 @@ initialize_periodic_events(void)
STMT_BEGIN name ## _event = find_periodic_event( #name ); STMT_END
NAMED_CALLBACK(check_descriptor);
+ NAMED_CALLBACK(prune_old_routers);
NAMED_CALLBACK(dirvote);
NAMED_CALLBACK(fetch_networkstatus);
NAMED_CALLBACK(launch_descriptor_fetches);
@@ -1574,6 +1598,30 @@ teardown_periodic_events(void)
periodic_events_initialized = 0;
}
+static mainloop_event_t *rescan_periodic_events_ev = NULL;
+
+/** Callback: rescan the periodic event list. */
+static void
+rescan_periodic_events_cb(mainloop_event_t *event, void *arg)
+{
+ (void)event;
+ (void)arg;
+ rescan_periodic_events(get_options());
+}
+
+/**
+ * Schedule an event that will rescan which periodic events should run.
+ **/
+MOCK_IMPL(void,
+schedule_rescan_periodic_events,(void))
+{
+ if (!rescan_periodic_events_ev) {
+ rescan_periodic_events_ev =
+ mainloop_event_new(rescan_periodic_events_cb, NULL);
+ }
+ mainloop_event_activate(rescan_periodic_events_ev);
+}
+
/** Do a pass at all our periodic events, disable those we don't need anymore
* and enable those we need now using the given options. */
void
@@ -1608,7 +1656,11 @@ rescan_periodic_events(const or_options_t *options)
periodic_event_enable(item);
} else {
log_debug(LD_GENERAL, "Disabling periodic event %s", item->name);
- periodic_event_disable(item);
+ if (item->flags & PERIODIC_EVENT_FLAG_RUN_ON_DISABLE) {
+ periodic_event_schedule_and_disable(item);
+ } else {
+ periodic_event_disable(item);
+ }
}
}
}
@@ -1683,6 +1735,30 @@ mainloop_schedule_postloop_cleanup(void)
mainloop_event_activate(postloop_cleanup_ev);
}
+/** Event to run 'scheduled_shutdown_cb' */
+static mainloop_event_t *scheduled_shutdown_ev=NULL;
+
+/** Callback: run a scheduled shutdown */
+static void
+scheduled_shutdown_cb(mainloop_event_t *ev, void *arg)
+{
+ (void)ev;
+ (void)arg;
+ log_notice(LD_GENERAL, "Clean shutdown finished. Exiting.");
+ tor_shutdown_event_loop_and_exit(0);
+}
+
+/** Schedule the mainloop to exit after <b>delay_sec</b> seconds. */
+void
+mainloop_schedule_shutdown(int delay_sec)
+{
+ const struct timeval delay_tv = { delay_sec, 0 };
+ if (! scheduled_shutdown_ev) {
+ scheduled_shutdown_ev = mainloop_event_new(scheduled_shutdown_cb, NULL);
+ }
+ mainloop_event_schedule(scheduled_shutdown_ev, &delay_tv);
+}
+
#define LONGEST_TIMER_PERIOD (30 * 86400)
/** Helper: Return the number of seconds between <b>now</b> and <b>next</b>,
* clipped to the range [1 second, LONGEST_TIMER_PERIOD]. */
@@ -1707,16 +1783,16 @@ safe_timer_diff(time_t now, time_t next)
}
/** Perform regular maintenance tasks. This function gets run once per
- * second by second_elapsed_callback().
+ * second.
*/
-static void
-run_scheduled_events(time_t now)
+static int
+second_elapsed_callback(time_t now, const or_options_t *options)
{
- const or_options_t *options = get_options();
-
- /* 0. See if we've been asked to shut down and our timeout has
- * expired; or if our bandwidth limits are exhausted and we
- * should hibernate; or if it's time to wake up from hibernation.
+ /* 0. See if our bandwidth limits are exhausted and we should hibernate
+ *
+ * Note: we have redundant mechanisms to handle the case where it's
+ * time to wake up from hibernation; or where we have a scheduled
+ * shutdown and it's time to run it, but this will also handle those.
*/
consider_hibernation(now);
@@ -1726,10 +1802,13 @@ run_scheduled_events(time_t now)
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. */
+ // TODO: client
+ // Also, schedule this rather than probing 1x / sec
fetch_bridge_descriptors(options, now);
}
if (accounting_is_enabled(options)) {
+ // TODO: refactor or rewrite?
accounting_run_housekeeping(now);
}
@@ -1740,6 +1819,7 @@ run_scheduled_events(time_t now)
*/
/* (If our circuit build timeout can ever become lower than a second (which
* it can't, currently), we should do this more often.) */
+ // TODO: All expire stuff can become NET_PARTICIPANT, RUN_ON_DISABLE
circuit_expire_building();
circuit_expire_waiting_for_better_guard();
@@ -1776,6 +1856,9 @@ run_scheduled_events(time_t now)
/* 11b. check pending unconfigured managed proxies */
if (!net_is_disabled() && pt_proxies_configuration_pending())
pt_configure_remaining_proxies();
+
+ /* Run again in a second. */
+ return 1;
}
/* Periodic callback: rotate the onion keys after the period defined by the
@@ -1922,6 +2005,55 @@ add_entropy_callback(time_t now, const or_options_t *options)
return ENTROPY_INTERVAL;
}
+/** Periodic callback: if there has been no network usage in a while,
+ * enter a dormant state. */
+STATIC int
+check_network_participation_callback(time_t now, const or_options_t *options)
+{
+ /* If we're a server, we can't become dormant. */
+ if (server_mode(options)) {
+ goto found_activity;
+ }
+
+ /* If we're running an onion service, we can't become dormant. */
+ /* XXXX this would be nice to change, so that we can be dormant with a
+ * service. */
+ if (hs_service_get_num_services() || rend_num_services()) {
+ goto found_activity;
+ }
+
+ /* If we have any currently open entry streams other than "linked"
+ * connections used for directory requests, those count as user activity.
+ */
+ if (options->DormantTimeoutDisabledByIdleStreams) {
+ if (connection_get_by_type_nonlinked(CONN_TYPE_AP) != NULL) {
+ goto found_activity;
+ }
+ }
+
+ /* XXXX Make this configurable? */
+/** How often do we check whether we have had network activity? */
+#define CHECK_PARTICIPATION_INTERVAL (5*60)
+
+ /* Become dormant if there has been no user activity in a long time.
+ * (The funny checks below are in order to prevent overflow.) */
+ time_t time_since_last_activity = 0;
+ if (get_last_user_activity_time() < now)
+ time_since_last_activity = now - get_last_user_activity_time();
+ if (time_since_last_activity >= options->DormantClientTimeout) {
+ log_notice(LD_GENERAL, "No user activity in a long time: becoming"
+ " dormant.");
+ set_network_participation(false);
+ rescan_periodic_events(options);
+ }
+
+ return CHECK_PARTICIPATION_INTERVAL;
+
+ found_activity:
+ note_user_activity(now);
+ return CHECK_PARTICIPATION_INTERVAL;
+}
+
/**
* Periodic callback: if we're an authority, make sure we test
* the routers on the network for reachability.
@@ -2215,6 +2347,27 @@ retry_dns_callback(time_t now, const or_options_t *options)
return RETRY_DNS_INTERVAL;
}
+/**
+ * Periodic callback: prune routerlist of old information about Tor network.
+ */
+static int
+prune_old_routers_callback(time_t now, const or_options_t *options)
+{
+#define ROUTERLIST_PRUNING_INTERVAL (60*60) // 1 hour.
+ (void)now;
+ (void)options;
+
+ if (!net_is_disabled()) {
+ /* If any networkstatus documents are no longer recent, we need to
+ * update all the descriptors' running status. */
+ /* Remove dead routers. */
+ log_debug(LD_GENERAL, "Pruning routerlist...");
+ routerlist_remove_old_routers();
+ }
+
+ return ROUTERLIST_PRUNING_INTERVAL;
+}
+
/** Periodic callback: consider rebuilding or and re-uploading our descriptor
* (if we've passed our internal checks). */
static int
@@ -2234,12 +2387,6 @@ check_descriptor_callback(time_t now, const or_options_t *options)
check_descriptor_ipaddress_changed(now);
mark_my_descriptor_dirty_if_too_old(now);
consider_publishable_server(0);
- /* If any networkstatus documents are no longer recent, we need to
- * update all the descriptors' running status. */
- /* Remove dead routers. */
- /* XXXX This doesn't belong here, but it was here in the pre-
- * XXXX refactoring code. */
- routerlist_remove_old_routers();
}
return CHECK_DESCRIPTOR_INTERVAL;
@@ -2497,36 +2644,19 @@ hs_service_callback(time_t now, const or_options_t *options)
return 1;
}
-/** Timer: used to invoke second_elapsed_callback() once per second. */
-static periodic_timer_t *second_timer = NULL;
-
-/**
- * Enable or disable the per-second timer as appropriate, creating it if
- * necessary.
+/*
+ * Periodic callback: Send once-per-second events to the controller(s).
+ * This is called every second.
*/
-void
-reschedule_per_second_timer(void)
+static int
+control_per_second_events_callback(time_t now, const or_options_t *options)
{
- struct timeval one_second;
- one_second.tv_sec = 1;
- one_second.tv_usec = 0;
-
- if (! second_timer) {
- second_timer = periodic_timer_new(tor_libevent_get_base(),
- &one_second,
- second_elapsed_callback,
- NULL);
- tor_assert(second_timer);
- }
+ (void) options;
+ (void) now;
- const bool run_per_second_events =
- control_any_per_second_event_enabled() || ! net_is_completely_disabled();
+ control_per_second_events();
- if (run_per_second_events) {
- periodic_timer_launch(second_timer, &one_second);
- } else {
- periodic_timer_disable(second_timer);
- }
+ return 1;
}
/** Last time that update_current_time was called. */
@@ -2565,6 +2695,11 @@ update_current_time(time_t now)
if (seconds_elapsed < -NUM_JUMPED_SECONDS_BEFORE_WARN) {
// moving back in time is always a bad sign.
circuit_note_clock_jumped(seconds_elapsed, false);
+
+ /* Don't go dormant just because we jumped in time. */
+ if (is_participating_on_network()) {
+ reset_user_activity(now);
+ }
} else if (seconds_elapsed >= NUM_JUMPED_SECONDS_BEFORE_WARN) {
/* Compare the monotonic clock to the result of time(). */
const int32_t monotime_msec_passed =
@@ -2586,6 +2721,11 @@ update_current_time(time_t now)
if (clock_jumped || seconds_elapsed >= NUM_IDLE_SECONDS_BEFORE_WARN) {
circuit_note_clock_jumped(seconds_elapsed, ! clock_jumped);
}
+
+ /* Don't go dormant just because we jumped in time. */
+ if (is_participating_on_network()) {
+ reset_user_activity(now);
+ }
} else if (seconds_elapsed > 0) {
stats_n_seconds_working += seconds_elapsed;
}
@@ -2594,31 +2734,6 @@ update_current_time(time_t now)
current_second = now;
}
-/** Libevent callback: invoked once every second. */
-static void
-second_elapsed_callback(periodic_timer_t *timer, void *arg)
-{
- /* XXXX This could be sensibly refactored into multiple callbacks, and we
- * could use Libevent's timers for this rather than checking the current
- * time against a bunch of timeouts every second. */
- time_t now;
- (void)timer;
- (void)arg;
-
- now = time(NULL);
-
- /* We don't need to do this once-per-second any more: time-updating is
- * only in this callback _because it is a callback_. It should be fine
- * to disable this callback, and the time will still get updated.
- */
- update_current_time(now);
-
- /* Maybe some controller events are ready to fire */
- control_per_second_events();
-
- run_scheduled_events(now);
-}
-
#ifdef HAVE_SYSTEMD_209
static periodic_timer_t *systemd_watchdog_timer = NULL;
@@ -2703,15 +2818,18 @@ initialize_mainloop_events(void)
int
do_main_loop(void)
{
+ /* For now, starting Tor always counts as user activity. Later, we might
+ * have an option to control this.
+ */
+ reset_user_activity(approx_time());
+ set_network_participation(true);
+
/* initialize the periodic events first, so that code that depends on the
* events being present does not assert.
*/
initialize_periodic_events();
initialize_mainloop_events();
- /* set up once-a-second callback. */
- reschedule_per_second_timer();
-
#ifdef HAVE_SYSTEMD_209
uint64_t watchdog_delay;
/* set up systemd watchdog notification. */
@@ -2896,7 +3014,6 @@ tor_mainloop_free_all(void)
smartlist_free(connection_array);
smartlist_free(closeable_connection_lst);
smartlist_free(active_linked_connection_lst);
- periodic_timer_free(second_timer);
teardown_periodic_events();
tor_event_free(shutdown_did_not_work_event);
tor_event_free(initialize_periodic_events_event);
@@ -2904,6 +3021,8 @@ tor_mainloop_free_all(void)
mainloop_event_free(schedule_active_linked_connections_event);
mainloop_event_free(postloop_cleanup_ev);
mainloop_event_free(handle_deferred_signewnym_ev);
+ mainloop_event_free(scheduled_shutdown_ev);
+ mainloop_event_free(rescan_periodic_events_ev);
#ifdef HAVE_SYSTEMD_209
periodic_timer_free(systemd_watchdog_timer);
diff --git a/src/core/mainloop/mainloop.h b/src/core/mainloop/mainloop.h
index 632733d9a6..14e80ebb21 100644
--- a/src/core/mainloop/mainloop.h
+++ b/src/core/mainloop/mainloop.h
@@ -65,6 +65,7 @@ void reschedule_or_state_save(void);
void reschedule_dirvote(const or_options_t *options);
void mainloop_schedule_postloop_cleanup(void);
void rescan_periodic_events(const or_options_t *options);
+MOCK_DECL(void, schedule_rescan_periodic_events,(void));
void update_current_time(time_t now);
@@ -81,11 +82,12 @@ uint64_t get_main_loop_error_count(void);
uint64_t get_main_loop_idle_count(void);
void periodic_events_on_new_options(const or_options_t *options);
-void reschedule_per_second_timer(void);
void do_signewnym(time_t);
time_t get_last_signewnym_time(void);
+void mainloop_schedule_shutdown(int delay_sec);
+
void tor_init_connection_lists(void);
void initialize_mainloop_events(void);
void tor_mainloop_free_all(void);
@@ -102,6 +104,9 @@ STATIC void close_closeable_connections(void);
STATIC void initialize_periodic_events(void);
STATIC void teardown_periodic_events(void);
STATIC int get_my_roles(const or_options_t *);
+STATIC int check_network_participation_callback(time_t now,
+ const or_options_t *options);
+
#ifdef TOR_UNIT_TESTS
extern smartlist_t *connection_array;
diff --git a/src/core/mainloop/netstatus.c b/src/core/mainloop/netstatus.c
index f026474494..ed7c952dcd 100644
--- a/src/core/mainloop/netstatus.c
+++ b/src/core/mainloop/netstatus.c
@@ -6,6 +6,7 @@
#include "core/or/or.h"
#include "core/mainloop/netstatus.h"
+#include "core/mainloop/mainloop.h"
#include "app/config/config.h"
#include "feature/hibernate/hibernate.h"
@@ -26,3 +27,75 @@ net_is_completely_disabled(void)
{
return get_options()->DisableNetwork || we_are_fully_hibernating();
}
+
+/**
+ * The time at which we've last seen "user activity" -- that is, any activity
+ * that should keep us as a participant on the network.
+ */
+static time_t last_user_activity_seen = 0;
+
+/**
+ * True iff we are currently a "network participant" -- that is, we
+ * are building circuits, fetching directory information, and so on.
+ **/
+static bool participating_on_network = false;
+
+/**
+ * Record the fact that we have seen "user activity" at the time now. Move
+ * "last activity seen" time forwards, but never backwards.
+ *
+ * If we were previously not participating on the network, set our
+ * participation status to true, and launch periodic events as appropriate.
+ **/
+void
+note_user_activity(time_t now)
+{
+ last_user_activity_seen = MAX(now, last_user_activity_seen);
+
+ if (! participating_on_network) {
+ log_notice(LD_GENERAL, "Tor is no longer dormant.");
+ set_network_participation(true);
+ schedule_rescan_periodic_events();
+ }
+}
+
+/**
+ * Change the time at which "user activitiy" was last seen to <b>now</b>.
+ *
+ * Unlike note_user_actity, this function sets the time without checking
+ * whether it is in the past, and without causing any rescan of periodic events
+ * or change in participation status.
+ */
+void
+reset_user_activity(time_t now)
+{
+ last_user_activity_seen = now;
+}
+
+/**
+ * Return the most recent time at which we recorded "user activity".
+ **/
+time_t
+get_last_user_activity_time(void)
+{
+ return last_user_activity_seen;
+}
+
+/**
+ * Set the field that remembers whether we are currently participating on the
+ * network. Does not schedule or un-schedule periodic events.
+ **/
+void
+set_network_participation(bool participation)
+{
+ participating_on_network = participation;
+}
+
+/**
+ * Return true iff we are currently participating on the network.
+ **/
+bool
+is_participating_on_network(void)
+{
+ return participating_on_network;
+}
diff --git a/src/core/mainloop/netstatus.h b/src/core/mainloop/netstatus.h
index e9310c2929..58c994fd14 100644
--- a/src/core/mainloop/netstatus.h
+++ b/src/core/mainloop/netstatus.h
@@ -10,4 +10,11 @@
int net_is_disabled(void);
int net_is_completely_disabled(void);
+void note_user_activity(time_t now);
+void reset_user_activity(time_t now);
+time_t get_last_user_activity_time(void);
+
+void set_network_participation(bool participation);
+bool is_participating_on_network(void);
+
#endif
diff --git a/src/core/mainloop/periodic.c b/src/core/mainloop/periodic.c
index c1785eb38f..9f9b178e43 100644
--- a/src/core/mainloop/periodic.c
+++ b/src/core/mainloop/periodic.c
@@ -45,10 +45,6 @@ periodic_event_dispatch(mainloop_event_t *ev, void *data)
periodic_event_item_t *event = data;
tor_assert(ev == event->ev);
- if (BUG(!periodic_event_is_enabled(event))) {
- return;
- }
-
time_t now = time(NULL);
update_current_time(now);
const or_options_t *options = get_options();
@@ -57,7 +53,7 @@ periodic_event_dispatch(mainloop_event_t *ev, void *data)
int next_interval = 0;
if (!periodic_event_is_enabled(event)) {
- /* The event got disabled from inside its callback; no need to
+ /* The event got disabled from inside its callback, or before: no need to
* reschedule. */
return;
}
@@ -172,3 +168,19 @@ periodic_event_disable(periodic_event_item_t *event)
mainloop_event_cancel(event->ev);
event->enabled = 0;
}
+
+/**
+ * Disable an event, then schedule it to run once.
+ * Do nothing if the event was already disabled.
+ */
+void
+periodic_event_schedule_and_disable(periodic_event_item_t *event)
+{
+ tor_assert(event);
+ if (!periodic_event_is_enabled(event))
+ return;
+
+ periodic_event_disable(event);
+
+ mainloop_event_activate(event->ev);
+}
diff --git a/src/core/mainloop/periodic.h b/src/core/mainloop/periodic.h
index 4c8c3c96cc..05ba4297f3 100644
--- a/src/core/mainloop/periodic.h
+++ b/src/core/mainloop/periodic.h
@@ -15,6 +15,10 @@
#define PERIODIC_EVENT_ROLE_BRIDGEAUTH (1U << 4)
#define PERIODIC_EVENT_ROLE_HS_SERVICE (1U << 5)
#define PERIODIC_EVENT_ROLE_DIRSERVER (1U << 6)
+#define PERIODIC_EVENT_ROLE_CONTROLEV (1U << 7)
+
+#define PERIODIC_EVENT_ROLE_NET_PARTICIPANT (1U << 8)
+#define PERIODIC_EVENT_ROLE_ALL (1U << 9)
/* Helper macro to make it a bit less annoying to defined groups of roles that
* are often used. */
@@ -25,10 +29,6 @@
/* Authorities that is both bridge and directory. */
#define PERIODIC_EVENT_ROLE_AUTHORITIES \
(PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_DIRAUTH)
-/* All roles. */
-#define PERIODIC_EVENT_ROLE_ALL \
- (PERIODIC_EVENT_ROLE_AUTHORITIES | PERIODIC_EVENT_ROLE_CLIENT | \
- PERIODIC_EVENT_ROLE_HS_SERVICE | PERIODIC_EVENT_ROLE_ROUTER)
/*
* Event flags which can change the behavior of an event.
@@ -39,6 +39,11 @@
* the net_is_disabled() check. */
#define PERIODIC_EVENT_FLAG_NEED_NET (1U << 0)
+/* Indicate that if the event is enabled, it needs to be run once before
+ * it becomes disabled.
+ */
+#define PERIODIC_EVENT_FLAG_RUN_ON_DISABLE (1U << 1)
+
/** Callback function for a periodic event to take action. The return value
* influences the next time the function will get called. Return
* PERIODIC_EVENT_NO_UPDATE to not update <b>last_action_time</b> and be polled
@@ -83,6 +88,6 @@ void periodic_event_destroy(periodic_event_item_t *event);
void periodic_event_reschedule(periodic_event_item_t *event);
void periodic_event_enable(periodic_event_item_t *event);
void periodic_event_disable(periodic_event_item_t *event);
+void periodic_event_schedule_and_disable(periodic_event_item_t *event);
#endif /* !defined(TOR_PERIODIC_H) */
-
diff --git a/src/core/or/circuitbuild.c b/src/core/or/circuitbuild.c
index da137accc3..d3744dc1c1 100644
--- a/src/core/or/circuitbuild.c
+++ b/src/core/or/circuitbuild.c
@@ -1657,22 +1657,25 @@ route_len_for_purpose(uint8_t purpose, extend_info_t *exit_ei)
STATIC int
new_route_len(uint8_t purpose, extend_info_t *exit_ei, smartlist_t *nodes)
{
- int num_acceptable_routers;
int routelen;
tor_assert(nodes);
routelen = route_len_for_purpose(purpose, exit_ei);
- num_acceptable_routers = count_acceptable_nodes(nodes);
+ int num_acceptable_direct = count_acceptable_nodes(nodes, 1);
+ int num_acceptable_indirect = count_acceptable_nodes(nodes, 0);
- log_debug(LD_CIRC,"Chosen route length %d (%d/%d routers suitable).",
- routelen, num_acceptable_routers, smartlist_len(nodes));
+ log_debug(LD_CIRC,"Chosen route length %d (%d direct and %d indirect "
+ "routers suitable).", routelen, num_acceptable_direct,
+ num_acceptable_indirect);
- if (num_acceptable_routers < routelen) {
+ if (num_acceptable_direct < 1 || num_acceptable_indirect < routelen - 1) {
log_info(LD_CIRC,
- "Not enough acceptable routers (%d/%d). Discarding this circuit.",
- num_acceptable_routers, routelen);
+ "Not enough acceptable routers (%d/%d direct and %d/%d "
+ "indirect routers suitable). Discarding this circuit.",
+ num_acceptable_direct, routelen,
+ num_acceptable_indirect, routelen);
return -1;
}
@@ -2314,7 +2317,7 @@ circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *exit_ei)
* particular router. See bug #25885.)
*/
MOCK_IMPL(STATIC int,
-count_acceptable_nodes, (smartlist_t *nodes))
+count_acceptable_nodes, (smartlist_t *nodes, int direct))
{
int num=0;
@@ -2328,7 +2331,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_any_descriptor(node))
+ if (! node_has_preferred_descriptor(node, direct))
continue;
/* The node has a descriptor, so we can just check the ntor key directly */
if (!node_has_curve25519_onion_key(node))
diff --git a/src/core/or/circuitbuild.h b/src/core/or/circuitbuild.h
index cee71b297b..93f903f060 100644
--- a/src/core/or/circuitbuild.h
+++ b/src/core/or/circuitbuild.h
@@ -84,7 +84,8 @@ void circuit_upgrade_circuits_from_guard_wait(void);
STATIC circid_t get_unique_circ_id_by_chan(channel_t *chan);
STATIC int new_route_len(uint8_t purpose, extend_info_t *exit_ei,
smartlist_t *nodes);
-MOCK_DECL(STATIC int, count_acceptable_nodes, (smartlist_t *nodes));
+MOCK_DECL(STATIC int, count_acceptable_nodes, (smartlist_t *nodes,
+ int direct));
STATIC int onion_extend_cpath(origin_circuit_t *circ);
diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c
index 58aefcf8f2..7b51313e8a 100644
--- a/src/core/or/connection_edge.c
+++ b/src/core/or/connection_edge.c
@@ -62,6 +62,7 @@
#include "app/config/config.h"
#include "core/mainloop/connection.h"
#include "core/mainloop/mainloop.h"
+#include "core/mainloop/netstatus.h"
#include "core/or/channel.h"
#include "core/or/circuitbuild.h"
#include "core/or/circuitlist.h"
@@ -297,6 +298,11 @@ connection_edge_process_inbuf(edge_connection_t *conn, int package_partial)
}
return 0;
case AP_CONN_STATE_OPEN:
+ if (! conn->base_.linked) {
+ note_user_activity(approx_time());
+ }
+
+ /* falls through. */
case EXIT_CONN_STATE_OPEN:
if (connection_edge_package_raw_inbuf(conn, package_partial, NULL) < 0) {
/* (We already sent an end cell if possible) */
@@ -751,6 +757,11 @@ connection_edge_flushed_some(edge_connection_t *conn)
{
switch (conn->base_.state) {
case AP_CONN_STATE_OPEN:
+ if (! conn->base_.linked) {
+ note_user_activity(approx_time());
+ }
+
+ /* falls through. */
case EXIT_CONN_STATE_OPEN:
connection_edge_consider_sending_sendme(conn);
break;
diff --git a/src/core/or/or.h b/src/core/or/or.h
index acf092c8dc..e4b374b122 100644
--- a/src/core/or/or.h
+++ b/src/core/or/or.h
@@ -97,6 +97,8 @@ struct curve25519_public_key_t;
#define SIGNEWNYM 129
#define SIGCLEARDNSCACHE 130
#define SIGHEARTBEAT 131
+#define SIGACTIVE 132
+#define SIGDORMANT 133
#if (SIZEOF_CELL_T != 0)
/* On Irix, stdlib.h defines a cell_t type, so we need to make sure
diff --git a/src/core/or/versions.c b/src/core/or/versions.c
index 06274996a7..5d4effcaf8 100644
--- a/src/core/or/versions.c
+++ b/src/core/or/versions.c
@@ -377,6 +377,63 @@ sort_version_list(smartlist_t *versions, int remove_duplicates)
smartlist_uniq(versions, compare_tor_version_str_ptr_, tor_free_);
}
+/** If there are more than this many entries, we're probably under
+ * some kind of weird DoS. */
+static const int MAX_PROTOVER_SUMMARY_MAP_LEN = 1024;
+
+/**
+ * Map from protover string to protover_summary_flags_t.
+ */
+static strmap_t *protover_summary_map = NULL;
+
+/**
+ * Helper. Given a non-NULL protover string <b>protocols</b>, set <b>out</b>
+ * to its summary, and memoize the result in <b>protover_summary_map</b>.
+ */
+static void
+memoize_protover_summary(protover_summary_flags_t *out,
+ const char *protocols)
+{
+ if (!protover_summary_map)
+ protover_summary_map = strmap_new();
+
+ if (strmap_size(protover_summary_map) >= MAX_PROTOVER_SUMMARY_MAP_LEN) {
+ protover_summary_cache_free_all();
+ protover_summary_map = strmap_new();
+ }
+
+ const protover_summary_flags_t *cached =
+ strmap_get(protover_summary_map, protocols);
+
+ if (cached != NULL) {
+ /* We found a cached entry; no need to parse this one. */
+ memcpy(out, cached, sizeof(protover_summary_flags_t));
+ tor_assert(out->protocols_known);
+ return;
+ }
+
+ memset(out, 0, sizeof(*out));
+ 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);
+
+ protover_summary_flags_t *new_cached = tor_memdup(out, sizeof(*out));
+ cached = strmap_set(protover_summary_map, protocols, new_cached);
+ tor_assert(!cached);
+}
+
/** Summarize the protocols listed in <b>protocols</b> into <b>out</b>,
* falling back or correcting them based on <b>version</b> as appropriate.
*/
@@ -388,21 +445,7 @@ summarize_protover_flags(protover_summary_flags_t *out,
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);
+ memoize_protover_summary(out, protocols);
}
if (version && !strcmpstart(version, "Tor ")) {
if (!out->protocols_known) {
@@ -420,3 +463,13 @@ summarize_protover_flags(protover_summary_flags_t *out,
}
}
}
+
+/**
+ * Free all space held in the protover_summary_map.
+ */
+void
+protover_summary_cache_free_all(void)
+{
+ strmap_free(protover_summary_map, tor_free_);
+ protover_summary_map = NULL;
+}
diff --git a/src/core/or/versions.h b/src/core/or/versions.h
index 0c773f3f4c..4fc50a0018 100644
--- a/src/core/or/versions.h
+++ b/src/core/or/versions.h
@@ -41,4 +41,6 @@ void summarize_protover_flags(protover_summary_flags_t *out,
const char *protocols,
const char *version);
+void protover_summary_cache_free_all(void);
+
#endif /* !defined(TOR_VERSIONS_H) */
diff --git a/src/feature/client/dnsserv.c b/src/feature/client/dnsserv.c
index ea4951f915..e5abe5c6aa 100644
--- a/src/feature/client/dnsserv.c
+++ b/src/feature/client/dnsserv.c
@@ -28,6 +28,7 @@
#include "core/or/connection_edge.h"
#include "feature/control/control.h"
#include "core/mainloop/mainloop.h"
+#include "core/mainloop/netstatus.h"
#include "core/or/policies.h"
#include "feature/control/control_connection_st.h"
@@ -213,6 +214,9 @@ dnsserv_launch_request(const char *name, int reverse,
edge_connection_t *conn;
char *q_name;
+ /* Launching a request for a user counts as user activity. */
+ note_user_activity(approx_time());
+
/* 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;
diff --git a/src/feature/control/control.c b/src/feature/control/control.c
index 795902e6f4..94679dfd22 100644
--- a/src/feature/control/control.c
+++ b/src/feature/control/control.c
@@ -92,6 +92,7 @@
#include "lib/crypt_ops/crypto_util.h"
#include "lib/encoding/confline.h"
#include "lib/evloop/compat_libevent.h"
+#include "lib/version/torversion.h"
#include "feature/dircache/cached_dir_st.h"
#include "feature/control/control_connection_st.h"
@@ -367,7 +368,7 @@ control_update_global_event_mask(void)
control_get_bytes_rw_last_sec(&r, &w);
}
if (any_old_per_sec_events != control_any_per_second_event_enabled()) {
- reschedule_per_second_timer();
+ rescan_periodic_events(get_options());
}
#undef NEWLY_ENABLED
@@ -1680,6 +1681,8 @@ static const struct signal_t signal_table[] = {
{ SIGNEWNYM, "NEWNYM" },
{ SIGCLEARDNSCACHE, "CLEARDNSCACHE"},
{ SIGHEARTBEAT, "HEARTBEAT"},
+ { SIGACTIVE, "ACTIVE" },
+ { SIGDORMANT, "DORMANT" },
{ 0, NULL },
};
@@ -2352,7 +2355,11 @@ getinfo_helper_dir(control_connection_t *control_conn,
*answer = tor_strdup(consensus->dir);
}
if (!*answer) { /* try loading it from disk */
- *answer = networkstatus_read_cached_consensus("ns");
+ tor_mmap_t *mapped = networkstatus_map_cached_consensus("ns");
+ if (mapped) {
+ *answer = tor_memdup_nulterm(mapped->data, mapped->size);
+ tor_munmap_file(mapped);
+ }
if (!*answer) { /* generate an error */
*errmsg = "Could not open cached consensus. "
"Make sure FetchUselessDescriptors is set to 1.";
diff --git a/src/feature/dirauth/dirvote.c b/src/feature/dirauth/dirvote.c
index 80a5b54737..066a9e6e8a 100644
--- a/src/feature/dirauth/dirvote.c
+++ b/src/feature/dirauth/dirvote.c
@@ -413,7 +413,8 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
{
networkstatus_t *v;
- if (!(v = networkstatus_parse_vote_from_string(status, NULL,
+ if (!(v = networkstatus_parse_vote_from_string(status, strlen(status),
+ NULL,
v3_ns->type))) {
log_err(LD_BUG,"Generated a networkstatus %s we couldn't parse: "
"<<%s>>",
@@ -2410,7 +2411,8 @@ networkstatus_compute_consensus(smartlist_t *votes,
{
networkstatus_t *c;
- if (!(c = networkstatus_parse_vote_from_string(result, NULL,
+ if (!(c = networkstatus_parse_vote_from_string(result, strlen(result),
+ NULL,
NS_TYPE_CONSENSUS))) {
log_err(LD_BUG, "Generated a networkstatus consensus we couldn't "
"parse.");
@@ -3133,7 +3135,8 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out)
*msg_out = NULL;
again:
- vote = networkstatus_parse_vote_from_string(vote_body, &end_of_vote,
+ vote = networkstatus_parse_vote_from_string(vote_body, strlen(vote_body),
+ &end_of_vote,
NS_TYPE_VOTE);
if (!end_of_vote)
end_of_vote = vote_body + strlen(vote_body);
@@ -3391,7 +3394,9 @@ dirvote_compute_consensuses(void)
flavor_name);
continue;
}
- consensus = networkstatus_parse_vote_from_string(consensus_body, NULL,
+ consensus = networkstatus_parse_vote_from_string(consensus_body,
+ strlen(consensus_body),
+ NULL,
NS_TYPE_CONSENSUS);
if (!consensus) {
log_warn(LD_DIR, "Couldn't parse %s consensus we generated!",
@@ -3530,7 +3535,7 @@ dirvote_add_signatures_to_pending_consensus(
* just in case we break detached signature processing at some point. */
{
networkstatus_t *v = networkstatus_parse_vote_from_string(
- pc->body, NULL,
+ pc->body, strlen(pc->body), NULL,
NS_TYPE_CONSENSUS);
tor_assert(v);
networkstatus_vote_free(v);
@@ -3655,7 +3660,9 @@ dirvote_publish_consensus(void)
continue;
}
- if (networkstatus_set_current_consensus(pending->body, name, 0, NULL))
+ if (networkstatus_set_current_consensus(pending->body,
+ strlen(pending->body),
+ name, 0, NULL))
log_warn(LD_DIR, "Error publishing %s consensus", name);
else
log_notice(LD_DIR, "Published %s consensus", name);
diff --git a/src/feature/dirauth/process_descs.c b/src/feature/dirauth/process_descs.c
index c379f25bdd..dca87b3eaf 100644
--- a/src/feature/dirauth/process_descs.c
+++ b/src/feature/dirauth/process_descs.c
@@ -519,7 +519,8 @@ WRA_MORE_SEVERE(was_router_added_t a, was_router_added_t b)
/** As for dirserv_add_descriptor(), but accepts multiple documents, and
* returns the most severe error that occurred for any one of them. */
was_router_added_t
-dirserv_add_multiple_descriptors(const char *desc, uint8_t purpose,
+dirserv_add_multiple_descriptors(const char *desc, size_t desclen,
+ uint8_t purpose,
const char *source,
const char **msg)
{
@@ -536,6 +537,11 @@ dirserv_add_multiple_descriptors(const char *desc, uint8_t purpose,
r=ROUTER_ADDED_SUCCESSFULLY; /*Least severe return value. */
+ if (!string_is_utf8_no_bom(desc, desclen)) {
+ *msg = "descriptor(s) or extrainfo(s) not valid UTF-8 or had BOM.";
+ return ROUTER_AUTHDIR_REJECTS;
+ }
+
format_iso_time(time_buf, now);
if (tor_snprintf(annotation_buf, sizeof(annotation_buf),
"@uploaded-at %s\n"
@@ -552,7 +558,7 @@ dirserv_add_multiple_descriptors(const char *desc, uint8_t purpose,
s = desc;
list = smartlist_new();
- if (!router_parse_list_from_string(&s, NULL, list, SAVED_NOWHERE, 0, 0,
+ if (!router_parse_list_from_string(&s, s+desclen, list, SAVED_NOWHERE, 0, 0,
annotation_buf, NULL)) {
SMARTLIST_FOREACH(list, routerinfo_t *, ri, {
msg_out = NULL;
@@ -568,7 +574,7 @@ dirserv_add_multiple_descriptors(const char *desc, uint8_t purpose,
smartlist_clear(list);
s = desc;
- if (!router_parse_list_from_string(&s, NULL, list, SAVED_NOWHERE, 1, 0,
+ if (!router_parse_list_from_string(&s, s+desclen, list, SAVED_NOWHERE, 1, 0,
NULL, NULL)) {
SMARTLIST_FOREACH(list, extrainfo_t *, ei, {
msg_out = NULL;
diff --git a/src/feature/dirauth/process_descs.h b/src/feature/dirauth/process_descs.h
index ad9d5c3d4c..5a0914acd8 100644
--- a/src/feature/dirauth/process_descs.h
+++ b/src/feature/dirauth/process_descs.h
@@ -17,7 +17,8 @@ void dirserv_free_fingerprint_list(void);
int dirserv_add_own_fingerprint(crypto_pk_t *pk);
enum was_router_added_t dirserv_add_multiple_descriptors(
- const char *desc, uint8_t purpose,
+ const char *desc, size_t desclen,
+ uint8_t purpose,
const char *source,
const char **msg);
enum was_router_added_t dirserv_add_descriptor(routerinfo_t *ri,
diff --git a/src/feature/dirauth/shared_random_state.c b/src/feature/dirauth/shared_random_state.c
index 38c7fd76d0..1ce06744d4 100644
--- a/src/feature/dirauth/shared_random_state.c
+++ b/src/feature/dirauth/shared_random_state.c
@@ -22,6 +22,7 @@
#include "feature/dirauth/shared_random_state.h"
#include "feature/dircommon/voting_schedule.h"
#include "lib/encoding/confline.h"
+#include "lib/version/torversion.h"
#include "app/config/or_state_st.h"
diff --git a/src/feature/dircache/consdiffmgr.c b/src/feature/dircache/consdiffmgr.c
index e79aad6efb..8ecab5ca69 100644
--- a/src/feature/dircache/consdiffmgr.c
+++ b/src/feature/dircache/consdiffmgr.c
@@ -189,6 +189,7 @@ static consdiff_cfg_t consdiff_cfg = {
static int consdiffmgr_ensure_space_for_files(int n);
static int consensus_queue_compression_work(const char *consensus,
+ size_t consensus_len,
const networkstatus_t *as_parsed);
static int consensus_diff_queue_diff_work(consensus_cache_entry_t *diff_from,
consensus_cache_entry_t *diff_to);
@@ -509,8 +510,25 @@ get_max_age_to_cache(void)
MAX_MAX_AGE_TO_CACHE);
}
+#ifdef TOR_UNIT_TESTS
+/** As consdiffmgr_add_consensus, but requires a nul-terminated input. For
+ * testing. */
+int
+consdiffmgr_add_consensus_nulterm(const char *consensus,
+ const networkstatus_t *as_parsed)
+{
+ size_t len = strlen(consensus);
+ /* make a non-nul-terminated copy so that we can have a better chance
+ * of catching errors. */
+ char *ctmp = tor_memdup(consensus, len);
+ int r = consdiffmgr_add_consensus(ctmp, len, as_parsed);
+ tor_free(ctmp);
+ return r;
+}
+#endif
+
/**
- * Given a string containing a networkstatus consensus, and the results of
+ * Given a buffer containing a networkstatus consensus, and the results of
* having parsed that consensus, add that consensus to the cache if it is not
* already present and not too old. Create new consensus diffs from or to
* that consensus as appropriate.
@@ -519,6 +537,7 @@ get_max_age_to_cache(void)
*/
int
consdiffmgr_add_consensus(const char *consensus,
+ size_t consensus_len,
const networkstatus_t *as_parsed)
{
if (BUG(consensus == NULL) || BUG(as_parsed == NULL))
@@ -544,7 +563,7 @@ consdiffmgr_add_consensus(const char *consensus,
}
/* We don't have it. Add it to the cache. */
- return consensus_queue_compression_work(consensus, as_parsed);
+ return consensus_queue_compression_work(consensus, consensus_len, as_parsed);
}
/**
@@ -1387,19 +1406,21 @@ typedef struct consensus_diff_worker_job_t {
} consensus_diff_worker_job_t;
/** Given a consensus_cache_entry_t, check whether it has a label claiming
- * that it was compressed. If so, uncompress its contents into <b>out</b> and
- * set <b>outlen</b> to hold their size. If not, just copy the body into
- * <b>out</b> and set <b>outlen</b> to its length. Return 0 on success,
- * -1 on failure.
- *
- * In all cases, the output is nul-terminated. */
+ * that it was compressed. If so, uncompress its contents into *<b>out</b> and
+ * set <b>outlen</b> to hold their size, and set *<b>owned_out</b> to a pointer
+ * that the caller will need to free. If not, just set *<b>out</b> and
+ * <b>outlen</b> to its extent in memory. Return 0 on success, -1 on failure.
+ **/
STATIC int
-uncompress_or_copy(char **out, size_t *outlen,
- consensus_cache_entry_t *ent)
+uncompress_or_set_ptr(const char **out, size_t *outlen,
+ char **owned_out,
+ consensus_cache_entry_t *ent)
{
const uint8_t *body;
size_t bodylen;
+ *owned_out = NULL;
+
if (consensus_cache_entry_get_body(ent, &body, &bodylen) < 0)
return -1;
@@ -1410,8 +1431,17 @@ uncompress_or_copy(char **out, size_t *outlen,
if (lv_compression)
method = compression_method_get_by_name(lv_compression);
- return tor_uncompress(out, outlen, (const char *)body, bodylen,
+ int rv;
+ if (method == NO_METHOD) {
+ *out = (const char *)body;
+ *outlen = bodylen;
+ rv = 0;
+ } else {
+ rv = tor_uncompress(owned_out, outlen, (const char *)body, bodylen,
method, 1, LOG_WARN);
+ *out = *owned_out;
+ }
+ return rv;
}
/**
@@ -1478,16 +1508,17 @@ consensus_diff_worker_threadfn(void *state_, void *work_)
char *consensus_diff;
{
- char *diff_from_nt = NULL, *diff_to_nt = NULL;
+ const char *diff_from_nt = NULL, *diff_to_nt = NULL;
+ char *owned1 = NULL, *owned2 = NULL;
size_t diff_from_nt_len, diff_to_nt_len;
- if (uncompress_or_copy(&diff_from_nt, &diff_from_nt_len,
- job->diff_from) < 0) {
+ if (uncompress_or_set_ptr(&diff_from_nt, &diff_from_nt_len, &owned1,
+ job->diff_from) < 0) {
return WQ_RPL_REPLY;
}
- if (uncompress_or_copy(&diff_to_nt, &diff_to_nt_len,
- job->diff_to) < 0) {
- tor_free(diff_from_nt);
+ if (uncompress_or_set_ptr(&diff_to_nt, &diff_to_nt_len, &owned2,
+ job->diff_to) < 0) {
+ tor_free(owned1);
return WQ_RPL_REPLY;
}
tor_assert(diff_from_nt);
@@ -1496,9 +1527,12 @@ consensus_diff_worker_threadfn(void *state_, void *work_)
// XXXX ugh; this is going to calculate the SHA3 of both its
// XXXX inputs again, even though we already have that. Maybe it's time
// XXXX to change the API here?
- consensus_diff = consensus_diff_generate(diff_from_nt, diff_to_nt);
- tor_free(diff_from_nt);
- tor_free(diff_to_nt);
+ consensus_diff = consensus_diff_generate(diff_from_nt,
+ diff_from_nt_len,
+ diff_to_nt,
+ diff_to_nt_len);
+ tor_free(owned1);
+ tor_free(owned2);
}
if (!consensus_diff) {
/* Couldn't generate consensus; we'll leave the reply blank. */
@@ -1746,8 +1780,8 @@ consensus_compress_worker_threadfn(void *state_, void *work_)
(const uint8_t *)consensus, bodylen);
{
const char *start, *end;
- if (router_get_networkstatus_v3_signed_boundaries(consensus,
- &start, &end) < 0) {
+ if (router_get_networkstatus_v3_signed_boundaries(consensus, bodylen,
+ &start, &end) < 0) {
start = consensus;
end = consensus+bodylen;
}
@@ -1811,14 +1845,15 @@ static int background_compression = 0;
*/
static int
consensus_queue_compression_work(const char *consensus,
+ size_t consensus_len,
const networkstatus_t *as_parsed)
{
tor_assert(consensus);
tor_assert(as_parsed);
consensus_compress_worker_job_t *job = tor_malloc_zero(sizeof(*job));
- job->consensus = tor_strdup(consensus);
- job->consensus_len = strlen(consensus);
+ job->consensus = tor_memdup_nulterm(consensus, consensus_len);
+ job->consensus_len = strlen(job->consensus);
job->flavor = as_parsed->flavor;
char va_str[ISO_TIME_LEN+1];
diff --git a/src/feature/dircache/consdiffmgr.h b/src/feature/dircache/consdiffmgr.h
index 66c3d65002..011c8799d6 100644
--- a/src/feature/dircache/consdiffmgr.h
+++ b/src/feature/dircache/consdiffmgr.h
@@ -22,6 +22,7 @@ typedef struct consdiff_cfg_t {
struct consensus_cache_entry_t; // from conscache.h
int consdiffmgr_add_consensus(const char *consensus,
+ size_t consensus_len,
const networkstatus_t *as_parsed);
consdiff_status_t consdiffmgr_find_consensus(
@@ -68,8 +69,14 @@ STATIC consensus_cache_entry_t *cdm_cache_lookup_consensus(
STATIC int cdm_entry_get_sha3_value(uint8_t *digest_out,
consensus_cache_entry_t *ent,
const char *label);
-STATIC int uncompress_or_copy(char **out, size_t *outlen,
- consensus_cache_entry_t *ent);
+STATIC int uncompress_or_set_ptr(const char **out, size_t *outlen,
+ char **owned_out,
+ consensus_cache_entry_t *ent);
#endif /* defined(CONSDIFFMGR_PRIVATE) */
+#ifdef TOR_UNIT_TESTS
+int consdiffmgr_add_consensus_nulterm(const char *consensus,
+ const networkstatus_t *as_parsed);
+#endif
+
#endif /* !defined(TOR_CONSDIFFMGR_H) */
diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c
index 872a88018f..4032223db4 100644
--- a/src/feature/dircache/dircache.c
+++ b/src/feature/dircache/dircache.c
@@ -166,22 +166,16 @@ write_http_response_header_impl(dir_connection_t *conn, ssize_t length,
buf_free(buf);
}
-/** As write_http_response_header_impl, but sets encoding and content-typed
- * based on whether the response will be <b>compressed</b> or not. */
+/** As write_http_response_header_impl, but translates method into
+ * encoding */
static void
write_http_response_headers(dir_connection_t *conn, ssize_t length,
compress_method_t method,
const char *extra_headers, long cache_lifetime)
{
- const char *methodname = compression_method_get_name(method);
- const char *doctype;
- if (method == NO_METHOD)
- doctype = "text/plain";
- else
- doctype = "application/octet-stream";
write_http_response_header_impl(conn, length,
- doctype,
- methodname,
+ "text/plain",
+ compression_method_get_name(method),
extra_headers,
cache_lifetime);
}
@@ -1608,8 +1602,8 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
const char *msg = "[None]";
uint8_t purpose = authdir_mode_bridge(options) ?
ROUTER_PURPOSE_BRIDGE : ROUTER_PURPOSE_GENERAL;
- was_router_added_t r = dirserv_add_multiple_descriptors(body, purpose,
- conn->base_.address, &msg);
+ was_router_added_t r = dirserv_add_multiple_descriptors(body, body_len,
+ purpose, conn->base_.address, &msg);
tor_assert(msg);
if (r == ROUTER_ADDED_SUCCESSFULLY) {
diff --git a/src/feature/dircache/dirserv.c b/src/feature/dircache/dirserv.c
index 57178cd506..4366000e2e 100644
--- a/src/feature/dircache/dirserv.c
+++ b/src/feature/dircache/dirserv.c
@@ -234,6 +234,7 @@ free_cached_dir_(void *_d)
* validation is performed. */
void
dirserv_set_cached_consensus_networkstatus(const char *networkstatus,
+ size_t networkstatus_len,
const char *flavor_name,
const common_digests_t *digests,
const uint8_t *sha3_as_signed,
@@ -244,7 +245,9 @@ dirserv_set_cached_consensus_networkstatus(const char *networkstatus,
if (!cached_consensuses)
cached_consensuses = strmap_new();
- new_networkstatus = new_cached_dir(tor_strdup(networkstatus), published);
+ new_networkstatus =
+ new_cached_dir(tor_memdup_nulterm(networkstatus, networkstatus_len),
+ published);
memcpy(&new_networkstatus->digests, digests, sizeof(common_digests_t));
memcpy(&new_networkstatus->digest_sha3_as_signed, sha3_as_signed,
DIGEST256_LEN);
diff --git a/src/feature/dircache/dirserv.h b/src/feature/dircache/dirserv.h
index 41e1376688..aa1e2494ca 100644
--- a/src/feature/dircache/dirserv.h
+++ b/src/feature/dircache/dirserv.h
@@ -84,6 +84,7 @@ int directory_too_idle_to_fetch_descriptors(const or_options_t *options,
cached_dir_t *dirserv_get_consensus(const char *flavor_name);
void dirserv_set_cached_consensus_networkstatus(const char *consensus,
+ size_t consensus_len,
const char *flavor_name,
const common_digests_t *digests,
const uint8_t *sha3_as_signed,
diff --git a/src/feature/dirclient/dirclient.c b/src/feature/dirclient/dirclient.c
index f6a712e429..cd88fa5ebf 100644
--- a/src/feature/dirclient/dirclient.c
+++ b/src/feature/dirclient/dirclient.c
@@ -2205,24 +2205,31 @@ handle_response_fetch_consensus(dir_connection_t *conn,
if (looks_like_a_consensus_diff(body, body_len)) {
/* First find our previous consensus. Maybe it's in ram, maybe not. */
cached_dir_t *cd = dirserv_get_consensus(flavname);
- const char *consensus_body;
- char *owned_consensus = NULL;
+ const char *consensus_body = NULL;
+ size_t consensus_body_len;
+ tor_mmap_t *mapped_consensus = NULL;
if (cd) {
consensus_body = cd->dir;
+ consensus_body_len = cd->dir_len;
} else {
- owned_consensus = networkstatus_read_cached_consensus(flavname);
- consensus_body = owned_consensus;
+ mapped_consensus = networkstatus_map_cached_consensus(flavname);
+ if (mapped_consensus) {
+ consensus_body = mapped_consensus->data;
+ consensus_body_len = mapped_consensus->size;
+ }
}
if (!consensus_body) {
log_warn(LD_DIR, "Received a consensus diff, but we can't find "
"any %s-flavored consensus in our current cache.",flavname);
+ tor_munmap_file(mapped_consensus);
networkstatus_consensus_download_failed(0, flavname);
// XXXX if this happens too much, see below
return -1;
}
- new_consensus = consensus_diff_apply(consensus_body, body);
- tor_free(owned_consensus);
+ new_consensus = consensus_diff_apply(consensus_body, consensus_body_len,
+ body, body_len);
+ tor_munmap_file(mapped_consensus);
if (new_consensus == NULL) {
log_warn(LD_DIR, "Could not apply consensus diff received from server "
"'%s:%d'", conn->base_.address, conn->base_.port);
@@ -2244,7 +2251,9 @@ handle_response_fetch_consensus(dir_connection_t *conn,
sourcename = "downloaded";
}
- if ((r=networkstatus_set_current_consensus(consensus, flavname, 0,
+ if ((r=networkstatus_set_current_consensus(consensus,
+ strlen(consensus),
+ flavname, 0,
conn->identity_digest))<0) {
log_fn(r<-1?LOG_WARN:LOG_INFO, LD_DIR,
"Unable to load %s consensus directory %s from "
diff --git a/src/feature/dircommon/consdiff.c b/src/feature/dircommon/consdiff.c
index f8ced8503f..15266f77bf 100644
--- a/src/feature/dircommon/consdiff.c
+++ b/src/feature/dircommon/consdiff.c
@@ -101,11 +101,11 @@ smartlist_add_linecpy(smartlist_t *lst, memarea_t *area, const char *s)
/* This is a separate, mockable function so that we can override it when
* fuzzing. */
MOCK_IMPL(STATIC int,
-consensus_compute_digest,(const char *cons,
+consensus_compute_digest,(const char *cons, size_t len,
consensus_digest_t *digest_out))
{
int r = crypto_digest256((char*)digest_out->sha3_256,
- cons, strlen(cons), DIGEST_SHA3_256);
+ cons, len, DIGEST_SHA3_256);
return r;
}
@@ -114,11 +114,11 @@ consensus_compute_digest,(const char *cons,
/* This is a separate, mockable function so that we can override it when
* fuzzing. */
MOCK_IMPL(STATIC int,
-consensus_compute_digest_as_signed,(const char *cons,
+consensus_compute_digest_as_signed,(const char *cons, size_t len,
consensus_digest_t *digest_out))
{
return router_get_networkstatus_v3_sha3_as_signed(digest_out->sha3_256,
- cons);
+ cons, len);
}
/** Return true iff <b>d1</b> and <b>d2</b> contain the same digest */
@@ -1229,7 +1229,8 @@ consdiff_apply_diff(const smartlist_t *cons1,
cons2_str = consensus_join_lines(cons2);
consensus_digest_t cons2_digests;
- if (consensus_compute_digest(cons2_str, &cons2_digests) < 0) {
+ if (consensus_compute_digest(cons2_str, strlen(cons2_str),
+ &cons2_digests) < 0) {
/* LCOV_EXCL_START -- digest can't fail */
log_warn(LD_CONSDIFF, "Could not compute digests of the consensus "
"resulting from applying a consensus diff.");
@@ -1283,12 +1284,13 @@ consdiff_apply_diff(const smartlist_t *cons1,
* generated cdlines will become invalid.
*/
STATIC int
-consensus_split_lines(smartlist_t *out, const char *s, memarea_t *area)
+consensus_split_lines(smartlist_t *out,
+ const char *s, size_t len,
+ memarea_t *area)
{
- const char *end_of_str = s + strlen(s);
- tor_assert(*end_of_str == '\0');
+ const char *end_of_str = s + len;
- while (*s) {
+ while (s < end_of_str) {
const char *eol = memchr(s, '\n', end_of_str - s);
if (!eol) {
/* File doesn't end with newline. */
@@ -1334,25 +1336,25 @@ consensus_join_lines(const smartlist_t *inp)
* success, retun a newly allocated string containing that diff. On failure,
* return NULL. */
char *
-consensus_diff_generate(const char *cons1,
- const char *cons2)
+consensus_diff_generate(const char *cons1, size_t cons1len,
+ const char *cons2, size_t cons2len)
{
consensus_digest_t d1, d2;
smartlist_t *lines1 = NULL, *lines2 = NULL, *result_lines = NULL;
int r1, r2;
char *result = NULL;
- r1 = consensus_compute_digest_as_signed(cons1, &d1);
- r2 = consensus_compute_digest(cons2, &d2);
+ r1 = consensus_compute_digest_as_signed(cons1, cons1len, &d1);
+ r2 = consensus_compute_digest(cons2, cons2len, &d2);
if (BUG(r1 < 0 || r2 < 0))
return NULL; // LCOV_EXCL_LINE
memarea_t *area = memarea_new();
lines1 = smartlist_new();
lines2 = smartlist_new();
- if (consensus_split_lines(lines1, cons1, area) < 0)
+ if (consensus_split_lines(lines1, cons1, cons1len, area) < 0)
goto done;
- if (consensus_split_lines(lines2, cons2, area) < 0)
+ if (consensus_split_lines(lines2, cons2, cons2len, area) < 0)
goto done;
result_lines = consdiff_gen_diff(lines1, lines2, &d1, &d2, area);
@@ -1375,7 +1377,9 @@ consensus_diff_generate(const char *cons1,
* consensus. On failure, return NULL. */
char *
consensus_diff_apply(const char *consensus,
- const char *diff)
+ size_t consensus_len,
+ const char *diff,
+ size_t diff_len)
{
consensus_digest_t d1;
smartlist_t *lines1 = NULL, *lines2 = NULL;
@@ -1383,15 +1387,15 @@ consensus_diff_apply(const char *consensus,
char *result = NULL;
memarea_t *area = memarea_new();
- r1 = consensus_compute_digest_as_signed(consensus, &d1);
+ r1 = consensus_compute_digest_as_signed(consensus, consensus_len, &d1);
if (BUG(r1 < 0))
return NULL; // LCOV_EXCL_LINE
lines1 = smartlist_new();
lines2 = smartlist_new();
- if (consensus_split_lines(lines1, consensus, area) < 0)
+ if (consensus_split_lines(lines1, consensus, consensus_len, area) < 0)
goto done;
- if (consensus_split_lines(lines2, diff, area) < 0)
+ if (consensus_split_lines(lines2, diff, diff_len, area) < 0)
goto done;
result = consdiff_apply_diff(lines1, lines2, &d1);
diff --git a/src/feature/dircommon/consdiff.h b/src/feature/dircommon/consdiff.h
index a5e4ba5cbf..eb7c9f9fe0 100644
--- a/src/feature/dircommon/consdiff.h
+++ b/src/feature/dircommon/consdiff.h
@@ -7,10 +7,10 @@
#include "core/or/or.h"
-char *consensus_diff_generate(const char *cons1,
- const char *cons2);
-char *consensus_diff_apply(const char *consensus,
- const char *diff);
+char *consensus_diff_generate(const char *cons1, size_t cons1len,
+ const char *cons2, size_t cons2len);
+char *consensus_diff_apply(const char *consensus, size_t consensus_len,
+ const char *diff, size_t diff_len);
int looks_like_a_consensus_diff(const char *document, size_t len);
@@ -78,7 +78,8 @@ STATIC int smartlist_slice_string_pos(const smartlist_slice_t *slice,
STATIC void set_changed(bitarray_t *changed1, bitarray_t *changed2,
const smartlist_slice_t *slice1,
const smartlist_slice_t *slice2);
-STATIC int consensus_split_lines(smartlist_t *out, const char *s,
+STATIC int consensus_split_lines(smartlist_t *out,
+ const char *s, size_t len,
struct memarea_t *area);
STATIC void smartlist_add_linecpy(smartlist_t *lst, struct memarea_t *area,
const char *s);
@@ -86,10 +87,10 @@ STATIC int lines_eq(const cdline_t *a, const cdline_t *b);
STATIC int line_str_eq(const cdline_t *a, const char *b);
MOCK_DECL(STATIC int,
- consensus_compute_digest,(const char *cons,
+ consensus_compute_digest,(const char *cons, size_t len,
consensus_digest_t *digest_out));
MOCK_DECL(STATIC int,
- consensus_compute_digest_as_signed,(const char *cons,
+ consensus_compute_digest_as_signed,(const char *cons, size_t len,
consensus_digest_t *digest_out));
MOCK_DECL(STATIC int,
consensus_digest_eq,(const uint8_t *d1,
diff --git a/src/feature/dirparse/authcert_parse.c b/src/feature/dirparse/authcert_parse.c
index 2ba46bb8fa..334baf8b1a 100644
--- a/src/feature/dirparse/authcert_parse.c
+++ b/src/feature/dirparse/authcert_parse.c
@@ -24,7 +24,8 @@ static token_rule_t dir_key_certificate_table[] = {
/** Parse a key certificate from <b>s</b>; point <b>end-of-string</b> to
* the first character after the certificate. */
authority_cert_t *
-authority_cert_parse_from_string(const char *s, const char **end_of_string)
+authority_cert_parse_from_string(const char *s, size_t maxlen,
+ const char **end_of_string)
{
/** Reject any certificate at least this big; it is probably an overflow, an
* attack, a bug, or some other nonsense. */
@@ -35,24 +36,25 @@ authority_cert_parse_from_string(const char *s, const char **end_of_string)
char digest[DIGEST_LEN];
directory_token_t *tok;
char fp_declared[DIGEST_LEN];
- char *eos;
+ const char *eos;
size_t len;
int found;
memarea_t *area = NULL;
+ const char *end_of_s = s + maxlen;
const char *s_dup = s;
- s = eat_whitespace(s);
- eos = strstr(s, "\ndir-key-certification");
+ s = eat_whitespace_eos(s, end_of_s);
+ eos = tor_memstr(s, end_of_s - s, "\ndir-key-certification");
if (! eos) {
log_warn(LD_DIR, "No signature found on key certificate");
return NULL;
}
- eos = strstr(eos, "\n-----END SIGNATURE-----\n");
+ eos = tor_memstr(eos, end_of_s - eos, "\n-----END SIGNATURE-----\n");
if (! eos) {
log_warn(LD_DIR, "No end-of-signature found on key certificate");
return NULL;
}
- eos = strchr(eos+2, '\n');
+ eos = memchr(eos+2, '\n', end_of_s - (eos+2));
tor_assert(eos);
++eos;
len = eos - s;
@@ -69,7 +71,7 @@ authority_cert_parse_from_string(const char *s, const char **end_of_string)
log_warn(LD_DIR, "Error tokenizing key certificate");
goto err;
}
- if (router_get_hash_impl(s, strlen(s), digest, "dir-key-certificate-version",
+ if (router_get_hash_impl(s, eos - s, digest, "dir-key-certificate-version",
"\ndir-key-certification", '\n', DIGEST_SHA1) < 0)
goto err;
tok = smartlist_get(tokens, 0);
diff --git a/src/feature/dirparse/authcert_parse.h b/src/feature/dirparse/authcert_parse.h
index f63525e04d..e4e9fec993 100644
--- a/src/feature/dirparse/authcert_parse.h
+++ b/src/feature/dirparse/authcert_parse.h
@@ -13,6 +13,7 @@
#define TOR_AUTHCERT_PARSE_H
authority_cert_t *authority_cert_parse_from_string(const char *s,
+ size_t maxlen,
const char **end_of_string);
#endif /* !defined(TOR_AUTHCERT_PARSE_H) */
diff --git a/src/feature/dirparse/microdesc_parse.c b/src/feature/dirparse/microdesc_parse.c
index aebff5a35f..8ad9626377 100644
--- a/src/feature/dirparse/microdesc_parse.c
+++ b/src/feature/dirparse/microdesc_parse.c
@@ -18,6 +18,7 @@
#include "feature/dirparse/routerparse.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/nickname.h"
+#include "feature/nodelist/nodefamily.h"
#include "feature/relay/router.h"
#include "lib/crypt_ops/crypto_curve25519.h"
#include "lib/crypt_ops/crypto_ed25519.h"
@@ -32,7 +33,7 @@ static token_rule_t microdesc_token_table[] = {
T01("ntor-onion-key", K_ONION_KEY_NTOR, GE(1), NO_OBJ ),
T0N("id", K_ID, GE(2), NO_OBJ ),
T0N("a", K_A, GE(1), NO_OBJ ),
- T01("family", K_FAMILY, ARGS, NO_OBJ ),
+ T01("family", K_FAMILY, CONCAT_ARGS, NO_OBJ ),
T01("p", K_P, CONCAT_ARGS, NO_OBJ ),
T01("p6", K_P6, CONCAT_ARGS, NO_OBJ ),
A01("@last-listed", A_LAST_LISTED, CONCAT_ARGS, NO_OBJ ),
@@ -222,16 +223,9 @@ microdescs_parse_from_string(const char *s, const char *eos,
}
if ((tok = find_opt_by_keyword(tokens, K_FAMILY))) {
- int i;
- md->family = smartlist_new();
- for (i=0;i<tok->n_args;++i) {
- if (!is_legal_nickname_or_hexdigest(tok->args[i])) {
- log_warn(LD_DIR, "Illegal nickname %s in family line",
- escaped(tok->args[i]));
- goto next;
- }
- smartlist_add_strdup(md->family, tok->args[i]);
- }
+ md->family = nodefamily_parse(tok->args[0],
+ NULL,
+ NF_WARN_MALFORMED);
}
if ((tok = find_opt_by_keyword(tokens, K_P))) {
diff --git a/src/feature/dirparse/ns_parse.c b/src/feature/dirparse/ns_parse.c
index 72299e8071..3fccec1540 100644
--- a/src/feature/dirparse/ns_parse.c
+++ b/src/feature/dirparse/ns_parse.c
@@ -151,10 +151,11 @@ static token_rule_t networkstatus_vote_footer_token_table[] = {
* -1. */
int
router_get_networkstatus_v3_signed_boundaries(const char *s,
+ size_t len,
const char **start_out,
const char **end_out)
{
- return router_get_hash_impl_helper(s, strlen(s),
+ return router_get_hash_impl_helper(s, len,
"network-status-version",
"\ndirectory-signature",
' ', LOG_INFO,
@@ -166,12 +167,13 @@ router_get_networkstatus_v3_signed_boundaries(const char *s,
* signed portion can be identified. Return 0 on success, -1 on failure. */
int
router_get_networkstatus_v3_sha3_as_signed(uint8_t *digest_out,
- const char *s)
+ const char *s, size_t len)
{
const char *start, *end;
- if (router_get_networkstatus_v3_signed_boundaries(s, &start, &end) < 0) {
+ if (router_get_networkstatus_v3_signed_boundaries(s, len,
+ &start, &end) < 0) {
start = s;
- end = s + strlen(s);
+ end = s + len;
}
tor_assert(start);
tor_assert(end);
@@ -182,9 +184,10 @@ router_get_networkstatus_v3_sha3_as_signed(uint8_t *digest_out,
/** Set <b>digests</b> to all the digests of the consensus document in
* <b>s</b> */
int
-router_get_networkstatus_v3_hashes(const char *s, common_digests_t *digests)
+router_get_networkstatus_v3_hashes(const char *s, size_t len,
+ common_digests_t *digests)
{
- return router_get_hashes_impl(s,strlen(s),digests,
+ return router_get_hashes_impl(s, len, digests,
"network-status-version",
"\ndirectory-signature",
' ');
@@ -195,13 +198,13 @@ router_get_networkstatus_v3_hashes(const char *s, common_digests_t *digests)
* return the start of the directory footer, or the next directory signature.
* If none is found, return the end of the string. */
static inline const char *
-find_start_of_next_routerstatus(const char *s)
+find_start_of_next_routerstatus(const char *s, const char *s_eos)
{
const char *eos, *footer, *sig;
- if ((eos = strstr(s, "\nr ")))
+ if ((eos = tor_memstr(s, s_eos - s, "\nr ")))
++eos;
else
- eos = s + strlen(s);
+ eos = s_eos;
footer = tor_memstr(s, eos-s, "\ndirectory-footer");
sig = tor_memstr(s, eos-s, "\ndirectory-signature");
@@ -289,7 +292,8 @@ routerstatus_parse_guardfraction(const char *guardfraction_str,
**/
STATIC routerstatus_t *
routerstatus_parse_entry_from_string(memarea_t *area,
- const char **s, smartlist_t *tokens,
+ const char **s, const char *s_eos,
+ smartlist_t *tokens,
networkstatus_t *vote,
vote_routerstatus_t *vote_rs,
int consensus_method,
@@ -308,7 +312,7 @@ routerstatus_parse_entry_from_string(memarea_t *area,
flav = FLAV_NS;
tor_assert(flav == FLAV_NS || flav == FLAV_MICRODESC);
- eos = find_start_of_next_routerstatus(*s);
+ eos = find_start_of_next_routerstatus(*s, s_eos);
if (tokenize_string(area,*s, eos, tokens, rtrstatus_token_table,0)) {
log_warn(LD_DIR, "Error tokenizing router status");
@@ -1051,7 +1055,9 @@ extract_shared_random_srvs(networkstatus_t *ns, smartlist_t *tokens)
/** Parse a v3 networkstatus vote, opinion, or consensus (depending on
* ns_type), from <b>s</b>, and return the result. Return NULL on failure. */
networkstatus_t *
-networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
+networkstatus_parse_vote_from_string(const char *s,
+ size_t s_len,
+ const char **eos_out,
networkstatus_type_t ns_type)
{
smartlist_t *tokens = smartlist_new();
@@ -1067,20 +1073,22 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
memarea_t *area = NULL, *rs_area = NULL;
consensus_flavor_t flav = FLAV_NS;
char *last_kwd=NULL;
+ const char *eos = s + s_len;
tor_assert(s);
if (eos_out)
*eos_out = NULL;
- if (router_get_networkstatus_v3_hashes(s, &ns_digests) ||
- router_get_networkstatus_v3_sha3_as_signed(sha3_as_signed, s)<0) {
+ if (router_get_networkstatus_v3_hashes(s, s_len, &ns_digests) ||
+ router_get_networkstatus_v3_sha3_as_signed(sha3_as_signed,
+ s, s_len)<0) {
log_warn(LD_DIR, "Unable to compute digest of network-status");
goto err;
}
area = memarea_new();
- end_of_header = find_start_of_next_routerstatus(s);
+ end_of_header = find_start_of_next_routerstatus(s, eos);
if (tokenize_string(area, s, end_of_header, tokens,
(ns_type == NS_TYPE_CONSENSUS) ?
networkstatus_consensus_token_table :
@@ -1111,10 +1119,12 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
if (ns_type != NS_TYPE_CONSENSUS) {
const char *end_of_cert = NULL;
- if (!(cert = strstr(s, "\ndir-key-certificate-version")))
+ if (!(cert = tor_memstr(s, end_of_header - s,
+ "\ndir-key-certificate-version")))
goto err;
++cert;
- ns->cert = authority_cert_parse_from_string(cert, &end_of_cert);
+ ns->cert = authority_cert_parse_from_string(cert, end_of_header - cert,
+ &end_of_cert);
if (!ns->cert || !end_of_cert || end_of_cert > end_of_header)
goto err;
}
@@ -1424,10 +1434,10 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
s = end_of_header;
ns->routerstatus_list = smartlist_new();
- while (!strcmpstart(s, "r ")) {
+ while (eos - s >= 2 && fast_memeq(s, "r ", 2)) {
if (ns->type != NS_TYPE_CONSENSUS) {
vote_routerstatus_t *rs = tor_malloc_zero(sizeof(vote_routerstatus_t));
- if (routerstatus_parse_entry_from_string(rs_area, &s, rs_tokens, ns,
+ if (routerstatus_parse_entry_from_string(rs_area, &s, eos, rs_tokens, ns,
rs, 0, 0)) {
smartlist_add(ns->routerstatus_list, rs);
} else {
@@ -1435,7 +1445,8 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
}
} else {
routerstatus_t *rs;
- if ((rs = routerstatus_parse_entry_from_string(rs_area, &s, rs_tokens,
+ if ((rs = routerstatus_parse_entry_from_string(rs_area, &s, eos,
+ rs_tokens,
NULL, NULL,
ns->consensus_method,
flav))) {
@@ -1480,10 +1491,10 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
/* Parse footer; check signature. */
footer_tokens = smartlist_new();
- if ((end_of_footer = strstr(s, "\nnetwork-status-version ")))
+ if ((end_of_footer = tor_memstr(s, eos-s, "\nnetwork-status-version ")))
++end_of_footer;
else
- end_of_footer = s + strlen(s);
+ end_of_footer = eos;
if (tokenize_string(area,s, end_of_footer, footer_tokens,
networkstatus_vote_footer_token_table, 0)) {
log_warn(LD_DIR, "Error tokenizing network-status vote footer.");
diff --git a/src/feature/dirparse/ns_parse.h b/src/feature/dirparse/ns_parse.h
index 22438d73a7..85d9ded685 100644
--- a/src/feature/dirparse/ns_parse.h
+++ b/src/feature/dirparse/ns_parse.h
@@ -12,18 +12,19 @@
#ifndef TOR_NS_PARSE_H
#define TOR_NS_PARSE_H
-int router_get_networkstatus_v3_hashes(const char *s,
+int router_get_networkstatus_v3_hashes(const char *s, size_t len,
common_digests_t *digests);
-int router_get_networkstatus_v3_signed_boundaries(const char *s,
+int router_get_networkstatus_v3_signed_boundaries(const char *s, size_t len,
const char **start_out,
const char **end_out);
int router_get_networkstatus_v3_sha3_as_signed(uint8_t *digest_out,
- const char *s);
+ const char *s, size_t len);
int compare_vote_routerstatus_entries(const void **_a, const void **_b);
int networkstatus_verify_bw_weights(networkstatus_t *ns, int);
enum networkstatus_type_t;
networkstatus_t *networkstatus_parse_vote_from_string(const char *s,
+ size_t len,
const char **eos_out,
enum networkstatus_type_t ns_type);
@@ -35,7 +36,8 @@ STATIC int routerstatus_parse_guardfraction(const char *guardfraction_str,
struct memarea_t;
STATIC routerstatus_t *routerstatus_parse_entry_from_string(
struct memarea_t *area,
- const char **s, smartlist_t *tokens,
+ const char **s, const char *eos,
+ smartlist_t *tokens,
networkstatus_t *vote,
vote_routerstatus_t *vote_rs,
int consensus_method,
diff --git a/src/feature/hibernate/hibernate.c b/src/feature/hibernate/hibernate.c
index 6f8795cecc..feeb3d92ef 100644
--- a/src/feature/hibernate/hibernate.c
+++ b/src/feature/hibernate/hibernate.c
@@ -66,8 +66,9 @@ static hibernate_state_t hibernate_state = HIBERNATE_STATE_INITIAL;
/** If are hibernating, when do we plan to wake up? Set to 0 if we
* aren't hibernating. */
static time_t hibernate_end_time = 0;
-/** If we are shutting down, when do we plan finally exit? Set to 0 if
- * we aren't shutting down. */
+/** If we are shutting down, when do we plan finally exit? Set to 0 if we
+ * aren't shutting down. (This is obsolete; scheduled shutdowns are supposed
+ * to happen from mainloop_schedule_shutdown() now.) */
static time_t shutdown_time = 0;
/** A timed event that we'll use when it's time to wake up from
@@ -867,7 +868,13 @@ hibernate_begin(hibernate_state_t new_state, time_t now)
log_notice(LD_GENERAL,"Interrupt: we have stopped accepting new "
"connections, and will shut down in %d seconds. Interrupt "
"again to exit now.", options->ShutdownWaitLength);
- shutdown_time = time(NULL) + options->ShutdownWaitLength;
+ /* We add an arbitrary delay here so that even if something goes wrong
+ * with the mainloop shutdown code, we can still shutdown from
+ * consider_hibernation() if we call it... but so that the
+ * mainloop_schedule_shutdown() mechanism will be the first one called.
+ */
+ shutdown_time = time(NULL) + options->ShutdownWaitLength + 5;
+ mainloop_schedule_shutdown(options->ShutdownWaitLength);
#ifdef HAVE_SYSTEMD
/* tell systemd that we may need more than the default 90 seconds to shut
* down so they don't kill us. add some extra time to actually finish
@@ -1096,11 +1103,12 @@ consider_hibernation(time_t now)
hibernate_state_t prev_state = hibernate_state;
/* If we're in 'exiting' mode, then we just shut down after the interval
- * elapses. */
+ * elapses. The mainloop was supposed to catch this via
+ * mainloop_schedule_shutdown(), but apparently it didn't. */
if (hibernate_state == HIBERNATE_STATE_EXITING) {
tor_assert(shutdown_time);
if (shutdown_time <= now) {
- log_notice(LD_GENERAL, "Clean shutdown finished. Exiting.");
+ log_notice(LD_BUG, "Mainloop did not catch shutdown event; exiting.");
tor_shutdown_event_loop_and_exit(0);
}
return; /* if exiting soon, don't worry about bandwidth limits */
@@ -1112,7 +1120,7 @@ consider_hibernation(time_t now)
if (hibernate_end_time > now && accounting_enabled) {
/* If we're hibernating, don't wake up until it's time, regardless of
* whether we're in a new interval. */
- return ;
+ return;
} else {
hibernate_end_time_elapsed(now);
}
@@ -1240,8 +1248,6 @@ on_hibernate_state_change(hibernate_state_t prev_state)
if (prev_state != HIBERNATE_STATE_INITIAL) {
rescan_periodic_events(get_options());
}
-
- reschedule_per_second_timer();
}
/** Free all resources held by the accounting module */
diff --git a/src/feature/hs/hs_descriptor.c b/src/feature/hs/hs_descriptor.c
index 8515314b38..1b2008c804 100644
--- a/src/feature/hs/hs_descriptor.c
+++ b/src/feature/hs/hs_descriptor.c
@@ -1400,6 +1400,50 @@ encrypted_data_length_is_valid(size_t len)
return 0;
}
+/* Build the KEYS component for the authorized client computation. The format
+ * of the construction is:
+ *
+ * SECRET_SEED = x25519(sk, pk)
+ * KEYS = KDF(subcredential | SECRET_SEED, 40)
+ *
+ * Set the <b>keys_out</b> argument to point to the buffer containing the KEYS,
+ * and return the buffer's length. The caller should wipe and free its content
+ * once done with it. This function can't fail. */
+static size_t
+build_descriptor_cookie_keys(const uint8_t *subcredential,
+ size_t subcredential_len,
+ const curve25519_secret_key_t *sk,
+ const curve25519_public_key_t *pk,
+ uint8_t **keys_out)
+{
+ uint8_t secret_seed[CURVE25519_OUTPUT_LEN];
+ uint8_t *keystream;
+ size_t keystream_len = HS_DESC_CLIENT_ID_LEN + HS_DESC_COOKIE_KEY_LEN;
+ crypto_xof_t *xof;
+
+ tor_assert(subcredential);
+ tor_assert(sk);
+ tor_assert(pk);
+ tor_assert(keys_out);
+
+ keystream = tor_malloc_zero(keystream_len);
+
+ /* Calculate x25519(sk, pk) to get the secret seed. */
+ curve25519_handshake(secret_seed, sk, pk);
+
+ /* Calculate KEYS = KDF(subcredential | SECRET_SEED, 40) */
+ xof = crypto_xof_new();
+ crypto_xof_add_bytes(xof, subcredential, subcredential_len);
+ crypto_xof_add_bytes(xof, secret_seed, sizeof(secret_seed));
+ crypto_xof_squeeze_bytes(xof, keystream, keystream_len);
+ crypto_xof_free(xof);
+
+ memwipe(secret_seed, 0, sizeof(secret_seed));
+
+ *keys_out = keystream;
+ return keystream_len;
+}
+
/* Decrypt the descriptor cookie given the descriptor, the auth client,
* and the client secret key. On sucess, return 0 and a newly allocated
* descriptor cookie descriptor_cookie_out. On error or if the client id
@@ -1412,12 +1456,11 @@ decrypt_descriptor_cookie(const hs_descriptor_t *desc,
uint8_t **descriptor_cookie_out)
{
int ret = -1;
- uint8_t secret_seed[CURVE25519_OUTPUT_LEN];
- uint8_t keystream[HS_DESC_CLIENT_ID_LEN + HS_DESC_COOKIE_KEY_LEN];
- uint8_t *cookie_key = NULL;
+ uint8_t *keystream = NULL;
+ size_t keystream_length = 0;
uint8_t *descriptor_cookie = NULL;
+ const uint8_t *cookie_key = NULL;
crypto_cipher_t *cipher = NULL;
- crypto_xof_t *xof = NULL;
tor_assert(desc);
tor_assert(client);
@@ -1429,16 +1472,13 @@ decrypt_descriptor_cookie(const hs_descriptor_t *desc,
sizeof(*client_auth_sk)));
tor_assert(!tor_mem_is_zero((char *) desc->subcredential, DIGEST256_LEN));
- /* Calculate x25519(client_x, hs_Y) */
- curve25519_handshake(secret_seed, client_auth_sk,
- &desc->superencrypted_data.auth_ephemeral_pubkey);
-
- /* Calculate KEYS = KDF(subcredential | SECRET_SEED, 40) */
- xof = crypto_xof_new();
- crypto_xof_add_bytes(xof, desc->subcredential, DIGEST256_LEN);
- crypto_xof_add_bytes(xof, secret_seed, sizeof(secret_seed));
- crypto_xof_squeeze_bytes(xof, keystream, sizeof(keystream));
- crypto_xof_free(xof);
+ /* Get the KEYS component to derive the CLIENT-ID and COOKIE-KEY. */
+ keystream_length =
+ build_descriptor_cookie_keys(desc->subcredential, DIGEST256_LEN,
+ client_auth_sk,
+ &desc->superencrypted_data.auth_ephemeral_pubkey,
+ &keystream);
+ tor_assert(keystream_length > 0);
/* If the client id of auth client is not the same as the calculcated
* client id, it means that this auth client is invaild according to the
@@ -1464,8 +1504,8 @@ decrypt_descriptor_cookie(const hs_descriptor_t *desc,
if (cipher) {
crypto_cipher_free(cipher);
}
- memwipe(secret_seed, 0, sizeof(secret_seed));
- memwipe(keystream, 0, sizeof(keystream));
+ memwipe(keystream, 0, keystream_length);
+ tor_free(keystream);
return ret;
}
@@ -2878,11 +2918,10 @@ hs_desc_build_authorized_client(const uint8_t *subcredential,
const uint8_t *descriptor_cookie,
hs_desc_authorized_client_t *client_out)
{
- uint8_t secret_seed[CURVE25519_OUTPUT_LEN];
- uint8_t keystream[HS_DESC_CLIENT_ID_LEN + HS_DESC_COOKIE_KEY_LEN];
- uint8_t *cookie_key;
+ uint8_t *keystream = NULL;
+ size_t keystream_length = 0;
+ const uint8_t *cookie_key;
crypto_cipher_t *cipher;
- crypto_xof_t *xof;
tor_assert(client_auth_pk);
tor_assert(auth_ephemeral_sk);
@@ -2898,18 +2937,14 @@ hs_desc_build_authorized_client(const uint8_t *subcredential,
tor_assert(!tor_mem_is_zero((char *) subcredential,
DIGEST256_LEN));
- /* Calculate x25519(hs_y, client_X) */
- curve25519_handshake(secret_seed,
- auth_ephemeral_sk,
- client_auth_pk);
-
- /* Calculate KEYS = KDF(subcredential | SECRET_SEED, 40) */
- xof = crypto_xof_new();
- crypto_xof_add_bytes(xof, subcredential, DIGEST256_LEN);
- crypto_xof_add_bytes(xof, secret_seed, sizeof(secret_seed));
- crypto_xof_squeeze_bytes(xof, keystream, sizeof(keystream));
- crypto_xof_free(xof);
+ /* Get the KEYS part so we can derive the CLIENT-ID and COOKIE-KEY. */
+ keystream_length =
+ build_descriptor_cookie_keys(subcredential, DIGEST256_LEN,
+ auth_ephemeral_sk, client_auth_pk,
+ &keystream);
+ tor_assert(keystream_length > 0);
+ /* Extract the CLIENT-ID and COOKIE-KEY from the KEYS. */
memcpy(client_out->client_id, keystream, HS_DESC_CLIENT_ID_LEN);
cookie_key = keystream + HS_DESC_CLIENT_ID_LEN;
@@ -2924,8 +2959,8 @@ hs_desc_build_authorized_client(const uint8_t *subcredential,
(const char *) descriptor_cookie,
HS_DESC_DESCRIPTOR_COOKIE_LEN);
- memwipe(secret_seed, 0, sizeof(secret_seed));
- memwipe(keystream, 0, sizeof(keystream));
+ memwipe(keystream, 0, keystream_length);
+ tor_free(keystream);
crypto_cipher_free(cipher);
}
diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c
index 7d56c9e2ad..ee0b64a969 100644
--- a/src/feature/hs/hs_service.c
+++ b/src/feature/hs/hs_service.c
@@ -2931,8 +2931,8 @@ set_descriptor_revision_counter(hs_service_descriptor_t *hs_desc, time_t now,
/* The OPE module returns CRYPTO_OPE_ERROR in case of errors. */
tor_assert_nonfatal(rev_counter < CRYPTO_OPE_ERROR);
- log_info(LD_REND, "Encrypted revision counter %d to %ld",
- (int) seconds_since_start_of_srv, (long int) rev_counter);
+ log_info(LD_REND, "Encrypted revision counter %d to %" PRIu64,
+ (int) seconds_since_start_of_srv, rev_counter);
hs_desc->desc->plaintext_data.revision_counter = rev_counter;
}
@@ -3667,8 +3667,8 @@ hs_service_lookup_current_desc(const ed25519_public_key_t *pk)
}
/* Return the number of service we have configured and usable. */
-unsigned int
-hs_service_get_num_services(void)
+MOCK_IMPL(unsigned int,
+hs_service_get_num_services,(void))
{
if (hs_service_map == NULL) {
return 0;
diff --git a/src/feature/hs/hs_service.h b/src/feature/hs/hs_service.h
index a8a9faaea9..be1155bcd1 100644
--- a/src/feature/hs/hs_service.h
+++ b/src/feature/hs/hs_service.h
@@ -310,7 +310,7 @@ hs_service_t *hs_service_new(const or_options_t *options);
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);
+MOCK_DECL(unsigned int, hs_service_get_num_services,(void));
void hs_service_stage_services(const smartlist_t *service_list);
int hs_service_load_all_keys(void);
int hs_service_get_version_from_key(const hs_service_t *service);
diff --git a/src/feature/nodelist/authcert.c b/src/feature/nodelist/authcert.c
index b111422d0d..2c4915e913 100644
--- a/src/feature/nodelist/authcert.c
+++ b/src/feature/nodelist/authcert.c
@@ -380,7 +380,8 @@ trusted_dirs_load_certs_from_string(const char *contents, int source,
int added_trusted_cert = 0;
for (s = contents; *s; s = eos) {
- authority_cert_t *cert = authority_cert_parse_from_string(s, &eos);
+ authority_cert_t *cert = authority_cert_parse_from_string(s, strlen(s),
+ &eos);
cert_list_t *cl;
if (!cert) {
failure_code = -1;
diff --git a/src/feature/nodelist/microdesc.c b/src/feature/nodelist/microdesc.c
index 3f5085412f..eadca69548 100644
--- a/src/feature/nodelist/microdesc.c
+++ b/src/feature/nodelist/microdesc.c
@@ -23,6 +23,7 @@
#include "feature/nodelist/dirlist.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/nodefamily.h"
#include "feature/nodelist/nodelist.h"
#include "feature/nodelist/routerlist.h"
#include "feature/relay/router.h"
@@ -884,10 +885,7 @@ microdesc_free_(microdesc_t *md, const char *fname, int lineno)
if (md->body && md->saved_location != SAVED_IN_CACHE)
tor_free(md->body);
- if (md->family) {
- SMARTLIST_FOREACH(md->family, char *, cp, tor_free(cp));
- smartlist_free(md->family);
- }
+ nodefamily_free(md->family);
short_policy_free(md->exit_policy);
short_policy_free(md->ipv6_exit_policy);
diff --git a/src/feature/nodelist/microdesc_st.h b/src/feature/nodelist/microdesc_st.h
index d23da13137..30c896181d 100644
--- a/src/feature/nodelist/microdesc_st.h
+++ b/src/feature/nodelist/microdesc_st.h
@@ -9,6 +9,7 @@
struct curve25519_public_key_t;
struct ed25519_public_key_t;
+struct nodefamily_t;
struct short_policy_t;
/** A microdescriptor is the smallest amount of information needed to build a
@@ -69,8 +70,8 @@ struct microdesc_t {
tor_addr_t ipv6_addr;
/** As routerinfo_t.ipv6_orport */
uint16_t ipv6_orport;
- /** As routerinfo_t.family */
- smartlist_t *family;
+ /** As routerinfo_t.family, with readable members parsed. */
+ struct nodefamily_t *family;
/** IPv4 exit policy summary */
struct short_policy_t *exit_policy;
/** IPv6 exit policy summary */
diff --git a/src/feature/nodelist/networkstatus.c b/src/feature/nodelist/networkstatus.c
index 51e720a984..5e62903d48 100644
--- a/src/feature/nodelist/networkstatus.c
+++ b/src/feature/nodelist/networkstatus.c
@@ -116,8 +116,6 @@ STATIC networkstatus_t *current_md_consensus = NULL;
typedef struct consensus_waiting_for_certs_t {
/** The consensus itself. */
networkstatus_t *consensus;
- /** The encoded version of the consensus, nul-terminated. */
- char *body;
/** When did we set the current value of consensus_waiting_for_certs? If
* this is too recent, we shouldn't try to fetch a new consensus for a
* little while, to give ourselves time to get certificates for this one. */
@@ -210,14 +208,11 @@ networkstatus_reset_download_failures(void)
download_status_reset(&consensus_bootstrap_dl_status[i]);
}
-/**
- * Read and and return the cached consensus of type <b>flavorname</b>. If
- * <b>unverified</b> is true, get the one we haven't verified. Return NULL if
- * the file isn't there. */
+/** Return the filename used to cache the consensus of a given flavor */
static char *
-networkstatus_read_cached_consensus_impl(int flav,
- const char *flavorname,
- int unverified_consensus)
+networkstatus_get_cache_fname(int flav,
+ const char *flavorname,
+ int unverified_consensus)
{
char buf[128];
const char *prefix;
@@ -232,21 +227,35 @@ networkstatus_read_cached_consensus_impl(int flav,
tor_snprintf(buf, sizeof(buf), "%s-%s-consensus", prefix, flavorname);
}
- char *filename = get_cachedir_fname(buf);
- char *result = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL);
+ return get_cachedir_fname(buf);
+}
+
+/**
+ * Read and and return the cached consensus of type <b>flavorname</b>. If
+ * <b>unverified</b> is false, get the one we haven't verified. Return NULL if
+ * the file isn't there. */
+static tor_mmap_t *
+networkstatus_map_cached_consensus_impl(int flav,
+ const char *flavorname,
+ int unverified_consensus)
+{
+ char *filename = networkstatus_get_cache_fname(flav,
+ flavorname,
+ unverified_consensus);
+ tor_mmap_t *result = tor_mmap_file(filename);
tor_free(filename);
return result;
}
-/** Return a new string containing the current cached consensus of flavor
- * <b>flavorname</b>. */
-char *
-networkstatus_read_cached_consensus(const char *flavorname)
- {
+/** Map the file containing the current cached consensus of flavor
+ * <b>flavorname</b> */
+tor_mmap_t *
+networkstatus_map_cached_consensus(const char *flavorname)
+{
int flav = networkstatus_parse_flavor_name(flavorname);
if (flav < 0)
return NULL;
- return networkstatus_read_cached_consensus_impl(flav, flavorname, 0);
+ return networkstatus_map_cached_consensus_impl(flav, flavorname, 0);
}
/** Read every cached v3 consensus networkstatus from the disk. */
@@ -259,24 +268,26 @@ router_reload_consensus_networkstatus(void)
/* FFFF Suppress warnings if cached consensus is bad? */
for (flav = 0; flav < N_CONSENSUS_FLAVORS; ++flav) {
const char *flavor = networkstatus_get_flavor_name(flav);
- char *s = networkstatus_read_cached_consensus_impl(flav, flavor, 0);
- if (s) {
- if (networkstatus_set_current_consensus(s, flavor, flags, NULL) < -1) {
+ tor_mmap_t *m = networkstatus_map_cached_consensus_impl(flav, flavor, 0);
+ if (m) {
+ if (networkstatus_set_current_consensus(m->data, m->size,
+ flavor, flags, NULL) < -1) {
log_warn(LD_FS, "Couldn't load consensus %s networkstatus from cache",
flavor);
}
- tor_free(s);
+ tor_munmap_file(m);
}
- s = networkstatus_read_cached_consensus_impl(flav, flavor, 1);
- if (s) {
- if (networkstatus_set_current_consensus(s, flavor,
+ m = networkstatus_map_cached_consensus_impl(flav, flavor, 1);
+ if (m) {
+ if (networkstatus_set_current_consensus(m->data, m->size,
+ flavor,
flags | NSSET_WAS_WAITING_FOR_CERTS,
NULL)) {
log_info(LD_FS, "Couldn't load unverified consensus %s networkstatus "
"from cache", flavor);
}
- tor_free(s);
+ tor_munmap_file(m);
}
}
@@ -1841,6 +1852,7 @@ warn_early_consensus(const networkstatus_t *c, const char *flavor,
*/
int
networkstatus_set_current_consensus(const char *consensus,
+ size_t consensus_len,
const char *flavor,
unsigned flags,
const char *source_dir)
@@ -1869,7 +1881,9 @@ networkstatus_set_current_consensus(const char *consensus,
}
/* Make sure it's parseable. */
- c = networkstatus_parse_vote_from_string(consensus, NULL, NS_TYPE_CONSENSUS);
+ c = networkstatus_parse_vote_from_string(consensus,
+ consensus_len,
+ NULL, NS_TYPE_CONSENSUS);
if (!c) {
log_warn(LD_DIR, "Unable to parse networkstatus consensus");
result = -2;
@@ -1957,14 +1971,12 @@ networkstatus_set_current_consensus(const char *consensus,
c->valid_after > current_valid_after) {
waiting = &consensus_waiting_for_certs[flav];
networkstatus_vote_free(waiting->consensus);
- tor_free(waiting->body);
waiting->consensus = c;
free_consensus = 0;
- waiting->body = tor_strdup(consensus);
waiting->set_at = now;
waiting->dl_failed = 0;
if (!from_cache) {
- write_str_to_file(unverified_fname, consensus, 0);
+ write_bytes_to_file(unverified_fname, consensus, consensus_len, 0);
}
if (dl_certs)
authority_certs_fetch_missing(c, now, source_dir);
@@ -2055,10 +2067,6 @@ networkstatus_set_current_consensus(const char *consensus,
waiting->consensus->valid_after <= c->valid_after) {
networkstatus_vote_free(waiting->consensus);
waiting->consensus = NULL;
- if (consensus != waiting->body)
- tor_free(waiting->body);
- else
- waiting->body = NULL;
waiting->set_at = 0;
waiting->dl_failed = 0;
if (unlink(unverified_fname) != 0) {
@@ -2108,17 +2116,18 @@ networkstatus_set_current_consensus(const char *consensus,
if (we_want_to_fetch_flavor(options, flav)) {
if (dir_server_mode(get_options())) {
dirserv_set_cached_consensus_networkstatus(consensus,
+ consensus_len,
flavor,
&c->digests,
c->digest_sha3_as_signed,
c->valid_after);
- consdiffmgr_add_consensus(consensus, c);
+ consdiffmgr_add_consensus(consensus, consensus_len, c);
}
}
if (!from_cache) {
- write_str_to_file(consensus_fname, consensus, 0);
+ write_bytes_to_file(consensus_fname, consensus, consensus_len, 0);
}
warn_early_consensus(c, flavor, now);
@@ -2154,14 +2163,16 @@ networkstatus_note_certs_arrived(const char *source_dir)
if (!waiting->consensus)
continue;
if (networkstatus_check_consensus_signature(waiting->consensus, 0)>=0) {
- char *waiting_body = waiting->body;
- if (!networkstatus_set_current_consensus(
- waiting_body,
- flavor_name,
- NSSET_WAS_WAITING_FOR_CERTS,
- source_dir)) {
- tor_free(waiting_body);
+ tor_mmap_t *mapping = networkstatus_map_cached_consensus_impl(
+ i, flavor_name, 1);
+ if (mapping) {
+ networkstatus_set_current_consensus(mapping->data,
+ mapping->size,
+ flavor_name,
+ NSSET_WAS_WAITING_FOR_CERTS,
+ source_dir);
}
+ tor_munmap_file(mapping);
}
}
}
@@ -2389,7 +2400,9 @@ networkstatus_dump_bridge_status_to_file(time_t now)
published, thresholds, fingerprint_line ? fingerprint_line : "",
status);
fname = get_datadir_fname("networkstatus-bridges");
- write_str_to_file(fname,published_thresholds_and_status,0);
+ if (write_str_to_file(fname,published_thresholds_and_status,0)<0) {
+ log_warn(LD_DIRSERV, "Unable to write networkstatus-bridges file.");
+ }
tor_free(thresholds);
tor_free(published_thresholds_and_status);
tor_free(fname);
@@ -2718,6 +2731,5 @@ networkstatus_free_all(void)
networkstatus_vote_free(waiting->consensus);
waiting->consensus = NULL;
}
- tor_free(waiting->body);
}
}
diff --git a/src/feature/nodelist/networkstatus.h b/src/feature/nodelist/networkstatus.h
index 7b1a0ff72f..147104fecf 100644
--- a/src/feature/nodelist/networkstatus.h
+++ b/src/feature/nodelist/networkstatus.h
@@ -16,7 +16,7 @@
void networkstatus_reset_warnings(void);
void networkstatus_reset_download_failures(void);
-char *networkstatus_read_cached_consensus(const char *flavorname);
+tor_mmap_t *networkstatus_map_cached_consensus(const char *flavorname);
int router_reload_consensus_networkstatus(void);
void routerstatus_free_(routerstatus_t *rs);
#define routerstatus_free(rs) \
@@ -106,6 +106,7 @@ int networkstatus_consensus_has_ipv6(const or_options_t* options);
#define NSSET_ACCEPT_OBSOLETE 8
#define NSSET_REQUIRE_FLAVOR 16
int networkstatus_set_current_consensus(const char *consensus,
+ size_t consensus_len,
const char *flavor,
unsigned flags,
const char *source_dir);
@@ -157,4 +158,3 @@ extern networkstatus_t *current_md_consensus;
#endif /* defined(NETWORKSTATUS_PRIVATE) */
#endif /* !defined(TOR_NETWORKSTATUS_H) */
-
diff --git a/src/feature/nodelist/nodefamily.c b/src/feature/nodelist/nodefamily.c
new file mode 100644
index 0000000000..6b504c0ac4
--- /dev/null
+++ b/src/feature/nodelist/nodefamily.c
@@ -0,0 +1,373 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file nodefamily.c
+ * \brief Code to manipulate encoded, reference-counted node families. We
+ * use these tricks to save space, since these families would otherwise
+ * require a large number of tiny allocations.
+ **/
+
+#include "core/or/or.h"
+#include "feature/nodelist/nickname.h"
+#include "feature/nodelist/nodefamily.h"
+#include "feature/nodelist/nodefamily_st.h"
+#include "feature/nodelist/nodelist.h"
+#include "feature/relay/router.h"
+#include "feature/nodelist/routerlist.h"
+
+#include "ht.h"
+#include "siphash.h"
+
+#include "lib/container/smartlist.h"
+#include "lib/ctime/di_ops.h"
+#include "lib/defs/digest_sizes.h"
+#include "lib/log/util_bug.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * Allocate and return a blank node family with space to hold <b>n_members</b>
+ * members.
+ */
+static nodefamily_t *
+nodefamily_alloc(int n_members)
+{
+ size_t alloc_len = offsetof(nodefamily_t, family_members) +
+ NODEFAMILY_ARRAY_SIZE(n_members);
+ nodefamily_t *nf = tor_malloc_zero(alloc_len);
+ nf->n_members = n_members;
+ return nf;
+}
+
+/**
+ * Hashtable hash implementation.
+ */
+static inline unsigned int
+nodefamily_hash(const nodefamily_t *nf)
+{
+ return (unsigned) siphash24g(nf->family_members,
+ NODEFAMILY_ARRAY_SIZE(nf->n_members));
+}
+
+/**
+ * Hashtable equality implementation.
+ */
+static inline unsigned int
+nodefamily_eq(const nodefamily_t *a, const nodefamily_t *b)
+{
+ return (a->n_members == b->n_members) &&
+ fast_memeq(a->family_members, b->family_members,
+ NODEFAMILY_ARRAY_SIZE(a->n_members));
+}
+
+static HT_HEAD(nodefamily_map, nodefamily_t) the_node_families
+ = HT_INITIALIZER();
+
+HT_PROTOTYPE(nodefamily_map, nodefamily_t, ht_ent, nodefamily_hash,
+ nodefamily_eq)
+HT_GENERATE2(nodefamily_map, nodefamily_t, ht_ent, nodefamily_hash,
+ node_family_eq, 0.6, tor_reallocarray_, tor_free_)
+
+/**
+ * Parse the family declaration in <b>s</b>, returning the canonical
+ * <b>nodefamily_t</b> for its members. Return NULL on error.
+ *
+ * If <b>rsa_id_self</b> is provided, it is a DIGEST_LEN-byte digest
+ * for the router that declared this family: insert it into the
+ * family declaration if it is not there already.
+ *
+ * If NF_WARN_MALFORMED is set in <b>flags</b>, warn about any
+ * elements that we can't parse. (By default, we log at info.)
+ *
+ * If NF_REJECT_MALFORMED is set in <b>flags</b>, treat any unparseable
+ * elements as an error. (By default, we simply omit them.)
+ **/
+nodefamily_t *
+nodefamily_parse(const char *s, const uint8_t *rsa_id_self,
+ unsigned flags)
+{
+ smartlist_t *sl = smartlist_new();
+ smartlist_split_string(sl, s, NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ nodefamily_t *result = nodefamily_from_members(sl, rsa_id_self, flags);
+ SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ smartlist_free(sl);
+ return result;
+}
+
+/**
+ * qsort helper for encoded nodefamily elements.
+ **/
+static int
+compare_members(const void *a, const void *b)
+{
+ return fast_memcmp(a, b, NODEFAMILY_MEMBER_LEN);
+}
+
+/**
+ * Parse the member strings in <b>members</b>, returning their canonical
+ * <b>nodefamily_t</b>. Return NULL on error.
+ *
+ * If <b>rsa_id_self</b> is provided, it is a DIGEST_LEN-byte digest
+ * for the router that declared this family: insert it into the
+ * family declaration if it is not there already.
+ *
+ * The <b>flags</b> element is interpreted as in nodefamily_parse().
+ **/
+nodefamily_t *
+nodefamily_from_members(const smartlist_t *members,
+ const uint8_t *rsa_id_self,
+ unsigned flags)
+{
+ const int n_self = rsa_id_self ? 1 : 0;
+ int n_bad_elements = 0;
+ int n_members = smartlist_len(members) + n_self;
+ nodefamily_t *tmp = nodefamily_alloc(n_members);
+ uint8_t *ptr = NODEFAMILY_MEMBER_PTR(tmp, 0);
+
+ SMARTLIST_FOREACH_BEGIN(members, const char *, cp) {
+ bool bad_element = true;
+ if (is_legal_nickname(cp)) {
+ ptr[0] = NODEFAMILY_BY_NICKNAME;
+ tor_assert(strlen(cp) < DIGEST_LEN); // guaranteed by is_legal_nickname
+ memcpy(ptr+1, cp, strlen(cp));
+ bad_element = false;
+ } else if (is_legal_hexdigest(cp)) {
+ char digest_buf[DIGEST_LEN];
+ char nn_buf[MAX_NICKNAME_LEN+1];
+ char nn_char=0;
+ if (hex_digest_nickname_decode(cp, digest_buf, &nn_char, nn_buf)==0) {
+ bad_element = false;
+ ptr[0] = NODEFAMILY_BY_RSA_ID;
+ memcpy(ptr+1, digest_buf, DIGEST_LEN);
+ }
+ }
+
+ if (bad_element) {
+ const int severity = (flags & NF_WARN_MALFORMED) ? LOG_WARN : LOG_INFO;
+ log_fn(severity, LD_GENERAL,
+ "Bad element %s while parsing a node family.",
+ escaped(cp));
+ ++n_bad_elements;
+ } else {
+ ptr += NODEFAMILY_MEMBER_LEN;
+ }
+ } SMARTLIST_FOREACH_END(cp);
+
+ if (n_bad_elements && (flags & NF_REJECT_MALFORMED))
+ goto err;
+
+ if (rsa_id_self) {
+ /* Add self. */
+ ptr[0] = NODEFAMILY_BY_RSA_ID;
+ memcpy(ptr+1, rsa_id_self, DIGEST_LEN);
+ }
+
+ n_members -= n_bad_elements;
+
+ /* Sort tmp into canonical order. */
+ qsort(tmp->family_members, n_members, NODEFAMILY_MEMBER_LEN,
+ compare_members);
+
+ /* Remove duplicates. */
+ int i;
+ for (i = 0; i < n_members-1; ++i) {
+ uint8_t *thisptr = NODEFAMILY_MEMBER_PTR(tmp, i);
+ uint8_t *nextptr = NODEFAMILY_MEMBER_PTR(tmp, i+1);
+ if (fast_memeq(thisptr, nextptr, NODEFAMILY_MEMBER_LEN)) {
+ memmove(thisptr, nextptr, (n_members-i-1)*NODEFAMILY_MEMBER_LEN);
+ --n_members;
+ --i;
+ }
+ }
+ int n_members_alloc = tmp->n_members;
+ tmp->n_members = n_members;
+
+ /* See if we already allocated this family. */
+ nodefamily_t *found = HT_FIND(nodefamily_map, &the_node_families, tmp);
+ if (found) {
+ /* If we did, great: incref it and return it. */
+ ++found->refcnt;
+ tor_free(tmp);
+ return found;
+ } else {
+ /* If not, insert it into the hashtable. */
+ if (n_members_alloc != n_members) {
+ /* Compact the family if needed */
+ nodefamily_t *tmp2 = nodefamily_alloc(n_members);
+ memcpy(tmp2->family_members, tmp->family_members,
+ n_members * NODEFAMILY_MEMBER_LEN);
+ tor_free(tmp);
+ tmp = tmp2;
+ }
+
+ tmp->refcnt = 1;
+ HT_INSERT(nodefamily_map, &the_node_families, tmp);
+ return tmp;
+ }
+
+ err:
+ tor_free(tmp);
+ return NULL;
+}
+
+/**
+ * Drop our reference to <b>family</b>, freeing it if there are no more
+ * references.
+ */
+void
+nodefamily_free_(nodefamily_t *family)
+{
+ if (family == NULL)
+ return;
+
+ --family->refcnt;
+
+ if (family->refcnt == 0) {
+ HT_REMOVE(nodefamily_map, &the_node_families, family);
+ tor_free(family);
+ }
+}
+
+/**
+ * Return true iff <b>family</b> contains the SHA1 RSA1024 identity
+ * <b>rsa_id</b>.
+ */
+bool
+nodefamily_contains_rsa_id(const nodefamily_t *family,
+ const uint8_t *rsa_id)
+{
+ if (family == NULL)
+ return false;
+
+ unsigned i;
+ for (i = 0; i < family->n_members; ++i) {
+ const uint8_t *ptr = NODEFAMILY_MEMBER_PTR(family, i);
+ if (ptr[0] == NODEFAMILY_BY_RSA_ID &&
+ fast_memeq(ptr+1, rsa_id, DIGEST_LEN)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return true iff <b>family</b> contains the nickname <b>name</b>.
+ */
+bool
+nodefamily_contains_nickname(const nodefamily_t *family,
+ const char *name)
+{
+ if (family == NULL)
+ return false;
+
+ unsigned i;
+ for (i = 0; i < family->n_members; ++i) {
+ const uint8_t *ptr = NODEFAMILY_MEMBER_PTR(family, i);
+ // note that the strcasecmp() is safe because there is always at least one
+ // NUL in the encoded nickname, because all legal nicknames are less than
+ // DIGEST_LEN bytes long.
+ if (ptr[0] == NODEFAMILY_BY_NICKNAME && !strcasecmp((char*)ptr+1, name)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Return true if <b>family</b> contains the nickname or the RSA ID for
+ * <b>node</b>
+ **/
+bool
+nodefamily_contains_node(const nodefamily_t *family,
+ const node_t *node)
+{
+ return
+ nodefamily_contains_nickname(family, node_get_nickname(node))
+ ||
+ nodefamily_contains_rsa_id(family, node_get_rsa_id_digest(node));
+}
+
+/**
+ * Look up every entry in <b>family</b>, and add add the corresponding
+ * node_t to <b>out</b>.
+ **/
+void
+nodefamily_add_nodes_to_smartlist(const nodefamily_t *family,
+ smartlist_t *out)
+{
+ if (!family)
+ return;
+
+ unsigned i;
+ for (i = 0; i < family->n_members; ++i) {
+ const uint8_t *ptr = NODEFAMILY_MEMBER_PTR(family, i);
+ const node_t *node = NULL;
+ switch (ptr[0]) {
+ case NODEFAMILY_BY_NICKNAME:
+ node = node_get_by_nickname((char*)ptr+1, NNF_NO_WARN_UNNAMED);
+ break;
+ case NODEFAMILY_BY_RSA_ID:
+ node = node_get_by_id((char*)ptr+1);
+ break;
+ default:
+ /* LCOV_EXCL_START */
+ tor_assert_nonfatal_unreached();
+ break;
+ /* LCOV_EXCL_STOP */
+ }
+ if (node)
+ smartlist_add(out, (void *)node);
+ }
+}
+
+/**
+ * Encode <b>family</b> as a space-separated string.
+ */
+char *
+nodefamily_format(const nodefamily_t *family)
+{
+ if (!family)
+ return tor_strdup("");
+
+ unsigned i;
+ smartlist_t *sl = smartlist_new();
+ for (i = 0; i < family->n_members; ++i) {
+ const uint8_t *ptr = NODEFAMILY_MEMBER_PTR(family, i);
+ switch (ptr[0]) {
+ case NODEFAMILY_BY_NICKNAME:
+ smartlist_add_strdup(sl, (char*)ptr+1);
+ break;
+ case NODEFAMILY_BY_RSA_ID: {
+ char buf[HEX_DIGEST_LEN+2];
+ buf[0]='$';
+ base16_encode(buf+1, sizeof(buf)-1, (char*)ptr+1, DIGEST_LEN);
+ smartlist_add_strdup(sl, buf);
+ break;
+ }
+ default:
+ /* LCOV_EXCL_START */
+ tor_assert_nonfatal_unreached();
+ break;
+ /* LCOV_EXCL_STOP */
+ }
+ }
+
+ char *result = smartlist_join_strings(sl, " ", 0, NULL);
+ SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ smartlist_free(sl);
+ return result;
+}
+
+/**
+ * Free all storage held in the nodefamily map.
+ **/
+void
+nodefamily_free_all(void)
+{
+ HT_CLEAR(nodefamily_map, &the_node_families);
+}
diff --git a/src/feature/nodelist/nodefamily.h b/src/feature/nodelist/nodefamily.h
new file mode 100644
index 0000000000..342f161a07
--- /dev/null
+++ b/src/feature/nodelist/nodefamily.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file nodefamily.h
+ * \brief Header file for nodefamily.c.
+ **/
+
+#ifndef TOR_NODEFAMILY_H
+#define TOR_NODEFAMILY_H
+
+#include "lib/malloc/malloc.h"
+#include <stdbool.h>
+
+typedef struct nodefamily_t nodefamily_t;
+struct node_t;
+struct smartlist_t;
+
+#define NF_WARN_MALFORMED (1u<<0)
+#define NF_REJECT_MALFORMED (1u<<1)
+
+nodefamily_t *nodefamily_parse(const char *s,
+ const uint8_t *rsa_id_self,
+ unsigned flags);
+nodefamily_t *nodefamily_from_members(const struct smartlist_t *members,
+ const uint8_t *rsa_id_self,
+ unsigned flags);
+void nodefamily_free_(nodefamily_t *family);
+#define nodefamily_free(family) \
+ FREE_AND_NULL(nodefamily_t, nodefamily_free_, (family))
+
+bool nodefamily_contains_rsa_id(const nodefamily_t *family,
+ const uint8_t *rsa_id);
+bool nodefamily_contains_nickname(const nodefamily_t *family,
+ const char *name);
+bool nodefamily_contains_node(const nodefamily_t *family,
+ const struct node_t *node);
+void nodefamily_add_nodes_to_smartlist(const nodefamily_t *family,
+ struct smartlist_t *out);
+char *nodefamily_format(const nodefamily_t *family);
+
+void nodefamily_free_all(void);
+
+#endif
diff --git a/src/feature/nodelist/nodefamily_st.h b/src/feature/nodelist/nodefamily_st.h
new file mode 100644
index 0000000000..f88ada494a
--- /dev/null
+++ b/src/feature/nodelist/nodefamily_st.h
@@ -0,0 +1,48 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_NODEFAMILY_ST_H
+#define TOR_NODEFAMILY_ST_H
+
+#include "orconfig.h"
+#include "ht.h"
+
+struct nodefamily_t {
+ /** Entry for this nodefamily_t within the hashtable. */
+ HT_ENTRY(nodefamily_t) ht_ent;
+ /** Reference count. (The hashtable is not treated as a reference */
+ uint32_t refcnt;
+ /** Number of items encoded in <b>family_members</b>. */
+ uint32_t n_members;
+ /* A byte-array encoding the members of this family. We encode each member
+ * as one byte to indicate whether it's a nickname or a fingerprint, plus
+ * DIGEST_LEN bytes of data. The entries are lexically sorted.
+ */
+ uint8_t family_members[FLEXIBLE_ARRAY_MEMBER];
+};
+
+#define NODEFAMILY_MEMBER_LEN (1+DIGEST_LEN)
+
+/** Tag byte, indicates that the following bytes are a NUL-padded nickname.
+ */
+#define NODEFAMILY_BY_NICKNAME 0
+/** Tag byte, indicates that the following bytes are a RSA1024 SHA1 ID.
+ */
+#define NODEFAMILY_BY_RSA_ID 1
+
+/**
+ * Number of bytes to allocate in the array for a nodefamily_t with N members.
+ **/
+#define NODEFAMILY_ARRAY_SIZE(n) \
+ ((n) * NODEFAMILY_MEMBER_LEN)
+
+/**
+ * Pointer to the i'th member of <b>nf</b>, as encoded.
+ */
+#define NODEFAMILY_MEMBER_PTR(nf, i) \
+ (&((nf)->family_members[(i) * NODEFAMILY_MEMBER_LEN]))
+
+#endif
diff --git a/src/feature/nodelist/nodelist.c b/src/feature/nodelist/nodelist.c
index a39eb1d5b8..f93ecd5bfe 100644
--- a/src/feature/nodelist/nodelist.c
+++ b/src/feature/nodelist/nodelist.c
@@ -59,6 +59,7 @@
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/node_select.h"
+#include "feature/nodelist/nodefamily.h"
#include "feature/nodelist/nodelist.h"
#include "feature/nodelist/routerlist.h"
#include "feature/nodelist/routerset.h"
@@ -1503,19 +1504,6 @@ node_is_me(const node_t *node)
return router_digest_is_me(node->identity);
}
-/** Return <b>node</b> declared family (as a list of names), or NULL if
- * the node didn't declare a family. */
-const smartlist_t *
-node_get_declared_family(const node_t *node)
-{
- if (node->ri && node->ri->declared_family)
- return node->ri->declared_family;
- else if (node->md && node->md->family)
- return node->md->family;
- else
- return NULL;
-}
-
/* Does this node have a valid IPv6 address?
* Prefer node_has_ipv6_orport() or node_has_ipv6_dirport() for
* checking specific ports. */
@@ -1866,6 +1854,9 @@ int
addrs_in_same_network_family(const tor_addr_t *a1,
const tor_addr_t *a2)
{
+ if (tor_addr_is_null(a1) || tor_addr_is_null(a2))
+ return 0;
+
switch (tor_addr_family(a1)) {
case AF_INET:
return 0 == tor_addr_compare_masked(a1, a2, 16, CMP_SEMANTIC);
@@ -1881,7 +1872,7 @@ addrs_in_same_network_family(const tor_addr_t *a1,
* (case-insensitive), or if <b>node's</b> identity key digest
* matches a hexadecimal value stored in <b>nickname</b>. Return
* false otherwise. */
-static int
+STATIC int
node_nickname_matches(const node_t *node, const char *nickname)
{
const char *n = node_get_nickname(node);
@@ -1893,7 +1884,7 @@ node_nickname_matches(const node_t *node, const char *nickname)
}
/** Return true iff <b>node</b> is named by some nickname in <b>lst</b>. */
-static inline int
+STATIC int
node_in_nickname_smartlist(const smartlist_t *lst, const node_t *node)
{
if (!lst) return 0;
@@ -1904,6 +1895,61 @@ node_in_nickname_smartlist(const smartlist_t *lst, const node_t *node)
return 0;
}
+/** Return true iff n1's declared family contains n2. */
+STATIC int
+node_family_contains(const node_t *n1, const node_t *n2)
+{
+ if (n1->ri && n1->ri->declared_family) {
+ return node_in_nickname_smartlist(n1->ri->declared_family, n2);
+ } else if (n1->md) {
+ return nodefamily_contains_node(n1->md->family, n2);
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * Return true iff <b>node</b> has declared a nonempty family.
+ **/
+STATIC bool
+node_has_declared_family(const node_t *node)
+{
+ if (node->ri && node->ri->declared_family &&
+ smartlist_len(node->ri->declared_family)) {
+ return true;
+ }
+
+ if (node->md && node->md->family) {
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Add to <b>out</b> every node_t that is listed by <b>node</b> as being in
+ * its family. (Note that these nodes are not in node's family unless they
+ * also agree that node is in their family.)
+ **/
+STATIC void
+node_lookup_declared_family(smartlist_t *out, const node_t *node)
+{
+ if (node->ri && node->ri->declared_family &&
+ smartlist_len(node->ri->declared_family)) {
+ SMARTLIST_FOREACH_BEGIN(node->ri->declared_family, const char *, name) {
+ const node_t *n2 = node_get_by_nickname(name, NNF_NO_WARN_UNNAMED);
+ if (n2) {
+ smartlist_add(out, (node_t *)n2);
+ }
+ } SMARTLIST_FOREACH_END(name);
+ return;
+ }
+
+ if (node->md && node->md->family) {
+ nodefamily_add_nodes_to_smartlist(node->md->family, out);
+ }
+}
+
/** Return true iff r1 and r2 are in the same family, but not the same
* router. */
int
@@ -1916,19 +1962,20 @@ nodes_in_same_family(const node_t *node1, const node_t *node2)
tor_addr_t a1, a2;
node_get_addr(node1, &a1);
node_get_addr(node2, &a2);
- if (addrs_in_same_network_family(&a1, &a2))
+
+ tor_addr_port_t ap6_1, ap6_2;
+ node_get_pref_ipv6_orport(node1, &ap6_1);
+ node_get_pref_ipv6_orport(node2, &ap6_2);
+
+ if (addrs_in_same_network_family(&a1, &a2) ||
+ addrs_in_same_network_family(&ap6_1.addr, &ap6_2.addr))
return 1;
}
/* Are they in the same family because the agree they are? */
- {
- const smartlist_t *f1, *f2;
- f1 = node_get_declared_family(node1);
- f2 = node_get_declared_family(node2);
- if (f1 && f2 &&
- node_in_nickname_smartlist(f1, node2) &&
- node_in_nickname_smartlist(f2, node1))
- return 1;
+ if (node_family_contains(node1, node2) &&
+ node_family_contains(node2, node1)) {
+ return 1;
}
/* Are they in the same family because the user says they are? */
@@ -1956,13 +2003,10 @@ void
nodelist_add_node_and_family(smartlist_t *sl, const node_t *node)
{
const smartlist_t *all_nodes = nodelist_get_list();
- const smartlist_t *declared_family;
const or_options_t *options = get_options();
tor_assert(node);
- declared_family = node_get_declared_family(node);
-
/* Let's make sure that we have the node itself, if it's a real node. */
{
const node_t *real_node = node_get_by_id(node->identity);
@@ -1973,35 +2017,35 @@ nodelist_add_node_and_family(smartlist_t *sl, const node_t *node)
/* First, add any nodes with similar network addresses. */
if (options->EnforceDistinctSubnets) {
tor_addr_t node_addr;
+ tor_addr_port_t node_ap6;
node_get_addr(node, &node_addr);
+ node_get_pref_ipv6_orport(node, &node_ap6);
SMARTLIST_FOREACH_BEGIN(all_nodes, const node_t *, node2) {
tor_addr_t a;
+ tor_addr_port_t ap6;
node_get_addr(node2, &a);
- if (addrs_in_same_network_family(&a, &node_addr))
+ node_get_pref_ipv6_orport(node2, &ap6);
+ if (addrs_in_same_network_family(&a, &node_addr) ||
+ addrs_in_same_network_family(&ap6.addr, &node_ap6.addr))
smartlist_add(sl, (void*)node2);
} SMARTLIST_FOREACH_END(node2);
}
- /* Now, add all nodes in the declared_family of this node, if they
+ /* Now, add all nodes in the declared family of this node, if they
* also declare this node to be in their family. */
- if (declared_family) {
+ if (node_has_declared_family(node)) {
+ smartlist_t *declared_family = smartlist_new();
+ node_lookup_declared_family(declared_family, node);
+
/* Add every r such that router declares familyness with node, and node
* declares familyhood with router. */
- SMARTLIST_FOREACH_BEGIN(declared_family, const char *, name) {
- const node_t *node2;
- const smartlist_t *family2;
- if (!(node2 = node_get_by_nickname(name, NNF_NO_WARN_UNNAMED)))
- continue;
- if (!(family2 = node_get_declared_family(node2)))
- continue;
- SMARTLIST_FOREACH_BEGIN(family2, const char *, name2) {
- if (node_nickname_matches(node, name2)) {
- smartlist_add(sl, (void*)node2);
- break;
- }
- } SMARTLIST_FOREACH_END(name2);
- } SMARTLIST_FOREACH_END(name);
+ SMARTLIST_FOREACH_BEGIN(declared_family, const node_t *, node2) {
+ if (node_family_contains(node2, node)) {
+ smartlist_add(sl, (void*)node2);
+ }
+ } SMARTLIST_FOREACH_END(node2);
+ smartlist_free(declared_family);
}
/* If the user declared any families locally, honor those too. */
diff --git a/src/feature/nodelist/nodelist.h b/src/feature/nodelist/nodelist.h
index cf7658cf9d..32300eb00c 100644
--- a/src/feature/nodelist/nodelist.h
+++ b/src/feature/nodelist/nodelist.h
@@ -68,7 +68,6 @@ const char *node_get_platform(const node_t *node);
uint32_t node_get_prim_addr_ipv4h(const node_t *node);
void node_get_address_string(const node_t *node, char *cp, size_t len);
long node_get_declared_uptime(const node_t *node);
-const smartlist_t *node_get_declared_family(const node_t *node);
const struct ed25519_public_key_t *node_get_ed25519_id(const node_t *node);
int node_ed25519_id_matches(const node_t *node,
const struct ed25519_public_key_t *id);
@@ -155,10 +154,16 @@ int count_loading_descriptors_progress(void);
#ifdef NODELIST_PRIVATE
+STATIC int node_nickname_matches(const node_t *node, const char *nickname);
+STATIC int node_in_nickname_smartlist(const smartlist_t *lst,
+ const node_t *node);
+STATIC int node_family_contains(const node_t *n1, const node_t *n2);
+STATIC bool node_has_declared_family(const node_t *node);
+STATIC void node_lookup_declared_family(smartlist_t *out, const node_t *node);
+
#ifdef TOR_UNIT_TESTS
-STATIC void
-node_set_hsdir_index(node_t *node, const networkstatus_t *ns);
+STATIC void node_set_hsdir_index(node_t *node, const networkstatus_t *ns);
#endif /* defined(TOR_UNIT_TESTS) */
diff --git a/src/feature/relay/dns.c b/src/feature/relay/dns.c
index bc507d47f6..701719af95 100644
--- a/src/feature/relay/dns.c
+++ b/src/feature/relay/dns.c
@@ -1357,6 +1357,42 @@ evdns_err_is_transient(int err)
}
}
+/**
+ * Return number of configured nameservers in <b>the_evdns_base</b>.
+ */
+size_t
+number_of_configured_nameservers(void)
+{
+ return evdns_base_count_nameservers(the_evdns_base);
+}
+
+#ifdef HAVE_EVDNS_BASE_GET_NAMESERVER_ADDR
+/**
+ * Return address of configured nameserver in <b>the_evdns_base</b>
+ * at index <b>idx</b>.
+ */
+tor_addr_t *
+configured_nameserver_address(const size_t idx)
+{
+ struct sockaddr_storage sa;
+ ev_socklen_t sa_len = sizeof(sa);
+
+ if (evdns_base_get_nameserver_addr(the_evdns_base, (int)idx,
+ (struct sockaddr *)&sa,
+ sa_len) > 0) {
+ tor_addr_t *tor_addr = tor_malloc(sizeof(tor_addr_t));
+ if (tor_addr_from_sockaddr(tor_addr,
+ (const struct sockaddr *)&sa,
+ NULL) == 0) {
+ return tor_addr;
+ }
+ tor_free(tor_addr);
+ }
+
+ return NULL;
+}
+#endif
+
/** Configure eventdns nameservers if force is true, or if the configuration
* has changed since the last time we called this function, or if we failed on
* our last attempt. On Unix, this reads from /etc/resolv.conf or
@@ -1388,16 +1424,23 @@ configure_nameservers(int force)
evdns_set_log_fn(evdns_log_cb);
if (conf_fname) {
log_debug(LD_FS, "stat()ing %s", conf_fname);
- if (stat(sandbox_intern_string(conf_fname), &st)) {
+ int missing_resolv_conf = 0;
+ int stat_res = stat(sandbox_intern_string(conf_fname), &st);
+
+ if (stat_res) {
log_warn(LD_EXIT, "Unable to stat resolver configuration in '%s': %s",
conf_fname, strerror(errno));
- goto err;
- }
- if (!force && resolv_conf_fname && !strcmp(conf_fname,resolv_conf_fname)
+ missing_resolv_conf = 1;
+ } else if (!force && resolv_conf_fname &&
+ !strcmp(conf_fname,resolv_conf_fname)
&& st.st_mtime == resolv_conf_mtime) {
log_info(LD_EXIT, "No change to '%s'", conf_fname);
return 0;
}
+
+ if (stat_res == 0 && st.st_size == 0)
+ missing_resolv_conf = 1;
+
if (nameservers_configured) {
evdns_base_search_clear(the_evdns_base);
evdns_base_clear_nameservers_and_suspend(the_evdns_base);
@@ -1410,20 +1453,34 @@ configure_nameservers(int force)
sandbox_intern_string("/etc/hosts"));
}
#endif /* defined(DNS_OPTION_HOSTSFILE) && defined(USE_LIBSECCOMP) */
- log_info(LD_EXIT, "Parsing resolver configuration in '%s'", conf_fname);
- if ((r = evdns_base_resolv_conf_parse(the_evdns_base, flags,
- sandbox_intern_string(conf_fname)))) {
- log_warn(LD_EXIT, "Unable to parse '%s', or no nameservers in '%s' (%d)",
- conf_fname, conf_fname, r);
- goto err;
- }
- if (evdns_base_count_nameservers(the_evdns_base) == 0) {
- log_warn(LD_EXIT, "Unable to find any nameservers in '%s'.", conf_fname);
- goto err;
+
+ if (!missing_resolv_conf) {
+ log_info(LD_EXIT, "Parsing resolver configuration in '%s'", conf_fname);
+ if ((r = evdns_base_resolv_conf_parse(the_evdns_base, flags,
+ sandbox_intern_string(conf_fname)))) {
+ log_warn(LD_EXIT, "Unable to parse '%s', or no nameservers "
+ "in '%s' (%d)", conf_fname, conf_fname, r);
+
+ if (r != 6) // "r = 6" means "no DNS servers were in resolv.conf" -
+ goto err; // in which case we expect libevent to add 127.0.0.1 as
+ // fallback.
+ }
+ if (evdns_base_count_nameservers(the_evdns_base) == 0) {
+ log_warn(LD_EXIT, "Unable to find any nameservers in '%s'.",
+ conf_fname);
+ }
+
+ tor_free(resolv_conf_fname);
+ resolv_conf_fname = tor_strdup(conf_fname);
+ resolv_conf_mtime = st.st_mtime;
+ } else {
+ log_warn(LD_EXIT, "Could not read your DNS config from '%s' - "
+ "please investigate your DNS configuration. "
+ "This is possibly a problem. Meanwhile, falling"
+ " back to local DNS at 127.0.0.1.", conf_fname);
+ evdns_base_nameserver_ip_add(the_evdns_base, "127.0.0.1");
}
- tor_free(resolv_conf_fname);
- resolv_conf_fname = tor_strdup(conf_fname);
- resolv_conf_mtime = st.st_mtime;
+
if (nameservers_configured)
evdns_base_resume(the_evdns_base);
}
diff --git a/src/feature/relay/dns.h b/src/feature/relay/dns.h
index 1dd6f903d1..5758ea4363 100644
--- a/src/feature/relay/dns.h
+++ b/src/feature/relay/dns.h
@@ -45,6 +45,11 @@ size_t dns_cache_handle_oom(time_t now, size_t min_remove_bytes);
#ifdef DNS_PRIVATE
#include "feature/relay/dns_structs.h"
+size_t number_of_configured_nameservers(void);
+#ifdef HAVE_EVDNS_BASE_GET_NAMESERVER_ADDR
+tor_addr_t *configured_nameserver_address(const size_t idx);
+#endif
+
MOCK_DECL(STATIC int,dns_resolve_impl,(edge_connection_t *exitconn,
int is_resolve,or_circuit_t *oncirc, char **hostname_out,
int *made_connection_pending_out, cached_resolve_t **resolve_out));
diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c
index 4afcddc675..9d61ced11c 100644
--- a/src/feature/relay/router.c
+++ b/src/feature/relay/router.c
@@ -49,6 +49,7 @@
#include "lib/encoding/confline.h"
#include "lib/osinfo/uname.h"
#include "lib/tls/tortls.h"
+#include "lib/version/torversion.h"
#include "feature/dirauth/authmode.h"
@@ -636,7 +637,7 @@ load_authority_keyset(int legacy, crypto_pk_t **key_out,
fname);
goto done;
}
- parsed = authority_cert_parse_from_string(cert, &eos);
+ parsed = authority_cert_parse_from_string(cert, strlen(cert), &eos);
if (!parsed) {
log_warn(LD_DIR, "Unable to parse certificate in %s", fname);
goto done;
diff --git a/src/feature/rend/rendcache.c b/src/feature/rend/rendcache.c
index 848386b97d..b851e71959 100644
--- a/src/feature/rend/rendcache.c
+++ b/src/feature/rend/rendcache.c
@@ -45,7 +45,7 @@ STATIC digestmap_t *rend_cache_v2_dir = NULL;
* looked up in this cache and if present, it is discarded from the fetched
* descriptor. At the end, all IP(s) in the cache, for a specific service
* 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,
+ * Which means that if at least one IP was not in this cache, thus usable,
* 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 unusable.
*
diff --git a/src/feature/rend/rendmid.c b/src/feature/rend/rendmid.c
index 8ca19a2522..67a8b5f8f4 100644
--- a/src/feature/rend/rendmid.c
+++ b/src/feature/rend/rendmid.c
@@ -236,8 +236,8 @@ rend_mid_establish_rendezvous(or_circuit_t *circ, const uint8_t *request,
goto err;
}
- /* Check if we are configured to accept established rendezvous cells from
- * client or in other words Tor2Web clients. */
+ /* Check if we are configured to defend ourselves from clients that
+ * attempt to establish rendezvous points directly to us. */
if (channel_is_client(circ->p_chan) &&
dos_should_refuse_single_hop_client()) {
/* Note it down for the heartbeat log purposes. */
diff --git a/src/include.am b/src/include.am
index d2f83da814..8279499936 100644
--- a/src/include.am
+++ b/src/include.am
@@ -25,6 +25,7 @@ include src/lib/osinfo/include.am
include src/lib/process/include.am
include src/lib/sandbox/include.am
include src/lib/string/include.am
+include src/lib/subsys/include.am
include src/lib/smartlist_core/include.am
include src/lib/term/include.am
include src/lib/testsupport/include.am
@@ -32,6 +33,7 @@ include src/lib/thread/include.am
include src/lib/time/include.am
include src/lib/tls/include.am
include src/lib/trace/include.am
+include src/lib/version/include.am
include src/lib/wallclock/include.am
include src/trunnel/include.am
diff --git a/src/lib/cc/.may_include b/src/lib/cc/.may_include
index 2b06e8519c..fa1478ce46 100644
--- a/src/lib/cc/.may_include
+++ b/src/lib/cc/.may_include
@@ -1 +1,2 @@
orconfig.h
+lib/cc/*.h \ No newline at end of file
diff --git a/src/lib/cc/ctassert.h b/src/lib/cc/ctassert.h
new file mode 100644
index 0000000000..e42976360f
--- /dev/null
+++ b/src/lib/cc/ctassert.h
@@ -0,0 +1,53 @@
+/* Copyright (c) 2018 The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file ctassert.h
+ *
+ * \brief Compile-time assertions: CTASSERT(expression).
+ */
+
+#ifndef TOR_CTASSERT_H
+#define TOR_CTASSERT_H
+
+#include "lib/cc/compat_compiler.h"
+
+/**
+ * CTASSERT(expression)
+ *
+ * Trigger a compiler error if expression is false.
+ */
+#if __STDC_VERSION__ >= 201112L
+
+/* If C11 is available, just use _Static_assert. */
+#define CTASSERT(x) _Static_assert((x), #x)
+
+#else
+
+/*
+ * If C11 is not available, expand __COUNTER__, or __INCLUDE_LEVEL__
+ * and __LINE__, or just __LINE__, with an intermediate preprocessor
+ * macro CTASSERT_EXPN, and then use CTASSERT_DECL to paste the
+ * expansions together into a unique name.
+ *
+ * We use this name as a typedef of an array type with a positive
+ * length if the assertion is true, and a negative length of the
+ * assertion is false, which is invalid and hence triggers a compiler
+ * error.
+ */
+#if defined(__COUNTER__)
+#define CTASSERT(x) CTASSERT_EXPN((x), c, __COUNTER__)
+#elif defined(__INCLUDE_LEVEL__)
+#define CTASSERT(x) CTASSERT_EXPN((x), __INCLUDE_LEVEL__, __LINE__)
+#else
+/* hope it's unique enough */
+#define CTASSERT(x) CTASSERT_EXPN((x), l, __LINE__)
+#endif
+
+#define CTASSERT_EXPN(x, a, b) CTASSERT_DECL(x, a, b)
+#define CTASSERT_DECL(x, a, b) \
+ typedef char tor_ctassert_##a##_##b[(x) ? 1 : -1] ATTR_UNUSED
+
+#endif
+
+#endif /* !defined(TOR_CTASSERT_H) */
diff --git a/src/lib/cc/include.am b/src/lib/cc/include.am
index 2ae90f97dd..52cf8a9f72 100644
--- a/src/lib/cc/include.am
+++ b/src/lib/cc/include.am
@@ -1,4 +1,5 @@
noinst_HEADERS += \
src/lib/cc/compat_compiler.h \
+ src/lib/cc/ctassert.h \
src/lib/cc/torint.h
diff --git a/src/lib/compress/.may_include b/src/lib/compress/.may_include
index 68fe9f1c54..4870259ec9 100644
--- a/src/lib/compress/.may_include
+++ b/src/lib/compress/.may_include
@@ -8,5 +8,6 @@ lib/intmath/*.h
lib/log/*.h
lib/malloc/*.h
lib/string/*.h
+lib/subsys/*.h
lib/testsupport/*.h
lib/thread/*.h
diff --git a/src/lib/compress/compress.c b/src/lib/compress/compress.c
index 2ad9b15b2e..6cb9bd492b 100644
--- a/src/lib/compress/compress.c
+++ b/src/lib/compress/compress.c
@@ -29,10 +29,12 @@
#include "lib/compress/compress.h"
#include "lib/compress/compress_lzma.h"
#include "lib/compress/compress_none.h"
+#include "lib/compress/compress_sys.h"
#include "lib/compress/compress_zlib.h"
#include "lib/compress/compress_zstd.h"
#include "lib/intmath/cmp.h"
#include "lib/malloc/malloc.h"
+#include "lib/subsys/subsys.h"
#include "lib/thread/threads.h"
/** Total number of bytes allocated for compression state overhead. */
@@ -660,7 +662,7 @@ tor_compress_state_size(const tor_compress_state_t *state)
}
/** Initialize all compression modules. */
-void
+int
tor_compress_init(void)
{
atomic_counter_init(&total_compress_allocation);
@@ -668,6 +670,8 @@ tor_compress_init(void)
tor_zlib_init();
tor_lzma_init();
tor_zstd_init();
+
+ return 0;
}
/** Warn if we had any problems while setting up our compression libraries.
@@ -677,5 +681,20 @@ tor_compress_init(void)
void
tor_compress_log_init_warnings(void)
{
+ // XXXX can we move this into tor_compress_init() after all? log.c queues
+ // XXXX log messages at startup.
tor_zstd_warn_if_version_mismatched();
}
+
+static int
+subsys_compress_initialize(void)
+{
+ return tor_compress_init();
+}
+
+const subsys_fns_t sys_compress = {
+ .name = "compress",
+ .supported = true,
+ .level = -70,
+ .initialize = subsys_compress_initialize,
+};
diff --git a/src/lib/compress/compress.h b/src/lib/compress/compress.h
index 4466e27c4d..4dd6506238 100644
--- a/src/lib/compress/compress.h
+++ b/src/lib/compress/compress.h
@@ -89,7 +89,7 @@ void tor_compress_free_(tor_compress_state_t *state);
size_t tor_compress_state_size(const tor_compress_state_t *state);
-void tor_compress_init(void);
+int tor_compress_init(void);
void tor_compress_log_init_warnings(void);
struct buf_t;
diff --git a/src/lib/compress/compress_sys.h b/src/lib/compress/compress_sys.h
new file mode 100644
index 0000000000..a162140cfb
--- /dev/null
+++ b/src/lib/compress/compress_sys.h
@@ -0,0 +1,14 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file compress_sys.h
+ * \brief Declare subsystem object for the compress module
+ **/
+
+#ifndef TOR_COMPRESS_SYS_H
+#define TOR_COMPRESS_SYS_H
+
+extern const struct subsys_fns_t sys_compress;
+
+#endif /* !defined(TOR_COMPRESS_SYS_H) */
diff --git a/src/lib/compress/include.am b/src/lib/compress/include.am
index 75c9032bd2..b952779578 100644
--- a/src/lib/compress/include.am
+++ b/src/lib/compress/include.am
@@ -22,5 +22,6 @@ noinst_HEADERS += \
src/lib/compress/compress.h \
src/lib/compress/compress_lzma.h \
src/lib/compress/compress_none.h \
+ src/lib/compress/compress_sys.h \
src/lib/compress/compress_zlib.h \
src/lib/compress/compress_zstd.h
diff --git a/src/lib/crypt_ops/.may_include b/src/lib/crypt_ops/.may_include
index a0fa4ec05c..352fde858c 100644
--- a/src/lib/crypt_ops/.may_include
+++ b/src/lib/crypt_ops/.may_include
@@ -12,6 +12,7 @@ lib/malloc/*.h
lib/intmath/*.h
lib/sandbox/*.h
lib/string/*.h
+lib/subsys/*.h
lib/testsupport/testsupport.h
lib/thread/*.h
lib/log/*.h
diff --git a/src/lib/crypt_ops/crypto_init.c b/src/lib/crypt_ops/crypto_init.c
index 9d6e2da0d0..4c4cc3e43b 100644
--- a/src/lib/crypt_ops/crypto_init.c
+++ b/src/lib/crypt_ops/crypto_init.c
@@ -20,6 +20,9 @@
#include "lib/crypt_ops/crypto_openssl_mgt.h"
#include "lib/crypt_ops/crypto_nss_mgt.h"
#include "lib/crypt_ops/crypto_rand.h"
+#include "lib/crypt_ops/crypto_sys.h"
+
+#include "lib/subsys/subsys.h"
#include "siphash.h"
@@ -202,3 +205,47 @@ tor_is_using_nss(void)
return 0;
#endif
}
+
+static int
+subsys_crypto_initialize(void)
+{
+ if (crypto_early_init() < 0)
+ return -1;
+ crypto_dh_init();
+ return 0;
+}
+
+static void
+subsys_crypto_shutdown(void)
+{
+ crypto_global_cleanup();
+}
+
+static void
+subsys_crypto_prefork(void)
+{
+ crypto_prefork();
+}
+
+static void
+subsys_crypto_postfork(void)
+{
+ crypto_postfork();
+}
+
+static void
+subsys_crypto_thread_cleanup(void)
+{
+ crypto_thread_cleanup();
+}
+
+const struct subsys_fns_t sys_crypto = {
+ .name = "crypto",
+ .supported = true,
+ .level = -60,
+ .initialize = subsys_crypto_initialize,
+ .shutdown = subsys_crypto_shutdown,
+ .prefork = subsys_crypto_prefork,
+ .postfork = subsys_crypto_postfork,
+ .thread_cleanup = subsys_crypto_thread_cleanup,
+};
diff --git a/src/lib/crypt_ops/crypto_sys.h b/src/lib/crypt_ops/crypto_sys.h
new file mode 100644
index 0000000000..31644d088b
--- /dev/null
+++ b/src/lib/crypt_ops/crypto_sys.h
@@ -0,0 +1,14 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file log_crypto.h
+ * \brief Declare subsystem object for the crypto module.
+ **/
+
+#ifndef TOR_CRYPTO_SYS_H
+#define TOR_CRYPTO_SYS_H
+
+extern const struct subsys_fns_t sys_crypto;
+
+#endif /* !defined(TOR_CRYPTO_SYS_H) */
diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am
index 1022096fdc..d0ccc13bff 100644
--- a/src/lib/crypt_ops/include.am
+++ b/src/lib/crypt_ops/include.am
@@ -66,5 +66,6 @@ noinst_HEADERS += \
src/lib/crypt_ops/crypto_rand.h \
src/lib/crypt_ops/crypto_rsa.h \
src/lib/crypt_ops/crypto_s2k.h \
+ src/lib/crypt_ops/crypto_sys.h \
src/lib/crypt_ops/crypto_util.h \
src/lib/crypt_ops/digestset.h
diff --git a/src/lib/err/.may_include b/src/lib/err/.may_include
index 48cc0ef088..daa1b6e4ca 100644
--- a/src/lib/err/.may_include
+++ b/src/lib/err/.may_include
@@ -1,3 +1,5 @@
orconfig.h
lib/cc/*.h
lib/err/*.h
+lib/subsys/*.h
+lib/version/*.h \ No newline at end of file
diff --git a/src/lib/err/include.am b/src/lib/err/include.am
index f2a409c51e..43adcd2694 100644
--- a/src/lib/err/include.am
+++ b/src/lib/err/include.am
@@ -6,8 +6,9 @@ noinst_LIBRARIES += src/lib/libtor-err-testing.a
endif
src_lib_libtor_err_a_SOURCES = \
- src/lib/err/backtrace.c \
- src/lib/err/torerr.c
+ src/lib/err/backtrace.c \
+ src/lib/err/torerr.c \
+ src/lib/err/torerr_sys.c
src_lib_libtor_err_testing_a_SOURCES = \
$(src_lib_libtor_err_a_SOURCES)
@@ -16,4 +17,5 @@ src_lib_libtor_err_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
noinst_HEADERS += \
src/lib/err/backtrace.h \
- src/lib/err/torerr.h
+ src/lib/err/torerr.h \
+ src/lib/err/torerr_sys.h
diff --git a/src/lib/err/torerr.c b/src/lib/err/torerr.c
index f9e139f967..e9de86837f 100644
--- a/src/lib/err/torerr.c
+++ b/src/lib/err/torerr.c
@@ -123,6 +123,16 @@ tor_log_set_sigsafe_err_fds(const int *fds, int n)
}
/**
+ * Reset the list of emergency error fds to its default.
+ */
+void
+tor_log_reset_sigsafe_err_fds(void)
+{
+ int fds[] = { STDERR_FILENO };
+ tor_log_set_sigsafe_err_fds(fds, 1);
+}
+
+/**
* Set the granularity (in ms) to use when reporting fatal errors outside
* the logging system.
*/
diff --git a/src/lib/err/torerr.h b/src/lib/err/torerr.h
index d4bba6916f..b415ef73ef 100644
--- a/src/lib/err/torerr.h
+++ b/src/lib/err/torerr.h
@@ -39,6 +39,7 @@ void tor_raw_assertion_failed_msg_(const char *file, int line,
void tor_log_err_sigsafe(const char *m, ...);
int tor_log_get_sigsafe_err_fds(const int **out);
void tor_log_set_sigsafe_err_fds(const int *fds, int n);
+void tor_log_reset_sigsafe_err_fds(void);
void tor_log_sigsafe_err_set_granularity(int ms);
int format_hex_number_sigsafe(unsigned long x, char *buf, int max_len);
diff --git a/src/lib/err/torerr_sys.c b/src/lib/err/torerr_sys.c
new file mode 100644
index 0000000000..96bb1308a4
--- /dev/null
+++ b/src/lib/err/torerr_sys.c
@@ -0,0 +1,40 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file torerr_sys.c
+ * \brief Subsystem object for the error handling subsystem.
+ **/
+
+#include "orconfig.h"
+#include "lib/err/backtrace.h"
+#include "lib/err/torerr.h"
+#include "lib/err/torerr_sys.h"
+#include "lib/subsys/subsys.h"
+#include "lib/version/torversion.h"
+
+#include <stddef.h>
+
+static int
+subsys_torerr_initialize(void)
+{
+ if (configure_backtrace_handler(get_version()) < 0)
+ return -1;
+ tor_log_reset_sigsafe_err_fds();
+
+ return 0;
+}
+static void
+subsys_torerr_shutdown(void)
+{
+ tor_log_reset_sigsafe_err_fds();
+ clean_up_backtrace_handler();
+}
+
+const subsys_fns_t sys_torerr = {
+ .name = "err",
+ .level = -100,
+ .supported = true,
+ .initialize = subsys_torerr_initialize,
+ .shutdown = subsys_torerr_shutdown
+};
diff --git a/src/lib/err/torerr_sys.h b/src/lib/err/torerr_sys.h
new file mode 100644
index 0000000000..b56270d538
--- /dev/null
+++ b/src/lib/err/torerr_sys.h
@@ -0,0 +1,14 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file torerr_sys.h
+ * \brief Declare subsystem object for torerr.c
+ **/
+
+#ifndef TOR_TORERR_SYS_H
+#define TOR_TORERR_SYS_H
+
+extern const struct subsys_fns_t sys_torerr;
+
+#endif /* !defined(TOR_TORERR_SYS_H) */
diff --git a/src/lib/evloop/workqueue.c b/src/lib/evloop/workqueue.c
index 931f65e710..5471f87b04 100644
--- a/src/lib/evloop/workqueue.c
+++ b/src/lib/evloop/workqueue.c
@@ -15,7 +15,7 @@
*
* The main thread informs the worker threads of pending work by using a
* condition variable. The workers inform the main process of completed work
- * by using an alert_sockets_t object, as implemented in compat_threads.c.
+ * by using an alert_sockets_t object, as implemented in net/alertsock.c.
*
* The main thread can also queue an "update" that will be handled by all the
* workers. This is useful for updating state that all the workers share.
@@ -622,8 +622,8 @@ reply_event_cb(evutil_socket_t sock, short events, void *arg)
tp->reply_cb(tp);
}
-/** Register the threadpool <b>tp</b>'s reply queue with the libevent
- * mainloop of <b>base</b>. If <b>tp</b> is provided, it is run after
+/** Register the threadpool <b>tp</b>'s reply queue with Tor's global
+ * libevent mainloop. If <b>cb</b> is provided, it is run after
* each time there is work to process from the reply queue. Return 0 on
* success, -1 on failure.
*/
diff --git a/src/lib/evloop/workqueue.h b/src/lib/evloop/workqueue.h
index da292d1f05..10d5d47464 100644
--- a/src/lib/evloop/workqueue.h
+++ b/src/lib/evloop/workqueue.h
@@ -63,7 +63,6 @@ replyqueue_t *threadpool_get_replyqueue(threadpool_t *tp);
replyqueue_t *replyqueue_new(uint32_t alertsocks_flags);
void replyqueue_process(replyqueue_t *queue);
-struct event_base;
int threadpool_register_reply_event(threadpool_t *tp,
void (*cb)(threadpool_t *tp));
diff --git a/src/lib/log/.may_include b/src/lib/log/.may_include
index 852173aab3..11c87f0a0d 100644
--- a/src/lib/log/.may_include
+++ b/src/lib/log/.may_include
@@ -9,7 +9,7 @@ lib/lock/*.h
lib/log/*.h
lib/malloc/*.h
lib/string/*.h
+lib/subsys/*.h
lib/testsupport/*.h
+lib/version/*.h
lib/wallclock/*.h
-
-micro-revision.i \ No newline at end of file
diff --git a/src/lib/log/include.am b/src/lib/log/include.am
index 4a6c9b3686..9d3dbe3104 100644
--- a/src/lib/log/include.am
+++ b/src/lib/log/include.am
@@ -7,9 +7,9 @@ endif
src_lib_libtor_log_a_SOURCES = \
src/lib/log/escape.c \
- src/lib/log/git_revision.c \
src/lib/log/ratelim.c \
src/lib/log/log.c \
+ src/lib/log/log_sys.c \
src/lib/log/util_bug.c
if WIN32
@@ -21,16 +21,10 @@ src_lib_libtor_log_testing_a_SOURCES = \
src_lib_libtor_log_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_log_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
-# Declare that these object files depend on micro-revision.i. Without this
-# rule, we could try to build them before micro-revision.i was created.
-src/lib/log/git_revision.$(OBJEXT) \
- src/lib/log/src_lib_libtor_log_testing_a-git_revision.$(OBJEXT): \
- micro-revision.i
-
noinst_HEADERS += \
src/lib/log/escape.h \
- src/lib/log/git_revision.h \
src/lib/log/ratelim.h \
src/lib/log/log.h \
+ src/lib/log/log_sys.h \
src/lib/log/util_bug.h \
src/lib/log/win32err.h
diff --git a/src/lib/log/log.c b/src/lib/log/log.c
index d60ce6308a..46107fe848 100644
--- a/src/lib/log/log.c
+++ b/src/lib/log/log.c
@@ -32,7 +32,8 @@
#define LOG_PRIVATE
#include "lib/log/log.h"
-#include "lib/log/git_revision.h"
+#include "lib/log/log_sys.h"
+#include "lib/version/git_revision.h"
#include "lib/log/ratelim.h"
#include "lib/lock/compat_mutex.h"
#include "lib/smartlist_core/smartlist_core.h"
diff --git a/src/lib/log/log_sys.c b/src/lib/log/log_sys.c
new file mode 100644
index 0000000000..e20f3156ca
--- /dev/null
+++ b/src/lib/log/log_sys.c
@@ -0,0 +1,35 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file log_sys.c
+ * \brief Setup and tear down the logging module.
+ **/
+
+#include "orconfig.h"
+#include "lib/subsys/subsys.h"
+#include "lib/log/escape.h"
+#include "lib/log/log.h"
+#include "lib/log/log_sys.h"
+
+static int
+subsys_logging_initialize(void)
+{
+ init_logging(0);
+ return 0;
+}
+
+static void
+subsys_logging_shutdown(void)
+{
+ logs_free_all();
+ escaped(NULL);
+}
+
+const subsys_fns_t sys_logging = {
+ .name = "log",
+ .supported = true,
+ .level = -90,
+ .initialize = subsys_logging_initialize,
+ .shutdown = subsys_logging_shutdown,
+};
diff --git a/src/lib/log/log_sys.h b/src/lib/log/log_sys.h
new file mode 100644
index 0000000000..f7afbb279d
--- /dev/null
+++ b/src/lib/log/log_sys.h
@@ -0,0 +1,14 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file log_sys.h
+ * \brief Declare subsystem object for the logging module.
+ **/
+
+#ifndef TOR_LOG_SYS_H
+#define TOR_LOG_SYS_H
+
+extern const struct subsys_fns_t sys_logging;
+
+#endif /* !defined(TOR_LOG_SYS_H) */
diff --git a/src/lib/net/.may_include b/src/lib/net/.may_include
index 13b209bbed..f93f0e1552 100644
--- a/src/lib/net/.may_include
+++ b/src/lib/net/.may_include
@@ -11,5 +11,6 @@ lib/lock/*.h
lib/log/*.h
lib/net/*.h
lib/string/*.h
+lib/subsys/*.h
lib/testsupport/*.h
lib/malloc/*.h \ No newline at end of file
diff --git a/src/lib/net/address.c b/src/lib/net/address.c
index 03767e2950..a351b9df28 100644
--- a/src/lib/net/address.c
+++ b/src/lib/net/address.c
@@ -40,6 +40,7 @@
#include "lib/net/address.h"
#include "lib/net/socket.h"
+#include "lib/cc/ctassert.h"
#include "lib/container/smartlist.h"
#include "lib/ctime/di_ops.h"
#include "lib/log/log.h"
@@ -98,6 +99,7 @@
#if AF_UNSPEC != 0
#error We rely on AF_UNSPEC being 0. Let us know about your platform, please!
#endif
+CTASSERT(AF_UNSPEC == 0);
/** Convert the tor_addr_t in <b>a</b>, with port in <b>port</b>, into a
* sockaddr object in *<b>sa_out</b> of object size <b>len</b>. If not enough
@@ -1187,14 +1189,22 @@ tor_addr_parse(tor_addr_t *addr, const char *src)
int result;
struct in_addr in_tmp;
struct in6_addr in6_tmp;
+ int brackets_detected = 0;
+
tor_assert(addr && src);
- if (src[0] == '[' && src[1])
+
+ size_t len = strlen(src);
+
+ if (len && src[0] == '[' && src[len - 1] == ']') {
+ brackets_detected = 1;
src = tmp = tor_strndup(src+1, strlen(src)-2);
+ }
if (tor_inet_pton(AF_INET6, src, &in6_tmp) > 0) {
result = AF_INET6;
tor_addr_from_in6(addr, &in6_tmp);
- } else if (tor_inet_pton(AF_INET, src, &in_tmp) > 0) {
+ } else if (!brackets_detected &&
+ tor_inet_pton(AF_INET, src, &in_tmp) > 0) {
result = AF_INET;
tor_addr_from_in(addr, &in_tmp);
} else {
diff --git a/src/lib/net/inaddr.c b/src/lib/net/inaddr.c
index dcd8fcdd65..0960d323c5 100644
--- a/src/lib/net/inaddr.c
+++ b/src/lib/net/inaddr.c
@@ -168,6 +168,13 @@ tor_inet_pton(int af, const char *src, void *dst)
if (af == AF_INET) {
return tor_inet_aton(src, dst);
} else if (af == AF_INET6) {
+ ssize_t len = strlen(src);
+
+ /* Reject if src has needless trailing ':'. */
+ if (len > 2 && src[len - 1] == ':' && src[len - 2] != ':') {
+ return 0;
+ }
+
struct in6_addr *out = dst;
uint16_t words[8];
int gapPos = -1, i, setWords=0;
@@ -207,7 +214,6 @@ tor_inet_pton(int af, const char *src, void *dst)
return 0;
if (TOR_ISXDIGIT(*src)) {
char *next;
- ssize_t len;
long r = strtol(src, &next, 16);
if (next == NULL || next == src) {
/* The 'next == src' error case can happen on versions of openbsd
diff --git a/src/lib/net/include.am b/src/lib/net/include.am
index ff0967e786..8a88f0f2ae 100644
--- a/src/lib/net/include.am
+++ b/src/lib/net/include.am
@@ -11,6 +11,7 @@ src_lib_libtor_net_a_SOURCES = \
src/lib/net/buffers_net.c \
src/lib/net/gethostname.c \
src/lib/net/inaddr.c \
+ src/lib/net/network_sys.c \
src/lib/net/resolve.c \
src/lib/net/socket.c \
src/lib/net/socketpair.c
@@ -28,6 +29,7 @@ noinst_HEADERS += \
src/lib/net/inaddr.h \
src/lib/net/inaddr_st.h \
src/lib/net/nettypes.h \
+ src/lib/net/network_sys.h \
src/lib/net/resolve.h \
src/lib/net/socket.h \
src/lib/net/socketpair.h \
diff --git a/src/lib/net/network_sys.c b/src/lib/net/network_sys.c
new file mode 100644
index 0000000000..ac49288ee6
--- /dev/null
+++ b/src/lib/net/network_sys.c
@@ -0,0 +1,44 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file network_sys.c
+ * \brief Subsystem object for networking setup.
+ **/
+
+#include "orconfig.h"
+#include "lib/subsys/subsys.h"
+#include "lib/net/network_sys.h"
+#include "lib/net/resolve.h"
+#include "lib/net/socket.h"
+
+#ifdef _WIN32
+#include <winsock2.h>
+#include <windows.h>
+#endif
+
+static int
+subsys_network_initialize(void)
+{
+ if (network_init() < 0)
+ return -1;
+
+ return 0;
+}
+
+static void
+subsys_network_shutdown(void)
+{
+#ifdef _WIN32
+ WSACleanup();
+#endif
+ tor_free_getaddrinfo_cache();
+}
+
+const subsys_fns_t sys_network = {
+ .name = "network",
+ .level = -90,
+ .supported = true,
+ .initialize = subsys_network_initialize,
+ .shutdown = subsys_network_shutdown,
+};
diff --git a/src/lib/net/network_sys.h b/src/lib/net/network_sys.h
new file mode 100644
index 0000000000..62b778bb66
--- /dev/null
+++ b/src/lib/net/network_sys.h
@@ -0,0 +1,14 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file log_network.h
+ * \brief Declare subsystem object for the network module.
+ **/
+
+#ifndef TOR_NETWORK_SYS_H
+#define TOR_NETWORK_SYS_H
+
+extern const struct subsys_fns_t sys_network;
+
+#endif /* !defined(TOR_NETWORK_SYS_H) */
diff --git a/src/lib/osinfo/uname.c b/src/lib/osinfo/uname.c
index 9d1923695d..7111ae31d2 100644
--- a/src/lib/osinfo/uname.c
+++ b/src/lib/osinfo/uname.c
@@ -46,22 +46,33 @@ get_uname,(void))
#ifdef _WIN32
OSVERSIONINFOEX info;
int i;
+ int is_client = 0;
+ int is_server = 0;
const char *plat = NULL;
static struct {
- unsigned major; unsigned minor; const char *version;
+ unsigned major; unsigned minor;
+ const char *client_version; const char *server_version;
} win_version_table[] = {
- { 6, 2, "Windows 8" },
- { 6, 1, "Windows 7" },
- { 6, 0, "Windows Vista" },
- { 5, 2, "Windows Server 2003" },
- { 5, 1, "Windows XP" },
- { 5, 0, "Windows 2000" },
- /* { 4, 0, "Windows NT 4.0" }, */
- { 4, 90, "Windows Me" },
- { 4, 10, "Windows 98" },
- /* { 4, 0, "Windows 95" } */
- { 3, 51, "Windows NT 3.51" },
- { 0, 0, NULL }
+ /* This table must be sorted in descending order.
+ * Sources:
+ * https://en.wikipedia.org/wiki/List_of_Microsoft_Windows_versions
+ * https://docs.microsoft.com/en-us/windows/desktop/api/winnt/
+ * ns-winnt-_osversioninfoexa#remarks
+ */
+ /* Windows Server 2019 is indistinguishable from Windows Server 2016
+ * using GetVersionEx().
+ { 10, 0, NULL, "Windows Server 2019" }, */
+ { 10, 0, "Windows 10", "Windows Server 2016" },
+ { 6, 3, "Windows 8.1", "Windows Server 2012 R2" },
+ { 6, 2, "Windows 8", "Windows Server 2012" },
+ { 6, 1, "Windows 7", "Windows Server 2008 R2" },
+ { 6, 0, "Windows Vista", "Windows Server 2008" },
+ { 5, 2, "Windows XP Professional", "Windows Server 2003" },
+ /* Windows XP did not have a server version, but we need something here */
+ { 5, 1, "Windows XP", "Windows XP Server" },
+ { 5, 0, "Windows 2000 Professional", "Windows 2000 Server" },
+ /* Earlier versions are not supported by GetVersionEx(). */
+ { 0, 0, NULL, NULL }
};
memset(&info, 0, sizeof(info));
info.dwOSVersionInfoSize = sizeof(info);
@@ -71,25 +82,34 @@ get_uname,(void))
uname_result_is_set = 1;
return uname_result;
}
- if (info.dwMajorVersion == 4 && info.dwMinorVersion == 0) {
- if (info.dwPlatformId == VER_PLATFORM_WIN32_NT)
- plat = "Windows NT 4.0";
- else
- plat = "Windows 95";
+#ifdef VER_NT_SERVER
+ if (info.wProductType == VER_NT_SERVER ||
+ info.wProductType == VER_NT_DOMAIN_CONTROLLER) {
+ is_server = 1;
} else {
- for (i=0; win_version_table[i].major>0; ++i) {
- if (win_version_table[i].major == info.dwMajorVersion &&
- win_version_table[i].minor == info.dwMinorVersion) {
- plat = win_version_table[i].version;
- break;
+ is_client = 1;
+ }
+#endif /* defined(VER_NT_SERVER) */
+ /* Search the version table for a matching version */
+ for (i=0; win_version_table[i].major>0; ++i) {
+ if (win_version_table[i].major == info.dwMajorVersion &&
+ win_version_table[i].minor == info.dwMinorVersion) {
+ if (is_server) {
+ plat = win_version_table[i].server_version;
+ } else {
+ /* Use client versions for clients, and when we don't know if it
+ * is a client or a server. */
+ plat = win_version_table[i].client_version;
}
+ break;
}
}
if (plat) {
strlcpy(uname_result, plat, sizeof(uname_result));
} else {
- if (info.dwMajorVersion > 6 ||
- (info.dwMajorVersion==6 && info.dwMinorVersion>2))
+ if (info.dwMajorVersion > win_version_table[0].major ||
+ (info.dwMajorVersion == win_version_table[0].major &&
+ info.dwMinorVersion > win_version_table[0].minor))
tor_snprintf(uname_result, sizeof(uname_result),
"Very recent version of Windows [major=%d,minor=%d]",
(int)info.dwMajorVersion,(int)info.dwMinorVersion);
@@ -98,12 +118,25 @@ get_uname,(void))
"Unrecognized version of Windows [major=%d,minor=%d]",
(int)info.dwMajorVersion,(int)info.dwMinorVersion);
}
-#ifdef VER_NT_SERVER
- if (info.wProductType == VER_NT_SERVER ||
- info.wProductType == VER_NT_DOMAIN_CONTROLLER) {
- strlcat(uname_result, " [server]", sizeof(uname_result));
- }
-#endif /* defined(VER_NT_SERVER) */
+ /* Now append extra information to the name.
+ *
+ * Microsoft's API documentation says that on Windows 8.1 and later,
+ * GetVersionEx returns Windows 8 (6.2) for applications without an
+ * app compatibility manifest (including tor's default build).
+ *
+ * But in our testing, we have seen the actual Windows version on
+ * Windows Server 2012 R2, even without a manifest. */
+ if (info.dwMajorVersion > 6 ||
+ (info.dwMajorVersion == 6 && info.dwMinorVersion >= 2)) {
+ /* When GetVersionEx() returns Windows 8, the actual OS may be any
+ * later version. */
+ strlcat(uname_result, " [or later]", sizeof(uname_result));
+ }
+ /* When we don't know if the OS is a client or server version, we use
+ * the client version, and this qualifier. */
+ if (!is_server && !is_client) {
+ strlcat(uname_result, " [client or server]", sizeof(uname_result));
+ }
#else /* !(defined(_WIN32)) */
/* LCOV_EXCL_START -- can't provoke uname failure */
strlcpy(uname_result, "Unknown platform", sizeof(uname_result));
diff --git a/src/lib/process/.may_include b/src/lib/process/.may_include
index 05414d2a96..a2d57a52f3 100644
--- a/src/lib/process/.may_include
+++ b/src/lib/process/.may_include
@@ -11,6 +11,7 @@ lib/malloc/*.h
lib/net/*.h
lib/process/*.h
lib/string/*.h
+lib/subsys/*.h
lib/testsupport/*.h
lib/thread/*.h
diff --git a/src/lib/process/include.am b/src/lib/process/include.am
index c6cc3a6699..2aa30cc3c1 100644
--- a/src/lib/process/include.am
+++ b/src/lib/process/include.am
@@ -12,7 +12,8 @@ src_lib_libtor_process_a_SOURCES = \
src/lib/process/restrict.c \
src/lib/process/setuid.c \
src/lib/process/subprocess.c \
- src/lib/process/waitpid.c
+ src/lib/process/waitpid.c \
+ src/lib/process/winprocess_sys.c
src_lib_libtor_process_testing_a_SOURCES = \
$(src_lib_libtor_process_a_SOURCES)
@@ -26,4 +27,5 @@ noinst_HEADERS += \
src/lib/process/restrict.h \
src/lib/process/setuid.h \
src/lib/process/subprocess.h \
- src/lib/process/waitpid.h
+ src/lib/process/waitpid.h \
+ src/lib/process/winprocess_sys.h
diff --git a/src/lib/process/winprocess_sys.c b/src/lib/process/winprocess_sys.c
new file mode 100644
index 0000000000..ef66f8bfb1
--- /dev/null
+++ b/src/lib/process/winprocess_sys.c
@@ -0,0 +1,64 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file winprocess_sys.c
+ * \brief Subsystem object for windows process setup.
+ **/
+
+#include "orconfig.h"
+#include "lib/subsys/subsys.h"
+#include "lib/process/winprocess_sys.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#ifdef _WIN32
+#include <windows.h>
+
+#define WINPROCESS_SYS_ENABLED true
+
+static int
+subsys_winprocess_initialize(void)
+{
+#ifndef HeapEnableTerminationOnCorruption
+#define HeapEnableTerminationOnCorruption 1
+#endif
+
+ /* On heap corruption, just give up; don't try to play along. */
+ HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
+
+ /* SetProcessDEPPolicy is only supported on 32-bit Windows.
+ * (On 64-bit Windows it always fails, and some compilers don't like the
+ * PSETDEP cast.)
+ * 32-bit Windows defines _WIN32.
+ * 64-bit Windows defines _WIN32 and _WIN64. */
+#ifndef _WIN64
+ /* Call SetProcessDEPPolicy to permanently enable DEP.
+ The function will not resolve on earlier versions of Windows,
+ and failure is not dangerous. */
+ HMODULE hMod = GetModuleHandleA("Kernel32.dll");
+ if (hMod) {
+ typedef BOOL (WINAPI *PSETDEP)(DWORD);
+ PSETDEP setdeppolicy = (PSETDEP)GetProcAddress(hMod,
+ "SetProcessDEPPolicy");
+ if (setdeppolicy) {
+ /* PROCESS_DEP_ENABLE | PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION */
+ setdeppolicy(3);
+ }
+ }
+#endif /* !defined(_WIN64) */
+
+ return 0;
+}
+#else /* !defined(_WIN32) */
+#define WINPROCESS_SYS_ENABLED false
+#define subsys_winprocess_initialize NULL
+#endif /* defined(_WIN32) */
+
+const subsys_fns_t sys_winprocess = {
+ .name = "winprocess",
+ .level = -100,
+ .supported = WINPROCESS_SYS_ENABLED,
+ .initialize = subsys_winprocess_initialize,
+};
diff --git a/src/lib/process/winprocess_sys.h b/src/lib/process/winprocess_sys.h
new file mode 100644
index 0000000000..cb096e0c92
--- /dev/null
+++ b/src/lib/process/winprocess_sys.h
@@ -0,0 +1,14 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file winprocess_sys.h
+ * \brief Declare subsystem object for winprocess.c
+ **/
+
+#ifndef TOR_WINPROCESS_SYS_H
+#define TOR_WINPROCESS_SYS_H
+
+extern const struct subsys_fns_t sys_winprocess;
+
+#endif /* !defined(TOR_WINPROCESS_SYS_H) */
diff --git a/src/lib/string/util_string.c b/src/lib/string/util_string.c
index b2b85d151d..e76e73046f 100644
--- a/src/lib/string/util_string.c
+++ b/src/lib/string/util_string.c
@@ -541,3 +541,16 @@ string_is_utf8(const char *str, size_t len)
}
return true;
}
+
+/** As string_is_utf8(), but returns false if the string begins with a UTF-8
+ * byte order mark (BOM).
+ */
+int
+string_is_utf8_no_bom(const char *str, size_t len)
+{
+ if (len >= 3 && (!strcmpstart(str, "\uFEFF") ||
+ !strcmpstart(str, "\uFFFE"))) {
+ return false;
+ }
+ return string_is_utf8(str, len);
+}
diff --git a/src/lib/string/util_string.h b/src/lib/string/util_string.h
index 746ece0d33..99467a27c3 100644
--- a/src/lib/string/util_string.h
+++ b/src/lib/string/util_string.h
@@ -53,5 +53,6 @@ const char *find_str_at_start_of_line(const char *haystack,
int string_is_C_identifier(const char *string);
int string_is_utf8(const char *str, size_t len);
+int string_is_utf8_no_bom(const char *str, size_t len);
#endif /* !defined(TOR_UTIL_STRING_H) */
diff --git a/src/lib/subsys/.may_include b/src/lib/subsys/.may_include
new file mode 100644
index 0000000000..2b06e8519c
--- /dev/null
+++ b/src/lib/subsys/.may_include
@@ -0,0 +1 @@
+orconfig.h
diff --git a/src/lib/subsys/include.am b/src/lib/subsys/include.am
new file mode 100644
index 0000000000..4741126b14
--- /dev/null
+++ b/src/lib/subsys/include.am
@@ -0,0 +1,3 @@
+
+noinst_HEADERS += \
+ src/lib/subsys/subsys.h
diff --git a/src/lib/subsys/subsys.h b/src/lib/subsys/subsys.h
new file mode 100644
index 0000000000..2452ec6e2f
--- /dev/null
+++ b/src/lib/subsys/subsys.h
@@ -0,0 +1,95 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_SUBSYS_T
+#define TOR_SUBSYS_T
+
+#include <stdbool.h>
+
+struct dispatch_connector_t;
+
+/**
+ * A subsystem is a part of Tor that is initialized, shut down, configured,
+ * and connected to other parts of Tor.
+ *
+ * All callbacks are optional -- if a callback is set to NULL, the subsystem
+ * manager will treat it as a no-op.
+ *
+ * You should use c99 named-field initializers with this structure: we
+ * will be adding more fields, often in the middle of the structure.
+ **/
+typedef struct subsys_fns_t {
+ /**
+ * The name of this subsystem. It should be a programmer-readable
+ * identifier.
+ **/
+ const char *name;
+
+ /**
+ * Whether this subsystem is supported -- that is, whether it is compiled
+ * into Tor. For most subsystems, this should be true.
+ **/
+ bool supported;
+
+ /**
+ * The 'initialization level' for the subsystem. It should run from -100
+ * through +100. The subsystems are initialized from lowest level to
+ * highest, and shut down from highest level to lowest.
+ **/
+ int level;
+
+ /**
+ * Initialize any global components of this subsystem.
+ *
+ * This function MAY rely on any lower-level subsystem being initialized.
+ *
+ * This function MUST NOT rely on any runtime configuration information;
+ * it is only for global state or pre-configuration state.
+ *
+ * (If you need to do any setup that depends on configuration, you'll need
+ * to declare a configuration callback. (Not yet designed))
+ *
+ * This function MUST NOT have any parts that can fail.
+ **/
+ int (*initialize)(void);
+
+ /**
+ * Connect a subsystem to the message dispatch system.
+ **/
+ int (*add_pubsub)(struct dispatch_connector_t *);
+
+ /**
+ * Perform any necessary pre-fork cleanup. This function may not fail.
+ */
+ void (*prefork)(void);
+
+ /**
+ * Perform any necessary post-fork setup. This function may not fail.
+ */
+ void (*postfork)(void);
+
+ /**
+ * Free any thread-local resources held by this subsystem. Called before
+ * the thread exits.
+ */
+ void (*thread_cleanup)(void);
+
+ /**
+ * Free all resources held by this subsystem.
+ *
+ * This function is not allowed to fail.
+ **/
+ void (*shutdown)(void);
+
+} subsys_fns_t;
+
+#define MIN_SUBSYS_LEVEL -100
+#define MAX_SUBSYS_LEVEL 100
+
+/* All tor "libraries" (in src/libs) should have a subsystem level equal to or
+ * less than this value. */
+#define SUBSYS_LEVEL_LIBS -10
+
+#endif
diff --git a/src/lib/thread/.may_include b/src/lib/thread/.may_include
index fc56f46836..02711348c5 100644
--- a/src/lib/thread/.may_include
+++ b/src/lib/thread/.may_include
@@ -2,6 +2,7 @@ orconfig.h
lib/cc/*.h
lib/lock/*.h
lib/log/*.h
+lib/subsys/*.h
lib/testsupport/*.h
lib/thread/*.h
lib/wallclock/*.h
diff --git a/src/lib/thread/compat_threads.c b/src/lib/thread/compat_threads.c
index 7f1970af45..0b466da212 100644
--- a/src/lib/thread/compat_threads.c
+++ b/src/lib/thread/compat_threads.c
@@ -14,9 +14,11 @@
#include "orconfig.h"
#include <stdlib.h>
#include "lib/thread/threads.h"
+#include "lib/thread/thread_sys.h"
#include "lib/log/log.h"
#include "lib/log/util_bug.h"
+#include "lib/subsys/subsys.h"
#include <string.h>
@@ -109,3 +111,17 @@ atomic_counter_exchange(atomic_counter_t *counter, size_t newval)
return oldval;
}
#endif /* !defined(HAVE_WORKING_STDATOMIC) */
+
+static int
+subsys_threads_initialize(void)
+{
+ tor_threads_init();
+ return 0;
+}
+
+const subsys_fns_t sys_threads = {
+ .name = "threads",
+ .supported = true,
+ .level = -95,
+ .initialize = subsys_threads_initialize,
+};
diff --git a/src/lib/thread/include.am b/src/lib/thread/include.am
index 9ec23d166e..695795a2c8 100644
--- a/src/lib/thread/include.am
+++ b/src/lib/thread/include.am
@@ -23,5 +23,6 @@ src_lib_libtor_thread_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_thread_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
noinst_HEADERS += \
- src/lib/thread/threads.h \
- src/lib/thread/numcpus.h
+ src/lib/thread/numcpus.h \
+ src/lib/thread/thread_sys.h \
+ src/lib/thread/threads.h
diff --git a/src/lib/thread/thread_sys.h b/src/lib/thread/thread_sys.h
new file mode 100644
index 0000000000..984abe88e8
--- /dev/null
+++ b/src/lib/thread/thread_sys.h
@@ -0,0 +1,14 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file threads_sys.h
+ * \brief Declare subsystem object for threads library
+ **/
+
+#ifndef TOR_THREADS_SYS_H
+#define TOR_THREADS_SYS_H
+
+extern const struct subsys_fns_t sys_threads;
+
+#endif /* !defined(TOR_THREADS_SYS_H) */
diff --git a/src/lib/time/.may_include b/src/lib/time/.may_include
index 2c7e37a836..40a18805ac 100644
--- a/src/lib/time/.may_include
+++ b/src/lib/time/.may_include
@@ -4,6 +4,7 @@ lib/cc/*.h
lib/err/*.h
lib/intmath/*.h
lib/log/*.h
+lib/subsys/*.h
lib/time/*.h
lib/wallclock/*.h
diff --git a/src/lib/time/include.am b/src/lib/time/include.am
index a3f93a3744..dae16f49ac 100644
--- a/src/lib/time/include.am
+++ b/src/lib/time/include.am
@@ -7,6 +7,7 @@ endif
src_lib_libtor_time_a_SOURCES = \
src/lib/time/compat_time.c \
+ src/lib/time/time_sys.c \
src/lib/time/tvdiff.c
src_lib_libtor_time_testing_a_SOURCES = \
@@ -16,4 +17,5 @@ src_lib_libtor_time_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
noinst_HEADERS += \
src/lib/time/compat_time.h \
+ src/lib/time/time_sys.h \
src/lib/time/tvdiff.h
diff --git a/src/lib/time/time_sys.c b/src/lib/time/time_sys.c
new file mode 100644
index 0000000000..b29ca35e69
--- /dev/null
+++ b/src/lib/time/time_sys.c
@@ -0,0 +1,26 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file time_sys.c
+ * \brief Subsystem object for monotime setup.
+ **/
+
+#include "orconfig.h"
+#include "lib/subsys/subsys.h"
+#include "lib/time/time_sys.h"
+#include "lib/time/compat_time.h"
+
+static int
+subsys_time_initialize(void)
+{
+ monotime_init();
+ return 0;
+}
+
+const subsys_fns_t sys_time = {
+ .name = "time",
+ .level = -90,
+ .supported = true,
+ .initialize = subsys_time_initialize,
+};
diff --git a/src/lib/time/time_sys.h b/src/lib/time/time_sys.h
new file mode 100644
index 0000000000..0f1aebc268
--- /dev/null
+++ b/src/lib/time/time_sys.h
@@ -0,0 +1,14 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file log_time.h
+ * \brief Declare subsystem object for the time module.
+ **/
+
+#ifndef TOR_TIME_SYS_H
+#define TOR_TIME_SYS_H
+
+extern const struct subsys_fns_t sys_time;
+
+#endif /* !defined(TOR_TIME_SYS_H) */
diff --git a/src/lib/tls/.may_include b/src/lib/tls/.may_include
index 2840e590b8..79301bc318 100644
--- a/src/lib/tls/.may_include
+++ b/src/lib/tls/.may_include
@@ -11,6 +11,7 @@ lib/log/*.h
lib/malloc/*.h
lib/net/*.h
lib/string/*.h
+lib/subsys/*.h
lib/testsupport/testsupport.h
lib/tls/*.h
diff --git a/src/lib/tls/include.am b/src/lib/tls/include.am
index a664b29fb2..1817739eef 100644
--- a/src/lib/tls/include.am
+++ b/src/lib/tls/include.am
@@ -36,5 +36,6 @@ noinst_HEADERS += \
src/lib/tls/tortls.h \
src/lib/tls/tortls_internal.h \
src/lib/tls/tortls_st.h \
+ src/lib/tls/tortls_sys.h \
src/lib/tls/x509.h \
src/lib/tls/x509_internal.h
diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c
index 56f70bc371..654cacacf7 100644
--- a/src/lib/tls/tortls.c
+++ b/src/lib/tls/tortls.c
@@ -7,6 +7,7 @@
#define TOR_X509_PRIVATE
#include "lib/tls/x509.h"
#include "lib/tls/x509_internal.h"
+#include "lib/tls/tortls_sys.h"
#include "lib/tls/tortls.h"
#include "lib/tls/tortls_st.h"
#include "lib/tls/tortls_internal.h"
@@ -15,6 +16,7 @@
#include "lib/crypt_ops/crypto_rsa.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/net/socket.h"
+#include "lib/subsys/subsys.h"
#ifdef _WIN32
#include <winsock2.h>
@@ -440,3 +442,15 @@ tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity)
return rv;
}
+
+static void
+subsys_tortls_shutdown(void)
+{
+ tor_tls_free_all();
+}
+
+const subsys_fns_t sys_tortls = {
+ .name = "tortls",
+ .level = -50,
+ .shutdown = subsys_tortls_shutdown
+};
diff --git a/src/lib/tls/tortls_sys.h b/src/lib/tls/tortls_sys.h
new file mode 100644
index 0000000000..fd909f6019
--- /dev/null
+++ b/src/lib/tls/tortls_sys.h
@@ -0,0 +1,14 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file tortls_sys.h
+ * \brief Declare subsystem object for the tortls module
+ **/
+
+#ifndef TOR_TORTLS_SYS_H
+#define TOR_TORTLS_SYS_H
+
+extern const struct subsys_fns_t sys_tortls;
+
+#endif /* !defined(TOR_TORTLS_SYS_H) */
diff --git a/src/lib/version/.may_include b/src/lib/version/.may_include
new file mode 100644
index 0000000000..d159ceb41f
--- /dev/null
+++ b/src/lib/version/.may_include
@@ -0,0 +1,3 @@
+orconfig.h
+micro-revision.i
+lib/version/*.h \ No newline at end of file
diff --git a/src/lib/log/git_revision.c b/src/lib/version/git_revision.c
index 9d29ecd2a2..e5b2ff534e 100644
--- a/src/lib/log/git_revision.c
+++ b/src/lib/version/git_revision.c
@@ -4,7 +4,7 @@
/* See LICENSE for licensing information */
#include "orconfig.h"
-#include "lib/log/git_revision.h"
+#include "lib/version/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
diff --git a/src/lib/log/git_revision.h b/src/lib/version/git_revision.h
index 0ce1190795..0ce1190795 100644
--- a/src/lib/log/git_revision.h
+++ b/src/lib/version/git_revision.h
diff --git a/src/lib/version/include.am b/src/lib/version/include.am
new file mode 100644
index 0000000000..6944eb05e3
--- /dev/null
+++ b/src/lib/version/include.am
@@ -0,0 +1,25 @@
+
+noinst_LIBRARIES += src/lib/libtor-version.a
+
+if UNITTESTS_ENABLED
+noinst_LIBRARIES += src/lib/libtor-version-testing.a
+endif
+
+src_lib_libtor_version_a_SOURCES = \
+ src/lib/version/git_revision.c \
+ src/lib/version/version.c
+
+src_lib_libtor_version_testing_a_SOURCES = \
+ $(src_lib_libtor_version_a_SOURCES)
+src_lib_libtor_version_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
+src_lib_libtor_version_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+
+# Declare that these object files depend on micro-revision.i. Without this
+# rule, we could try to build them before micro-revision.i was created.
+src/lib/version/git_revision.$(OBJEXT) \
+ src/lib/version/src_lib_libtor_version_testing_a-git_revision.$(OBJEXT): \
+ micro-revision.i
+
+noinst_HEADERS += \
+ src/lib/version/git_revision.h \
+ src/lib/version/torversion.h
diff --git a/src/lib/version/torversion.h b/src/lib/version/torversion.h
new file mode 100644
index 0000000000..761d6f25ab
--- /dev/null
+++ b/src/lib/version/torversion.h
@@ -0,0 +1,12 @@
+/* Copyright 2001-2004 Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_VERSION_H
+#define TOR_VERSION_H
+
+const char *get_version(void);
+const char *get_short_version(void);
+
+#endif /* !defined(TOR_VERSION_H) */
diff --git a/src/lib/version/version.c b/src/lib/version/version.c
new file mode 100644
index 0000000000..29ada39c9d
--- /dev/null
+++ b/src/lib/version/version.c
@@ -0,0 +1,50 @@
+/* Copyright 2001-2004 Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "lib/version/torversion.h"
+#include "lib/version/git_revision.h"
+
+#include <stdio.h>
+#include <string.h>
+
+/** A shorter version of this Tor process's version, for export in our router
+ * descriptor. (Does not include the git version, if any.) */
+static const char the_short_tor_version[] =
+ VERSION
+#ifdef TOR_BUILD_TAG
+ " ("TOR_BUILD_TAG")"
+#endif
+ "";
+
+#define MAX_VERSION_LEN 128
+
+/** The version of this Tor process, possibly including git version */
+static char the_tor_version[MAX_VERSION_LEN] = "";
+
+/** Return the current Tor version. */
+const char *
+get_version(void)
+{
+ if (the_tor_version[0] == 0) {
+ if (strlen(tor_git_revision)) {
+ snprintf(the_tor_version, sizeof(the_tor_version),
+ "%s (git-%s)", the_short_tor_version, tor_git_revision);
+ } else {
+ snprintf(the_tor_version, sizeof(the_tor_version),
+ "%s", the_short_tor_version);
+ }
+ the_tor_version[sizeof(the_tor_version)-1] = 0;
+ }
+
+ return the_tor_version;
+}
+
+/** Return the current Tor version, without any git tag. */
+const char *
+get_short_version(void)
+{
+ return the_short_tor_version;
+}
diff --git a/src/lib/wallclock/.may_include b/src/lib/wallclock/.may_include
index dc010da063..ce7a26472b 100644
--- a/src/lib/wallclock/.may_include
+++ b/src/lib/wallclock/.may_include
@@ -3,4 +3,5 @@ lib/cc/*.h
lib/err/*.h
lib/wallclock/*.h
lib/string/*.h
+lib/subsys/*.h
lib/testsupport/*.h
diff --git a/src/lib/wallclock/approx_time.c b/src/lib/wallclock/approx_time.c
index bb9a292369..0b0ef382c2 100644
--- a/src/lib/wallclock/approx_time.c
+++ b/src/lib/wallclock/approx_time.c
@@ -9,7 +9,9 @@
**/
#include "orconfig.h"
+#include "lib/subsys/subsys.h"
#include "lib/wallclock/approx_time.h"
+#include "lib/wallclock/wallclock_sys.h"
/* =====
* Cached time
@@ -41,3 +43,17 @@ update_approx_time(time_t now)
cached_approx_time = now;
}
#endif /* !defined(TIME_IS_FAST) */
+
+static int
+subsys_wallclock_initialize(void)
+{
+ update_approx_time(time(NULL));
+ return 0;
+}
+
+const subsys_fns_t sys_wallclock = {
+ .name = "wallclock",
+ .supported = true,
+ .level = -99,
+ .initialize = subsys_wallclock_initialize,
+};
diff --git a/src/lib/wallclock/include.am b/src/lib/wallclock/include.am
index 1961639bd7..2351252e0c 100644
--- a/src/lib/wallclock/include.am
+++ b/src/lib/wallclock/include.am
@@ -19,4 +19,5 @@ noinst_HEADERS += \
src/lib/wallclock/approx_time.h \
src/lib/wallclock/timeval.h \
src/lib/wallclock/time_to_tm.h \
- src/lib/wallclock/tor_gettimeofday.h
+ src/lib/wallclock/tor_gettimeofday.h \
+ src/lib/wallclock/wallclock_sys.h
diff --git a/src/lib/wallclock/wallclock_sys.h b/src/lib/wallclock/wallclock_sys.h
new file mode 100644
index 0000000000..e009578a83
--- /dev/null
+++ b/src/lib/wallclock/wallclock_sys.h
@@ -0,0 +1,14 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file wallclock_sys.h
+ * \brief Declare subsystem object for the wallclock module.
+ **/
+
+#ifndef TOR_WALLCLOCK_SYS_H
+#define TOR_WALLCLOCK_SYS_H
+
+extern const struct subsys_fns_t sys_wallclock;
+
+#endif /* !defined(TOR_WALLCLOCK_SYS_H) */
diff --git a/src/rust/build.rs b/src/rust/build.rs
index 123d5c0682..bf566c56bf 100644
--- a/src/rust/build.rs
+++ b/src/rust/build.rs
@@ -162,6 +162,7 @@ pub fn main() {
cfg.component("tor-malloc");
cfg.component("tor-wallclock");
cfg.component("tor-err-testing");
+ cfg.component("tor-version-testing");
cfg.component("tor-intmath-testing");
cfg.component("tor-ctime-testing");
cfg.component("curve25519_donna");
diff --git a/src/rust/tor_util/strings.rs b/src/rust/tor_util/strings.rs
index d64275e06b..71a908a58c 100644
--- a/src/rust/tor_util/strings.rs
+++ b/src/rust/tor_util/strings.rs
@@ -105,11 +105,7 @@ macro_rules! cstr {
($($bytes:expr),*) => (
::std::ffi::CStr::from_bytes_with_nul(
concat!($($bytes),*, "\0").as_bytes()
- ).unwrap_or(
- unsafe{
- ::std::ffi::CStr::from_bytes_with_nul_unchecked(b"\0")
- }
- )
+ ).unwrap_or_default()
)
}
diff --git a/src/test/bench.c b/src/test/bench.c
index 959d4374b1..ff8707d41c 100644
--- a/src/test/bench.c
+++ b/src/test/bench.c
@@ -24,6 +24,7 @@
#include "core/or/circuitlist.h"
#include "app/config/config.h"
+#include "app/main/subsysmgr.h"
#include "lib/crypt_ops/crypto_curve25519.h"
#include "lib/crypt_ops/crypto_dh.h"
#include "core/crypto/onion_ntor.h"
@@ -690,9 +691,10 @@ main(int argc, const char **argv)
char *errmsg;
or_options_t *options;
- tor_threads_init();
+ subsystems_init_upto(SUBSYS_LEVEL_LIBS);
+ flush_log_messages_from_startup();
+
tor_compress_init();
- init_logging(1);
if (argc == 4 && !strcmp(argv[1], "diff")) {
const int N = 200;
@@ -702,11 +704,13 @@ main(int argc, const char **argv)
perror("X");
return 1;
}
+ size_t f1len = strlen(f1);
+ size_t f2len = strlen(f2);
for (i = 0; i < N; ++i) {
- char *diff = consensus_diff_generate(f1, f2);
+ char *diff = consensus_diff_generate(f1, f1len, f2, f2len);
tor_free(diff);
}
- char *diff = consensus_diff_generate(f1, f2);
+ char *diff = consensus_diff_generate(f1, f1len, f2, f2len);
printf("%s", diff);
tor_free(f1);
tor_free(f2);
@@ -737,7 +741,6 @@ main(int argc, const char **argv)
init_protocol_warning_severity_level();
options = options_new();
- init_logging(1);
options->command = CMD_RUN_UNITTESTS;
options->DataDirectory = tor_strdup("");
options->KeyDirectory = tor_strdup("");
diff --git a/src/test/fuzz/fuzz_consensus.c b/src/test/fuzz/fuzz_consensus.c
index b56702a650..1a4195b418 100644
--- a/src/test/fuzz/fuzz_consensus.c
+++ b/src/test/fuzz/fuzz_consensus.c
@@ -61,13 +61,13 @@ int
fuzz_main(const uint8_t *data, size_t sz)
{
networkstatus_t *ns;
- char *str = tor_memdup_nulterm(data, sz);
const char *eos = NULL;
networkstatus_type_t tp = NS_TYPE_CONSENSUS;
if (tor_memstr(data, MIN(sz, 1024), "tus vote"))
tp = NS_TYPE_VOTE;
const char *what = (tp == NS_TYPE_CONSENSUS) ? "consensus" : "vote";
- ns = networkstatus_parse_vote_from_string(str,
+ ns = networkstatus_parse_vote_from_string((const char *)data,
+ sz,
&eos,
tp);
if (ns) {
@@ -76,6 +76,6 @@ fuzz_main(const uint8_t *data, size_t sz)
} else {
log_debug(LD_GENERAL, "Parsing as %s failed", what);
}
- tor_free(str);
+
return 0;
}
diff --git a/src/test/fuzz/fuzz_diff.c b/src/test/fuzz/fuzz_diff.c
index 1079856fdb..64aecc8a64 100644
--- a/src/test/fuzz/fuzz_diff.c
+++ b/src/test/fuzz/fuzz_diff.c
@@ -10,9 +10,11 @@
#include "test/fuzz/fuzzing.h"
static int
-mock_consensus_compute_digest_(const char *c, consensus_digest_t *d)
+mock_consensus_compute_digest_(const char *c, size_t len,
+ consensus_digest_t *d)
{
(void)c;
+ (void)len;
memset(d->sha3_256, 3, sizeof(d->sha3_256));
return 0;
}
@@ -42,28 +44,34 @@ fuzz_main(const uint8_t *stdin_buf, size_t data_size)
if (! separator)
return 0;
size_t c1_len = separator - stdin_buf;
- char *c1 = tor_memdup_nulterm(stdin_buf, c1_len);
+ const char *c1 = (const char *)stdin_buf;
size_t c2_len = data_size - c1_len - SEPLEN;
- char *c2 = tor_memdup_nulterm(separator + SEPLEN, c2_len);
+ const char *c2 = (const char *)separator + SEPLEN;
- char *c3 = consensus_diff_generate(c1, c2);
+ const char *cp = memchr(c1, 0, c1_len);
+ if (cp)
+ c1_len = cp - c1;
+
+ cp = memchr(c2, 0, c2_len);
+ if (cp)
+ c2_len = cp - c2;
+
+ char *c3 = consensus_diff_generate(c1, c1_len, c2, c2_len);
if (c3) {
- char *c4 = consensus_diff_apply(c1, c3);
+ char *c4 = consensus_diff_apply(c1, c1_len, c3, strlen(c3));
tor_assert(c4);
- if (strcmp(c2, c4)) {
- printf("%s\n", escaped(c1));
- printf("%s\n", escaped(c2));
+ int equal = (c2_len == strlen(c4)) && fast_memeq(c2, c4, c2_len);
+ if (! equal) {
+ //printf("%s\n", escaped(c1));
+ //printf("%s\n", escaped(c2));
printf("%s\n", escaped(c3));
printf("%s\n", escaped(c4));
}
- tor_assert(! strcmp(c2, c4));
+ tor_assert(equal);
tor_free(c3);
tor_free(c4);
}
- tor_free(c1);
- tor_free(c2);
return 0;
}
-
diff --git a/src/test/fuzz/fuzz_diff_apply.c b/src/test/fuzz/fuzz_diff_apply.c
index 165d0e6126..9b25185225 100644
--- a/src/test/fuzz/fuzz_diff_apply.c
+++ b/src/test/fuzz/fuzz_diff_apply.c
@@ -10,9 +10,11 @@
#include "test/fuzz/fuzzing.h"
static int
-mock_consensus_compute_digest_(const char *c, consensus_digest_t *d)
+mock_consensus_compute_digest_(const char *c, size_t len,
+ consensus_digest_t *d)
{
(void)c;
+ (void)len;
memset(d->sha3_256, 3, sizeof(d->sha3_256));
return 0;
}
@@ -50,16 +52,13 @@ fuzz_main(const uint8_t *stdin_buf, size_t data_size)
if (! separator)
return 0;
size_t c1_len = separator - stdin_buf;
- char *c1 = tor_memdup_nulterm(stdin_buf, c1_len);
+ const char *c1 = (const char *)stdin_buf;
size_t c2_len = data_size - c1_len - SEPLEN;
- char *c2 = tor_memdup_nulterm(separator + SEPLEN, c2_len);
+ const char *c2 = (const char *)separator + SEPLEN;
- char *c3 = consensus_diff_apply(c1, c2);
+ char *c3 = consensus_diff_apply(c1, c1_len, c2, c2_len);
- tor_free(c1);
- tor_free(c2);
tor_free(c3);
return 0;
}
-
diff --git a/src/test/fuzz/fuzz_vrs.c b/src/test/fuzz/fuzz_vrs.c
index 0b869aa5c0..3c6d205a3f 100644
--- a/src/test/fuzz/fuzz_vrs.c
+++ b/src/test/fuzz/fuzz_vrs.c
@@ -53,24 +53,24 @@ fuzz_cleanup(void)
int
fuzz_main(const uint8_t *data, size_t sz)
{
- char *str = tor_memdup_nulterm(data, sz);
const char *s;
routerstatus_t *rs_ns = NULL, *rs_md = NULL, *rs_vote = NULL;
vote_routerstatus_t *vrs = tor_malloc_zero(sizeof(*vrs));
smartlist_t *tokens = smartlist_new();
+ const char *eos = (const char *)data + sz;
- s = str;
- rs_ns = routerstatus_parse_entry_from_string(area, &s, tokens,
+ s = (const char *)data;
+ rs_ns = routerstatus_parse_entry_from_string(area, &s, eos, tokens,
NULL, NULL, 26, FLAV_NS);
tor_assert(smartlist_len(tokens) == 0);
- s = str;
- rs_md = routerstatus_parse_entry_from_string(area, &s, tokens,
+ s = (const char *)data;
+ rs_md = routerstatus_parse_entry_from_string(area, &s, eos, tokens,
NULL, NULL, 26, FLAV_MICRODESC);
tor_assert(smartlist_len(tokens) == 0);
- s = str;
- rs_vote = routerstatus_parse_entry_from_string(area, &s, tokens,
+ s = (const char *)data;
+ rs_vote = routerstatus_parse_entry_from_string(area, &s, eos, tokens,
dummy_vote, vrs, 26, FLAV_NS);
tor_assert(smartlist_len(tokens) == 0);
@@ -82,6 +82,6 @@ fuzz_main(const uint8_t *data, size_t sz)
vote_routerstatus_free(vrs);
memarea_clear(area);
smartlist_free(tokens);
- tor_free(str);
+
return 0;
}
diff --git a/src/test/fuzz/fuzzing_common.c b/src/test/fuzz/fuzzing_common.c
index 1401e4c28d..21aa07cfe2 100644
--- a/src/test/fuzz/fuzzing_common.c
+++ b/src/test/fuzz/fuzzing_common.c
@@ -3,12 +3,14 @@
#define CRYPTO_ED25519_PRIVATE
#include "orconfig.h"
#include "core/or/or.h"
+#include "app/main/subsysmgr.h"
#include "lib/err/backtrace.h"
#include "app/config/config.h"
#include "test/fuzz/fuzzing.h"
#include "lib/compress/compress.h"
#include "lib/crypt_ops/crypto_ed25519.h"
#include "lib/crypt_ops/crypto_init.h"
+#include "lib/version/torversion.h"
static or_options_t *mock_options = NULL;
static const or_options_t *
@@ -94,12 +96,10 @@ disable_signature_checking(void)
static void
global_init(void)
{
- tor_threads_init();
- tor_compress_init();
+ subsystems_init_upto(SUBSYS_LEVEL_LIBS);
+ flush_log_messages_from_startup();
- /* Initialise logging first */
- init_logging(1);
- configure_backtrace_handler(get_version());
+ tor_compress_init();
if (crypto_global_init(0, NULL, NULL) < 0)
abort();
diff --git a/src/test/include.am b/src/test/include.am
index ecb7689579..e5eae56e25 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -181,6 +181,7 @@ src_test_test_SOURCES += \
src/test/test_x509.c \
src/test/test_helpers.c \
src/test/test_dns.c \
+ src/test/test_parsecommon.c \
src/test/testing_common.c \
src/test/testing_rsakeys.c \
src/ext/tinytest.c
diff --git a/src/test/test.c b/src/test/test.c
index 70d91e3967..17b736d305 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -847,8 +847,8 @@ struct testgroup_t testgroups[] = {
{ "circuitbuild/", circuitbuild_tests },
{ "circuitlist/", circuitlist_tests },
{ "circuitmux/", circuitmux_tests },
- { "circuituse/", circuituse_tests },
{ "circuitstats/", circuitstats_tests },
+ { "circuituse/", circuituse_tests },
{ "compat/libevent/", compat_libevent_tests },
{ "config/", config_tests },
{ "connection/", connection_tests },
@@ -865,34 +865,36 @@ struct testgroup_t testgroups[] = {
#endif
{ "crypto/pem/", pem_tests },
{ "dir/", dir_tests },
- { "dir_handle_get/", dir_handle_get_tests },
{ "dir/md/", microdesc_tests },
{ "dir/voting-schedule/", voting_schedule_tests },
+ { "dir_handle_get/", dir_handle_get_tests },
+ { "dns/", dns_tests },
{ "dos/", dos_tests },
{ "entryconn/", entryconn_tests },
{ "entrynodes/", entrynodes_tests },
- { "guardfraction/", guardfraction_tests },
{ "extorport/", extorport_tests },
{ "geoip/", geoip_tests },
- { "legacy_hs/", hs_tests },
+ { "guardfraction/", guardfraction_tests },
{ "hs_cache/", hs_cache },
{ "hs_cell/", hs_cell_tests },
+ { "hs_client/", hs_client_tests },
{ "hs_common/", hs_common_tests },
{ "hs_config/", hs_config_tests },
{ "hs_control/", hs_control_tests },
{ "hs_descriptor/", hs_descriptor },
+ { "hs_intropoint/", hs_intropoint_tests },
{ "hs_ntor/", hs_ntor_tests },
{ "hs_service/", hs_service_tests },
- { "hs_client/", hs_client_tests },
- { "hs_intropoint/", hs_intropoint_tests },
{ "introduce/", introduce_tests },
{ "keypin/", keypin_tests },
+ { "legacy_hs/", hs_tests },
{ "link-handshake/", link_handshake_tests },
{ "mainloop/", mainloop_tests },
{ "nodelist/", nodelist_tests },
{ "oom/", oom_tests },
{ "oos/", oos_tests },
{ "options/", options_tests },
+ { "parsecommon/", parsecommon_tests },
{ "periodic-event/" , periodic_event_tests },
{ "policy/" , policy_tests },
{ "procmon/", procmon_tests },
@@ -910,8 +912,8 @@ struct testgroup_t testgroups[] = {
{ "routerlist/", routerlist_tests },
{ "routerset/" , routerset_tests },
{ "scheduler/", scheduler_tests },
- { "socks/", socks_tests },
{ "shared-random/", sr_tests },
+ { "socks/", socks_tests },
{ "status/" , status_tests },
{ "storagedir/", storagedir_tests },
{ "tortls/", tortls_tests },
@@ -921,10 +923,9 @@ struct testgroup_t testgroups[] = {
{ "tortls/x509/", x509_tests },
{ "util/", util_tests },
{ "util/format/", util_format_tests },
+ { "util/handle/", handle_tests },
{ "util/logging/", logging_tests },
{ "util/process/", util_process_tests },
{ "util/thread/", thread_tests },
- { "util/handle/", handle_tests },
- { "dns/", dns_tests },
END_OF_GROUPS
};
diff --git a/src/test/test.h b/src/test/test.h
index a46fedf3e0..092356f0fb 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -177,11 +177,11 @@ extern const struct testcase_setup_t ed25519_test_setup;
extern struct testcase_t accounting_tests[];
extern struct testcase_t addr_tests[];
-extern struct testcase_t address_tests[];
extern struct testcase_t address_set_tests[];
+extern struct testcase_t address_tests[];
extern struct testcase_t bridges_tests[];
-extern struct testcase_t bwmgt_tests[];
extern struct testcase_t buffer_tests[];
+extern struct testcase_t bwmgt_tests[];
extern struct testcase_t cell_format_tests[];
extern struct testcase_t cell_queue_tests[];
extern struct testcase_t channel_tests[];
@@ -191,8 +191,8 @@ extern struct testcase_t checkdir_tests[];
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 circuituse_tests[];
extern struct testcase_t compat_libevent_tests[];
extern struct testcase_t config_tests[];
extern struct testcase_t connection_tests[];
@@ -200,30 +200,32 @@ extern struct testcase_t conscache_tests[];
extern struct testcase_t consdiff_tests[];
extern struct testcase_t consdiffmgr_tests[];
extern struct testcase_t container_tests[];
-extern struct testcase_t controller_tests[];
extern struct testcase_t controller_event_tests[];
-extern struct testcase_t crypto_tests[];
+extern struct testcase_t controller_tests[];
extern struct testcase_t crypto_ope_tests[];
extern struct testcase_t crypto_openssl_tests[];
-extern struct testcase_t dir_tests[];
+extern struct testcase_t crypto_tests[];
extern struct testcase_t dir_handle_get_tests[];
+extern struct testcase_t dir_tests[];
+extern struct testcase_t dns_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[];
extern struct testcase_t extorport_tests[];
extern struct testcase_t geoip_tests[];
-extern struct testcase_t hs_tests[];
+extern struct testcase_t guardfraction_tests[];
+extern struct testcase_t handle_tests[];
extern struct testcase_t hs_cache[];
extern struct testcase_t hs_cell_tests[];
+extern struct testcase_t hs_client_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_intropoint_tests[];
extern struct testcase_t hs_ntor_tests[];
extern struct testcase_t hs_service_tests[];
-extern struct testcase_t hs_client_tests[];
-extern struct testcase_t hs_intropoint_tests[];
+extern struct testcase_t hs_tests[];
extern struct testcase_t introduce_tests[];
extern struct testcase_t keypin_tests[];
extern struct testcase_t link_handshake_tests[];
@@ -234,6 +236,7 @@ extern struct testcase_t nodelist_tests[];
extern struct testcase_t oom_tests[];
extern struct testcase_t oos_tests[];
extern struct testcase_t options_tests[];
+extern struct testcase_t parsecommon_tests[];
extern struct testcase_t pem_tests[];
extern struct testcase_t periodic_event_tests[];
extern struct testcase_t policy_tests[];
@@ -252,19 +255,17 @@ extern struct testcase_t routerkeys_tests[];
extern struct testcase_t routerlist_tests[];
extern struct testcase_t routerset_tests[];
extern struct testcase_t scheduler_tests[];
-extern struct testcase_t storagedir_tests[];
extern struct testcase_t socks_tests[];
+extern struct testcase_t sr_tests[];
extern struct testcase_t status_tests[];
+extern struct testcase_t storagedir_tests[];
extern struct testcase_t thread_tests[];
-extern struct testcase_t tortls_tests[];
extern struct testcase_t tortls_openssl_tests[];
-extern struct testcase_t util_tests[];
+extern struct testcase_t tortls_tests[];
extern struct testcase_t util_format_tests[];
extern struct testcase_t util_process_tests[];
+extern struct testcase_t util_tests[];
extern struct testcase_t voting_schedule_tests[];
-extern struct testcase_t dns_tests[];
-extern struct testcase_t handle_tests[];
-extern struct testcase_t sr_tests[];
extern struct testcase_t x509_tests[];
extern struct testcase_t slow_crypto_tests[];
diff --git a/src/test/test_addr.c b/src/test/test_addr.c
index a9004048a5..1d97db52a6 100644
--- a/src/test/test_addr.c
+++ b/src/test/test_addr.c
@@ -723,7 +723,7 @@ test_addr_ip6_helpers(void *arg)
;
}
-/** Test tor_addr_port_parse(). */
+/** Test tor_addr_parse() and tor_addr_port_parse(). */
static void
test_addr_parse(void *arg)
{
@@ -734,6 +734,60 @@ test_addr_parse(void *arg)
/* Correct call. */
(void)arg;
+ r= tor_addr_parse(&addr, "192.0.2.1");
+ tt_int_op(r,OP_EQ, AF_INET);
+ tor_addr_to_str(buf, &addr, sizeof(buf), 0);
+ tt_str_op(buf,OP_EQ, "192.0.2.1");
+
+ r= tor_addr_parse(&addr, "11:22::33:44");
+ tt_int_op(r,OP_EQ, AF_INET6);
+ tor_addr_to_str(buf, &addr, sizeof(buf), 0);
+ tt_str_op(buf,OP_EQ, "11:22::33:44");
+
+ r= tor_addr_parse(&addr, "[11:22::33:44]");
+ tt_int_op(r,OP_EQ, AF_INET6);
+ tor_addr_to_str(buf, &addr, sizeof(buf), 0);
+ tt_str_op(buf,OP_EQ, "11:22::33:44");
+
+ r= tor_addr_parse(&addr, "11:22:33:44:55:66:1.2.3.4");
+ tt_int_op(r,OP_EQ, AF_INET6);
+ tor_addr_to_str(buf, &addr, sizeof(buf), 0);
+ tt_str_op(buf,OP_EQ, "11:22:33:44:55:66:102:304");
+
+ r= tor_addr_parse(&addr, "11:22::33:44:1.2.3.4");
+ tt_int_op(r,OP_EQ, AF_INET6);
+ tor_addr_to_str(buf, &addr, sizeof(buf), 0);
+ tt_str_op(buf,OP_EQ, "11:22::33:44:102:304");
+
+ /* Empty string. */
+ r= tor_addr_parse(&addr, "");
+ tt_int_op(r,OP_EQ, -1);
+
+ /* Square brackets around IPv4 address. */
+ r= tor_addr_parse(&addr, "[192.0.2.1]");
+ tt_int_op(r,OP_EQ, -1);
+
+ /* Only left square bracket. */
+ r= tor_addr_parse(&addr, "[11:22::33:44");
+ tt_int_op(r,OP_EQ, -1);
+
+ /* Only right square bracket. */
+ r= tor_addr_parse(&addr, "11:22::33:44]");
+ tt_int_op(r,OP_EQ, -1);
+
+ /* Leading colon. */
+ r= tor_addr_parse(&addr, ":11:22::33:44");
+ tt_int_op(r,OP_EQ, -1);
+
+ /* Trailing colon. */
+ r= tor_addr_parse(&addr, "11:22::33:44:");
+ tt_int_op(r,OP_EQ, -1);
+
+ /* Too many hex words in IPv4-mapped IPv6 address. */
+ r= tor_addr_parse(&addr, "11:22:33:44:55:66:77:88:1.2.3.4");
+ tt_int_op(r,OP_EQ, -1);
+
+ /* Correct call. */
r= tor_addr_port_parse(LOG_DEBUG,
"192.0.2.1:1234",
&addr, &port, -1);
diff --git a/src/test/test_address.c b/src/test/test_address.c
index e99220f838..a823fd9cd5 100644
--- a/src/test/test_address.c
+++ b/src/test/test_address.c
@@ -24,6 +24,8 @@
#endif /* defined(HAVE_IFCONF_TO_SMARTLIST) */
#include "core/or/or.h"
+#include "feature/nodelist/routerinfo_st.h"
+#include "feature/nodelist/node_st.h"
#include "feature/nodelist/nodelist.h"
#include "lib/net/address.h"
#include "test/test.h"
@@ -1170,6 +1172,78 @@ test_address_tor_addr_in_same_network_family(void *ignored)
return;
}
+static node_t *
+helper_create_mock_node(char id_char)
+{
+ node_t *node = tor_malloc_zero(sizeof(node_t));
+ routerinfo_t *ri = tor_malloc_zero(sizeof(routerinfo_t));
+ tor_addr_make_null(&ri->ipv6_addr, AF_INET6);
+ node->ri = ri;
+ memset(node->identity, id_char, sizeof(node->identity));
+ return node;
+}
+
+static void
+helper_free_mock_node(node_t *node)
+{
+ if (!node)
+ return;
+ tor_free(node->ri);
+ tor_free(node);
+}
+
+#define NODE_SET_IPV4(node, ipv4_addr, ipv4_port) { \
+ tor_addr_t addr; \
+ tor_addr_parse(&addr, ipv4_addr); \
+ node->ri->addr = tor_addr_to_ipv4h(&addr); \
+ node->ri->or_port = ipv4_port; \
+ }
+
+#define NODE_CLEAR_IPV4(node) { \
+ node->ri->addr = 0; \
+ node->ri->or_port = 0; \
+ }
+
+#define NODE_SET_IPV6(node, ipv6_addr_str, ipv6_port) { \
+ tor_addr_parse(&node->ri->ipv6_addr, ipv6_addr_str); \
+ node->ri->ipv6_orport = ipv6_port; \
+ }
+
+static void
+test_address_tor_node_in_same_network_family(void *ignored)
+{
+ (void)ignored;
+ node_t *node_a = helper_create_mock_node('a');
+ node_t *node_b = helper_create_mock_node('b');
+
+ NODE_SET_IPV4(node_a, "8.8.8.8", 1);
+ NODE_SET_IPV4(node_b, "8.8.4.4", 1);
+
+ tt_int_op(nodes_in_same_family(node_a, node_b), OP_EQ, 1);
+
+ NODE_SET_IPV4(node_a, "8.8.8.8", 1);
+ NODE_SET_IPV4(node_b, "1.1.1.1", 1);
+
+ tt_int_op(nodes_in_same_family(node_a, node_b), OP_EQ, 0);
+
+ NODE_CLEAR_IPV4(node_a);
+ NODE_SET_IPV6(node_a, "2001:470:20::2", 1);
+
+ tt_int_op(nodes_in_same_family(node_a, node_b), OP_EQ, 0);
+
+ NODE_CLEAR_IPV4(node_b);
+ NODE_SET_IPV6(node_b, "2606:4700:4700::1111", 1);
+
+ tt_int_op(nodes_in_same_family(node_a, node_b), OP_EQ, 0);
+
+ NODE_SET_IPV6(node_a, "2606:4700:4700::1001", 1);
+ tt_int_op(nodes_in_same_family(node_a, node_b), OP_EQ, 1);
+
+ done:
+ helper_free_mock_node(node_a);
+ helper_free_mock_node(node_b);
+}
+
#define ADDRESS_TEST(name, flags) \
{ #name, test_address_ ## name, flags, NULL, NULL }
@@ -1202,6 +1276,6 @@ struct testcase_t address_tests[] = {
ADDRESS_TEST(tor_addr_to_mapped_ipv4h, 0),
ADDRESS_TEST(tor_addr_eq_ipv4h, 0),
ADDRESS_TEST(tor_addr_in_same_network_family, 0),
+ ADDRESS_TEST(tor_node_in_same_network_family, 0),
END_OF_TESTCASES
};
-
diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c
index 02eadecd98..dd47ad7689 100644
--- a/src/test/test_circuitbuild.c
+++ b/src/test/test_circuitbuild.c
@@ -21,11 +21,11 @@ static smartlist_t dummy_nodes;
static extend_info_t dummy_ei;
static int
-mock_count_acceptable_nodes(smartlist_t *nodes)
+mock_count_acceptable_nodes(smartlist_t *nodes, int direct)
{
(void)nodes;
- return DEFAULT_ROUTE_LEN + 1;
+ return direct ? 1 : DEFAULT_ROUTE_LEN + 1;
}
/* Test route lengths when the caller of new_route_len() doesn't
diff --git a/src/test/test_compat_libevent.c b/src/test/test_compat_libevent.c
index 3f505d013b..ade76bdb07 100644
--- a/src/test/test_compat_libevent.c
+++ b/src/test/test_compat_libevent.c
@@ -187,4 +187,3 @@ struct testcase_t compat_libevent_tests[] = {
TT_FORK, NULL, NULL },
END_OF_TESTCASES
};
-
diff --git a/src/test/test_consdiff.c b/src/test/test_consdiff.c
index b836befd22..23e8f7167e 100644
--- a/src/test/test_consdiff.c
+++ b/src/test/test_consdiff.c
@@ -14,6 +14,39 @@
#define tt_str_eq_line(a,b) \
tt_assert(line_str_eq((b),(a)))
+static int
+consensus_split_lines_(smartlist_t *out, const char *s, memarea_t *area)
+{
+ size_t len = strlen(s);
+ return consensus_split_lines(out, s, len, area);
+}
+
+static int
+consensus_compute_digest_(const char *cons,
+ consensus_digest_t *digest_out)
+{
+ size_t len = strlen(cons);
+ char *tmp = tor_memdup(cons, len);
+ // We use memdup here to ensure that the input is NOT nul-terminated.
+ // This makes it likelier for us to spot bugs.
+ int r = consensus_compute_digest(tmp, len, digest_out);
+ tor_free(tmp);
+ return r;
+}
+
+static int
+consensus_compute_digest_as_signed_(const char *cons,
+ consensus_digest_t *digest_out)
+{
+ size_t len = strlen(cons);
+ char *tmp = tor_memdup(cons, len);
+ // We use memdup here to ensure that the input is NOT nul-terminated.
+ // This makes it likelier for us to spot bugs.
+ int r = consensus_compute_digest_as_signed(tmp, len, digest_out);
+ tor_free(tmp);
+ return r;
+}
+
static void
test_consdiff_smartlist_slice(void *arg)
{
@@ -58,7 +91,7 @@ test_consdiff_smartlist_slice_string_pos(void *arg)
/* Create a regular smartlist. */
(void)arg;
- consensus_split_lines(sl, "a\nd\nc\na\nb\n", area);
+ consensus_split_lines_(sl, "a\nd\nc\na\nb\n", area);
/* See that smartlist_slice_string_pos respects the bounds of the slice. */
sls = smartlist_slice(sl, 2, 5);
@@ -87,8 +120,8 @@ test_consdiff_lcs_lengths(void *arg)
int e_lengths2[] = { 0, 1, 1, 2, 3, 4 };
(void)arg;
- consensus_split_lines(sl1, "a\nb\nc\nd\ne\n", area);
- consensus_split_lines(sl2, "a\nc\nd\ni\ne\n", area);
+ consensus_split_lines_(sl1, "a\nb\nc\nd\ne\n", area);
+ consensus_split_lines_(sl2, "a\nc\nd\ni\ne\n", area);
sls1 = smartlist_slice(sl1, 0, -1);
sls2 = smartlist_slice(sl2, 0, -1);
@@ -119,10 +152,10 @@ test_consdiff_trim_slices(void *arg)
memarea_t *area = memarea_new();
(void)arg;
- consensus_split_lines(sl1, "a\nb\nb\nb\nd\n", area);
- consensus_split_lines(sl2, "a\nc\nc\nc\nd\n", area);
- consensus_split_lines(sl3, "a\nb\nb\nb\na\n", area);
- consensus_split_lines(sl4, "c\nb\nb\nb\nc\n", area);
+ consensus_split_lines_(sl1, "a\nb\nb\nb\nd\n", area);
+ consensus_split_lines_(sl2, "a\nc\nc\nc\nd\n", area);
+ consensus_split_lines_(sl3, "a\nb\nb\nb\na\n", area);
+ consensus_split_lines_(sl4, "c\nb\nb\nb\nc\n", area);
sls1 = smartlist_slice(sl1, 0, -1);
sls2 = smartlist_slice(sl2, 0, -1);
sls3 = smartlist_slice(sl3, 0, -1);
@@ -165,8 +198,8 @@ test_consdiff_set_changed(void *arg)
memarea_t *area = memarea_new();
(void)arg;
- consensus_split_lines(sl1, "a\nb\na\na\n", area);
- consensus_split_lines(sl2, "a\na\na\na\n", area);
+ consensus_split_lines_(sl1, "a\nb\na\na\n", area);
+ consensus_split_lines_(sl2, "a\na\na\na\n", area);
/* Length of sls1 is 0. */
sls1 = smartlist_slice(sl1, 0, 0);
@@ -240,8 +273,8 @@ test_consdiff_calc_changes(void *arg)
memarea_t *area = memarea_new();
(void)arg;
- consensus_split_lines(sl1, "a\na\na\na\n", area);
- consensus_split_lines(sl2, "a\na\na\na\n", area);
+ consensus_split_lines_(sl1, "a\na\na\na\n", area);
+ consensus_split_lines_(sl2, "a\na\na\na\n", area);
sls1 = smartlist_slice(sl1, 0, -1);
sls2 = smartlist_slice(sl2, 0, -1);
@@ -259,7 +292,7 @@ test_consdiff_calc_changes(void *arg)
tt_assert(!bitarray_is_set(changed2, 3));
smartlist_clear(sl2);
- consensus_split_lines(sl2, "a\nb\na\nb\n", area);
+ consensus_split_lines_(sl2, "a\nb\na\nb\n", area);
tor_free(sls1);
tor_free(sls2);
sls1 = smartlist_slice(sl1, 0, -1);
@@ -282,7 +315,7 @@ test_consdiff_calc_changes(void *arg)
bitarray_clear(changed1, 3);
smartlist_clear(sl2);
- consensus_split_lines(sl2, "b\nb\nb\nb\n", area);
+ consensus_split_lines_(sl2, "b\nb\nb\nb\n", area);
tor_free(sls1);
tor_free(sls2);
sls1 = smartlist_slice(sl1, 0, -1);
@@ -610,8 +643,8 @@ test_consdiff_gen_ed_diff(void *arg)
/* Test 'a', 'c' and 'd' together. See that it is done in reverse order. */
smartlist_clear(cons1);
smartlist_clear(cons2);
- consensus_split_lines(cons1, "A\nB\nC\nD\nE\n", area);
- consensus_split_lines(cons2, "A\nC\nO\nE\nU\n", area);
+ consensus_split_lines_(cons1, "A\nB\nC\nD\nE\n", area);
+ consensus_split_lines_(cons2, "A\nC\nO\nE\nU\n", area);
diff = gen_ed_diff(cons1, cons2, area);
tt_ptr_op(NULL, OP_NE, diff);
tt_int_op(7, OP_EQ, smartlist_len(diff));
@@ -627,8 +660,8 @@ test_consdiff_gen_ed_diff(void *arg)
smartlist_clear(cons1);
smartlist_clear(cons2);
- consensus_split_lines(cons1, "B\n", area);
- consensus_split_lines(cons2, "A\nB\n", area);
+ consensus_split_lines_(cons1, "B\n", area);
+ consensus_split_lines_(cons2, "A\nB\n", area);
diff = gen_ed_diff(cons1, cons2, area);
tt_ptr_op(NULL, OP_NE, diff);
tt_int_op(3, OP_EQ, smartlist_len(diff));
@@ -656,7 +689,7 @@ test_consdiff_apply_ed_diff(void *arg)
diff = smartlist_new();
setup_capture_of_logs(LOG_WARN);
- consensus_split_lines(cons1, "A\nB\nC\nD\nE\n", area);
+ consensus_split_lines_(cons1, "A\nB\nC\nD\nE\n", area);
/* Command without range. */
smartlist_add_linecpy(diff, area, "a");
@@ -829,7 +862,7 @@ test_consdiff_apply_ed_diff(void *arg)
smartlist_clear(diff);
/* Test appending text, 'a'. */
- consensus_split_lines(diff, "3a\nU\nO\n.\n0a\nV\n.\n", area);
+ consensus_split_lines_(diff, "3a\nU\nO\n.\n0a\nV\n.\n", area);
cons2 = apply_ed_diff(cons1, diff, 0);
tt_ptr_op(NULL, OP_NE, cons2);
tt_int_op(8, OP_EQ, smartlist_len(cons2));
@@ -846,7 +879,7 @@ test_consdiff_apply_ed_diff(void *arg)
smartlist_free(cons2);
/* Test deleting text, 'd'. */
- consensus_split_lines(diff, "4d\n1,2d\n", area);
+ consensus_split_lines_(diff, "4d\n1,2d\n", area);
cons2 = apply_ed_diff(cons1, diff, 0);
tt_ptr_op(NULL, OP_NE, cons2);
tt_int_op(2, OP_EQ, smartlist_len(cons2));
@@ -857,7 +890,7 @@ test_consdiff_apply_ed_diff(void *arg)
smartlist_free(cons2);
/* Test changing text, 'c'. */
- consensus_split_lines(diff, "4c\nT\nX\n.\n1,2c\nM\n.\n", area);
+ consensus_split_lines_(diff, "4c\nT\nX\n.\n1,2c\nM\n.\n", area);
cons2 = apply_ed_diff(cons1, diff, 0);
tt_ptr_op(NULL, OP_NE, cons2);
tt_int_op(5, OP_EQ, smartlist_len(cons2));
@@ -871,7 +904,7 @@ test_consdiff_apply_ed_diff(void *arg)
smartlist_free(cons2);
/* Test 'a', 'd' and 'c' together. */
- consensus_split_lines(diff, "4c\nT\nX\n.\n2d\n0a\nM\n.\n", area);
+ consensus_split_lines_(diff, "4c\nT\nX\n.\n2d\n0a\nM\n.\n", area);
cons2 = apply_ed_diff(cons1, diff, 0);
tt_ptr_op(NULL, OP_NE, cons2);
tt_int_op(6, OP_EQ, smartlist_len(cons2));
@@ -918,12 +951,12 @@ test_consdiff_gen_diff(void *arg)
);
tt_int_op(0, OP_EQ,
- consensus_compute_digest_as_signed(cons1_str, &digests1));
+ consensus_compute_digest_as_signed_(cons1_str, &digests1));
tt_int_op(0, OP_EQ,
- consensus_compute_digest(cons2_str, &digests2));
+ consensus_compute_digest_(cons2_str, &digests2));
- consensus_split_lines(cons1, cons1_str, area);
- consensus_split_lines(cons2, cons2_str, area);
+ consensus_split_lines_(cons1, cons1_str, area);
+ consensus_split_lines_(cons2, cons2_str, area);
diff = consdiff_gen_diff(cons1, cons2, &digests1, &digests2, area);
tt_ptr_op(NULL, OP_EQ, diff);
@@ -937,9 +970,9 @@ test_consdiff_gen_diff(void *arg)
"directory-signature foo bar\nbar\n"
);
tt_int_op(0, OP_EQ,
- consensus_compute_digest_as_signed(cons1_str, &digests1));
+ consensus_compute_digest_as_signed_(cons1_str, &digests1));
smartlist_clear(cons1);
- consensus_split_lines(cons1, cons1_str, area);
+ consensus_split_lines_(cons1, cons1_str, area);
diff = consdiff_gen_diff(cons1, cons2, &digests1, &digests2, area);
tt_ptr_op(NULL, OP_NE, diff);
tt_int_op(11, OP_EQ, smartlist_len(diff));
@@ -991,8 +1024,8 @@ test_consdiff_apply_diff(void *arg)
"directory-signature foo bar\nbar\n"
);
tt_int_op(0, OP_EQ,
- consensus_compute_digest(cons1_str, &digests1));
- consensus_split_lines(cons1, cons1_str, area);
+ consensus_compute_digest_(cons1_str, &digests1));
+ consensus_split_lines_(cons1, cons1_str, area);
/* diff doesn't have enough lines. */
cons2 = consdiff_apply_diff(cons1, diff, &digests1);
@@ -1182,4 +1215,3 @@ struct testcase_t consdiff_tests[] = {
CONSDIFF_LEGACY(apply_diff),
END_OF_TESTCASES
};
-
diff --git a/src/test/test_consdiffmgr.c b/src/test/test_consdiffmgr.c
index 6c451da685..b84753ff83 100644
--- a/src/test/test_consdiffmgr.c
+++ b/src/test/test_consdiffmgr.c
@@ -21,6 +21,23 @@
#include "test/test.h"
#include "test/log_test_helpers.h"
+#define consdiffmgr_add_consensus consdiffmgr_add_consensus_nulterm
+
+static char *
+consensus_diff_apply_(const char *c, const char *d)
+{
+ size_t c_len = strlen(c);
+ size_t d_len = strlen(d);
+ // We use memdup here to ensure that the input is NOT nul-terminated.
+ // This makes it likelier for us to spot bugs.
+ char *c_tmp = tor_memdup(c, c_len);
+ char *d_tmp = tor_memdup(d, d_len);
+ char *result = consensus_diff_apply(c_tmp, c_len, d_tmp, d_len);
+ tor_free(c_tmp);
+ tor_free(d_tmp);
+ return result;
+}
+
// ============================== Setup/teardown the consdiffmgr
// These functions get run before/after each test in this module
@@ -153,7 +170,8 @@ lookup_diff_from(consensus_cache_entry_t **out,
const char *str1)
{
uint8_t digest[DIGEST256_LEN];
- if (router_get_networkstatus_v3_sha3_as_signed(digest, str1)<0) {
+ if (router_get_networkstatus_v3_sha3_as_signed(digest,
+ str1, strlen(str1))<0) {
TT_FAIL(("Unable to compute sha3-as-signed"));
return CONSDIFF_NOT_FOUND;
}
@@ -175,14 +193,15 @@ lookup_apply_and_verify_diff(consensus_flavor_t flav,
consensus_cache_entry_incref(ent);
size_t size;
- char *diff_string = NULL;
- int r = uncompress_or_copy(&diff_string, &size, ent);
+ const char *diff_string = NULL;
+ char *diff_owned = NULL;
+ int r = uncompress_or_set_ptr(&diff_string, &size, &diff_owned, ent);
consensus_cache_entry_decref(ent);
if (diff_string == NULL || r < 0)
return -1;
- char *applied = consensus_diff_apply(str1, diff_string);
- tor_free(diff_string);
+ char *applied = consensus_diff_apply(str1, strlen(str1), diff_string, size);
+ tor_free(diff_owned);
if (applied == NULL)
return -1;
@@ -282,7 +301,8 @@ test_consdiffmgr_add(void *arg)
(void) arg;
time_t now = approx_time();
- char *body = NULL;
+ const char *body = NULL;
+ char *body_owned = NULL;
consensus_cache_entry_t *ent = NULL;
networkstatus_t *ns_tmp = fake_ns_new(FLAV_NS, now);
@@ -324,7 +344,7 @@ test_consdiffmgr_add(void *arg)
tt_assert(ent);
consensus_cache_entry_incref(ent);
size_t s;
- r = uncompress_or_copy(&body, &s, ent);
+ r = uncompress_or_set_ptr(&body, &s, &body_owned, ent);
tt_int_op(r, OP_EQ, 0);
tt_int_op(s, OP_EQ, 4);
tt_mem_op(body, OP_EQ, "quux", 4);
@@ -337,7 +357,7 @@ test_consdiffmgr_add(void *arg)
networkstatus_vote_free(ns_tmp);
teardown_capture_of_logs();
consensus_cache_entry_decref(ent);
- tor_free(body);
+ tor_free(body_owned);
}
static void
@@ -370,7 +390,8 @@ test_consdiffmgr_make_diffs(void *arg)
ns = fake_ns_new(FLAV_MICRODESC, now-3600);
md_ns_body = fake_ns_body_new(FLAV_MICRODESC, now-3600);
r = consdiffmgr_add_consensus(md_ns_body, ns);
- router_get_networkstatus_v3_sha3_as_signed(md_ns_sha3, md_ns_body);
+ router_get_networkstatus_v3_sha3_as_signed(md_ns_sha3, md_ns_body,
+ strlen(md_ns_body));
networkstatus_vote_free(ns);
tt_int_op(r, OP_EQ, 0);
@@ -414,7 +435,7 @@ test_consdiffmgr_make_diffs(void *arg)
r = consensus_cache_entry_get_body(diff, &diff_body, &diff_size);
tt_int_op(r, OP_EQ, 0);
diff_text = tor_memdup_nulterm(diff_body, diff_size);
- applied = consensus_diff_apply(md_ns_body, diff_text);
+ applied = consensus_diff_apply_(md_ns_body, diff_text);
tt_assert(applied);
tt_str_op(applied, OP_EQ, md_ns_body_2);
diff --git a/src/test/test_dir.c b/src/test/test_dir.c
index c18aa99fea..26ba269abd 100644
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@ -94,6 +94,23 @@
#define NS_MODULE dir
+static networkstatus_t *
+networkstatus_parse_vote_from_string_(const char *s,
+ const char **eos_out,
+ enum networkstatus_type_t ns_type)
+{
+ size_t len = strlen(s);
+ // memdup so that it won't be nul-terminated.
+ char *tmp = tor_memdup(s, len);
+ networkstatus_t *result =
+ networkstatus_parse_vote_from_string(tmp, len, eos_out, ns_type);
+ if (eos_out && *eos_out) {
+ *eos_out = s + (*eos_out - tmp);
+ }
+ tor_free(tmp);
+ return result;
+}
+
static void
test_dir_nicknames(void *arg)
{
@@ -2806,11 +2823,17 @@ test_a_networkstatus(
MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
/* Parse certificates and keys. */
- cert1 = mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ cert1 = mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1,
+ strlen(AUTHORITY_CERT_1),
+ NULL);
tt_assert(cert1);
- cert2 = authority_cert_parse_from_string(AUTHORITY_CERT_2, NULL);
+ cert2 = authority_cert_parse_from_string(AUTHORITY_CERT_2,
+ strlen(AUTHORITY_CERT_2),
+ NULL);
tt_assert(cert2);
- cert3 = authority_cert_parse_from_string(AUTHORITY_CERT_3, NULL);
+ cert3 = authority_cert_parse_from_string(AUTHORITY_CERT_3,
+ strlen(AUTHORITY_CERT_3),
+ NULL);
tt_assert(cert3);
sign_skey_1 = crypto_pk_new();
sign_skey_2 = crypto_pk_new();
@@ -2912,7 +2935,7 @@ test_a_networkstatus(
sign_skey_leg1,
FLAV_NS);
tt_assert(consensus_text);
- con = networkstatus_parse_vote_from_string(consensus_text, NULL,
+ con = networkstatus_parse_vote_from_string_(consensus_text, NULL,
NS_TYPE_CONSENSUS);
tt_assert(con);
//log_notice(LD_GENERAL, "<<%s>>\n<<%s>>\n<<%s>>\n",
@@ -2924,7 +2947,7 @@ test_a_networkstatus(
sign_skey_leg1,
FLAV_MICRODESC);
tt_assert(consensus_text_md);
- con_md = networkstatus_parse_vote_from_string(consensus_text_md, NULL,
+ con_md = networkstatus_parse_vote_from_string_(consensus_text_md, NULL,
NS_TYPE_CONSENSUS);
tt_assert(con_md);
tt_int_op(con_md->flavor,OP_EQ, FLAV_MICRODESC);
@@ -3023,13 +3046,13 @@ test_a_networkstatus(
tt_assert(consensus_text3);
tt_assert(consensus_text_md2);
tt_assert(consensus_text_md3);
- con2 = networkstatus_parse_vote_from_string(consensus_text2, NULL,
+ con2 = networkstatus_parse_vote_from_string_(consensus_text2, NULL,
NS_TYPE_CONSENSUS);
- con3 = networkstatus_parse_vote_from_string(consensus_text3, NULL,
+ con3 = networkstatus_parse_vote_from_string_(consensus_text3, NULL,
NS_TYPE_CONSENSUS);
- con_md2 = networkstatus_parse_vote_from_string(consensus_text_md2, NULL,
+ con_md2 = networkstatus_parse_vote_from_string_(consensus_text_md2, NULL,
NS_TYPE_CONSENSUS);
- con_md3 = networkstatus_parse_vote_from_string(consensus_text_md3, NULL,
+ con_md3 = networkstatus_parse_vote_from_string_(consensus_text_md3, NULL,
NS_TYPE_CONSENSUS);
tt_assert(con2);
tt_assert(con3);
@@ -6044,9 +6067,10 @@ test_dir_assumed_flags(void *arg)
"192.168.0.1 9001 0\n"
"m thisoneislongerbecauseitisa256bitmddigest33\n"
"s Fast Guard Stable\n";
+ const char *eos = str1 + strlen(str1);
const char *cp = str1;
- rs = routerstatus_parse_entry_from_string(area, &cp, tokens, NULL, NULL,
+ rs = routerstatus_parse_entry_from_string(area, &cp, eos, tokens, NULL, NULL,
24, FLAV_MICRODESC);
tt_assert(rs);
tt_assert(rs->is_flagged_running);
diff --git a/src/test/test_dir_common.c b/src/test/test_dir_common.c
index 86d2838944..eadeb11921 100644
--- a/src/test/test_dir_common.c
+++ b/src/test/test_dir_common.c
@@ -42,14 +42,20 @@ dir_common_authority_pk_init(authority_cert_t **cert1,
{
/* Parse certificates and keys. */
authority_cert_t *cert;
- cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ cert = authority_cert_parse_from_string(AUTHORITY_CERT_1,
+ strlen(AUTHORITY_CERT_1),
+ NULL);
tt_assert(cert);
tt_assert(cert->identity_key);
*cert1 = cert;
tt_assert(*cert1);
- *cert2 = authority_cert_parse_from_string(AUTHORITY_CERT_2, NULL);
+ *cert2 = authority_cert_parse_from_string(AUTHORITY_CERT_2,
+ strlen(AUTHORITY_CERT_2),
+ NULL);
tt_assert(*cert2);
- *cert3 = authority_cert_parse_from_string(AUTHORITY_CERT_3, NULL);
+ *cert3 = authority_cert_parse_from_string(AUTHORITY_CERT_3,
+ strlen(AUTHORITY_CERT_3),
+ NULL);
tt_assert(*cert3);
*sign_skey_1 = crypto_pk_new();
*sign_skey_2 = crypto_pk_new();
@@ -266,7 +272,9 @@ dir_common_add_rs_and_parse(networkstatus_t *vote, networkstatus_t **vote_out,
/* dump the vote and try to parse it. */
v_text = format_networkstatus_vote(sign_skey, vote);
tt_assert(v_text);
- *vote_out = networkstatus_parse_vote_from_string(v_text, NULL, NS_TYPE_VOTE);
+ *vote_out = networkstatus_parse_vote_from_string(v_text,
+ strlen(v_text),
+ NULL, NS_TYPE_VOTE);
done:
if (v_text)
@@ -424,4 +432,3 @@ dir_common_construct_vote_3(networkstatus_t **vote, authority_cert_t *cert,
return 0;
}
-
diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c
index 1d4a36d7fc..2ce98769af 100644
--- a/src/test/test_dir_handle_get.c
+++ b/src/test/test_dir_handle_get.c
@@ -72,6 +72,8 @@ ENABLE_GCC_WARNING(overlength-strings)
#define NOT_ENOUGH_CONSENSUS_SIGNATURES "HTTP/1.0 404 " \
"Consensus not signed by sufficient number of requested authorities\r\n\r\n"
+#define consdiffmgr_add_consensus consdiffmgr_add_consensus_nulterm
+
static dir_connection_t *
new_dir_conn(void)
{
@@ -1275,7 +1277,9 @@ test_dir_handle_get_server_keys_authority(void* data)
size_t body_used = 0;
(void) data;
- mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+ mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE,
+ strlen(TEST_CERTIFICATE),
+ NULL);
MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
@@ -1425,7 +1429,9 @@ test_dir_handle_get_server_keys_sk(void* data)
size_t body_used = 0;
(void) data;
- mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+ mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE,
+ strlen(TEST_CERTIFICATE),
+ NULL);
MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
@@ -2393,7 +2399,9 @@ test_dir_handle_get_status_vote_next_authority(void* data)
routerlist_free_all();
dirvote_free_all();
- mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+ mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE,
+ strlen(TEST_CERTIFICATE),
+ NULL);
/* create a trusted ds */
ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest,
@@ -2471,7 +2479,9 @@ test_dir_handle_get_status_vote_current_authority(void* data)
routerlist_free_all();
dirvote_free_all();
- mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+ mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE,
+ strlen(TEST_CERTIFICATE),
+ NULL);
/* create a trusted ds */
ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest,
diff --git a/src/test/test_dns.c b/src/test/test_dns.c
index 8369f844f6..ea0fcf8c5e 100644
--- a/src/test/test_dns.c
+++ b/src/test/test_dns.c
@@ -1,6 +1,7 @@
/* Copyright (c) 2015-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+#include "orconfig.h"
#include "core/or/or.h"
#include "test/test.h"
@@ -13,9 +14,71 @@
#include "core/or/edge_connection_st.h"
#include "core/or/or_circuit_st.h"
+#include "app/config/or_options_st.h"
+#include "app/config/config.h"
+
+#include <event2/event.h>
+#include <event2/dns.h>
#define NS_MODULE dns
+#ifdef HAVE_EVDNS_BASE_GET_NAMESERVER_ADDR
+#define NS_SUBMODULE configure_nameservers_fallback
+
+static or_options_t options = {
+ .ORPort_set = 1,
+};
+
+static const or_options_t *
+mock_get_options(void)
+{
+ return &options;
+}
+
+static void
+NS(test_main)(void *arg)
+{
+ (void)arg;
+ tor_addr_t *nameserver_addr = NULL;
+
+ MOCK(get_options, mock_get_options);
+
+ options.ServerDNSResolvConfFile = (char *)"no_such_file!!!";
+
+ dns_init(); // calls configure_nameservers()
+
+ tt_int_op(number_of_configured_nameservers(), OP_EQ, 1);
+
+ nameserver_addr = configured_nameserver_address(0);
+
+ tt_assert(tor_addr_family(nameserver_addr) == AF_INET);
+ tt_assert(tor_addr_eq_ipv4h(nameserver_addr, 0x7f000001));
+
+#ifndef _WIN32
+ tor_free(nameserver_addr);
+
+ options.ServerDNSResolvConfFile = (char *)"/dev/null";
+
+ dns_init();
+
+ tt_int_op(number_of_configured_nameservers(), OP_EQ, 1);
+
+ nameserver_addr = configured_nameserver_address(0);
+
+ tt_assert(tor_addr_family(nameserver_addr) == AF_INET);
+ tt_assert(tor_addr_eq_ipv4h(nameserver_addr, 0x7f000001));
+#endif
+
+ UNMOCK(get_options);
+
+ done:
+ tor_free(nameserver_addr);
+ return;
+}
+
+#undef NS_SUBMODULE
+#endif
+
#define NS_SUBMODULE clip_ttl
static void
@@ -736,6 +799,9 @@ NS(test_main)(void *arg)
#undef NS_SUBMODULE
struct testcase_t dns_tests[] = {
+#ifdef HAVE_EVDNS_BASE_GET_NAMESERVER_ADDR
+ TEST_CASE(configure_nameservers_fallback),
+#endif
TEST_CASE(clip_ttl),
TEST_CASE(resolve),
TEST_CASE_ASPECT(resolve_impl, addr_is_ip_no_need_to_resolve),
diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c
index 52b87a84a8..16b14c0b73 100644
--- a/src/test/test_entrynodes.c
+++ b/src/test/test_entrynodes.c
@@ -2847,13 +2847,16 @@ test_entry_guard_outdated_dirserver_exclusion(void *arg)
digests, 3, 7, 0);
/* ... and check that because we failed to fetch microdescs from all our
- * primaries, we didnt end up selecting a primary for fetching dir info */
+ * primaries, we didn't end up selecting a primary for fetching dir info */
expect_log_msg_containing("No primary or confirmed guards available.");
teardown_capture_of_logs();
}
done:
+ UNMOCK(networkstatus_get_latest_consensus_by_flavor);
+ UNMOCK(directory_initiate_request);
smartlist_free(digests);
+ tor_free(mock_ns_val);
tor_free(args);
if (conn) {
tor_free(conn->requested_resource);
diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c
index 95f7ed14ba..6198573f22 100644
--- a/src/test/test_hs_common.c
+++ b/src/test/test_hs_common.c
@@ -630,7 +630,7 @@ test_disaster_srv(void *arg)
get_disaster_srv(1, srv_one);
get_disaster_srv(2, srv_two);
- /* Check that the cached ones where updated */
+ /* Check that the cached ones were updated */
tt_mem_op(cached_disaster_srv_one, OP_EQ, srv_one, DIGEST256_LEN);
tt_mem_op(cached_disaster_srv_two, OP_EQ, srv_two, DIGEST256_LEN);
diff --git a/src/test/test_mainloop.c b/src/test/test_mainloop.c
index 92ce2e9918..8dfd5f619a 100644
--- a/src/test/test_mainloop.c
+++ b/src/test/test_mainloop.c
@@ -6,11 +6,20 @@
* \brief Tests for functions closely related to the Tor main loop
*/
+#define CONFIG_PRIVATE
+#define MAINLOOP_PRIVATE
+
#include "test/test.h"
#include "test/log_test_helpers.h"
#include "core/or/or.h"
+#include "core/mainloop/connection.h"
#include "core/mainloop/mainloop.h"
+#include "core/mainloop/netstatus.h"
+
+#include "feature/hs/hs_service.h"
+
+#include "app/config/config.h"
static const uint64_t BILLION = 1000000000;
@@ -131,12 +140,146 @@ test_mainloop_update_time_jumps(void *arg)
monotime_disable_test_mocking();
}
+static int schedule_rescan_called = 0;
+static void
+mock_schedule_rescan_periodic_events(void)
+{
+ ++schedule_rescan_called;
+}
+
+static void
+test_mainloop_user_activity(void *arg)
+{
+ (void)arg;
+ const time_t start = 1542658829;
+ update_approx_time(start);
+
+ MOCK(schedule_rescan_periodic_events, mock_schedule_rescan_periodic_events);
+
+ reset_user_activity(start);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start);
+
+ set_network_participation(false);
+
+ // reset can move backwards and forwards, but does not change network
+ // participation.
+ reset_user_activity(start-10);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start-10);
+ reset_user_activity(start+10);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start+10);
+
+ tt_int_op(schedule_rescan_called, OP_EQ, 0);
+ tt_int_op(false, OP_EQ, is_participating_on_network());
+
+ // "note" can only move forward. Calling it from a non-participating
+ // state makes us rescan the periodic callbacks and set participation.
+ note_user_activity(start+20);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start+20);
+ tt_int_op(true, OP_EQ, is_participating_on_network());
+ tt_int_op(schedule_rescan_called, OP_EQ, 1);
+
+ // Calling it again will move us forward, but not call rescan again.
+ note_user_activity(start+25);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start+25);
+ tt_int_op(true, OP_EQ, is_participating_on_network());
+ tt_int_op(schedule_rescan_called, OP_EQ, 1);
+
+ // We won't move backwards.
+ note_user_activity(start+20);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start+25);
+ tt_int_op(true, OP_EQ, is_participating_on_network());
+ tt_int_op(schedule_rescan_called, OP_EQ, 1);
+
+ done:
+ UNMOCK(schedule_rescan_periodic_events);
+}
+
+static unsigned int
+mock_get_num_services(void)
+{
+ return 1;
+}
+
+static connection_t *
+mock_connection_gbtu(int type)
+{
+ (void) type;
+ return (void *)"hello fellow connections";
+}
+
+static void
+test_mainloop_check_participation(void *arg)
+{
+ (void)arg;
+ or_options_t *options = options_new();
+ const time_t start = 1542658829;
+ const time_t ONE_DAY = 24*60*60;
+
+ // Suppose we've been idle for a day or two
+ reset_user_activity(start - 2*ONE_DAY);
+ set_network_participation(true);
+ check_network_participation_callback(start, options);
+ tt_int_op(is_participating_on_network(), OP_EQ, false);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start-2*ONE_DAY);
+
+ // suppose we've been idle for 2 days... but we are a server.
+ reset_user_activity(start - 2*ONE_DAY);
+ options->ORPort_set = 1;
+ set_network_participation(true);
+ check_network_participation_callback(start+2, options);
+ tt_int_op(is_participating_on_network(), OP_EQ, true);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start+2);
+ options->ORPort_set = 0;
+
+ // idle for 2 days, but we have a hidden service.
+ reset_user_activity(start - 2*ONE_DAY);
+ set_network_participation(true);
+ MOCK(hs_service_get_num_services, mock_get_num_services);
+ check_network_participation_callback(start+3, options);
+ tt_int_op(is_participating_on_network(), OP_EQ, true);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start+3);
+ UNMOCK(hs_service_get_num_services);
+
+ // idle for 2 days but we have at least one user connection
+ MOCK(connection_get_by_type_nonlinked, mock_connection_gbtu);
+ reset_user_activity(start - 2*ONE_DAY);
+ set_network_participation(true);
+ options->DormantTimeoutDisabledByIdleStreams = 1;
+ check_network_participation_callback(start+10, options);
+ tt_int_op(is_participating_on_network(), OP_EQ, true);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start+10);
+
+ // as above, but DormantTimeoutDisabledByIdleStreams is not set
+ reset_user_activity(start - 2*ONE_DAY);
+ set_network_participation(true);
+ options->DormantTimeoutDisabledByIdleStreams = 0;
+ check_network_participation_callback(start+13, options);
+ tt_int_op(is_participating_on_network(), OP_EQ, false);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start-2*ONE_DAY);
+ UNMOCK(connection_get_by_type_nonlinked);
+ options->DormantTimeoutDisabledByIdleStreams = 1;
+
+ // idle for 2 days but DormantClientTimeout is 3 days
+ reset_user_activity(start - 2*ONE_DAY);
+ set_network_participation(true);
+ options->DormantClientTimeout = ONE_DAY * 3;
+ check_network_participation_callback(start+30, options);
+ tt_int_op(is_participating_on_network(), OP_EQ, true);
+ tt_i64_op(get_last_user_activity_time(), OP_EQ, start-2*ONE_DAY);
+
+ done:
+ or_options_free(options);
+ UNMOCK(hs_service_get_num_services);
+ UNMOCK(connection_get_by_type_nonlinked);
+}
+
#define MAINLOOP_TEST(name) \
{ #name, test_mainloop_## name , TT_FORK, NULL, NULL }
struct testcase_t mainloop_tests[] = {
MAINLOOP_TEST(update_time_normal),
MAINLOOP_TEST(update_time_jumps),
+ MAINLOOP_TEST(user_activity),
+ MAINLOOP_TEST(check_participation),
END_OF_TESTCASES
};
-
diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c
index 8ede2690ec..3318408d53 100644
--- a/src/test/test_microdesc.c
+++ b/src/test/test_microdesc.c
@@ -11,6 +11,7 @@
#include "feature/dirparse/routerparse.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/nodefamily.h"
#include "feature/nodelist/routerlist.h"
#include "feature/nodelist/torcert.h"
@@ -70,6 +71,7 @@ test_md_cache(void *data)
const char *test_md3_noannotation = strchr(test_md3, '\n')+1;
time_t time1, time2, time3;
char *fn = NULL, *s = NULL;
+ char *encoded_family = NULL;
(void)data;
options = get_options_mutable();
@@ -172,8 +174,9 @@ test_md_cache(void *data)
tt_ptr_op(md1->family, OP_EQ, NULL);
tt_ptr_op(md3->family, OP_NE, NULL);
- tt_int_op(smartlist_len(md3->family), OP_EQ, 3);
- tt_str_op(smartlist_get(md3->family, 0), OP_EQ, "nodeX");
+
+ encoded_family = nodefamily_format(md3->family);
+ tt_str_op(encoded_family, OP_EQ, "nodeX nodeY nodeZ");
/* Now rebuild the cache! */
tt_int_op(microdesc_cache_rebuild(mc, 1), OP_EQ, 0);
@@ -254,6 +257,7 @@ test_md_cache(void *data)
smartlist_free(wanted);
tor_free(s);
tor_free(fn);
+ tor_free(encoded_family);
}
static const char truncated_md[] =
diff --git a/src/test/test_nodelist.c b/src/test/test_nodelist.c
index 1af6db68ec..0287be3305 100644
--- a/src/test/test_nodelist.c
+++ b/src/test/test_nodelist.c
@@ -6,15 +6,19 @@
* \brief Unit tests for nodelist related functions.
**/
+#define NODELIST_PRIVATE
+
#include "core/or/or.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/nodefamily.h"
#include "feature/nodelist/nodelist.h"
#include "feature/nodelist/torcert.h"
#include "feature/nodelist/microdesc_st.h"
#include "feature/nodelist/networkstatus_st.h"
#include "feature/nodelist/node_st.h"
+#include "feature/nodelist/nodefamily_st.h"
#include "feature/nodelist/routerinfo_st.h"
#include "feature/nodelist/routerstatus_st.h"
@@ -231,6 +235,381 @@ test_nodelist_ed_id(void *arg)
#undef N_NODES
}
+static void
+test_nodelist_nodefamily(void *arg)
+{
+ (void)arg;
+ /* hex ID digests */
+ const char h1[] = "5B435D6869206861206C65207363617270652070";
+ const char h2[] = "75C3B220616E6461726520696E206769726F2061";
+ const char h3[] = "2074726F766172206461206D616E67696172652C";
+ const char h4[] = "206D656E747265206E6F6E2076616C65206C2769";
+ const char h5[] = "6E766572736F2E202D2D5072696D6F204C657669";
+
+ /* binary ID digests */
+ uint8_t d1[DIGEST_LEN], d2[DIGEST_LEN], d3[DIGEST_LEN], d4[DIGEST_LEN],
+ d5[DIGEST_LEN];
+ base16_decode((char*)d1, sizeof(d1), h1, strlen(h1));
+ base16_decode((char*)d2, sizeof(d2), h2, strlen(h2));
+ base16_decode((char*)d3, sizeof(d3), h3, strlen(h3));
+ base16_decode((char*)d4, sizeof(d4), h4, strlen(h4));
+ base16_decode((char*)d5, sizeof(d5), h5, strlen(h5));
+
+ char *enc=NULL, *enc2=NULL;
+
+ nodefamily_t *nf1 = NULL;
+ nodefamily_t *nf2 = NULL;
+ nodefamily_t *nf3 = NULL;
+
+ enc = nodefamily_format(NULL);
+ tt_str_op(enc, OP_EQ, "");
+ tor_free(enc);
+
+ /* Make sure that sorting and de-duplication work. */
+ tor_asprintf(&enc, "$%s hello", h1);
+ nf1 = nodefamily_parse(enc, NULL, 0);
+ tt_assert(nf1);
+ tor_free(enc);
+
+ tor_asprintf(&enc, "hello hello $%s hello", h1);
+ nf2 = nodefamily_parse(enc, NULL, 0);
+ tt_assert(nf2);
+ tt_ptr_op(nf1, OP_EQ, nf2);
+ tor_free(enc);
+
+ tor_asprintf(&enc, "%s $%s hello", h1, h1);
+ nf3 = nodefamily_parse(enc, NULL, 0);
+ tt_assert(nf3);
+ tt_ptr_op(nf1, OP_EQ, nf3);
+ tor_free(enc);
+
+ tt_assert(nodefamily_contains_rsa_id(nf1, d1));
+ tt_assert(! nodefamily_contains_rsa_id(nf1, d2));
+ tt_assert(nodefamily_contains_nickname(nf1, "hello"));
+ tt_assert(nodefamily_contains_nickname(nf1, "HELLO"));
+ tt_assert(! nodefamily_contains_nickname(nf1, "goodbye"));
+
+ tt_int_op(nf1->refcnt, OP_EQ, 3);
+ nodefamily_free(nf3);
+ tt_int_op(nf1->refcnt, OP_EQ, 2);
+
+ /* Try parsing with a provided self RSA digest. */
+ nf3 = nodefamily_parse("hello ", d1, 0);
+ tt_assert(nf3);
+ tt_ptr_op(nf1, OP_EQ, nf3);
+
+ /* Do we get the expected result when we re-encode? */
+ tor_asprintf(&enc, "hello $%s", h1);
+ enc2 = nodefamily_format(nf1);
+ tt_str_op(enc2, OP_EQ, enc);
+ tor_free(enc2);
+ tor_free(enc);
+
+ /* Make sure that we get a different result if we give a different digest. */
+ nodefamily_free(nf3);
+ tor_asprintf(&enc, "hello $%s hello", h3);
+ nf3 = nodefamily_parse(enc, NULL, 0);
+ tt_assert(nf3);
+ tt_ptr_op(nf1, OP_NE, nf3);
+ tor_free(enc);
+
+ tt_assert(nodefamily_contains_rsa_id(nf3, d3));
+ tt_assert(! nodefamily_contains_rsa_id(nf3, d2));
+ tt_assert(! nodefamily_contains_rsa_id(nf3, d1));
+ tt_assert(nodefamily_contains_nickname(nf3, "hello"));
+ tt_assert(! nodefamily_contains_nickname(nf3, "goodbye"));
+
+ nodefamily_free(nf1);
+ nodefamily_free(nf2);
+ nodefamily_free(nf3);
+
+ /* Try one with several digests, all with nicknames appended, in different
+ formats. */
+ tor_asprintf(&enc, "%s $%s $%s=res $%s~ist", h1, h2, h3, h4);
+ nf1 = nodefamily_parse(enc, d5, 0);
+ tt_assert(nf1);
+ tt_assert(nodefamily_contains_rsa_id(nf1, d1));
+ tt_assert(nodefamily_contains_rsa_id(nf1, d2));
+ tt_assert(nodefamily_contains_rsa_id(nf1, d3));
+ tt_assert(nodefamily_contains_rsa_id(nf1, d4));
+ tt_assert(nodefamily_contains_rsa_id(nf1, d5));
+ /* Nicknames aren't preserved when ids are present, since node naming is
+ * deprecated */
+ tt_assert(! nodefamily_contains_nickname(nf3, "res"));
+ tor_free(enc);
+ tor_asprintf(&enc, "$%s $%s $%s $%s $%s", h4, h3, h1, h5, h2);
+ enc2 = nodefamily_format(nf1);
+ tt_str_op(enc, OP_EQ, enc2);
+ tor_free(enc);
+ tor_free(enc2);
+
+ /* Try ones where we parse the empty string. */
+ nf2 = nodefamily_parse("", NULL, 0);
+ nf3 = nodefamily_parse("", d4, 0);
+ tt_assert(nf2);
+ tt_assert(nf3);
+ tt_ptr_op(nf2, OP_NE, nf3);
+
+ tt_assert(! nodefamily_contains_rsa_id(nf2, d4));
+ tt_assert(nodefamily_contains_rsa_id(nf3, d4));
+ tt_assert(! nodefamily_contains_rsa_id(nf2, d5));
+ tt_assert(! nodefamily_contains_rsa_id(nf3, d5));
+ tt_assert(! nodefamily_contains_nickname(nf2, "fred"));
+ tt_assert(! nodefamily_contains_nickname(nf3, "bosco"));
+
+ /* The NULL family should contain nothing. */
+ tt_assert(! nodefamily_contains_rsa_id(NULL, d4));
+ tt_assert(! nodefamily_contains_rsa_id(NULL, d5));
+
+ done:
+ tor_free(enc);
+ tor_free(enc2);
+ nodefamily_free(nf1);
+ nodefamily_free(nf2);
+ nodefamily_free(nf3);
+ nodefamily_free_all();
+}
+
+static void
+test_nodelist_nodefamily_parse_err(void *arg)
+{
+ (void)arg;
+ nodefamily_t *nf1 = NULL;
+ char *enc = NULL;
+ const char *semibogus =
+ "sdakljfdslkfjdsaklfjdkl9sdf " // too long for nickname
+ "$jkASDFLkjsadfjhkl " // not hex
+ "$7468696e67732d696e2d7468656d73656c766573 " // ok
+ "reticulatogranulate "// ok
+ "$73656d69616e7468726f706f6c6f676963616c6c79 " // too long for hex
+ "$616273656e746d696e6465646e6573736573" // too short for hex
+ ;
+
+ setup_capture_of_logs(LOG_WARN);
+
+ // We only get two items when we parse this.
+ for (int reject = 0; reject <= 1; ++reject) {
+ for (int log_at_warn = 0; log_at_warn <= 1; ++log_at_warn) {
+ unsigned flags = log_at_warn ? NF_WARN_MALFORMED : 0;
+ flags |= reject ? NF_REJECT_MALFORMED : 0;
+ nf1 = nodefamily_parse(semibogus, NULL, flags);
+ if (reject) {
+ tt_assert(nf1 == NULL);
+ } else {
+ tt_assert(nf1);
+ enc = nodefamily_format(nf1);
+ tt_str_op(enc, OP_EQ,
+ "reticulatogranulate "
+ "$7468696E67732D696E2D7468656D73656C766573");
+ tor_free(enc);
+ }
+
+ if (log_at_warn) {
+ expect_log_msg_containing("$616273656e746d696e6465646e6573736573");
+ expect_log_msg_containing("sdakljfdslkfjdsaklfjdkl9sdf");
+ } else {
+ tt_int_op(mock_saved_log_n_entries(), OP_EQ, 0);
+ }
+ mock_clean_saved_logs();
+ }
+ }
+
+ done:
+ tor_free(enc);
+ nodefamily_free(nf1);
+ teardown_capture_of_logs();
+}
+
+static const node_t *
+mock_node_get_by_id(const char *id)
+{
+ if (fast_memeq(id, "!!!!!!!!!!!!!!!!!!!!", DIGEST_LEN))
+ return NULL;
+
+ // use tor_free, not node_free.
+ node_t *fake_node = tor_malloc_zero(sizeof(node_t));
+ memcpy(fake_node->identity, id, DIGEST_LEN);
+ return fake_node;
+}
+
+static const node_t *
+mock_node_get_by_nickname(const char *nn, unsigned flags)
+{
+ (void)flags;
+ if (!strcmp(nn, "nonesuch"))
+ return NULL;
+
+ // use tor_free, not node_free.
+ node_t *fake_node = tor_malloc_zero(sizeof(node_t));
+ strlcpy(fake_node->identity, nn, DIGEST_LEN);
+ return fake_node;
+}
+
+static void
+test_nodelist_nodefamily_lookup(void *arg)
+{
+ (void)arg;
+ MOCK(node_get_by_nickname, mock_node_get_by_nickname);
+ MOCK(node_get_by_id, mock_node_get_by_id);
+ smartlist_t *sl = smartlist_new();
+ nodefamily_t *nf1 = NULL;
+ char *mem_op_hex_tmp = NULL;
+
+ // 'null' is allowed.
+ nodefamily_add_nodes_to_smartlist(NULL, sl);
+ tt_int_op(smartlist_len(sl), OP_EQ, 0);
+
+ // Try a real family
+ nf1 = nodefamily_parse("$EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE "
+ "$2121212121212121212121212121212121212121 "
+ "$3333333333333333333333333333333333333333 "
+ "erewhon nonesuch", NULL, 0);
+ tt_assert(nf1);
+ nodefamily_add_nodes_to_smartlist(nf1, sl);
+ // There were 5 elements; 2 were dropped because the mocked lookup failed.
+ tt_int_op(smartlist_len(sl), OP_EQ, 3);
+
+ const node_t *n = smartlist_get(sl, 0);
+ tt_str_op(n->identity, OP_EQ, "erewhon");
+ n = smartlist_get(sl, 1);
+ test_memeq_hex(n->identity, "3333333333333333333333333333333333333333");
+ n = smartlist_get(sl, 2);
+ test_memeq_hex(n->identity, "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE");
+
+ done:
+ UNMOCK(node_get_by_nickname);
+ UNMOCK(node_get_by_id);
+ SMARTLIST_FOREACH(sl, node_t *, fake_node, tor_free(fake_node));
+ smartlist_free(sl);
+ nodefamily_free(nf1);
+ tor_free(mem_op_hex_tmp);
+}
+
+static void
+test_nodelist_nickname_matches(void *arg)
+{
+ (void)arg;
+ node_t mock_node;
+ routerstatus_t mock_rs;
+ memset(&mock_node, 0, sizeof(mock_node));
+ memset(&mock_rs, 0, sizeof(mock_rs));
+
+ strlcpy(mock_rs.nickname, "evilgeniuses", sizeof(mock_rs.nickname));
+ mock_node.rs = &mock_rs;
+ memcpy(mock_node.identity, ".forabettertomorrow.", DIGEST_LEN);
+
+#define match(x) tt_assert(node_nickname_matches(&mock_node, (x)))
+#define no_match(x) tt_assert(! node_nickname_matches(&mock_node, (x)))
+
+ match("evilgeniuses");
+ match("EvilGeniuses");
+ match("EvilGeniuses");
+ match("2e666f7261626574746572746f6d6f72726f772e");
+ match("2E666F7261626574746572746F6D6F72726F772E");
+ match("$2e666f7261626574746572746f6d6f72726f772e");
+ match("$2E666F7261626574746572746F6D6F72726F772E");
+ match("$2E666F7261626574746572746F6D6F72726F772E~evilgeniuses");
+ match("$2E666F7261626574746572746F6D6F72726F772E~EVILGENIUSES");
+
+ no_match("evilgenius");
+ no_match("evilgeniuseses");
+ no_match("evil.genius");
+ no_match("$2E666F7261626574746572746F6D6F72726FFFFF");
+ no_match("2E666F7261626574746572746F6D6F72726FFFFF");
+ no_match("$2E666F7261626574746572746F6D6F72726F772E~fred");
+ no_match("$2E666F7261626574746572746F6D6F72726F772E=EVILGENIUSES");
+ done:
+ ;
+}
+
+static void
+test_nodelist_node_nodefamily(void *arg)
+{
+ (void)arg;
+ node_t mock_node1;
+ routerstatus_t mock_rs;
+ microdesc_t mock_md;
+
+ node_t mock_node2;
+ routerinfo_t mock_ri;
+
+ smartlist_t *nodes=smartlist_new();
+
+ memset(&mock_node1, 0, sizeof(mock_node1));
+ memset(&mock_node2, 0, sizeof(mock_node2));
+ memset(&mock_rs, 0, sizeof(mock_rs));
+ memset(&mock_md, 0, sizeof(mock_md));
+ memset(&mock_ri, 0, sizeof(mock_ri));
+
+ mock_node1.rs = &mock_rs;
+ mock_node1.md = &mock_md;
+
+ mock_node2.ri = &mock_ri;
+
+ strlcpy(mock_rs.nickname, "nodeone", sizeof(mock_rs.nickname));
+ mock_ri.nickname = tor_strdup("nodetwo");
+
+ memcpy(mock_node1.identity, "NodeOneNode1NodeOne1", DIGEST_LEN);
+ memcpy(mock_node2.identity, "SecondNodeWe'reTestn", DIGEST_LEN);
+
+ // empty families.
+ tt_assert(! node_family_contains(&mock_node1, &mock_node2));
+ tt_assert(! node_family_contains(&mock_node2, &mock_node1));
+
+ // Families contain nodes, but not these nodes
+ mock_ri.declared_family = smartlist_new();
+ smartlist_add(mock_ri.declared_family, (char*)"NodeThree");
+ mock_md.family = nodefamily_parse("NodeFour", NULL, 0);
+ tt_assert(! node_family_contains(&mock_node1, &mock_node2));
+ tt_assert(! node_family_contains(&mock_node2, &mock_node1));
+
+ // Families contain one another.
+ smartlist_add(mock_ri.declared_family, (char*)
+ "4e6f64654f6e654e6f6465314e6f64654f6e6531");
+ tt_assert(! node_family_contains(&mock_node1, &mock_node2));
+ tt_assert(node_family_contains(&mock_node2, &mock_node1));
+
+ nodefamily_free(mock_md.family);
+ mock_md.family = nodefamily_parse(
+ "NodeFour "
+ "5365636f6e644e6f64655765277265546573746e", NULL, 0);
+ tt_assert(node_family_contains(&mock_node1, &mock_node2));
+ tt_assert(node_family_contains(&mock_node2, &mock_node1));
+
+ // Try looking up families now.
+ MOCK(node_get_by_nickname, mock_node_get_by_nickname);
+ MOCK(node_get_by_id, mock_node_get_by_id);
+
+ node_lookup_declared_family(nodes, &mock_node1);
+ tt_int_op(smartlist_len(nodes), OP_EQ, 2);
+ const node_t *n = smartlist_get(nodes, 0);
+ tt_str_op(n->identity, OP_EQ, "NodeFour");
+ n = smartlist_get(nodes, 1);
+ tt_mem_op(n->identity, OP_EQ, "SecondNodeWe'reTestn", DIGEST_LEN);
+
+ // free, try the other one.
+ SMARTLIST_FOREACH(nodes, node_t *, x, tor_free(x));
+ smartlist_clear(nodes);
+
+ node_lookup_declared_family(nodes, &mock_node2);
+ tt_int_op(smartlist_len(nodes), OP_EQ, 2);
+ n = smartlist_get(nodes, 0);
+ tt_str_op(n->identity, OP_EQ, "NodeThree");
+ n = smartlist_get(nodes, 1);
+ // This gets a truncated hex hex ID since it was looked up by name
+ tt_str_op(n->identity, OP_EQ, "4e6f64654f6e654e6f6");
+
+ done:
+ UNMOCK(node_get_by_nickname);
+ UNMOCK(node_get_by_id);
+ smartlist_free(mock_ri.declared_family);
+ nodefamily_free(mock_md.family);
+ tor_free(mock_ri.nickname);
+ // use tor_free, these aren't real nodes
+ SMARTLIST_FOREACH(nodes, node_t *, x, tor_free(x));
+ smartlist_free(nodes);
+}
+
#define NODE(name, flags) \
{ #name, test_nodelist_##name, (flags), NULL, NULL }
@@ -239,6 +618,10 @@ struct testcase_t nodelist_tests[] = {
NODE(node_get_verbose_nickname_not_named, TT_FORK),
NODE(node_is_dir, TT_FORK),
NODE(ed_id, TT_FORK),
+ NODE(nodefamily, TT_FORK),
+ NODE(nodefamily_parse_err, TT_FORK),
+ NODE(nodefamily_lookup, TT_FORK),
+ NODE(nickname_matches, 0),
+ NODE(node_nodefamily, TT_FORK),
END_OF_TESTCASES
};
-
diff --git a/src/test/test_options.c b/src/test/test_options.c
index f14e620eeb..376d77626f 100644
--- a/src/test/test_options.c
+++ b/src/test/test_options.c
@@ -425,6 +425,7 @@ get_options_test_data(const char *conf)
// with options_init(), but about a dozen tests break when I do that.
// Being kinda lame and just fixing the immedate breakage for now..
result->opt->ConnectionPadding = -1; // default must be "auto"
+ result->opt->DormantClientTimeout = 1800; // must be over 600.
rv = config_get_lines(conf, &cl, 1);
tt_int_op(rv, OP_EQ, 0);
diff --git a/src/test/test_parsecommon.c b/src/test/test_parsecommon.c
new file mode 100644
index 0000000000..6da125dd0a
--- /dev/null
+++ b/src/test/test_parsecommon.c
@@ -0,0 +1,594 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "core/or/or.h"
+#include "test/test.h"
+#include "lib/memarea/memarea.h"
+#include "lib/encoding/binascii.h"
+#include "feature/dirparse/parsecommon.h"
+#include "test/log_test_helpers.h"
+
+static void
+test_parsecommon_tokenize_string_null(void *arg)
+{
+
+ memarea_t *area = memarea_new();
+ smartlist_t *tokens = smartlist_new();
+
+ (void)arg;
+
+ const char *str_with_null = "a\0bccccccccc";
+
+ int retval =
+ tokenize_string(area, str_with_null,
+ str_with_null + 3,
+ tokens, NULL, 0);
+
+ tt_int_op(retval, OP_EQ, -1);
+
+ done:
+ memarea_drop_all(area);
+ smartlist_free(tokens);
+ return;
+}
+
+static void
+test_parsecommon_tokenize_string_multiple_lines(void *arg)
+{
+ memarea_t *area = memarea_new();
+ smartlist_t *tokens = smartlist_new();
+
+ (void)arg;
+
+ token_rule_t table[] = {
+ T01("uptime", K_UPTIME, GE(1), NO_OBJ),
+ T01("hibernating", K_HIBERNATING, GE(1), NO_OBJ),
+ T1( "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ),
+ END_OF_TABLE,
+ };
+
+ char *str = tor_strdup(
+ "hibernating 0\nuptime 1024\n"
+ "published 2018-10-15 10:00:00\n");
+
+ int retval =
+ tokenize_string(area, str, NULL,
+ tokens, table, 0);
+
+ tt_int_op(smartlist_len(tokens), OP_EQ, 3);
+ directory_token_t *token = smartlist_get(tokens, 0);
+
+ tt_int_op(token->tp, OP_EQ, K_HIBERNATING);
+
+ token = smartlist_get(tokens, 1);
+
+ tt_int_op(token->tp, OP_EQ, K_UPTIME);
+
+ token = smartlist_get(tokens, 2);
+
+ tt_int_op(token->tp, OP_EQ, K_PUBLISHED);
+
+ tt_int_op(retval, OP_EQ, 0);
+
+ done:
+ tor_free(str);
+ memarea_drop_all(area);
+ smartlist_free(tokens);
+ return;
+}
+
+static void
+test_parsecommon_tokenize_string_min_cnt(void *arg)
+{
+ memarea_t *area = memarea_new();
+ smartlist_t *tokens = smartlist_new();
+
+ (void)arg;
+
+ token_rule_t table[] = {
+ T01("uptime", K_UPTIME, EQ(2), NO_OBJ),
+ T01("hibernating", K_HIBERNATING, GE(1), NO_OBJ),
+ END_OF_TABLE,
+ };
+
+ // Missing "uptime"
+ char *str = tor_strdup("uptime 1024\nhibernating 0\n");
+
+ int retval =
+ tokenize_string(area, str, NULL,
+ tokens, table, 0);
+
+ tt_int_op(retval, OP_EQ, -1);
+
+ done:
+ tor_free(str);
+ memarea_drop_all(area);
+ smartlist_free(tokens);
+ return;
+}
+
+static void
+test_parsecommon_tokenize_string_max_cnt(void *arg)
+{
+ memarea_t *area = memarea_new();
+ smartlist_t *tokens = smartlist_new();
+
+ (void)arg;
+
+ token_rule_t table[] = {
+ T01("uptime", K_UPTIME, EQ(1), NO_OBJ),
+ T01("hibernating", K_HIBERNATING, GE(1), NO_OBJ),
+ END_OF_TABLE,
+ };
+
+ // "uptime" expected once, but occurs twice in input.
+ char *str = tor_strdup(
+ "uptime 1024\nuptime 2048\nhibernating 0\n");
+
+ int retval =
+ tokenize_string(area, str, NULL,
+ tokens, table, 0);
+
+ tt_int_op(retval, OP_EQ, -1);
+
+ done:
+ tor_free(str);
+ memarea_drop_all(area);
+ smartlist_free(tokens);
+ return;
+}
+
+static void
+test_parsecommon_tokenize_string_at_start(void *arg)
+{
+ memarea_t *area = memarea_new();
+ smartlist_t *tokens = smartlist_new();
+
+ (void)arg;
+
+ token_rule_t table[] = {
+ T1_START("client-name", C_CLIENT_NAME, CONCAT_ARGS, NO_OBJ),
+ T01("uptime", K_UPTIME, EQ(1), NO_OBJ),
+ END_OF_TABLE,
+ };
+
+ // "client-name" is not the first line.
+ char *str = tor_strdup(
+ "uptime 1024\nclient-name Alice\n");
+
+ int retval =
+ tokenize_string(area, str, NULL, tokens, table, 0);
+
+ tt_int_op(retval, OP_EQ, -1);
+
+ done:
+ tor_free(str);
+ memarea_drop_all(area);
+ smartlist_free(tokens);
+ return;
+}
+
+static void
+test_parsecommon_tokenize_string_at_end(void *arg)
+{
+ memarea_t *area = memarea_new();
+ smartlist_t *tokens = smartlist_new();
+
+ (void)arg;
+
+ token_rule_t table[] = {
+ T1_END("client-name", C_CLIENT_NAME, CONCAT_ARGS, NO_OBJ),
+ T01("uptime", K_UPTIME, EQ(1), NO_OBJ),
+ END_OF_TABLE,
+ };
+
+ // "client-name" is not the last line.
+ char *str = tor_strdup(
+ "client-name Alice\nuptime 1024\n");
+
+ int retval =
+ tokenize_string(area, str, NULL, tokens, table, 0);
+
+ tt_int_op(retval, OP_EQ, -1);
+
+ done:
+ tor_free(str);
+ memarea_drop_all(area);
+ smartlist_free(tokens);
+ return;
+}
+
+static void
+test_parsecommon_tokenize_string_no_annotations(void *arg)
+{
+ memarea_t *area = memarea_new();
+ smartlist_t *tokens = smartlist_new();
+
+ (void)arg;
+
+ token_rule_t table[] = {
+ A01("@last-listed", A_LAST_LISTED, CONCAT_ARGS, NO_OBJ),
+ END_OF_TABLE,
+ };
+
+ char *str = tor_strdup("@last-listed 2018-09-21 15:30:03\n");
+
+ int retval =
+ tokenize_string(area, str, NULL, tokens, table, 0);
+
+ tt_int_op(retval, OP_EQ, -1);
+
+ done:
+ tor_free(str);
+ memarea_drop_all(area);
+ smartlist_free(tokens);
+ return;
+}
+
+static void
+test_parsecommon_get_next_token_success(void *arg)
+{
+ memarea_t *area = memarea_new();
+ const char *str = "uptime 1024";
+ const char *end = str + strlen(str);
+ const char **s = &str;
+ token_rule_t table = T01("uptime", K_UPTIME, GE(1), NO_OBJ);
+ (void)arg;
+
+ directory_token_t *token = get_next_token(area, s, end, &table);
+
+ tt_int_op(token->tp, OP_EQ, K_UPTIME);
+ tt_int_op(token->n_args, OP_EQ, 1);
+ tt_str_op(*(token->args), OP_EQ, "1024");
+ tt_assert(!token->object_type);
+ tt_int_op(token->object_size, OP_EQ, 0);
+ tt_assert(!token->object_body);
+
+ tt_ptr_op(*s, OP_EQ, end);
+
+ done:
+ memarea_drop_all(area);
+ return;
+}
+
+static void
+test_parsecommon_get_next_token_concat_args(void *arg)
+{
+ memarea_t *area = memarea_new();
+ const char *str = "proto A=1 B=2";
+ const char *end = str + strlen(str);
+ const char **s = &str;
+ token_rule_t rule = T01("proto", K_PROTO, CONCAT_ARGS, NO_OBJ);
+ (void)arg;
+
+ directory_token_t *token = get_next_token(area, s, end, &rule);
+
+ tt_int_op(token->tp, OP_EQ, K_PROTO);
+ tt_int_op(token->n_args, OP_EQ, 1);
+ tt_str_op(*(token->args), OP_EQ, "A=1 B=2");
+
+ done:
+ memarea_drop_all(area);
+}
+
+static void
+test_parsecommon_get_next_token_parse_keys(void *arg)
+{
+ (void)arg;
+
+ memarea_t *area = memarea_new();
+ const char *str =
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMDdIya33BfNlHOkzoTKSTT8EjD64waMfUr372syVHiFjHhObwKwGA5u\n"
+ "sHaMIe9r+Ij/4C1dKyuXkcz3DOl6gWNhTD7dZ89I+Okoh1jWe30jxCiAcywC22p5\n"
+ "XLhrDkX1A63Z7XCH9ltwU2WMqWsVM98N2GR6MTujP7wtqdLExYN1AgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n";
+
+ const char *end = str + strlen(str);
+ const char **s = (const char **)&str;
+ directory_token_t *token = NULL;
+ directory_token_t *token2 = NULL;
+
+ token_rule_t rule = T1("onion-key", R_IPO_ONION_KEY, NO_ARGS, NEED_KEY_1024);
+
+ token = get_next_token(area, s, end, &rule);
+ tt_assert(token);
+
+ tt_int_op(token->tp, OP_EQ, R_IPO_ONION_KEY);
+ tt_int_op(token->n_args, OP_EQ, 0);
+ tt_str_op(token->object_type, OP_EQ, "RSA PUBLIC KEY");
+ tt_int_op(token->object_size, OP_EQ, 0);
+ tt_assert(!token->object_body);
+ tt_assert(token->key);
+ tt_assert(!token->error);
+
+ const char *str2 =
+ "client-key\n"
+ "-----BEGIN RSA PRIVATE KEY-----\n"
+ "MIICXAIBAAKBgQCwS810a2auH2PQchOBz9smNgjlDu31aq0IYlUohSYbhcv5AJ+d\n"
+ "DY0nfZWzS+mZPwzL3UiEnTt6PVv7AgoZ5V9ZJWJTKIURjJpkK0mstfJKHKIZhf84\n"
+ "pmFfRej9GQViB6NLtp1obOXJgJixSlMfw9doDI4NoAnEISCyH/tD77Qs2wIDAQAB\n"
+ "AoGAbDg8CKkdQOnX9c7xFpCnsE8fKqz9eddgHHNwXw1NFTwOt+2gDWKSMZmv2X5S\n"
+ "CVZg3owZxf5W0nT0D6Ny2+6nliak7foYAvkD0BsCiBhgftwC0zAo6k5rIbUKB3PJ\n"
+ "QLFXgpJhqWuXkODyt/hS/GTernR437WVSEGp1bnALqiFabECQQDaqHOxzoWY/nvH\n"
+ "KrfUi8EhqCnqERlRHwrW0MQZ1RPvF16OPPma+xa+ht/amfh3vYN5tZY82Zm43gGl\n"
+ "XWL5cZhNAkEAzmdSootYVnqLLLRMfHKXnO1XbaEcA/08MDNKGlSclBJixFenE8jX\n"
+ "iQsUbHwMJuGONvzWpRGPBP2f8xBd28ZtxwJARY+LZshtpfNniz/ixYJESaHG28je\n"
+ "xfjbKOW3TQSFV+2WTifFvHEeljQwKMoMyoMGvYRwLCGJjs9JtMLVxsdFjQJBAKwD\n"
+ "3BBvBQ39TuPQ1zWX4tb7zjMlY83HTFP3Sriq71tP/1QWoL2SUl56B2lp8E6vB/C3\n"
+ "wsMK4SCNprHRYAd7VZ0CQDKn6Zhd11P94PLs0msybFEh1VXr6CEW/BrxBgbL4ls6\n"
+ "dbX5XO0z4Ra8gYXgObgimhyMDYO98Idt5+Z3HIdyrSc=\n"
+ "-----END RSA PRIVATE KEY-----\n";
+
+ const char *end2 = str2 + strlen(str2);
+ const char **s2 = (const char **)&str2;
+
+ token_rule_t rule2 = T01("client-key", C_CLIENT_KEY, NO_ARGS,
+ NEED_SKEY_1024);
+
+ token2 = get_next_token(area, s2, end2, &rule2);
+ tt_assert(token2);
+
+ tt_int_op(token2->tp, OP_EQ, C_CLIENT_KEY);
+ tt_int_op(token2->n_args, OP_EQ, 0);
+ tt_str_op(token2->object_type, OP_EQ, "RSA PRIVATE KEY");
+ tt_int_op(token2->object_size, OP_EQ, 0);
+ tt_assert(!token2->object_body);
+ tt_assert(token2->key);
+ tt_assert(!token->error);
+
+ done:
+ if (token) token_clear(token);
+ if (token2) token_clear(token2);
+ memarea_drop_all(area);
+}
+
+static void
+test_parsecommon_get_next_token_object(void *arg)
+{
+ memarea_t *area = memarea_new();
+
+ const char *str =
+ "directory-signature 0232AF901C31A04EE9848595AF9BB7620D4C5B2E "
+ "CD1FD971855430880D3C31E0331C5C55800C2F79\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "dLTbc1Lad/OWKBJhA/dERzDHumswTAzBFAWAz2vnQhLsebs1SOm0W/vceEsiEkiF\n"
+ "A+JJSzIyfywJc6Mnk7aKMEIFjOO/MaxuAp4zv+q+JonJkF0ExjMqvKR0D6pSFmfN\n"
+ "cnemnxGHxNuPDnKl0imbWKmWDsHtwgi4zWeTq3MekfMOXKi6gIh+bDFzCs9/Vquh\n"
+ "uNKJI1jW/A2DEKeaSAODEv9VoCsYSvbVVEuHCBWjeNAurd5aL26BrAolW6m7pkD6\n"
+ "I+cQ8dQG6Wa/Zt6gLXtBbOP2o/iDI7ahDP9diNkBI/rm4nfp9j4piTwsqpi7xz9J\n"
+ "Ua9DEZB9KbJHVX1rGShrLA==\n"
+ "-----END SIGNATURE-----\n";
+
+ const char *end = str + strlen(str);
+ const char **s = &str;
+ token_rule_t rule = T("directory-signature", K_DIRECTORY_SIGNATURE,
+ GE(2), NEED_OBJ);
+ (void)arg;
+
+ directory_token_t *token = get_next_token(area, s, end, &rule);
+
+ tt_int_op(token->tp, OP_EQ, K_DIRECTORY_SIGNATURE);
+ tt_int_op(token->n_args, OP_EQ, 2);
+ tt_str_op(token->args[0], OP_EQ,
+ "0232AF901C31A04EE9848595AF9BB7620D4C5B2E");
+ tt_str_op(token->args[1], OP_EQ,
+ "CD1FD971855430880D3C31E0331C5C55800C2F79");
+
+ tt_assert(!token->error);
+
+ char decoded[256];
+ const char *signature =
+ "dLTbc1Lad/OWKBJhA/dERzDHumswTAzBFAWAz2vnQhLsebs1SOm0W/vceEsiEkiF\n"
+ "A+JJSzIyfywJc6Mnk7aKMEIFjOO/MaxuAp4zv+q+JonJkF0ExjMqvKR0D6pSFmfN\n"
+ "cnemnxGHxNuPDnKl0imbWKmWDsHtwgi4zWeTq3MekfMOXKi6gIh+bDFzCs9/Vquh\n"
+ "uNKJI1jW/A2DEKeaSAODEv9VoCsYSvbVVEuHCBWjeNAurd5aL26BrAolW6m7pkD6\n"
+ "I+cQ8dQG6Wa/Zt6gLXtBbOP2o/iDI7ahDP9diNkBI/rm4nfp9j4piTwsqpi7xz9J\n"
+ "Ua9DEZB9KbJHVX1rGShrLA==\n";
+ tt_assert(signature);
+ size_t signature_len = strlen(signature);
+ base64_decode(decoded, sizeof(decoded), signature, signature_len);
+
+ tt_str_op(token->object_type, OP_EQ, "SIGNATURE");
+ tt_int_op(token->object_size, OP_EQ, 256);
+ tt_mem_op(token->object_body, OP_EQ, decoded, 256);
+
+ tt_assert(!token->key);
+
+ done:
+ memarea_drop_all(area);
+}
+
+static void
+test_parsecommon_get_next_token_err_too_many_args(void *arg)
+{
+ memarea_t *area = memarea_new();
+ const char *str = "uptime 1024 1024 1024";
+ const char *end = str + strlen(str);
+ const char **s = &str;
+ token_rule_t table = T01("uptime", K_UPTIME, EQ(1), NO_OBJ);
+ (void)arg;
+
+ directory_token_t *token = get_next_token(area, s, end, &table);
+
+ tt_int_op(token->tp, OP_EQ, ERR_);
+ tt_str_op(token->error, OP_EQ, "Too many arguments to uptime");
+
+ done:
+ memarea_drop_all(area);
+ return;
+}
+
+static void
+test_parsecommon_get_next_token_err_too_few_args(void *arg)
+{
+ memarea_t *area = memarea_new();
+ const char *str = "uptime";
+ const char *end = str + strlen(str);
+ const char **s = &str;
+ token_rule_t table = T01("uptime", K_UPTIME, EQ(1), NO_OBJ);
+ (void)arg;
+
+ directory_token_t *token = get_next_token(area, s, end, &table);
+
+ tt_int_op(token->tp, OP_EQ, ERR_);
+ tt_str_op(token->error, OP_EQ, "Too few arguments to uptime");
+
+ done:
+ memarea_drop_all(area);
+ return;
+}
+
+static void
+test_parsecommon_get_next_token_err_obj_missing_endline(void *arg)
+{
+ memarea_t *area = memarea_new();
+
+ const char *str =
+ "directory-signature 0232AF901C31A04EE9848595AF9BB7620D4C5B2E "
+ "CD1FD971855430880D3C31E0331C5C55800C2F79\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "dLTbc1Lad/OWKBJhA/dERzDHumswTAzBFAWAz2vnQhLsebs1SOm0W/vceEsiEkiF\n"
+ "A+JJSzIyfywJc6Mnk7aKMEIFjOO/MaxuAp4zv+q+JonJkF0ExjMqvKR0D6pSFmfN\n"
+ "cnemnxGHxNuPDnKl0imbWKmWDsHtwgi4zWeTq3MekfMOXKi6gIh+bDFzCs9/Vquh\n"
+ "uNKJI1jW/A2DEKeaSAODEv9VoCsYSvbVVEuHCBWjeNAurd5aL26BrAolW6m7pkD6\n"
+ "I+cQ8dQG6Wa/Zt6gLXtBbOP2o/iDI7ahDP9diNkBI/rm4nfp9j4piTwsqpi7xz9J\n"
+ "Ua9DEZB9KbJHVX1rGShrLA==\n";
+
+ const char *end = str + strlen(str);
+ const char **s = &str;
+ token_rule_t rule = T("directory-signature", K_DIRECTORY_SIGNATURE,
+ GE(2), NEED_OBJ);
+ (void)arg;
+
+ directory_token_t *token = get_next_token(area, s, end, &rule);
+
+ tt_int_op(token->tp, OP_EQ, ERR_);
+ tt_str_op(token->error, OP_EQ, "Malformed object: missing object end line");
+
+ done:
+ memarea_drop_all(area);
+ return;
+}
+
+static void
+test_parsecommon_get_next_token_err_bad_beginline(void *arg)
+{
+ memarea_t *area = memarea_new();
+
+ const char *str =
+ "directory-signature 0232AF901C31A04EE9848595AF9BB7620D4C5B2E "
+ "CD1FD971855430880D3C31E0331C5C55800C2F79\n"
+ "-----BEGIN SIGNATURE-Z---\n"
+ "dLTbc1Lad/OWKBJhA/dERzDHumswTAzBFAWAz2vnQhLsebs1SOm0W/vceEsiEkiF\n"
+ "A+JJSzIyfywJc6Mnk7aKMEIFjOO/MaxuAp4zv+q+JonJkF0ExjMqvKR0D6pSFmfN\n"
+ "cnemnxGHxNuPDnKl0imbWKmWDsHtwgi4zWeTq3MekfMOXKi6gIh+bDFzCs9/Vquh\n"
+ "uNKJI1jW/A2DEKeaSAODEv9VoCsYSvbVVEuHCBWjeNAurd5aL26BrAolW6m7pkD6\n"
+ "I+cQ8dQG6Wa/Zt6gLXtBbOP2o/iDI7ahDP9diNkBI/rm4nfp9j4piTwsqpi7xz9J\n"
+ "Ua9DEZB9KbJHVX1rGShrLA==\n"
+ "-----END SIGNATURE-----\n";
+
+ const char *end = str + strlen(str);
+ const char **s = &str;
+ token_rule_t rule = T("directory-signature", K_DIRECTORY_SIGNATURE,
+ GE(2), NEED_OBJ);
+ (void)arg;
+
+ directory_token_t *token = get_next_token(area, s, end, &rule);
+
+ tt_int_op(token->tp, OP_EQ, ERR_);
+ tt_str_op(token->error, OP_EQ, "Malformed object: bad begin line");
+
+ done:
+ memarea_drop_all(area);
+ return;
+}
+
+static void
+test_parsecommon_get_next_token_err_tag_mismatch(void *arg)
+{
+ memarea_t *area = memarea_new();
+
+ const char *str =
+ "directory-signature 0232AF901C31A04EE9848595AF9BB7620D4C5B2E "
+ "CD1FD971855430880D3C31E0331C5C55800C2F79\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "dLTbc1Lad/OWKBJhA/dERzDHumswTAzBFAWAz2vnQhLsebs1SOm0W/vceEsiEkiF\n"
+ "A+JJSzIyfywJc6Mnk7aKMEIFjOO/MaxuAp4zv+q+JonJkF0ExjMqvKR0D6pSFmfN\n"
+ "cnemnxGHxNuPDnKl0imbWKmWDsHtwgi4zWeTq3MekfMOXKi6gIh+bDFzCs9/Vquh\n"
+ "uNKJI1jW/A2DEKeaSAODEv9VoCsYSvbVVEuHCBWjeNAurd5aL26BrAolW6m7pkD6\n"
+ "I+cQ8dQG6Wa/Zt6gLXtBbOP2o/iDI7ahDP9diNkBI/rm4nfp9j4piTwsqpi7xz9J\n"
+ "Ua9DEZB9KbJHVX1rGShrLA==\n"
+ "-----END SOMETHINGELSE-----\n";
+
+ const char *end = str + strlen(str);
+ const char **s = &str;
+ token_rule_t rule = T("directory-signature", K_DIRECTORY_SIGNATURE,
+ GE(2), NEED_OBJ);
+ (void)arg;
+
+ directory_token_t *token = get_next_token(area, s, end, &rule);
+
+ tt_int_op(token->tp, OP_EQ, ERR_);
+ tt_str_op(token->error, OP_EQ,
+ "Malformed object: mismatched end tag SIGNATURE");
+
+ done:
+ memarea_drop_all(area);
+ return;
+}
+
+static void
+test_parsecommon_get_next_token_err_bad_base64(void *arg)
+{
+ memarea_t *area = memarea_new();
+
+ const char *str =
+ "directory-signature 0232AF901C31A04EE9848595AF9BB7620D4C5B2E "
+ "CD1FD971855430880D3C31E0331C5C55800C2F79\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "%%@%%%%%%%!!!'\n"
+ "-----END SIGNATURE-----\n";
+
+ const char *end = str + strlen(str);
+ const char **s = &str;
+ token_rule_t rule = T("directory-signature", K_DIRECTORY_SIGNATURE,
+ GE(2), NEED_OBJ);
+ (void)arg;
+
+ directory_token_t *token = get_next_token(area, s, end, &rule);
+
+ tt_int_op(token->tp, OP_EQ, ERR_);
+ tt_str_op(token->error, OP_EQ, "Malformed object: bad base64-encoded data");
+
+ done:
+ memarea_drop_all(area);
+ return;
+}
+
+#define PARSECOMMON_TEST(name) \
+ { #name, test_parsecommon_ ## name, 0, NULL, NULL }
+
+struct testcase_t parsecommon_tests[] = {
+ PARSECOMMON_TEST(tokenize_string_null),
+ PARSECOMMON_TEST(tokenize_string_multiple_lines),
+ PARSECOMMON_TEST(tokenize_string_min_cnt),
+ PARSECOMMON_TEST(tokenize_string_max_cnt),
+ PARSECOMMON_TEST(tokenize_string_at_start),
+ PARSECOMMON_TEST(tokenize_string_at_end),
+ PARSECOMMON_TEST(tokenize_string_no_annotations),
+ PARSECOMMON_TEST(get_next_token_success),
+ PARSECOMMON_TEST(get_next_token_concat_args),
+ PARSECOMMON_TEST(get_next_token_parse_keys),
+ PARSECOMMON_TEST(get_next_token_object),
+ PARSECOMMON_TEST(get_next_token_err_too_many_args),
+ PARSECOMMON_TEST(get_next_token_err_too_few_args),
+ PARSECOMMON_TEST(get_next_token_err_obj_missing_endline),
+ PARSECOMMON_TEST(get_next_token_err_bad_beginline),
+ PARSECOMMON_TEST(get_next_token_err_tag_mismatch),
+ PARSECOMMON_TEST(get_next_token_err_bad_base64),
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_periodic_event.c b/src/test/test_periodic_event.c
index 86dedd85d8..f3d518eb7b 100644
--- a/src/test/test_periodic_event.c
+++ b/src/test/test_periodic_event.c
@@ -19,6 +19,7 @@
#include "feature/hibernate/hibernate.h"
#include "feature/hs/hs_service.h"
#include "core/mainloop/mainloop.h"
+#include "core/mainloop/netstatus.h"
#include "core/mainloop/periodic.h"
/** Helper function: This is replaced in some tests for the event callbacks so
@@ -50,6 +51,8 @@ test_pe_initialize(void *arg)
* need to run the main loop and then wait for a second delaying the unit
* tests. Instead, we'll test the callback work indepedently elsewhere. */
initialize_periodic_events();
+ set_network_participation(false);
+ rescan_periodic_events(get_options());
/* Validate that all events have been set up. */
for (int i = 0; periodic_events[i].name; ++i) {
@@ -59,7 +62,9 @@ test_pe_initialize(void *arg)
tt_u64_op(item->last_action_time, OP_EQ, 0);
/* Every event must have role(s) assign to it. This is done statically. */
tt_u64_op(item->roles, OP_NE, 0);
- tt_uint_op(periodic_event_is_enabled(item), OP_EQ, 0);
+ int should_be_enabled = (item->roles & PERIODIC_EVENT_ROLE_ALL) &&
+ !(item->flags & PERIODIC_EVENT_FLAG_NEED_NET);
+ tt_uint_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled);
}
done:
@@ -79,6 +84,8 @@ test_pe_launch(void *arg)
* network gets enabled. */
consider_hibernation(time(NULL));
+ set_network_participation(true);
+
/* Hack: We'll set a dumb fn() of each events so they don't get called when
* dispatching them. We just want to test the state of the callbacks, not
* the whole code path. */
@@ -90,6 +97,7 @@ test_pe_launch(void *arg)
options = get_options_mutable();
options->SocksPort_set = 1;
periodic_events_on_new_options(options);
+
#if 0
/* Lets make sure that before intialization, we can't scan the periodic
* events list and launch them. Lets try by being a Client. */
@@ -106,13 +114,12 @@ test_pe_launch(void *arg)
/* Now that we've initialized, rescan the list to launch. */
periodic_events_on_new_options(options);
+ int mask = PERIODIC_EVENT_ROLE_CLIENT|PERIODIC_EVENT_ROLE_ALL|
+ PERIODIC_EVENT_ROLE_NET_PARTICIPANT;
for (int i = 0; periodic_events[i].name; ++i) {
periodic_event_item_t *item = &periodic_events[i];
- if (item->roles & PERIODIC_EVENT_ROLE_CLIENT) {
- tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1);
- } else {
- tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
- }
+ int should_be_enabled = !!(item->roles & mask);
+ tt_int_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled);
// enabled or not, the event has not yet been run.
tt_u64_op(item->last_action_time, OP_EQ, 0);
}
@@ -124,7 +131,8 @@ test_pe_launch(void *arg)
unsigned roles = get_my_roles(options);
tt_uint_op(roles, OP_EQ,
- PERIODIC_EVENT_ROLE_RELAY|PERIODIC_EVENT_ROLE_DIRSERVER);
+ PERIODIC_EVENT_ROLE_RELAY|PERIODIC_EVENT_ROLE_DIRSERVER|
+ PERIODIC_EVENT_ROLE_ALL|PERIODIC_EVENT_ROLE_NET_PARTICIPANT);
for (int i = 0; periodic_events[i].name; ++i) {
periodic_event_item_t *item = &periodic_events[i];
@@ -144,17 +152,23 @@ test_pe_launch(void *arg)
/* Disable everything and we'll enable them ALL. */
options->SocksPort_set = 0;
options->ORPort_set = 0;
+ options->DisableNetwork = 1;
+ set_network_participation(false);
periodic_events_on_new_options(options);
for (int i = 0; periodic_events[i].name; ++i) {
periodic_event_item_t *item = &periodic_events[i];
- tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
+ int should_be_enabled = (item->roles & PERIODIC_EVENT_ROLE_ALL) &&
+ !(item->flags & PERIODIC_EVENT_FLAG_NEED_NET);
+ tt_int_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled);
}
/* Enable everything. */
options->SocksPort_set = 1; options->ORPort_set = 1;
options->BridgeRelay = 1; options->AuthoritativeDir = 1;
options->V3AuthoritativeDir = 1; options->BridgeAuthoritativeDir = 1;
+ options->DisableNetwork = 0;
+ set_network_participation(true);
register_dummy_hidden_service(&service);
periodic_events_on_new_options(options);
/* Note down the reference because we need to remove this service from the
@@ -165,7 +179,8 @@ test_pe_launch(void *arg)
for (int i = 0; periodic_events[i].name; ++i) {
periodic_event_item_t *item = &periodic_events[i];
- tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1);
+ tt_int_op(periodic_event_is_enabled(item), OP_EQ,
+ (item->roles != PERIODIC_EVENT_ROLE_CONTROLEV));
}
done:
@@ -187,42 +202,49 @@ test_pe_get_roles(void *arg)
or_options_t *options = get_options_mutable();
tt_assert(options);
+ set_network_participation(true);
+
+ const int ALL = PERIODIC_EVENT_ROLE_ALL |
+ PERIODIC_EVENT_ROLE_NET_PARTICIPANT;
/* Nothing configured, should be no roles. */
+ tt_assert(net_is_disabled());
roles = get_my_roles(options);
- tt_int_op(roles, OP_EQ, 0);
+ tt_int_op(roles, OP_EQ, ALL);
/* Indicate we have a SocksPort, roles should be come Client. */
options->SocksPort_set = 1;
roles = get_my_roles(options);
- tt_int_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_CLIENT);
+ tt_int_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_CLIENT|ALL);
/* Now, we'll add a ORPort so should now be a Relay + Client. */
options->ORPort_set = 1;
roles = get_my_roles(options);
tt_int_op(roles, OP_EQ,
(PERIODIC_EVENT_ROLE_CLIENT | PERIODIC_EVENT_ROLE_RELAY |
- PERIODIC_EVENT_ROLE_DIRSERVER));
+ PERIODIC_EVENT_ROLE_DIRSERVER | ALL));
/* Now add a Bridge. */
options->BridgeRelay = 1;
roles = get_my_roles(options);
tt_int_op(roles, OP_EQ,
(PERIODIC_EVENT_ROLE_CLIENT | PERIODIC_EVENT_ROLE_RELAY |
- PERIODIC_EVENT_ROLE_BRIDGE | PERIODIC_EVENT_ROLE_DIRSERVER));
+ PERIODIC_EVENT_ROLE_BRIDGE | PERIODIC_EVENT_ROLE_DIRSERVER |
+ ALL));
tt_assert(roles & PERIODIC_EVENT_ROLE_ROUTER);
/* Unset client so we can solely test Router role. */
options->SocksPort_set = 0;
roles = get_my_roles(options);
tt_int_op(roles, OP_EQ,
- PERIODIC_EVENT_ROLE_ROUTER | PERIODIC_EVENT_ROLE_DIRSERVER);
+ PERIODIC_EVENT_ROLE_ROUTER | PERIODIC_EVENT_ROLE_DIRSERVER |
+ ALL);
/* Reset options so we can test authorities. */
options->SocksPort_set = 0;
options->ORPort_set = 0;
options->BridgeRelay = 0;
roles = get_my_roles(options);
- tt_int_op(roles, OP_EQ, 0);
+ tt_int_op(roles, OP_EQ, ALL);
/* Now upgrade to Dirauth. */
options->DirPort_set = 1;
@@ -230,7 +252,7 @@ test_pe_get_roles(void *arg)
options->V3AuthoritativeDir = 1;
roles = get_my_roles(options);
tt_int_op(roles, OP_EQ,
- PERIODIC_EVENT_ROLE_DIRAUTH|PERIODIC_EVENT_ROLE_DIRSERVER);
+ PERIODIC_EVENT_ROLE_DIRAUTH|PERIODIC_EVENT_ROLE_DIRSERVER|ALL);
tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
/* Now Bridge Authority. */
@@ -238,7 +260,7 @@ test_pe_get_roles(void *arg)
options->BridgeAuthoritativeDir = 1;
roles = get_my_roles(options);
tt_int_op(roles, OP_EQ,
- PERIODIC_EVENT_ROLE_BRIDGEAUTH|PERIODIC_EVENT_ROLE_DIRSERVER);
+ PERIODIC_EVENT_ROLE_BRIDGEAUTH|PERIODIC_EVENT_ROLE_DIRSERVER|ALL);
tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
/* Move that bridge auth to become a relay. */
@@ -246,7 +268,7 @@ test_pe_get_roles(void *arg)
roles = get_my_roles(options);
tt_int_op(roles, OP_EQ,
(PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_RELAY
- | PERIODIC_EVENT_ROLE_DIRSERVER));
+ | PERIODIC_EVENT_ROLE_DIRSERVER|ALL));
tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
/* And now an Hidden service. */
@@ -257,7 +279,8 @@ test_pe_get_roles(void *arg)
remove_service(get_hs_service_map(), &service);
tt_int_op(roles, OP_EQ,
(PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_RELAY |
- PERIODIC_EVENT_ROLE_HS_SERVICE | PERIODIC_EVENT_ROLE_DIRSERVER));
+ PERIODIC_EVENT_ROLE_HS_SERVICE | PERIODIC_EVENT_ROLE_DIRSERVER |
+ ALL));
tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
done:
diff --git a/src/test/test_rebind.py b/src/test/test_rebind.py
index c63341a681..6f759b717e 100644
--- a/src/test/test_rebind.py
+++ b/src/test/test_rebind.py
@@ -1,6 +1,7 @@
from __future__ import print_function
import errno
+import logging
import os
import random
import socket
@@ -13,7 +14,7 @@ LOG_WAIT = 0.1
LOG_CHECK_LIMIT = LOG_TIMEOUT / LOG_WAIT
def fail(msg):
- print('FAIL')
+ logging.error('FAIL')
sys.exit(msg)
def try_connecting_to_socksport():
@@ -29,8 +30,9 @@ def wait_for_log(s):
l = tor_process.stdout.readline()
l = l.decode('utf8')
if s in l:
+ logging.info('Tor logged: "{}"'.format(l.strip()))
return
- print('Tor logged: "{}", waiting for "{}"'.format(l.strip(), s))
+ logging.info('Tor logged: "{}", waiting for "{}"'.format(l.strip(), s))
# readline() returns a blank string when there is no output
# avoid busy-waiting
if len(s) == 0:
@@ -55,6 +57,10 @@ def pick_random_port():
return port
+logging.basicConfig(level=logging.DEBUG,
+ format='%(asctime)s.%(msecs)03d %(message)s',
+ datefmt='%Y-%m-%d %H:%M:%S')
+
if sys.hexversion < 0x02070000:
fail("ERROR: unsupported Python version (should be >= 2.7)")
@@ -67,14 +73,23 @@ socks_port = pick_random_port()
assert control_port != 0
assert socks_port != 0
+if len(sys.argv) < 3:
+ fail('Usage: %s <path-to-tor> <data-dir>' % sys.argv[0])
+
if not os.path.exists(sys.argv[1]):
fail('ERROR: cannot find tor at %s' % sys.argv[1])
+if not os.path.exists(sys.argv[2]):
+ fail('ERROR: cannot find datadir at %s' % sys.argv[2])
tor_path = sys.argv[1]
+data_dir = sys.argv[2]
tor_process = subprocess.Popen([tor_path,
+ '-DataDirectory', data_dir,
'-ControlPort', '127.0.0.1:{}'.format(control_port),
'-SOCKSPort', '127.0.0.1:{}'.format(socks_port),
+ '-Log', 'debug stdout',
+ '-LogTimeGranularity', '1',
'-FetchServerDescriptors', '0'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
@@ -82,9 +97,6 @@ tor_process = subprocess.Popen([tor_path,
if tor_process == None:
fail('ERROR: running tor failed')
-if len(sys.argv) < 2:
- fail('Usage: %s <path-to-tor>' % sys.argv[0])
-
wait_for_log('Opened Control listener on')
try_connecting_to_socksport()
@@ -108,13 +120,13 @@ try_connecting_to_socksport()
control_socket.sendall('SIGNAL HALT\r\n'.encode('utf8'))
wait_for_log('exiting cleanly')
-print('OK')
+logging.info('OK')
try:
tor_process.terminate()
except OSError as e:
if e.errno == errno.ESRCH: # errno 3: No such process
# assume tor has already exited due to SIGNAL HALT
- print("Tor has already exited")
+ logging.warn("Tor has already exited")
else:
raise
diff --git a/src/test/test_rebind.sh b/src/test/test_rebind.sh
index 76eb9f2e4d..498072de35 100755
--- a/src/test/test_rebind.sh
+++ b/src/test/test_rebind.sh
@@ -14,6 +14,19 @@ fi
exitcode=0
-"${PYTHON:-python}" "${abs_top_srcdir:-.}/src/test/test_rebind.py" "${TESTING_TOR_BINARY}" || exitcode=1
+tmpdir=
+clean () { test -n "$tmpdir" && test -d "$tmpdir" && rm -rf "$tmpdir" || :; }
+trap clean EXIT HUP INT TERM
+
+tmpdir="`mktemp -d -t tor_rebind_test.XXXXXX`"
+if [ -z "$tmpdir" ]; then
+ echo >&2 mktemp failed
+ exit 2
+elif [ ! -d "$tmpdir" ]; then
+ echo >&2 mktemp failed to make a directory
+ exit 3
+fi
+
+"${PYTHON:-python}" "${abs_top_srcdir:-.}/src/test/test_rebind.py" "${TESTING_TOR_BINARY}" "$tmpdir" || exitcode=1
exit ${exitcode}
diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c
index 67af2fd484..1071a095fe 100644
--- a/src/test/test_routerlist.c
+++ b/src/test/test_routerlist.c
@@ -265,7 +265,9 @@ test_router_pick_directory_server_impl(void *arg)
/* Init SR subsystem. */
MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
- mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1,
+ strlen(AUTHORITY_CERT_1),
+ NULL);
sr_init(0);
UNMOCK(get_my_v3_authority_cert);
@@ -275,7 +277,9 @@ test_router_pick_directory_server_impl(void *arg)
construct_consensus(&consensus_text_md, now);
tt_assert(consensus_text_md);
- con_md = networkstatus_parse_vote_from_string(consensus_text_md, NULL,
+ con_md = networkstatus_parse_vote_from_string(consensus_text_md,
+ strlen(consensus_text_md),
+ NULL,
NS_TYPE_CONSENSUS);
tt_assert(con_md);
tt_int_op(con_md->flavor,OP_EQ, FLAV_MICRODESC);
@@ -475,7 +479,9 @@ test_directory_guard_fetch_with_no_dirinfo(void *arg)
/* 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);
+ mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1,
+ strlen(AUTHORITY_CERT_1),
+ NULL);
sr_init(0);
UNMOCK(get_my_v3_authority_cert);
@@ -648,7 +654,9 @@ test_skew_common(void *arg, time_t now, unsigned long *offset)
/* 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);
+ mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1,
+ strlen(AUTHORITY_CERT_1),
+ NULL);
sr_init(0);
UNMOCK(get_my_v3_authority_cert);
@@ -662,7 +670,8 @@ test_skew_common(void *arg, time_t now, unsigned long *offset)
MOCK(clock_skew_warning, mock_clock_skew_warning);
/* Caller will call teardown_capture_of_logs() */
setup_capture_of_logs(LOG_WARN);
- retval = networkstatus_set_current_consensus(consensus, "microdesc", 0,
+ retval = networkstatus_set_current_consensus(consensus, strlen(consensus),
+ "microdesc", 0,
NULL);
done:
diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c
index 8a7fb95cc1..433661f128 100644
--- a/src/test/test_shared_random.c
+++ b/src/test/test_shared_random.c
@@ -65,7 +65,9 @@ init_authority_state(void)
MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
or_options_t *options = get_options_mutable();
- mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1,
+ strlen(AUTHORITY_CERT_1),
+ NULL);
tt_assert(mock_cert);
options->AuthoritativeDir = 1;
tt_int_op(load_ed_keys(options, time(NULL)), OP_GE, 0);
@@ -421,7 +423,9 @@ test_sr_commit(void *arg)
{ /* Setup a minimal dirauth environment for this test */
or_options_t *options = get_options_mutable();
- auth_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ auth_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1,
+ strlen(AUTHORITY_CERT_1),
+ NULL);
tt_assert(auth_cert);
options->AuthoritativeDir = 1;
@@ -824,7 +828,9 @@ test_sr_setup_commits(void)
{ /* Setup a minimal dirauth environment for this test */
or_options_t *options = get_options_mutable();
- auth_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ auth_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1,
+ strlen(AUTHORITY_CERT_1),
+ NULL);
tt_assert(auth_cert);
options->AuthoritativeDir = 1;
diff --git a/src/test/test_util.c b/src/test/test_util.c
index 7bc1b7921a..bcface64fd 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -4024,6 +4024,13 @@ test_util_string_is_utf8(void *ptr)
tt_int_op(1, OP_EQ, string_is_utf8("ascii\x7f\n", 7));
tt_int_op(1, OP_EQ, string_is_utf8("Risqu\u00e9=1", 9));
+ /* Test the utf8_no_bom function */
+ tt_int_op(0, OP_EQ, string_is_utf8_no_bom("\uFEFF", 3));
+ tt_int_op(0, OP_EQ, string_is_utf8_no_bom("\uFFFE", 3));
+ tt_int_op(0, OP_EQ, string_is_utf8_no_bom("\uFEFFlove", 7));
+ tt_int_op(1, OP_EQ, string_is_utf8_no_bom("loveandrespect",
+ strlen("loveandrespect")));
+
// Validate exactly 'len' bytes.
tt_int_op(0, OP_EQ, string_is_utf8("\0\x80", 2));
tt_int_op(0, OP_EQ, string_is_utf8("Risqu\u00e9=1", 6));
@@ -5808,6 +5815,18 @@ test_util_ipv4_validation(void *arg)
}
static void
+test_util_ipv6_validation(void *arg)
+{
+ (void)arg;
+
+ tt_assert(string_is_valid_ipv6_address("2a00:1450:401b:800::200e"));
+ tt_assert(!string_is_valid_ipv6_address("11:22::33:44:"));
+
+ done:
+ return;
+}
+
+static void
test_util_writepid(void *arg)
{
(void) arg;
@@ -6498,6 +6517,7 @@ struct testcase_t util_tests[] = {
UTIL_TEST(hostname_validation, 0),
UTIL_TEST(dest_validation_edgecase, 0),
UTIL_TEST(ipv4_validation, 0),
+ UTIL_TEST(ipv6_validation, 0),
UTIL_TEST(writepid, 0),
UTIL_TEST(get_avail_disk_space, 0),
UTIL_TEST(touch_file, 0),
diff --git a/src/test/testing_common.c b/src/test/testing_common.c
index c52683afca..6d2db28f15 100644
--- a/src/test/testing_common.c
+++ b/src/test/testing_common.c
@@ -25,6 +25,8 @@
#include "lib/compress/compress.h"
#include "lib/evloop/compat_libevent.h"
#include "lib/crypt_ops/crypto_init.h"
+#include "lib/version/torversion.h"
+#include "app/main/subsysmgr.h"
#include <stdio.h>
#ifdef HAVE_FCNTL_H
@@ -230,12 +232,12 @@ void
tinytest_prefork(void)
{
free_pregenerated_keys();
- crypto_prefork();
+ subsystems_prefork();
}
void
tinytest_postfork(void)
{
- crypto_postfork();
+ subsystems_postfork();
init_pregenerated_keys();
}
@@ -250,24 +252,15 @@ main(int c, const char **v)
int loglevel = LOG_ERR;
int accel_crypto = 0;
- /* We must initialise logs before we call tor_assert() */
- init_logging(1);
+ subsystems_init_upto(SUBSYS_LEVEL_LIBS);
- update_approx_time(time(NULL));
options = options_new();
- tor_threads_init();
- tor_compress_init();
-
- network_init();
-
- monotime_init();
struct tor_libevent_cfg cfg;
memset(&cfg, 0, sizeof(cfg));
tor_libevent_initialize(&cfg);
control_initialize_event_queue();
- configure_backtrace_handler(get_version());
for (i_out = i = 1; i < c; ++i) {
if (!strcmp(v[i], "--warn")) {
@@ -294,6 +287,7 @@ main(int c, const char **v)
s.masks[LOG_WARN-LOG_ERR] |= LD_BUG;
add_stream_log(&s, "", fileno(stdout));
}
+ flush_log_messages_from_startup();
init_protocol_warning_severity_level();
options->command = CMD_RUN_UNITTESTS;
@@ -334,8 +328,6 @@ main(int c, const char **v)
free_pregenerated_keys();
- crypto_global_cleanup();
-
if (have_failed)
return 1;
else
diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h
index 94bcbbd4dc..cfc3bc9e9e 100644
--- a/src/win32/orconfig.h
+++ b/src/win32/orconfig.h
@@ -218,7 +218,7 @@
#define USING_TWOS_COMPLEMENT
/* Version number of package */
-#define VERSION "0.3.5.5-alpha-dev"
+#define VERSION "0.4.0.0-alpha-dev"