summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore10
-rw-r--r--ChangeLog491
-rw-r--r--Makefile.am11
-rw-r--r--README2
-rw-r--r--ReleaseNotes710
-rw-r--r--changes/assert_event_base5
-rw-r--r--changes/bug156092
-rw-r--r--changes/bug160234
-rw-r--r--changes/bug160564
-rw-r--r--changes/bug167024
-rw-r--r--changes/bug168257
-rw-r--r--changes/bug171517
-rw-r--r--changes/bug171543
-rw-r--r--changes/bug17173-socket-hack-rv3
-rw-r--r--changes/bug17237_0273
-rw-r--r--changes/bug172513
-rw-r--r--changes/bug173474
-rw-r--r--changes/bug173544
-rw-r--r--changes/bug173643
-rw-r--r--changes/bug173983
-rw-r--r--changes/bug174013
-rw-r--r--changes/bug174023
-rw-r--r--changes/bug174033
-rw-r--r--changes/bug174046
-rw-r--r--changes/bug175514
-rw-r--r--changes/bug175834
-rw-r--r--changes/bug176754
-rw-r--r--changes/bug177223
-rw-r--r--changes/bug1773210
-rw-r--r--changes/bug177727
-rw-r--r--changes/bug177813
-rw-r--r--changes/bug178183
-rw-r--r--changes/bug178194
-rw-r--r--changes/bug178273
-rw-r--r--changes/bug1785210
-rw-r--r--changes/bug179064
-rw-r--r--changes/bug179234
-rw-r--r--changes/bug180507
-rw-r--r--changes/bug180514
-rw-r--r--changes/bug180896
-rw-r--r--changes/bug181163
-rw-r--r--changes/bug181845
-rw-r--r--changes/bug182085
-rw-r--r--changes/bug182213
-rw-r--r--changes/bug182426
-rw-r--r--changes/bug1825812
-rw-r--r--changes/bug182594
-rw-r--r--changes/bug182616
-rw-r--r--changes/bug182964
-rw-r--r--changes/bug183485
-rw-r--r--changes/bug183743
-rw-r--r--changes/feature178409
-rw-r--r--changes/geoip-december20154
-rw-r--r--changes/geoip-january20164
-rw-r--r--changes/geoip-october20153
-rw-r--r--changes/ifaddrs-tests-network-configs5
-rw-r--r--changes/test_asan3
-rw-r--r--changes/ticket177444
-rw-r--r--changes/trunnel_update5
-rw-r--r--configure.ac249
-rw-r--r--contrib/win32build/tor-mingw.nsi.in2
-rw-r--r--doc/HACKING659
-rw-r--r--doc/HACKING/CodingStandards.md245
-rw-r--r--doc/HACKING/GettingStarted.md187
-rw-r--r--doc/HACKING/HelpfulTools.md293
-rw-r--r--doc/HACKING/HowToReview.md85
-rw-r--r--doc/HACKING/README.1st.md62
-rw-r--r--doc/HACKING/ReleasingTor.md143
-rw-r--r--doc/HACKING/WritingTests.md445
-rw-r--r--doc/WritingTests.txt273
-rw-r--r--doc/include.am12
-rw-r--r--doc/tor.1.txt268
-rw-r--r--scripts/codegen/get_mozilla_ciphers.py6
-rwxr-xr-xscripts/maint/analyze_callgraph.py50
-rwxr-xr-xscripts/maint/display_callgraph.py2
-rw-r--r--scripts/maint/fallback.blacklist41
-rw-r--r--scripts/maint/fallback.whitelist100
-rwxr-xr-xscripts/maint/lintChanges.py24
-rwxr-xr-xscripts/maint/sortChanges.py28
-rwxr-xr-xscripts/maint/updateFallbackDirs.py1257
-rw-r--r--src/common/address.c145
-rw-r--r--src/common/address.h70
-rw-r--r--src/common/aes.c183
-rw-r--r--src/common/aes.h3
-rw-r--r--src/common/backtrace.c7
-rw-r--r--src/common/compat.c235
-rw-r--r--src/common/compat.h27
-rw-r--r--src/common/compat_libevent.c36
-rw-r--r--src/common/compat_libevent.h37
-rw-r--r--src/common/compat_openssl.h46
-rw-r--r--src/common/container.c81
-rw-r--r--src/common/container.h66
-rw-r--r--src/common/crypto.c746
-rw-r--r--src/common/crypto.h64
-rw-r--r--src/common/crypto_curve25519.c16
-rw-r--r--src/common/crypto_ed25519.c26
-rw-r--r--src/common/crypto_ed25519.h5
-rw-r--r--src/common/di_ops.c4
-rw-r--r--src/common/include.am4
-rw-r--r--src/common/log.c44
-rw-r--r--src/common/memarea.c2
-rw-r--r--src/common/procmon.c3
-rw-r--r--src/common/sandbox.c4
-rw-r--r--src/common/torgzip.c4
-rw-r--r--src/common/torint.h24
-rw-r--r--src/common/torlog.h17
-rw-r--r--src/common/tortls.c454
-rw-r--r--src/common/tortls.h123
-rw-r--r--src/common/util.c181
-rw-r--r--src/common/util.h11
-rw-r--r--src/common/util_format.c2
-rw-r--r--src/common/util_process.c4
-rw-r--r--src/ext/README4
-rw-r--r--src/ext/csiphash.c57
-rw-r--r--src/ext/ed25519/donna/ed25519_tor.c3
-rw-r--r--src/ext/ed25519/ref10/randombytes.h2
-rw-r--r--src/ext/eventdns.c17
-rw-r--r--src/ext/ht.h26
-rw-r--r--src/ext/include.am13
-rw-r--r--src/ext/keccak-tiny/README.markdown82
-rw-r--r--src/ext/keccak-tiny/do.sh5
-rw-r--r--src/ext/keccak-tiny/keccak-tiny-unrolled.c368
-rw-r--r--src/ext/keccak-tiny/keccak-tiny.c163
-rw-r--r--src/ext/keccak-tiny/keccak-tiny.h66
-rw-r--r--src/ext/trunnel/trunnel-impl.h2
-rw-r--r--src/ext/trunnel/trunnel.c2
-rw-r--r--src/ext/trunnel/trunnel.h2
-rw-r--r--src/or/buffers.c30
-rw-r--r--src/or/channel.c4
-rw-r--r--src/or/channel.h2
-rw-r--r--src/or/channeltls.c32
-rw-r--r--src/or/circuitbuild.c58
-rw-r--r--src/or/circuitlist.c115
-rw-r--r--src/or/circuitmux.c20
-rw-r--r--src/or/circuitmux_ewma.c8
-rw-r--r--src/or/circuituse.c44
-rw-r--r--src/or/config.c423
-rw-r--r--src/or/config.h39
-rw-r--r--src/or/connection.c386
-rw-r--r--src/or/connection.h77
-rw-r--r--src/or/connection_edge.c235
-rw-r--r--src/or/connection_edge.h21
-rw-r--r--src/or/connection_or.c83
-rw-r--r--src/or/control.c93
-rw-r--r--src/or/control.h8
-rw-r--r--src/or/directory.c861
-rw-r--r--src/or/directory.h67
-rw-r--r--src/or/dirserv.c33
-rw-r--r--src/or/dirserv.h6
-rw-r--r--src/or/dirvote.c17
-rw-r--r--src/or/dirvote.h7
-rw-r--r--src/or/dns.c35
-rw-r--r--src/or/dns.h12
-rw-r--r--src/or/dnsserv.c2
-rw-r--r--src/or/entrynodes.c54
-rw-r--r--src/or/eventdns_tor.h3
-rw-r--r--src/or/ext_orport.c5
-rw-r--r--src/or/fallback_dirs.inc206
-rw-r--r--src/or/fp_pair.c4
-rw-r--r--src/or/geoip.c17
-rw-r--r--src/or/geoip.h1
-rw-r--r--src/or/hibernate.c22
-rw-r--r--src/or/hibernate.h1
-rw-r--r--src/or/include.am10
-rw-r--r--src/or/keypin.c8
-rw-r--r--src/or/main.c1001
-rw-r--r--src/or/main.h4
-rw-r--r--src/or/microdesc.c6
-rw-r--r--src/or/networkstatus.c396
-rw-r--r--src/or/networkstatus.h12
-rw-r--r--src/or/nodelist.c339
-rw-r--r--src/or/nodelist.h12
-rw-r--r--src/or/onion_fast.c8
-rw-r--r--src/or/or.h277
-rw-r--r--src/or/periodic.c120
-rw-r--r--src/or/periodic.h37
-rw-r--r--src/or/policies.c1249
-rw-r--r--src/or/policies.h82
-rw-r--r--src/or/relay.c16
-rw-r--r--src/or/rendcache.c179
-rw-r--r--src/or/rendcache.h37
-rw-r--r--src/or/rendclient.c47
-rw-r--r--src/or/rendcommon.c19
-rw-r--r--src/or/rendcommon.h2
-rw-r--r--src/or/rendmid.c9
-rw-r--r--src/or/rendservice.c154
-rw-r--r--src/or/rephist.c61
-rw-r--r--src/or/replaycache.c26
-rw-r--r--src/or/replaycache.h2
-rw-r--r--src/or/router.c251
-rw-r--r--src/or/router.h10
-rw-r--r--src/or/routerkeys.c4
-rw-r--r--src/or/routerlist.c466
-rw-r--r--src/or/routerlist.h21
-rw-r--r--src/or/routerparse.c63
-rw-r--r--src/or/routerparse.h3
-rw-r--r--src/or/routerset.c6
-rw-r--r--src/or/statefile.c7
-rw-r--r--src/or/status.c22
-rw-r--r--src/or/transports.c8
-rw-r--r--src/test/Makefile.nmake3
-rw-r--r--src/test/bench.c45
-rwxr-xr-xsrc/test/bt_test.py9
-rw-r--r--src/test/include.am51
-rw-r--r--src/test/log_test_helpers.c113
-rw-r--r--src/test/log_test_helpers.h56
-rw-r--r--src/test/rend_test_helpers.c73
-rw-r--r--src/test/rend_test_helpers.h15
-rw-r--r--src/test/test-memwipe.c2
-rw-r--r--src/test/test.c30
-rw-r--r--src/test/test_accounting.c26
-rw-r--r--src/test/test_addr.c1
-rw-r--r--src/test/test_address.c211
-rw-r--r--src/test/test_bt_cl.c1
-rw-r--r--src/test/test_channeltls.c2
-rw-r--r--src/test/test_circuitmux.c4
-rw-r--r--src/test/test_compat_libevent.c224
-rw-r--r--src/test/test_config.c1409
-rw-r--r--src/test/test_connection.c761
-rw-r--r--src/test/test_crypto.c644
-rw-r--r--src/test/test_dir.c1211
-rw-r--r--src/test/test_dir_common.c425
-rw-r--r--src/test/test_dir_common.h52
-rw-r--r--src/test/test_dir_handle_get.c2552
-rw-r--r--src/test/test_dns.c506
-rw-r--r--src/test/test_entrynodes.c226
-rw-r--r--src/test/test_extorport.c5
-rw-r--r--src/test/test_nodelist.c41
-rw-r--r--src/test/test_ntor_cl.c1
-rw-r--r--src/test/test_options.c4260
-rw-r--r--src/test/test_policy.c1288
-rw-r--r--src/test/test_procmon.c58
-rw-r--r--src/test/test_rendcache.c1340
-rw-r--r--src/test/test_replay.c34
-rw-r--r--src/test/test_routerlist.c354
-rw-r--r--src/test/test_routerset.c102
-rw-r--r--src/test/test_status.c15
-rw-r--r--src/test/test_switch_id.c191
-rwxr-xr-xsrc/test/test_switch_id.sh25
-rw-r--r--src/test/test_threads.c2
-rw-r--r--src/test/test_tortls.c2834
-rw-r--r--src/test/test_util.c320
-rw-r--r--src/test/test_util_format.c302
-rw-r--r--src/test/test_util_process.c82
-rw-r--r--src/test/test_workqueue.c10
-rw-r--r--src/test/testing_common.c15
-rw-r--r--src/test/vote_descriptors.inc94
-rw-r--r--src/tools/include.am3
-rw-r--r--src/trunnel/README21
-rw-r--r--src/trunnel/ed25519_cert.c8
-rw-r--r--src/trunnel/ed25519_cert.h2
-rw-r--r--src/trunnel/include.am4
-rw-r--r--src/trunnel/link_handshake.c20
-rw-r--r--src/trunnel/link_handshake.h2
-rw-r--r--src/trunnel/pwbox.c14
-rw-r--r--src/trunnel/pwbox.h2
-rw-r--r--src/win32/orconfig.h5
257 files changed, 32837 insertions, 4662 deletions
diff --git a/.gitignore b/.gitignore
index 135df7b204..b141e80e89 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,6 +27,12 @@
cscope.*
# OSX junk
*.dSYM
+.DS_Store
+# updateFallbackDirs.py temp files
+details-*.json
+uptime-*.json
+*.full_url
+*.last_modified
# /
/Makefile
@@ -148,6 +154,8 @@ cscope.*
/src/ext/ed25519/ref10/libed25519_ref10.lib
/src/ext/ed25519/donna/libed25519_donna.a
/src/ext/ed25519/donna/libed25519_donna.lib
+/src/ext/keccak-tiny/libkeccak-tiny.a
+/src/ext/keccak-tiny/libkeccak-tiny.lib
# /src/or/
/src/or/Makefile
@@ -171,6 +179,7 @@ cscope.*
/src/test/test-child
/src/test/test-memwipe
/src/test/test-ntor-cl
+/src/test/test-switch-id
/src/test/test_workqueue
/src/test/test.exe
/src/test/test-slow.exe
@@ -178,6 +187,7 @@ cscope.*
/src/test/test-child.exe
/src/test/test-ntor-cl.exe
/src/test/test-memwipe.exe
+/src/test/test-switch-id.exe
/src/test/test_workqueue.exe
/src/test/test_zero_length_keys.sh
/src/test/test_ntor.sh
diff --git a/ChangeLog b/ChangeLog
index 46005a9093..6ab821f9b3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,494 @@
+Changes in version 0.2.8.1-alpha - 2016-02-04
+ Tor 0.2.8.1-alpha is the first alpha release in its series. It
+ includes numerous small features and bugfixes against previous Tor
+ versions, and numerous small infrastructure improvements. The most
+ notable features are a set of improvements to the directory subsystem.
+
+ o Major features (security, Linux):
+ - When Tor starts as root on Linux and is told to switch user ID, it
+ can now retain the capability to bind to low ports. By default,
+ Tor will do this only when it's switching user ID and some low
+ ports have been configured. You can change this behavior with the
+ new option KeepBindCapabilities. Closes ticket 8195.
+
+ o Major features (directory system):
+ - When bootstrapping multiple consensus downloads at a time, use the
+ first one that starts downloading, and close the rest. This
+ reduces failures when authorities or fallback directories are slow
+ or down. Together with the code for feature 15775, this feature
+ should reduces failures due to fallback churn. Implements ticket
+ 4483. Patch by "teor". Implements IPv4 portions of proposal 210 by
+ "mikeperry" and "teor".
+ - Include a trial list of default fallback directories, based on an
+ opt-in survey of suitable relays. Doing this should make clients
+ bootstrap more quickly and reliably, and reduce the load on the
+ directory authorities. Closes ticket 15775. Patch by "teor".
+ Candidates identified using an OnionOO script by "weasel", "teor",
+ "gsathya", and "karsten".
+ - Previously only relays that explicitly opened a directory port
+ (DirPort) accepted directory requests from clients. Now all
+ relays, with and without a DirPort, accept and serve tunneled
+ directory requests that they receive through their ORPort. You can
+ disable this behavior using the new DirCache option. Closes
+ ticket 12538.
+
+ o Major key updates:
+ - Update the V3 identity key for the dannenberg directory authority:
+ it was changed on 18 November 2015. Closes task 17906. Patch
+ by "teor".
+
+ o Minor features (security, clock):
+ - Warn when the system clock appears to move back in time (when the
+ state file was last written in the future). Tor doesn't know that
+ consensuses have expired if the clock is in the past. Patch by
+ "teor". Implements ticket 17188.
+
+ o Minor features (security, exit policies):
+ - ExitPolicyRejectPrivate now rejects more private addresses by
+ default. Specifically, it now rejects the relay's outbound bind
+ addresses (if configured), and the relay's configured port
+ addresses (such as ORPort and DirPort). Fixes bug 17027; bugfix on
+ 0.2.0.11-alpha. Patch by "teor".
+
+ o Minor features (security, memory erasure):
+ - Set the unused entries in a smartlist to NULL. This helped catch
+ a (harmless) bug, and shouldn't affect performance too much.
+ Implements ticket 17026.
+ - Use SecureMemoryWipe() function to securely clean memory on
+ Windows. Previously we'd use OpenSSL's OPENSSL_cleanse() function.
+ Implements feature 17986.
+ - Use explicit_bzero or memset_s when present. Previously, we'd use
+ OpenSSL's OPENSSL_cleanse() function. Closes ticket 7419; patches
+ from <logan@hackers.mu> and <selven@hackers.mu>.
+ - Make memwipe() do nothing when passed a NULL pointer or buffer of
+ zero size. Check size argument to memwipe() for underflow. Fixes
+ bug 18089; bugfix on 0.2.3.25 and 0.2.4.6-alpha. Reported by "gk",
+ patch by "teor".
+
+ o Minor features (security, RNG):
+ - Adjust Tor's use of OpenSSL's RNG APIs so that they absolutely,
+ positively are not allowed to fail. Previously we depended on
+ internal details of OpenSSL's behavior. Closes ticket 17686.
+ - Never use the system entropy output directly for anything besides
+ seeding the PRNG. When we want to generate important keys, instead
+ of using system entropy directly, we now hash it with the PRNG
+ stream. This may help resist certain attacks based on broken OS
+ entropy implementations. Closes part of ticket 17694.
+ - Use modern system calls (like getentropy() or getrandom()) to
+ generate strong entropy on platforms that have them. Closes
+ ticket 13696.
+
+ o Minor features (accounting):
+ - Added two modes to the AccountingRule option: One for limiting
+ only the number of bytes sent ("AccountingRule out"), and one for
+ limiting only the number of bytes received ("AccountingRule in").
+ Closes ticket 15989; patch from "unixninja92".
+
+ o Minor features (build):
+ - Since our build process now uses "make distcheck", we no longer
+ force "make dist" to depend on "make check". Closes ticket 17893;
+ patch from "cypherpunks."
+ - Tor now builds successfully with the recent OpenSSL 1.1
+ development branch, and with the latest LibreSSL. Closes tickets
+ 17549, 17921, and 17984.
+
+ o Minor features (controller):
+ - Adds the FallbackDir entries to 'GETINFO config/defaults'. Closes
+ tickets 16774 and 17817. Patch by George Tankersley.
+ - New 'GETINFO hs/service/desc/id/' command to retrieve a hidden
+ service descriptor from a service's local hidden service
+ descriptor cache. Closes ticket 14846.
+ - Add 'GETINFO exit-policy/reject-private/[default,relay]', so
+ controllers can examine the the reject rules added by
+ ExitPolicyRejectPrivate. This makes it easier for stem to display
+ exit policies.
+
+ o Minor features (crypto):
+ - Add SHA512 support to crypto.c. Closes ticket 17663; patch from
+ George Tankersley.
+ - Add SHA3 and SHAKE support to crypto.c. Closes ticket 17783.
+ - When allocating a digest state object, allocate no more space than
+ we actually need. Previously, we would allocate as much space as
+ the state for the largest algorithm would need. This change saves
+ up to 672 bytes per circuit. Closes ticket 17796.
+ - Improve performance when hashing non-multiple of 8 sized buffers,
+ based on Andrew Moon's public domain SipHash-2-4 implementation.
+ Fixes bug 17544; bugfix on 0.2.5.3-alpha.
+
+ o Minor features (directory downloads):
+ - Wait for busy authorities and fallback directories to become non-
+ busy when bootstrapping. (A similar change was made in 6c443e987d
+ for directory caches chosen from the consensus.) Closes ticket
+ 17864; patch by "teor".
+ - Add UseDefaultFallbackDirs, which enables any hard-coded fallback
+ directory mirrors. The default is 1; set it to 0 to disable
+ fallbacks. Implements ticket 17576. Patch by "teor".
+
+ o Minor features (geoip):
+ - Update geoip and geoip6 to the January 5 2016 Maxmind GeoLite2
+ Country database.
+
+ o Minor features (IPv6):
+ - Add an argument 'ipv6=address:orport' to the DirAuthority and
+ FallbackDir torrc options, to specify an IPv6 address for an
+ authority or fallback directory. Add hard-coded ipv6 addresses for
+ directory authorities that have them. Closes ticket 17327; patch
+ from Nick Mathewson and "teor".
+ - Add address policy assume_action support for IPv6 addresses.
+ - Limit IPv6 mask bits to 128.
+ - Warn when comparing against an AF_UNSPEC address in a policy, it's
+ almost always a bug. Closes ticket 17863; patch by "teor".
+ - Allow users to configure directory authorities and fallback
+ directory servers with IPv6 addresses and ORPorts. Resolves
+ ticket 6027.
+ - routerset_parse now accepts IPv6 literal addresses. Fixes bug
+ 17060; bugfix on 0.2.1.3-alpha. Patch by "teor".
+ - Make tor_ersatz_socketpair work on IPv6-only systems. Fixes bug
+ 17638; bugfix on 0.0.2pre8. Patch by "teor".
+
+ o Minor features (logging):
+ - When logging to syslog, allow a tag to be added to the syslog
+ identity (the string prepended to every log message). The tag can
+ be configured with SyslogIdentityTag and defaults to none. Setting
+ it to "foo" will cause logs to be tagged as "Tor-foo". Closes
+ ticket 17194.
+
+ o Minor features (portability):
+ - Use timingsafe_memcmp() where available. Closes ticket 17944;
+ patch from <logan@hackers.mu>.
+
+ o Minor features (relay, address discovery):
+ - Add a family argument to get_interface_addresses_raw() and
+ subfunctions to make network interface address interogation more
+ efficient. Now Tor can specifically ask for IPv4, IPv6 or both
+ types of interfaces from the operating system. Resolves
+ ticket 17950.
+ - When get_interface_address6_list(.,AF_UNSPEC,.) is called and
+ fails to enumerate interface addresses using the platform-specific
+ API, have it rely on the UDP socket fallback technique to try and
+ find out what IP addresses (both IPv4 and IPv6) our machine has.
+ Resolves ticket 17951.
+
+ o Minor features (replay cache):
+ - The replay cache now uses SHA256 instead of SHA1. Implements
+ feature 8961. Patch by "teor", issue reported by "rransom".
+
+ o Minor features (unix file permissions):
+ - Defer creation of Unix sockets until after setuid. This avoids
+ needing CAP_CHOWN and CAP_FOWNER when using systemd's
+ CapabilityBoundingSet, or chown and fowner when using SELinux.
+ Implements part of ticket 17562. Patch from Jamie Nguyen.
+ - If any directory created by Tor is marked as group readable, the
+ filesystem group is allowed to be either the default GID or the
+ root user. Allowing root to read the DataDirectory prevents the
+ need for CAP_READ_SEARCH when using systemd's
+ CapabilityBoundingSet, or dac_read_search when using SELinux.
+ Implements part of ticket 17562. Patch from Jamie Nguyen.
+ - Introduce a new DataDirectoryGroupReadable option. If it is set to
+ 1, the DataDirectory will be made readable by the default GID.
+ Implements part of ticket 17562. Patch from Jamie Nguyen.
+
+ o Minor bugfixes (accounting):
+ - The max bandwidth when using 'AccountRule sum' is now correctly
+ logged. Fixes bug 18024; bugfix on 0.2.6.1-alpha. Patch
+ from "unixninja92".
+
+ o Minor bugfixes (code correctness):
+ - When closing an entry connection, generate a warning if we should
+ have sent an end cell for it but we haven't. Fixes bug 17876;
+ bugfix on 0.2.3.2-alpha.
+ - Assert that allocated memory held by the reputation code is freed
+ according to its internal counters. Fixes bug 17753; bugfix
+ on tor-0.1.1.1-alpha.
+ - Assert when the TLS contexts fail to initialize. Fixes bug 17683;
+ bugfix on 0.0.6.
+
+ o Minor bugfixes (compilation):
+ - Mark all object files that include micro-revision.i as depending
+ on it, so as to make parallel builds more reliable. Fixes bug
+ 17826; bugfix on 0.2.5.1-alpha.
+ - Don't try to use the pthread_condattr_setclock() function unless
+ it actually exists. Fixes compilation on NetBSD-6.x. Fixes bug
+ 17819; bugfix on 0.2.6.3-alpha.
+ - Fix backtrace compilation on FreeBSD. Fixes bug 17827; bugfix
+ on tor-0.2.5.2-alpha.
+ - Fix compilation of sandbox.c with musl-libc. Fixes bug 17347;
+ bugfix on 0.2.5.1-alpha. Patch from 'jamestk'.
+ - Fix search for libevent libraries on OpenBSD (and other systems
+ that install libevent 1 and libevent 2 in parallel). Fixes bug
+ 16651; bugfix on 0.1.0.7-rc. Patch from "rubiate".
+ - Isolate environment variables meant for tests from the rest of the
+ build system. Fixes bug 17818; bugfix on tor-0.2.7.3-rc.
+ - Replace usage of 'INLINE' with 'inline'. Fixes bug 17804; bugfix
+ on tor-0.0.2pre8.
+ - Remove config.log only from make distclean, not from make clean.
+ Fixes bug 17924; bugfix on 0.2.4.1-alpha.
+
+ o Minor bugfixes (crypto):
+ - Check the return value of HMAC() and assert on failure. Fixes bug
+ 17658; bugfix on 0.2.3.6-alpha. Patch by "teor".
+
+ o Minor bugfixes (fallback directories):
+ - Mark fallbacks as "too busy" when they return a 503 response,
+ rather than just marking authorities. Fixes bug 17572; bugfix on
+ 0.2.4.7-alpha. Patch by "teor".
+
+ o Minor bugfixes (IPv6):
+ - Update the limits in max_dl_per_request for IPv6 address length.
+ Fixes bug 17573; bugfix on 0.2.1.5-alpha.
+
+ o Minor bugfixes (linux seccomp2 sandbox):
+ - Fix a crash when using offline master ed25519 keys with the Linux
+ seccomp2 sandbox enabled. Fixes bug 17675; bugfix on 0.2.7.3-alpha.
+
+ o Minor bugfixes (logging):
+ - In log messages that include a function name, use __FUNCTION__
+ instead of __PRETTY_FUNCTION__. In GCC, these are synonymous, but
+ with clang __PRETTY_FUNCTION__ has extra information we don't
+ need. Fixes bug 16563; bugfix on 0.0.2pre8. Fix by Tom van
+ der Woerdt.
+ - Remove needless quotes from a log message about unparseable
+ addresses. Fixes bug 17843; bugfix on 0.2.3.3-alpha.
+
+ o Minor bugfixes (portability):
+ - Remove an #endif from configure.ac so that we correctly detect the
+ presence of in6_addr.s6_addr32. Fixes bug 17923; bugfix
+ on 0.2.0.13-alpha.
+
+ o Minor bugfixes (relays):
+ - Check that both the ORPort and DirPort (if present) are reachable
+ before publishing a relay descriptor. Otherwise, relays publish a
+ descriptor with DirPort 0 when the DirPort reachability test takes
+ longer than the ORPort reachability test. Fixes bug 18050; bugfix
+ on 0.1.0.1-rc. Reported by "starlight", patch by "teor".
+
+ o Minor bugfixes (relays, hidden services):
+ - Refuse connection requests to private OR addresses unless
+ ExtendAllowPrivateAddresses is set. Previously, tor would connect,
+ then refuse to send any cells to a private address. Fixes bugs
+ 17674 and 8976; bugfix on 0.2.3.21-rc. Patch by "teor".
+
+ o Minor bugfixes (safe logging):
+ - When logging a malformed hostname received through socks4, scrub
+ it if SafeLogging says we should. Fixes bug 17419; bugfix
+ on 0.1.1.16-rc.
+
+ o Minor bugfixes (statistics code):
+ - Consistently check for overflow in round_*_to_next_multiple_of
+ functions, and add unit tests with additional and maximal values.
+ Fixes part of bug 13192; bugfix on 0.2.2.1-alpha.
+ - Handle edge cases in the laplace functions: avoid division by
+ zero, avoid taking the log of zero, and silence clang type
+ conversion warnings using round and trunc. Add unit tests for edge
+ cases with maximal values. Fixes part of bug 13192; bugfix
+ on 0.2.6.2-alpha.
+
+ o Minor bugfixes (testing):
+ - The test for log_heartbeat was incorrectly failing in timezones
+ with non-integer offsets. Instead of comparing the end of the time
+ string against a constant, compare it to the output of
+ format_local_iso_time when given the correct input. Fixes bug
+ 18039; bugfix on 0.2.5.4-alpha.
+ - Make unit tests pass on IPv6-only systems, and systems without
+ localhost addresses (like some FreeBSD jails). Fixes bug 17632;
+ bugfix on 0.2.7.3-rc. Patch by "teor".
+ - Fix a memory leak in the ntor test. Fixes bug 17778; bugfix
+ on 0.2.4.8-alpha.
+ - Check the full results of SHA256 and SHA512 digests in the unit
+ tests. Bugfix on 0.2.2.4-alpha. Patch by "teor".
+
+ o Code simplification and refactoring:
+ - Move logging of redundant policy entries in
+ policies_parse_exit_policy_internal into its own function. Closes
+ ticket 17608; patch from "juce".
+ - Extract the more complicated parts of circuit_mark_for_close()
+ into a new function that we run periodically before circuits are
+ freed. This change removes more than half of the functions
+ currently in the "blob". Closes ticket 17218.
+ - Clean up a little duplicated code in
+ crypto_expand_key_material_TAP(). Closes ticket 17587; patch
+ from "pfrankw".
+ - Decouple the list of streams waiting to be attached to circuits
+ from the overall connection list. This change makes it possible to
+ attach streams quickly while simplifying Tor's callgraph and
+ avoiding O(N) scans of the entire connection list. Closes
+ ticket 17590.
+ - When a direct directory request fails immediately on launch,
+ instead of relaunching that request from inside the code that
+ launches it, instead mark the connection for teardown. This change
+ simplifies Tor's callback and prevents the directory-request
+ launching code from invoking itself recursively. Closes
+ ticket 17589
+ - Remove code for configuring OpenSSL dynamic locks; OpenSSL doesn't
+ use them. Closes ticket 17926.
+
+ o Documentation:
+ - Add a description of the correct use of the '--keygen' command-
+ line option. Closes ticket 17583; based on text by 's7r'.
+ - Document the minimum HeartbeatPeriod value. Closes ticket 15638.
+ - Explain actual minima for BandwidthRate. Closes ticket 16382.
+ - Fix a minor formatting typo in the manpage. Closes ticket 17791.
+ - Mention torspec URL in the manpage and point the reader to it
+ whenever we mention a document that belongs in torspce. Fixes
+ issue 17392.
+
+ o Removed features:
+ - Remove client-side support for connecting to Tor relays running
+ versions of Tor before 0.2.3.6-alpha. These relays didn't support
+ the v3 TLS handshake protocol, and are no longer allowed on the
+ Tor network. Implements the client side of ticket 11150. Based on
+ patches by Tom van der Woerdt.
+
+ o Testing:
+ - Add unit tests to check for common RNG failure modes, such as
+ returning all zeroes, identical values, or incrementing values
+ (OpenSSL's rand_predictable feature). Patch by "teor".
+ - Log more information when the backtrace tests fail. Closes ticket
+ 17892. Patch from "cypherpunks."
+ - Always test both ed25519 backends, so that we can be sure that our
+ batch-open replacement code works. Part of ticket 16794.
+ - Cover dns_resolve_impl() in dns.c with unit tests. Implements a
+ portion of ticket 16831.
+ - More unit tests for compat_libevent.c, procmon.c, tortls.c,
+ util_format.c, directory.c, and options_validate.c. Closes tickets
+ 17075, 17082, 17084, 17003, and 17076 respectively. Patches from
+ Ola Bini.
+ - Unit tests for directory_handle_command_get. Closes ticket 17004.
+ Patch from Reinaldo de Souza Jr.
+
+
+Changes in version 0.2.7.6 - 2015-12-10
+ Tor version 0.2.7.6 fixes a major bug in entry guard selection, as
+ well as a minor bug in hidden service reliability.
+
+ o Major bugfixes (guard selection):
+ - Actually look at the Guard flag when selecting a new directory
+ guard. When we implemented the directory guard design, we
+ accidentally started treating all relays as if they have the Guard
+ flag during guard selection, leading to weaker anonymity and worse
+ performance. Fixes bug 17772; bugfix on 0.2.4.8-alpha. Discovered
+ by Mohsen Imani.
+
+ o Minor features (geoip):
+ - Update geoip and geoip6 to the December 1 2015 Maxmind GeoLite2
+ Country database.
+
+ o Minor bugfixes (compilation):
+ - When checking for net/pfvar.h, include netinet/in.h if possible.
+ This fixes transparent proxy detection on OpenBSD. Fixes bug
+ 17551; bugfix on 0.1.2.1-alpha. Patch from "rubiate".
+ - Fix a compilation warning with Clang 3.6: Do not check the
+ presence of an address which can never be NULL. Fixes bug 17781.
+
+ o Minor bugfixes (correctness):
+ - When displaying an IPv6 exit policy, include the mask bits
+ correctly even when the number is greater than 31. Fixes bug
+ 16056; bugfix on 0.2.4.7-alpha. Patch from "gturner".
+ - The wrong list was used when looking up expired intro points in a
+ rend service object, causing what we think could be reachability
+ issues for hidden services, and triggering a BUG log. Fixes bug
+ 16702; bugfix on 0.2.7.2-alpha.
+ - Fix undefined behavior in the tor_cert_checksig function. Fixes
+ bug 17722; bugfix on 0.2.7.2-alpha.
+
+
+Changes in version 0.2.7.5 - 2015-11-20
+ The Tor 0.2.7 release series is dedicated to the memory of Tor user
+ and privacy advocate Caspar Bowden (1961-2015). Caspar worked
+ tirelessly to advocate human rights regardless of national borders,
+ and oppose the encroachments of mass surveillance. He opposed national
+ exceptionalism, he brought clarity to legal and policy debates, he
+ understood and predicted the impact of mass surveillance on the world,
+ and he laid the groundwork for resisting it. While serving on the Tor
+ Project's board of directors, he brought us his uncompromising focus
+ on technical excellence in the service of humankind. Caspar was an
+ inimitable force for good and a wonderful friend. He was kind,
+ humorous, generous, gallant, and believed we should protect one
+ another without exception. We honor him here for his ideals, his
+ efforts, and his accomplishments. Please honor his memory with works
+ that would make him proud.
+
+ Tor 0.2.7.5 is the first stable release in the Tor 0.2.7 series.
+
+ The 0.2.7 series adds a more secure identity key type for relays,
+ improves cryptography performance, resolves several longstanding
+ hidden-service performance issues, improves controller support for
+ hidden services, and includes small bugfixes and performance
+ improvements throughout the program. This release series also includes
+ more tests than before, and significant simplifications to which parts
+ of Tor invoke which others.
+
+ (This release contains no code changes since 0.2.7.4-rc.)
+
+
+Changes in version 0.2.7.4-rc - 2015-10-21
+ Tor 0.2.7.4-rc is the second release candidate in the 0.2.7 series. It
+ fixes some important memory leaks, and a scary-looking (but mostly
+ harmless in practice) invalid-read bug. It also has a few small
+ bugfixes, notably fixes for compilation and portability on different
+ platforms. If no further significant bounds are found, the next
+ release will the the official stable release.
+
+ o Major bugfixes (security, correctness):
+ - Fix an error that could cause us to read 4 bytes before the
+ beginning of an openssl string. This bug could be used to cause
+ Tor to crash on systems with unusual malloc implementations, or
+ systems with unusual hardening installed. Fixes bug 17404; bugfix
+ on 0.2.3.6-alpha.
+
+ o Major bugfixes (correctness):
+ - Fix a use-after-free bug in validate_intro_point_failure(). Fixes
+ bug 17401; bugfix on 0.2.7.3-rc.
+
+ o Major bugfixes (memory leaks):
+ - Fix a memory leak in ed25519 batch signature checking. Fixes bug
+ 17398; bugfix on 0.2.6.1-alpha.
+ - Fix a memory leak in rend_cache_failure_entry_free(). Fixes bug
+ 17402; bugfix on 0.2.7.3-rc.
+ - Fix a memory leak when reading an expired signing key from disk.
+ Fixes bug 17403; bugfix on 0.2.7.2-rc.
+
+ o Minor features (geoIP):
+ - Update geoip and geoip6 to the October 9 2015 Maxmind GeoLite2
+ Country database.
+
+ o Minor bugfixes (compilation):
+ - Repair compilation with the most recent (unreleased, alpha)
+ vesions of OpenSSL 1.1. Fixes part of ticket 17237.
+ - Fix an integer overflow warning in test_crypto_slow.c. Fixes bug
+ 17251; bugfix on 0.2.7.2-alpha.
+ - Fix compilation of sandbox.c with musl-libc. Fixes bug 17347;
+ bugfix on 0.2.5.1-alpha. Patch from 'jamestk'.
+
+ o Minor bugfixes (portability):
+ - Use libexecinfo on FreeBSD to enable backtrace support. Fixes
+ part of bug 17151; bugfix on 0.2.5.2-alpha. Patch from
+ Marcin Cieślak.
+
+ o Minor bugfixes (sandbox):
+ - Add the "hidserv-stats" filename to our sandbox filter for the
+ HiddenServiceStatistics option to work properly. Fixes bug 17354;
+ bugfix on tor-0.2.6.2-alpha. Patch from David Goulet.
+
+ o Minor bugfixes (testing):
+ - Add unit tests for get_interface_address* failure cases. Fixes bug
+ 17173; bugfix on 0.2.7.3-rc. Patch by fk/teor.
+ - Fix breakage when running 'make check' with BSD make. Fixes bug
+ 17154; bugfix on 0.2.7.3-rc. Patch by Marcin Cieślak.
+ - Make the get_ifaddrs_* unit tests more tolerant of different
+ network configurations. (Don't assume every test box has an IPv4
+ address, and don't assume every test box has a non-localhost
+ address.) Fixes bug 17255; bugfix on 0.2.7.3-rc. Patch by "teor".
+ - Skip backtrace tests when backtrace support is not compiled in.
+ Fixes part of bug 17151; bugfix on 0.2.7.1-alpha. Patch from
+ Marcin Cieślak.
+
+ o Documentation:
+ - Fix capitalization of SOCKS in sample torrc. Closes ticket 15609.
+ - Note that HiddenServicePorts can take a unix domain socket. Closes
+ ticket 17364.
+
+
Changes in version 0.2.7.3-rc - 2015-09-25
Tor 0.2.7.3-rc is the first release candidate in the 0.2.7 series. It
contains numerous usability fixes for Ed25519 keys, safeguards against
diff --git a/Makefile.am b/Makefile.am
index beea69d95e..fe6f64bb66 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,8 +4,9 @@
# See LICENSE for licensing information
# "foreign" means we don't follow GNU package layout standards
-# 1.9 means we require automake vesion 1.9
-AUTOMAKE_OPTIONS = foreign 1.9 subdir-objects
+# 1.11 means we require automake vesion 1.11
+# "subdir-objects" means put .o files in the same directory as the .c files
+AUTOMAKE_OPTIONS = foreign 1.11 subdir-objects
ACLOCAL_AMFLAGS = -I m4
@@ -42,7 +43,11 @@ EXTRA_DIST+= \
if COVERAGE_ENABLED
TEST_CFLAGS=-fno-inline -fprofile-arcs -ftest-coverage
+if DISABLE_ASSERTS_IN_UNIT_TESTS
+TEST_CPPFLAGS=-DTOR_UNIT_TESTS -DTOR_COVERAGE -DDISABLE_ASSERTS_IN_UNIT_TESTS
+else
TEST_CPPFLAGS=-DTOR_UNIT_TESTS -DTOR_COVERAGE
+endif
TEST_NETWORK_FLAGS=--coverage --hs-multi-client 1
else
TEST_CFLAGS=
@@ -73,8 +78,6 @@ dist-rpm: dist-gzip
echo "RPM build finished"; \
#end of dist-rpm
-dist: check
-
doxygen:
doxygen && cd doc/doxygen/latex && make
diff --git a/README b/README
index 342376faf4..d246a6930e 100644
--- a/README
+++ b/README
@@ -26,4 +26,4 @@ Frequently Asked Questions:
To get started working on Tor development:
- See the doc/HACKING file.
+ See the doc/HACKING directory.
diff --git a/ReleaseNotes b/ReleaseNotes
index 44cda49b2d..1e9f7f2e8a 100644
--- a/ReleaseNotes
+++ b/ReleaseNotes
@@ -1,7 +1,715 @@
-
This document summarizes new features and bugfixes in each stable release
of Tor. If you want to see more detailed descriptions of the changes in
each development snapshot, see the ChangeLog file.
+
+Changes in version 0.2.7.6 - 2015-12-10
+ Tor version 0.2.7.6 fixes a major bug in entry guard selection, as
+ well as a minor bug in hidden service reliability.
+
+ o Major bugfixes (guard selection):
+ - Actually look at the Guard flag when selecting a new directory
+ guard. When we implemented the directory guard design, we
+ accidentally started treating all relays as if they have the Guard
+ flag during guard selection, leading to weaker anonymity and worse
+ performance. Fixes bug 17772; bugfix on 0.2.4.8-alpha. Discovered
+ by Mohsen Imani.
+
+ o Minor features (geoip):
+ - Update geoip and geoip6 to the December 1 2015 Maxmind GeoLite2
+ Country database.
+
+ o Minor bugfixes (compilation):
+ - When checking for net/pfvar.h, include netinet/in.h if possible.
+ This fixes transparent proxy detection on OpenBSD. Fixes bug
+ 17551; bugfix on 0.1.2.1-alpha. Patch from "rubiate".
+ - Fix a compilation warning with Clang 3.6: Do not check the
+ presence of an address which can never be NULL. Fixes bug 17781.
+
+ o Minor bugfixes (correctness):
+ - When displaying an IPv6 exit policy, include the mask bits
+ correctly even when the number is greater than 31. Fixes bug
+ 16056; bugfix on 0.2.4.7-alpha. Patch from "gturner".
+ - The wrong list was used when looking up expired intro points in a
+ rend service object, causing what we think could be reachability
+ issues for hidden services, and triggering a BUG log. Fixes bug
+ 16702; bugfix on 0.2.7.2-alpha.
+ - Fix undefined behavior in the tor_cert_checksig function. Fixes
+ bug 17722; bugfix on 0.2.7.2-alpha.
+
+
+Changes in version 0.2.7.5 - 2015-11-20
+ The Tor 0.2.7 release series is dedicated to the memory of Tor user
+ and privacy advocate Caspar Bowden (1961-2015). Caspar worked
+ tirelessly to advocate human rights regardless of national borders,
+ and oppose the encroachments of mass surveillance. He opposed national
+ exceptionalism, he brought clarity to legal and policy debates, he
+ understood and predicted the impact of mass surveillance on the world,
+ and he laid the groundwork for resisting it. While serving on the Tor
+ Project's board of directors, he brought us his uncompromising focus
+ on technical excellence in the service of humankind. Caspar was an
+ inimitable force for good and a wonderful friend. He was kind,
+ humorous, generous, gallant, and believed we should protect one
+ another without exception. We honor him here for his ideals, his
+ efforts, and his accomplishments. Please honor his memory with works
+ that would make him proud.
+
+ Tor 0.2.7.5 is the first stable release in the Tor 0.2.7 series.
+
+ The 0.2.7 series adds a more secure identity key type for relays,
+ improves cryptography performance, resolves several longstanding
+ hidden-service performance issues, improves controller support for
+ hidden services, and includes small bugfixes and performance
+ improvements throughout the program. This release series also includes
+ more tests than before, and significant simplifications to which parts
+ of Tor invoke which others. For a full list of changes, see below.
+
+ o New system requirements:
+ - Tor no longer includes workarounds to support Libevent versions
+ before 1.3e. Libevent 2.0 or later is recommended. Closes
+ ticket 15248.
+ - Tor no longer supports copies of OpenSSL that are missing support
+ for Elliptic Curve Cryptography. (We began using ECC when
+ available in 0.2.4.8-alpha, for more safe and efficient key
+ negotiation.) In particular, support for at least one of P256 or
+ P224 is now required, with manual configuration needed if only
+ P224 is available. Resolves ticket 16140.
+ - Tor no longer supports versions of OpenSSL before 1.0. (If you are
+ on an operating system that has not upgraded to OpenSSL 1.0 or
+ later, and you compile Tor from source, you will need to install a
+ more recent OpenSSL to link Tor against.) These versions of
+ OpenSSL are still supported by the OpenSSL, but the numerous
+ cryptographic improvements in later OpenSSL releases makes them a
+ clear choice. Resolves ticket 16034.
+
+ o Major features (controller):
+ - Add the ADD_ONION and DEL_ONION commands that allow the creation
+ and management of hidden services via the controller. Closes
+ ticket 6411.
+ - New "GETINFO onions/current" and "GETINFO onions/detached"
+ commands to get information about hidden services created via the
+ controller. Part of ticket 6411.
+ - New HSFETCH command to launch a request for a hidden service
+ descriptor. Closes ticket 14847.
+ - New HSPOST command to upload a hidden service descriptor. Closes
+ ticket 3523. Patch by "DonnchaC".
+
+ o Major features (Ed25519 identity keys, Proposal 220):
+ - Add support for offline encrypted Ed25519 master keys. To use this
+ feature on your tor relay, run "tor --keygen" to make a new master
+ key (or to make a new signing key if you already have a master
+ key). Closes ticket 13642.
+ - All relays now maintain a stronger identity key, using the Ed25519
+ elliptic curve signature format. This master key is designed so
+ that it can be kept offline. Relays also generate an online
+ signing key, and a set of other Ed25519 keys and certificates.
+ These are all automatically regenerated and rotated as needed.
+ Implements part of ticket 12498.
+ - Directory authorities now vote on Ed25519 identity keys along with
+ RSA1024 keys. Implements part of ticket 12498.
+ - Directory authorities track which Ed25519 identity keys have been
+ used with which RSA1024 identity keys, and do not allow them to
+ vary freely. Implements part of ticket 12498.
+ - Microdescriptors now include Ed25519 identity keys. Implements
+ part of ticket 12498.
+ - Add a --newpass option to allow changing or removing the
+ passphrase of an encrypted key with tor --keygen. Implements part
+ of ticket 16769.
+ - Add a new OfflineMasterKey option to tell Tor never to try loading
+ or generating a secret Ed25519 identity key. You can use this in
+ combination with tor --keygen to manage offline and/or encrypted
+ Ed25519 keys. Implements ticket 16944.
+ - On receiving a HUP signal, check to see whether the Ed25519
+ signing key has changed, and reload it if so. Closes ticket 16790.
+ - Significant usability improvements for Ed25519 key management. Log
+ messages are better, and the code can recover from far more
+ failure conditions. Thanks to "s7r" for reporting and diagnosing
+ so many of these!
+
+ o Major features (ECC performance):
+ - Improve the runtime speed of Ed25519 signature verification by
+ using Ed25519-donna's batch verification support. Implements
+ ticket 16533.
+ - Improve the speed of Ed25519 operations and Curve25519 keypair
+ generation when built targeting 32 bit x86 platforms with SSE2
+ available. Implements ticket 16535.
+ - Improve the runtime speed of Ed25519 operations by using the
+ public-domain Ed25519-donna by Andrew M. ("floodyberry").
+ Implements ticket 16467.
+ - Improve the runtime speed of the ntor handshake by using an
+ optimized curve25519 basepoint scalarmult implementation from the
+ public-domain Ed25519-donna by Andrew M. ("floodyberry"), based on
+ ideas by Adam Langley. Implements ticket 9663.
+
+ o Major features (Hidden services):
+ - Hidden services, if using the EntryNodes option, are required to
+ use more than one EntryNode, in order to avoid a guard discovery
+ attack. (This would only affect people who had configured hidden
+ services and manually specified the EntryNodes option with a
+ single entry-node. The impact was that it would be easy to
+ remotely identify the guard node used by such a hidden service.
+ See ticket for more information.) Fixes ticket 14917.
+ - Add the torrc option HiddenServiceNumIntroductionPoints, to
+ specify a fixed number of introduction points. Its maximum value
+ is 10 and default is 3. Using this option can increase a hidden
+ service's reliability under load, at the cost of making it more
+ visible that the hidden service is facing extra load. Closes
+ ticket 4862.
+ - Remove the adaptive algorithm for choosing the number of
+ introduction points, which used to change the number of
+ introduction points (poorly) depending on the number of
+ connections the HS sees. Closes ticket 4862.
+
+ o Major features (onion key cross-certification):
+ - Relay descriptors now include signatures of their own identity
+ keys, made using the TAP and ntor onion keys. These signatures
+ allow relays to prove ownership of their own onion keys. Because
+ of this change, microdescriptors will no longer need to include
+ RSA identity keys. Implements proposal 228; closes ticket 12499.
+
+ o Major bugfixes (client-side privacy, also in 0.2.6.9):
+ - Properly separate out each SOCKSPort when applying stream
+ isolation. The error occurred because each port's session group
+ was being overwritten by a default value when the listener
+ connection was initialized. Fixes bug 16247; bugfix on
+ 0.2.6.3-alpha. Patch by "jojelino".
+
+ o Major bugfixes (hidden service clients, stability, also in 0.2.6.10):
+ - Stop refusing to store updated hidden service descriptors on a
+ client. This reverts commit 9407040c59218 (which indeed fixed bug
+ 14219, but introduced a major hidden service reachability
+ regression detailed in bug 16381). This is a temporary fix since
+ we can live with the minor issue in bug 14219 (it just results in
+ some load on the network) but the regression of 16381 is too much
+ of a setback. First-round fix for bug 16381; bugfix
+ on 0.2.6.3-alpha.
+
+ o Major bugfixes (hidden services):
+ - Revert commit that made directory authorities assign the HSDir
+ flag to relay without a DirPort; this was bad because such relays
+ can't handle BEGIN_DIR cells. Fixes bug 15850; bugfix
+ on tor-0.2.6.3-alpha.
+ - When cannibalizing a circuit for an introduction point, always
+ extend to the chosen exit node (creating a 4 hop circuit).
+ Previously Tor would use the current circuit exit node, which
+ changed the original choice of introduction point, and could cause
+ the hidden service to skip excluded introduction points or
+ reconnect to a skipped introduction point. Fixes bug 16260; bugfix
+ on 0.1.0.1-rc.
+
+ o Major bugfixes (memory leaks):
+ - Fix a memory leak in ed25519 batch signature checking. Fixes bug
+ 17398; bugfix on 0.2.6.1-alpha.
+
+ o Major bugfixes (open file limit):
+ - The open file limit wasn't checked before calling
+ tor_accept_socket_nonblocking(), which would make Tor exceed the
+ limit. Now, before opening a new socket, Tor validates the open
+ file limit just before, and if the max has been reached, return an
+ error. Fixes bug 16288; bugfix on 0.1.1.1-alpha.
+
+ o Major bugfixes (security, correctness):
+ - Fix an error that could cause us to read 4 bytes before the
+ beginning of an openssl string. This bug could be used to cause
+ Tor to crash on systems with unusual malloc implementations, or
+ systems with unusual hardening installed. Fixes bug 17404; bugfix
+ on 0.2.3.6-alpha.
+
+ o Major bugfixes (stability, also in 0.2.6.10):
+ - Stop crashing with an assertion failure when parsing certain kinds
+ of malformed or truncated microdescriptors. Fixes bug 16400;
+ bugfix on 0.2.6.1-alpha. Found by "torkeln"; fix based on a patch
+ by "cypherpunks_backup".
+ - Stop random client-side assertion failures that could occur when
+ connecting to a busy hidden service, or connecting to a hidden
+ service while a NEWNYM is in progress. Fixes bug 16013; bugfix
+ on 0.1.0.1-rc.
+
+ o Minor features (client, SOCKS):
+ - Add GroupWritable and WorldWritable options to unix-socket based
+ SocksPort and ControlPort options. These options apply to a single
+ socket, and override {Control,Socks}SocketsGroupWritable. Closes
+ ticket 15220.
+ - Relax the validation done to hostnames in SOCKS5 requests, and
+ allow a single trailing '.' to cope with clients that pass FQDNs
+ using that syntax to explicitly indicate that the domain name is
+ fully-qualified. Fixes bug 16674; bugfix on 0.2.6.2-alpha.
+ - Relax the validation of hostnames in SOCKS5 requests, allowing the
+ character '_' to appear, in order to cope with domains observed in
+ the wild that are serving non-RFC compliant records. Resolves
+ ticket 16430.
+
+ o Minor features (client-side privacy):
+ - New KeepAliveIsolateSOCKSAuth option to indefinitely extend circuit
+ lifespan when IsolateSOCKSAuth and streams with SOCKS
+ authentication are attached to the circuit. This allows
+ applications like TorBrowser to manage circuit lifetime on their
+ own. Implements feature 15482.
+ - When logging malformed hostnames from SOCKS5 requests, respect
+ SafeLogging configuration. Fixes bug 16891; bugfix on 0.1.1.16-rc.
+
+ o Minor features (clock-jump tolerance):
+ - Recover better when our clock jumps back many hours, like might
+ happen for Tails or Whonix users who start with a very wrong
+ hardware clock, use Tor to discover a more accurate time, and then
+ fix their clock. Resolves part of ticket 8766.
+
+ o Minor features (command-line interface):
+ - Make --hash-password imply --hush to prevent unnecessary noise.
+ Closes ticket 15542. Patch from "cypherpunks".
+ - Print a warning whenever we find a relative file path being used
+ as torrc option. Resolves issue 14018.
+
+ o Minor features (compilation):
+ - Give a warning as early as possible when trying to build with an
+ unsupported OpenSSL version. Closes ticket 16901.
+ - Use C99 variadic macros when the compiler is not GCC. This avoids
+ failing compilations on MSVC, and fixes a log-file-based race
+ condition in our old workarounds. Original patch from Gisle Vanem.
+
+ o Minor features (control protocol):
+ - Support network-liveness GETINFO key and NETWORK_LIVENESS event in
+ the control protocol. Resolves ticket 15358.
+
+ o Minor features (controller):
+ - Add DirAuthority lines for default directory authorities to the
+ output of the "GETINFO config/defaults" command if not already
+ present. Implements ticket 14840.
+ - Controllers can now use "GETINFO hs/client/desc/id/..." to
+ retrieve items from the client's hidden service descriptor cache.
+ Closes ticket 14845.
+ - Implement a new controller command "GETINFO status/fresh-relay-
+ descs" to fetch a descriptor/extrainfo pair that was generated on
+ demand just for the controller's use. Implements ticket 14784.
+
+ o Minor features (directory authorities):
+ - Directory authorities no longer vote against the "Fast", "Stable",
+ and "HSDir" flags just because they were going to vote against
+ "Running": if the consensus turns out to be that the router was
+ running, then the authority's vote should count. Patch from Peter
+ Retzlaff; closes issue 8712.
+
+ o Minor features (directory authorities, security, also in 0.2.6.9):
+ - The HSDir flag given by authorities now requires the Stable flag.
+ For the current network, this results in going from 2887 to 2806
+ HSDirs. Also, it makes it harder for an attacker to launch a sybil
+ attack by raising the effort for a relay to become Stable to
+ require at the very least 7 days, while maintaining the 96 hours
+ uptime requirement for HSDir. Implements ticket 8243.
+
+ o Minor features (DoS-resistance):
+ - Make it harder for attackers to overload hidden services with
+ introductions, by blocking multiple introduction requests on the
+ same circuit. Resolves ticket 15515.
+
+ o Minor features (geoip):
+ - Update geoip and geoip6 to the October 9 2015 Maxmind GeoLite2
+ Country database.
+
+ o Minor features (hidden services):
+ - Add the new options "HiddenServiceMaxStreams" and
+ "HiddenServiceMaxStreamsCloseCircuit" to allow hidden services to
+ limit the maximum number of simultaneous streams per circuit, and
+ optionally tear down the circuit when the limit is exceeded. Part
+ of ticket 16052.
+ - Client now uses an introduction point failure cache to know when
+ to fetch or keep a descriptor in their cache. Previously, failures
+ were recorded implicitly, but not explicitly remembered. Closes
+ ticket 16389.
+ - Relays need to have the Fast flag to get the HSDir flag. As this
+ is being written, we'll go from 2745 HSDirs down to 2342, a ~14%
+ drop. This change should make some attacks against the hidden
+ service directory system harder. Fixes ticket 15963.
+ - Turn on hidden service statistics collection by setting the torrc
+ option HiddenServiceStatistics to "1" by default. (This keeps
+ track only of the fraction of traffic used by hidden services, and
+ the total number of hidden services in existence.) Closes
+ ticket 15254.
+ - To avoid leaking HS popularity, don't cycle the introduction point
+ when we've handled a fixed number of INTRODUCE2 cells but instead
+ cycle it when a random number of introductions is reached, thus
+ making it more difficult for an attacker to find out the amount of
+ clients that have used the introduction point for a specific HS.
+ Closes ticket 15745.
+
+ o Minor features (logging):
+ - Include the Tor version in all LD_BUG log messages, since people
+ tend to cut and paste those into the bugtracker. Implements
+ ticket 15026.
+
+ o Minor features (pluggable transports):
+ - When launching managed pluggable transports on Linux systems,
+ attempt to have the kernel deliver a SIGTERM on tor exit if the
+ pluggable transport process is still running. Resolves
+ ticket 15471.
+ - When launching managed pluggable transports, setup a valid open
+ stdin in the child process that can be used to detect if tor has
+ terminated. The "TOR_PT_EXIT_ON_STDIN_CLOSE" environment variable
+ can be used by implementations to detect this new behavior.
+ Resolves ticket 15435.
+
+ o Minor bugfixes (torrc exit policies):
+ - In each instance above, usage advice is provided to avoid the
+ message. Resolves ticket 16069. Patch by "teor". Fixes part of bug
+ 16069; bugfix on 0.2.4.7-alpha.
+ - In torrc, "accept6 *" and "reject6 *" ExitPolicy lines now only
+ produce IPv6 wildcard addresses. Previously they would produce
+ both IPv4 and IPv6 wildcard addresses. Patch by "teor". Fixes part
+ of bug 16069; bugfix on 0.2.4.7-alpha.
+ - When parsing torrc ExitPolicies, we now issue an info-level
+ message when expanding an "accept/reject *" line to include both
+ IPv4 and IPv6 wildcard addresses. Related to ticket 16069.
+ - When parsing torrc ExitPolicies, we now warn for a number of cases
+ where the user's intent is likely to differ from Tor's actual
+ behavior. These include: using an IPv4 address with an accept6 or
+ reject6 line; using "private" on an accept6 or reject6 line; and
+ including any ExitPolicy lines after accept *:* or reject *:*.
+ Related to ticket 16069.
+
+ o Minor bugfixes (command-line interface):
+ - When "--quiet" is provided along with "--validate-config", do not
+ write anything to stdout on success. Fixes bug 14994; bugfix
+ on 0.2.3.3-alpha.
+ - When complaining about bad arguments to "--dump-config", use
+ stderr, not stdout.
+ - Print usage information for --dump-config when it is used without
+ an argument. Also, fix the error message to use different wording
+ and add newline at the end. Fixes bug 15541; bugfix
+ on 0.2.5.1-alpha.
+
+ o Minor bugfixes (compilation):
+ - Fix compilation of sandbox.c with musl-libc. Fixes bug 17347;
+ bugfix on 0.2.5.1-alpha. Patch from 'jamestk'.
+ - Repair compilation with the most recent (unreleased, alpha)
+ vesions of OpenSSL 1.1. Fixes part of ticket 17237.
+
+ o Minor bugfixes (compilation, also in 0.2.6.9):
+ - Build with --enable-systemd correctly when libsystemd is
+ installed, but systemd is not. Fixes bug 16164; bugfix on
+ 0.2.6.3-alpha. Patch from Peter Palfrader.
+
+ o Minor bugfixes (configuration, unit tests):
+ - Only add the default fallback directories when the DirAuthorities,
+ AlternateDirAuthority, and FallbackDir directory config options
+ are set to their defaults. The default fallback directory list is
+ currently empty, this fix will only change tor's behavior when it
+ has default fallback directories. Includes unit tests for
+ consider_adding_dir_servers(). Fixes bug 15642; bugfix on
+ 90f6071d8dc0 in 0.2.4.7-alpha. Patch by "teor".
+
+ o Minor bugfixes (controller):
+ - Add the descriptor ID in each HS_DESC control event. It was
+ missing, but specified in control-spec.txt. Fixes bug 15881;
+ bugfix on 0.2.5.2-alpha.
+
+ o Minor bugfixes (correctness):
+ - For correctness, avoid modifying a constant string in
+ handle_control_postdescriptor. Fixes bug 15546; bugfix
+ on 0.1.1.16-rc.
+ - Remove side-effects from tor_assert() calls. This was harmless,
+ because we never disable assertions, but it is bad style and
+ unnecessary. Fixes bug 15211; bugfix on 0.2.5.5, 0.2.2.36,
+ and 0.2.0.10.
+ - When calling channel_free_list(), avoid calling smartlist_remove()
+ while inside a FOREACH loop. This partially reverts commit
+ 17356fe7fd96af where the correct SMARTLIST_DEL_CURRENT was
+ incorrectly removed. Fixes bug 16924; bugfix on 0.2.4.4-alpha.
+
+ o Minor bugfixes (crypto error-handling, also in 0.2.6.10):
+ - Check for failures from crypto_early_init, and refuse to continue.
+ A previous typo meant that we could keep going with an
+ uninitialized crypto library, and would have OpenSSL initialize
+ its own PRNG. Fixes bug 16360; bugfix on 0.2.5.2-alpha, introduced
+ when implementing ticket 4900. Patch by "teor".
+
+ o Minor bugfixes (hidden service):
+ - Fix an out-of-bounds read when parsing invalid INTRODUCE2 cells on
+ a client authorized hidden service. Fixes bug 15823; bugfix
+ on 0.2.1.6-alpha.
+ - Remove an extraneous newline character from the end of hidden
+ service descriptors. Fixes bug 15296; bugfix on 0.2.0.10-alpha.
+
+ o Minor bugfixes (Linux seccomp2 sandbox):
+ - Use the sandbox in tor_open_cloexec whether or not O_CLOEXEC is
+ defined. Patch by "teor". Fixes bug 16515; bugfix on 0.2.3.1-alpha.
+ - Allow bridge authorities to run correctly under the seccomp2
+ sandbox. Fixes bug 16964; bugfix on 0.2.5.1-alpha.
+ - Add the "hidserv-stats" filename to our sandbox filter for the
+ HiddenServiceStatistics option to work properly. Fixes bug 17354;
+ bugfix on tor-0.2.6.2-alpha. Patch from David Goulet.
+
+ o Minor bugfixes (Linux seccomp2 sandbox, also in 0.2.6.10):
+ - Allow pipe() and pipe2() syscalls in the seccomp2 sandbox: we need
+ these when eventfd2() support is missing. Fixes bug 16363; bugfix
+ on 0.2.6.3-alpha. Patch from "teor".
+
+ o Minor bugfixes (Linux seccomp2 sandbox, also in 0.2.6.9):
+ - Allow systemd connections to work with the Linux seccomp2 sandbox
+ code. Fixes bug 16212; bugfix on 0.2.6.2-alpha. Patch by
+ Peter Palfrader.
+ - Fix sandboxing to work when running as a relay, by allowing the
+ renaming of secret_id_key, and allowing the eventfd2 and futex
+ syscalls. Fixes bug 16244; bugfix on 0.2.6.1-alpha. Patch by
+ Peter Palfrader.
+
+ o Minor bugfixes (logging):
+ - When building Tor under Clang, do not include an extra set of
+ parentheses in log messages that include function names. Fixes bug
+ 15269; bugfix on every released version of Tor when compiled with
+ recent enough Clang.
+
+ o Minor bugfixes (network):
+ - When attempting to use fallback technique for network interface
+ lookup, disregard loopback and multicast addresses since they are
+ unsuitable for public communications.
+
+ o Minor bugfixes (open file limit):
+ - Fix set_max_file_descriptors() to set by default the max open file
+ limit to the current limit when setrlimit() fails. Fixes bug
+ 16274; bugfix on tor- 0.2.0.10-alpha. Patch by dgoulet.
+
+ o Minor bugfixes (portability):
+ - Check correctly for Windows socket errors in the workqueue
+ backend. Fixes bug 16741; bugfix on 0.2.6.3-alpha.
+ - Try harder to normalize the exit status of the Tor process to the
+ standard-provided range. Fixes bug 16975; bugfix on every version
+ of Tor ever.
+ - Use libexecinfo on FreeBSD to enable backtrace support. Fixes part
+ of bug 17151; bugfix on 0.2.5.2-alpha. Patch from Marcin Cieślak.
+
+ o Minor bugfixes (relay):
+ - Ensure that worker threads actually exit when a fatal error or
+ shutdown is indicated. This fix doesn't currently affect the
+ behavior of Tor, because Tor workers never indicates fatal error
+ or shutdown except in the unit tests. Fixes bug 16868; bugfix
+ on 0.2.6.3-alpha.
+ - Fix a rarely-encountered memory leak when failing to initialize
+ the thread pool. Fixes bug 16631; bugfix on 0.2.6.3-alpha. Patch
+ from "cypherpunks".
+ - Unblock threads before releasing the work queue mutex to ensure
+ predictable scheduling behavior. Fixes bug 16644; bugfix
+ on 0.2.6.3-alpha.
+
+ o Minor bugfixes (security, exit policies):
+ - ExitPolicyRejectPrivate now also rejects the relay's published
+ IPv6 address (if any), and any publicly routable IPv4 or IPv6
+ addresses on any local interfaces. ticket 17027. Patch by "teor".
+ Fixes bug 17027; bugfix on 0.2.0.11-alpha.
+
+ o Minor bugfixes (statistics):
+ - Disregard the ConnDirectionStatistics torrc options when Tor is
+ not a relay since in that mode of operation no sensible data is
+ being collected and because Tor might run into measurement hiccups
+ when running as a client for some time, then becoming a relay.
+ Fixes bug 15604; bugfix on 0.2.2.35.
+
+ o Minor bugfixes (systemd):
+ - Tor's systemd unit file no longer contains extraneous spaces.
+ These spaces would sometimes confuse tools like deb-systemd-
+ helper. Fixes bug 16162; bugfix on 0.2.5.5-alpha.
+
+ o Minor bugfixes (test networks):
+ - When self-testing reachability, use ExtendAllowPrivateAddresses to
+ determine if local/private addresses imply reachability. The
+ previous fix used TestingTorNetwork, which implies
+ ExtendAllowPrivateAddresses, but this excluded rare configurations
+ where ExtendAllowPrivateAddresses is set but TestingTorNetwork is
+ not. Fixes bug 15771; bugfix on 0.2.6.1-alpha. Patch by "teor",
+ issue discovered by CJ Ess.
+
+ o Minor bugfixes (tests, also in 0.2.6.9):
+ - Fix a crash in the unit tests when built with MSVC2013. Fixes bug
+ 16030; bugfix on 0.2.6.2-alpha. Patch from "NewEraCracker".
+
+ o Code simplification and refactoring:
+ - Change the function that's called when we need to retry all
+ downloads so that it only reschedules the downloads to happen
+ immediately, rather than launching them all at once itself. This
+ further simplifies Tor's callgraph.
+ - Define WINVER and _WIN32_WINNT centrally, in orconfig.h, in order
+ to ensure they remain consistent and visible everywhere.
+ - Move some format-parsing functions out of crypto.c and
+ crypto_curve25519.c into crypto_format.c and/or util_format.c.
+ - Move the client-only parts of init_keys() into a separate
+ function. Closes ticket 16763.
+ - Move the hacky fallback code out of get_interface_address6() into
+ separate function and get it covered with unit-tests. Resolves
+ ticket 14710.
+ - Refactor hidden service client-side cache lookup to intelligently
+ report its various failure cases, and disentangle failure cases
+ involving a lack of introduction points. Closes ticket 14391.
+ - Remove some vestigial workarounds for the MSVC6 compiler. We
+ haven't supported that in ages.
+ - Remove the unused "nulterminate" argument from buf_pullup().
+ - Simplify the microdesc_free() implementation so that it no longer
+ appears (to code analysis tools) to potentially invoke a huge
+ suite of other microdesc functions.
+ - Simply the control graph further by deferring the inner body of
+ directory_all_unreachable() into a callback. Closes ticket 16762.
+ - The link authentication code has been refactored for better
+ testability and reliability. It now uses code generated with the
+ "trunnel" binary encoding generator, to reduce the risk of bugs
+ due to programmer error. Done as part of ticket 12498.
+ - Treat the loss of an owning controller as equivalent to a SIGTERM
+ signal. This removes a tiny amount of duplicated code, and
+ simplifies our callgraph. Closes ticket 16788.
+ - Use our own Base64 encoder instead of OpenSSL's, to allow more
+ control over the output. Part of ticket 15652.
+ - When generating an event to send to the controller, we no longer
+ put the event over the network immediately. Instead, we queue
+ these events, and use a Libevent callback to deliver them. This
+ change simplifies Tor's callgraph by reducing the number of
+ functions from which all other Tor functions are reachable. Closes
+ ticket 16695.
+ - Wrap Windows-only C files inside '#ifdef _WIN32' so that tools
+ that try to scan or compile every file on Unix won't decide that
+ they are broken.
+
+ o Documentation:
+ - Fix capitalization of SOCKS in sample torrc. Closes ticket 15609.
+ - Improve the descriptions of statistics-related torrc options in
+ the manpage to describe rationale and possible uses cases. Fixes
+ issue 15550.
+ - Improve the layout and formatting of ./configure --help messages.
+ Closes ticket 15024. Patch from "cypherpunks".
+ - Include a specific and (hopefully) accurate documentation of the
+ torrc file's meta-format in doc/torrc_format.txt. This is mainly
+ of interest to people writing programs to parse or generate torrc
+ files. This document is not a commitment to long-term
+ compatibility; some aspects of the current format are a bit
+ ridiculous. Closes ticket 2325.
+ - Include the TUNING document in our source tarball. It is referred
+ to in the ChangeLog and an error message. Fixes bug 16929; bugfix
+ on 0.2.6.1-alpha.
+ - Note that HiddenServicePorts can take a unix domain socket. Closes
+ ticket 17364.
+ - Recommend a 40 GB example AccountingMax in torrc.sample rather
+ than a 4 GB max. Closes ticket 16742.
+ - Standardize on the term "server descriptor" in the manual page.
+ Previously, we had used "router descriptor", "server descriptor",
+ and "relay descriptor" interchangeably. Part of ticket 14987.
+ - Advise users on how to configure separate IPv4 and IPv6 exit
+ policies in the manpage and sample torrcs. Related to ticket 16069.
+ - Fix an error in the manual page and comments for
+ TestingDirAuthVoteHSDir[IsStrict], which suggested that a HSDir
+ required "ORPort connectivity". While this is true, it is in no
+ way unique to the HSDir flag. Of all the flags, only HSDirs need a
+ DirPort configured in order for the authorities to assign that
+ particular flag. Patch by "teor". Fixed as part of 14882; bugfix
+ on 0.2.6.3-alpha.
+ - Fix the usage message of tor-resolve(1) so that it no longer lists
+ the removed -F option. Fixes bug 16913; bugfix on 0.2.2.28-beta.
+
+ o Removed code:
+ - Remove `USE_OPENSSL_BASE64` and the corresponding fallback code
+ and always use the internal Base64 decoder. The internal decoder
+ has been part of tor since tor-0.2.0.10-alpha, and no one should
+ be using the OpenSSL one. Part of ticket 15652.
+ - Remove the 'tor_strclear()' function; use memwipe() instead.
+ Closes ticket 14922.
+ - Remove the code that would try to aggressively flush controller
+ connections while writing to them. This code was introduced in
+ 0.1.2.7-alpha, in order to keep output buffers from exceeding
+ their limits. But there is no longer a maximum output buffer size,
+ and flushing data in this way caused some undesirable recursions
+ in our call graph. Closes ticket 16480.
+ - The internal pure-C tor-fw-helper tool is now removed from the Tor
+ distribution, in favor of the pure-Go clone available from
+ https://gitweb.torproject.org/tor-fw-helper.git/ . The libraries
+ used by the C tor-fw-helper are not, in our opinion, very
+ confidence- inspiring in their secure-programming techniques.
+ Closes ticket 13338.
+
+ o Removed features:
+ - Remove the (seldom-used) DynamicDHGroups feature. For anti-
+ fingerprinting we now recommend pluggable transports; for forward-
+ secrecy in TLS, we now use the P-256 group. Closes ticket 13736.
+ - Remove the HidServDirectoryV2 option. Now all relays offer to
+ store hidden service descriptors. Related to 16543.
+ - Remove the VoteOnHidServDirectoriesV2 option, since all
+ authorities have long set it to 1. Closes ticket 16543.
+ - Remove the undocumented "--digests" command-line option. It
+ complicated our build process, caused subtle build issues on
+ multiple platforms, and is now redundant since we started
+ including git version identifiers. Closes ticket 14742.
+ - Tor no longer contains checks for ancient directory cache versions
+ that didn't know about microdescriptors.
+ - Tor no longer contains workarounds for stat files generated by
+ super-old versions of Tor that didn't choose guards sensibly.
+
+ o Testing:
+ - The test-network.sh script now supports performance testing.
+ Requires corresponding chutney performance testing changes. Patch
+ by "teor". Closes ticket 14175.
+ - Add a new set of callgraph analysis scripts that use clang to
+ produce a list of which Tor functions are reachable from which
+ other Tor functions. We're planning to use these to help simplify
+ our code structure by identifying illogical dependencies.
+ - Add new 'test-full' and 'test-full-online' targets to run all
+ tests, including integration tests with stem and chutney.
+ - Autodetect CHUTNEY_PATH if the chutney and Tor sources are side-
+ by-side in the same parent directory. Closes ticket 16903. Patch
+ by "teor".
+ - Document use of coverity, clang static analyzer, and clang dynamic
+ undefined behavior and address sanitizers in doc/HACKING. Include
+ detailed usage instructions in the blacklist. Patch by "teor".
+ Closes ticket 15817.
+ - Make "bridges+hs" the default test network. This tests almost all
+ tor functionality during make test-network, while allowing tests
+ to succeed on non-IPv6 systems. Requires chutney commit 396da92 in
+ test-network-bridges-hs. Closes tickets 16945 (tor) and 16946
+ (chutney). Patches by "teor".
+ - Make the test-workqueue test work on Windows by initializing the
+ network before we begin.
+ - New make target (make test-network-all) to run multiple applicable
+ chutney test cases. Patch from Teor; closes 16953.
+ - Now that OpenSSL has its own scrypt implementation, add an unit
+ test that checks for interoperability between libscrypt_scrypt()
+ and OpenSSL's EVP_PBE_scrypt() so that we could not use libscrypt
+ and rely on EVP_PBE_scrypt() whenever possible. Resolves
+ ticket 16189.
+ - The link authentication protocol code now has extensive tests.
+ - The relay descriptor signature testing code now has
+ extensive tests.
+ - The test_workqueue program now runs faster, and is enabled by
+ default as a part of "make check".
+ - Unit test dns_resolve(), dns_clip_ttl() and dns_get_expiry_ttl()
+ functions in dns.c. Implements a portion of ticket 16831.
+ - Use environment variables rather than autoconf substitutions to
+ send variables from the build system to the test scripts. This
+ change should be easier to maintain, and cause 'make distcheck' to
+ work better than before. Fixes bug 17148.
+ - When building Tor with testing coverage enabled, run Chutney tests
+ (if any) using the 'tor-cov' coverage binary.
+ - When running test-network or test-stem, check for the absence of
+ stem/chutney before doing any build operations.
+ - Add a test to verify that the compiler does not eliminate our
+ memwipe() implementation. Closes ticket 15377.
+ - Add make rule `check-changes` to verify the format of changes
+ files. Closes ticket 15180.
+ - Add unit tests for control_event_is_interesting(). Add a compile-
+ time check that the number of events doesn't exceed the capacity
+ of control_event_t.event_mask. Closes ticket 15431, checks for
+ bugs similar to 13085. Patch by "teor".
+ - Command-line argument tests moved to Stem. Resolves ticket 14806.
+ - Integrate the ntor, backtrace, and zero-length keys tests into the
+ automake test suite. Closes ticket 15344.
+ - Remove assertions during builds to determine Tor's test coverage.
+ We don't want to trigger these even in assertions, so including
+ them artificially makes our branch coverage look worse than it is.
+ This patch provides the new test-stem-full and coverage-html-full
+ configure options. Implements ticket 15400.
+ - New TestingDirAuthVote{Exit,Guard,HSDir}IsStrict flags to
+ explicitly manage consensus flags in testing networks. Patch by
+ "robgjansen", modified by "teor". Implements part of ticket 14882.
+ - Check for matching value in server response in ntor_ref.py. Fixes
+ bug 15591; bugfix on 0.2.4.8-alpha. Reported and fixed
+ by "joelanders".
+ - Set the severity correctly when testing
+ get_interface_addresses_ifaddrs() and
+ get_interface_addresses_win32(), so that the tests fail gracefully
+ instead of triggering an assertion. Fixes bug 15759; bugfix on
+ 0.2.6.3-alpha. Reported by Nicolas Derive.
+
Changes in version 0.2.6.10 - 2015-07-12
Tor version 0.2.6.10 fixes some significant stability and hidden
service client bugs, bulletproofs the cryptography init process, and
diff --git a/changes/assert_event_base b/changes/assert_event_base
new file mode 100644
index 0000000000..b887795518
--- /dev/null
+++ b/changes/assert_event_base
@@ -0,0 +1,5 @@
+ o Minor features (robustness):
+ - Exit immediately with an error message if the code attempts to
+ use libevent without having initialized it. This should resolve
+ some frequently-made mistakes in our unit tests. Closes ticket
+ 18241.
diff --git a/changes/bug15609 b/changes/bug15609
deleted file mode 100644
index efaccdeaae..0000000000
--- a/changes/bug15609
+++ /dev/null
@@ -1,2 +0,0 @@
- o Documentation:
- - Fix capitalization of SOCKS in sample torrc. Closes ticket 15609.
diff --git a/changes/bug16023 b/changes/bug16023
new file mode 100644
index 0000000000..7451e271a0
--- /dev/null
+++ b/changes/bug16023
@@ -0,0 +1,4 @@
+ o Minor feature (hidden service, control port)
+ - Add the onion address to the HS_DESC event for the UPLOADED action
+ both on success or failure. It was previously hardcoded with UNKNOWN.
+ Fixes #16023;
diff --git a/changes/bug16056 b/changes/bug16056
deleted file mode 100644
index e3311c0f93..0000000000
--- a/changes/bug16056
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (relay, IPv6):
- - When displaying an IPv6 exit policy, include the mask bits correctly
- even when the number is greater than 31. Fixes bug 16056; bugfix on
- 0.2.4.7-alpha. Patch from "gturner". \ No newline at end of file
diff --git a/changes/bug16702 b/changes/bug16702
deleted file mode 100644
index 5de36cd351..0000000000
--- a/changes/bug16702
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (hidden service)
- - The wrong list was used when looking up expired intro points in a rend
- service object causing what we think could be reachability issues and
- triggering a BUG log. Fixes 16702; bugfix on tor-0.2.7.2-alpha.
diff --git a/changes/bug16825 b/changes/bug16825
new file mode 100644
index 0000000000..75c5627efd
--- /dev/null
+++ b/changes/bug16825
@@ -0,0 +1,7 @@
+ o Minor bugfixes:
+ - Count receipt of new microdescriptors as progress towards
+ bootstrapping. Now, when a user who has set EntryNodes finishes
+ bootstrapping, Tor automatically repopulates the guard set based
+ on this new directory information. Fixes bug 16825; bugfix on
+ 0.2.3.1-alpha.
+
diff --git a/changes/bug17151 b/changes/bug17151
deleted file mode 100644
index 0993b90eac..0000000000
--- a/changes/bug17151
+++ /dev/null
@@ -1,7 +0,0 @@
- o Minor bugfixes (portability):
- - Use libexecinfo on FreeBSD, to enable backtrace support. Fixes part of
- bug 17151; bugfix on 0.2.5.2-alpha. Patch from Marcin Cieślak.
-
- o Minor bugfixes (testing):
- - Skip backtrace tests when backtrace support is not compiled in. Fixes
- part of bug 17151; bugfix on 0.2.7.1-alpha. Patch from Marcin Cieślak.
diff --git a/changes/bug17154 b/changes/bug17154
deleted file mode 100644
index 6ad7b74468..0000000000
--- a/changes/bug17154
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor bugfixes (testing):
- - Fix breakage when running 'make check' with BSD make. Fixes bug
- 17154; bugfix on 0.2.7.3-rc. Patch by Marcin Cieślak.
diff --git a/changes/bug17173-socket-hack-rv b/changes/bug17173-socket-hack-rv
deleted file mode 100644
index d5132114b4..0000000000
--- a/changes/bug17173-socket-hack-rv
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor bug fixes (addresses, testing):
- - Add unit tests for get_interface_address* failure cases.
- Fixes bug 17173; bugfix on 0.2.7.3-rc. Patch by fk/teor.
diff --git a/changes/bug17237_027 b/changes/bug17237_027
deleted file mode 100644
index e5978d0ec8..0000000000
--- a/changes/bug17237_027
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor features (compilation):
- - Repair compilation with the most recent (unreleased, alpha)
- vesions of OpenSSL 1.1. Fixes part of ticket 17237.
diff --git a/changes/bug17251 b/changes/bug17251
deleted file mode 100644
index edd7739d2f..0000000000
--- a/changes/bug17251
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor bugfixes (compilation):
- - Fix an integer overflow warning in test_crypto_slow.c.
- Fixes bug 17251; bugfix on 0.2.7.2-alpha.
diff --git a/changes/bug17347 b/changes/bug17347
deleted file mode 100644
index 487b1320e2..0000000000
--- a/changes/bug17347
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (compilation):
- - Fix compilation of sandbox.c with musl-libc.
- Fixes bug 17347; bugfix on 0.2.5.1-alpha.
- Patch from 'jamestk'.
diff --git a/changes/bug17354 b/changes/bug17354
deleted file mode 100644
index 53da007fbb..0000000000
--- a/changes/bug17354
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (sandbox):
- - Add the "hidserv-stats" filename to our sandbox filter for the
- HiddenServiceStatistics option to work properly. Fixes bug 17354;
- bugfix on tor-0.2.6.2-alpha~54^2~1. Patch from David Goulet.
diff --git a/changes/bug17364 b/changes/bug17364
deleted file mode 100644
index dd9ff12784..0000000000
--- a/changes/bug17364
+++ /dev/null
@@ -1,3 +0,0 @@
- o Documentation:
- - Note that HiddenServicePorts can take a unix domain socket.
- Closes ticket 17364.
diff --git a/changes/bug17398 b/changes/bug17398
deleted file mode 100644
index 66e27a6966..0000000000
--- a/changes/bug17398
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor bugfixes (memory leaks):
- - Fix a memory leak in ed25519 batch signature checking.
- Fixes bug 17398; bugfix on 0.2.6.1-alpha.
diff --git a/changes/bug17401 b/changes/bug17401
deleted file mode 100644
index a22f79c431..0000000000
--- a/changes/bug17401
+++ /dev/null
@@ -1,3 +0,0 @@
- o Major bugfixes (correctness):
- - Fix a use-after-free bug in validate_intro_point_failure().
- Fixes bug 17401; bugfix on 0.2.7.3-rc.
diff --git a/changes/bug17402 b/changes/bug17402
deleted file mode 100644
index 4760e00b04..0000000000
--- a/changes/bug17402
+++ /dev/null
@@ -1,3 +0,0 @@
- o Major bugfixes (memory leak):
- - Fix a memory leak in rend_cache_failure_entry_free().
- Fixes bug 17402; bugfix on 0.2.7.3-rc.
diff --git a/changes/bug17403 b/changes/bug17403
deleted file mode 100644
index e83a4a247b..0000000000
--- a/changes/bug17403
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor bugfixes (memory leaks):
- - Fix a memory leak when reading an expired signing key from disk.
- Fixes bug 17403; bugfix on 0.2.7.2-rc.
diff --git a/changes/bug17404 b/changes/bug17404
deleted file mode 100644
index d524f6662d..0000000000
--- a/changes/bug17404
+++ /dev/null
@@ -1,6 +0,0 @@
- o Major bugfixes (security, correctness):
- - Fix a programming error that could cause us to read 4 bytes before
- the beginning of an openssl string. This could be used to provoke
- a crash on systems with an unusual malloc implementation, or
- systems with unsual hardening installed. Fixes bug 17404; bugfix
- on 0.2.3.6-alpha.
diff --git a/changes/bug17551 b/changes/bug17551
deleted file mode 100644
index 27e467979e..0000000000
--- a/changes/bug17551
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (compilation):
- - When checking for net/pfvar.h, include netinet/in.h if possible.
- This fixes transparent proxy detection on OpenBSD. Fixes bug
- 17551; bugfix on 0.1.2.1-alpha. Patch from "rubiate".
diff --git a/changes/bug17583 b/changes/bug17583
deleted file mode 100644
index d77d46759a..0000000000
--- a/changes/bug17583
+++ /dev/null
@@ -1,4 +0,0 @@
- o Documentation:
- - Add a description of the correct use of the '--keygen' command-line
- option. Closes ticket 17583; based on text by 's7r'.
-
diff --git a/changes/bug17675 b/changes/bug17675
deleted file mode 100644
index 8326a0b9e8..0000000000
--- a/changes/bug17675
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (linux seccomp2 sandbox):
- - Fix a crash when using offline master ed25519 keys with the
- Linux seccomp2 sandbox enabled. Fixes bug 17675; bugfix on
- 0.2.7.3-alpha.
diff --git a/changes/bug17722 b/changes/bug17722
deleted file mode 100644
index 1b18d4af2b..0000000000
--- a/changes/bug17722
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor bugfixes (code correctness)
- - Fix undefined behavior in the tor_cert_checksig function. Fixes bug
- 17722; bugfix on tor-0.2.7.2-alpha.
diff --git a/changes/bug17732 b/changes/bug17732
new file mode 100644
index 0000000000..010645d3c9
--- /dev/null
+++ b/changes/bug17732
@@ -0,0 +1,10 @@
+ o Minor features (compilation):
+ - Note our minimum required autoconf/automake versions in the
+ appropriate locations. Closes ticket 17732.
+
+ o Dependency updates:
+ - Tor now uses Autoconf version 2.63 or later, and Automake 1.11 or later
+ (released in 2008 and 2009 respectively). If you are building Tor from
+ the git repository instead of from the source distribution, and your
+ tools are older than this, you will need to upgrade.
+ Closes ticket 17732.
diff --git a/changes/bug17772 b/changes/bug17772
deleted file mode 100644
index 54d457c601..0000000000
--- a/changes/bug17772
+++ /dev/null
@@ -1,7 +0,0 @@
- o Major bugfixes (guard selection):
- - Actually look at the Guard flag when selecting a new directory
- guard. When we implemented the directory guard design, we
- accidentally started treating all relays as if they have the Guard
- flag during guard selection, leading to weaker anonymity and worse
- performance. Fixes bug 17222; bugfix on 0.2.4.8-alpha. Discovered
- by Mohsen Imani.
diff --git a/changes/bug17781 b/changes/bug17781
deleted file mode 100644
index 01ed231b0a..0000000000
--- a/changes/bug17781
+++ /dev/null
@@ -1,3 +0,0 @@
- o Compilation fixes:
- - Fix a compilation warning with Clang 3.6: Do not check the
- presence of an address which can never be NULL. Fixes bug 17781.
diff --git a/changes/bug17818 b/changes/bug17818
deleted file mode 100644
index 3d9afe329e..0000000000
--- a/changes/bug17818
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor bugfixes (compilation):
- - Isolate environment variables meant for tests from the rest of the
- build system. Fixes bug 17818; bugfix on tor-0.2.7.3-rc.
diff --git a/changes/bug17819 b/changes/bug17819
deleted file mode 100644
index 45c55f74b6..0000000000
--- a/changes/bug17819
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (compilation):
- - Don't try to use the pthrad_condattr_setclock() function unless
- it actually exists. Fixes compilation on NetBSD-6.x. Fixes bug
- 17819; bugfix on 0.2.6.3-alpha.
diff --git a/changes/bug17827 b/changes/bug17827
deleted file mode 100644
index 04cd3b5977..0000000000
--- a/changes/bug17827
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor bugfixes (compilation):
- - Fix backtrace compilation on FreeBSD. Fixes bug 17827; bugfix on
- tor-0.2.5.2-alpha.
diff --git a/changes/bug17852 b/changes/bug17852
new file mode 100644
index 0000000000..b36b55f9d6
--- /dev/null
+++ b/changes/bug17852
@@ -0,0 +1,10 @@
+ o Minor features (code hardening):
+ - Use tor_snprintf() and tor_vsnprintf() even in external and
+ low-level code, to harden against accidental failures to NUL-
+ terminate. Part of ticket 17852. Patch from 'jsturgix'. Found
+ with Flawfinder.
+
+ o Minor bugfixes (private directory):
+ - Prevent a race condition when creating private directories.
+ Fixes part of bug 17852; bugfix on 0.2pre13. Part of ticket
+ 17852. Patch from 'jsturgix'. Found with Flawfinder.
diff --git a/changes/bug17906 b/changes/bug17906
deleted file mode 100644
index fff76d1c59..0000000000
--- a/changes/bug17906
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor features (authorities):
- - Update the V3 identity key for dannenberg, it was changed on
- 18 November 2015.
- Closes task #17906. Patch by "teor".
diff --git a/changes/bug17923 b/changes/bug17923
deleted file mode 100644
index 48026a5c2f..0000000000
--- a/changes/bug17923
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes:
- - Add an #endif to configure.ac so that we correctly detect
- the presence of in6_addr.s6_addr32. Fixes bug 17923; bugfix on
- 0.2.0.13-alpha.
diff --git a/changes/bug18050 b/changes/bug18050
deleted file mode 100644
index ce24a7738a..0000000000
--- a/changes/bug18050
+++ /dev/null
@@ -1,7 +0,0 @@
- o Minor fixes (relays):
- - Check that both the ORPort and DirPort (if present) are reachable
- before publishing a relay descriptor. Otherwise, relays publish a
- descriptor with DirPort 0 when the DirPort reachability test takes
- longer than the ORPort reachability test.
- Closes bug #18050. Reported by "starlight", patch by "teor".
- Bugfix on 0.1.0.1-rc, commit a1f1fa6ab on 27 Feb 2005.
diff --git a/changes/bug18051 b/changes/bug18051
new file mode 100644
index 0000000000..1b11867d19
--- /dev/null
+++ b/changes/bug18051
@@ -0,0 +1,4 @@
+ o Minor bugfixes:
+ - When generating a URL for a directory server on an IPv6 address,
+ wrap the IPv6 address in square brackets. Fixes bug 18051;
+ bugfix on ?. Patch from Malek.
diff --git a/changes/bug18089 b/changes/bug18089
deleted file mode 100644
index c1fb342f77..0000000000
--- a/changes/bug18089
+++ /dev/null
@@ -1,6 +0,0 @@
- o Minor fixes (security):
- - Make memwipe() do nothing when passed a NULL pointer
- or zero size. Check size argument to memwipe() for underflow.
- Closes bug #18089. Reported by "gk", patch by "teor".
- Bugfix on 0.2.3.25 and 0.2.4.6-alpha (#7352),
- commit 49dd5ef3 on 7 Nov 2012.
diff --git a/changes/bug18116 b/changes/bug18116
new file mode 100644
index 0000000000..23ff8f7775
--- /dev/null
+++ b/changes/bug18116
@@ -0,0 +1,3 @@
+ o Major bugfixes:
+ - Correctly handle detaching circuits from cmuxes when doing
+ circuit_free_all() on shutdown again; fixes bug #18116.
diff --git a/changes/bug18184 b/changes/bug18184
new file mode 100644
index 0000000000..af493df75f
--- /dev/null
+++ b/changes/bug18184
@@ -0,0 +1,5 @@
+ o Removed platform support:
+ - Tor no longer attempts to support platforms where the "time_t" type
+ is unsigned. (To the best of our knowledge, only OpenVMS does this,
+ and Tor has never actually built on OpenVMS.) Closes ticket 18184.
+
diff --git a/changes/bug18208 b/changes/bug18208
new file mode 100644
index 0000000000..d6d194bb4d
--- /dev/null
+++ b/changes/bug18208
@@ -0,0 +1,5 @@
+ o Minor bug fixes (exit policies, security):
+ - Refresh an exit relay's exit policy when interface addresses change.
+ Previously, tor only refreshed the exit policy when the configured
+ external address changed.
+ Closes ticket 18208 on tor 0.2.7.3. Patch by "teor".
diff --git a/changes/bug18221 b/changes/bug18221
new file mode 100644
index 0000000000..afc240422a
--- /dev/null
+++ b/changes/bug18221
@@ -0,0 +1,3 @@
+ o Minor features (crypto):
+ - Validate the Diffie-Hellman hard coded parameters and ensure that
+ p is a safe prime, and g is suitable. Closes ticket 18221.
diff --git a/changes/bug18242 b/changes/bug18242
new file mode 100644
index 0000000000..e91f6d5eb3
--- /dev/null
+++ b/changes/bug18242
@@ -0,0 +1,6 @@
+ o Minor bugfixes (testing):
+ - We no longer disable assertions in the unit tests when coverage
+ is enabled. Instead, we require you to say --disable-asserts-in-tests
+ to the configure script if you need assertions disabled in the
+ unit tests (for example, if you want to perform branch coverage).
+ Fixes bug 18242; bugfix on 0.2.7.1-alpha.
diff --git a/changes/bug18258 b/changes/bug18258
new file mode 100644
index 0000000000..00e122466f
--- /dev/null
+++ b/changes/bug18258
@@ -0,0 +1,12 @@
+ o Code simplification and refactoring:
+ - Remove specialized code for non-inplace AES_CTR. 99% of our AES
+ is inplace, so there's no need to have a separate implementation
+ for the non-inplace code. Closes ticket 18258. Patch from
+ Malek.
+
+ o New requirements:
+ - Tor no longer supports versions of OpenSSL with a broken
+ implementation of counter mode. (This bug was present in OpenSSL
+ 1.0.0, and was fixed in OpenSSL 1.0.0a.) Tor still detects, but
+ no longer runs with, these versions.
+
diff --git a/changes/bug18259 b/changes/bug18259
new file mode 100644
index 0000000000..1084b8f524
--- /dev/null
+++ b/changes/bug18259
@@ -0,0 +1,4 @@
+ o Code simplification and refactoring:
+ - Simplify return types for some crypto functions that can't
+ actually fail. Patch from Hassan Alsibyani. Closes ticket
+ 18259.
diff --git a/changes/bug18261 b/changes/bug18261
new file mode 100644
index 0000000000..df4c74f337
--- /dev/null
+++ b/changes/bug18261
@@ -0,0 +1,6 @@
+ o Minor features (crypto):
+ - Fix a segfault during startup: If unix socket was configured as
+ listener (such as a ControlSocket or a SocksPort unix socket), and
+ tor was started as root but not configured to switch to another
+ user, tor would segfault while trying to string compare a NULL
+ value. Fixes bug 18261; bugfix on 0.2.8.1-alpha. Patch by weasel.
diff --git a/changes/bug18296 b/changes/bug18296
new file mode 100644
index 0000000000..1e98200be9
--- /dev/null
+++ b/changes/bug18296
@@ -0,0 +1,4 @@
+ o Minor bugfixes (containers):
+ - If we somehow attempt to construct a heap with more than
+ 1073741822 elements, avoid an integer overflow when maintaining
+ the heap property. Fixes bug 18296; bugfix on 0.1.2.1-alpha.
diff --git a/changes/bug18348 b/changes/bug18348
new file mode 100644
index 0000000000..03978eda19
--- /dev/null
+++ b/changes/bug18348
@@ -0,0 +1,5 @@
+ o Major bug fixes (relays, bridge clients):
+ - Ensure relays always allow IPv4 OR and Dir connections.
+ Ensure bridge clients use the address configured in the bridge line.
+ Fixes bug 18348; bugfix on 0.2.8.1-alpha.
+ Reported by sysrqb, patch by teor.
diff --git a/changes/bug18374 b/changes/bug18374
new file mode 100644
index 0000000000..4764c53f56
--- /dev/null
+++ b/changes/bug18374
@@ -0,0 +1,3 @@
+ o Minor bugfixes (configuration):
+ - Fix a tiny memory leak when parsing a port configuration ending in
+ ":auto". Fixes bug 18374; bugfix on 0.2.3.3-alpha.
diff --git a/changes/feature17840 b/changes/feature17840
new file mode 100644
index 0000000000..b8b3b7f5b5
--- /dev/null
+++ b/changes/feature17840
@@ -0,0 +1,9 @@
+ o Minor feature (IPv6):
+ - Add ClientUseIPv4, which is set to 1 by default. If set to 0, tor
+ avoids using IPv4 for client OR and directory connections.
+ - Add ClientPreferIPv6DirPort, which is set to 0 by default. If set
+ to 1, tor prefers IPv6 directory addresses.
+ - Try harder to fulfil IP version restrictions ClientUseIPv4 0 and
+ ClientUseIPv6 0; and the preferences ClientPreferIPv6ORPort and
+ ClientPreferIPv6DirPort.
+ Closes ticket 17840; patch by "teor".
diff --git a/changes/geoip-december2015 b/changes/geoip-december2015
deleted file mode 100644
index 597bcc92f8..0000000000
--- a/changes/geoip-december2015
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor features:
- - Update geoip and geoip6 to the December 1 2015 Maxmind GeoLite2
- Country database.
-
diff --git a/changes/geoip-january2016 b/changes/geoip-january2016
deleted file mode 100644
index fe2d5c7dc7..0000000000
--- a/changes/geoip-january2016
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor features:
- - Update geoip and geoip6 to the January 5 2016 Maxmind GeoLite2
- Country database.
-
diff --git a/changes/geoip-october2015 b/changes/geoip-october2015
deleted file mode 100644
index f20febec5a..0000000000
--- a/changes/geoip-october2015
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor features:
- - Update geoip and geoip6 to the October 9 2015 Maxmind GeoLite2 Country database.
-
diff --git a/changes/ifaddrs-tests-network-configs b/changes/ifaddrs-tests-network-configs
deleted file mode 100644
index 6b5ed4d484..0000000000
--- a/changes/ifaddrs-tests-network-configs
+++ /dev/null
@@ -1,5 +0,0 @@
- o Minor bugfixes (testing):
- - Make the get_ifaddrs_* unit tests more tolerant of different network
- configurations. (Don't assume every test box has an IPv4 address, and
- Don't assume every test box has a non-localhost address.
- Fixes bug 17255; bugfix on 0.2.7.3-rc. Patch by "teor".
diff --git a/changes/test_asan b/changes/test_asan
new file mode 100644
index 0000000000..ce0aa978ff
--- /dev/null
+++ b/changes/test_asan
@@ -0,0 +1,3 @@
+ o Testing:
+ - Fix several warnings from clang's address sanitizer produced in the
+ unit tests.
diff --git a/changes/ticket17744 b/changes/ticket17744
new file mode 100644
index 0000000000..57a4a595c8
--- /dev/null
+++ b/changes/ticket17744
@@ -0,0 +1,4 @@
+ o Code simplification and refactoring:
+ - Quote all the string interpolations in configure.ac -- even
+ those which we are pretty sure can't contain spaces. Closes
+ ticket 17744. Patch from "zerosion". \ No newline at end of file
diff --git a/changes/trunnel_update b/changes/trunnel_update
new file mode 100644
index 0000000000..4f8c1855b6
--- /dev/null
+++ b/changes/trunnel_update
@@ -0,0 +1,5 @@
+ o Minor bugfixes (code correctness):
+ - Update to the latest version of Trunnel, which tries harder
+ to avoid generating code that can invoke memcpy(p,NULL,0).
+ Bug found by clang address sanitizer. Fixes bug 18373. Bugfix
+ on 0.2.7.2-alpha.
diff --git a/configure.ac b/configure.ac
index bf6d7f66ea..a4581e54c3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,7 +3,8 @@ dnl Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson
dnl Copyright (c) 2007-2015, The Tor Project, Inc.
dnl See LICENSE for licensing information
-AC_INIT([tor],[0.2.7.6-dev])
+AC_PREREQ([2.63])
+AC_INIT([tor],[0.2.8.1-alpha-dev])
AC_CONFIG_SRCDIR([src/or/main.c])
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE
@@ -14,8 +15,8 @@ AC_CANONICAL_HOST
PKG_PROG_PKG_CONFIG
-if test -f /etc/redhat-release ; then
- if test -f /usr/kerberos/include ; then
+if test -f "/etc/redhat-release"; then
+ if test -f "/usr/kerberos/include"; then
CPPFLAGS="$CPPFLAGS -I/usr/kerberos/include"
fi
fi
@@ -41,11 +42,20 @@ AC_ARG_ENABLE(unittests,
AS_HELP_STRING(--disable-unittests, [don't build unit tests for Tor. Risky!]))
AC_ARG_ENABLE(coverage,
AS_HELP_STRING(--enable-coverage, [enable coverage support in the unit-test build]))
+AC_ARG_ENABLE(asserts-in-tests,
+ AS_HELP_STRING(--disable-asserts-in-tests, [disable tor_assert() calls in the unit tests, for branch coverage]))
AC_ARG_ENABLE(system-torrc,
AS_HELP_STRING(--disable-system-torrc, [don't look for a system-wide torrc file]))
+if test "x$enable_coverage" != "xyes" -a "x$enable_asserts_in_tests" = "xno" ; then
+ AC_MSG_ERROR([Can't disable assertions outside of coverage build])
+fi
+
+AM_CONDITIONAL(UNITTESTS_ENABLED, test "x$enable_unittests" != "xno")
+AM_CONDITIONAL(COVERAGE_ENABLED, test "x$enable_coverage" = "xyes")
AM_CONDITIONAL(UNITTESTS_ENABLED, test x$enable_unittests != xno)
AM_CONDITIONAL(COVERAGE_ENABLED, test x$enable_coverage = xyes)
+AM_CONDITIONAL(DISABLE_ASSERTS_IN_UNIT_TESTS, test "x$enable_asserts_in_tests" = "xno")
if test "$enable_static_tor" = "yes"; then
enable_static_libevent="yes";
@@ -59,8 +69,8 @@ if test "$enable_system_torrc" = "no"; then
[Defined if we're not going to look for a torrc in SYSCONF])
fi
-AM_CONDITIONAL(USE_OPENBSD_MALLOC, test x$enable_openbsd_malloc = xyes)
-if test x$enable_instrument_downloads = xyes; then
+AM_CONDITIONAL(USE_OPENBSD_MALLOC, test "x$enable_openbsd_malloc" = "xyes")
+if test "x$enable_instrument_downloads" = "xyes"; then
AC_DEFINE(INSTRUMENT_DOWNLOADS, 1,
[Defined if we want to keep track of how much of each kind of resource we download.])
fi
@@ -68,16 +78,16 @@ fi
AC_ARG_ENABLE(transparent,
AS_HELP_STRING(--disable-transparent, [disable transparent proxy support]),
[case "${enableval}" in
- yes) transparent=true ;;
- no) transparent=false ;;
+ "yes") transparent=true ;;
+ "no") transparent=false ;;
*) AC_MSG_ERROR(bad value for --enable-transparent) ;;
esac], [transparent=true])
AC_ARG_ENABLE(asciidoc,
AS_HELP_STRING(--disable-asciidoc, [don't use asciidoc (disables building of manpages)]),
[case "${enableval}" in
- yes) asciidoc=true ;;
- no) asciidoc=false ;;
+ "yes") asciidoc=true ;;
+ "no") asciidoc=false ;;
*) AC_MSG_ERROR(bad value for --disable-asciidoc) ;;
esac], [asciidoc=true])
@@ -85,22 +95,22 @@ AC_ARG_ENABLE(asciidoc,
AC_ARG_ENABLE(systemd,
AS_HELP_STRING(--enable-systemd, [enable systemd notification support]),
[case "${enableval}" in
- yes) systemd=true ;;
- no) systemd=false ;;
+ "yes") systemd=true ;;
+ "no") systemd=false ;;
* ) AC_MSG_ERROR(bad value for --enable-systemd) ;;
esac], [systemd=auto])
# systemd support
-if test x$enable_systemd = xno ; then
+if test "x$enable_systemd" = "xno"; then
have_systemd=no;
else
PKG_CHECK_MODULES(SYSTEMD,
[libsystemd-daemon],
have_systemd=yes,
have_systemd=no)
- if test x$have_systemd=xno; then
+ if test "x$have_systemd" = "xno"; then
AC_MSG_NOTICE([Okay, checking for systemd a different way...])
PKG_CHECK_MODULES(SYSTEMD,
[libsystemd],
@@ -109,7 +119,7 @@ else
fi
fi
-if test x$have_systemd = xyes; then
+if test "x$have_systemd" = "xyes"; then
AC_DEFINE(HAVE_SYSTEMD,1,[Have systemd])
TOR_SYSTEMD_CFLAGS="${SYSTEMD_CFLAGS}"
TOR_SYSTEMD_LIBS="${SYSTEMD_LIBS}"
@@ -119,11 +129,11 @@ fi
AC_SUBST(TOR_SYSTEMD_CFLAGS)
AC_SUBST(TOR_SYSTEMD_LIBS)
-if test x$enable_systemd = xyes -a x$have_systemd != xyes ; then
+if test "x$enable_systemd" = "xyes" -a "x$have_systemd" != "xyes" ; then
AC_MSG_ERROR([Explicitly requested systemd support, but systemd not found])
fi
-case $host in
+case "$host" in
*-*-solaris* )
AC_DEFINE(_REENTRANT, 1, [Define on some platforms to activate x_r() functions in time.h])
;;
@@ -156,7 +166,7 @@ fi
# Tor2web mode flag
AC_ARG_ENABLE(tor2web-mode,
AS_HELP_STRING(--enable-tor2web-mode, [support tor2web non-anonymous mode]),
-[if test x$enableval = xyes; then
+[if test "x$enableval" = "xyes"; then
CFLAGS="$CFLAGS -D ENABLE_TOR2WEB_MODE=1"
fi])
@@ -184,10 +194,10 @@ dnl because that will find any cc on the system, not only the cross-compiler,
dnl and then verify that a binary built with this compiler runs on the
dnl build system. It will then come to the false conclusion that we're not
dnl cross-compiling.
-if test x$enable_tool_name_check != xno; then
- if test x$ac_tool_warned = xyes; then
+if test "x$enable_tool_name_check" != "xno"; then
+ if test "x$ac_tool_warned" = "xyes"; then
AC_MSG_ERROR([We are cross compiling but could not find a properly named toolchain. Do you have your cross-compiling toolchain in PATH? (You can --disable-tool-name-check to ignore this.)])
- elif test "x$ac_ct_AR" != x -a x$cross_compiling = xmaybe; then
+ elif test "x$ac_ct_AR" != "x" -a "x$cross_compiling" = "xmaybe"; then
AC_MSG_ERROR([We think we are cross compiling but could not find a properly named toolchain. Do you have your cross-compiling toolchain in PATH? (You can --disable-tool-name-check to ignore this.)])
fi
fi
@@ -206,11 +216,11 @@ dnl check for asciidoc and a2x
AC_PATH_PROG([ASCIIDOC], [asciidoc], none)
AC_PATH_PROGS([A2X], [a2x a2x.py], none)
-AM_CONDITIONAL(USE_ASCIIDOC, test x$asciidoc = xtrue)
+AM_CONDITIONAL(USE_ASCIIDOC, test "x$asciidoc" = "xtrue")
-AM_CONDITIONAL(USE_FW_HELPER, test x$natpmp = xtrue || test x$upnp = xtrue)
-AM_CONDITIONAL(NAT_PMP, test x$natpmp = xtrue)
-AM_CONDITIONAL(MINIUPNPC, test x$upnp = xtrue)
+AM_CONDITIONAL(USE_FW_HELPER, test "x$natpmp" = "xtrue" || test "x$upnp" = "xtrue")
+AM_CONDITIONAL(NAT_PMP, test "x$natpmp" = "xtrue")
+AM_CONDITIONAL(MINIUPNPC, test "x$upnp" = "xtrue")
AM_PROG_CC_C_O
AC_PROG_CC_C99
@@ -236,7 +246,7 @@ AC_C_FLEXIBLE_ARRAY_MEMBER
]),
[tor_cv_c_flexarray=yes],
[tor_cv_c_flexarray=no])])
- if test $tor_cv_flexarray = yes ; then
+ if test "$tor_cv_flexarray" = "yes"; then
AC_DEFINE([FLEXIBLE_ARRAY_MEMBER], [], [Define to nothing if C supports flexible array members, and to 1 if it does not.])
else
AC_DEFINE([FLEXIBLE_ARRAY_MEMBER], [1], [Define to nothing if C supports flexible array members, and to 1 if it does not.])
@@ -303,7 +313,7 @@ bwin32=false; AC_MSG_RESULT([no]),
bwin32=cross; AC_MSG_RESULT([cross])
)
-if test "$bwin32" = cross; then
+if test "$bwin32" = "cross"; then
AC_MSG_CHECKING([for win32 (cross)])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#ifdef _WIN32
@@ -337,7 +347,7 @@ AH_BOTTOM([
])
-AM_CONDITIONAL(BUILD_NT_SERVICES, test x$bwin32 = xtrue)
+AM_CONDITIONAL(BUILD_NT_SERVICES, test "x$bwin32" = "xtrue")
dnl Enable C99 when compiling with MIPSpro
AC_MSG_CHECKING([for MIPSpro compiler])
@@ -350,7 +360,7 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM(, [
bmipspro=false; AC_MSG_RESULT(no),
bmipspro=true; AC_MSG_RESULT(yes))
-if test "$bmipspro" = true; then
+if test "$bmipspro" = "true"; then
CFLAGS="$CFLAGS -c99"
fi
@@ -374,25 +384,27 @@ AC_SEARCH_LIBS(pthread_detach, [pthread])
AM_CONDITIONAL(THREADS_WIN32, test "$bwin32" = "true")
AM_CONDITIONAL(THREADS_PTHREADS, test "$bwin32" = "false")
-dnl -------------------------------------------------------------------
-dnl Check for functions before libevent, since libevent-1.2 apparently
-dnl exports strlcpy without defining it in a header.
-
AC_CHECK_FUNCS(
_NSGetEnviron \
+ RtlSecureZeroMemory \
+ SecureZeroMemory \
accept4 \
backtrace \
backtrace_symbols_fd \
clock_gettime \
eventfd \
+ explicit_bzero \
+ timingsafe_memcmp \
flock \
ftime \
getaddrinfo \
+ getentropy \
getifaddrs \
getpass \
getrlimit \
gettimeofday \
gmtime_r \
+ htonll \
inet_aton \
ioctl \
issetugid \
@@ -400,6 +412,7 @@ AC_CHECK_FUNCS(
localtime_r \
lround \
memmem \
+ memset_s \
pipe \
pipe2 \
prctl \
@@ -422,13 +435,13 @@ AC_CHECK_FUNCS(
_vscprintf
)
-if test "$bwin32" != true; then
+if test "$bwin32" != "true"; then
AC_CHECK_HEADERS(pthread.h)
AC_CHECK_FUNCS(pthread_create)
AC_CHECK_FUNCS(pthread_condattr_setclock)
fi
-if test "$bwin32" = true; then
+if test "$bwin32" = "true"; then
AC_CHECK_DECLS([SecureZeroMemory, _getwch], , , [
#include <windows.h>
#include <conio.h>
@@ -436,12 +449,13 @@ if test "$bwin32" = true; then
])
fi
-AM_CONDITIONAL(BUILD_READPASSPHRASE_C, test x$ac_cv_func_readpassphrase = xno && test $bwin32 = false)
+AM_CONDITIONAL(BUILD_READPASSPHRASE_C,
+ test "x$ac_cv_func_readpassphrase" = "xno" && test "$bwin32" = "false")
dnl ------------------------------------------------------
dnl Where do you live, libevent? And how do we call you?
-if test "$bwin32" = true; then
+if test "$bwin32" = "true"; then
TOR_LIB_WS32=-lws2_32
TOR_LIB_IPHLPAPI=-liphlpapi
# Some of the cargo-cults recommend -lwsock32 as well, but I don't
@@ -464,7 +478,7 @@ dnl On Gnu/Linux or any place we require it, we'll add librt to the Libevent
dnl linking for static builds.
STATIC_LIBEVENT_FLAGS=""
if test "$enable_static_libevent" = "yes"; then
- if test "$have_rt" = yes; then
+ if test "$have_rt" = "yes"; then
STATIC_LIBEVENT_FLAGS=" -lrt "
fi
fi
@@ -505,12 +519,10 @@ AC_CHECK_MEMBERS([struct event.min_heap_idx], , ,
AC_CHECK_HEADERS(event2/event.h event2/dns.h event2/bufferevent_ssl.h)
-LIBS="$save_LIBS"
-LDFLAGS="$save_LDFLAGS"
-CPPFLAGS="$save_CPPFLAGS"
-
+LIBS="$STATIC_LIBEVENT_FLAGS $TOR_LIB_WS32 $save_LIBS"
-AM_CONDITIONAL(USE_EXTERNAL_EVDNS, test x$ac_cv_header_event2_dns_h = xyes)
+AM_CONDITIONAL(USE_EXTERNAL_EVDNS,
+ test "x$ac_cv_header_event2_dns_h" = "xyes")
if test "$enable_static_libevent" = "yes"; then
if test "$tor_cv_library_libevent_dir" = "(system)"; then
@@ -519,13 +531,29 @@ if test "$enable_static_libevent" = "yes"; then
TOR_LIBEVENT_LIBS="$TOR_LIBDIR_libevent/libevent.a $STATIC_LIBEVENT_FLAGS"
fi
else
- TOR_LIBEVENT_LIBS="-levent"
+ if test "x$ac_cv_header_event2_event_h" = "xyes"; then
+ AC_SEARCH_LIBS(event_new, [event event_core], , AC_MSG_ERROR("libevent2 is installed but linking it failed while searching for event_new"))
+ AC_SEARCH_LIBS(evdns_base_new, [event event_extra], , AC_MSG_ERROR("libevent2 is installed but linking it failed while searching for evdns_base_new"))
+
+ if test "$ac_cv_search_event_new" != "none required"; then
+ TOR_LIBEVENT_LIBS="$ac_cv_search_event_new"
+ fi
+ if test "$ac_cv_search_evdns_base_new" != "none required"; then
+ TOR_LIBEVENT_LIBS="$ac_cv_search_evdns_base_new $TOR_LIBEVENT_LIBS"
+ fi
+ else
+ TOR_LIBEVENT_LIBS="-levent"
+ fi
fi
+LIBS="$save_LIBS"
+LDFLAGS="$save_LDFLAGS"
+CPPFLAGS="$save_CPPFLAGS"
+
dnl This isn't the best test for Libevent 2.0.3-alpha. Once it's released,
dnl we can do much better.
-if test "$enable_bufferevents" = "yes" ; then
- if test "$ac_cv_header_event2_bufferevent_ssl_h" != "yes" ; then
+if test "$enable_bufferevents" = "yes"; then
+ if test "$ac_cv_header_event2_bufferevent_ssl_h" != "yes"; then
AC_MSG_ERROR([You've asked for bufferevent support, but you're using a version of Libevent without SSL support. This won't work. We need Libevent 2.0.8-rc or later, and you don't seem to even have Libevent 2.0.3-alpha.])
else
@@ -543,7 +571,7 @@ int x = 1;
#endif
])], [event_version_number_works=yes; AC_MSG_RESULT([yes]) ],
[event_version_number_works=no; AC_MSG_RESULT([no])])
- if test "$event_version_number_works" != 'yes'; then
+ if test "$event_version_number_works" != "yes"; then
AC_MSG_WARN([Version detection on Libevent seems broken. Your Libevent installation is probably screwed up or very old.])
else
AC_MSG_CHECKING([whether Libevent is new enough for bufferevents])
@@ -603,7 +631,7 @@ ALT_openssl_WITHVAL=""
AC_ARG_WITH(ssl-dir,
AS_HELP_STRING(--with-ssl-dir=PATH, [obsolete alias for --with-openssl-dir]),
[
- if test "x$withval" != xno && test "x$withval" != "x" ; then
+ if test "x$withval" != "xno" && test "x$withval" != "x"; then
ALT_openssl_WITHVAL="$withval"
fi
])
@@ -703,6 +731,19 @@ else
fi
AC_SUBST(TOR_ZLIB_LIBS)
+dnl ----------------------------------------------------------------------
+dnl Check if libcap is available for capabilities.
+
+tor_cap_pkg_debian="libcap2"
+tor_cap_pkg_redhat="libcap"
+tor_cap_devpkg_debian="libcap-dev"
+tor_cap_devpkg_redhat="libcap-devel"
+
+AC_CHECK_LIB([cap], [cap_init], [],
+ AC_MSG_NOTICE([Libcap was not found. Capabilities will not be usable.])
+)
+AC_CHECK_FUNCS(cap_set_proc)
+
dnl ---------------------------------------------------------------------
dnl Now that we know about our major libraries, we can check for compiler
dnl and linker hardening options. We need to do this with the libraries known,
@@ -710,16 +751,16 @@ dnl since sometimes the linker will like an option but not be willing to
dnl use it with a build of a library.
all_ldflags_for_check="$TOR_LDFLAGS_zlib $TOR_LDFLAGS_openssl $TOR_LDFLAGS_libevent"
-all_libs_for_check="$TOR_ZLIB_LIBS $TOR_LIB_MATH $TOR_LIBEVENT_LIBS $TOR_OPENSSL_LIBS $TOR_SYSTEMD_LIBS $TOR_LIB_WS32 $TOR_LIB_GDI"
+all_libs_for_check="$TOR_ZLIB_LIBS $TOR_LIB_MATH $TOR_LIBEVENT_LIBS $TOR_OPENSSL_LIBS $TOR_SYSTEMD_LIBS $TOR_LIB_WS32 $TOR_LIB_GDI $TOR_CAP_LIBS"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [
#if !defined(__clang__)
#error
#endif])], have_clang=yes, have_clang=no)
-if test x$enable_gcc_hardening != xno; then
+if test "x$enable_gcc_hardening" != "xno"; then
CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=2"
- if test x$have_clang = xyes; then
+ if test "x$have_clang" = "xyes"; then
TOR_CHECK_CFLAGS(-Qunused-arguments)
fi
TOR_CHECK_CFLAGS(-fstack-protector-all, also_link)
@@ -742,13 +783,13 @@ m4_ifdef([AS_VAR_IF],[
fi
fi
-if test x$enable_expensive_hardening = xyes ; then
+if test "x$enable_expensive_hardening" = "xyes"; then
TOR_CHECK_CFLAGS([-fsanitize=address])
TOR_CHECK_CFLAGS([-fsanitize=undefined])
TOR_CHECK_CFLAGS([-fno-omit-frame-pointer])
fi
-if test x$enable_linker_hardening != xno; then
+if test "x$enable_linker_hardening" != "xno"; then
TOR_CHECK_LDFLAGS(-z relro -z now, "$all_ldflags_for_check", "$all_libs_for_check")
fi
@@ -762,7 +803,7 @@ saved_CFLAGS="$CFLAGS"
TOR_CHECK_CFLAGS(-fomit-frame-pointer)
F_OMIT_FRAME_POINTER=''
if test "$saved_CFLAGS" != "$CFLAGS"; then
- if test x$enable_expensive_hardening != xyes ; then
+ if test "x$enable_expensive_hardening" != "xyes"; then
F_OMIT_FRAME_POINTER='-fomit-frame-pointer'
fi
fi
@@ -871,10 +912,10 @@ AC_CACHE_CHECK([for nacl compiled with a fast curve25519 implementation],
dnl Okay, now we need to figure out which one to actually use. Fall back
dnl to curve25519-donna.c
- if test x$tor_cv_can_use_curve25519_donna_c64 != xno; then
+ if test "x$tor_cv_can_use_curve25519_donna_c64" != "xno"; then
build_curve25519_donna_c64=yes
use_curve25519_donna=yes
- elif test x$tor_cv_can_use_curve25519_nacl = xyes; then
+ elif test "x$tor_cv_can_use_curve25519_nacl" = "xyes"; then
use_curve25519_nacl=yes
CURVE25519_LIBS=-lnacl
else
@@ -882,16 +923,18 @@ AC_CACHE_CHECK([for nacl compiled with a fast curve25519 implementation],
use_curve25519_donna=yes
fi
-if test x$use_curve25519_donna = xyes; then
+if test "x$use_curve25519_donna" = "xyes"; then
AC_DEFINE(USE_CURVE25519_DONNA, 1,
[Defined if we should use an internal curve25519_donna{,_c64} implementation])
fi
-if test x$use_curve25519_nacl = xyes; then
+if test "x$use_curve25519_nacl" = "xyes"; then
AC_DEFINE(USE_CURVE25519_NACL, 1,
[Defined if we should use a curve25519 from nacl])
fi
-AM_CONDITIONAL(BUILD_CURVE25519_DONNA, test x$build_curve25519_donna = xyes)
-AM_CONDITIONAL(BUILD_CURVE25519_DONNA_C64, test x$build_curve25519_donna_c64 = xyes)
+AM_CONDITIONAL(BUILD_CURVE25519_DONNA,
+ test "x$build_curve25519_donna" = "xyes")
+AM_CONDITIONAL(BUILD_CURVE25519_DONNA_C64,
+ test "x$build_curve25519_donna_c64" = "xyes")
AC_SUBST(CURVE25519_LIBS)
dnl Make sure to enable support for large off_t if available.
@@ -903,6 +946,7 @@ AC_CHECK_HEADERS(
fcntl.h \
signal.h \
string.h \
+ sys/capability.h \
sys/fcntl.h \
sys/stat.h \
sys/time.h \
@@ -943,6 +987,7 @@ AC_CHECK_HEADERS(
sys/select.h \
sys/socket.h \
sys/statvfs.h \
+ sys/syscall.h \
sys/sysctl.h \
sys/syslimits.h \
sys/time.h \
@@ -1029,20 +1074,20 @@ AC_CHECK_HEADERS(linux/netfilter_ipv6/ip6_tables.h,
#include <linux/if.h>
#endif])
-if test x$transparent = xtrue ; then
+if test "x$transparent" = "xtrue"; then
transparent_ok=0
- if test x$net_if_found = x1 && test x$net_pfvar_found = x1 ; then
+ if test "x$net_if_found" = "x1" && test "x$net_pfvar_found" = "x1"; then
transparent_ok=1
fi
- if test x$linux_netfilter_ipv4 = x1 ; then
+ if test "x$linux_netfilter_ipv4" = "x1"; then
transparent_ok=1
fi
- if test x$linux_netfilter_ipv6_ip6_tables = x1 ; then
+ if test "x$linux_netfilter_ipv6_ip6_tables" = "x1"; then
transparent_ok=1
fi
- if test x$transparent_ok = x1 ; then
+ if test "x$transparent_ok" = "x1"; then
AC_DEFINE(USE_TRANSPARENT, 1, "Define to enable transparent proxy support")
- case $host in
+ case "$host" in
*-*-openbsd* | *-*-bitrig*)
AC_DEFINE(OPENBSD, 1, "Define to handle pf on OpenBSD properly") ;;
esac
@@ -1145,7 +1190,7 @@ AC_CHECK_TYPES([rlim_t], , ,
])
AX_CHECK_SIGN([time_t],
- [ AC_DEFINE(TIME_T_IS_SIGNED, 1, [Define if time_t is signed]) ],
+ [ : ],
[ : ], [
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
@@ -1158,8 +1203,8 @@ AX_CHECK_SIGN([time_t],
#endif
])
-if test "$ax_cv_decl_time_t_signed" = no; then
- AC_MSG_WARN([You have an unsigned time_t; some things will probably break. Please tell the Tor developers about your interesting platform.])
+if test "$ax_cv_decl_time_t_signed" = "no"; then
+ AC_MSG_ERROR([You have an unsigned time_t; Tor does not support that. Please tell the Tor developers about your interesting platform.])
fi
AX_CHECK_SIGN([size_t],
@@ -1170,7 +1215,7 @@ AX_CHECK_SIGN([size_t],
#endif
])
-if test "$ax_cv_decl_size_t_signed" = yes; then
+if test "$ax_cv_decl_size_t_signed" = "yes"; then
AC_MSG_ERROR([You have a signed size_t; that's grossly nonconformant.])
fi
@@ -1205,12 +1250,12 @@ return memcmp(&p1,&p2,sizeof(char*))?1:0; }]])],
[tor_cv_null_is_zero=no],
[tor_cv_null_is_zero=cross])])
-if test "$tor_cv_null_is_zero" = cross ; then
+if test "$tor_cv_null_is_zero" = "cross"; then
# Cross-compiling; let's hope that the target isn't raving mad.
AC_MSG_NOTICE([Cross-compiling: we'll assume that NULL is represented as a sequence of 0-valued bytes.])
fi
-if test "$tor_cv_null_is_zero" != no; then
+if test "$tor_cv_null_is_zero" != "no"; then
AC_DEFINE([NULL_REP_IS_ZERO_BYTES], 1,
[Define to 1 iff memset(0) sets pointers to NULL])
fi
@@ -1229,12 +1274,12 @@ return memcmp(&d1,&d2,sizeof(d1))?1:0; }]])],
[tor_cv_dbl0_is_zero=no],
[tor_cv_dbl0_is_zero=cross])])
-if test "$tor_cv_dbl0_is_zero" = cross ; then
+if test "$tor_cv_dbl0_is_zero" = "cross"; then
# Cross-compiling; let's hope that the target isn't raving mad.
AC_MSG_NOTICE([Cross-compiling: we'll assume that 0.0 can be represented as a sequence of 0-valued bytes.])
fi
-if test "$tor_cv_dbl0_is_zero" != no; then
+if test "$tor_cv_dbl0_is_zero" != "no"; then
AC_DEFINE([DOUBLE_0_REP_IS_ZERO_BYTES], 1,
[Define to 1 iff memset(0) sets doubles to 0.0])
fi
@@ -1253,12 +1298,12 @@ int main () { return malloc(0)?0:1; }]])],
[tor_cv_malloc_zero_works=no],
[tor_cv_malloc_zero_works=cross])])
-if test "$tor_cv_malloc_zero_works" = cross; then
+if test "$tor_cv_malloc_zero_works" = "cross"; then
# Cross-compiling; let's hope that the target isn't raving mad.
AC_MSG_NOTICE([Cross-compiling: we'll assume that we need to check malloc() arguments for 0.])
fi
-if test "$tor_cv_malloc_zero_works" = yes; then
+if test "$tor_cv_malloc_zero_works" = "yes"; then
AC_DEFINE([MALLOC_ZERO_WORKS], 1,
[Define to 1 iff malloc(0) returns a pointer])
fi
@@ -1272,14 +1317,15 @@ return problem ? 1 : 0; }]])],
[tor_cv_twos_complement=no],
[tor_cv_twos_complement=cross])])
-if test "$tor_cv_twos_complement" = cross ; then
+if test "$tor_cv_twos_complement" = "cross"; then
# Cross-compiling; let's hope that the target isn't raving mad.
AC_MSG_NOTICE([Cross-compiling: we'll assume that negative integers are represented with two's complement.])
fi
-if test "$tor_cv_twos_complement" != no ; then
+if test "$tor_cv_twos_complement" != "no"; then
AC_DEFINE([USING_TWOS_COMPLEMENT], 1,
- [Define to 1 iff we represent negative integers with two's complement])
+ [Define to 1 iff we represent negative integers with
+ two's complement])
fi
# What does shifting a negative value do?
@@ -1290,12 +1336,12 @@ AC_CACHE_CHECK([whether right-shift on negative values does sign-extension], tor
[tor_cv_sign_extend=no],
[tor_cv_sign_extend=cross])])
-if test "$tor_cv_sign_extend" = cross ; then
+if test "$tor_cv_sign_extend" = "cross"; then
# Cross-compiling; let's hope that the target isn't raving mad.
AC_MSG_NOTICE([Cross-compiling: we'll assume that right-shifting negative integers causes sign-extension])
fi
-if test "$tor_cv_sign_extend" != no ; then
+if test "$tor_cv_sign_extend" != "no"; then
AC_DEFINE([RSHIFT_DOES_SIGN_EXTEND], 1,
[Define to 1 iff right-shifting a negative value performs sign-extension])
fi
@@ -1324,18 +1370,18 @@ AC_ARG_WITH(tcmalloc,
AS_HELP_STRING(--with-tcmalloc, [use tcmalloc memory allocation library]),
[ tcmalloc=yes ], [ tcmalloc=no ])
-if test x$tcmalloc = xyes ; then
+if test "x$tcmalloc" = "xyes"; then
LDFLAGS="-ltcmalloc $LDFLAGS"
fi
using_custom_malloc=no
-if test x$enable_openbsd_malloc = xyes ; then
+if test "x$enable_openbsd_malloc" = "xyes"; then
using_custom_malloc=yes
fi
-if test x$tcmalloc = xyes ; then
+if test "x$tcmalloc" = "xyes"; then
using_custom_malloc=yes
fi
-if test $using_custom_malloc = no ; then
+if test "$using_custom_malloc" = "no"; then
AC_CHECK_FUNCS(mallinfo)
fi
@@ -1452,20 +1498,20 @@ int main(int c, char **v) { char **t = environ; }])],
tor_cv_have_environ_declared=yes,
tor_cv_have_environ_declared=no))
-if test "$tor_cv_have_func_macro" = 'yes'; then
+if test "$tor_cv_have_func_macro" = "yes"; then
AC_DEFINE(HAVE_MACRO__func__, 1, [Defined if the compiler supports __func__])
fi
-if test "$tor_cv_have_FUNC_macro" = 'yes'; then
+if test "$tor_cv_have_FUNC_macro" = "yes"; then
AC_DEFINE(HAVE_MACRO__FUNC__, 1, [Defined if the compiler supports __FUNC__])
fi
-if test "$tor_cv_have_FUNCTION_macro" = 'yes'; then
+if test "$tor_cv_have_FUNCTION_macro" = "yes"; then
AC_DEFINE(HAVE_MACRO__FUNCTION__, 1,
[Defined if the compiler supports __FUNCTION__])
fi
-if test "$tor_cv_have_environ_declared" = 'yes'; then
+if test "$tor_cv_have_environ_declared" = "yes"; then
AC_DEFINE(HAVE_EXTERN_ENVIRON_DECLARED, 1,
[Defined if we have extern char **environ already declared])
fi
@@ -1501,7 +1547,7 @@ AC_SUBST(BINDIR)
LOCALSTATEDIR=`eval echo $localstatedir`
AC_SUBST(LOCALSTATEDIR)
-if test "$bwin32" = true; then
+if test "$bwin32" = "true"; then
# Test if the linker supports the --nxcompat and --dynamicbase options
# for Windows
save_LDFLAGS="$LDFLAGS"
@@ -1517,7 +1563,7 @@ fi
# Set CFLAGS _after_ all the above checks, since our warnings are stricter
# than autoconf's macros like.
-if test "$GCC" = yes; then
+if test "$GCC" = "yes"; then
# Disable GCC's strict aliasing checks. They are an hours-to-debug
# accident waiting to happen.
CFLAGS="$CFLAGS -Wall -fno-strict-aliasing"
@@ -1540,7 +1586,8 @@ esac
# Add some more warnings which we use in development but not in the
# released versions. (Some relevant gcc versions can't handle these.)
-if test x$enable_gcc_warnings = xyes || test x$enable_gcc_warnings_advisory = xyes; then
+if test "x$enable_gcc_warnings" = "xyes" ||
+ test "x$enable_gcc_warnings_advisory" = "xyes"; then
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [
#if !defined(__GNUC__) || (__GNUC__ < 4)
@@ -1569,7 +1616,7 @@ if test x$enable_gcc_warnings = xyes || test x$enable_gcc_warnings_advisory = xy
have_shorten64_flag=no)
CFLAGS="$save_CFLAGS"
- case $host in
+ case "$host" in
*-*-openbsd* | *-*-bitrig*)
# Some OpenBSD versions (like 4.8) have -Wsystem-headers by default.
# That's fine, except that the headers don't pass -Wredundant-decls.
@@ -1584,18 +1631,18 @@ if test x$enable_gcc_warnings = xyes || test x$enable_gcc_warnings_advisory = xy
CFLAGS="$CFLAGS -Wwrite-strings -Wmissing-declarations -Wredundant-decls"
CFLAGS="$CFLAGS -Wnested-externs -Wbad-function-cast -Wswitch-enum"
- if test x$enable_gcc_warnings = xyes; then
+ if test "x$enable_gcc_warnings" = "xyes"; then
CFLAGS="$CFLAGS -Werror"
fi
# Disabled, so we can use mallinfo(): -Waggregate-return
- if test x$have_gcc4 = xyes ; then
+ if test "x$have_gcc4" = "xyes"; then
# These warnings break gcc 3.3.5 and work on gcc 4.0.2
CFLAGS="$CFLAGS -Winit-self -Wmissing-field-initializers -Wold-style-definition"
fi
- if test x$have_gcc42 = xyes ; then
+ if test "x$have_gcc42" = "xyes"; then
# These warnings break gcc 4.0.2 and work on gcc 4.2
# XXXX020 See if any of these work with earlier versions.
CFLAGS="$CFLAGS -Waddress -Wmissing-noreturn -Wstrict-overflow=1"
@@ -1603,24 +1650,24 @@ if test x$enable_gcc_warnings = xyes || test x$enable_gcc_warnings_advisory = xy
# We used to use -Wstrict-overflow=5, but that breaks us heavily under 4.3.
fi
- if test x$have_gcc42 = xyes && test x$have_clang = xno; then
+ if test "x$have_gcc42" = "xyes" && test "x$have_clang" = "xno"; then
# These warnings break gcc 4.0.2 and clang, but work on gcc 4.2
CFLAGS="$CFLAGS -Wnormalized=id -Woverride-init"
fi
- if test x$have_gcc43 = xyes ; then
+ if test "x$have_gcc43" = "xyes"; then
# These warnings break gcc 4.2 and work on gcc 4.3
# XXXX020 See if any of these work with earlier versions.
CFLAGS="$CFLAGS -Wextra -Warray-bounds"
fi
- if test x$have_gcc46 = xyes ; then
+ if test "x$have_gcc46" = "xyes"; then
# This warning was added in gcc 4.3, but it appears to generate
# spurious warnings in gcc 4.4. I don't know if it works in 4.5.
CFLAGS="$CFLAGS -Wlogical-op"
fi
- if test x$have_shorten64_flag = xyes ; then
+ if test "x$have_shorten64_flag" = "xyes"; then
CFLAGS="$CFLAGS -Wshorten-64-to-32"
fi
@@ -1630,7 +1677,7 @@ if test x$enable_gcc_warnings = xyes || test x$enable_gcc_warnings_advisory = xy
# CFLAGS="$CFLAGS -Winline"
fi
-if test "$enable_coverage" = yes && test "$have_clang" = "no"; then
+if test "$enable_coverage" = "yes" && test "$have_clang" = "no"; then
case "$host_os" in
darwin*)
AC_MSG_WARN([Tried to enable coverage on OSX without using the clang compiler. This might not work! If coverage fails, use CC=clang when configuring with --enable-profiling.])
@@ -1653,7 +1700,7 @@ AC_CONFIG_FILES([
scripts/maint/updateVersions.pl
])
-if test x$asciidoc = xtrue && test "$ASCIIDOC" = "none" ; then
+if test "x$asciidoc" = "xtrue" && test "$ASCIIDOC" = "none"; then
regular_mans="doc/tor doc/tor-gencert doc/tor-resolve doc/torify"
for file in $regular_mans ; do
if ! [[ -f "$srcdir/$file.1.in" ]] || ! [[ -f "$srcdir/$file.html.in" ]] ; then
diff --git a/contrib/win32build/tor-mingw.nsi.in b/contrib/win32build/tor-mingw.nsi.in
index 08cef8dfbc..9a4de0ab08 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.2.7.6-dev"
+!define VERSION "0.2.8.1-alpha-dev"
!define INSTALLER "tor-${VERSION}-win32.exe"
!define WEBSITE "https://www.torproject.org/"
!define LICENSE "LICENSE"
diff --git a/doc/HACKING b/doc/HACKING
deleted file mode 100644
index e92d675a43..0000000000
--- a/doc/HACKING
+++ /dev/null
@@ -1,659 +0,0 @@
-Hacking Tor: An Incomplete Guide
-================================
-
-Getting started
----------------
-
-For full information on how Tor is supposed to work, look at the files in
-https://gitweb.torproject.org/torspec.git/tree
-
-For an explanation of how to change Tor's design to work differently, look at
-https://gitweb.torproject.org/torspec.git/blob_plain/HEAD:/proposals/001-process.txt
-
-For the latest version of the code, get a copy of git, and
-
- git clone https://git.torproject.org/git/tor
-
-We talk about Tor on the tor-talk mailing list. Design proposals and
-discussion belong on the tor-dev mailing list. We hang around on
-irc.oftc.net, with general discussion happening on #tor and development
-happening on #tor-dev.
-
-How we use Git branches
------------------------
-
-Each main development series (like 0.2.1, 0.2.2, etc) has its main work
-applied to a single branch. At most one series can be the development series
-at a time; all other series are maintenance series that get bug-fixes only.
-The development series is built in a git branch called "master"; the
-maintenance series are built in branches called "maint-0.2.0", "maint-0.2.1",
-and so on. We regularly merge the active maint branches forward.
-
-For all series except the development series, we also have a "release" branch
-(as in "release-0.2.1"). The release series is based on the corresponding
-maintenance series, except that it deliberately lags the maint series for
-most of its patches, so that bugfix patches are not typically included in a
-maintenance release until they've been tested for a while in a development
-release. Occasionally, we'll merge an urgent bugfix into the release branch
-before it gets merged into maint, but that's rare.
-
-If you're working on a bugfix for a bug that occurs in a particular version,
-base your bugfix branch on the "maint" branch for the first supported series
-that has that bug. (As of June 2013, we're supporting 0.2.3 and later.) If
-you're working on a new feature, base it on the master branch.
-
-
-How we log changes
-------------------
-
-When you do a commit that needs a ChangeLog entry, add a new file to
-the "changes" toplevel subdirectory. It should have the format of a
-one-entry changelog section from the current ChangeLog file, as in
-
- o Major bugfixes:
- - Fix a potential buffer overflow. Fixes bug 99999; bugfix on
- 0.3.1.4-beta.
-
-To write a changes file, first categorize the change. Some common categories
-are: Minor bugfixes, Major bugfixes, Minor features, Major features, Code
-simplifications and refactoring. Then say what the change does. If
-it's a bugfix, mention what bug it fixes and when the bug was
-introduced. To find out which Git tag the change was introduced in,
-you can use "git describe --contains <sha1 of commit>".
-
-If at all possible, try to create this file in the same commit where you are
-making the change. Please give it a distinctive name that no other branch will
-use for the lifetime of your change. To verify the format of the changes file,
-you can use "make check-changes".
-
-When we go to make a release, we will concatenate all the entries
-in changes to make a draft changelog, and clear the directory. We'll
-then edit the draft changelog into a nice readable format.
-
-What needs a changes file?::
- A not-exhaustive list: Anything that might change user-visible
- behavior. Anything that changes internals, documentation, or the build
- system enough that somebody could notice. Big or interesting code
- rewrites. Anything about which somebody might plausibly wonder "when
- did that happen, and/or why did we do that" 6 months down the line.
-
-Why use changes files instead of Git commit messages?::
- Git commit messages are written for developers, not users, and they
- are nigh-impossible to revise after the fact.
-
-Why use changes files instead of entries in the ChangeLog?::
- Having every single commit touch the ChangeLog file tended to create
- zillions of merge conflicts.
-
-Useful tools
-------------
-
-These aren't strictly necessary for hacking on Tor, but they can help track
-down bugs.
-
-Jenkins
-~~~~~~~
-
-https://jenkins.torproject.org
-
-Dmalloc
-~~~~~~~
-
-The dmalloc library will keep track of memory allocation, so you can find out
-if we're leaking memory, doing any double-frees, or so on.
-
- dmalloc -l ~/dmalloc.log
- (run the commands it tells you)
- ./configure --with-dmalloc
-
-Valgrind
-~~~~~~~~
-
-valgrind --leak-check=yes --error-limit=no --show-reachable=yes src/or/tor
-
-(Note that if you get a zillion openssl warnings, you will also need to
-pass --undef-value-errors=no to valgrind, or rebuild your openssl
-with -DPURIFY.)
-
-Coverity
-~~~~~~~~
-
-Nick regularly runs the coverity static analyzer on the Tor codebase.
-
-The preprocessor define __COVERITY__ is used to work around instances
-where coverity picks up behavior that we wish to permit.
-
-clang Static Analyzer
-~~~~~~~~~~~~~~~~~~~~~
-
-The clang static analyzer can be run on the Tor codebase using Xcode (WIP)
-or a command-line build.
-
-The preprocessor define __clang_analyzer__ is used to work around instances
-where clang picks up behavior that we wish to permit.
-
-clang Runtime Sanitizers
-~~~~~~~~~~~~~~~~
-
-To build the Tor codebase with the clang Address and Undefined Behavior
-sanitizers, see the file contrib/clang/sanitize_blacklist.txt.
-
-Preprocessor workarounds for instances where clang picks up behavior that
-we wish to permit are also documented in the blacklist file.
-
-Running lcov for unit test coverage
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Lcov is a utility that generates pretty HTML reports of test code coverage.
-To generate such a report:
-
------
- ./configure --enable-coverage
- make
- make coverage-html
- $BROWSER ./coverage_html/index.html
------
-
-This will run the tor unit test suite `./src/test/test` and generate the HTML
-coverage code report under the directory ./coverage_html/. To change the
-output directory, use `make coverage-html HTML_COVER_DIR=./funky_new_cov_dir`.
-
-Coverage diffs using lcov are not currently implemented, but are being
-investigated (as of July 2014).
-
-Running the unit tests
-~~~~~~~~~~~~~~~~~~~~~~
-
-To quickly run all the tests distributed with Tor:
------
- make check
------
-
-To run the fast unit tests only:
------
- make test
------
-
-To selectively run just some tests (the following can be combined
-arbitrarily):
------
- ./src/test/test <name_of_test> [<name of test 2>] ...
- ./src/test/test <prefix_of_name_of_test>.. [<prefix_of_name_of_test2>..] ...
- ./src/test/test :<name_of_excluded_test> [:<name_of_excluded_test2]...
------
-
-To run all tests, including those based on Stem or Chutney:
------
- make test-full
------
-
-To run all tests, including those basedd on Stem or Chutney that require a
-working connection to the internet:
------
- make test-full-online
------
-
-Running gcov for unit test coverage
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
------
- ./configure --enable-coverage
- make
- make check
- # or--- make test-full ? make test-full-online?
- mkdir coverage-output
- ./scripts/test/coverage coverage-output
------
-
-(On OSX, you'll need to start with "--enable-coverage CC=clang".)
-
-Then, look at the .gcov files in coverage-output. '-' before a line means
-that the compiler generated no code for that line. '######' means that the
-line was never reached. Lines with numbers were called that number of times.
-
-If that doesn't work:
- * Try configuring Tor with --disable-gcc-hardening
- * You might need to run 'make clean' after you run './configure'.
-
-If you make changes to Tor and want to get another set of coverage results,
-you can run "make reset-gcov" to clear the intermediary gcov output.
-
-If you have two different "coverage-output" directories, and you want to see
-a meaningful diff between them, you can run:
-
------
- ./scripts/test/cov-diff coverage-output1 coverage-output2 | less
------
-
-In this diff, any lines that were visited at least once will have coverage
-"1". This lets you inspect what you (probably) really want to know: which
-untested lines were changed? Are there any new untested lines?
-
-Running integration tests
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-We have the beginnings of a set of scripts to run integration tests using
-Chutney. To try them, set CHUTNEY_PATH to your chutney source directory, and
-run "make test-network".
-
-We also have scripts to run integration tests using Stem. To try them, set
-STEM_SOURCE_DIR to your Stem source directory, and run "test-stem".
-
-Profiling Tor with oprofile
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The oprofile tool runs (on Linux only!) to tell you what functions Tor is
-spending its CPU time in, so we can identify berformance pottlenecks.
-
-Here are some basic instructions
-
- - Build tor with debugging symbols (you probably already have, unless
- you messed with CFLAGS during the build process).
- - Build all the libraries you care about with debugging symbols
- (probably you only care about libssl, maybe zlib and Libevent).
- - Copy this tor to a new directory
- - Copy all the libraries it uses to that dir too (ldd ./tor will
- tell you)
- - Set LD_LIBRARY_PATH to include that dir. ldd ./tor should now
- show you it's using the libs in that dir
- - Run that tor
- - Reset oprofiles counters/start it
- * "opcontrol --reset; opcontrol --start", if Nick remembers right.
- - After a while, have it dump the stats on tor and all the libs
- in that dir you created.
- * "opcontrol --dump;"
- * "opreport -l that_dir/*"
- - Profit
-
-Generating and analyzing a callgraph
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-1. Run ./scripts/maint/generate_callgraph.sh . This will generate a
- bunch of files in a new ./callgraph directory.
-
-2. Run ./scripts/maint/analyze_callgraph.py callgraph/src/*/* . This
- will do a lot of graph operations and then dump out a new
- "callgraph.pkl" file, containing data in Python's "pickle" format.
-
-3. Run ./scripts/maint/display_callgraph.py . It will display:
- - the number of functions reachable from each function.
- - all strongly-connnected components in the Tor callgraph
- - the largest bottlenecks in the largest SCC in the Tor callgraph.
-
-Note that currently the callgraph generator can't detect calls that pass
-through function pointers.
-
-Coding conventions
-------------------
-
-Patch checklist
-~~~~~~~~~~~~~~~
-
-If possible, send your patch as one of these (in descending order of
-preference)
-
- - A git branch we can pull from
- - Patches generated by git format-patch
- - A unified diff
-
-Did you remember...
-
- - To build your code while configured with --enable-gcc-warnings?
- - To run "make check-spaces" on your code?
- - To run "make check-docs" to see whether all new options are on
- the manpage?
- - To write unit tests, as possible?
- - To base your code on the appropriate branch?
- - To include a file in the "changes" directory as appropriate?
-
-Whitespace and C conformance
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Invoke "make check-spaces" from time to time, so it can tell you about
-deviations from our C whitespace style. Generally, we use:
-
- - Unix-style line endings
- - K&R-style indentation
- - No space before newlines
- - A blank line at the end of each file
- - Never more than one blank line in a row
- - Always spaces, never tabs
- - No more than 79-columns per line.
- - Two spaces per indent.
- - A space between control keywords and their corresponding paren
- "if (x)", "while (x)", and "switch (x)", never "if(x)", "while(x)", or
- "switch(x)".
- - A space between anything and an open brace.
- - No space between a function name and an opening paren. "puts(x)", not
- "puts (x)".
- - Function declarations at the start of the line.
-
-We try hard to build without warnings everywhere. In particular, if you're
-using gcc, you should invoke the configure script with the option
-"--enable-gcc-warnings". This will give a bunch of extra warning flags to
-the compiler, and help us find divergences from our preferred C style.
-
-Getting emacs to edit Tor source properly
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Nick likes to put the following snippet in his .emacs file:
-
------
- (add-hook 'c-mode-hook
- (lambda ()
- (font-lock-mode 1)
- (set-variable 'show-trailing-whitespace t)
-
- (let ((fname (expand-file-name (buffer-file-name))))
- (cond
- ((string-match "^/home/nickm/src/libevent" fname)
- (set-variable 'indent-tabs-mode t)
- (set-variable 'c-basic-offset 4)
- (set-variable 'tab-width 4))
- ((string-match "^/home/nickm/src/tor" fname)
- (set-variable 'indent-tabs-mode nil)
- (set-variable 'c-basic-offset 2))
- ((string-match "^/home/nickm/src/openssl" fname)
- (set-variable 'indent-tabs-mode t)
- (set-variable 'c-basic-offset 8)
- (set-variable 'tab-width 8))
- ))))
------
-
-You'll note that it defaults to showing all trailing whitespace. The "cond"
-test detects whether the file is one of a few C free software projects that I
-often edit, and sets up the indentation level and tab preferences to match
-what they want.
-
-If you want to try this out, you'll need to change the filename regex
-patterns to match where you keep your Tor files.
-
-If you use emacs for editing Tor and nothing else, you could always just say:
-
------
- (add-hook 'c-mode-hook
- (lambda ()
- (font-lock-mode 1)
- (set-variable 'show-trailing-whitespace t)
- (set-variable 'indent-tabs-mode nil)
- (set-variable 'c-basic-offset 2)))
------
-
-There is probably a better way to do this. No, we are probably not going
-to clutter the files with emacs stuff.
-
-
-Functions to use
-~~~~~~~~~~~~~~~~
-
-We have some wrapper functions like tor_malloc, tor_free, tor_strdup, and
-tor_gettimeofday; use them instead of their generic equivalents. (They
-always succeed or exit.)
-
-You can get a full list of the compatibility functions that Tor provides by
-looking through src/common/util.h and src/common/compat.h. You can see the
-available containers in src/common/containers.h. You should probably
-familiarize yourself with these modules before you write too much code, or
-else you'll wind up reinventing the wheel.
-
-Use 'INLINE' instead of 'inline', so that we work properly on Windows.
-
-Calling and naming conventions
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Whenever possible, functions should return -1 on error and 0 on success.
-
-For multi-word identifiers, use lowercase words combined with
-underscores. (e.g., "multi_word_identifier"). Use ALL_CAPS for macros and
-constants.
-
-Typenames should end with "_t".
-
-Function names should be prefixed with a module name or object name. (In
-general, code to manipulate an object should be a module with the same name
-as the object, so it's hard to tell which convention is used.)
-
-Functions that do things should have imperative-verb names
-(e.g. buffer_clear, buffer_resize); functions that return booleans should
-have predicate names (e.g. buffer_is_empty, buffer_needs_resizing).
-
-If you find that you have four or more possible return code values, it's
-probably time to create an enum. If you find that you are passing three or
-more flags to a function, it's probably time to create a flags argument that
-takes a bitfield.
-
-What To Optimize
-~~~~~~~~~~~~~~~~
-
-Don't optimize anything if it's not in the critical path. Right now, the
-critical path seems to be AES, logging, and the network itself. Feel free to
-do your own profiling to determine otherwise.
-
-Log conventions
-~~~~~~~~~~~~~~~
-
-https://www.torproject.org/docs/faq#LogLevel
-
-No error or warning messages should be expected during normal OR or OP
-operation.
-
-If a library function is currently called such that failure always means ERR,
-then the library function should log WARN and let the caller log ERR.
-
-Every message of severity INFO or higher should either (A) be intelligible
-to end-users who don't know the Tor source; or (B) somehow inform the
-end-users that they aren't expected to understand the message (perhaps
-with a string like "internal error"). Option (A) is to be preferred to
-option (B).
-
-Doxygen
-~~~~~~~~
-
-We use the 'doxygen' utility to generate documentation from our
-source code. Here's how to use it:
-
- 1. Begin every file that should be documented with
- /**
- * \file filename.c
- * \brief Short description of the file.
- **/
-
- (Doxygen will recognize any comment beginning with /** as special.)
-
- 2. Before any function, structure, #define, or variable you want to
- document, add a comment of the form:
-
- /** Describe the function's actions in imperative sentences.
- *
- * Use blank lines for paragraph breaks
- * - and
- * - hyphens
- * - for
- * - lists.
- *
- * Write <b>argument_names</b> in boldface.
- *
- * \code
- * place_example_code();
- * between_code_and_endcode_commands();
- * \endcode
- */
-
- 3. Make sure to escape the characters "<", ">", "\", "%" and "#" as "\<",
- "\>", "\\", "\%", and "\#".
-
- 4. To document structure members, you can use two forms:
-
- struct foo {
- /** You can put the comment before an element; */
- int a;
- int b; /**< Or use the less-than symbol to put the comment
- * after the element. */
- };
-
- 5. To generate documentation from the Tor source code, type:
-
- $ doxygen -g
-
- To generate a file called 'Doxyfile'. Edit that file and run
- 'doxygen' to generate the API documentation.
-
- 6. See the Doxygen manual for more information; this summary just
- scratches the surface.
-
-Doxygen comment conventions
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Say what functions do as a series of one or more imperative sentences, as
-though you were telling somebody how to be the function. In other words, DO
-NOT say:
-
- /** The strtol function parses a number.
- *
- * nptr -- the string to parse. It can include whitespace.
- * endptr -- a string pointer to hold the first thing that is not part
- * of the number, if present.
- * base -- the numeric base.
- * returns: the resulting number.
- */
- long strtol(const char *nptr, char **nptr, int base);
-
-Instead, please DO say:
-
- /** Parse a number in radix <b>base</b> from the string <b>nptr</b>,
- * and return the result. Skip all leading whitespace. If
- * <b>endptr</b> is not NULL, set *<b>endptr</b> to the first character
- * after the number parsed.
- **/
- long strtol(const char *nptr, char **nptr, int base);
-
-Doxygen comments are the contract in our abstraction-by-contract world: if
-the functions that call your function rely on it doing something, then your
-function should mention that it does that something in the documentation. If
-you rely on a function doing something beyond what is in its documentation,
-then you should watch out, or it might do something else later.
-
-Putting out a new release
--------------------------
-
-Here are the steps Roger takes when putting out a new Tor release:
-
-1) Use it for a while, as a client, as a relay, as a hidden service,
-and as a directory authority. See if it has any obvious bugs, and
-resolve those.
-
-1.5) As applicable, merge the maint-X branch into the release-X branch.
-
-2) Gather the changes/* files into a changelog entry, rewriting many
-of them and reordering to focus on what users and funders would find
-interesting and understandable.
-
- 2.1) Make sure that everything that wants a bug number has one.
- Make sure that everything which is a bugfix says what version
- it was a bugfix on.
- 2.2) Concatenate them.
- 2.3) Sort them by section. Within each section, sort by "version it's
- a bugfix on", else by numerical ticket order.
-
- 2.4) Clean them up:
-
- Standard idioms:
- "Fixes bug 9999; bugfix on 0.3.3.3-alpha."
-
- One space after a period.
-
- Make stuff very terse
-
- Make sure each section name ends with a colon
-
- Describe the user-visible problem right away
-
- Mention relevant config options by name. If they're rare or unusual,
- remind people what they're for
-
- Avoid starting lines with open-paren
-
- Present and imperative tense: not past.
-
- 'Relays', not 'servers' or 'nodes' or 'Tor relays'.
-
- "Stop FOOing", not "Fix a bug where we would FOO".
-
- Try not to let any given section be longer than about a page. Break up
- long sections into subsections by some sort of common subtopic. This
- guideline is especially important when organizing Release Notes for
- new stable releases.
-
- If a given changes stanza showed up in a different release (e.g.
- maint-0.2.1), be sure to make the stanzas identical (so people can
- distinguish if these are the same change).
-
- 2.5) Merge them in.
-
- 2.6) Clean everything one last time.
-
- 2.7) Run ./scripts/maint/format_changelog.py to make it prettier.
-
-3) Compose a short release blurb to highlight the user-facing
-changes. Insert said release blurb into the ChangeLog stanza. If it's
-a stable release, add it to the ReleaseNotes file too. If we're adding
-to a release-0.2.x branch, manually commit the changelogs to the later
-git branches too.
-
-4) In maint-0.2.x, bump the version number in configure.ac and run
- scripts/maint/updateVersions.pl to update version numbers in other
- places, and commit. Then merge maint-0.2.x into release-0.2.x.
-
- (NOTE: TO bump the version number, edit configure.ac, and then run
- either make, or 'perl scripts/maint/updateVersions.pl', depending on
- your version.)
-
-5) Make dist, put the tarball up somewhere, and tell #tor about it. Wait
-a while to see if anybody has problems building it. Try to get Sebastian
-or somebody to try building it on Windows.
-
-6) Get at least two of weasel/arma/sebastian to put the new version number
-in their approved versions list.
-
-7) Sign the tarball, then sign and push the git tag:
- gpg -ba <the_tarball>
- git tag -u <keyid> tor-0.2.x.y-status
- git push origin tag tor-0.2.x.y-status
-
-8a) scp the tarball and its sig to the dist website, i.e.
-/srv/dist-master.torproject.org/htdocs/ on dist-master. When you want
-it to go live, you run "static-update-component dist.torproject.org"
-on dist-master.
-
-8b) Edit "include/versions.wmi" and "Makefile" to note the new version.
-
-9) Email the packagers (cc'ing tor-assistants) that a new tarball is up.
- The current list of packagers is:
- {weasel,gk,mikeperry} at torproject dot org
- {blueness} at gentoo dot org
- {paul} at invizbox dot io
- {ondrej.mikle} at gmail dot com
- {lfleischer} at archlinux dot org
-
-10) Add the version number to Trac. To do this, go to Trac, log in,
-select "Admin" near the top of the screen, then select "Versions" from
-the menu on the left. At the right, there will be an "Add version"
-box. By convention, we enter the version in the form "Tor:
-0.2.2.23-alpha" (or whatever the version is), and we select the date as
-the date in the ChangeLog.
-
-11) Forward-port the ChangeLog.
-
-12) Wait up to a day or two (for a development release), or until most
-packages are up (for a stable release), and mail the release blurb and
-changelog to tor-talk or tor-announce.
-
- (We might be moving to faster announcements, but don't announce until
- the website is at least updated.)
-
-13) If it's a stable release, bump the version number in the maint-x.y.z
- branch to "newversion-dev", and do a "merge -s ours" merge to avoid
- taking that change into master. Do a similar 'merge -s theirs'
- merge to get the change (and only that change) into release. (Some
- of the build scripts require that maint merge cleanly into release.)
-
diff --git a/doc/HACKING/CodingStandards.md b/doc/HACKING/CodingStandards.md
new file mode 100644
index 0000000000..4aafa5ddd4
--- /dev/null
+++ b/doc/HACKING/CodingStandards.md
@@ -0,0 +1,245 @@
+Coding conventions for Tor
+==========================
+
+tl;dr:
+
+ - Run configure with `--enable-gcc-warnings`
+ - Run `make check-spaces` to catch whitespace errors
+ - Document your functions
+ - Write unit tests
+ - Add a file in `changes` for your branch.
+
+Patch checklist
+---------------
+
+If possible, send your patch as one of these (in descending order of
+preference)
+
+ - A git branch we can pull from
+ - Patches generated by git format-patch
+ - A unified diff
+
+Did you remember...
+
+ - To build your code while configured with `--enable-gcc-warnings`?
+ - To run `make check-spaces` on your code?
+ - To run `make check-docs` to see whether all new options are on
+ the manpage?
+ - To write unit tests, as possible?
+ - To base your code on the appropriate branch?
+ - To include a file in the `changes` directory as appropriate?
+
+How we use Git branches
+=======================
+
+Each main development series (like 0.2.1, 0.2.2, etc) has its main work
+applied to a single branch. At most one series can be the development series
+at a time; all other series are maintenance series that get bug-fixes only.
+The development series is built in a git branch called "master"; the
+maintenance series are built in branches called "maint-0.2.0", "maint-0.2.1",
+and so on. We regularly merge the active maint branches forward.
+
+For all series except the development series, we also have a "release" branch
+(as in "release-0.2.1"). The release series is based on the corresponding
+maintenance series, except that it deliberately lags the maint series for
+most of its patches, so that bugfix patches are not typically included in a
+maintenance release until they've been tested for a while in a development
+release. Occasionally, we'll merge an urgent bugfix into the release branch
+before it gets merged into maint, but that's rare.
+
+If you're working on a bugfix for a bug that occurs in a particular version,
+base your bugfix branch on the "maint" branch for the first supported series
+that has that bug. (As of June 2013, we're supporting 0.2.3 and later.) If
+you're working on a new feature, base it on the master branch.
+
+
+How we log changes
+==================
+
+When you do a commit that needs a ChangeLog entry, add a new file to
+the `changes` toplevel subdirectory. It should have the format of a
+one-entry changelog section from the current ChangeLog file, as in
+
+- Major bugfixes:
+ - Fix a potential buffer overflow. Fixes bug 99999; bugfix on
+ 0.3.1.4-beta.
+
+To write a changes file, first categorize the change. Some common categories
+are: Minor bugfixes, Major bugfixes, Minor features, Major features, Code
+simplifications and refactoring. Then say what the change does. If
+it's a bugfix, mention what bug it fixes and when the bug was
+introduced. To find out which Git tag the change was introduced in,
+you can use `git describe --contains <sha1 of commit>`.
+
+If at all possible, try to create this file in the same commit where you are
+making the change. Please give it a distinctive name that no other branch will
+use for the lifetime of your change. To verify the format of the changes file,
+you can use `make check-changes`.
+
+When we go to make a release, we will concatenate all the entries
+in changes to make a draft changelog, and clear the directory. We'll
+then edit the draft changelog into a nice readable format.
+
+To make sure that stuff is in the right format, we use
+scripts/maint/lintChanges.py to check the changes files for
+(superficial) validity. You can run this script on your own changes
+files!
+
+What needs a changes file?
+
+ * A not-exhaustive list: Anything that might change user-visible
+ behavior. Anything that changes internals, documentation, or the build
+ system enough that somebody could notice. Big or interesting code
+ rewrites. Anything about which somebody might plausibly wonder "when
+ did that happen, and/or why did we do that" 6 months down the line.
+
+Why use changes files instead of Git commit messages?
+
+ * Git commit messages are written for developers, not users, and they
+ are nigh-impossible to revise after the fact.
+
+Why use changes files instead of entries in the ChangeLog?
+
+ * Having every single commit touch the ChangeLog file tended to create
+ zillions of merge conflicts.
+
+Whitespace and C conformance
+----------------------------
+
+Invoke `make check-spaces` from time to time, so it can tell you about
+deviations from our C whitespace style. Generally, we use:
+
+ - Unix-style line endings
+ - K&R-style indentation
+ - No space before newlines
+ - A blank line at the end of each file
+ - Never more than one blank line in a row
+ - Always spaces, never tabs
+ - No more than 79-columns per line.
+ - Two spaces per indent.
+ - A space between control keywords and their corresponding paren
+ `if (x)`, `while (x)`, and `switch (x)`, never `if(x)`, `while(x)`, or
+ `switch(x)`.
+ - A space between anything and an open brace.
+ - No space between a function name and an opening paren. `puts(x)`, not
+ `puts (x)`.
+ - Function declarations at the start of the line.
+
+We try hard to build without warnings everywhere. In particular, if you're
+using gcc, you should invoke the configure script with the option
+`--enable-gcc-warnings`. This will give a bunch of extra warning flags to
+the compiler, and help us find divergences from our preferred C style.
+
+Functions to use; functions not to use
+--------------------------------------
+
+We have some wrapper functions like `tor_malloc`, `tor_free`, `tor_strdup`, and
+`tor_gettimeofday;` use them instead of their generic equivalents. (They
+always succeed or exit.)
+
+You can get a full list of the compatibility functions that Tor provides by
+looking through `src/common/util*.h` and `src/common/compat*.h`. You can see the
+available containers in `src/common/containers*.h`. You should probably
+familiarize yourself with these modules before you write too much code, or
+else you'll wind up reinventing the wheel.
+
+We don't use `strcat` or `strcpy` or `sprintf` of any of those notoriously broken
+old C functions. Use `strlcat`, `strlcpy`, or `tor_snprintf/tor_asprintf` instead.
+
+We don't call `memcmp()` directly. Use `fast_memeq()`, `fast_memneq()`,
+`tor_memeq()`, or `tor_memneq()` for most purposes.
+
+Functions not to write
+----------------------
+
+Try to never hand-write new code to parse or generate binary
+formats. Instead, use trunnel if at all possible. See
+
+ https://gitweb.torproject.org/trunnel.git/tree
+
+for more information about trunnel.
+
+For information on adding new trunnel code to Tor, see src/trunnel/README
+
+
+Calling and naming conventions
+------------------------------
+
+Whenever possible, functions should return -1 on error and 0 on success.
+
+For multi-word identifiers, use lowercase words combined with
+underscores. (e.g., `multi_word_identifier`). Use ALL_CAPS for macros and
+constants.
+
+Typenames should end with `_t`.
+
+Function names should be prefixed with a module name or object name. (In
+general, code to manipulate an object should be a module with the same name
+as the object, so it's hard to tell which convention is used.)
+
+Functions that do things should have imperative-verb names
+(e.g. `buffer_clear`, `buffer_resize`); functions that return booleans should
+have predicate names (e.g. `buffer_is_empty`, `buffer_needs_resizing`).
+
+If you find that you have four or more possible return code values, it's
+probably time to create an enum. If you find that you are passing three or
+more flags to a function, it's probably time to create a flags argument that
+takes a bitfield.
+
+What To Optimize
+----------------
+
+Don't optimize anything if it's not in the critical path. Right now, the
+critical path seems to be AES, logging, and the network itself. Feel free to
+do your own profiling to determine otherwise.
+
+Log conventions
+---------------
+
+`https://www.torproject.org/docs/faq#LogLevel`
+
+No error or warning messages should be expected during normal OR or OP
+operation.
+
+If a library function is currently called such that failure always means ERR,
+then the library function should log WARN and let the caller log ERR.
+
+Every message of severity INFO or higher should either (A) be intelligible
+to end-users who don't know the Tor source; or (B) somehow inform the
+end-users that they aren't expected to understand the message (perhaps
+with a string like "internal error"). Option (A) is to be preferred to
+option (B).
+
+
+
+Doxygen comment conventions
+---------------------------
+
+Say what functions do as a series of one or more imperative sentences, as
+though you were telling somebody how to be the function. In other words, DO
+NOT say:
+
+ /** The strtol function parses a number.
+ *
+ * nptr -- the string to parse. It can include whitespace.
+ * endptr -- a string pointer to hold the first thing that is not part
+ * of the number, if present.
+ * base -- the numeric base.
+ * returns: the resulting number.
+ */
+ long strtol(const char *nptr, char **nptr, int base);
+
+Instead, please DO say:
+
+ /** Parse a number in radix <b>base</b> from the string <b>nptr</b>,
+ * and return the result. Skip all leading whitespace. If
+ * <b>endptr</b> is not NULL, set *<b>endptr</b> to the first character
+ * after the number parsed.
+ **/
+ long strtol(const char *nptr, char **nptr, int base);
+
+Doxygen comments are the contract in our abstraction-by-contract world: if
+the functions that call your function rely on it doing something, then your
+function should mention that it does that something in the documentation. If
+you rely on a function doing something beyond what is in its documentation,
+then you should watch out, or it might do something else later.
diff --git a/doc/HACKING/GettingStarted.md b/doc/HACKING/GettingStarted.md
new file mode 100644
index 0000000000..0295adc1ff
--- /dev/null
+++ b/doc/HACKING/GettingStarted.md
@@ -0,0 +1,187 @@
+
+Getting started in Tor development
+==================================
+
+Congratulations! You've found this file, and you're reading it! This
+means that you might be interested in getting started in developing Tor.
+
+(This guide is just about Tor itself--the small network program at the
+heart of the Tor network--and not about all the other programs in the
+whole Tor ecosystem.)
+
+
+If you are looking for a more bare-bones, less user-friendly information
+dump of important information, you might like reading doc/HACKING
+instead. You should probably read it before you write your first patch.
+
+
+Required background
+-------------------
+
+First, I'm going to assume that you can build Tor from source, and that
+you know enough of the C language to read and write it. (See the README
+file that comes with the Tor source for more information on building it,
+and any high-quality guide to C for information on programming.)
+
+I'm also going to assume that you know a little bit about how to use
+Git, or that you're able to follow one of the several excellent guides
+at http://git-scm.org to learn.
+
+Most Tor developers develop using some Unix-based system, such as Linux,
+BSD, or OSX. It's okay to develop on Windows if you want, but you're
+going to have a more difficult time.
+
+
+Getting your first patch into Tor
+---------------------------------
+
+Once you've reached this point, here's what you need to know.
+
+ 1. Get the source.
+
+ We keep our source under version control in Git. To get the latest
+ version, run
+
+ git clone https://git.torproject.org/git/tor
+
+ This will give you a checkout of the master branch. If you're
+ going to fix a bug that appears in a stable version, check out the
+ appropriate "maint" branch, as in:
+
+ git checkout maint-0.2.7
+
+ 2. Find your way around the source
+
+ Our overall code structure is explained in the "torguts" documents,
+ currently at
+
+ git clone https://git.torproject.org/user/nickm/torguts.git
+
+ Find a part of the code that looks interesting to you, and start
+ looking around it to see how it fits together!
+
+ We do some unusual things in our codebase. Our testing-related
+ practices and kludges are explained in doc/WritingTests.txt.
+
+ If you see something that doesn't make sense, we love to get
+ questions!
+
+ 3. Find something cool to hack on.
+
+ You may already have a good idea of what you'd like to work on, or
+ you might be looking for a way to contribute.
+
+ Many people have gotten started by looking for an area where they
+ personally felt Tor was underperforming, and investigating ways to
+ fix it. If you're looking for ideas, you can head to our bug
+ tracker at trac.torproject.org and look for tickets that have
+ received the "easy" tag: these are ones that developers think would
+ be pretty simple for a new person to work on. For a bigger
+ challenge, you might want to look for tickets with the "lorax"
+ keyword: these are tickets that the developers think might be a
+ good idea to build, but which we have no time to work on any time
+ soon.
+
+ Or you might find another open ticket that piques your
+ interest. It's all fine!
+
+ For your first patch, it is probably NOT a good idea to make
+ something huge or invasive. In particular, you should probably
+ avoid:
+
+ * Major changes spread across many parts of the codebase.
+ * Major changes to programming practice or coding style.
+ * Huge new features or protocol changes.
+
+ 4. Meet the developers!
+
+ We discuss stuff on the tor-dev mailing list and on the #tor-dev
+ IRC channel on OFTC. We're generally friendly and approachable,
+ and we like to talk about how Tor fits together. If we have ideas
+ about how something should be implemented, we'll be happy to share
+ them.
+
+ We currently have a patch workshop at least once a week, where
+ people share patches they've made and discuss how to make them
+ better. The time might change in the future, but generally,
+ there's no bad time to talk, and ask us about patch ideas.
+
+ 5. Do you need to write a design proposal?
+
+ If your idea is very large, or it will require a change to Tor's
+ protocols, there needs to be a written design proposal before it
+ can be merged. (We use this process to manage changes in the
+ protocols.) To write one, see the instructions at
+ https://gitweb.torproject.org/torspec.git/tree/proposals/001-process.txt
+ . If you'd like help writing a proposal, just ask! We're happy to
+ help out with good ideas.
+
+ You might also like to look around the rest of that directory, to
+ see more about open and past proposed changes to Tor's behavior.
+
+ 6. Writing your patch
+
+ As you write your code, you'll probably want it to fit in with the
+ standards of the rest of the Tor codebase so it will be easy for us
+ to review and merge. You can learn our coding standards in
+ doc/HACKING.
+
+ If your patch is large and/or is divided into multiple logical
+ components, remember to divide it into a series of Git commits. A
+ series of small changes is much easier to review than one big lump.
+
+ 7. Testing your patch
+
+ We prefer that all new or modified code have unit tests for it to
+ ensure that it runs correctly. Also, all code should actually be
+ _run_ by somebody, to make sure it works.
+
+ See doc/WritingTests.txt for more information on how we test things
+ in Tor. If you'd like any help writing tests, just ask! We're
+ glad to help out.
+
+ 8. Submitting your patch
+
+ We review patches through tickets on our bugtracker at
+ trac.torproject.org. You can either upload your patches there, or
+ put them at a public git repository somewhere we can fetch them
+ (like github or bitbucket) and then paste a link on the appropriate
+ trac ticket.
+
+ Once your patches are available, write a short explanation of what
+ you've done on trac, and then change the status of the ticket to
+ needs_review.
+
+ 9. Review, Revision, and Merge
+
+ With any luck, somebody will review your patch soon! If not, you
+ can ask on the IRC channel; sometimes we get really busy and take
+ longer than we should. But don't let us slow you down: you're the
+ one who's offering help here, and we should respect your time and
+ contributions.
+
+ When your patch is reviewed, one of these things will happen:
+
+ * The reviewer will say "looks good to me" and your
+ patch will get merged right into Tor. [Assuming we're not
+ in the middle of a code-freeze window. If the codebase is
+ frozen, your patch will go into the next release series.]
+
+ * OR the reviewer will say "looks good, just needs some small
+ changes!" And then the reviewer will make those changes,
+ and merge the modified patch into Tor.
+
+ * OR the reviewer will say "Here are some questions and
+ comments," followed by a bunch of stuff that the reviewer
+ thinks should change in your code, or questions that the
+ reviewer has.
+
+ At this point, you might want to make the requested changes
+ yourself, and comment on the trac ticket once you have done
+ so. Or if you disagree with any of the comments, you should
+ say so! And if you won't have time to make some of the
+ changes, you should say that too, so that other developers
+ will be able to pick up the unfinished portion.
+
+ Congratulations! You have now written your first patch, and gotten
+ it integrated into mainline Tor.
diff --git a/doc/HACKING/HelpfulTools.md b/doc/HACKING/HelpfulTools.md
new file mode 100644
index 0000000000..a7f36e6c7e
--- /dev/null
+++ b/doc/HACKING/HelpfulTools.md
@@ -0,0 +1,293 @@
+Useful tools
+============
+
+These aren't strictly necessary for hacking on Tor, but they can help track
+down bugs.
+
+Jenkins
+-------
+
+ https://jenkins.torproject.org
+
+Dmalloc
+-------
+
+The dmalloc library will keep track of memory allocation, so you can find out
+if we're leaking memory, doing any double-frees, or so on.
+
+ dmalloc -l -/dmalloc.log
+ (run the commands it tells you)
+ ./configure --with-dmalloc
+
+Valgrind
+--------
+
+ valgrind --leak-check=yes --error-limit=no --show-reachable=yes src/or/tor
+
+(Note that if you get a zillion openssl warnings, you will also need to
+pass `--undef-value-errors=no` to valgrind, or rebuild your openssl
+with `-DPURIFY`.)
+
+Coverity
+--------
+
+Nick regularly runs the coverity static analyzer on the Tor codebase.
+
+The preprocessor define `__COVERITY__` is used to work around instances
+where coverity picks up behavior that we wish to permit.
+
+clang Static Analyzer
+---------------------
+
+The clang static analyzer can be run on the Tor codebase using Xcode (WIP)
+or a command-line build.
+
+The preprocessor define `__clang_analyzer__` is used to work around instances
+where clang picks up behavior that we wish to permit.
+
+clang Runtime Sanitizers
+------------------------
+
+To build the Tor codebase with the clang Address and Undefined Behavior
+sanitizers, see the file `contrib/clang/sanitize_blacklist.txt`.
+
+Preprocessor workarounds for instances where clang picks up behavior that
+we wish to permit are also documented in the blacklist file.
+
+Running lcov for unit test coverage
+-----------------------------------
+
+Lcov is a utility that generates pretty HTML reports of test code coverage.
+To generate such a report:
+
+ ./configure --enable-coverage
+ make
+ make coverage-html
+ $BROWSER ./coverage_html/index.html
+
+This will run the tor unit test suite `./src/test/test` and generate the HTML
+coverage code report under the directory `./coverage_html/`. To change the
+output directory, use `make coverage-html HTML_COVER_DIR=./funky_new_cov_dir`.
+
+Coverage diffs using lcov are not currently implemented, but are being
+investigated (as of July 2014).
+
+Running the unit tests
+----------------------
+
+To quickly run all the tests distributed with Tor:
+
+ make check
+
+To run the fast unit tests only:
+
+ make test
+
+To selectively run just some tests (the following can be combined
+arbitrarily):
+
+ ./src/test/test <name_of_test> [<name of test 2>] ...
+ ./src/test/test <prefix_of_name_of_test>.. [<prefix_of_name_of_test2>..] ...
+ ./src/test/test :<name_of_excluded_test> [:<name_of_excluded_test2]...
+
+To run all tests, including those based on Stem or Chutney:
+
+ make test-full
+
+To run all tests, including those based on Stem or Chutney that require a
+working connection to the internet:
+
+ make test-full-online
+
+Running gcov for unit test coverage
+-----------------------------------
+
+ ./configure --enable-coverage
+ make
+ make check
+ # or--- make test-full ? make test-full-online?
+ mkdir coverage-output
+ ./scripts/test/coverage coverage-output
+
+(On OSX, you'll need to start with `--enable-coverage CC=clang`.)
+
+Then, look at the .gcov files in `coverage-output`. '-' before a line means
+that the compiler generated no code for that line. '######' means that the
+line was never reached. Lines with numbers were called that number of times.
+
+If that doesn't work:
+
+ * Try configuring Tor with `--disable-gcc-hardening`
+ * You might need to run `make clean` after you run `./configure`.
+
+If you make changes to Tor and want to get another set of coverage results,
+you can run `make reset-gcov` to clear the intermediary gcov output.
+
+If you have two different `coverage-output` directories, and you want to see
+a meaningful diff between them, you can run:
+
+ ./scripts/test/cov-diff coverage-output1 coverage-output2 | less
+
+In this diff, any lines that were visited at least once will have coverage
+"1". This lets you inspect what you (probably) really want to know: which
+untested lines were changed? Are there any new untested lines?
+
+Running integration tests
+-------------------------
+
+We have the beginnings of a set of scripts to run integration tests using
+Chutney. To try them, set CHUTNEY_PATH to your chutney source directory, and
+run `make test-network`.
+
+We also have scripts to run integration tests using Stem. To try them, set
+`STEM_SOURCE_DIR` to your Stem source directory, and run `test-stem`.
+
+Profiling Tor with oprofile
+---------------------------
+
+The oprofile tool runs (on Linux only!) to tell you what functions Tor is
+spending its CPU time in, so we can identify performance bottlenecks.
+
+Here are some basic instructions
+
+ - Build tor with debugging symbols (you probably already have, unless
+ you messed with CFLAGS during the build process).
+ - Build all the libraries you care about with debugging symbols
+ (probably you only care about libssl, maybe zlib and Libevent).
+ - Copy this tor to a new directory
+ - Copy all the libraries it uses to that dir too (`ldd ./tor` will
+ tell you)
+ - Set LD_LIBRARY_PATH to include that dir. `ldd ./tor` should now
+ show you it's using the libs in that dir
+ - Run that tor
+ - Reset oprofiles counters/start it
+ * `opcontrol --reset; opcontrol --start`, if Nick remembers right.
+ - After a while, have it dump the stats on tor and all the libs
+ in that dir you created.
+ * `opcontrol --dump;`
+ * `opreport -l that_dir/*`
+ - Profit
+
+Generating and analyzing a callgraph
+------------------------------------
+
+1. Run `./scripts/maint/generate_callgraph.sh`. This will generate a
+ bunch of files in a new ./callgraph directory.
+
+2. Run `./scripts/maint/analyze_callgraph.py callgraph/src/*/*`. This
+ will do a lot of graph operations and then dump out a new
+ `callgraph.pkl` file, containing data in Python's 'pickle' format.
+
+3. Run `./scripts/maint/display_callgraph.py`. It will display:
+ - the number of functions reachable from each function.
+ - all strongly-connnected components in the Tor callgraph
+ - the largest bottlenecks in the largest SCC in the Tor callgraph.
+
+Note that currently the callgraph generator can't detect calls that pass
+through function pointers.
+
+Getting emacs to edit Tor source properly
+-----------------------------------------
+
+Nick likes to put the following snippet in his .emacs file:
+
+
+ (add-hook 'c-mode-hook
+ (lambda ()
+ (font-lock-mode 1)
+ (set-variable 'show-trailing-whitespace t)
+
+ (let ((fname (expand-file-name (buffer-file-name))))
+ (cond
+ ((string-match "^/home/nickm/src/libevent" fname)
+ (set-variable 'indent-tabs-mode t)
+ (set-variable 'c-basic-offset 4)
+ (set-variable 'tab-width 4))
+ ((string-match "^/home/nickm/src/tor" fname)
+ (set-variable 'indent-tabs-mode nil)
+ (set-variable 'c-basic-offset 2))
+ ((string-match "^/home/nickm/src/openssl" fname)
+ (set-variable 'indent-tabs-mode t)
+ (set-variable 'c-basic-offset 8)
+ (set-variable 'tab-width 8))
+ ))))
+
+
+You'll note that it defaults to showing all trailing whitespace. The `cond`
+test detects whether the file is one of a few C free software projects that I
+often edit, and sets up the indentation level and tab preferences to match
+what they want.
+
+If you want to try this out, you'll need to change the filename regex
+patterns to match where you keep your Tor files.
+
+If you use emacs for editing Tor and nothing else, you could always just say:
+
+
+ (add-hook 'c-mode-hook
+ (lambda ()
+ (font-lock-mode 1)
+ (set-variable 'show-trailing-whitespace t)
+ (set-variable 'indent-tabs-mode nil)
+ (set-variable 'c-basic-offset 2)))
+
+
+There is probably a better way to do this. No, we are probably not going
+to clutter the files with emacs stuff.
+
+
+Doxygen
+-------
+
+We use the 'doxygen' utility to generate documentation from our
+source code. Here's how to use it:
+
+ 1. Begin every file that should be documented with
+
+ /**
+ * \file filename.c
+ * \brief Short description of the file.
+ */
+
+ (Doxygen will recognize any comment beginning with /** as special.)
+
+ 2. Before any function, structure, #define, or variable you want to
+ document, add a comment of the form:
+
+ /** Describe the function's actions in imperative sentences.
+ *
+ * Use blank lines for paragraph breaks
+ * - and
+ * - hyphens
+ * - for
+ * - lists.
+ *
+ * Write <b>argument_names</b> in boldface.
+ *
+ * \code
+ * place_example_code();
+ * between_code_and_endcode_commands();
+ * \endcode
+ */
+
+ 3. Make sure to escape the characters `<`, `>`, `\`, `%` and `#` as `\<`,
+ `\>`, `\\`, `\%` and `\#`.
+
+ 4. To document structure members, you can use two forms:
+
+ struct foo {
+ /** You can put the comment before an element; */
+ int a;
+ int b; /**< Or use the less-than symbol to put the comment
+ * after the element. */
+ };
+
+ 5. To generate documentation from the Tor source code, type:
+
+ $ doxygen -g
+
+ to generate a file called `Doxyfile`. Edit that file and run
+ `doxygen` to generate the API documentation.
+
+ 6. See the Doxygen manual for more information; this summary just
+ scratches the surface.
diff --git a/doc/HACKING/HowToReview.md b/doc/HACKING/HowToReview.md
new file mode 100644
index 0000000000..de7891c923
--- /dev/null
+++ b/doc/HACKING/HowToReview.md
@@ -0,0 +1,85 @@
+How to review a patch
+=====================
+
+Some folks have said that they'd like to review patches more often, but they
+don't know how.
+
+So, here are a bunch of things to check for when reviewing a patch!
+
+Note that if you can't do every one of these, that doesn't mean you can't do
+a good review! Just make it clear what you checked for and what you didn't.
+
+
+Top-level smell-checks
+----------------------
+
+(Difficulty: easy)
+
+- Does it compile with `--enable-gcc-warnings`?
+
+- Does `make check-spaces` pass?
+
+- Does it have a reasonable amount of tests? Do they pass? Do they leak
+ memory?
+
+- Do all the new functions, global variables, types, and structure members have
+ documentation?
+
+- Do all the functions, global variables, types, and structure members with
+ modified behavior have modified documentation?
+
+- Do all the new torrc options have documentation?
+
+- If this changes Tor's behavior on the wire, is there a design proposal?
+
+
+
+Let's look at the code!
+-----------------------
+
+- Does the code conform to CodingStandards.txt?
+
+- Does the code leak memory?
+
+- If two or more pointers ever point to the same object, is it clear which
+ pointer "owns" the object?
+
+- Are all allocated resources freed?
+
+- Are all pointers that should be const, const?
+
+- Are `#defines` used for 'magic' numbers?
+
+- Can you understand what the code is trying to do?
+
+- Can you convince yourself that the code really does that?
+
+- Is there duplicated code that could be turned into a function?
+
+
+Let's look at the documentation!
+--------------------------------
+
+- Does the documentation confirm to CodingStandards.txt?
+
+- Does it make sense?
+
+- Can you predict what the function will do from its documentation?
+
+
+Let's think about security!
+---------------------------
+
+- If there are any arrays, buffers, are you 100% sure that they cannot
+ overflow?
+
+- If there is any integer math, can it overflow or underflow?
+
+- If there are any allocations, are you sure there are corresponding
+ deallocations?
+
+- Is there a safer pattern that could be used in any case?
+
+- Have they used one of the Forbidden Functions?
+
+(Also see your favorite secure C programming guides.)
diff --git a/doc/HACKING/README.1st.md b/doc/HACKING/README.1st.md
new file mode 100644
index 0000000000..8299fe634a
--- /dev/null
+++ b/doc/HACKING/README.1st.md
@@ -0,0 +1,62 @@
+
+In this directory
+-----------------
+
+This directory has helpful information about what you need to know to
+hack on Tor!
+
+First, read `GettingStarted.md` to learn how to get a start in Tor
+development.
+
+If you've decided to write a patch, `CodingStandards.txt` will give
+you a bunch of information about how we structure our code.
+
+It's important to get code right! Reading `WritingTests.md` will
+tell you how to write and run tests in the Tor codebase.
+
+There are a bunch of other programs we use to help maintain and
+develop the codebase: `HelpfulTools.md` can tell you how to use them
+with Tor.
+
+If it's your job to put out Tor releases, see `ReleasingTor.md` so
+that you don't miss any steps!
+
+
+-----------------------
+
+For full information on how Tor is supposed to work, look at the files in
+`https://gitweb.torproject.org/torspec.git/tree`.
+
+For an explanation of how to change Tor's design to work differently, look at
+`https://gitweb.torproject.org/torspec.git/blob_plain/HEAD:/proposals/001-process.txt`.
+
+For the latest version of the code, get a copy of git, and
+
+ git clone https://git.torproject.org/git/tor
+
+We talk about Tor on the `tor-talk` mailing list. Design proposals and
+discussion belong on the `tor-dev` mailing list. We hang around on
+irc.oftc.net, with general discussion happening on #tor and development
+happening on `#tor-dev`.
+
+The other files in this `HACKING` directory may also be useful as you
+get started working with Tor.
+
+Happy hacking!
+
+
+-----------------------
+
+XXXXX also describe
+
+doc/HACKING/WritingTests.md
+
+torguts.git
+
+torspec.git
+
+The design paper
+
+freehaven.net/anonbib
+
+XXXX describe these and add links.
diff --git a/doc/HACKING/ReleasingTor.md b/doc/HACKING/ReleasingTor.md
new file mode 100644
index 0000000000..2378aef568
--- /dev/null
+++ b/doc/HACKING/ReleasingTor.md
@@ -0,0 +1,143 @@
+
+Putting out a new release
+-------------------------
+
+Here are the steps Roger takes when putting out a new Tor release:
+
+1. Use it for a while, as a client, as a relay, as a hidden service,
+ and as a directory authority. See if it has any obvious bugs, and
+ resolve those.
+
+ As applicable, merge the `maint-X` branch into the `release-X` branch.
+
+2. Gather the `changes/*` files into a changelog entry, rewriting many
+ of them and reordering to focus on what users and funders would find
+ interesting and understandable.
+
+ 1. Make sure that everything that wants a bug number has one.
+ Make sure that everything which is a bugfix says what version
+ it was a bugfix on.
+
+ 2. Concatenate them.
+
+ 3. Sort them by section. Within each section, sort by "version it's
+ a bugfix on", else by numerical ticket order.
+
+ 4. Clean them up:
+
+ Standard idioms:
+ `Fixes bug 9999; bugfix on 0.3.3.3-alpha.`
+
+ One space after a period.
+
+ Make stuff very terse
+
+ Make sure each section name ends with a colon
+
+ Describe the user-visible problem right away
+
+ Mention relevant config options by name. If they're rare or unusual,
+ remind people what they're for
+
+ Avoid starting lines with open-paren
+
+ Present and imperative tense: not past.
+
+ 'Relays', not 'servers' or 'nodes' or 'Tor relays'.
+
+ "Stop FOOing", not "Fix a bug where we would FOO".
+
+ Try not to let any given section be longer than about a page. Break up
+ long sections into subsections by some sort of common subtopic. This
+ guideline is especially important when organizing Release Notes for
+ new stable releases.
+
+ If a given changes stanza showed up in a different release (e.g.
+ maint-0.2.1), be sure to make the stanzas identical (so people can
+ distinguish if these are the same change).
+
+ 5. Merge them in.
+
+ 6. Clean everything one last time.
+
+ 7. Run `./scripts/maint/format_changelog.py` to make it prettier.
+
+3. Compose a short release blurb to highlight the user-facing
+ changes. Insert said release blurb into the ChangeLog stanza. If it's
+ a stable release, add it to the ReleaseNotes file too. If we're adding
+ to a release-0.2.x branch, manually commit the changelogs to the later
+ git branches too.
+
+ If you're doing the first stable release in a series, you need to
+ create a ReleaseNotes for the series as a whole. To get started
+ there, copy all of the Changelog entries from the series into a new
+ file, and run `./scripts/maint/sortChanges.py` on it. That will
+ group them by category. Then kill every bugfix entry for fixing
+ bugs that were introduced within that release series; those aren't
+ relevant changes since the last series. At that point, it's time
+ to start sorting and condensing entries. (Generally, we don't edit the
+ text of existing entries, though.)
+
+4. In `maint-0.2.x`, bump the version number in `configure.ac` and run
+ `scripts/maint/updateVersions.pl` to update version numbers in other
+ places, and commit. Then merge `maint-0.2.x` into `release-0.2.x`.
+
+ (NOTE: To bump the version number, edit `configure.ac`, and then run
+ either `make`, or `perl scripts/maint/updateVersions.pl`, depending on
+ your version.)
+
+5. Make distcheck, put the tarball up somewhere, and tell `#tor` about
+ it. Wait a while to see if anybody has problems building it. Try to
+ get Sebastian or somebody to try building it on Windows.
+
+6. Get at least two of weasel/arma/Sebastian to put the new version number
+ in their approved versions list.
+
+7. Sign the tarball, then sign and push the git tag:
+
+ gpg -ba <the_tarball>
+ git tag -u <keyid> tor-0.2.x.y-status
+ git push origin tag tor-0.2.x.y-status
+
+8. scp the tarball and its sig to the dist website, i.e.
+ `/srv/dist-master.torproject.org/htdocs/` on dist-master. When you want
+ it to go live, you run "static-update-component dist.torproject.org"
+ on dist-master.
+
+ Edit `include/versions.wmi` and `Makefile` to note the new version.
+
+ (NOTE: Due to #17805, there can only be one stable version listed at
+ once. Nonetheless, do not call your version "alpha" if it is stable,
+ or people will get confused.)
+
+9. Email the packagers (cc'ing tor-assistants) that a new tarball is up.
+ The current list of packagers is:
+
+ - {weasel,gk,mikeperry} at torproject dot org
+ - {blueness} at gentoo dot org
+ - {paul} at invizbox dot io
+ - {ondrej.mikle} at gmail dot com
+ - {lfleischer} at archlinux dot org
+ - {tails-dev} at boum dot org
+
+10. Add the version number to Trac. To do this, go to Trac, log in,
+ select "Admin" near the top of the screen, then select "Versions" from
+ the menu on the left. At the right, there will be an "Add version"
+ box. By convention, we enter the version in the form "Tor:
+ 0.2.2.23-alpha" (or whatever the version is), and we select the date as
+ the date in the ChangeLog.
+
+11. Forward-port the ChangeLog (and ReleaseNotes if appropriate).
+
+12. Wait up to a day or two (for a development release), or until most
+ packages are up (for a stable release), and mail the release blurb and
+ changelog to tor-talk or tor-announce.
+
+ (We might be moving to faster announcements, but don't announce until
+ the website is at least updated.)
+
+13. If it's a stable release, bump the version number in the `maint-x.y.z`
+ branch to "newversion-dev", and do a `merge -s ours` merge to avoid
+ taking that change into master. Do a similar `merge -s theirs`
+ merge to get the change (and only that change) into release. (Some
+ of the build scripts require that maint merge cleanly into release.)
diff --git a/doc/HACKING/WritingTests.md b/doc/HACKING/WritingTests.md
new file mode 100644
index 0000000000..4e98d3d645
--- /dev/null
+++ b/doc/HACKING/WritingTests.md
@@ -0,0 +1,445 @@
+
+Writing tests for Tor: an incomplete guide
+==========================================
+
+Tor uses a variety of testing frameworks and methodologies to try to
+keep from introducing bugs. The major ones are:
+
+ 1. Unit tests written in C and shipped with the Tor distribution.
+
+ 2. Integration tests written in Python and shipped with the Tor
+ distribution.
+
+ 3. Integration tests written in Python and shipped with the Stem
+ library. Some of these use the Tor controller protocol.
+
+ 4. System tests written in Python and SH, and shipped with the
+ Chutney package. These work by running many instances of Tor
+ locally, and sending traffic through them.
+
+ 5. The Shadow network simulator.
+
+How to run these tests
+----------------------
+
+### The easy version
+
+To run all the tests that come bundled with Tor, run `make check`.
+
+To run the Stem tests as well, fetch stem from the git repository,
+set `STEM_SOURCE_DIR` to the checkout, and run `make test-stem`.
+
+To run the Chutney tests as well, fetch chutney from the git repository,
+set `CHUTNEY_PATH` to the checkout, and run `make test-network`.
+
+To run all of the above, run `make test-full`.
+
+To run all of the above, plus tests that require a working connection to the
+internet, run `make test-full-online`.
+
+### Running particular subtests
+
+The Tor unit tests are divided into separate programs and a couple of
+bundled unit test programs.
+
+Separate programs are easy. For example, to run the memwipe tests in
+isolation, you just run `./src/test/test-memwipe`.
+
+To run tests within the unit test programs, you can specify the name
+of the test. The string ".." can be used as a wildcard at the end of the
+test name. For example, to run all the cell format tests, enter
+`./src/test/test cellfmt/..`. To run
+
+Many tests that need to mess with global state run in forked subprocesses in
+order to keep from contaminating one another. But when debugging a failing test,
+you might want to run it without forking a subprocess. To do so, use the
+`--no-fork` option with a single test. (If you specify it along with
+multiple tests, they might interfere.)
+
+You can turn on logging in the unit tests by passing one of `--debug`,
+`--info`, `--notice`, or `--warn`. By default only errors are displayed.
+
+Unit tests are divided into `./src/test/test` and `./src/test/test-slow`.
+The former are those that should finish in a few seconds; the latter tend to
+take more time, and may include CPU-intensive operations, deliberate delays,
+and stuff like that.
+
+### Finding test coverage
+
+Test coverage is a measurement of which lines your tests actually visit.
+
+When you configure Tor with the `--enable-coverage` option, it should
+build with support for coverage in the unit tests, and in a special
+`tor-cov` binary.
+
+Then, run the tests you'd like to see coverage from. If you have old
+coverage output, you may need to run `reset-gcov` first.
+
+Now you've got a bunch of files scattered around your build directories
+called `*.gcda`. In order to extract the coverage output from them, make a
+temporary directory for them and run `./scripts/test/coverage ${TMPDIR}`,
+where `${TMPDIR}` is the temporary directory you made. This will create a
+`.gcov` file for each source file under tests, containing that file's source
+annotated with the number of times the tests hit each line. (You'll need to
+have gcov installed.)
+
+You can get a summary of the test coverage for each file by running
+`./scripts/test/cov-display ${TMPDIR}/*` . Each line lists the file's name,
+the number of uncovered lines, the number of uncovered lines, and the
+coverage percentage.
+
+For a summary of the test coverage for each _function_, run
+`./scripts/test/cov-display -f ${TMPDIR}/*`.
+
+### Comparing test coverage
+
+Sometimes it's useful to compare test coverage for a branch you're writing to
+coverage from another branch (such as git master, for example). But you
+can't run `diff` on the two coverage outputs directly, since the actual
+number of times each line is executed aren't so important, and aren't wholly
+deterministic.
+
+Instead, follow the instructions above for each branch, creating a separate
+temporary directory for each. Then, run `./scripts/test/cov-diff ${D1}
+${D2}`, where D1 and D2 are the directories you want to compare. This will
+produce a diff of the two directories, with all lines normalized to be either
+covered or uncovered.
+
+To count new or modified uncovered lines in D2, you can run:
+
+ ./scripts/test/cov-diff ${D1} ${D2}" | grep '^+ *\#' | wc -l
+
+
+What kinds of test should I write?
+----------------------------------
+
+Integration testing and unit testing are complementary: it's probably a
+good idea to make sure that your code is hit by both if you can.
+
+If your code is very-low level, and its behavior is easily described in
+terms of a relation between inputs and outputs, or a set of state
+transitions, then it's a natural fit for unit tests. (If not, please
+consider refactoring it until most of it _is_ a good fit for unit
+tests!)
+
+If your code adds new externally visible functionality to Tor, it would
+be great to have a test for that functionality. That's where
+integration tests more usually come in.
+
+Unit and regression tests: Does this function do what it's supposed to?
+-----------------------------------------------------------------------
+
+Most of Tor's unit tests are made using the "tinytest" testing framework.
+You can see a guide to using it in the tinytest manual at
+
+ https://github.com/nmathewson/tinytest/blob/master/tinytest-manual.md
+
+To add a new test of this kind, either edit an existing C file in `src/test/`,
+or create a new C file there. Each test is a single function that must
+be indexed in the table at the end of the file. We use the label "done:" as
+a cleanup point for all test functions.
+
+(Make sure you read `tinytest-manual.md` before proceeding.)
+
+I use the term "unit test" and "regression tests" very sloppily here.
+
+### A simple example
+
+Here's an example of a test function for a simple function in util.c:
+
+ static void
+ test_util_writepid(void *arg)
+ {
+ (void) arg;
+
+ char *contents = NULL;
+ const char *fname = get_fname("tmp_pid");
+ unsigned long pid;
+ char c;
+
+ write_pidfile(fname);
+
+ contents = read_file_to_str(fname, 0, NULL);
+ tt_assert(contents);
+
+ int n = sscanf(contents, "%lu\n%c", &pid, &c);
+ tt_int_op(n, OP_EQ, 1);
+ tt_int_op(pid, OP_EQ, getpid());
+
+ done:
+ tor_free(contents);
+ }
+
+This should look pretty familiar to you if you've read the tinytest
+manual. One thing to note here is that we use the testing-specific
+function `get_fname` to generate a file with respect to a temporary
+directory that the tests use. You don't need to delete the file;
+it will get removed when the tests are done.
+
+Also note our use of `OP_EQ` instead of `==` in the `tt_int_op()` calls.
+We define `OP_*` macros to use instead of the binary comparison
+operators so that analysis tools can more easily parse our code.
+(Coccinelle really hates to see `==` used as a macro argument.)
+
+Finally, remember that by convention, all `*_free()` functions that
+Tor defines are defined to accept NULL harmlessly. Thus, you don't
+need to say `if (contents)` in the cleanup block.
+
+### Exposing static functions for testing
+
+Sometimes you need to test a function, but you don't want to expose
+it outside its usual module.
+
+To support this, Tor's build system compiles a testing version of
+each module, with extra identifiers exposed. If you want to
+declare a function as static but available for testing, use the
+macro `STATIC` instead of `static`. Then, make sure there's a
+macro-protected declaration of the function in the module's header.
+
+For example, `crypto_curve25519.h` contains:
+
+ #ifdef CRYPTO_CURVE25519_PRIVATE
+ STATIC int curve25519_impl(uint8_t *output, const uint8_t *secret,
+ const uint8_t *basepoint);
+ #endif
+
+The `crypto_curve25519.c` file and the `test_crypto.c` file both define
+`CRYPTO_CURVE25519_PRIVATE`, so they can see this declaration.
+
+### STOP! Does this test really test?
+
+When writing tests, it's not enough to just generate coverage on all the
+lines of the code that you're testing: It's important to make sure that
+the test _really tests_ the code.
+
+For example, here is a _bad_ test for the unlink() function (which is
+supposed to remove a file).
+
+ static void
+ test_unlink_badly(void *arg)
+ {
+ (void) arg;
+ int r;
+
+ const char *fname = get_fname("tmpfile");
+
+ /* If the file isn't there, unlink returns -1 and sets ENOENT */
+ r = unlink(fname);
+ tt_int_op(n, OP_EQ, -1);
+ tt_int_op(errno, OP_EQ, ENOENT);
+
+ /* If the file DOES exist, unlink returns 0. */
+ write_str_to_file(fname, "hello world", 0);
+ r = unlink(fnme);
+ tt_int_op(r, OP_EQ, 0);
+
+ done:
+ tor_free(contents);
+ }
+
+
+This test might get very high coverage on unlink(). So why is it a
+bad test? Because it doesn't check that unlink() *actually removes the
+named file*!
+
+Remember, the purpose of a test is to succeed if the code does what
+it's supposed to do, and fail otherwise. Try to design your tests so
+that they check for the code's intended and documented functionality
+as much as possible.
+
+
+### Mock functions for testing in isolation
+
+Often we want to test that a function works right, but the function to
+be tested depends on other functions whose behavior is hard to observe,
+or which require a working Tor network, or something like that.
+
+To write tests for this case, you can replace the underlying functions
+with testing stubs while your unit test is running. You need to declare
+the underlying function as 'mockable', as follows:
+
+ MOCK_DECL(returntype, functionname, (argument list));
+
+and then later implement it as:
+
+ MOCK_IMPL(returntype, functionname, (argument list))
+ {
+ /* implementation here */
+ }
+
+For example, if you had a 'connect to remote server' function, you could
+declare it as:
+
+
+ MOCK_DECL(int, connect_to_remote, (const char *name, status_t *status));
+
+When you declare a function this way, it will be declared as normal in
+regular builds, but when the module is built for testing, it is declared
+as a function pointer initialized to the actual implementation.
+
+In your tests, if you want to override the function with a temporary
+replacement, you say:
+
+ MOCK(functionname, replacement_function_name);
+
+And later, you can restore the original function with:
+
+ UNMOCK(functionname);
+
+For more information, see the definitions of this mocking logic in
+`testsupport.h`.
+
+### Okay but what should my tests actually do?
+
+We talk above about "test coverage" -- making sure that your tests visit
+every line of code, or every branch of code. But visiting the code isn't
+enough: we want to verify that it's correct.
+
+So when writing tests, try to make tests that should pass with any correct
+implementation of the code, and that should fail if the code doesn't do what
+it's supposed to do.
+
+You can write "black-box" tests or "glass-box" tests. A black-box test is
+one that you write without looking at the structure of the function. A
+glass-box one is one you implement while looking at how the function is
+implemented.
+
+In either case, make sure to consider common cases *and* edge cases; success
+cases and failure csaes.
+
+For example, consider testing this function:
+
+ /** Remove all elements E from sl such that E==element. Preserve
+ * the order of any elements before E, but elements after E can be
+ * rearranged.
+ */
+ void smartlist_remove(smartlist_t *sl, const void *element);
+
+In order to test it well, you should write tests for at least all of the
+following cases. (These would be black-box tests, since we're only looking
+at the declared behavior for the function:
+
+ * Remove an element that is in the smartlist.
+ * Remove an element that is not in the smartlist.
+ * Remove an element that appears in the smartlist more than once.
+
+And your tests should verify that it behaves correct. At minimum, you should
+test:
+
+ * That other elements before E are in the same order after you call the
+ functions.
+ * That the target element is really removed.
+ * That _only_ the target element is removed.
+
+When you consider edge cases, you might try:
+
+ * Remove an element from an empty list.
+ * Remove an element from a singleton list containing that element.
+ * Remove an element for a list containing several instances of that
+ element, and nothing else.
+
+Now let's look at the implementation:
+
+ void
+ smartlist_remove(smartlist_t *sl, const void *element)
+ {
+ int i;
+ if (element == NULL)
+ return;
+ for (i=0; i < sl->num_used; i++)
+ if (sl->list[i] == element) {
+ sl->list[i] = sl->list[--sl->num_used]; /* swap with the end */
+ i--; /* so we process the new i'th element */
+ sl->list[sl->num_used] = NULL;
+ }
+ }
+
+Based on the implementation, we now see three more edge cases to test:
+
+ * Removing NULL from the list.
+ * Removing an element from the end of the list
+ * Removing an element from a position other than the end of the list.
+
+
+### What should my tests NOT do?
+
+Tests shouldn't require a network connection.
+
+Whenever possible, tests shouldn't take more than a second. Put the test
+into test/slow if it genuinely needs to be run.
+
+Tests should not alter global state unless they run with `TT_FORK`: Tests
+should not require other tests to be run before or after them.
+
+Tests should not leak memory or other resources. To find out if your tests
+are leaking memory, run them under valgrind (see HelpfulTools.txt for more
+information on how to do that).
+
+When possible, tests should not be over-fit to the implementation. That is,
+the test should verify that the documented behavior is implemented, but
+should not break if other permissible behavior is later implemented.
+
+
+### Advanced techniques: Namespaces
+
+Sometimes, when you're doing a lot of mocking at once, it's convenient to
+isolate your identifiers within a single namespace. If this were C++, we'd
+already have namespaces, but for C, we do the best we can with macros and
+token-pasting.
+
+We have some macros defined for this purpose in `src/test/test.h`. To use
+them, you define `NS_MODULE` to a prefix to be used for your identifiers, and
+then use other macros in place of identifier names. See `src/test/test.h` for
+more documentation.
+
+
+Integration tests: Calling Tor from the outside
+-----------------------------------------------
+
+Some tests need to invoke Tor from the outside, and shouldn't run from the
+same process as the Tor test program. Reasons for doing this might include:
+
+ * Testing the actual behavior of Tor when run from the command line
+ * Testing that a crash-handler correctly logs a stack trace
+ * Verifying that violating a sandbox or capability requirement will
+ actually crash the program.
+ * Needing to run as root in order to test capability inheritance or
+ user switching.
+
+To add one of these, you generally want a new C program in `src/test`. Add it
+to `TESTS` and `noinst_PROGRAMS` if it can run on its own and return success or
+failure. If it needs to be invoked multiple times, or it needs to be
+wrapped, add a new shell script to `TESTS`, and the new program to
+`noinst_PROGRAMS`. If you need access to any environment variable from the
+makefile (eg `${PYTHON}` for a python interpreter), then make sure that the
+makefile exports them.
+
+Writing integration tests with Stem
+-----------------------------------
+
+The 'stem' library includes extensive unit tests for the Tor controller
+protocol.
+
+For more information on writing new tests for stem, have a look around
+the `test/*` directory in stem, and find a good example to emulate. You
+might want to start with
+`https://gitweb.torproject.org/stem.git/tree/test/integ/control/controller.py`
+to improve Tor's test coverage.
+
+You can run stem tests from tor with `make test-stem`, or see
+`https://stem.torproject.org/faq.html#how-do-i-run-the-tests`.
+
+System testing with Chutney
+---------------------------
+
+The 'chutney' program configures and launches a set of Tor relays,
+authorities, and clients on your local host. It has a `test network`
+functionality to send traffic through them and verify that the traffic
+arrives correctly.
+
+You can write new test networks by adding them to `networks`. To add
+them to Tor's tests, add them to the `test-network` or `test-network-all`
+targets in `Makefile.am`.
+
+(Adding new kinds of program to chutney will still require hacking the
+code.)
diff --git a/doc/WritingTests.txt b/doc/WritingTests.txt
deleted file mode 100644
index 62a17e3709..0000000000
--- a/doc/WritingTests.txt
+++ /dev/null
@@ -1,273 +0,0 @@
-
-Writing tests for Tor: an incomplete guide
-==========================================
-
-Tor uses a variety of testing frameworks and methodologies to try to
-keep from introducing bugs. The major ones are:
-
- 1. Unit tests written in C and shipped with the Tor distribution.
-
- 2. Integration tests written in Python and shipped with the Tor
- distribution.
-
- 3. Integration tests written in Python and shipped with the Stem
- library. Some of these use the Tor controller protocol.
-
- 4. System tests written in Python and SH, and shipped with the
- Chutney package. These work by running many instances of Tor
- locally, and sending traffic through them.
-
- 5. The Shadow network simulator.
-
-How to run these tests
-----------------------
-
-=== The easy version
-
-To run all the tests that come bundled with Tor, run "make check"
-
-To run the Stem tests as well, fetch stem from the git repository,
-set STEM_SOURCE_DIR to the checkout, and run "make test-stem".
-
-To run the Chutney tests as well, fetch chutney from the git repository,
-set CHUTNEY_PATH to the checkout, and run "make test-network".
-
-To run all of the above, run "make test-full".
-
-To run all of the above, plus tests that require a working connection to the
-internet, run "make test-full-online".
-
-=== Running particular subtests
-
-The Tor unit tests are divided into separate programs and a couple of
-bundled unit test programs.
-
-Separate programs are easy. For example, to run the memwipe tests in
-isolation, you just run ./src/test/test-memwipe .
-
-To run tests within the unit test programs, you can specify the name
-of the test. The string ".." can be used as a wildcard at the end of the
-test name. For example, to run all the cell format tests, enter
-"./src/test/test cellfmt/..". To run
-
-Many tests that need to mess with global state run in forked subprocesses in
-order to keep from contaminating one another. But when debugging a failing test,
-you might want to run it without forking a subprocess. To do so, use the
-"--no-fork" option with a single test. (If you specify it along with
-multiple tests, they might interfere.)
-
-You can turn on logging in the unit tests by passing one of "--debug",
-"--info", "--notice", or "--warn". By default only errors are displayed.
-
-Unit tests are divided into "./src/test/test" and "./src/test/test-slow".
-The former are those that should finish in a few seconds; the latter tend to
-take more time, and may include CPU-intensive operations, deliberate delays,
-and stuff like that.
-
-=== Finding test coverage
-
-When you configure Tor with the --enable-coverage option, it should
-build with support for coverage in the unit tests, and in a special
-"tor-cov" binary.
-
-Then, run the tests you'd like to see coverage from. If you have old
-coverage output, you may need to run "reset-gcov" first.
-
-Now you've got a bunch of files scattered around your build directories
-called "*.gcda". In order to extract the coverage output from them, make a
-temporary directory for them and run "./scripts/test/coverage ${TMPDIR}",
-where ${TMPDIR} is the temporary directory you made. This will create a
-".gcov" file for each source file under tests, containing that file's source
-annotated with the number of times the tests hit each line. (You'll need to
-have gcov installed.)
-
-You can get a summary of the test coverage for each file by running
-"./scripts/test/cov-display ${TMPDIR}/*" . Each line lists the file's name,
-the number of uncovered lines, the number of uncovered lines, and the
-coverage percentage.
-
-For a summary of the test coverage for each _function_, run
-"./scripts/test/cov-display -f ${TMPDIR}/*" .
-
-=== Comparing test coverage
-
-Sometimes it's useful to compare test coverage for a branch you're writing to
-coverage from another branch (such as git master, for example). But you
-can't run "diff" on the two coverage outputs directly, since the actual
-number of times each line is executed aren't so important, and aren't wholly
-deterministic.
-
-Instead, follow the instructions above for each branch, creating a separate
-temporary directory for each. Then, run "./scripts/test/cov-diff ${D1}
-${D2}", where D1 and D2 are the directories you want to compare. This will
-produce a diff of the two directories, with all lines normalized to be either
-covered or uncovered.
-
-To count new or modified uncovered lines in D2, you can run:
-
- "./scripts/test/cov-diff ${D1} ${D2}" | grep '^+ *\#' |wc -l
-
-
-What kinds of test should I write?
-----------------------------------
-
-Integration testing and unit testing are complementary: it's probably a
-good idea to make sure that your code is hit by both if you can.
-
-If your code is very-low level, and its behavior is easily described in
-terms of a relation between inputs and outputs, or a set of state
-transitions, then it's a natural fit for unit tests. (If not, please
-consider refactoring it until most of it _is_ a good fit for unit
-tests!)
-
-If your code adds new externally visible functionality to Tor, it would
-be great to have a test for that functionality. That's where
-integration tests more usually come in.
-
-Unit and regression tests: Does this function do what it's supposed to?
------------------------------------------------------------------------
-
-Most of Tor's unit tests are made using the "tinytest" testing framework.
-You can see a guide to using it in the tinytest manual at
-
- https://github.com/nmathewson/tinytest/blob/master/tinytest-manual.md
-
-To add a new test of this kind, either edit an existing C file in src/test/,
-or create a new C file there. Each test is a single function that must
-be indexed in the table at the end of the file. We use the label "done:" as
-a cleanup point for all test functions.
-
-(Make sure you read tinytest-manual.md before proceeding.)
-
-I use the term "unit test" and "regression tests" very sloppily here.
-
-=== A simple example
-
-Here's an example of a test function for a simple function in util.c:
-
- static void
- test_util_writepid(void *arg)
- {
- (void) arg;
-
- char *contents = NULL;
- const char *fname = get_fname("tmp_pid");
- unsigned long pid;
- char c;
-
- write_pidfile(fname);
-
- contents = read_file_to_str(fname, 0, NULL);
- tt_assert(contents);
-
- int n = sscanf(contents, "%lu\n%c", &pid, &c);
- tt_int_op(n, OP_EQ, 1);
- tt_int_op(pid, OP_EQ, getpid());
-
- done:
- tor_free(contents);
- }
-
-This should look pretty familiar to you if you've read the tinytest
-manual. One thing to note here is that we use the testing-specific
-function "get_fname" to generate a file with respect to a temporary
-directory that the tests use. You don't need to delete the file;
-it will get removed when the tests are done.
-
-Also note our use of OP_EQ instead of == in the tt_int_op() calls.
-We define OP_* macros to use instead of the binary comparison
-operators so that analysis tools can more easily parse our code.
-(Coccinelle really hates to see == used as a macro argument.)
-
-Finally, remember that by convention, all *_free() functions that
-Tor defines are defined to accept NULL harmlessly. Thus, you don't
-need to say "if (contents)" in the cleanup block.
-
-=== Exposing static functions for testing
-
-Sometimes you need to test a function, but you don't want to expose
-it outside its usual module.
-
-To support this, Tor's build system compiles a testing version of
-each module, with extra identifiers exposed. If you want to
-declare a function as static but available for testing, use the
-macro "STATIC" instead of "static". Then, make sure there's a
-macro-protected declaration of the function in the module's header.
-
-For example, crypto_curve25519.h contains:
-
-#ifdef CRYPTO_CURVE25519_PRIVATE
-STATIC int curve25519_impl(uint8_t *output, const uint8_t *secret,
- const uint8_t *basepoint);
-#endif
-
-The crypto_curve25519.c file and the test_crypto.c file both define
-CRYPTO_CURVE25519_PRIVATE, so they can see this declaration.
-
-=== Mock functions for testing in isolation
-
-Often we want to test that a function works right, but the function to
-be tested depends on other functions whose behavior is hard to observe,
-or which require a working Tor network, or something like that.
-
-To write tests for this case, you can replace the underlying functions
-with testing stubs while your unit test is running. You need to declare
-the underlying function as 'mockable', as follows:
-
- MOCK_DECL(returntype, functionname, (argument list));
-
-and then later implement it as:
-
- MOCK_IMPL(returntype, functionname, (argument list))
- {
- /* implementation here */
- }
-
-For example, if you had a 'connect to remote server' function, you could
-declare it as:
-
-
- MOCK_DECL(int, connect_to_remote, (const char *name, status_t *status));
-
-When you declare a function this way, it will be declared as normal in
-regular builds, but when the module is built for testing, it is declared
-as a function pointer initialized to the actual implementation.
-
-In your tests, if you want to override the function with a temporary
-replacement, you say:
-
- MOCK(functionname, replacement_function_name);
-
-And later, you can restore the original function with:
-
- UNMOCK(functionname);
-
-For more information, see the definitions of this mocking logic in
-testsupport.h.
-
-
-=== Advanced techniques: Namespaces
-
-XXXX write this. danah boyd made us some really awesome stuff here.
-
-
-Integration tests: Calling Tor from the outside
------------------------------------------------
-
-XXXX WRITEME
-
-Writing integration tests with Stem
------------------------------------
-
-XXXX WRITEME
-
-System testing with Chutney
----------------------------
-
-XXXX WRITEME
-
-Who knows what evil lurks in the timings of networks? The Shadow knows!
------------------------------------------------------------------------
-
-XXXX WRITEME
-
diff --git a/doc/include.am b/doc/include.am
index 41d3d2a0c7..7164a4b2a0 100644
--- a/doc/include.am
+++ b/doc/include.am
@@ -34,12 +34,18 @@ nodist_man1_MANS =
doc_DATA =
endif
-EXTRA_DIST+= doc/HACKING doc/asciidoc-helper.sh \
+EXTRA_DIST+= doc/asciidoc-helper.sh \
$(html_in) $(man_in) $(txt_in) \
doc/state-contents.txt \
doc/torrc_format.txt \
doc/TUNING \
- doc/WritingTests.txt
+ doc/HACKING/README.1st.md \
+ doc/HACKING/CodingStandards.md \
+ doc/HACKING/GettingStarted.md \
+ doc/HACKING/HelpfulTools.md \
+ doc/HACKING/HowToReview.md \
+ doc/HACKING/ReleasingTor.md \
+ doc/HACKING/WritingTests.md
docdir = @docdir@
@@ -84,5 +90,5 @@ doc/tor-gencert.1: doc/tor-gencert.1.in
doc/tor-resolve.1: doc/tor-resolve.1.in
doc/torify.1: doc/torify.1.in
-CLEANFILES+= $(asciidoc_product) config.log
+CLEANFILES+= $(asciidoc_product)
DISTCLEANFILES+= $(html_in) $(man_in)
diff --git a/doc/tor.1.txt b/doc/tor.1.txt
index c011bff926..250ed67a04 100644
--- a/doc/tor.1.txt
+++ b/doc/tor.1.txt
@@ -121,8 +121,8 @@ COMMAND-LINE OPTIONS
Other options can be specified on the command-line in the format "--option
value", in the format "option value", or in a configuration file. For
instance, you can tell Tor to start listening for SOCKS connections on port
-9999 by passing --SOCKSPort 9999 or SOCKSPort 9999 to it on the command line,
-or by putting "SOCKSPort 9999" in the configuration file. You will need to
+9999 by passing --SocksPort 9999 or SocksPort 9999 to it on the command line,
+or by putting "SocksPort 9999" in the configuration file. You will need to
quote options with spaces in them: if you want Tor to log all debugging
messages to debug.log, you will probably need to say --Log 'debug file
debug.log'.
@@ -148,26 +148,31 @@ the defaults file.
This rule is simple for options that take a single value, but it can become
complicated for options that are allowed to occur more than once: if you
-specify four SOCKSPorts in your configuration file, and one more SOCKSPort on
+specify four SocksPorts in your configuration file, and one more SocksPort on
the command line, the option on the command line will replace __all__ of the
-SOCKSPorts in the configuration file. If this isn't what you want, prefix
-the option name with a plus sign, and it will be appended to the previous set
-of options instead.
+SocksPorts in the configuration file. If this isn't what you want, prefix
+the option name with a plus sign (+), and it will be appended to the previous
+set of options instead. For example, setting SocksPort 9100 will use only
+port 9100, but setting +SocksPort 9100 will use ports 9100 and 9050 (because
+this is the default).
Alternatively, you might want to remove every instance of an option in the
configuration file, and not replace it at all: you might want to say on the
-command line that you want no SOCKSPorts at all. To do that, prefix the
-option name with a forward slash.
+command line that you want no SocksPorts at all. To do that, prefix the
+option name with a forward slash (/). You can use the plus sign (+) and the
+forward slash (/) in the configuration file and on the command line.
GENERAL OPTIONS
---------------
[[BandwidthRate]] **BandwidthRate** __N__ **bytes**|**KBytes**|**MBytes**|**GBytes**|**KBits**|**MBits**|**GBits**::
- A token bucket limits the average incoming bandwidth usage on this node to
- the specified number of bytes per second, and the average outgoing
+ A token bucket limits the average incoming bandwidth usage on this node
+ to the specified number of bytes per second, and the average outgoing
bandwidth usage to that same value. If you want to run a relay in the
- public network, this needs to be _at the very least_ 30 KBytes (that is,
- 30720 bytes). (Default: 1 GByte) +
+ public network, this needs to be _at the very least_ 75 KBytes for a
+ relay (that is, 600 kbits) or 50 KBytes for a bridge (400 kbits) -- but of
+ course, more is better; we recommend at least 250 KBytes (2 mbits) if
+ possible. (Default: 1 GByte) +
+
With this option, and in other options that take arguments in bytes,
KBytes, and so on, other formats are also supported. Notably, "KBytes" can
@@ -239,7 +244,7 @@ GENERAL OPTIONS
any pluggable transport proxy that tries to launch __transport__. +
(Example: ServerTransportOptions obfs45 shared-secret=bridgepasswd cache=/var/lib/tor/cache)
-[[ExtORPort]] **ExtORPort** \['address':]__port__|**auto**
+[[ExtORPort]] **ExtORPort** \['address':]__port__|**auto**::
Open this port to listen for Extended ORPort connections from your
pluggable transports.
@@ -300,15 +305,16 @@ GENERAL OPTIONS
[[ControlPort]] **ControlPort** __PORT__|**unix:**__path__|**auto** [__flags__]::
If set, Tor will accept connections on this port and allow those
connections to control the Tor process using the Tor Control Protocol
- (described in control-spec.txt). Note: unless you also specify one or
- more of **HashedControlPassword** or **CookieAuthentication**,
- setting this option will cause Tor to allow any process on the local
- host to control it. (Setting both authentication methods means either
- method is sufficient to authenticate to Tor.) This
+ (described in control-spec.txt in
+ https://spec.torproject.org[torspec]). Note: unless you also
+ specify one or more of **HashedControlPassword** or
+ **CookieAuthentication**, setting this option will cause Tor to allow
+ any process on the local host to control it. (Setting both authentication
+ methods means eithermethod is sufficient to authenticate to Tor.) This
option is required for many Tor controllers; most use the value of 9051.
- Set it to "auto" to have Tor pick a port for you. (Default: 0)
+ Set it to "auto" to have Tor pick a port for you. (Default: 0) +
+
- Recognized flags are::
+ Recognized flags are...
**GroupWritable**;;
Unix domain sockets only: makes the socket get created as
group-writable.
@@ -370,10 +376,26 @@ GENERAL OPTIONS
[[DataDirectory]] **DataDirectory** __DIR__::
Store working data in DIR (Default: @LOCALSTATEDIR@/lib/tor)
-[[FallbackDir]] **FallbackDir** __address__:__port__ orport=__port__ id=__fingerprint__ [weight=__num__]::
+[[DataDirectoryGroupReadable]] **DataDirectoryGroupReadable** **0**|**1**::
+ If this option is set to 0, don't allow the filesystem group to read the
+ DataDirectory. If the option is set to 1, make the DataDirectory readable
+ by the default GID. (Default: 0)
+
+[[FallbackDir]] **FallbackDir** __address__:__port__ orport=__port__ id=__fingerprint__ [weight=__num__] [ipv6=__address__:__orport__]::
When we're unable to connect to any directory cache for directory info
- (usually because we don't know about any yet) we try a FallbackDir.
- By default, the directory authorities are also FallbackDirs.
+ (usually because we don't know about any yet) we try a directory authority.
+ Clients also simultaneously try a FallbackDir, to avoid hangs on client
+ startup if a directory authority is down. Clients retry FallbackDirs more
+ often than directory authorities, to reduce the load on the directory
+ authorities.
+ By default, the directory authorities are also FallbackDirs. Specifying a
+ FallbackDir replaces Tor's default hard-coded FallbackDirs (if any).
+ (See the **DirAuthority** entry for an explanation of each flag.)
+
+[[UseDefaultFallbackDirs]] **UseDefaultFallbackDirs** **0**|**1**::
+ Use Tor's default hard-coded FallbackDirs (if any). (When a
+ FallbackDir line is present, it replaces the hard-coded FallbackDirs,
+ regardless of the value of UseDefaultFallbackDirs.) (Default: 1)
[[DirAuthority]] **DirAuthority** [__nickname__] [**flags**] __address__:__port__ __fingerprint__::
Use a nonstandard authoritative directory server at the provided address
@@ -386,9 +408,16 @@ GENERAL OPTIONS
"bridge" flag is set. If a flag "orport=**port**" is given, Tor will use the
given port when opening encrypted tunnels to the dirserver. If a flag
"weight=**num**" is given, then the directory server is chosen randomly
- with probability proportional to that weight (default 1.0). Lastly, if a
+ with probability proportional to that weight (default 1.0). If a
flag "v3ident=**fp**" is given, the dirserver is a v3 directory authority
- whose v3 long-term signing key has the fingerprint **fp**. +
+ whose v3 long-term signing key has the fingerprint **fp**. Lastly,
+ if an "ipv6=__address__:__orport__" flag is present, then the directory
+ authority is listening for IPv6 connections on the indicated IPv6 address
+ and OR Port. +
+ +
+ Tor will contact the authority at __address__:__port__ (the DirPort) to
+ download directory documents. If an IPv6 address is supplied, Tor will
+ also download directory documents at the IPv6 address on the DirPort. +
+
If no **DirAuthority** line is given, Tor will use the default directory
authorities. NOTE: this option is intended for setting up a private Tor
@@ -603,6 +632,10 @@ GENERAL OPTIONS
If 1, Tor will overwrite logs at startup and in response to a HUP signal,
instead of appending to them. (Default: 0)
+[[SyslogIdentityTag]] **SyslogIdentityTag** __tag__::
+ When logging to syslog, adds a tag to the syslog identity such that
+ log entries are marked with "Tor-__tag__". (Default: none)
+
[[SafeLogging]] **SafeLogging** **0**|**1**|**relay**::
Tor can scrub potentially sensitive strings from log messages (e.g.
addresses) by replacing them with the string [scrubbed]. This way logs can
@@ -617,6 +650,14 @@ GENERAL OPTIONS
[[User]] **User** __UID__::
On startup, setuid to this user and setgid to their primary group.
+[[KeepBindCapabilities]] **KeepBindCapabilities** **0**|**1**|**auto**::
+ On Linux, when we are started as root and we switch our identity using
+ the **User** option, the **KeepBindCapabilities** option tells us whether to
+ try to retain our ability to bind to low ports. If this value is 1, we
+ try to keep the capability; if it is 0 we do not; and if it is **auto**,
+ we keep the capability only if we are configured to listen on a low port.
+ (Default: auto.)
+
[[HardwareAccel]] **HardwareAccel** **0**|**1**::
If non-zero, try to use built-in (static) crypto hardware acceleration when
available. (Default: 0)
@@ -981,7 +1022,7 @@ The following options are useful only for clients (that is, if
the same circuit. Currently, two addresses are "too close" if they lie in
the same /16 range. (Default: 1)
-[[SOCKSPort]] **SOCKSPort** \['address':]__port__|**unix:**__path__|**auto** [_flags_] [_isolation flags_]::
+[[SocksPort]] **SocksPort** \['address':]__port__|**unix:**__path__|**auto** [_flags_] [_isolation flags_]::
Open this port to listen for connections from SOCKS-speaking
applications. Set this to 0 if you don't want to allow application
connections via SOCKS. Set it to "auto" to have Tor pick a port for
@@ -996,7 +1037,7 @@ The following options are useful only for clients (that is, if
to use your computer as an open proxy. +
+
The _isolation flags_ arguments give Tor rules for which streams
- received on this SOCKSPort are allowed to share circuits with one
+ received on this SocksPort are allowed to share circuits with one
another. Recognized isolation flags are:
**IsolateClientAddr**;;
Don't share circuits with streams from a different
@@ -1023,11 +1064,11 @@ The following options are useful only for clients (that is, if
If no other isolation rules would prevent it, allow streams
on this port to share circuits with streams from every other
port with the same session group. (By default, streams received
- on different SOCKSPorts, TransPorts, etc are always isolated from one
+ on different SocksPorts, TransPorts, etc are always isolated from one
another. This option overrides that behavior.)
-[[OtherSOCKSPortFlags]]::
- Other recognized __flags__ for a SOCKSPort are:
+[[OtherSocksPortFlags]]::
+ Other recognized __flags__ for a SocksPort are:
**NoIPv4Traffic**;;
Tell exits to not connect to IPv4 addresses in response to SOCKS
requests on this connection.
@@ -1079,14 +1120,14 @@ The following options are useful only for clients (that is, if
authentication" when IsolateSOCKSAuth is disabled, or when this
option is set.
-[[SOCKSListenAddress]] **SOCKSListenAddress** __IP__[:__PORT__]::
+[[SocksListenAddress]] **SocksListenAddress** __IP__[:__PORT__]::
Bind to this address to listen for connections from Socks-speaking
applications. (Default: 127.0.0.1) You can also specify a port (e.g.
192.168.0.1:9100). This directive can be specified multiple times to bind
to multiple addresses/ports. (DEPRECATED: As of 0.2.3.x-alpha, you can
- now use multiple SOCKSPort entries, and provide addresses for SOCKSPort
- entries, so SOCKSListenAddress no longer has a purpose. For backward
- compatibility, SOCKSListenAddress is only allowed when SOCKSPort is just
+ now use multiple SocksPort entries, and provide addresses for SocksPort
+ entries, so SocksListenAddress no longer has a purpose. For backward
+ compatibility, SocksListenAddress is only allowed when SocksPort is just
a port number.)
[[SocksPolicy]] **SocksPolicy** __policy__,__policy__,__...__::
@@ -1293,7 +1334,7 @@ The following options are useful only for clients (that is, if
Use 0 if you don't want to allow NATD connections. Set the port
to "auto" to have Tor pick a port for you. This directive can be
specified multiple times to bind to multiple addresses/ports. See
- SOCKSPort for an explanation of isolation flags. +
+ SocksPort for an explanation of isolation flags. +
+
This option is only for people who cannot use TransPort. (Default: 0)
@@ -1321,7 +1362,7 @@ The following options are useful only for clients (that is, if
doesn't handle arbitrary DNS request types. Set the port to "auto" to
have Tor pick a port for
you. This directive can be specified multiple times to bind to multiple
- addresses/ports. See SOCKSPort for an explanation of isolation
+ addresses/ports. See SocksPort for an explanation of isolation
flags. (Default: 0)
[[DNSListenAddress]] **DNSListenAddress** __IP__[:__PORT__]::
@@ -1470,17 +1511,33 @@ The following options are useful only for clients (that is, if
If no defaults are available there, these options default to 20, .80,
.60, and 100, respectively.
+[[ClientUseIPv4]] **ClientUseIPv4** **0**|**1**::
+ If this option is set to 0, Tor will avoid connecting to directory servers
+ and entry nodes over IPv4. Note that clients with an IPv4
+ address in a **Bridge**, proxy, or pluggable transport line will try
+ connecting over IPv4 even if **ClientUseIPv4** is set to 0. (Default: 1)
+
[[ClientUseIPv6]] **ClientUseIPv6** **0**|**1**::
- If this option is set to 1, Tor might connect to entry nodes over
- IPv6. Note that clients configured with an IPv6 address in a
- **Bridge** line will try connecting over IPv6 even if
- **ClientUseIPv6** is set to 0. (Default: 0)
+ If this option is set to 1, Tor might connect to directory servers or
+ entry nodes over IPv6. Note that clients configured with an IPv6 address
+ in a **Bridge**, proxy, or pluggable transport line will try connecting
+ over IPv6 even if **ClientUseIPv6** is set to 0. (Default: 0)
+
+[[ClientPreferIPv6DirPort]] **ClientPreferIPv6DirPort** **0**|**1**|**auto**::
+ If this option is set to 1, Tor prefers a directory port with an IPv6
+ address over one with IPv4, for direct connections, if a given directory
+ server has both. (Tor also prefers an IPv6 DirPort if IPv4Client is set to
+ 0.) If this option is set to auto, clients prefer IPv4. Other things may
+ influence the choice. This option breaks a tie to the favor of IPv6.
+ (Default: auto)
-[[ClientPreferIPv6ORPort]] **ClientPreferIPv6ORPort** **0**|**1**::
+[[ClientPreferIPv6ORPort]] **ClientPreferIPv6ORPort** **0**|**1**|**auto**::
If this option is set to 1, Tor prefers an OR port with an IPv6
- address over one with IPv4 if a given entry node has both. Other
- things may influence the choice. This option breaks a tie to the
- favor of IPv6. (Default: 0)
+ address over one with IPv4 if a given entry node has both. (Tor also
+ prefers an IPv6 ORPort if IPv4Client is set to 0.) If this option is set
+ to auto, Tor bridge clients prefer the configured bridge address, and
+ other clients prefer IPv4. Other things may influence the choice. This
+ option breaks a tie to the favor of IPv6. (Default: auto)
[[PathsNeededToBuildCircuits]] **PathsNeededToBuildCircuits** __NUM__::
Tor clients don't build circuits for user traffic until they know
@@ -1584,7 +1641,7 @@ is non-zero):
used with accept6/reject6.) +
+
Private addresses are rejected by default (at the beginning of your exit
- policy), along with the configured primary public IPv4 and IPv6 addresses,
+ policy), along with any configured primary public IPv4 and IPv6 addresses,
and any public IPv4 and IPv6 addresses on any interface on the relay.
These private addresses are rejected unless you set the
ExitPolicyRejectPrivate config option to 0. For example, once you've done
@@ -1622,10 +1679,13 @@ is non-zero):
IPv4 and IPv6 addresses.
[[ExitPolicyRejectPrivate]] **ExitPolicyRejectPrivate** **0**|**1**::
- Reject all private (local) networks, along with your own configured public
- IPv4 and IPv6 addresses, at the beginning of your exit policy. Also reject
- any public IPv4 and IPv6 addresses on any interface on the relay. (If
- IPv6Exit is not set, all IPv6 addresses will be rejected anyway.)
+ Reject all private (local) networks, along with any configured public
+ IPv4 and IPv6 addresses, at the beginning of your exit policy. (This
+ includes the IPv4 and IPv6 addresses advertised by the relay, any
+ OutboundBindAddress, and the bind addresses of any port options, such as
+ ORPort and DirPort.) This also rejects any public IPv4 and IPv6 addresses
+ on any interface on the relay. (If IPv6Exit is not set, all IPv6 addresses
+ will be rejected anyway.)
See above entry on ExitPolicy.
(Default: 1)
@@ -1734,13 +1794,14 @@ is non-zero):
Log a heartbeat message every **HeartbeatPeriod** seconds. This is
a log level __notice__ message, designed to let you know your Tor
server is still alive and doing useful things. Settings this
- to 0 will disable the heartbeat. (Default: 6 hours)
+ to 0 will disable the heartbeat. Otherwise, it must be at least 30
+ minutes. (Default: 6 hours)
[[AccountingMax]] **AccountingMax** __N__ **bytes**|**KBytes**|**MBytes**|**GBytes**|**KBits**|**MBits**|**GBits**|**TBytes**::
Limits the max number of bytes sent and received within a set time period
using a given calculation rule (see: AccountingStart, AccountingRule).
Useful if you need to stay under a specific bandwidth. By default, the
- number used for calculation is the max of either the bytes sent or
+ number used for calculation is the max of either the bytes sent or
received. For example, with AccountingMax set to 1 GByte, a server
could send 900 MBytes and receive 800 MBytes and continue running.
It will only hibernate once one of the two reaches 1 GByte. This can
@@ -1756,12 +1817,14 @@ is non-zero):
of the time, which is more useful than a set of slow servers that are
always "available".
-[[AccountingRule]] **AccountingRule** **sum**|**max**::
+[[AccountingRule]] **AccountingRule** **sum**|**max**|**in**|**out**::
How we determine when our AccountingMax has been reached (when we
should hibernate) during a time interval. Set to "max" to calculate
using the higher of either the sent or received bytes (this is the
default functionality). Set to "sum" to calculate using the sent
- plus received bytes. (Default: max)
+ plus received bytes. Set to "in" to calculate using only the
+ received bytes. Set to "out" to calculate using only the sent bytes.
+ (Default: max)
[[AccountingStart]] **AccountingStart** **day**|**week**|**month** [__day__] __HH:MM__::
Specify how long accounting periods last. If **month** is given, each
@@ -1811,7 +1874,7 @@ is non-zero):
[[ServerDNSTestAddresses]] **ServerDNSTestAddresses** __address__,__address__,__...__::
When we're detecting DNS hijacking, make sure that these __valid__ addresses
aren't getting redirected. If they are, then our DNS is completely useless,
- and we'll reset our exit policy to "reject *:*". This option only affects
+ and we'll reset our exit policy to "reject \*:*". This option only affects
name lookups that your server does on behalf of clients. (Default:
"www.google.com, www.mit.edu, www.yahoo.com, www.slashdot.org")
@@ -1908,9 +1971,11 @@ is non-zero):
(Default: 1)
[[ExtendAllowPrivateAddresses]] **ExtendAllowPrivateAddresses** **0**|**1**::
- When this option is enabled, Tor routers allow EXTEND request to
- localhost, RFC1918 addresses, and so on. This can create security issues;
- you should probably leave it off. (Default: 0)
+ When this option is enabled, Tor will connect to localhost, RFC1918
+ addresses, and so on. In particular, Tor will make direct connections, and
+ Tor routers allow EXTEND requests, to these private addresses. This can
+ create security issues; you should probably leave it off.
+ (Default: 0)
[[MaxMemInQueues]] **MaxMemInQueues** __N__ **bytes**|**KB**|**MB**|**GB**::
This option configures a threshold above which Tor will assume that it
@@ -1947,11 +2012,6 @@ if DirPort is non-zero):
to set up a separate webserver. There's a sample disclaimer in
contrib/operator-tools/tor-exit-notice.html.
-[[HidServDirectoryV2]] **HidServDirectoryV2** **0**|**1**::
- When this option is set, Tor accepts and serves v2 hidden service
- descriptors. Setting DirPort is not required for this, because clients
- connect via the ORPort by default. (Default: 1)
-
[[DirPort]] **DirPort** \['address':]__PORT__|**auto** [_flags_]::
If this option is nonzero, advertise the directory service on this port.
Set it to "auto" to have Tor pick a port for you. This option can occur
@@ -1975,6 +2035,12 @@ if DirPort is non-zero):
except that port specifiers are ignored. Any address not matched by
some entry in the policy is accepted.
+[[DirCache]] **DirCache** **0**|**1**::
+ When this option is set, Tor caches all current directory documents and
+ accepts client requests for them. Setting DirPort is not required for this,
+ because clients connect via the ORPort by default. Setting either DirPort
+ or BridgeRelay and setting DirCache to 0 is not supported. (Default: 1)
+
DIRECTORY AUTHORITY SERVER OPTIONS
----------------------------------
@@ -1995,8 +2061,8 @@ on the public Tor network.
[[V3AuthoritativeDirectory]] **V3AuthoritativeDirectory** **0**|**1**::
When this option is set in addition to **AuthoritativeDirectory**, Tor
generates version 3 network statuses and serves descriptors, etc as
- described in doc/spec/dir-spec.txt (for Tor clients and servers running at
- least 0.2.0.x).
+ described in dir-spec.txt file of https://spec.torproject.org/[torspec]
+ (for Tor clients and servers running atleast 0.2.0.x).
[[VersioningAuthoritativeDirectory]] **VersioningAuthoritativeDirectory** **0**|**1**::
When this option is set to 1, Tor adds information on which versions of
@@ -2012,7 +2078,7 @@ on the public Tor network.
multiple times: the values from multiple lines are spliced together. When
this is set then **VersioningAuthoritativeDirectory** should be set too.
-[[RecommendedPackageVersions]] **RecommendedPackageVersions** __PACKAGENAME__ __VERSION__ __URL__ __DIGESTTYPE__**=**__DIGEST__ ::
+[[RecommendedPackages]] **RecommendedPackages** __PACKAGENAME__ __VERSION__ __URL__ __DIGESTTYPE__**=**__DIGEST__ ::
Adds "package" line to the directory authority's vote. This information
is used to vote on the correct URL and digest for the released versions
of different Tor-related packages, so that the consensus can certify
@@ -2163,11 +2229,6 @@ on the public Tor network.
that fine-grained information about nodes can be discarded when it hasn't
changed for a given amount of time. (Default: 24 hours)
-[[VoteOnHidServDirectoriesV2]] **VoteOnHidServDirectoriesV2** **0**|**1**::
- When this option is set in addition to **AuthoritativeDirectory**, Tor
- votes on whether to accept relays as hidden service directories.
- (Default: 1)
-
[[AuthDirHasIPv6Connectivity]] **AuthDirHasIPv6Connectivity** **0**|**1**::
Authoritative directories only. When set to 0, OR ports with an
IPv6 address are being accepted without reachability testing.
@@ -2295,10 +2356,18 @@ The following options are used for running a testing Tor network.
TestingClientDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60
TestingServerConsensusDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60
TestingClientConsensusDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60
+ TestingClientBootstrapConsensusAuthorityDownloadSchedule 0, 2,
+ 4 (for 40 seconds), 8, 16, 32, 60
+ TestingClientBootstrapConsensusFallbackDownloadSchedule 0, 1,
+ 4 (for 40 seconds), 8, 16, 32, 60
+ TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule 0, 1,
+ 4 (for 40 seconds), 8, 16, 32, 60
TestingBridgeDownloadSchedule 60, 30, 30, 60
TestingClientMaxIntervalWithoutRequest 5 seconds
TestingDirConnectionMaxStall 30 seconds
TestingConsensusMaxDownloadTries 80
+ TestingClientBootstrapConsensusMaxDownloadTries 80
+ TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries 80
TestingDescriptorMaxDownloadTries 80
TestingMicrodescMaxDownloadTries 80
TestingCertMaxDownloadTries 80
@@ -2359,6 +2428,36 @@ The following options are used for running a testing Tor network.
requires that **TestingTorNetwork** is set. (Default: 0, 0, 60, 300, 600,
1800, 3600, 3600, 3600, 10800, 21600, 43200)
+[[TestingClientBootstrapConsensusAuthorityDownloadSchedule]] **TestingClientBootstrapConsensusAuthorityDownloadSchedule** __N__,__N__,__...__::
+ Schedule for when clients should download consensuses from authorities if
+ they are bootstrapping (that is, they don't have a usable, reasonably live
+ consensus). Only used by clients fetching from a list of fallback
+ directory mirrors. This schedule is advanced by (potentially concurrent)
+ connection attempts, unlike other schedules, which are advanced by
+ connection failures. Changing this schedule requires that
+ **TestingTorNetwork** is set. (Default: 10, 11, 3600, 10800, 25200, 54000,
+ 111600, 262800)
+
+[[TestingClientBootstrapConsensusFallbackDownloadSchedule]] **TestingClientBootstrapConsensusFallbackDownloadSchedule** __N__,__N__,__...__::
+ Schedule for when clients should download consensuses from fallback
+ directory mirrors if they are bootstrapping (that is, they don't have a
+ usable, reasonably live consensus). Only used by clients fetching from a
+ list of fallback directory mirrors. This schedule is advanced by
+ (potentially concurrent) connection attempts, unlike other schedules, which
+ are advanced by connection failures. Changing this schedule requires that
+ **TestingTorNetwork** is set. (Default: 0, 1, 4, 11, 3600, 10800, 25200,
+ 54000, 111600, 262800)
+
+[[TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule]] **TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule** __N__,__N__,__...__::
+ Schedule for when clients should download consensuses from authorities if
+ they are bootstrapping (that is, they don't have a usable, reasonably live
+ consensus). Only used by clients which don't have or won't fetch from a
+ list of fallback directory mirrors. This schedule is advanced by
+ (potentially concurrent) connection attempts, unlike other schedules,
+ which are advanced by connection failures. Changing this schedule requires
+ that **TestingTorNetwork** is set. (Default: 0, 3, 7, 3600, 10800, 25200,
+ 54000, 111600, 262800)
+
[[TestingBridgeDownloadSchedule]] **TestingBridgeDownloadSchedule** __N__,__N__,__...__::
Schedule for when clients should download bridge descriptors. Changing this
requires that **TestingTorNetwork** is set. (Default: 3600, 900, 900, 3600)
@@ -2375,9 +2474,19 @@ The following options are used for running a testing Tor network.
5 minutes)
[[TestingConsensusMaxDownloadTries]] **TestingConsensusMaxDownloadTries** __NUM__::
- Try this often to download a consensus before giving up. Changing
+ Try this many times to download a consensus before giving up. Changing
this requires that **TestingTorNetwork** is set. (Default: 8)
+[[TestingClientBootstrapConsensusMaxDownloadTries]] **TestingClientBootstrapConsensusMaxDownloadTries** __NUM__::
+ Try this many times to download a consensus while bootstrapping using
+ fallback directory mirrors before giving up. Changing this requires that
+ **TestingTorNetwork** is set. (Default: 7)
+
+[[TestingClientBootstrapConsensusMaxInProgressTries]] **TestingClientBootstrapConsensusMaxInProgressTries** __NUM__::
+ Try this many simultaneous connections to download a consensus before
+ waiting for one to complete, timeout, or error out. Changing this
+ requires that **TestingTorNetwork** is set. (Default: 4)
+
[[TestingDescriptorMaxDownloadTries]] **TestingDescriptorMaxDownloadTries** __NUM__::
Try this often to download a server descriptor before giving up.
Changing this requires that **TestingTorNetwork** is set. (Default: 8)
@@ -2431,7 +2540,7 @@ The following options are used for running a testing Tor network.
information on how to specify nodes.
+
In order for this option to have any effect, **TestingTorNetwork**
- and **VoteOnHidServDirectoriesV2** both have to be set.
+ must be set.
[[TestingDirAuthVoteHSDirIsStrict]] **TestingDirAuthVoteHSDirIsStrict** **0**|**1** ::
If True (1), a node will never receive the HSDir flag unless it is specified
@@ -2460,7 +2569,7 @@ The following options are used for running a testing Tor network.
authority on a testing network. Overrides the usual default lower bound
of 4 KB. (Default: 0)
-[[TestingLinkCertLifetime]] **TestingLinkCertifetime** __N__ **seconds**|**minutes**|**hours**|**days**|**weeks**|**months**::
+[[TestingLinkCertLifetime]] **TestingLinkCertLifetime** __N__ **seconds**|**minutes**|**hours**|**days**|**weeks**|**months**::
Overrides the default lifetime for the certificates used to authenticate
our X509 link cert with our ed25519 signing key.
(Default: 2 days)
@@ -2470,8 +2579,10 @@ The following options are used for running a testing Tor network.
key.
(Default: 2 days)
-[[TestingLinkKeySlop]] **TestingLinkKeySlop** __N__ **seconds**|**minutes**|**hours**::
-[[TestingAuthKeySlop]] **TestingAuthKeySlop** __N__ **seconds**|**minutes**|**hours**::
+[[TestingLinkKeySlop]] **TestingLinkKeySlop** __N__ **seconds**|**minutes**|**hours** +
+
+[[TestingAuthKeySlop]] **TestingAuthKeySlop** __N__ **seconds**|**minutes**|**hours** +
+
[[TestingSigningKeySlop]] **TestingSigningKeySlop** __N__ **seconds**|**minutes**|**hours**::
How early before the official expiration of a an Ed25519 signing key do
we replace it and issue a new key?
@@ -2572,8 +2683,8 @@ __DataDirectory__**/bw_accounting**::
__DataDirectory__**/control_auth_cookie**::
Used for cookie authentication with the controller. Location can be
overridden by the CookieAuthFile config option. Regenerated on startup. See
- control-spec.txt for details. Only used when cookie authentication is
- enabled.
+ control-spec.txt in https://spec.torproject.org/[torspec] for details.
+ Only used when cookie authentication is enabled.
__DataDirectory__**/lock**::
This file is used to prevent two Tor instances from using same data
@@ -2660,11 +2771,12 @@ SEE ALSO
**https://www.torproject.org/**
+**torspec: https://spec.torproject.org **
BUGS
----
-Plenty, probably. Tor is still in development. Please report them.
+Plenty, probably. Tor is still in development. Please report them at https://trac.torproject.org/.
AUTHORS
-------
diff --git a/scripts/codegen/get_mozilla_ciphers.py b/scripts/codegen/get_mozilla_ciphers.py
index e0a662bea0..e673ec7dc6 100644
--- a/scripts/codegen/get_mozilla_ciphers.py
+++ b/scripts/codegen/get_mozilla_ciphers.py
@@ -29,7 +29,7 @@ def ossl(s):
#####
# Read the cpp file to understand what Ciphers map to what name :
# Make "ciphers" a map from name used in the javascript to a cipher macro name
-fileA = open(ff('security/manager/ssl/src/nsNSSComponent.cpp'),'r')
+fileA = open(ff('security/manager/ssl/nsNSSComponent.cpp'),'r')
# The input format is a file containing exactly one section of the form:
# static CipherPref CipherPrefs[] = {
@@ -71,7 +71,7 @@ for line in cipherLines:
assert not key_pending
key_pending = m.group(1)
continue
- m = re.search(r'^\s*(\S+)(?:,\s*(true|false))?\s*}', line)
+ m = re.search(r'^\s*(\S+)(?:,\s*(true|false))+\s*}', line)
if m:
assert key_pending
key = key_pending
@@ -107,7 +107,7 @@ fileC.close()
# Build a map enabled_ciphers from javascript name to "true" or "false",
# and an (unordered!) list of the macro names for those ciphers that are
# enabled.
-fileB = open(ff('netwerk/base/public/security-prefs.js'), 'r')
+fileB = open(ff('netwerk/base/security-prefs.js'), 'r')
enabled_ciphers = {}
for line in fileB:
diff --git a/scripts/maint/analyze_callgraph.py b/scripts/maint/analyze_callgraph.py
index b28460489a..8ce5827f07 100755
--- a/scripts/maint/analyze_callgraph.py
+++ b/scripts/maint/analyze_callgraph.py
@@ -9,19 +9,28 @@ import os
class Parser:
def __init__(self):
self.calls = {}
+ self.definedIn = {}
def enter_func(self, name):
- if self.infunc and not self.extern:
+ if self.infunc and not self.extern and self.calledfns:
+ if self.infunc in self.definedIn:
+ #print "{}: {} or {}?".format(
+ # self.infunc, self.definedIn[self.infunc], self.module)
+ self.definedIn[self.infunc] = 'nil'
+ else:
+ self.definedIn[self.infunc] = self.module
self.calls.setdefault(self.infunc, set()).update( self.calledfns )
self.calledfns = set()
self.infunc = name
self.extern = False
- def parse_callgraph_file(self, inp):
+ def parse_callgraph_file(self, inp, module):
self.infunc = None
self.extern = False
self.calledfns = set()
+ self.module = module
+
for line in inp:
m = re.match(r"Call graph node for function: '([^']+)'", line)
if m:
@@ -189,13 +198,27 @@ def connection_bottlenecks(callgraph):
if __name__ == '__main__':
p = Parser()
for fname in sys.argv[1:]:
+ modname = re.sub(r'.*/', '', fname).replace('.callgraph', '.c')
with open(fname, 'r') as f:
- p.parse_callgraph_file(f)
+ p.parse_callgraph_file(f, modname)
- sys.stdout.flush
+ sys.stdout.flush()
print "Building callgraph"
callgraph = p.extract_callgraph()
+ inModule = p.definedIn
+
+ print "Deriving module callgraph"
+ modCallgraph = {}
+ for fn in callgraph:
+ fnMod = inModule[fn]
+ for called in callgraph[fn]:
+ try:
+ calledMod = inModule[called]
+ except KeyError:
+ continue
+ modCallgraph.setdefault(fnMod, set()).add(calledMod)
+ del modCallgraph['nil']
print "Finding strongly connected components"
sccs = strongly_connected_components(callgraph)
@@ -206,11 +229,28 @@ if __name__ == '__main__':
print "Finding bottlenecks..."
bottlenecks = connection_bottlenecks(callgraph)
+ print "Finding module SCCs"
+ modSCCS = strongly_connected_components(modCallgraph)
+
+ print "Finding module TC"
+ modTC = transitive_closure(modCallgraph)
+
+ print "Finding module bottlenecks"
+ modB = connection_bottlenecks(modCallgraph)
+
data = {
'callgraph' : callgraph,
'sccs' : sccs,
'closure' : closure,
- 'bottlenecks' : bottlenecks }
+ 'bottlenecks' : bottlenecks,
+ 'modules' : p.definedIn,
+ 'modItems' : {
+ 'callgraph' : modCallgraph,
+ 'sccs' : modSCCS,
+ 'closure' : modTC,
+ 'bottlenecks' : modB,
+ }
+ }
with open('callgraph.pkl', 'w') as f:
cPickle.dump(data, f)
diff --git a/scripts/maint/display_callgraph.py b/scripts/maint/display_callgraph.py
index 211bfda28d..c9001c6d96 100755
--- a/scripts/maint/display_callgraph.py
+++ b/scripts/maint/display_callgraph.py
@@ -4,6 +4,8 @@ import cPickle
data = cPickle.load(open("callgraph.pkl"))
+# data = data['modItems']
+
callgraph = data['callgraph']
closure = data['closure']
sccs = data['sccs']
diff --git a/scripts/maint/fallback.blacklist b/scripts/maint/fallback.blacklist
new file mode 100644
index 0000000000..3c0f8fc5c6
--- /dev/null
+++ b/scripts/maint/fallback.blacklist
@@ -0,0 +1,41 @@
+# updateFallbackDirs.py directory mirror blacklist
+#
+# Format:
+# [ IPv4[:DirPort] ] [ orport=<ORPort> ] [ id=<ID> ] ...
+# [ ipv6=<IPv6>[:<IPv6 ORPort>] ]
+#
+# If a sufficiently specific group of attributes matches, the directory mirror
+# will be excluded: (each group is listed on its own line)
+# <IPv4>, <DirPort>
+# <IPv4>, <ORPort>
+# <ID>
+# <IPv6>, <DirPort>
+# <IPv6>, <IPv6 ORPort>
+# If DirPort and ORPort are not present, the entire IP address is blacklisted.
+# (The blacklist overrides the whitelist.)
+
+# If a relay operator doesn't want their relay to be a FallbackDir,
+# enter the following information here:
+# <IPv4>:<DirPort> orport=<ORPort> id=<ID> ipv6=<IPv6>:<IPv6 ORPort>
+
+# https://lists.torproject.org/pipermail/tor-relays/2015-December/008364.html
+87.181.248.227:9030 orport=443 id=8827944C4BDCBDAC9079803F47823403C11A9B7A
+
+# https://lists.torproject.org/pipermail/tor-relays/2015-December/008368.html
+149.18.2.82:9030 orport=9001 id=953DB709F2A2DECC8D7560661F934E64411444F7
+
+# https://lists.torproject.org/pipermail/tor-relays/2015-December/008384.html
+80.82.215.199:80 orport=443 id=3BEFAB76461B6B99DCF34C285E933562F5712AE4 ipv6=[2001:4ba0:cafe:a18::1]:443
+
+# https://lists.torproject.org/pipermail/tor-relays/2016-January/008515.html
+# later opt-out in
+# https://lists.torproject.org/pipermail/tor-relays/2016-January/008521.html
+5.9.158.75:80 orport=443 id=F1BE15429B3CE696D6807F4D4A58B1BFEC45C822 ipv6=[2a01:4f8:190:514a::2]:443
+
+# Email sent directly to teor, verified using relay contact info
+5.34.183.168:80 orport=443 id=601C92108A568742A7A6D9473FE3A414F7149070
+217.12.199.208:8080 orport=22 id=BCFB0933367D626715DA32A147F417194A5D48D6
+
+# https://lists.torproject.org/pipermail/tor-relays/2016-January/008555.html
+62.210.207.124:9030 orport=9001 id=58938B1A5C4029B4415D38A4F36B7724273F4755 ipv6=[2001:bc8:31eb:100::1]:9001
+62.210.207.124:9130 orport=9101 id=338D0AB6DBAB7B529B9C91B2FD770658000693C4 ipv6=[2001:bc8:31eb:100::1]:9101
diff --git a/scripts/maint/fallback.whitelist b/scripts/maint/fallback.whitelist
new file mode 100644
index 0000000000..d5ddcd2e82
--- /dev/null
+++ b/scripts/maint/fallback.whitelist
@@ -0,0 +1,100 @@
+# updateFallbackDirs.py directory mirror whitelist
+#
+# Format:
+# IPv4:DirPort orport=<ORPort> id=<ID> [ ipv6=<IPv6>:<IPv6 ORPort> ]
+#
+# All attributes must match for the directory mirror to be included.
+# If the fallback has an ipv6 key, the whitelist line must also have
+# it, and vice versa, otherwise they don't match.
+# (The blacklist overrides the whitelist.)
+
+# If a relay operator wants their relay to be a FallbackDir,
+# enter the following information here:
+# <IPv4>:<DirPort> orport=<ORPort> id=<ID> [ ipv6=<IPv6>:<IPv6 ORPort> ]
+
+# https://lists.torproject.org/pipermail/tor-relays/2015-December/008362.html
+78.47.18.110:443 orport=80 id=F8D27B163B9247B232A2EEE68DD8B698695C28DE
+131.188.40.188:443 orport=80 id=EBE718E1A49EE229071702964F8DB1F318075FF8
+
+# https://lists.torproject.org/pipermail/tor-relays/2015-December/008363.html
+84.219.173.60:9030 orport=443 id=855BC2DABE24C861CD887DB9B2E950424B49FC34
+
+# https://lists.torproject.org/pipermail/tor-relays/2015-December/008365.html
+81.7.17.171:80 orport=443 id=00C4B4731658D3B4987132A3F77100CFCB190D97 ipv6=[2a02:180:1:1::517:11ab]:443
+
+# https://lists.torproject.org/pipermail/tor-relays/2015-December/008366.html
+5.39.88.19:9030 orport=9001 id=7CB8C31432A796731EA7B6BF4025548DFEB25E0C ipv6=[2001:41d0:8:9a13::1]:9050
+
+# https://lists.torproject.org/pipermail/tor-relays/2015-December/008367.html
+# Email sent directly to teor with intended DirPort settings
+88.198.38.226:22 orport=443 id=4B9E2C56FB42B891794FE2CD2FCAD08A320CC3BB ipv6=[2a01:4f8:a0:1351::2]:80
+213.239.210.204:22 orport=443 id=5BFDECCE9B4A23AE14EC767C5A2C1E10558B00B9 ipv6=[2a01:4f8:a0:9474::2]:80
+213.239.220.25:22 orport=443 id=BEE2317AE127EB681C5AE1551C1EA0630580638A ipv6=[2a01:4f8:a0:710c::2]:80
+85.10.201.38:22 orport=443 id=F6279A203C1950ACF592322A235647A05BFBCF91 ipv6=[2a01:4f8:a0:43cc::2]:80
+
+# https://lists.torproject.org/pipermail/tor-relays/2015-December/008370.html
+# https://lists.torproject.org/pipermail/tor-relays/2016-January/008517.html
+# https://lists.torproject.org/pipermail/tor-relays/2016-January/008555.html
+62.210.124.124:9030 orport=9001 id=86E78DD3720C78DA8673182EF96C54B162CD660C ipv6=[2001:bc8:3f23:100::1]:9001
+62.210.124.124:9130 orport=9101 id=2EBD117806EE43C3CC885A8F1E4DC60F207E7D3E ipv6=[2001:bc8:3f23:100::1]:9101
+# DirPort TBC in next consensus
+212.47.237.95:9030 orport=9001 id=3F5D8A879C58961BB45A3D26AC41B543B40236D6
+212.47.237.95:9130 orport=9101 id=6FB38EB22E57EF7ED5EF00238F6A48E553735D88
+
+# https://lists.torproject.org/pipermail/tor-relays/2015-December/008372.html
+# IPv6 tunnel available on request (is this a good idea?)
+108.53.208.157:80 orport=443 id=4F0DB7E687FC7C0AE55C8F243DA8B0EB27FBF1F2
+
+# https://lists.torproject.org/pipermail/tor-relays/2015-December/008373.html
+167.114.35.28:9030 orport=9001 id=E65D300F11E1DB12C534B0146BDAB6972F1A8A48
+
+# https://lists.torproject.org/pipermail/tor-relays/2015-December/008374.html
+170.130.1.7:9030 orport=9001 id=FA3415659444AE006E7E9E5375E82F29700CFDFD
+
+# https://lists.torproject.org/pipermail/tor-relays/2015-December/008378.html
+144.76.14.145:110 orport=143 id=14419131033443AE6E21DA82B0D307F7CAE42BDB ipv6=[2a01:4f8:190:9490::dead]:443
+
+# https://lists.torproject.org/pipermail/tor-relays/2015-December/008379.html
+85.25.138.93:9030 orport=4029 id=6DE61A6F72C1E5418A66BFED80DFB63E4C77668F
+
+# https://lists.torproject.org/pipermail/tor-relays/2015-December/008380.html
+5.175.233.86:80 orport=443 id=5525D0429BFE5DC4F1B0E9DE47A4CFA169661E33
+
+# https://lists.torproject.org/pipermail/tor-relays/2015-December/008381.html
+# Sent additional email to teor with more relays
+178.254.44.135:9030 orport=9001 id=8FA37B93397015B2BC5A525C908485260BE9F422
+178.254.20.134:80 orport=443 id=9F5068310818ED7C70B0BC4087AB55CB12CB4377
+178.254.20.134:9030 orport=9001 id=2CE96A8A1DA032664C90F574AFFBECE18A6E8DFC
+178.254.44.135:80 orport=443 id=AE6A8C18E7499B586CD36246AC4BCAFFBBF93AB2
+178.254.13.126:80 orport=443 id=F9246DEF2B653807236DA134F2AEAB103D58ABFE
+178.254.13.126:9030 orport=9001 id=0C475BA4D3AA3C289B716F95954CAD616E50C4E5
+
+# https://lists.torproject.org/pipermail/tor-relays/2015-December/008382.html
+51.255.33.237:9091 orport=9001 id=A360C21FA87FFA2046D92C17086A6B47E5C68109
+
+# https://lists.torproject.org/pipermail/tor-relays/2015-December/008383.html
+81.7.14.246:80 orport=443 id=CE75BF0972ADD52AF8807602374E495C815DB304 ipv6=[2a02:180:a:51::dead]:443
+
+# https://lists.torproject.org/pipermail/tor-relays/2015-December/008384.html
+149.202.98.161:80 orport=443 id=54660C671B47E6986B465B80444414BD19E5A34B ipv6=[2001:41d0:8:4528::161]:443
+193.111.136.162:80 orport=443 id=C79552275DFCD486B942510EF663ED36ACA1A84B ipv6=[2001:4ba0:cafe:10d0::1]:443
+
+# https://lists.torproject.org/pipermail/tor-relays/2015-December/008416.html
+185.100.84.212:80 orport=443 id=330CD3DB6AD266DC70CDB512B036957D03D9BC59 ipv6=[2a06:1700:0:7::1]:443
+
+# https://lists.torproject.org/pipermail/tor-relays/2015-December/008417.html
+178.16.208.56:80 orport=443 id=2CDCFED0142B28B002E89D305CBA2E26063FADE2 ipv6=[2a00:1c20:4089:1234:cd49:b58a:9ebe:67ec]:443
+178.16.208.57:80 orport=443 id=92CFD9565B24646CAC2D172D3DB503D69E777B8A ipv6=[2a00:1c20:4089:1234:7825:2c5d:1ecd:c66f]:443
+
+# https://lists.torproject.org/pipermail/tor-relays/2016-January/008513.html
+178.62.173.203:9030 orport=9001 id=DD85503F2D1F52EF9EAD621E942298F46CD2FC10 ipv6=[2a03:b0c0:0:1010::a4:b001]:9001
+
+# https://lists.torproject.org/pipermail/tor-relays/2016-January/008534.html
+5.9.110.236:9030 orport=9001 id=0756B7CD4DFC8182BE23143FAC0642F515182CEB ipv6=[2a01:4f8:162:51e2::2]:9001
+
+# https://lists.torproject.org/pipermail/tor-relays/2016-January/008542.html
+178.62.199.226:80 orport=443 id=CBEFF7BA4A4062045133C053F2D70524D8BBE5BE ipv6=[2a03:b0c0:2:d0::b7:5001]:443
+
+# Email sent directly to teor, verified using relay contact info
+217.12.199.208:80 orport=443 id=DF3AED4322B1824BF5539AE54B2D1B38E080FF05
+217.12.210.214:80 orport=443 id=943C0C6841C1E914B9FCA796C6846620A5AF9BC7
diff --git a/scripts/maint/lintChanges.py b/scripts/maint/lintChanges.py
index c2fc01d2bf..a73110dab1 100755
--- a/scripts/maint/lintChanges.py
+++ b/scripts/maint/lintChanges.py
@@ -7,6 +7,20 @@ import re
import os
+KNOWN_GROUPS=set([
+ "Minor bugfix",
+ "Minor bugfixes",
+ "Major bugfix",
+ "Major bugfixes",
+ "Minor feature",
+ "Minor features",
+ "Major feature",
+ "Major features",
+ "Testing",
+ "Documentation",
+ "Code simplification and refactoring",
+ "Removed features"])
+
def lintfile(fname):
have_warned = []
@@ -31,8 +45,16 @@ def lintfile(fname):
lines = contents.split("\n")
isBug = ("bug" in lines[0] or "fix" in lines[0])
- if not re.match(r'^[ ]{2}o (.*)', contents):
+ m = re.match(r'^[ ]{2}o ([^\(:]*)([^:]*):', contents)
+ if not m:
warn("header not in format expected")
+ elif m.group(1).strip() not in KNOWN_GROUPS:
+ warn("Weird header: %r"%m.group(1))
+ elif ( ("bugfix" in m.group(1) or "feature" in m.group(1)) and
+ ("Removed" not in m.group(1)) and
+ '(' not in m.group(2)):
+ warn("Missing subcategory on %s"%m.group(1))
+
contents = " ".join(contents.split())
diff --git a/scripts/maint/sortChanges.py b/scripts/maint/sortChanges.py
index 7e25cefd53..d6ec0e269d 100755
--- a/scripts/maint/sortChanges.py
+++ b/scripts/maint/sortChanges.py
@@ -16,10 +16,36 @@ def fetch(fn):
s = "%s\n" % s.rstrip()
return s
+CSR='Code simplification and refactoring'
+
+REPLACEMENTS = {
+ # plurals
+ 'Minor bugfix' : 'Minor bugfixes',
+ 'Major bugfix' : 'Major bugfixes',
+ 'Minor feature' : 'Minor features',
+ 'Major feature' : 'Major features',
+ 'Removed feature' : 'Removed features',
+ 'Code simplification and refactorings' : CSR,
+ 'Code simplifications and refactoring' : CSR,
+ 'Code simplifications and refactorings' : CSR,
+
+ # wrong words
+ 'Minor fix' : 'Minor bugfixes',
+ 'Major fix' : 'Major bugfixes',
+ 'Minor fixes' : 'Minor bugfixes',
+ 'Major fixes' : 'Major bugfixes',
+ 'Minor enhancement' : 'Minor features',
+ 'Minor enhancements' : 'Minor features',
+ 'Major enhancement' : 'Major features',
+ 'Major enhancements' : 'Major features',
+}
+
def score(s,fname=None):
m = re.match(r'^ +o ([^\n]*)\n(.*)', s, re.M|re.S)
if not m:
print >>sys.stderr, "Can't score %r from %s"%(s,fname)
+ heading = m.group(1)
+ heading = REPLACEMENTS.get(heading, heading)
lw = m.group(1).lower()
if lw.startswith("major feature"):
score = 0
@@ -36,7 +62,7 @@ def score(s,fname=None):
else:
score = 100
- return (score, lw, m.group(1), m.group(2))
+ return (score, lw, heading, m.group(2))
def splitChanges(s):
this_entry = []
diff --git a/scripts/maint/updateFallbackDirs.py b/scripts/maint/updateFallbackDirs.py
new file mode 100755
index 0000000000..fd7d350c2e
--- /dev/null
+++ b/scripts/maint/updateFallbackDirs.py
@@ -0,0 +1,1257 @@
+#!/usr/bin/python
+
+# Usage: scripts/maint/updateFallbackDirs.py > src/or/fallback_dirs.inc
+#
+# Then read the generated list to ensure no-one slipped anything funny into
+# their name or contactinfo
+
+# Script by weasel, April 2015
+# Portions by gsathya & karsten, 2013
+# https://trac.torproject.org/projects/tor/attachment/ticket/8374/dir_list.2.py
+# Modifications by teor, 2015
+
+import StringIO
+import string
+import re
+import datetime
+import gzip
+import os.path
+import json
+import math
+import sys
+import urllib
+import urllib2
+import hashlib
+import dateutil.parser
+# bson_lazy provides bson
+#from bson import json_util
+
+import logging
+logging.basicConfig(level=logging.DEBUG)
+
+## Top-Level Configuration
+
+# Output all candidate fallbacks, or only output selected fallbacks?
+OUTPUT_CANDIDATES = False
+
+## OnionOO Settings
+
+ONIONOO = 'https://onionoo.torproject.org/'
+#ONIONOO = 'https://onionoo.thecthulhu.com/'
+
+# Don't bother going out to the Internet, just use the files available locally,
+# even if they're very old
+LOCAL_FILES_ONLY = False
+
+## Whitelist / Blacklist Filter Settings
+
+# The whitelist contains entries that are included if all attributes match
+# (IPv4, dirport, orport, id, and optionally IPv6 and IPv6 orport)
+# The blacklist contains (partial) entries that are excluded if any
+# sufficiently specific group of attributes matches:
+# IPv4 & DirPort
+# IPv4 & ORPort
+# ID
+# IPv6 & DirPort
+# IPv6 & IPv6 ORPort
+# If neither port is included in the blacklist, the entire IP address is
+# blacklisted.
+
+# What happens to entries in neither list?
+# When True, they are included, when False, they are excluded
+INCLUDE_UNLISTED_ENTRIES = True if OUTPUT_CANDIDATES else False
+
+# If an entry is in both lists, what happens?
+# When True, it is excluded, when False, it is included
+BLACKLIST_EXCLUDES_WHITELIST_ENTRIES = True
+
+WHITELIST_FILE_NAME = 'scripts/maint/fallback.whitelist'
+BLACKLIST_FILE_NAME = 'scripts/maint/fallback.blacklist'
+
+# The number of bytes we'll read from a filter file before giving up
+MAX_LIST_FILE_SIZE = 1024 * 1024
+
+## Eligibility Settings
+
+ADDRESS_AND_PORT_STABLE_DAYS = 120
+# What time-weighted-fraction of these flags must FallbackDirs
+# Equal or Exceed?
+CUTOFF_RUNNING = .95
+CUTOFF_V2DIR = .95
+CUTOFF_GUARD = .95
+# What time-weighted-fraction of these flags must FallbackDirs
+# Equal or Fall Under?
+# .00 means no bad exits
+PERMITTED_BADEXIT = .00
+
+## List Length Limits
+
+# The target for these parameters is 20% of the guards in the network
+# This is around 200 as of October 2015
+FALLBACK_PROPORTION_OF_GUARDS = None if OUTPUT_CANDIDATES else 0.2
+
+# Limit the number of fallbacks (eliminating lowest by weight)
+MAX_FALLBACK_COUNT = None if OUTPUT_CANDIDATES else 500
+# Emit a C #error if the number of fallbacks is below
+MIN_FALLBACK_COUNT = 100
+
+## Fallback Weight Settings
+
+# Any fallback with the Exit flag has its weight multipled by this fraction
+EXIT_WEIGHT_FRACTION = 1.0
+
+# If True, emit a C #error if we can't satisfy various constraints
+# If False, emit a C comment instead
+STRICT_FALLBACK_WEIGHTS = False
+
+# Limit the proportional weight
+# If a single fallback's weight is too high, it will see too many clients
+# We reweight using a lower threshold to provide some leeway for:
+# * elimination of low weight relays
+# * consensus weight changes
+# * fallback directory losses over time
+# A relay weighted at 1 in 10 fallbacks will see about 10% of clients that
+# use the fallback directories. (The 9 directory authorities see a similar
+# proportion of clients.)
+TARGET_MAX_WEIGHT_FRACTION = 1/10.0
+REWEIGHTING_FUDGE_FACTOR = 0.8
+MAX_WEIGHT_FRACTION = TARGET_MAX_WEIGHT_FRACTION * REWEIGHTING_FUDGE_FACTOR
+# If a single fallback's weight is too low, it's pointless adding it.
+# (Final weights may be slightly higher than this, due to low weight relays
+# being excluded.)
+# A relay weighted at 1 in 1000 fallbacks will see about 0.1% of clients.
+MIN_WEIGHT_FRACTION = 0.0 if OUTPUT_CANDIDATES else 1/1000.0
+
+## Other Configuration Parameters
+
+# older entries' weights are adjusted with ALPHA^(age in days)
+AGE_ALPHA = 0.99
+
+# this factor is used to scale OnionOO entries to [0,1]
+ONIONOO_SCALE_ONE = 999.
+
+## Parsing Functions
+
+def parse_ts(t):
+ return datetime.datetime.strptime(t, "%Y-%m-%d %H:%M:%S")
+
+def remove_bad_chars(raw_string, bad_char_list):
+ # Remove each character in the bad_char_list
+ escaped_string = raw_string
+ for c in bad_char_list:
+ escaped_string = escaped_string.replace(c, '')
+ return escaped_string
+
+def cleanse_whitespace(raw_string):
+ # Replace all whitespace characters with a space
+ escaped_string = raw_string
+ for c in string.whitespace:
+ escaped_string = escaped_string.replace(c, ' ')
+ return escaped_string
+
+def cleanse_c_multiline_comment(raw_string):
+ # Prevent a malicious / unanticipated string from breaking out
+ # of a C-style multiline comment
+ # This removes '/*' and '*/'
+ # To deal with '//', the end comment must be on its own line
+ bad_char_list = '*'
+ # Prevent a malicious string from using C nulls
+ bad_char_list += '\0'
+ # Be safer by removing bad characters entirely
+ escaped_string = remove_bad_chars(raw_string, bad_char_list)
+ # Embedded newlines should be removed by tor/onionoo, but let's be paranoid
+ escaped_string = cleanse_whitespace(escaped_string)
+ # Some compilers may further process the content of comments
+ # There isn't much we can do to cover every possible case
+ # But comment-based directives are typically only advisory
+ return escaped_string
+
+def cleanse_c_string(raw_string):
+ # Prevent a malicious address/fingerprint string from breaking out
+ # of a C-style string
+ bad_char_list = '"'
+ # Prevent a malicious string from using escapes
+ bad_char_list += '\\'
+ # Prevent a malicious string from using C nulls
+ bad_char_list += '\0'
+ # Be safer by removing bad characters entirely
+ escaped_string = remove_bad_chars(raw_string, bad_char_list)
+ # Embedded newlines should be removed by tor/onionoo, but let's be paranoid
+ escaped_string = cleanse_whitespace(escaped_string)
+ # Some compilers may further process the content of strings
+ # There isn't much we can do to cover every possible case
+ # But this typically only results in changes to the string data
+ return escaped_string
+
+## OnionOO Source Functions
+
+# a dictionary of source metadata for each onionoo query we've made
+fetch_source = {}
+
+# register source metadata for 'what'
+# assumes we only retrieve one document for each 'what'
+def register_fetch_source(what, url, relays_published, version):
+ fetch_source[what] = {}
+ fetch_source[what]['url'] = url
+ fetch_source[what]['relays_published'] = relays_published
+ fetch_source[what]['version'] = version
+
+# list each registered source's 'what'
+def fetch_source_list():
+ return sorted(fetch_source.keys())
+
+# given 'what', provide a multiline C comment describing the source
+def describe_fetch_source(what):
+ desc = '/*'
+ desc += '\n'
+ desc += 'Onionoo Source: '
+ desc += cleanse_c_multiline_comment(what)
+ desc += ' Date: '
+ desc += cleanse_c_multiline_comment(fetch_source[what]['relays_published'])
+ desc += ' Version: '
+ desc += cleanse_c_multiline_comment(fetch_source[what]['version'])
+ desc += '\n'
+ desc += 'URL: '
+ desc += cleanse_c_multiline_comment(fetch_source[what]['url'])
+ desc += '\n'
+ desc += '*/'
+ return desc
+
+## File Processing Functions
+
+def write_to_file(str, file_name, max_len):
+ try:
+ with open(file_name, 'w') as f:
+ f.write(str[0:max_len])
+ except EnvironmentError, error:
+ logging.debug('Writing file %s failed: %d: %s'%
+ (file_name,
+ error.errno,
+ error.strerror)
+ )
+
+def read_from_file(file_name, max_len):
+ try:
+ if os.path.isfile(file_name):
+ with open(file_name, 'r') as f:
+ return f.read(max_len)
+ except EnvironmentError, error:
+ logging.debug('Loading file %s failed: %d: %s'%
+ (file_name,
+ error.errno,
+ error.strerror)
+ )
+ return None
+
+def load_possibly_compressed_response_json(response):
+ if response.info().get('Content-Encoding') == 'gzip':
+ buf = StringIO.StringIO( response.read() )
+ f = gzip.GzipFile(fileobj=buf)
+ return json.load(f)
+ else:
+ return json.load(response)
+
+def load_json_from_file(json_file_name):
+ # An exception here may be resolved by deleting the .last_modified
+ # and .json files, and re-running the script
+ try:
+ with open(json_file_name, 'r') as f:
+ return json.load(f)
+ except EnvironmentError, error:
+ raise Exception('Reading not-modified json file %s failed: %d: %s'%
+ (json_file_name,
+ error.errno,
+ error.strerror)
+ )
+
+## OnionOO Functions
+
+def datestr_to_datetime(datestr):
+ # Parse datetimes like: Fri, 02 Oct 2015 13:34:14 GMT
+ if datestr is not None:
+ dt = dateutil.parser.parse(datestr)
+ else:
+ # Never modified - use start of epoch
+ dt = datetime.datetime.utcfromtimestamp(0)
+ # strip any timezone out (in case they're supported in future)
+ dt = dt.replace(tzinfo=None)
+ return dt
+
+def onionoo_fetch(what, **kwargs):
+ params = kwargs
+ params['type'] = 'relay'
+ #params['limit'] = 10
+ params['first_seen_days'] = '%d-'%(ADDRESS_AND_PORT_STABLE_DAYS,)
+ params['last_seen_days'] = '-7'
+ params['flag'] = 'V2Dir'
+ url = ONIONOO + what + '?' + urllib.urlencode(params)
+
+ # Unfortunately, the URL is too long for some OS filenames,
+ # but we still don't want to get files from different URLs mixed up
+ base_file_name = what + '-' + hashlib.sha1(url).hexdigest()
+
+ full_url_file_name = base_file_name + '.full_url'
+ MAX_FULL_URL_LENGTH = 1024
+
+ last_modified_file_name = base_file_name + '.last_modified'
+ MAX_LAST_MODIFIED_LENGTH = 64
+
+ json_file_name = base_file_name + '.json'
+
+ if LOCAL_FILES_ONLY:
+ # Read from the local file, don't write to anything
+ response_json = load_json_from_file(json_file_name)
+ else:
+ # store the full URL to a file for debugging
+ # no need to compare as long as you trust SHA-1
+ write_to_file(url, full_url_file_name, MAX_FULL_URL_LENGTH)
+
+ request = urllib2.Request(url)
+ request.add_header('Accept-encoding', 'gzip')
+
+ # load the last modified date from the file, if it exists
+ last_mod_date = read_from_file(last_modified_file_name,
+ MAX_LAST_MODIFIED_LENGTH)
+ if last_mod_date is not None:
+ request.add_header('If-modified-since', last_mod_date)
+
+ # Parse last modified date
+ last_mod = datestr_to_datetime(last_mod_date)
+
+ # Not Modified and still recent enough to be useful
+ # Onionoo / Globe used to use 6 hours, but we can afford a day
+ required_freshness = datetime.datetime.utcnow()
+ # strip any timezone out (to match dateutil.parser)
+ required_freshness = required_freshness.replace(tzinfo=None)
+ required_freshness -= datetime.timedelta(hours=24)
+
+ # Make the OnionOO request
+ response_code = 0
+ try:
+ response = urllib2.urlopen(request)
+ response_code = response.getcode()
+ except urllib2.HTTPError, error:
+ response_code = error.code
+ if response_code == 304: # not modified
+ pass
+ else:
+ raise Exception("Could not get " + url + ": "
+ + str(error.code) + ": " + error.reason)
+
+ if response_code == 200: # OK
+ last_mod = datestr_to_datetime(response.info().get('Last-Modified'))
+
+ # Check for freshness
+ if last_mod < required_freshness:
+ if last_mod_date is not None:
+ date_message = "Outdated data: last updated " + last_mod_date
+ else:
+ date_message = "No data: never downloaded "
+ raise Exception(date_message + " from " + url)
+
+ # Process the data
+ if response_code == 200: # OK
+
+ response_json = load_possibly_compressed_response_json(response)
+
+ with open(json_file_name, 'w') as f:
+ # use the most compact json representation to save space
+ json.dump(response_json, f, separators=(',',':'))
+
+ # store the last modified date in its own file
+ if response.info().get('Last-modified') is not None:
+ write_to_file(response.info().get('Last-Modified'),
+ last_modified_file_name,
+ MAX_LAST_MODIFIED_LENGTH)
+
+ elif response_code == 304: # Not Modified
+
+ response_json = load_json_from_file(json_file_name)
+
+ else: # Unexpected HTTP response code not covered in the HTTPError above
+ raise Exception("Unexpected HTTP response code to " + url + ": "
+ + str(response_code))
+
+ register_fetch_source(what,
+ url,
+ response_json['relays_published'],
+ response_json['version'])
+
+ return response_json
+
+def fetch(what, **kwargs):
+ #x = onionoo_fetch(what, **kwargs)
+ # don't use sort_keys, as the order of or_addresses is significant
+ #print json.dumps(x, indent=4, separators=(',', ': '))
+ #sys.exit(0)
+
+ return onionoo_fetch(what, **kwargs)
+
+## Fallback Candidate Class
+
+class Candidate(object):
+ CUTOFF_ADDRESS_AND_PORT_STABLE = (datetime.datetime.now()
+ - datetime.timedelta(ADDRESS_AND_PORT_STABLE_DAYS))
+
+ def __init__(self, details):
+ for f in ['fingerprint', 'nickname', 'last_changed_address_or_port',
+ 'consensus_weight', 'or_addresses', 'dir_address']:
+ if not f in details: raise Exception("Document has no %s field."%(f,))
+
+ if not 'contact' in details:
+ details['contact'] = None
+ if not 'flags' in details or details['flags'] is None:
+ details['flags'] = []
+ details['last_changed_address_or_port'] = parse_ts(
+ details['last_changed_address_or_port'])
+ self._data = details
+ self._stable_sort_or_addresses()
+
+ self._fpr = self._data['fingerprint']
+ self._running = self._guard = self._v2dir = 0.
+ self._split_dirport()
+ self._compute_orport()
+ if self.orport is None:
+ raise Exception("Failed to get an orport for %s."%(self._fpr,))
+ self._compute_ipv6addr()
+ if self.ipv6addr is None:
+ logging.debug("Failed to get an ipv6 address for %s."%(self._fpr,))
+ # Reduce the weight of exits to EXIT_WEIGHT_FRACTION * consensus_weight
+ if self.is_exit():
+ current_weight = self._data['consensus_weight']
+ exit_weight = current_weight * EXIT_WEIGHT_FRACTION
+ self._data['original_consensus_weight'] = current_weight
+ self._data['consensus_weight'] = exit_weight
+
+ def _stable_sort_or_addresses(self):
+ # replace self._data['or_addresses'] with a stable ordering,
+ # sorting the secondary addresses in string order
+ # leave the received order in self._data['or_addresses_raw']
+ self._data['or_addresses_raw'] = self._data['or_addresses']
+ or_address_primary = self._data['or_addresses'][:1]
+ # subsequent entries in the or_addresses array are in an arbitrary order
+ # so we stabilise the addresses by sorting them in string order
+ or_addresses_secondaries_stable = sorted(self._data['or_addresses'][1:])
+ or_addresses_stable = or_address_primary + or_addresses_secondaries_stable
+ self._data['or_addresses'] = or_addresses_stable
+
+ def get_fingerprint(self):
+ return self._fpr
+
+ # is_valid_ipv[46]_address by gsathya, karsten, 2013
+ @staticmethod
+ def is_valid_ipv4_address(address):
+ if not isinstance(address, (str, unicode)):
+ return False
+
+ # check if there are four period separated values
+ if address.count(".") != 3:
+ return False
+
+ # checks that each value in the octet are decimal values between 0-255
+ for entry in address.split("."):
+ if not entry.isdigit() or int(entry) < 0 or int(entry) > 255:
+ return False
+ elif entry[0] == "0" and len(entry) > 1:
+ return False # leading zeros, for instance in "1.2.3.001"
+
+ return True
+
+ @staticmethod
+ def is_valid_ipv6_address(address):
+ if not isinstance(address, (str, unicode)):
+ return False
+
+ # remove brackets
+ address = address[1:-1]
+
+ # addresses are made up of eight colon separated groups of four hex digits
+ # with leading zeros being optional
+ # https://en.wikipedia.org/wiki/IPv6#Address_format
+
+ colon_count = address.count(":")
+
+ if colon_count > 7:
+ return False # too many groups
+ elif colon_count != 7 and not "::" in address:
+ return False # not enough groups and none are collapsed
+ elif address.count("::") > 1 or ":::" in address:
+ return False # multiple groupings of zeros can't be collapsed
+
+ found_ipv4_on_previous_entry = False
+ for entry in address.split(":"):
+ # If an IPv6 address has an embedded IPv4 address,
+ # it must be the last entry
+ if found_ipv4_on_previous_entry:
+ return False
+ if not re.match("^[0-9a-fA-f]{0,4}$", entry):
+ if not Candidate.is_valid_ipv4_address(entry):
+ return False
+ else:
+ found_ipv4_on_previous_entry = True
+
+ return True
+
+ def _split_dirport(self):
+ # Split the dir_address into dirip and dirport
+ (self.dirip, _dirport) = self._data['dir_address'].split(':', 2)
+ self.dirport = int(_dirport)
+
+ def _compute_orport(self):
+ # Choose the first ORPort that's on the same IPv4 address as the DirPort.
+ # In rare circumstances, this might not be the primary ORPort address.
+ # However, _stable_sort_or_addresses() ensures we choose the same one
+ # every time, even if onionoo changes the order of the secondaries.
+ self._split_dirport()
+ self.orport = None
+ for i in self._data['or_addresses']:
+ if i != self._data['or_addresses'][0]:
+ logging.debug('Secondary IPv4 Address Used for %s: %s'%(self._fpr, i))
+ (ipaddr, port) = i.rsplit(':', 1)
+ if (ipaddr == self.dirip) and Candidate.is_valid_ipv4_address(ipaddr):
+ self.orport = int(port)
+ return
+
+ def _compute_ipv6addr(self):
+ # Choose the first IPv6 address that uses the same port as the ORPort
+ # Or, choose the first IPv6 address in the list
+ # _stable_sort_or_addresses() ensures we choose the same IPv6 address
+ # every time, even if onionoo changes the order of the secondaries.
+ self.ipv6addr = None
+ self.ipv6orport = None
+ # Choose the first IPv6 address that uses the same port as the ORPort
+ for i in self._data['or_addresses']:
+ (ipaddr, port) = i.rsplit(':', 1)
+ if (port == self.orport) and Candidate.is_valid_ipv6_address(ipaddr):
+ self.ipv6addr = ipaddr
+ self.ipv6orport = port
+ return
+ # Choose the first IPv6 address in the list
+ for i in self._data['or_addresses']:
+ (ipaddr, port) = i.rsplit(':', 1)
+ if Candidate.is_valid_ipv6_address(ipaddr):
+ self.ipv6addr = ipaddr
+ self.ipv6orport = port
+ return
+
+ @staticmethod
+ def _extract_generic_history(history, which='unknown'):
+ # given a tree like this:
+ # {
+ # "1_month": {
+ # "count": 187,
+ # "factor": 0.001001001001001001,
+ # "first": "2015-02-27 06:00:00",
+ # "interval": 14400,
+ # "last": "2015-03-30 06:00:00",
+ # "values": [
+ # 999,
+ # 999
+ # ]
+ # },
+ # "1_week": {
+ # "count": 169,
+ # "factor": 0.001001001001001001,
+ # "first": "2015-03-23 07:30:00",
+ # "interval": 3600,
+ # "last": "2015-03-30 07:30:00",
+ # "values": [ ...]
+ # },
+ # "1_year": {
+ # "count": 177,
+ # "factor": 0.001001001001001001,
+ # "first": "2014-04-11 00:00:00",
+ # "interval": 172800,
+ # "last": "2015-03-29 00:00:00",
+ # "values": [ ...]
+ # },
+ # "3_months": {
+ # "count": 185,
+ # "factor": 0.001001001001001001,
+ # "first": "2014-12-28 06:00:00",
+ # "interval": 43200,
+ # "last": "2015-03-30 06:00:00",
+ # "values": [ ...]
+ # }
+ # },
+ # extract exactly one piece of data per time interval,
+ # using smaller intervals where available.
+ #
+ # returns list of (age, length, value) dictionaries.
+
+ generic_history = []
+
+ periods = history.keys()
+ periods.sort(key = lambda x: history[x]['interval'])
+ now = datetime.datetime.now()
+ newest = now
+ for p in periods:
+ h = history[p]
+ interval = datetime.timedelta(seconds = h['interval'])
+ this_ts = parse_ts(h['last'])
+
+ if (len(h['values']) != h['count']):
+ logging.warn('Inconsistent value count in %s document for %s'
+ %(p, which))
+ for v in reversed(h['values']):
+ if (this_ts <= newest):
+ agt1 = now - this_ts
+ agt2 = interval
+ agetmp1 = (agt1.microseconds + (agt1.seconds + agt1.days * 24 * 3600)
+ * 10**6) / 10**6
+ agetmp2 = (agt2.microseconds + (agt2.seconds + agt2.days * 24 * 3600)
+ * 10**6) / 10**6
+ generic_history.append(
+ { 'age': agetmp1,
+ 'length': agetmp2,
+ 'value': v
+ })
+ newest = this_ts
+ this_ts -= interval
+
+ if (this_ts + interval != parse_ts(h['first'])):
+ logging.warn('Inconsistent time information in %s document for %s'
+ %(p, which))
+
+ #print json.dumps(generic_history, sort_keys=True,
+ # indent=4, separators=(',', ': '))
+ return generic_history
+
+ @staticmethod
+ def _avg_generic_history(generic_history):
+ a = []
+ for i in generic_history:
+ if i['age'] > (ADDRESS_AND_PORT_STABLE_DAYS * 24 * 3600):
+ continue
+ if (i['length'] is not None
+ and i['age'] is not None
+ and i['value'] is not None):
+ w = i['length'] * math.pow(AGE_ALPHA, i['age']/(3600*24))
+ a.append( (i['value'] * w, w) )
+
+ sv = math.fsum(map(lambda x: x[0], a))
+ sw = math.fsum(map(lambda x: x[1], a))
+
+ if sw == 0.0:
+ svw = 0.0
+ else:
+ svw = sv/sw
+ return svw
+
+ def _add_generic_history(self, history):
+ periods = r['read_history'].keys()
+ periods.sort(key = lambda x: r['read_history'][x]['interval'] )
+
+ print periods
+
+ def add_running_history(self, history):
+ pass
+
+ def add_uptime(self, uptime):
+ logging.debug('Adding uptime %s.'%(self._fpr,))
+
+ # flags we care about: Running, V2Dir, Guard
+ if not 'flags' in uptime:
+ logging.debug('No flags in document for %s.'%(self._fpr,))
+ return
+
+ for f in ['Running', 'Guard', 'V2Dir']:
+ if not f in uptime['flags']:
+ logging.debug('No %s in flags for %s.'%(f, self._fpr,))
+ return
+
+ running = self._extract_generic_history(uptime['flags']['Running'],
+ '%s-Running'%(self._fpr))
+ guard = self._extract_generic_history(uptime['flags']['Guard'],
+ '%s-Guard'%(self._fpr))
+ v2dir = self._extract_generic_history(uptime['flags']['V2Dir'],
+ '%s-V2Dir'%(self._fpr))
+ if 'BadExit' in uptime['flags']:
+ badexit = self._extract_generic_history(uptime['flags']['BadExit'],
+ '%s-BadExit'%(self._fpr))
+
+ self._running = self._avg_generic_history(running) / ONIONOO_SCALE_ONE
+ self._guard = self._avg_generic_history(guard) / ONIONOO_SCALE_ONE
+ self._v2dir = self._avg_generic_history(v2dir) / ONIONOO_SCALE_ONE
+ self._badexit = None
+ if 'BadExit' in uptime['flags']:
+ self._badexit = self._avg_generic_history(badexit) / ONIONOO_SCALE_ONE
+
+ def is_candidate(self):
+ if (self._data['last_changed_address_or_port'] >
+ self.CUTOFF_ADDRESS_AND_PORT_STABLE):
+ logging.debug('%s not a candidate: changed address/port recently (%s)',
+ self._fpr, self._data['last_changed_address_or_port'])
+ return False
+ if self._running < CUTOFF_RUNNING:
+ logging.debug('%s not a candidate: running avg too low (%lf)',
+ self._fpr, self._running)
+ return False
+ if self._v2dir < CUTOFF_V2DIR:
+ logging.debug('%s not a candidate: v2dir avg too low (%lf)',
+ self._fpr, self._v2dir)
+ return False
+ if self._badexit is not None and self._badexit > PERMITTED_BADEXIT:
+ logging.debug('%s not a candidate: badexit avg too high (%lf)',
+ self._fpr, self._badexit)
+ return False
+ # if the relay doesn't report a version, also exclude the relay
+ if (not self._data.has_key('recommended_version')
+ or not self._data['recommended_version']):
+ return False
+ if self._guard < CUTOFF_GUARD:
+ logging.debug('%s not a candidate: guard avg too low (%lf)',
+ self._fpr, self._guard)
+ return False
+ return True
+
+ def is_in_whitelist(self, relaylist):
+ """ A fallback matches if each key in the whitelist line matches:
+ ipv4
+ dirport
+ orport
+ id
+ ipv6 address and port (if present)
+ If the fallback has an ipv6 key, the whitelist line must also have
+ it, and vice versa, otherwise they don't match. """
+ for entry in relaylist:
+ if entry['ipv4'] != self.dirip:
+ continue
+ if int(entry['dirport']) != self.dirport:
+ continue
+ if int(entry['orport']) != self.orport:
+ continue
+ if entry['id'] != self._fpr:
+ continue
+ if (entry.has_key('ipv6')
+ and self.ipv6addr is not None and self.ipv6orport is not None):
+ # if both entry and fallback have an ipv6 address, compare them
+ if entry['ipv6'] != self.ipv6addr + ':' + self.ipv6orport:
+ continue
+ # if the fallback has an IPv6 address but the whitelist entry
+ # doesn't, or vice versa, the whitelist entry doesn't match
+ elif entry.has_key('ipv6') and self.ipv6addr is None:
+ continue
+ elif not entry.has_key('ipv6') and self.ipv6addr is not None:
+ continue
+ return True
+ return False
+
+ def is_in_blacklist(self, relaylist):
+ """ A fallback matches a blacklist line if a sufficiently specific group
+ of attributes matches:
+ ipv4 & dirport
+ ipv4 & orport
+ id
+ ipv6 & dirport
+ ipv6 & ipv6 orport
+ If the fallback and the blacklist line both have an ipv6 key,
+ their values will be compared, otherwise, they will be ignored.
+ If there is no dirport and no orport, the entry matches all relays on
+ that ip. """
+ for entry in relaylist:
+ for key in entry:
+ value = entry[key]
+ if key == 'ipv4' and value == self.dirip:
+ # if the dirport is present, check it too
+ if entry.has_key('dirport'):
+ if int(entry['dirport']) == self.dirport:
+ return True
+ # if the orport is present, check it too
+ elif entry.has_key('orport'):
+ if int(entry['orport']) == self.orport:
+ return True
+ else:
+ return True
+ if key == 'id' and value == self._fpr:
+ return True
+ if (key == 'ipv6'
+ and self.ipv6addr is not None and self.ipv6orport is not None):
+ # if both entry and fallback have an ipv6 address, compare them,
+ # otherwise, disregard ipv6 addresses
+ if value == self.ipv6addr + ':' + self.ipv6orport:
+ # if the dirport is present, check it too
+ if entry.has_key('dirport'):
+ if int(entry['dirport']) == self.dirport:
+ return True
+ # if the orport is present, check it too
+ elif entry.has_key('orport'):
+ if int(entry['orport']) == self.orport:
+ return True
+ else:
+ return True
+ return False
+
+ def is_exit(self):
+ return 'Exit' in self._data['flags']
+
+ def is_guard(self):
+ return 'Guard' in self._data['flags']
+
+ def fallback_weight_fraction(self, total_weight):
+ return float(self._data['consensus_weight']) / total_weight
+
+ # return the original consensus weight, if it exists,
+ # or, if not, return the consensus weight
+ def original_consensus_weight(self):
+ if self._data.has_key('original_consensus_weight'):
+ return self._data['original_consensus_weight']
+ else:
+ return self._data['consensus_weight']
+
+ def original_fallback_weight_fraction(self, total_weight):
+ return float(self.original_consensus_weight()) / total_weight
+
+ def fallbackdir_line(self, total_weight, original_total_weight):
+ # /*
+ # nickname
+ # flags
+ # weight / total (percentage)
+ # [original weight / original total (original percentage)]
+ # [contact]
+ # */
+ # "address:dirport orport=port id=fingerprint"
+ # "[ipv6=addr:orport]"
+ # "weight=num",
+ # Multiline C comment
+ s = '/*'
+ s += '\n'
+ s += cleanse_c_multiline_comment(self._data['nickname'])
+ s += '\n'
+ s += 'Flags: '
+ s += cleanse_c_multiline_comment(' '.join(sorted(self._data['flags'])))
+ s += '\n'
+ weight = self._data['consensus_weight']
+ percent_weight = self.fallback_weight_fraction(total_weight)*100
+ s += 'Fallback Weight: %d / %d (%.3f%%)'%(weight, total_weight,
+ percent_weight)
+ s += '\n'
+ o_weight = self.original_consensus_weight()
+ if o_weight != weight:
+ o_percent_weight = self.original_fallback_weight_fraction(
+ original_total_weight)*100
+ s += 'Consensus Weight: %d / %d (%.3f%%)'%(o_weight,
+ original_total_weight,
+ o_percent_weight)
+ s += '\n'
+ if self._data['contact'] is not None:
+ s += cleanse_c_multiline_comment(self._data['contact'])
+ s += '\n'
+ s += '*/'
+ s += '\n'
+ # Multi-Line C string with trailing comma (part of a string list)
+ # This makes it easier to diff the file, and remove IPv6 lines using grep
+ # Integers don't need escaping
+ s += '"%s orport=%d id=%s"'%(
+ cleanse_c_string(self._data['dir_address']),
+ self.orport,
+ cleanse_c_string(self._fpr))
+ s += '\n'
+ if self.ipv6addr is not None:
+ s += '" ipv6=%s:%s"'%(
+ cleanse_c_string(self.ipv6addr), cleanse_c_string(self.ipv6orport))
+ s += '\n'
+ s += '" weight=%d",'%(weight)
+ return s
+
+## Fallback Candidate List Class
+
+class CandidateList(dict):
+ def __init__(self):
+ pass
+
+ def _add_relay(self, details):
+ if not 'dir_address' in details: return
+ c = Candidate(details)
+ self[ c.get_fingerprint() ] = c
+
+ def _add_uptime(self, uptime):
+ try:
+ fpr = uptime['fingerprint']
+ except KeyError:
+ raise Exception("Document has no fingerprint field.")
+
+ try:
+ c = self[fpr]
+ except KeyError:
+ logging.debug('Got unknown relay %s in uptime document.'%(fpr,))
+ return
+
+ c.add_uptime(uptime)
+
+ def _add_details(self):
+ logging.debug('Loading details document.')
+ d = fetch('details',
+ fields=('fingerprint,nickname,contact,last_changed_address_or_port,' +
+ 'consensus_weight,or_addresses,dir_address,' +
+ 'recommended_version,flags'))
+ logging.debug('Loading details document done.')
+
+ if not 'relays' in d: raise Exception("No relays found in document.")
+
+ for r in d['relays']: self._add_relay(r)
+
+ def _add_uptimes(self):
+ logging.debug('Loading uptime document.')
+ d = fetch('uptime')
+ logging.debug('Loading uptime document done.')
+
+ if not 'relays' in d: raise Exception("No relays found in document.")
+ for r in d['relays']: self._add_uptime(r)
+
+ def add_relays(self):
+ self._add_details()
+ self._add_uptimes()
+
+ def count_guards(self):
+ guard_count = 0
+ for fpr in self.keys():
+ if self[fpr].is_guard():
+ guard_count += 1
+ return guard_count
+
+ # Find fallbacks that fit the uptime, stability, and flags criteria
+ def compute_fallbacks(self):
+ self.fallbacks = map(lambda x: self[x],
+ sorted(
+ filter(lambda x: self[x].is_candidate(),
+ self.keys()),
+ key=lambda x: self[x]._data['consensus_weight'],
+ reverse=True)
+ )
+
+ @staticmethod
+ def load_relaylist(file_name):
+ """ Read each line in the file, and parse it like a FallbackDir line:
+ an IPv4 address and optional port:
+ <IPv4 address>:<port>
+ which are parsed into dictionary entries:
+ ipv4=<IPv4 address>
+ dirport=<port>
+ followed by a series of key=value entries:
+ orport=<port>
+ id=<fingerprint>
+ ipv6=<IPv6 address>:<IPv6 orport>
+ each line's key/value pairs are placed in a dictonary,
+ (of string -> string key/value pairs),
+ and these dictionaries are placed in an array.
+ comments start with # and are ignored """
+ relaylist = []
+ file_data = read_from_file(file_name, MAX_LIST_FILE_SIZE)
+ if file_data is None:
+ return relaylist
+ for line in file_data.split('\n'):
+ relay_entry = {}
+ # ignore comments
+ line_comment_split = line.split('#')
+ line = line_comment_split[0]
+ # cleanup whitespace
+ line = cleanse_whitespace(line)
+ line = line.strip()
+ if len(line) == 0:
+ continue
+ for item in line.split(' '):
+ item = item.strip()
+ if len(item) == 0:
+ continue
+ key_value_split = item.split('=')
+ kvl = len(key_value_split)
+ if kvl < 1 or kvl > 2:
+ print '#error Bad %s item: %s, format is key=value.'%(
+ file_name, item)
+ if kvl == 1:
+ # assume that entries without a key are the ipv4 address,
+ # perhaps with a dirport
+ ipv4_maybe_dirport = key_value_split[0]
+ ipv4_maybe_dirport_split = ipv4_maybe_dirport.split(':')
+ dirl = len(ipv4_maybe_dirport_split)
+ if dirl < 1 or dirl > 2:
+ print '#error Bad %s IPv4 item: %s, format is ipv4:port.'%(
+ file_name, item)
+ if dirl >= 1:
+ relay_entry['ipv4'] = ipv4_maybe_dirport_split[0]
+ if dirl == 2:
+ relay_entry['dirport'] = ipv4_maybe_dirport_split[1]
+ elif kvl == 2:
+ relay_entry[key_value_split[0]] = key_value_split[1]
+ relaylist.append(relay_entry)
+ return relaylist
+
+ # apply the fallback whitelist and blacklist
+ def apply_filter_lists(self):
+ excluded_count = 0
+ logging.debug('Applying whitelist and blacklist.')
+ # parse the whitelist and blacklist
+ whitelist = self.load_relaylist(WHITELIST_FILE_NAME)
+ blacklist = self.load_relaylist(BLACKLIST_FILE_NAME)
+ filtered_fallbacks = []
+ for f in self.fallbacks:
+ in_whitelist = f.is_in_whitelist(whitelist)
+ in_blacklist = f.is_in_blacklist(blacklist)
+ if in_whitelist and in_blacklist:
+ if BLACKLIST_EXCLUDES_WHITELIST_ENTRIES:
+ # exclude
+ excluded_count += 1
+ logging.debug('Excluding %s: in both blacklist and whitelist.' %
+ f._fpr)
+ else:
+ # include
+ filtered_fallbacks.append(f)
+ elif in_whitelist:
+ # include
+ filtered_fallbacks.append(f)
+ elif in_blacklist:
+ # exclude
+ excluded_count += 1
+ logging.debug('Excluding %s: in blacklist.' %
+ f._fpr)
+ else:
+ if INCLUDE_UNLISTED_ENTRIES:
+ # include
+ filtered_fallbacks.append(f)
+ else:
+ # exclude
+ excluded_count += 1
+ logging.debug('Excluding %s: in neither blacklist nor whitelist.' %
+ f._fpr)
+ self.fallbacks = filtered_fallbacks
+ return excluded_count
+
+ @staticmethod
+ def summarise_filters(initial_count, excluded_count):
+ return '/* Whitelist & blacklist excluded %d of %d candidates. */'%(
+ excluded_count, initial_count)
+
+ # Remove any fallbacks in excess of MAX_FALLBACK_COUNT,
+ # starting with the lowest-weighted fallbacks
+ # total_weight should be recalculated after calling this
+ def exclude_excess_fallbacks(self):
+ if MAX_FALLBACK_COUNT is not None:
+ self.fallbacks = self.fallbacks[:MAX_FALLBACK_COUNT]
+
+ # Clamp the weight of all fallbacks to MAX_WEIGHT_FRACTION * total_weight
+ # fallbacks are kept sorted, but since excessive weights are reduced to
+ # the maximum acceptable weight, these relays end up with equal weights
+ def clamp_high_weight_fallbacks(self, total_weight):
+ if MAX_WEIGHT_FRACTION * len(self.fallbacks) < 1.0:
+ error_str = 'Max Fallback Weight %.3f%% is unachievable'%(
+ MAX_WEIGHT_FRACTION)
+ error_str += ' with Current Fallback Count %d.'%(len(self.fallbacks))
+ if STRICT_FALLBACK_WEIGHTS:
+ print '#error ' + error_str
+ else:
+ print '/* ' + error_str + ' */'
+ relays_clamped = 0
+ max_acceptable_weight = total_weight * MAX_WEIGHT_FRACTION
+ for f in self.fallbacks:
+ frac_weight = f.fallback_weight_fraction(total_weight)
+ if frac_weight > MAX_WEIGHT_FRACTION:
+ relays_clamped += 1
+ current_weight = f._data['consensus_weight']
+ # if we already have an original weight, keep it
+ if (not f._data.has_key('original_consensus_weight')
+ or f._data['original_consensus_weight'] == current_weight):
+ f._data['original_consensus_weight'] = current_weight
+ f._data['consensus_weight'] = max_acceptable_weight
+ return relays_clamped
+
+ # Remove any fallbacks with weights lower than MIN_WEIGHT_FRACTION
+ # total_weight should be recalculated after calling this
+ def exclude_low_weight_fallbacks(self, total_weight):
+ self.fallbacks = filter(
+ lambda x:
+ x.fallback_weight_fraction(total_weight) >= MIN_WEIGHT_FRACTION,
+ self.fallbacks)
+
+ def fallback_weight_total(self):
+ return sum(f._data['consensus_weight'] for f in self.fallbacks)
+
+ def fallback_min_weight(self):
+ if len(self.fallbacks) > 0:
+ return self.fallbacks[-1]
+ else:
+ return None
+
+ def fallback_max_weight(self):
+ if len(self.fallbacks) > 0:
+ return self.fallbacks[0]
+ else:
+ return None
+
+ def summarise_fallbacks(self, eligible_count, eligible_weight,
+ relays_clamped, clamped_weight,
+ guard_count, target_count, max_count):
+ # Report:
+ # the number of fallback directories (with min & max limits);
+ # #error if below minimum count
+ # the total weight, min & max fallback proportions
+ # #error if outside max weight proportion
+ # Multiline C comment with #error if things go bad
+ s = '/*'
+ s += '\n'
+ s += 'Fallback Directory Summary'
+ s += '\n'
+ # Integers don't need escaping in C comments
+ fallback_count = len(self.fallbacks)
+ if FALLBACK_PROPORTION_OF_GUARDS is None:
+ fallback_proportion = ''
+ else:
+ fallback_proportion = ' (%d * %f)'%(guard_count,
+ FALLBACK_PROPORTION_OF_GUARDS)
+ s += 'Final Count: %d (Eligible %d, Usable %d, Target %d%s'%(
+ min(max_count, fallback_count),
+ eligible_count,
+ fallback_count,
+ target_count,
+ fallback_proportion)
+ if MAX_FALLBACK_COUNT is not None:
+ s += ', Clamped to %d'%(MAX_FALLBACK_COUNT)
+ s += ')\n'
+ if fallback_count < MIN_FALLBACK_COUNT:
+ s += '*/'
+ s += '\n'
+ # We must have a minimum number of fallbacks so they are always
+ # reachable, and are in diverse locations
+ s += '#error Fallback Count %d is too low. '%(fallback_count)
+ s += 'Must be at least %d for diversity. '%(MIN_FALLBACK_COUNT)
+ s += 'Try adding entries to the whitelist, '
+ s += 'or setting INCLUDE_UNLISTED_ENTRIES = True.'
+ s += '\n'
+ s += '/*'
+ s += '\n'
+ total_weight = self.fallback_weight_total()
+ min_fb = self.fallback_min_weight()
+ min_weight = min_fb._data['consensus_weight']
+ min_percent = min_fb.fallback_weight_fraction(total_weight)*100.0
+ max_fb = self.fallback_max_weight()
+ max_weight = max_fb._data['consensus_weight']
+ max_frac = max_fb.fallback_weight_fraction(total_weight)
+ max_percent = max_frac*100.0
+ s += 'Final Weight: %d (Eligible %d)'%(total_weight, eligible_weight)
+ s += '\n'
+ s += 'Max Weight: %d (%.3f%%) (Clamped to %.3f%%)'%(
+ max_weight,
+ max_percent,
+ TARGET_MAX_WEIGHT_FRACTION*100)
+ s += '\n'
+ s += 'Min Weight: %d (%.3f%%) (Clamped to %.3f%%)'%(
+ min_weight,
+ min_percent,
+ MIN_WEIGHT_FRACTION*100)
+ s += '\n'
+ if eligible_count != fallback_count:
+ s += 'Excluded: %d (Clamped, Below Target, or Low Weight)'%(
+ eligible_count - fallback_count)
+ s += '\n'
+ if relays_clamped > 0:
+ s += 'Clamped: %d (%.3f%%) Excess Weight, '%(
+ clamped_weight,
+ (100.0 * clamped_weight) / total_weight)
+ s += '%d High Weight Fallbacks (%.1f%%)'%(
+ relays_clamped,
+ (100.0 * relays_clamped) / fallback_count)
+ s += '\n'
+ s += '*/'
+ if max_frac > TARGET_MAX_WEIGHT_FRACTION:
+ s += '\n'
+ # We must restrict the maximum fallback weight, so an adversary
+ # at or near the fallback doesn't see too many clients
+ error_str = 'Max Fallback Weight %.3f%% is too high. '%(max_frac*100)
+ error_str += 'Must be at most %.3f%% for client anonymity.'%(
+ TARGET_MAX_WEIGHT_FRACTION*100)
+ if STRICT_FALLBACK_WEIGHTS:
+ s += '#error ' + error_str
+ else:
+ s += '/* ' + error_str + ' */'
+ return s
+
+## Main Function
+
+def list_fallbacks():
+ """ Fetches required onionoo documents and evaluates the
+ fallback directory criteria for each of the relays """
+
+ candidates = CandidateList()
+ candidates.add_relays()
+
+ guard_count = candidates.count_guards()
+ if FALLBACK_PROPORTION_OF_GUARDS is None:
+ target_count = guard_count
+ else:
+ target_count = int(guard_count * FALLBACK_PROPORTION_OF_GUARDS)
+ # the maximum number of fallbacks is the least of:
+ # - the target fallback count (FALLBACK_PROPORTION_OF_GUARDS * guard count)
+ # - the maximum fallback count (MAX_FALLBACK_COUNT)
+ if MAX_FALLBACK_COUNT is None:
+ max_count = guard_count
+ else:
+ max_count = min(target_count, MAX_FALLBACK_COUNT)
+
+ candidates.compute_fallbacks()
+
+ initial_count = len(candidates.fallbacks)
+ excluded_count = candidates.apply_filter_lists()
+ print candidates.summarise_filters(initial_count, excluded_count)
+
+ eligible_count = len(candidates.fallbacks)
+ eligible_weight = candidates.fallback_weight_total()
+
+ # print the raw fallback list
+ #total_weight = candidates.fallback_weight_total()
+ #for x in candidates.fallbacks:
+ # print x.fallbackdir_line(total_weight, total_weight)
+
+ # When candidates are excluded, total_weight decreases, and
+ # the proportional weight of other candidates increases.
+ candidates.exclude_excess_fallbacks()
+ total_weight = candidates.fallback_weight_total()
+
+ # When candidates are reweighted, total_weight decreases, and
+ # the proportional weight of other candidates increases.
+ # Previously low-weight candidates might obtain sufficient proportional
+ # weights to be included.
+ # Save the weight at which we reweighted fallbacks for the summary.
+ pre_clamp_total_weight = total_weight
+ relays_clamped = candidates.clamp_high_weight_fallbacks(total_weight)
+
+ # When candidates are excluded, total_weight decreases, and
+ # the proportional weight of other candidates increases.
+ # No new low weight candidates will be created during exclusions.
+ # However, high weight candidates may increase over the maximum proportion.
+ # This should not be an issue, except in pathological cases.
+ candidates.exclude_low_weight_fallbacks(total_weight)
+ total_weight = candidates.fallback_weight_total()
+
+ # check we haven't exceeded TARGET_MAX_WEIGHT_FRACTION
+ # since reweighting preserves the orginal sort order,
+ # the maximum weights will be at the head of the list
+ if len(candidates.fallbacks) > 0:
+ max_weight_fb = candidates.fallback_max_weight()
+ max_weight = max_weight_fb.fallback_weight_fraction(total_weight)
+ if max_weight > TARGET_MAX_WEIGHT_FRACTION:
+ error_str = 'Maximum fallback weight: %.3f%% exceeds target %.3f%%. '%(
+ max_weight*100.0,
+ TARGET_MAX_WEIGHT_FRACTION*100.0)
+ error_str += 'Try decreasing REWEIGHTING_FUDGE_FACTOR.'
+ if STRICT_FALLBACK_WEIGHTS:
+ print '#error ' + error_str
+ else:
+ print '/* ' + error_str + ' */'
+
+ print candidates.summarise_fallbacks(eligible_count, eligible_weight,
+ relays_clamped,
+ pre_clamp_total_weight - total_weight,
+ guard_count, target_count, max_count)
+ else:
+ print '/* No Fallbacks met criteria */'
+
+ for s in fetch_source_list():
+ print describe_fetch_source(s)
+
+ for x in candidates.fallbacks[:max_count]:
+ print x.fallbackdir_line(total_weight, pre_clamp_total_weight)
+ #print json.dumps(candidates[x]._data, sort_keys=True, indent=4,
+ # separators=(',', ': '), default=json_util.default)
+
+if __name__ == "__main__":
+ list_fallbacks()
diff --git a/src/common/address.c b/src/common/address.c
index cfa8fd1dca..b41d75e509 100644
--- a/src/common/address.c
+++ b/src/common/address.c
@@ -148,7 +148,9 @@ tor_addr_make_af_unix(tor_addr_t *a)
}
/** Set the tor_addr_t in <b>a</b> to contain the socket address contained in
- * <b>sa</b>. Return 0 on success and -1 on failure. */
+ * <b>sa</b>. IF <b>port_out</b> is non-NULL and <b>sa</b> contains a port,
+ * set *<b>port_out</b> to that port. Return 0 on success and -1 on
+ * failure. */
int
tor_addr_from_sockaddr(tor_addr_t *a, const struct sockaddr *sa,
uint16_t *port_out)
@@ -908,6 +910,59 @@ tor_addr_is_loopback(const tor_addr_t *addr)
}
}
+/* Is addr valid?
+ * Checks that addr is non-NULL and not tor_addr_is_null().
+ * If for_listening is true, IPv4 addr 0.0.0.0 is allowed.
+ * It means "bind to all addresses on the local machine". */
+int
+tor_addr_is_valid(const tor_addr_t *addr, int for_listening)
+{
+ /* NULL addresses are invalid regardless of for_listening */
+ if (addr == NULL) {
+ return 0;
+ }
+
+ /* Only allow IPv4 0.0.0.0 for_listening. */
+ if (for_listening && addr->family == AF_INET
+ && tor_addr_to_ipv4h(addr) == 0) {
+ return 1;
+ }
+
+ /* Otherwise, the address is valid if it's not tor_addr_is_null() */
+ return !tor_addr_is_null(addr);
+}
+
+/* Is the network-order IPv4 address v4n_addr valid?
+ * Checks that addr is not zero.
+ * Except if for_listening is true, where IPv4 addr 0.0.0.0 is allowed. */
+int
+tor_addr_is_valid_ipv4n(uint32_t v4n_addr, int for_listening)
+{
+ /* Any IPv4 address is valid with for_listening. */
+ if (for_listening) {
+ return 1;
+ }
+
+ /* Otherwise, zero addresses are invalid. */
+ return v4n_addr != 0;
+}
+
+/* Is port valid?
+ * Checks that port is not 0.
+ * Except if for_listening is true, where port 0 is allowed.
+ * It means "OS chooses a port". */
+int
+tor_port_is_valid(uint16_t port, int for_listening)
+{
+ /* Any port value is valid with for_listening. */
+ if (for_listening) {
+ return 1;
+ }
+
+ /* Otherwise, zero ports are invalid. */
+ return port != 0;
+}
+
/** Set <b>dest</b> to equal the IPv4 address in <b>v4addr</b> (given in
* network order). */
void
@@ -1039,6 +1094,8 @@ tor_addr_compare_masked(const tor_addr_t *addr1, const tor_addr_t *addr2,
return r;
}
case AF_INET6: {
+ if (mbits > 128)
+ mbits = 128;
const uint8_t *a1 = tor_addr_to_in6_addr8(addr1);
const uint8_t *a2 = tor_addr_to_in6_addr8(addr2);
const int bytes = mbits >> 3;
@@ -1272,7 +1329,7 @@ typedef ULONG (WINAPI *GetAdaptersAddresses_fn_t)(
* into smartlist of <b>tor_addr_t</b> structures.
*/
STATIC smartlist_t *
-ifaddrs_to_smartlist(const struct ifaddrs *ifa)
+ifaddrs_to_smartlist(const struct ifaddrs *ifa, sa_family_t family)
{
smartlist_t *result = smartlist_new();
const struct ifaddrs *i;
@@ -1286,6 +1343,8 @@ ifaddrs_to_smartlist(const struct ifaddrs *ifa)
if (i->ifa_addr->sa_family != AF_INET &&
i->ifa_addr->sa_family != AF_INET6)
continue;
+ if (family != AF_UNSPEC && i->ifa_addr->sa_family != family)
+ continue;
if (tor_addr_from_sockaddr(&tmp, i->ifa_addr, NULL) < 0)
continue;
smartlist_add(result, tor_memdup(&tmp, sizeof(tmp)));
@@ -1299,7 +1358,7 @@ ifaddrs_to_smartlist(const struct ifaddrs *ifa)
* <b>tor_addr_t</b> structures.
*/
STATIC smartlist_t *
-get_interface_addresses_ifaddrs(int severity)
+get_interface_addresses_ifaddrs(int severity, sa_family_t family)
{
/* Most free Unixy systems provide getifaddrs, which gives us a linked list
@@ -1312,7 +1371,7 @@ get_interface_addresses_ifaddrs(int severity)
return NULL;
}
- result = ifaddrs_to_smartlist(ifa);
+ result = ifaddrs_to_smartlist(ifa, family);
freeifaddrs(ifa);
@@ -1354,7 +1413,7 @@ ip_adapter_addresses_to_smartlist(const IP_ADAPTER_ADDRESSES *addresses)
* <b>tor_addr_t</b> structures.
*/
STATIC smartlist_t *
-get_interface_addresses_win32(int severity)
+get_interface_addresses_win32(int severity, sa_family_t family)
{
/* Windows XP began to provide GetAdaptersAddresses. Windows 2000 had a
@@ -1388,7 +1447,7 @@ get_interface_addresses_win32(int severity)
/* Guess how much space we need. */
size = 15*1024;
addresses = tor_malloc(size);
- res = fn(AF_UNSPEC, FLAGS, NULL, addresses, &size);
+ res = fn(family, FLAGS, NULL, addresses, &size);
if (res == ERROR_BUFFER_OVERFLOW) {
/* we didn't guess that we needed enough space; try again */
tor_free(addresses);
@@ -1462,15 +1521,26 @@ ifreq_to_smartlist(char *buf, size_t buflen)
* <b>tor_addr_t</b> structures.
*/
STATIC smartlist_t *
-get_interface_addresses_ioctl(int severity)
+get_interface_addresses_ioctl(int severity, sa_family_t family)
{
/* Some older unixy systems make us use ioctl(SIOCGIFCONF) */
struct ifconf ifc;
int fd;
smartlist_t *result = NULL;
- /* This interface, AFAICT, only supports AF_INET addresses */
- fd = socket(AF_INET, SOCK_DGRAM, 0);
+ /* This interface, AFAICT, only supports AF_INET addresses,
+ * except on AIX. For Solaris, we could use SIOCGLIFCONF. */
+
+ /* Bail out if family is neither AF_INET nor AF_UNSPEC since
+ * ioctl() technique supports non-IPv4 interface addresses on
+ * a small number of niche systems only. If family is AF_UNSPEC,
+ * fall back to getting AF_INET addresses only. */
+ if (family == AF_UNSPEC)
+ family = AF_INET;
+ else if (family != AF_INET)
+ return NULL;
+
+ fd = socket(family, SOCK_DGRAM, 0);
if (fd < 0) {
tor_log(severity, LD_NET, "socket failed: %s", strerror(errno));
goto done;
@@ -1505,21 +1575,23 @@ get_interface_addresses_ioctl(int severity)
/** Try to ask our network interfaces what addresses they are bound to.
* Return a new smartlist of tor_addr_t on success, and NULL on failure.
* (An empty smartlist indicates that we successfully learned that we have no
- * addresses.) Log failure messages at <b>severity</b>. */
+ * addresses.) Log failure messages at <b>severity</b>. Only return the
+ * interface addresses of requested <b>family</b> and ignore the addresses
+ * of other address families. */
MOCK_IMPL(smartlist_t *,
-get_interface_addresses_raw,(int severity))
+get_interface_addresses_raw,(int severity, sa_family_t family))
{
smartlist_t *result = NULL;
#if defined(HAVE_IFADDRS_TO_SMARTLIST)
- if ((result = get_interface_addresses_ifaddrs(severity)))
+ if ((result = get_interface_addresses_ifaddrs(severity, family)))
return result;
#endif
#if defined(HAVE_IP_ADAPTER_TO_SMARTLIST)
- if ((result = get_interface_addresses_win32(severity)))
+ if ((result = get_interface_addresses_win32(severity, family)))
return result;
#endif
#if defined(HAVE_IFCONF_TO_SMARTLIST)
- if ((result = get_interface_addresses_ioctl(severity)))
+ if ((result = get_interface_addresses_ioctl(severity, family)))
return result;
#endif
(void) severity;
@@ -1527,7 +1599,7 @@ get_interface_addresses_raw,(int severity))
}
/** Return true iff <b>a</b> is a multicast address. */
-STATIC int
+int
tor_addr_is_multicast(const tor_addr_t *a)
{
sa_family_t family = tor_addr_family(a);
@@ -1544,8 +1616,9 @@ tor_addr_is_multicast(const tor_addr_t *a)
}
/** Attempt to retrieve IP address of current host by utilizing some
- * UDP socket trickery. Only look for address of given <b>family</b>.
- * Set result to *<b>addr</b>. Return 0 on success, -1 on failure.
+ * UDP socket trickery. Only look for address of given <b>family</b>
+ * (only AF_INET and AF_INET6 are supported). Set result to *<b>addr</b>.
+ * Return 0 on success, -1 on failure.
*/
MOCK_IMPL(int,
get_interface_address6_via_udp_socket_hack,(int severity,
@@ -1683,15 +1756,9 @@ MOCK_IMPL(smartlist_t *,get_interface_address6_list,(int severity,
tor_addr_t addr;
/* Try to do this the smart way if possible. */
- if ((addrs = get_interface_addresses_raw(severity))) {
+ if ((addrs = get_interface_addresses_raw(severity, family))) {
SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a)
{
- if (family != AF_UNSPEC && family != tor_addr_family(a)) {
- SMARTLIST_DEL_CURRENT(addrs, a);
- tor_free(a);
- continue;
- }
-
if (tor_addr_is_loopback(a) ||
tor_addr_is_multicast(a)) {
SMARTLIST_DEL_CURRENT(addrs, a);
@@ -1717,15 +1784,27 @@ MOCK_IMPL(smartlist_t *,get_interface_address6_list,(int severity,
}
/* Okay, the smart way is out. */
- if (get_interface_address6_via_udp_socket_hack(severity,family,&addr))
- return smartlist_new();
- if (!include_internal && tor_addr_is_internal(&addr, 0)) {
- return smartlist_new();
- } else {
- addrs = smartlist_new();
- smartlist_add(addrs, tor_dup_addr(&addr));
- return addrs;
+ addrs = smartlist_new();
+
+ if (family == AF_INET || family == AF_UNSPEC) {
+ if (get_interface_address6_via_udp_socket_hack(severity,AF_INET,
+ &addr) == 0) {
+ if (include_internal || !tor_addr_is_internal(&addr, 0)) {
+ smartlist_add(addrs, tor_dup_addr(&addr));
+ }
+ }
}
+
+ if (family == AF_INET6 || family == AF_UNSPEC) {
+ if (get_interface_address6_via_udp_socket_hack(severity,AF_INET6,
+ &addr) == 0) {
+ if (include_internal || !tor_addr_is_internal(&addr, 0)) {
+ smartlist_add(addrs, tor_dup_addr(&addr));
+ }
+ }
+ }
+
+ return addrs;
}
/* ======
@@ -1781,7 +1860,7 @@ tor_addr_port_parse(int severity, const char *addrport,
}
/** Given an address of the form "host[:port]", try to divide it into its host
- * ane port portions, setting *<b>address_out</b> to a newly allocated string
+ * and port portions, setting *<b>address_out</b> to a newly allocated string
* holding the address portion and *<b>port_out</b> to the port (or 0 if no
* port is given). Return 0 on success, -1 on failure. */
int
diff --git a/src/common/address.h b/src/common/address.h
index d2841e1c9d..558eb52b35 100644
--- a/src/common/address.h
+++ b/src/common/address.h
@@ -73,13 +73,13 @@ typedef struct tor_addr_port_t
#define TOR_ADDR_NULL {AF_UNSPEC, {0}}
-static INLINE const struct in6_addr *tor_addr_to_in6(const tor_addr_t *a);
-static INLINE uint32_t tor_addr_to_ipv4n(const tor_addr_t *a);
-static INLINE uint32_t tor_addr_to_ipv4h(const tor_addr_t *a);
-static INLINE uint32_t tor_addr_to_mapped_ipv4h(const tor_addr_t *a);
-static INLINE sa_family_t tor_addr_family(const tor_addr_t *a);
-static INLINE const struct in_addr *tor_addr_to_in(const tor_addr_t *a);
-static INLINE int tor_addr_eq_ipv4h(const tor_addr_t *a, uint32_t u);
+static inline const struct in6_addr *tor_addr_to_in6(const tor_addr_t *a);
+static inline uint32_t tor_addr_to_ipv4n(const tor_addr_t *a);
+static inline uint32_t tor_addr_to_ipv4h(const tor_addr_t *a);
+static inline uint32_t tor_addr_to_mapped_ipv4h(const tor_addr_t *a);
+static inline sa_family_t tor_addr_family(const tor_addr_t *a);
+static inline const struct in_addr *tor_addr_to_in(const tor_addr_t *a);
+static inline int tor_addr_eq_ipv4h(const tor_addr_t *a, uint32_t u);
socklen_t tor_addr_to_sockaddr(const tor_addr_t *a, uint16_t port,
struct sockaddr *sa_out, socklen_t len);
@@ -91,7 +91,7 @@ char *tor_sockaddr_to_str(const struct sockaddr *sa);
/** Return an in6_addr* equivalent to <b>a</b>, or NULL if <b>a</b> is not
* an IPv6 address. */
-static INLINE const struct in6_addr *
+static inline const struct in6_addr *
tor_addr_to_in6(const tor_addr_t *a)
{
return a->family == AF_INET6 ? &a->addr.in6_addr : NULL;
@@ -115,14 +115,14 @@ tor_addr_to_in6(const tor_addr_t *a)
/** Return an IPv4 address in network order for <b>a</b>, or 0 if
* <b>a</b> is not an IPv4 address. */
-static INLINE uint32_t
+static inline uint32_t
tor_addr_to_ipv4n(const tor_addr_t *a)
{
return a->family == AF_INET ? a->addr.in_addr.s_addr : 0;
}
/** Return an IPv4 address in host order for <b>a</b>, or 0 if
* <b>a</b> is not an IPv4 address. */
-static INLINE uint32_t
+static inline uint32_t
tor_addr_to_ipv4h(const tor_addr_t *a)
{
return ntohl(tor_addr_to_ipv4n(a));
@@ -131,7 +131,7 @@ tor_addr_to_ipv4h(const tor_addr_t *a)
* 0 if <b>a</b> is not an IPv6 address.
*
* (Does not check whether the address is really a mapped address */
-static INLINE uint32_t
+static inline uint32_t
tor_addr_to_mapped_ipv4h(const tor_addr_t *a)
{
if (a->family == AF_INET6) {
@@ -149,21 +149,21 @@ tor_addr_to_mapped_ipv4h(const tor_addr_t *a)
}
/** Return the address family of <b>a</b>. Possible values are:
* AF_INET6, AF_INET, AF_UNSPEC. */
-static INLINE sa_family_t
+static inline sa_family_t
tor_addr_family(const tor_addr_t *a)
{
return a->family;
}
/** Return an in_addr* equivalent to <b>a</b>, or NULL if <b>a</b> is not
* an IPv4 address. */
-static INLINE const struct in_addr *
+static inline const struct in_addr *
tor_addr_to_in(const tor_addr_t *a)
{
return a->family == AF_INET ? &a->addr.in_addr : NULL;
}
/** Return true iff <b>a</b> is an IPv4 address equal to the host-ordered
* address in <b>u</b>. */
-static INLINE int
+static inline int
tor_addr_eq_ipv4h(const tor_addr_t *a, uint32_t u)
{
return a->family == AF_INET ? (tor_addr_to_ipv4h(a) == u) : 0;
@@ -221,6 +221,7 @@ int tor_addr_is_internal_(const tor_addr_t *ip, int for_listening,
const char *filename, int lineno);
#define tor_addr_is_internal(addr, for_listening) \
tor_addr_is_internal_((addr), (for_listening), SHORT_FILE__, __LINE__)
+int tor_addr_is_multicast(const tor_addr_t *a);
/** Longest length that can be required for a reverse lookup name. */
/* 32 nybbles, 32 dots, 8 characters of "ip6.arpa", 1 NUL: 73 characters. */
@@ -266,6 +267,27 @@ void tor_addr_from_in6(tor_addr_t *dest, const struct in6_addr *in6);
int tor_addr_is_null(const tor_addr_t *addr);
int tor_addr_is_loopback(const tor_addr_t *addr);
+int tor_addr_is_valid(const tor_addr_t *addr, int for_listening);
+int tor_addr_is_valid_ipv4n(uint32_t v4n_addr, int for_listening);
+#define tor_addr_is_valid_ipv4h(v4h_addr, for_listening) \
+ tor_addr_is_valid_ipv4n(htonl(v4h_addr), (for_listening))
+int tor_port_is_valid(uint16_t port, int for_listening);
+/* Are addr and port both valid? */
+#define tor_addr_port_is_valid(addr, port, for_listening) \
+ (tor_addr_is_valid((addr), (for_listening)) && \
+ tor_port_is_valid((port), (for_listening)))
+/* Are ap->addr and ap->port both valid? */
+#define tor_addr_port_is_valid_ap(ap, for_listening) \
+ tor_addr_port_is_valid(&(ap)->addr, (ap)->port, (for_listening))
+/* Are the network-order v4addr and port both valid? */
+#define tor_addr_port_is_valid_ipv4n(v4n_addr, port, for_listening) \
+ (tor_addr_is_valid_ipv4n((v4n_addr), (for_listening)) && \
+ tor_port_is_valid((port), (for_listening)))
+/* Are the host-order v4addr and port both valid? */
+#define tor_addr_port_is_valid_ipv4h(v4h_addr, port, for_listening) \
+ (tor_addr_is_valid_ipv4h((v4h_addr), (for_listening)) && \
+ tor_port_is_valid((port), (for_listening)))
+
int tor_addr_port_split(int severity, const char *addrport,
char **address_out, uint16_t *port_out);
@@ -288,7 +310,7 @@ char *tor_dup_ip(uint32_t addr) ATTR_MALLOC;
MOCK_DECL(int,get_interface_address,(int severity, uint32_t *addr));
/** Free a smartlist of IP addresses returned by get_interface_address_list.
*/
-static INLINE void
+static inline void
free_interface_address_list(smartlist_t *addrs)
{
free_interface_address6_list(addrs);
@@ -301,7 +323,7 @@ free_interface_address_list(smartlist_t *addrs)
* Returns NULL on failure.
* Use free_interface_address_list to free the returned list.
*/
-static INLINE smartlist_t *
+static inline smartlist_t *
get_interface_address_list(int severity, int include_internal)
{
return get_interface_address6_list(severity, AF_INET, include_internal);
@@ -310,27 +332,31 @@ get_interface_address_list(int severity, int include_internal)
tor_addr_port_t *tor_addr_port_new(const tor_addr_t *addr, uint16_t port);
#ifdef ADDRESS_PRIVATE
-MOCK_DECL(smartlist_t *,get_interface_addresses_raw,(int severity));
-STATIC int tor_addr_is_multicast(const tor_addr_t *a);
+MOCK_DECL(smartlist_t *,get_interface_addresses_raw,(int severity,
+ sa_family_t family));
MOCK_DECL(int,get_interface_address6_via_udp_socket_hack,(int severity,
sa_family_t family,
tor_addr_t *addr));
#ifdef HAVE_IFADDRS_TO_SMARTLIST
-STATIC smartlist_t *ifaddrs_to_smartlist(const struct ifaddrs *ifa);
-STATIC smartlist_t *get_interface_addresses_ifaddrs(int severity);
+STATIC smartlist_t *ifaddrs_to_smartlist(const struct ifaddrs *ifa,
+ sa_family_t family);
+STATIC smartlist_t *get_interface_addresses_ifaddrs(int severity,
+ sa_family_t family);
#endif
#ifdef HAVE_IP_ADAPTER_TO_SMARTLIST
STATIC smartlist_t *ip_adapter_addresses_to_smartlist(
const IP_ADAPTER_ADDRESSES *addresses);
-STATIC smartlist_t *get_interface_addresses_win32(int severity);
+STATIC smartlist_t *get_interface_addresses_win32(int severity,
+ sa_family_t family);
#endif
#ifdef HAVE_IFCONF_TO_SMARTLIST
STATIC smartlist_t *ifreq_to_smartlist(char *ifr,
size_t buflen);
-STATIC smartlist_t *get_interface_addresses_ioctl(int severity);
+STATIC smartlist_t *get_interface_addresses_ioctl(int severity,
+ sa_family_t family);
#endif
#endif // ADDRESS_PRIVATE
diff --git a/src/common/aes.c b/src/common/aes.c
index 5f2c3f2f03..fd2043372e 100644
--- a/src/common/aes.c
+++ b/src/common/aes.c
@@ -81,47 +81,34 @@
#ifdef USE_EVP_AES_CTR
-struct aes_cnt_cipher {
- EVP_CIPHER_CTX evp;
-};
+/* We don't actually define the struct here. */
aes_cnt_cipher_t *
aes_new_cipher(const char *key, const char *iv)
{
- aes_cnt_cipher_t *cipher;
- cipher = tor_malloc_zero(sizeof(aes_cnt_cipher_t));
- EVP_EncryptInit(&cipher->evp, EVP_aes_128_ctr(),
+ EVP_CIPHER_CTX *cipher = EVP_CIPHER_CTX_new();
+ EVP_EncryptInit(cipher, EVP_aes_128_ctr(),
(const unsigned char*)key, (const unsigned char *)iv);
- return cipher;
+ return (aes_cnt_cipher_t *) cipher;
}
void
-aes_cipher_free(aes_cnt_cipher_t *cipher)
+aes_cipher_free(aes_cnt_cipher_t *cipher_)
{
- if (!cipher)
+ if (!cipher_)
return;
- EVP_CIPHER_CTX_cleanup(&cipher->evp);
- memwipe(cipher, 0, sizeof(aes_cnt_cipher_t));
- tor_free(cipher);
-}
-void
-aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len,
- char *output)
-{
- int outl;
-
- tor_assert(len < INT_MAX);
-
- EVP_EncryptUpdate(&cipher->evp, (unsigned char*)output,
- &outl, (const unsigned char *)input, (int)len);
+ EVP_CIPHER_CTX *cipher = (EVP_CIPHER_CTX *) cipher_;
+ EVP_CIPHER_CTX_cleanup(cipher);
+ EVP_CIPHER_CTX_free(cipher);
}
void
-aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len)
+aes_crypt_inplace(aes_cnt_cipher_t *cipher_, char *data, size_t len)
{
int outl;
+ EVP_CIPHER_CTX *cipher = (EVP_CIPHER_CTX *) cipher_;
tor_assert(len < INT_MAX);
- EVP_EncryptUpdate(&cipher->evp, (unsigned char*)data,
+ EVP_EncryptUpdate(cipher, (unsigned char*)data,
&outl, (unsigned char*)data, (int)len);
}
int
@@ -182,10 +169,6 @@ struct aes_cnt_cipher {
* we're testing it or because we have hardware acceleration configured */
static int should_use_EVP = 0;
-/** True iff we have tested the counter-mode implementation and found that it
- * doesn't have the counter-mode bug from OpenSSL 1.0.0. */
-static int should_use_openssl_CTR = 0;
-
/** Check whether we should use the EVP interface for AES. If <b>force_val</b>
* is nonnegative, we use use EVP iff it is true. Otherwise, we use EVP
* if there is an engine enabled for aes-ecb. */
@@ -250,13 +233,9 @@ evaluate_ctr_for_aes(void)
if (fast_memneq(output, encrypt_zero, 16)) {
/* Counter mode is buggy */
- log_notice(LD_CRYPTO, "This OpenSSL has a buggy version of counter mode; "
- "not using it.");
- } else {
- /* Counter mode is okay */
- log_info(LD_CRYPTO, "This OpenSSL has a good implementation of counter "
- "mode; using it.");
- should_use_openssl_CTR = 1;
+ log_err(LD_CRYPTO, "This OpenSSL has a buggy version of counter mode; "
+ "quitting tor.");
+ exit(1);
}
return 0;
}
@@ -267,29 +246,6 @@ evaluate_ctr_for_aes(void)
#define COUNTER(c, n) ((c)->counter ## n)
#endif
-/**
- * Helper function: set <b>cipher</b>'s internal buffer to the encrypted
- * value of the current counter.
- */
-static INLINE void
-aes_fill_buf_(aes_cnt_cipher_t *cipher)
-{
- /* We don't currently use OpenSSL's counter mode implementation because:
- * 1) some versions have known bugs
- * 2) its attitude towards IVs is not our own
- * 3) changing the counter position was not trivial, last time I looked.
- * None of these issues are insurmountable in principle.
- */
-
- if (cipher->using_evp) {
- int outl=16, inl=16;
- EVP_EncryptUpdate(&cipher->key.evp, cipher->buf, &outl,
- cipher->ctr_buf.buf, inl);
- } else {
- AES_encrypt(cipher->ctr_buf.buf, cipher->buf, &cipher->key.aes);
- }
-}
-
static void aes_set_key(aes_cnt_cipher_t *cipher, const char *key,
int key_bits);
static void aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv);
@@ -342,10 +298,7 @@ aes_set_key(aes_cnt_cipher_t *cipher, const char *key, int key_bits)
cipher->pos = 0;
- if (should_use_openssl_CTR)
- memset(cipher->buf, 0, sizeof(cipher->buf));
- else
- aes_fill_buf_(cipher);
+ memset(cipher->buf, 0, sizeof(cipher->buf));
}
/** Release storage held by <b>cipher</b>
@@ -381,63 +334,6 @@ evp_block128_fn(const uint8_t in[16],
EVP_EncryptUpdate(ctx, out, &outl, in, inl);
}
-/** Encrypt <b>len</b> bytes from <b>input</b>, storing the result in
- * <b>output</b>. Uses the key in <b>cipher</b>, and advances the counter
- * by <b>len</b> bytes as it encrypts.
- */
-void
-aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len,
- char *output)
-{
- if (should_use_openssl_CTR) {
- if (cipher->using_evp) {
- /* In openssl 1.0.0, there's an if'd out EVP_aes_128_ctr in evp.h. If
- * it weren't disabled, it might be better just to use that.
- */
- CRYPTO_ctr128_encrypt((const unsigned char *)input,
- (unsigned char *)output,
- len,
- &cipher->key.evp,
- cipher->ctr_buf.buf,
- cipher->buf,
- &cipher->pos,
- evp_block128_fn);
- } else {
- AES_ctr128_encrypt((const unsigned char *)input,
- (unsigned char *)output,
- len,
- &cipher->key.aes,
- cipher->ctr_buf.buf,
- cipher->buf,
- &cipher->pos);
- }
- return;
- } else {
- int c = cipher->pos;
- if (PREDICT_UNLIKELY(!len)) return;
-
- while (1) {
- do {
- if (len-- == 0) { cipher->pos = c; return; }
- *(output++) = *(input++) ^ cipher->buf[c];
- } while (++c != 16);
- cipher->pos = c = 0;
- if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 0))) {
- if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 1))) {
- if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 2))) {
- ++COUNTER(cipher, 3);
- UPDATE_CTR_BUF(cipher, 3);
- }
- UPDATE_CTR_BUF(cipher, 2);
- }
- UPDATE_CTR_BUF(cipher, 1);
- }
- UPDATE_CTR_BUF(cipher, 0);
- aes_fill_buf_(cipher);
- }
- }
-}
-
/** Encrypt <b>len</b> bytes from <b>input</b>, storing the results in place.
* Uses the key in <b>cipher</b>, and advances the counter by <b>len</b> bytes
* as it encrypts.
@@ -445,32 +341,26 @@ aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len,
void
aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len)
{
- if (should_use_openssl_CTR) {
- aes_crypt(cipher, data, len, data);
- return;
+ if (cipher->using_evp) {
+ /* In openssl 1.0.0, there's an if'd out EVP_aes_128_ctr in evp.h. If
+ * it weren't disabled, it might be better just to use that.
+ */
+ CRYPTO_ctr128_encrypt((const unsigned char *)data,
+ (unsigned char *)data,
+ len,
+ &cipher->key.evp,
+ cipher->ctr_buf.buf,
+ cipher->buf,
+ &cipher->pos,
+ evp_block128_fn);
} else {
- int c = cipher->pos;
- if (PREDICT_UNLIKELY(!len)) return;
-
- while (1) {
- do {
- if (len-- == 0) { cipher->pos = c; return; }
- *(data++) ^= cipher->buf[c];
- } while (++c != 16);
- cipher->pos = c = 0;
- if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 0))) {
- if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 1))) {
- if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 2))) {
- ++COUNTER(cipher, 3);
- UPDATE_CTR_BUF(cipher, 3);
- }
- UPDATE_CTR_BUF(cipher, 2);
- }
- UPDATE_CTR_BUF(cipher, 1);
- }
- UPDATE_CTR_BUF(cipher, 0);
- aes_fill_buf_(cipher);
- }
+ AES_ctr128_encrypt((const unsigned char *)data,
+ (unsigned char *)data,
+ len,
+ &cipher->key.aes,
+ cipher->ctr_buf.buf,
+ cipher->buf,
+ &cipher->pos);
}
}
@@ -487,9 +377,6 @@ aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv)
#endif
cipher->pos = 0;
memcpy(cipher->ctr_buf.buf, iv, 16);
-
- if (!should_use_openssl_CTR)
- aes_fill_buf_(cipher);
}
#endif
diff --git a/src/common/aes.h b/src/common/aes.h
index df2f3aa65d..bd0456511b 100644
--- a/src/common/aes.h
+++ b/src/common/aes.h
@@ -13,13 +13,10 @@
* \brief Headers for aes.c
*/
-struct aes_cnt_cipher;
typedef struct aes_cnt_cipher aes_cnt_cipher_t;
aes_cnt_cipher_t* aes_new_cipher(const char *key, const char *iv);
void aes_cipher_free(aes_cnt_cipher_t *cipher);
-void aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len,
- char *output);
void aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len);
int evaluate_evp_for_aes(int force_value);
diff --git a/src/common/backtrace.c b/src/common/backtrace.c
index bed0442471..94de1eb5ee 100644
--- a/src/common/backtrace.c
+++ b/src/common/backtrace.c
@@ -215,9 +215,10 @@ int
configure_backtrace_handler(const char *tor_version)
{
tor_free(bt_version);
- if (!tor_version)
- tor_version = "";
- tor_asprintf(&bt_version, "Tor %s", tor_version);
+ if (tor_version)
+ tor_asprintf(&bt_version, "Tor %s", tor_version);
+ else
+ tor_asprintf(&bt_version, "Tor");
return install_bt_handler();
}
diff --git a/src/common/compat.c b/src/common/compat.c
index 7d72b4b7fd..87f56644cb 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -71,6 +71,9 @@
#ifdef HAVE_SYS_STATVFS_H
#include <sys/statvfs.h>
#endif
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/capability.h>
+#endif
#ifdef _WIN32
#include <conio.h>
@@ -573,14 +576,17 @@ tor_vasprintf(char **strp, const char *fmt, va_list args)
int len, r;
va_list tmp_args;
va_copy(tmp_args, args);
- len = vsnprintf(buf, sizeof(buf), fmt, tmp_args);
+ /* vsnprintf() was properly checked but tor_vsnprintf() available so
+ * why not use it? */
+ len = tor_vsnprintf(buf, sizeof(buf), fmt, tmp_args);
va_end(tmp_args);
if (len < (int)sizeof(buf)) {
*strp = tor_strdup(buf);
return len;
}
strp_tmp = tor_malloc(len+1);
- r = vsnprintf(strp_tmp, len+1, fmt, args);
+ /* use of tor_vsnprintf() will ensure string is null terminated */
+ r = tor_vsnprintf(strp_tmp, len+1, fmt, args);
if (r != len) {
tor_free(strp_tmp);
*strp = NULL;
@@ -714,7 +720,8 @@ strtok_helper(char *cp, const char *sep)
}
/** Implementation of strtok_r for platforms whose coders haven't figured out
- * how to write one. Hey guys! You can use this code here for free! */
+ * how to write one. Hey, retrograde libc developers! You can use this code
+ * here for free! */
char *
tor_strtok_r_impl(char *str, const char *sep, char **lasts)
{
@@ -1078,7 +1085,7 @@ static int n_sockets_open = 0;
static tor_mutex_t *socket_accounting_mutex = NULL;
/** Helper: acquire the socket accounting lock. */
-static INLINE void
+static inline void
socket_accounting_lock(void)
{
if (PREDICT_UNLIKELY(!socket_accounting_mutex))
@@ -1087,7 +1094,7 @@ socket_accounting_lock(void)
}
/** Helper: release the socket accounting lock. */
-static INLINE void
+static inline void
socket_accounting_unlock(void)
{
tor_mutex_release(socket_accounting_mutex);
@@ -1163,7 +1170,7 @@ tor_close_socket(tor_socket_t s)
#ifdef DEBUG_SOCKET_COUNTING
/** Helper: if DEBUG_SOCKET_COUNTING is enabled, remember that <b>s</b> is
* now an open socket. */
-static INLINE void
+static inline void
mark_socket_open(tor_socket_t s)
{
/* XXXX This bitarray business will NOT work on windows: sockets aren't
@@ -1486,6 +1493,20 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
}
#ifdef NEED_ERSATZ_SOCKETPAIR
+
+static inline socklen_t
+SIZEOF_SOCKADDR(int domain)
+{
+ switch (domain) {
+ case AF_INET:
+ return sizeof(struct sockaddr_in);
+ case AF_INET6:
+ return sizeof(struct sockaddr_in6);
+ default:
+ return 0;
+ }
+}
+
/**
* Helper used to implement socketpair on systems that lack it, by
* making a direct connection to localhost.
@@ -1501,13 +1522,21 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
tor_socket_t listener = TOR_INVALID_SOCKET;
tor_socket_t connector = TOR_INVALID_SOCKET;
tor_socket_t acceptor = TOR_INVALID_SOCKET;
- struct sockaddr_in listen_addr;
- struct sockaddr_in connect_addr;
+ tor_addr_t listen_tor_addr;
+ struct sockaddr_storage connect_addr_ss, listen_addr_ss;
+ struct sockaddr *listen_addr = (struct sockaddr *) &listen_addr_ss;
+ uint16_t listen_port = 0;
+ tor_addr_t connect_tor_addr;
+ uint16_t connect_port = 0;
+ struct sockaddr *connect_addr = (struct sockaddr *) &connect_addr_ss;
socklen_t size;
int saved_errno = -1;
+ int ersatz_domain = AF_INET;
- memset(&connect_addr, 0, sizeof(connect_addr));
- memset(&listen_addr, 0, sizeof(listen_addr));
+ memset(&connect_tor_addr, 0, sizeof(connect_tor_addr));
+ memset(&connect_addr_ss, 0, sizeof(connect_addr_ss));
+ memset(&listen_tor_addr, 0, sizeof(listen_tor_addr));
+ memset(&listen_addr_ss, 0, sizeof(listen_addr_ss));
if (protocol
#ifdef AF_UNIX
@@ -1524,47 +1553,71 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
return -EINVAL;
}
- listener = tor_open_socket(AF_INET, type, 0);
- if (!SOCKET_OK(listener))
- return -tor_socket_errno(-1);
- memset(&listen_addr, 0, sizeof(listen_addr));
- listen_addr.sin_family = AF_INET;
- listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- listen_addr.sin_port = 0; /* kernel chooses port. */
- if (bind(listener, (struct sockaddr *) &listen_addr, sizeof (listen_addr))
- == -1)
+ listener = tor_open_socket(ersatz_domain, type, 0);
+ if (!SOCKET_OK(listener)) {
+ int first_errno = tor_socket_errno(-1);
+ if (first_errno == SOCK_ERRNO(EPROTONOSUPPORT)
+ && ersatz_domain == AF_INET) {
+ /* Assume we're on an IPv6-only system */
+ ersatz_domain = AF_INET6;
+ listener = tor_open_socket(ersatz_domain, type, 0);
+ if (!SOCKET_OK(listener)) {
+ /* Keep the previous behaviour, which was to return the IPv4 error.
+ * (This may be less informative on IPv6-only systems.)
+ * XX/teor - is there a better way to decide which errno to return?
+ * (I doubt we care much either way, once there is an error.)
+ */
+ return -first_errno;
+ }
+ }
+ }
+ /* If there is no 127.0.0.1 or ::1, this will and must fail. Otherwise, we
+ * risk exposing a socketpair on a routable IP address. (Some BSD jails
+ * use a routable address for localhost. Fortunately, they have the real
+ * AF_UNIX socketpair.) */
+ if (ersatz_domain == AF_INET) {
+ tor_addr_from_ipv4h(&listen_tor_addr, INADDR_LOOPBACK);
+ } else {
+ tor_addr_parse(&listen_tor_addr, "[::1]");
+ }
+ tor_assert(tor_addr_is_loopback(&listen_tor_addr));
+ size = tor_addr_to_sockaddr(&listen_tor_addr,
+ 0 /* kernel chooses port. */,
+ listen_addr,
+ sizeof(listen_addr_ss));
+ if (bind(listener, listen_addr, size) == -1)
goto tidy_up_and_fail;
if (listen(listener, 1) == -1)
goto tidy_up_and_fail;
- connector = tor_open_socket(AF_INET, type, 0);
+ connector = tor_open_socket(ersatz_domain, type, 0);
if (!SOCKET_OK(connector))
goto tidy_up_and_fail;
/* We want to find out the port number to connect to. */
- size = sizeof(connect_addr);
- if (getsockname(listener, (struct sockaddr *) &connect_addr, &size) == -1)
+ size = sizeof(connect_addr_ss);
+ if (getsockname(listener, connect_addr, &size) == -1)
goto tidy_up_and_fail;
- if (size != sizeof (connect_addr))
+ if (size != SIZEOF_SOCKADDR (connect_addr->sa_family))
goto abort_tidy_up_and_fail;
- if (connect(connector, (struct sockaddr *) &connect_addr,
- sizeof(connect_addr)) == -1)
+ if (connect(connector, connect_addr, size) == -1)
goto tidy_up_and_fail;
- size = sizeof(listen_addr);
- acceptor = tor_accept_socket(listener,
- (struct sockaddr *) &listen_addr, &size);
+ size = sizeof(listen_addr_ss);
+ acceptor = tor_accept_socket(listener, listen_addr, &size);
if (!SOCKET_OK(acceptor))
goto tidy_up_and_fail;
- if (size != sizeof(listen_addr))
+ if (size != SIZEOF_SOCKADDR(listen_addr->sa_family))
goto abort_tidy_up_and_fail;
/* Now check we are talking to ourself by matching port and host on the
two sockets. */
- if (getsockname(connector, (struct sockaddr *) &connect_addr, &size) == -1)
+ if (getsockname(connector, connect_addr, &size) == -1)
goto tidy_up_and_fail;
- if (size != sizeof (connect_addr)
- || listen_addr.sin_family != connect_addr.sin_family
- || listen_addr.sin_addr.s_addr != connect_addr.sin_addr.s_addr
- || listen_addr.sin_port != connect_addr.sin_port) {
+ /* Set *_tor_addr and *_port to the address and port that was used */
+ tor_addr_from_sockaddr(&listen_tor_addr, listen_addr, &listen_port);
+ tor_addr_from_sockaddr(&connect_tor_addr, connect_addr, &connect_port);
+ if (size != SIZEOF_SOCKADDR (connect_addr->sa_family)
+ || tor_addr_compare(&listen_tor_addr, &connect_tor_addr, CMP_SEMANTIC)
+ || listen_port != connect_port) {
goto abort_tidy_up_and_fail;
}
tor_close_socket(listener);
@@ -1590,6 +1643,9 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
tor_close_socket(acceptor);
return -saved_errno;
}
+
+#undef SIZEOF_SOCKADDR
+
#endif
/* Return the maximum number of allowed sockets. */
@@ -1917,17 +1973,99 @@ tor_getpwuid(uid_t uid)
}
#endif
+/** Return true iff we were compiled with capability support, and capabilities
+ * seem to work. **/
+int
+have_capability_support(void)
+{
+#ifdef HAVE_LINUX_CAPABILITIES
+ cap_t caps = cap_get_proc();
+ if (caps == NULL)
+ return 0;
+ cap_free(caps);
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+#ifdef HAVE_LINUX_CAPABILITIES
+/** Helper. Drop all capabilities but a small set, and set PR_KEEPCAPS as
+ * appropriate.
+ *
+ * If pre_setuid, retain only CAP_NET_BIND_SERVICE, CAP_SETUID, and
+ * CAP_SETGID, and use PR_KEEPCAPS to ensure that capabilities persist across
+ * setuid().
+ *
+ * If not pre_setuid, retain only CAP_NET_BIND_SERVICE, and disable
+ * PR_KEEPCAPS.
+ *
+ * Return 0 on success, and -1 on failure.
+ */
+static int
+drop_capabilities(int pre_setuid)
+{
+ /* We keep these three capabilities, and these only, as we setuid.
+ * After we setuid, we drop all but the first. */
+ const cap_value_t caplist[] = {
+ CAP_NET_BIND_SERVICE, CAP_SETUID, CAP_SETGID
+ };
+ const char *where = pre_setuid ? "pre-setuid" : "post-setuid";
+ const int n_effective = pre_setuid ? 3 : 1;
+ const int n_permitted = pre_setuid ? 3 : 1;
+ const int n_inheritable = 1;
+ const int keepcaps = pre_setuid ? 1 : 0;
+
+ /* Sets whether we keep capabilities across a setuid. */
+ if (prctl(PR_SET_KEEPCAPS, keepcaps) < 0) {
+ log_warn(LD_CONFIG, "Unable to call prctl() %s: %s",
+ where, strerror(errno));
+ return -1;
+ }
+
+ cap_t caps = cap_get_proc();
+ if (!caps) {
+ log_warn(LD_CONFIG, "Unable to call cap_get_proc() %s: %s",
+ where, strerror(errno));
+ return -1;
+ }
+ cap_clear(caps);
+
+ cap_set_flag(caps, CAP_EFFECTIVE, n_effective, caplist, CAP_SET);
+ cap_set_flag(caps, CAP_PERMITTED, n_permitted, caplist, CAP_SET);
+ cap_set_flag(caps, CAP_INHERITABLE, n_inheritable, caplist, CAP_SET);
+
+ int r = cap_set_proc(caps);
+ cap_free(caps);
+ if (r < 0) {
+ log_warn(LD_CONFIG, "No permission to set capabilities %s: %s",
+ where, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
/** Call setuid and setgid to run as <b>user</b> and switch to their
* primary group. Return 0 on success. On failure, log and return -1.
+ *
+ * If SWITCH_ID_KEEP_BINDLOW is set in 'flags', try to use the capability
+ * system to retain the abilitity to bind low ports.
+ *
+ * If SWITCH_ID_WARN_IF_NO_CAPS is set in flags, also warn if we have
+ * don't have capability support.
*/
int
-switch_id(const char *user)
+switch_id(const char *user, const unsigned flags)
{
#ifndef _WIN32
const struct passwd *pw = NULL;
uid_t old_uid;
gid_t old_gid;
static int have_already_switched_id = 0;
+ const int keep_bindlow = !!(flags & SWITCH_ID_KEEP_BINDLOW);
+ const int warn_if_no_caps = !!(flags & SWITCH_ID_WARN_IF_NO_CAPS);
tor_assert(user);
@@ -1951,6 +2089,20 @@ switch_id(const char *user)
return -1;
}
+#ifdef HAVE_LINUX_CAPABILITIES
+ (void) warn_if_no_caps;
+ if (keep_bindlow) {
+ if (drop_capabilities(1))
+ return -1;
+ }
+#else
+ (void) keep_bindlow;
+ if (warn_if_no_caps) {
+ log_warn(LD_CONFIG, "KeepBindCapabilities set, but no capability support "
+ "on this system.");
+ }
+#endif
+
/* Properly switch egid,gid,euid,uid here or bail out */
if (setgroups(1, &pw->pw_gid)) {
log_warn(LD_GENERAL, "Error setting groups to gid %d: \"%s\".",
@@ -2004,6 +2156,12 @@ switch_id(const char *user)
/* We've properly switched egid, gid, euid, uid, and supplementary groups if
* we're here. */
+#ifdef HAVE_LINUX_CAPABILITIES
+ if (keep_bindlow) {
+ if (drop_capabilities(0))
+ return -1;
+ }
+#endif
#if !defined(CYGWIN) && !defined(__CYGWIN__)
/* If we tried to drop privilege to a group/user other than root, attempt to
@@ -2051,9 +2209,9 @@ switch_id(const char *user)
#else
(void)user;
+ (void)flags;
- log_warn(LD_CONFIG,
- "User specified but switching users is unsupported on your OS.");
+ log_warn(LD_CONFIG, "Switching users is unsupported on your OS.");
return -1;
#endif
}
@@ -2537,8 +2695,7 @@ static int uname_result_is_set = 0;
/** Return a pointer to a description of our platform.
*/
-const char *
-get_uname(void)
+MOCK_IMPL(const char *, get_uname, (void))
{
#ifdef HAVE_UNAME
struct utsname u;
diff --git a/src/common/compat.h b/src/common/compat.h
index 66cc079259..c5df4dc20c 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -84,9 +84,7 @@
/* inline is __inline on windows. */
#ifdef _WIN32
-#define INLINE __inline
-#else
-#define INLINE inline
+#define inline __inline
#endif
/* Try to get a reasonable __func__ substitute in place. */
@@ -127,6 +125,7 @@
#define ATTR_CONST __attribute__((const))
#define ATTR_MALLOC __attribute__((malloc))
#define ATTR_NORETURN __attribute__((noreturn))
+#define ATTR_WUR __attribute__((warn_unused_result))
/* Alas, nonnull is not at present a good idea for us. We'd like to get
* warnings when we pass NULL where we shouldn't (which nonnull does, albeit
* spottily), but we don't want to tell the compiler to make optimizations
@@ -162,6 +161,7 @@
#define ATTR_NORETURN
#define ATTR_NONNULL(x)
#define ATTR_UNUSED
+#define ATTR_WUR
#define PREDICT_LIKELY(exp) (exp)
#define PREDICT_UNLIKELY(exp) (exp)
#endif
@@ -297,7 +297,7 @@ const void *tor_memmem(const void *haystack, size_t hlen, const void *needle,
size_t nlen) ATTR_NONNULL((1,3));
static const void *tor_memstr(const void *haystack, size_t hlen,
const char *needle) ATTR_NONNULL((1,3));
-static INLINE const void *
+static inline const void *
tor_memstr(const void *haystack, size_t hlen, const char *needle)
{
return tor_memmem(haystack, hlen, needle, strlen(needle));
@@ -308,7 +308,7 @@ tor_memstr(const void *haystack, size_t hlen, const char *needle)
#define DECLARE_CTYPE_FN(name) \
static int TOR_##name(char c); \
extern const uint32_t TOR_##name##_TABLE[]; \
- static INLINE int TOR_##name(char c) { \
+ static inline int TOR_##name(char c) { \
uint8_t u = c; \
return !!(TOR_##name##_TABLE[(u >> 5) & 7] & (1u << (u & 31))); \
}
@@ -610,7 +610,7 @@ typedef enum {
} socks5_reply_status_t;
/* ===== OS compatibility */
-const char *get_uname(void);
+MOCK_DECL(const char *, get_uname, (void));
uint16_t get_uint16(const void *cp) ATTR_NONNULL((1));
uint32_t get_uint32(const void *cp) ATTR_NONNULL((1));
@@ -622,7 +622,7 @@ void set_uint64(void *cp, uint64_t v) ATTR_NONNULL((1));
/* These uint8 variants are defined to make the code more uniform. */
#define get_uint8(cp) (*(const uint8_t*)(cp))
static void set_uint8(void *cp, uint8_t v);
-static INLINE void
+static inline void
set_uint8(void *cp, uint8_t v)
{
*(uint8_t*)cp = v;
@@ -634,7 +634,18 @@ typedef unsigned long rlim_t;
int get_max_sockets(void);
int set_max_file_descriptors(rlim_t limit, int *max);
int tor_disable_debugger_attach(void);
-int switch_id(const char *user);
+
+#if defined(HAVE_SYS_CAPABILITY_H) && defined(HAVE_CAP_SET_PROC)
+#define HAVE_LINUX_CAPABILITIES
+#endif
+
+int have_capability_support(void);
+
+/** Flag for switch_id; see switch_id() for documentation */
+#define SWITCH_ID_KEEP_BINDLOW (1<<0)
+/** Flag for switch_id; see switch_id() for documentation */
+#define SWITCH_ID_WARN_IF_NO_CAPS (1<<1)
+int switch_id(const char *user, unsigned flags);
#ifdef HAVE_PWD_H
char *get_user_homedir(const char *username);
#endif
diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c
index a366b6c9c6..c367ee4edb 100644
--- a/src/common/compat_libevent.c
+++ b/src/common/compat_libevent.c
@@ -11,6 +11,7 @@
#include "orconfig.h"
#include "compat.h"
+#define COMPAT_LIBEVENT_PRIVATE
#include "compat_libevent.h"
#include "crypto.h"
@@ -28,39 +29,11 @@
#include <event.h>
#endif
-/** A number representing a version of Libevent.
-
- This is a 4-byte number, with the first three bytes representing the
- major, minor, and patchlevel respectively of the library. The fourth
- byte is unused.
-
- This is equivalent to the format of LIBEVENT_VERSION_NUMBER on Libevent
- 2.0.1 or later. For versions of Libevent before 1.4.0, which followed the
- format of "1.0, 1.0a, 1.0b", we define 1.0 to be equivalent to 1.0.0, 1.0a
- to be equivalent to 1.0.1, and so on.
-*/
-typedef uint32_t le_version_t;
-
-/** @{ */
-/** Macros: returns the number of a libevent version as a le_version_t */
-#define V(major, minor, patch) \
- (((major) << 24) | ((minor) << 16) | ((patch) << 8))
-#define V_OLD(major, minor, patch) \
- V((major), (minor), (patch)-'a'+1)
-/** @} */
-
-/** Represetns a version of libevent so old we can't figure out what version
- * it is. */
-#define LE_OLD V(0,0,0)
-/** Represents a version of libevent so weird we can't figure out what version
- * it is. */
-#define LE_OTHER V(0,0,99)
-
/** A string which, if it appears in a libevent log, should be ignored. */
static const char *suppress_msg = NULL;
/** Callback function passed to event_set_log() so we can intercept
* log messages from libevent. */
-static void
+STATIC void
libevent_logging_callback(int severity, const char *msg)
{
char buf[1024];
@@ -274,6 +247,7 @@ tor_libevent_initialize(tor_libevent_cfg *torcfg)
MOCK_IMPL(struct event_base *,
tor_libevent_get_base, (void))
{
+ tor_assert(the_event_base != NULL);
return the_event_base;
}
@@ -291,7 +265,7 @@ tor_libevent_get_method(void)
/** Return the le_version_t for the version of libevent specified in the
* string <b>v</b>. If the version is very new or uses an unrecognized
* version, format, return LE_OTHER. */
-static le_version_t
+STATIC le_version_t
tor_decode_libevent_version(const char *v)
{
unsigned major, minor, patchlevel;
@@ -322,7 +296,7 @@ tor_decode_libevent_version(const char *v)
* Two different versions with different numbers are sure not to be binary
* compatible. Two different versions with the same numbers have a decent
* chance of binary compatibility.*/
-static int
+STATIC int
le_versions_compatibility(le_version_t v)
{
if (v == LE_OTHER)
diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h
index 39181efb7b..8ee02c0b6d 100644
--- a/src/common/compat_libevent.h
+++ b/src/common/compat_libevent.h
@@ -91,5 +91,42 @@ void tor_gettimeofday_cache_set(const struct timeval *tv);
#endif
void tor_gettimeofday_cached_monotonic(struct timeval *tv);
+#ifdef COMPAT_LIBEVENT_PRIVATE
+/** A number representing a version of Libevent.
+
+ This is a 4-byte number, with the first three bytes representing the
+ major, minor, and patchlevel respectively of the library. The fourth
+ byte is unused.
+
+ This is equivalent to the format of LIBEVENT_VERSION_NUMBER on Libevent
+ 2.0.1 or later. For versions of Libevent before 1.4.0, which followed the
+ format of "1.0, 1.0a, 1.0b", we define 1.0 to be equivalent to 1.0.0, 1.0a
+ to be equivalent to 1.0.1, and so on.
+*/
+typedef uint32_t le_version_t;
+
+/** @{ */
+/** Macros: returns the number of a libevent version as a le_version_t */
+#define V(major, minor, patch) \
+ (((major) << 24) | ((minor) << 16) | ((patch) << 8))
+#define V_OLD(major, minor, patch) \
+ V((major), (minor), (patch)-'a'+1)
+/** @} */
+
+/** Represetns a version of libevent so old we can't figure out what version
+ * it is. */
+#define LE_OLD V(0,0,0)
+/** Represents a version of libevent so weird we can't figure out what version
+ * it is. */
+#define LE_OTHER V(0,0,99)
+
+STATIC void
+libevent_logging_callback(int severity, const char *msg);
+STATIC le_version_t
+tor_decode_libevent_version(const char *v);
+STATIC int
+le_versions_compatibility(le_version_t v);
+#endif
+
#endif
diff --git a/src/common/compat_openssl.h b/src/common/compat_openssl.h
new file mode 100644
index 0000000000..d5333a26d6
--- /dev/null
+++ b/src/common/compat_openssl.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_COMPAT_OPENSSL_H
+#define TOR_COMPAT_OPENSSL_H
+
+#include <openssl/opensslv.h>
+
+/**
+ * \file compat_openssl.h
+ *
+ * \brief compatability definitions for working with different openssl forks
+ **/
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0)
+#error "We require OpenSSL >= 1.0.0"
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) && \
+ ! defined(LIBRESSL_VERSION_NUMBER)
+/* We define this macro if we're trying to build with the majorly refactored
+ * API in OpenSSL 1.1 */
+#define OPENSSL_1_1_API
+#endif
+
+#ifndef OPENSSL_1_1_API
+#define OPENSSL_VERSION SSLEAY_VERSION
+#define OpenSSL_version(v) SSLeay_version(v)
+#define OpenSSL_version_num() SSLeay()
+#define RAND_OpenSSL() RAND_SSLeay()
+#define STATE_IS_SW_SERVER_HELLO(st) \
+ (((st) == SSL3_ST_SW_SRVR_HELLO_A) || \
+ ((st) == SSL3_ST_SW_SRVR_HELLO_B))
+#define OSSL_HANDSHAKE_STATE int
+#define CONST_IF_OPENSSL_1_1_API
+#else
+#define STATE_IS_SW_SERVER_HELLO(st) \
+ ((st) == TLS_ST_SW_SRVR_HELLO)
+#define CONST_IF_OPENSSL_1_1_API const
+#endif
+
+#endif
+
diff --git a/src/common/container.c b/src/common/container.c
index 8c66bd89e4..1de8e7ded4 100644
--- a/src/common/container.c
+++ b/src/common/container.c
@@ -55,6 +55,7 @@ smartlist_free,(smartlist_t *sl))
void
smartlist_clear(smartlist_t *sl)
{
+ memset(sl->list, 0, sizeof(void *) * sl->num_used);
sl->num_used = 0;
}
@@ -63,7 +64,7 @@ smartlist_clear(smartlist_t *sl)
#endif
/** Make sure that <b>sl</b> can hold at least <b>size</b> entries. */
-static INLINE void
+static inline void
smartlist_ensure_capacity(smartlist_t *sl, size_t size)
{
/* Set MAX_CAPACITY to MIN(INT_MAX, SIZE_MAX / sizeof(void*)) */
@@ -83,10 +84,11 @@ smartlist_ensure_capacity(smartlist_t *sl, size_t size)
while (size > higher)
higher *= 2;
}
- tor_assert(higher <= INT_MAX); /* Redundant */
- sl->capacity = (int) higher;
sl->list = tor_reallocarray(sl->list, sizeof(void *),
- ((size_t)sl->capacity));
+ ((size_t)higher));
+ memset(sl->list + sl->capacity, 0,
+ sizeof(void *) * (higher - sl->capacity));
+ sl->capacity = (int) higher;
}
#undef ASSERT_CAPACITY
#undef MAX_CAPACITY
@@ -126,6 +128,7 @@ smartlist_remove(smartlist_t *sl, const void *element)
if (sl->list[i] == element) {
sl->list[i] = sl->list[--sl->num_used]; /* swap with the end */
i--; /* so we process the new i'th element */
+ sl->list[sl->num_used] = NULL;
}
}
@@ -135,9 +138,11 @@ void *
smartlist_pop_last(smartlist_t *sl)
{
tor_assert(sl);
- if (sl->num_used)
- return sl->list[--sl->num_used];
- else
+ if (sl->num_used) {
+ void *tmp = sl->list[--sl->num_used];
+ sl->list[sl->num_used] = NULL;
+ return tmp;
+ } else
return NULL;
}
@@ -168,6 +173,7 @@ smartlist_string_remove(smartlist_t *sl, const char *element)
tor_free(sl->list[i]);
sl->list[i] = sl->list[--sl->num_used]; /* swap with the end */
i--; /* so we process the new i'th element */
+ sl->list[sl->num_used] = NULL;
}
}
}
@@ -324,6 +330,7 @@ smartlist_intersect(smartlist_t *sl1, const smartlist_t *sl2)
if (!smartlist_contains(sl2, sl1->list[i])) {
sl1->list[i] = sl1->list[--sl1->num_used]; /* swap with the end */
i--; /* so we process the new i'th element */
+ sl1->list[sl1->num_used] = NULL;
}
}
@@ -348,6 +355,7 @@ smartlist_del(smartlist_t *sl, int idx)
tor_assert(idx>=0);
tor_assert(idx < sl->num_used);
sl->list[idx] = sl->list[--sl->num_used];
+ sl->list[sl->num_used] = NULL;
}
/** Remove the <b>idx</b>th element of sl; if idx is not the last element,
@@ -363,6 +371,7 @@ smartlist_del_keeporder(smartlist_t *sl, int idx)
--sl->num_used;
if (idx < sl->num_used)
memmove(sl->list+idx, sl->list+idx+1, sizeof(void*)*(sl->num_used-idx));
+ sl->list[sl->num_used] = NULL;
}
/** Insert the value <b>val</b> as the new <b>idx</b>th element of
@@ -831,9 +840,17 @@ smartlist_sort_pointers(smartlist_t *sl)
*
* For a 1-indexed array, we would use LEFT_CHILD[x] = 2*x and RIGHT_CHILD[x]
* = 2*x + 1. But this is C, so we have to adjust a little. */
-//#define LEFT_CHILD(i) ( ((i)+1)*2 - 1)
-//#define RIGHT_CHILD(i) ( ((i)+1)*2 )
-//#define PARENT(i) ( ((i)+1)/2 - 1)
+
+/* MAX_PARENT_IDX is the largest IDX in the smartlist which might have
+ * children whose indices fit inside an int.
+ * LEFT_CHILD(MAX_PARENT_IDX) == INT_MAX-2;
+ * RIGHT_CHILD(MAX_PARENT_IDX) == INT_MAX-1;
+ * LEFT_CHILD(MAX_PARENT_IDX + 1) == INT_MAX // impossible, see max list size.
+ */
+#define MAX_PARENT_IDX ((INT_MAX - 2) / 2)
+/* If this is true, then i is small enough to potentially have children
+ * in the smartlist, and it is save to use LEFT_CHILD/RIGHT_CHILD on it. */
+#define IDX_MAY_HAVE_CHILDREN(i) ((i) <= MAX_PARENT_IDX)
#define LEFT_CHILD(i) ( 2*(i) + 1 )
#define RIGHT_CHILD(i) ( 2*(i) + 2 )
#define PARENT(i) ( ((i)-1) / 2 )
@@ -860,13 +877,21 @@ smartlist_sort_pointers(smartlist_t *sl)
/** Helper. <b>sl</b> may have at most one violation of the heap property:
* the item at <b>idx</b> may be greater than one or both of its children.
* Restore the heap property. */
-static INLINE void
+static inline void
smartlist_heapify(smartlist_t *sl,
int (*compare)(const void *a, const void *b),
int idx_field_offset,
int idx)
{
while (1) {
+ if (! IDX_MAY_HAVE_CHILDREN(idx)) {
+ /* idx is so large that it cannot have any children, since doing so
+ * would mean the smartlist was over-capacity. Therefore it cannot
+ * violate the heap property by being greater than a child (since it
+ * doesn't have any). */
+ return;
+ }
+
int left_idx = LEFT_CHILD(idx);
int best_idx;
@@ -940,9 +965,11 @@ smartlist_pqueue_pop(smartlist_t *sl,
*IDXP(top)=-1;
if (--sl->num_used) {
sl->list[0] = sl->list[sl->num_used];
+ sl->list[sl->num_used] = NULL;
UPDATE_IDX(0);
smartlist_heapify(sl, compare, idx_field_offset, 0);
}
+ sl->list[sl->num_used] = NULL;
return top;
}
@@ -962,9 +989,11 @@ smartlist_pqueue_remove(smartlist_t *sl,
--sl->num_used;
*IDXP(item) = -1;
if (idx == sl->num_used) {
+ sl->list[sl->num_used] = NULL;
return;
} else {
sl->list[idx] = sl->list[sl->num_used];
+ sl->list[sl->num_used] = NULL;
UPDATE_IDX(idx);
smartlist_heapify(sl, compare, idx_field_offset, idx);
}
@@ -1057,35 +1086,35 @@ DEFINE_MAP_STRUCTS(digestmap_t, char key[DIGEST_LEN], digestmap_);
DEFINE_MAP_STRUCTS(digest256map_t, uint8_t key[DIGEST256_LEN], digest256map_);
/** Helper: compare strmap_entry_t objects by key value. */
-static INLINE int
+static inline int
strmap_entries_eq(const strmap_entry_t *a, const strmap_entry_t *b)
{
return !strcmp(a->key, b->key);
}
/** Helper: return a hash value for a strmap_entry_t. */
-static INLINE unsigned int
+static inline unsigned int
strmap_entry_hash(const strmap_entry_t *a)
{
return (unsigned) siphash24g(a->key, strlen(a->key));
}
/** Helper: compare digestmap_entry_t objects by key value. */
-static INLINE int
+static inline int
digestmap_entries_eq(const digestmap_entry_t *a, const digestmap_entry_t *b)
{
return tor_memeq(a->key, b->key, DIGEST_LEN);
}
/** Helper: return a hash value for a digest_map_t. */
-static INLINE unsigned int
+static inline unsigned int
digestmap_entry_hash(const digestmap_entry_t *a)
{
return (unsigned) siphash24g(a->key, DIGEST_LEN);
}
/** Helper: compare digestmap_entry_t objects by key value. */
-static INLINE int
+static inline int
digest256map_entries_eq(const digest256map_entry_t *a,
const digest256map_entry_t *b)
{
@@ -1093,7 +1122,7 @@ digest256map_entries_eq(const digest256map_entry_t *a,
}
/** Helper: return a hash value for a digest_map_t. */
-static INLINE unsigned int
+static inline unsigned int
digest256map_entry_hash(const digest256map_entry_t *a)
{
return (unsigned) siphash24g(a->key, DIGEST256_LEN);
@@ -1116,49 +1145,49 @@ HT_GENERATE2(digest256map_impl, digest256map_entry_t, node,
digest256map_entry_hash,
digest256map_entries_eq, 0.6, tor_reallocarray_, tor_free_)
-static INLINE void
+static inline void
strmap_entry_free(strmap_entry_t *ent)
{
tor_free(ent->key);
tor_free(ent);
}
-static INLINE void
+static inline void
digestmap_entry_free(digestmap_entry_t *ent)
{
tor_free(ent);
}
-static INLINE void
+static inline void
digest256map_entry_free(digest256map_entry_t *ent)
{
tor_free(ent);
}
-static INLINE void
+static inline void
strmap_assign_tmp_key(strmap_entry_t *ent, const char *key)
{
ent->key = (char*)key;
}
-static INLINE void
+static inline void
digestmap_assign_tmp_key(digestmap_entry_t *ent, const char *key)
{
memcpy(ent->key, key, DIGEST_LEN);
}
-static INLINE void
+static inline void
digest256map_assign_tmp_key(digest256map_entry_t *ent, const uint8_t *key)
{
memcpy(ent->key, key, DIGEST256_LEN);
}
-static INLINE void
+static inline void
strmap_assign_key(strmap_entry_t *ent, const char *key)
{
ent->key = tor_strdup(key);
}
-static INLINE void
+static inline void
digestmap_assign_key(digestmap_entry_t *ent, const char *key)
{
memcpy(ent->key, key, DIGEST_LEN);
}
-static INLINE void
+static inline void
digest256map_assign_key(digest256map_entry_t *ent, const uint8_t *key)
{
memcpy(ent->key, key, DIGEST256_LEN);
diff --git a/src/common/container.h b/src/common/container.h
index bf4f04762c..af7d5c37ce 100644
--- a/src/common/container.h
+++ b/src/common/container.h
@@ -53,21 +53,21 @@ void smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2);
#ifdef DEBUG_SMARTLIST
/** Return the number of items in sl.
*/
-static INLINE int smartlist_len(const smartlist_t *sl);
-static INLINE int smartlist_len(const smartlist_t *sl) {
+static inline int smartlist_len(const smartlist_t *sl);
+static inline int smartlist_len(const smartlist_t *sl) {
tor_assert(sl);
return (sl)->num_used;
}
/** Return the <b>idx</b>th element of sl.
*/
-static INLINE void *smartlist_get(const smartlist_t *sl, int idx);
-static INLINE void *smartlist_get(const smartlist_t *sl, int idx) {
+static inline void *smartlist_get(const smartlist_t *sl, int idx);
+static inline void *smartlist_get(const smartlist_t *sl, int idx) {
tor_assert(sl);
tor_assert(idx>=0);
tor_assert(sl->num_used > idx);
return sl->list[idx];
}
-static INLINE void smartlist_set(smartlist_t *sl, int idx, void *val) {
+static inline void smartlist_set(smartlist_t *sl, int idx, void *val) {
tor_assert(sl);
tor_assert(idx>=0);
tor_assert(sl->num_used > idx);
@@ -81,7 +81,7 @@ static INLINE void smartlist_set(smartlist_t *sl, int idx, void *val) {
/** Exchange the elements at indices <b>idx1</b> and <b>idx2</b> of the
* smartlist <b>sl</b>. */
-static INLINE void smartlist_swap(smartlist_t *sl, int idx1, int idx2)
+static inline void smartlist_swap(smartlist_t *sl, int idx1, int idx2)
{
if (idx1 != idx2) {
void *elt = smartlist_get(sl, idx1);
@@ -500,64 +500,64 @@ void* strmap_remove_lc(strmap_t *map, const char *key);
#define DECLARE_TYPED_DIGESTMAP_FNS(prefix, maptype, valtype) \
typedef struct maptype maptype; \
typedef struct prefix##iter_t *prefix##iter_t; \
- ATTR_UNUSED static INLINE maptype* \
+ ATTR_UNUSED static inline maptype* \
prefix##new(void) \
{ \
return (maptype*)digestmap_new(); \
} \
- ATTR_UNUSED static INLINE digestmap_t* \
+ ATTR_UNUSED static inline digestmap_t* \
prefix##to_digestmap(maptype *map) \
{ \
return (digestmap_t*)map; \
} \
- ATTR_UNUSED static INLINE valtype* \
+ ATTR_UNUSED static inline valtype* \
prefix##get(maptype *map, const char *key) \
{ \
return (valtype*)digestmap_get((digestmap_t*)map, key); \
} \
- ATTR_UNUSED static INLINE valtype* \
+ ATTR_UNUSED static inline valtype* \
prefix##set(maptype *map, const char *key, valtype *val) \
{ \
return (valtype*)digestmap_set((digestmap_t*)map, key, val); \
} \
- ATTR_UNUSED static INLINE valtype* \
+ ATTR_UNUSED static inline valtype* \
prefix##remove(maptype *map, const char *key) \
{ \
return (valtype*)digestmap_remove((digestmap_t*)map, key); \
} \
- ATTR_UNUSED static INLINE void \
+ ATTR_UNUSED static inline void \
prefix##free(maptype *map, void (*free_val)(void*)) \
{ \
digestmap_free((digestmap_t*)map, free_val); \
} \
- ATTR_UNUSED static INLINE int \
+ ATTR_UNUSED static inline int \
prefix##isempty(maptype *map) \
{ \
return digestmap_isempty((digestmap_t*)map); \
} \
- ATTR_UNUSED static INLINE int \
+ ATTR_UNUSED static inline int \
prefix##size(maptype *map) \
{ \
return digestmap_size((digestmap_t*)map); \
} \
- ATTR_UNUSED static INLINE \
+ ATTR_UNUSED static inline \
prefix##iter_t *prefix##iter_init(maptype *map) \
{ \
return (prefix##iter_t*) digestmap_iter_init((digestmap_t*)map); \
} \
- ATTR_UNUSED static INLINE \
+ ATTR_UNUSED static inline \
prefix##iter_t *prefix##iter_next(maptype *map, prefix##iter_t *iter) \
{ \
return (prefix##iter_t*) digestmap_iter_next( \
(digestmap_t*)map, (digestmap_iter_t*)iter); \
} \
- ATTR_UNUSED static INLINE prefix##iter_t* \
+ ATTR_UNUSED static inline prefix##iter_t* \
prefix##iter_next_rmv(maptype *map, prefix##iter_t *iter) \
{ \
return (prefix##iter_t*) digestmap_iter_next_rmv( \
(digestmap_t*)map, (digestmap_iter_t*)iter); \
} \
- ATTR_UNUSED static INLINE void \
+ ATTR_UNUSED static inline void \
prefix##iter_get(prefix##iter_t *iter, \
const char **keyp, \
valtype **valp) \
@@ -566,7 +566,7 @@ void* strmap_remove_lc(strmap_t *map, const char *key);
digestmap_iter_get((digestmap_iter_t*) iter, keyp, &v); \
*valp = v; \
} \
- ATTR_UNUSED static INLINE int \
+ ATTR_UNUSED static inline int \
prefix##iter_done(prefix##iter_t *iter) \
{ \
return digestmap_iter_done((digestmap_iter_t*)iter); \
@@ -584,7 +584,7 @@ void* strmap_remove_lc(strmap_t *map, const char *key);
/** A random-access array of one-bit-wide elements. */
typedef unsigned int bitarray_t;
/** Create a new bit array that can hold <b>n_bits</b> bits. */
-static INLINE bitarray_t *
+static inline bitarray_t *
bitarray_init_zero(unsigned int n_bits)
{
/* round up to the next int. */
@@ -594,7 +594,7 @@ bitarray_init_zero(unsigned int n_bits)
/** Expand <b>ba</b> from holding <b>n_bits_old</b> to <b>n_bits_new</b>,
* clearing all new bits. Returns a possibly changed pointer to the
* bitarray. */
-static INLINE bitarray_t *
+static inline bitarray_t *
bitarray_expand(bitarray_t *ba,
unsigned int n_bits_old, unsigned int n_bits_new)
{
@@ -611,26 +611,26 @@ bitarray_expand(bitarray_t *ba,
return (bitarray_t*) ptr;
}
/** Free the bit array <b>ba</b>. */
-static INLINE void
+static inline void
bitarray_free(bitarray_t *ba)
{
tor_free(ba);
}
/** Set the <b>bit</b>th bit in <b>b</b> to 1. */
-static INLINE void
+static inline void
bitarray_set(bitarray_t *b, int bit)
{
b[bit >> BITARRAY_SHIFT] |= (1u << (bit & BITARRAY_MASK));
}
/** Set the <b>bit</b>th bit in <b>b</b> to 0. */
-static INLINE void
+static inline void
bitarray_clear(bitarray_t *b, int bit)
{
b[bit >> BITARRAY_SHIFT] &= ~ (1u << (bit & BITARRAY_MASK));
}
/** Return true iff <b>bit</b>th bit in <b>b</b> is nonzero. NOTE: does
* not necessarily return 1 on true. */
-static INLINE unsigned int
+static inline unsigned int
bitarray_is_set(bitarray_t *b, int bit)
{
return b[bit >> BITARRAY_SHIFT] & (1u << (bit & BITARRAY_MASK));
@@ -645,7 +645,7 @@ typedef struct {
#define BIT(n) ((n) & set->mask)
/** Add the digest <b>digest</b> to <b>set</b>. */
-static INLINE void
+static inline void
digestset_add(digestset_t *set, const char *digest)
{
const uint64_t x = siphash24g(digest, 20);
@@ -661,7 +661,7 @@ digestset_add(digestset_t *set, const char *digest)
/** If <b>digest</b> is in <b>set</b>, return nonzero. Otherwise,
* <em>probably</em> return zero. */
-static INLINE int
+static inline int
digestset_contains(const digestset_t *set, const char *digest)
{
const uint64_t x = siphash24g(digest, 20);
@@ -689,33 +689,33 @@ double find_nth_double(double *array, int n_elements, int nth);
int32_t find_nth_int32(int32_t *array, int n_elements, int nth);
uint32_t find_nth_uint32(uint32_t *array, int n_elements, int nth);
long find_nth_long(long *array, int n_elements, int nth);
-static INLINE int
+static inline int
median_int(int *array, int n_elements)
{
return find_nth_int(array, n_elements, (n_elements-1)/2);
}
-static INLINE time_t
+static inline time_t
median_time(time_t *array, int n_elements)
{
return find_nth_time(array, n_elements, (n_elements-1)/2);
}
-static INLINE double
+static inline double
median_double(double *array, int n_elements)
{
return find_nth_double(array, n_elements, (n_elements-1)/2);
}
-static INLINE uint32_t
+static inline uint32_t
median_uint32(uint32_t *array, int n_elements)
{
return find_nth_uint32(array, n_elements, (n_elements-1)/2);
}
-static INLINE int32_t
+static inline int32_t
median_int32(int32_t *array, int n_elements)
{
return find_nth_int32(array, n_elements, (n_elements-1)/2);
}
-static INLINE uint32_t
+static inline uint32_t
third_quartile_uint32(uint32_t *array, int n_elements)
{
return find_nth_uint32(array, n_elements, (n_elements*3)/4);
diff --git a/src/common/crypto.c b/src/common/crypto.c
index 57981f9a00..7298ec084e 100644
--- a/src/common/crypto.c
+++ b/src/common/crypto.c
@@ -21,18 +21,13 @@
#undef OCSP_RESPONSE
#endif
-#include <openssl/opensslv.h>
-
#define CRYPTO_PRIVATE
#include "crypto.h"
+#include "compat_openssl.h"
#include "crypto_curve25519.h"
#include "crypto_ed25519.h"
#include "crypto_format.h"
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0)
-#error "We require OpenSSL >= 1.0.0"
-#endif
-
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
@@ -48,6 +43,7 @@
#include <ctype.h>
#endif
#ifdef HAVE_UNISTD_H
+#define _GNU_SOURCE
#include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
@@ -56,6 +52,9 @@
#ifdef HAVE_SYS_FCNTL_H
#include <sys/fcntl.h>
#endif
+#ifdef HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#endif
#include "torlog.h"
#include "aes.h"
@@ -65,6 +64,8 @@
#include "sandbox.h"
#include "util_format.h"
+#include "keccak-tiny/keccak-tiny.h"
+
#ifdef ANDROID
/* Android's OpenSSL seems to have removed all of its Engine support. */
#define DISABLE_ENGINES
@@ -73,6 +74,9 @@
/** Longest recognized */
#define MAX_DNS_LABEL_SIZE 63
+/** Largest strong entropy request */
+#define MAX_STRONGEST_RAND_SIZE 256
+
/** Macro: is k a valid RSA public or private key? */
#define PUBLIC_KEY_OK(k) ((k) && (k)->key && (k)->key->n)
/** Macro: is k a valid RSA private key? */
@@ -110,7 +114,7 @@ static int tor_check_dh_key(int severity, BIGNUM *bn);
/** Return the number of bytes added by padding method <b>padding</b>.
*/
-static INLINE int
+static inline int
crypto_get_rsa_padding_overhead(int padding)
{
switch (padding)
@@ -122,7 +126,7 @@ crypto_get_rsa_padding_overhead(int padding)
/** Given a padding method <b>padding</b>, return the correct OpenSSL constant.
*/
-static INLINE int
+static inline int
crypto_get_rsa_padding(int padding)
{
switch (padding)
@@ -227,7 +231,7 @@ const char *
crypto_openssl_get_version_str(void)
{
if (crypto_openssl_version_str == NULL) {
- const char *raw_version = SSLeay_version(SSLEAY_VERSION);
+ const char *raw_version = OpenSSL_version(OPENSSL_VERSION);
crypto_openssl_version_str = parse_openssl_version_str(raw_version);
}
return crypto_openssl_version_str;
@@ -248,14 +252,16 @@ crypto_openssl_get_header_version_str(void)
/** Make sure that openssl is using its default PRNG. Return 1 if we had to
* adjust it; 0 otherwise. */
-static int
+STATIC int
crypto_force_rand_ssleay(void)
{
- if (RAND_get_rand_method() != RAND_SSLeay()) {
+ RAND_METHOD *default_method;
+ default_method = RAND_OpenSSL();
+ if (RAND_get_rand_method() != default_method) {
log_notice(LD_CRYPTO, "It appears that one of our engines has provided "
"a replacement the OpenSSL RNG. Resetting it to the default "
"implementation.");
- RAND_set_rand_method(RAND_SSLeay());
+ RAND_set_rand_method(default_method);
return 1;
}
return 0;
@@ -270,8 +276,7 @@ crypto_init_siphash_key(void)
if (have_seeded_siphash)
return 0;
- if (crypto_rand((char*) &key, sizeof(key)) < 0)
- return -1;
+ crypto_rand((char*) &key, sizeof(key));
siphash_set_global_key(&key);
have_seeded_siphash = 1;
return 0;
@@ -291,16 +296,18 @@ crypto_early_init(void)
setup_openssl_threading();
- if (SSLeay() == OPENSSL_VERSION_NUMBER &&
- !strcmp(SSLeay_version(SSLEAY_VERSION), OPENSSL_VERSION_TEXT)) {
+ unsigned long version_num = OpenSSL_version_num();
+ const char *version_str = OpenSSL_version(OPENSSL_VERSION);
+ if (version_num == OPENSSL_VERSION_NUMBER &&
+ !strcmp(version_str, OPENSSL_VERSION_TEXT)) {
log_info(LD_CRYPTO, "OpenSSL version matches version from headers "
- "(%lx: %s).", SSLeay(), SSLeay_version(SSLEAY_VERSION));
+ "(%lx: %s).", version_num, version_str);
} else {
log_warn(LD_CRYPTO, "OpenSSL version from headers does not match the "
"version we're running with. If you get weird crashes, that "
"might be why. (Compiled with %lx: %s; running with %lx: %s).",
(unsigned long)OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_TEXT,
- SSLeay(), SSLeay_version(SSLEAY_VERSION));
+ version_num, version_str);
}
crypto_force_rand_ssleay();
@@ -322,7 +329,8 @@ int
crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
{
if (!crypto_global_initialized_) {
- crypto_early_init();
+ if (crypto_early_init() < 0)
+ return -1;
crypto_global_initialized_ = 1;
@@ -365,8 +373,12 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
used by Tor and the set of algorithms available in the engine */
log_engine("RSA", ENGINE_get_default_RSA());
log_engine("DH", ENGINE_get_default_DH());
+#ifdef OPENSSL_1_1_API
+ log_engine("EC", ENGINE_get_default_EC());
+#else
log_engine("ECDH", ENGINE_get_default_ECDH());
log_engine("ECDSA", ENGINE_get_default_ECDSA());
+#endif
log_engine("RAND", ENGINE_get_default_RAND());
log_engine("RAND (which we will not use)", ENGINE_get_default_RAND());
log_engine("SHA1", ENGINE_get_digest_engine(NID_sha1));
@@ -404,11 +416,7 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
void
crypto_thread_cleanup(void)
{
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)
ERR_remove_thread_state(NULL);
-#else
- ERR_remove_state(0);
-#endif
}
/** used by tortls.c: wrap an RSA* in a crypto_pk_t. */
@@ -432,9 +440,10 @@ crypto_pk_get_rsa_(crypto_pk_t *env)
}
/** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t. Iff
- * private is set, include the private-key portion of the key. */
-EVP_PKEY *
-crypto_pk_get_evp_pkey_(crypto_pk_t *env, int private)
+ * private is set, include the private-key portion of the key. Return a valid
+ * pointer on success, and NULL on failure. */
+MOCK_IMPL(EVP_PKEY *,
+ crypto_pk_get_evp_pkey_,(crypto_pk_t *env, int private))
{
RSA *key = NULL;
EVP_PKEY *pkey = NULL;
@@ -470,8 +479,8 @@ crypto_dh_get_dh_(crypto_dh_t *dh)
/** Allocate and return storage for a public key. The key itself will not yet
* be set.
*/
-crypto_pk_t *
-crypto_pk_new(void)
+MOCK_IMPL(crypto_pk_t *,
+ crypto_pk_new,(void))
{
RSA *rsa;
@@ -553,8 +562,8 @@ crypto_cipher_free(crypto_cipher_t *env)
/** Generate a <b>bits</b>-bit new public/private keypair in <b>env</b>.
* Return 0 on success, -1 on failure.
*/
-int
-crypto_pk_generate_key_with_bits(crypto_pk_t *env, int bits)
+MOCK_IMPL(int,
+ crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits))
{
tor_assert(env);
@@ -656,7 +665,8 @@ crypto_pk_read_private_key_from_filename(crypto_pk_t *env,
return 0;
}
-/** Helper function to implement crypto_pk_write_*_key_to_string. */
+/** Helper function to implement crypto_pk_write_*_key_to_string. Return 0 on
+ * success, -1 on failure. */
static int
crypto_pk_write_key_to_string_impl(crypto_pk_t *env, char **dest,
size_t *len, int is_public)
@@ -897,7 +907,8 @@ crypto_pk_dup_key(crypto_pk_t *env)
return env;
}
-/** Make a real honest-to-goodness copy of <b>env</b>, and return it. */
+/** Make a real honest-to-goodness copy of <b>env</b>, and return it.
+ * Returns NULL on failure. */
crypto_pk_t *
crypto_pk_copy_full(crypto_pk_t *env)
{
@@ -1189,7 +1200,8 @@ crypto_pk_public_hybrid_encrypt(crypto_pk_t *env,
return -1;
}
-/** Invert crypto_pk_public_hybrid_encrypt. */
+/** Invert crypto_pk_public_hybrid_encrypt. Returns the number of bytes
+ * written on success, -1 on failure. */
int
crypto_pk_private_hybrid_decrypt(crypto_pk_t *env,
char *to,
@@ -1315,7 +1327,7 @@ crypto_pk_get_digest(const crypto_pk_t *pk, char *digest_out)
/** Compute all digests of the DER encoding of <b>pk</b>, and store them
* in <b>digests_out</b>. Return 0 on success, -1 on failure. */
int
-crypto_pk_get_all_digests(crypto_pk_t *pk, digests_t *digests_out)
+crypto_pk_get_common_digests(crypto_pk_t *pk, common_digests_t *digests_out)
{
unsigned char *buf = NULL;
int len;
@@ -1323,7 +1335,7 @@ crypto_pk_get_all_digests(crypto_pk_t *pk, digests_t *digests_out)
len = i2d_RSAPublicKey(pk->key, &buf);
if (len < 0 || buf == NULL)
return -1;
- if (crypto_digest_all(digests_out, (char*)buf, len) < 0) {
+ if (crypto_common_digests(digests_out, (char*)buf, len) < 0) {
OPENSSL_free(buf);
return -1;
}
@@ -1332,7 +1344,7 @@ crypto_pk_get_all_digests(crypto_pk_t *pk, digests_t *digests_out)
}
/** Copy <b>in</b> to the <b>outlen</b>-byte buffer <b>out</b>, adding spaces
- * every four spaces. */
+ * every four characters. */
void
crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in)
{
@@ -1484,7 +1496,7 @@ crypto_cipher_get_key(crypto_cipher_t *env)
/** Encrypt <b>fromlen</b> bytes from <b>from</b> using the cipher
* <b>env</b>; on success, store the result to <b>to</b> and return 0.
- * On failure, return -1.
+ * Does not check for failure.
*/
int
crypto_cipher_encrypt(crypto_cipher_t *env, char *to,
@@ -1497,13 +1509,14 @@ crypto_cipher_encrypt(crypto_cipher_t *env, char *to,
tor_assert(to);
tor_assert(fromlen < SIZE_T_CEILING);
- aes_crypt(env->cipher, from, fromlen, to);
+ memcpy(to, from, fromlen);
+ aes_crypt_inplace(env->cipher, to, fromlen);
return 0;
}
/** Decrypt <b>fromlen</b> bytes from <b>from</b> using the cipher
* <b>env</b>; on success, store the result to <b>to</b> and return 0.
- * On failure, return -1.
+ * Does not check for failure.
*/
int
crypto_cipher_decrypt(crypto_cipher_t *env, char *to,
@@ -1514,19 +1527,19 @@ crypto_cipher_decrypt(crypto_cipher_t *env, char *to,
tor_assert(to);
tor_assert(fromlen < SIZE_T_CEILING);
- aes_crypt(env->cipher, from, fromlen, to);
+ memcpy(to, from, fromlen);
+ aes_crypt_inplace(env->cipher, to, fromlen);
return 0;
}
/** Encrypt <b>len</b> bytes on <b>from</b> using the cipher in <b>env</b>;
- * on success, return 0. On failure, return -1.
+ * on success. Does not check for failure.
*/
-int
+void
crypto_cipher_crypt_inplace(crypto_cipher_t *env, char *buf, size_t len)
{
tor_assert(len < SIZE_T_CEILING);
aes_crypt_inplace(env->cipher, buf, len);
- return 0;
}
/** Encrypt <b>fromlen</b> bytes (at least 1) from <b>from</b> with the key in
@@ -1591,7 +1604,7 @@ crypto_cipher_decrypt_with_iv(const char *key,
/** Compute the SHA1 digest of the <b>len</b> bytes on data stored in
* <b>m</b>. Write the DIGEST_LEN byte result into <b>digest</b>.
- * Return 0 on success, -1 on failure.
+ * Return 0 on success, 1 on failure.
*/
int
crypto_digest(char *digest, const char *m, size_t len)
@@ -1603,32 +1616,52 @@ crypto_digest(char *digest, const char *m, size_t len)
/** Compute a 256-bit digest of <b>len</b> bytes in data stored in <b>m</b>,
* using the algorithm <b>algorithm</b>. Write the DIGEST_LEN256-byte result
- * into <b>digest</b>. Return 0 on success, -1 on failure. */
+ * into <b>digest</b>. Return 0 on success, 1 on failure. */
int
crypto_digest256(char *digest, const char *m, size_t len,
digest_algorithm_t algorithm)
{
tor_assert(m);
tor_assert(digest);
- tor_assert(algorithm == DIGEST_SHA256);
- return (SHA256((const unsigned char*)m,len,(unsigned char*)digest) == NULL);
+ tor_assert(algorithm == DIGEST_SHA256 || algorithm == DIGEST_SHA3_256);
+ if (algorithm == DIGEST_SHA256)
+ return (SHA256((const uint8_t*)m,len,(uint8_t*)digest) == NULL);
+ else
+ return (sha3_256((uint8_t *)digest, DIGEST256_LEN,(const uint8_t *)m, len)
+ == -1);
+}
+
+/** Compute a 512-bit digest of <b>len</b> bytes in data stored in <b>m</b>,
+ * using the algorithm <b>algorithm</b>. Write the DIGEST_LEN512-byte result
+ * into <b>digest</b>. Return 0 on success, 1 on failure. */
+int
+crypto_digest512(char *digest, const char *m, size_t len,
+ digest_algorithm_t algorithm)
+{
+ tor_assert(m);
+ tor_assert(digest);
+ tor_assert(algorithm == DIGEST_SHA512 || algorithm == DIGEST_SHA3_512);
+ if (algorithm == DIGEST_SHA512)
+ return (SHA512((const unsigned char*)m,len,(unsigned char*)digest)
+ == NULL);
+ else
+ return (sha3_512((uint8_t*)digest, DIGEST512_LEN, (const uint8_t*)m, len)
+ == -1);
}
-/** Set the digests_t in <b>ds_out</b> to contain every digest on the
+/** Set the common_digests_t in <b>ds_out</b> to contain every digest on the
* <b>len</b> bytes in <b>m</b> that we know how to compute. Return 0 on
* success, -1 on failure. */
int
-crypto_digest_all(digests_t *ds_out, const char *m, size_t len)
+crypto_common_digests(common_digests_t *ds_out, const char *m, size_t len)
{
- int i;
tor_assert(ds_out);
memset(ds_out, 0, sizeof(*ds_out));
if (crypto_digest(ds_out->d[DIGEST_SHA1], m, len) < 0)
return -1;
- for (i = DIGEST_SHA256; i < N_DIGEST_ALGORITHMS; ++i) {
- if (crypto_digest256(ds_out->d[i], m, len, i) < 0)
- return -1;
- }
+ if (crypto_digest256(ds_out->d[DIGEST_SHA256], m, len, DIGEST_SHA256) < 0)
+ return -1;
+
return 0;
}
@@ -1641,6 +1674,12 @@ crypto_digest_algorithm_get_name(digest_algorithm_t alg)
return "sha1";
case DIGEST_SHA256:
return "sha256";
+ case DIGEST_SHA512:
+ return "sha512";
+ case DIGEST_SHA3_256:
+ return "sha3-256";
+ case DIGEST_SHA3_512:
+ return "sha3-512";
default:
tor_fragile_assert();
return "??unknown_digest??";
@@ -1656,27 +1695,90 @@ crypto_digest_algorithm_parse_name(const char *name)
return DIGEST_SHA1;
else if (!strcmp(name, "sha256"))
return DIGEST_SHA256;
+ else if (!strcmp(name, "sha512"))
+ return DIGEST_SHA512;
+ else if (!strcmp(name, "sha3-256"))
+ return DIGEST_SHA3_256;
+ else if (!strcmp(name, "sha3-512"))
+ return DIGEST_SHA3_512;
else
return -1;
}
+/** Given an algorithm, return the digest length in bytes. */
+static inline size_t
+crypto_digest_algorithm_get_length(digest_algorithm_t alg)
+{
+ switch (alg) {
+ case DIGEST_SHA1:
+ return DIGEST_LEN;
+ case DIGEST_SHA256:
+ return DIGEST256_LEN;
+ case DIGEST_SHA512:
+ return DIGEST512_LEN;
+ case DIGEST_SHA3_256:
+ return DIGEST256_LEN;
+ case DIGEST_SHA3_512:
+ return DIGEST512_LEN;
+ default:
+ tor_assert(0);
+ return 0; /* Unreachable */
+ }
+}
+
/** Intermediate information about the digest of a stream of data. */
struct crypto_digest_t {
+ digest_algorithm_t algorithm; /**< Which algorithm is in use? */
+ /** State for the digest we're using. Only one member of the
+ * union is usable, depending on the value of <b>algorithm</b>. Note also
+ * that space for other members might not even be allocated!
+ */
union {
SHA_CTX sha1; /**< state for SHA1 */
SHA256_CTX sha2; /**< state for SHA256 */
- } d; /**< State for the digest we're using. Only one member of the
- * union is usable, depending on the value of <b>algorithm</b>. */
- digest_algorithm_bitfield_t algorithm : 8; /**< Which algorithm is in use? */
+ SHA512_CTX sha512; /**< state for SHA512 */
+ keccak_state sha3; /**< state for SHA3-[256,512] */
+ } d;
};
+/**
+ * Return the number of bytes we need to malloc in order to get a
+ * crypto_digest_t for <b>alg</b>, or the number of bytes we need to wipe
+ * when we free one.
+ */
+static size_t
+crypto_digest_alloc_bytes(digest_algorithm_t alg)
+{
+ /* Helper: returns the number of bytes in the 'f' field of 'st' */
+#define STRUCT_FIELD_SIZE(st, f) (sizeof( ((st*)0)->f ))
+ /* Gives the length of crypto_digest_t through the end of the field 'd' */
+#define END_OF_FIELD(f) (STRUCT_OFFSET(crypto_digest_t, f) + \
+ STRUCT_FIELD_SIZE(crypto_digest_t, f))
+ switch (alg) {
+ case DIGEST_SHA1:
+ return END_OF_FIELD(d.sha1);
+ case DIGEST_SHA256:
+ return END_OF_FIELD(d.sha2);
+ case DIGEST_SHA512:
+ return END_OF_FIELD(d.sha512);
+ case DIGEST_SHA3_256:
+ case DIGEST_SHA3_512:
+ return END_OF_FIELD(d.sha3);
+ default:
+ tor_assert(0);
+ return 0;
+ }
+#undef END_OF_FIELD
+#undef STRUCT_FIELD_SIZE
+}
+
/** Allocate and return a new digest object to compute SHA1 digests.
*/
crypto_digest_t *
crypto_digest_new(void)
{
crypto_digest_t *r;
- r = tor_malloc(sizeof(crypto_digest_t));
+ r = tor_malloc(crypto_digest_alloc_bytes(DIGEST_SHA1));
SHA1_Init(&r->d.sha1);
r->algorithm = DIGEST_SHA1;
return r;
@@ -1688,9 +1790,28 @@ crypto_digest_t *
crypto_digest256_new(digest_algorithm_t algorithm)
{
crypto_digest_t *r;
- tor_assert(algorithm == DIGEST_SHA256);
- r = tor_malloc(sizeof(crypto_digest_t));
- SHA256_Init(&r->d.sha2);
+ tor_assert(algorithm == DIGEST_SHA256 || algorithm == DIGEST_SHA3_256);
+ r = tor_malloc(crypto_digest_alloc_bytes(algorithm));
+ if (algorithm == DIGEST_SHA256)
+ SHA256_Init(&r->d.sha2);
+ else
+ keccak_digest_init(&r->d.sha3, 256);
+ r->algorithm = algorithm;
+ return r;
+}
+
+/** Allocate and return a new digest object to compute 512-bit digests
+ * using <b>algorithm</b>. */
+crypto_digest_t *
+crypto_digest512_new(digest_algorithm_t algorithm)
+{
+ crypto_digest_t *r;
+ tor_assert(algorithm == DIGEST_SHA512 || algorithm == DIGEST_SHA3_512);
+ r = tor_malloc(crypto_digest_alloc_bytes(algorithm));
+ if (algorithm == DIGEST_SHA512)
+ SHA512_Init(&r->d.sha512);
+ else
+ keccak_digest_init(&r->d.sha3, 512);
r->algorithm = algorithm;
return r;
}
@@ -1702,7 +1823,8 @@ crypto_digest_free(crypto_digest_t *digest)
{
if (!digest)
return;
- memwipe(digest, 0, sizeof(crypto_digest_t));
+ size_t bytes = crypto_digest_alloc_bytes(digest->algorithm);
+ memwipe(digest, 0, bytes);
tor_free(digest);
}
@@ -1726,6 +1848,13 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
case DIGEST_SHA256:
SHA256_Update(&digest->d.sha2, (void*)data, len);
break;
+ case DIGEST_SHA512:
+ SHA512_Update(&digest->d.sha512, (void*)data, len);
+ break;
+ case DIGEST_SHA3_256: /* FALLSTHROUGH */
+ case DIGEST_SHA3_512:
+ keccak_digest_update(&digest->d.sha3, (const uint8_t *)data, len);
+ break;
default:
tor_fragile_assert();
break;
@@ -1734,33 +1863,45 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
/** Compute the hash of the data that has been passed to the digest
* object; write the first out_len bytes of the result to <b>out</b>.
- * <b>out_len</b> must be \<= DIGEST256_LEN.
+ * <b>out_len</b> must be \<= DIGEST512_LEN.
*/
void
crypto_digest_get_digest(crypto_digest_t *digest,
char *out, size_t out_len)
{
- unsigned char r[DIGEST256_LEN];
+ unsigned char r[DIGEST512_LEN];
crypto_digest_t tmpenv;
tor_assert(digest);
tor_assert(out);
+ tor_assert(out_len <= crypto_digest_algorithm_get_length(digest->algorithm));
+
+ /* The SHA-3 code handles copying into a temporary ctx, and also can handle
+ * short output buffers by truncating appropriately. */
+ if (digest->algorithm == DIGEST_SHA3_256 ||
+ digest->algorithm == DIGEST_SHA3_512) {
+ keccak_digest_sum(&digest->d.sha3, (uint8_t *)out, out_len);
+ return;
+ }
+
+ const size_t alloc_bytes = crypto_digest_alloc_bytes(digest->algorithm);
/* memcpy into a temporary ctx, since SHA*_Final clears the context */
- memcpy(&tmpenv, digest, sizeof(crypto_digest_t));
+ memcpy(&tmpenv, digest, alloc_bytes);
switch (digest->algorithm) {
case DIGEST_SHA1:
- tor_assert(out_len <= DIGEST_LEN);
SHA1_Final(r, &tmpenv.d.sha1);
break;
case DIGEST_SHA256:
- tor_assert(out_len <= DIGEST256_LEN);
SHA256_Final(r, &tmpenv.d.sha2);
break;
+ case DIGEST_SHA512:
+ SHA512_Final(r, &tmpenv.d.sha512);
+ break;
+ case DIGEST_SHA3_256: /* FALLSTHROUGH */
+ case DIGEST_SHA3_512:
+ log_warn(LD_BUG, "Handling unexpected algorithm %d", digest->algorithm);
+ tor_assert(0); /* This is fatal, because it should never happen. */
default:
- log_warn(LD_BUG, "Called with unknown algorithm %d", digest->algorithm);
- /* If fragile_assert is not enabled, then we should at least not
- * leak anything. */
- memwipe(r, 0xff, sizeof(r));
- tor_fragile_assert();
+ tor_assert(0); /* Unreachable. */
break;
}
memcpy(out, r, out_len);
@@ -1773,15 +1914,14 @@ crypto_digest_get_digest(crypto_digest_t *digest,
crypto_digest_t *
crypto_digest_dup(const crypto_digest_t *digest)
{
- crypto_digest_t *r;
tor_assert(digest);
- r = tor_malloc(sizeof(crypto_digest_t));
- memcpy(r,digest,sizeof(crypto_digest_t));
- return r;
+ const size_t alloc_bytes = crypto_digest_alloc_bytes(digest->algorithm);
+ return tor_memdup(digest, alloc_bytes);
}
/** Replace the state of the digest object <b>into</b> with the state
- * of the digest object <b>from</b>.
+ * of the digest object <b>from</b>. Requires that 'into' and 'from'
+ * have the same digest type.
*/
void
crypto_digest_assign(crypto_digest_t *into,
@@ -1789,14 +1929,16 @@ crypto_digest_assign(crypto_digest_t *into,
{
tor_assert(into);
tor_assert(from);
- memcpy(into,from,sizeof(crypto_digest_t));
+ tor_assert(into->algorithm == from->algorithm);
+ const size_t alloc_bytes = crypto_digest_alloc_bytes(from->algorithm);
+ memcpy(into,from,alloc_bytes);
}
/** Given a list of strings in <b>lst</b>, set the <b>len_out</b>-byte digest
* at <b>digest_out</b> to the hash of the concatenation of those strings,
* plus the optional string <b>append</b>, computed with the algorithm
* <b>alg</b>.
- * <b>out_len</b> must be \<= DIGEST256_LEN. */
+ * <b>out_len</b> must be \<= DIGEST512_LEN. */
void
crypto_digest_smartlist(char *digest_out, size_t len_out,
const smartlist_t *lst,
@@ -1811,7 +1953,7 @@ crypto_digest_smartlist(char *digest_out, size_t len_out,
* optional string <b>prepend</b>, those strings,
* and the optional string <b>append</b>, computed with the algorithm
* <b>alg</b>.
- * <b>out_len</b> must be \<= DIGEST256_LEN. */
+ * <b>len_out</b> must be \<= DIGEST512_LEN. */
void
crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
const char *prepend,
@@ -1819,11 +1961,27 @@ crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
const char *append,
digest_algorithm_t alg)
{
- crypto_digest_t *d;
- if (alg == DIGEST_SHA1)
- d = crypto_digest_new();
- else
- d = crypto_digest256_new(alg);
+ crypto_digest_t *d = NULL;
+ switch (alg) {
+ case DIGEST_SHA1:
+ d = crypto_digest_new();
+ break;
+ case DIGEST_SHA256: /* FALLSTHROUGH */
+ case DIGEST_SHA3_256:
+ d = crypto_digest256_new(alg);
+ break;
+ case DIGEST_SHA512: /* FALLSTHROUGH */
+ case DIGEST_SHA3_512:
+ d = crypto_digest512_new(alg);
+ break;
+ default:
+ log_warn(LD_BUG, "Called with unknown algorithm %d", alg);
+ /* If fragile_assert is not enabled, wipe output and return
+ * without running any calculations */
+ memwipe(digest_out, 0xff, len_out);
+ tor_fragile_assert();
+ goto free;
+ }
if (prepend)
crypto_digest_add_bytes(d, prepend, strlen(prepend));
SMARTLIST_FOREACH(lst, const char *, cp,
@@ -1831,23 +1989,78 @@ crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
if (append)
crypto_digest_add_bytes(d, append, strlen(append));
crypto_digest_get_digest(d, digest_out, len_out);
+
+ free:
crypto_digest_free(d);
}
/** Compute the HMAC-SHA-256 of the <b>msg_len</b> bytes in <b>msg</b>, using
* the <b>key</b> of length <b>key_len</b>. Store the DIGEST256_LEN-byte
- * result in <b>hmac_out</b>.
+ * result in <b>hmac_out</b>. Asserts on failure.
*/
void
crypto_hmac_sha256(char *hmac_out,
const char *key, size_t key_len,
const char *msg, size_t msg_len)
{
+ unsigned char *rv = NULL;
/* If we've got OpenSSL >=0.9.8 we can use its hmac implementation. */
tor_assert(key_len < INT_MAX);
tor_assert(msg_len < INT_MAX);
- HMAC(EVP_sha256(), key, (int)key_len, (unsigned char*)msg, (int)msg_len,
- (unsigned char*)hmac_out, NULL);
+ tor_assert(hmac_out);
+ rv = HMAC(EVP_sha256(), key, (int)key_len, (unsigned char*)msg, (int)msg_len,
+ (unsigned char*)hmac_out, NULL);
+ tor_assert(rv);
+}
+
+/** Internal state for a eXtendable-Output Function (XOF). */
+struct crypto_xof_t {
+ keccak_state s;
+};
+
+/** Allocate a new XOF object backed by SHAKE-256. The security level
+ * provided is a function of the length of the output used. Read and
+ * understand FIPS-202 A.2 "Additional Consideration for Extendable-Output
+ * Functions" before using this construct.
+ */
+crypto_xof_t *
+crypto_xof_new(void)
+{
+ crypto_xof_t *xof;
+ xof = tor_malloc(sizeof(crypto_xof_t));
+ keccak_xof_init(&xof->s, 256);
+ return xof;
+}
+
+/** Absorb bytes into a XOF object. Must not be called after a call to
+ * crypto_xof_squeeze_bytes() for the same instance, and will assert
+ * if attempted.
+ */
+void
+crypto_xof_add_bytes(crypto_xof_t *xof, const uint8_t *data, size_t len)
+{
+ int i = keccak_xof_absorb(&xof->s, data, len);
+ tor_assert(i == 0);
+}
+
+/** Squeeze bytes out of a XOF object. Calling this routine will render
+ * the XOF instance ineligible to absorb further data.
+ */
+void
+crypto_xof_squeeze_bytes(crypto_xof_t *xof, uint8_t *out, size_t len)
+{
+ int i = keccak_xof_squeeze(&xof->s, out, len);
+ tor_assert(i == 0);
+}
+
+/** Cleanse and deallocate a XOF object. */
+void
+crypto_xof_free(crypto_xof_t *xof)
+{
+ if (!xof)
+ return;
+ memwipe(xof, 0, sizeof(crypto_xof_t));
+ tor_free(xof);
}
/* DH */
@@ -1862,6 +2075,71 @@ static BIGNUM *dh_param_p_tls = NULL;
/** Shared G parameter for our DH key exchanges. */
static BIGNUM *dh_param_g = NULL;
+/** Validate a given set of Diffie-Hellman parameters. This is moderately
+ * computationally expensive (milliseconds), so should only be called when
+ * the DH parameters change. Returns 0 on success, * -1 on failure.
+ */
+static int
+crypto_validate_dh_params(const BIGNUM *p, const BIGNUM *g)
+{
+ DH *dh = NULL;
+ int ret = -1;
+
+ /* Copy into a temporary DH object. */
+ if (!(dh = DH_new()))
+ goto out;
+ if (!(dh->p = BN_dup(p)))
+ goto out;
+ if (!(dh->g = BN_dup(g)))
+ goto out;
+
+ /* Perform the validation. */
+ int codes = 0;
+ if (!DH_check(dh, &codes))
+ goto out;
+ if (BN_is_word(dh->g, DH_GENERATOR_2)) {
+ /* Per https://wiki.openssl.org/index.php/Diffie-Hellman_parameters
+ *
+ * OpenSSL checks the prime is congruent to 11 when g = 2; while the
+ * IETF's primes are congruent to 23 when g = 2.
+ */
+ BN_ULONG residue = BN_mod_word(dh->p, 24);
+ if (residue == 11 || residue == 23)
+ codes &= ~DH_NOT_SUITABLE_GENERATOR;
+ }
+ if (codes != 0) /* Specifics on why the params suck is irrelevant. */
+ goto out;
+
+ /* Things are probably not evil. */
+ ret = 0;
+
+ out:
+ if (dh)
+ DH_free(dh);
+ return ret;
+}
+
+/** Set the global Diffie-Hellman generator, used for both TLS and internal
+ * DH stuff.
+ */
+static void
+crypto_set_dh_generator(void)
+{
+ BIGNUM *generator;
+ int r;
+
+ if (dh_param_g)
+ return;
+
+ generator = BN_new();
+ tor_assert(generator);
+
+ r = BN_set_word(generator, DH_GENERATOR);
+ tor_assert(r);
+
+ dh_param_g = generator;
+}
+
/** Set the global TLS Diffie-Hellman modulus. Use the Apache mod_ssl DH
* modulus. */
void
@@ -1894,6 +2172,8 @@ crypto_set_tls_dh_prime(void)
tor_assert(tls_prime);
dh_param_p_tls = tls_prime;
+ crypto_set_dh_generator();
+ tor_assert(0 == crypto_validate_dh_params(dh_param_p_tls, dh_param_g));
}
/** Initialize dh_param_p and dh_param_g if they are not already
@@ -1901,18 +2181,13 @@ crypto_set_tls_dh_prime(void)
static void
init_dh_param(void)
{
- BIGNUM *circuit_dh_prime, *generator;
+ BIGNUM *circuit_dh_prime;
int r;
if (dh_param_p && dh_param_g)
return;
circuit_dh_prime = BN_new();
- generator = BN_new();
- tor_assert(circuit_dh_prime && generator);
-
- /* Set our generator for all DH parameters */
- r = BN_set_word(generator, DH_GENERATOR);
- tor_assert(r);
+ tor_assert(circuit_dh_prime);
/* This is from rfc2409, section 6.2. It's a safe prime, and
supposedly it equals:
@@ -1928,7 +2203,8 @@ init_dh_param(void)
/* Set the new values as the global DH parameters. */
dh_param_p = circuit_dh_prime;
- dh_param_g = generator;
+ crypto_set_dh_generator();
+ tor_assert(0 == crypto_validate_dh_params(dh_param_p, dh_param_g));
if (!dh_param_p_tls) {
crypto_set_tls_dh_prime();
@@ -1941,7 +2217,8 @@ init_dh_param(void)
*/
#define DH_PRIVATE_KEY_BITS 320
-/** Allocate and return a new DH object for a key exchange.
+/** Allocate and return a new DH object for a key exchange. Returns NULL on
+ * failure.
*/
crypto_dh_t *
crypto_dh_new(int dh_type)
@@ -2164,7 +2441,7 @@ int
crypto_expand_key_material_TAP(const uint8_t *key_in, size_t key_in_len,
uint8_t *key_out, size_t key_out_len)
{
- int i;
+ int i, r = -1;
uint8_t *cp, *tmp = tor_malloc(key_in_len+1);
uint8_t digest[DIGEST_LEN];
@@ -2176,19 +2453,16 @@ crypto_expand_key_material_TAP(const uint8_t *key_in, size_t key_in_len,
++i, cp += DIGEST_LEN) {
tmp[key_in_len] = i;
if (crypto_digest((char*)digest, (const char *)tmp, key_in_len+1))
- goto err;
+ goto exit;
memcpy(cp, digest, MIN(DIGEST_LEN, key_out_len-(cp-key_out)));
}
- memwipe(tmp, 0, key_in_len+1);
- tor_free(tmp);
- memwipe(digest, 0, sizeof(digest));
- return 0;
- err:
+ r = 0;
+ exit:
memwipe(tmp, 0, key_in_len+1);
tor_free(tmp);
memwipe(digest, 0, sizeof(digest));
- return -1;
+ return r;
}
/** Expand some secret key material according to RFC5869, using SHA256 as the
@@ -2196,7 +2470,7 @@ crypto_expand_key_material_TAP(const uint8_t *key_in, size_t key_in_len,
* secret key material; the <b>salt_in_len</b> bytes at <b>salt_in</b> and the
* <b>info_in_len</b> bytes in <b>info_in_len</b> are the algorithm's "salt"
* and "info" parameters respectively. On success, write <b>key_out_len</b>
- * bytes to <b>key_out</b> and return 0. On failure, return -1.
+ * bytes to <b>key_out</b> and return 0. Assert on failure.
*/
int
crypto_expand_key_material_rfc5869_sha256(
@@ -2280,23 +2554,18 @@ crypto_seed_weak_rng(tor_weak_rng_t *rng)
}
/** Try to get <b>out_len</b> bytes of the strongest entropy we can generate,
- * storing it into <b>out</b>.
+ * via system calls, storing it into <b>out</b>. Return 0 on success, -1 on
+ * failure. A maximum request size of 256 bytes is imposed.
*/
-int
-crypto_strongest_rand(uint8_t *out, size_t out_len)
+static int
+crypto_strongest_rand_syscall(uint8_t *out, size_t out_len)
{
-#ifdef _WIN32
+ tor_assert(out_len <= MAX_STRONGEST_RAND_SIZE);
+
+#if defined(_WIN32)
static int provider_set = 0;
static HCRYPTPROV provider;
-#else
- static const char *filenames[] = {
- "/dev/srandom", "/dev/urandom", "/dev/random", NULL
- };
- int fd, i;
- size_t n;
-#endif
-#ifdef _WIN32
if (!provider_set) {
if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT)) {
@@ -2311,7 +2580,84 @@ crypto_strongest_rand(uint8_t *out, size_t out_len)
}
return 0;
+#elif defined(__linux__) && defined(SYS_getrandom)
+ static int getrandom_works = 1; /* Be optimitic about our chances... */
+
+ /* getrandom() isn't as straight foward as getentropy(), and has
+ * no glibc wrapper.
+ *
+ * As far as I can tell from getrandom(2) and the source code, the
+ * requests we issue will always succeed (though it will block on the
+ * call if /dev/urandom isn't seeded yet), since we are NOT specifying
+ * GRND_NONBLOCK and the request is <= 256 bytes.
+ *
+ * The manpage is unclear on what happens if a signal interrupts the call
+ * while the request is blocked due to lack of entropy....
+ *
+ * We optimistically assume that getrandom() is available and functional
+ * because it is the way of the future, and 2 branch mispredicts pale in
+ * comparision to the overheads involved with failing to open
+ * /dev/srandom followed by opening and reading from /dev/urandom.
+ */
+ if (PREDICT_LIKELY(getrandom_works)) {
+ long ret;
+ /* A flag of '0' here means to read from '/dev/urandom', and to
+ * block if insufficient entropy is available to service the
+ * request.
+ */
+ const unsigned int flags = 0;
+ do {
+ ret = syscall(SYS_getrandom, out, out_len, flags);
+ } while (ret == -1 && ((errno == EINTR) ||(errno == EAGAIN)));
+
+ if (PREDICT_UNLIKELY(ret == -1)) {
+ tor_assert(errno != EAGAIN);
+ tor_assert(errno != EINTR);
+
+ /* Probably ENOSYS. */
+ log_warn(LD_CRYPTO, "Can't get entropy from getrandom().");
+ getrandom_works = 0; /* Don't bother trying again. */
+ return -1;
+ }
+
+ tor_assert(ret == (long)out_len);
+ return 0;
+ }
+
+ return -1; /* getrandom() previously failed unexpectedly. */
+#elif defined(HAVE_GETENTROPY)
+ /* getentropy() is what Linux's getrandom() wants to be when it grows up.
+ * the only gotcha is that requests are limited to 256 bytes.
+ */
+ return getentropy(out, out_len);
+#else
+ (void) out;
+#endif
+
+ /* This platform doesn't have a supported syscall based random. */
+ return -1;
+}
+
+/** Try to get <b>out_len</b> bytes of the strongest entropy we can generate,
+ * via the per-platform fallback mechanism, storing it into <b>out</b>.
+ * Return 0 on success, -1 on failure. A maximum request size of 256 bytes
+ * is imposed.
+ */
+static int
+crypto_strongest_rand_fallback(uint8_t *out, size_t out_len)
+{
+#ifdef _WIN32
+ /* Windows exclusively uses crypto_strongest_rand_syscall(). */
+ (void)out;
+ (void)out_len;
+ return -1;
#else
+ static const char *filenames[] = {
+ "/dev/srandom", "/dev/urandom", "/dev/random", NULL
+ };
+ int fd, i;
+ size_t n;
+
for (i = 0; filenames[i]; ++i) {
log_debug(LD_FS, "Opening %s for entropy", filenames[i]);
fd = open(sandbox_intern_string(filenames[i]), O_RDONLY, 0);
@@ -2329,14 +2675,95 @@ crypto_strongest_rand(uint8_t *out, size_t out_len)
return 0;
}
- log_warn(LD_CRYPTO, "Cannot get strong entropy: no entropy source found.");
return -1;
#endif
}
+/** Try to get <b>out_len</b> bytes of the strongest entropy we can generate,
+ * storing it into <b>out</b>. Return 0 on success, -1 on failure. A maximum
+ * request size of 256 bytes is imposed.
+ */
+static int
+crypto_strongest_rand_raw(uint8_t *out, size_t out_len)
+{
+ static const size_t sanity_min_size = 16;
+ static const int max_attempts = 3;
+ tor_assert(out_len <= MAX_STRONGEST_RAND_SIZE);
+
+ /* For buffers >= 16 bytes (128 bits), we sanity check the output by
+ * zero filling the buffer and ensuring that it actually was at least
+ * partially modified.
+ *
+ * Checking that any individual byte is non-zero seems like it would
+ * fail too often (p = out_len * 1/256) for comfort, but this is an
+ * "adjust according to taste" sort of check.
+ */
+ memwipe(out, 0, out_len);
+ for (int i = 0; i < max_attempts; i++) {
+ /* Try to use the syscall/OS favored mechanism to get strong entropy. */
+ if (crypto_strongest_rand_syscall(out, out_len) != 0) {
+ /* Try to use the less-favored mechanism to get strong entropy. */
+ if (crypto_strongest_rand_fallback(out, out_len) != 0) {
+ /* Welp, we tried. Hopefully the calling code terminates the process
+ * since we're basically boned without good entropy.
+ */
+ log_warn(LD_CRYPTO,
+ "Cannot get strong entropy: no entropy source found.");
+ return -1;
+ }
+ }
+
+ if ((out_len < sanity_min_size) || !tor_mem_is_zero((char*)out, out_len))
+ return 0;
+ }
+
+ /* We tried max_attempts times to fill a buffer >= 128 bits long,
+ * and each time it returned all '0's. Either the system entropy
+ * source is busted, or the user should go out and buy a ticket to
+ * every lottery on the planet.
+ */
+ log_warn(LD_CRYPTO, "Strong OS entropy returned all zero buffer.");
+ return -1;
+}
+
+/** Try to get <b>out_len</b> bytes of the strongest entropy we can generate,
+ * storing it into <b>out</b>.
+ */
+void
+crypto_strongest_rand(uint8_t *out, size_t out_len)
+{
+#define DLEN SHA512_DIGEST_LENGTH
+ /* We're going to hash DLEN bytes from the system RNG together with some
+ * bytes from the openssl PRNG, in order to yield DLEN bytes.
+ */
+ uint8_t inp[DLEN*2];
+ uint8_t tmp[DLEN];
+ tor_assert(out);
+ while (out_len) {
+ crypto_rand((char*) inp, DLEN);
+ if (crypto_strongest_rand_raw(inp+DLEN, DLEN) < 0) {
+ log_err(LD_CRYPTO, "Failed to load strong entropy when generating an "
+ "important key. Exiting.");
+ /* Die with an assertion so we get a stack trace. */
+ tor_assert(0);
+ }
+ if (out_len >= DLEN) {
+ SHA512(inp, sizeof(inp), out);
+ out += DLEN;
+ out_len -= DLEN;
+ } else {
+ SHA512(inp, sizeof(inp), tmp);
+ memcpy(out, tmp, out_len);
+ break;
+ }
+ }
+ memwipe(tmp, 0, sizeof(tmp));
+ memwipe(inp, 0, sizeof(inp));
+#undef DLEN
+}
+
/** Seed OpenSSL's random number generator with bytes from the operating
- * system. <b>startup</b> should be true iff we have just started Tor and
- * have not yet allocated a bunch of fds. Return 0 on success, -1 on failure.
+ * system. Return 0 on success, -1 on failure.
*/
int
crypto_seed_rng(void)
@@ -2351,41 +2778,51 @@ crypto_seed_rng(void)
if (rand_poll_ok == 0)
log_warn(LD_CRYPTO, "RAND_poll() failed.");
- load_entropy_ok = !crypto_strongest_rand(buf, sizeof(buf));
+ load_entropy_ok = !crypto_strongest_rand_raw(buf, sizeof(buf));
if (load_entropy_ok) {
RAND_seed(buf, sizeof(buf));
}
memwipe(buf, 0, sizeof(buf));
- if (rand_poll_ok || load_entropy_ok)
+ if ((rand_poll_ok || load_entropy_ok) && RAND_status() == 1)
return 0;
else
return -1;
}
-/** Write <b>n</b> bytes of strong random data to <b>to</b>. Return 0 on
- * success, -1 on failure, with support for mocking for unit tests.
+/** Write <b>n</b> bytes of strong random data to <b>to</b>. Supports mocking
+ * for unit tests.
+ *
+ * This function is not allowed to fail; if it would fail to generate strong
+ * entropy, it must terminate the process instead.
*/
-MOCK_IMPL(int,
+MOCK_IMPL(void,
crypto_rand, (char *to, size_t n))
{
- return crypto_rand_unmocked(to, n);
+ crypto_rand_unmocked(to, n);
}
-/** Write <b>n</b> bytes of strong random data to <b>to</b>. Return 0 on
- * success, -1 on failure. Most callers will want crypto_rand instead.
+/** Write <b>n</b> bytes of strong random data to <b>to</b>. Most callers
+ * will want crypto_rand instead.
+ *
+ * This function is not allowed to fail; if it would fail to generate strong
+ * entropy, it must terminate the process instead.
*/
-int
+void
crypto_rand_unmocked(char *to, size_t n)
{
int r;
+ if (n == 0)
+ return;
+
tor_assert(n < INT_MAX);
tor_assert(to);
r = RAND_bytes((unsigned char*)to, (int)n);
- if (r == 0)
- crypto_log_errors(LOG_WARN, "generating random data");
- return (r == 1) ? 0 : -1;
+ /* We consider a PRNG failure non-survivable. Let's assert so that we get a
+ * stack trace about where it happened.
+ */
+ tor_assert(r >= 0);
}
/** Return a pseudorandom integer, chosen uniformly from the values
@@ -2411,8 +2848,8 @@ crypto_rand_int(unsigned int max)
}
}
-/** Return a pseudorandom integer, chosen uniformly from the values <i>i</i>
- * such that <b>min</b> &lt;= <i>i</i> &lt <b>max</b>.
+/** Return a pseudorandom integer, chosen uniformly from the values i such
+ * that min <= i < max.
*
* <b>min</b> MUST be in range [0, <b>max</b>).
* <b>max</b> MUST be in range (min, INT_MAX].
@@ -2489,7 +2926,7 @@ crypto_rand_double(void)
/** Generate and return a new random hostname starting with <b>prefix</b>,
* ending with <b>suffix</b>, and containing no fewer than
* <b>min_rand_len</b> and no more than <b>max_rand_len</b> random base32
- * characters between.
+ * characters. Does not check for failure.
*
* Clip <b>max_rand_len</b> to MAX_DNS_LABEL_SIZE.
**/
@@ -2587,13 +3024,32 @@ memwipe(void *mem, uint8_t byte, size_t sz)
* have this function call "memset". A smart compiler could inline it, then
* eliminate dead memsets, and declare itself to be clever. */
+#if defined(SecureZeroMemory) || defined(HAVE_SECUREZEROMEMORY)
+ /* Here's what you do on windows. */
+ SecureZeroMemory(mem,sz);
+#elif defined(HAVE_RTLSECUREZEROMEMORY)
+ RtlSecureZeroMemory(mem,sz);
+#elif defined(HAVE_EXPLICIT_BZERO)
+ /* The BSDs provide this. */
+ explicit_bzero(mem, sz);
+#elif defined(HAVE_MEMSET_S)
+ /* This is in the C99 standard. */
+ memset_s(mem, sz, 0, sz);
+#else
/* This is a slow and ugly function from OpenSSL that fills 'mem' with junk
* based on the pointer value, then uses that junk to update a global
* variable. It's an elaborate ruse to trick the compiler into not
* optimizing out the "wipe this memory" code. Read it if you like zany
* programming tricks! In later versions of Tor, we should look for better
- * not-optimized-out memory wiping stuff. */
+ * not-optimized-out memory wiping stuff...
+ *
+ * ...or maybe not. In practice, there are pure-asm implementations of
+ * OPENSSL_cleanse() on most platforms, which ought to do the job.
+ **/
+
OPENSSL_cleanse(mem, sz);
+#endif
+
/* Just in case some caller of memwipe() is relying on getting a buffer
* filled with a particular value, fill the buffer.
*
@@ -2628,6 +3084,10 @@ openssl_locking_cb_(int mode, int n, const char *file, int line)
tor_mutex_release(openssl_mutexes_[n]);
}
+#if 0
+/* This code is disabled, because OpenSSL never actually uses these callbacks.
+ */
+
/** OpenSSL helper type: wraps a Tor mutex so that OpenSSL can use it
* as a lock. */
struct CRYPTO_dynlock_value {
@@ -2672,6 +3132,7 @@ openssl_dynlock_destroy_cb_(struct CRYPTO_dynlock_value *v,
tor_mutex_free(v->lock);
tor_free(v);
}
+#endif
static void
tor_set_openssl_thread_id(CRYPTO_THREADID *threadid)
@@ -2681,7 +3142,7 @@ tor_set_openssl_thread_id(CRYPTO_THREADID *threadid)
/** @{ */
/** Helper: Construct mutexes, and set callbacks to help OpenSSL handle being
- * multithreaded. */
+ * multithreaded. Returns 0. */
static int
setup_openssl_threading(void)
{
@@ -2693,23 +3154,22 @@ setup_openssl_threading(void)
openssl_mutexes_[i] = tor_mutex_new();
CRYPTO_set_locking_callback(openssl_locking_cb_);
CRYPTO_THREADID_set_callback(tor_set_openssl_thread_id);
+#if 0
CRYPTO_set_dynlock_create_callback(openssl_dynlock_create_cb_);
CRYPTO_set_dynlock_lock_callback(openssl_dynlock_lock_cb_);
CRYPTO_set_dynlock_destroy_callback(openssl_dynlock_destroy_cb_);
+#endif
return 0;
}
-/** Uninitialize the crypto library. Return 0 on success, -1 on failure.
+/** Uninitialize the crypto library. Return 0 on success. Does not detect
+ * failure.
*/
int
crypto_global_cleanup(void)
{
EVP_cleanup();
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)
ERR_remove_thread_state(NULL);
-#else
- ERR_remove_state(0);
-#endif
ERR_free_strings();
if (dh_param_p)
diff --git a/src/common/crypto.h b/src/common/crypto.h
index 6256f7346b..2da7f9e2a1 100644
--- a/src/common/crypto.h
+++ b/src/common/crypto.h
@@ -16,6 +16,7 @@
#include <stdio.h>
#include "torint.h"
#include "testsupport.h"
+#include "compat.h"
/*
Macro to create an arbitrary OpenSSL version number as used by
@@ -54,6 +55,8 @@
/** Length of the output of our second (improved) message digests. (For now
* this is just sha256, but it could be any other 256-bit digest.) */
#define DIGEST256_LEN 32
+/** Length of the output of our 64-bit optimized message digests (SHA512). */
+#define DIGEST512_LEN 64
/** Length of our symmetric cipher's keys. */
#define CIPHER_KEY_LEN 16
/** Length of our symmetric cipher's IV. */
@@ -69,6 +72,9 @@
/** Length of a sha256 message digest when encoded in base64 with trailing =
* signs removed. */
#define BASE64_DIGEST256_LEN 43
+/** Length of a sha512 message digest when encoded in base64 with trailing =
+ * signs removed. */
+#define BASE64_DIGEST512_LEN 86
/** Constant used to indicate OAEP padding for public-key encryption */
#define PK_PKCS1_OAEP_PADDING 60002
@@ -83,43 +89,49 @@
#define HEX_DIGEST_LEN 40
/** Length of hex encoding of SHA256 digest, not including final NUL. */
#define HEX_DIGEST256_LEN 64
+/** Length of hex encoding of SHA512 digest, not including final NUL. */
+#define HEX_DIGEST512_LEN 128
typedef enum {
DIGEST_SHA1 = 0,
DIGEST_SHA256 = 1,
+ DIGEST_SHA512 = 2,
+ DIGEST_SHA3_256 = 3,
+ DIGEST_SHA3_512 = 4,
} digest_algorithm_t;
-#define N_DIGEST_ALGORITHMS (DIGEST_SHA256+1)
-#define digest_algorithm_bitfield_t ENUM_BF(digest_algorithm_t)
+#define N_DIGEST_ALGORITHMS (DIGEST_SHA3_512+1)
+#define N_COMMON_DIGEST_ALGORITHMS (DIGEST_SHA256+1)
-/** A set of all the digests we know how to compute, taken on a single
- * string. Any digests that are shorter than 256 bits are right-padded
+/** A set of all the digests we commonly compute, taken on a single
+ * string. Any digests that are shorter than 512 bits are right-padded
* with 0 bits.
*
- * Note that this representation wastes 12 bytes for the SHA1 case, so
+ * Note that this representation wastes 44 bytes for the SHA1 case, so
* don't use it for anything where we need to allocate a whole bunch at
* once.
**/
typedef struct {
- char d[N_DIGEST_ALGORITHMS][DIGEST256_LEN];
-} digests_t;
+ char d[N_COMMON_DIGEST_ALGORITHMS][DIGEST256_LEN];
+} common_digests_t;
typedef struct crypto_pk_t crypto_pk_t;
typedef struct crypto_cipher_t crypto_cipher_t;
typedef struct crypto_digest_t crypto_digest_t;
+typedef struct crypto_xof_t crypto_xof_t;
typedef struct crypto_dh_t crypto_dh_t;
/* global state */
const char * crypto_openssl_get_version_str(void);
const char * crypto_openssl_get_header_version_str(void);
-int crypto_early_init(void);
+int crypto_early_init(void) ATTR_WUR;
int crypto_global_init(int hardwareAccel,
const char *accelName,
- const char *accelPath);
+ const char *accelPath) ATTR_WUR;
void crypto_thread_cleanup(void);
int crypto_global_cleanup(void);
/* environment setup */
-crypto_pk_t *crypto_pk_new(void);
+MOCK_DECL(crypto_pk_t *,crypto_pk_new,(void));
void crypto_pk_free(crypto_pk_t *env);
void crypto_set_tls_dh_prime(void);
@@ -128,7 +140,7 @@ crypto_cipher_t *crypto_cipher_new_with_iv(const char *key, const char *iv);
void crypto_cipher_free(crypto_cipher_t *env);
/* public key crypto */
-int crypto_pk_generate_key_with_bits(crypto_pk_t *env, int bits);
+MOCK_DECL(int, crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits));
#define crypto_pk_generate_key(env) \
crypto_pk_generate_key_with_bits((env), (PK_BYTES*8))
@@ -180,7 +192,8 @@ int crypto_pk_private_hybrid_decrypt(crypto_pk_t *env, char *to,
int crypto_pk_asn1_encode(crypto_pk_t *pk, char *dest, size_t dest_len);
crypto_pk_t *crypto_pk_asn1_decode(const char *str, size_t len);
int crypto_pk_get_digest(const crypto_pk_t *pk, char *digest_out);
-int crypto_pk_get_all_digests(crypto_pk_t *pk, digests_t *digests_out);
+int crypto_pk_get_common_digests(crypto_pk_t *pk,
+ common_digests_t *digests_out);
int crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out,int add_space);
int crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out);
@@ -194,7 +207,7 @@ int crypto_cipher_encrypt(crypto_cipher_t *env, char *to,
const char *from, size_t fromlen);
int crypto_cipher_decrypt(crypto_cipher_t *env, char *to,
const char *from, size_t fromlen);
-int crypto_cipher_crypt_inplace(crypto_cipher_t *env, char *d, size_t len);
+void crypto_cipher_crypt_inplace(crypto_cipher_t *env, char *d, size_t len);
int crypto_cipher_encrypt_with_iv(const char *key,
char *to, size_t tolen,
@@ -207,7 +220,9 @@ int crypto_cipher_decrypt_with_iv(const char *key,
int crypto_digest(char *digest, const char *m, size_t len);
int crypto_digest256(char *digest, const char *m, size_t len,
digest_algorithm_t algorithm);
-int crypto_digest_all(digests_t *ds_out, const char *m, size_t len);
+int crypto_digest512(char *digest, const char *m, size_t len,
+ digest_algorithm_t algorithm);
+int crypto_common_digests(common_digests_t *ds_out, const char *m, size_t len);
struct smartlist_t;
void crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
const char *prepend,
@@ -221,6 +236,7 @@ const char *crypto_digest_algorithm_get_name(digest_algorithm_t alg);
int crypto_digest_algorithm_parse_name(const char *name);
crypto_digest_t *crypto_digest_new(void);
crypto_digest_t *crypto_digest256_new(digest_algorithm_t algorithm);
+crypto_digest_t *crypto_digest512_new(digest_algorithm_t algorithm);
void crypto_digest_free(crypto_digest_t *digest);
void crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
size_t len);
@@ -232,6 +248,10 @@ void crypto_digest_assign(crypto_digest_t *into,
void crypto_hmac_sha256(char *hmac_out,
const char *key, size_t key_len,
const char *msg, size_t msg_len);
+crypto_xof_t *crypto_xof_new(void);
+void crypto_xof_add_bytes(crypto_xof_t *xof, const uint8_t *data, size_t len);
+void crypto_xof_squeeze_bytes(crypto_xof_t *xof, uint8_t *out, size_t len);
+void crypto_xof_free(crypto_xof_t *xof);
/* Key negotiation */
#define DH_TYPE_CIRCUIT 1
@@ -258,10 +278,10 @@ int crypto_expand_key_material_rfc5869_sha256(
uint8_t *key_out, size_t key_out_len);
/* random numbers */
-int crypto_seed_rng(void);
-MOCK_DECL(int,crypto_rand,(char *to, size_t n));
-int crypto_rand_unmocked(char *to, size_t n);
-int crypto_strongest_rand(uint8_t *out, size_t out_len);
+int crypto_seed_rng(void) ATTR_WUR;
+MOCK_DECL(void,crypto_rand,(char *to, size_t n));
+void crypto_rand_unmocked(char *to, size_t n);
+void crypto_strongest_rand(uint8_t *out, size_t out_len);
int crypto_rand_int(unsigned int max);
int crypto_rand_int_range(unsigned int min, unsigned int max);
uint64_t crypto_rand_uint64_range(uint64_t min, uint64_t max);
@@ -289,11 +309,15 @@ struct evp_pkey_st;
struct dh_st;
struct rsa_st *crypto_pk_get_rsa_(crypto_pk_t *env);
crypto_pk_t *crypto_new_pk_from_rsa_(struct rsa_st *rsa);
-struct evp_pkey_st *crypto_pk_get_evp_pkey_(crypto_pk_t *env,
- int private);
+MOCK_DECL(struct evp_pkey_st *, crypto_pk_get_evp_pkey_,(crypto_pk_t *env,
+ int private));
struct dh_st *crypto_dh_get_dh_(crypto_dh_t *dh);
void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in);
+#ifdef CRYPTO_PRIVATE
+STATIC int crypto_force_rand_ssleay(void);
+#endif
+
#endif
diff --git a/src/common/crypto_curve25519.c b/src/common/crypto_curve25519.c
index ac0b08a552..2002483265 100644
--- a/src/common/crypto_curve25519.c
+++ b/src/common/crypto_curve25519.c
@@ -111,19 +111,11 @@ curve25519_public_key_is_ok(const curve25519_public_key_t *key)
int
curve25519_rand_seckey_bytes(uint8_t *out, int extra_strong)
{
- uint8_t k_tmp[CURVE25519_SECKEY_LEN];
+ if (extra_strong)
+ crypto_strongest_rand(out, CURVE25519_SECKEY_LEN);
+ else
+ crypto_rand((char*)out, CURVE25519_SECKEY_LEN);
- if (crypto_rand((char*)out, CURVE25519_SECKEY_LEN) < 0)
- return -1;
- if (extra_strong && !crypto_strongest_rand(k_tmp, CURVE25519_SECKEY_LEN)) {
- /* If they asked for extra-strong entropy and we have some, use it as an
- * HMAC key to improve not-so-good entropy rather than using it directly,
- * just in case the extra-strong entropy is less amazing than we hoped. */
- crypto_hmac_sha256((char*) out,
- (const char *)k_tmp, sizeof(k_tmp),
- (const char *)out, CURVE25519_SECKEY_LEN);
- }
- memwipe(k_tmp, 0, sizeof(k_tmp));
return 0;
}
diff --git a/src/common/crypto_ed25519.c b/src/common/crypto_ed25519.c
index 1749efc34c..9df665f66a 100644
--- a/src/common/crypto_ed25519.c
+++ b/src/common/crypto_ed25519.c
@@ -96,6 +96,28 @@ get_ed_impl(void)
return ed25519_impl;
}
+#ifdef TOR_UNIT_TESTS
+static const ed25519_impl_t *saved_ed25519_impl = NULL;
+void
+crypto_ed25519_testing_force_impl(const char *name)
+{
+ tor_assert(saved_ed25519_impl == NULL);
+ saved_ed25519_impl = ed25519_impl;
+ if (! strcmp(name, "donna")) {
+ ed25519_impl = &impl_donna;
+ } else {
+ tor_assert(!strcmp(name, "ref10"));
+ ed25519_impl = &impl_ref10;
+ }
+}
+void
+crypto_ed25519_testing_restore_impl(void)
+{
+ ed25519_impl = saved_ed25519_impl;
+ saved_ed25519_impl = NULL;
+}
+#endif
+
/**
* Initialize a new ed25519 secret key in <b>seckey_out</b>. If
* <b>extra_strong</b>, take the RNG inputs directly from the operating
@@ -107,7 +129,9 @@ ed25519_secret_key_generate(ed25519_secret_key_t *seckey_out,
{
int r;
uint8_t seed[32];
- if (! extra_strong || crypto_strongest_rand(seed, sizeof(seed)) < 0)
+ if (extra_strong)
+ crypto_strongest_rand(seed, sizeof(seed));
+ else
crypto_rand((char*)seed, sizeof(seed));
r = get_ed_impl()->seckey_expand(seckey_out->seckey, seed);
diff --git a/src/common/crypto_ed25519.h b/src/common/crypto_ed25519.h
index bdac12eb27..4fa7ea11cf 100644
--- a/src/common/crypto_ed25519.h
+++ b/src/common/crypto_ed25519.h
@@ -111,5 +111,10 @@ int ed25519_pubkey_eq(const ed25519_public_key_t *key1,
void ed25519_set_impl_params(int use_donna);
void ed25519_init(void);
+#ifdef TOR_UNIT_TESTS
+void crypto_ed25519_testing_force_impl(const char *name);
+void crypto_ed25519_testing_restore_impl(void);
+#endif
+
#endif
diff --git a/src/common/di_ops.c b/src/common/di_ops.c
index c9d1350880..70f2da7377 100644
--- a/src/common/di_ops.c
+++ b/src/common/di_ops.c
@@ -25,6 +25,9 @@
int
tor_memcmp(const void *a, const void *b, size_t len)
{
+#ifdef HAVE_TIMINGSAFE_MEMCMP
+ return timingsafe_memcmp(a, b, len);
+#else
const uint8_t *x = a;
const uint8_t *y = b;
size_t i = len;
@@ -83,6 +86,7 @@ tor_memcmp(const void *a, const void *b, size_t len)
}
return retval;
+#endif /* timingsafe_memcmp */
}
/**
diff --git a/src/common/include.am b/src/common/include.am
index 7de93ba2ac..5afb30da6a 100644
--- a/src/common/include.am
+++ b/src/common/include.am
@@ -78,7 +78,8 @@ LIBOR_A_SOURCES = \
$(threads_impl_source) \
$(readpassphrase_source)
-src/common/log.o: micro-revision.i
+src/common/src_common_libor_testing_a-log.$(OBJEXT) \
+ src/common/log.$(OBJEXT): micro-revision.i
LIBOR_CRYPTO_A_SOURCES = \
src/common/aes.c \
@@ -118,6 +119,7 @@ COMMONHEADERS = \
src/common/ciphers.inc \
src/common/compat.h \
src/common/compat_libevent.h \
+ src/common/compat_openssl.h \
src/common/compat_threads.h \
src/common/container.h \
src/common/crypto.h \
diff --git a/src/common/log.c b/src/common/log.c
index e23691b6ab..4779751543 100644
--- a/src/common/log.c
+++ b/src/common/log.c
@@ -64,7 +64,7 @@ typedef struct logfile_t {
static void log_free(logfile_t *victim);
/** Helper: map a log severity to descriptive string. */
-static INLINE const char *
+static inline const char *
sev_to_string(int severity)
{
switch (severity) {
@@ -80,7 +80,7 @@ sev_to_string(int severity)
}
/** Helper: decide whether to include the function name in the log message. */
-static INLINE int
+static inline int
should_log_function_name(log_domain_mask_t domain, int severity)
{
switch (severity) {
@@ -149,10 +149,14 @@ static int pretty_fn_has_parens = 0;
/** Lock the log_mutex to prevent others from changing the logfile_t list */
#define LOCK_LOGS() STMT_BEGIN \
+ tor_assert(log_mutex_initialized); \
tor_mutex_acquire(&log_mutex); \
STMT_END
/** Unlock the log_mutex */
-#define UNLOCK_LOGS() STMT_BEGIN tor_mutex_release(&log_mutex); STMT_END
+#define UNLOCK_LOGS() STMT_BEGIN \
+ tor_assert(log_mutex_initialized); \
+ tor_mutex_release(&log_mutex); \
+ STMT_END
/** What's the lowest log level anybody cares about? Checking this lets us
* bail out early from log_debug if we aren't debugging. */
@@ -163,7 +167,7 @@ static void close_log(logfile_t *victim);
static char *domain_to_string(log_domain_mask_t domain,
char *buf, size_t buflen);
-static INLINE char *format_msg(char *buf, size_t buf_len,
+static inline char *format_msg(char *buf, size_t buf_len,
log_domain_mask_t domain, int severity, const char *funcname,
const char *suffix,
const char *format, va_list ap, size_t *msg_len_out)
@@ -199,7 +203,7 @@ set_log_time_granularity(int granularity_msec)
/** Helper: Write the standard prefix for log lines to a
* <b>buf_len</b> character buffer in <b>buf</b>.
*/
-static INLINE size_t
+static inline size_t
log_prefix_(char *buf, size_t buf_len, int severity)
{
time_t t;
@@ -278,7 +282,7 @@ const char bug_suffix[] = " (on Tor " VERSION
* than once.) Return a pointer to the first character of the message
* portion of the formatted string.
*/
-static INLINE char *
+static inline char *
format_msg(char *buf, size_t buf_len,
log_domain_mask_t domain, int severity, const char *funcname,
const char *suffix,
@@ -393,7 +397,7 @@ pending_log_message_free(pending_log_message_t *msg)
/** Return true iff <b>lf</b> would like to receive a message with the
* specified <b>severity</b> in the specified <b>domain</b>.
*/
-static INLINE int
+static inline int
logfile_wants_message(const logfile_t *lf, int severity,
log_domain_mask_t domain)
{
@@ -416,7 +420,7 @@ logfile_wants_message(const logfile_t *lf, int severity,
* we already deferred this message for pending callbacks and don't need to do
* it again. Otherwise, if we need to do it, do it, and set
* <b>callbacks_deferred</b> to 1. */
-static INLINE void
+static inline void
logfile_deliver(logfile_t *lf, const char *buf, size_t msg_len,
const char *msg_after_prefix, log_domain_mask_t domain,
int severity, int *callbacks_deferred)
@@ -482,9 +486,12 @@ logv,(int severity, log_domain_mask_t domain, const char *funcname,
/* check that severity is sane. Overrunning the masks array leads to
* interesting and hard to diagnose effects */
assert(severity >= LOG_ERR && severity <= LOG_DEBUG);
+ /* check that we've initialised the log mutex before we try to lock it */
+ assert(log_mutex_initialized);
LOCK_LOGS();
- if ((! (domain & LD_NOCB)) && smartlist_len(pending_cb_messages))
+ if ((! (domain & LD_NOCB)) && pending_cb_messages
+ && smartlist_len(pending_cb_messages))
flush_pending_log_callbacks();
if (queue_startup_messages &&
@@ -939,7 +946,7 @@ flush_pending_log_callbacks(void)
smartlist_t *messages, *messages_tmp;
LOCK_LOGS();
- if (0 == smartlist_len(pending_cb_messages)) {
+ if (!pending_cb_messages || 0 == smartlist_len(pending_cb_messages)) {
UNLOCK_LOGS();
return;
}
@@ -1097,14 +1104,25 @@ add_file_log(const log_severity_list_t *severity, const char *filename,
#ifdef HAVE_SYSLOG_H
/**
* Add a log handler to send messages to they system log facility.
+ *
+ * If this is the first log handler, opens syslog with ident Tor or
+ * Tor-<syslog_identity_tag> if that is not NULL.
*/
int
-add_syslog_log(const log_severity_list_t *severity)
+add_syslog_log(const log_severity_list_t *severity,
+ const char* syslog_identity_tag)
{
logfile_t *lf;
- if (syslog_count++ == 0)
+ if (syslog_count++ == 0) {
/* This is the first syslog. */
- openlog("Tor", LOG_PID | LOG_NDELAY, LOGFACILITY);
+ static char buf[256];
+ if (syslog_identity_tag) {
+ tor_snprintf(buf, sizeof(buf), "Tor-%s", syslog_identity_tag);
+ } else {
+ tor_snprintf(buf, sizeof(buf), "Tor");
+ }
+ openlog(buf, LOG_PID | LOG_NDELAY, LOGFACILITY);
+ }
lf = tor_malloc_zero(sizeof(logfile_t));
lf->fd = -1;
diff --git a/src/common/memarea.c b/src/common/memarea.c
index 6841ba54e7..a8e6d455d6 100644
--- a/src/common/memarea.c
+++ b/src/common/memarea.c
@@ -61,7 +61,7 @@
#endif
/** Increment <b>ptr</b> until it is aligned to MEMAREA_ALIGN. */
-static INLINE void *
+static inline void *
realign_pointer(void *ptr)
{
uintptr_t x = (uintptr_t)ptr;
diff --git a/src/common/procmon.c b/src/common/procmon.c
index 2d0f021724..346a0c6943 100644
--- a/src/common/procmon.c
+++ b/src/common/procmon.c
@@ -192,7 +192,8 @@ tor_process_monitor_new(struct event_base *base,
tor_procmon_callback_t cb, void *cb_arg,
const char **msg)
{
- tor_process_monitor_t *procmon = tor_malloc(sizeof(tor_process_monitor_t));
+ tor_process_monitor_t *procmon = tor_malloc_zero(
+ sizeof(tor_process_monitor_t));
struct parsed_process_specifier_t ppspec;
tor_assert(msg != NULL);
diff --git a/src/common/sandbox.c b/src/common/sandbox.c
index 950a92fbb3..d747a87563 100644
--- a/src/common/sandbox.c
+++ b/src/common/sandbox.c
@@ -199,6 +199,10 @@ static int filter_nopar_gen[] = {
SCMP_SYS(stat64),
#endif
+#ifdef __NR_getrandom
+ SCMP_SYS(getrandom),
+#endif
+
/*
* These socket syscalls are not required on x86_64 and not supported with
* some libseccomp versions (eg: 1.0.1)
diff --git a/src/common/torgzip.c b/src/common/torgzip.c
index 4f23407e23..5ba8ec4501 100644
--- a/src/common/torgzip.c
+++ b/src/common/torgzip.c
@@ -91,7 +91,7 @@ tor_zlib_get_header_version_str(void)
}
/** Return the 'bits' value to tell zlib to use <b>method</b>.*/
-static INLINE int
+static inline int
method_bits(compress_method_t method, zlib_compression_level_t level)
{
/* Bits+16 means "use gzip" in zlib >= 1.2 */
@@ -104,7 +104,7 @@ method_bits(compress_method_t method, zlib_compression_level_t level)
}
}
-static INLINE int
+static inline int
get_memlevel(zlib_compression_level_t level)
{
switch (level) {
diff --git a/src/common/torint.h b/src/common/torint.h
index 6171700898..480ba1a596 100644
--- a/src/common/torint.h
+++ b/src/common/torint.h
@@ -312,8 +312,6 @@ typedef uint32_t uintptr_t;
#ifndef TIME_MAX
-#ifdef TIME_T_IS_SIGNED
-
#if (SIZEOF_TIME_T == SIZEOF_INT)
#define TIME_MAX ((time_t)INT_MAX)
#elif (SIZEOF_TIME_T == SIZEOF_LONG)
@@ -321,20 +319,24 @@ typedef uint32_t uintptr_t;
#elif (SIZEOF_TIME_T == 8)
#define TIME_MAX ((time_t)INT64_MAX)
#else
-#error "Can't define (signed) TIME_MAX"
+#error "Can't define TIME_MAX"
#endif
-#else
-/* Unsigned case */
-#if (SIZEOF_TIME_T == 4)
-#define TIME_MAX ((time_t)UINT32_MAX)
+#endif /* ifndef(TIME_MAX) */
+
+#ifndef TIME_MIN
+
+#if (SIZEOF_TIME_T == SIZEOF_INT)
+#define TIME_MIN ((time_t)INT_MIN)
+#elif (SIZEOF_TIME_T == SIZEOF_LONG)
+#define TIME_MIN ((time_t)LONG_MIN)
#elif (SIZEOF_TIME_T == 8)
-#define TIME_MAX ((time_t)UINT64_MAX)
+#define TIME_MIN ((time_t)INT64_MIN)
#else
-#error "Can't define (unsigned) TIME_MAX"
+#error "Can't define TIME_MIN"
#endif
-#endif /* time_t_is_signed */
-#endif /* ifndef(TIME_MAX) */
+
+#endif /* ifndef(TIME_MIN) */
#ifndef SIZE_MAX
#if (SIZEOF_SIZE_T == 4)
diff --git a/src/common/torlog.h b/src/common/torlog.h
index 67edf14c04..3e8667895f 100644
--- a/src/common/torlog.h
+++ b/src/common/torlog.h
@@ -135,7 +135,8 @@ void add_stream_log(const log_severity_list_t *severity, const char *name,
int add_file_log(const log_severity_list_t *severity, const char *filename,
const int truncate);
#ifdef HAVE_SYSLOG_H
-int add_syslog_log(const log_severity_list_t *severity);
+int add_syslog_log(const log_severity_list_t *severity,
+ const char* syslog_identity_tag);
#endif
int add_callback_log(const log_severity_list_t *severity, log_callback cb);
void logs_set_domain_logging(int enabled);
@@ -183,25 +184,25 @@ void log_fn_ratelim_(struct ratelim_t *ratelim, int severity,
/** Log a message at level <b>severity</b>, using a pretty-printed version
* of the current function name. */
#define log_fn(severity, domain, args...) \
- log_fn_(severity, domain, __PRETTY_FUNCTION__, args)
+ log_fn_(severity, domain, __FUNCTION__, args)
/** As log_fn, but use <b>ratelim</b> (an instance of ratelim_t) to control
* the frequency at which messages can appear.
*/
#define log_fn_ratelim(ratelim, severity, domain, args...) \
- log_fn_ratelim_(ratelim, severity, domain, __PRETTY_FUNCTION__, args)
+ log_fn_ratelim_(ratelim, severity, domain, __FUNCTION__, args)
#define log_debug(domain, args...) \
STMT_BEGIN \
if (PREDICT_UNLIKELY(log_global_min_severity_ == LOG_DEBUG)) \
- log_fn_(LOG_DEBUG, domain, __PRETTY_FUNCTION__, args); \
+ log_fn_(LOG_DEBUG, domain, __FUNCTION__, args); \
STMT_END
#define log_info(domain, args...) \
- log_fn_(LOG_INFO, domain, __PRETTY_FUNCTION__, args)
+ log_fn_(LOG_INFO, domain, __FUNCTION__, args)
#define log_notice(domain, args...) \
- log_fn_(LOG_NOTICE, domain, __PRETTY_FUNCTION__, args)
+ log_fn_(LOG_NOTICE, domain, __FUNCTION__, args)
#define log_warn(domain, args...) \
- log_fn_(LOG_WARN, domain, __PRETTY_FUNCTION__, args)
+ log_fn_(LOG_WARN, domain, __FUNCTION__, args)
#define log_err(domain, args...) \
- log_fn_(LOG_ERR, domain, __PRETTY_FUNCTION__, args)
+ log_fn_(LOG_ERR, domain, __FUNCTION__, args)
#else /* ! defined(__GNUC__) */
diff --git a/src/common/tortls.c b/src/common/tortls.c
index 536043e558..8f2dc4bf2c 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -16,6 +16,8 @@
#include "orconfig.h"
+#define TORTLS_PRIVATE
+
#include <assert.h>
#ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include <winsock.h>"*/
#include <winsock2.h>
@@ -38,9 +40,6 @@
#include <openssl/opensslv.h>
#include "crypto.h"
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0)
-#error "We require OpenSSL >= 1.0.0"
-#endif
#ifdef OPENSSL_NO_EC
#error "We require OpenSSL with ECC support"
#endif
@@ -69,6 +68,7 @@
#include "compat_libevent.h"
#endif
+#define TORTLS_PRIVATE
#include "tortls.h"
#include "util.h"
#include "torlog.h"
@@ -80,11 +80,6 @@
#define X509_get_notAfter_const(cert) \
((const ASN1_TIME*) X509_get_notAfter((X509 *)cert))
-/* Enable the "v2" TLS handshake.
- */
-#define V2_HANDSHAKE_SERVER
-#define V2_HANDSHAKE_CLIENT
-
/* Copied from or.h */
#define LEGAL_NICKNAME_CHARACTERS \
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
@@ -113,29 +108,6 @@
#define SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0x0010
#endif
-/** Structure that we use for a single certificate. */
-struct tor_x509_cert_t {
- X509 *cert;
- uint8_t *encoded;
- size_t encoded_len;
- unsigned pkey_digests_set : 1;
- digests_t cert_digests;
- digests_t pkey_digests;
-};
-
-/** Holds a SSL_CTX object and related state used to configure TLS
- * connections.
- */
-typedef struct tor_tls_context_t {
- int refcnt;
- SSL_CTX *ctx;
- tor_x509_cert_t *my_link_cert;
- tor_x509_cert_t *my_id_cert;
- tor_x509_cert_t *my_auth_cert;
- crypto_pk_t *link_key;
- crypto_pk_t *auth_key;
-} tor_tls_context_t;
-
/** Return values for tor_tls_classify_client_ciphers.
*
* @{
@@ -154,60 +126,12 @@ typedef struct tor_tls_context_t {
#define CIPHERS_UNRESTRICTED 3
/** @} */
-#define TOR_TLS_MAGIC 0x71571571
-
-typedef enum {
- TOR_TLS_ST_HANDSHAKE, TOR_TLS_ST_OPEN, TOR_TLS_ST_GOTCLOSE,
- TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED, TOR_TLS_ST_RENEGOTIATE,
- TOR_TLS_ST_BUFFEREVENT
-} tor_tls_state_t;
-#define tor_tls_state_bitfield_t ENUM_BF(tor_tls_state_t)
-
-/** Holds a SSL object and its associated data. Members are only
- * accessed from within tortls.c.
- */
-struct tor_tls_t {
- uint32_t magic;
- tor_tls_context_t *context; /** A link to the context object for this tls. */
- SSL *ssl; /**< An OpenSSL SSL object. */
- int socket; /**< The underlying file descriptor for this TLS connection. */
- char *address; /**< An address to log when describing this connection. */
- tor_tls_state_bitfield_t state : 3; /**< The current SSL state,
- * depending on which operations
- * have completed successfully. */
- unsigned int isServer:1; /**< True iff this is a server-side connection */
- unsigned int wasV2Handshake:1; /**< True iff the original handshake for
- * this connection used the updated version
- * of the connection protocol (client sends
- * different cipher list, server sends only
- * one certificate). */
- /** True iff we should call negotiated_callback when we're done reading. */
- unsigned int got_renegotiate:1;
- /** Return value from tor_tls_classify_client_ciphers, or 0 if we haven't
- * called that function yet. */
- int8_t client_cipher_list_type;
- /** Incremented every time we start the server side of a handshake. */
- uint8_t server_handshake_count;
- size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last
- * time. */
- /** Last values retrieved from BIO_number_read()/write(); see
- * tor_tls_get_n_raw_bytes() for usage.
- */
- unsigned long last_write_count;
- unsigned long last_read_count;
- /** If set, a callback to invoke whenever the client tries to renegotiate
- * the handshake. */
- void (*negotiated_callback)(tor_tls_t *tls, void *arg);
- /** Argument to pass to negotiated_callback. */
- void *callback_arg;
-};
-
/** The ex_data index in which we store a pointer to an SSL object's
* corresponding tor_tls_t object. */
-static int tor_tls_object_ex_data_index = -1;
+STATIC int tor_tls_object_ex_data_index = -1;
/** Helper: Allocate tor_tls_object_ex_data_index. */
-static void
+STATIC void
tor_tls_allocate_tor_tls_object_ex_data_index(void)
{
if (tor_tls_object_ex_data_index == -1) {
@@ -219,7 +143,7 @@ tor_tls_allocate_tor_tls_object_ex_data_index(void)
/** Helper: given a SSL* pointer, return the tor_tls_t object using that
* pointer. */
-static INLINE tor_tls_t *
+STATIC inline tor_tls_t *
tor_tls_get_by_ssl(const SSL *ssl)
{
tor_tls_t *result = SSL_get_ex_data(ssl, tor_tls_object_ex_data_index);
@@ -230,21 +154,7 @@ tor_tls_get_by_ssl(const SSL *ssl)
static void tor_tls_context_decref(tor_tls_context_t *ctx);
static void tor_tls_context_incref(tor_tls_context_t *ctx);
-static X509* tor_tls_create_certificate(crypto_pk_t *rsa,
- crypto_pk_t *rsa_sign,
- const char *cname,
- const char *cname_sign,
- unsigned int cert_lifetime);
-
-static int tor_tls_context_init_one(tor_tls_context_t **ppcontext,
- crypto_pk_t *identity,
- unsigned int key_lifetime,
- unsigned int flags,
- int is_client);
-static tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity,
- unsigned int key_lifetime,
- unsigned int flags,
- int is_client);
+
static int check_cert_lifetime_internal(int severity, const X509 *cert,
int past_tolerance, int future_tolerance);
@@ -252,8 +162,8 @@ static int check_cert_lifetime_internal(int severity, const X509 *cert,
* to touch them.
*
* @{ */
-static tor_tls_context_t *server_tls_context = NULL;
-static tor_tls_context_t *client_tls_context = NULL;
+STATIC tor_tls_context_t *server_tls_context = NULL;
+STATIC tor_tls_context_t *client_tls_context = NULL;
/**@}*/
/** True iff tor_tls_init() has been called. */
@@ -347,7 +257,7 @@ tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
/** Log all pending tls errors at level <b>severity</b> in log domain
* <b>domain</b>. Use <b>doing</b> to describe our current activities.
*/
-static void
+STATIC void
tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing)
{
unsigned long err;
@@ -359,7 +269,7 @@ tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing)
/** Convert an errno (or a WSAerrno on windows) into a TOR_TLS_* error
* code. */
-static int
+STATIC int
tor_errno_to_tls_error(int e)
{
switch (e) {
@@ -410,7 +320,7 @@ tor_tls_err_to_string(int err)
* If an error has occurred, log it at level <b>severity</b> and describe the
* current action as <b>doing</b>.
*/
-static int
+STATIC int
tor_tls_get_error(tor_tls_t *tls, int r, int extra,
const char *doing, int severity, int domain)
{
@@ -466,8 +376,9 @@ tor_tls_init(void)
#if (SIZEOF_VOID_P >= 8 && \
OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,1))
- long version = SSLeay();
+ long version = OpenSSL_version_num();
+ /* LCOV_EXCL_START : we can't test these lines on the same machine */
if (version >= OPENSSL_V_SERIES(1,0,1)) {
/* Warn if we could *almost* be running with much faster ECDH.
If we're built for a 64-bit target, using OpenSSL 1.0.1, but we
@@ -494,6 +405,7 @@ tor_tls_init(void)
"support (using the enable-ec_nistp_64_gcc_128 option "
"when configuring it) would make ECDH much faster.");
}
+ /* LCOV_EXCL_STOP */
#endif
tor_tls_allocate_tor_tls_object_ex_data_index();
@@ -524,7 +436,7 @@ tor_tls_free_all(void)
* it: We always accept peer certs and complete the handshake. We
* don't validate them until later.
*/
-static int
+STATIC int
always_accept_verify_cb(int preverify_ok,
X509_STORE_CTX *x509_ctx)
{
@@ -539,16 +451,20 @@ tor_x509_name_new(const char *cname)
{
int nid;
X509_NAME *name;
+ /* LCOV_EXCL_BR_START : these branches will only fail on OOM errors */
if (!(name = X509_NAME_new()))
return NULL;
if ((nid = OBJ_txt2nid("commonName")) == NID_undef) goto error;
if (!(X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC,
(unsigned char*)cname, -1, -1, 0)))
goto error;
+ /* LCOV_EXCL_BR_STOP */
return name;
error:
+ /* LCOV_EXCL_START : these lines will only execute on out of memory errors*/
X509_NAME_free(name);
return NULL;
+ /* LCOV_EXCL_STOP */
}
/** Generate and sign an X509 certificate with the public key <b>rsa</b>,
@@ -559,12 +475,12 @@ tor_x509_name_new(const char *cname)
*
* Return a certificate on success, NULL on failure.
*/
-static X509 *
-tor_tls_create_certificate(crypto_pk_t *rsa,
- crypto_pk_t *rsa_sign,
- const char *cname,
- const char *cname_sign,
- unsigned int cert_lifetime)
+MOCK_IMPL(STATIC X509 *,
+ tor_tls_create_certificate,(crypto_pk_t *rsa,
+ crypto_pk_t *rsa_sign,
+ const char *cname,
+ const char *cname_sign,
+ unsigned int cert_lifetime))
{
/* OpenSSL generates self-signed certificates with random 64-bit serial
* numbers, so let's do that too. */
@@ -601,8 +517,7 @@ tor_tls_create_certificate(crypto_pk_t *rsa,
goto error;
{ /* our serial number is 8 random bytes. */
- if (crypto_rand((char *)serial_tmp, sizeof(serial_tmp)) < 0)
- goto error;
+ crypto_rand((char *)serial_tmp, sizeof(serial_tmp));
if (!(serial_number = BN_bin2bn(serial_tmp, sizeof(serial_tmp), NULL)))
goto error;
if (!(BN_to_ASN1_INTEGER(serial_number, X509_get_serialNumber(x509))))
@@ -731,7 +646,9 @@ tor_x509_cert_free(tor_x509_cert_t *cert)
X509_free(cert->cert);
tor_free(cert->encoded);
memwipe(cert, 0x03, sizeof(*cert));
+ /* LCOV_EXCL_BR_START since cert will never be NULL here */
tor_free(cert);
+ /* LCOV_EXCL_BR_STOP */
}
/**
@@ -739,8 +656,8 @@ tor_x509_cert_free(tor_x509_cert_t *cert)
*
* Steals a reference to x509_cert.
*/
-static tor_x509_cert_t *
-tor_x509_cert_new(X509 *x509_cert)
+MOCK_IMPL(STATIC tor_x509_cert_t *,
+ tor_x509_cert_new,(X509 *x509_cert))
{
tor_x509_cert_t *cert;
EVP_PKEY *pkey;
@@ -754,10 +671,12 @@ tor_x509_cert_new(X509 *x509_cert)
length = i2d_X509(x509_cert, &buf);
cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
if (length <= 0 || buf == NULL) {
+ /* LCOV_EXCL_START for the same reason as the exclusion above */
tor_free(cert);
log_err(LD_CRYPTO, "Couldn't get length of encoded x509 certificate");
X509_free(x509_cert);
return NULL;
+ /* LCOV_EXCL_STOP */
}
cert->encoded_len = (size_t) length;
cert->encoded = tor_malloc(length);
@@ -766,13 +685,13 @@ tor_x509_cert_new(X509 *x509_cert)
cert->cert = x509_cert;
- crypto_digest_all(&cert->cert_digests,
+ crypto_common_digests(&cert->cert_digests,
(char*)cert->encoded, cert->encoded_len);
if ((pkey = X509_get_pubkey(x509_cert)) &&
(rsa = EVP_PKEY_get1_RSA(pkey))) {
crypto_pk_t *pk = crypto_new_pk_from_rsa_(rsa);
- crypto_pk_get_all_digests(pk, &cert->pkey_digests);
+ crypto_pk_get_common_digests(pk, &cert->pkey_digests);
cert->pkey_digests_set = 1;
crypto_pk_free(pk);
EVP_PKEY_free(pkey);
@@ -835,7 +754,7 @@ tor_x509_cert_get_der(const tor_x509_cert_t *cert,
/** Return a set of digests for the public key in <b>cert</b>, or NULL if this
* cert's public key is not one we know how to take the digest of. */
-const digests_t *
+const common_digests_t *
tor_x509_cert_get_id_digests(const tor_x509_cert_t *cert)
{
if (cert->pkey_digests_set)
@@ -845,7 +764,7 @@ tor_x509_cert_get_id_digests(const tor_x509_cert_t *cert)
}
/** Return a set of digests for the public key in <b>cert</b>. */
-const digests_t *
+const common_digests_t *
tor_x509_cert_get_cert_digests(const tor_x509_cert_t *cert)
{
return &cert->cert_digests;
@@ -864,7 +783,9 @@ tor_tls_context_decref(tor_tls_context_t *ctx)
tor_x509_cert_free(ctx->my_auth_cert);
crypto_pk_free(ctx->link_key);
crypto_pk_free(ctx->auth_key);
+ /* LCOV_EXCL_BR_START since ctx will never be NULL here */
tor_free(ctx);
+ /* LCOV_EXCL_BR_STOP */
}
}
@@ -960,11 +881,13 @@ tor_tls_cert_is_valid(int severity,
int check_rsa_1024)
{
check_no_tls_errors();
-
EVP_PKEY *cert_key;
- EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert);
int r, key_ok = 0;
+ if (!signing_cert || !cert)
+ goto bad;
+
+ EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert);
if (!signing_key)
goto bad;
r = X509_verify(cert->cert, signing_key);
@@ -988,7 +911,7 @@ tor_tls_cert_is_valid(int severity,
} else if (cert_key) {
int min_bits = 1024;
#ifdef EVP_PKEY_EC
- if (EVP_PKEY_type(cert_key->type) == EVP_PKEY_EC)
+ if (EVP_PKEY_base_id(cert_key) == EVP_PKEY_EC)
min_bits = 128;
#endif
if (EVP_PKEY_bits(cert_key) >= min_bits)
@@ -1085,7 +1008,7 @@ tor_tls_context_init(unsigned flags,
* it generates new certificates; all new connections will use
* the new SSL context.
*/
-static int
+STATIC int
tor_tls_context_init_one(tor_tls_context_t **ppcontext,
crypto_pk_t *identity,
unsigned int key_lifetime,
@@ -1119,7 +1042,7 @@ tor_tls_context_init_one(tor_tls_context_t **ppcontext,
* <b>identity</b> should be set to the identity key used to sign the
* certificate.
*/
-static tor_tls_context_t *
+STATIC tor_tls_context_t *
tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
unsigned flags, int is_client)
{
@@ -1200,23 +1123,6 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
* historically been chosen for fingerprinting resistance. */
SSL_CTX_set_options(result->ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
- /* Disable TLS1.1 and TLS1.2 if they exist. We need to do this to
- * workaround a bug present in all OpenSSL 1.0.1 versions (as of 1
- * June 2012), wherein renegotiating while using one of these TLS
- * protocols will cause the client to send a TLS 1.0 ServerHello
- * rather than a ServerHello written with the appropriate protocol
- * version. Once some version of OpenSSL does TLS1.1 and TLS1.2
- * renegotiation properly, we can turn them back on when built with
- * that version. */
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,1,'e')
-#ifdef SSL_OP_NO_TLSv1_2
- SSL_CTX_set_options(result->ctx, SSL_OP_NO_TLSv1_2);
-#endif
-#ifdef SSL_OP_NO_TLSv1_1
- SSL_CTX_set_options(result->ctx, SSL_OP_NO_TLSv1_1);
-#endif
-#endif
-
/* Disable TLS tickets if they're supported. We never want to use them;
* using them can make our perfect forward secrecy a little worse, *and*
* create an opportunity to fingerprint us (since it's unusual to use them
@@ -1343,11 +1249,13 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
}
/** Invoked when a TLS state changes: log the change at severity 'debug' */
-static void
+STATIC void
tor_tls_debug_state_callback(const SSL *ssl, int type, int val)
{
+ /* LCOV_EXCL_START since this depends on whether debug is captured or not */
log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].",
ssl, SSL_state_string_long(ssl), type, val);
+ /* LCOV_EXCL_STOP */
}
/* Return the name of the negotiated ciphersuite in use on <b>tls</b> */
@@ -1357,13 +1265,11 @@ tor_tls_get_ciphersuite_name(tor_tls_t *tls)
return SSL_get_cipher(tls->ssl);
}
-#ifdef V2_HANDSHAKE_SERVER
-
/* Here's the old V2 cipher list we sent from 0.2.1.1-alpha up to
* 0.2.3.17-beta. If a client is using this list, we can't believe the ciphers
* that it claims to support. We'll prune this list to remove the ciphers
* *we* don't recognize. */
-static uint16_t v2_cipher_list[] = {
+STATIC uint16_t v2_cipher_list[] = {
0xc00a, /* TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA */
0xc014, /* TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA */
0x0039, /* TLS1_TXT_DHE_RSA_WITH_AES_256_SHA */
@@ -1399,11 +1305,12 @@ static int v2_cipher_list_pruned = 0;
/** Return 0 if <b>m</b> does not support the cipher with ID <b>cipher</b>;
* return 1 if it does support it, or if we have no way to tell. */
-static int
+STATIC int
find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher)
{
const SSL_CIPHER *c;
#ifdef HAVE_SSL_CIPHER_FIND
+ (void) m;
{
unsigned char cipherid[3];
tor_assert(ssl);
@@ -1416,7 +1323,9 @@ find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher)
tor_assert((SSL_CIPHER_get_id(c) & 0xffff) == cipher);
return c != NULL;
}
-#elif defined(HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR)
+#else
+
+# if defined(HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR)
if (m && m->get_cipher_by_char) {
unsigned char cipherid[3];
set_uint16(cipherid, htons(cipher));
@@ -1427,9 +1336,9 @@ find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher)
if (c)
tor_assert((c->id & 0xffff) == cipher);
return c != NULL;
- } else
-#endif
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0)
+ }
+# endif
+# ifndef OPENSSL_1_1_API
if (m && m->get_cipher && m->num_ciphers) {
/* It would seem that some of the "let's-clean-up-openssl" forks have
* removed the get_cipher_by_char function. Okay, so now you get a
@@ -1444,11 +1353,12 @@ find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher)
}
return 0;
}
-#endif
+# endif
(void) ssl;
(void) m;
(void) cipher;
return 1; /* No way to search */
+#endif
}
/** Remove from v2_cipher_list every cipher that we don't support, so that
@@ -1481,7 +1391,7 @@ prune_v2_cipher_list(const SSL *ssl)
* client it is. Return one of CIPHERS_ERR, CIPHERS_V1, CIPHERS_V2,
* CIPHERS_UNRESTRICTED.
**/
-static int
+STATIC int
tor_tls_classify_client_ciphers(const SSL *ssl,
STACK_OF(SSL_CIPHER) *peer_ciphers)
{
@@ -1504,7 +1414,7 @@ tor_tls_classify_client_ciphers(const SSL *ssl,
/* Now we need to see if there are any ciphers whose presence means we're
* dealing with an updated Tor. */
for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) {
- SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i);
+ const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i);
const char *ciphername = SSL_CIPHER_get_name(cipher);
if (strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA) &&
strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA) &&
@@ -1521,7 +1431,7 @@ tor_tls_classify_client_ciphers(const SSL *ssl,
{
const uint16_t *v2_cipher = v2_cipher_list;
for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) {
- SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i);
+ const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i);
uint16_t id = SSL_CIPHER_get_id(cipher) & 0xffff;
if (id == 0x00ff) /* extended renegotiation indicator. */
continue;
@@ -1543,7 +1453,7 @@ tor_tls_classify_client_ciphers(const SSL *ssl,
smartlist_t *elts = smartlist_new();
char *s;
for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) {
- SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i);
+ const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i);
const char *ciphername = SSL_CIPHER_get_name(cipher);
smartlist_add(elts, (char*)ciphername);
}
@@ -1563,7 +1473,7 @@ tor_tls_classify_client_ciphers(const SSL *ssl,
/** Return true iff the cipher list suggested by the client for <b>ssl</b> is
* a list that indicates that the client knows how to do the v2 TLS connection
* handshake. */
-static int
+STATIC int
tor_tls_client_is_using_v2_ciphers(const SSL *ssl)
{
STACK_OF(SSL_CIPHER) *ciphers;
@@ -1587,11 +1497,10 @@ tor_tls_client_is_using_v2_ciphers(const SSL *ssl)
* do not send or request extra certificates in v2 handshakes.</li>
* <li>To detect renegotiation</li></ul>
*/
-static void
+STATIC void
tor_tls_server_info_callback(const SSL *ssl, int type, int val)
{
tor_tls_t *tls;
- int ssl_state;
(void) val;
tor_tls_debug_state_callback(ssl, type, val);
@@ -1599,11 +1508,9 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
if (type != SSL_CB_ACCEPT_LOOP)
return;
- ssl_state = SSL_state(ssl);
- if ((ssl_state != SSL3_ST_SW_SRVR_HELLO_A) &&
- (ssl_state != SSL3_ST_SW_SRVR_HELLO_B))
+ OSSL_HANDSHAKE_STATE ssl_state = SSL_get_state(ssl);
+ if (! STATE_IS_SW_SERVER_HELLO(ssl_state))
return;
-
tls = tor_tls_get_by_ssl(ssl);
if (tls) {
/* Check whether we're watching for renegotiates. If so, this is one! */
@@ -1633,11 +1540,12 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
if (tls) {
tls->wasV2Handshake = 1;
} else {
+ /* LCOV_EXCL_START this line is not reachable */
log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!");
+ /* LCOV_EXCL_STOP */
}
}
}
-#endif
/** Callback to get invoked on a server after we've read the list of ciphers
* the client supports, but before we pick our own ciphersuite.
@@ -1651,10 +1559,11 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
* authentication on the fly. But as long as we return 0, we won't actually be
* setting up a shared secret, and all will be fine.
*/
-static int
+STATIC int
tor_tls_session_secret_cb(SSL *ssl, void *secret, int *secret_len,
STACK_OF(SSL_CIPHER) *peer_ciphers,
- SSL_CIPHER **cipher, void *arg)
+ CONST_IF_OPENSSL_1_1_API SSL_CIPHER **cipher,
+ void *arg)
{
(void) secret;
(void) secret_len;
@@ -1747,12 +1656,9 @@ tor_tls_new(int sock, int isServer)
log_warn(LD_NET, "Newly created BIO has read count %lu, write count %lu",
result->last_read_count, result->last_write_count);
}
-#ifdef V2_HANDSHAKE_SERVER
if (isServer) {
SSL_set_info_callback(result->ssl, tor_tls_server_info_callback);
- } else
-#endif
- {
+ } else {
SSL_set_info_callback(result->ssl, tor_tls_debug_state_callback);
}
@@ -1791,13 +1697,11 @@ tor_tls_set_renegotiate_callback(tor_tls_t *tls,
tls->negotiated_callback = cb;
tls->callback_arg = arg;
tls->got_renegotiate = 0;
-#ifdef V2_HANDSHAKE_SERVER
if (cb) {
SSL_set_info_callback(tls->ssl, tor_tls_server_info_callback);
} else {
SSL_set_info_callback(tls->ssl, tor_tls_debug_state_callback);
}
-#endif
}
/** If this version of openssl requires it, turn on renegotiation on
@@ -1830,8 +1734,13 @@ tor_tls_block_renegotiation(tor_tls_t *tls)
void
tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls)
{
+#if defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) && \
+ SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION != 0
long options = SSL_get_options(tls->ssl);
tor_assert(0 != (options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION));
+#else
+ (void) tls;
+#endif
}
/** Return whether this tls initiated the connect (client) or
@@ -1884,7 +1793,6 @@ tor_tls_read,(tor_tls_t *tls, char *cp, size_t len))
tor_assert(len<INT_MAX);
r = SSL_read(tls->ssl, cp, (int)len);
if (r > 0) {
-#ifdef V2_HANDSHAKE_SERVER
if (tls->got_renegotiate) {
/* Renegotiation happened! */
log_info(LD_NET, "Got a TLS renegotiation from %s", ADDR(tls));
@@ -1892,7 +1800,6 @@ tor_tls_read,(tor_tls_t *tls, char *cp, size_t len))
tls->negotiated_callback(tls, tls->callback_arg);
tls->got_renegotiate = 0;
}
-#endif
return r;
}
err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_DEBUG, LD_NET);
@@ -1909,10 +1816,10 @@ tor_tls_read,(tor_tls_t *tls, char *cp, size_t len))
/** Total number of bytes that we've used TLS to send. Used to track TLS
* overhead. */
-static uint64_t total_bytes_written_over_tls = 0;
+STATIC uint64_t total_bytes_written_over_tls = 0;
/** Total number of bytes that TLS has put on the network for us. Used to
* track TLS overhead. */
-static uint64_t total_bytes_written_by_tls = 0;
+STATIC uint64_t total_bytes_written_by_tls = 0;
/** Underlying function for TLS writing. Write up to <b>n</b>
* characters from <b>cp</b> onto <b>tls</b>. On success, returns the
@@ -1957,12 +1864,14 @@ int
tor_tls_handshake(tor_tls_t *tls)
{
int r;
- int oldstate;
tor_assert(tls);
tor_assert(tls->ssl);
tor_assert(tls->state == TOR_TLS_ST_HANDSHAKE);
+
check_no_tls_errors();
- oldstate = SSL_state(tls->ssl);
+
+ OSSL_HANDSHAKE_STATE oldstate = SSL_get_state(tls->ssl);
+
if (tls->isServer) {
log_debug(LD_HANDSHAKE, "About to call SSL_accept on %p (%s)", tls,
SSL_state_string_long(tls->ssl));
@@ -1972,7 +1881,10 @@ tor_tls_handshake(tor_tls_t *tls)
SSL_state_string_long(tls->ssl));
r = SSL_connect(tls->ssl);
}
- if (oldstate != SSL_state(tls->ssl))
+
+ OSSL_HANDSHAKE_STATE newstate = SSL_get_state(tls->ssl);
+
+ if (oldstate != newstate)
log_debug(LD_HANDSHAKE, "After call, %p was in state %s",
tls, SSL_state_string_long(tls->ssl));
/* We need to call this here and not earlier, since OpenSSL has a penchant
@@ -2008,7 +1920,6 @@ tor_tls_finish_handshake(tor_tls_t *tls)
SSL_set_info_callback(tls->ssl, NULL);
SSL_set_verify(tls->ssl, SSL_VERIFY_PEER, always_accept_verify_cb);
SSL_clear_mode(tls->ssl, SSL_MODE_NO_AUTO_CHAIN);
-#ifdef V2_HANDSHAKE_SERVER
if (tor_tls_client_is_using_v2_ciphers(tls->ssl)) {
/* This check is redundant, but back when we did it in the callback,
* we might have not been able to look up the tor_tls_t if the code
@@ -2023,26 +1934,10 @@ tor_tls_finish_handshake(tor_tls_t *tls)
} else {
tls->wasV2Handshake = 0;
}
-#endif
} else {
-#ifdef V2_HANDSHAKE_CLIENT
- /* If we got no ID cert, we're a v2 handshake. */
- X509 *cert = SSL_get_peer_certificate(tls->ssl);
- STACK_OF(X509) *chain = SSL_get_peer_cert_chain(tls->ssl);
- int n_certs = sk_X509_num(chain);
- if (n_certs > 1 || (n_certs == 1 && cert != sk_X509_value(chain, 0))) {
- log_debug(LD_HANDSHAKE, "Server sent back multiple certificates; it "
- "looks like a v1 handshake on %p", tls);
- tls->wasV2Handshake = 0;
- } else {
- log_debug(LD_HANDSHAKE,
- "Server sent back a single certificate; looks like "
- "a v2 handshake on %p.", tls);
- tls->wasV2Handshake = 1;
- }
- if (cert)
- X509_free(cert);
-#endif
+ /* Client-side */
+ tls->wasV2Handshake = 1;
+ /* XXXX this can move, probably? -NM */
if (SSL_set_cipher_list(tls->ssl, SERVER_CIPHER_LIST) == 0) {
tls_log_errors(NULL, LOG_WARN, LD_HANDSHAKE, "re-setting ciphers");
r = TOR_TLS_ERROR_MISC;
@@ -2052,52 +1947,6 @@ tor_tls_finish_handshake(tor_tls_t *tls)
return r;
}
-#ifdef USE_BUFFEREVENTS
-/** Put <b>tls</b>, which must be a client connection, into renegotiation
- * mode. */
-int
-tor_tls_start_renegotiating(tor_tls_t *tls)
-{
- int r = SSL_renegotiate(tls->ssl);
- if (r <= 0) {
- return tor_tls_get_error(tls, r, 0, "renegotiating", LOG_WARN,
- LD_HANDSHAKE);
- }
- return 0;
-}
-#endif
-
-/** Client only: Renegotiate a TLS session. When finished, returns
- * TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD, or
- * TOR_TLS_WANTWRITE.
- */
-int
-tor_tls_renegotiate(tor_tls_t *tls)
-{
- int r;
- tor_assert(tls);
- /* We could do server-initiated renegotiation too, but that would be tricky.
- * Instead of "SSL_renegotiate, then SSL_do_handshake until done" */
- tor_assert(!tls->isServer);
-
- check_no_tls_errors();
- if (tls->state != TOR_TLS_ST_RENEGOTIATE) {
- int r = SSL_renegotiate(tls->ssl);
- if (r <= 0) {
- return tor_tls_get_error(tls, r, 0, "renegotiating", LOG_WARN,
- LD_HANDSHAKE);
- }
- tls->state = TOR_TLS_ST_RENEGOTIATE;
- }
- r = SSL_do_handshake(tls->ssl);
- if (r == 1) {
- tls->state = TOR_TLS_ST_OPEN;
- return TOR_TLS_DONE;
- } else
- return tor_tls_get_error(tls, r, 0, "renegotiating handshake", LOG_INFO,
- LD_HANDSHAKE);
-}
-
/** Shut down an open tls connection <b>tls</b>. When finished, returns
* TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD,
* or TOR_TLS_WANTWRITE.
@@ -2251,15 +2100,14 @@ log_cert_lifetime(int severity, const X509 *cert, const char *problem)
*
* Note that a reference is added to cert_out, so it needs to be
* freed. id_cert_out doesn't. */
-static void
-try_to_extract_certs_from_tls(int severity, tor_tls_t *tls,
- X509 **cert_out, X509 **id_cert_out)
+MOCK_IMPL(STATIC void,
+try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls,
+ X509 **cert_out, X509 **id_cert_out))
{
X509 *cert = NULL, *id_cert = NULL;
STACK_OF(X509) *chain = NULL;
int num_in_chain, i;
*cert_out = *id_cert_out = NULL;
-
if (!(cert = SSL_get_peer_certificate(tls->ssl)))
return;
*cert_out = cert;
@@ -2476,114 +2324,7 @@ check_no_tls_errors_(const char *fname, int line)
int
tor_tls_used_v1_handshake(tor_tls_t *tls)
{
-#if defined(V2_HANDSHAKE_SERVER) && defined(V2_HANDSHAKE_CLIENT)
return ! tls->wasV2Handshake;
-#else
- if (tls->isServer) {
-# ifdef V2_HANDSHAKE_SERVER
- return ! tls->wasV2Handshake;
-# endif
- } else {
-# ifdef V2_HANDSHAKE_CLIENT
- return ! tls->wasV2Handshake;
-# endif
- }
- return 1;
-#endif
-}
-
-/** Return true iff <b>name</b> is a DN of a kind that could only
- * occur in a v3-handshake-indicating certificate */
-static int
-dn_indicates_v3_cert(X509_NAME *name)
-{
-#ifdef DISABLE_V3_LINKPROTO_CLIENTSIDE
- (void)name;
- return 0;
-#else
- X509_NAME_ENTRY *entry;
- int n_entries;
- ASN1_OBJECT *obj;
- ASN1_STRING *str;
- unsigned char *s;
- int len, r;
-
- n_entries = X509_NAME_entry_count(name);
- if (n_entries != 1)
- return 1; /* More than one entry in the DN. */
- entry = X509_NAME_get_entry(name, 0);
-
- obj = X509_NAME_ENTRY_get_object(entry);
- if (OBJ_obj2nid(obj) != OBJ_txt2nid("commonName"))
- return 1; /* The entry isn't a commonName. */
-
- str = X509_NAME_ENTRY_get_data(entry);
- len = ASN1_STRING_to_UTF8(&s, str);
- if (len < 0)
- return 0;
- if (len < 4) {
- OPENSSL_free(s);
- return 1;
- }
- r = fast_memneq(s + len - 4, ".net", 4);
- OPENSSL_free(s);
- return r;
-#endif
-}
-
-/** Return true iff the peer certificate we're received on <b>tls</b>
- * indicates that this connection should use the v3 (in-protocol)
- * authentication handshake.
- *
- * Only the connection initiator should use this, and only once the initial
- * handshake is done; the responder detects a v1 handshake by cipher types,
- * and a v3/v2 handshake by Versions cell vs renegotiation.
- */
-int
-tor_tls_received_v3_certificate(tor_tls_t *tls)
-{
- check_no_tls_errors();
-
- X509 *cert = SSL_get_peer_certificate(tls->ssl);
- EVP_PKEY *key = NULL;
- X509_NAME *issuer_name, *subject_name;
- int is_v3 = 0;
-
- if (!cert) {
- log_warn(LD_BUG, "Called on a connection with no peer certificate");
- goto done;
- }
-
- subject_name = X509_get_subject_name(cert);
- issuer_name = X509_get_issuer_name(cert);
-
- if (X509_name_cmp(subject_name, issuer_name) == 0) {
- is_v3 = 1; /* purportedly self signed */
- goto done;
- }
-
- if (dn_indicates_v3_cert(subject_name) ||
- dn_indicates_v3_cert(issuer_name)) {
- is_v3 = 1; /* DN is fancy */
- goto done;
- }
-
- key = X509_get_pubkey(cert);
- if (EVP_PKEY_bits(key) != 1024 ||
- EVP_PKEY_type(key->type) != EVP_PKEY_RSA) {
- is_v3 = 1; /* Key is fancy */
- goto done;
- }
-
- done:
- tls_log_errors(tls, LOG_WARN, LD_NET, "checking for a v3 cert");
-
- if (key)
- EVP_PKEY_free(key);
- if (cert)
- X509_free(cert);
-
- return is_v3;
}
/** Return the number of server handshakes that we've noticed doing on
@@ -2629,7 +2370,7 @@ SSL_get_server_random(SSL *s, uint8_t *out, size_t len)
#endif
#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
-static size_t
+STATIC size_t
SSL_SESSION_get_master_key(SSL_SESSION *s, uint8_t *out, size_t len)
{
tor_assert(s);
@@ -2652,7 +2393,6 @@ tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out))
#define TLSSECRET_MAGIC "Tor V3 handshake TLS cross-certification"
uint8_t buf[128];
size_t len;
-
tor_assert(tls);
SSL *const ssl = tls->ssl;
@@ -2676,12 +2416,14 @@ tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out))
size_t r = SSL_get_client_random(ssl, buf, client_random_len);
tor_assert(r == client_random_len);
}
+
{
size_t r = SSL_get_server_random(ssl,
buf+client_random_len,
server_random_len);
tor_assert(r == server_random_len);
}
+
uint8_t *master_key = tor_malloc_zero(master_key_len);
{
size_t r = SSL_SESSION_get_master_key(session, master_key, master_key_len);
diff --git a/src/common/tortls.h b/src/common/tortls.h
index 124b77160f..336115ae6b 100644
--- a/src/common/tortls.h
+++ b/src/common/tortls.h
@@ -12,6 +12,7 @@
**/
#include "crypto.h"
+#include "compat_openssl.h"
#include "compat.h"
#include "testsupport.h"
@@ -51,6 +52,120 @@ typedef struct tor_x509_cert_t tor_x509_cert_t;
case TOR_TLS_ERROR_IO
#define TOR_TLS_IS_ERROR(rv) ((rv) < TOR_TLS_CLOSE)
+
+#ifdef TORTLS_PRIVATE
+#define TOR_TLS_MAGIC 0x71571571
+
+typedef enum {
+ TOR_TLS_ST_HANDSHAKE, TOR_TLS_ST_OPEN, TOR_TLS_ST_GOTCLOSE,
+ TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED, TOR_TLS_ST_RENEGOTIATE,
+ TOR_TLS_ST_BUFFEREVENT
+} tor_tls_state_t;
+#define tor_tls_state_bitfield_t ENUM_BF(tor_tls_state_t)
+
+/** Holds a SSL_CTX object and related state used to configure TLS
+ * connections.
+ */
+typedef struct tor_tls_context_t {
+ int refcnt;
+ SSL_CTX *ctx;
+ tor_x509_cert_t *my_link_cert;
+ tor_x509_cert_t *my_id_cert;
+ tor_x509_cert_t *my_auth_cert;
+ crypto_pk_t *link_key;
+ crypto_pk_t *auth_key;
+} tor_tls_context_t;
+
+/** Structure that we use for a single certificate. */
+struct tor_x509_cert_t {
+ X509 *cert;
+ uint8_t *encoded;
+ size_t encoded_len;
+ unsigned pkey_digests_set : 1;
+ common_digests_t cert_digests;
+ common_digests_t pkey_digests;
+};
+
+/** Holds a SSL object and its associated data. Members are only
+ * accessed from within tortls.c.
+ */
+struct tor_tls_t {
+ uint32_t magic;
+ tor_tls_context_t *context; /** A link to the context object for this tls. */
+ SSL *ssl; /**< An OpenSSL SSL object. */
+ int socket; /**< The underlying file descriptor for this TLS connection. */
+ char *address; /**< An address to log when describing this connection. */
+ tor_tls_state_bitfield_t state : 3; /**< The current SSL state,
+ * depending on which operations
+ * have completed successfully. */
+ unsigned int isServer:1; /**< True iff this is a server-side connection */
+ unsigned int wasV2Handshake:1; /**< True iff the original handshake for
+ * this connection used the updated version
+ * of the connection protocol (client sends
+ * different cipher list, server sends only
+ * one certificate). */
+ /** True iff we should call negotiated_callback when we're done reading. */
+ unsigned int got_renegotiate:1;
+ /** Return value from tor_tls_classify_client_ciphers, or 0 if we haven't
+ * called that function yet. */
+ int8_t client_cipher_list_type;
+ /** Incremented every time we start the server side of a handshake. */
+ uint8_t server_handshake_count;
+ size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last
+ * time. */
+ /** Last values retrieved from BIO_number_read()/write(); see
+ * tor_tls_get_n_raw_bytes() for usage.
+ */
+ unsigned long last_write_count;
+ unsigned long last_read_count;
+ /** If set, a callback to invoke whenever the client tries to renegotiate
+ * the handshake. */
+ void (*negotiated_callback)(tor_tls_t *tls, void *arg);
+ /** Argument to pass to negotiated_callback. */
+ void *callback_arg;
+};
+
+STATIC int tor_errno_to_tls_error(int e);
+STATIC int tor_tls_get_error(tor_tls_t *tls, int r, int extra,
+ const char *doing, int severity, int domain);
+STATIC tor_tls_t *tor_tls_get_by_ssl(const SSL *ssl);
+STATIC void tor_tls_allocate_tor_tls_object_ex_data_index(void);
+STATIC int always_accept_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx);
+STATIC int tor_tls_classify_client_ciphers(const SSL *ssl,
+ STACK_OF(SSL_CIPHER) *peer_ciphers);
+STATIC int tor_tls_client_is_using_v2_ciphers(const SSL *ssl);
+MOCK_DECL(STATIC void, try_to_extract_certs_from_tls,
+ (int severity, tor_tls_t *tls, X509 **cert_out, X509 **id_cert_out));
+#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
+STATIC size_t SSL_SESSION_get_master_key(SSL_SESSION *s, uint8_t *out,
+ size_t len);
+#endif
+STATIC void tor_tls_debug_state_callback(const SSL *ssl, int type, int val);
+STATIC void tor_tls_server_info_callback(const SSL *ssl, int type, int val);
+STATIC int tor_tls_session_secret_cb(SSL *ssl, void *secret,
+ int *secret_len,
+ STACK_OF(SSL_CIPHER) *peer_ciphers,
+ CONST_IF_OPENSSL_1_1_API SSL_CIPHER **cipher,
+ void *arg);
+STATIC int find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m,
+ uint16_t cipher);
+MOCK_DECL(STATIC X509*, tor_tls_create_certificate,(crypto_pk_t *rsa,
+ crypto_pk_t *rsa_sign,
+ const char *cname,
+ const char *cname_sign,
+ unsigned int cert_lifetime));
+STATIC tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity,
+ unsigned int key_lifetime, unsigned flags, int is_client);
+MOCK_DECL(STATIC tor_x509_cert_t *, tor_x509_cert_new,(X509 *x509_cert));
+STATIC int tor_tls_context_init_one(tor_tls_context_t **ppcontext,
+ crypto_pk_t *identity,
+ unsigned int key_lifetime,
+ unsigned int flags,
+ int is_client);
+STATIC void tls_log_errors(tor_tls_t *tls, int severity, int domain,
+ const char *doing);
+#endif
+
const char *tor_tls_err_to_string(int err);
void tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz);
@@ -81,7 +196,6 @@ MOCK_DECL(int, tor_tls_read, (tor_tls_t *tls, char *cp, size_t len));
int tor_tls_write(tor_tls_t *tls, const char *cp, size_t n);
int tor_tls_handshake(tor_tls_t *tls);
int tor_tls_finish_handshake(tor_tls_t *tls);
-int tor_tls_renegotiate(tor_tls_t *tls);
void tor_tls_unblock_renegotiation(tor_tls_t *tls);
void tor_tls_block_renegotiation(tor_tls_t *tls);
void tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls);
@@ -99,7 +213,6 @@ int tor_tls_get_buffer_sizes(tor_tls_t *tls,
MOCK_DECL(double, tls_get_write_overhead_ratio, (void));
int tor_tls_used_v1_handshake(tor_tls_t *tls);
-int tor_tls_received_v3_certificate(tor_tls_t *tls);
int tor_tls_get_num_server_handshakes(tor_tls_t *tls);
int tor_tls_server_got_renegotiate(tor_tls_t *tls);
MOCK_DECL(int,tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out));
@@ -125,8 +238,10 @@ tor_x509_cert_t *tor_x509_cert_decode(const uint8_t *certificate,
size_t certificate_len);
void tor_x509_cert_get_der(const tor_x509_cert_t *cert,
const uint8_t **encoded_out, size_t *size_out);
-const digests_t *tor_x509_cert_get_id_digests(const tor_x509_cert_t *cert);
-const digests_t *tor_x509_cert_get_cert_digests(const tor_x509_cert_t *cert);
+const common_digests_t *tor_x509_cert_get_id_digests(
+ const tor_x509_cert_t *cert);
+const common_digests_t *tor_x509_cert_get_cert_digests(
+ const tor_x509_cert_t *cert);
int tor_tls_get_my_certs(int server,
const tor_x509_cert_t **link_cert_out,
const tor_x509_cert_t **id_cert_out);
diff --git a/src/common/util.c b/src/common/util.c
index b33c80fd45..488f1b6ca8 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -207,7 +207,7 @@ tor_malloc_zero_(size_t size DMALLOC_PARAMS)
#define SQRT_SIZE_MAX_P1 (((size_t)1) << (sizeof(size_t)*4))
/** Return non-zero if and only if the product of the arguments is exact. */
-static INLINE int
+static inline int
size_mul_check(const size_t x, const size_t y)
{
/* This first check is equivalent to
@@ -488,42 +488,58 @@ round_to_power_of_2(uint64_t u64)
}
/** Return the lowest x such that x is at least <b>number</b>, and x modulo
- * <b>divisor</b> == 0. */
+ * <b>divisor</b> == 0. If no such x can be expressed as an unsigned, return
+ * UINT_MAX */
unsigned
round_to_next_multiple_of(unsigned number, unsigned divisor)
{
+ tor_assert(divisor > 0);
+ if (UINT_MAX - divisor + 1 < number)
+ return UINT_MAX;
number += divisor - 1;
number -= number % divisor;
return number;
}
/** Return the lowest x such that x is at least <b>number</b>, and x modulo
- * <b>divisor</b> == 0. */
+ * <b>divisor</b> == 0. If no such x can be expressed as a uint32_t, return
+ * UINT32_MAX */
uint32_t
round_uint32_to_next_multiple_of(uint32_t number, uint32_t divisor)
{
+ tor_assert(divisor > 0);
+ if (UINT32_MAX - divisor + 1 < number)
+ return UINT32_MAX;
+
number += divisor - 1;
number -= number % divisor;
return number;
}
/** Return the lowest x such that x is at least <b>number</b>, and x modulo
- * <b>divisor</b> == 0. */
+ * <b>divisor</b> == 0. If no such x can be expressed as a uint64_t, return
+ * UINT64_MAX */
uint64_t
round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor)
{
+ tor_assert(divisor > 0);
+ if (UINT64_MAX - divisor + 1 < number)
+ return UINT64_MAX;
number += divisor - 1;
number -= number % divisor;
return number;
}
/** Return the lowest x in [INT64_MIN, INT64_MAX] such that x is at least
- * <b>number</b>, and x modulo <b>divisor</b> == 0. */
+ * <b>number</b>, and x modulo <b>divisor</b> == 0. If no such x can be
+ * expressed as an int64_t, return INT64_MAX */
int64_t
round_int64_to_next_multiple_of(int64_t number, int64_t divisor)
{
tor_assert(divisor > 0);
- if (number >= 0 && INT64_MAX - divisor + 1 >= number)
+ if (INT64_MAX - divisor + 1 < number)
+ return INT64_MAX;
+ if (number >= 0)
number += divisor - 1;
number -= number % divisor;
return number;
@@ -537,33 +553,44 @@ int64_t
sample_laplace_distribution(double mu, double b, double p)
{
double result;
-
tor_assert(p >= 0.0 && p < 1.0);
+
/* This is the "inverse cumulative distribution function" from:
* http://en.wikipedia.org/wiki/Laplace_distribution */
- result = mu - b * (p > 0.5 ? 1.0 : -1.0)
- * tor_mathlog(1.0 - 2.0 * fabs(p - 0.5));
-
- if (result >= INT64_MAX)
- return INT64_MAX;
- else if (result <= INT64_MIN)
+ if (p <= 0.0) {
+ /* Avoid taking log(0.0) == -INFINITY, as some processors or compiler
+ * options can cause the program to trap. */
return INT64_MIN;
- else
- return (int64_t) result;
+ }
+
+ result = mu - b * (p > 0.5 ? 1.0 : -1.0)
+ * tor_mathlog(1.0 - 2.0 * fabs(p - 0.5));
+
+ return clamp_double_to_int64(result);
}
-/** Add random noise between INT64_MIN and INT64_MAX coming from a
- * Laplace distribution with mu = 0 and b = <b>delta_f</b>/<b>epsilon</b>
- * to <b>signal</b> based on the provided <b>random</b> value in
- * [0.0, 1.0[. */
+/** Add random noise between INT64_MIN and INT64_MAX coming from a Laplace
+ * distribution with mu = 0 and b = <b>delta_f</b>/<b>epsilon</b> to
+ * <b>signal</b> based on the provided <b>random</b> value in [0.0, 1.0[.
+ * The epsilon value must be between ]0.0, 1.0]. delta_f must be greater
+ * than 0. */
int64_t
add_laplace_noise(int64_t signal, double random, double delta_f,
double epsilon)
{
- int64_t noise = sample_laplace_distribution(
- 0.0, /* just add noise, no further signal */
- delta_f / epsilon, random);
+ int64_t noise;
+
+ /* epsilon MUST be between ]0.0, 1.0] */
+ tor_assert(epsilon > 0.0 && epsilon <= 1.0);
+ /* delta_f MUST be greater than 0. */
+ tor_assert(delta_f > 0.0);
+ /* Just add noise, no further signal */
+ noise = sample_laplace_distribution(0.0,
+ delta_f / epsilon,
+ random);
+
+ /* Clip (signal + noise) to [INT64_MIN, INT64_MAX] */
if (noise > 0 && INT64_MAX - noise < signal)
return INT64_MAX;
else if (noise < 0 && INT64_MIN - noise > signal)
@@ -2029,9 +2056,10 @@ int
check_private_dir(const char *dirname, cpd_check_t check,
const char *effective_user)
{
+ int fd;
int r;
struct stat st;
- char *f;
+ //char *f;
#ifndef _WIN32
unsigned unwanted_bits = 0;
const struct passwd *pw = NULL;
@@ -2041,18 +2069,34 @@ check_private_dir(const char *dirname, cpd_check_t check,
(void)effective_user;
#endif
+ /*
+ * Goal is to harden the implementation by removing any
+ * potential for race between stat() and chmod().
+ * chmod() accepts filename as argument. If an attacker can move
+ * the file between stat() and chmod(), a potential race exists.
+ *
+ * Several suggestions taken from:
+ * https://developer.apple.com/library/mac/documentation/Security/Conceptual/SecureCodingGuide/Articles/RaceConditions.html
+ */
tor_assert(dirname);
- f = tor_strdup(dirname);
- clean_name_for_stat(f);
- log_debug(LD_FS, "stat()ing %s", f);
- r = stat(sandbox_intern_string(f), &st);
- tor_free(f);
- if (r) {
+
+ /* Open directory.
+ * O_NOFOLLOW to ensure that it does not follow symbolic links */
+ fd = open(sandbox_intern_string(dirname), O_NOFOLLOW);
+
+ /* Was there an error? Maybe the directory does not exist? */
+ if (fd == -1) {
+
if (errno != ENOENT) {
+ /* Other directory error */
log_warn(LD_FS, "Directory %s cannot be read: %s", dirname,
strerror(errno));
return -1;
}
+
+ /* Received ENOENT: Directory does not exist */
+
+ /* Should we create the directory? */
if (check & CPD_CREATE) {
log_info(LD_GENERAL, "Creating directory %s", dirname);
#if defined (_WIN32)
@@ -2064,23 +2108,51 @@ check_private_dir(const char *dirname, cpd_check_t check,
r = mkdir(dirname, 0700);
}
#endif
+
+ /* check for mkdir() error */
if (r) {
log_warn(LD_FS, "Error creating directory %s: %s", dirname,
strerror(errno));
return -1;
}
+
+ /* we just created the directory. try to open it again.
+ * permissions on the directory will be checked again below.*/
+ fd = open(sandbox_intern_string(dirname), O_NOFOLLOW);
+
+ if ( fd == -1 ) return -1;
+
} else if (!(check & CPD_CHECK)) {
log_warn(LD_FS, "Directory %s does not exist.", dirname);
return -1;
}
+
/* XXXX In the case where check==CPD_CHECK, we should look at the
* parent directory a little harder. */
return 0;
}
+
+ tor_assert(fd);
+
+ //f = tor_strdup(dirname);
+ //clean_name_for_stat(f);
+ log_debug(LD_FS, "stat()ing %s", dirname);
+ //r = stat(sandbox_intern_string(f), &st);
+ r = fstat(fd, &st);
+ if (r == -1) {
+ log_warn(LD_FS, "fstat() on directory %s failed.", dirname);
+ close(fd);
+ return -1;
+ }
+ //tor_free(f);
+
+ /* check that dirname is a directory */
if (!(st.st_mode & S_IFDIR)) {
log_warn(LD_FS, "%s is not a directory", dirname);
+ close(fd);
return -1;
}
+
#ifndef _WIN32
if (effective_user) {
/* Look up the user and group information.
@@ -2097,7 +2169,6 @@ check_private_dir(const char *dirname, cpd_check_t check,
running_uid = getuid();
running_gid = getgid();
}
-
if (st.st_uid != running_uid) {
const struct passwd *pw = NULL;
char *process_ownername = NULL;
@@ -2113,10 +2184,11 @@ check_private_dir(const char *dirname, cpd_check_t check,
pw ? pw->pw_name : "<unknown>", (int)st.st_uid);
tor_free(process_ownername);
+ close(fd);
return -1;
}
if ( (check & (CPD_GROUP_OK|CPD_GROUP_READ))
- && (st.st_gid != running_gid) ) {
+ && (st.st_gid != running_gid) && (st.st_gid != 0)) {
struct group *gr;
char *process_groupname = NULL;
gr = getgrgid(running_gid);
@@ -2129,6 +2201,7 @@ check_private_dir(const char *dirname, cpd_check_t check,
gr ? gr->gr_name : "<unknown>", (int)st.st_gid);
tor_free(process_groupname);
+ close(fd);
return -1;
}
if (check & (CPD_GROUP_OK|CPD_GROUP_READ)) {
@@ -2141,6 +2214,7 @@ check_private_dir(const char *dirname, cpd_check_t check,
if (check & CPD_CHECK_MODE_ONLY) {
log_warn(LD_FS, "Permissions on directory %s are too permissive.",
dirname);
+ close(fd);
return -1;
}
log_warn(LD_FS, "Fixing permissions on directory %s", dirname);
@@ -2150,15 +2224,18 @@ check_private_dir(const char *dirname, cpd_check_t check,
new_mode |= 0050; /* Group should have rx */
}
new_mode &= ~unwanted_bits; /* Clear the bits that we didn't want set...*/
- if (chmod(dirname, new_mode)) {
+ if (fchmod(fd, new_mode)) {
log_warn(LD_FS, "Could not chmod directory %s: %s", dirname,
strerror(errno));
+ close(fd);
return -1;
} else {
+ close(fd);
return 0;
}
}
#endif
+ close(fd);
return 0;
}
@@ -2873,6 +2950,9 @@ expand_filename(const char *filename)
{
tor_assert(filename);
#ifdef _WIN32
+ /* Might consider using GetFullPathName() as described here:
+ * http://etutorials.org/Programming/secure+programming/Chapter+3.+Input+Validation/3.7+Validating+Filenames+and+Paths/
+ */
return tor_strdup(filename);
#else
if (*filename == '~') {
@@ -4424,7 +4504,7 @@ tor_get_exit_code(process_handle_t *process_handle,
/** Helper: return the number of characters in <b>s</b> preceding the first
* occurrence of <b>ch</b>. If <b>ch</b> does not occur in <b>s</b>, return
* the length of <b>s</b>. Should be equivalent to strspn(s, "ch"). */
-static INLINE size_t
+static inline size_t
str_num_before(const char *s, char ch)
{
const char *cp = strchr(s, ch);
@@ -5385,3 +5465,38 @@ tor_weak_random_range(tor_weak_rng_t *rng, int32_t top)
return result;
}
+/** Cast a given double value to a int64_t. Return 0 if number is NaN.
+ * Returns either INT64_MIN or INT64_MAX if number is outside of the int64_t
+ * range. */
+int64_t
+clamp_double_to_int64(double number)
+{
+ int exp;
+
+ /* NaN is a special case that can't be used with the logic below. */
+ if (isnan(number)) {
+ return 0;
+ }
+
+ /* Time to validate if result can overflows a int64_t value. Fun with
+ * float! Find that exponent exp such that
+ * number == x * 2^exp
+ * for some x with abs(x) in [0.5, 1.0). Note that this implies that the
+ * magnitude of number is strictly less than 2^exp.
+ *
+ * If number is infinite, the call to frexp is legal but the contents of
+ * exp are unspecified. */
+ frexp(number, &exp);
+
+ /* If the magnitude of number is strictly less than 2^63, the truncated
+ * version of number is guaranteed to be representable. The only
+ * representable integer for which this is not the case is INT64_MIN, but
+ * it is covered by the logic below. */
+ if (isfinite(number) && exp <= 63) {
+ return number;
+ }
+
+ /* Handle infinities and finite numbers with magnitude >= 2^63. */
+ return signbit(number) ? INT64_MIN : INT64_MAX;
+}
+
diff --git a/src/common/util.h b/src/common/util.h
index 8bb4505e86..d42b5b2b0d 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -29,6 +29,9 @@
#ifndef O_TEXT
#define O_TEXT 0
#endif
+#ifndef O_NOFOLLOW
+#define O_NOFOLLOW 0
+#endif
/* Replace assert() with a variant that sends failures to the log before
* calling assert() normally.
@@ -45,9 +48,10 @@
#error "Sorry; we don't support building with NDEBUG."
#endif
-/* Don't use assertions during coverage. It leads to tons of unreached
- * branches which in reality are only assertions we didn't hit. */
-#ifdef TOR_COVERAGE
+/* Sometimes we don't want to use assertions during branch coverage tests; it
+ * leads to tons of unreached branches which in reality are only assertions we
+ * didn't hit. */
+#if defined(TOR_UNIT_TESTS) && defined(DISABLE_ASSERTS_IN_UNIT_TESTS)
#define tor_assert(a) STMT_BEGIN \
(void)(a); \
STMT_END
@@ -185,6 +189,7 @@ int64_t sample_laplace_distribution(double mu, double b, double p);
int64_t add_laplace_noise(int64_t signal, double random, double delta_f,
double epsilon);
int n_bits_set_u8(uint8_t v);
+int64_t clamp_double_to_int64(double number);
/* Compute the CEIL of <b>a</b> divided by <b>b</b>, for nonnegative <b>a</b>
* and positive <b>b</b>. Works on integer types only. Not defined if a+b can
diff --git a/src/common/util_format.c b/src/common/util_format.c
index dc544a6c2e..8d99138506 100644
--- a/src/common/util_format.c
+++ b/src/common/util_format.c
@@ -465,7 +465,7 @@ base16_encode(char *dest, size_t destlen, const char *src, size_t srclen)
}
/** Helper: given a hex digit, return its value, or -1 if it isn't hex. */
-static INLINE int
+static inline int
hex_decode_digit_(char c)
{
switch (c) {
diff --git a/src/common/util_process.c b/src/common/util_process.c
index 849a5c0b63..1e3b02cc9a 100644
--- a/src/common/util_process.c
+++ b/src/common/util_process.c
@@ -45,13 +45,13 @@ struct waitpid_callback_t {
unsigned running;
};
-static INLINE unsigned int
+static inline unsigned int
process_map_entry_hash_(const waitpid_callback_t *ent)
{
return (unsigned) ent->pid;
}
-static INLINE unsigned int
+static inline unsigned int
process_map_entries_eq_(const waitpid_callback_t *a,
const waitpid_callback_t *b)
{
diff --git a/src/ext/README b/src/ext/README
index 5501aba758..7ce1bc3b74 100644
--- a/src/ext/README
+++ b/src/ext/README
@@ -65,6 +65,10 @@ ed25519/donna/*
Andrew Moon's semi-portable ed25519-donna implementation of
ed25519. Public domain.
+keccak-tiny/
+
+ David Leon Gil's portable Keccak implementation. CC0.
+
readpassphrase.[ch]
Portable readpassphrase implementation from OpenSSH portable, version
diff --git a/src/ext/csiphash.c b/src/ext/csiphash.c
index 27c5358ebe..b60f73a7ff 100644
--- a/src/ext/csiphash.c
+++ b/src/ext/csiphash.c
@@ -97,65 +97,48 @@
#endif
uint64_t siphash24(const void *src, unsigned long src_sz, const struct sipkey *key) {
+ const uint8_t *m = src;
uint64_t k0 = key->k0;
uint64_t k1 = key->k1;
- uint64_t b = (uint64_t)src_sz << 56;
-#ifdef UNALIGNED_OK
- const uint64_t *in = (uint64_t*)src;
-#else
- /* On platforms where alignment matters, if 'in' is a pointer to a
- * datatype that must be aligned, the compiler is allowed to
- * generate code that assumes that it is aligned as such.
- */
- const uint8_t *in = (uint8_t *)src;
-#endif
-
- uint64_t t;
- uint8_t *pt, *m;
+ uint64_t last7 = (uint64_t)(src_sz & 0xff) << 56;
+ size_t i, blocks;
uint64_t v0 = k0 ^ 0x736f6d6570736575ULL;
uint64_t v1 = k1 ^ 0x646f72616e646f6dULL;
uint64_t v2 = k0 ^ 0x6c7967656e657261ULL;
uint64_t v3 = k1 ^ 0x7465646279746573ULL;
- while (src_sz >= 8) {
+ for (i = 0, blocks = (src_sz & ~7); i < blocks; i+= 8) {
#ifdef UNALIGNED_OK
- uint64_t mi = _le64toh(*in);
- in += 1;
+ uint64_t mi = _le64toh(*(m + i));
#else
uint64_t mi;
- memcpy(&mi, in, 8);
+ memcpy(&mi, m + i, 8);
mi = _le64toh(mi);
- in += 8;
#endif
- src_sz -= 8;
v3 ^= mi;
DOUBLE_ROUND(v0,v1,v2,v3);
v0 ^= mi;
}
- t = 0; pt = (uint8_t*)&t; m = (uint8_t*)in;
- switch (src_sz) {
- case 7: pt[6] = m[6];
- case 6: pt[5] = m[5];
- case 5: pt[4] = m[4];
-#ifdef UNALIGNED_OK
- case 4: *((uint32_t*)&pt[0]) = *((uint32_t*)&m[0]); break;
-#else
- case 4: pt[3] = m[3];
-#endif
- case 3: pt[2] = m[2];
- case 2: pt[1] = m[1];
- case 1: pt[0] = m[0];
+ switch (src_sz - blocks) {
+ case 7: last7 |= (uint64_t)m[i + 6] << 48;
+ case 6: last7 |= (uint64_t)m[i + 5] << 40;
+ case 5: last7 |= (uint64_t)m[i + 4] << 32;
+ case 4: last7 |= (uint64_t)m[i + 3] << 24;
+ case 3: last7 |= (uint64_t)m[i + 2] << 16;
+ case 2: last7 |= (uint64_t)m[i + 1] << 8;
+ case 1: last7 |= (uint64_t)m[i + 0] ;
+ case 0:
+ default:;
}
- b |= _le64toh(t);
-
- v3 ^= b;
+ v3 ^= last7;
DOUBLE_ROUND(v0,v1,v2,v3);
- v0 ^= b; v2 ^= 0xff;
+ v0 ^= last7;
+ v2 ^= 0xff;
DOUBLE_ROUND(v0,v1,v2,v3);
DOUBLE_ROUND(v0,v1,v2,v3);
- return (v0 ^ v1) ^ (v2 ^ v3);
+ return v0 ^ v1 ^ v2 ^ v3;
}
diff --git a/src/ext/ed25519/donna/ed25519_tor.c b/src/ext/ed25519/donna/ed25519_tor.c
index 12493f7d14..ac726ba045 100644
--- a/src/ext/ed25519/donna/ed25519_tor.c
+++ b/src/ext/ed25519/donna/ed25519_tor.c
@@ -148,8 +148,7 @@ ed25519_donna_seckey(unsigned char *sk)
{
ed25519_secret_key seed;
- if (crypto_strongest_rand(seed, 32))
- return -1;
+ crypto_strongest_rand(seed, 32);
ed25519_extsk(sk, seed);
diff --git a/src/ext/ed25519/ref10/randombytes.h b/src/ext/ed25519/ref10/randombytes.h
index fc709fcefc..8bf31631f0 100644
--- a/src/ext/ed25519/ref10/randombytes.h
+++ b/src/ext/ed25519/ref10/randombytes.h
@@ -1,4 +1,4 @@
/* Added for Tor. */
#include "crypto.h"
#define randombytes(b, n) \
- (crypto_strongest_rand((b), (n)))
+ (crypto_strongest_rand((b), (n)), 0)
diff --git a/src/ext/eventdns.c b/src/ext/eventdns.c
index a0c7ff29fa..fc5657cbb4 100644
--- a/src/ext/eventdns.c
+++ b/src/ext/eventdns.c
@@ -388,7 +388,7 @@ debug_ntoa(u32 address)
{
static char buf[32];
u32 a = ntohl(address);
- snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
+ tor_snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
(int)(u8)((a>>24)&0xff),
(int)(u8)((a>>16)&0xff),
(int)(u8)((a>>8 )&0xff),
@@ -436,12 +436,7 @@ evdns_log(int warn, const char *fmt, ...)
if (!evdns_log_fn)
return;
va_start(args,fmt);
-#ifdef _WIN32
- _vsnprintf(buf, sizeof(buf), fmt, args);
-#else
- vsnprintf(buf, sizeof(buf), fmt, args);
-#endif
- buf[sizeof(buf)-1] = '\0';
+ tor_vsnprintf(buf, sizeof(buf), fmt, args);
evdns_log_fn(warn, buf);
va_end(args);
}
@@ -762,7 +757,7 @@ reply_handle(struct evdns_request *const req, u16 flags, u32 ttl, struct reply *
/* we regard these errors as marking a bad nameserver */
if (req->reissue_count < global_max_reissues) {
char msg[64];
- snprintf(msg, sizeof(msg), "Bad response %d (%s)",
+ tor_snprintf(msg, sizeof(msg), "Bad response %d (%s)",
error, evdns_err_to_string(error));
nameserver_failed(req->ns, msg);
if (!request_reissue(req)) return;
@@ -805,7 +800,7 @@ reply_handle(struct evdns_request *const req, u16 flags, u32 ttl, struct reply *
}
}
-static INLINE int
+static inline int
name_parse(u8 *packet, int length, int *idx, char *name_out, size_t name_out_len) {
int name_end = -1;
int j = *idx;
@@ -1705,7 +1700,7 @@ evdns_server_request_add_ptr_reply(struct evdns_server_request *req, struct in_a
assert(!(in && inaddr_name));
if (in) {
a = ntohl(in->s_addr);
- snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa",
+ tor_snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa",
(int)(u8)((a )&0xff),
(int)(u8)((a>>8 )&0xff),
(int)(u8)((a>>16)&0xff),
@@ -2638,7 +2633,7 @@ int evdns_resolve_reverse(const struct in_addr *in, int flags, evdns_callback_ty
u32 a;
assert(in);
a = ntohl(in->s_addr);
- snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa",
+ tor_snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa",
(int)(u8)((a )&0xff),
(int)(u8)((a>>8 )&0xff),
(int)(u8)((a>>16)&0xff),
diff --git a/src/ext/ht.h b/src/ext/ht.h
index 19a67a6a41..28d1fe49d5 100644
--- a/src/ext/ht.h
+++ b/src/ext/ht.h
@@ -61,7 +61,7 @@
#define HT_INIT(name, head) name##_HT_INIT(head)
#define HT_REP_IS_BAD_(name, head) name##_HT_REP_IS_BAD_(head)
/* Helper: */
-static INLINE unsigned
+static inline unsigned
ht_improve_hash(unsigned h)
{
/* Aim to protect against poor hash functions by adding logic here
@@ -75,7 +75,7 @@ ht_improve_hash(unsigned h)
#if 0
/** Basic string hash function, from Java standard String.hashCode(). */
-static INLINE unsigned
+static inline unsigned
ht_string_hash(const char *s)
{
unsigned h = 0;
@@ -90,7 +90,7 @@ ht_string_hash(const char *s)
#if 0
/** Basic string hash function, from Python's str.__hash__() */
-static INLINE unsigned
+static inline unsigned
ht_string_hash(const char *s)
{
unsigned h;
@@ -143,7 +143,7 @@ ht_string_hash(const char *s)
int name##_HT_GROW(struct name *ht, unsigned min_capacity); \
void name##_HT_CLEAR(struct name *ht); \
int name##_HT_REP_IS_BAD_(const struct name *ht); \
- static INLINE void \
+ static inline void \
name##_HT_INIT(struct name *head) { \
head->hth_table_length = 0; \
head->hth_table = NULL; \
@@ -153,7 +153,7 @@ ht_string_hash(const char *s)
} \
/* Helper: returns a pointer to the right location in the table \
* 'head' to find or insert the element 'elm'. */ \
- static INLINE struct type ** \
+ static inline struct type ** \
name##_HT_FIND_P_(struct name *head, struct type *elm) \
{ \
struct type **p; \
@@ -169,7 +169,7 @@ ht_string_hash(const char *s)
} \
/* Return a pointer to the element in the table 'head' matching 'elm', \
* or NULL if no such element exists */ \
- ATTR_UNUSED static INLINE struct type * \
+ ATTR_UNUSED static inline struct type * \
name##_HT_FIND(const struct name *head, struct type *elm) \
{ \
struct type **p; \
@@ -180,7 +180,7 @@ ht_string_hash(const char *s)
} \
/* Insert the element 'elm' into the table 'head'. Do not call this \
* function if the table might already contain a matching element. */ \
- ATTR_UNUSED static INLINE void \
+ ATTR_UNUSED static inline void \
name##_HT_INSERT(struct name *head, struct type *elm) \
{ \
struct type **p; \
@@ -195,7 +195,7 @@ ht_string_hash(const char *s)
/* Insert the element 'elm' into the table 'head'. If there already \
* a matching element in the table, replace that element and return \
* it. */ \
- ATTR_UNUSED static INLINE struct type * \
+ ATTR_UNUSED static inline struct type * \
name##_HT_REPLACE(struct name *head, struct type *elm) \
{ \
struct type **p, *r; \
@@ -216,7 +216,7 @@ ht_string_hash(const char *s)
} \
/* Remove any element matching 'elm' from the table 'head'. If such \
* an element is found, return it; otherwise return NULL. */ \
- ATTR_UNUSED static INLINE struct type * \
+ ATTR_UNUSED static inline struct type * \
name##_HT_REMOVE(struct name *head, struct type *elm) \
{ \
struct type **p, *r; \
@@ -234,7 +234,7 @@ ht_string_hash(const char *s)
* using 'data' as its second argument. If the function returns \
* nonzero, remove the most recently examined element before invoking \
* the function again. */ \
- ATTR_UNUSED static INLINE void \
+ ATTR_UNUSED static inline void \
name##_HT_FOREACH_FN(struct name *head, \
int (*fn)(struct type *, void *), \
void *data) \
@@ -260,7 +260,7 @@ ht_string_hash(const char *s)
/* Return a pointer to the first element in the table 'head', under \
* an arbitrary order. This order is stable under remove operations, \
* but not under others. If the table is empty, return NULL. */ \
- ATTR_UNUSED static INLINE struct type ** \
+ ATTR_UNUSED static inline struct type ** \
name##_HT_START(struct name *head) \
{ \
unsigned b = 0; \
@@ -279,7 +279,7 @@ ht_string_hash(const char *s)
* NULL. If 'elm' is to be removed from the table, you must call \
* this function for the next value before you remove it. \
*/ \
- ATTR_UNUSED static INLINE struct type ** \
+ ATTR_UNUSED static inline struct type ** \
name##_HT_NEXT(struct name *head, struct type **elm) \
{ \
if ((*elm)->field.hte_next) { \
@@ -299,7 +299,7 @@ ht_string_hash(const char *s)
return NULL; \
} \
} \
- ATTR_UNUSED static INLINE struct type ** \
+ ATTR_UNUSED static inline struct type ** \
name##_HT_NEXT_RMV(struct name *head, struct type **elm) \
{ \
unsigned h = HT_ELT_HASH_(*elm, field, hashfn); \
diff --git a/src/ext/include.am b/src/ext/include.am
index 47d4c03d0b..bf678f2c9d 100644
--- a/src/ext/include.am
+++ b/src/ext/include.am
@@ -135,3 +135,16 @@ noinst_HEADERS += $(ED25519_DONNA_HDRS)
LIBED25519_DONNA=src/ext/ed25519/donna/libed25519_donna.a
noinst_LIBRARIES += $(LIBED25519_DONNA)
+src_ext_keccak_tiny_libkeccak_tiny_a_CFLAGS=
+
+src_ext_keccak_tiny_libkeccak_tiny_a_SOURCES= \
+ src/ext/keccak-tiny/keccak-tiny-unrolled.c
+
+LIBKECCAK_TINY_HDRS = \
+ src/ext/keccak-tiny/keccak-tiny.h
+
+noinst_HEADERS += $(LIBKECCAK_TINY_HDRS)
+
+LIBKECCAK_TINY=src/ext/keccak-tiny/libkeccak-tiny.a
+noinst_LIBRARIES += $(LIBKECCAK_TINY)
+
diff --git a/src/ext/keccak-tiny/README.markdown b/src/ext/keccak-tiny/README.markdown
new file mode 100644
index 0000000000..784d6f6bdb
--- /dev/null
+++ b/src/ext/keccak-tiny/README.markdown
@@ -0,0 +1,82 @@
+# libkeccak-tiny
+
+An implementation of the FIPS-202-defined SHA-3 and SHAKE functions
+in 120 cloc (156 lines). One C file, one header.
+
+The `Keccak-f[1600]` permutation is fully unrolled; it's nearly as fast
+as the Keccak team's optimized permutation.
+
+## Building
+
+ > clang -O3 -march=native -std=c11 -Wextra -dynamic -shared keccak-tiny.c -o libkeccak-tiny.dylib
+
+If you don't have a modern libc that includes the `memset_s` function,
+you can just add `-D"memset_s(W,WL,V,OL)=memset(W,V,OL)` to the command
+line.
+
+## Using
+
+Build the library, include the header, and do, e.g.,
+
+ shake256(out, 256, in, inlen);
+
+That's it.
+
+(Note: You can request less output from the fixed-output-length
+functions, but not more.)
+
+## TweetShake
+
+The relevant tweets:
+
+```C
+// @hashbreaker Inspired by TweetNaCl!
+// Keccak and SHA-3 are supposedly hard to implement. So, how many tweets does it take to get to the center of a sponge...?
+#define decshake(bits) int shake##bits(unsigned char* o, unsigned long, unsigned char*, unsigned long); /*begin keccak.h*/
+#define decsha3(bits) int sha3_##bits(unsigned char*,unsigned long,unsigned char*,unsigned long);
+decshake(128) decshake(256) decsha3(224) decsha3(256) decsha3(384) decsha3(512) /*end keccak.h*/
+#define K static const /* Keccak constants: rho rotations, pi lanes, and iota RCs */ /*begin keccak.c*/
+typedef unsigned char byte;typedef byte*bytes;typedef unsigned long z;typedef unsigned long long u8;K u8 V=1ULL<<63;K u8 W=1ULL<<31;/*!gcc*/
+#define V (1ULL<<63)
+#define W (1ULL<31)
+K byte rho[24]={1,3,6,10,15,21,28,36,45,55,2,14,27,41,56,8,25,43,62,18,39,61,20,44};K u8 RC[24]={1,0x8082,V|0x808a,V|W|0x8000,0x808b,W|1,V|W
+|0x8081,V|0x8009,138,136,W|0x8009,W|10,W|0x808b,V|0x8b,V|0x8089,V|0x8003,V|0x8002,V|0x80,0x800a,V|W|0xa,V|W|0x8081,V|0x8080,W|1,V|W|0x8008};
+K byte pi[25]={10,7,11,17,18,3,5,16,8,21,24,4,15,23,19,13,12,2,20,14,22,9,6,1}; /**helpers:*/static inline z min(z a,z b){return (a<b)?a:b;}
+#define ROL(x, s) /* rotary shift */ (((x) << s) | ((x) >> (64-s))) /**macros to fully unroll the Keccak-f[1600] permutation:*/
+#define R24(e) /* repeat 24 times */ e e e e e e e e e e e e e e e e e e e e e e e e
+#define L5(v,s,e) /* 5-unroll a loop */ v=0; e; v+=s; e; v+=s; e; v+=s; e; v+=s; e; v+=s; /**the permutation:*/
+static inline void keccakf(u8* a){u8 b[5]={0};u8 t=0;byte x,y,i=0; /*24 rounds:*/R24( L5(x,1,b[x]=0;L5(y,5, /*parity*/ b[x] ^= a[x+y]))
+L5(x,1,L5(y,5,/*theta*/a[y+x] ^= b[(x+4)%5] ^ ROL(b[(x+1)%5],1))) t=a[1];x=0;R24(b[0]=a[pi[x]];/*rho*/a[pi[x]]=ROL(t, rho[x]);t=b[0];x++;)
+L5(y,5,L5(x,1, /*chi*/ b[x] = a[y+x]) L5(x,1, a[y+x] = b[x] ^ ~b[(x+1)%5] & b[(x+2)%5])) /*iota*/ a[0] ^= RC[i]; i++; )} /**keccak-f!**/
+#define FOR(i, ST, L, S) /*obvious*/ do { for (z i = 0; i < L; i += ST) { S; } } while (0) /**now, the sponge construction in hash mode**/
+#define appl(NAME, S) /*macro to define array comprehensions*/ static inline void NAME(bytes dst, bytes src, z len) { FOR(i, 1, len, S); }
+/*helpers:*/ static inline void clear(bytes a) { FOR(i,1,200,a[i]=0); } appl(xorin, dst[i] ^= src[i]) appl(set, src[i] = dst[i])
+#define foldP(I, L, F) /* macro to fold app P F */ while (L >= r) { /*apply F*/ F(a, I, r); /*permute*/ keccakf(A); I += r; L -= r; }
+static inline int hash(bytes o,z olen,bytes in,z ilen,z r,byte D){ if((o == (void*)0)||((in == (void*)0)&&ilen != 0)||(r >= 200))return -1;
+/*absorb*/u8 A[25]={0};bytes a=(bytes)A;/*full blocks*/foldP(in,ilen,xorin);/*last block*/xorin(a,in,ilen);/**ds+padstart*/a[ilen]^=D;
+/*padend:*/a[r-1]^=0x80; /**permute**/keccakf(A); /**squeeze:**/foldP(o,olen,set);/*last bytes*/set(a,o,olen);/*done!*/clear(a);return 0;}
+#define defshake(bits) int shake##bits(bytes o, z olen, bytes in, z ilen) {return hash(o,olen,in,ilen,200-(bits/4),0x1f);}
+#define defsha3(bits) int sha3_##bits(bytes o,z olen,bytes in,z ilen) {return hash(o,min(olen,200-(bits/4)),in,ilen,200-(bits/4),0x06);}
+/*define the SHA3 and SHAKE instances:*/defshake(128) defshake(256) defsha3(224) defsha3(256) defsha3(384) defsha3(512)/*end keccak.c*/
+// ...chomp. 24 kinda legible tweets (3232 bytes). And a simple interface: shake256(digest, digestlen, in, inlen)
+// Clang recommended. GCC users will need to insert "#define V (1ULL<<63)" and "#define W (1ULL<31)" at the point marked "/*!gcc*/"
+// If you're using as a prefix MAC, you MUST replace the body of "clear" with "memset_s(a, 200, 0, 200)" to avoid misoptimization.
+// @everyone_who_is_still_using_sha1 Please stop using SHA-1.
+// Oh, one more thing: a C11-threaded, memmapped shake256sum in 10 tweets. (Your libc may need a shim for C11 thread support.)
+// echo -n string stdio stdint fcntl sys/mman sys/stat sys/types unistd threads|tr ' ' \\n|xargs -n1 -I_ echo '#include <_.h>'
+#include "kcksum_tweet.h"
+#define E(LABEL, MSG) if (err != 0) { strerror_r(err, serr, 1024); fprintf(stderr, "%s: '%s' %s\n", serr, fn, MSG); goto LABEL;}
+static mtx_t iomtx;void h(void* v);void h(void* v){char* fn=(char*)v;int err=0;char serr[1024]={0};/*open file*/int fd=open(fn, O_RDONLY);
+err=!fd;E(ret,"couldn't be opened.");/*stat it*/struct stat stat;err=fstat(fd,&stat);E(close,"doesn't exist.");err=!!(stat.st_mode&S_IFDIR);
+E(close,"not a regular file.");z length=(size_t)stat.st_size;/*mmap the file*/bytes in=length?mmap(0,length,PROT_READ,MAP_SHARED,fd,0):NULL;
+if(length&&(in==MAP_FAILED)){E(close,"mmap-ing failed.");}byte out[64]={0};/*hash it*/shake256(out,64,in,length);length&&munmap(in,length);
+/*lock io*/mtx_lock(&iomtx);printf("SHAKE256('%s') = ", fn);FOR(i,1,64,printf("%02x",out[i]));printf("\n");mtx_unlock(&iomtx);/*unlock io*/
+close:close(fd);ret:thrd_exit(err);}int main(int argc,char** argv){int err=0; mtx_init(&iomtx, mtx_plain); thrd_t t[4]; int res[4],i,j,k;
+for(i=1;i<argc;i+=4){for(j=0;j<4;j++){if((j+i)==argc){/*out of files*/goto join;} /*spawn*/ thrd_create(t + j,h,argv[i + j]);}
+join: for (k = 0; k < j; k++) { /*wait*/ err |= thrd_join(t[k], res + k); err |= res[k];} } mtx_destroy(&iomtx); return err; } /* done! */
+```
+
+
+## License
+
+[CC0](http://creativecommons.org/publicdomain/zero/1.0/)
diff --git a/src/ext/keccak-tiny/do.sh b/src/ext/keccak-tiny/do.sh
new file mode 100644
index 0000000000..cf99f249e7
--- /dev/null
+++ b/src/ext/keccak-tiny/do.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env sh
+cc=$(which clang-3.6||which gcc-4.9||which clang||||which gcc)
+so=$(test -f /etc/asl.conf && printf dylib|| printf so)
+$cc "-Dinline=__attribute__((__always_inline__))" -O3 -march=native -std=c11 -Wextra -Wpedantic -Wall -dynamic -shared keccak-tiny.c -o libkeccak-tiny.$so
+$cc -Os -march=native -std=c11 -Wextra -Wpedantic -Wall -dynamic -shared keccak-tiny.c -o libkeccak-tiny-small.$so
diff --git a/src/ext/keccak-tiny/keccak-tiny-unrolled.c b/src/ext/keccak-tiny/keccak-tiny-unrolled.c
new file mode 100644
index 0000000000..4b4f51c7cf
--- /dev/null
+++ b/src/ext/keccak-tiny/keccak-tiny-unrolled.c
@@ -0,0 +1,368 @@
+/** libkeccak-tiny
+ *
+ * A single-file implementation of SHA-3 and SHAKE.
+ *
+ * Implementor: David Leon Gil
+ * License: CC0, attribution kindly requested. Blame taken too,
+ * but not liability.
+ */
+#include "keccak-tiny.h"
+
+#include <string.h>
+#include "crypto.h"
+
+/******** The Keccak-f[1600] permutation ********/
+
+/*** Constants. ***/
+static const uint8_t rho[24] = \
+ { 1, 3, 6, 10, 15, 21,
+ 28, 36, 45, 55, 2, 14,
+ 27, 41, 56, 8, 25, 43,
+ 62, 18, 39, 61, 20, 44};
+static const uint8_t pi[24] = \
+ {10, 7, 11, 17, 18, 3,
+ 5, 16, 8, 21, 24, 4,
+ 15, 23, 19, 13, 12, 2,
+ 20, 14, 22, 9, 6, 1};
+static const uint64_t RC[24] = \
+ {1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL,
+ 0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL,
+ 0x8aULL, 0x88ULL, 0x80008009ULL, 0x8000000aULL,
+ 0x8000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL,
+ 0x8000000000008002ULL, 0x8000000000000080ULL, 0x800aULL, 0x800000008000000aULL,
+ 0x8000000080008081ULL, 0x8000000000008080ULL, 0x80000001ULL, 0x8000000080008008ULL};
+
+/*** Helper macros to unroll the permutation. ***/
+#define rol(x, s) (((x) << s) | ((x) >> (64 - s)))
+#define REPEAT6(e) e e e e e e
+#define REPEAT24(e) REPEAT6(e e e e)
+#define REPEAT5(e) e e e e e
+#define FOR5(v, s, e) \
+ v = 0; \
+ REPEAT5(e; v += s;)
+
+/*** Keccak-f[1600] ***/
+static inline void keccakf(void* state) {
+ uint64_t* a = (uint64_t*)state;
+ uint64_t b[5] = {0};
+ uint64_t t = 0;
+ uint8_t x, y, i = 0;
+
+ REPEAT24(
+ // Theta
+ FOR5(x, 1,
+ b[x] = 0;
+ FOR5(y, 5,
+ b[x] ^= a[x + y]; ))
+ FOR5(x, 1,
+ FOR5(y, 5,
+ a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); ))
+ // Rho and pi
+ t = a[1];
+ x = 0;
+ REPEAT24(b[0] = a[pi[x]];
+ a[pi[x]] = rol(t, rho[x]);
+ t = b[0];
+ x++; )
+ // Chi
+ FOR5(y,
+ 5,
+ FOR5(x, 1,
+ b[x] = a[y + x];)
+ FOR5(x, 1,
+ a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]); ))
+ // Iota
+ a[0] ^= RC[i];
+ i++; )
+}
+
+/******** The FIPS202-defined functions. ********/
+
+/*** Some helper macros. ***/
+
+#define _(S) do { S } while (0)
+#define FOR(i, ST, L, S) \
+ _(for (size_t i = 0; i < L; i += ST) { S; })
+#define mkapply_ds(NAME, S) \
+ static inline void NAME(uint8_t* dst, \
+ const uint8_t* src, \
+ size_t len) { \
+ FOR(i, 1, len, S); \
+ }
+#define mkapply_sd(NAME, S) \
+ static inline void NAME(const uint8_t* src, \
+ uint8_t* dst, \
+ size_t len) { \
+ FOR(i, 1, len, S); \
+ }
+
+mkapply_ds(xorin, dst[i] ^= src[i]) // xorin
+mkapply_sd(setout, dst[i] = src[i]) // setout
+
+#define P keccakf
+#define Plen KECCAK_MAX_RATE
+
+#define KECCAK_DELIM_DIGEST 0x06
+#define KECCAK_DELIM_XOF 0x1f
+
+// Fold P*F over the full blocks of an input.
+#define foldP(I, L, F) \
+ while (L >= s->rate) { \
+ F(s->a, I, s->rate); \
+ P(s->a); \
+ I += s->rate; \
+ L -= s->rate; \
+ }
+
+static inline void
+keccak_absorb_blocks(keccak_state *s, const uint8_t *buf, size_t nr_blocks)
+{
+ size_t blen = nr_blocks * s->rate;
+ foldP(buf, blen, xorin);
+}
+
+static int
+keccak_update(keccak_state *s, const uint8_t *buf, size_t len)
+{
+ if (s->finalized)
+ return -1;
+ if ((buf == NULL) && len != 0)
+ return -1;
+
+ size_t remaining = len;
+ while (remaining > 0) {
+ if (s->offset == 0) {
+ const size_t blocks = remaining / s->rate;
+ size_t direct_bytes = blocks * s->rate;
+ if (direct_bytes > 0) {
+ keccak_absorb_blocks(s, buf, blocks);
+ remaining -= direct_bytes;
+ buf += direct_bytes;
+ }
+ }
+
+ const size_t buf_avail = s->rate - s->offset;
+ const size_t buf_bytes = (buf_avail > remaining) ? remaining : buf_avail;
+ if (buf_bytes > 0) {
+ memcpy(&s->block[s->offset], buf, buf_bytes);
+ s->offset += buf_bytes;
+ remaining -= buf_bytes;
+ buf += buf_bytes;
+ }
+ if (s->offset == s->rate) {
+ keccak_absorb_blocks(s, s->block, 1);
+ s->offset = 0;
+ }
+ }
+ return 0;
+}
+
+static void
+keccak_finalize(keccak_state *s)
+{
+ // Xor in the DS and pad frame.
+ s->a[s->offset] ^= s->delim;
+ s->a[s->rate - 1] ^= 0x80;
+ // Xor in the last block.
+ xorin(s->a, s->block, s->offset);
+
+ memwipe(s->block, 0, sizeof(s->block));
+ s->finalized = 1;
+ s->offset = s->rate;
+}
+
+static inline void
+keccak_squeeze_blocks(keccak_state *s, uint8_t *out, size_t nr_blocks)
+{
+ for (size_t n = 0; n < nr_blocks; n++) {
+ keccakf(s->a);
+ setout(s->a, out, s->rate);
+ out += s->rate;
+ }
+}
+
+static int
+keccak_squeeze(keccak_state *s, uint8_t *out, size_t outlen)
+{
+ if (!s->finalized)
+ return -1;
+
+ size_t remaining = outlen;
+ while (remaining > 0) {
+ if (s->offset == s->rate) {
+ const size_t blocks = remaining / s->rate;
+ const size_t direct_bytes = blocks * s->rate;
+ if (blocks > 0) {
+ keccak_squeeze_blocks(s, out, blocks);
+ out += direct_bytes;
+ remaining -= direct_bytes;
+ }
+
+ if (remaining > 0) {
+ keccak_squeeze_blocks(s, s->block, 1);
+ s->offset = 0;
+ }
+ }
+
+ const size_t buf_bytes = s->rate - s->offset;
+ const size_t indirect_bytes = (buf_bytes > remaining) ? remaining : buf_bytes;
+ if (indirect_bytes > 0) {
+ memcpy(out, &s->block[s->offset], indirect_bytes);
+ out += indirect_bytes;
+ s->offset += indirect_bytes;
+ remaining -= indirect_bytes;
+ }
+ }
+ return 0;
+}
+
+int
+keccak_digest_init(keccak_state *s, size_t bits)
+{
+ if (s == NULL)
+ return -1;
+ if (bits != 224 && bits != 256 && bits != 384 && bits != 512)
+ return -1;
+
+ keccak_cleanse(s);
+ s->rate = KECCAK_RATE(bits);
+ s->delim = KECCAK_DELIM_DIGEST;
+ return 0;
+}
+
+int
+keccak_digest_update(keccak_state *s, const uint8_t *buf, size_t len)
+{
+ if (s == NULL)
+ return -1;
+ if (s->delim != KECCAK_DELIM_DIGEST)
+ return -1;
+
+ return keccak_update(s, buf, len);
+}
+
+int
+keccak_digest_sum(const keccak_state *s, uint8_t *out, size_t outlen)
+{
+ if (s == NULL)
+ return -1;
+ if (s->delim != KECCAK_DELIM_DIGEST)
+ return -1;
+ if (out == NULL || outlen > 4 * (KECCAK_MAX_RATE - s->rate) / 8)
+ return -1;
+
+ // Work in a copy so that incremental/rolling hashes are easy.
+ keccak_state s_tmp;
+ keccak_clone(&s_tmp, s);
+ keccak_finalize(&s_tmp);
+ int ret = keccak_squeeze(&s_tmp, out, outlen);
+ keccak_cleanse(&s_tmp);
+ return ret;
+}
+
+int
+keccak_xof_init(keccak_state *s, size_t bits)
+{
+ if (s == NULL)
+ return -1;
+ if (bits != 128 && bits != 256)
+ return -1;
+
+ keccak_cleanse(s);
+ s->rate = KECCAK_RATE(bits);
+ s->delim = KECCAK_DELIM_XOF;
+ return 0;
+}
+
+int
+keccak_xof_absorb(keccak_state *s, const uint8_t *buf, size_t len)
+{
+ if (s == NULL)
+ return -1;
+ if (s->delim != KECCAK_DELIM_XOF)
+ return -1;
+
+ return keccak_update(s, buf, len);
+}
+
+int
+keccak_xof_squeeze(keccak_state *s, uint8_t *out, size_t outlen)
+{
+ if (s == NULL)
+ return -1;
+ if (s->delim != KECCAK_DELIM_XOF)
+ return -1;
+
+ if (!s->finalized)
+ keccak_finalize(s);
+
+ return keccak_squeeze(s, out, outlen);
+}
+
+void
+keccak_clone(keccak_state *out, const keccak_state *in)
+{
+ memcpy(out, in, sizeof(keccak_state));
+}
+
+void
+keccak_cleanse(keccak_state *s)
+{
+ memwipe(s, 0, sizeof(keccak_state));
+}
+
+/** The sponge-based hash construction. **/
+static inline int hash(uint8_t* out, size_t outlen,
+ const uint8_t* in, size_t inlen,
+ size_t bits, uint8_t delim) {
+ if ((out == NULL) || ((in == NULL) && inlen != 0)) {
+ return -1;
+ }
+
+ int ret = 0;
+ keccak_state s;
+
+ switch (delim) {
+ case KECCAK_DELIM_DIGEST:
+ ret |= keccak_digest_init(&s, bits);
+ ret |= keccak_digest_update(&s, in, inlen);
+ // Use the internal API instead of sum to avoid the memcpy.
+ keccak_finalize(&s);
+ ret |= keccak_squeeze(&s, out, outlen);
+ break;
+ case KECCAK_DELIM_XOF:
+ ret |= keccak_xof_init(&s, bits);
+ ret |= keccak_xof_absorb(&s, in, inlen);
+ ret |= keccak_xof_squeeze(&s, out, outlen);
+ break;
+ default:
+ return -1;
+ }
+ keccak_cleanse(&s);
+ return ret;
+}
+
+/*** Helper macros to define SHA3 and SHAKE instances. ***/
+#define defshake(bits) \
+ int shake##bits(uint8_t* out, size_t outlen, \
+ const uint8_t* in, size_t inlen) { \
+ return hash(out, outlen, in, inlen, bits, KECCAK_DELIM_XOF); \
+ }
+#define defsha3(bits) \
+ int sha3_##bits(uint8_t* out, size_t outlen, \
+ const uint8_t* in, size_t inlen) { \
+ if (outlen > (bits/8)) { \
+ return -1; \
+ } \
+ return hash(out, outlen, in, inlen, bits, KECCAK_DELIM_DIGEST); \
+ }
+
+/*** FIPS202 SHAKE VOFs ***/
+defshake(128)
+defshake(256)
+
+/*** FIPS202 SHA3 FOFs ***/
+defsha3(224)
+defsha3(256)
+defsha3(384)
+defsha3(512)
diff --git a/src/ext/keccak-tiny/keccak-tiny.c b/src/ext/keccak-tiny/keccak-tiny.c
new file mode 100644
index 0000000000..76d89fa78c
--- /dev/null
+++ b/src/ext/keccak-tiny/keccak-tiny.c
@@ -0,0 +1,163 @@
+/** libkeccak-tiny
+ *
+ * A single-file implementation of SHA-3 and SHAKE.
+ *
+ * Implementor: David Leon Gil
+ * License: CC0, attribution kindly requested. Blame taken too,
+ * but not liability.
+ */
+#include "keccak-tiny.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/******** The Keccak-f[1600] permutation ********/
+
+/*** Constants. ***/
+static const uint8_t rho[24] = \
+ { 1, 3, 6, 10, 15, 21,
+ 28, 36, 45, 55, 2, 14,
+ 27, 41, 56, 8, 25, 43,
+ 62, 18, 39, 61, 20, 44};
+static const uint8_t pi[24] = \
+ {10, 7, 11, 17, 18, 3,
+ 5, 16, 8, 21, 24, 4,
+ 15, 23, 19, 13, 12, 2,
+ 20, 14, 22, 9, 6, 1};
+static const uint64_t RC[24] = \
+ {1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL,
+ 0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL,
+ 0x8aULL, 0x88ULL, 0x80008009ULL, 0x8000000aULL,
+ 0x8000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL,
+ 0x8000000000008002ULL, 0x8000000000000080ULL, 0x800aULL, 0x800000008000000aULL,
+ 0x8000000080008081ULL, 0x8000000000008080ULL, 0x80000001ULL, 0x8000000080008008ULL};
+
+/*** Helper macros to unroll the permutation. ***/
+#define rol(x, s) (((x) << s) | ((x) >> (64 - s)))
+#define REPEAT6(e) e e e e e e
+#define REPEAT24(e) REPEAT6(e e e e)
+#define REPEAT5(e) e e e e e
+#define FOR5(v, s, e) \
+ v = 0; \
+ REPEAT5(e; v += s;)
+
+/*** Keccak-f[1600] ***/
+static inline void keccakf(void* state) {
+ uint64_t* a = (uint64_t*)state;
+ uint64_t b[5] = {0};
+ uint64_t t = 0;
+ uint8_t x, y;
+
+ for (int i = 0; i < 24; i++) {
+ // Theta
+ FOR5(x, 1,
+ b[x] = 0;
+ FOR5(y, 5,
+ b[x] ^= a[x + y]; ))
+ FOR5(x, 1,
+ FOR5(y, 5,
+ a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); ))
+ // Rho and pi
+ t = a[1];
+ x = 0;
+ REPEAT24(b[0] = a[pi[x]];
+ a[pi[x]] = rol(t, rho[x]);
+ t = b[0];
+ x++; )
+ // Chi
+ FOR5(y,
+ 5,
+ FOR5(x, 1,
+ b[x] = a[y + x];)
+ FOR5(x, 1,
+ a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]); ))
+ // Iota
+ a[0] ^= RC[i];
+ }
+}
+
+/******** The FIPS202-defined functions. ********/
+
+/*** Some helper macros. ***/
+
+#define _(S) do { S } while (0)
+#define FOR(i, ST, L, S) \
+ _(for (size_t i = 0; i < L; i += ST) { S; })
+#define mkapply_ds(NAME, S) \
+ static inline void NAME(uint8_t* dst, \
+ const uint8_t* src, \
+ size_t len) { \
+ FOR(i, 1, len, S); \
+ }
+#define mkapply_sd(NAME, S) \
+ static inline void NAME(const uint8_t* src, \
+ uint8_t* dst, \
+ size_t len) { \
+ FOR(i, 1, len, S); \
+ }
+
+mkapply_ds(xorin, dst[i] ^= src[i]) // xorin
+mkapply_sd(setout, dst[i] = src[i]) // setout
+
+#define P keccakf
+#define Plen 200
+
+// Fold P*F over the full blocks of an input.
+#define foldP(I, L, F) \
+ while (L >= rate) { \
+ F(a, I, rate); \
+ P(a); \
+ I += rate; \
+ L -= rate; \
+ }
+
+/** The sponge-based hash construction. **/
+static inline int hash(uint8_t* out, size_t outlen,
+ const uint8_t* in, size_t inlen,
+ size_t rate, uint8_t delim) {
+ if ((out == NULL) || ((in == NULL) && inlen != 0) || (rate >= Plen)) {
+ return -1;
+ }
+ uint8_t a[Plen] = {0};
+ // Absorb input.
+ foldP(in, inlen, xorin);
+ // Xor in the DS and pad frame.
+ a[inlen] ^= delim;
+ a[rate - 1] ^= 0x80;
+ // Xor in the last block.
+ xorin(a, in, inlen);
+ // Apply P
+ P(a);
+ // Squeeze output.
+ foldP(out, outlen, setout);
+ setout(a, out, outlen);
+ memset_s(a, 200, 0, 200);
+ return 0;
+}
+
+/*** Helper macros to define SHA3 and SHAKE instances. ***/
+#define defshake(bits) \
+ int shake##bits(uint8_t* out, size_t outlen, \
+ const uint8_t* in, size_t inlen) { \
+ return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x1f); \
+ }
+#define defsha3(bits) \
+ int sha3_##bits(uint8_t* out, size_t outlen, \
+ const uint8_t* in, size_t inlen) { \
+ if (outlen > (bits/8)) { \
+ return -1; \
+ } \
+ return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x06); \
+ }
+
+/*** FIPS202 SHAKE VOFs ***/
+defshake(128)
+defshake(256)
+
+/*** FIPS202 SHA3 FOFs ***/
+defsha3(224)
+defsha3(256)
+defsha3(384)
+defsha3(512)
diff --git a/src/ext/keccak-tiny/keccak-tiny.h b/src/ext/keccak-tiny/keccak-tiny.h
new file mode 100644
index 0000000000..7efea2319e
--- /dev/null
+++ b/src/ext/keccak-tiny/keccak-tiny.h
@@ -0,0 +1,66 @@
+#ifndef KECCAK_FIPS202_H
+#define KECCAK_FIPS202_H
+
+#include <stddef.h>
+#include "torint.h"
+
+#define KECCAK_MAX_RATE 200
+
+/* Calculate the rate (block size) from the security target. */
+#define KECCAK_RATE(bits) (KECCAK_MAX_RATE - (bits / 4))
+
+/* The internal structure of a FIPS202 hash/xof instance. Most callers
+ * should treat this as an opaque structure.
+ */
+typedef struct keccak_state {
+ uint8_t a[KECCAK_MAX_RATE];
+ size_t rate;
+ uint8_t delim;
+
+ uint8_t block[KECCAK_MAX_RATE];
+ size_t offset;
+
+ uint8_t finalized : 1;
+} keccak_state;
+
+/* Initialize a Keccak instance suitable for SHA-3 hash functions. */
+int keccak_digest_init(keccak_state *s, size_t bits);
+
+/* Feed more data into the SHA-3 hash instance. */
+int keccak_digest_update(keccak_state *s, const uint8_t *buf, size_t len);
+
+/* Calculate the SHA-3 hash digest. The state is unmodified to support
+ * calculating multiple/rolling digests.
+ */
+int keccak_digest_sum(const keccak_state *s, uint8_t *out, size_t outlen);
+
+/* Initialize a Keccak instance suitable for XOFs (SHAKE-128/256). */
+int keccak_xof_init(keccak_state *s, size_t bits);
+
+/* Absorb more data into the XOF. Must not be called after a squeeze call. */
+int keccak_xof_absorb(keccak_state *s, const uint8_t *buf, size_t len);
+
+/* Squeeze data out of the XOF. Must not attempt to absorb additional data,
+ * after a squeeze has been called.
+ */
+int keccak_xof_squeeze(keccak_state *s, uint8_t *out, size_t outlen);
+
+/* Clone an existing hash/XOF instance. */
+void keccak_clone(keccak_state *out, const keccak_state *in);
+
+/* Cleanse sensitive data from a given hash instance. */
+void keccak_cleanse(keccak_state *s);
+
+#define decshake(bits) \
+ int shake##bits(uint8_t*, size_t, const uint8_t*, size_t);
+
+#define decsha3(bits) \
+ int sha3_##bits(uint8_t*, size_t, const uint8_t*, size_t);
+
+decshake(128)
+decshake(256)
+decsha3(224)
+decsha3(256)
+decsha3(384)
+decsha3(512)
+#endif
diff --git a/src/ext/trunnel/trunnel-impl.h b/src/ext/trunnel/trunnel-impl.h
index a97caf282f..dfe5f89e1a 100644
--- a/src/ext/trunnel/trunnel-impl.h
+++ b/src/ext/trunnel/trunnel-impl.h
@@ -1,4 +1,4 @@
-/* trunnel-impl.h -- copied from Trunnel v1.4.3
+/* trunnel-impl.h -- copied from Trunnel v1.4.4
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
diff --git a/src/ext/trunnel/trunnel.c b/src/ext/trunnel/trunnel.c
index 25a46d9022..0ed75aa9a4 100644
--- a/src/ext/trunnel/trunnel.c
+++ b/src/ext/trunnel/trunnel.c
@@ -1,4 +1,4 @@
-/* trunnel.c -- copied from Trunnel v1.4.3
+/* trunnel.c -- copied from Trunnel v1.4.4
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
diff --git a/src/ext/trunnel/trunnel.h b/src/ext/trunnel/trunnel.h
index 6140a3953f..62e87ee50c 100644
--- a/src/ext/trunnel/trunnel.h
+++ b/src/ext/trunnel/trunnel.h
@@ -1,4 +1,4 @@
-/* trunnel.h -- copied from Trunnel v1.4.3
+/* trunnel.h -- copied from Trunnel v1.4.4
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
diff --git a/src/or/buffers.c b/src/or/buffers.c
index cc2f6f409b..cdb499b8da 100644
--- a/src/or/buffers.c
+++ b/src/or/buffers.c
@@ -78,7 +78,7 @@ static int parse_socks_client(const uint8_t *data, size_t datalen,
/** Return the next character in <b>chunk</b> onto which data can be appended.
* If the chunk is full, this might be off the end of chunk->mem. */
-static INLINE char *
+static inline char *
CHUNK_WRITE_PTR(chunk_t *chunk)
{
return chunk->data + chunk->datalen;
@@ -86,7 +86,7 @@ CHUNK_WRITE_PTR(chunk_t *chunk)
/** Return the number of bytes that can be written onto <b>chunk</b> without
* running out of space. */
-static INLINE size_t
+static inline size_t
CHUNK_REMAINING_CAPACITY(const chunk_t *chunk)
{
return (chunk->mem + chunk->memlen) - (chunk->data + chunk->datalen);
@@ -94,7 +94,7 @@ CHUNK_REMAINING_CAPACITY(const chunk_t *chunk)
/** Move all bytes stored in <b>chunk</b> to the front of <b>chunk</b>->mem,
* to free up space at the end. */
-static INLINE void
+static inline void
chunk_repack(chunk_t *chunk)
{
if (chunk->datalen && chunk->data != &chunk->mem[0]) {
@@ -118,7 +118,7 @@ chunk_free_unchecked(chunk_t *chunk)
total_bytes_allocated_in_chunks -= CHUNK_ALLOC_SIZE(chunk->memlen);
tor_free(chunk);
}
-static INLINE chunk_t *
+static inline chunk_t *
chunk_new_with_alloc_size(size_t alloc)
{
chunk_t *ch;
@@ -136,7 +136,7 @@ chunk_new_with_alloc_size(size_t alloc)
/** Expand <b>chunk</b> until it can hold <b>sz</b> bytes, and return a
* new pointer to <b>chunk</b>. Old pointers are no longer valid. */
-static INLINE chunk_t *
+static inline chunk_t *
chunk_grow(chunk_t *chunk, size_t sz)
{
off_t offset;
@@ -165,7 +165,7 @@ chunk_grow(chunk_t *chunk, size_t sz)
/** Return the allocation size we'd like to use to hold <b>target</b>
* bytes. */
-static INLINE size_t
+static inline size_t
preferred_chunk_size(size_t target)
{
size_t sz = MIN_CHUNK_ALLOC;
@@ -255,7 +255,7 @@ buf_get_first_chunk_data(const buf_t *buf, const char **cp, size_t *sz)
#endif
/** Remove the first <b>n</b> bytes from buf. */
-static INLINE void
+static inline void
buf_remove_from_front(buf_t *buf, size_t n)
{
tor_assert(buf->datalen >= n);
@@ -452,7 +452,7 @@ buf_get_total_allocation(void)
* <b>chunk</b> (which must be on <b>buf</b>). If we get an EOF, set
* *<b>reached_eof</b> to 1. Return -1 on error, 0 on eof or blocking,
* and the number of bytes read otherwise. */
-static INLINE int
+static inline int
read_to_chunk(buf_t *buf, chunk_t *chunk, tor_socket_t fd, size_t at_most,
int *reached_eof, int *socket_error)
{
@@ -488,7 +488,7 @@ read_to_chunk(buf_t *buf, chunk_t *chunk, tor_socket_t fd, size_t at_most,
/** As read_to_chunk(), but return (negative) error code on error, blocking,
* or TLS, and the number of bytes read otherwise. */
-static INLINE int
+static inline int
read_to_chunk_tls(buf_t *buf, chunk_t *chunk, tor_tls_t *tls,
size_t at_most)
{
@@ -611,7 +611,7 @@ read_to_buf_tls(tor_tls_t *tls, size_t at_most, buf_t *buf)
* the bytes written from *<b>buf_flushlen</b>. Return the number of bytes
* written on success, 0 on blocking, -1 on failure.
*/
-static INLINE int
+static inline int
flush_chunk(tor_socket_t s, buf_t *buf, chunk_t *chunk, size_t sz,
size_t *buf_flushlen)
{
@@ -646,7 +646,7 @@ flush_chunk(tor_socket_t s, buf_t *buf, chunk_t *chunk, size_t sz,
* bytes written from *<b>buf_flushlen</b>. Return the number of bytes
* written on success, and a TOR_TLS error code on failure or blocking.
*/
-static INLINE int
+static inline int
flush_chunk_tls(tor_tls_t *tls, buf_t *buf, chunk_t *chunk,
size_t sz, size_t *buf_flushlen)
{
@@ -797,7 +797,7 @@ write_to_buf(const char *string, size_t string_len, buf_t *buf)
/** Helper: copy the first <b>string_len</b> bytes from <b>buf</b>
* onto <b>string</b>.
*/
-static INLINE void
+static inline void
peek_from_buf(char *string, size_t string_len, const buf_t *buf)
{
chunk_t *chunk;
@@ -842,7 +842,7 @@ fetch_from_buf(char *string, size_t string_len, buf_t *buf)
/** True iff the cell command <b>command</b> is one that implies a
* variable-length cell in Tor link protocol <b>linkproto</b>. */
-static INLINE int
+static inline int
cell_command_is_var_length(uint8_t command, int linkproto)
{
/* If linkproto is v2 (2), CELL_VERSIONS is the only variable-length cells
@@ -1083,7 +1083,7 @@ buf_find_pos_of_char(char ch, buf_pos_t *out)
/** Advance <b>pos</b> by a single character, if there are any more characters
* in the buffer. Returns 0 on success, -1 on failure. */
-static INLINE int
+static inline int
buf_pos_inc(buf_pos_t *pos)
{
++pos->pos;
@@ -1945,7 +1945,7 @@ parse_socks(const char *data, size_t datalen, socks_request_t *req,
log_warn(LD_PROTOCOL,
"Your application (using socks4 to port %d) gave Tor "
"a malformed hostname: %s. Rejecting the connection.",
- req->port, escaped(req->address));
+ req->port, escaped_safe_str_client(req->address));
return -1;
}
if (authend != authstart) {
diff --git a/src/or/channel.c b/src/or/channel.c
index 21522a5303..46e833854b 100644
--- a/src/or/channel.c
+++ b/src/or/channel.c
@@ -127,13 +127,13 @@ typedef struct channel_idmap_entry_s {
TOR_LIST_HEAD(channel_list_s, channel_s) channel_list;
} channel_idmap_entry_t;
-static INLINE unsigned
+static inline unsigned
channel_idmap_hash(const channel_idmap_entry_t *ent)
{
return (unsigned) siphash24g(ent->digest, DIGEST_LEN);
}
-static INLINE int
+static inline int
channel_idmap_eq(const channel_idmap_entry_t *a,
const channel_idmap_entry_t *b)
{
diff --git a/src/or/channel.h b/src/or/channel.h
index 2b38ca7e19..5fa2aa8ab7 100644
--- a/src/or/channel.h
+++ b/src/or/channel.h
@@ -531,7 +531,7 @@ channel_t * channel_next_with_digest(channel_t *chan);
CHANNEL_IS_OPEN(chan) || \
CHANNEL_IS_MAINT(chan))
-static INLINE int
+static inline int
channel_is_in_state(channel_t *chan, channel_state_t state)
{
return chan->state == state;
diff --git a/src/or/channeltls.c b/src/or/channeltls.c
index c90f569233..016ef75456 100644
--- a/src/or/channeltls.c
+++ b/src/or/channeltls.c
@@ -1663,30 +1663,9 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
#define NETINFO_NOTICE_SKEW 3600
if (labs(apparent_skew) > NETINFO_NOTICE_SKEW &&
router_get_by_id_digest(chan->conn->identity_digest)) {
- char dbuf[64];
- int severity;
- /*XXXX be smarter about when everybody says we are skewed. */
- if (router_digest_is_trusted_dir(chan->conn->identity_digest))
- severity = LOG_WARN;
- else
- severity = LOG_INFO;
- format_time_interval(dbuf, sizeof(dbuf), apparent_skew);
- log_fn(severity, LD_GENERAL,
- "Received NETINFO cell with skewed time from "
- "server at %s:%d. It seems that our clock is %s by %s, or "
- "that theirs is %s. Tor requires an accurate clock to work: "
- "please check your time and date settings.",
- chan->conn->base_.address,
- (int)(chan->conn->base_.port),
- apparent_skew > 0 ? "ahead" : "behind",
- dbuf,
- apparent_skew > 0 ? "behind" : "ahead");
- if (severity == LOG_WARN) /* only tell the controller if an authority */
- control_event_general_status(LOG_WARN,
- "CLOCK_SKEW SKEW=%ld SOURCE=OR:%s:%d",
- apparent_skew,
- chan->conn->base_.address,
- chan->conn->base_.port);
+ int trusted = router_digest_is_trusted_dir(chan->conn->identity_digest);
+ clock_skew_warning(TO_CONN(chan->conn), apparent_skew, trusted, LD_GENERAL,
+ "NETINFO cell", "OR");
}
/* XXX maybe act on my_apparent_addr, if the source is sufficiently
@@ -1840,7 +1819,8 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
chan->conn->handshake_state->authenticated = 1;
{
- const digests_t *id_digests = tor_x509_cert_get_id_digests(id_cert);
+ const common_digests_t *id_digests =
+ tor_x509_cert_get_id_digests(id_cert);
crypto_pk_t *identity_rcvd;
if (!id_digests)
ERR("Couldn't compute digests for key in ID cert");
@@ -2130,7 +2110,7 @@ channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan)
{
crypto_pk_t *identity_rcvd =
tor_tls_cert_get_key(chan->conn->handshake_state->id_cert);
- const digests_t *id_digests =
+ const common_digests_t *id_digests =
tor_x509_cert_get_id_digests(chan->conn->handshake_state->id_cert);
/* This must exist; we checked key type when reading the cert. */
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 0688398f6d..daf0b2af0b 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -498,6 +498,14 @@ circuit_handle_first_hop(origin_circuit_t *circ)
tor_assert(firsthop);
tor_assert(firsthop->extend_info);
+ /* XX/teor - does tor ever need build a circuit directly to itself? */
+ if (tor_addr_is_internal(&firsthop->extend_info->addr, 0) &&
+ !get_options()->ExtendAllowPrivateAddresses) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Client asked me to connect directly to a private address");
+ return -END_CIRC_REASON_TORPROTOCOL;
+ }
+
/* now see if we're already connected to the first OR in 'route' */
log_debug(LD_CIRC,"Looking for firsthop '%s'",
fmt_addrport(&firsthop->extend_info->addr,
@@ -737,7 +745,7 @@ inform_testing_reachability(void)
/** Return true iff we should send a create_fast cell to start building a given
* circuit */
-static INLINE int
+static inline int
should_use_create_fast_for_circuit(origin_circuit_t *circ)
{
const or_options_t *options = get_options();
@@ -1770,7 +1778,7 @@ pick_tor2web_rendezvous_node(router_crn_flags_t flags,
router_add_running_nodes_to_smartlist(all_live_nodes,
allow_invalid,
0, 0, 0,
- need_desc);
+ need_desc, 0);
/* Filter all_live_nodes to only add live *and* whitelisted RPs to
* the list whitelisted_live_rps. */
@@ -2136,7 +2144,9 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state)
const node_t *choice;
smartlist_t *excluded;
const or_options_t *options = get_options();
- router_crn_flags_t flags = CRN_NEED_GUARD|CRN_NEED_DESC;
+ /* If possible, choose an entry server with a preferred address,
+ * otherwise, choose one with an allowed address */
+ router_crn_flags_t flags = CRN_NEED_GUARD|CRN_NEED_DESC|CRN_PREF_ADDR;
const node_t *node;
if (state && options->UseEntryGuards &&
@@ -2153,14 +2163,6 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state)
* family. */
nodelist_add_node_and_family(excluded, node);
}
- if (firewall_is_fascist_or()) {
- /* Exclude all ORs that we can't reach through our firewall */
- smartlist_t *nodes = nodelist_get_list();
- SMARTLIST_FOREACH(nodes, const node_t *, node, {
- if (!fascist_firewall_allows_node(node))
- smartlist_add(excluded, (void*)node);
- });
- }
/* and exclude current entry guards and their families,
* unless we're in a test network, and excluding guards
* would exclude all nodes (i.e. we're in an incredibly small tor network,
@@ -2239,9 +2241,11 @@ onion_extend_cpath(origin_circuit_t *circ)
if (r) {
/* If we're a client, use the preferred address rather than the
primary address, for potentially connecting to an IPv6 OR
- port. */
- info = extend_info_from_node(r, server_mode(get_options()) == 0);
- tor_assert(info);
+ port. Servers always want the primary (IPv4) address. */
+ int client = (server_mode(get_options()) == 0);
+ info = extend_info_from_node(r, client);
+ /* Clients can fail to find an allowed address */
+ tor_assert(info || client);
}
} else {
const node_t *r =
@@ -2316,33 +2320,43 @@ extend_info_new(const char *nickname, const char *digest,
* <b>for_direct_connect</b> is true, in which case the preferred
* address is used instead. May return NULL if there is not enough
* info about <b>node</b> to extend to it--for example, if there is no
- * routerinfo_t or microdesc_t.
+ * routerinfo_t or microdesc_t, or if for_direct_connect is true and none of
+ * the node's addresses are allowed by tor's firewall and IP version config.
**/
extend_info_t *
extend_info_from_node(const node_t *node, int for_direct_connect)
{
tor_addr_port_t ap;
+ int valid_addr = 0;
if (node->ri == NULL && (node->rs == NULL || node->md == NULL))
return NULL;
+ /* Choose a preferred address first, but fall back to an allowed address.
+ * choose_address returns 1 on success, but get_prim_orport returns 0. */
if (for_direct_connect)
- node_get_pref_orport(node, &ap);
+ valid_addr = fascist_firewall_choose_address_node(node,
+ FIREWALL_OR_CONNECTION,
+ 0, &ap);
else
- node_get_prim_orport(node, &ap);
+ valid_addr = !node_get_prim_orport(node, &ap);
- log_debug(LD_CIRC, "using %s for %s",
- fmt_addrport(&ap.addr, ap.port),
- node->ri ? node->ri->nickname : node->rs->nickname);
+ if (valid_addr)
+ log_debug(LD_CIRC, "using %s for %s",
+ fmt_addrport(&ap.addr, ap.port),
+ node->ri ? node->ri->nickname : node->rs->nickname);
+ else
+ log_warn(LD_CIRC, "Could not choose valid address for %s",
+ node->ri ? node->ri->nickname : node->rs->nickname);
- if (node->ri)
+ if (valid_addr && node->ri)
return extend_info_new(node->ri->nickname,
node->identity,
node->ri->onion_pkey,
node->ri->onion_curve25519_pkey,
&ap.addr,
ap.port);
- else if (node->rs && node->md)
+ else if (valid_addr && node->rs && node->md)
return extend_info_new(node->rs->nickname,
node->identity,
node->md->onion_pkey,
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index 716024df6a..ade371d39f 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -44,11 +44,17 @@ static smartlist_t *global_circuitlist = NULL;
/** A list of all the circuits in CIRCUIT_STATE_CHAN_WAIT. */
static smartlist_t *circuits_pending_chans = NULL;
+/** A list of all the circuits that have been marked with
+ * circuit_mark_for_close and which are waiting for circuit_about_to_free. */
+static smartlist_t *circuits_pending_close = NULL;
+
static void circuit_free_cpath_node(crypt_path_t *victim);
static void cpath_ref_decref(crypt_path_reference_t *cpath_ref);
//static void circuit_set_rend_token(or_circuit_t *circ, int is_rend_circ,
// const uint8_t *token);
static void circuit_clear_rend_token(or_circuit_t *circ);
+static void circuit_about_to_free_atexit(circuit_t *circ);
+static void circuit_about_to_free(circuit_t *circ);
/********* END VARIABLES ************/
@@ -66,7 +72,7 @@ typedef struct chan_circid_circuit_map_t {
/** Helper for hash tables: compare the channel and circuit ID for a and
* b, and return less than, equal to, or greater than zero appropriately.
*/
-static INLINE int
+static inline int
chan_circid_entries_eq_(chan_circid_circuit_map_t *a,
chan_circid_circuit_map_t *b)
{
@@ -75,7 +81,7 @@ chan_circid_entries_eq_(chan_circid_circuit_map_t *a,
/** Helper: return a hash based on circuit ID and the pointer value of
* chan in <b>a</b>. */
-static INLINE unsigned int
+static inline unsigned int
chan_circid_entry_hash_(chan_circid_circuit_map_t *a)
{
/* Try to squeze the siphash input into 8 bytes to save any extra siphash
@@ -451,16 +457,27 @@ circuit_count_pending_on_channel(channel_t *chan)
void
circuit_close_all_marked(void)
{
+ if (circuits_pending_close == NULL)
+ return;
+
smartlist_t *lst = circuit_get_global_list();
- SMARTLIST_FOREACH_BEGIN(lst, circuit_t *, circ) {
- /* Fix up index if SMARTLIST_DEL_CURRENT just moved this one. */
- circ->global_circuitlist_idx = circ_sl_idx;
- if (circ->marked_for_close) {
- circ->global_circuitlist_idx = -1;
- circuit_free(circ);
- SMARTLIST_DEL_CURRENT(lst, circ);
+ SMARTLIST_FOREACH_BEGIN(circuits_pending_close, circuit_t *, circ) {
+ tor_assert(circ->marked_for_close);
+
+ /* Remove it from the circuit list. */
+ int idx = circ->global_circuitlist_idx;
+ smartlist_del(lst, idx);
+ if (idx < smartlist_len(lst)) {
+ circuit_t *replacement = smartlist_get(lst, idx);
+ replacement->global_circuitlist_idx = idx;
}
+ circ->global_circuitlist_idx = -1;
+
+ circuit_about_to_free(circ);
+ circuit_free(circ);
} SMARTLIST_FOREACH_END(circ);
+
+ smartlist_clear(circuits_pending_close);
}
/** Return the head of the global linked list of circuits. */
@@ -885,6 +902,7 @@ circuit_free_all(void)
}
}
tmp->global_circuitlist_idx = -1;
+ circuit_about_to_free_atexit(tmp);
circuit_free(tmp);
SMARTLIST_DEL_CURRENT(lst, tmp);
} SMARTLIST_FOREACH_END(tmp);
@@ -895,6 +913,9 @@ circuit_free_all(void)
smartlist_free(circuits_pending_chans);
circuits_pending_chans = NULL;
+ smartlist_free(circuits_pending_close);
+ circuits_pending_close = NULL;
+
{
chan_circid_circuit_map_t **elt, **next, *c;
for (elt = HT_START(chan_circid_map, &chan_circid_map);
@@ -1030,7 +1051,7 @@ circuit_get_by_global_id(uint32_t id)
* If <b>found_entry_out</b> is provided, set it to true if we have a
* placeholder entry for circid/chan, and leave it unset otherwise.
*/
-static INLINE circuit_t *
+static inline circuit_t *
circuit_get_by_circid_channel_impl(circid_t circ_id, channel_t *chan,
int *found_entry_out)
{
@@ -1703,6 +1724,65 @@ circuit_mark_for_close_, (circuit_t *circ, int reason, int line,
reason = END_CIRC_REASON_NONE;
}
+ circ->marked_for_close = line;
+ circ->marked_for_close_file = file;
+ circ->marked_for_close_reason = reason;
+ circ->marked_for_close_orig_reason = orig_reason;
+
+ if (!CIRCUIT_IS_ORIGIN(circ)) {
+ or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
+ if (or_circ->rend_splice) {
+ if (!or_circ->rend_splice->base_.marked_for_close) {
+ /* do this after marking this circuit, to avoid infinite recursion. */
+ circuit_mark_for_close(TO_CIRCUIT(or_circ->rend_splice), reason);
+ }
+ or_circ->rend_splice = NULL;
+ }
+ }
+
+ if (circuits_pending_close == NULL)
+ circuits_pending_close = smartlist_new();
+
+ smartlist_add(circuits_pending_close, circ);
+}
+
+/** Called immediately before freeing a marked circuit <b>circ</b> from
+ * circuit_free_all() while shutting down Tor; this is a safe-at-shutdown
+ * version of circuit_about_to_free(). It's important that it at least
+ * do circuitmux_detach_circuit() when appropriate.
+ */
+static void
+circuit_about_to_free_atexit(circuit_t *circ)
+{
+
+ if (circ->n_chan) {
+ circuit_clear_cell_queue(circ, circ->n_chan);
+ circuitmux_detach_circuit(circ->n_chan->cmux, circ);
+ circuit_set_n_circid_chan(circ, 0, NULL);
+ }
+
+ if (! CIRCUIT_IS_ORIGIN(circ)) {
+ or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
+
+ if (or_circ->p_chan) {
+ circuit_clear_cell_queue(circ, or_circ->p_chan);
+ circuitmux_detach_circuit(or_circ->p_chan->cmux, circ);
+ circuit_set_p_circid_chan(or_circ, 0, NULL);
+ }
+ }
+}
+
+/** Called immediately before freeing a marked circuit <b>circ</b>.
+ * Disconnects the circuit from other data structures, launches events
+ * as appropriate, and performs other housekeeping.
+ */
+static void
+circuit_about_to_free(circuit_t *circ)
+{
+
+ int reason = circ->marked_for_close_reason;
+ int orig_reason = circ->marked_for_close_orig_reason;
+
if (circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) {
onion_pending_remove(TO_OR_CIRCUIT(circ));
}
@@ -1726,6 +1806,7 @@ circuit_mark_for_close_, (circuit_t *circ, int reason, int line,
(circ->state == CIRCUIT_STATE_OPEN)?CIRC_EVENT_CLOSED:CIRC_EVENT_FAILED,
orig_reason);
}
+
if (circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
int timed_out = (reason == END_CIRC_REASON_TIMEOUT);
@@ -1810,20 +1891,6 @@ circuit_mark_for_close_, (circuit_t *circ, int reason, int line,
connection_edge_destroy(circ->n_circ_id, conn);
ocirc->p_streams = NULL;
}
-
- circ->marked_for_close = line;
- circ->marked_for_close_file = file;
-
- if (!CIRCUIT_IS_ORIGIN(circ)) {
- or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
- if (or_circ->rend_splice) {
- if (!or_circ->rend_splice->base_.marked_for_close) {
- /* do this after marking this circuit, to avoid infinite recursion. */
- circuit_mark_for_close(TO_CIRCUIT(or_circ->rend_splice), reason);
- }
- or_circ->rend_splice = NULL;
- }
- }
}
/** Given a marked circuit <b>circ</b>, aggressively free its cell queues to
diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c
index a77bffac90..94d1eb66e3 100644
--- a/src/or/circuitmux.c
+++ b/src/or/circuitmux.c
@@ -186,10 +186,10 @@ struct chanid_circid_muxinfo_t {
* Static function declarations
*/
-static INLINE int
+static inline int
chanid_circid_entries_eq(chanid_circid_muxinfo_t *a,
chanid_circid_muxinfo_t *b);
-static INLINE unsigned int
+static inline unsigned int
chanid_circid_entry_hash(chanid_circid_muxinfo_t *a);
static chanid_circid_muxinfo_t *
circuitmux_find_map_entry(circuitmux_t *cmux, circuit_t *circ);
@@ -199,12 +199,12 @@ circuitmux_make_circuit_active(circuitmux_t *cmux, circuit_t *circ,
static void
circuitmux_make_circuit_inactive(circuitmux_t *cmux, circuit_t *circ,
cell_direction_t direction);
-static INLINE void
+static inline void
circuitmux_move_active_circ_to_tail(circuitmux_t *cmux, circuit_t *circ,
cell_direction_t direction);
-static INLINE circuit_t **
+static inline circuit_t **
circuitmux_next_active_circ_p(circuitmux_t *cmux, circuit_t *circ);
-static INLINE circuit_t **
+static inline circuit_t **
circuitmux_prev_active_circ_p(circuitmux_t *cmux, circuit_t *circ);
static void circuitmux_assert_okay_pass_one(circuitmux_t *cmux);
static void circuitmux_assert_okay_pass_two(circuitmux_t *cmux);
@@ -226,7 +226,7 @@ static int64_t global_destroy_ctr = 0;
* used by circuitmux_notify_xmit_cells().
*/
-static INLINE void
+static inline void
circuitmux_move_active_circ_to_tail(circuitmux_t *cmux, circuit_t *circ,
cell_direction_t direction)
{
@@ -306,7 +306,7 @@ circuitmux_move_active_circ_to_tail(circuitmux_t *cmux, circuit_t *circ,
circuitmux_assert_okay_paranoid(cmux);
}
-static INLINE circuit_t **
+static inline circuit_t **
circuitmux_next_active_circ_p(circuitmux_t *cmux, circuit_t *circ)
{
tor_assert(cmux);
@@ -319,7 +319,7 @@ circuitmux_next_active_circ_p(circuitmux_t *cmux, circuit_t *circ)
}
}
-static INLINE circuit_t **
+static inline circuit_t **
circuitmux_prev_active_circ_p(circuitmux_t *cmux, circuit_t *circ)
{
tor_assert(cmux);
@@ -338,7 +338,7 @@ circuitmux_prev_active_circ_p(circuitmux_t *cmux, circuit_t *circ)
* than zero appropriately.
*/
-static INLINE int
+static inline int
chanid_circid_entries_eq(chanid_circid_muxinfo_t *a,
chanid_circid_muxinfo_t *b)
{
@@ -349,7 +349,7 @@ chanid_circid_entries_eq(chanid_circid_muxinfo_t *a,
* Helper: return a hash based on circuit ID and channel ID in a.
*/
-static INLINE unsigned int
+static inline unsigned int
chanid_circid_entry_hash(chanid_circid_muxinfo_t *a)
{
return (((unsigned int)(a->circ_id) << 8) ^
diff --git a/src/or/circuitmux_ewma.c b/src/or/circuitmux_ewma.c
index 1c0318de06..0c61fb2ec4 100644
--- a/src/or/circuitmux_ewma.c
+++ b/src/or/circuitmux_ewma.c
@@ -115,7 +115,7 @@ TO_EWMA_POL_CIRC_DATA(circuitmux_policy_circ_data_t *);
* if the cast is impossible.
*/
-static INLINE ewma_policy_data_t *
+static inline ewma_policy_data_t *
TO_EWMA_POL_DATA(circuitmux_policy_data_t *pol)
{
if (!pol) return NULL;
@@ -130,7 +130,7 @@ TO_EWMA_POL_DATA(circuitmux_policy_data_t *pol)
* and assert if the cast is impossible.
*/
-static INLINE ewma_policy_circ_data_t *
+static inline ewma_policy_circ_data_t *
TO_EWMA_POL_CIRC_DATA(circuitmux_policy_circ_data_t *pol)
{
if (!pol) return NULL;
@@ -147,7 +147,7 @@ static int compare_cell_ewma_counts(const void *p1, const void *p2);
static unsigned cell_ewma_tick_from_timeval(const struct timeval *now,
double *remainder_out);
static circuit_t * cell_ewma_to_circuit(cell_ewma_t *ewma);
-static INLINE double get_scale_factor(unsigned from_tick, unsigned to_tick);
+static inline double get_scale_factor(unsigned from_tick, unsigned to_tick);
static cell_ewma_t * pop_first_cell_ewma(ewma_policy_data_t *pol);
static void remove_cell_ewma(ewma_policy_data_t *pol, cell_ewma_t *ewma);
static void scale_single_cell_ewma(cell_ewma_t *ewma, unsigned cur_tick);
@@ -644,7 +644,7 @@ cell_ewma_set_scale_factor(const or_options_t *options,
/** Return the multiplier necessary to convert the value of a cell sent in
* 'from_tick' to one sent in 'to_tick'. */
-static INLINE double
+static inline double
get_scale_factor(unsigned from_tick, unsigned to_tick)
{
/* This math can wrap around, but that's okay: unsigned overflow is
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index 00340fd689..5ffd1f68c7 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -92,7 +92,7 @@ circuit_is_acceptable(const origin_circuit_t *origin_circ,
/* decide if this circ is suitable for this conn */
/* for rend circs, circ->cpath->prev is not the last router in the
- * circuit, it's the magical extra bob hop. so just check the nickname
+ * circuit, it's the magical extra service hop. so just check the nickname
* of the one we meant to finish at.
*/
build_state = origin_circ->build_state;
@@ -1123,7 +1123,7 @@ circuit_build_needed_circs(time_t now)
* don't require an exit circuit, review in #13814.
* This allows HSs to function in a consensus without exits. */
if (router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN)
- connection_ap_attach_pending();
+ connection_ap_rescan_and_attach_pending();
/* make sure any hidden services have enough intro points
* HS intro point streams only require an internal circuit */
@@ -1475,7 +1475,7 @@ circuit_has_opened(origin_circuit_t *circ)
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
rend_client_rendcirc_has_opened(circ);
/* Start building an intro circ if we don't have one yet. */
- connection_ap_attach_pending();
+ connection_ap_attach_pending(1);
/* This isn't a call to circuit_try_attaching_streams because a
* circuit in _C_ESTABLISH_REND state isn't connected to its
* hidden service yet, thus we can't attach streams to it yet,
@@ -1493,11 +1493,11 @@ circuit_has_opened(origin_circuit_t *circ)
circuit_try_attaching_streams(circ);
break;
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
- /* at Bob, waiting for introductions */
+ /* at the service, waiting for introductions */
rend_service_intro_has_opened(circ);
break;
case CIRCUIT_PURPOSE_S_CONNECT_REND:
- /* at Bob, connecting to rend point */
+ /* at the service, connecting to rend point */
rend_service_rendezvous_has_opened(circ);
break;
case CIRCUIT_PURPOSE_TESTING:
@@ -1537,14 +1537,14 @@ void
circuit_try_attaching_streams(origin_circuit_t *circ)
{
/* Attach streams to this circuit if we can. */
- connection_ap_attach_pending();
+ connection_ap_attach_pending(1);
/* The call to circuit_try_clearing_isolation_state here will do
* nothing and return 0 if we didn't attach any streams to circ
* above. */
if (circuit_try_clearing_isolation_state(circ)) {
/* Maybe *now* we can attach some streams to this circuit. */
- connection_ap_attach_pending();
+ connection_ap_attach_pending(1);
}
}
@@ -1617,32 +1617,32 @@ circuit_build_failed(origin_circuit_t *circ)
circuit_testing_failed(circ, failed_at_last_hop);
break;
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
- /* at Bob, waiting for introductions */
+ /* at the service, waiting for introductions */
if (circ->base_.state != CIRCUIT_STATE_OPEN) {
circuit_increment_failure_count();
}
- /* no need to care here, because bob will rebuild intro
+ /* no need to care here, because the service will rebuild intro
* points periodically. */
break;
case CIRCUIT_PURPOSE_C_INTRODUCING:
- /* at Alice, connecting to intro point */
- /* Don't increment failure count, since Bob may have picked
+ /* at the client, connecting to intro point */
+ /* Don't increment failure count, since the service may have picked
* the introduction point maliciously */
- /* Alice will pick a new intro point when this one dies, if
+ /* The client will pick a new intro point when this one dies, if
* the stream in question still cares. No need to act here. */
break;
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
- /* at Alice, waiting for Bob */
+ /* at the client, waiting for the service */
circuit_increment_failure_count();
- /* Alice will pick a new rend point when this one dies, if
+ /* the client will pick a new rend point when this one dies, if
* the stream in question still cares. No need to act here. */
break;
case CIRCUIT_PURPOSE_S_CONNECT_REND:
- /* at Bob, connecting to rend point */
- /* Don't increment failure count, since Alice may have picked
+ /* at the service, connecting to rend point */
+ /* Don't increment failure count, since the client may have picked
* the rendezvous point maliciously */
log_info(LD_REND,
- "Couldn't connect to Alice's chosen rend point %s "
+ "Couldn't connect to the client's chosen rend point %s "
"(%s hop failed).",
escaped(build_state_get_exit_nickname(circ->build_state)),
failed_at_last_hop?"last":"non-last");
@@ -1986,6 +1986,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
"No intro points for '%s': re-fetching service descriptor.",
safe_str_client(rend_data->onion_address));
rend_client_refetch_v2_renddesc(rend_data);
+ connection_ap_mark_as_non_pending_circuit(conn);
ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_RENDDESC_WAIT;
return 0;
}
@@ -2005,8 +2006,13 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
if (r && node_has_descriptor(r)) {
/* We might want to connect to an IPv6 bridge for loading
descriptors so we use the preferred address rather than
- the primary. */
+ the primary. */
extend_info = extend_info_from_node(r, conn->want_onehop ? 1 : 0);
+ if (!extend_info) {
+ log_warn(LD_CIRC,"Could not make a one-hop connection to %s. "
+ "Discarding this circuit.", conn->chosen_exit_name);
+ return -1;
+ }
} else {
log_debug(LD_DIR, "considering %d, %s",
want_onehop, conn->chosen_exit_name);
@@ -2240,7 +2246,7 @@ consider_recording_trackhost(const entry_connection_t *conn,
char fp[HEX_DIGEST_LEN+1];
/* Search the addressmap for this conn's destination. */
- /* If he's not in the address map.. */
+ /* If they're not in the address map.. */
if (!options->TrackHostExits ||
addressmap_have_mapping(conn->socks_request->address,
options->TrackHostExitsExpire))
diff --git a/src/or/config.c b/src/or/config.c
index 3b35b5a8d7..ba02e9f180 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -190,10 +190,12 @@ static config_var_t option_vars_[] = {
V(CircuitPriorityHalflife, DOUBLE, "-100.0"), /*negative:'Use default'*/
V(ClientDNSRejectInternalAddresses, BOOL,"1"),
V(ClientOnly, BOOL, "0"),
- V(ClientPreferIPv6ORPort, BOOL, "0"),
+ V(ClientPreferIPv6ORPort, AUTOBOOL, "auto"),
+ V(ClientPreferIPv6DirPort, AUTOBOOL, "auto"),
V(ClientRejectInternalAddresses, BOOL, "1"),
V(ClientTransportPlugin, LINELIST, NULL),
V(ClientUseIPv6, BOOL, "0"),
+ V(ClientUseIPv4, BOOL, "1"),
V(ConsensusParams, STRING, NULL),
V(ConnLimit, UINT, "1000"),
V(ConnDirectionStatistics, BOOL, "0"),
@@ -212,6 +214,7 @@ static config_var_t option_vars_[] = {
V(CookieAuthFile, STRING, NULL),
V(CountPrivateBandwidth, BOOL, "0"),
V(DataDirectory, FILENAME, NULL),
+ V(DataDirectoryGroupReadable, BOOL, "0"),
V(DisableNetwork, BOOL, "0"),
V(DirAllowPrivateAddresses, BOOL, "0"),
V(TestingAuthDirTimeToLearnReachability, INTERVAL, "30 minutes"),
@@ -221,6 +224,7 @@ static config_var_t option_vars_[] = {
V(DirPortFrontPage, FILENAME, NULL),
VAR("DirReqStatistics", BOOL, DirReqStatistics_option, "1"),
VAR("DirAuthority", LINELIST, DirAuthorities, NULL),
+ V(DirCache, BOOL, "1"),
V(DirAuthorityFallbackRate, DOUBLE, "1.0"),
V(DisableAllSwap, BOOL, "0"),
V(DisableDebuggerAttachment, BOOL, "1"),
@@ -251,6 +255,7 @@ static config_var_t option_vars_[] = {
V(ExtORPortCookieAuthFileGroupReadable, BOOL, "0"),
V(ExtraInfoStatistics, BOOL, "1"),
V(FallbackDir, LINELIST, NULL),
+ V(UseDefaultFallbackDirs, BOOL, "1"),
OBSOLETE("FallbackNetworkstatusFile"),
V(FascistFirewall, BOOL, "0"),
@@ -308,10 +313,12 @@ static config_var_t option_vars_[] = {
V(Socks5ProxyUsername, STRING, NULL),
V(Socks5ProxyPassword, STRING, NULL),
V(KeepalivePeriod, INTERVAL, "5 minutes"),
+ V(KeepBindCapabilities, AUTOBOOL, "auto"),
VAR("Log", LINELIST, Logs, NULL),
V(LogMessageDomains, BOOL, "0"),
V(LogTimeGranularity, MSEC_INTERVAL, "1 second"),
V(TruncateLogFile, BOOL, "0"),
+ V(SyslogIdentityTag, STRING, NULL),
V(LongLivedPorts, CSV,
"21,22,706,1863,5050,5190,5222,5223,6523,6667,6697,8300"),
VAR("MapAddress", LINELIST, AddressMap, NULL),
@@ -473,10 +480,40 @@ static config_var_t option_vars_[] = {
V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 60, "
"300, 600, 1800, 3600, 3600, 3600, "
"10800, 21600, 43200"),
+ /* With the TestingClientBootstrapConsensus*Download* below:
+ * Clients with only authorities will try:
+ * - 3 authorities over 10 seconds, then wait 60 minutes.
+ * Clients with authorities and fallbacks will try:
+ * - 2 authorities and 4 fallbacks over 21 seconds, then wait 60 minutes.
+ * Clients will also retry when an application request arrives.
+ * After a number of failed reqests, clients retry every 3 days + 1 hour.
+ *
+ * Clients used to try 2 authorities over 10 seconds, then wait for
+ * 60 minutes or an application request.
+ *
+ * When clients have authorities and fallbacks available, they use these
+ * schedules: (we stagger the times to avoid thundering herds) */
+ V(TestingClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL,
+ "10, 11, 3600, 10800, 25200, 54000, 111600, 262800" /* 3 days + 1 hour */),
+ V(TestingClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL,
+ "0, 1, 4, 11, 3600, 10800, 25200, 54000, 111600, 262800"),
+ /* When clients only have authorities available, they use this schedule: */
+ V(TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule, CSV_INTERVAL,
+ "0, 3, 7, 3600, 10800, 25200, 54000, 111600, 262800"),
+ /* We don't want to overwhelm slow networks (or mirrors whose replies are
+ * blocked), but we also don't want to fail if only some mirrors are
+ * blackholed. Clients will try 3 directories simultaneously.
+ * (Relays never use simultaneous connections.) */
+ V(TestingClientBootstrapConsensusMaxInProgressTries, UINT, "3"),
V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "3600, 900, 900, 3600"),
V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "10 minutes"),
V(TestingDirConnectionMaxStall, INTERVAL, "5 minutes"),
V(TestingConsensusMaxDownloadTries, UINT, "8"),
+ /* Since we try connections rapidly and simultaneously, we can afford
+ * to give up earlier. (This protects against overloading directories.) */
+ V(TestingClientBootstrapConsensusMaxDownloadTries, UINT, "7"),
+ /* We want to give up much earlier if we're only using authorities. */
+ V(TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries, UINT, "4"),
V(TestingDescriptorMaxDownloadTries, UINT, "8"),
V(TestingMicrodescMaxDownloadTries, UINT, "8"),
V(TestingCertMaxDownloadTries, UINT, "8"),
@@ -523,10 +560,18 @@ static const config_var_t testing_tor_network_defaults[] = {
"15, 20, 30, 60"),
V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, "
"15, 20, 30, 60"),
+ V(TestingClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL,
+ "0, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"),
+ V(TestingClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL,
+ "0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"),
+ V(TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule, CSV_INTERVAL,
+ "0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"),
V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "60, 30, 30, 60"),
V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "5 seconds"),
V(TestingDirConnectionMaxStall, INTERVAL, "30 seconds"),
V(TestingConsensusMaxDownloadTries, UINT, "80"),
+ V(TestingClientBootstrapConsensusMaxDownloadTries, UINT, "80"),
+ V(TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries, UINT, "80"),
V(TestingDescriptorMaxDownloadTries, UINT, "80"),
V(TestingMicrodescMaxDownloadTries, UINT, "80"),
V(TestingCertMaxDownloadTries, UINT, "80"),
@@ -547,7 +592,6 @@ static const config_var_t testing_tor_network_defaults[] = {
static char *get_windows_conf_root(void);
#endif
static int options_act_reversible(const or_options_t *old_options, char **msg);
-static int options_act(const or_options_t *old_options);
static int options_transition_allowed(const or_options_t *old,
const or_options_t *new,
char **msg);
@@ -558,15 +602,12 @@ static int options_transition_affects_descriptor(
static int check_nickname_list(char **lst, const char *name, char **msg);
static char *get_bindaddr_from_transport_listen_line(const char *line,
const char *transport);
-static int parse_dir_authority_line(const char *line,
- dirinfo_type_t required_type,
- int validate_only);
-static void port_cfg_free(port_cfg_t *port);
static int parse_ports(or_options_t *options, int validate_only,
char **msg_out, int *n_ports_out,
int *world_writable_control_socket);
static int check_server_ports(const smartlist_t *ports,
- const or_options_t *options);
+ const or_options_t *options,
+ int *num_low_ports_out);
static int validate_data_directory(or_options_t *options);
static int write_configuration_file(const char *fname,
@@ -625,15 +666,15 @@ static char *global_dirfrontpagecontents = NULL;
static smartlist_t *configured_ports = NULL;
/** Return the contents of our frontpage string, or NULL if not configured. */
-const char *
-get_dirportfrontpage(void)
+MOCK_IMPL(const char*,
+get_dirportfrontpage, (void))
{
return global_dirfrontpagecontents;
}
-/** Return the currently configured options. */
-or_options_t *
-get_options_mutable(void)
+/** Returns the currently configured options. */
+MOCK_IMPL(or_options_t *,
+get_options_mutable, (void))
{
tor_assert(global_options);
return global_options;
@@ -793,7 +834,6 @@ config_free_all(void)
tor_free(torrc_fname);
tor_free(torrc_defaults_fname);
- tor_free(the_tor_version);
tor_free(global_dirfrontpagecontents);
tor_free(the_short_tor_version);
@@ -865,6 +905,7 @@ static const char *default_authorities[] = {
"128.31.0.39:9131 9695 DFC3 5FFE B861 329B 9F1A B04C 4639 7020 CE31",
"tor26 orport=443 "
"v3ident=14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 "
+ "ipv6=[2001:858:2:2:aabb:0:563b:1526]:443 "
"86.59.21.38:80 847B 1F85 0344 D787 6491 A548 92F9 0493 4E4E B85D",
"dizum orport=443 "
"v3ident=E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 "
@@ -873,30 +914,43 @@ static const char *default_authorities[] = {
"82.94.251.203:80 4A0C CD2D DC79 9508 3D73 F5D6 6710 0C8A 5831 F16D",
"gabelmoo orport=443 "
"v3ident=ED03BB616EB2F60BEC80151114BB25CEF515B226 "
+ "ipv6=[2001:638:a000:4140::ffff:189]:443 "
"131.188.40.189:80 F204 4413 DAC2 E02E 3D6B CF47 35A1 9BCA 1DE9 7281",
"dannenberg orport=443 "
"v3ident=0232AF901C31A04EE9848595AF9BB7620D4C5B2E "
"193.23.244.244:80 7BE6 83E6 5D48 1413 21C5 ED92 F075 C553 64AC 7123",
"urras orport=80 "
"v3ident=80550987E1D626E3EBA5E5E75A458DE0626D088C "
- "208.83.223.34:443 0AD3 FA88 4D18 F89E EA2D 89C0 1937 9E0E 7FD9 4417",
+ "208.83.223.34:443 0AD3 FA88 4D18 F89E EA2D 89C0 1937 9E0E 7FD9 4417"
+ /* XX/teor - urras may have an IPv6 address, but it's not in urras'
+ * descriptor as of 11 Dec 2015. See #17813. */,
"maatuska orport=80 "
"v3ident=49015F787433103580E3B66A1707A00E60F2D15B "
+ "ipv6=[2001:67c:289c::9]:80 "
"171.25.193.9:443 BD6A 8292 55CB 08E6 6FBE 7D37 4836 3586 E46B 3810",
"Faravahar orport=443 "
"v3ident=EFCBE720AB3A82B99F9E953CD5BF50F7EEFC7B97 "
"154.35.175.225:80 CF6D 0AAF B385 BE71 B8E1 11FC 5CFF 4B47 9237 33BC",
"longclaw orport=443 "
"v3ident=23D15D965BC35114467363C165C4F724B64B4F66 "
+ "ipv6=[2620:13:4000:8000:60:f3ff:fea1:7cff]:443 "
"199.254.238.52:80 74A9 1064 6BCE EFBC D2E8 74FC 1DC9 9743 0F96 8145",
NULL
};
+/** List of fallback directory authorities. The list is generated by opt-in of
+ * relays that meet certain stability criteria.
+ */
+static const char *default_fallbacks[] = {
+#include "fallback_dirs.inc"
+ NULL
+};
+
/** Add the default directory authorities directly into the trusted dir list,
* but only add them insofar as they share bits with <b>type</b>.
* Each authority's bits are restricted to the bits shared with <b>type</b>.
* If <b>type</b> is ALL_DIRINFO or NO_DIRINFO (zero), add all authorities. */
-static void
+STATIC void
add_default_trusted_dir_authorities(dirinfo_type_t type)
{
int i;
@@ -914,13 +968,10 @@ MOCK_IMPL(void,
add_default_fallback_dir_servers,(void))
{
int i;
- const char *fallback[] = {
- NULL
- };
- for (i=0; fallback[i]; i++) {
- if (parse_dir_fallback_line(fallback[i], 0)<0) {
+ for (i=0; default_fallbacks[i]; i++) {
+ if (parse_dir_fallback_line(default_fallbacks[i], 0)<0) {
log_err(LD_BUG, "Couldn't parse internal FallbackDir line %s",
- fallback[i]);
+ default_fallbacks[i]);
}
}
}
@@ -990,6 +1041,7 @@ consider_adding_dir_servers(const or_options_t *options,
!smartlist_len(router_get_fallback_dir_servers()) || !old_options ||
!config_lines_eq(options->DirAuthorities, old_options->DirAuthorities) ||
!config_lines_eq(options->FallbackDir, old_options->FallbackDir) ||
+ (options->UseDefaultFallbackDirs != old_options->UseDefaultFallbackDirs) ||
!config_lines_eq(options->AlternateBridgeAuthority,
old_options->AlternateBridgeAuthority) ||
!config_lines_eq(options->AlternateDirAuthority,
@@ -1018,8 +1070,8 @@ consider_adding_dir_servers(const or_options_t *options,
type |= V3_DIRINFO | EXTRAINFO_DIRINFO | MICRODESC_DIRINFO;
/* Only add the default fallback directories when the DirAuthorities,
* AlternateDirAuthority, and FallbackDir directory config options
- * are set to their defaults. */
- if (!options->FallbackDir) {
+ * are set to their defaults, and when UseDefaultFallbackDirs is 1. */
+ if (!options->FallbackDir && options->UseDefaultFallbackDirs) {
add_default_fallback_dir_servers();
}
}
@@ -1044,6 +1096,9 @@ consider_adding_dir_servers(const or_options_t *options,
return 0;
}
+/* Helps determine flags to pass to switch_id. */
+static int have_low_ports = -1;
+
/** Fetch the active option list, and take actions based on it. All of the
* things we do should survive being done repeatedly. If present,
* <b>old_options</b> contains the previous value of the options.
@@ -1178,7 +1233,16 @@ options_act_reversible(const or_options_t *old_options, char **msg)
/* Setuid/setgid as appropriate */
if (options->User) {
- if (switch_id(options->User) != 0) {
+ tor_assert(have_low_ports != -1);
+ unsigned switch_id_flags = 0;
+ if (options->KeepBindCapabilities == 1) {
+ switch_id_flags |= SWITCH_ID_KEEP_BINDLOW;
+ switch_id_flags |= SWITCH_ID_WARN_IF_NO_CAPS;
+ }
+ if (options->KeepBindCapabilities == -1 && have_low_ports) {
+ switch_id_flags |= SWITCH_ID_KEEP_BINDLOW;
+ }
+ if (switch_id(options->User, switch_id_flags) != 0) {
/* No need to roll back, since you can't change the value. */
*msg = tor_strdup("Problem with User value. See logs for details.");
goto done;
@@ -1186,16 +1250,30 @@ options_act_reversible(const or_options_t *old_options, char **msg)
}
/* Ensure data directory is private; create if possible. */
+ cpd_check_t cpd_opts = running_tor ? CPD_CREATE : CPD_CHECK;
+ if (options->DataDirectoryGroupReadable)
+ cpd_opts |= CPD_GROUP_READ;
if (check_private_dir(options->DataDirectory,
- running_tor ? CPD_CREATE : CPD_CHECK,
+ cpd_opts,
options->User)<0) {
tor_asprintf(msg,
"Couldn't access/create private data directory \"%s\"",
options->DataDirectory);
+
goto done;
/* No need to roll back, since you can't change the value. */
}
+#ifndef _WIN32
+ if (options->DataDirectoryGroupReadable) {
+ /* Only new dirs created get new opts, also enforce group read. */
+ if (chmod(options->DataDirectory, 0750)) {
+ log_warn(LD_FS,"Unable to make %s group-readable: %s",
+ options->DataDirectory, strerror(errno));
+ }
+ }
+#endif
+
/* Bail out at this point if we're not going to be a client or server:
* we don't run Tor itself. */
if (!running_tor)
@@ -1368,7 +1446,7 @@ options_transition_requires_fresh_tls_context(const or_options_t *old_options,
* Note: We haven't moved all the "act on new configuration" logic
* here yet. Some is still in do_hup() and other places.
*/
-static int
+STATIC int
options_act(const or_options_t *old_options)
{
config_line_t *cl;
@@ -1390,10 +1468,12 @@ options_act(const or_options_t *old_options)
if (options->DisableDebuggerAttachment && !disabled_debugger_attach &&
running_tor) {
int ok = tor_disable_debugger_attach();
+ /* LCOV_EXCL_START the warned_debugger_attach is 0 can't reach inside. */
if (warned_debugger_attach && ok == 1) {
log_notice(LD_CONFIG, "Disabled attaching debuggers for unprivileged "
"users.");
}
+ /* LCOV_EXCL_STOP */
disabled_debugger_attach = (ok == 1);
} else if (!options->DisableDebuggerAttachment &&
!warned_debugger_attach) {
@@ -1420,12 +1500,14 @@ options_act(const or_options_t *old_options)
#endif
#ifdef ENABLE_TOR2WEB_MODE
+/* LCOV_EXCL_START */
if (!options->Tor2webMode) {
log_err(LD_CONFIG, "This copy of Tor was compiled to run in "
"'tor2web mode'. It can only be run with the Tor2webMode torrc "
"option enabled.");
return -1;
}
+/* LCOV_EXCL_STOP */
#else
if (options->Tor2webMode) {
log_err(LD_CONFIG, "This copy of Tor was not compiled to run in "
@@ -1437,7 +1519,7 @@ options_act(const or_options_t *old_options)
#endif
/* If we are a bridge with a pluggable transport proxy but no
- Extended ORPort, inform the user that she is missing out. */
+ Extended ORPort, inform the user that they are missing out. */
if (server_mode(options) && options->ServerTransportPlugin &&
!options->ExtORPort_lines) {
log_notice(LD_CONFIG, "We use pluggable transports but the Extended "
@@ -1689,8 +1771,8 @@ options_act(const or_options_t *old_options)
if (revise_trackexithosts)
addressmap_clear_excluded_trackexithosts(options);
- if (!options->AutomapHostsOnResolve) {
- if (old_options->AutomapHostsOnResolve)
+ if (!options->AutomapHostsOnResolve &&
+ old_options->AutomapHostsOnResolve) {
revise_automap_entries = 1;
} else {
if (!smartlist_strings_eq(old_options->AutomapHostsSuffixes,
@@ -1829,8 +1911,8 @@ options_act(const or_options_t *old_options)
print_notice = 1;
}
if (print_notice)
- log_notice(LD_CONFIG, "Configured to measure statistics. Look for "
- "the *-stats files that will first be written to the "
+ log_notice(LD_CONFIG, "Configured to measure statistics. Look for "
+ "the *-stats files that will first be written to the "
"data directory in 24 hours from now.");
}
@@ -2611,7 +2693,7 @@ options_validate_cb(void *old_options, void *options, void *default_options,
/** Log a warning message iff <b>filepath</b> is not absolute.
* Warning message must contain option name <b>option</b> and
- * an absolute path that <b>filepath<b> will resolve to.
+ * an absolute path that <b>filepath</b> will resolve to.
*
* In case <b>filepath</b> is absolute, do nothing.
*/
@@ -2998,6 +3080,8 @@ options_validate(or_options_t *old_options, or_options_t *options,
}
}
+ /* Terminate Reachable*Addresses with reject *
+ */
for (i=0; i<3; i++) {
config_line_t **linep =
(i==0) ? &options->ReachableAddresses :
@@ -3007,8 +3091,6 @@ options_validate(or_options_t *old_options, or_options_t *options,
continue;
/* We need to end with a reject *:*, not an implicit accept *:* */
for (;;) {
- if (!strcmp((*linep)->value, "reject *:*")) /* already there */
- break;
linep = &((*linep)->next);
if (!*linep) {
*linep = tor_malloc_zero(sizeof(config_line_t));
@@ -3024,11 +3106,29 @@ options_validate(or_options_t *old_options, or_options_t *options,
if ((options->ReachableAddresses ||
options->ReachableORAddresses ||
- options->ReachableDirAddresses) &&
+ options->ReachableDirAddresses ||
+ options->ClientUseIPv4 == 0) &&
server_mode(options))
REJECT("Servers must be able to freely connect to the rest "
"of the Internet, so they must not set Reachable*Addresses "
- "or FascistFirewall.");
+ "or FascistFirewall or FirewallPorts or ClientUseIPv4 0.");
+
+ /* We check if Reachable*Addresses blocks all addresses in
+ * parse_reachable_addresses(). */
+
+#define WARN_PLEASE_USE_IPV6_LOG_MSG \
+ "ClientPreferIPv6%sPort 1 is ignored unless tor is using IPv6. " \
+ "Please set ClientUseIPv6 1, ClientUseIPv4 0, or configure bridges."
+
+ if (!fascist_firewall_use_ipv6(options)
+ && options->ClientPreferIPv6ORPort == 1)
+ log_warn(LD_CONFIG, WARN_PLEASE_USE_IPV6_LOG_MSG, "OR");
+
+ if (!fascist_firewall_use_ipv6(options)
+ && options->ClientPreferIPv6DirPort == 1)
+ log_warn(LD_CONFIG, WARN_PLEASE_USE_IPV6_LOG_MSG, "Dir");
+
+#undef WARN_PLEASE_USE_IPV6_LOG_MSG
if (options->UseBridges &&
server_mode(options))
@@ -3380,8 +3480,30 @@ options_validate(or_options_t *old_options, or_options_t *options,
options->AccountingRule = ACCT_SUM;
else if (!strcmp(options->AccountingRule_option, "max"))
options->AccountingRule = ACCT_MAX;
+ else if (!strcmp(options->AccountingRule_option, "in"))
+ options->AccountingRule = ACCT_IN;
+ else if (!strcmp(options->AccountingRule_option, "out"))
+ options->AccountingRule = ACCT_OUT;
else
- REJECT("AccountingRule must be 'sum' or 'max'");
+ REJECT("AccountingRule must be 'sum', 'max', 'in', or 'out'");
+ }
+
+ if (options->DirPort_set && !options->DirCache) {
+ REJECT("DirPort configured but DirCache disabled. DirPort requires "
+ "DirCache.");
+ }
+
+ if (options->BridgeRelay && !options->DirCache) {
+ REJECT("We're a bridge but DirCache is disabled. BridgeRelay requires "
+ "DirCache.");
+ }
+
+ if (server_mode(options)) {
+ char *msg = NULL;
+ if (have_enough_mem_for_dircache(options, 0, &msg)) {
+ log_warn(LD_CONFIG, "%s", msg);
+ tor_free(msg);
+ }
}
if (options->HTTPProxy) { /* parse it now */
@@ -3532,6 +3654,13 @@ options_validate(or_options_t *old_options, or_options_t *options,
if (validate_addr_policies(options, msg) < 0)
return -1;
+ /* If FallbackDir is set, we don't UseDefaultFallbackDirs */
+ if (options->UseDefaultFallbackDirs && options->FallbackDir) {
+ log_info(LD_CONFIG, "You have set UseDefaultFallbackDirs 1 and "
+ "FallbackDir(s). Ignoring UseDefaultFallbackDirs, and "
+ "using the FallbackDir(s) you have set.");
+ }
+
if (validate_dir_servers(options, old_options) < 0)
REJECT("Directory authority/fallback line did not parse. See logs "
"for details.");
@@ -3734,10 +3863,16 @@ options_validate(or_options_t *old_options, or_options_t *options,
CHECK_DEFAULT(TestingClientDownloadSchedule);
CHECK_DEFAULT(TestingServerConsensusDownloadSchedule);
CHECK_DEFAULT(TestingClientConsensusDownloadSchedule);
+ CHECK_DEFAULT(TestingClientBootstrapConsensusAuthorityDownloadSchedule);
+ CHECK_DEFAULT(TestingClientBootstrapConsensusFallbackDownloadSchedule);
+ CHECK_DEFAULT(TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule);
CHECK_DEFAULT(TestingBridgeDownloadSchedule);
CHECK_DEFAULT(TestingClientMaxIntervalWithoutRequest);
CHECK_DEFAULT(TestingDirConnectionMaxStall);
CHECK_DEFAULT(TestingConsensusMaxDownloadTries);
+ CHECK_DEFAULT(TestingClientBootstrapConsensusMaxDownloadTries);
+ CHECK_DEFAULT(TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries);
+ CHECK_DEFAULT(TestingClientBootstrapConsensusMaxInProgressTries);
CHECK_DEFAULT(TestingDescriptorMaxDownloadTries);
CHECK_DEFAULT(TestingMicrodescMaxDownloadTries);
CHECK_DEFAULT(TestingCertMaxDownloadTries);
@@ -3812,11 +3947,41 @@ options_validate(or_options_t *old_options, or_options_t *options,
}
if (options->TestingConsensusMaxDownloadTries < 2) {
- REJECT("TestingConsensusMaxDownloadTries must be greater than 1.");
+ REJECT("TestingConsensusMaxDownloadTries must be greater than 2.");
} else if (options->TestingConsensusMaxDownloadTries > 800) {
COMPLAIN("TestingConsensusMaxDownloadTries is insanely high.");
}
+ if (options->TestingClientBootstrapConsensusMaxDownloadTries < 2) {
+ REJECT("TestingClientBootstrapConsensusMaxDownloadTries must be greater "
+ "than 2."
+ );
+ } else if (options->TestingClientBootstrapConsensusMaxDownloadTries > 800) {
+ COMPLAIN("TestingClientBootstrapConsensusMaxDownloadTries is insanely "
+ "high.");
+ }
+
+ if (options->TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries
+ < 2) {
+ REJECT("TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries must "
+ "be greater than 2."
+ );
+ } else if (
+ options->TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries
+ > 800) {
+ COMPLAIN("TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries is "
+ "insanely high.");
+ }
+
+ if (options->TestingClientBootstrapConsensusMaxInProgressTries < 1) {
+ REJECT("TestingClientBootstrapConsensusMaxInProgressTries must be greater "
+ "than 0.");
+ } else if (options->TestingClientBootstrapConsensusMaxInProgressTries
+ > 100) {
+ COMPLAIN("TestingClientBootstrapConsensusMaxInProgressTries is insanely "
+ "high.");
+ }
+
if (options->TestingDescriptorMaxDownloadTries < 2) {
REJECT("TestingDescriptorMaxDownloadTries must be greater than 1.");
} else if (options->TestingDescriptorMaxDownloadTries > 800) {
@@ -3949,6 +4114,52 @@ compute_real_max_mem_in_queues(const uint64_t val, int log_guess)
}
}
+/* If we have less than 300 MB suggest disabling dircache */
+#define DIRCACHE_MIN_MB_BANDWIDTH 300
+#define DIRCACHE_MIN_BANDWIDTH (DIRCACHE_MIN_MB_BANDWIDTH*ONE_MEGABYTE)
+#define STRINGIFY(val) #val
+
+/** Create a warning message for emitting if we are a dircache but may not have
+ * enough system memory, or if we are not a dircache but probably should be.
+ * Return -1 when a message is returned in *msg*, else return 0. */
+STATIC int
+have_enough_mem_for_dircache(const or_options_t *options, size_t total_mem,
+ char **msg)
+{
+ *msg = NULL;
+ /* XXX We should possibly be looking at MaxMemInQueues here
+ * unconditionally. Or we should believe total_mem unconditionally. */
+ if (total_mem == 0) {
+ if (get_total_system_memory(&total_mem) < 0) {
+ total_mem = options->MaxMemInQueues >= SIZE_MAX ?
+ SIZE_MAX : (size_t)options->MaxMemInQueues;
+ }
+ }
+ if (options->DirCache) {
+ if (total_mem < DIRCACHE_MIN_BANDWIDTH) {
+ if (options->BridgeRelay) {
+ *msg = strdup("Running a Bridge with less than "
+ STRINGIFY(DIRCACHE_MIN_MB_BANDWIDTH) " MB of memory is "
+ "not recommended.");
+ } else {
+ *msg = strdup("Being a directory cache (default) with less than "
+ STRINGIFY(DIRCACHE_MIN_MB_BANDWIDTH) " MB of memory is "
+ "not recommended and may consume most of the available "
+ "resources, consider disabling this functionality by "
+ "setting the DirCache option to 0.");
+ }
+ }
+ } else {
+ if (total_mem >= DIRCACHE_MIN_BANDWIDTH) {
+ *msg = strdup("DirCache is disabled and we are configured as a "
+ "relay. This may disqualify us from becoming a guard in the "
+ "future.");
+ }
+ }
+ return *msg == NULL ? 0 : -1;
+}
+#undef STRINGIFY
+
/** Helper: return true iff s1 and s2 are both NULL, or both non-NULL
* equal strings. */
static int
@@ -3996,6 +4207,18 @@ options_transition_allowed(const or_options_t *old,
return -1;
}
+ if (old->KeepBindCapabilities != new_val->KeepBindCapabilities) {
+ *msg = tor_strdup("While Tor is running, changing KeepBindCapabilities is "
+ "not allowed.");
+ return -1;
+ }
+
+ if (!opt_streq(old->SyslogIdentityTag, new_val->SyslogIdentityTag)) {
+ *msg = tor_strdup("While Tor is running, changing "
+ "SyslogIdentityTag is not allowed.");
+ return -1;
+ }
+
if ((old->HardwareAccel != new_val->HardwareAccel)
|| !opt_streq(old->AccelName, new_val->AccelName)
|| !opt_streq(old->AccelDir, new_val->AccelDir)) {
@@ -4125,7 +4348,8 @@ options_transition_affects_descriptor(const or_options_t *old_options,
!opt_streq(old_options->MyFamily, new_options->MyFamily) ||
!opt_streq(old_options->AccountingStart, new_options->AccountingStart) ||
old_options->AccountingMax != new_options->AccountingMax ||
- public_server_mode(old_options) != public_server_mode(new_options))
+ public_server_mode(old_options) != public_server_mode(new_options) ||
+ old_options->DirCache != new_options->DirCache)
return 1;
return 0;
@@ -4937,7 +5161,7 @@ options_init_logs(const or_options_t *old_options, or_options_t *options,
!strcasecmp(smartlist_get(elts,0), "syslog")) {
#ifdef HAVE_SYSLOG_H
if (!validate_only) {
- add_syslog_log(severity);
+ add_syslog_log(severity, options->SyslogIdentityTag);
}
#else
log_warn(LD_CONFIG, "Syslog is not supported on this system. Sorry.");
@@ -5510,13 +5734,14 @@ get_options_for_server_transport(const char *transport)
* (minus whatever bits it's missing) as a valid authority.
* Return 0 on success or filtering out by type,
* or -1 if the line isn't well-formed or if we can't add it. */
-static int
+STATIC int
parse_dir_authority_line(const char *line, dirinfo_type_t required_type,
int validate_only)
{
smartlist_t *items = NULL;
int r;
char *addrport=NULL, *address=NULL, *nickname=NULL, *fingerprint=NULL;
+ tor_addr_port_t ipv6_addrport, *ipv6_addrport_ptr = NULL;
uint16_t dir_port = 0, or_port = 0;
char digest[DIGEST_LEN];
char v3_digest[DIGEST_LEN];
@@ -5573,6 +5798,20 @@ parse_dir_authority_line(const char *line, dirinfo_type_t required_type,
} else {
type |= V3_DIRINFO|EXTRAINFO_DIRINFO|MICRODESC_DIRINFO;
}
+ } else if (!strcasecmpstart(flag, "ipv6=")) {
+ if (ipv6_addrport_ptr) {
+ log_warn(LD_CONFIG, "Redundant ipv6 addr/port on DirAuthority line");
+ } else {
+ if (tor_addr_port_parse(LOG_WARN, flag+strlen("ipv6="),
+ &ipv6_addrport.addr, &ipv6_addrport.port,
+ -1) < 0
+ || tor_addr_family(&ipv6_addrport.addr) != AF_INET6) {
+ log_warn(LD_CONFIG, "Bad ipv6 addr/port %s on DirAuthority line",
+ escaped(flag));
+ goto err;
+ }
+ ipv6_addrport_ptr = &ipv6_addrport;
+ }
} else {
log_warn(LD_CONFIG, "Unrecognized flag '%s' on DirAuthority line",
flag);
@@ -5615,6 +5854,7 @@ parse_dir_authority_line(const char *line, dirinfo_type_t required_type,
log_debug(LD_DIR, "Trusted %d dirserver at %s:%d (%s)", (int)type,
address, (int)dir_port, (char*)smartlist_get(items,0));
if (!(ds = trusted_dir_server_new(nickname, address, dir_port, or_port,
+ ipv6_addrport_ptr,
digest, v3_digest, type, weight)))
goto err;
dir_server_add(ds);
@@ -5652,6 +5892,7 @@ parse_dir_fallback_line(const char *line,
int ok;
char id[DIGEST_LEN];
char *address=NULL;
+ tor_addr_port_t ipv6_addrport, *ipv6_addrport_ptr = NULL;
double weight=1.0;
memset(id, 0, sizeof(id));
@@ -5670,6 +5911,20 @@ parse_dir_fallback_line(const char *line,
} else if (!strcmpstart(cp, "id=")) {
ok = !base16_decode(id, DIGEST_LEN,
cp+strlen("id="), strlen(cp)-strlen("id="));
+ } else if (!strcasecmpstart(cp, "ipv6=")) {
+ if (ipv6_addrport_ptr) {
+ log_warn(LD_CONFIG, "Redundant ipv6 addr/port on FallbackDir line");
+ } else {
+ if (tor_addr_port_parse(LOG_WARN, cp+strlen("ipv6="),
+ &ipv6_addrport.addr, &ipv6_addrport.port,
+ -1) < 0
+ || tor_addr_family(&ipv6_addrport.addr) != AF_INET6) {
+ log_warn(LD_CONFIG, "Bad ipv6 addr/port %s on FallbackDir line",
+ escaped(cp));
+ goto end;
+ }
+ ipv6_addrport_ptr = &ipv6_addrport;
+ }
} else if (!strcmpstart(cp, "weight=")) {
int ok;
const char *wstring = cp + strlen("weight=");
@@ -5711,7 +5966,8 @@ parse_dir_fallback_line(const char *line,
if (!validate_only) {
dir_server_t *ds;
- ds = fallback_dir_server_new(&addr, dirport, orport, id, weight);
+ ds = fallback_dir_server_new(&addr, dirport, orport, ipv6_addrport_ptr,
+ id, weight);
if (!ds) {
log_warn(LD_CONFIG, "Couldn't create FallbackDir %s", escaped(line));
goto end;
@@ -5730,7 +5986,7 @@ parse_dir_fallback_line(const char *line,
}
/** Allocate and return a new port_cfg_t with reasonable defaults. */
-static port_cfg_t *
+STATIC port_cfg_t *
port_cfg_new(size_t namelen)
{
tor_assert(namelen <= SIZE_T_CEILING - sizeof(port_cfg_t) - 1);
@@ -5742,7 +5998,7 @@ port_cfg_new(size_t namelen)
}
/** Free all storage held in <b>port</b> */
-static void
+STATIC void
port_cfg_free(port_cfg_t *port)
{
tor_free(port);
@@ -5796,9 +6052,9 @@ warn_nonlocal_ext_orports(const smartlist_t *ports, const char *portname)
} SMARTLIST_FOREACH_END(port);
}
-/** Given a list of port_cfg_t in <b>ports</b>, warn any controller port there
- * is listening on any non-loopback address. If <b>forbid_nonlocal</b> is
- * true, then emit a stronger warning and remove the port from the list.
+/** Given a list of port_cfg_t in <b>ports</b>, warn if any controller port
+ * there is listening on any non-loopback address. If <b>forbid_nonlocal</b>
+ * is true, then emit a stronger warning and remove the port from the list.
*/
static void
warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid_nonlocal)
@@ -5838,15 +6094,6 @@ warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid_nonlocal)
} SMARTLIST_FOREACH_END(port);
}
-#define CL_PORT_NO_STREAM_OPTIONS (1u<<0)
-#define CL_PORT_WARN_NONLOCAL (1u<<1)
-#define CL_PORT_ALLOW_EXTRA_LISTENADDR (1u<<2)
-#define CL_PORT_SERVER_OPTIONS (1u<<3)
-#define CL_PORT_FORBID_NONLOCAL (1u<<4)
-#define CL_PORT_TAKES_HOSTNAMES (1u<<5)
-#define CL_PORT_IS_UNIXSOCKET (1u<<6)
-#define CL_PORT_DFLT_GROUP_WRITABLE (1u<<7)
-
#ifdef HAVE_SYS_UN_H
/** Parse the given <b>addrport</b> and set <b>path_out</b> if a Unix socket
@@ -5934,7 +6181,7 @@ config_parse_unix_port(const char *addrport, char **path_out)
* <b>out</b> for every port that the client should listen on. Return 0
* on success, -1 on failure.
*/
-static int
+STATIC int
parse_port_config(smartlist_t *out,
const config_line_t *ports,
const config_line_t *listenaddrs,
@@ -6126,6 +6373,7 @@ parse_port_config(smartlist_t *out,
tor_free(addrtmp);
goto err;
}
+ tor_free(addrtmp);
} else {
/* Try parsing integer port before address, because, who knows?
"9050" might be a valid address. */
@@ -6140,7 +6388,7 @@ parse_port_config(smartlist_t *out,
}
port = ptmp;
} else {
- log_warn(LD_CONFIG, "Couldn't parse address '%s' for %sPort",
+ log_warn(LD_CONFIG, "Couldn't parse address %s for %sPort",
escaped(addrport), portname);
goto err;
}
@@ -6528,10 +6776,13 @@ parse_ports(or_options_t *options, int validate_only,
}
}
- if (check_server_ports(ports, options) < 0) {
+ int n_low_ports = 0;
+ if (check_server_ports(ports, options, &n_low_ports) < 0) {
*msg = tor_strdup("Misconfigured server ports");
goto err;
}
+ if (have_low_ports < 0)
+ have_low_ports = (n_low_ports > 0);
*n_ports_out = smartlist_len(ports);
@@ -6585,10 +6836,12 @@ parse_ports(or_options_t *options, int validate_only,
}
/** Given a list of <b>port_cfg_t</b> in <b>ports</b>, check them for internal
- * consistency and warn as appropriate. */
+ * consistency and warn as appropriate. Set *<b>n_low_ports_out</b> to the
+ * number of sub-1024 ports we will be binding. */
static int
check_server_ports(const smartlist_t *ports,
- const or_options_t *options)
+ const or_options_t *options,
+ int *n_low_ports_out)
{
int n_orport_advertised = 0;
int n_orport_advertised_ipv4 = 0;
@@ -6651,23 +6904,31 @@ check_server_ports(const smartlist_t *ports,
r = -1;
}
- if (n_low_port && options->AccountingMax) {
+ if (n_low_port && options->AccountingMax &&
+ (!have_capability_support() || options->KeepBindCapabilities == 0)) {
+ const char *extra = "";
+ if (options->KeepBindCapabilities == 0 && have_capability_support())
+ extra = ", and you have disabled KeepBindCapabilities.";
log_warn(LD_CONFIG,
"You have set AccountingMax to use hibernation. You have also "
- "chosen a low DirPort or OrPort. This combination can make Tor stop "
+ "chosen a low DirPort or OrPort%s."
+ "This combination can make Tor stop "
"working when it tries to re-attach the port after a period of "
"hibernation. Please choose a different port or turn off "
"hibernation unless you know this combination will work on your "
- "platform.");
+ "platform.", extra);
}
+ if (n_low_ports_out)
+ *n_low_ports_out = n_low_port;
+
return r;
}
/** Return a list of port_cfg_t for client ports parsed from the
* options. */
-const smartlist_t *
-get_configured_ports(void)
+MOCK_IMPL(const smartlist_t *,
+get_configured_ports,(void))
{
if (!configured_ports)
configured_ports = smartlist_new();
@@ -7151,7 +7412,7 @@ getinfo_helper_config(control_connection_t *conn,
smartlist_free(sl);
} else if (!strcmp(question, "config/defaults")) {
smartlist_t *sl = smartlist_new();
- int i, dirauth_lines_seen = 0;
+ int i, dirauth_lines_seen = 0, fallback_lines_seen = 0;
for (i = 0; option_vars_[i].name; ++i) {
const config_var_t *var = &option_vars_[i];
if (var->initvalue != NULL) {
@@ -7162,6 +7423,13 @@ getinfo_helper_config(control_connection_t *conn,
*/
++dirauth_lines_seen;
}
+ if (strcmp(option_vars_[i].name, "FallbackDir") == 0) {
+ /*
+ * Similarly count fallback lines, so that we can decided later
+ * to add the defaults manually.
+ */
+ ++fallback_lines_seen;
+ }
char *val = esc_for_log(var->initvalue);
smartlist_add_asprintf(sl, "%s %s\n",var->name,val);
tor_free(val);
@@ -7187,6 +7455,24 @@ getinfo_helper_config(control_connection_t *conn,
}
}
+ if (fallback_lines_seen == 0 &&
+ get_options()->UseDefaultFallbackDirs == 1) {
+ /*
+ * We didn't see any explicitly configured fallback mirrors,
+ * so add the defaults to the list manually.
+ *
+ * default_fallbacks is included earlier in this file and
+ * is a const char ** NULL-terminated array of fallback config lines.
+ */
+ const char **i;
+
+ for (i = default_fallbacks; *i != NULL; ++i) {
+ char *val = esc_for_log(*i);
+ smartlist_add_asprintf(sl, "FallbackDir %s\n", val);
+ tor_free(val);
+ }
+ }
+
*answer = smartlist_join_strings(sl, "", 0, NULL);
SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
smartlist_free(sl);
@@ -7329,8 +7615,7 @@ init_cookie_authentication(const char *fname, const char *header,
/* Generate the cookie */
*cookie_out = tor_malloc(cookie_len);
- if (crypto_rand((char *)*cookie_out, cookie_len) < 0)
- goto done;
+ crypto_rand((char *)*cookie_out, cookie_len);
/* Create the string that should be written on the file. */
memcpy(cookie_file_str, header, strlen(header));
diff --git a/src/or/config.h b/src/or/config.h
index 0ee1e1a3c4..c7ce57d2aa 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -14,9 +14,9 @@
#include "testsupport.h"
-const char *get_dirportfrontpage(void);
-MOCK_DECL(const or_options_t *,get_options,(void));
-or_options_t *get_options_mutable(void);
+MOCK_DECL(const char*, get_dirportfrontpage, (void));
+MOCK_DECL(const or_options_t *, get_options, (void));
+MOCK_DECL(or_options_t *, get_options_mutable, (void));
int set_options(or_options_t *new_val, char **msg);
void config_free_all(void);
const char *safe_str_client(const char *address);
@@ -76,7 +76,7 @@ int write_to_data_subdir(const char* subdir, const char* fname,
int get_num_cpus(const or_options_t *options);
-const smartlist_t *get_configured_ports(void);
+MOCK_DECL(const smartlist_t *,get_configured_ports,(void));
int get_first_advertised_port_by_type_af(int listener_type,
int address_family);
#define get_primary_or_port() \
@@ -136,10 +136,23 @@ smartlist_t *get_options_from_transport_options_line(const char *line,
smartlist_t *get_options_for_server_transport(const char *transport);
#ifdef CONFIG_PRIVATE
+
+#define CL_PORT_NO_STREAM_OPTIONS (1u<<0)
+#define CL_PORT_WARN_NONLOCAL (1u<<1)
+#define CL_PORT_ALLOW_EXTRA_LISTENADDR (1u<<2)
+#define CL_PORT_SERVER_OPTIONS (1u<<3)
+#define CL_PORT_FORBID_NONLOCAL (1u<<4)
+#define CL_PORT_TAKES_HOSTNAMES (1u<<5)
+#define CL_PORT_IS_UNIXSOCKET (1u<<6)
+#define CL_PORT_DFLT_GROUP_WRITABLE (1u<<7)
+
+STATIC int options_act(const or_options_t *old_options);
#ifdef TOR_UNIT_TESTS
extern struct config_format_t options_format;
#endif
+STATIC port_cfg_t *port_cfg_new(size_t namelen);
+STATIC void port_cfg_free(port_cfg_t *port);
STATIC void or_options_free(or_options_t *options);
STATIC int options_validate(or_options_t *old_options,
or_options_t *options,
@@ -150,10 +163,22 @@ STATIC int parse_transport_line(const or_options_t *options,
int server);
STATIC int consider_adding_dir_servers(const or_options_t *options,
const or_options_t *old_options);
+STATIC void add_default_trusted_dir_authorities(dirinfo_type_t type);
MOCK_DECL(STATIC void, add_default_fallback_dir_servers, (void));
-STATIC int
-parse_dir_fallback_line(const char *line,
- int validate_only);
+STATIC int parse_dir_authority_line(const char *line,
+ dirinfo_type_t required_type,
+ int validate_only);
+STATIC int parse_dir_fallback_line(const char *line, int validate_only);
+STATIC int have_enough_mem_for_dircache(const or_options_t *options,
+ size_t total_mem, char **msg);
+STATIC int parse_port_config(smartlist_t *out,
+ const config_line_t *ports,
+ const config_line_t *listenaddrs,
+ const char *portname,
+ int listener_type,
+ const char *defaultaddr,
+ int defaultport,
+ const unsigned flags);
#endif
#endif
diff --git a/src/or/connection.c b/src/or/connection.c
index 78176d3768..0f2b3e356e 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -19,6 +19,7 @@
*/
#define TOR_CHANNEL_INTERNAL_
#define CONNECTION_PRIVATE
+#include "backtrace.h"
#include "channel.h"
#include "channeltls.h"
#include "circuitbuild.h"
@@ -37,6 +38,7 @@
#include "ext_orport.h"
#include "geoip.h"
#include "main.h"
+#include "nodelist.h"
#include "policies.h"
#include "reasons.h"
#include "relay.h"
@@ -44,6 +46,7 @@
#include "rendcommon.h"
#include "rephist.h"
#include "router.h"
+#include "routerlist.h"
#include "transports.h"
#include "routerparse.h"
#include "transports.h"
@@ -678,6 +681,13 @@ connection_free,(connection_t *conn))
if (conn->type == CONN_TYPE_CONTROL) {
connection_control_closed(TO_CONTROL_CONN(conn));
}
+#if 1
+ /* DEBUGGING */
+ if (conn->type == CONN_TYPE_AP) {
+ connection_ap_warn_and_unmark_if_pending_circ(TO_ENTRY_CONN(conn),
+ "connection_free");
+ }
+#endif
connection_unregister_events(conn);
connection_free_(conn);
}
@@ -1104,7 +1114,6 @@ connection_listener_new(const struct sockaddr *listensockaddr,
start_reading = 1;
tor_addr_from_sockaddr(&addr, listensockaddr, &usePort);
-
log_notice(LD_NET, "Opening %s on %s",
conn_type_to_string(type), fmt_addrport(&addr, usePort));
@@ -1128,11 +1137,12 @@ connection_listener_new(const struct sockaddr *listensockaddr,
tor_socket_strerror(errno));
}
-#if defined USE_TRANSPARENT && defined(IP_TRANSPARENT)
+#if defined(USE_TRANSPARENT) && defined(IP_TRANSPARENT)
if (options->TransProxyType_parsed == TPT_TPROXY &&
type == CONN_TYPE_AP_TRANS_LISTENER) {
int one = 1;
- if (setsockopt(s, SOL_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0) {
+ if (setsockopt(s, SOL_IP, IP_TRANSPARENT, (void*)&one,
+ (socklen_t)sizeof(one)) < 0) {
const char *extra = "";
int e = tor_socket_errno(s);
if (e == EPERM)
@@ -1146,16 +1156,11 @@ connection_listener_new(const struct sockaddr *listensockaddr,
#ifdef IPV6_V6ONLY
if (listensockaddr->sa_family == AF_INET6) {
-#ifdef _WIN32
- /* In Redmond, this kind of thing passes for standards-conformance. */
- DWORD one = 1;
-#else
int one = 1;
-#endif
/* We need to set IPV6_V6ONLY so that this socket can't get used for
* IPv4 connections. */
if (setsockopt(s,IPPROTO_IPV6, IPV6_V6ONLY,
- (void*)&one, sizeof(one)) < 0) {
+ (void*)&one, (socklen_t)sizeof(one)) < 0) {
int e = tor_socket_errno(s);
log_warn(LD_NET, "Error setting IPV6_V6ONLY flag: %s",
tor_socket_strerror(e));
@@ -1437,7 +1442,7 @@ connection_handle_listener_read(connection_t *conn, int new_type)
if (!SOCKET_OK(news)) { /* accept() error */
int e = tor_socket_errno(conn->s);
if (ERRNO_IS_ACCEPT_EAGAIN(e)) {
- return 0; /* he hung up before we could accept(). that's fine. */
+ return 0; /* they hung up before we could accept(). that's fine. */
} else if (ERRNO_IS_RESOURCE_LIMIT(e)) {
warn_too_many_conns();
return 0;
@@ -1597,6 +1602,8 @@ connection_init_accepted_conn(connection_t *conn,
break;
case CONN_TYPE_AP_TRANS_LISTENER:
TO_ENTRY_CONN(conn)->is_transparent_ap = 1;
+ /* XXXX028 -- is this correct still, with the addition of
+ * pending_entry_connections ? */
conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
return connection_ap_process_transparent(TO_ENTRY_CONN(conn));
case CONN_TYPE_AP_NATD_LISTENER:
@@ -1616,13 +1623,18 @@ connection_init_accepted_conn(connection_t *conn,
return 0;
}
-static int
-connection_connect_sockaddr(connection_t *conn,
+/** Take conn, make a nonblocking socket; try to connect to
+ * sa, binding to bindaddr if sa is not localhost. If fail, return -1 and if
+ * applicable put your best guess about errno into *<b>socket_error</b>.
+ * If connected return 1, if EAGAIN return 0.
+ */
+MOCK_IMPL(STATIC int,
+connection_connect_sockaddr,(connection_t *conn,
const struct sockaddr *sa,
socklen_t sa_len,
const struct sockaddr *bindaddr,
socklen_t bindaddr_len,
- int *socket_error)
+ int *socket_error))
{
tor_socket_t s;
int inprogress = 0;
@@ -1705,11 +1717,82 @@ connection_connect_sockaddr(connection_t *conn,
return inprogress ? 0 : 1;
}
+/* Log a message if connection attempt is made when IPv4 or IPv6 is disabled.
+ * Log a less severe message if we couldn't conform to ClientPreferIPv6ORPort
+ * or ClientPreferIPv6ORPort. */
+static void
+connection_connect_log_client_use_ip_version(const connection_t *conn)
+{
+ const or_options_t *options = get_options();
+
+ /* Only clients care about ClientUseIPv4/6, bail out early on servers, and
+ * on connections we don't care about */
+ if (server_mode(options) || !conn || conn->type == CONN_TYPE_EXIT) {
+ return;
+ }
+
+ /* We're only prepared to log OR and DIR connections here */
+ if (conn->type != CONN_TYPE_OR && conn->type != CONN_TYPE_DIR) {
+ return;
+ }
+
+ const int must_ipv4 = !fascist_firewall_use_ipv6(options);
+ const int must_ipv6 = (options->ClientUseIPv4 == 0);
+ const int pref_ipv6 = (conn->type == CONN_TYPE_OR
+ ? fascist_firewall_prefer_ipv6_orport(options)
+ : fascist_firewall_prefer_ipv6_dirport(options));
+ tor_addr_t real_addr;
+ tor_addr_make_null(&real_addr, AF_UNSPEC);
+
+ /* OR conns keep the original address in real_addr, as addr gets overwritten
+ * with the descriptor address */
+ if (conn->type == CONN_TYPE_OR) {
+ const or_connection_t *or_conn = TO_OR_CONN((connection_t *)conn);
+ tor_addr_copy(&real_addr, &or_conn->real_addr);
+ } else if (conn->type == CONN_TYPE_DIR) {
+ tor_addr_copy(&real_addr, &conn->addr);
+ }
+
+ /* Check if we broke a mandatory address family restriction */
+ if ((must_ipv4 && tor_addr_family(&real_addr) == AF_INET6)
+ || (must_ipv6 && tor_addr_family(&real_addr) == AF_INET)) {
+ log_warn(LD_BUG, "%s connection to %s violated ClientUseIPv%s 0.",
+ conn->type == CONN_TYPE_OR ? "OR" : "Dir",
+ fmt_addr(&real_addr),
+ options->ClientUseIPv4 == 0 ? "4" : "6");
+ log_backtrace(LOG_WARN, LD_BUG, "Address came from");
+ }
+
+ /* Bridges are allowed to break IPv4/IPv6 ORPort preferences to connect to
+ * the node's configured address when ClientPreferIPv6ORPort is auto */
+ if (options->UseBridges && conn->type == CONN_TYPE_OR
+ && options->ClientPreferIPv6ORPort == -1) {
+ return;
+ }
+
+ /* Check if we couldn't satisfy an address family preference */
+ if ((!pref_ipv6 && tor_addr_family(&real_addr) == AF_INET6)
+ || (pref_ipv6 && tor_addr_family(&real_addr) == AF_INET)) {
+ log_info(LD_NET, "Connection to %s doesn't satisfy ClientPreferIPv6%sPort "
+ "%d, with ClientUseIPv4 %d, and fascist_firewall_use_ipv6 %d "
+ "(ClientUseIPv6 %d and UseBridges %d).",
+ fmt_addr(&real_addr),
+ conn->type == CONN_TYPE_OR ? "OR" : "Dir",
+ conn->type == CONN_TYPE_OR ? options->ClientPreferIPv6ORPort
+ : options->ClientPreferIPv6DirPort,
+ options->ClientUseIPv4, fascist_firewall_use_ipv6(options),
+ options->ClientUseIPv6, options->UseBridges);
+ }
+}
+
/** Take conn, make a nonblocking socket; try to connect to
- * addr:port (they arrive in *host order*). If fail, return -1 and if
+ * addr:port (port arrives in *host order*). If fail, return -1 and if
* applicable put your best guess about errno into *<b>socket_error</b>.
* Else assign s to conn-\>s: if connected return 1, if EAGAIN return 0.
*
+ * addr:port can be different to conn->addr:conn->port if connecting through
+ * a proxy.
+ *
* address is used to make the logs useful.
*
* On success, add conn to the list of polled connections.
@@ -1726,6 +1809,10 @@ connection_connect(connection_t *conn, const char *address,
const or_options_t *options = get_options();
int protocol_family;
+ /* Log if we didn't stick to ClientUseIPv4/6 or ClientPreferIPv6OR/DirPort
+ */
+ connection_connect_log_client_use_ip_version(conn);
+
if (tor_addr_family(addr) == AF_INET6)
protocol_family = PF_INET6;
else
@@ -2381,6 +2468,15 @@ retry_listener_ports(smartlist_t *old_conns,
if (port->server_cfg.no_listen)
continue;
+#ifndef _WIN32
+ /* We don't need to be root to create a UNIX socket, so defer until after
+ * setuid. */
+ const or_options_t *options = get_options();
+ if (port->is_unix_addr && !geteuid() && (options->User) &&
+ strcmp(options->User, "root"))
+ continue;
+#endif
+
if (port->is_unix_addr) {
listensockaddr = (struct sockaddr *)
create_unix_sockaddr(port->unix_addr,
@@ -3585,7 +3681,7 @@ connection_read_to_buf(connection_t *conn, ssize_t *max_to_read,
}
/* Call even if result is 0, since the global read bucket may
- * have reached 0 on a different conn, and this guy needs to
+ * have reached 0 on a different conn, and this connection needs to
* know to stop reading. */
connection_consider_empty_read_buckets(conn);
if (n_written > 0 && connection_is_writing(conn))
@@ -4081,7 +4177,7 @@ connection_handle_write_impl(connection_t *conn, int force)
}
/* Call even if result is 0, since the global write bucket may
- * have reached 0 on a different conn, and this guy needs to
+ * have reached 0 on a different conn, and this connection needs to
* know to stop writing. */
connection_consider_empty_write_buckets(conn);
if (n_read > 0 && connection_is_reading(conn))
@@ -4209,24 +4305,32 @@ connection_write_to_buf_impl_,(const char *string, size_t len,
}
}
+/** Return a connection_t * from get_connection_array() that satisfies test on
+ * var, and that is not marked for close. */
+#define CONN_GET_TEMPLATE(var, test) \
+ STMT_BEGIN \
+ smartlist_t *conns = get_connection_array(); \
+ SMARTLIST_FOREACH(conns, connection_t *, var, \
+ { \
+ if (var && (test) && !var->marked_for_close) \
+ return var; \
+ }); \
+ return NULL; \
+ STMT_END
+
/** Return a connection with given type, address, port, and purpose;
- * or NULL if no such connection exists. */
-connection_t *
-connection_get_by_type_addr_port_purpose(int type,
+ * or NULL if no such connection exists (or if all such connections are marked
+ * for close). */
+MOCK_IMPL(connection_t *,
+connection_get_by_type_addr_port_purpose,(int type,
const tor_addr_t *addr, uint16_t port,
- int purpose)
+ int purpose))
{
- smartlist_t *conns = get_connection_array();
- SMARTLIST_FOREACH(conns, connection_t *, conn,
- {
- if (conn->type == type &&
+ CONN_GET_TEMPLATE(conn,
+ (conn->type == type &&
tor_addr_eq(&conn->addr, addr) &&
conn->port == port &&
- conn->purpose == purpose &&
- !conn->marked_for_close)
- return conn;
- });
- return NULL;
+ conn->purpose == purpose));
}
/** Return the stream with id <b>id</b> if it is not already marked for
@@ -4235,13 +4339,7 @@ connection_get_by_type_addr_port_purpose(int type,
connection_t *
connection_get_by_global_id(uint64_t id)
{
- smartlist_t *conns = get_connection_array();
- SMARTLIST_FOREACH(conns, connection_t *, conn,
- {
- if (conn->global_identifier == id)
- return conn;
- });
- return NULL;
+ CONN_GET_TEMPLATE(conn, conn->global_identifier == id);
}
/** Return a connection of type <b>type</b> that is not marked for close.
@@ -4249,13 +4347,7 @@ connection_get_by_global_id(uint64_t id)
connection_t *
connection_get_by_type(int type)
{
- smartlist_t *conns = get_connection_array();
- SMARTLIST_FOREACH(conns, connection_t *, conn,
- {
- if (conn->type == type && !conn->marked_for_close)
- return conn;
- });
- return NULL;
+ CONN_GET_TEMPLATE(conn, conn->type == type);
}
/** Return a connection of type <b>type</b> that is in state <b>state</b>,
@@ -4264,13 +4356,7 @@ connection_get_by_type(int type)
connection_t *
connection_get_by_type_state(int type, int state)
{
- smartlist_t *conns = get_connection_array();
- SMARTLIST_FOREACH(conns, connection_t *, conn,
- {
- if (conn->type == type && conn->state == state && !conn->marked_for_close)
- return conn;
- });
- return NULL;
+ CONN_GET_TEMPLATE(conn, conn->type == type && conn->state == state);
}
/** Return a connection of type <b>type</b> that has rendquery equal
@@ -4281,55 +4367,141 @@ connection_t *
connection_get_by_type_state_rendquery(int type, int state,
const char *rendquery)
{
- smartlist_t *conns = get_connection_array();
-
tor_assert(type == CONN_TYPE_DIR ||
type == CONN_TYPE_AP || type == CONN_TYPE_EXIT);
tor_assert(rendquery);
- SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
- if (conn->type == type &&
- !conn->marked_for_close &&
- (!state || state == conn->state)) {
- if (type == CONN_TYPE_DIR &&
+ CONN_GET_TEMPLATE(conn,
+ (conn->type == type &&
+ (!state || state == conn->state)) &&
+ (
+ (type == CONN_TYPE_DIR &&
TO_DIR_CONN(conn)->rend_data &&
!rend_cmp_service_ids(rendquery,
TO_DIR_CONN(conn)->rend_data->onion_address))
- return conn;
- else if (CONN_IS_EDGE(conn) &&
+ ||
+ (CONN_IS_EDGE(conn) &&
TO_EDGE_CONN(conn)->rend_data &&
!rend_cmp_service_ids(rendquery,
TO_EDGE_CONN(conn)->rend_data->onion_address))
- return conn;
- }
- } SMARTLIST_FOREACH_END(conn);
- return NULL;
+ ));
}
+#define CONN_FIRST_AND_FREE_TEMPLATE(sl) \
+ STMT_BEGIN \
+ if (smartlist_len(sl) > 0) { \
+ void *first_item = smartlist_get(sl, 0); \
+ smartlist_free(sl); \
+ return first_item; \
+ } else { \
+ smartlist_free(sl); \
+ return NULL; \
+ } \
+ STMT_END
+
/** Return a directory connection (if any one exists) that is fetching
- * the item described by <b>state</b>/<b>resource</b> */
+ * the item described by <b>purpose</b>/<b>resource</b>, otherwise return NULL.
+ */
dir_connection_t *
-connection_dir_get_by_purpose_and_resource(int purpose,
+connection_dir_get_by_purpose_and_resource(
+ int purpose,
const char *resource)
{
- smartlist_t *conns = get_connection_array();
+ smartlist_t *conns = connection_dir_list_by_purpose_and_resource(
+ purpose,
+ resource);
+ CONN_FIRST_AND_FREE_TEMPLATE(conns);
+}
- SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
- dir_connection_t *dirconn;
- if (conn->type != CONN_TYPE_DIR || conn->marked_for_close ||
- conn->purpose != purpose)
- continue;
- dirconn = TO_DIR_CONN(conn);
- if (dirconn->requested_resource == NULL) {
- if (resource == NULL)
- return dirconn;
- } else if (resource) {
- if (0 == strcmp(resource, dirconn->requested_resource))
- return dirconn;
- }
- } SMARTLIST_FOREACH_END(conn);
+/** Return a new smartlist of dir_connection_t * from get_connection_array()
+ * that satisfy conn_test on connection_t *conn_var, and dirconn_test on
+ * dir_connection_t *dirconn_var. conn_var must be of CONN_TYPE_DIR and not
+ * marked for close to be included in the list. */
+#define DIR_CONN_LIST_TEMPLATE(conn_var, conn_test, \
+ dirconn_var, dirconn_test) \
+ STMT_BEGIN \
+ smartlist_t *conns = get_connection_array(); \
+ smartlist_t *dir_conns = smartlist_new(); \
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn_var) { \
+ if (conn_var && (conn_test) \
+ && conn_var->type == CONN_TYPE_DIR \
+ && !conn_var->marked_for_close) { \
+ dir_connection_t *dirconn_var = TO_DIR_CONN(conn_var); \
+ if (dirconn_var && (dirconn_test)) { \
+ smartlist_add(dir_conns, dirconn_var); \
+ } \
+ } \
+ } SMARTLIST_FOREACH_END(conn_var); \
+ return dir_conns; \
+ STMT_END
+
+/** Return a list of directory connections that are fetching the item
+ * described by <b>purpose</b>/<b>resource</b>. If there are none,
+ * return an empty list. This list must be freed using smartlist_free,
+ * but the pointers in it must not be freed.
+ * Note that this list should not be cached, as the pointers in it can be
+ * freed if their connections close. */
+smartlist_t *
+connection_dir_list_by_purpose_and_resource(
+ int purpose,
+ const char *resource)
+{
+ DIR_CONN_LIST_TEMPLATE(conn,
+ conn->purpose == purpose,
+ dirconn,
+ 0 == strcmp_opt(resource,
+ dirconn->requested_resource));
+}
- return NULL;
+/** Return a directory connection (if any one exists) that is fetching
+ * the item described by <b>purpose</b>/<b>resource</b>/<b>state</b>,
+ * otherwise return NULL. */
+dir_connection_t *
+connection_dir_get_by_purpose_resource_and_state(
+ int purpose,
+ const char *resource,
+ int state)
+{
+ smartlist_t *conns =
+ connection_dir_list_by_purpose_resource_and_state(
+ purpose,
+ resource,
+ state);
+ CONN_FIRST_AND_FREE_TEMPLATE(conns);
+}
+
+#undef CONN_FIRST_AND_FREE_TEMPLATE
+
+/** Return a list of directory connections that are fetching the item
+ * described by <b>purpose</b>/<b>resource</b>/<b>state</b>. If there are
+ * none, return an empty list. This list must be freed using smartlist_free,
+ * but the pointers in it must not be freed.
+ * Note that this list should not be cached, as the pointers in it can be
+ * freed if their connections close. */
+smartlist_t *
+connection_dir_list_by_purpose_resource_and_state(
+ int purpose,
+ const char *resource,
+ int state)
+{
+ DIR_CONN_LIST_TEMPLATE(conn,
+ conn->purpose == purpose && conn->state == state,
+ dirconn,
+ 0 == strcmp_opt(resource,
+ dirconn->requested_resource));
+}
+
+#undef DIR_CONN_LIST_TEMPLATE
+
+/** Return an arbitrary active OR connection that isn't <b>this_conn</b>.
+ *
+ * We use this to guess if we should tell the controller that we
+ * didn't manage to connect to any of our bridges. */
+static connection_t *
+connection_get_another_active_or_conn(const or_connection_t *this_conn)
+{
+ CONN_GET_TEMPLATE(conn,
+ conn != TO_CONN(this_conn) && conn->type == CONN_TYPE_OR);
}
/** Return 1 if there are any active OR connections apart from
@@ -4340,23 +4512,18 @@ connection_dir_get_by_purpose_and_resource(int purpose,
int
any_other_active_or_conns(const or_connection_t *this_conn)
{
- smartlist_t *conns = get_connection_array();
- SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
- if (conn == TO_CONN(this_conn)) { /* don't consider this conn */
- continue;
- }
-
- if (conn->type == CONN_TYPE_OR &&
- !conn->marked_for_close) {
- log_debug(LD_DIR, "%s: Found an OR connection: %s",
- __func__, conn->address);
- return 1;
- }
- } SMARTLIST_FOREACH_END(conn);
+ connection_t *conn = connection_get_another_active_or_conn(this_conn);
+ if (conn != NULL) {
+ log_debug(LD_DIR, "%s: Found an OR connection: %s",
+ __func__, conn->address);
+ return 1;
+ }
return 0;
}
+#undef CONN_GET_TEMPLATE
+
/** Return 1 if <b>conn</b> is a listener conn, else return 0. */
int
connection_is_listener(connection_t *conn)
@@ -5012,3 +5179,34 @@ connection_free_all(void)
#endif
}
+/** Log a warning, and possibly emit a control event, that <b>received</b> came
+ * at a skewed time. <b>trusted</b> indicates that the <b>source</b> was one
+ * that we had more faith in and therefore the warning level should have higher
+ * severity.
+ */
+void
+clock_skew_warning(const connection_t *conn, long apparent_skew, int trusted,
+ log_domain_mask_t domain, const char *received,
+ const char *source)
+{
+ char dbuf[64];
+ char *ext_source = NULL;
+ format_time_interval(dbuf, sizeof(dbuf), apparent_skew);
+ if (conn)
+ tor_asprintf(&ext_source, "%s:%s:%d", source, conn->address, conn->port);
+ else
+ ext_source = tor_strdup(source);
+ log_fn(trusted ? LOG_WARN : LOG_INFO, domain,
+ "Received %s with skewed time (%s): "
+ "It seems that our clock is %s by %s, or that theirs is %s%s. "
+ "Tor requires an accurate clock to work: please check your time, "
+ "timezone, and date settings.", received, ext_source,
+ apparent_skew > 0 ? "ahead" : "behind", dbuf,
+ apparent_skew > 0 ? "behind" : "ahead",
+ (!conn || trusted) ? "" : ", or they are sending us the wrong time");
+ if (trusted)
+ control_event_general_status(LOG_WARN, "CLOCK_SKEW SKEW=%ld SOURCE=%s",
+ apparent_skew, ext_source);
+ tor_free(ext_source);
+}
+
diff --git a/src/or/connection.h b/src/or/connection.h
index b6ff3d7bd6..ec5c3945ec 100644
--- a/src/or/connection.h
+++ b/src/or/connection.h
@@ -146,12 +146,12 @@ static void connection_write_to_buf(const char *string, size_t len,
/* DOCDOC connection_write_to_buf_zlib */
static void connection_write_to_buf_zlib(const char *string, size_t len,
dir_connection_t *conn, int done);
-static INLINE void
+static inline void
connection_write_to_buf(const char *string, size_t len, connection_t *conn)
{
connection_write_to_buf_impl_(string, len, conn, 0);
}
-static INLINE void
+static inline void
connection_write_to_buf_zlib(const char *string, size_t len,
dir_connection_t *conn, int done)
{
@@ -163,7 +163,7 @@ static size_t connection_get_inbuf_len(connection_t *conn);
/* DOCDOC connection_get_outbuf_len */
static size_t connection_get_outbuf_len(connection_t *conn);
-static INLINE size_t
+static inline size_t
connection_get_inbuf_len(connection_t *conn)
{
IF_HAS_BUFFEREVENT(conn, {
@@ -173,7 +173,7 @@ connection_get_inbuf_len(connection_t *conn)
}
}
-static INLINE size_t
+static inline size_t
connection_get_outbuf_len(connection_t *conn)
{
IF_HAS_BUFFEREVENT(conn, {
@@ -186,14 +186,64 @@ 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);
-connection_t *connection_get_by_type_addr_port_purpose(int type,
- const tor_addr_t *addr,
- uint16_t port, int purpose);
+MOCK_DECL(connection_t *,connection_get_by_type_addr_port_purpose,(int type,
+ const tor_addr_t *addr,
+ uint16_t port, int purpose));
connection_t *connection_get_by_type_state(int type, int state);
connection_t *connection_get_by_type_state_rendquery(int type, int state,
const char *rendquery);
dir_connection_t *connection_dir_get_by_purpose_and_resource(
- int state, const char *resource);
+ int purpose,
+ const char *resource);
+dir_connection_t *connection_dir_get_by_purpose_resource_and_state(
+ int purpose,
+ const char *resource,
+ int state);
+smartlist_t *connection_dir_list_by_purpose_and_resource(
+ int purpose,
+ const char *resource);
+smartlist_t *connection_dir_list_by_purpose_resource_and_state(
+ int purpose,
+ const char *resource,
+ int state);
+
+#define CONN_LEN_AND_FREE_TEMPLATE(sl) \
+ STMT_BEGIN \
+ int len = smartlist_len(sl); \
+ smartlist_free(sl); \
+ return len; \
+ STMT_END
+
+/** Return a count of directory connections that are fetching the item
+ * described by <b>purpose</b>/<b>resource</b>. */
+static inline int
+connection_dir_count_by_purpose_and_resource(
+ int purpose,
+ const char *resource)
+{
+ smartlist_t *conns = connection_dir_list_by_purpose_and_resource(
+ purpose,
+ resource);
+ CONN_LEN_AND_FREE_TEMPLATE(conns);
+}
+
+/** Return a count of directory connections that are fetching the item
+ * described by <b>purpose</b>/<b>resource</b>/<b>state</b>. */
+static inline int
+connection_dir_count_by_purpose_resource_and_state(
+ int purpose,
+ const char *resource,
+ int state)
+{
+ smartlist_t *conns =
+ connection_dir_list_by_purpose_resource_and_state(
+ purpose,
+ resource,
+ state);
+ CONN_LEN_AND_FREE_TEMPLATE(conns);
+}
+
+#undef CONN_LEN_AND_FREE_TEMPLATE
int any_other_active_or_conns(const or_connection_t *this_conn);
@@ -210,6 +260,10 @@ int connection_or_nonopen_was_started_here(or_connection_t *conn);
void connection_dump_buffer_mem_stats(int severity);
void remove_file_if_very_old(const char *fname, time_t now);
+void clock_skew_warning(const connection_t *conn, long apparent_skew,
+ int trusted, log_domain_mask_t domain,
+ const char *received, const char *source);
+
#ifdef USE_BUFFEREVENTS
int connection_type_uses_bufferevent(connection_t *conn);
void connection_configure_bufferevent_callbacks(connection_t *conn);
@@ -235,6 +289,13 @@ void connection_buckets_note_empty_ts(uint32_t *timestamp_var,
int tokens_before,
size_t tokens_removed,
const struct timeval *tvnow);
+MOCK_DECL(STATIC int,connection_connect_sockaddr,
+ (connection_t *conn,
+ const struct sockaddr *sa,
+ socklen_t sa_len,
+ const struct sockaddr *bindaddr,
+ socklen_t bindaddr_len,
+ int *socket_error));
#endif
#endif
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 729ef8a4c7..dd81142094 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -11,6 +11,9 @@
#define CONNECTION_EDGE_PRIVATE
#include "or.h"
+
+#include "backtrace.h"
+
#include "addressmap.h"
#include "buffers.h"
#include "channel.h"
@@ -503,6 +506,16 @@ connection_edge_finished_connecting(edge_connection_t *edge_conn)
return connection_edge_process_inbuf(edge_conn, 1);
}
+/** A list of all the entry_connection_t * objects that are not marked
+ * for close, and are in AP_CONN_STATE_CIRCUIT_WAIT.
+ *
+ * (Right now, we check in several places to make sure that this list is
+ * correct. When it's incorrect, we'll fix it, and log a BUG message.)
+ */
+static smartlist_t *pending_entry_connections = NULL;
+
+static int untried_pending_connections = 0;
+
/** Common code to connection_(ap|exit)_about_to_close. */
static void
connection_edge_about_to_close(edge_connection_t *edge_conn)
@@ -525,6 +538,8 @@ connection_ap_about_to_close(entry_connection_t *entry_conn)
edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(entry_conn);
connection_t *conn = ENTRY_TO_CONN(entry_conn);
+ connection_edge_about_to_close(edge_conn);
+
if (entry_conn->socks_request->has_finished == 0) {
/* since conn gets removed right after this function finishes,
* there's no point trying to send back a reply at this point. */
@@ -543,6 +558,20 @@ connection_ap_about_to_close(entry_connection_t *entry_conn)
conn->marked_for_close_file, conn->marked_for_close);
dnsserv_reject_request(entry_conn);
}
+
+ if (TO_CONN(edge_conn)->state == AP_CONN_STATE_CIRCUIT_WAIT) {
+ smartlist_remove(pending_entry_connections, entry_conn);
+ }
+
+#if 1
+ /* Check to make sure that this isn't in pending_entry_connections if it
+ * didn't actually belong there. */
+ if (TO_CONN(edge_conn)->type == CONN_TYPE_AP) {
+ connection_ap_warn_and_unmark_if_pending_circ(entry_conn,
+ "about_to_close");
+ }
+#endif
+
control_event_stream_bandwidth(edge_conn);
control_event_stream_status(entry_conn, STREAM_EVENT_CLOSED,
edge_conn->end_reason);
@@ -711,26 +740,181 @@ connection_ap_expire_beginning(void)
} SMARTLIST_FOREACH_END(base_conn);
}
-/** Tell any AP streams that are waiting for a new circuit to try again,
- * either attaching to an available circ or launching a new one.
+/**
+ * As connection_ap_attach_pending, but first scans the entire connection
+ * array to see if any elements are missing.
*/
void
-connection_ap_attach_pending(void)
+connection_ap_rescan_and_attach_pending(void)
{
entry_connection_t *entry_conn;
smartlist_t *conns = get_connection_array();
+
+ if (PREDICT_UNLIKELY(NULL == pending_entry_connections))
+ pending_entry_connections = smartlist_new();
+
SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
if (conn->marked_for_close ||
conn->type != CONN_TYPE_AP ||
conn->state != AP_CONN_STATE_CIRCUIT_WAIT)
continue;
+
entry_conn = TO_ENTRY_CONN(conn);
+ tor_assert(entry_conn);
+ if (! smartlist_contains(pending_entry_connections, entry_conn)) {
+ log_warn(LD_BUG, "Found a connection %p that was supposed to be "
+ "in pending_entry_connections, but wasn't. No worries; "
+ "adding it.",
+ pending_entry_connections);
+ untried_pending_connections = 1;
+ connection_ap_mark_as_pending_circuit(entry_conn);
+ }
+
+ } SMARTLIST_FOREACH_END(conn);
+
+ connection_ap_attach_pending(1);
+}
+
+#ifdef DEBUGGING_17659
+#define UNMARK() do { \
+ entry_conn->marked_pending_circ_line = 0; \
+ entry_conn->marked_pending_circ_file = 0; \
+ } while (0)
+#else
+#define UNMARK() do { } while (0)
+#endif
+
+/** Tell any AP streams that are listed as waiting for a new circuit to try
+ * again, either attaching to an available circ or launching a new one.
+ *
+ * If <b>retry</b> is false, only check the list if it contains at least one
+ * streams that we have not yet tried to attach to a circuit.
+ */
+void
+connection_ap_attach_pending(int retry)
+{
+ if (PREDICT_UNLIKELY(!pending_entry_connections)) {
+ return;
+ }
+
+ if (untried_pending_connections == 0 && !retry)
+ return;
+
+ /* Don't allow modifications to pending_entry_connections while we are
+ * iterating over it. */
+ smartlist_t *pending = pending_entry_connections;
+ pending_entry_connections = smartlist_new();
+
+ SMARTLIST_FOREACH_BEGIN(pending,
+ entry_connection_t *, entry_conn) {
+ connection_t *conn = ENTRY_TO_CONN(entry_conn);
+ tor_assert(conn && entry_conn);
+ if (conn->marked_for_close) {
+ UNMARK();
+ continue;
+ }
+ if (conn->magic != ENTRY_CONNECTION_MAGIC) {
+ log_warn(LD_BUG, "%p has impossible magic value %u.",
+ entry_conn, (unsigned)conn->magic);
+ UNMARK();
+ continue;
+ }
+ if (conn->state != AP_CONN_STATE_CIRCUIT_WAIT) {
+ log_warn(LD_BUG, "%p is no longer in circuit_wait. Its current state "
+ "is %s. Why is it on pending_entry_connections?",
+ entry_conn,
+ conn_state_to_string(conn->type, conn->state));
+ UNMARK();
+ continue;
+ }
+
if (connection_ap_handshake_attach_circuit(entry_conn) < 0) {
if (!conn->marked_for_close)
connection_mark_unattached_ap(entry_conn,
END_STREAM_REASON_CANT_ATTACH);
}
- } SMARTLIST_FOREACH_END(conn);
+
+ if (! conn->marked_for_close &&
+ conn->type == CONN_TYPE_AP &&
+ conn->state == AP_CONN_STATE_CIRCUIT_WAIT) {
+ if (!smartlist_contains(pending_entry_connections, entry_conn)) {
+ smartlist_add(pending_entry_connections, entry_conn);
+ continue;
+ }
+ }
+
+ UNMARK();
+ } SMARTLIST_FOREACH_END(entry_conn);
+
+ smartlist_free(pending);
+ untried_pending_connections = 0;
+}
+
+/** Mark <b>entry_conn</b> as needing to get attached to a circuit.
+ *
+ * And <b>entry_conn</b> must be in AP_CONN_STATE_CIRCUIT_WAIT,
+ * should not already be pending a circuit. The circuit will get
+ * launched or the connection will get attached the next time we
+ * call connection_ap_attach_pending().
+ */
+void
+connection_ap_mark_as_pending_circuit_(entry_connection_t *entry_conn,
+ const char *fname, int lineno)
+{
+ connection_t *conn = ENTRY_TO_CONN(entry_conn);
+ tor_assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
+ tor_assert(conn->magic == ENTRY_CONNECTION_MAGIC);
+ if (conn->marked_for_close)
+ return;
+
+ if (PREDICT_UNLIKELY(NULL == pending_entry_connections))
+ pending_entry_connections = smartlist_new();
+
+ if (PREDICT_UNLIKELY(smartlist_contains(pending_entry_connections,
+ entry_conn))) {
+ log_warn(LD_BUG, "What?? pending_entry_connections already contains %p! "
+ "(Called from %s:%d.)",
+ entry_conn, fname, lineno);
+#ifdef DEBUGGING_17659
+ const char *f2 = entry_conn->marked_pending_circ_file;
+ log_warn(LD_BUG, "(Previously called from %s:%d.)\n",
+ f2 ? f2 : "<NULL>",
+ entry_conn->marked_pending_circ_line);
+#endif
+ log_backtrace(LOG_WARN, LD_BUG, "To debug, this may help");
+ return;
+ }
+
+#ifdef DEBUGGING_17659
+ entry_conn->marked_pending_circ_line = (uint16_t) lineno;
+ entry_conn->marked_pending_circ_file = fname;
+#endif
+
+ untried_pending_connections = 1;
+ smartlist_add(pending_entry_connections, entry_conn);
+}
+
+/** Mark <b>entry_conn</b> as no longer waiting for a circuit. */
+void
+connection_ap_mark_as_non_pending_circuit(entry_connection_t *entry_conn)
+{
+ if (PREDICT_UNLIKELY(NULL == pending_entry_connections))
+ return;
+ UNMARK();
+ smartlist_remove(pending_entry_connections, entry_conn);
+}
+
+/** DOCDOC */
+void
+connection_ap_warn_and_unmark_if_pending_circ(entry_connection_t *entry_conn,
+ const char *where)
+{
+ if (pending_entry_connections &&
+ smartlist_contains(pending_entry_connections, entry_conn)) {
+ log_warn(LD_BUG, "What was %p doing in pending_entry_connections in %s?",
+ entry_conn, where);
+ connection_ap_mark_as_non_pending_circuit(entry_conn);
+ }
}
/** Tell any AP streams that are waiting for a one-hop tunnel to
@@ -851,12 +1035,13 @@ connection_ap_detach_retriable(entry_connection_t *conn,
* a tunneled directory connection, then just attach it. */
ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CIRCUIT_WAIT;
circuit_detach_stream(TO_CIRCUIT(circ),ENTRY_TO_EDGE_CONN(conn));
- return connection_ap_handshake_attach_circuit(conn);
+ connection_ap_mark_as_pending_circuit(conn);
} else {
+ CONNECTION_AP_EXPECT_NONPENDING(conn);
ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CONTROLLER_WAIT;
circuit_detach_stream(TO_CIRCUIT(circ),ENTRY_TO_EDGE_CONN(conn));
- return 0;
}
+ return 0;
}
/** Check if <b>conn</b> is using a dangerous port. Then warn and/or
@@ -905,6 +1090,7 @@ connection_ap_rewrite_and_attach_if_allowed(entry_connection_t *conn,
const or_options_t *options = get_options();
if (options->LeaveStreamsUnattached) {
+ CONNECTION_AP_EXPECT_NONPENDING(conn);
ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CONTROLLER_WAIT;
return 0;
}
@@ -1454,10 +1640,12 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
/* If we were given a circuit to attach to, try to attach. Otherwise,
* try to find a good one and attach to that. */
int rv;
- if (circ)
- rv = connection_ap_handshake_attach_chosen_circuit(conn, circ, cpath);
- else
- rv = connection_ap_handshake_attach_circuit(conn);
+ if (circ) {
+ rv = connection_ap_handshake_attach_chosen_circuit(conn, circ, cpath);
+ } else {
+ connection_ap_mark_as_pending_circuit(conn);
+ rv = 0;
+ }
/* If the above function returned 0 then we're waiting for a circuit.
* if it returned 1, we're attached. Both are okay. But if it returned
@@ -1554,6 +1742,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
* Also, a fetch could have been requested if the onion address was not
* found in the cache previously. */
if (refetch_desc || !rend_client_any_intro_points_usable(entry)) {
+ connection_ap_mark_as_non_pending_circuit(conn);
base_conn->state = AP_CONN_STATE_RENDDESC_WAIT;
log_info(LD_REND, "Unknown descriptor %s. Fetching.",
safe_str_client(rend_data->onion_address));
@@ -1564,11 +1753,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
/* We have the descriptor so launch a connection to the HS. */
base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
log_info(LD_REND, "Descriptor is here. Great.");
- if (connection_ap_handshake_attach_circuit(conn) < 0) {
- if (!base_conn->marked_for_close)
- connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
- return -1;
- }
+ connection_ap_mark_as_pending_circuit(conn);
return 0;
}
@@ -2324,12 +2509,7 @@ connection_ap_make_link(connection_t *partner,
control_event_stream_status(conn, STREAM_EVENT_NEW, 0);
/* attaching to a dirty circuit is fine */
- if (connection_ap_handshake_attach_circuit(conn) < 0) {
- if (!base_conn->marked_for_close)
- connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
- return NULL;
- }
-
+ connection_ap_mark_as_pending_circuit(conn);
log_info(LD_APP,"... application connection created and linked.");
return conn;
}
@@ -2771,8 +2951,8 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
return 0;
}
/* Make sure to get the 'real' address of the previous hop: the
- * caller might want to know whether his IP address has changed, and
- * we might already have corrected base_.addr[ess] for the relay's
+ * caller might want to know whether the remote IP address has changed,
+ * and we might already have corrected base_.addr[ess] for the relay's
* canonical IP address. */
if (or_circ && or_circ->p_chan)
address = tor_strdup(channel_get_actual_remote_address(or_circ->p_chan));
@@ -3478,3 +3658,12 @@ circuit_clear_isolation(origin_circuit_t *circ)
circ->socks_username_len = circ->socks_password_len = 0;
}
+/** Free all storage held in module-scoped variables for connection_edge.c */
+void
+connection_edge_free_all(void)
+{
+ untried_pending_connections = 0;
+ smartlist_free(pending_entry_connections);
+ pending_entry_connections = NULL;
+}
+
diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h
index 7c0b9c0767..2aba801461 100644
--- a/src/or/connection_edge.h
+++ b/src/or/connection_edge.h
@@ -64,7 +64,20 @@ int connection_edge_is_rendezvous_stream(edge_connection_t *conn);
int connection_ap_can_use_exit(const entry_connection_t *conn,
const node_t *exit);
void connection_ap_expire_beginning(void);
-void connection_ap_attach_pending(void);
+void connection_ap_rescan_and_attach_pending(void);
+void connection_ap_attach_pending(int retry);
+void connection_ap_mark_as_pending_circuit_(entry_connection_t *entry_conn,
+ const char *file, int line);
+#define connection_ap_mark_as_pending_circuit(c) \
+ connection_ap_mark_as_pending_circuit_((c), __FILE__, __LINE__)
+void connection_ap_mark_as_non_pending_circuit(entry_connection_t *entry_conn);
+#define CONNECTION_AP_EXPECT_NONPENDING(c) do { \
+ if (ENTRY_TO_CONN(c)->state == AP_CONN_STATE_CIRCUIT_WAIT) { \
+ log_warn(LD_BUG, "At %s:%d: %p was unexpectedly in circuit_wait.", \
+ __FILE__, __LINE__, (c)); \
+ connection_ap_mark_as_non_pending_circuit(c); \
+ } \
+ } while (0)
void connection_ap_fail_onehop(const char *failed_digest,
cpath_build_state_t *build_state);
void circuit_discard_optional_exit_enclaves(extend_info_t *info);
@@ -100,6 +113,12 @@ int connection_edge_update_circuit_isolation(const entry_connection_t *conn,
void circuit_clear_isolation(origin_circuit_t *circ);
streamid_t get_unique_stream_id_by_circ(origin_circuit_t *circ);
+void connection_edge_free_all(void);
+
+void connection_ap_warn_and_unmark_if_pending_circ(
+ entry_connection_t *entry_conn,
+ const char *where);
+
/** @name Begin-cell flags
*
* These flags are used in RELAY_BEGIN cells to change the default behavior
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index a967c93aca..6f40e6a78e 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -1450,17 +1450,12 @@ connection_tls_continue_handshake(or_connection_t *conn)
{
int result;
check_no_tls_errors();
- again:
- if (conn->base_.state == OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING) {
- // log_notice(LD_OR, "Renegotiate with %p", conn->tls);
- result = tor_tls_renegotiate(conn->tls);
- // log_notice(LD_OR, "Result: %d", result);
- } else {
- tor_assert(conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING);
- // log_notice(LD_OR, "Continue handshake with %p", conn->tls);
- result = tor_tls_handshake(conn->tls);
- // log_notice(LD_OR, "Result: %d", result);
- }
+
+ tor_assert(conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING);
+ // log_notice(LD_OR, "Continue handshake with %p", conn->tls);
+ result = tor_tls_handshake(conn->tls);
+ // log_notice(LD_OR, "Result: %d", result);
+
switch (result) {
CASE_TOR_TLS_ERROR_ANY:
log_info(LD_OR,"tls error [%s]. breaking connection.",
@@ -1469,23 +1464,10 @@ connection_tls_continue_handshake(or_connection_t *conn)
case TOR_TLS_DONE:
if (! tor_tls_used_v1_handshake(conn->tls)) {
if (!tor_tls_is_server(conn->tls)) {
- if (conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING) {
- if (tor_tls_received_v3_certificate(conn->tls)) {
- log_info(LD_OR, "Client got a v3 cert! Moving on to v3 "
- "handshake with ciphersuite %s",
- tor_tls_get_ciphersuite_name(conn->tls));
- return connection_or_launch_v3_or_handshake(conn);
- } else {
- log_debug(LD_OR, "Done with initial SSL handshake (client-side)."
- " Requesting renegotiation.");
- connection_or_change_state(conn,
- OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING);
- goto again;
- }
- }
- // log_notice(LD_OR,"Done. state was %d.", conn->base_.state);
+ tor_assert(conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING);
+ return connection_or_launch_v3_or_handshake(conn);
} else {
- /* v2/v3 handshake, but not a client. */
+ /* v2/v3 handshake, but we are not a client. */
log_debug(LD_OR, "Done with initial SSL handshake (server-side). "
"Expecting renegotiation or VERSIONS cell");
tor_tls_set_renegotiate_callback(conn->tls,
@@ -1498,6 +1480,7 @@ connection_tls_continue_handshake(or_connection_t *conn)
return 0;
}
}
+ tor_assert(tor_tls_is_server(conn->tls));
return connection_tls_finish_handshake(conn);
case TOR_TLS_WANTWRITE:
connection_start_writing(TO_CONN(conn));
@@ -1533,22 +1516,8 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event,
if (! tor_tls_used_v1_handshake(conn->tls)) {
if (!tor_tls_is_server(conn->tls)) {
if (conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING) {
- if (tor_tls_received_v3_certificate(conn->tls)) {
- log_info(LD_OR, "Client got a v3 cert!");
- if (connection_or_launch_v3_or_handshake(conn) < 0)
- connection_or_close_for_error(conn, 0);
- return;
- } else {
- connection_or_change_state(conn,
- OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING);
- tor_tls_unblock_renegotiation(conn->tls);
- if (bufferevent_ssl_renegotiate(conn->base_.bufev)<0) {
- log_warn(LD_OR, "Start_renegotiating went badly.");
- connection_or_close_for_error(conn, 0);
- }
- tor_tls_unblock_renegotiation(conn->tls);
- return; /* ???? */
- }
+ if (connection_or_launch_v3_or_handshake(conn) < 0)
+ connection_or_close_for_error(conn, 0);
}
} else {
const int handshakes = tor_tls_get_num_server_handshakes(conn->tls);
@@ -1612,11 +1581,11 @@ connection_or_nonopen_was_started_here(or_connection_t *conn)
}
/** <b>Conn</b> just completed its handshake. Return 0 if all is well, and
- * return -1 if he is lying, broken, or otherwise something is wrong.
+ * return -1 if they are lying, broken, or otherwise something is wrong.
*
* If we initiated this connection (<b>started_here</b> is true), make sure
* the other side sent a correctly formed certificate. If I initiated the
- * connection, make sure it's the right guy.
+ * connection, make sure it's the right relay by checking the certificate.
*
* Otherwise (if we _didn't_ initiate this connection), it's okay for
* the certificate to be weird or absent.
@@ -1632,7 +1601,7 @@ connection_or_nonopen_was_started_here(or_connection_t *conn)
* 1) Set conn->circ_id_type according to tor-spec.txt.
* 2) If we're an authdirserver and we initiated the connection: drop all
* descriptors that claim to be on that IP/port but that aren't
- * this guy; and note that this guy is reachable.
+ * this relay; and note that this relay is reachable.
* 3) If this is a bridge and we didn't configure its identity
* fingerprint, remember the keyid we just learned.
*/
@@ -1785,7 +1754,7 @@ connection_or_client_used(or_connection_t *conn)
*
* Make sure we are happy with the person we just handshaked with.
*
- * If he initiated the connection, make sure he's not already connected,
+ * If they initiated the connection, make sure they're not already connected,
* then initialize conn from the information in router.
*
* If all is successful, call circuit_n_conn_done() to handle events
@@ -1800,6 +1769,8 @@ connection_tls_finish_handshake(or_connection_t *conn)
char digest_rcvd[DIGEST_LEN];
int started_here = connection_or_nonopen_was_started_here(conn);
+ tor_assert(!started_here);
+
log_debug(LD_HANDSHAKE,"%s tls handshake on %p with %s done, using "
"ciphersuite %s. verifying.",
started_here?"outgoing":"incoming",
@@ -1815,10 +1786,8 @@ connection_tls_finish_handshake(or_connection_t *conn)
if (tor_tls_used_v1_handshake(conn->tls)) {
conn->link_proto = 1;
- if (!started_here) {
- connection_or_init_conn_from_address(conn, &conn->base_.addr,
- conn->base_.port, digest_rcvd, 0);
- }
+ connection_or_init_conn_from_address(conn, &conn->base_.addr,
+ conn->base_.port, digest_rcvd, 0);
tor_tls_block_renegotiation(conn->tls);
rep_hist_note_negotiated_link_proto(1, started_here);
return connection_or_set_state_open(conn);
@@ -1826,10 +1795,8 @@ connection_tls_finish_handshake(or_connection_t *conn)
connection_or_change_state(conn, OR_CONN_STATE_OR_HANDSHAKING_V2);
if (connection_init_or_handshake_state(conn, started_here) < 0)
return -1;
- if (!started_here) {
- connection_or_init_conn_from_address(conn, &conn->base_.addr,
- conn->base_.port, digest_rcvd, 0);
- }
+ connection_or_init_conn_from_address(conn, &conn->base_.addr,
+ conn->base_.port, digest_rcvd, 0);
return connection_or_send_versions(conn, 0);
}
}
@@ -1844,7 +1811,6 @@ static int
connection_or_launch_v3_or_handshake(or_connection_t *conn)
{
tor_assert(connection_or_nonopen_was_started_here(conn));
- tor_assert(tor_tls_received_v3_certificate(conn->tls));
circuit_build_times_network_is_live(get_circuit_build_times_mutable());
@@ -2290,8 +2256,7 @@ connection_or_send_auth_challenge_cell(or_connection_t *conn)
auth_challenge_cell_t *ac = auth_challenge_cell_new();
- if (crypto_rand((char*)ac->challenge, sizeof(ac->challenge)) < 0)
- goto done;
+ crypto_rand((char*)ac->challenge, sizeof(ac->challenge));
auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_TLSSECRET);
auth_challenge_cell_set_n_methods(ac,
@@ -2353,7 +2318,7 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
{
const tor_x509_cert_t *id_cert=NULL, *link_cert=NULL;
- const digests_t *my_digests, *their_digests;
+ const common_digests_t *my_digests, *their_digests;
const uint8_t *my_id, *their_id, *client_id, *server_id;
if (tor_tls_get_my_certs(server, &link_cert, &id_cert))
goto err;
diff --git a/src/or/control.c b/src/or/control.c
index 220e7e514f..55b287d27b 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -192,7 +192,7 @@ static void flush_queued_events_cb(evutil_socket_t fd, short what, void *arg);
/** Given a control event code for a message event, return the corresponding
* log severity. */
-static INLINE int
+static inline int
event_to_log_severity(int event)
{
switch (event) {
@@ -206,7 +206,7 @@ event_to_log_severity(int event)
}
/** Given a log severity, return the corresponding control event code. */
-static INLINE int
+static inline int
log_severity_to_event(int severity)
{
switch (severity) {
@@ -325,7 +325,7 @@ control_event_is_interesting(int event)
/** Append a NUL-terminated string <b>s</b> to the end of
* <b>conn</b>-\>outbuf.
*/
-static INLINE void
+static inline void
connection_write_str_to_buf(const char *s, control_connection_t *conn)
{
size_t len = strlen(s);
@@ -428,7 +428,7 @@ read_escaped_data(const char *data, size_t len, char **out)
/** If the first <b>in_len_max</b> characters in <b>start</b> contain a
* double-quoted string with escaped characters, return the length of that
* string (as encoded, including quotes). Otherwise return -1. */
-static INLINE int
+static inline int
get_escaped_string_length(const char *start, size_t in_len_max,
int *chars_out)
{
@@ -1927,6 +1927,22 @@ getinfo_helper_dir(control_connection_t *control_conn,
*errmsg = "Not found in cache";
return -1;
}
+ } else if (!strcmpstart(question, "hs/service/desc/id/")) {
+ rend_cache_entry_t *e = NULL;
+
+ question += strlen("hs/service/desc/id/");
+ if (strlen(question) != REND_SERVICE_ID_LEN_BASE32) {
+ *errmsg = "Invalid address";
+ return -1;
+ }
+
+ if (!rend_cache_lookup_v2_desc_as_service(question, &e)) {
+ /* Descriptor found in cache */
+ *answer = tor_strdup(e->desc);
+ } else {
+ *errmsg = "Not found in cache";
+ return -1;
+ }
} else if (!strcmpstart(question, "md/id/")) {
const node_t *node = node_get_by_hex_id(question+strlen("md/id/"));
const microdesc_t *md = NULL;
@@ -2481,6 +2497,8 @@ static const getinfo_item_t getinfo_items[] = {
PREFIX("extra-info/digest/", dir, "Extra-info documents by digest."),
PREFIX("hs/client/desc/id", dir,
"Hidden Service descriptor in client's cache by onion."),
+ PREFIX("hs/service/desc/id/", dir,
+ "Hidden Service descriptor in services's cache by onion."),
PREFIX("net/listeners/", listeners, "Bound addresses by type"),
ITEM("ns/all", networkstatus,
"Brief summary of router status (v2 directory format)"),
@@ -2544,6 +2562,12 @@ static const getinfo_item_t getinfo_items[] = {
"v3 Networkstatus consensus as retrieved from a DirPort."),
ITEM("exit-policy/default", policies,
"The default value appended to the configured exit policy."),
+ ITEM("exit-policy/reject-private/default", policies,
+ "The default rules appended to the configured exit policy by"
+ " ExitPolicyRejectPrivate."),
+ ITEM("exit-policy/reject-private/relay", policies,
+ "The relay-specific rules appended to the configured exit policy by"
+ " ExitPolicyRejectPrivate."),
ITEM("exit-policy/full", policies, "The entire exit policy of onion router"),
ITEM("exit-policy/ipv4", policies, "IPv4 parts of exit policy"),
ITEM("exit-policy/ipv6", policies, "IPv6 parts of exit policy"),
@@ -2840,12 +2864,26 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
}
/* now circ refers to something that is ready to be extended */
+ int first_node = zero_circ;
SMARTLIST_FOREACH(nodes, const node_t *, node,
{
- extend_info_t *info = extend_info_from_node(node, 0);
- tor_assert(info); /* True, since node_has_descriptor(node) == true */
+ extend_info_t *info = extend_info_from_node(node, first_node);
+ if (first_node && !info) {
+ log_warn(LD_CONTROL,
+ "controller tried to connect to a node that doesn't have any "
+ "addresses that are allowed by the firewall configuration; "
+ "circuit marked for closing.");
+ circuit_mark_for_close(TO_CIRCUIT(circ), -END_CIRC_REASON_CONNECTFAILED);
+ connection_write_str_to_buf("551 Couldn't start circuit\r\n", conn);
+ goto done;
+ } else {
+ /* True, since node_has_descriptor(node) == true and we are extending
+ * to the node's primary address */
+ tor_assert(info);
+ }
circuit_append_new_exit(circ, info);
extend_info_free(info);
+ first_node = 0;
});
/* now that we've populated the cpath, start extending */
@@ -2987,6 +3025,7 @@ handle_control_attachstream(control_connection_t *conn, uint32_t len,
edge_conn->end_reason = 0;
if (tmpcirc)
circuit_detach_stream(tmpcirc, edge_conn);
+ CONNECTION_AP_EXPECT_NONPENDING(ap_conn);
TO_CONN(edge_conn)->state = AP_CONN_STATE_CONTROLLER_WAIT;
}
@@ -3418,8 +3457,7 @@ handle_control_authchallenge(control_connection_t *conn, uint32_t len,
tor_free(client_nonce);
return -1;
}
- const int fail = crypto_rand(server_nonce, SAFECOOKIE_SERVER_NONCE_LEN);
- tor_assert(!fail);
+ crypto_rand(server_nonce, SAFECOOKIE_SERVER_NONCE_LEN);
/* Now compute and send the server-to-controller response, and the
* server's nonce. */
@@ -6233,6 +6271,31 @@ get_desc_id_from_query(const rend_data_t *rend_data, const char *hsdir_fp)
return desc_id;
}
+/** send HS_DESC CREATED event when a local service generates a descriptor.
+ *
+ * <b>service_id</b> is the descriptor onion address.
+ * <b>desc_id_base32</b> is the descriptor ID.
+ * <b>replica</b> is the the descriptor replica number.
+ */
+void
+control_event_hs_descriptor_created(const char *service_id,
+ const char *desc_id_base32,
+ int replica)
+{
+ if (!service_id || !desc_id_base32) {
+ log_warn(LD_BUG, "Called with service_digest==%p, "
+ "desc_id_base32==%p", service_id, desc_id_base32);
+ return;
+ }
+
+ send_control_event(EVENT_HS_DESC,
+ "650 HS_DESC CREATED %s UNKNOWN UNKNOWN %s "
+ "REPLICA=%d\r\n",
+ service_id,
+ desc_id_base32,
+ replica);
+}
+
/** send HS_DESC upload event.
*
* <b>service_id</b> is the descriptor onion address.
@@ -6321,6 +6384,7 @@ control_event_hs_descriptor_receive_end(const char *action,
*/
void
control_event_hs_descriptor_upload_end(const char *action,
+ const char *onion_address,
const char *id_digest,
const char *reason)
{
@@ -6337,8 +6401,9 @@ control_event_hs_descriptor_upload_end(const char *action,
}
send_control_event(EVENT_HS_DESC,
- "650 HS_DESC %s UNKNOWN UNKNOWN %s%s\r\n",
+ "650 HS_DESC %s %s UNKNOWN %s%s\r\n",
action,
+ rend_hsaddress_str_or_unknown(onion_address),
node_describe_longname_by_id(id_digest),
reason_field ? reason_field : "");
@@ -6368,14 +6433,17 @@ control_event_hs_descriptor_received(const char *onion_address,
* called when we successfully uploaded a hidden service descriptor.
*/
void
-control_event_hs_descriptor_uploaded(const char *id_digest)
+control_event_hs_descriptor_uploaded(const char *id_digest,
+ const char *onion_address)
{
if (!id_digest) {
log_warn(LD_BUG, "Called with id_digest==%p",
id_digest);
return;
}
- control_event_hs_descriptor_upload_end("UPLOADED", id_digest, NULL);
+
+ control_event_hs_descriptor_upload_end("UPLOADED", onion_address,
+ id_digest, NULL);
}
/** Send HS_DESC event to inform controller that query <b>rend_query</b>
@@ -6437,6 +6505,7 @@ control_event_hs_descriptor_content(const char *onion_address,
*/
void
control_event_hs_descriptor_upload_failed(const char *id_digest,
+ const char *onion_address,
const char *reason)
{
if (!id_digest) {
@@ -6444,7 +6513,7 @@ control_event_hs_descriptor_upload_failed(const char *id_digest,
id_digest);
return;
}
- control_event_hs_descriptor_upload_end("UPLOAD_FAILED",
+ control_event_hs_descriptor_upload_end("UPLOAD_FAILED", onion_address,
id_digest, reason);
}
diff --git a/src/or/control.h b/src/or/control.h
index fdf7903cb8..c5191f93bb 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -117,6 +117,9 @@ MOCK_DECL(const char *, node_describe_longname_by_id,(const char *id_digest));
void control_event_hs_descriptor_requested(const rend_data_t *rend_query,
const char *desc_id_base32,
const char *hs_dir);
+void control_event_hs_descriptor_created(const char *service_id,
+ const char *desc_id_base32,
+ int replica);
void control_event_hs_descriptor_upload(const char *service_id,
const char *desc_id_base32,
const char *hs_dir);
@@ -126,16 +129,19 @@ void control_event_hs_descriptor_receive_end(const char *action,
const char *id_digest,
const char *reason);
void control_event_hs_descriptor_upload_end(const char *action,
+ const char *onion_address,
const char *hs_dir,
const char *reason);
void control_event_hs_descriptor_received(const char *onion_address,
const rend_data_t *rend_data,
const char *id_digest);
-void control_event_hs_descriptor_uploaded(const char *hs_dir);
+void control_event_hs_descriptor_uploaded(const char *hs_dir,
+ const char *onion_address);
void control_event_hs_descriptor_failed(const rend_data_t *rend_data,
const char *id_digest,
const char *reason);
void control_event_hs_descriptor_upload_failed(const char *hs_dir,
+ const char *onion_address,
const char *reason);
void control_event_hs_descriptor_content(const char *onion_address,
const char *desc_id,
diff --git a/src/or/directory.c b/src/or/directory.c
index 9461606f1b..627699118c 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -4,6 +4,7 @@
/* See LICENSE for licensing information */
#include "or.h"
+#include "backtrace.h"
#include "buffers.h"
#include "circuitbuild.h"
#include "config.h"
@@ -82,18 +83,18 @@ static void dir_microdesc_download_failed(smartlist_t *failed,
static void note_client_request(int purpose, int compressed, size_t bytes);
static int client_likes_consensus(networkstatus_t *v, const char *want_url);
-static void directory_initiate_command_rend(const tor_addr_t *addr,
- uint16_t or_port,
- uint16_t dir_port,
- const char *digest,
- uint8_t dir_purpose,
- uint8_t router_purpose,
- dir_indirection_t indirection,
- const char *resource,
- const char *payload,
- size_t payload_len,
- time_t if_modified_since,
- const rend_data_t *rend_query);
+static void directory_initiate_command_rend(
+ const tor_addr_port_t *or_addr_port,
+ const tor_addr_port_t *dir_addr_port,
+ const char *digest,
+ uint8_t dir_purpose,
+ uint8_t router_purpose,
+ dir_indirection_t indirection,
+ const char *resource,
+ const char *payload,
+ size_t payload_len,
+ time_t if_modified_since,
+ const rend_data_t *rend_query);
/********* START VARIABLES **********/
@@ -143,7 +144,7 @@ purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose)
/** Return a newly allocated string describing <b>auth</b>. Only describes
* authority features. */
-static char *
+STATIC char *
authdir_type_to_string(dirinfo_type_t auth)
{
char *result;
@@ -162,7 +163,7 @@ authdir_type_to_string(dirinfo_type_t auth)
}
/** Return a string describing a given directory connection purpose. */
-static const char *
+STATIC const char *
dir_conn_purpose_to_string(int purpose)
{
switch (purpose)
@@ -313,7 +314,6 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
SMARTLIST_FOREACH_BEGIN(dirservers, dir_server_t *, ds) {
routerstatus_t *rs = &(ds->fake_status);
size_t upload_len = payload_len;
- tor_addr_t ds_addr;
if ((type & ds->type) == 0)
continue;
@@ -344,11 +344,12 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
log_info(LD_DIR, "Uploading an extrainfo too (length %d)",
(int) extrainfo_len);
}
- tor_addr_from_ipv4h(&ds_addr, ds->addr);
if (purpose_needs_anonymity(dir_purpose, router_purpose)) {
indirection = DIRIND_ANONYMOUS;
- } else if (!fascist_firewall_allows_address_dir(&ds_addr,ds->dir_port)) {
- if (fascist_firewall_allows_address_or(&ds_addr,ds->or_port))
+ } else if (!fascist_firewall_allows_dir_server(ds,
+ FIREWALL_DIR_CONNECTION,
+ 0)) {
+ if (fascist_firewall_allows_dir_server(ds, FIREWALL_OR_CONNECTION, 0))
indirection = DIRIND_ONEHOP;
else
indirection = DIRIND_ANONYMOUS;
@@ -370,7 +371,7 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
/** Return true iff, according to the values in <b>options</b>, we should be
* using directory guards for direct downloads of directory information. */
-static int
+STATIC int
should_use_directory_guards(const or_options_t *options)
{
/* Public (non-bridge) servers never use directory guards. */
@@ -425,14 +426,17 @@ directory_pick_generic_dirserver(dirinfo_type_t type, int pds_flags,
* Use <b>pds_flags</b> as arguments to router_pick_directory_server()
* or router_pick_trusteddirserver().
*/
-MOCK_IMPL(void, directory_get_from_dirserver, (uint8_t dir_purpose,
- uint8_t router_purpose,
- const char *resource,
- int pds_flags))
+MOCK_IMPL(void, directory_get_from_dirserver, (
+ uint8_t dir_purpose,
+ uint8_t router_purpose,
+ const char *resource,
+ int pds_flags,
+ download_want_authority_t want_authority))
{
const routerstatus_t *rs = NULL;
const or_options_t *options = get_options();
- int prefer_authority = directory_fetches_from_authorities(options);
+ int prefer_authority = (directory_fetches_from_authorities(options)
+ || want_authority == DL_WANT_AUTHORITY);
int require_authority = 0;
int get_via_tor = purpose_needs_anonymity(dir_purpose, router_purpose);
dirinfo_type_t type = dir_fetch_type(dir_purpose, router_purpose, resource);
@@ -493,11 +497,14 @@ MOCK_IMPL(void, directory_get_from_dirserver, (uint8_t dir_purpose,
const node_t *node = choose_random_dirguard(type);
if (node && node->ri) {
/* every bridge has a routerinfo. */
- tor_addr_t addr;
routerinfo_t *ri = node->ri;
- node_get_addr(node, &addr);
- directory_initiate_command(&addr,
- ri->or_port, 0/*no dirport*/,
+ /* clients always make OR connections to bridges */
+ tor_addr_port_t or_ap;
+ /* we are willing to use a non-preferred address if we need to */
+ fascist_firewall_choose_address_node(node, FIREWALL_OR_CONNECTION, 0,
+ &or_ap);
+ directory_initiate_command(&or_ap.addr, or_ap.port,
+ NULL, 0, /*no dirport*/
ri->cache_info.identity_digest,
dir_purpose,
router_purpose,
@@ -606,6 +613,80 @@ dirind_is_anon(dir_indirection_t ind)
return ind == DIRIND_ANON_DIRPORT || ind == DIRIND_ANONYMOUS;
}
+/* Choose reachable OR and Dir addresses and ports from status, copying them
+ * into use_or_ap and use_dir_ap. If indirection is anonymous, then we're
+ * connecting via another relay, so choose the primary IPv4 address and ports.
+ *
+ * status should have at least one reachable address, if we can't choose a
+ * reachable address, warn and return -1. Otherwise, return 0.
+ */
+static int
+directory_choose_address_routerstatus(const routerstatus_t *status,
+ dir_indirection_t indirection,
+ tor_addr_port_t *use_or_ap,
+ tor_addr_port_t *use_dir_ap)
+{
+ tor_assert(status != NULL);
+ tor_assert(use_or_ap != NULL);
+ tor_assert(use_dir_ap != NULL);
+
+ const int anonymized_connection = dirind_is_anon(indirection);
+ int have_or = 0, have_dir = 0;
+
+ /* We expect status to have at least one reachable address if we're
+ * connecting to it directly.
+ *
+ * Therefore, we can simply use the other address if the one we want isn't
+ * allowed by the firewall.
+ *
+ * (When Tor uploads and downloads a hidden service descriptor, it uses
+ * DIRIND_ANONYMOUS, except for Tor2Web, which uses DIRIND_ONEHOP.
+ * So this code will only modify the address for Tor2Web's HS descriptor
+ * fetches. Even Single Onion Servers (NYI) use DIRIND_ANONYMOUS, to avoid
+ * HSDirs denying service by rejecting descriptors.)
+ */
+
+ /* Initialise the OR / Dir addresses */
+ tor_addr_make_null(&use_or_ap->addr, AF_UNSPEC);
+ use_or_ap->port = 0;
+ tor_addr_make_null(&use_dir_ap->addr, AF_UNSPEC);
+ use_dir_ap->port = 0;
+
+ if (anonymized_connection) {
+ /* Use the primary (IPv4) OR address if we're making an indirect
+ * connection. */
+ tor_addr_from_ipv4h(&use_or_ap->addr, status->addr);
+ use_or_ap->port = status->or_port;
+ have_or = 1;
+ } else {
+ /* We use an IPv6 address if we have one and we prefer it.
+ * Use the preferred address and port if they are reachable, otherwise,
+ * use the alternate address and port (if any).
+ */
+ have_or = fascist_firewall_choose_address_rs(status,
+ FIREWALL_OR_CONNECTION, 0,
+ use_or_ap);
+ }
+
+ have_dir = fascist_firewall_choose_address_rs(status,
+ FIREWALL_DIR_CONNECTION, 0,
+ use_dir_ap);
+
+ /* We rejected both addresses. This isn't great. */
+ if (!have_or && !have_dir) {
+ log_warn(LD_BUG, "Rejected all OR and Dir addresses from %s when "
+ "launching a directory connection to: IPv4 %s OR %d Dir %d "
+ "IPv6 %s OR %d Dir %d", routerstatus_describe(status),
+ fmt_addr32(status->addr), status->or_port,
+ status->dir_port, fmt_addr(&status->ipv6_addr),
+ status->ipv6_orport, status->dir_port);
+ log_backtrace(LOG_WARN, LD_BUG, "Addresses came from");
+ return -1;
+ }
+
+ return 0;
+}
+
/** Same as directory_initiate_command_routerstatus(), but accepts
* rendezvous data to fetch a hidden service descriptor. */
void
@@ -621,8 +702,11 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
{
const or_options_t *options = get_options();
const node_t *node;
- tor_addr_t addr;
+ tor_addr_port_t use_or_ap, use_dir_ap;
const int anonymized_connection = dirind_is_anon(indirection);
+
+ tor_assert(status != NULL);
+
node = node_get_by_id(status->identity_digest);
if (!node && anonymized_connection) {
@@ -631,7 +715,6 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
routerstatus_describe(status));
return;
}
- tor_addr_from_ipv4h(&addr, status->addr);
if (options->ExcludeNodes && options->StrictNodes &&
routerset_contains_routerstatus(options->ExcludeNodes, status, -1)) {
@@ -643,13 +726,30 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
return;
}
- directory_initiate_command_rend(&addr,
- status->or_port, status->dir_port,
- status->identity_digest,
- dir_purpose, router_purpose,
- indirection, resource,
- payload, payload_len, if_modified_since,
- rend_query);
+ /* At this point, if we are a clients making a direct connection to a
+ * directory server, we have selected a server that has at least one address
+ * allowed by ClientUseIPv4/6 and Reachable{"",OR,Dir}Addresses. This
+ * selection uses the preference in ClientPreferIPv6{OR,Dir}Port, if
+ * possible. (If UseBridges is set, clients always use IPv6, and prefer it
+ * by default.)
+ *
+ * Now choose an address that we can use to connect to the directory server.
+ */
+ if (directory_choose_address_routerstatus(status, indirection, &use_or_ap,
+ &use_dir_ap) < 0) {
+ return;
+ }
+
+ /* We don't retry the alternate OR/Dir address for the same directory if
+ * the address we choose fails (#6772).
+ * Instead, we'll retry another directory on failure. */
+
+ directory_initiate_command_rend(&use_or_ap, &use_dir_ap,
+ status->identity_digest,
+ dir_purpose, router_purpose,
+ indirection, resource,
+ payload, payload_len, if_modified_since,
+ rend_query);
}
/** Launch a new connection to the directory server <b>status</b> to
@@ -666,15 +766,15 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
* When fetching a rendezvous descriptor, <b>resource</b> is the service ID we
* want to fetch.
*/
-void
-directory_initiate_command_routerstatus(const routerstatus_t *status,
- uint8_t dir_purpose,
- uint8_t router_purpose,
- dir_indirection_t indirection,
- const char *resource,
- const char *payload,
- size_t payload_len,
- time_t if_modified_since)
+MOCK_IMPL(void, directory_initiate_command_routerstatus,
+ (const routerstatus_t *status,
+ uint8_t dir_purpose,
+ uint8_t router_purpose,
+ dir_indirection_t indirection,
+ const char *resource,
+ const char *payload,
+ size_t payload_len,
+ time_t if_modified_since))
{
directory_initiate_command_routerstatus_rend(status, dir_purpose,
router_purpose,
@@ -712,7 +812,7 @@ connection_dir_request_failed(dir_connection_t *conn)
return; /* this was a test fetch. don't retry. */
}
if (!entry_list_is_constrained(get_options()))
- router_set_status(conn->identity_digest, 0); /* don't try him again */
+ router_set_status(conn->identity_digest, 0); /* don't try this one again */
if (conn->base_.purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
conn->base_.purpose == DIR_PURPOSE_FETCH_EXTRAINFO) {
log_info(LD_DIR, "Giving up on serverdesc/extrainfo fetch from "
@@ -871,27 +971,52 @@ directory_command_should_use_begindir(const or_options_t *options,
if (indirection == DIRIND_DIRECT_CONN || indirection == DIRIND_ANON_DIRPORT)
return 0;
if (indirection == DIRIND_ONEHOP)
- if (!fascist_firewall_allows_address_or(addr, or_port) ||
+ if (!fascist_firewall_allows_address_addr(addr, or_port,
+ FIREWALL_OR_CONNECTION, 0, 0) ||
directory_fetches_from_authorities(options))
return 0; /* We're firewalled or are acting like a relay -- also no. */
return 1;
}
-/** Helper for directory_initiate_command_routerstatus: send the
- * command to a server whose address is <b>address</b>, whose IP is
- * <b>addr</b>, whose directory port is <b>dir_port</b>, whose tor version
- * <b>supports_begindir</b>, and whose identity key digest is
- * <b>digest</b>. */
+/** Helper for directory_initiate_command_rend: send the
+ * command to a server whose OR address/port is <b>or_addr</b>/<b>or_port</b>,
+ * whose directory address/port is <b>dir_addr</b>/<b>dir_port</b>, whose
+ * identity key digest is <b>digest</b>, with purposes <b>dir_purpose</b> and
+ * <b>router_purpose</b>, making an (in)direct connection as specified in
+ * <b>indirection</b>, with command <b>resource</b>, <b>payload</b> of
+ * <b>payload_len</b>, and asking for a result only <b>if_modified_since</b>.
+ */
void
-directory_initiate_command(const tor_addr_t *_addr,
- uint16_t or_port, uint16_t dir_port,
+directory_initiate_command(const tor_addr_t *or_addr, uint16_t or_port,
+ const tor_addr_t *dir_addr, uint16_t dir_port,
const char *digest,
uint8_t dir_purpose, uint8_t router_purpose,
dir_indirection_t indirection, const char *resource,
const char *payload, size_t payload_len,
time_t if_modified_since)
{
- directory_initiate_command_rend(_addr, or_port, dir_port,
+ tor_addr_port_t or_ap, dir_ap;
+
+ /* Use the null tor_addr and 0 port if the address or port isn't valid. */
+ if (tor_addr_port_is_valid(or_addr, or_port, 0)) {
+ tor_addr_copy(&or_ap.addr, or_addr);
+ or_ap.port = or_port;
+ } else {
+ /* the family doesn't matter here, so make it IPv4 */
+ tor_addr_make_null(&or_ap.addr, AF_INET);
+ or_ap.port = or_port = 0;
+ }
+
+ if (tor_addr_port_is_valid(dir_addr, dir_port, 0)) {
+ tor_addr_copy(&dir_ap.addr, dir_addr);
+ dir_ap.port = dir_port;
+ } else {
+ /* the family doesn't matter here, so make it IPv4 */
+ tor_addr_make_null(&dir_ap.addr, AF_INET);
+ dir_ap.port = dir_port = 0;
+ }
+
+ directory_initiate_command_rend(&or_ap, &dir_ap,
digest, dir_purpose,
router_purpose, indirection,
resource, payload, payload_len,
@@ -911,10 +1036,11 @@ is_sensitive_dir_purpose(uint8_t dir_purpose)
}
/** Same as directory_initiate_command(), but accepts rendezvous data to
- * fetch a hidden service descriptor. */
+ * fetch a hidden service descriptor, and takes its address & port arguments
+ * as tor_addr_port_t. */
static void
-directory_initiate_command_rend(const tor_addr_t *_addr,
- uint16_t or_port, uint16_t dir_port,
+directory_initiate_command_rend(const tor_addr_port_t *or_addr_port,
+ const tor_addr_port_t *dir_addr_port,
const char *digest,
uint8_t dir_purpose, uint8_t router_purpose,
dir_indirection_t indirection,
@@ -923,23 +1049,37 @@ directory_initiate_command_rend(const tor_addr_t *_addr,
time_t if_modified_since,
const rend_data_t *rend_query)
{
+ tor_assert(or_addr_port);
+ tor_assert(dir_addr_port);
+ tor_assert(or_addr_port->port || dir_addr_port->port);
+ tor_assert(digest);
+
dir_connection_t *conn;
const or_options_t *options = get_options();
int socket_error = 0;
- int use_begindir = directory_command_should_use_begindir(options, _addr,
- or_port, router_purpose, indirection);
+ const int use_begindir = directory_command_should_use_begindir(options,
+ &or_addr_port->addr, or_addr_port->port,
+ router_purpose, indirection);
const int anonymized_connection = dirind_is_anon(indirection);
- tor_addr_t addr;
-
- tor_assert(_addr);
- tor_assert(or_port || dir_port);
- tor_assert(digest);
+ const int or_connection = use_begindir || anonymized_connection;
- tor_addr_copy(&addr, _addr);
+ tor_addr_t addr;
+ tor_addr_copy(&addr, &(or_connection ? or_addr_port : dir_addr_port)->addr);
+ uint16_t port = (or_connection ? or_addr_port : dir_addr_port)->port;
+ uint16_t dir_port = dir_addr_port->port;
log_debug(LD_DIR, "anonymized %d, use_begindir %d.",
anonymized_connection, use_begindir);
+ if (!dir_port && !use_begindir) {
+ char ipaddr[TOR_ADDR_BUF_LEN];
+ tor_addr_to_str(ipaddr, &addr, TOR_ADDR_BUF_LEN, 0);
+ log_warn(LD_BUG, "Cannot use directory server without dirport or "
+ "begindir! Address: %s, DirPort: %d, Connection Port: %d",
+ escaped_safe_str_client(ipaddr), dir_port, port);
+ return;
+ }
+
log_debug(LD_DIR, "Initiating %s", dir_conn_purpose_to_string(dir_purpose));
#ifndef NON_ANONYMOUS_MODE_ENABLED
@@ -951,18 +1091,37 @@ directory_initiate_command_rend(const tor_addr_t *_addr,
/* ensure that we don't make direct connections when a SOCKS server is
* configured. */
- if (!anonymized_connection && !use_begindir && !options->HTTPProxy &&
+ if (!or_connection && !options->HTTPProxy &&
(options->Socks4Proxy || options->Socks5Proxy)) {
log_warn(LD_DIR, "Cannot connect to a directory server through a "
"SOCKS proxy!");
return;
}
+ if (or_connection && (!or_addr_port->port
+ || tor_addr_is_null(&or_addr_port->addr))) {
+ log_warn(LD_DIR, "Cannot make an OR connection without an OR port.");
+ log_backtrace(LOG_WARN, LD_BUG, "Address came from");
+ return;
+ } else if (!or_connection && (!dir_addr_port->port
+ || tor_addr_is_null(&dir_addr_port->addr))) {
+ log_warn(LD_DIR, "Cannot make a Dir connection without a Dir port.");
+ log_backtrace(LOG_WARN, LD_BUG, "Address came from");
+
+ return;
+ }
+
+ /* ensure we don't make excess connections when we're already downloading
+ * a consensus during bootstrap */
+ if (connection_dir_avoid_extra_connection_for_purpose(dir_purpose)) {
+ return;
+ }
+
conn = dir_connection_new(tor_addr_family(&addr));
/* set up conn so it's got all the data we need to remember */
tor_addr_copy(&conn->base_.addr, &addr);
- conn->base_.port = use_begindir ? or_port : dir_port;
+ conn->base_.port = port;
conn->base_.address = tor_dup_addr(&addr);
memcpy(conn->identity_digest, digest, DIGEST_LEN);
@@ -980,7 +1139,7 @@ directory_initiate_command_rend(const tor_addr_t *_addr,
if (rend_query)
conn->rend_data = rend_data_dup(rend_query);
- if (!anonymized_connection && !use_begindir) {
+ if (!or_connection) {
/* then we want to connect to dirport directly */
if (options->HTTPProxy) {
@@ -991,16 +1150,16 @@ directory_initiate_command_rend(const tor_addr_t *_addr,
switch (connection_connect(TO_CONN(conn), conn->base_.address, &addr,
dir_port, &socket_error)) {
case -1:
- connection_dir_request_failed(conn); /* retry if we want */
- /* XXX we only pass 'conn' above, not 'resource', 'payload',
- * etc. So in many situations it can't retry! -RD */
- connection_free(TO_CONN(conn));
+ connection_mark_for_close(TO_CONN(conn));
return;
case 1:
/* start flushing conn */
conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING;
/* fall through */
case 0:
+ if (connection_dir_close_consensus_conn_if_extra(conn)) {
+ return;
+ }
/* queue the command on the outbuf */
directory_send_command(conn, dir_purpose, 1, resource,
payload, payload_len,
@@ -1044,6 +1203,9 @@ directory_initiate_command_rend(const tor_addr_t *_addr,
connection_mark_for_close(TO_CONN(conn));
return;
}
+ if (connection_dir_close_consensus_conn_if_extra(conn)) {
+ return;
+ }
conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING;
/* queue the command on the outbuf */
directory_send_command(conn, dir_purpose, 0, resource,
@@ -1138,6 +1300,23 @@ directory_get_consensus_url(const char *resource)
return url;
}
+/**
+ * Copies the ipv6 from source to destination, subject to buffer size limit
+ * size. If decorate is true, makes sure the copied address is decorated.
+ */
+static void
+copy_ipv6_address(char* destination, const char* source, size_t len,
+ int decorate) {
+ tor_assert(destination);
+ tor_assert(source);
+
+ if (decorate && source[0] != '[') {
+ tor_snprintf(destination, len, "[%s]", source);
+ } else {
+ strlcpy(destination, source, len);
+ }
+}
+
/** Queue an appropriate HTTP command on conn-\>outbuf. The other args
* are as in directory_initiate_command().
*/
@@ -1149,6 +1328,9 @@ directory_send_command(dir_connection_t *conn,
{
char proxystring[256];
char hoststring[128];
+ /* NEEDS to be the same size hoststring.
+ Will be decorated with brackets around it if it is ipv6. */
+ char decorated_address[128];
smartlist_t *headers = smartlist_new();
char *url;
char request[8192];
@@ -1161,12 +1343,20 @@ directory_send_command(dir_connection_t *conn,
if (resource)
conn->requested_resource = tor_strdup(resource);
+ /* decorate the ip address if it is ipv6 */
+ if (strchr(conn->base_.address, ':')) {
+ copy_ipv6_address(decorated_address, conn->base_.address,
+ sizeof(decorated_address), 1);
+ } else {
+ strlcpy(decorated_address, conn->base_.address, sizeof(decorated_address));
+ }
+
/* come up with a string for which Host: we want */
if (conn->base_.port == 80) {
- strlcpy(hoststring, conn->base_.address, sizeof(hoststring));
+ strlcpy(hoststring, decorated_address, sizeof(hoststring));
} else {
- tor_snprintf(hoststring, sizeof(hoststring),"%s:%d",
- conn->base_.address, conn->base_.port);
+ tor_snprintf(hoststring, sizeof(hoststring), "%s:%d",
+ decorated_address, conn->base_.port);
}
/* Format if-modified-since */
@@ -1574,7 +1764,7 @@ load_downloaded_routers(const char *body, smartlist_t *which,
added = router_load_routers_from_string(body, NULL, SAVED_NOWHERE, which,
descriptor_digests, buf);
- if (general)
+ if (added && general)
control_event_bootstrap(BOOTSTRAP_STATUS_LOADING_DESCRIPTORS,
count_loading_descriptors_progress());
return added;
@@ -1598,7 +1788,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
size_t body_len = 0, orig_len = 0;
int status_code;
time_t date_header = 0;
- long delta;
+ long apparent_skew;
compress_method_t compression;
int plausible;
int skewed = 0;
@@ -1657,28 +1847,15 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
* and the date header. (We used to check now-date_header, but that's
* inaccurate if we spend a lot of time downloading.)
*/
- delta = conn->base_.timestamp_lastwritten - date_header;
- if (labs(delta)>ALLOW_DIRECTORY_TIME_SKEW) {
- char dbuf[64];
+ apparent_skew = conn->base_.timestamp_lastwritten - date_header;
+ if (labs(apparent_skew)>ALLOW_DIRECTORY_TIME_SKEW) {
int trusted = router_digest_is_trusted_dir(conn->identity_digest);
- format_time_interval(dbuf, sizeof(dbuf), delta);
- log_fn(trusted ? LOG_WARN : LOG_INFO,
- LD_HTTP,
- "Received directory with skewed time (server '%s:%d'): "
- "It seems that our clock is %s by %s, or that theirs is %s. "
- "Tor requires an accurate clock to work: please check your time, "
- "timezone, and date settings.",
- conn->base_.address, conn->base_.port,
- delta>0 ? "ahead" : "behind", dbuf,
- delta>0 ? "behind" : "ahead");
+ clock_skew_warning(TO_CONN(conn), apparent_skew, trusted, LD_HTTP,
+ "directory", "DIRSERV");
skewed = 1; /* don't check the recommended-versions line */
- if (trusted)
- control_event_general_status(LOG_WARN,
- "CLOCK_SKEW SKEW=%ld SOURCE=DIRSERV:%s:%d",
- delta, conn->base_.address, conn->base_.port);
} else {
log_debug(LD_HTTP, "Time on received directory is within tolerance; "
- "we are %ld seconds skewed. (That's okay.)", delta);
+ "we are %ld seconds skewed. (That's okay.)", apparent_skew);
}
}
(void) skewed; /* skewed isn't used yet. */
@@ -1788,7 +1965,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
routers_update_all_from_networkstatus(now, 3);
update_microdescs_from_networkstatus(now);
update_microdesc_downloads(now);
- directory_info_has_arrived(now, 0);
+ directory_info_has_arrived(now, 0, 0);
log_info(LD_DIR, "Successfully loaded consensus.");
}
@@ -1824,7 +2001,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
* ones got flushed to disk so it's safe to call this on them */
connection_dir_download_cert_failed(conn, status_code);
} else {
- directory_info_has_arrived(now, 0);
+ directory_info_has_arrived(now, 0, 0);
log_info(LD_DIR, "Successfully loaded certificates from fetch.");
}
} else {
@@ -1938,7 +2115,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
if (load_downloaded_routers(body, which, descriptor_digests,
conn->router_purpose,
conn->base_.address))
- directory_info_has_arrived(now, 0);
+ directory_info_has_arrived(now, 0, 0);
}
}
if (which) { /* mark remaining ones as failed */
@@ -1989,8 +2166,11 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
/* Mark remaining ones as failed. */
dir_microdesc_download_failed(which, status_code);
}
- control_event_bootstrap(BOOTSTRAP_STATUS_LOADING_DESCRIPTORS,
- count_loading_descriptors_progress());
+ if (mds && smartlist_len(mds)) {
+ control_event_bootstrap(BOOTSTRAP_STATUS_LOADING_DESCRIPTORS,
+ count_loading_descriptors_progress());
+ directory_info_has_arrived(now, 0, 1);
+ }
SMARTLIST_FOREACH(which, char *, cp, tor_free(cp));
smartlist_free(which);
smartlist_free(mds);
@@ -2186,17 +2366,23 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
if (conn->base_.purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2) {
#define SEND_HS_DESC_UPLOAD_FAILED_EVENT(reason) ( \
- control_event_hs_descriptor_upload_failed(conn->identity_digest, \
- reason) )
+ control_event_hs_descriptor_upload_failed( \
+ conn->identity_digest, \
+ conn->rend_data->onion_address, \
+ reason) )
log_info(LD_REND,"Uploaded rendezvous descriptor (status %d "
"(%s))",
status_code, escaped(reason));
+ /* Without the rend data, we'll have a problem identifying what has been
+ * uploaded for which service. */
+ tor_assert(conn->rend_data);
switch (status_code) {
case 200:
log_info(LD_REND,
"Uploading rendezvous descriptor: finished with status "
"200 (%s)", escaped(reason));
- control_event_hs_descriptor_uploaded(conn->identity_digest);
+ control_event_hs_descriptor_uploaded(conn->identity_digest,
+ conn->rend_data->onion_address);
rend_service_desc_has_uploaded(conn->rend_data);
break;
case 400:
@@ -2593,7 +2779,7 @@ client_likes_consensus(networkstatus_t *v, const char *want_url)
/** Return the compression level we should use for sending a compressed
* response of size <b>n_bytes</b>. */
-static zlib_compression_level_t
+STATIC zlib_compression_level_t
choose_compression_level(ssize_t n_bytes)
{
if (! have_been_under_memory_pressure()) {
@@ -2614,7 +2800,7 @@ choose_compression_level(ssize_t n_bytes)
* service descriptor. On finding one, write a response into
* conn-\>outbuf. If the request is unrecognized, send a 400.
* Always return 0. */
-static int
+STATIC int
directory_handle_command_get(dir_connection_t *conn, const char *headers,
const char *req_body, size_t req_body_len)
{
@@ -2874,7 +3060,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
});
if (global_write_bucket_low(TO_CONN(conn), estimated_len, 2)) {
- write_http_status_line(conn, 503, "Directory busy, try again later.");
+ write_http_status_line(conn, 503, "Directory busy, try again later");
goto vote_done;
}
write_http_response_header(conn, body_len ? body_len : -1, compressed,
@@ -3071,7 +3257,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
len += c->cache_info.signed_descriptor_len);
if (global_write_bucket_low(TO_CONN(conn), compressed?len/2:len, 2)) {
- write_http_status_line(conn, 503, "Directory busy, try again later.");
+ write_http_status_line(conn, 503, "Directory busy, try again later");
goto keys_done;
}
@@ -3398,7 +3584,7 @@ connection_dir_finished_flushing(dir_connection_t *conn)
tor_assert(conn->base_.type == CONN_TYPE_DIR);
/* Note that we have finished writing the directory response. For direct
- * connections this means we're done, for tunneled connections its only
+ * connections this means we're done; for tunneled connections it's only
* an intermediate step. */
if (conn->dirreq_id)
geoip_change_dirreq_state(conn->dirreq_id, DIRREQ_TUNNELED,
@@ -3439,8 +3625,205 @@ connection_dir_finished_flushing(dir_connection_t *conn)
return 0;
}
+/* A helper function for connection_dir_close_consensus_conn_if_extra()
+ * and connection_dir_close_extra_consensus_conns() that returns 0 if
+ * we can't have, or don't want to close, excess consensus connections. */
+STATIC int
+connection_dir_would_close_consensus_conn_helper(void)
+{
+ const or_options_t *options = get_options();
+
+ /* we're only interested in closing excess connections if we could
+ * have created any in the first place */
+ if (!networkstatus_consensus_can_use_multiple_directories(options)) {
+ return 0;
+ }
+
+ /* We want to close excess connections downloading a consensus.
+ * If there aren't any excess, we don't have anything to close. */
+ if (!networkstatus_consensus_has_excess_connections()) {
+ return 0;
+ }
+
+ /* If we have excess connections, but none of them are downloading a
+ * consensus, and we are still bootstrapping (that is, we have no usable
+ * consensus), we don't want to close any until one starts downloading. */
+ if (!networkstatus_consensus_is_downloading_usable_flavor()
+ && networkstatus_consensus_is_boostrapping(time(NULL))) {
+ return 0;
+ }
+
+ /* If we have just stopped bootstrapping (that is, just parsed a consensus),
+ * we might still have some excess connections hanging around. So we still
+ * have to check if we want to close any, even if we've stopped
+ * bootstrapping. */
+ return 1;
+}
+
+/* Check if we would close excess consensus connections. If we would, any
+ * new consensus connection would become excess immediately, so return 1.
+ * Otherwise, return 0. */
+int
+connection_dir_avoid_extra_connection_for_purpose(unsigned int purpose)
+{
+ const or_options_t *options = get_options();
+
+ /* We're not interested in connections that aren't fetching a consensus. */
+ if (purpose != DIR_PURPOSE_FETCH_CONSENSUS) {
+ return 0;
+ }
+
+ /* we're only interested in avoiding excess connections if we could
+ * have created any in the first place */
+ if (!networkstatus_consensus_can_use_multiple_directories(options)) {
+ return 0;
+ }
+
+ /* If there are connections downloading a consensus, and we are still
+ * bootstrapping (that is, we have no usable consensus), we can be sure that
+ * any further connections would be excess. */
+ if (networkstatus_consensus_is_downloading_usable_flavor()
+ && networkstatus_consensus_is_boostrapping(time(NULL))) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Check if we have excess consensus download connection attempts, and close
+ * conn:
+ * - if we don't have a consensus, and we're downloading a consensus, and conn
+ * is not downloading a consensus yet, close it;
+ * - if we do have a consensus, conn is excess, close it. */
+int
+connection_dir_close_consensus_conn_if_extra(dir_connection_t *conn)
+{
+ tor_assert(conn);
+ tor_assert(conn->base_.type == CONN_TYPE_DIR);
+
+ /* We're not interested in connections that aren't fetching a consensus. */
+ if (conn->base_.purpose != DIR_PURPOSE_FETCH_CONSENSUS) {
+ return 0;
+ }
+
+ /* The connection has already been closed */
+ if (conn->base_.marked_for_close) {
+ return 0;
+ }
+
+ if (!connection_dir_would_close_consensus_conn_helper()) {
+ return 0;
+ }
+
+ const int we_are_bootstrapping = networkstatus_consensus_is_boostrapping(
+ time(NULL));
+
+ /* We don't want to check other connections to see if they are downloading,
+ * as this is prone to race-conditions. So leave it for
+ * connection_dir_consider_close_extra_consensus_conns() to clean up.
+ *
+ * But if conn has just started connecting, or we have a consensus already,
+ * we can be sure it's not needed any more. */
+ if (!we_are_bootstrapping
+ || conn->base_.state == DIR_CONN_STATE_CONNECTING) {
+ connection_close_immediate(&conn->base_);
+ connection_mark_for_close(&conn->base_);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Check if we have excess consensus download connection attempts, and close
+ * them:
+ * - if we don't have a consensus, and we're downloading a consensus, keep an
+ * earlier connection, or a connection to a fallback directory, and close
+ * all other connections;
+ * - if we do have a consensus, close all connections: they are all excess. */
+void
+connection_dir_close_extra_consensus_conns(void)
+{
+ if (!connection_dir_would_close_consensus_conn_helper()) {
+ return;
+ }
+
+ int we_are_bootstrapping = networkstatus_consensus_is_boostrapping(
+ time(NULL));
+
+ const char *usable_resource = networkstatus_get_flavor_name(
+ usable_consensus_flavor());
+ smartlist_t *consens_usable_conns =
+ connection_dir_list_by_purpose_and_resource(
+ DIR_PURPOSE_FETCH_CONSENSUS,
+ usable_resource);
+
+ /* If we want to keep a connection that's downloading, find a connection to
+ * keep, favouring:
+ * - connections opened earlier (they are likely to have progressed further)
+ * - connections to fallbacks (to reduce the load on authorities) */
+ dir_connection_t *kept_download_conn = NULL;
+ int kept_is_authority = 0;
+ if (we_are_bootstrapping) {
+ SMARTLIST_FOREACH_BEGIN(consens_usable_conns,
+ dir_connection_t *, d) {
+ tor_assert(d);
+ int d_is_authority = router_digest_is_trusted_dir(d->identity_digest);
+ /* keep the first connection that is past the connecting state, but
+ * prefer fallbacks. */
+ if (d->base_.state != DIR_CONN_STATE_CONNECTING) {
+ if (!kept_download_conn || (kept_is_authority && !d_is_authority)) {
+ kept_download_conn = d;
+ kept_is_authority = d_is_authority;
+ /* we've found the earliest fallback, and want to keep it regardless
+ * of any other connections */
+ if (!kept_is_authority)
+ break;
+ }
+ }
+ } SMARTLIST_FOREACH_END(d);
+ }
+
+ SMARTLIST_FOREACH_BEGIN(consens_usable_conns,
+ dir_connection_t *, d) {
+ tor_assert(d);
+ /* don't close this connection if it's the one we want to keep */
+ if (kept_download_conn && d == kept_download_conn)
+ continue;
+ /* mark all other connections for close */
+ if (!d->base_.marked_for_close) {
+ connection_close_immediate(&d->base_);
+ connection_mark_for_close(&d->base_);
+ }
+ } SMARTLIST_FOREACH_END(d);
+
+ smartlist_free(consens_usable_conns);
+ consens_usable_conns = NULL;
+
+ /* make sure we've closed all excess connections */
+ const int final_connecting_conn_count =
+ connection_dir_count_by_purpose_resource_and_state(
+ DIR_PURPOSE_FETCH_CONSENSUS,
+ usable_resource,
+ DIR_CONN_STATE_CONNECTING);
+ if (final_connecting_conn_count > 0) {
+ log_warn(LD_BUG, "Expected 0 consensus connections connecting after "
+ "cleanup, got %d.", final_connecting_conn_count);
+ }
+ const int expected_final_conn_count = (we_are_bootstrapping ? 1 : 0);
+ const int final_conn_count =
+ connection_dir_count_by_purpose_and_resource(
+ DIR_PURPOSE_FETCH_CONSENSUS,
+ usable_resource);
+ if (final_conn_count > expected_final_conn_count) {
+ log_warn(LD_BUG, "Expected %d consensus connections after cleanup, got "
+ "%d.", expected_final_conn_count, final_connecting_conn_count);
+ }
+}
+
/** Connected handler for directory connections: begin sending data to the
- * server */
+ * server, and return 0, or, if the connection is an excess bootstrap
+ * connection, close all excess bootstrap connections.
+ * Only used when connections don't immediately connect. */
int
connection_dir_finished_connecting(dir_connection_t *conn)
{
@@ -3451,31 +3834,63 @@ connection_dir_finished_connecting(dir_connection_t *conn)
log_debug(LD_HTTP,"Dir connection to router %s:%u established.",
conn->base_.address,conn->base_.port);
- conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING; /* start flushing conn */
+ if (connection_dir_close_consensus_conn_if_extra(conn)) {
+ return -1;
+ }
+
+ /* start flushing conn */
+ conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING;
return 0;
}
/** Decide which download schedule we want to use based on descriptor type
- * in <b>dls</b> and whether we are acting as directory <b>server</b>, and
- * then return a list of int pointers defining download delays in seconds.
- * Helper function for download_status_increment_failure() and
- * download_status_reset(). */
-static const smartlist_t *
-find_dl_schedule_and_len(download_status_t *dls, int server)
+ * in <b>dls</b> and <b>options</b>.
+ * Then return a list of int pointers defining download delays in seconds.
+ * Helper function for download_status_increment_failure(),
+ * download_status_reset(), and download_status_increment_attempt(). */
+STATIC const smartlist_t *
+find_dl_schedule(download_status_t *dls, const or_options_t *options)
{
+ const int dir_server = dir_server_mode(options);
+ const int multi_d = networkstatus_consensus_can_use_multiple_directories(
+ options);
+ const int we_are_bootstrapping = networkstatus_consensus_is_boostrapping(
+ time(NULL));
+ const int use_fallbacks = networkstatus_consensus_can_use_extra_fallbacks(
+ options);
switch (dls->schedule) {
case DL_SCHED_GENERIC:
- if (server)
- return get_options()->TestingServerDownloadSchedule;
- else
- return get_options()->TestingClientDownloadSchedule;
+ if (dir_server) {
+ return options->TestingServerDownloadSchedule;
+ } else {
+ return options->TestingClientDownloadSchedule;
+ }
case DL_SCHED_CONSENSUS:
- if (server)
- return get_options()->TestingServerConsensusDownloadSchedule;
- else
- return get_options()->TestingClientConsensusDownloadSchedule;
+ if (!multi_d) {
+ return options->TestingServerConsensusDownloadSchedule;
+ } else {
+ if (we_are_bootstrapping) {
+ if (!use_fallbacks) {
+ /* A bootstrapping client without extra fallback directories */
+ return
+ options->TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule;
+ } else if (dls->want_authority) {
+ /* A bootstrapping client with extra fallback directories, but
+ * connecting to an authority */
+ return
+ options->TestingClientBootstrapConsensusAuthorityDownloadSchedule;
+ } else {
+ /* A bootstrapping client connecting to extra fallback directories
+ */
+ return
+ options->TestingClientBootstrapConsensusFallbackDownloadSchedule;
+ }
+ } else {
+ return options->TestingClientConsensusDownloadSchedule;
+ }
+ }
case DL_SCHED_BRIDGE:
- return get_options()->TestingBridgeDownloadSchedule;
+ return options->TestingBridgeDownloadSchedule;
default:
tor_assert(0);
}
@@ -3484,54 +3899,168 @@ find_dl_schedule_and_len(download_status_t *dls, int server)
return NULL;
}
-/** Called when an attempt to download <b>dls</b> has failed with HTTP status
+/* Find the current delay for dls based on schedule.
+ * Set dls->next_attempt_at based on now, and return the delay.
+ * Helper for download_status_increment_failure and
+ * download_status_increment_attempt. */
+STATIC int
+download_status_schedule_get_delay(download_status_t *dls,
+ const smartlist_t *schedule,
+ time_t now)
+{
+ tor_assert(dls);
+ tor_assert(schedule);
+
+ int delay = INT_MAX;
+ uint8_t dls_schedule_position = (dls->increment_on
+ == DL_SCHED_INCREMENT_ATTEMPT
+ ? dls->n_download_attempts
+ : dls->n_download_failures);
+
+ if (dls_schedule_position < smartlist_len(schedule))
+ delay = *(int *)smartlist_get(schedule, dls_schedule_position);
+ else if (dls_schedule_position == IMPOSSIBLE_TO_DOWNLOAD)
+ delay = INT_MAX;
+ else
+ delay = *(int *)smartlist_get(schedule, smartlist_len(schedule) - 1);
+
+ /* A negative delay makes no sense. Knowing that delay is
+ * non-negative allows us to safely do the wrapping check below. */
+ tor_assert(delay >= 0);
+
+ /* Avoid now+delay overflowing INT_MAX, by comparing with a subtraction
+ * that won't overflow (since delay is non-negative). */
+ if (delay < INT_MAX && now <= INT_MAX - delay) {
+ dls->next_attempt_at = now+delay;
+ } else {
+ dls->next_attempt_at = TIME_MAX;
+ }
+
+ return delay;
+}
+
+/* Log a debug message about item, which increments on increment_action, has
+ * incremented dls_n_download_increments times. The message varies based on
+ * was_schedule_incremented (if not, not_incremented_response is logged), and
+ * the values of increment, dls_next_attempt_at, and now.
+ * Helper for download_status_increment_failure and
+ * download_status_increment_attempt. */
+static void
+download_status_log_helper(const char *item, int was_schedule_incremented,
+ const char *increment_action,
+ const char *not_incremented_response,
+ uint8_t dls_n_download_increments, int increment,
+ time_t dls_next_attempt_at, time_t now)
+{
+ if (item) {
+ if (!was_schedule_incremented)
+ log_debug(LD_DIR, "%s %s %d time(s); I'll try again %s.",
+ item, increment_action, (int)dls_n_download_increments,
+ not_incremented_response);
+ else if (increment == 0)
+ log_debug(LD_DIR, "%s %s %d time(s); I'll try again immediately.",
+ item, increment_action, (int)dls_n_download_increments);
+ else if (dls_next_attempt_at < TIME_MAX)
+ log_debug(LD_DIR, "%s %s %d time(s); I'll try again in %d seconds.",
+ item, increment_action, (int)dls_n_download_increments,
+ (int)(dls_next_attempt_at-now));
+ else
+ log_debug(LD_DIR, "%s %s %d time(s); Giving up for a while.",
+ item, increment_action, (int)dls_n_download_increments);
+ }
+}
+
+/** Determine when a failed download attempt should be retried.
+ * Called when an attempt to download <b>dls</b> has failed with HTTP status
* <b>status_code</b>. Increment the failure count (if the code indicates a
- * real failure) and set <b>dls</b>-\>next_attempt_at to an appropriate time
- * in the future. */
+ * real failure, or if we're a server) and set <b>dls</b>-\>next_attempt_at to
+ * an appropriate time in the future and return it.
+ * If <b>dls->increment_on</b> is DL_SCHED_INCREMENT_ATTEMPT, increment the
+ * failure count, and return a time in the far future for the next attempt (to
+ * avoid an immediate retry). */
time_t
download_status_increment_failure(download_status_t *dls, int status_code,
const char *item, int server, time_t now)
{
- const smartlist_t *schedule;
- int increment;
+ int increment = -1;
tor_assert(dls);
+
+ /* only count the failure if it's permanent, or we're a server */
if (status_code != 503 || server) {
if (dls->n_download_failures < IMPOSSIBLE_TO_DOWNLOAD-1)
++dls->n_download_failures;
}
- schedule = find_dl_schedule_and_len(dls, server);
+ if (dls->increment_on == DL_SCHED_INCREMENT_FAILURE) {
+ /* We don't find out that a failure-based schedule has attempted a
+ * connection until that connection fails.
+ * We'll never find out about successful connections, but this doesn't
+ * matter, because schedules are reset after a successful download.
+ */
+ if (dls->n_download_attempts < IMPOSSIBLE_TO_DOWNLOAD-1)
+ ++dls->n_download_attempts;
- if (dls->n_download_failures < smartlist_len(schedule))
- increment = *(int *)smartlist_get(schedule, dls->n_download_failures);
- else if (dls->n_download_failures == IMPOSSIBLE_TO_DOWNLOAD)
- increment = INT_MAX;
- else
- increment = *(int *)smartlist_get(schedule, smartlist_len(schedule) - 1);
+ /* only return a failure retry time if this schedule increments on failures
+ */
+ const smartlist_t *schedule = find_dl_schedule(dls, get_options());
+ increment = download_status_schedule_get_delay(dls, schedule, now);
+ }
- if (increment < INT_MAX)
- dls->next_attempt_at = now+increment;
- else
- dls->next_attempt_at = TIME_MAX;
+ download_status_log_helper(item, !dls->increment_on, "failed",
+ "concurrently", dls->n_download_failures,
+ increment, dls->next_attempt_at, now);
- if (item) {
- if (increment == 0)
- log_debug(LD_DIR, "%s failed %d time(s); I'll try again immediately.",
- item, (int)dls->n_download_failures);
- else if (dls->next_attempt_at < TIME_MAX)
- log_debug(LD_DIR, "%s failed %d time(s); I'll try again in %d seconds.",
- item, (int)dls->n_download_failures,
- (int)(dls->next_attempt_at-now));
- else
- log_debug(LD_DIR, "%s failed %d time(s); Giving up for a while.",
- item, (int)dls->n_download_failures);
+ if (dls->increment_on == DL_SCHED_INCREMENT_ATTEMPT) {
+ /* stop this schedule retrying on failure, it will launch concurrent
+ * connections instead */
+ return TIME_MAX;
+ } else {
+ return dls->next_attempt_at;
}
+}
+
+/** Determine when the next download attempt should be made when using an
+ * attempt-based (potentially concurrent) download schedule.
+ * Called when an attempt to download <b>dls</b> is being initiated.
+ * Increment the attempt count and set <b>dls</b>-\>next_attempt_at to an
+ * appropriate time in the future and return it.
+ * If <b>dls->increment_on</b> is DL_SCHED_INCREMENT_FAILURE, don't increment
+ * the attempts, and return a time in the far future (to avoid launching a
+ * concurrent attempt). */
+time_t
+download_status_increment_attempt(download_status_t *dls, const char *item,
+ time_t now)
+{
+ int delay = -1;
+ tor_assert(dls);
+
+ if (dls->increment_on == DL_SCHED_INCREMENT_FAILURE) {
+ /* this schedule should retry on failure, and not launch any concurrent
+ attempts */
+ log_info(LD_BUG, "Tried to launch an attempt-based connection on a "
+ "failure-based schedule.");
+ return TIME_MAX;
+ }
+
+ if (dls->n_download_attempts < IMPOSSIBLE_TO_DOWNLOAD-1)
+ ++dls->n_download_attempts;
+
+ const smartlist_t *schedule = find_dl_schedule(dls, get_options());
+ delay = download_status_schedule_get_delay(dls, schedule, now);
+
+ download_status_log_helper(item, dls->increment_on, "attempted",
+ "on failure", dls->n_download_attempts,
+ delay, dls->next_attempt_at, now);
+
return dls->next_attempt_at;
}
/** Reset <b>dls</b> so that it will be considered downloadable
* immediately, and/or to show that we don't need it anymore.
*
+ * Must be called to initialise a download schedule, otherwise the zeroth item
+ * in the schedule will never be used.
+ *
* (We find the zeroth element of the download schedule, and set
* next_attempt_at to be the appropriate offset from 'now'. In most
* cases this means setting it to 'now', so the item will be immediately
@@ -3540,14 +4069,16 @@ download_status_increment_failure(download_status_t *dls, int status_code,
void
download_status_reset(download_status_t *dls)
{
- if (dls->n_download_failures == IMPOSSIBLE_TO_DOWNLOAD)
+ if (dls->n_download_failures == IMPOSSIBLE_TO_DOWNLOAD
+ || dls->n_download_attempts == IMPOSSIBLE_TO_DOWNLOAD)
return; /* Don't reset this. */
- const smartlist_t *schedule = find_dl_schedule_and_len(
- dls, get_options()->DirPort_set);
+ const smartlist_t *schedule = find_dl_schedule(dls, get_options());
dls->n_download_failures = 0;
+ dls->n_download_attempts = 0;
dls->next_attempt_at = time(NULL) + *(int *)smartlist_get(schedule, 0);
+ /* Don't reset dls->want_authority or dls->increment_on */
}
/** Return the number of failures on <b>dls</b> since the last success (if
@@ -3558,6 +4089,22 @@ download_status_get_n_failures(const download_status_t *dls)
return dls->n_download_failures;
}
+/** Return the number of attempts to download <b>dls</b> since the last success
+ * (if any). This can differ from download_status_get_n_failures() due to
+ * outstanding concurrent attempts. */
+int
+download_status_get_n_attempts(const download_status_t *dls)
+{
+ return dls->n_download_attempts;
+}
+
+/** Return the next time to attempt to download <b>dls</b>. */
+time_t
+download_status_get_next_attempt_at(const download_status_t *dls)
+{
+ return dls->next_attempt_at;
+}
+
/** Called when one or more routerdesc (or extrainfo, if <b>was_extrainfo</b>)
* fetches have failed (with uppercase fingerprints listed in <b>failed</b>,
* either as descriptor digests or as identity digests based on
diff --git a/src/or/directory.h b/src/or/directory.h
index 4899eb5c8c..2630705fdb 100644
--- a/src/or/directory.h
+++ b/src/or/directory.h
@@ -16,10 +16,12 @@ int directories_have_accepted_server_descriptor(void);
void directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
dirinfo_type_t type, const char *payload,
size_t payload_len, size_t extrainfo_len);
-MOCK_DECL(void, directory_get_from_dirserver, (uint8_t dir_purpose,
- uint8_t router_purpose,
- const char *resource,
- int pds_flags));
+MOCK_DECL(void, directory_get_from_dirserver, (
+ uint8_t dir_purpose,
+ uint8_t router_purpose,
+ const char *resource,
+ int pds_flags,
+ download_want_authority_t want_authority));
void directory_get_from_all_authorities(uint8_t dir_purpose,
uint8_t router_purpose,
const char *resource);
@@ -37,14 +39,16 @@ typedef enum {
DIRIND_ANON_DIRPORT,
} dir_indirection_t;
-void directory_initiate_command_routerstatus(const routerstatus_t *status,
- uint8_t dir_purpose,
- uint8_t router_purpose,
- dir_indirection_t indirection,
- const char *resource,
- const char *payload,
- size_t payload_len,
- time_t if_modified_since);
+MOCK_DECL(void, directory_initiate_command_routerstatus,
+ (const routerstatus_t *status,
+ uint8_t dir_purpose,
+ uint8_t router_purpose,
+ dir_indirection_t indirection,
+ const char *resource,
+ const char *payload,
+ size_t payload_len,
+ time_t if_modified_since));
+
void directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
uint8_t dir_purpose,
uint8_t router_purpose,
@@ -64,14 +68,17 @@ int connection_dir_process_inbuf(dir_connection_t *conn);
int connection_dir_finished_flushing(dir_connection_t *conn);
int connection_dir_finished_connecting(dir_connection_t *conn);
void connection_dir_about_to_close(dir_connection_t *dir_conn);
-void directory_initiate_command(const tor_addr_t *addr,
- uint16_t or_port, uint16_t dir_port,
+void directory_initiate_command(const tor_addr_t *or_addr, uint16_t or_port,
+ const tor_addr_t *dir_addr, uint16_t dir_port,
const char *digest,
uint8_t dir_purpose, uint8_t router_purpose,
dir_indirection_t indirection,
const char *resource,
const char *payload, size_t payload_len,
time_t if_modified_since);
+int connection_dir_avoid_extra_connection_for_purpose(unsigned int purpose);
+int connection_dir_close_consensus_conn_if_extra(dir_connection_t *conn);
+void connection_dir_close_extra_consensus_conns(void);
#define DSR_HEX (1<<0)
#define DSR_BASE64 (1<<1)
@@ -90,34 +97,41 @@ int router_supports_extrainfo(const char *identity_digest, int is_authority);
time_t download_status_increment_failure(download_status_t *dls,
int status_code, const char *item,
int server, time_t now);
+time_t download_status_increment_attempt(download_status_t *dls,
+ const char *item, time_t now);
/** Increment the failure count of the download_status_t <b>dls</b>, with
* the optional status code <b>sc</b>. */
#define download_status_failed(dls, sc) \
download_status_increment_failure((dls), (sc), NULL, \
- get_options()->DirPort_set, time(NULL))
+ dir_server_mode(get_options()), \
+ time(NULL))
void download_status_reset(download_status_t *dls);
static int download_status_is_ready(download_status_t *dls, time_t now,
int max_failures);
/** Return true iff, as of <b>now</b>, the resource tracked by <b>dls</b> is
* ready to get its download reattempted. */
-static INLINE int
+static inline int
download_status_is_ready(download_status_t *dls, time_t now,
int max_failures)
{
- return (dls->n_download_failures <= max_failures
- && dls->next_attempt_at <= now);
+ int under_failure_limit = (dls->n_download_failures <= max_failures
+ && dls->n_download_attempts <= max_failures);
+ return (under_failure_limit && dls->next_attempt_at <= now);
}
static void download_status_mark_impossible(download_status_t *dl);
/** Mark <b>dl</b> as never downloadable. */
-static INLINE void
+static inline void
download_status_mark_impossible(download_status_t *dl)
{
dl->n_download_failures = IMPOSSIBLE_TO_DOWNLOAD;
+ dl->n_download_attempts = IMPOSSIBLE_TO_DOWNLOAD;
}
int download_status_get_n_failures(const download_status_t *dls);
+int download_status_get_n_attempts(const download_status_t *dls);
+time_t download_status_get_next_attempt_at(const download_status_t *dls);
#ifdef TOR_UNIT_TESTS
/* Used only by directory.c and test_dir.c */
@@ -127,6 +141,21 @@ STATIC int purpose_needs_anonymity(uint8_t dir_purpose,
uint8_t router_purpose);
STATIC dirinfo_type_t dir_fetch_type(int dir_purpose, int router_purpose,
const char *resource);
+STATIC int directory_handle_command_get(dir_connection_t *conn,
+ const char *headers,
+ const char *req_body,
+ size_t req_body_len);
+STATIC int connection_dir_would_close_consensus_conn_helper(void);
+STATIC int download_status_schedule_get_delay(download_status_t *dls,
+ const smartlist_t *schedule,
+ time_t now);
+
+STATIC char* authdir_type_to_string(dirinfo_type_t auth);
+STATIC const char * dir_conn_purpose_to_string(int purpose);
+STATIC int should_use_directory_guards(const or_options_t *options);
+STATIC zlib_compression_level_t choose_compression_level(ssize_t n_bytes);
+STATIC const smartlist_t *find_dl_schedule(download_status_t *dls,
+ const or_options_t *options);
#endif
#endif
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index 8d9f166556..ee85cebf7e 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -797,7 +797,7 @@ list_single_server_status(const routerinfo_t *desc, int is_live)
}
/* DOCDOC running_long_enough_to_decide_unreachable */
-static INLINE int
+static inline int
running_long_enough_to_decide_unreachable(void)
{
return time_of_process_start
@@ -1091,13 +1091,13 @@ directory_fetches_from_authorities(const or_options_t *options)
return 1; /* we don't know our IP address; ask an authority. */
refuseunknown = ! router_my_exit_policy_is_reject_star() &&
should_refuse_unknown_exits(options);
- if (!options->DirPort_set && !refuseunknown)
+ if (!dir_server_mode(options) && !refuseunknown)
return 0;
if (!server_mode(options) || !advertised_server_mode())
return 0;
me = router_get_my_routerinfo();
- if (!me || (!me->dir_port && !refuseunknown))
- return 0; /* if dirport not advertised, return 0 too */
+ if (!me || (!me->supports_tunnelled_dir_requests && !refuseunknown))
+ return 0; /* if we don't service directory requests, return 0 too */
return 1;
}
@@ -1128,7 +1128,7 @@ directory_fetches_dir_info_later(const or_options_t *options)
int
directory_caches_unknown_auth_certs(const or_options_t *options)
{
- return options->DirPort_set || options->BridgeRelay;
+ return dir_server_mode(options) || options->BridgeRelay;
}
/** Return 1 if we want to keep descriptors, networkstatuses, etc around
@@ -1137,7 +1137,7 @@ directory_caches_unknown_auth_certs(const or_options_t *options)
int
directory_caches_dir_info(const or_options_t *options)
{
- if (options->BridgeRelay || options->DirPort_set)
+ if (options->BridgeRelay || dir_server_mode(options))
return 1;
if (!server_mode(options) || !advertised_server_mode())
return 0;
@@ -1153,7 +1153,7 @@ directory_caches_dir_info(const or_options_t *options)
int
directory_permits_begindir_requests(const or_options_t *options)
{
- return options->BridgeRelay != 0 || options->DirPort_set;
+ return options->BridgeRelay != 0 || dir_server_mode(options);
}
/** Return 1 if we have no need to fetch new descriptors. This generally
@@ -1230,7 +1230,7 @@ free_cached_dir_(void *_d)
void
dirserv_set_cached_consensus_networkstatus(const char *networkstatus,
const char *flavor_name,
- const digests_t *digests,
+ const common_digests_t *digests,
time_t published)
{
cached_dir_t *new_networkstatus;
@@ -1239,7 +1239,7 @@ dirserv_set_cached_consensus_networkstatus(const char *networkstatus,
cached_consensuses = strmap_new();
new_networkstatus = new_cached_dir(tor_strdup(networkstatus), published);
- memcpy(&new_networkstatus->digests, digests, sizeof(digests_t));
+ memcpy(&new_networkstatus->digests, digests, sizeof(common_digests_t));
old_networkstatus = strmap_set(cached_consensuses, flavor_name,
new_networkstatus);
if (old_networkstatus)
@@ -1302,7 +1302,7 @@ static uint32_t guard_bandwidth_excluding_exits_kb = 0;
/** Helper: estimate the uptime of a router given its stated uptime and the
* amount of time since it last stated its stated uptime. */
-static INLINE long
+static inline long
real_uptime(const routerinfo_t *router, time_t now)
{
if (now < router->cache_info.published_on)
@@ -1350,8 +1350,9 @@ dirserv_thinks_router_is_unreliable(time_t now,
}
/** Return true iff <b>router</b> should be assigned the "HSDir" flag.
+ *
* Right now this means it advertises support for it, it has a high uptime,
- * it has a DirPort open, it has the Stable and Fast flag and it's currently
+ * it's a directory cache, it has the Stable and Fast flags, and it's currently
* considered Running.
*
* This function needs to be called after router-\>is_running has
@@ -1378,7 +1379,8 @@ dirserv_thinks_router_is_hs_dir(const routerinfo_t *router,
else
uptime = real_uptime(router, now);
- return (router->wants_to_be_hs_dir && router->dir_port &&
+ return (router->wants_to_be_hs_dir &&
+ router->supports_tunnelled_dir_requests &&
node->is_stable && node->is_fast &&
uptime >= get_options()->MinUptimeHidServDirectoryV2 &&
router_is_active(router, node, now));
@@ -1921,7 +1923,7 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version,
rs->is_hs_dir?" HSDir":"",
rs->is_flagged_running?" Running":"",
rs->is_stable?" Stable":"",
- (rs->dir_port!=0)?" V2Dir":"",
+ rs->is_v2_dir?" V2Dir":"",
rs->is_valid?" Valid":"");
/* length of "opt v \n" */
@@ -2185,6 +2187,7 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs,
strlcpy(rs->nickname, ri->nickname, sizeof(rs->nickname));
rs->or_port = ri->or_port;
rs->dir_port = ri->dir_port;
+ rs->is_v2_dir = ri->supports_tunnelled_dir_requests;
if (options->AuthDirHasIPv6Connectivity == 1 &&
!tor_addr_is_null(&ri->ipv6_addr) &&
node->last_reachable6 >= now - REACHABLE_TIMEOUT) {
@@ -3100,7 +3103,7 @@ dirserv_get_routerdescs(smartlist_t *descs_out, const char *key,
DSR_HEX|DSR_SORT_UNIQ);
SMARTLIST_FOREACH_BEGIN(digests, const char *, d) {
if (router_digest_is_me(d)) {
- /* make sure desc_routerinfo exists */
+ /* calling router_get_my_routerinfo() to make sure it exists */
const routerinfo_t *ri = router_get_my_routerinfo();
if (ri)
smartlist_add(descs_out, (void*) &(ri->cache_info));
@@ -3132,7 +3135,7 @@ dirserv_get_routerdescs(smartlist_t *descs_out, const char *key,
* router listening at <b>address</b>:<b>or_port</b>, and has yielded
* a certificate with digest <b>digest_rcvd</b>.
*
- * Inform the reachability checker that we could get to this guy.
+ * Inform the reachability checker that we could get to this relay.
*/
void
dirserv_orconn_tls_done(const tor_addr_t *addr,
diff --git a/src/or/dirserv.h b/src/or/dirserv.h
index d07339bc12..df23450ef9 100644
--- a/src/or/dirserv.h
+++ b/src/or/dirserv.h
@@ -63,9 +63,9 @@ 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,
- const char *flavor_name,
- const digests_t *digests,
- time_t published);
+ const char *flavor_name,
+ const common_digests_t *digests,
+ time_t published);
void dirserv_clear_old_networkstatuses(time_t cutoff);
int dirserv_get_routerdesc_fingerprints(smartlist_t *fps_out, const char *key,
const char **msg,
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index d8e6ee2229..a81d1a96b1 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -54,7 +54,6 @@ static int dirvote_perform_vote(void);
static void dirvote_clear_votes(int all_votes);
static int dirvote_compute_consensuses(void);
static int dirvote_publish_consensus(void);
-static char *make_consensus_method_list(int low, int high, const char *sep);
/* =====
* Voting
@@ -564,7 +563,7 @@ consensus_method_is_supported(int method)
/** Return a newly allocated string holding the numbers between low and high
* (inclusive) that are supported consensus methods. */
-static char *
+STATIC char *
make_consensus_method_list(int low, int high, const char *separator)
{
char *list;
@@ -2115,14 +2114,14 @@ networkstatus_add_detached_signatures(networkstatus_t *target,
/** Make sure all the digests we know match, and at least one matches. */
{
- digests_t *digests = strmap_get(sigs->digests, flavor);
+ common_digests_t *digests = strmap_get(sigs->digests, flavor);
int n_matches = 0;
int alg;
if (!digests) {
*msg_out = "No digests for given consensus flavor";
return -1;
}
- for (alg = DIGEST_SHA1; alg < N_DIGEST_ALGORITHMS; ++alg) {
+ for (alg = DIGEST_SHA1; alg < N_COMMON_DIGEST_ALGORITHMS; ++alg) {
if (!tor_mem_is_zero(digests->d[alg], DIGEST256_LEN)) {
if (fast_memeq(target->digests.d[alg], digests->d[alg],
DIGEST256_LEN)) {
@@ -2315,7 +2314,7 @@ networkstatus_get_detached_signatures(smartlist_t *consensuses)
/* start with SHA256; we don't include SHA1 for anything but the basic
* consensus. */
- for (alg = DIGEST_SHA256; alg < N_DIGEST_ALGORITHMS; ++alg) {
+ for (alg = DIGEST_SHA256; alg < N_COMMON_DIGEST_ALGORITHMS; ++alg) {
char d[HEX_DIGEST256_LEN+1];
const char *alg_name =
crypto_digest_algorithm_get_name(alg);
@@ -3373,8 +3372,8 @@ dirvote_free_all(void)
* ==== */
/** Return the body of the consensus that we're currently trying to build. */
-const char *
-dirvote_get_pending_consensus(consensus_flavor_t flav)
+MOCK_IMPL(const char *,
+dirvote_get_pending_consensus, (consensus_flavor_t flav))
{
tor_assert(((int)flav) >= 0 && (int)flav < N_CONSENSUS_FLAVORS);
return pending_consensuses[flav].body;
@@ -3382,8 +3381,8 @@ dirvote_get_pending_consensus(consensus_flavor_t flav)
/** Return the signatures that we know for the consensus that we're currently
* trying to build. */
-const char *
-dirvote_get_pending_detached_signatures(void)
+MOCK_IMPL(const char *,
+dirvote_get_pending_detached_signatures, (void))
{
return pending_consensus_signatures;
}
diff --git a/src/or/dirvote.h b/src/or/dirvote.h
index dca8540870..cc526ea34e 100644
--- a/src/or/dirvote.h
+++ b/src/or/dirvote.h
@@ -136,8 +136,10 @@ int dirvote_add_signatures(const char *detached_signatures_body,
const char **msg_out);
/* Item access */
-const char *dirvote_get_pending_consensus(consensus_flavor_t flav);
-const char *dirvote_get_pending_detached_signatures(void);
+MOCK_DECL(const char*, dirvote_get_pending_consensus,
+ (consensus_flavor_t flav));
+MOCK_DECL(const char*, dirvote_get_pending_detached_signatures, (void));
+
#define DGV_BY_ID 1
#define DGV_INCLUDE_PENDING 2
#define DGV_INCLUDE_PREVIOUS 4
@@ -175,6 +177,7 @@ STATIC char *format_networkstatus_vote(crypto_pk_t *private_key,
STATIC char *dirvote_compute_params(smartlist_t *votes, int method,
int total_authorities);
STATIC char *compute_consensus_package_lines(smartlist_t *votes);
+STATIC char *make_consensus_method_list(int low, int high, const char *sep);
#endif
#endif
diff --git a/src/or/dns.c b/src/or/dns.c
index d71246d61e..3f5dfd2a8a 100644
--- a/src/or/dns.c
+++ b/src/or/dns.c
@@ -107,13 +107,9 @@ static void dns_found_answer(const char *address, uint8_t query_type,
const tor_addr_t *addr,
const char *hostname,
uint32_t ttl);
-static int launch_resolve(cached_resolve_t *resolve);
static void add_wildcarded_test_address(const char *address);
static int configure_nameservers(int force);
static int answer_is_wildcarded(const char *ip);
-static int set_exitconn_info_from_resolve(edge_connection_t *exitconn,
- const cached_resolve_t *resolve,
- char **hostname_out);
static int evdns_err_is_transient(int err);
static void inform_pending_connections(cached_resolve_t *resolve);
static void make_pending_resolve_cached(cached_resolve_t *cached);
@@ -138,7 +134,7 @@ static int dns_is_broken_for_ipv6 = 0;
/** Function to compare hashed resolves on their addresses; used to
* implement hash tables. */
-static INLINE int
+static inline int
cached_resolves_eq(cached_resolve_t *a, cached_resolve_t *b)
{
/* make this smarter one day? */
@@ -147,7 +143,7 @@ cached_resolves_eq(cached_resolve_t *a, cached_resolve_t *b)
}
/** Hash function for cached_resolve objects */
-static INLINE unsigned int
+static inline unsigned int
cached_resolve_hash(cached_resolve_t *a)
{
return (unsigned) siphash24g((const uint8_t*)a->address, strlen(a->address));
@@ -859,10 +855,10 @@ dns_resolve_impl,(edge_connection_t *exitconn, int is_resolve,
* Return -2 on a transient error, -1 on a permenent error, and 1 on
* a successful lookup.
*/
-static int
-set_exitconn_info_from_resolve(edge_connection_t *exitconn,
- const cached_resolve_t *resolve,
- char **hostname_out)
+MOCK_IMPL(STATIC int,
+set_exitconn_info_from_resolve,(edge_connection_t *exitconn,
+ const cached_resolve_t *resolve,
+ char **hostname_out))
{
int ipv4_ok, ipv6_ok, answer_with_ipv4, r;
uint32_t begincell_flags;
@@ -1130,7 +1126,7 @@ dns_cancel_pending_resolve,(const char *address))
/** Return true iff <b>address</b> is one of the addresses we use to verify
* that well-known sites aren't being hijacked by our DNS servers. */
-static INLINE int
+static inline int
is_test_address(const char *address)
{
const or_options_t *options = get_options();
@@ -1664,8 +1660,8 @@ launch_one_resolve(const char *address, uint8_t query_type,
/** For eventdns: start resolving as necessary to find the target for
* <b>exitconn</b>. Returns -1 on error, -2 on transient error,
* 0 on "resolve launched." */
-static int
-launch_resolve(cached_resolve_t *resolve)
+MOCK_IMPL(STATIC int,
+launch_resolve,(cached_resolve_t *resolve))
{
tor_addr_t a;
int r;
@@ -2118,5 +2114,18 @@ assert_cache_ok_(void)
}
});
}
+
#endif
+cached_resolve_t
+*dns_get_cache_entry(cached_resolve_t *query)
+{
+ return HT_FIND(cache_map, &cache_root, query);
+}
+
+void
+dns_insert_cache_entry(cached_resolve_t *new_entry)
+{
+ HT_INSERT(cache_map, &cache_root, new_entry);
+}
+
diff --git a/src/or/dns.h b/src/or/dns.h
index 6af7796dbb..c2778b216c 100644
--- a/src/or/dns.h
+++ b/src/or/dns.h
@@ -42,6 +42,18 @@ uint8_t answer_type,const cached_resolve_t *resolved));
MOCK_DECL(STATIC void,send_resolved_hostname_cell,(edge_connection_t *conn,
const char *hostname));
+
+cached_resolve_t *dns_get_cache_entry(cached_resolve_t *query);
+void dns_insert_cache_entry(cached_resolve_t *new_entry);
+
+MOCK_DECL(STATIC int,
+set_exitconn_info_from_resolve,(edge_connection_t *exitconn,
+ const cached_resolve_t *resolve,
+ char **hostname_out));
+
+MOCK_DECL(STATIC int,
+launch_resolve,(cached_resolve_t *resolve));
+
#endif
#endif
diff --git a/src/or/dnsserv.c b/src/or/dnsserv.c
index f7710908bd..ded0d8431b 100644
--- a/src/or/dnsserv.c
+++ b/src/or/dnsserv.c
@@ -125,6 +125,7 @@ evdns_server_callback(struct evdns_server_request *req, void *data_)
/* Make a new dummy AP connection, and attach the request to it. */
entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET);
conn = ENTRY_TO_EDGE_CONN(entry_conn);
+ CONNECTION_AP_EXPECT_NONPENDING(entry_conn);
TO_CONN(conn)->state = AP_CONN_STATE_RESOLVE_WAIT;
conn->is_dns_request = 1;
@@ -199,6 +200,7 @@ dnsserv_launch_request(const char *name, int reverse,
/* Make a new dummy AP connection, and attach the request to it. */
entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET);
conn = ENTRY_TO_EDGE_CONN(entry_conn);
+ CONNECTION_AP_EXPECT_NONPENDING(entry_conn);
conn->base_.state = AP_CONN_STATE_RESOLVE_WAIT;
tor_addr_copy(&TO_CONN(conn)->addr, &control_conn->base_.addr);
diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c
index ebf675166b..771a0ef377 100644
--- a/src/or/entrynodes.c
+++ b/src/or/entrynodes.c
@@ -87,7 +87,7 @@ get_entry_guards(void)
/** Check whether the entry guard <b>e</b> is usable, given the directory
* authorities' opinion about the router (stored in <b>ri</b>) and the user's
- * configuration (in <b>options</b>). Set <b>e</b>-&gt;bad_since
+ * configuration (in <b>options</b>). Set <b>e</b>->bad_since
* accordingly. Return true iff the entry guard's status changes.
*
* If it's not usable, set *<b>reason</b> to a static string explaining why.
@@ -117,6 +117,9 @@ entry_guard_set_status(entry_guard_t *e, const node_t *node,
*reason = "not recommended as a guard";
else if (routerset_contains_node(options->ExcludeNodes, node))
*reason = "excluded";
+ /* We only care about OR connection connectivity for entry guards. */
+ else if (!fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, 0))
+ *reason = "unreachable by config";
else if (e->path_bias_disabled)
*reason = "path-biased";
@@ -268,7 +271,7 @@ entry_is_live(const entry_guard_t *e, entry_is_live_flags_t flags,
*msg = "not fast/stable";
return NULL;
}
- if (!fascist_firewall_allows_node(node)) {
+ if (!fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, 0)) {
*msg = "unreachable by config";
return NULL;
}
@@ -918,7 +921,8 @@ entry_guards_set_from_config(const or_options_t *options)
} else if (routerset_contains_node(options->ExcludeNodes, node)) {
SMARTLIST_DEL_CURRENT(entry_nodes, node);
continue;
- } else if (!fascist_firewall_allows_node(node)) {
+ } else if (!fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION,
+ 0)) {
SMARTLIST_DEL_CURRENT(entry_nodes, node);
continue;
} else if (! node->is_possible_guard) {
@@ -1152,7 +1156,7 @@ choose_random_entry_impl(cpath_build_state_t *state, int for_directory,
} else {
/* Try to have at least 2 choices available. This way we don't
* get stuck with a single live-but-crummy entry and just keep
- * using him.
+ * using it.
* (We might get 2 live-but-crummy entry guards, but so be it.) */
preferred_min = 2;
}
@@ -2116,8 +2120,18 @@ launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge)
return;
}
- directory_initiate_command(&bridge->addr,
- bridge->port, 0/*no dirport*/,
+ /* Until we get a descriptor for the bridge, we only know one address for
+ * it. */
+ if (!fascist_firewall_allows_address_addr(&bridge->addr, bridge->port,
+ FIREWALL_OR_CONNECTION, 0, 0)) {
+ log_notice(LD_CONFIG, "Tried to fetch a descriptor directly from a "
+ "bridge, but that bridge is not reachable through our "
+ "firewall.");
+ return;
+ }
+
+ directory_initiate_command(&bridge->addr, bridge->port,
+ NULL, 0, /*no dirport*/
bridge->identity,
DIR_PURPOSE_FETCH_SERVERDESC,
ROUTER_PURPOSE_BRIDGE,
@@ -2178,7 +2192,9 @@ fetch_bridge_descriptors(const or_options_t *options, time_t now)
!options->UpdateBridgesFromAuthority, !num_bridge_auths);
if (ask_bridge_directly &&
- !fascist_firewall_allows_address_or(&bridge->addr, bridge->port)) {
+ !fascist_firewall_allows_address_addr(&bridge->addr, bridge->port,
+ FIREWALL_OR_CONNECTION, 0,
+ 0)) {
log_notice(LD_DIR, "Bridge at '%s' isn't reachable by our "
"firewall policy. %s.",
fmt_addrport(&bridge->addr, bridge->port),
@@ -2205,7 +2221,7 @@ fetch_bridge_descriptors(const or_options_t *options, time_t now)
log_info(LD_DIR, "Fetching bridge info '%s' from bridge authority.",
resource);
directory_get_from_dirserver(DIR_PURPOSE_FETCH_SERVERDESC,
- ROUTER_PURPOSE_BRIDGE, resource, 0);
+ ROUTER_PURPOSE_BRIDGE, resource, 0, DL_WANT_AUTHORITY);
}
}
SMARTLIST_FOREACH_END(bridge);
@@ -2226,6 +2242,7 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node)
* does so through an address from any source other than node_get_addr().
*/
tor_addr_t addr;
+ const or_options_t *options = get_options();
if (node->ri) {
routerinfo_t *ri = node->ri;
@@ -2258,9 +2275,15 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node)
}
}
- /* Mark which address to use based on which bridge_t we got. */
- node->ipv6_preferred = (tor_addr_family(&bridge->addr) == AF_INET6 &&
- !tor_addr_is_null(&node->ri->ipv6_addr));
+ if (options->ClientPreferIPv6ORPort == -1) {
+ /* Mark which address to use based on which bridge_t we got. */
+ node->ipv6_preferred = (tor_addr_family(&bridge->addr) == AF_INET6 &&
+ !tor_addr_is_null(&node->ri->ipv6_addr));
+ } else {
+ /* Mark which address to use based on user preference */
+ node->ipv6_preferred = (fascist_firewall_prefer_ipv6_orport(options) &&
+ !tor_addr_is_null(&node->ri->ipv6_addr));
+ }
/* XXXipv6 we lack support for falling back to another address for
the same relay, warn the user */
@@ -2269,10 +2292,13 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node)
node_get_pref_orport(node, &ap);
log_notice(LD_CONFIG,
"Bridge '%s' has both an IPv4 and an IPv6 address. "
- "Will prefer using its %s address (%s).",
+ "Will prefer using its %s address (%s) based on %s.",
ri->nickname,
- tor_addr_family(&ap.addr) == AF_INET6 ? "IPv6" : "IPv4",
- fmt_addrport(&ap.addr, ap.port));
+ node->ipv6_preferred ? "IPv6" : "IPv4",
+ fmt_addrport(&ap.addr, ap.port),
+ options->ClientPreferIPv6ORPort == -1 ?
+ "the configured Bridge address" :
+ "ClientPreferIPv6ORPort");
}
}
if (node->rs) {
diff --git a/src/or/eventdns_tor.h b/src/or/eventdns_tor.h
index 9d51f0960e..f41c5c0099 100644
--- a/src/or/eventdns_tor.h
+++ b/src/or/eventdns_tor.h
@@ -12,9 +12,6 @@ typedef unsigned int uint;
#ifndef HAVE_U_CHAR
typedef unsigned char u_char;
#endif
-#ifdef _WIN32
-#define inline __inline
-#endif
#include "torint.h"
/* These are for debugging possible memory leaks. */
diff --git a/src/or/ext_orport.c b/src/or/ext_orport.c
index e8c8aa60a4..810fa0d9aa 100644
--- a/src/or/ext_orport.c
+++ b/src/or/ext_orport.c
@@ -151,7 +151,7 @@ init_ext_or_cookie_authentication(int is_enabled)
}
/** Read data from <b>conn</b> and see if the client sent us the
- * authentication type that she prefers to use in this session.
+ * authentication type that they prefer to use in this session.
*
* Return -1 if we received corrupted data or if we don't support the
* authentication type. Return 0 if we need more data in
@@ -193,8 +193,7 @@ handle_client_auth_nonce(const char *client_nonce, size_t client_nonce_len,
return -1;
/* Get our nonce */
- if (crypto_rand(server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN) < 0)
- return -1;
+ crypto_rand(server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN);
{ /* set up macs */
size_t hmac_s_msg_len = strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST) +
diff --git a/src/or/fallback_dirs.inc b/src/or/fallback_dirs.inc
new file mode 100644
index 0000000000..d05f16f4ea
--- /dev/null
+++ b/src/or/fallback_dirs.inc
@@ -0,0 +1,206 @@
+/* Trial fallbacks for 0.2.8.1-alpha with ADDRESS_AND_PORT_STABLE_DAYS = 30
+ * This works around an issue where relays post a descriptor without a DirPort
+ * when restarted. If these relays stay up, they will have been up for 120 days
+ * by the 0.2.8 stable release -- teor */
+/* Whitelist & blacklist excluded 1070 of 1091 candidates. */
+/*
+Fallback Directory Summary
+Final Count: 21 (Eligible 21, Usable 21, Target 290 (1454 * 0.200000), Clamped to 500)
+*/
+/* Ignore low fallback numbers in alpha builds -- teor
+#error Fallback Count 21 is too low. Must be at least 100 for diversity. Try adding entries to the whitelist, or setting INCLUDE_UNLISTED_ENTRIES = True.
+*/
+/*
+Final Weight: 491920 (Eligible 546000)
+Max Weight: 43680 (8.879%) (Clamped to 10.000%)
+Min Weight: 8080 (1.643%) (Clamped to 0.100%)
+Clamped: 54080 (10.994%) Excess Weight, 4 High Weight Fallbacks (19.0%)
+*/
+/*
+Onionoo Source: details Date: 2016-01-18 00:00:00 Version: 3.0
+URL: https://onionoo.torproject.org/details?fields=fingerprint%2Cnickname%2Ccontact%2Clast_changed_address_or_port%2Cconsensus_weight%2Cor_addresses%2Cdir_address%2Crecommended_version%2Cflags&flag=V2Dir&type=relay&last_seen_days=-7&first_seen_days=30-
+*/
+/*
+Onionoo Source: uptime Date: 2016-01-18 00:00:00 Version: 3.0
+URL: https://onionoo.torproject.org/uptime?first_seen_days=30-&flag=V2Dir&type=relay&last_seen_days=-7
+*/
+/*
+wagner
+Flags: Fast Guard Running Stable V2Dir Valid
+Fallback Weight: 43680 / 491920 (8.879%)
+Consensus Weight: 62600 / 546000 (11.465%)
+Rarely used email <trff914 AT gmail DOT com>
+*/
+"5.175.233.86:80 orport=443 id=5525D0429BFE5DC4F1B0E9DE47A4CFA169661E33"
+" weight=43680",
+/*
+kitten2
+Flags: Fast Guard HSDir Running Stable V2Dir Valid
+Fallback Weight: 43680 / 491920 (8.879%)
+Consensus Weight: 59100 / 546000 (10.824%)
+0xEFB74277ECE4E222 Aeris <aeris+tor AT imirhil DOT fr> - 1aerisnnLWPchhDSXpxWGYWwLiSFUVFnd
+*/
+"62.210.124.124:9130 orport=9101 id=2EBD117806EE43C3CC885A8F1E4DC60F207E7D3E"
+" ipv6=[2001:bc8:3f23:100::1]:9101"
+" weight=43680",
+/*
+kitten1
+Flags: Fast Guard HSDir Running Stable V2Dir Valid
+Fallback Weight: 43680 / 491920 (8.879%)
+Consensus Weight: 57600 / 546000 (10.549%)
+0xEFB74277ECE4E222 Aeris <aeris+tor AT imirhil DOT fr> - 1aerisnnLWPchhDSXpxWGYWwLiSFUVFnd
+*/
+"62.210.124.124:9030 orport=9001 id=86E78DD3720C78DA8673182EF96C54B162CD660C"
+" ipv6=[2001:bc8:3f23:100::1]:9001"
+" weight=43680",
+/*
+fluxe4
+Flags: Fast Guard HSDir Running Stable V2Dir Valid
+Fallback Weight: 43680 / 491920 (8.879%)
+Consensus Weight: 49500 / 546000 (9.066%)
+Sebastian <tor@sebastianhahn.net> - 12NbRAjAG5U3LLWETSF7fSTcdaz32Mu5CN
+*/
+"131.188.40.188:443 orport=80 id=EBE718E1A49EE229071702964F8DB1F318075FF8"
+" weight=43680",
+/*
+BabylonNetwork03
+Flags: Exit Fast Guard HSDir Running Stable V2Dir Valid
+Fallback Weight: 38700 / 491920 (7.867%)
+Babylon Network | noc <AT> babylon <DOT> network | PGP 0x2A540FA5 | 1HiSG8pia5DdDLUMyYNkF9sicGozojZLnH
+*/
+"193.111.136.162:80 orport=443 id=C79552275DFCD486B942510EF663ED36ACA1A84B"
+" ipv6=[2001:4ba0:cafe:10d0::1]:443"
+" weight=38700",
+/*
+tornoderdednl
+Flags: Fast Guard Running Stable V2Dir Valid
+Fallback Weight: 33000 / 491920 (6.708%)
+0x4871E82F Thom Wiggers <thom @AT@ RDED POINT NL> BTC 1DLyDFV13zhCWJYHMh5bk5C58yYvpxqxfQ
+*/
+"178.62.199.226:80 orport=443 id=CBEFF7BA4A4062045133C053F2D70524D8BBE5BE"
+" ipv6=[2a03:b0c0:2:d0::b7:5001]:443"
+" weight=33000",
+/*
+fluxe3
+Flags: Fast Guard HSDir Running Stable V2Dir Valid
+Fallback Weight: 31500 / 491920 (6.403%)
+Sebastian <tor@sebastianhahn.net> - 12NbRAjAG5U3LLWETSF7fSTcdaz32Mu5CN
+*/
+"78.47.18.110:443 orport=80 id=F8D27B163B9247B232A2EEE68DD8B698695C28DE"
+" weight=31500",
+/*
+BabylonNetwork02
+Flags: Exit Fast Guard HSDir Running Stable V2Dir Valid
+Fallback Weight: 27300 / 491920 (5.550%)
+Babylon Network | noc <AT> babylon <DOT> network | PGP 0x2A540FA5 | 1HiSG8pia5DdDLUMyYNkF9sicGozojZLnH
+*/
+"149.202.98.161:80 orport=443 id=54660C671B47E6986B465B80444414BD19E5A34B"
+" ipv6=[2001:41d0:8:4528::161]:443"
+" weight=27300",
+/*
+coby
+Flags: Fast Guard HSDir Running Stable V2Dir Valid
+Fallback Weight: 20400 / 491920 (4.147%)
+c0by <coby AT 127001 dot ovh>
+*/
+"51.255.33.237:9091 orport=9001 id=A360C21FA87FFA2046D92C17086A6B47E5C68109"
+" weight=20400",
+/*
+kili
+Flags: Fast Guard HSDir Running Stable V2Dir Valid
+Fallback Weight: 19300 / 491920 (3.923%)
+0x49CBC553 Joost Rijneveld <joost AT joostrijneveld dot nl>
+*/
+"178.62.173.203:9030 orport=9001 id=DD85503F2D1F52EF9EAD621E942298F46CD2FC10"
+" ipv6=[2a03:b0c0:0:1010::a4:b001]:9001"
+" weight=19300",
+/*
+Logforme
+Flags: Fast Guard HSDir Running Stable V2Dir Valid
+Fallback Weight: 18600 / 491920 (3.781%)
+Logforme <m7527 AT abc dot se>
+*/
+"84.219.173.60:9030 orport=443 id=855BC2DABE24C861CD887DB9B2E950424B49FC34"
+" weight=18600",
+/*
+eriador
+Flags: Fast Guard HSDir Running Stable V2Dir Valid
+Fallback Weight: 17400 / 491920 (3.537%)
+hwertiout695@safe-mail.net
+*/
+"85.25.138.93:9030 orport=4029 id=6DE61A6F72C1E5418A66BFED80DFB63E4C77668F"
+" weight=17400",
+/*
+Doedel24
+Flags: Fast Guard HSDir Running Stable V2Dir Valid
+Fallback Weight: 17000 / 491920 (3.456%)
+Felix <zwiebel ta quantentunnel tod de>
+*/
+"178.254.20.134:9030 orport=9001 id=2CE96A8A1DA032664C90F574AFFBECE18A6E8DFC"
+" weight=17000",
+/*
+GrmmlLitavis
+Flags: Fast Guard HSDir Running Stable V2Dir Valid
+Fallback Weight: 15500 / 491920 (3.151%)
+<tor AT grmml DOT eu>
+*/
+"5.39.88.19:9030 orport=9001 id=7CB8C31432A796731EA7B6BF4025548DFEB25E0C"
+" ipv6=[2001:41d0:8:9a13::1]:9050"
+" weight=15500",
+/*
+Doedel21
+Flags: Fast Guard HSDir Running Stable V2Dir Valid
+Fallback Weight: 13800 / 491920 (2.805%)
+Felix <zwiebel ta quantentunnel tod de>
+*/
+"178.254.44.135:80 orport=443 id=AE6A8C18E7499B586CD36246AC4BCAFFBBF93AB2"
+" weight=13800",
+/*
+Unnamed
+Flags: Fast Guard HSDir Running Stable V2Dir Valid
+Fallback Weight: 13400 / 491920 (2.724%)
+monitor0penmailbox0rg
+*/
+"217.12.199.208:80 orport=443 id=DF3AED4322B1824BF5539AE54B2D1B38E080FF05"
+" weight=13400",
+/*
+Doedel26
+Flags: Fast Guard HSDir Running Stable V2Dir Valid
+Fallback Weight: 12800 / 491920 (2.602%)
+Felix <zwiebel ta quantentunnel tod de>
+*/
+"178.254.20.134:80 orport=443 id=9F5068310818ED7C70B0BC4087AB55CB12CB4377"
+" weight=12800",
+/*
+Doedel22
+Flags: Fast Guard HSDir Running Stable V2Dir Valid
+Fallback Weight: 12000 / 491920 (2.439%)
+Felix <zwiebel ta quantentunnel tod de>
+*/
+"178.254.44.135:9030 orport=9001 id=8FA37B93397015B2BC5A525C908485260BE9F422"
+" weight=12000",
+/*
+kitten4
+Flags: Fast Guard HSDir Running Stable V2Dir Valid
+Fallback Weight: 10100 / 491920 (2.053%)
+0xEFB74277ECE4E222 Aeris <aeris+tor AT imirhil DOT fr> - 1aerisnnLWPchhDSXpxWGYWwLiSFUVFnd
+*/
+"212.47.237.95:9130 orport=9101 id=6FB38EB22E57EF7ED5EF00238F6A48E553735D88"
+" weight=10100",
+/*
+Binnacle
+Flags: Fast Guard HSDir Running Stable V2Dir Valid
+Fallback Weight: 8320 / 491920 (1.691%)
+starlight dot YYYYqQ at binnacle dot cx
+*/
+"108.53.208.157:80 orport=443 id=4F0DB7E687FC7C0AE55C8F243DA8B0EB27FBF1F2"
+" weight=8320",
+/*
+PedicaboMundi
+Flags: Fast Guard HSDir Running Stable V2Dir Valid
+Fallback Weight: 8080 / 491920 (1.643%)
+0x43DE8191 - 12LiRiasTEL346ZFjgCh5e3nBexQuvDBTg
+*/
+"144.76.14.145:110 orport=143 id=14419131033443AE6E21DA82B0D307F7CAE42BDB"
+" ipv6=[2a01:4f8:190:9490::dead]:443"
+" weight=8080",
diff --git a/src/or/fp_pair.c b/src/or/fp_pair.c
index 42bebcd847..c863d4176c 100644
--- a/src/or/fp_pair.c
+++ b/src/or/fp_pair.c
@@ -21,7 +21,7 @@ struct fp_pair_map_s {
*/
/** Compare fp_pair_entry_t objects by key value. */
-static INLINE int
+static inline int
fp_pair_map_entries_eq(const fp_pair_map_entry_t *a,
const fp_pair_map_entry_t *b)
{
@@ -29,7 +29,7 @@ fp_pair_map_entries_eq(const fp_pair_map_entry_t *a,
}
/** Return a hash value for an fp_pair_entry_t. */
-static INLINE unsigned int
+static inline unsigned int
fp_pair_map_entry_hash(const fp_pair_map_entry_t *a)
{
tor_assert(sizeof(a->key) == DIGEST_LEN*2);
diff --git a/src/or/geoip.c b/src/or/geoip.c
index 120ce479cc..26030ae52a 100644
--- a/src/or/geoip.c
+++ b/src/or/geoip.c
@@ -18,7 +18,6 @@
#include "geoip.h"
#include "routerlist.h"
-static void clear_geoip_db(void);
static void init_geoip_countries(void);
/** An entry from the GeoIP IPv4 file: maps an IPv4 range to a country. */
@@ -483,7 +482,7 @@ static HT_HEAD(clientmap, clientmap_entry_t) client_history =
HT_INITIALIZER();
/** Hashtable helper: compute a hash of a clientmap_entry_t. */
-static INLINE unsigned
+static inline unsigned
clientmap_entry_hash(const clientmap_entry_t *a)
{
unsigned h = (unsigned) tor_addr_hash(&a->addr);
@@ -494,7 +493,7 @@ clientmap_entry_hash(const clientmap_entry_t *a)
return h;
}
/** Hashtable helper: compare two clientmap_entry_t values for equality. */
-static INLINE int
+static inline int
clientmap_entries_eq(const clientmap_entry_t *a, const clientmap_entry_t *b)
{
if (strcmp_opt(a->transport_name, b->transport_name))
@@ -970,7 +969,7 @@ geoip_get_dirreq_history(dirreq_type_t type)
&ent->completion_time);
if (time_diff == 0)
time_diff = 1; /* Avoid DIV/0; "instant" answers are impossible
- * by law of nature or something, but a milisecond
+ * by law of nature or something, but a millisecond
* is a bit greater than "instantly" */
bytes_per_second = (uint32_t)(1000 * ent->response_size / time_diff);
dltimes[ent_sl_idx] = bytes_per_second;
@@ -1207,9 +1206,9 @@ geoip_format_dirreq_stats(time_t now)
{
char t[ISO_TIME_LEN+1];
int i;
- char *v3_ips_string, *v3_reqs_string, *v3_direct_dl_string,
- *v3_tunneled_dl_string;
- char *result;
+ char *v3_ips_string = NULL, *v3_reqs_string = NULL,
+ *v3_direct_dl_string = NULL, *v3_tunneled_dl_string = NULL;
+ char *result = NULL;
if (!start_of_dirreq_stats_interval)
return NULL; /* Not initialized. */
@@ -1280,6 +1279,8 @@ geoip_dirreq_stats_write(time_t now)
/* Generate history string .*/
str = geoip_format_dirreq_stats(now);
+ if (! str)
+ goto done;
/* Write dirreq-stats string to disk. */
if (!check_or_create_data_subdir("stats")) {
@@ -1666,7 +1667,7 @@ getinfo_helper_geoip(control_connection_t *control_conn,
}
/** Release all storage held by the GeoIP databases and country list. */
-static void
+STATIC void
clear_geoip_db(void)
{
if (geoip_countries) {
diff --git a/src/or/geoip.h b/src/or/geoip.h
index 8a3486c7ac..3f1bba01f8 100644
--- a/src/or/geoip.h
+++ b/src/or/geoip.h
@@ -18,6 +18,7 @@
STATIC int geoip_parse_entry(const char *line, sa_family_t family);
STATIC int geoip_get_country_by_ipv4(uint32_t ipaddr);
STATIC int geoip_get_country_by_ipv6(const struct in6_addr *addr);
+STATIC void clear_geoip_db(void);
#endif
int should_record_bridge_info(const or_options_t *options);
int geoip_load_file(sa_family_t family, const char *filename);
diff --git a/src/or/hibernate.c b/src/or/hibernate.c
index 356e11f6ec..7f8530b221 100644
--- a/src/or/hibernate.c
+++ b/src/or/hibernate.c
@@ -412,11 +412,15 @@ configure_accounting(time_t now)
/** Return the relevant number of bytes sent/received this interval
* based on the set AccountingRule */
-static uint64_t
+uint64_t
get_accounting_bytes(void)
{
if (get_options()->AccountingRule == ACCT_SUM)
return n_bytes_read_in_interval+n_bytes_written_in_interval;
+ else if (get_options()->AccountingRule == ACCT_IN)
+ return n_bytes_read_in_interval;
+ else if (get_options()->AccountingRule == ACCT_OUT)
+ return n_bytes_written_in_interval;
else
return MAX(n_bytes_read_in_interval, n_bytes_written_in_interval);
}
@@ -490,7 +494,7 @@ reset_accounting(time_t now)
}
/** Return true iff we should save our bandwidth usage to disk. */
-static INLINE int
+static inline int
time_to_record_bandwidth_usage(time_t now)
{
/* Note every 600 sec */
@@ -1010,7 +1014,7 @@ getinfo_helper_accounting(control_connection_t *conn,
else
*answer = tor_strdup("awake");
} else if (!strcmp(question, "accounting/bytes")) {
- tor_asprintf(answer, U64_FORMAT" "U64_FORMAT,
+ tor_asprintf(answer, U64_FORMAT" "U64_FORMAT,
U64_PRINTF_ARG(n_bytes_read_in_interval),
U64_PRINTF_ARG(n_bytes_written_in_interval));
} else if (!strcmp(question, "accounting/bytes-left")) {
@@ -1022,6 +1026,18 @@ getinfo_helper_accounting(control_connection_t *conn,
total_left = limit - total_bytes;
tor_asprintf(answer, U64_FORMAT" "U64_FORMAT,
U64_PRINTF_ARG(total_left), U64_PRINTF_ARG(total_left));
+ } else if (get_options()->AccountingRule == ACCT_IN) {
+ uint64_t read_left = 0;
+ if (n_bytes_read_in_interval < limit)
+ read_left = limit - n_bytes_read_in_interval;
+ tor_asprintf(answer, U64_FORMAT" "U64_FORMAT,
+ U64_PRINTF_ARG(read_left), U64_PRINTF_ARG(limit));
+ } else if (get_options()->AccountingRule == ACCT_OUT) {
+ uint64_t write_left = 0;
+ if (n_bytes_written_in_interval < limit)
+ write_left = limit - n_bytes_written_in_interval;
+ tor_asprintf(answer, U64_FORMAT" "U64_FORMAT,
+ U64_PRINTF_ARG(limit), U64_PRINTF_ARG(write_left));
} else {
uint64_t read_left = 0, write_left = 0;
if (n_bytes_read_in_interval < limit)
diff --git a/src/or/hibernate.h b/src/or/hibernate.h
index b9e619c5ad..e0d0c29bdb 100644
--- a/src/or/hibernate.h
+++ b/src/or/hibernate.h
@@ -19,6 +19,7 @@ MOCK_DECL(int, accounting_is_enabled, (const or_options_t *options));
int accounting_get_interval_length(void);
MOCK_DECL(time_t, accounting_get_end_time, (void));
void configure_accounting(time_t now);
+uint64_t get_accounting_bytes(void);
void accounting_run_housekeeping(time_t now);
void accounting_add_bytes(size_t n_read, size_t n_written, int seconds);
int accounting_record_bandwidth_usage(time_t now, or_state_t *state);
diff --git a/src/or/include.am b/src/or/include.am
index a3ac49c5d6..712ae18406 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -63,6 +63,7 @@ LIBTOR_A_SOURCES = \
src/or/onion_fast.c \
src/or/onion_tap.c \
src/or/transports.c \
+ src/or/periodic.c \
src/or/policies.c \
src/or/reasons.c \
src/or/relay.c \
@@ -92,7 +93,8 @@ src_or_libtor_testing_a_SOURCES = $(LIBTOR_A_SOURCES)
src_or_tor_SOURCES = src/or/tor_main.c
AM_CPPFLAGS += -I$(srcdir)/src/or -Isrc/or
-src/or/tor_main.o: micro-revision.i
+src/or/tor_main.$(OBJEXT) \
+ src/or/src_or_tor_cov-tor_main.$(OBJEXT): micro-revision.i
AM_CPPFLAGS += -DSHARE_DATADIR="\"$(datadir)\"" \
-DLOCALSTATEDIR="\"$(localstatedir)\"" \
@@ -108,7 +110,7 @@ src_or_libtor_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
src_or_tor_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@
src_or_tor_LDADD = src/or/libtor.a src/common/libor.a \
- src/common/libor-crypto.a $(LIBDONNA) \
+ src/common/libor-crypto.a $(LIBKECCAK_TINY) $(LIBDONNA) \
src/common/libor-event.a src/trunnel/libor-trunnel.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
@TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@
@@ -119,7 +121,7 @@ src_or_tor_cov_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_or_tor_cov_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
src_or_tor_cov_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@
src_or_tor_cov_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \
- src/common/libor-crypto-testing.a $(LIBDONNA) \
+ src/common/libor-crypto-testing.a $(LIBKECCAK_TINY) $(LIBDONNA) \
src/common/libor-event-testing.a src/trunnel/libor-trunnel-testing.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
@TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@
@@ -154,6 +156,7 @@ ORHEADERS = \
src/or/dnsserv.h \
src/or/eventdns_tor.h \
src/or/ext_orport.h \
+ src/or/fallback_dirs.inc \
src/or/fp_pair.h \
src/or/geoip.h \
src/or/entrynodes.h \
@@ -170,6 +173,7 @@ ORHEADERS = \
src/or/onion_tap.h \
src/or/or.h \
src/or/transports.h \
+ src/or/periodic.h \
src/or/policies.h \
src/or/reasons.h \
src/or/relay.h \
diff --git a/src/or/keypin.c b/src/or/keypin.c
index 047d2b069b..574a76d51e 100644
--- a/src/or/keypin.c
+++ b/src/or/keypin.c
@@ -57,14 +57,14 @@ static HT_HEAD(edmap, keypin_ent_st) the_ed_map = HT_INITIALIZER();
/** Hashtable helper: compare two keypin table entries and return true iff
* they have the same RSA key IDs. */
-static INLINE int
+static inline int
keypin_ents_eq_rsa(const keypin_ent_t *a, const keypin_ent_t *b)
{
return tor_memeq(a->rsa_id, b->rsa_id, sizeof(a->rsa_id));
}
/** Hashtable helper: hash a keypin table entries based on its RSA key ID */
-static INLINE unsigned
+static inline unsigned
keypin_ent_hash_rsa(const keypin_ent_t *a)
{
return (unsigned) siphash24g(a->rsa_id, sizeof(a->rsa_id));
@@ -72,14 +72,14 @@ return (unsigned) siphash24g(a->rsa_id, sizeof(a->rsa_id));
/** Hashtable helper: compare two keypin table entries and return true iff
* they have the same ed25519 keys */
-static INLINE int
+static inline int
keypin_ents_eq_ed(const keypin_ent_t *a, const keypin_ent_t *b)
{
return tor_memeq(a->ed25519_key, b->ed25519_key, sizeof(a->ed25519_key));
}
/** Hashtable helper: hash a keypin table entries based on its ed25519 key */
-static INLINE unsigned
+static inline unsigned
keypin_ent_hash_ed(const keypin_ent_t *a)
{
return (unsigned) siphash24g(a->ed25519_key, sizeof(a->ed25519_key));
diff --git a/src/or/main.c b/src/or/main.c
index 534a6acc51..96a56fcb90 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -44,6 +44,7 @@
#include "nodelist.h"
#include "ntmain.h"
#include "onion.h"
+#include "periodic.h"
#include "policies.h"
#include "transports.h"
#include "relay.h"
@@ -190,32 +191,6 @@ int quiet_level = 0;
*
****************************************************************************/
-#if 0 && defined(USE_BUFFEREVENTS)
-static void
-free_old_inbuf(connection_t *conn)
-{
- if (! conn->inbuf)
- return;
-
- tor_assert(conn->outbuf);
- tor_assert(buf_datalen(conn->inbuf) == 0);
- tor_assert(buf_datalen(conn->outbuf) == 0);
- buf_free(conn->inbuf);
- buf_free(conn->outbuf);
- conn->inbuf = conn->outbuf = NULL;
-
- if (conn->read_event) {
- event_del(conn->read_event);
- tor_event_free(conn->read_event);
- }
- if (conn->write_event) {
- event_del(conn->read_event);
- tor_event_free(conn->write_event);
- }
- conn->read_event = conn->write_event = NULL;
-}
-#endif
-
#if defined(_WIN32) && defined(USE_BUFFEREVENTS)
/** Remove the kernel-space send and receive buffers for <b>s</b>. For use
* with IOCP only. */
@@ -224,11 +199,13 @@ set_buffer_lengths_to_zero(tor_socket_t s)
{
int zero = 0;
int r = 0;
- if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&zero, sizeof(zero))) {
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&zero,
+ (socklen_t)sizeof(zero))) {
log_warn(LD_NET, "Unable to clear SO_SNDBUF");
r = -1;
}
- if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&zero, sizeof(zero))) {
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&zero,
+ (socklen_t)sizeof(zero))) {
log_warn(LD_NET, "Unable to clear SO_RCVBUF");
r = -1;
}
@@ -499,8 +476,7 @@ connection_in_array(connection_t *conn)
return smartlist_contains(connection_array, conn);
}
-/** Set <b>*array</b> to an array of all connections, and <b>*n</b>
- * to the length of the array. <b>*array</b> and <b>*n</b> must not
+/** Set <b>*array</b> to an array of all connections. <b>*array</b> must not
* be modified.
*/
smartlist_t *
@@ -944,18 +920,6 @@ conn_close_if_marked(int i)
* would make much more sense to react in
* connection_handle_read_impl, or to just stop reading in
* mark_and_flush */
-#if 0
-#define MARKED_READING_RATE 180
- static ratelim_t marked_read_lim = RATELIM_INIT(MARKED_READING_RATE);
- char *m;
- if ((m = rate_limit_log(&marked_read_lim, now))) {
- log_warn(LD_BUG, "Marked connection (fd %d, type %s, state %s) "
- "is still reading; that shouldn't happen.%s",
- (int)conn->s, conn_type_to_string(conn->type),
- conn_state_to_string(conn->type, conn->state), m);
- tor_free(m);
- }
-#endif
conn->read_blocked_on_bw = 1;
connection_stop_reading(conn);
}
@@ -1038,12 +1002,12 @@ directory_all_unreachable(time_t now)
/** This function is called whenever we successfully pull down some new
* network statuses or server descriptors. */
void
-directory_info_has_arrived(time_t now, int from_cache)
+directory_info_has_arrived(time_t now, int from_cache, int suppress_logs)
{
const or_options_t *options = get_options();
if (!router_have_minimum_dir_info()) {
- int quiet = from_cache ||
+ int quiet = suppress_logs || from_cache ||
directory_too_idle_to_fetch_descriptors(options, now);
tor_log(quiet ? LOG_INFO : LOG_NOTICE, LD_DIR,
"I learned some more directory information, but not enough to "
@@ -1227,39 +1191,85 @@ get_signewnym_epoch(void)
return newnym_epoch;
}
-typedef struct {
- time_t last_rotated_x509_certificate;
- time_t check_v3_certificate;
- time_t check_listeners;
- time_t download_networkstatus;
- time_t try_getting_descriptors;
- time_t reset_descriptor_failures;
- time_t add_entropy;
- time_t write_bridge_status_file;
- time_t downrate_stability;
- time_t save_stability;
- time_t clean_caches;
- time_t recheck_bandwidth;
- time_t check_for_expired_networkstatus;
- time_t write_stats_files;
- time_t write_bridge_stats;
- time_t check_port_forwarding;
- time_t launch_reachability_tests;
- time_t retry_dns_init;
- time_t next_heartbeat;
- time_t check_descriptor;
- /** When do we next launch DNS wildcarding checks? */
- time_t check_for_correct_dns;
- /** When do we next make sure our Ed25519 keys aren't about to expire? */
- time_t check_ed_keys;
-
-} time_to_t;
-
-static time_to_t time_to = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+/** True iff we have initialized all the members of <b>periodic_events</b>.
+ * Used to prevent double-initialization. */
+static int periodic_events_initialized = 0;
+
+/* Declare all the timer callback functions... */
+#undef CALLBACK
+#define CALLBACK(name) \
+ static int name ## _callback(time_t, const or_options_t *)
+CALLBACK(rotate_onion_key);
+CALLBACK(check_ed_keys);
+CALLBACK(launch_descriptor_fetches);
+CALLBACK(reset_descriptor_failures);
+CALLBACK(rotate_x509_certificate);
+CALLBACK(add_entropy);
+CALLBACK(launch_reachability_tests);
+CALLBACK(downrate_stability);
+CALLBACK(save_stability);
+CALLBACK(check_authority_cert);
+CALLBACK(check_expired_networkstatus);
+CALLBACK(write_stats_file);
+CALLBACK(record_bridge_stats);
+CALLBACK(clean_caches);
+CALLBACK(rend_cache_failure_clean);
+CALLBACK(retry_dns);
+CALLBACK(check_descriptor);
+CALLBACK(check_for_reachability_bw);
+CALLBACK(fetch_networkstatus);
+CALLBACK(retry_listeners);
+CALLBACK(expire_old_ciruits_serverside);
+CALLBACK(check_dns_honesty);
+CALLBACK(write_bridge_ns);
+CALLBACK(check_fw_helper_app);
+CALLBACK(heartbeat);
+
+#undef CALLBACK
+
+/* Now we declare an array of periodic_event_item_t for each periodic event */
+#define CALLBACK(name) PERIODIC_EVENT(name)
+
+static periodic_event_item_t periodic_events[] = {
+ CALLBACK(rotate_onion_key),
+ CALLBACK(check_ed_keys),
+ CALLBACK(launch_descriptor_fetches),
+ CALLBACK(reset_descriptor_failures),
+ CALLBACK(rotate_x509_certificate),
+ CALLBACK(add_entropy),
+ CALLBACK(launch_reachability_tests),
+ CALLBACK(downrate_stability),
+ CALLBACK(save_stability),
+ CALLBACK(check_authority_cert),
+ CALLBACK(check_expired_networkstatus),
+ CALLBACK(write_stats_file),
+ CALLBACK(record_bridge_stats),
+ CALLBACK(clean_caches),
+ CALLBACK(rend_cache_failure_clean),
+ CALLBACK(retry_dns),
+ CALLBACK(check_descriptor),
+ CALLBACK(check_for_reachability_bw),
+ CALLBACK(fetch_networkstatus),
+ CALLBACK(retry_listeners),
+ CALLBACK(expire_old_ciruits_serverside),
+ CALLBACK(check_dns_honesty),
+ CALLBACK(write_bridge_ns),
+ CALLBACK(check_fw_helper_app),
+ CALLBACK(heartbeat),
+ END_OF_PERIODIC_EVENTS
};
-
-/** Reset all the time_to's so we'll do all our actions again as if we
+#undef CALLBACK
+
+/* These are pointers to members of periodic_events[] that are used to
+ * implement particular callbacks. We keep them separate here so that we
+ * can access them by name. We also keep them inside periodic_events[]
+ * so that we can implement "reset all timers" in a reasonable way. */
+static periodic_event_item_t *check_descriptor_event=NULL;
+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;
+
+/** Reset all the periodic events so we'll do all our actions again as if we
* just started up.
* Useful if our clock just moved back a long time from the future,
* so we don't wait until that future arrives again before acting.
@@ -1267,7 +1277,77 @@ static time_to_t time_to = {
void
reset_all_main_loop_timers(void)
{
- memset(&time_to, 0, sizeof(time_to));
+ int i;
+ for (i = 0; periodic_events[i].name; ++i) {
+ periodic_event_reschedule(&periodic_events[i]);
+ }
+}
+
+/** Return the member of periodic_events[] whose name is <b>name</b>.
+ * Return NULL if no such event is found.
+ */
+static periodic_event_item_t *
+find_periodic_event(const char *name)
+{
+ int i;
+ for (i = 0; periodic_events[i].name; ++i) {
+ if (strcmp(name, periodic_events[i].name) == 0)
+ return &periodic_events[i];
+ }
+ return NULL;
+}
+
+/** Helper, run one second after setup:
+ * Initializes all members of periodic_events and starts them running.
+ *
+ * (We do this one second after setup for backward-compatibility reasons;
+ * it might not actually be necessary.) */
+static void
+initialize_periodic_events_cb(evutil_socket_t fd, short events, void *data)
+{
+ (void) fd;
+ (void) events;
+ (void) data;
+ int i;
+ for (i = 0; periodic_events[i].name; ++i) {
+ periodic_event_launch(&periodic_events[i]);
+ }
+}
+
+/** Set up all the members of periodic_events[], and configure them all to be
+ * launched from a callback. */
+STATIC void
+initialize_periodic_events(void)
+{
+ tor_assert(periodic_events_initialized == 0);
+ periodic_events_initialized = 1;
+
+ int i;
+ for (i = 0; periodic_events[i].name; ++i) {
+ periodic_event_setup(&periodic_events[i]);
+ }
+
+#define NAMED_CALLBACK(name) \
+ STMT_BEGIN name ## _event = find_periodic_event( #name ); STMT_END
+
+ NAMED_CALLBACK(check_descriptor);
+ NAMED_CALLBACK(fetch_networkstatus);
+ NAMED_CALLBACK(launch_descriptor_fetches);
+ NAMED_CALLBACK(check_dns_honesty);
+
+ struct timeval one_second = { 1, 0 };
+ event_base_once(tor_libevent_get_base(), -1, EV_TIMEOUT,
+ initialize_periodic_events_cb, NULL,
+ &one_second);
+}
+
+STATIC void
+teardown_periodic_events(void)
+{
+ int i;
+ for (i = 0; periodic_events[i].name; ++i) {
+ periodic_event_destroy(&periodic_events[i]);
+ }
}
/**
@@ -1278,7 +1358,8 @@ reset_all_main_loop_timers(void)
void
reschedule_descriptor_update_check(void)
{
- time_to.check_descriptor = 0;
+ tor_assert(check_descriptor_event);
+ periodic_event_reschedule(check_descriptor_event);
}
/**
@@ -1288,8 +1369,34 @@ reschedule_descriptor_update_check(void)
void
reschedule_directory_downloads(void)
{
- time_to.download_networkstatus = 0;
- time_to.try_getting_descriptors = 0;
+ tor_assert(fetch_networkstatus_event);
+ tor_assert(launch_descriptor_fetches_event);
+
+ periodic_event_reschedule(fetch_networkstatus_event);
+ periodic_event_reschedule(launch_descriptor_fetches_event);
+}
+
+#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]. */
+static inline int
+safe_timer_diff(time_t now, time_t next)
+{
+ if (next > now) {
+ /* There were no computers at signed TIME_MIN (1902 on 32-bit systems),
+ * and nothing that could run Tor. It's a bug if 'next' is around then.
+ * On 64-bit systems with signed TIME_MIN, TIME_MIN is before the Big
+ * Bang. We cannot extrapolate past a singularity, but there was probably
+ * nothing that could run Tor then, either.
+ **/
+ tor_assert(next > TIME_MIN + LONGEST_TIMER_PERIOD);
+
+ if (next - LONGEST_TIMER_PERIOD > now)
+ return LONGEST_TIMER_PERIOD;
+ return (int)(next - now);
+ } else {
+ return 1;
+ }
}
/** Perform regular maintenance tasks. This function gets run once per
@@ -1298,13 +1405,8 @@ reschedule_directory_downloads(void)
static void
run_scheduled_events(time_t now)
{
- static int should_init_bridge_stats = 1;
const or_options_t *options = get_options();
- int is_server = server_mode(options);
- int i;
- int have_dir_info;
-
/* 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.
@@ -1322,12 +1424,103 @@ run_scheduled_events(time_t now)
/* 0c. If we've deferred log messages for the controller, handle them now */
flush_pending_log_callbacks();
+ if (options->UseBridges && !options->DisableNetwork) {
+ fetch_bridge_descriptors(options, now);
+ }
+
+ if (accounting_is_enabled(options)) {
+ accounting_run_housekeeping(now);
+ }
+
+ if (authdir_mode_v3(options)) {
+ dirvote_act(options, now);
+ }
+
+ /* 2d. Cleanup excess consensus bootstrap connections every second.
+ * connection_dir_close_consensus_conn_if_extra() will close connections
+ * that are clearly excess, but this check is more thorough. */
+ connection_dir_close_extra_consensus_conns();
+
+ /* 3a. Every second, we examine pending circuits and prune the
+ * ones which have been pending for more than a few seconds.
+ * We do this before step 4, so it can try building more if
+ * it's not comfortable with the number of available circuits.
+ */
+ /* (If our circuit build timeout can ever become lower than a second (which
+ * it can't, currently), we should do this more often.) */
+ circuit_expire_building();
+
+ /* 3b. Also look at pending streams and prune the ones that 'began'
+ * a long time ago but haven't gotten a 'connected' yet.
+ * Do this before step 4, so we can put them back into pending
+ * state to be picked up by the new circuit.
+ */
+ connection_ap_expire_beginning();
+
+ /* 3c. And expire connections that we've held open for too long.
+ */
+ connection_expire_held_open();
+
+ /* 4. Every second, we try a new circuit if there are no valid
+ * circuits. Every NewCircuitPeriod seconds, we expire circuits
+ * that became dirty more than MaxCircuitDirtiness seconds ago,
+ * and we make a new circ if there are no clean circuits.
+ */
+ const int have_dir_info = router_have_minimum_dir_info();
+ if (have_dir_info && !net_is_disabled()) {
+ circuit_build_needed_circs(now);
+ } else {
+ circuit_expire_old_circs_as_needed(now);
+ }
+
+ /* 5. We do housekeeping for each connection... */
+ connection_or_set_bad_connections(NULL, 0);
+ int i;
+ for (i=0;i<smartlist_len(connection_array);i++) {
+ run_connection_housekeeping(i, now);
+ }
+
+ /* 6. And remove any marked circuits... */
+ circuit_close_all_marked();
+
+ /* 7. And upload service descriptors if necessary. */
+ if (have_completed_a_circuit() && !net_is_disabled()) {
+ rend_consider_services_upload(now);
+ rend_consider_descriptor_republication();
+ }
+
+ /* 8. and blow away any connections that need to die. have to do this now,
+ * because if we marked a conn for close and left its socket -1, then
+ * we'll pass it to poll/select and bad things will happen.
+ */
+ close_closeable_connections();
+
+ /* 8b. And if anything in our state is ready to get flushed to disk, we
+ * flush it. */
+ or_state_save(now);
+
+ /* 8c. Do channel cleanup just like for connections */
+ channel_run_cleanup();
+ channel_listener_run_cleanup();
+
+ /* 11b. check pending unconfigured managed proxies */
+ if (!net_is_disabled() && pt_proxies_configuration_pending())
+ pt_configure_remaining_proxies();
+}
+
+static int
+rotate_onion_key_callback(time_t now, const or_options_t *options)
+{
/* 1a. Every MIN_ONION_KEY_LIFETIME seconds, rotate the onion keys,
* shut down and restart all cpuworkers, and update the directory if
* necessary.
*/
- if (is_server &&
- get_onion_key_set_at()+MIN_ONION_KEY_LIFETIME < now) {
+ if (server_mode(options)) {
+ time_t rotation_time = get_onion_key_set_at()+MIN_ONION_KEY_LIFETIME;
+ if (rotation_time > now) {
+ return safe_timer_diff(now, rotation_time);
+ }
+
log_info(LD_GENERAL,"Rotating onion key.");
rotate_onion_key();
cpuworkers_rotate_keyinfo();
@@ -1336,9 +1529,15 @@ run_scheduled_events(time_t now)
}
if (advertised_server_mode() && !options->DisableNetwork)
router_upload_dir_desc_to_dirservers(0);
+ return MIN_ONION_KEY_LIFETIME;
}
+ return PERIODIC_EVENT_NO_UPDATE;
+}
- if (is_server && time_to.check_ed_keys < now) {
+static int
+check_ed_keys_callback(time_t now, const or_options_t *options)
+{
+ if (server_mode(options)) {
if (should_make_new_ed_keys(options, now)) {
if (load_ed_keys(options, now) < 0 ||
generate_ed_link_cert(options, now)) {
@@ -1347,199 +1546,255 @@ run_scheduled_events(time_t now)
exit(0);
}
}
- time_to.check_ed_keys = now + 30;
+ return 30;
}
+ return PERIODIC_EVENT_NO_UPDATE;
+}
- if (!should_delay_dir_fetches(options, NULL) &&
- time_to.try_getting_descriptors < now) {
- update_all_descriptor_downloads(now);
- update_extrainfo_downloads(now);
- if (router_have_minimum_dir_info())
- time_to.try_getting_descriptors = now + LAZY_DESCRIPTOR_RETRY_INTERVAL;
- else
- time_to.try_getting_descriptors = now + GREEDY_DESCRIPTOR_RETRY_INTERVAL;
- }
+static int
+launch_descriptor_fetches_callback(time_t now, const or_options_t *options)
+{
+ if (should_delay_dir_fetches(options, NULL))
+ return PERIODIC_EVENT_NO_UPDATE;
- if (time_to.reset_descriptor_failures < now) {
- router_reset_descriptor_download_failures();
- time_to.reset_descriptor_failures =
- now + DESCRIPTOR_FAILURE_RESET_INTERVAL;
- }
+ update_all_descriptor_downloads(now);
+ update_extrainfo_downloads(now);
+ if (router_have_minimum_dir_info())
+ return LAZY_DESCRIPTOR_RETRY_INTERVAL;
+ else
+ return GREEDY_DESCRIPTOR_RETRY_INTERVAL;
+}
- if (options->UseBridges && !options->DisableNetwork)
- fetch_bridge_descriptors(options, now);
+static int
+reset_descriptor_failures_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
+ (void)options;
+ router_reset_descriptor_download_failures();
+ return DESCRIPTOR_FAILURE_RESET_INTERVAL;
+}
+
+static int
+rotate_x509_certificate_callback(time_t now, const or_options_t *options)
+{
+ static int first = 1;
+ (void)now;
+ (void)options;
+ if (first) {
+ first = 0;
+ return MAX_SSL_KEY_LIFETIME_INTERNAL;
+ }
/* 1b. Every MAX_SSL_KEY_LIFETIME_INTERNAL seconds, we change our
* TLS context. */
- if (!time_to.last_rotated_x509_certificate)
- time_to.last_rotated_x509_certificate = now;
- if (time_to.last_rotated_x509_certificate +
- MAX_SSL_KEY_LIFETIME_INTERNAL < now) {
- log_info(LD_GENERAL,"Rotating tls context.");
- if (router_initialize_tls_context() < 0) {
- log_warn(LD_BUG, "Error reinitializing TLS context");
- /* XXX is it a bug here, that we just keep going? -RD */
- }
- time_to.last_rotated_x509_certificate = now;
- /* We also make sure to rotate the TLS connections themselves if they've
- * been up for too long -- but that's done via is_bad_for_new_circs in
- * connection_run_housekeeping() above. */
+ log_info(LD_GENERAL,"Rotating tls context.");
+ if (router_initialize_tls_context() < 0) {
+ log_warn(LD_BUG, "Error reinitializing TLS context");
+ tor_assert(0);
}
- if (time_to.add_entropy < now) {
- if (time_to.add_entropy) {
- /* We already seeded once, so don't die on failure. */
- crypto_seed_rng();
- }
-/** How often do we add more entropy to OpenSSL's RNG pool? */
-#define ENTROPY_INTERVAL (60*60)
- time_to.add_entropy = now + ENTROPY_INTERVAL;
+ /* We also make sure to rotate the TLS connections themselves if they've
+ * been up for too long -- but that's done via is_bad_for_new_circs in
+ * run_connection_housekeeping() above. */
+ return MAX_SSL_KEY_LIFETIME_INTERNAL;
+}
+
+static int
+add_entropy_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
+ (void)options;
+ /* We already seeded once, so don't die on failure. */
+ if (crypto_seed_rng() < 0) {
+ log_warn(LD_GENERAL, "Tried to re-seed RNG, but failed. We already "
+ "seeded once, though, so we won't exit here.");
}
- /* 1c. If we have to change the accounting interval or record
- * bandwidth used in this accounting interval, do so. */
- if (accounting_is_enabled(options))
- accounting_run_housekeeping(now);
+ /** How often do we add more entropy to OpenSSL's RNG pool? */
+#define ENTROPY_INTERVAL (60*60)
+ return ENTROPY_INTERVAL;
+}
- if (time_to.launch_reachability_tests < now &&
- (authdir_mode_tests_reachability(options)) &&
- !net_is_disabled()) {
- time_to.launch_reachability_tests = now + REACHABILITY_TEST_INTERVAL;
+static int
+launch_reachability_tests_callback(time_t now, const or_options_t *options)
+{
+ if (authdir_mode_tests_reachability(options) &&
+ !net_is_disabled()) {
/* try to determine reachability of the other Tor relays */
dirserv_test_reachability(now);
}
+ return REACHABILITY_TEST_INTERVAL;
+}
+static int
+downrate_stability_callback(time_t now, const or_options_t *options)
+{
+ (void)options;
/* 1d. Periodically, we discount older stability information so that new
* stability info counts more, and save the stability information to disk as
* appropriate. */
- if (time_to.downrate_stability < now)
- time_to.downrate_stability = rep_hist_downrate_old_runs(now);
+ time_t next = rep_hist_downrate_old_runs(now);
+ return safe_timer_diff(now, next);
+}
+
+static int
+save_stability_callback(time_t now, const or_options_t *options)
+{
if (authdir_mode_tests_reachability(options)) {
- if (time_to.save_stability < now) {
- if (time_to.save_stability && rep_hist_record_mtbf_data(now, 1)<0) {
- log_warn(LD_GENERAL, "Couldn't store mtbf data.");
- }
-#define SAVE_STABILITY_INTERVAL (30*60)
- time_to.save_stability = now + SAVE_STABILITY_INTERVAL;
+ if (rep_hist_record_mtbf_data(now, 1)<0) {
+ log_warn(LD_GENERAL, "Couldn't store mtbf data.");
}
}
+#define SAVE_STABILITY_INTERVAL (30*60)
+ return SAVE_STABILITY_INTERVAL;
+}
+static int
+check_authority_cert_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
+ (void)options;
/* 1e. Periodically, if we're a v3 authority, we check whether our cert is
* close to expiring and warn the admin if it is. */
- if (time_to.check_v3_certificate < now) {
- v3_authority_check_key_expiry();
+ v3_authority_check_key_expiry();
#define CHECK_V3_CERTIFICATE_INTERVAL (5*60)
- time_to.check_v3_certificate = now + CHECK_V3_CERTIFICATE_INTERVAL;
- }
+ return CHECK_V3_CERTIFICATE_INTERVAL;
+}
+static int
+check_expired_networkstatus_callback(time_t now, const or_options_t *options)
+{
+ (void)options;
/* 1f. Check whether our networkstatus has expired.
*/
- if (time_to.check_for_expired_networkstatus < now) {
- networkstatus_t *ns = networkstatus_get_latest_consensus();
- /*XXXX RD: This value needs to be the same as REASONABLY_LIVE_TIME in
- * networkstatus_get_reasonably_live_consensus(), but that value is way
- * way too high. Arma: is the bridge issue there resolved yet? -NM */
+ networkstatus_t *ns = networkstatus_get_latest_consensus();
+ /*XXXX RD: This value needs to be the same as REASONABLY_LIVE_TIME in
+ * networkstatus_get_reasonably_live_consensus(), but that value is way
+ * way too high. Arma: is the bridge issue there resolved yet? -NM */
#define NS_EXPIRY_SLOP (24*60*60)
- if (ns && ns->valid_until < now+NS_EXPIRY_SLOP &&
- router_have_minimum_dir_info()) {
- router_dir_info_changed();
- }
-#define CHECK_EXPIRED_NS_INTERVAL (2*60)
- time_to.check_for_expired_networkstatus = now + CHECK_EXPIRED_NS_INTERVAL;
+ if (ns && ns->valid_until < now+NS_EXPIRY_SLOP &&
+ router_have_minimum_dir_info()) {
+ router_dir_info_changed();
}
+#define CHECK_EXPIRED_NS_INTERVAL (2*60)
+ return CHECK_EXPIRED_NS_INTERVAL;
+}
+static int
+write_stats_file_callback(time_t now, const or_options_t *options)
+{
/* 1g. Check whether we should write statistics to disk.
*/
- if (time_to.write_stats_files < now) {
#define CHECK_WRITE_STATS_INTERVAL (60*60)
- time_t next_time_to_write_stats_files = (time_to.write_stats_files > 0 ?
- time_to.write_stats_files : now) + CHECK_WRITE_STATS_INTERVAL;
- if (options->CellStatistics) {
- time_t next_write =
- rep_hist_buffer_stats_write(time_to.write_stats_files);
- if (next_write && next_write < next_time_to_write_stats_files)
- next_time_to_write_stats_files = next_write;
- }
- if (options->DirReqStatistics) {
- time_t next_write = geoip_dirreq_stats_write(time_to.write_stats_files);
- if (next_write && next_write < next_time_to_write_stats_files)
- next_time_to_write_stats_files = next_write;
- }
- if (options->EntryStatistics) {
- time_t next_write = geoip_entry_stats_write(time_to.write_stats_files);
- if (next_write && next_write < next_time_to_write_stats_files)
- next_time_to_write_stats_files = next_write;
- }
- if (options->HiddenServiceStatistics) {
- time_t next_write = rep_hist_hs_stats_write(time_to.write_stats_files);
- if (next_write && next_write < next_time_to_write_stats_files)
- next_time_to_write_stats_files = next_write;
- }
- if (options->ExitPortStatistics) {
- time_t next_write = rep_hist_exit_stats_write(time_to.write_stats_files);
- if (next_write && next_write < next_time_to_write_stats_files)
- next_time_to_write_stats_files = next_write;
- }
- if (options->ConnDirectionStatistics) {
- time_t next_write = rep_hist_conn_stats_write(time_to.write_stats_files);
- if (next_write && next_write < next_time_to_write_stats_files)
- next_time_to_write_stats_files = next_write;
- }
- if (options->BridgeAuthoritativeDir) {
- time_t next_write = rep_hist_desc_stats_write(time_to.write_stats_files);
- if (next_write && next_write < next_time_to_write_stats_files)
- next_time_to_write_stats_files = next_write;
- }
- time_to.write_stats_files = next_time_to_write_stats_files;
+ time_t next_time_to_write_stats_files = now + CHECK_WRITE_STATS_INTERVAL;
+ if (options->CellStatistics) {
+ time_t next_write =
+ rep_hist_buffer_stats_write(now);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
+ }
+ if (options->DirReqStatistics) {
+ time_t next_write = geoip_dirreq_stats_write(now);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
+ }
+ if (options->EntryStatistics) {
+ time_t next_write = geoip_entry_stats_write(now);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
+ }
+ if (options->HiddenServiceStatistics) {
+ time_t next_write = rep_hist_hs_stats_write(now);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
+ }
+ if (options->ExitPortStatistics) {
+ time_t next_write = rep_hist_exit_stats_write(now);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
+ }
+ if (options->ConnDirectionStatistics) {
+ time_t next_write = rep_hist_conn_stats_write(now);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
+ }
+ if (options->BridgeAuthoritativeDir) {
+ time_t next_write = rep_hist_desc_stats_write(now);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
}
+ return safe_timer_diff(now, next_time_to_write_stats_files);
+}
+
+static int
+record_bridge_stats_callback(time_t now, const or_options_t *options)
+{
+ static int should_init_bridge_stats = 1;
+
/* 1h. Check whether we should write bridge statistics to disk.
*/
if (should_record_bridge_info(options)) {
- if (time_to.write_bridge_stats < now) {
- if (should_init_bridge_stats) {
- /* (Re-)initialize bridge statistics. */
+ if (should_init_bridge_stats) {
+ /* (Re-)initialize bridge statistics. */
geoip_bridge_stats_init(now);
- time_to.write_bridge_stats = now + WRITE_STATS_INTERVAL;
should_init_bridge_stats = 0;
- } else {
- /* Possibly write bridge statistics to disk and ask when to write
- * them next time. */
- time_to.write_bridge_stats = geoip_bridge_stats_write(
- time_to.write_bridge_stats);
- }
+ return WRITE_STATS_INTERVAL;
+ } else {
+ /* Possibly write bridge statistics to disk and ask when to write
+ * them next time. */
+ time_t next = geoip_bridge_stats_write(now);
+ return safe_timer_diff(now, next);
}
} else if (!should_init_bridge_stats) {
/* Bridge mode was turned off. Ensure that stats are re-initialized
* next time bridge mode is turned on. */
should_init_bridge_stats = 1;
}
+ return PERIODIC_EVENT_NO_UPDATE;
+}
+static int
+clean_caches_callback(time_t now, const or_options_t *options)
+{
/* Remove old information from rephist and the rend cache. */
- if (time_to.clean_caches < now) {
- rep_history_clean(now - options->RephistTrackTime);
- rend_cache_clean(now);
- rend_cache_clean_v2_descs_as_dir(now, 0);
- microdesc_cache_rebuild(NULL, 0);
+ rep_history_clean(now - options->RephistTrackTime);
+ rend_cache_clean(now, REND_CACHE_TYPE_CLIENT);
+ rend_cache_clean(now, REND_CACHE_TYPE_SERVICE);
+ rend_cache_clean_v2_descs_as_dir(now, 0);
+ microdesc_cache_rebuild(NULL, 0);
#define CLEAN_CACHES_INTERVAL (30*60)
- time_to.clean_caches = now + CLEAN_CACHES_INTERVAL;
- }
+ return CLEAN_CACHES_INTERVAL;
+}
+
+static int
+rend_cache_failure_clean_callback(time_t now, const or_options_t *options)
+{
+ (void)options;
/* We don't keep entries that are more than five minutes old so we try to
* clean it as soon as we can since we want to make sure the client waits
* as little as possible for reachability reasons. */
rend_cache_failure_clean(now);
+ return 30;
+}
+static int
+retry_dns_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
#define RETRY_DNS_INTERVAL (10*60)
/* If we're a server and initializing dns failed, retry periodically. */
- if (time_to.retry_dns_init < now) {
- time_to.retry_dns_init = now + RETRY_DNS_INTERVAL;
- if (is_server && has_dns_init_failed())
- dns_init();
- }
+ if (server_mode(options) && has_dns_init_failed())
+ dns_init();
+ return RETRY_DNS_INTERVAL;
+}
/* 2. Periodically, we consider force-uploading our descriptor
* (if we've passed our internal checks). */
+static int
+check_descriptor_callback(time_t now, const or_options_t *options)
+{
/** How often do we check whether part of our router info has changed in a
* way that would require an upload? That includes checking whether our IP
* address has changed. */
@@ -1547,185 +1802,178 @@ run_scheduled_events(time_t now)
/* 2b. Once per minute, regenerate and upload the descriptor if the old
* one is inaccurate. */
- if (time_to.check_descriptor < now && !options->DisableNetwork) {
- static int dirport_reachability_count = 0;
- time_to.check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL;
+ if (!options->DisableNetwork) {
check_descriptor_bandwidth_changed(now);
check_descriptor_ipaddress_changed(now);
mark_my_descriptor_dirty_if_too_old(now);
consider_publishable_server(0);
- /* also, check religiously for reachability, if it's within the first
- * 20 minutes of our uptime. */
- if (is_server &&
- (have_completed_a_circuit() || !any_predicted_circuits(now)) &&
- !we_are_hibernating()) {
- if (stats_n_seconds_working < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) {
- consider_testing_reachability(1, dirport_reachability_count==0);
- if (++dirport_reachability_count > 5)
- dirport_reachability_count = 0;
- } else if (time_to.recheck_bandwidth < now) {
- /* If we haven't checked for 12 hours and our bandwidth estimate is
- * low, do another bandwidth test. This is especially important for
- * bridges, since they might go long periods without much use. */
- const routerinfo_t *me = router_get_my_routerinfo();
- if (time_to.recheck_bandwidth && me &&
- me->bandwidthcapacity < me->bandwidthrate &&
- me->bandwidthcapacity < 51200) {
- reset_bandwidth_test();
- }
-#define BANDWIDTH_RECHECK_INTERVAL (12*60*60)
- time_to.recheck_bandwidth = now + BANDWIDTH_RECHECK_INTERVAL;
- }
- }
-
/* 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();
}
- /* 2c. Every minute (or every second if TestingTorNetwork), check
- * whether we want to download any networkstatus documents. */
+ return CHECK_DESCRIPTOR_INTERVAL;
+}
-/* How often do we check whether we should download network status
- * documents? */
-#define networkstatus_dl_check_interval(o) ((o)->TestingTorNetwork ? 1 : 60)
+static int
+check_for_reachability_bw_callback(time_t now, const or_options_t *options)
+{
+ /* XXXX This whole thing was stuck in the middle of what is now
+ * XXXX check_descriptor_callback. I'm not sure it's right. */
- if (!should_delay_dir_fetches(options, NULL) &&
- time_to.download_networkstatus < now) {
- time_to.download_networkstatus =
- now + networkstatus_dl_check_interval(options);
- update_networkstatus_downloads(now);
+ static int dirport_reachability_count = 0;
+ /* also, check religiously for reachability, if it's within the first
+ * 20 minutes of our uptime. */
+ if (server_mode(options) &&
+ (have_completed_a_circuit() || !any_predicted_circuits(now)) &&
+ !we_are_hibernating()) {
+ if (stats_n_seconds_working < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) {
+ consider_testing_reachability(1, dirport_reachability_count==0);
+ if (++dirport_reachability_count > 5)
+ dirport_reachability_count = 0;
+ return 1;
+ } else {
+ /* If we haven't checked for 12 hours and our bandwidth estimate is
+ * low, do another bandwidth test. This is especially important for
+ * bridges, since they might go long periods without much use. */
+ const routerinfo_t *me = router_get_my_routerinfo();
+ static int first_time = 1;
+ if (!first_time && me &&
+ me->bandwidthcapacity < me->bandwidthrate &&
+ me->bandwidthcapacity < 51200) {
+ reset_bandwidth_test();
+ }
+ first_time = 0;
+#define BANDWIDTH_RECHECK_INTERVAL (12*60*60)
+ return BANDWIDTH_RECHECK_INTERVAL;
+ }
}
+ return CHECK_DESCRIPTOR_INTERVAL;
+}
- /* 2c. Let directory voting happen. */
- if (authdir_mode_v3(options))
- dirvote_act(options, now);
+static int
+fetch_networkstatus_callback(time_t now, const or_options_t *options)
+{
+ /* 2c. Every minute (or every second if TestingTorNetwork, or during
+ * client bootstrap), check whether we want to download any networkstatus
+ * documents. */
- /* 3a. Every second, we examine pending circuits and prune the
- * ones which have been pending for more than a few seconds.
- * We do this before step 4, so it can try building more if
- * it's not comfortable with the number of available circuits.
- */
- /* (If our circuit build timeout can ever become lower than a second (which
- * it can't, currently), we should do this more often.) */
- circuit_expire_building();
+ /* How often do we check whether we should download network status
+ * documents? */
+ const int we_are_bootstrapping = networkstatus_consensus_is_boostrapping(
+ now);
+ const int prefer_mirrors = !directory_fetches_from_authorities(
+ get_options());
+ int networkstatus_dl_check_interval = 60;
+ /* check more often when testing, or when bootstrapping from mirrors
+ * (connection limits prevent too many connections being made) */
+ if (options->TestingTorNetwork
+ || (we_are_bootstrapping && prefer_mirrors)) {
+ networkstatus_dl_check_interval = 1;
+ }
- /* 3b. Also look at pending streams and prune the ones that 'began'
- * a long time ago but haven't gotten a 'connected' yet.
- * Do this before step 4, so we can put them back into pending
- * state to be picked up by the new circuit.
- */
- connection_ap_expire_beginning();
+ if (should_delay_dir_fetches(options, NULL))
+ return PERIODIC_EVENT_NO_UPDATE;
- /* 3c. And expire connections that we've held open for too long.
- */
- connection_expire_held_open();
+ update_networkstatus_downloads(now);
+ return networkstatus_dl_check_interval;
+}
+static int
+retry_listeners_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
+ (void)options;
/* 3d. And every 60 seconds, we relaunch listeners if any died. */
- if (!net_is_disabled() && time_to.check_listeners < now) {
+ if (!net_is_disabled()) {
retry_all_listeners(NULL, NULL, 0);
- time_to.check_listeners = now+60;
- }
-
- /* 4. Every second, we try a new circuit if there are no valid
- * circuits. Every NewCircuitPeriod seconds, we expire circuits
- * that became dirty more than MaxCircuitDirtiness seconds ago,
- * and we make a new circ if there are no clean circuits.
- */
- have_dir_info = router_have_minimum_dir_info();
- if (have_dir_info && !net_is_disabled()) {
- circuit_build_needed_circs(now);
- } else {
- circuit_expire_old_circs_as_needed(now);
+ return 60;
}
+ return PERIODIC_EVENT_NO_UPDATE;
+}
- /* every 10 seconds, but not at the same second as other such events */
- if (now % 10 == 5)
- circuit_expire_old_circuits_serverside(now);
-
- /* 5. We do housekeeping for each connection... */
- connection_or_set_bad_connections(NULL, 0);
- for (i=0;i<smartlist_len(connection_array);i++) {
- run_connection_housekeeping(i, now);
- }
-
- /* 6. And remove any marked circuits... */
- circuit_close_all_marked();
-
- /* 7. And upload service descriptors if necessary. */
- if (have_completed_a_circuit() && !net_is_disabled()) {
- rend_consider_services_upload(now);
- rend_consider_descriptor_republication();
- }
-
- /* 8. and blow away any connections that need to die. have to do this now,
- * because if we marked a conn for close and left its socket -1, then
- * we'll pass it to poll/select and bad things will happen.
- */
- close_closeable_connections();
-
- /* 8b. And if anything in our state is ready to get flushed to disk, we
- * flush it. */
- or_state_save(now);
-
- /* 8c. Do channel cleanup just like for connections */
- channel_run_cleanup();
- channel_listener_run_cleanup();
+static int
+expire_old_ciruits_serverside_callback(time_t now, const or_options_t *options)
+{
+ (void)options;
+ /* every 11 seconds, so not usually the same second as other such events */
+ circuit_expire_old_circuits_serverside(now);
+ return 11;
+}
+static int
+check_dns_honesty_callback(time_t now, const or_options_t *options)
+{
+ (void)now;
/* 9. and if we're an exit node, check whether our DNS is telling stories
* to us. */
- if (!net_is_disabled() &&
- public_server_mode(options) &&
- time_to.check_for_correct_dns < now &&
- ! router_my_exit_policy_is_reject_star()) {
- if (!time_to.check_for_correct_dns) {
- time_to.check_for_correct_dns =
- crypto_rand_time_range(now + 60, now + 180);
- } else {
- dns_launch_correctness_checks();
- time_to.check_for_correct_dns = now + 12*3600 +
- crypto_rand_int(12*3600);
- }
+ if (net_is_disabled() ||
+ ! public_server_mode(options) ||
+ router_my_exit_policy_is_reject_star())
+ return PERIODIC_EVENT_NO_UPDATE;
+
+ static int first_time = 1;
+ if (first_time) {
+ /* Don't launch right when we start */
+ first_time = 0;
+ return crypto_rand_int_range(60, 180);
}
+ dns_launch_correctness_checks();
+ return 12*3600 + crypto_rand_int(12*3600);
+}
+
+static int
+write_bridge_ns_callback(time_t now, const or_options_t *options)
+{
/* 10. write bridge networkstatus file to disk */
- if (options->BridgeAuthoritativeDir &&
- time_to.write_bridge_status_file < now) {
+ if (options->BridgeAuthoritativeDir) {
networkstatus_dump_bridge_status_to_file(now);
#define BRIDGE_STATUSFILE_INTERVAL (30*60)
- time_to.write_bridge_status_file = now+BRIDGE_STATUSFILE_INTERVAL;
+ return BRIDGE_STATUSFILE_INTERVAL;
}
+ return PERIODIC_EVENT_NO_UPDATE;
+}
+static int
+check_fw_helper_app_callback(time_t now, const or_options_t *options)
+{
+ if (net_is_disabled() ||
+ ! server_mode(options) ||
+ ! options->PortForwarding) {
+ return PERIODIC_EVENT_NO_UPDATE;
+ }
/* 11. check the port forwarding app */
- if (!net_is_disabled() &&
- time_to.check_port_forwarding < now &&
- options->PortForwarding &&
- is_server) {
+
#define PORT_FORWARDING_CHECK_INTERVAL 5
- smartlist_t *ports_to_forward = get_list_of_ports_to_forward();
- if (ports_to_forward) {
- tor_check_port_forwarding(options->PortForwardingHelper,
- ports_to_forward,
- now);
-
- SMARTLIST_FOREACH(ports_to_forward, char *, cp, tor_free(cp));
- smartlist_free(ports_to_forward);
- }
- time_to.check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL;
- }
+ smartlist_t *ports_to_forward = get_list_of_ports_to_forward();
+ if (ports_to_forward) {
+ tor_check_port_forwarding(options->PortForwardingHelper,
+ ports_to_forward,
+ now);
- /* 11b. check pending unconfigured managed proxies */
- if (!net_is_disabled() && pt_proxies_configuration_pending())
- pt_configure_remaining_proxies();
+ SMARTLIST_FOREACH(ports_to_forward, char *, cp, tor_free(cp));
+ smartlist_free(ports_to_forward);
+ }
+ return PORT_FORWARDING_CHECK_INTERVAL;
+}
+static int
+heartbeat_callback(time_t now, const or_options_t *options)
+{
+ static int first = 1;
/* 12. write the heartbeat message */
- if (options->HeartbeatPeriod &&
- time_to.next_heartbeat <= now) {
- if (time_to.next_heartbeat) /* don't log the first heartbeat */
- log_heartbeat(now);
- time_to.next_heartbeat = now+options->HeartbeatPeriod;
+ if (first) {
+ first = 0; /* Skip the first one. */
+ } else {
+ log_heartbeat(now);
}
+ /* XXXX This isn't such a good way to handle possible changes in the
+ * callback event */
+ return options->HeartbeatPeriod;
}
/** Timer: used to invoke second_elapsed_callback() once per second. */
@@ -1921,7 +2169,10 @@ got_libevent_error(void)
void
ip_address_changed(int at_interface)
{
- int server = server_mode(get_options());
+ const or_options_t *options = get_options();
+ int server = server_mode(options);
+ int exit_reject_private = (server && options->ExitRelay
+ && options->ExitPolicyRejectPrivate);
if (at_interface) {
if (! server) {
@@ -1935,10 +2186,15 @@ ip_address_changed(int at_interface)
reset_bandwidth_test();
stats_n_seconds_working = 0;
router_reset_reachability();
- mark_my_descriptor_dirty("IP address changed");
}
}
+ /* Exit relays incorporate interface addresses in their exit policies when
+ * ExitPolicyRejectPrivate is set */
+ if (exit_reject_private || (server && !at_interface)) {
+ mark_my_descriptor_dirty("IP address changed");
+ }
+
dns_servers_relaunch_checks();
}
@@ -1949,7 +2205,10 @@ dns_servers_relaunch_checks(void)
{
if (server_mode(get_options())) {
dns_reset_correctness_checks();
- time_to.check_for_correct_dns = 0;
+ if (periodic_events_initialized) {
+ tor_assert(check_dns_honesty_event);
+ periodic_event_reschedule(check_dns_honesty_event);
+ }
}
}
@@ -2043,6 +2302,13 @@ do_main_loop(void)
{
time_t now;
+ /* initialize the periodic events first, so that code that depends on the
+ * events being present does not assert.
+ */
+ if (! periodic_events_initialized) {
+ initialize_periodic_events();
+ }
+
/* initialize dns resolve map, spawn workers if needed */
if (dns_init() < 0) {
if (get_options()->ServerDNSAllowBrokenConfig)
@@ -2125,7 +2391,7 @@ do_main_loop(void)
* appropriate.)
*/
now = time(NULL);
- directory_info_has_arrived(now, 1);
+ directory_info_has_arrived(now, 1, 0);
if (server_mode(get_options())) {
/* launch cpuworkers. Need to do this *after* we've read the onion key. */
@@ -2253,6 +2519,11 @@ run_main_loop_once(void)
}
}
+ /* This will be pretty fast if nothing new is pending. Note that this gets
+ * called once per libevent loop, which will make it happen once per group
+ * of events that fire, or once per second. */
+ connection_ap_attach_pending(0);
+
return 1;
}
@@ -2827,6 +3098,7 @@ tor_free_all(int postfork)
channel_tls_free_all();
channel_free_all();
connection_free_all();
+ connection_edge_free_all();
scheduler_free_all();
memarea_clear_freelist();
nodelist_free_all();
@@ -2853,6 +3125,7 @@ tor_free_all(int postfork)
smartlist_free(closeable_connection_lst);
smartlist_free(active_linked_connection_lst);
periodic_timer_free(second_timer);
+ teardown_periodic_events();
#ifndef USE_BUFFEREVENTS
periodic_timer_free(refill_timer);
#endif
diff --git a/src/or/main.h b/src/or/main.h
index 447d3f4eca..74071166e8 100644
--- a/src/or/main.h
+++ b/src/or/main.h
@@ -48,7 +48,7 @@ MOCK_DECL(void,connection_start_writing,(connection_t *conn));
void connection_stop_reading_from_linked_conn(connection_t *conn);
void directory_all_unreachable(time_t now);
-void directory_info_has_arrived(time_t now, int from_cache);
+void directory_info_has_arrived(time_t now, int from_cache, int suppress_logs);
void ip_address_changed(int at_interface);
void dns_servers_relaunch_checks(void);
@@ -78,6 +78,8 @@ int tor_init(int argc, char **argv);
#ifdef MAIN_PRIVATE
STATIC void init_connection_lists(void);
STATIC void close_closeable_connections(void);
+STATIC void initialize_periodic_events(void);
+STATIC void teardown_periodic_events(void);
#endif
#endif
diff --git a/src/or/microdesc.c b/src/or/microdesc.c
index a9bab3ddc6..900a50eb41 100644
--- a/src/or/microdesc.c
+++ b/src/or/microdesc.c
@@ -47,14 +47,14 @@ struct microdesc_cache_t {
static microdesc_cache_t *get_microdesc_cache_noload(void);
/** Helper: computes a hash of <b>md</b> to place it in a hash table. */
-static INLINE unsigned int
+static inline unsigned int
microdesc_hash_(microdesc_t *md)
{
return (unsigned) siphash24g(md->digest, sizeof(md->digest));
}
/** Helper: compares <b>a</b> and </b> for equality for hash-table purposes. */
-static INLINE int
+static inline int
microdesc_eq_(microdesc_t *a, microdesc_t *b)
{
return tor_memeq(a->digest, b->digest, DIGEST256_LEN);
@@ -840,7 +840,7 @@ microdesc_list_missing_digest256(networkstatus_t *ns, microdesc_cache_t *cache,
/** Launch download requests for microdescriptors as appropriate.
*
* Specifically, we should launch download requests if we are configured to
- * download mirodescriptors, and there are some microdescriptors listed the
+ * download mirodescriptors, and there are some microdescriptors listed in the
* current microdesc consensus that we don't have, and either we never asked
* for them, or we failed to download them but we're willing to retry.
*/
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index 71a2c0f121..11ee618197 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -85,8 +85,30 @@ static time_t time_to_download_next_consensus[N_CONSENSUS_FLAVORS];
/** Download status for the current consensus networkstatus. */
static download_status_t consensus_dl_status[N_CONSENSUS_FLAVORS] =
{
- { 0, 0, DL_SCHED_CONSENSUS },
- { 0, 0, DL_SCHED_CONSENSUS },
+ { 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER,
+ DL_SCHED_INCREMENT_FAILURE },
+ { 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER,
+ DL_SCHED_INCREMENT_FAILURE },
+ };
+
+#define N_CONSENSUS_BOOTSTRAP_SCHEDULES 2
+#define CONSENSUS_BOOTSTRAP_SOURCE_AUTHORITY 0
+#define CONSENSUS_BOOTSTRAP_SOURCE_ANY_DIRSERVER 1
+
+/* Using DL_SCHED_INCREMENT_ATTEMPT on these schedules means that
+ * download_status_increment_failure won't increment these entries.
+ * However, any bootstrap connection failures that occur after we have
+ * a valid consensus will count against the failure counts on the non-bootstrap
+ * schedules. There should only be one of these, as all the others will have
+ * been cancelled. (This doesn't seem to be a significant issue.) */
+static download_status_t
+ consensus_bootstrap_dl_status[N_CONSENSUS_BOOTSTRAP_SCHEDULES] =
+ {
+ { 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_AUTHORITY,
+ DL_SCHED_INCREMENT_ATTEMPT },
+ /* During bootstrap, DL_WANT_ANY_DIRSERVER means "use fallbacks". */
+ { 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER,
+ DL_SCHED_INCREMENT_ATTEMPT },
};
/** True iff we have logged a warning about this OR's version being older than
@@ -97,6 +119,10 @@ static int have_warned_about_old_version = 0;
static int have_warned_about_new_version = 0;
static void routerstatus_list_update_named_server_map(void);
+static void update_consensus_bootstrap_multiple_downloads(
+ time_t now,
+ const or_options_t *options,
+ int we_are_bootstrapping);
/** Forget that we've warned about anything networkstatus-related, so we will
* give fresh warnings if the same behavior happens again. */
@@ -122,6 +148,9 @@ networkstatus_reset_download_failures(void)
for (i=0; i < N_CONSENSUS_FLAVORS; ++i)
download_status_reset(&consensus_dl_status[i]);
+
+ for (i=0; i < N_CONSENSUS_BOOTSTRAP_SCHEDULES; ++i)
+ download_status_reset(&consensus_bootstrap_dl_status[i]);
}
/** Read every cached v3 consensus networkstatus from the disk. */
@@ -734,6 +763,55 @@ we_want_to_fetch_flavor(const or_options_t *options, int flavor)
* fetching certs before we check whether there is a better one? */
#define DELAY_WHILE_FETCHING_CERTS (20*60)
+/* Check if a downloaded consensus flavor should still wait for certificates
+ * to download now.
+ * If so, return 1. If not, fail dls and return 0. */
+static int
+check_consensus_waiting_for_certs(int flavor, time_t now,
+ download_status_t *dls)
+{
+ consensus_waiting_for_certs_t *waiting;
+
+ /* We should always have a known flavor, because we_want_to_fetch_flavor()
+ * filters out unknown flavors. */
+ tor_assert(flavor >= 0 && flavor < N_CONSENSUS_FLAVORS);
+
+ waiting = &consensus_waiting_for_certs[flavor];
+ if (waiting->consensus) {
+ /* XXXX make sure this doesn't delay sane downloads. */
+ if (waiting->set_at + DELAY_WHILE_FETCHING_CERTS > now) {
+ return 1;
+ } else {
+ if (!waiting->dl_failed) {
+ download_status_failed(dls, 0);
+ waiting->dl_failed=1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Return the maximum download tries for a consensus, based on options and
+ * whether we_are_bootstrapping. */
+static int
+consensus_max_download_tries(const or_options_t *options,
+ int we_are_bootstrapping)
+{
+ int use_fallbacks = networkstatus_consensus_can_use_extra_fallbacks(options);
+
+ if (we_are_bootstrapping) {
+ if (use_fallbacks) {
+ return options->TestingClientBootstrapConsensusMaxDownloadTries;
+ } else {
+ return
+ options->TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries;
+ }
+ }
+
+ return options->TestingConsensusMaxDownloadTries;
+}
+
/** If we want to download a fresh consensus, launch a new download as
* appropriate. */
static void
@@ -741,12 +819,19 @@ update_consensus_networkstatus_downloads(time_t now)
{
int i;
const or_options_t *options = get_options();
+ const int we_are_bootstrapping = networkstatus_consensus_is_boostrapping(
+ now);
+ const int use_multi_conn =
+ networkstatus_consensus_can_use_multiple_directories(options);
+
+ if (should_delay_dir_fetches(options, NULL))
+ return;
for (i=0; i < N_CONSENSUS_FLAVORS; ++i) {
/* XXXX need some way to download unknown flavors if we are caching. */
const char *resource;
- consensus_waiting_for_certs_t *waiting;
networkstatus_t *c;
+ int max_in_progress_conns = 1;
if (! we_want_to_fetch_flavor(options, i))
continue;
@@ -762,35 +847,166 @@ update_consensus_networkstatus_downloads(time_t now)
resource = networkstatus_get_flavor_name(i);
- /* Let's make sure we remembered to update consensus_dl_status */
- tor_assert(consensus_dl_status[i].schedule == DL_SCHED_CONSENSUS);
+ /* Check if we already have enough connections in progress */
+ if (we_are_bootstrapping) {
+ max_in_progress_conns =
+ options->TestingClientBootstrapConsensusMaxInProgressTries;
+ }
+ if (connection_dir_count_by_purpose_and_resource(
+ DIR_PURPOSE_FETCH_CONSENSUS,
+ resource)
+ >= max_in_progress_conns) {
+ continue;
+ }
- if (!download_status_is_ready(&consensus_dl_status[i], now,
- options->TestingConsensusMaxDownloadTries))
- continue; /* We failed downloading a consensus too recently. */
- if (connection_dir_get_by_purpose_and_resource(
- DIR_PURPOSE_FETCH_CONSENSUS, resource))
- continue; /* There's an in-progress download.*/
+ /* Check if we want to launch another download for a usable consensus.
+ * Only used during bootstrap. */
+ if (we_are_bootstrapping && use_multi_conn
+ && i == usable_consensus_flavor()) {
+
+ /* Check if we're already downloading a usable consensus */
+ int consens_conn_count =
+ connection_dir_count_by_purpose_and_resource(
+ DIR_PURPOSE_FETCH_CONSENSUS,
+ resource);
+ int connect_consens_conn_count =
+ connection_dir_count_by_purpose_resource_and_state(
+ DIR_PURPOSE_FETCH_CONSENSUS,
+ resource,
+ DIR_CONN_STATE_CONNECTING);
+
+ if (i == usable_consensus_flavor()
+ && connect_consens_conn_count < consens_conn_count) {
+ continue;
+ }
- waiting = &consensus_waiting_for_certs[i];
- if (waiting->consensus) {
- /* XXXX make sure this doesn't delay sane downloads. */
- if (waiting->set_at + DELAY_WHILE_FETCHING_CERTS > now) {
- continue; /* We're still getting certs for this one. */
- } else {
- if (!waiting->dl_failed) {
- download_status_failed(&consensus_dl_status[i], 0);
- waiting->dl_failed=1;
- }
+ /* Make multiple connections for a bootstrap consensus download */
+ update_consensus_bootstrap_multiple_downloads(now, options,
+ we_are_bootstrapping);
+ } else {
+ /* Check if we failed downloading a consensus too recently */
+ int max_dl_tries = consensus_max_download_tries(options,
+ we_are_bootstrapping);
+
+ /* Let's make sure we remembered to update consensus_dl_status */
+ tor_assert(consensus_dl_status[i].schedule == DL_SCHED_CONSENSUS);
+
+ if (!download_status_is_ready(&consensus_dl_status[i],
+ now,
+ max_dl_tries)) {
+ continue;
}
+
+ /* Check if we're waiting for certificates to download */
+ if (check_consensus_waiting_for_certs(i, now, &consensus_dl_status[i]))
+ continue;
+
+ /* Try the requested attempt */
+ log_info(LD_DIR, "Launching %s standard networkstatus consensus "
+ "download.", networkstatus_get_flavor_name(i));
+ directory_get_from_dirserver(DIR_PURPOSE_FETCH_CONSENSUS,
+ ROUTER_PURPOSE_GENERAL, resource,
+ PDS_RETRY_IF_NO_SERVERS,
+ consensus_dl_status[i].want_authority);
}
+ }
+}
- log_info(LD_DIR, "Launching %s networkstatus consensus download.",
- networkstatus_get_flavor_name(i));
+/** When we're bootstrapping, launch one or more consensus download
+ * connections, if schedule indicates connection(s) should be made after now.
+ * If is_authority, connect to an authority, otherwise, use a fallback
+ * directory mirror.
+ */
+static void
+update_consensus_bootstrap_attempt_downloads(
+ time_t now,
+ const or_options_t *options,
+ int we_are_bootstrapping,
+ download_status_t *dls,
+ download_want_authority_t want_authority)
+{
+ int max_dl_tries = consensus_max_download_tries(options,
+ we_are_bootstrapping);
+ const char *resource = networkstatus_get_flavor_name(
+ usable_consensus_flavor());
+
+ /* Let's make sure we remembered to update schedule */
+ tor_assert(dls->schedule == DL_SCHED_CONSENSUS);
+
+ /* Allow for multiple connections in the same second, if the schedule value
+ * is 0. */
+ while (download_status_is_ready(dls, now, max_dl_tries)) {
+ log_info(LD_DIR, "Launching %s bootstrap %s networkstatus consensus "
+ "download.", resource, (want_authority == DL_WANT_AUTHORITY
+ ? "authority"
+ : "mirror"));
directory_get_from_dirserver(DIR_PURPOSE_FETCH_CONSENSUS,
ROUTER_PURPOSE_GENERAL, resource,
- PDS_RETRY_IF_NO_SERVERS);
+ PDS_RETRY_IF_NO_SERVERS, want_authority);
+ /* schedule the next attempt */
+ download_status_increment_attempt(dls, resource, now);
+ }
+}
+
+/** If we're bootstrapping, check the connection schedules and see if we want
+ * to make additional, potentially concurrent, consensus download
+ * connections.
+ * Only call when bootstrapping, and when we want to make additional
+ * connections. Only nodes that satisfy
+ * networkstatus_consensus_can_use_multiple_directories make additonal
+ * connections.
+ */
+static void
+update_consensus_bootstrap_multiple_downloads(time_t now,
+ const or_options_t *options,
+ int we_are_bootstrapping)
+{
+ const int usable_flavor = usable_consensus_flavor();
+
+ /* make sure we can use multiple connections */
+ if (!networkstatus_consensus_can_use_multiple_directories(options)) {
+ return;
+ }
+
+ /* If we've managed to validate a usable consensus, don't make additonal
+ * connections. */
+ if (!we_are_bootstrapping) {
+ return;
+ }
+
+ /* Launch concurrent consensus download attempt(s) based on the mirror and
+ * authority schedules. Try the mirror first - this makes it slightly more
+ * likely that we'll connect to the fallback first, and then end the
+ * authority connection attempt. */
+
+ /* If a consensus download fails because it's waiting for certificates,
+ * we'll fail both the authority and fallback schedules. This is better than
+ * failing only one of the schedules, and having the other continue
+ * unchecked.
+ */
+
+ /* If we don't have or can't use extra fallbacks, don't try them. */
+ if (networkstatus_consensus_can_use_extra_fallbacks(options)) {
+ download_status_t *dls_f =
+ &consensus_bootstrap_dl_status[CONSENSUS_BOOTSTRAP_SOURCE_ANY_DIRSERVER];
+
+ if (!check_consensus_waiting_for_certs(usable_flavor, now, dls_f)) {
+ /* During bootstrap, DL_WANT_ANY_DIRSERVER means "use fallbacks". */
+ update_consensus_bootstrap_attempt_downloads(now, options,
+ we_are_bootstrapping, dls_f,
+ DL_WANT_ANY_DIRSERVER);
+ }
+ }
+
+ /* Now try an authority. */
+ download_status_t *dls_a =
+ &consensus_bootstrap_dl_status[CONSENSUS_BOOTSTRAP_SOURCE_AUTHORITY];
+
+ if (!check_consensus_waiting_for_certs(usable_flavor, now, dls_a)) {
+ update_consensus_bootstrap_attempt_downloads(now, options,
+ we_are_bootstrapping, dls_a,
+ DL_WANT_AUTHORITY);
}
}
@@ -1057,6 +1273,104 @@ networkstatus_get_reasonably_live_consensus(time_t now, int flavor)
return NULL;
}
+/** Check if we're bootstrapping a consensus download. This means that we are
+ * only using the authorities and fallback directory mirrors to download the
+ * consensus flavour we'll use. */
+int
+networkstatus_consensus_is_boostrapping(time_t now)
+{
+ /* If we don't have a consensus, we must still be bootstrapping */
+ return !networkstatus_get_reasonably_live_consensus(
+ now,
+ usable_consensus_flavor());
+}
+
+/** Check if we can use multiple directories for a consensus download.
+ * Only clients (including bridge relays, which act like clients) benefit
+ * from multiple simultaneous consensus downloads. */
+int
+networkstatus_consensus_can_use_multiple_directories(
+ const or_options_t *options)
+{
+ /* If we are a client, bridge, bridge client, or hidden service */
+ return !public_server_mode(options);
+}
+
+/** Check if we can use fallback directory mirrors for a consensus download.
+ * If we have fallbacks and don't want to fetch from the authorities,
+ * we can use them. */
+int
+networkstatus_consensus_can_use_extra_fallbacks(const or_options_t *options)
+{
+ /* The list length comparisons are a quick way to check if we have any
+ * non-authority fallback directories. If we ever have any authorities that
+ * aren't fallback directories, we will need to change this code. */
+ tor_assert(smartlist_len(router_get_fallback_dir_servers())
+ >= smartlist_len(router_get_trusted_dir_servers()));
+ /* If we don't fetch from the authorities, and we have additional mirrors,
+ * we can use them. */
+ return (!directory_fetches_from_authorities(options)
+ && (smartlist_len(router_get_fallback_dir_servers())
+ > smartlist_len(router_get_trusted_dir_servers())));
+}
+
+/* Check if there is more than 1 consensus connection retrieving the usable
+ * consensus flavor. If so, return 1, if not, return 0.
+ *
+ * During normal operation, Tor only makes one consensus download
+ * connection. But clients can make multiple simultaneous consensus
+ * connections to improve bootstrap speed and reliability.
+ *
+ * If there is more than one connection, we must have connections left
+ * over from bootstrapping. However, some of the connections may have
+ * completed and been cleaned up, so it is not sufficient to check the
+ * return value of this function to see if a client could make multiple
+ * bootstrap connections. Use
+ * networkstatus_consensus_can_use_multiple_directories()
+ * and networkstatus_consensus_is_boostrapping(). */
+int
+networkstatus_consensus_has_excess_connections(void)
+{
+ const char *usable_resource = networkstatus_get_flavor_name(
+ usable_consensus_flavor());
+ const int consens_conn_usable_count =
+ connection_dir_count_by_purpose_and_resource(
+ DIR_PURPOSE_FETCH_CONSENSUS,
+ usable_resource);
+ /* The maximum number of connections we want downloading a usable consensus
+ * Always 1, whether bootstrapping or not. */
+ const int max_expected_consens_conn_usable_count = 1;
+
+ if (consens_conn_usable_count > max_expected_consens_conn_usable_count) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Is tor currently downloading a consensus of the usable flavor? */
+int
+networkstatus_consensus_is_downloading_usable_flavor(void)
+{
+ const char *usable_resource = networkstatus_get_flavor_name(
+ usable_consensus_flavor());
+ const int consens_conn_usable_count =
+ connection_dir_count_by_purpose_and_resource(
+ DIR_PURPOSE_FETCH_CONSENSUS,
+ usable_resource);
+
+ const int connect_consens_conn_usable_count =
+ connection_dir_count_by_purpose_resource_and_state(
+ DIR_PURPOSE_FETCH_CONSENSUS,
+ usable_resource,
+ DIR_CONN_STATE_CONNECTING);
+ if (connect_consens_conn_usable_count < consens_conn_usable_count) {
+ return 1;
+ }
+
+ return 0;
+}
+
/** Given two router status entries for the same router identity, return 1 if
* if the contents have changed between them. Otherwise, return 0. */
static int
@@ -1147,6 +1461,38 @@ networkstatus_copy_old_consensus_info(networkstatus_t *new_c,
} SMARTLIST_FOREACH_JOIN_END(rs_old, rs_new);
}
+#ifdef TOR_UNIT_TESTS
+/**Accept a <b>flavor</b> consensus <b>c</b> without any additional
+ * validation. This is exclusively for unit tests.
+ * We copy any ancillary information from a pre-existing consensus
+ * and then free the current one and replace it with the newly
+ * provided instance. Returns -1 on unrecognized flavor, 0 otherwise.
+ */
+int
+networkstatus_set_current_consensus_from_ns(networkstatus_t *c,
+ const char *flavor)
+{
+ int flav = networkstatus_parse_flavor_name(flavor);
+ switch (flav) {
+ case FLAV_NS:
+ if (current_ns_consensus) {
+ networkstatus_copy_old_consensus_info(c, current_ns_consensus);
+ networkstatus_vote_free(current_ns_consensus);
+ }
+ current_ns_consensus = c;
+ break;
+ case FLAV_MICRODESC:
+ if (current_md_consensus) {
+ networkstatus_copy_old_consensus_info(c, current_md_consensus);
+ networkstatus_vote_free(current_md_consensus);
+ }
+ current_md_consensus = c;
+ break;
+ }
+ return current_md_consensus ? 0 : -1;
+}
+#endif //TOR_UNIT_TESTS
+
/** Try to replace the current cached v3 networkstatus with the one in
* <b>consensus</b>. If we don't have enough certificates to validate it,
* store it in consensus_waiting_for_certs and launch a certificate fetch.
@@ -1180,7 +1526,7 @@ networkstatus_set_current_consensus(const char *consensus,
const unsigned dl_certs = !(flags & NSSET_DONT_DOWNLOAD_CERTS);
const unsigned accept_obsolete = flags & NSSET_ACCEPT_OBSOLETE;
const unsigned require_flavor = flags & NSSET_REQUIRE_FLAVOR;
- const digests_t *current_digests = NULL;
+ const common_digests_t *current_digests = NULL;
consensus_waiting_for_certs_t *waiting = NULL;
time_t current_valid_after = 0;
int free_consensus = 1; /* Free 'c' at the end of the function */
diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h
index d6e9e37013..4eab4d83f8 100644
--- a/src/or/networkstatus.h
+++ b/src/or/networkstatus.h
@@ -70,6 +70,14 @@ MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus_by_flavor,
networkstatus_t *networkstatus_get_live_consensus(time_t now);
networkstatus_t *networkstatus_get_reasonably_live_consensus(time_t now,
int flavor);
+int networkstatus_consensus_is_boostrapping(time_t now);
+int networkstatus_consensus_can_use_multiple_directories(
+ const or_options_t *options);
+int networkstatus_consensus_can_use_extra_fallbacks(
+ const or_options_t *options);
+int networkstatus_consensus_has_excess_connections(void);
+int networkstatus_consensus_is_downloading_usable_flavor(void);
+
#define NSSET_FROM_CACHE 1
#define NSSET_WAS_WAITING_FOR_CERTS 2
#define NSSET_DONT_DOWNLOAD_CERTS 4
@@ -106,6 +114,10 @@ int networkstatus_get_weight_scale_param(networkstatus_t *ns);
#ifdef NETWORKSTATUS_PRIVATE
STATIC void vote_routerstatus_free(vote_routerstatus_t *rs);
+#ifdef TOR_UNIT_TESTS
+STATIC int networkstatus_set_current_consensus_from_ns(networkstatus_t *c,
+ const char *flavor);
+#endif // TOR_UNIT_TESTS
#endif
#endif
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index 2f272a1d56..91353eea89 100644
--- a/src/or/nodelist.c
+++ b/src/or/nodelist.c
@@ -57,13 +57,13 @@ typedef struct nodelist_t {
} nodelist_t;
-static INLINE unsigned int
+static inline unsigned int
node_id_hash(const node_t *node)
{
return (unsigned) siphash24g(node->identity, DIGEST_LEN);
}
-static INLINE unsigned int
+static inline unsigned int
node_id_eq(const node_t *node1, const node_t *node2)
{
return tor_memeq(node1->identity, node2->identity, DIGEST_LEN);
@@ -224,7 +224,6 @@ nodelist_set_consensus(networkstatus_t *ns)
{
const or_options_t *options = get_options();
int authdir = authdir_mode_v3(options);
- int client = !server_mode(options);
init_nodelist();
if (ns->flavor == FLAV_MICRODESC)
@@ -261,7 +260,7 @@ nodelist_set_consensus(networkstatus_t *ns)
node->is_bad_exit = rs->is_bad_exit;
node->is_hs_dir = rs->is_hs_dir;
node->ipv6_preferred = 0;
- if (client && options->ClientPreferIPv6ORPort == 1 &&
+ if (fascist_firewall_prefer_ipv6_orport(options) &&
(tor_addr_is_null(&rs->ipv6_addr) == 0 ||
(node->md && tor_addr_is_null(&node->md->ipv6_addr) == 0)))
node->ipv6_preferred = 1;
@@ -291,7 +290,7 @@ nodelist_set_consensus(networkstatus_t *ns)
}
/** Helper: return true iff a node has a usable amount of information*/
-static INLINE int
+static inline int
node_is_usable(const node_t *node)
{
return (node->rs) || (node->ri);
@@ -644,12 +643,19 @@ node_is_named(const node_t *node)
int
node_is_dir(const node_t *node)
{
- if (node->rs)
- return node->rs->dir_port != 0;
- else if (node->ri)
- return node->ri->dir_port != 0;
- else
+ if (node->rs) {
+ routerstatus_t * rs = node->rs;
+ /* This is true if supports_tunnelled_dir_requests is true which
+ * indicates that we support directory request tunnelled or through the
+ * DirPort. */
+ return rs->is_v2_dir;
+ } else if (node->ri) {
+ routerinfo_t * ri = node->ri;
+ /* Both tunnelled request is supported or DirPort is set. */
+ return ri->supports_tunnelled_dir_requests;
+ } else {
return 0;
+ }
}
/** Return true iff <b>node</b> has either kind of usable descriptor -- that
@@ -754,6 +760,40 @@ node_exit_policy_is_exact(const node_t *node, sa_family_t family)
return 1;
}
+/* Check if the "addr" and port_field fields from r are a valid non-listening
+ * address/port. If so, set valid to true and add a newly allocated
+ * tor_addr_port_t containing "addr" and port_field to sl.
+ * "addr" is an IPv4 host-order address and port_field is a uint16_t.
+ * r is typically a routerinfo_t or routerstatus_t.
+ */
+#define SL_ADD_NEW_IPV4_AP(r, port_field, sl, valid) \
+ STMT_BEGIN \
+ if (tor_addr_port_is_valid_ipv4h((r)->addr, (r)->port_field, 0)) { \
+ valid = 1; \
+ tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); \
+ tor_addr_from_ipv4h(&ap->addr, (r)->addr); \
+ ap->port = (r)->port_field; \
+ smartlist_add((sl), ap); \
+ } \
+ STMT_END
+
+/* Check if the "addr" and port_field fields from r are a valid non-listening
+ * address/port. If so, set valid to true and add a newly allocated
+ * tor_addr_port_t containing "addr" and port_field to sl.
+ * "addr" is a tor_addr_t and port_field is a uint16_t.
+ * r is typically a routerinfo_t or routerstatus_t.
+ */
+#define SL_ADD_NEW_IPV6_AP(r, port_field, sl, valid) \
+ STMT_BEGIN \
+ if (tor_addr_port_is_valid(&(r)->ipv6_addr, (r)->port_field, 0)) { \
+ valid = 1; \
+ tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); \
+ tor_addr_copy(&ap->addr, &(r)->ipv6_addr); \
+ ap->port = (r)->port_field; \
+ smartlist_add((sl), ap); \
+ } \
+ STMT_END
+
/** Return list of tor_addr_port_t with all OR ports (in the sense IP
* addr + TCP port) for <b>node</b>. Caller must free all elements
* using tor_free() and free the list using smartlist_free().
@@ -766,30 +806,38 @@ smartlist_t *
node_get_all_orports(const node_t *node)
{
smartlist_t *sl = smartlist_new();
+ int valid = 0;
+ /* Find a valid IPv4 address and port */
if (node->ri != NULL) {
- if (node->ri->addr != 0) {
- tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t));
- tor_addr_from_ipv4h(&ap->addr, node->ri->addr);
- ap->port = node->ri->or_port;
- smartlist_add(sl, ap);
- }
- if (!tor_addr_is_null(&node->ri->ipv6_addr)) {
- tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t));
- tor_addr_copy(&ap->addr, &node->ri->ipv6_addr);
- ap->port = node->ri->or_port;
- smartlist_add(sl, ap);
- }
- } else if (node->rs != NULL) {
- tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t));
- tor_addr_from_ipv4h(&ap->addr, node->rs->addr);
- ap->port = node->rs->or_port;
- smartlist_add(sl, ap);
+ SL_ADD_NEW_IPV4_AP(node->ri, or_port, sl, valid);
+ }
+
+ /* If we didn't find a valid address/port in the ri, try the rs */
+ if (!valid && node->rs != NULL) {
+ SL_ADD_NEW_IPV4_AP(node->rs, or_port, sl, valid);
+ }
+
+ /* Find a valid IPv6 address and port */
+ valid = 0;
+ if (node->ri != NULL) {
+ SL_ADD_NEW_IPV6_AP(node->ri, ipv6_orport, sl, valid);
+ }
+
+ if (!valid && node->rs != NULL) {
+ SL_ADD_NEW_IPV6_AP(node->rs, ipv6_orport, sl, valid);
+ }
+
+ if (!valid && node->md != NULL) {
+ SL_ADD_NEW_IPV6_AP(node->md, ipv6_orport, sl, valid);
}
return sl;
}
+#undef SL_ADD_NEW_IPV4_AP
+#undef SL_ADD_NEW_IPV6_AP
+
/** Wrapper around node_get_prim_orport for backward
compatibility. */
void
@@ -805,9 +853,13 @@ node_get_addr(const node_t *node, tor_addr_t *addr_out)
uint32_t
node_get_prim_addr_ipv4h(const node_t *node)
{
- if (node->ri) {
+ /* Don't check the ORPort or DirPort, as this function isn't port-specific,
+ * and the node might have a valid IPv4 address, yet have a zero
+ * ORPort or DirPort.
+ */
+ if (node->ri && tor_addr_is_valid_ipv4h(node->ri->addr, 0)) {
return node->ri->addr;
- } else if (node->rs) {
+ } else if (node->rs && tor_addr_is_valid_ipv4h(node->rs->addr, 0)) {
return node->rs->addr;
}
return 0;
@@ -818,13 +870,13 @@ node_get_prim_addr_ipv4h(const node_t *node)
void
node_get_address_string(const node_t *node, char *buf, size_t len)
{
- if (node->ri) {
- strlcpy(buf, fmt_addr32(node->ri->addr), len);
- } else if (node->rs) {
+ uint32_t ipv4_addr = node_get_prim_addr_ipv4h(node);
+
+ if (tor_addr_is_valid_ipv4h(ipv4_addr, 0)) {
tor_addr_t addr;
- tor_addr_from_ipv4h(&addr, node->rs->addr);
+ tor_addr_from_ipv4h(&addr, ipv4_addr);
tor_addr_to_str(buf, &addr, len, 0);
- } else {
+ } else if (len > 0) {
buf[0] = '\0';
}
}
@@ -883,30 +935,83 @@ node_get_declared_family(const node_t *node)
return NULL;
}
+/* Does this node have a valid IPv6 address?
+ * Prefer node_has_ipv6_orport() or node_has_ipv6_dirport() for
+ * checking specific ports. */
+int
+node_has_ipv6_addr(const node_t *node)
+{
+ /* Don't check the ORPort or DirPort, as this function isn't port-specific,
+ * and the node might have a valid IPv6 address, yet have a zero
+ * ORPort or DirPort.
+ */
+ if (node->ri && tor_addr_is_valid(&node->ri->ipv6_addr, 0))
+ return 1;
+ if (node->rs && tor_addr_is_valid(&node->rs->ipv6_addr, 0))
+ return 1;
+ if (node->md && tor_addr_is_valid(&node->md->ipv6_addr, 0))
+ return 1;
+
+ return 0;
+}
+
+/* Does this node have a valid IPv6 ORPort? */
+int
+node_has_ipv6_orport(const node_t *node)
+{
+ tor_addr_port_t ipv6_orport;
+ node_get_pref_ipv6_orport(node, &ipv6_orport);
+ return tor_addr_port_is_valid_ap(&ipv6_orport, 0);
+}
+
+/* Does this node have a valid IPv6 DirPort? */
+int
+node_has_ipv6_dirport(const node_t *node)
+{
+ tor_addr_port_t ipv6_dirport;
+ node_get_pref_ipv6_dirport(node, &ipv6_dirport);
+ return tor_addr_port_is_valid_ap(&ipv6_dirport, 0);
+}
+
/** Return 1 if we prefer the IPv6 address and OR TCP port of
* <b>node</b>, else 0.
*
- * We prefer the IPv6 address if the router has an IPv6 address and
+ * We prefer the IPv6 address if the router has an IPv6 address,
+ * and we can use IPv6 addresses, and:
* i) the node_t says that it prefers IPv6
* or
- * ii) the router has no IPv4 address. */
+ * ii) the router has no IPv4 OR address.
+ *
+ * If you don't have a node, consider looking it up.
+ * If there is no node, use fascist_firewall_prefer_ipv6_orport().
+ */
int
-node_ipv6_preferred(const node_t *node)
+node_ipv6_or_preferred(const node_t *node)
{
+ const or_options_t *options = get_options();
tor_addr_port_t ipv4_addr;
node_assert_ok(node);
- if (node->ipv6_preferred || node_get_prim_orport(node, &ipv4_addr)) {
- if (node->ri)
- return !tor_addr_is_null(&node->ri->ipv6_addr);
- if (node->md)
- return !tor_addr_is_null(&node->md->ipv6_addr);
- if (node->rs)
- return !tor_addr_is_null(&node->rs->ipv6_addr);
+ /* XX/teor - node->ipv6_preferred is set from
+ * fascist_firewall_prefer_ipv6_orport() each time the consensus is loaded.
+ */
+ if (!fascist_firewall_use_ipv6(options)) {
+ return 0;
+ } else if (node->ipv6_preferred || node_get_prim_orport(node, &ipv4_addr)) {
+ return node_has_ipv6_orport(node);
}
return 0;
}
+#define RETURN_IPV4_AP(r, port_field, ap_out) \
+ STMT_BEGIN \
+ if (r && tor_addr_port_is_valid_ipv4h((r)->addr, (r)->port_field, 0)) { \
+ tor_addr_from_ipv4h(&(ap_out)->addr, (r)->addr); \
+ (ap_out)->port = (r)->port_field; \
+ return 0; \
+ } \
+ STMT_END
+
/** Copy the primary (IPv4) OR port (IP address and TCP port) for
* <b>node</b> into *<b>ap_out</b>. Return 0 if a valid address and
* port was copied, else return non-zero.*/
@@ -916,20 +1021,10 @@ node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out)
node_assert_ok(node);
tor_assert(ap_out);
- if (node->ri) {
- if (node->ri->addr == 0 || node->ri->or_port == 0)
- return -1;
- tor_addr_from_ipv4h(&ap_out->addr, node->ri->addr);
- ap_out->port = node->ri->or_port;
- return 0;
- }
- if (node->rs) {
- if (node->rs->addr == 0 || node->rs->or_port == 0)
- return -1;
- tor_addr_from_ipv4h(&ap_out->addr, node->rs->addr);
- ap_out->port = node->rs->or_port;
- return 0;
- }
+ RETURN_IPV4_AP(node->ri, or_port, ap_out);
+ RETURN_IPV4_AP(node->rs, or_port, ap_out);
+ /* Microdescriptors only have an IPv6 address */
+
return -1;
}
@@ -938,21 +1033,12 @@ node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out)
void
node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out)
{
- const or_options_t *options = get_options();
tor_assert(ap_out);
- /* Cheap implementation of config option ClientUseIPv6 -- simply
- don't prefer IPv6 when ClientUseIPv6 is not set and we're not a
- client running with bridges. See #4455 for more on this subject.
-
- Note that this filter is too strict since we're hindering not
- only clients! Erring on the safe side shouldn't be a problem
- though. XXX move this check to where outgoing connections are
- made? -LN */
- if ((options->ClientUseIPv6 || options->UseBridges) &&
- node_ipv6_preferred(node)) {
+ if (node_ipv6_or_preferred(node)) {
node_get_pref_ipv6_orport(node, ap_out);
} else {
+ /* the primary ORPort is always on IPv4 */
node_get_prim_orport(node, ap_out);
}
}
@@ -965,20 +1051,115 @@ node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out)
node_assert_ok(node);
tor_assert(ap_out);
- /* We prefer the microdesc over a potential routerstatus here. They
- are not being synchronised atm so there might be a chance that
- they differ at some point, f.ex. when flipping
- UseMicrodescriptors? -LN */
+ /* Prefer routerstatus over microdesc for consistency with the
+ * fascist_firewall_* functions. Also check if the address or port are valid,
+ * and try another alternative if they are not. */
- if (node->ri) {
+ if (node->ri && tor_addr_port_is_valid(&node->ri->ipv6_addr,
+ node->ri->ipv6_orport, 0)) {
tor_addr_copy(&ap_out->addr, &node->ri->ipv6_addr);
ap_out->port = node->ri->ipv6_orport;
- } else if (node->md) {
+ } else if (node->rs && tor_addr_port_is_valid(&node->rs->ipv6_addr,
+ node->rs->ipv6_orport, 0)) {
+ tor_addr_copy(&ap_out->addr, &node->rs->ipv6_addr);
+ ap_out->port = node->rs->ipv6_orport;
+ } else if (node->md && tor_addr_port_is_valid(&node->md->ipv6_addr,
+ node->md->ipv6_orport, 0)) {
tor_addr_copy(&ap_out->addr, &node->md->ipv6_addr);
ap_out->port = node->md->ipv6_orport;
- } else if (node->rs) {
+ } else {
+ tor_addr_make_null(&ap_out->addr, AF_INET6);
+ ap_out->port = 0;
+ }
+}
+
+/** Return 1 if we prefer the IPv6 address and Dir TCP port of
+ * <b>node</b>, else 0.
+ *
+ * We prefer the IPv6 address if the router has an IPv6 address,
+ * and we can use IPv6 addresses, and:
+ * i) the router has no IPv4 Dir address.
+ * or
+ * ii) our preference is for IPv6 Dir addresses.
+ *
+ * If there is no node, use fascist_firewall_prefer_ipv6_dirport().
+ */
+int
+node_ipv6_dir_preferred(const node_t *node)
+{
+ const or_options_t *options = get_options();
+ tor_addr_port_t ipv4_addr;
+ node_assert_ok(node);
+
+ /* node->ipv6_preferred is set from fascist_firewall_prefer_ipv6_orport(),
+ * so we can't use it to determine DirPort IPv6 preference.
+ * This means that bridge clients will use IPv4 DirPorts by default.
+ */
+ if (!fascist_firewall_use_ipv6(options)) {
+ return 0;
+ } else if (node_get_prim_dirport(node, &ipv4_addr)
+ || fascist_firewall_prefer_ipv6_dirport(get_options())) {
+ return node_has_ipv6_dirport(node);
+ }
+ return 0;
+}
+
+/** Copy the primary (IPv4) Dir port (IP address and TCP port) for
+ * <b>node</b> into *<b>ap_out</b>. Return 0 if a valid address and
+ * port was copied, else return non-zero.*/
+int
+node_get_prim_dirport(const node_t *node, tor_addr_port_t *ap_out)
+{
+ node_assert_ok(node);
+ tor_assert(ap_out);
+
+ RETURN_IPV4_AP(node->ri, dir_port, ap_out);
+ RETURN_IPV4_AP(node->rs, dir_port, ap_out);
+ /* Microdescriptors only have an IPv6 address */
+
+ return -1;
+}
+
+#undef RETURN_IPV4_AP
+
+/** Copy the preferred Dir port (IP address and TCP port) for
+ * <b>node</b> into *<b>ap_out</b>. */
+void
+node_get_pref_dirport(const node_t *node, tor_addr_port_t *ap_out)
+{
+ tor_assert(ap_out);
+
+ if (node_ipv6_dir_preferred(node)) {
+ node_get_pref_ipv6_dirport(node, ap_out);
+ } else {
+ /* the primary DirPort is always on IPv4 */
+ node_get_prim_dirport(node, ap_out);
+ }
+}
+
+/** Copy the preferred IPv6 Dir port (IP address and TCP port) for
+ * <b>node</b> into *<b>ap_out</b>. */
+void
+node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out)
+{
+ node_assert_ok(node);
+ tor_assert(ap_out);
+
+ /* Check if the address or port are valid, and try another alternative if
+ * they are not. Note that microdescriptors have no dir_port. */
+
+ /* Assume IPv4 and IPv6 dirports are the same */
+ if (node->ri && tor_addr_port_is_valid(&node->ri->ipv6_addr,
+ node->ri->dir_port, 0)) {
+ tor_addr_copy(&ap_out->addr, &node->ri->ipv6_addr);
+ ap_out->port = node->ri->dir_port;
+ } else if (node->rs && tor_addr_port_is_valid(&node->rs->ipv6_addr,
+ node->rs->dir_port, 0)) {
tor_addr_copy(&ap_out->addr, &node->rs->ipv6_addr);
- ap_out->port = node->rs->ipv6_orport;
+ ap_out->port = node->rs->dir_port;
+ } else {
+ tor_addr_make_null(&ap_out->addr, AF_INET6);
+ ap_out->port = 0;
}
}
@@ -1021,7 +1202,7 @@ nodelist_refresh_countries(void)
/** Return true iff router1 and router2 have similar enough network addresses
* that we should treat them as being in the same family */
-static INLINE int
+static inline int
addrs_in_same_network_family(const tor_addr_t *a1,
const tor_addr_t *a2)
{
@@ -1045,7 +1226,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 inline int
node_in_nickname_smartlist(const smartlist_t *lst, const node_t *node)
{
if (!lst) return 0;
diff --git a/src/or/nodelist.h b/src/or/nodelist.h
index a131e0dd4e..8271e7532a 100644
--- a/src/or/nodelist.h
+++ b/src/or/nodelist.h
@@ -55,10 +55,20 @@ void node_get_address_string(const node_t *node, char *cp, size_t len);
long node_get_declared_uptime(const node_t *node);
time_t node_get_published_on(const node_t *node);
const smartlist_t *node_get_declared_family(const node_t *node);
-int node_ipv6_preferred(const node_t *node);
+
+int node_has_ipv6_addr(const node_t *node);
+int node_has_ipv6_orport(const node_t *node);
+int node_has_ipv6_dirport(const node_t *node);
+/* Deprecated - use node_ipv6_or_preferred or node_ipv6_dir_preferred */
+#define node_ipv6_preferred(node) node_ipv6_or_preferred(node)
+int node_ipv6_or_preferred(const node_t *node);
int node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out);
void node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out);
void node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out);
+int node_ipv6_dir_preferred(const node_t *node);
+int node_get_prim_dirport(const node_t *node, tor_addr_port_t *ap_out);
+void node_get_pref_dirport(const node_t *node, tor_addr_port_t *ap_out);
+void node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out);
int node_has_curve25519_onion_key(const node_t *node);
MOCK_DECL(smartlist_t *, nodelist_get_list, (void));
diff --git a/src/or/onion_fast.c b/src/or/onion_fast.c
index 7584112570..22bef4eee0 100644
--- a/src/or/onion_fast.c
+++ b/src/or/onion_fast.c
@@ -30,10 +30,7 @@ fast_onionskin_create(fast_handshake_state_t **handshake_state_out,
{
fast_handshake_state_t *s;
*handshake_state_out = s = tor_malloc(sizeof(fast_handshake_state_t));
- if (crypto_rand((char*)s->state, sizeof(s->state)) < 0) {
- tor_free(s);
- return -1;
- }
+ crypto_rand((char*)s->state, sizeof(s->state));
memcpy(handshake_out, s->state, DIGEST_LEN);
return 0;
}
@@ -56,8 +53,7 @@ fast_server_handshake(const uint8_t *key_in, /* DIGEST_LEN bytes */
size_t out_len;
int r = -1;
- if (crypto_rand((char*)handshake_reply_out, DIGEST_LEN)<0)
- return -1;
+ crypto_rand((char*)handshake_reply_out, DIGEST_LEN);
memcpy(tmp, key_in, DIGEST_LEN);
memcpy(tmp+DIGEST_LEN, handshake_reply_out, DIGEST_LEN);
diff --git a/src/or/or.h b/src/or/or.h
index 4496cbcec3..0daa9e7025 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -458,9 +458,11 @@ typedef enum {
#define CIRCUIT_PURPOSE_OR_MIN_ 1
/** OR-side circuit purpose: normal circuit, at OR. */
#define CIRCUIT_PURPOSE_OR 1
-/** OR-side circuit purpose: At OR, from Bob, waiting for intro from Alices. */
+/** OR-side circuit purpose: At OR, from the service, waiting for intro from
+ * clients. */
#define CIRCUIT_PURPOSE_INTRO_POINT 2
-/** OR-side circuit purpose: At OR, from Alice, waiting for Bob. */
+/** OR-side circuit purpose: At OR, from the client, waiting for the service.
+ */
#define CIRCUIT_PURPOSE_REND_POINT_WAITING 3
/** OR-side circuit purpose: At OR, both circuits have this purpose. */
#define CIRCUIT_PURPOSE_REND_ESTABLISHED 4
@@ -479,43 +481,47 @@ typedef enum {
* to becoming open, or they are open and have sent the
* establish_rendezvous cell but haven't received an ack.
* circuits that are c_rend_ready are open and have received a
- * rend ack, but haven't heard from bob yet. if they have a
+ * rend ack, but haven't heard from the service yet. if they have a
* buildstate->pending_final_cpath then they're expecting a
- * cell from bob, else they're not.
+ * cell from the service, else they're not.
* circuits that are c_rend_ready_intro_acked are open, and
* some intro circ has sent its intro and received an ack.
* circuits that are c_rend_joined are open, have heard from
- * bob, and are talking to him.
+ * the service, and are talking to it.
*/
/** Client-side circuit purpose: Normal circuit, with cpath. */
#define CIRCUIT_PURPOSE_C_GENERAL 5
-/** Client-side circuit purpose: at Alice, connecting to intro point. */
+/** Client-side circuit purpose: at the client, connecting to intro point. */
#define CIRCUIT_PURPOSE_C_INTRODUCING 6
-/** Client-side circuit purpose: at Alice, sent INTRODUCE1 to intro point,
+/** Client-side circuit purpose: at the client, sent INTRODUCE1 to intro point,
* waiting for ACK/NAK. */
#define CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT 7
-/** Client-side circuit purpose: at Alice, introduced and acked, closing. */
+/** Client-side circuit purpose: at the client, introduced and acked, closing.
+ */
#define CIRCUIT_PURPOSE_C_INTRODUCE_ACKED 8
-/** Client-side circuit purpose: at Alice, waiting for ack. */
+/** Client-side circuit purpose: at the client, waiting for ack. */
#define CIRCUIT_PURPOSE_C_ESTABLISH_REND 9
-/** Client-side circuit purpose: at Alice, waiting for Bob. */
+/** Client-side circuit purpose: at the client, waiting for the service. */
#define CIRCUIT_PURPOSE_C_REND_READY 10
-/** Client-side circuit purpose: at Alice, waiting for Bob, INTRODUCE
- * has been acknowledged. */
+/** Client-side circuit purpose: at the client, waiting for the service,
+ * INTRODUCE has been acknowledged. */
#define CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED 11
-/** Client-side circuit purpose: at Alice, rendezvous established. */
+/** Client-side circuit purpose: at the client, rendezvous established. */
#define CIRCUIT_PURPOSE_C_REND_JOINED 12
/** This circuit is used for build time measurement only */
#define CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT 13
#define CIRCUIT_PURPOSE_C_MAX_ 13
-/** Hidden-service-side circuit purpose: at Bob, waiting for introductions. */
+/** Hidden-service-side circuit purpose: at the service, waiting for
+ * introductions. */
#define CIRCUIT_PURPOSE_S_ESTABLISH_INTRO 14
-/** Hidden-service-side circuit purpose: at Bob, successfully established
- * intro. */
+/** Hidden-service-side circuit purpose: at the service, successfully
+ * established intro. */
#define CIRCUIT_PURPOSE_S_INTRO 15
-/** Hidden-service-side circuit purpose: at Bob, connecting to rend point. */
+/** Hidden-service-side circuit purpose: at the service, connecting to rend
+ * point. */
#define CIRCUIT_PURPOSE_S_CONNECT_REND 16
-/** Hidden-service-side circuit purpose: at Bob, rendezvous established. */
+/** Hidden-service-side circuit purpose: at the service, rendezvous
+ * established. */
#define CIRCUIT_PURPOSE_S_REND_JOINED 17
/** A testing circuit; not meant to be used for actual traffic. */
#define CIRCUIT_PURPOSE_TESTING 18
@@ -915,18 +921,18 @@ typedef enum {
#define VAR_CELL_MAX_HEADER_SIZE 7
static int get_cell_network_size(int wide_circ_ids);
-static INLINE int get_cell_network_size(int wide_circ_ids)
+static inline int get_cell_network_size(int wide_circ_ids)
{
return wide_circ_ids ? CELL_MAX_NETWORK_SIZE : CELL_MAX_NETWORK_SIZE - 2;
}
static int get_var_cell_header_size(int wide_circ_ids);
-static INLINE int get_var_cell_header_size(int wide_circ_ids)
+static inline int get_var_cell_header_size(int wide_circ_ids)
{
return wide_circ_ids ? VAR_CELL_MAX_HEADER_SIZE :
VAR_CELL_MAX_HEADER_SIZE - 2;
}
static int get_circ_id_size(int wide_circ_ids);
-static INLINE int get_circ_id_size(int wide_circ_ids)
+static inline int get_circ_id_size(int wide_circ_ids)
{
return wide_circ_ids ? 4 : 2;
}
@@ -1302,7 +1308,7 @@ typedef struct connection_t {
* marked.) */
const char *marked_for_close_file; /**< For debugging: in which file were
* we marked for close? */
- char *address; /**< FQDN (or IP) of the guy on the other end.
+ char *address; /**< FQDN (or IP) of the other end.
* strdup into this, because free_connection() frees it. */
/** Another connection that's connected to this one in lieu of a socket. */
struct connection_t *linked_conn;
@@ -1646,6 +1652,13 @@ typedef struct entry_connection_t {
* request that we're going to try to answer. */
struct evdns_server_request *dns_server_request;
+#define DEBUGGING_17659
+
+#ifdef DEBUGGING_17659
+ uint16_t marked_pending_circ_line;
+ const char *marked_pending_circ_file;
+#endif
+
#define NUM_CIRCUITS_LAUNCHED_THRESHOLD 10
/** Number of times we've launched a circuit to handle this stream. If
* it gets too high, that could indicate an inconsistency between our
@@ -1799,38 +1812,38 @@ static control_connection_t *TO_CONTROL_CONN(connection_t *);
* invalid. */
static listener_connection_t *TO_LISTENER_CONN(connection_t *);
-static INLINE or_connection_t *TO_OR_CONN(connection_t *c)
+static inline or_connection_t *TO_OR_CONN(connection_t *c)
{
tor_assert(c->magic == OR_CONNECTION_MAGIC);
return DOWNCAST(or_connection_t, c);
}
-static INLINE dir_connection_t *TO_DIR_CONN(connection_t *c)
+static inline dir_connection_t *TO_DIR_CONN(connection_t *c)
{
tor_assert(c->magic == DIR_CONNECTION_MAGIC);
return DOWNCAST(dir_connection_t, c);
}
-static INLINE edge_connection_t *TO_EDGE_CONN(connection_t *c)
+static inline edge_connection_t *TO_EDGE_CONN(connection_t *c)
{
tor_assert(c->magic == EDGE_CONNECTION_MAGIC ||
c->magic == ENTRY_CONNECTION_MAGIC);
return DOWNCAST(edge_connection_t, c);
}
-static INLINE entry_connection_t *TO_ENTRY_CONN(connection_t *c)
+static inline entry_connection_t *TO_ENTRY_CONN(connection_t *c)
{
tor_assert(c->magic == ENTRY_CONNECTION_MAGIC);
return (entry_connection_t*) SUBTYPE_P(c, entry_connection_t, edge_.base_);
}
-static INLINE entry_connection_t *EDGE_TO_ENTRY_CONN(edge_connection_t *c)
+static inline entry_connection_t *EDGE_TO_ENTRY_CONN(edge_connection_t *c)
{
tor_assert(c->base_.magic == ENTRY_CONNECTION_MAGIC);
return (entry_connection_t*) SUBTYPE_P(c, entry_connection_t, edge_);
}
-static INLINE control_connection_t *TO_CONTROL_CONN(connection_t *c)
+static inline control_connection_t *TO_CONTROL_CONN(connection_t *c)
{
tor_assert(c->magic == CONTROL_CONNECTION_MAGIC);
return DOWNCAST(control_connection_t, c);
}
-static INLINE listener_connection_t *TO_LISTENER_CONN(connection_t *c)
+static inline listener_connection_t *TO_LISTENER_CONN(connection_t *c)
{
tor_assert(c->magic == LISTENER_CONNECTION_MAGIC);
return DOWNCAST(listener_connection_t, c);
@@ -1922,7 +1935,7 @@ typedef struct cached_dir_t {
size_t dir_len; /**< Length of <b>dir</b> (not counting its NUL). */
size_t dir_z_len; /**< Length of <b>dir_z</b>. */
time_t published; /**< When was this object published. */
- digests_t digests; /**< Digests of this object (networkstatus only) */
+ common_digests_t digests; /**< Digests of this object (networkstatus only) */
int refcnt; /**< Reference count for this cached_dir_t. */
} cached_dir_t;
@@ -1946,8 +1959,8 @@ typedef enum {
} saved_location_t;
#define saved_location_bitfield_t ENUM_BF(saved_location_t)
-/** Enumeration: what kind of download schedule are we using for a given
- * object? */
+/** Enumeration: what directory object is being downloaded?
+ * This determines which schedule is selected to perform the download. */
typedef enum {
DL_SCHED_GENERIC = 0,
DL_SCHED_CONSENSUS = 1,
@@ -1955,15 +1968,74 @@ typedef enum {
} download_schedule_t;
#define download_schedule_bitfield_t ENUM_BF(download_schedule_t)
+/** Enumeration: is the download schedule for downloading from an authority,
+ * or from any available directory mirror?
+ * During bootstrap, "any" means a fallback (or an authority, if there
+ * are no fallbacks).
+ * When we have a valid consensus, "any" means any directory server. */
+typedef enum {
+ DL_WANT_ANY_DIRSERVER = 0,
+ DL_WANT_AUTHORITY = 1,
+} download_want_authority_t;
+#define download_want_authority_bitfield_t \
+ ENUM_BF(download_want_authority_t)
+
+/** Enumeration: do we want to increment the schedule position each time a
+ * connection is attempted (these attempts can be concurrent), or do we want
+ * to increment the schedule position after a connection fails? */
+typedef enum {
+ DL_SCHED_INCREMENT_FAILURE = 0,
+ DL_SCHED_INCREMENT_ATTEMPT = 1,
+} download_schedule_increment_t;
+#define download_schedule_increment_bitfield_t \
+ ENUM_BF(download_schedule_increment_t)
+
/** Information about our plans for retrying downloads for a downloadable
- * object. */
+ * directory object.
+ * Each type of downloadable directory object has a corresponding retry
+ * <b>schedule</b>, which can be different depending on whether the object is
+ * being downloaded from an authority or a mirror (<b>want_authority</b>).
+ * <b>next_attempt_at</b> contains the next time we will attempt to download
+ * the object.
+ * For schedules that <b>increment_on</b> failure, <b>n_download_failures</b>
+ * is used to determine the position in the schedule. (Each schedule is a
+ * smartlist of integer delays, parsed from a CSV option.) Every time a
+ * connection attempt fails, <b>n_download_failures</b> is incremented,
+ * the new delay value is looked up from the schedule, and
+ * <b>next_attempt_at</b> is set delay seconds from the time the previous
+ * connection failed. Therefore, at most one failure-based connection can be
+ * in progress for each download_status_t.
+ * For schedules that <b>increment_on</b> attempt, <b>n_download_attempts</b>
+ * is used to determine the position in the schedule. Every time a
+ * connection attempt is made, <b>n_download_attempts</b> is incremented,
+ * the new delay value is looked up from the schedule, and
+ * <b>next_attempt_at</b> is set delay seconds from the time the previous
+ * connection was attempted. Therefore, multiple concurrent attempted-based
+ * connections can be in progress for each download_status_t.
+ * After an object is successfully downloaded, any other concurrent connections
+ * are terminated. A new schedule which starts at position 0 is used for
+ * subsequent downloads of the same object.
+ */
typedef struct download_status_t {
- time_t next_attempt_at; /**< When should we try downloading this descriptor
+ time_t next_attempt_at; /**< When should we try downloading this object
* again? */
- uint8_t n_download_failures; /**< Number of failures trying to download the
- * most recent descriptor. */
- download_schedule_bitfield_t schedule : 8;
-
+ uint8_t n_download_failures; /**< Number of failed downloads of the most
+ * recent object, since the last success. */
+ uint8_t n_download_attempts; /**< Number of (potentially concurrent) attempts
+ * to download the most recent object, since
+ * the last success. */
+ download_schedule_bitfield_t schedule : 8; /**< What kind of object is being
+ * downloaded? This determines the
+ * schedule used for the download.
+ */
+ download_want_authority_bitfield_t want_authority : 1; /**< Is the download
+ * happening from an authority
+ * or a mirror? This determines
+ * the schedule used for the
+ * download. */
+ download_schedule_increment_bitfield_t increment_on : 1; /**< does this
+ * schedule increment on each attempt,
+ * or after each failure? */
} download_status_t;
/** If n_download_failures is this high, the download can never happen. */
@@ -2081,6 +2153,11 @@ typedef struct {
* tests for it. */
unsigned int needs_retest_if_added:1;
+ /** True iff this router included "tunnelled-dir-server" in its descriptor,
+ * implying it accepts tunnelled directory requests, or it advertised
+ * dir_port > 0. */
+ unsigned int supports_tunnelled_dir_requests:1;
+
/** Tor can use this router for general positions in circuits; we got it
* from a directory server as usual, or we're an authority and a server
* uploaded it. */
@@ -2158,6 +2235,9 @@ typedef struct routerstatus_t {
* an exit node. */
unsigned int is_hs_dir:1; /**< True iff this router is a v2-or-later hidden
* service directory. */
+ unsigned int is_v2_dir:1; /** True iff this router publishes an open DirPort
+ * or it claims to accept tunnelled dir requests.
+ */
/** True iff we know version info for this router. (i.e., a "v" entry was
* included.) We'll replace all these with a big tor_version_t or a char[]
* if the number of traits we care about ever becomes incredibly big. */
@@ -2259,7 +2339,7 @@ typedef struct microdesc_t {
curve25519_public_key_t *onion_curve25519_pkey;
/** Ed25519 identity key, if included. */
ed25519_public_key_t *ed25519_identity_pkey;
- /** As routerinfo_t.ipv6_add */
+ /** As routerinfo_t.ipv6_addr */
tor_addr_t ipv6_addr;
/** As routerinfo_t.ipv6_orport */
uint16_t ipv6_orport;
@@ -2277,7 +2357,7 @@ typedef struct microdesc_t {
* Specifically, a node_t is a Tor router as we are using it: a router that
* we are considering for circuits, connections, and so on. A node_t is a
* thin wrapper around the routerstatus, routerinfo, and microdesc for a
- * single wrapper, and provides a consistent interface for all of them.
+ * single router, and provides a consistent interface for all of them.
*
* Also, a node_t has mutable state. While a routerinfo, a routerstatus,
* and a microdesc have[*] only the information read from a router
@@ -2334,7 +2414,8 @@ typedef struct node_t {
/* Local info: derived. */
- /** True if the IPv6 OR port is preferred over the IPv4 OR port. */
+ /** True if the IPv6 OR port is preferred over the IPv4 OR port.
+ * XX/teor - can this become out of date if the torrc changes? */
unsigned int ipv6_preferred:1;
/** According to the geoip db what country is this router in? */
@@ -2492,7 +2573,7 @@ typedef struct networkstatus_t {
struct authority_cert_t *cert; /**< Vote only: the voter's certificate. */
/** Digests of this document, as signed. */
- digests_t digests;
+ common_digests_t digests;
/** List of router statuses, sorted by identity digest. For a vote,
* the elements are vote_routerstatus_t; for a consensus, the elements
@@ -2892,6 +2973,14 @@ typedef struct circuit_t {
* where this circuit was marked.) */
const char *marked_for_close_file; /**< For debugging: in which file was this
* circuit marked for close? */
+ /** For what reason (See END_CIRC_REASON...) is this circuit being closed?
+ * This field is set in circuit_mark_for_close and used later in
+ * circuit_about_to_free. */
+ uint16_t marked_for_close_reason;
+ /** As marked_for_close_reason, but reflects the underlying reason for
+ * closing this circuit.
+ */
+ uint16_t marked_for_close_orig_reason;
/** Unique ID for measuring tunneled network status requests. */
uint64_t dirreq_id;
@@ -3281,27 +3370,27 @@ static const origin_circuit_t *CONST_TO_ORIGIN_CIRCUIT(const circuit_t *);
/** Return 1 iff <b>node</b> has Exit flag and no BadExit flag.
* Otherwise, return 0.
*/
-static INLINE int node_is_good_exit(const node_t *node)
+static inline int node_is_good_exit(const node_t *node)
{
return node->is_exit && ! node->is_bad_exit;
}
-static INLINE or_circuit_t *TO_OR_CIRCUIT(circuit_t *x)
+static inline or_circuit_t *TO_OR_CIRCUIT(circuit_t *x)
{
tor_assert(x->magic == OR_CIRCUIT_MAGIC);
return DOWNCAST(or_circuit_t, x);
}
-static INLINE const or_circuit_t *CONST_TO_OR_CIRCUIT(const circuit_t *x)
+static inline const or_circuit_t *CONST_TO_OR_CIRCUIT(const circuit_t *x)
{
tor_assert(x->magic == OR_CIRCUIT_MAGIC);
return DOWNCAST(or_circuit_t, x);
}
-static INLINE origin_circuit_t *TO_ORIGIN_CIRCUIT(circuit_t *x)
+static inline origin_circuit_t *TO_ORIGIN_CIRCUIT(circuit_t *x)
{
tor_assert(x->magic == ORIGIN_CIRCUIT_MAGIC);
return DOWNCAST(origin_circuit_t, x);
}
-static INLINE const origin_circuit_t *CONST_TO_ORIGIN_CIRCUIT(
+static inline const origin_circuit_t *CONST_TO_ORIGIN_CIRCUIT(
const circuit_t *x)
{
tor_assert(x->magic == ORIGIN_CIRCUIT_MAGIC);
@@ -3424,9 +3513,11 @@ typedef struct {
* each log message occurs? */
int TruncateLogFile; /**< Boolean: Should we truncate the log file
before we start writing? */
+ char *SyslogIdentityTag; /**< Identity tag to add for syslog logging. */
char *DebugLogFile; /**< Where to send verbose log messages. */
char *DataDirectory; /**< OR only: where to store long-term data. */
+ int DataDirectoryGroupReadable; /**< Boolean: Is the DataDirectory g+r? */
char *Nickname; /**< OR only: nickname of this onion router. */
char *Address; /**< OR only: configured address for this onion router. */
char *PidFile; /**< Where to store PID of Tor process. */
@@ -3748,6 +3839,8 @@ typedef struct {
/** List of fallback directory servers */
config_line_t *FallbackDir;
+ /** Whether to use the default hard-coded FallbackDirs */
+ int UseDefaultFallbackDirs;
/** Weight to apply to all directory authority rates if considering them
* along with fallbackdirs */
@@ -3807,9 +3900,11 @@ typedef struct {
* hibernate." */
/** How do we determine when our AccountingMax has been reached?
* "max" for when in or out reaches AccountingMax
- * "sum for when in plus out reaches AccountingMax */
+ * "sum" for when in plus out reaches AccountingMax
+ * "in" for when in reaches AccountingMax
+ * "out" for when out reaches AccountingMax */
char *AccountingRule_option;
- enum { ACCT_MAX, ACCT_SUM } AccountingRule;
+ enum { ACCT_MAX, ACCT_SUM, ACCT_IN, ACCT_OUT } AccountingRule;
/** Base64-encoded hash of accepted passwords for the control system. */
config_line_t *HashedControlPassword;
@@ -3883,6 +3978,10 @@ typedef struct {
/** Should we fetch our dir info at the start of the consensus period? */
int FetchDirInfoExtraEarly;
+ int DirCache; /**< Cache all directory documents and accept requests via
+ * tunnelled dir conns from clients. If 1, enabled (default);
+ * If 0, disabled. */
+
char *VirtualAddrNetworkIPv4; /**< Address and mask to hand out for virtual
* MAPADDRESS requests for IPv4 addresses */
char *VirtualAddrNetworkIPv6; /**< Address and mask to hand out for virtual
@@ -3983,12 +4082,24 @@ typedef struct {
* over randomly chosen exits. */
int ClientRejectInternalAddresses;
- /** If true, clients may connect over IPv6. XXX we don't really
- enforce this -- clients _may_ set up outgoing IPv6 connections
- even when this option is not set. */
+ /** If true, clients may connect over IPv4. If false, they will avoid
+ * connecting over IPv4. We enforce this for OR and Dir connections. */
+ int ClientUseIPv4;
+ /** If true, clients may connect over IPv6. If false, they will avoid
+ * connecting over IPv4. We enforce this for OR and Dir connections.
+ * Use fascist_firewall_use_ipv6() instead of accessing this value
+ * directly. */
int ClientUseIPv6;
- /** If true, prefer an IPv6 OR port over an IPv4 one. */
+ /** If true, prefer an IPv6 OR port over an IPv4 one for entry node
+ * connections. If auto, bridge clients prefer IPv6, and other clients
+ * prefer IPv4. Use node_ipv6_or_preferred() instead of accessing this value
+ * directly. */
int ClientPreferIPv6ORPort;
+ /** If true, prefer an IPv6 directory port over an IPv4 one for direct
+ * directory connections. If auto, bridge clients prefer IPv6, and other
+ * clients prefer IPv4. Use fascist_firewall_prefer_ipv6_dirport() instead of
+ * accessing this value directly. */
+ int ClientPreferIPv6DirPort;
/** The length of time that we think a consensus should be fresh. */
int V3AuthVotingInterval;
@@ -4014,7 +4125,7 @@ typedef struct {
char *ConsensusParams;
/** Authority only: minimum number of measured bandwidths we must see
- * before we only beliee measured bandwidths to assign flags. */
+ * before we only believe measured bandwidths to assign flags. */
int MinMeasuredBWsForAuthToIgnoreAdvertised;
/** The length of time that we think an initial consensus should be fresh.
@@ -4059,6 +4170,36 @@ typedef struct {
* on testing networks. */
smartlist_t *TestingClientConsensusDownloadSchedule;
+ /** Schedule for when clients should download consensuses from authorities
+ * if they are bootstrapping (that is, they don't have a usable, reasonably
+ * live consensus). Only used by clients fetching from a list of fallback
+ * directory mirrors.
+ *
+ * This schedule is incremented by (potentially concurrent) connection
+ * attempts, unlike other schedules, which are incremented by connection
+ * failures. Only altered on testing networks. */
+ smartlist_t *TestingClientBootstrapConsensusAuthorityDownloadSchedule;
+
+ /** Schedule for when clients should download consensuses from fallback
+ * directory mirrors if they are bootstrapping (that is, they don't have a
+ * usable, reasonably live consensus). Only used by clients fetching from a
+ * list of fallback directory mirrors.
+ *
+ * This schedule is incremented by (potentially concurrent) connection
+ * attempts, unlike other schedules, which are incremented by connection
+ * failures. Only altered on testing networks. */
+ smartlist_t *TestingClientBootstrapConsensusFallbackDownloadSchedule;
+
+ /** Schedule for when clients should download consensuses from authorities
+ * if they are bootstrapping (that is, they don't have a usable, reasonably
+ * live consensus). Only used by clients which don't have or won't fetch
+ * from a list of fallback directory mirrors.
+ *
+ * This schedule is incremented by (potentially concurrent) connection
+ * attempts, unlike other schedules, which are incremented by connection
+ * failures. Only altered on testing networks. */
+ smartlist_t *TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule;
+
/** Schedule for when clients should download bridge descriptors. Only
* altered on testing networks. */
smartlist_t *TestingBridgeDownloadSchedule;
@@ -4076,6 +4217,21 @@ typedef struct {
* up? Only altered on testing networks. */
int TestingConsensusMaxDownloadTries;
+ /** How many times will a client try to fetch a consensus while
+ * bootstrapping using a list of fallback directories, before it gives up?
+ * Only altered on testing networks. */
+ int TestingClientBootstrapConsensusMaxDownloadTries;
+
+ /** How many times will a client try to fetch a consensus while
+ * bootstrapping using only a list of authorities, before it gives up?
+ * Only altered on testing networks. */
+ int TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries;
+
+ /** How many simultaneous in-progress connections will we make when trying
+ * to fetch a consensus before we wait for one to complete, timeout, or
+ * error out? Only altered on testing networks. */
+ int TestingClientBootstrapConsensusMaxInProgressTries;
+
/** How many times will we try to download a router's descriptor before
* giving up? Only altered on testing networks. */
int TestingDescriptorMaxDownloadTries;
@@ -4316,6 +4472,9 @@ typedef struct {
int keygen_passphrase_fd;
int change_key_passphrase;
char *master_key_fname;
+
+ /** Autobool: Do we try to retain capabilities if we can? */
+ int KeepBindCapabilities;
} or_options_t;
/** Persistent state for an onion router, as saved to disk. */
@@ -4388,7 +4547,7 @@ typedef struct {
/** Change the next_write time of <b>state</b> to <b>when</b>, unless the
* state is already scheduled to be written to disk earlier than <b>when</b>.
*/
-static INLINE void or_state_mark_dirty(or_state_t *state, time_t when)
+static inline void or_state_mark_dirty(or_state_t *state, time_t when)
{
if (state->next_write > when)
state->next_write = when;
@@ -4999,9 +5158,13 @@ typedef struct dir_server_t {
char *description;
char *nickname;
char *address; /**< Hostname. */
+ /* XX/teor - why do we duplicate the address and port fields here and in
+ * fake_status? Surely we could just use fake_status (#17867). */
+ tor_addr_t ipv6_addr; /**< IPv6 address if present; AF_UNSPEC if not */
uint32_t addr; /**< IPv4 address. */
uint16_t dir_port; /**< Directory port. */
uint16_t or_port; /**< OR port: Used for tunneling connections. */
+ uint16_t ipv6_orport; /**< OR port corresponding to ipv6_addr. */
double weight; /** Weight used when selecting this node at random */
char digest[DIGEST_LEN]; /**< Digest of identity key. */
char v3_identity_digest[DIGEST_LEN]; /**< Digest of v3 (authority only,
@@ -5082,7 +5245,9 @@ typedef enum {
CRN_ALLOW_INVALID = 1<<3,
/* XXXX not used, apparently. */
CRN_WEIGHT_AS_EXIT = 1<<5,
- CRN_NEED_DESC = 1<<6
+ CRN_NEED_DESC = 1<<6,
+ /* On clients, only provide nodes that satisfy ClientPreferIPv6OR */
+ CRN_PREF_ADDR = 1<<7
} router_crn_flags_t;
/** Return value for router_add_to_routerlist() and dirserv_add_descriptor() */
diff --git a/src/or/periodic.c b/src/or/periodic.c
new file mode 100644
index 0000000000..109717f738
--- /dev/null
+++ b/src/or/periodic.c
@@ -0,0 +1,120 @@
+/* Copyright (c) 2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "compat_libevent.h"
+#include "config.h"
+#include "periodic.h"
+
+#ifdef HAVE_EVENT2_EVENT_H
+#include <event2/event.h>
+#else
+#include <event.h>
+#endif
+
+/** We disable any interval greater than this number of seconds, on the
+ * grounds that it is probably an absolute time mistakenly passed in as a
+ * relative time.
+ */
+static const int MAX_INTERVAL = 10 * 365 * 86400;
+
+/** Set the event <b>event</b> to run in <b>next_interval</b> seconds from
+ * now. */
+static void
+periodic_event_set_interval(periodic_event_item_t *event,
+ time_t next_interval)
+{
+ tor_assert(next_interval < MAX_INTERVAL);
+ struct timeval tv;
+ tv.tv_sec = next_interval;
+ tv.tv_usec = 0;
+ event_add(event->ev, &tv);
+}
+
+/** Wraps dispatches for periodic events, <b>data</b> will be a pointer to the
+ * event that needs to be called */
+static void
+periodic_event_dispatch(evutil_socket_t fd, short what, void *data)
+{
+ (void)fd;
+ (void)what;
+ periodic_event_item_t *event = data;
+
+ time_t now = time(NULL);
+ const or_options_t *options = get_options();
+ log_debug(LD_GENERAL, "Dispatching %s", event->name);
+ int r = event->fn(now, options);
+ int next_interval = 0;
+
+ /* update the last run time if action was taken */
+ if (r==0) {
+ log_err(LD_BUG, "Invalid return value for periodic event from %s.",
+ event->name);
+ tor_assert(r != 0);
+ } else if (r > 0) {
+ event->last_action_time = now;
+ /* If the event is meant to happen after ten years, that's likely
+ * a bug, and somebody gave an absolute time rather than an interval.
+ */
+ tor_assert(r < MAX_INTERVAL);
+ next_interval = r;
+ } else {
+ /* no action was taken, it is likely a precondition failed,
+ * we should reschedule for next second incase the precondition
+ * passes then */
+ next_interval = 1;
+ }
+
+ log_debug(LD_GENERAL, "Scheduling %s for %d seconds", event->name,
+ next_interval);
+ struct timeval tv = { next_interval , 0 };
+ event_add(event->ev, &tv);
+}
+
+/** Schedules <b>event</b> to run as soon as possible from now. */
+void
+periodic_event_reschedule(periodic_event_item_t *event)
+{
+ periodic_event_set_interval(event, 1);
+}
+
+/** Initializes the libevent backend for a periodic event. */
+void
+periodic_event_setup(periodic_event_item_t *event)
+{
+ if (event->ev) { /* Already setup? This is a bug */
+ log_err(LD_BUG, "Initial dispatch should only be done once.");
+ tor_assert(0);
+ }
+
+ event->ev = tor_event_new(tor_libevent_get_base(),
+ -1, 0,
+ periodic_event_dispatch,
+ event);
+ tor_assert(event->ev);
+}
+
+/** Handles initial dispatch for periodic events. It should happen 1 second
+ * after the events are created to mimic behaviour before #3199's refactor */
+void
+periodic_event_launch(periodic_event_item_t *event)
+{
+ if (! event->ev) { /* Not setup? This is a bug */
+ log_err(LD_BUG, "periodic_event_launch without periodic_event_setup");
+ tor_assert(0);
+ }
+
+ // Initial dispatch
+ periodic_event_dispatch(-1, EV_TIMEOUT, event);
+}
+
+/** Release all storage associated with <b>event</b> */
+void
+periodic_event_destroy(periodic_event_item_t *event)
+{
+ if (!event)
+ return;
+ tor_event_free(event->ev);
+ event->last_action_time = 0;
+}
+
diff --git a/src/or/periodic.h b/src/or/periodic.h
new file mode 100644
index 0000000000..bab0c91916
--- /dev/null
+++ b/src/or/periodic.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_PERIODIC_H
+#define TOR_PERIODIC_H
+
+#define PERIODIC_EVENT_NO_UPDATE (-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
+* again in the next second. If a positive value is returned it will update the
+* interval time. */
+typedef int (*periodic_event_helper_t)(time_t now,
+ const or_options_t *options);
+
+struct event;
+
+/** A single item for the periodic-events-function table. */
+typedef struct periodic_event_item_t {
+ periodic_event_helper_t fn; /**< The function to run the event */
+ time_t last_action_time; /**< The last time the function did something */
+ struct event *ev; /**< Libevent callback we're using to implement this */
+ const char *name; /**< Name of the function -- for debug */
+} periodic_event_item_t;
+
+/** events will get their interval from first execution */
+#define PERIODIC_EVENT(fn) { fn##_callback, 0, NULL, #fn }
+#define END_OF_PERIODIC_EVENTS { NULL, 0, NULL, NULL }
+
+void periodic_event_launch(periodic_event_item_t *event);
+void periodic_event_setup(periodic_event_item_t *event);
+void periodic_event_destroy(periodic_event_item_t *event);
+void periodic_event_reschedule(periodic_event_item_t *event);
+
+#endif
+
diff --git a/src/or/policies.c b/src/or/policies.c
index b247e6a64d..5a97c7a134 100644
--- a/src/or/policies.c
+++ b/src/or/policies.c
@@ -8,9 +8,12 @@
* \brief Code to parse and use address policies and exit policies.
**/
+#define POLICIES_PRIVATE
+
#include "or.h"
#include "config.h"
#include "dirserv.h"
+#include "networkstatus.h"
#include "nodelist.h"
#include "policies.h"
#include "router.h"
@@ -62,14 +65,15 @@ static const char *private_nets[] = {
NULL
};
-static int policies_parse_exit_policy_internal(config_line_t *cfg,
- smartlist_t **dest,
- int ipv6_exit,
- int rejectprivate,
- uint32_t local_address,
- tor_addr_t *ipv6_local_address,
- int reject_interface_addresses,
- int add_default_policy);
+static int policies_parse_exit_policy_internal(
+ config_line_t *cfg,
+ smartlist_t **dest,
+ int ipv6_exit,
+ int rejectprivate,
+ const smartlist_t *configured_addresses,
+ int reject_interface_addresses,
+ int reject_configured_port_addresses,
+ int add_default_policy);
/** Replace all "private" entries in *<b>policy</b> with their expanded
* equivalents. */
@@ -267,16 +271,76 @@ parse_reachable_addresses(void)
"Error parsing ReachableDirAddresses entry; ignoring.");
ret = -1;
}
+
+ /* We ignore ReachableAddresses for relays */
+ if (!server_mode(options)) {
+ if ((reachable_or_addr_policy
+ && policy_is_reject_star(reachable_or_addr_policy, AF_UNSPEC))
+ || (reachable_dir_addr_policy
+ && policy_is_reject_star(reachable_dir_addr_policy, AF_UNSPEC))) {
+ log_warn(LD_CONFIG, "Tor cannot connect to the Internet if "
+ "ReachableAddresses, ReachableORAddresses, or "
+ "ReachableDirAddresses reject all addresses. Please accept "
+ "some addresses in these options.");
+ } else if (options->ClientUseIPv4 == 1
+ && ((reachable_or_addr_policy
+ && policy_is_reject_star(reachable_or_addr_policy, AF_INET))
+ || (reachable_dir_addr_policy
+ && policy_is_reject_star(reachable_dir_addr_policy, AF_INET)))) {
+ log_warn(LD_CONFIG, "You have set ClientUseIPv4 1, but "
+ "ReachableAddresses, ReachableORAddresses, or "
+ "ReachableDirAddresses reject all IPv4 addresses. "
+ "Tor will not connect using IPv4.");
+ } else if (fascist_firewall_use_ipv6(options)
+ && ((reachable_or_addr_policy
+ && policy_is_reject_star(reachable_or_addr_policy, AF_INET6))
+ || (reachable_dir_addr_policy
+ && policy_is_reject_star(reachable_dir_addr_policy, AF_INET6)))) {
+ log_warn(LD_CONFIG, "You have configured tor to use IPv6 "
+ "(ClientUseIPv6 1 or UseBridges 1), but "
+ "ReachableAddresses, ReachableORAddresses, or "
+ "ReachableDirAddresses reject all IPv6 addresses. "
+ "Tor will not connect using IPv6.");
+ }
+ }
+
return ret;
}
-/** Return true iff the firewall options might block any address:port
- * combination.
+/* Return true iff ClientUseIPv4 0 or ClientUseIPv6 0 might block any OR or Dir
+ * address:port combination. */
+static int
+firewall_is_fascist_impl(void)
+{
+ const or_options_t *options = get_options();
+ /* Assume every non-bridge relay has an IPv4 address.
+ * Clients which use bridges may only know the IPv6 address of their
+ * bridge. */
+ return (options->ClientUseIPv4 == 0
+ || (!fascist_firewall_use_ipv6(options)
+ && options->UseBridges == 1));
+}
+
+/** Return true iff the firewall options, including ClientUseIPv4 0 and
+ * ClientUseIPv6 0, might block any OR address:port combination.
+ * Address preferences may still change which address is selected even if
+ * this function returns false.
*/
int
firewall_is_fascist_or(void)
{
- return reachable_or_addr_policy != NULL;
+ return (reachable_or_addr_policy != NULL || firewall_is_fascist_impl());
+}
+
+/** Return true iff the firewall options, including ClientUseIPv4 0 and
+ * ClientUseIPv6 0, might block any Dir address:port combination.
+ * Address preferences may still change which address is selected even if
+ * this function returns false.
+ */
+int
+firewall_is_fascist_dir(void)
+{
+ return (reachable_dir_addr_policy != NULL || firewall_is_fascist_impl());
}
/** Return true iff <b>policy</b> (possibly NULL) will allow a
@@ -314,49 +378,624 @@ addr_policy_permits_address(uint32_t addr, uint16_t port,
return addr_policy_permits_tor_addr(&a, port, policy);
}
-/** Return true iff we think our firewall will let us make an OR connection to
- * addr:port. */
-int
-fascist_firewall_allows_address_or(const tor_addr_t *addr, uint16_t port)
+/** Return true iff we think our firewall will let us make a connection to
+ * addr:port.
+ *
+ * If we are configured as a server, ignore any address family preference and
+ * just use IPv4.
+ * Otherwise:
+ * - return false for all IPv4 addresses:
+ * - if ClientUseIPv4 is 0, or
+ * if pref_only and pref_ipv6 are both true;
+ * - return false for all IPv6 addresses:
+ * - if fascist_firewall_use_ipv6() is 0, or
+ * - if pref_only is true and pref_ipv6 is false.
+ *
+ * Return false if addr is NULL or tor_addr_is_null(), or if port is 0. */
+STATIC int
+fascist_firewall_allows_address(const tor_addr_t *addr,
+ uint16_t port,
+ smartlist_t *firewall_policy,
+ int pref_only, int pref_ipv6)
{
+ const or_options_t *options = get_options();
+ const int client_mode = !server_mode(options);
+
+ if (!addr || tor_addr_is_null(addr) || !port) {
+ return 0;
+ }
+
+ /* Clients stop using IPv4 if it's disabled. In most cases, clients also
+ * stop using IPv4 if it's not preferred.
+ * Servers must have IPv4 enabled and preferred. */
+ if (tor_addr_family(addr) == AF_INET && client_mode &&
+ (!options->ClientUseIPv4 || (pref_only && pref_ipv6))) {
+ return 0;
+ }
+
+ /* Clients and Servers won't use IPv6 unless it's enabled (and in most
+ * cases, IPv6 must also be preferred before it will be used). */
+ if (tor_addr_family(addr) == AF_INET6 &&
+ (!fascist_firewall_use_ipv6(options) || (pref_only && !pref_ipv6))) {
+ return 0;
+ }
+
return addr_policy_permits_tor_addr(addr, port,
- reachable_or_addr_policy);
+ firewall_policy);
}
-/** Return true iff we think our firewall will let us make an OR connection to
- * <b>ri</b>. */
+/** Is this client configured to use IPv6?
+ * Use node_ipv6_or/dir_preferred() when checking a specific node and OR/Dir
+ * port: it supports bridge client per-node IPv6 preferences.
+ */
int
-fascist_firewall_allows_or(const routerinfo_t *ri)
+fascist_firewall_use_ipv6(const or_options_t *options)
+{
+ /* Clients use IPv6 if it's set, or they use bridges, or they don't use
+ * IPv4 */
+ return (options->ClientUseIPv6 == 1 || options->UseBridges == 1
+ || options->ClientUseIPv4 == 0);
+}
+
+/** Do we prefer to connect to IPv6, ignoring ClientPreferIPv6ORPort and
+ * ClientPreferIPv6DirPort?
+ * If we're unsure, return -1, otherwise, return 1 for IPv6 and 0 for IPv4.
+ */
+static int
+fascist_firewall_prefer_ipv6_impl(const or_options_t *options)
{
- /* XXXX proposal 118 */
- tor_addr_t addr;
- tor_addr_from_ipv4h(&addr, ri->addr);
- return fascist_firewall_allows_address_or(&addr, ri->or_port);
+ /*
+ Cheap implementation of config options ClientUseIPv4 & ClientUseIPv6 --
+ If we're a server or IPv6 is disabled, use IPv4.
+ If IPv4 is disabled, use IPv6.
+ */
+
+ if (server_mode(options) || !fascist_firewall_use_ipv6(options)) {
+ return 0;
+ }
+
+ if (!options->ClientUseIPv4) {
+ return 1;
+ }
+
+ return -1;
}
-/** Return true iff we think our firewall will let us make an OR connection to
- * <b>node</b>. */
+/** Do we prefer to connect to IPv6 ORPorts?
+ * Use node_ipv6_or_preferred() whenever possible: it supports bridge client
+ * per-node IPv6 preferences.
+ */
int
-fascist_firewall_allows_node(const node_t *node)
+fascist_firewall_prefer_ipv6_orport(const or_options_t *options)
{
- if (node->ri) {
- return fascist_firewall_allows_or(node->ri);
- } else if (node->rs) {
- tor_addr_t addr;
- tor_addr_from_ipv4h(&addr, node->rs->addr);
- return fascist_firewall_allows_address_or(&addr, node->rs->or_port);
+ int pref_ipv6 = fascist_firewall_prefer_ipv6_impl(options);
+
+ if (pref_ipv6 >= 0) {
+ return pref_ipv6;
+ }
+
+ /* We can use both IPv4 and IPv6 - which do we prefer? */
+ if (options->ClientPreferIPv6ORPort == 1) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/** Do we prefer to connect to IPv6 DirPorts?
+ *
+ * (node_ipv6_dir_preferred() doesn't support bridge client per-node IPv6
+ * preferences. There's no reason to use it instead of this function.)
+ */
+int
+fascist_firewall_prefer_ipv6_dirport(const or_options_t *options)
+{
+ int pref_ipv6 = fascist_firewall_prefer_ipv6_impl(options);
+
+ if (pref_ipv6 >= 0) {
+ return pref_ipv6;
+ }
+
+ /* We can use both IPv4 and IPv6 - which do we prefer? */
+ if (options->ClientPreferIPv6DirPort == 1) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/** Return true iff we think our firewall will let us make a connection to
+ * addr:port. Uses ReachableORAddresses or ReachableDirAddresses based on
+ * fw_connection.
+ * If pref_only is true, return true if addr is in the client's preferred
+ * address family, which is IPv6 if pref_ipv6 is true, and IPv4 otherwise.
+ * If pref_only is false, ignore pref_ipv6, and return true if addr is allowed.
+ */
+int
+fascist_firewall_allows_address_addr(const tor_addr_t *addr, uint16_t port,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6)
+{
+ if (fw_connection == FIREWALL_OR_CONNECTION) {
+ return fascist_firewall_allows_address(addr, port,
+ reachable_or_addr_policy,
+ pref_only, pref_ipv6);
+ } else if (fw_connection == FIREWALL_DIR_CONNECTION) {
+ return fascist_firewall_allows_address(addr, port,
+ reachable_dir_addr_policy,
+ pref_only, pref_ipv6);
+ } else {
+ log_warn(LD_BUG, "Bad firewall_connection_t value %d.",
+ fw_connection);
+ return 0;
+ }
+}
+
+/** Return true iff we think our firewall will let us make a connection to
+ * addr:port (ap). Uses ReachableORAddresses or ReachableDirAddresses based on
+ * fw_connection.
+ * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr().
+ */
+static int
+fascist_firewall_allows_address_ap(const tor_addr_port_t *ap,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6)
+{
+ tor_assert(ap);
+ return fascist_firewall_allows_address_addr(&ap->addr, ap->port,
+ fw_connection, pref_only,
+ pref_ipv6);
+}
+
+/* Return true iff we think our firewall will let us make a connection to
+ * ipv4h_or_addr:ipv4_or_port. ipv4h_or_addr is interpreted in host order.
+ * Uses ReachableORAddresses or ReachableDirAddresses based on
+ * fw_connection.
+ * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr().
+ */
+static int
+fascist_firewall_allows_address_ipv4h(uint32_t ipv4h_or_addr,
+ uint16_t ipv4_or_port,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6)
+{
+ tor_addr_t ipv4_or_addr;
+ tor_addr_from_ipv4h(&ipv4_or_addr, ipv4h_or_addr);
+ return fascist_firewall_allows_address_addr(&ipv4_or_addr, ipv4_or_port,
+ fw_connection, pref_only,
+ pref_ipv6);
+}
+
+/** Return true iff we think our firewall will let us make a connection to
+ * ipv4h_addr/ipv6_addr. Uses ipv4_orport/ipv6_orport/ReachableORAddresses or
+ * ipv4_dirport/ipv6_dirport/ReachableDirAddresses based on IPv4/IPv6 and
+ * <b>fw_connection</b>.
+ * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr().
+ */
+static int
+fascist_firewall_allows_base(uint32_t ipv4h_addr, uint16_t ipv4_orport,
+ uint16_t ipv4_dirport,
+ const tor_addr_t *ipv6_addr, uint16_t ipv6_orport,
+ uint16_t ipv6_dirport,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6)
+{
+ if (fascist_firewall_allows_address_ipv4h(ipv4h_addr,
+ (fw_connection == FIREWALL_OR_CONNECTION
+ ? ipv4_orport
+ : ipv4_dirport),
+ fw_connection,
+ pref_only, pref_ipv6)) {
+ return 1;
+ }
+
+ if (fascist_firewall_allows_address_addr(ipv6_addr,
+ (fw_connection == FIREWALL_OR_CONNECTION
+ ? ipv6_orport
+ : ipv6_dirport),
+ fw_connection,
+ pref_only, pref_ipv6)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/** Like fascist_firewall_allows_base(), but takes ri. */
+static int
+fascist_firewall_allows_ri_impl(const routerinfo_t *ri,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6)
+{
+ if (!ri) {
+ return 0;
+ }
+
+ /* Assume IPv4 and IPv6 DirPorts are the same */
+ return fascist_firewall_allows_base(ri->addr, ri->or_port, ri->dir_port,
+ &ri->ipv6_addr, ri->ipv6_orport,
+ ri->dir_port, fw_connection, pref_only,
+ pref_ipv6);
+}
+
+/** Like fascist_firewall_allows_rs, but doesn't consult the node. */
+static int
+fascist_firewall_allows_rs_impl(const routerstatus_t *rs,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6)
+{
+ if (!rs) {
+ return 0;
+ }
+
+ /* Assume IPv4 and IPv6 DirPorts are the same */
+ return fascist_firewall_allows_base(rs->addr, rs->or_port, rs->dir_port,
+ &rs->ipv6_addr, rs->ipv6_orport,
+ rs->dir_port, fw_connection, pref_only,
+ pref_ipv6);
+}
+
+/** Like fascist_firewall_allows_base(), but takes rs.
+ * Consults the corresponding node, then falls back to rs if node is NULL.
+ * This should only happen when there's no valid consensus, and rs doesn't
+ * correspond to a bridge client's bridge.
+ */
+int
+fascist_firewall_allows_rs(const routerstatus_t *rs,
+ firewall_connection_t fw_connection, int pref_only)
+{
+ if (!rs) {
+ return 0;
+ }
+
+ const node_t *node = node_get_by_id(rs->identity_digest);
+
+ if (node) {
+ return fascist_firewall_allows_node(node, fw_connection, pref_only);
} else {
+ /* There's no node-specific IPv6 preference, so use the generic IPv6
+ * preference instead. */
+ const or_options_t *options = get_options();
+ int pref_ipv6 = (fw_connection == FIREWALL_OR_CONNECTION
+ ? fascist_firewall_prefer_ipv6_orport(options)
+ : fascist_firewall_prefer_ipv6_dirport(options));
+
+ return fascist_firewall_allows_rs_impl(rs, fw_connection, pref_only,
+ pref_ipv6);
+ }
+}
+
+/** Return true iff we think our firewall will let us make a connection to
+ * ipv6_addr:ipv6_orport based on ReachableORAddresses.
+ * If <b>fw_connection</b> is FIREWALL_DIR_CONNECTION, returns 0.
+ * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr().
+ */
+static int
+fascist_firewall_allows_md_impl(const microdesc_t *md,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6)
+{
+ if (!md) {
+ return 0;
+ }
+
+ /* Can't check dirport, it doesn't have one */
+ if (fw_connection == FIREWALL_DIR_CONNECTION) {
+ return 0;
+ }
+
+ /* Also can't check IPv4, doesn't have that either */
+ return fascist_firewall_allows_address_addr(&md->ipv6_addr, md->ipv6_orport,
+ fw_connection, pref_only,
+ pref_ipv6);
+}
+
+/** Like fascist_firewall_allows_base(), but takes node, and looks up pref_ipv6
+ * from node_ipv6_or/dir_preferred(). */
+int
+fascist_firewall_allows_node(const node_t *node,
+ firewall_connection_t fw_connection,
+ int pref_only)
+{
+ if (!node) {
+ return 0;
+ }
+
+ node_assert_ok(node);
+
+ const int pref_ipv6 = (fw_connection == FIREWALL_OR_CONNECTION
+ ? node_ipv6_or_preferred(node)
+ : node_ipv6_dir_preferred(node));
+
+ /* Sometimes, the rs is missing the IPv6 address info, and we need to go
+ * all the way to the md */
+ if (node->ri && fascist_firewall_allows_ri_impl(node->ri, fw_connection,
+ pref_only, pref_ipv6)) {
+ return 1;
+ } else if (node->rs && fascist_firewall_allows_rs_impl(node->rs,
+ fw_connection,
+ pref_only,
+ pref_ipv6)) {
+ return 1;
+ } else if (node->md && fascist_firewall_allows_md_impl(node->md,
+ fw_connection,
+ pref_only,
+ pref_ipv6)) {
return 1;
+ } else {
+ /* If we know nothing, assume it's unreachable, we'll never get an address
+ * to connect to. */
+ return 0;
}
}
-/** Return true iff we think our firewall will let us make a directory
- * connection to addr:port. */
+/** Like fascist_firewall_allows_rs(), but takes ds. */
int
-fascist_firewall_allows_address_dir(const tor_addr_t *addr, uint16_t port)
+fascist_firewall_allows_dir_server(const dir_server_t *ds,
+ firewall_connection_t fw_connection,
+ int pref_only)
{
- return addr_policy_permits_tor_addr(addr, port,
- reachable_dir_addr_policy);
+ if (!ds) {
+ return 0;
+ }
+
+ /* A dir_server_t always has a fake_status. As long as it has the same
+ * addresses/ports in both fake_status and dir_server_t, this works fine.
+ * (See #17867.)
+ * This function relies on fascist_firewall_choose_address_rs looking up the
+ * node if it can, because that will get the latest info for the relay. */
+ return fascist_firewall_allows_rs(&ds->fake_status, fw_connection,
+ pref_only);
+}
+
+/** If a and b are both valid and allowed by fw_connection,
+ * choose one based on want_a and return it.
+ * Otherwise, return whichever is allowed.
+ * Otherwise, return NULL.
+ * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr().
+ */
+static const tor_addr_port_t *
+fascist_firewall_choose_address_impl(const tor_addr_port_t *a,
+ const tor_addr_port_t *b,
+ int want_a,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6)
+{
+ const tor_addr_port_t *use_a = NULL;
+ const tor_addr_port_t *use_b = NULL;
+
+ if (fascist_firewall_allows_address_ap(a, fw_connection, pref_only,
+ pref_ipv6)) {
+ use_a = a;
+ }
+
+ if (fascist_firewall_allows_address_ap(b, fw_connection, pref_only,
+ pref_ipv6)) {
+ use_b = b;
+ }
+
+ /* If both are allowed */
+ if (use_a && use_b) {
+ /* Choose a if we want it */
+ return (want_a ? use_a : use_b);
+ } else {
+ /* Choose a if we have it */
+ return (use_a ? use_a : use_b);
+ }
+}
+
+/** If a and b are both valid and preferred by fw_connection,
+ * choose one based on want_a and return it.
+ * Otherwise, return whichever is preferred.
+ * If neither are preferred, and pref_only is false:
+ * - If a and b are both allowed by fw_connection,
+ * choose one based on want_a and return it.
+ * - Otherwise, return whichever is preferred.
+ * Otherwise, return NULL. */
+STATIC const tor_addr_port_t *
+fascist_firewall_choose_address(const tor_addr_port_t *a,
+ const tor_addr_port_t *b,
+ int want_a,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6)
+{
+ const tor_addr_port_t *pref = fascist_firewall_choose_address_impl(
+ a, b, want_a,
+ fw_connection,
+ 1, pref_ipv6);
+ if (pref_only || pref) {
+ /* If there is a preferred address, use it. If we can only use preferred
+ * addresses, and neither address is preferred, pref will be NULL, and we
+ * want to return NULL, so return it. */
+ return pref;
+ } else {
+ /* If there's no preferred address, and we can return addresses that are
+ * not preferred, use an address that's allowed */
+ return fascist_firewall_choose_address_impl(a, b, want_a, fw_connection,
+ 0, pref_ipv6);
+ }
+}
+
+/** Copy an address and port into <b>ap</b> that we think our firewall will
+ * let us connect to. Uses ipv4_addr/ipv6_addr and
+ * ipv4_orport/ipv6_orport/ReachableORAddresses or
+ * ipv4_dirport/ipv6_dirport/ReachableDirAddresses based on IPv4/IPv6 and
+ * <b>fw_connection</b>.
+ * If pref_only, only choose preferred addresses. In either case, choose
+ * a preferred address before an address that's not preferred.
+ * If both addresses could be chosen (they are both preferred or both allowed)
+ * choose IPv6 if pref_ipv6 is true, otherwise choose IPv4.
+ * If neither address is chosen, return 0, else return 1. */
+static int
+fascist_firewall_choose_address_base(const tor_addr_t *ipv4_addr,
+ uint16_t ipv4_orport,
+ uint16_t ipv4_dirport,
+ const tor_addr_t *ipv6_addr,
+ uint16_t ipv6_orport,
+ uint16_t ipv6_dirport,
+ firewall_connection_t fw_connection,
+ int pref_only,
+ int pref_ipv6,
+ tor_addr_port_t* ap)
+{
+ const tor_addr_port_t *result = NULL;
+ const int want_ipv4 = !pref_ipv6;
+
+ tor_assert(ipv6_addr);
+ tor_assert(ap);
+
+ tor_addr_port_t ipv4_ap;
+ tor_addr_copy(&ipv4_ap.addr, ipv4_addr);
+ ipv4_ap.port = (fw_connection == FIREWALL_OR_CONNECTION
+ ? ipv4_orport
+ : ipv4_dirport);
+
+ tor_addr_port_t ipv6_ap;
+ tor_addr_copy(&ipv6_ap.addr, ipv6_addr);
+ ipv6_ap.port = (fw_connection == FIREWALL_OR_CONNECTION
+ ? ipv6_orport
+ : ipv6_dirport);
+
+ result = fascist_firewall_choose_address(&ipv4_ap, &ipv6_ap,
+ want_ipv4,
+ fw_connection, pref_only,
+ pref_ipv6);
+
+ if (result) {
+ tor_addr_copy(&ap->addr, &result->addr);
+ ap->port = result->port;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/** Like fascist_firewall_choose_address_base(), but takes a host-order IPv4
+ * address as the first parameter. */
+static int
+fascist_firewall_choose_address_ipv4h(uint32_t ipv4h_addr,
+ uint16_t ipv4_orport,
+ uint16_t ipv4_dirport,
+ const tor_addr_t *ipv6_addr,
+ uint16_t ipv6_orport,
+ uint16_t ipv6_dirport,
+ firewall_connection_t fw_connection,
+ int pref_only,
+ int pref_ipv6,
+ tor_addr_port_t* ap)
+{
+ tor_addr_t ipv4_addr;
+ tor_addr_from_ipv4h(&ipv4_addr, ipv4h_addr);
+ return fascist_firewall_choose_address_base(&ipv4_addr, ipv4_orport,
+ ipv4_dirport, ipv6_addr,
+ ipv6_orport, ipv6_dirport,
+ fw_connection, pref_only,
+ pref_ipv6, ap);
+}
+
+/** Like fascist_firewall_choose_address_base(), but takes <b>rs</b>.
+ * Consults the corresponding node, then falls back to rs if node is NULL.
+ * This should only happen when there's no valid consensus, and rs doesn't
+ * correspond to a bridge client's bridge.
+ */
+int
+fascist_firewall_choose_address_rs(const routerstatus_t *rs,
+ firewall_connection_t fw_connection,
+ int pref_only, tor_addr_port_t* ap)
+{
+ if (!rs) {
+ return 0;
+ }
+
+ tor_assert(ap);
+
+ const node_t *node = node_get_by_id(rs->identity_digest);
+
+ if (node) {
+ return fascist_firewall_choose_address_node(node, fw_connection, pref_only,
+ ap);
+ } else {
+ /* There's no node-specific IPv6 preference, so use the generic IPv6
+ * preference instead. */
+ const or_options_t *options = get_options();
+ int pref_ipv6 = (fw_connection == FIREWALL_OR_CONNECTION
+ ? fascist_firewall_prefer_ipv6_orport(options)
+ : fascist_firewall_prefer_ipv6_dirport(options));
+
+ /* Assume IPv4 and IPv6 DirPorts are the same.
+ * Assume the IPv6 OR and Dir addresses are the same. */
+ return fascist_firewall_choose_address_ipv4h(rs->addr,
+ rs->or_port,
+ rs->dir_port,
+ &rs->ipv6_addr,
+ rs->ipv6_orport,
+ rs->dir_port,
+ fw_connection,
+ pref_only,
+ pref_ipv6,
+ ap);
+ }
+}
+
+/** Like fascist_firewall_choose_address_base(), but takes <b>node</b>, and
+ * looks up the node's IPv6 preference rather than taking an argument
+ * for pref_ipv6. */
+int
+fascist_firewall_choose_address_node(const node_t *node,
+ firewall_connection_t fw_connection,
+ int pref_only, tor_addr_port_t *ap)
+{
+ if (!node) {
+ return 0;
+ }
+
+ node_assert_ok(node);
+
+ const int pref_ipv6_node = (fw_connection == FIREWALL_OR_CONNECTION
+ ? node_ipv6_or_preferred(node)
+ : node_ipv6_dir_preferred(node));
+
+ tor_addr_port_t ipv4_or_ap;
+ node_get_prim_orport(node, &ipv4_or_ap);
+ tor_addr_port_t ipv4_dir_ap;
+ node_get_prim_dirport(node, &ipv4_dir_ap);
+
+ tor_addr_port_t ipv6_or_ap;
+ node_get_pref_ipv6_orport(node, &ipv6_or_ap);
+ tor_addr_port_t ipv6_dir_ap;
+ node_get_pref_ipv6_dirport(node, &ipv6_dir_ap);
+
+ /* Assume the IPv6 OR and Dir addresses are the same. */
+ return fascist_firewall_choose_address_base(&ipv4_or_ap.addr,
+ ipv4_or_ap.port,
+ ipv4_dir_ap.port,
+ &ipv6_or_ap.addr,
+ ipv6_or_ap.port,
+ ipv6_dir_ap.port,
+ fw_connection,
+ pref_only,
+ pref_ipv6_node,
+ ap);
+}
+
+/** Like fascist_firewall_choose_address_rs(), but takes <b>ds</b>. */
+int
+fascist_firewall_choose_address_dir_server(const dir_server_t *ds,
+ firewall_connection_t fw_connection,
+ int pref_only,
+ tor_addr_port_t *ap)
+{
+ if (!ds) {
+ return 0;
+ }
+
+ /* A dir_server_t always has a fake_status. As long as it has the same
+ * addresses/ports in both fake_status and dir_server_t, this works fine.
+ * (See #17867.)
+ * This function relies on fascist_firewall_choose_address_rs looking up the
+ * node if it can, because that will get the latest info for the relay. */
+ return fascist_firewall_choose_address_rs(&ds->fake_status, fw_connection,
+ pref_only, ap);
}
/** Return 1 if <b>addr</b> is permitted to connect to our dir port,
@@ -443,7 +1082,7 @@ validate_addr_policies(const or_options_t *options, char **msg)
smartlist_t *addr_policy=NULL;
*msg = NULL;
- if (policies_parse_exit_policy_from_options(options,0,NULL,0,&addr_policy)) {
+ if (policies_parse_exit_policy_from_options(options,0,NULL,&addr_policy)) {
REJECT("Error in ExitPolicy entry.");
}
@@ -625,7 +1264,7 @@ typedef struct policy_map_ent_t {
static HT_HEAD(policy_map, policy_map_ent_t) policy_root = HT_INITIALIZER();
/** Return true iff a and b are equal. */
-static INLINE int
+static inline int
policy_eq(policy_map_ent_t *a, policy_map_ent_t *b)
{
return cmp_single_addr_policy(a->policy, b->policy) == 0;
@@ -693,6 +1332,10 @@ compare_known_tor_addr_to_addr_policy(const tor_addr_t *addr, uint16_t port,
/* We know the address and port, and we know the policy, so we can just
* compute an exact match. */
SMARTLIST_FOREACH_BEGIN(policy, addr_policy_t *, tmpe) {
+ if (tmpe->addr.family == AF_UNSPEC) {
+ log_warn(LD_BUG, "Policy contains an AF_UNSPEC address, which only "
+ "matches other AF_UNSPEC addresses.");
+ }
/* Address is known */
if (!tor_addr_compare_masked(addr, &tmpe->addr, tmpe->maskbits,
CMP_EXACT)) {
@@ -720,6 +1363,10 @@ compare_known_tor_addr_to_addr_policy_noport(const tor_addr_t *addr,
int maybe_accept = 0, maybe_reject = 0;
SMARTLIST_FOREACH_BEGIN(policy, addr_policy_t *, tmpe) {
+ if (tmpe->addr.family == AF_UNSPEC) {
+ log_warn(LD_BUG, "Policy contains an AF_UNSPEC address, which only "
+ "matches other AF_UNSPEC addresses.");
+ }
if (!tor_addr_compare_masked(addr, &tmpe->addr, tmpe->maskbits,
CMP_EXACT)) {
if (tmpe->prt_min <= 1 && tmpe->prt_max >= 65535) {
@@ -759,6 +1406,10 @@ compare_unknown_tor_addr_to_addr_policy(uint16_t port,
int maybe_accept = 0, maybe_reject = 0;
SMARTLIST_FOREACH_BEGIN(policy, addr_policy_t *, tmpe) {
+ if (tmpe->addr.family == AF_UNSPEC) {
+ log_warn(LD_BUG, "Policy contains an AF_UNSPEC address, which only "
+ "matches other AF_UNSPEC addresses.");
+ }
if (tmpe->prt_min <= port && port <= tmpe->prt_max) {
if (tmpe->maskbits == 0) {
/* Definitely matches, since it covers all addresses. */
@@ -864,7 +1515,7 @@ addr_policy_intersects(addr_policy_t *a, addr_policy_t *b)
/** Add the exit policy described by <b>more</b> to <b>policy</b>.
*/
-static void
+STATIC void
append_exit_policy_string(smartlist_t **policy, const char *more)
{
config_line_t tmp;
@@ -881,6 +1532,9 @@ append_exit_policy_string(smartlist_t **policy, const char *more)
void
addr_policy_append_reject_addr(smartlist_t **dest, const tor_addr_t *addr)
{
+ tor_assert(dest);
+ tor_assert(addr);
+
addr_policy_t p, *add;
memset(&p, 0, sizeof(p));
p.policy_type = ADDR_POLICY_REJECT;
@@ -893,6 +1547,71 @@ addr_policy_append_reject_addr(smartlist_t **dest, const tor_addr_t *addr)
if (!*dest)
*dest = smartlist_new();
smartlist_add(*dest, add);
+ log_debug(LD_CONFIG, "Adding a reject ExitPolicy 'reject %s:*'",
+ fmt_addr(addr));
+}
+
+/* Is addr public for the purposes of rejection? */
+static int
+tor_addr_is_public_for_reject(const tor_addr_t *addr)
+{
+ return (!tor_addr_is_null(addr) && !tor_addr_is_internal(addr, 0)
+ && !tor_addr_is_multicast(addr));
+}
+
+/* Add "reject <b>addr</b>:*" to <b>dest</b>, creating the list as needed.
+ * Filter the address, only adding an IPv4 reject rule if ipv4_rules
+ * is true, and similarly for ipv6_rules. Check each address returns true for
+ * tor_addr_is_public_for_reject before adding it.
+ */
+static void
+addr_policy_append_reject_addr_filter(smartlist_t **dest,
+ const tor_addr_t *addr,
+ int ipv4_rules,
+ int ipv6_rules)
+{
+ tor_assert(dest);
+ tor_assert(addr);
+
+ /* Only reject IP addresses which are public */
+ if (tor_addr_is_public_for_reject(addr)) {
+
+ /* Reject IPv4 addresses and IPv6 addresses based on the filters */
+ int is_ipv4 = tor_addr_is_v4(addr);
+ if ((is_ipv4 && ipv4_rules) || (!is_ipv4 && ipv6_rules)) {
+ addr_policy_append_reject_addr(dest, addr);
+ }
+ }
+}
+
+/** Add "reject addr:*" to <b>dest</b>, for each addr in addrs, creating the
+ * list as needed. */
+void
+addr_policy_append_reject_addr_list(smartlist_t **dest,
+ const smartlist_t *addrs)
+{
+ tor_assert(dest);
+ tor_assert(addrs);
+
+ SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, addr) {
+ addr_policy_append_reject_addr(dest, addr);
+ } SMARTLIST_FOREACH_END(addr);
+}
+
+/** Add "reject addr:*" to <b>dest</b>, for each addr in addrs, creating the
+ * list as needed. Filter using */
+static void
+addr_policy_append_reject_addr_list_filter(smartlist_t **dest,
+ const smartlist_t *addrs,
+ int ipv4_rules,
+ int ipv6_rules)
+{
+ tor_assert(dest);
+ tor_assert(addrs);
+
+ SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, addr) {
+ addr_policy_append_reject_addr_filter(dest, addr, ipv4_rules, ipv6_rules);
+ } SMARTLIST_FOREACH_END(addr);
}
/** Detect and excise "dead code" from the policy *<b>dest</b>. */
@@ -979,127 +1698,92 @@ exit_policy_remove_redundancies(smartlist_t *dest)
}
}
-#define DEFAULT_EXIT_POLICY \
- "reject *:25,reject *:119,reject *:135-139,reject *:445," \
- "reject *:563,reject *:1214,reject *:4661-4666," \
- "reject *:6346-6429,reject *:6699,reject *:6881-6999,accept *:*"
-
-/** Parse the exit policy <b>cfg</b> into the linked list *<b>dest</b>.
- *
- * If <b>ipv6_exit</b> is true, prepend "reject *6:*" to the policy.
+/** Reject private helper for policies_parse_exit_policy_internal: rejects
+ * publicly routable addresses on this exit relay.
*
- * If <b>rejectprivate</b> is true:
- * - prepend "reject private:*" to the policy.
- * - if local_address is non-zero, treat it as a host-order IPv4 address,
- * and prepend an entry that rejects it as a destination.
- * - if ipv6_local_address is non-NULL, prepend an entry that rejects it as
- * a destination.
- * - if reject_interface_addresses is true, prepend entries that reject each
+ * Add reject entries to the linked list *<b>dest</b>:
+ * <ul>
+ * <li>if configured_addresses is non-NULL, add entries that reject each
+ * tor_addr_t in the list as a destination.
+ * <li>if reject_interface_addresses is true, add entries that reject each
* public IPv4 and IPv6 address of each interface on this machine.
+ * <li>if reject_configured_port_addresses is true, add entries that reject
+ * each IPv4 and IPv6 address configured for a port.
+ * </ul>
*
- * If cfg doesn't end in an absolute accept or reject and if
- * <b>add_default_policy</b> is true, add the default exit
- * policy afterwards.
- *
- * Return -1 if we can't parse cfg, else return 0.
+ * IPv6 entries are only added if ipv6_exit is true. (All IPv6 addresses are
+ * already blocked by policies_parse_exit_policy_internal if ipv6_exit is
+ * false.)
*
- * This function is used to parse the exit policy from our torrc. For
- * the functions used to parse the exit policy from a router descriptor,
- * see router_add_exit_policy.
+ * The list in <b>dest</b> is created as needed.
*/
-static int
-policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest,
- int ipv6_exit,
- int rejectprivate,
- uint32_t local_address,
- tor_addr_t *ipv6_local_address,
- int reject_interface_addresses,
- int add_default_policy)
+void
+policies_parse_exit_policy_reject_private(
+ smartlist_t **dest,
+ int ipv6_exit,
+ const smartlist_t *configured_addresses,
+ int reject_interface_addresses,
+ int reject_configured_port_addresses)
{
- if (!ipv6_exit) {
- append_exit_policy_string(dest, "reject *6:*");
+ tor_assert(dest);
+
+ /* Reject configured addresses, if they are from public netblocks. */
+ if (configured_addresses) {
+ addr_policy_append_reject_addr_list_filter(dest, configured_addresses,
+ 1, ipv6_exit);
}
- if (rejectprivate) {
- /* Reject IPv4 and IPv6 reserved private netblocks */
- append_exit_policy_string(dest, "reject private:*");
- /* Reject our local IPv4 address */
- if (local_address) {
- char buf[POLICY_BUF_LEN];
- tor_snprintf(buf, sizeof(buf), "reject %s:*", fmt_addr32(local_address));
- append_exit_policy_string(dest, buf);
- log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for our published "
- "IPv4 address", buf);
- }
- /* Reject our local IPv6 address */
- if (ipv6_exit && ipv6_local_address != NULL) {
- if (tor_addr_is_v4(ipv6_local_address)) {
- log_warn(LD_CONFIG, "IPv4 address '%s' provided as our IPv6 local "
- "address", fmt_addr(ipv6_local_address));
- } else {
- char buf6[POLICY_BUF_LEN];
- tor_snprintf(buf6, sizeof(buf6), "reject [%s]:*",
- fmt_addr(ipv6_local_address));
- append_exit_policy_string(dest, buf6);
- log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for our "
- "published IPv6 address", buf6);
- }
- }
- /* Reject local addresses from public netblocks on any interface,
- * but don't reject our published addresses twice */
- if (reject_interface_addresses) {
- smartlist_t *public_addresses = NULL;
- char bufif[POLICY_BUF_LEN];
-
- /* Reject public IPv4 addresses on any interface,
- * but don't reject our published IPv4 address twice */
- public_addresses = get_interface_address6_list(LOG_INFO, AF_INET, 0);
- SMARTLIST_FOREACH_BEGIN(public_addresses, tor_addr_t *, a) {
- if (!tor_addr_eq_ipv4h(a, local_address)) {
- tor_snprintf(bufif, sizeof(bufif), "reject %s:*",
- fmt_addr(a));
- append_exit_policy_string(dest, bufif);
- log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for a local "
- "interface's public IPv4 address", bufif);
- }
- } SMARTLIST_FOREACH_END(a);
- free_interface_address6_list(public_addresses);
- if (ipv6_exit) {
- /* Reject public IPv6 addresses on any interface,
- * but don't reject our published IPv6 address (if any) twice */
- public_addresses = get_interface_address6_list(LOG_INFO, AF_INET6, 0);
- SMARTLIST_FOREACH_BEGIN(public_addresses, tor_addr_t *, a) {
- /* if we don't have an IPv6 local address, we won't have rejected
- * it above. This could happen if a future release does IPv6
- * autodiscovery, and we are waiting to discover our external IPv6
- * address */
- if (ipv6_local_address == NULL
- || !tor_addr_eq(ipv6_local_address, a)) {
- tor_snprintf(bufif, sizeof(bufif), "reject6 [%s]:*",
- fmt_addr(a));
- append_exit_policy_string(dest, bufif);
- log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for a local "
- "interface's public IPv6 address", bufif);
- }
- } SMARTLIST_FOREACH_END(a);
- free_interface_address6_list(public_addresses);
+ /* Reject configured port addresses, if they are from public netblocks. */
+ if (reject_configured_port_addresses) {
+ const smartlist_t *port_addrs = get_configured_ports();
+
+ SMARTLIST_FOREACH_BEGIN(port_addrs, port_cfg_t *, port) {
+
+ /* Only reject port IP addresses, not port unix sockets */
+ if (!port->is_unix_addr) {
+ addr_policy_append_reject_addr_filter(dest, &port->addr, 1, ipv6_exit);
}
+ } SMARTLIST_FOREACH_END(port);
+ }
+
+ /* Reject local addresses from public netblocks on any interface. */
+ if (reject_interface_addresses) {
+ smartlist_t *public_addresses = NULL;
+
+ /* Reject public IPv4 addresses on any interface */
+ public_addresses = get_interface_address6_list(LOG_INFO, AF_INET, 0);
+ addr_policy_append_reject_addr_list_filter(dest, public_addresses, 1, 0);
+ free_interface_address6_list(public_addresses);
+
+ /* Don't look for IPv6 addresses if we're configured as IPv4-only */
+ if (ipv6_exit) {
+ /* Reject public IPv6 addresses on any interface */
+ public_addresses = get_interface_address6_list(LOG_INFO, AF_INET6, 0);
+ addr_policy_append_reject_addr_list_filter(dest, public_addresses, 0, 1);
+ free_interface_address6_list(public_addresses);
}
}
- if (parse_addr_policy(cfg, dest, -1))
- return -1;
- /* Before we add the default policy and final rejects, check to see if
- * there are any lines after accept *:* or reject *:*. These lines have no
- * effect, and are most likely an error. */
+ /* If addresses were added multiple times, remove all but one of them. */
+ if (*dest) {
+ exit_policy_remove_redundancies(*dest);
+ }
+}
+
+/**
+ * Iterate through <b>policy</b> looking for redundant entries. Log a
+ * warning message with the first redundant entry, if any is found.
+ */
+static void
+policies_log_first_redundant_entry(const smartlist_t *policy)
+{
int found_final_effective_entry = 0;
int first_redundant_entry = 0;
- for (int i = 0; i < smartlist_len(*dest); ++i) {
+ tor_assert(policy);
+ SMARTLIST_FOREACH_BEGIN(policy, const addr_policy_t *, p) {
sa_family_t family;
- addr_policy_t *p;
int found_ipv4_wildcard = 0, found_ipv6_wildcard = 0;
-
- p = smartlist_get(*dest, i);
+ const int i = p_sl_idx;
/* Look for accept/reject *[4|6|]:* entires */
if (p->prt_min <= 1 && p->prt_max == 65535 && p->maskbits == 0) {
@@ -1122,22 +1806,23 @@ policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest,
if (found_ipv4_wildcard && found_ipv6_wildcard) {
found_final_effective_entry = 1;
/* if we're not on the final entry in the list */
- if (i < smartlist_len(*dest) - 1) {
+ if (i < smartlist_len(policy) - 1) {
first_redundant_entry = i + 1;
}
break;
}
- }
+ } SMARTLIST_FOREACH_END(p);
+
/* Work out if there are redundant trailing entries in the policy list */
if (found_final_effective_entry && first_redundant_entry > 0) {
- addr_policy_t *p;
+ const addr_policy_t *p;
/* Longest possible policy is
* "accept6 ffff:ffff:..255/128:10000-65535",
* which contains a max-length IPv6 address, plus 24 characters. */
char line[TOR_ADDR_BUF_LEN + 32];
- tor_assert(first_redundant_entry < smartlist_len(*dest));
- p = smartlist_get(*dest, first_redundant_entry);
+ tor_assert(first_redundant_entry < smartlist_len(policy));
+ p = smartlist_get(policy, first_redundant_entry);
/* since we've already parsed the policy into an addr_policy_t struct,
* we might not log exactly what the user typed in */
policy_write_item(line, TOR_ADDR_BUF_LEN + 32, p, 0);
@@ -1147,6 +1832,62 @@ policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest,
"accept/reject *:* as the last entry in any exit policy.)",
line);
}
+}
+
+#define DEFAULT_EXIT_POLICY \
+ "reject *:25,reject *:119,reject *:135-139,reject *:445," \
+ "reject *:563,reject *:1214,reject *:4661-4666," \
+ "reject *:6346-6429,reject *:6699,reject *:6881-6999,accept *:*"
+
+/** Parse the exit policy <b>cfg</b> into the linked list *<b>dest</b>.
+ *
+ * If <b>ipv6_exit</b> is false, prepend "reject *6:*" to the policy.
+ *
+ * If <b>rejectprivate</b> is true:
+ * - prepend "reject private:*" to the policy.
+ * - prepend entries that reject publicly routable addresses on this exit
+ * relay by calling policies_parse_exit_policy_reject_private
+ *
+ * If cfg doesn't end in an absolute accept or reject and if
+ * <b>add_default_policy</b> is true, add the default exit
+ * policy afterwards.
+ *
+ * Return -1 if we can't parse cfg, else return 0.
+ *
+ * This function is used to parse the exit policy from our torrc. For
+ * the functions used to parse the exit policy from a router descriptor,
+ * see router_add_exit_policy.
+ */
+static int
+policies_parse_exit_policy_internal(config_line_t *cfg,
+ smartlist_t **dest,
+ int ipv6_exit,
+ int rejectprivate,
+ const smartlist_t *configured_addresses,
+ int reject_interface_addresses,
+ int reject_configured_port_addresses,
+ int add_default_policy)
+{
+ if (!ipv6_exit) {
+ append_exit_policy_string(dest, "reject *6:*");
+ }
+ if (rejectprivate) {
+ /* Reject IPv4 and IPv6 reserved private netblocks */
+ append_exit_policy_string(dest, "reject private:*");
+ /* Reject IPv4 and IPv6 publicly routable addresses on this exit relay */
+ policies_parse_exit_policy_reject_private(
+ dest, ipv6_exit,
+ configured_addresses,
+ reject_interface_addresses,
+ reject_configured_port_addresses);
+ }
+ if (parse_addr_policy(cfg, dest, -1))
+ return -1;
+
+ /* Before we add the default policy and final rejects, check to see if
+ * there are any lines after accept *:* or reject *:*. These lines have no
+ * effect, and are most likely an error. */
+ policies_log_first_redundant_entry(*dest);
if (add_default_policy) {
append_exit_policy_string(dest, DEFAULT_EXIT_POLICY);
@@ -1167,12 +1908,8 @@ policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest,
* If <b>EXIT_POLICY_REJECT_PRIVATE</b> bit is set in <b>options</b>:
* - prepend an entry that rejects all destinations in all netblocks
* reserved for private use.
- * - if local_address is non-zero, treat it as a host-order IPv4 address,
- * and prepend an entry that rejects it as a destination.
- * - if ipv6_local_address is non-NULL, prepend an entry that rejects it as
- * a destination.
- * - if reject_interface_addresses is true, prepend entries that reject each
- * public IPv4 and IPv6 address of each interface on this machine.
+ * - prepend entries that reject publicly routable addresses on this exit
+ * relay by calling policies_parse_exit_policy_internal
*
* If <b>EXIT_POLICY_ADD_DEFAULT</b> bit is set in <b>options</b>, append
* default exit policy entries to <b>result</b> smartlist.
@@ -1180,9 +1917,7 @@ policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest,
int
policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
exit_policy_parser_cfg_t options,
- uint32_t local_address,
- tor_addr_t *ipv6_local_address,
- int reject_interface_addresses)
+ const smartlist_t *configured_addresses)
{
int ipv6_enabled = (options & EXIT_POLICY_IPV6_ENABLED) ? 1 : 0;
int reject_private = (options & EXIT_POLICY_REJECT_PRIVATE) ? 1 : 0;
@@ -1190,12 +1925,62 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
return policies_parse_exit_policy_internal(cfg,dest,ipv6_enabled,
reject_private,
- local_address,
- ipv6_local_address,
- reject_interface_addresses,
+ configured_addresses,
+ reject_private,
+ reject_private,
add_default);
}
+/** Helper function that adds a copy of addr to a smartlist as long as it is
+ * non-NULL and not tor_addr_is_null().
+ *
+ * The caller is responsible for freeing all the tor_addr_t* in the smartlist.
+ */
+static void
+policies_copy_addr_to_smartlist(smartlist_t *addr_list, const tor_addr_t *addr)
+{
+ if (addr && !tor_addr_is_null(addr)) {
+ tor_addr_t *addr_copy = tor_malloc(sizeof(tor_addr_t));
+ tor_addr_copy(addr_copy, addr);
+ smartlist_add(addr_list, addr_copy);
+ }
+}
+
+/** Helper function that adds ipv4h_addr to a smartlist as a tor_addr_t *,
+ * as long as it is not tor_addr_is_null(), by converting it to a tor_addr_t
+ * and passing it to policies_add_addr_to_smartlist.
+ *
+ * The caller is responsible for freeing all the tor_addr_t* in the smartlist.
+ */
+static void
+policies_copy_ipv4h_to_smartlist(smartlist_t *addr_list, uint32_t ipv4h_addr)
+{
+ if (ipv4h_addr) {
+ tor_addr_t ipv4_tor_addr;
+ tor_addr_from_ipv4h(&ipv4_tor_addr, ipv4h_addr);
+ policies_copy_addr_to_smartlist(addr_list, &ipv4_tor_addr);
+ }
+}
+
+/** Helper function that adds copies of
+ * or_options->OutboundBindAddressIPv[4|6]_ to a smartlist as tor_addr_t *, as
+ * long as or_options is non-NULL, and the addresses are not
+ * tor_addr_is_null(), by passing them to policies_add_addr_to_smartlist.
+ *
+ * The caller is responsible for freeing all the tor_addr_t* in the smartlist.
+ */
+static void
+policies_copy_outbound_addresses_to_smartlist(smartlist_t *addr_list,
+ const or_options_t *or_options)
+{
+ if (or_options) {
+ policies_copy_addr_to_smartlist(addr_list,
+ &or_options->OutboundBindAddressIPv4_);
+ policies_copy_addr_to_smartlist(addr_list,
+ &or_options->OutboundBindAddressIPv6_);
+ }
+}
+
/** Parse <b>ExitPolicy</b> member of <b>or_options</b> into <b>result</b>
* smartlist.
* If <b>or_options->IPv6Exit</b> is false, prepend an entry that
@@ -1205,11 +1990,13 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
* - prepend an entry that rejects all destinations in all netblocks reserved
* for private use.
* - if local_address is non-zero, treat it as a host-order IPv4 address, and
- * prepend an entry that rejects it as a destination.
- * - if ipv6_local_address is non-NULL, prepend an entry that rejects it as a
- * destination.
- * - if reject_interface_addresses is true, prepend entries that reject each
- * public IPv4 and IPv6 address of each interface on this machine.
+ * add it to the list of configured addresses.
+ * - if ipv6_local_address is non-NULL, and not the null tor_addr_t, add it
+ * to the list of configured addresses.
+ * - if or_options->OutboundBindAddressIPv4_ is not the null tor_addr_t, add
+ * it to the list of configured addresses.
+ * - if or_options->OutboundBindAddressIPv6_ is not the null tor_addr_t, add
+ * it to the list of configured addresses.
*
* If <b>or_options->BridgeRelay</b> is false, append entries of default
* Tor exit policy into <b>result</b> smartlist.
@@ -1220,18 +2007,23 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
int
policies_parse_exit_policy_from_options(const or_options_t *or_options,
uint32_t local_address,
- tor_addr_t *ipv6_local_address,
- int reject_interface_addresses,
+ const tor_addr_t *ipv6_local_address,
smartlist_t **result)
{
exit_policy_parser_cfg_t parser_cfg = 0;
+ smartlist_t *configured_addresses = NULL;
+ int rv = 0;
+ /* Short-circuit for non-exit relays */
if (or_options->ExitRelay == 0) {
append_exit_policy_string(result, "reject *4:*");
append_exit_policy_string(result, "reject *6:*");
return 0;
}
+ configured_addresses = smartlist_new();
+
+ /* Configure the parser */
if (or_options->IPv6Exit) {
parser_cfg |= EXIT_POLICY_IPV6_ENABLED;
}
@@ -1244,10 +2036,19 @@ policies_parse_exit_policy_from_options(const or_options_t *or_options,
parser_cfg |= EXIT_POLICY_ADD_DEFAULT;
}
- return policies_parse_exit_policy(or_options->ExitPolicy,result,
- parser_cfg,local_address,
- ipv6_local_address,
- reject_interface_addresses);
+ /* Copy the configured addresses into the tor_addr_t* list */
+ policies_copy_ipv4h_to_smartlist(configured_addresses, local_address);
+ policies_copy_addr_to_smartlist(configured_addresses, ipv6_local_address);
+ policies_copy_outbound_addresses_to_smartlist(configured_addresses,
+ or_options);
+
+ rv = policies_parse_exit_policy(or_options->ExitPolicy, result, parser_cfg,
+ configured_addresses);
+
+ SMARTLIST_FOREACH(configured_addresses, tor_addr_t *, a, tor_free(a));
+ smartlist_free(configured_addresses);
+
+ return rv;
}
/** Add "reject *:*" to the end of the policy in *<b>dest</b>, allocating
@@ -1355,7 +2156,7 @@ policy_is_reject_star(const smartlist_t *policy, sa_family_t family)
/** Write a single address policy to the buf_len byte buffer at buf. Return
* the number of characters written, or -1 on failure. */
int
-policy_write_item(char *buf, size_t buflen, addr_policy_t *policy,
+policy_write_item(char *buf, size_t buflen, const addr_policy_t *policy,
int format_for_desc)
{
size_t written = 0;
@@ -1873,7 +2674,7 @@ compare_tor_addr_to_short_policy(const tor_addr_t *addr, uint16_t port,
* allows exit enclaving. Trying it anyway would open up a cool attack
* where the node refuses due to exitpolicy, the client reacts in
* surprise by rewriting the node's exitpolicy to reject *:*, and then
- * a bad guy targets users by causing them to attempt such connections
+ * an adversary targets users by causing them to attempt such connections
* to 98% of the exits.
*
* Once microdescriptors can handle addresses in special cases (e.g. if
@@ -1934,6 +2735,53 @@ compare_tor_addr_to_node_policy(const tor_addr_t *addr, uint16_t port,
}
}
+/**
+ * Given <b>policy_list</b>, a list of addr_policy_t, produce a string
+ * representation of the list.
+ * If <b>include_ipv4</b> is true, include IPv4 entries.
+ * If <b>include_ipv6</b> is true, include IPv6 entries.
+ */
+char *
+policy_dump_to_string(const smartlist_t *policy_list,
+ int include_ipv4,
+ int include_ipv6)
+{
+ smartlist_t *policy_string_list;
+ char *policy_string = NULL;
+
+ policy_string_list = smartlist_new();
+
+ SMARTLIST_FOREACH_BEGIN(policy_list, addr_policy_t *, tmpe) {
+ char *pbuf;
+ int bytes_written_to_pbuf;
+ if ((tor_addr_family(&tmpe->addr) == AF_INET6) && (!include_ipv6)) {
+ continue; /* Don't include IPv6 parts of address policy */
+ }
+ if ((tor_addr_family(&tmpe->addr) == AF_INET) && (!include_ipv4)) {
+ continue; /* Don't include IPv4 parts of address policy */
+ }
+
+ pbuf = tor_malloc(POLICY_BUF_LEN);
+ bytes_written_to_pbuf = policy_write_item(pbuf,POLICY_BUF_LEN, tmpe, 1);
+
+ if (bytes_written_to_pbuf < 0) {
+ log_warn(LD_BUG, "policy_dump_to_string ran out of room!");
+ tor_free(pbuf);
+ goto done;
+ }
+
+ smartlist_add(policy_string_list,pbuf);
+ } SMARTLIST_FOREACH_END(tmpe);
+
+ policy_string = smartlist_join_strings(policy_string_list, "\n", 0, NULL);
+
+ done:
+ SMARTLIST_FOREACH(policy_string_list, char *, str, tor_free(str));
+ smartlist_free(policy_string_list);
+
+ return policy_string;
+}
+
/** Implementation for GETINFO control command: knows the answer for questions
* about "exit-policy/..." */
int
@@ -1945,6 +2793,57 @@ getinfo_helper_policies(control_connection_t *conn,
(void) errmsg;
if (!strcmp(question, "exit-policy/default")) {
*answer = tor_strdup(DEFAULT_EXIT_POLICY);
+ } else if (!strcmp(question, "exit-policy/reject-private/default")) {
+ smartlist_t *private_policy_strings;
+ const char **priv = private_nets;
+
+ private_policy_strings = smartlist_new();
+
+ while (*priv != NULL) {
+ /* IPv6 addresses are in "[]" and contain ":",
+ * IPv4 addresses are not in "[]" and contain "." */
+ smartlist_add_asprintf(private_policy_strings, "reject %s:*", *priv);
+ priv++;
+ }
+
+ *answer = smartlist_join_strings(private_policy_strings,
+ ",", 0, NULL);
+
+ SMARTLIST_FOREACH(private_policy_strings, char *, str, tor_free(str));
+ smartlist_free(private_policy_strings);
+ } else if (!strcmp(question, "exit-policy/reject-private/relay")) {
+ const or_options_t *options = get_options();
+ const routerinfo_t *me = router_get_my_routerinfo();
+
+ if (!me) {
+ *errmsg = "router_get_my_routerinfo returned NULL";
+ return -1;
+ }
+
+ if (!options->ExitPolicyRejectPrivate) {
+ *answer = tor_strdup("");
+ return 0;
+ }
+
+ smartlist_t *private_policy_list = smartlist_new();
+ smartlist_t *configured_addresses = smartlist_new();
+
+ /* Copy the configured addresses into the tor_addr_t* list */
+ policies_copy_ipv4h_to_smartlist(configured_addresses, me->addr);
+ policies_copy_addr_to_smartlist(configured_addresses, &me->ipv6_addr);
+ policies_copy_outbound_addresses_to_smartlist(configured_addresses,
+ options);
+
+ policies_parse_exit_policy_reject_private(
+ &private_policy_list,
+ options->IPv6Exit,
+ configured_addresses,
+ 1, 1);
+ *answer = policy_dump_to_string(private_policy_list, 1, 1);
+
+ addr_policy_list_free(private_policy_list);
+ SMARTLIST_FOREACH(configured_addresses, tor_addr_t *, a, tor_free(a));
+ smartlist_free(configured_addresses);
} else if (!strcmpstart(question, "exit-policy/")) {
const routerinfo_t *me = router_get_my_routerinfo();
diff --git a/src/or/policies.h b/src/or/policies.h
index f200d7babe..757d2f31df 100644
--- a/src/or/policies.h
+++ b/src/or/policies.h
@@ -22,13 +22,44 @@
#define EXIT_POLICY_REJECT_PRIVATE (1 << 1)
#define EXIT_POLICY_ADD_DEFAULT (1 << 2)
+typedef enum firewall_connection_t {
+ FIREWALL_OR_CONNECTION = 0,
+ FIREWALL_DIR_CONNECTION = 1
+} firewall_connection_t;
+
typedef int exit_policy_parser_cfg_t;
int firewall_is_fascist_or(void);
-int fascist_firewall_allows_address_or(const tor_addr_t *addr, uint16_t port);
-int fascist_firewall_allows_or(const routerinfo_t *ri);
-int fascist_firewall_allows_node(const node_t *node);
-int fascist_firewall_allows_address_dir(const tor_addr_t *addr, uint16_t port);
+int firewall_is_fascist_dir(void);
+int fascist_firewall_use_ipv6(const or_options_t *options);
+int fascist_firewall_prefer_ipv6_orport(const or_options_t *options);
+int fascist_firewall_prefer_ipv6_dirport(const or_options_t *options);
+
+int fascist_firewall_allows_address_addr(const tor_addr_t *addr,
+ uint16_t port,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6);
+
+int fascist_firewall_allows_rs(const routerstatus_t *rs,
+ firewall_connection_t fw_connection,
+ int pref_only);
+int fascist_firewall_allows_node(const node_t *node,
+ firewall_connection_t fw_connection,
+ int pref_only);
+int fascist_firewall_allows_dir_server(const dir_server_t *ds,
+ firewall_connection_t fw_connection,
+ int pref_only);
+
+int fascist_firewall_choose_address_rs(const routerstatus_t *rs,
+ firewall_connection_t fw_connection,
+ int pref_only, tor_addr_port_t* ap);
+int fascist_firewall_choose_address_node(const node_t *node,
+ firewall_connection_t fw_connection,
+ int pref_only, tor_addr_port_t* ap);
+int fascist_firewall_choose_address_dir_server(const dir_server_t *ds,
+ firewall_connection_t fw_connection,
+ int pref_only, tor_addr_port_t* ap);
+
int dir_policy_permits_address(const tor_addr_t *addr);
int socks_policy_permits_address(const tor_addr_t *addr);
int authdir_policy_permits_address(uint32_t addr, uint16_t port);
@@ -44,30 +75,38 @@ addr_policy_t *addr_policy_get_canonical_entry(addr_policy_t *ent);
int cmp_addr_policies(smartlist_t *a, smartlist_t *b);
MOCK_DECL(addr_policy_result_t, compare_tor_addr_to_addr_policy,
(const tor_addr_t *addr, uint16_t port, const smartlist_t *policy));
-
addr_policy_result_t compare_tor_addr_to_node_policy(const tor_addr_t *addr,
uint16_t port, const node_t *node);
-int policies_parse_exit_policy_from_options(const or_options_t *or_options,
- uint32_t local_address,
- tor_addr_t *ipv6_local_address,
- int reject_interface_addresses,
- smartlist_t **result);
+int policies_parse_exit_policy_from_options(
+ const or_options_t *or_options,
+ uint32_t local_address,
+ const tor_addr_t *ipv6_local_address,
+ smartlist_t **result);
int policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
exit_policy_parser_cfg_t options,
- uint32_t local_address,
- tor_addr_t *ipv6_local_address,
- int reject_interface_addresses);
+ const smartlist_t *configured_addresses);
+void policies_parse_exit_policy_reject_private(
+ smartlist_t **dest,
+ int ipv6_exit,
+ const smartlist_t *configured_addresses,
+ int reject_interface_addresses,
+ int reject_configured_port_addresses);
void policies_exit_policy_append_reject_star(smartlist_t **dest);
void addr_policy_append_reject_addr(smartlist_t **dest,
const tor_addr_t *addr);
+void addr_policy_append_reject_addr_list(smartlist_t **dest,
+ const smartlist_t *addrs);
void policies_set_node_exitpolicy_to_reject_all(node_t *exitrouter);
int exit_policy_is_general_exit(smartlist_t *policy);
int policy_is_reject_star(const smartlist_t *policy, sa_family_t family);
+char * policy_dump_to_string(const smartlist_t *policy_list,
+ int include_ipv4,
+ int include_ipv6);
int getinfo_helper_policies(control_connection_t *conn,
const char *question, char **answer,
const char **errmsg);
-int policy_write_item(char *buf, size_t buflen, addr_policy_t *item,
+int policy_write_item(char *buf, size_t buflen, const addr_policy_t *item,
int format_for_desc);
void addr_policy_list_free(smartlist_t *p);
@@ -84,5 +123,20 @@ addr_policy_result_t compare_tor_addr_to_short_policy(
const tor_addr_t *addr, uint16_t port,
const short_policy_t *policy);
+#ifdef POLICIES_PRIVATE
+STATIC void append_exit_policy_string(smartlist_t **policy, const char *more);
+STATIC int fascist_firewall_allows_address(const tor_addr_t *addr,
+ uint16_t port,
+ smartlist_t *firewall_policy,
+ int pref_only, int pref_ipv6);
+STATIC const tor_addr_port_t * fascist_firewall_choose_address(
+ const tor_addr_port_t *a,
+ const tor_addr_port_t *b,
+ int want_a,
+ firewall_connection_t fw_connection,
+ int pref_only, int pref_ipv6);
+
+#endif
+
#endif
diff --git a/src/or/relay.c b/src/or/relay.c
index eddad6a0cb..9d44428c09 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -148,20 +148,15 @@ relay_digest_matches(crypto_digest_t *digest, cell_t *cell)
*
* If <b>encrypt_mode</b> is 1 then encrypt, else decrypt.
*
- * Return -1 if the crypto fails, else return 0.
+ * Returns 0.
*/
static int
relay_crypt_one_payload(crypto_cipher_t *cipher, uint8_t *in,
int encrypt_mode)
{
- int r;
(void)encrypt_mode;
- r = crypto_cipher_crypt_inplace(cipher, (char*) in, CELL_PAYLOAD_SIZE);
+ crypto_cipher_crypt_inplace(cipher, (char*) in, CELL_PAYLOAD_SIZE);
- if (r) {
- log_warn(LD_BUG,"Error during relay encryption");
- return -1;
- }
return 0;
}
@@ -833,7 +828,7 @@ connection_ap_process_end_not_open(
}
}
}
- /* check if he *ought* to have allowed it */
+ /* check if the exit *ought* to have allowed it */
adjust_exit_policy_from_exitpolicy_failure(circ,
conn,
@@ -1304,6 +1299,7 @@ connection_edge_process_relay_cell_not_open(
"Got 'connected' while not in state connect_wait. Dropping.");
return 0;
}
+ CONNECTION_AP_EXPECT_NONPENDING(entry_conn);
conn->base_.state = AP_CONN_STATE_OPEN;
log_info(LD_APP,"'connected' received for circid %u streamid %d "
"after %d seconds.",
@@ -2255,7 +2251,7 @@ circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint)
static size_t total_cells_allocated = 0;
/** Release storage held by <b>cell</b>. */
-static INLINE void
+static inline void
packed_cell_free_unchecked(packed_cell_t *cell)
{
--total_cells_allocated;
@@ -2299,7 +2295,7 @@ dump_cell_pool_usage(int severity)
}
/** Allocate a new copy of packed <b>cell</b>. */
-static INLINE packed_cell_t *
+static inline packed_cell_t *
packed_cell_copy(const cell_t *cell, int wide_circ_ids)
{
packed_cell_t *c = packed_cell_new();
diff --git a/src/or/rendcache.c b/src/or/rendcache.c
index d4bdd68698..c69671e289 100644
--- a/src/or/rendcache.c
+++ b/src/or/rendcache.c
@@ -3,9 +3,10 @@
/**
* \file rendcache.c
- * \brief Hidden service desriptor cache.
+ * \brief Hidden service descriptor cache.
**/
+#define RENDCACHE_PRIVATE
#include "rendcache.h"
#include "config.h"
@@ -15,11 +16,14 @@
/** Map from service id (as generated by rend_get_service_id) to
* rend_cache_entry_t. */
-static strmap_t *rend_cache = NULL;
+STATIC strmap_t *rend_cache = NULL;
+
+/** Map from service id to rend_cache_entry_t; only for hidden services. */
+static strmap_t *rend_cache_local_service = NULL;
/** Map from descriptor id to rend_cache_entry_t; only for hidden service
* directories. */
-static digestmap_t *rend_cache_v2_dir = NULL;
+STATIC digestmap_t *rend_cache_v2_dir = NULL;
/** (Client side only) Map from service id to rend_cache_failure_t. This
* cache is used to track intro point(IP) failures so we know when to keep
@@ -46,10 +50,10 @@ static digestmap_t *rend_cache_v2_dir = NULL;
* This scheme allows us to not realy on the descriptor's timestamp (which
* is rounded down to the hour) to know if we have a newer descriptor. We
* only rely on the usability of intro points from an internal state. */
-static strmap_t *rend_cache_failure = NULL;
+STATIC strmap_t *rend_cache_failure = NULL;
/** DOCDOC */
-static size_t rend_cache_total_allocation = 0;
+STATIC size_t rend_cache_total_allocation = 0;
/** Initializes the service descriptor cache.
*/
@@ -58,11 +62,12 @@ rend_cache_init(void)
{
rend_cache = strmap_new();
rend_cache_v2_dir = digestmap_new();
+ rend_cache_local_service = strmap_new();
rend_cache_failure = strmap_new();
}
/** Return the approximate number of bytes needed to hold <b>e</b>. */
-static size_t
+STATIC size_t
rend_cache_entry_allocation(const rend_cache_entry_t *e)
{
if (!e)
@@ -80,7 +85,7 @@ rend_cache_get_total_allocation(void)
}
/** Decrement the total bytes attributed to the rendezvous cache by n. */
-static void
+STATIC void
rend_cache_decrement_allocation(size_t n)
{
static int have_underflowed = 0;
@@ -97,7 +102,7 @@ rend_cache_decrement_allocation(size_t n)
}
/** Increase the total bytes attributed to the rendezvous cache by n. */
-static void
+STATIC void
rend_cache_increment_allocation(size_t n)
{
static int have_overflowed = 0;
@@ -113,7 +118,7 @@ rend_cache_increment_allocation(size_t n)
}
/** Helper: free a rend cache failure intro object. */
-static void
+STATIC void
rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t *entry)
{
if (entry == NULL) {
@@ -130,7 +135,7 @@ rend_cache_failure_intro_entry_free_(void *entry)
/** Allocate a rend cache failure intro object and return it. <b>failure</b>
* is set into the object. This function can not fail. */
-static rend_cache_failure_intro_t *
+STATIC rend_cache_failure_intro_t *
rend_cache_failure_intro_entry_new(rend_intro_point_failure_t failure)
{
rend_cache_failure_intro_t *entry = tor_malloc(sizeof(*entry));
@@ -140,7 +145,7 @@ rend_cache_failure_intro_entry_new(rend_intro_point_failure_t failure)
}
/** Helper: free a rend cache failure object. */
-static void
+STATIC void
rend_cache_failure_entry_free(rend_cache_failure_t *entry)
{
if (entry == NULL) {
@@ -156,7 +161,7 @@ rend_cache_failure_entry_free(rend_cache_failure_t *entry)
/** Helper: deallocate a rend_cache_failure_t. (Used with strmap_free(),
* which requires a function pointer whose argument is void*). */
-static void
+STATIC void
rend_cache_failure_entry_free_(void *entry)
{
rend_cache_failure_entry_free(entry);
@@ -164,7 +169,7 @@ rend_cache_failure_entry_free_(void *entry)
/** Allocate a rend cache failure object and return it. This function can
* not fail. */
-static rend_cache_failure_t *
+STATIC rend_cache_failure_t *
rend_cache_failure_entry_new(void)
{
rend_cache_failure_t *entry = tor_malloc(sizeof(*entry));
@@ -174,7 +179,7 @@ rend_cache_failure_entry_new(void)
/** Remove failure cache entry for the service ID in the given descriptor
* <b>desc</b>. */
-static void
+STATIC void
rend_cache_failure_remove(rend_service_descriptor_t *desc)
{
char service_id[REND_SERVICE_ID_LEN_BASE32 + 1];
@@ -194,7 +199,7 @@ rend_cache_failure_remove(rend_service_descriptor_t *desc)
}
/** Helper: free storage held by a single service descriptor cache entry. */
-static void
+STATIC void
rend_cache_entry_free(rend_cache_entry_t *e)
{
if (!e)
@@ -222,9 +227,11 @@ rend_cache_free_all(void)
{
strmap_free(rend_cache, rend_cache_entry_free_);
digestmap_free(rend_cache_v2_dir, rend_cache_entry_free_);
+ strmap_free(rend_cache_local_service, rend_cache_entry_free_);
strmap_free(rend_cache_failure, rend_cache_failure_entry_free_);
rend_cache = NULL;
rend_cache_v2_dir = NULL;
+ rend_cache_local_service = NULL;
rend_cache_failure = NULL;
rend_cache_total_allocation = 0;
}
@@ -258,24 +265,33 @@ rend_cache_failure_clean(time_t now)
} STRMAP_FOREACH_END;
}
-/** Removes all old entries from the service descriptor cache.
+/** Removes all old entries from the client or service descriptor cache.
*/
void
-rend_cache_clean(time_t now)
+rend_cache_clean(time_t now, rend_cache_type_t cache_type)
{
strmap_iter_t *iter;
const char *key;
void *val;
rend_cache_entry_t *ent;
time_t cutoff = now - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW;
- for (iter = strmap_iter_init(rend_cache); !strmap_iter_done(iter); ) {
+ strmap_t *cache = NULL;
+
+ if (cache_type == REND_CACHE_TYPE_CLIENT) {
+ cache = rend_cache;
+ } else if (cache_type == REND_CACHE_TYPE_SERVICE) {
+ cache = rend_cache_local_service;
+ }
+ tor_assert(cache);
+
+ for (iter = strmap_iter_init(cache); !strmap_iter_done(iter); ) {
strmap_iter_get(iter, &key, &val);
ent = (rend_cache_entry_t*)val;
if (ent->parsed->timestamp < cutoff) {
- iter = strmap_iter_next_rmv(rend_cache, iter);
+ iter = strmap_iter_next_rmv(cache, iter);
rend_cache_entry_free(ent);
} else {
- iter = strmap_iter_next(rend_cache, iter);
+ iter = strmap_iter_next(cache, iter);
}
}
}
@@ -305,10 +321,10 @@ rend_cache_failure_purge(void)
}
/** Lookup the rend failure cache using a relay identity digest in
- * <b>identity</b> and service ID <b>service_id</b>. If found, the intro
- * failure is set in <b>intro_entry</b> else it stays untouched. Return 1
- * iff found else 0. */
-static int
+ * <b>identity</b> which has DIGEST_LEN bytes and service ID <b>service_id</b>
+ * which is a null-terminated string. If found, the intro failure is set in
+ * <b>intro_entry</b> else it stays untouched. Return 1 iff found else 0. */
+STATIC int
cache_failure_intro_lookup(const uint8_t *identity, const char *service_id,
rend_cache_failure_intro_t **intro_entry)
{
@@ -352,7 +368,7 @@ cache_failure_intro_dup(const rend_cache_failure_intro_t *entry)
/** Add an intro point failure to the failure cache using the relay
* <b>identity</b> and service ID <b>service_id</b>. Record the
* <b>failure</b> in that object. */
-static void
+STATIC void
cache_failure_intro_add(const uint8_t *identity, const char *service_id,
rend_intro_point_failure_t failure)
{
@@ -379,7 +395,7 @@ cache_failure_intro_add(const uint8_t *identity, const char *service_id,
* descriptor and kept into the failure cache. Then, each intro points that
* are NOT in the descriptor but in the failure cache for the given
* <b>service_id</b> are removed from the failure cache. */
-static void
+STATIC void
validate_intro_point_failure(const rend_service_descriptor_t *desc,
const char *service_id)
{
@@ -535,6 +551,42 @@ rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e)
return ret;
}
+/*
+ * Lookup the v2 service descriptor with the service ID <b>query</b> in the
+ * local service descriptor cache. Return 0 if found and if <b>e</b> is
+ * non NULL, set it with the entry found. Else, a negative value is returned
+ * and <b>e</b> is untouched.
+ * -EINVAL means that <b>query</b> is not a valid service id.
+ * -ENOENT means that no entry in the cache was found. */
+int
+rend_cache_lookup_v2_desc_as_service(const char *query, rend_cache_entry_t **e)
+{
+ int ret = 0;
+ rend_cache_entry_t *entry = NULL;
+
+ tor_assert(rend_cache_local_service);
+ tor_assert(query);
+
+ if (!rend_valid_service_id(query)) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /* Lookup descriptor and return. */
+ entry = strmap_get_lc(rend_cache_local_service, query);
+ if (!entry) {
+ ret = -ENOENT;
+ goto end;
+ }
+
+ if (e) {
+ *e = entry;
+ }
+
+ end:
+ return ret;
+}
+
/** Lookup the v2 service descriptor with base32-encoded <b>desc_id</b> and
* copy the pointer to it to *<b>desc</b>. Return 1 on success, 0 on
* well-formed-but-not-found, and -1 on failure.
@@ -660,7 +712,6 @@ rend_cache_store_v2_desc_as_dir(const char *desc)
log_info(LD_REND, "Successfully stored service descriptor with desc ID "
"'%s' and len %d.",
safe_str(desc_id_base32), (int)encoded_size);
-
/* Statistics: Note down this potentially new HS. */
if (options->HiddenServiceStatistics) {
rep_hist_stored_maybe_new_hs(e->parsed->pk);
@@ -687,6 +738,80 @@ rend_cache_store_v2_desc_as_dir(const char *desc)
return RCS_OKAY;
}
+/** Parse the v2 service descriptor in <b>desc</b> and store it to the
+* local service rend cache. Don't attempt to decrypt the included list of
+* introduction points.
+*
+* If we have a newer descriptor with the same ID, ignore this one.
+* If we have an older descriptor with the same ID, replace it.
+*
+* Return an appropriate rend_cache_store_status_t.
+*/
+rend_cache_store_status_t
+rend_cache_store_v2_desc_as_service(const char *desc)
+{
+ rend_service_descriptor_t *parsed = NULL;
+ char desc_id[DIGEST_LEN];
+ char *intro_content = NULL;
+ size_t intro_size;
+ size_t encoded_size;
+ const char *next_desc;
+ char service_id[REND_SERVICE_ID_LEN_BASE32+1];
+ rend_cache_entry_t *e;
+ rend_cache_store_status_t retval = RCS_BADDESC;
+ tor_assert(rend_cache_local_service);
+ tor_assert(desc);
+
+ /* Parse the descriptor. */
+ if (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content,
+ &intro_size, &encoded_size,
+ &next_desc, desc, 0) < 0) {
+ log_warn(LD_REND, "Could not parse descriptor.");
+ goto err;
+ }
+ /* Compute service ID from public key. */
+ if (rend_get_service_id(parsed->pk, service_id)<0) {
+ log_warn(LD_REND, "Couldn't compute service ID.");
+ goto err;
+ }
+
+ /* Do we already have a newer descriptor? Allow new descriptors with a
+ rounded timestamp equal to or newer than the current descriptor */
+ e = (rend_cache_entry_t*) strmap_get_lc(rend_cache_local_service,
+ service_id);
+ if (e && e->parsed->timestamp > parsed->timestamp) {
+ log_info(LD_REND, "We already have a newer service descriptor for "
+ "service ID %s.", safe_str_client(service_id));
+ goto okay;
+ }
+ /* We don't care about the introduction points. */
+ tor_free(intro_content);
+ if (!e) {
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ strmap_set_lc(rend_cache_local_service, service_id, e);
+ } else {
+ rend_cache_decrement_allocation(rend_cache_entry_allocation(e));
+ rend_service_descriptor_free(e->parsed);
+ tor_free(e->desc);
+ }
+ e->parsed = parsed;
+ e->desc = tor_malloc_zero(encoded_size + 1);
+ strlcpy(e->desc, desc, encoded_size + 1);
+ e->len = encoded_size;
+ rend_cache_increment_allocation(rend_cache_entry_allocation(e));
+ log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.",
+ safe_str_client(service_id), (int)encoded_size);
+ return RCS_OKAY;
+
+ okay:
+ retval = RCS_OKAY;
+
+ err:
+ rend_service_descriptor_free(parsed);
+ tor_free(intro_content);
+ return retval;
+}
+
/** Parse the v2 service descriptor in <b>desc</b>, decrypt the included list
* of introduction points with <b>descriptor_cookie</b> (which may also be
* <b>NULL</b> if decryption is not necessary), and store the descriptor to
diff --git a/src/or/rendcache.h b/src/or/rendcache.h
index 0512058054..decb040ee7 100644
--- a/src/or/rendcache.h
+++ b/src/or/rendcache.h
@@ -48,14 +48,21 @@ typedef struct rend_cache_failure_t {
digestmap_t *intro_failures;
} rend_cache_failure_t;
+typedef enum {
+ REND_CACHE_TYPE_CLIENT = 1,
+ REND_CACHE_TYPE_SERVICE = 2,
+} rend_cache_type_t;
+
void rend_cache_init(void);
-void rend_cache_clean(time_t now);
+void rend_cache_clean(time_t now, rend_cache_type_t cache_type);
void rend_cache_failure_clean(time_t now);
void rend_cache_clean_v2_descs_as_dir(time_t now, size_t min_to_remove);
void rend_cache_purge(void);
void rend_cache_free_all(void);
int rend_cache_lookup_entry(const char *query, int version,
rend_cache_entry_t **entry_out);
+int rend_cache_lookup_v2_desc_as_service(const char *query,
+ rend_cache_entry_t **entry_out);
int rend_cache_lookup_v2_desc_as_dir(const char *query, const char **desc);
/** Return value from rend_cache_store_v2_desc_as_{dir,client}. */
typedef enum {
@@ -65,6 +72,8 @@ typedef enum {
} rend_cache_store_status_t;
rend_cache_store_status_t rend_cache_store_v2_desc_as_dir(const char *desc);
+rend_cache_store_status_t rend_cache_store_v2_desc_as_service(
+ const char *desc);
rend_cache_store_status_t rend_cache_store_v2_desc_as_client(const char *desc,
const char *desc_id_base32,
const rend_data_t *rend_query,
@@ -76,5 +85,31 @@ void rend_cache_intro_failure_note(rend_intro_point_failure_t failure,
const char *service_id);
void rend_cache_failure_purge(void);
+#ifdef RENDCACHE_PRIVATE
+
+STATIC size_t rend_cache_entry_allocation(const rend_cache_entry_t *e);
+STATIC void rend_cache_entry_free(rend_cache_entry_t *e);
+STATIC void rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t
+ *entry);
+STATIC void rend_cache_failure_entry_free(rend_cache_failure_t *entry);
+STATIC int cache_failure_intro_lookup(const uint8_t *identity,
+ const char *service_id,
+ rend_cache_failure_intro_t
+ **intro_entry);
+STATIC void rend_cache_decrement_allocation(size_t n);
+STATIC void rend_cache_increment_allocation(size_t n);
+STATIC rend_cache_failure_intro_t *rend_cache_failure_intro_entry_new(
+ rend_intro_point_failure_t failure);
+STATIC rend_cache_failure_t *rend_cache_failure_entry_new(void);
+STATIC void rend_cache_failure_remove(rend_service_descriptor_t *desc);
+STATIC void cache_failure_intro_add(const uint8_t *identity,
+ const char *service_id,
+ rend_intro_point_failure_t failure);
+STATIC void validate_intro_point_failure(const rend_service_descriptor_t *desc,
+ const char *service_id);
+
+STATIC void rend_cache_failure_entry_free_(void *entry);
+#endif
+
#endif /* TOR_RENDCACHE_H */
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index a39e518e99..b822295832 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -52,7 +52,7 @@ rend_client_introcirc_has_opened(origin_circuit_t *circ)
tor_assert(circ->cpath);
log_info(LD_REND,"introcirc is open");
- connection_ap_attach_pending();
+ connection_ap_attach_pending(1);
}
/** Send the establish-rendezvous cell along a rendezvous circuit. if
@@ -65,11 +65,7 @@ rend_client_send_establish_rendezvous(origin_circuit_t *circ)
tor_assert(circ->rend_data);
log_info(LD_REND, "Sending an ESTABLISH_RENDEZVOUS cell");
- if (crypto_rand(circ->rend_data->rend_cookie, REND_COOKIE_LEN) < 0) {
- log_warn(LD_BUG, "Internal error: Couldn't produce random cookie.");
- circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
- return -1;
- }
+ crypto_rand(circ->rend_data->rend_cookie, REND_COOKIE_LEN);
/* Set timestamp_dirty, because circuit_expire_building expects it,
* and the rend cookie also means we've used the circ. */
@@ -177,6 +173,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP,
AP_CONN_STATE_CIRCUIT_WAIT,
introcirc->rend_data->onion_address))) {
+ connection_ap_mark_as_non_pending_circuit(TO_ENTRY_CONN(conn));
conn->state = AP_CONN_STATE_RENDDESC_WAIT;
}
}
@@ -185,7 +182,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
goto cleanup;
}
- /* first 20 bytes of payload are the hash of Bob's pk */
+ /* first 20 bytes of payload are the hash of the service's pk */
intro_key = NULL;
SMARTLIST_FOREACH(entry->parsed->intro_nodes, rend_intro_point_t *,
intro, {
@@ -1059,9 +1056,11 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro,
rend_client_refetch_v2_renddesc(rend_query);
/* move all pending streams back to renddesc_wait */
+ /* NOTE: We can now do this faster, if we use pending_entry_connections */
while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP,
AP_CONN_STATE_CIRCUIT_WAIT,
rend_query->onion_address))) {
+ connection_ap_mark_as_non_pending_circuit(TO_ENTRY_CONN(conn));
conn->state = AP_CONN_STATE_RENDDESC_WAIT;
}
@@ -1097,9 +1096,9 @@ rend_client_rendezvous_acked(origin_circuit_t *circ, const uint8_t *request,
circ->base_.timestamp_dirty = time(NULL);
/* From a path bias point of view, this circuit is now successfully used.
- * Waiting any longer opens us up to attacks from Bob. He could induce
- * Alice to attempt to connect to his hidden service and never reply
- * to her rend requests */
+ * Waiting any longer opens us up to attacks from malicious hidden services.
+ * They could induce the client to attempt to connect to their hidden
+ * service and never reply to the client's rend requests */
pathbias_mark_use_success(circ);
/* XXXX This is a pretty brute-force approach. It'd be better to
@@ -1107,11 +1106,11 @@ rend_client_rendezvous_acked(origin_circuit_t *circ, const uint8_t *request,
* than trying to attach them all. See comments bug 743. */
/* If we already have the introduction circuit built, make sure we send
* the INTRODUCE cell _now_ */
- connection_ap_attach_pending();
+ connection_ap_attach_pending(1);
return 0;
}
-/** Bob sent us a rendezvous cell; join the circuits. */
+/** The service sent us a rendezvous cell; join the circuits. */
int
rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request,
size_t request_len)
@@ -1136,7 +1135,8 @@ rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request,
log_info(LD_REND,"Got RENDEZVOUS2 cell from hidden service.");
- /* first DH_KEY_LEN bytes are g^y from bob. Finish the dh handshake...*/
+ /* first DH_KEY_LEN bytes are g^y from the service. Finish the dh
+ * handshake...*/
tor_assert(circ->build_state);
tor_assert(circ->build_state->pending_final_cpath);
hop = circ->build_state->pending_final_cpath;
@@ -1165,7 +1165,7 @@ rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request,
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_REND_JOINED);
hop->state = CPATH_STATE_OPEN;
/* set the windows to default. these are the windows
- * that alice thinks bob has.
+ * that the client thinks the service has.
*/
hop->package_window = circuit_initial_package_window();
hop->deliver_window = CIRCWINDOW_START;
@@ -1226,12 +1226,7 @@ rend_client_desc_trynow(const char *query)
base_conn->timestamp_lastread = now;
base_conn->timestamp_lastwritten = now;
- if (connection_ap_handshake_attach_circuit(conn) < 0) {
- /* it will never work */
- log_warn(LD_REND,"Rendezvous attempt failed. Closing.");
- if (!base_conn->marked_for_close)
- connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
- }
+ connection_ap_mark_as_pending_circuit(conn);
} else { /* 404, or fetch didn't get that far */
log_notice(LD_REND,"Closing stream for '%s.onion': hidden service is "
"unavailable (try again later).",
@@ -1372,11 +1367,19 @@ rend_client_get_random_intro_impl(const rend_cache_entry_t *entry,
smartlist_del(usable_nodes, i);
goto again;
}
+#ifdef ENABLE_TOR2WEB_MODE
+ new_extend_info = extend_info_from_node(node, options->Tor2webMode);
+#else
new_extend_info = extend_info_from_node(node, 0);
+#endif
if (!new_extend_info) {
+ const char *alternate_reason = "";
+#ifdef ENABLE_TOR2WEB_MODE
+ alternate_reason = ", or we cannot connect directly to it";
+#endif
log_info(LD_REND, "We don't have a descriptor for the intro-point relay "
- "'%s'; trying another.",
- extend_info_describe(intro->extend_info));
+ "'%s'%s; trying another.",
+ extend_info_describe(intro->extend_info), alternate_reason);
smartlist_del(usable_nodes, i);
goto again;
} else {
diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c
index 22599e9830..8c02b67556 100644
--- a/src/or/rendcommon.c
+++ b/src/or/rendcommon.c
@@ -11,6 +11,7 @@
#include "or.h"
#include "circuitbuild.h"
#include "config.h"
+#include "control.h"
#include "rendclient.h"
#include "rendcommon.h"
#include "rendmid.h"
@@ -268,11 +269,7 @@ rend_encrypt_v2_intro_points_basic(char **encrypted_out,
tor_assert(client_cookies && smartlist_len(client_cookies) > 0);
/* Generate session key. */
- if (crypto_rand(session_key, CIPHER_KEY_LEN) < 0) {
- log_warn(LD_REND, "Unable to generate random session key to encrypt "
- "introduction point string.");
- goto done;
- }
+ crypto_rand(session_key, CIPHER_KEY_LEN);
/* Determine length of encrypted introduction points including session
* keys. */
@@ -334,11 +331,7 @@ rend_encrypt_v2_intro_points_basic(char **encrypted_out,
REND_BASIC_AUTH_CLIENT_MULTIPLE;
i < REND_BASIC_AUTH_CLIENT_MULTIPLE - 1; i++) {
client_part = tor_malloc_zero(REND_BASIC_AUTH_CLIENT_ENTRY_LEN);
- if (crypto_rand(client_part, REND_BASIC_AUTH_CLIENT_ENTRY_LEN) < 0) {
- log_warn(LD_REND, "Unable to generate fake client entry.");
- tor_free(client_part);
- goto done;
- }
+ crypto_rand(client_part, REND_BASIC_AUTH_CLIENT_ENTRY_LEN);
smartlist_add(encrypted_session_keys, client_part);
}
/* Sort smartlist and put elements in result in order. */
@@ -461,6 +454,7 @@ rend_encode_v2_descriptors(smartlist_t *descs_out,
smartlist_t *client_cookies)
{
char service_id[DIGEST_LEN];
+ char service_id_base32[REND_SERVICE_ID_LEN_BASE32+1];
uint32_t time_period;
char *ipos_base64 = NULL, *ipos = NULL, *ipos_encrypted = NULL,
*descriptor_cookie = NULL;
@@ -655,6 +649,11 @@ rend_encode_v2_descriptors(smartlist_t *descs_out,
goto err;
}
smartlist_add(descs_out, enc);
+ /* Add the uploaded descriptor to the local service's descriptor cache */
+ rend_cache_store_v2_desc_as_service(enc->desc_str);
+ base32_encode(service_id_base32, sizeof(service_id_base32),
+ service_id, REND_SERVICE_ID_LEN);
+ control_event_hs_descriptor_created(service_id_base32, desc_id_base32, k);
}
log_info(LD_REND, "Successfully encoded a v2 descriptor and "
diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h
index 3b2f86d614..04e34af453 100644
--- a/src/or/rendcommon.h
+++ b/src/or/rendcommon.h
@@ -19,7 +19,7 @@ typedef enum rend_intro_point_failure_t {
} rend_intro_point_failure_t;
/** Free all storage associated with <b>data</b> */
-static INLINE void
+static inline void
rend_data_free(rend_data_t *data)
{
if (!data) {
diff --git a/src/or/rendmid.c b/src/or/rendmid.c
index 2451acb514..cbfbcbe051 100644
--- a/src/or/rendmid.c
+++ b/src/or/rendmid.c
@@ -80,7 +80,7 @@ rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request,
goto err;
}
- /* The request is valid. First, compute the hash of Bob's PK.*/
+ /* The request is valid. First, compute the hash of the service's PK.*/
if (crypto_pk_get_digest(pk, pk_digest)<0) {
log_warn(LD_BUG, "Internal error: couldn't hash public key.");
goto err;
@@ -178,7 +178,8 @@ rend_mid_introduce(or_circuit_t *circ, const uint8_t *request,
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
(char*)request, REND_SERVICE_ID_LEN);
- /* The first 20 bytes are all we look at: they have a hash of Bob's PK. */
+ /* The first 20 bytes are all we look at: they have a hash of the service's
+ * PK. */
intro_circ = circuit_get_intro_point((const uint8_t*)request);
if (!intro_circ) {
log_info(LD_REND,
@@ -202,7 +203,7 @@ rend_mid_introduce(or_circuit_t *circ, const uint8_t *request,
"Unable to send INTRODUCE2 cell to Tor client.");
goto err;
}
- /* And send an ack down Alice's circuit. Empty body means succeeded. */
+ /* And send an ack down the client's circuit. Empty body means succeeded. */
if (relay_send_command_from_edge(0,TO_CIRCUIT(circ),
RELAY_COMMAND_INTRODUCE_ACK,
NULL,0,NULL)) {
@@ -337,7 +338,7 @@ rend_mid_rendezvous(or_circuit_t *circ, const uint8_t *request,
circ->circuit_carries_hs_traffic_stats = 1;
}
- /* Send the RENDEZVOUS2 cell to Alice. */
+ /* Send the RENDEZVOUS2 cell to the client. */
if (relay_send_command_from_edge(0, TO_CIRCUIT(rend_circ),
RELAY_COMMAND_RENDEZVOUS2,
(char*)(request+REND_COOKIE_LEN),
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 77d8b716a2..7471c6252f 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -1676,7 +1676,7 @@ rend_service_receive_introduction(origin_circuit_t *circuit,
/* help predict this next time */
rep_hist_note_used_internal(now, circ_needs_uptime, 1);
- /* Launch a circuit to alice's chosen rendezvous point.
+ /* Launch a circuit to the client's chosen rendezvous point.
*/
for (i=0;i<MAX_REND_FAILURES;i++) {
int flags = CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_IS_INTERNAL;
@@ -2970,7 +2970,7 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
/* Append the cpath entry. */
hop->state = CPATH_STATE_OPEN;
/* set the windows to default. these are the windows
- * that bob thinks alice has.
+ * that the service thinks the client has.
*/
hop->package_window = circuit_initial_package_window();
hop->deliver_window = CIRCWINDOW_START;
@@ -3203,39 +3203,72 @@ upload_service_descriptor(rend_service_t *service)
rendpostperiod = get_options()->RendPostPeriod;
- /* Upload descriptor? */
- if (get_options()->PublishHidServDescriptors) {
- networkstatus_t *c = networkstatus_get_latest_consensus();
- if (c && smartlist_len(c->routerstatus_list) > 0) {
- int seconds_valid, i, j, num_descs;
- smartlist_t *descs = smartlist_new();
- smartlist_t *client_cookies = smartlist_new();
- /* Either upload a single descriptor (including replicas) or one
- * descriptor for each authorized client in case of authorization
- * type 'stealth'. */
- num_descs = service->auth_type == REND_STEALTH_AUTH ?
- smartlist_len(service->clients) : 1;
- for (j = 0; j < num_descs; j++) {
- crypto_pk_t *client_key = NULL;
- rend_authorized_client_t *client = NULL;
- smartlist_clear(client_cookies);
- switch (service->auth_type) {
- case REND_NO_AUTH:
- /* Do nothing here. */
- break;
- case REND_BASIC_AUTH:
- SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *,
- cl, smartlist_add(client_cookies, cl->descriptor_cookie));
- break;
- case REND_STEALTH_AUTH:
- client = smartlist_get(service->clients, j);
- client_key = client->client_key;
- smartlist_add(client_cookies, client->descriptor_cookie);
- break;
- }
- /* Encode the current descriptor. */
+ networkstatus_t *c = networkstatus_get_latest_consensus();
+ if (c && smartlist_len(c->routerstatus_list) > 0) {
+ int seconds_valid, i, j, num_descs;
+ smartlist_t *descs = smartlist_new();
+ smartlist_t *client_cookies = smartlist_new();
+ /* Either upload a single descriptor (including replicas) or one
+ * descriptor for each authorized client in case of authorization
+ * type 'stealth'. */
+ num_descs = service->auth_type == REND_STEALTH_AUTH ?
+ smartlist_len(service->clients) : 1;
+ for (j = 0; j < num_descs; j++) {
+ crypto_pk_t *client_key = NULL;
+ rend_authorized_client_t *client = NULL;
+ smartlist_clear(client_cookies);
+ switch (service->auth_type) {
+ case REND_NO_AUTH:
+ /* Do nothing here. */
+ break;
+ case REND_BASIC_AUTH:
+ SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *,
+ cl, smartlist_add(client_cookies, cl->descriptor_cookie));
+ break;
+ case REND_STEALTH_AUTH:
+ client = smartlist_get(service->clients, j);
+ client_key = client->client_key;
+ smartlist_add(client_cookies, client->descriptor_cookie);
+ break;
+ }
+ /* Encode the current descriptor. */
+ seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
+ now, 0,
+ service->auth_type,
+ client_key,
+ client_cookies);
+ if (seconds_valid < 0) {
+ log_warn(LD_BUG, "Internal error: couldn't encode service "
+ "descriptor; not uploading.");
+ smartlist_free(descs);
+ smartlist_free(client_cookies);
+ return;
+ }
+ rend_get_service_id(service->desc->pk, serviceid);
+ if (get_options()->PublishHidServDescriptors) {
+ /* Post the current descriptors to the hidden service directories. */
+ log_info(LD_REND, "Launching upload for hidden service %s",
+ serviceid);
+ directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
+ seconds_valid);
+ }
+ /* Free memory for descriptors. */
+ for (i = 0; i < smartlist_len(descs); i++)
+ rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
+ smartlist_clear(descs);
+ /* Update next upload time. */
+ if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS
+ > rendpostperiod)
+ service->next_upload_time = now + rendpostperiod;
+ else if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS)
+ service->next_upload_time = now + seconds_valid + 1;
+ else
+ service->next_upload_time = now + seconds_valid -
+ REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + 1;
+ /* Post also the next descriptors, if necessary. */
+ if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) {
seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
- now, 0,
+ now, 1,
service->auth_type,
client_key,
client_cookies);
@@ -3246,51 +3279,23 @@ upload_service_descriptor(rend_service_t *service)
smartlist_free(client_cookies);
return;
}
- /* Post the current descriptors to the hidden service directories. */
- rend_get_service_id(service->desc->pk, serviceid);
- log_info(LD_REND, "Launching upload for hidden service %s",
- serviceid);
- directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
- seconds_valid);
+ if (get_options()->PublishHidServDescriptors) {
+ directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
+ seconds_valid);
+ }
/* Free memory for descriptors. */
for (i = 0; i < smartlist_len(descs); i++)
rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
smartlist_clear(descs);
- /* Update next upload time. */
- if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS
- > rendpostperiod)
- service->next_upload_time = now + rendpostperiod;
- else if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS)
- service->next_upload_time = now + seconds_valid + 1;
- else
- service->next_upload_time = now + seconds_valid -
- REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + 1;
- /* Post also the next descriptors, if necessary. */
- if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) {
- seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
- now, 1,
- service->auth_type,
- client_key,
- client_cookies);
- if (seconds_valid < 0) {
- log_warn(LD_BUG, "Internal error: couldn't encode service "
- "descriptor; not uploading.");
- smartlist_free(descs);
- smartlist_free(client_cookies);
- return;
- }
- directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
- seconds_valid);
- /* Free memory for descriptors. */
- for (i = 0; i < smartlist_len(descs); i++)
- rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
- smartlist_clear(descs);
- }
}
- smartlist_free(descs);
- smartlist_free(client_cookies);
- uploaded = 1;
+ }
+ smartlist_free(descs);
+ smartlist_free(client_cookies);
+ uploaded = 1;
+ if (get_options()->PublishHidServDescriptors) {
log_info(LD_REND, "Successfully uploaded v2 rend descriptors!");
+ } else {
+ log_info(LD_REND, "Successfully stored created v2 rend descriptors!");
}
}
@@ -3635,9 +3640,6 @@ rend_consider_services_upload(time_t now)
MIN_REND_INITIAL_POST_DELAY_TESTING :
MIN_REND_INITIAL_POST_DELAY);
- if (!get_options()->PublishHidServDescriptors)
- return;
-
for (i=0; i < smartlist_len(rend_service_list); ++i) {
service = smartlist_get(rend_service_list, i);
if (!service->next_upload_time) { /* never been uploaded yet */
diff --git a/src/or/rephist.c b/src/or/rephist.c
index fe0997c891..d55317947c 100644
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@ -148,7 +148,7 @@ get_link_history(const char *from_id, const char *to_id)
return NULL;
if (tor_digest_is_zero(to_id))
return NULL;
- lhist = (link_history_t*) digestmap_get(orhist->link_history_map, to_id);
+ lhist = digestmap_get(orhist->link_history_map, to_id);
if (!lhist) {
lhist = tor_malloc_zero(sizeof(link_history_t));
rephist_total_alloc += sizeof(link_history_t);
@@ -920,7 +920,7 @@ parse_possibly_bad_iso_time(const char *s, time_t *time_out)
* that's about as much before <b>now</b> as <b>t</b> was before
* <b>stored_at</b>.
*/
-static INLINE time_t
+static inline time_t
correct_time(time_t t, time_t now, time_t stored_at, time_t started_measuring)
{
if (t < started_measuring - 24*60*60*365)
@@ -1190,7 +1190,7 @@ commit_max(bw_array_t *b)
}
/** Shift the current observation time of <b>b</b> forward by one second. */
-static INLINE void
+static inline void
advance_obs(bw_array_t *b)
{
int nextidx;
@@ -1216,7 +1216,7 @@ advance_obs(bw_array_t *b)
/** Add <b>n</b> bytes to the number of bytes in <b>b</b> for second
* <b>when</b>. */
-static INLINE void
+static inline void
add_obs(bw_array_t *b, time_t when, uint64_t n)
{
if (when < b->cur_obs_time)
@@ -1250,6 +1250,18 @@ bw_array_new(void)
return b;
}
+/** Free storage held by bandwidth array <b>b</b>. */
+static void
+bw_array_free(bw_array_t *b)
+{
+ if (!b) {
+ return;
+ }
+
+ rephist_total_alloc -= sizeof(bw_array_t);
+ tor_free(b);
+}
+
/** Recent history of bandwidth observations for read operations. */
static bw_array_t *read_array = NULL;
/** Recent history of bandwidth observations for write operations. */
@@ -1266,10 +1278,11 @@ static bw_array_t *dir_write_array = NULL;
static void
bw_arrays_init(void)
{
- tor_free(read_array);
- tor_free(write_array);
- tor_free(dir_read_array);
- tor_free(dir_write_array);
+ bw_array_free(read_array);
+ bw_array_free(write_array);
+ bw_array_free(dir_read_array);
+ bw_array_free(dir_write_array);
+
read_array = bw_array_new();
write_array = bw_array_new();
dir_read_array = bw_array_new();
@@ -3026,21 +3039,21 @@ rep_hist_stored_maybe_new_hs(const crypto_pk_t *pubkey)
/* The number of cells that are supposed to be hidden from the adversary
* by adding noise from the Laplace distribution. This value, divided by
- * EPSILON, is Laplace parameter b. */
+ * EPSILON, is Laplace parameter b. It must be greather than 0. */
#define REND_CELLS_DELTA_F 2048
/* Security parameter for obfuscating number of cells with a value between
- * 0 and 1. Smaller values obfuscate observations more, but at the same
+ * ]0.0, 1.0]. Smaller values obfuscate observations more, but at the same
* time make statistics less usable. */
#define REND_CELLS_EPSILON 0.3
/* The number of cells that are supposed to be hidden from the adversary
* by rounding up to the next multiple of this number. */
#define REND_CELLS_BIN_SIZE 1024
-/* The number of service identities that are supposed to be hidden from
- * the adversary by adding noise from the Laplace distribution. This
- * value, divided by EPSILON, is Laplace parameter b. */
+/* The number of service identities that are supposed to be hidden from the
+ * adversary by adding noise from the Laplace distribution. This value,
+ * divided by EPSILON, is Laplace parameter b. It must be greater than 0. */
#define ONIONS_SEEN_DELTA_F 8
/* Security parameter for obfuscating number of service identities with a
- * value between 0 and 1. Smaller values obfuscate observations more, but
+ * value between ]0.0, 1.0]. Smaller values obfuscate observations more, but
* at the same time make statistics less usable. */
#define ONIONS_SEEN_EPSILON 0.3
/* The number of service identities that are supposed to be hidden from
@@ -3172,10 +3185,19 @@ rep_hist_free_all(void)
{
hs_stats_free(hs_stats);
digestmap_free(history_map, free_or_history);
- tor_free(read_array);
- tor_free(write_array);
- tor_free(dir_read_array);
- tor_free(dir_write_array);
+
+ bw_array_free(read_array);
+ read_array = NULL;
+
+ bw_array_free(write_array);
+ write_array = NULL;
+
+ bw_array_free(dir_read_array);
+ dir_read_array = NULL;
+
+ bw_array_free(dir_write_array);
+ dir_write_array = NULL;
+
tor_free(exit_bytes_read);
tor_free(exit_bytes_written);
tor_free(exit_streams);
@@ -3190,5 +3212,8 @@ rep_hist_free_all(void)
}
rep_hist_desc_stats_term();
total_descriptor_downloads = 0;
+
+ tor_assert(rephist_total_alloc == 0);
+ tor_assert(rephist_total_num == 0);
}
diff --git a/src/or/replaycache.c b/src/or/replaycache.c
index 569e0736cb..82e5c44d3d 100644
--- a/src/or/replaycache.c
+++ b/src/or/replaycache.c
@@ -23,7 +23,7 @@ replaycache_free(replaycache_t *r)
return;
}
- if (r->digests_seen) digestmap_free(r->digests_seen, tor_free_);
+ if (r->digests_seen) digest256map_free(r->digests_seen, tor_free_);
tor_free(r);
}
@@ -54,7 +54,7 @@ replaycache_new(time_t horizon, time_t interval)
r->scrub_interval = interval;
r->scrubbed = 0;
r->horizon = horizon;
- r->digests_seen = digestmap_new();
+ r->digests_seen = digest256map_new();
err:
return r;
@@ -69,7 +69,7 @@ replaycache_add_and_test_internal(
time_t *elapsed)
{
int rv = 0;
- char digest[DIGEST_LEN];
+ uint8_t digest[DIGEST256_LEN];
time_t *access_time;
/* sanity check */
@@ -80,10 +80,10 @@ replaycache_add_and_test_internal(
}
/* compute digest */
- crypto_digest(digest, (const char *)data, len);
+ crypto_digest256((char *)digest, (const char *)data, len, DIGEST_SHA256);
/* check map */
- access_time = digestmap_get(r->digests_seen, digest);
+ access_time = digest256map_get(r->digests_seen, digest);
/* seen before? */
if (access_time != NULL) {
@@ -114,7 +114,7 @@ replaycache_add_and_test_internal(
/* No, so no hit and update the digest map with the current time */
access_time = tor_malloc(sizeof(*access_time));
*access_time = present;
- digestmap_set(r->digests_seen, digest, access_time);
+ digest256map_set(r->digests_seen, digest, access_time);
}
/* now scrub the cache if it's time */
@@ -130,8 +130,8 @@ replaycache_add_and_test_internal(
STATIC void
replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r)
{
- digestmap_iter_t *itr = NULL;
- const char *digest;
+ digest256map_iter_t *itr = NULL;
+ const uint8_t *digest;
void *valp;
time_t *access_time;
@@ -149,19 +149,19 @@ replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r)
if (r->horizon == 0) return;
/* okay, scrub time */
- itr = digestmap_iter_init(r->digests_seen);
- while (!digestmap_iter_done(itr)) {
- digestmap_iter_get(itr, &digest, &valp);
+ itr = digest256map_iter_init(r->digests_seen);
+ while (!digest256map_iter_done(itr)) {
+ digest256map_iter_get(itr, &digest, &valp);
access_time = (time_t *)valp;
/* aged out yet? */
if (*access_time < present - r->horizon) {
/* Advance the iterator and remove this one */
- itr = digestmap_iter_next_rmv(r->digests_seen, itr);
+ itr = digest256map_iter_next_rmv(r->digests_seen, itr);
/* Free the value removed */
tor_free(access_time);
} else {
/* Just advance the iterator */
- itr = digestmap_iter_next(r->digests_seen, itr);
+ itr = digest256map_iter_next(r->digests_seen, itr);
}
}
diff --git a/src/or/replaycache.h b/src/or/replaycache.h
index 9b9daf3831..9c409f2fd7 100644
--- a/src/or/replaycache.h
+++ b/src/or/replaycache.h
@@ -26,7 +26,7 @@ struct replaycache_s {
/*
* Digest map: keys are digests, values are times the digest was last seen
*/
- digestmap_t *digests_seen;
+ digest256map_t *digests_seen;
};
#endif /* REPLAYCACHE_PRIVATE */
diff --git a/src/or/router.c b/src/or/router.c
index 841f6fde1b..3b03c09f2b 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -269,8 +269,8 @@ client_identity_key_is_set(void)
/** Return the key certificate for this v3 (voting) authority, or NULL
* if we have no such certificate. */
-authority_cert_t *
-get_my_v3_authority_cert(void)
+MOCK_IMPL(authority_cert_t *,
+get_my_v3_authority_cert, (void))
{
return authority_key_certificate;
}
@@ -1026,6 +1026,7 @@ init_keys(void)
ds = trusted_dir_server_new(options->Nickname, NULL,
router_get_advertised_dir_port(options, 0),
router_get_advertised_or_port(options),
+ NULL,
digest,
v3_digest,
type, 0.0);
@@ -1098,43 +1099,47 @@ check_whether_dirport_reachable(void)
can_reach_dir_port;
}
-/** Look at a variety of factors, and return 0 if we don't want to
- * advertise the fact that we have a DirPort open. Else return the
- * DirPort we want to advertise.
- *
- * Log a helpful message if we change our mind about whether to publish
- * a DirPort.
+/** The lower threshold of remaining bandwidth required to advertise (or
+ * automatically provide) directory services */
+/* XXX Should this be increased? */
+#define MIN_BW_TO_ADVERTISE_DIRSERVER 51200
+
+/** Return true iff we have enough configured bandwidth to cache directory
+ * information. */
+static int
+router_has_bandwidth_to_be_dirserver(const or_options_t *options)
+{
+ if (options->BandwidthRate < MIN_BW_TO_ADVERTISE_DIRSERVER) {
+ return 0;
+ }
+ if (options->RelayBandwidthRate > 0 &&
+ options->RelayBandwidthRate < MIN_BW_TO_ADVERTISE_DIRSERVER) {
+ return 0;
+ }
+ return 1;
+}
+
+/** Helper: Return 1 if we have sufficient resources for serving directory
+ * requests, return 0 otherwise.
+ * dir_port is either 0 or the configured DirPort number.
+ * If AccountingMax is set less than our advertised bandwidth, then don't
+ * serve requests. Likewise, if our advertised bandwidth is less than
+ * MIN_BW_TO_ADVERTISE_DIRSERVER, don't bother trying to serve requests.
*/
static int
-decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port)
+router_should_be_directory_server(const or_options_t *options, int dir_port)
{
static int advertising=1; /* start out assuming we will advertise */
int new_choice=1;
const char *reason = NULL;
- /* Section one: reasons to publish or not publish that aren't
- * worth mentioning to the user, either because they're obvious
- * or because they're normal behavior. */
-
- if (!dir_port) /* short circuit the rest of the function */
- return 0;
- if (authdir_mode(options)) /* always publish */
- return dir_port;
- if (net_is_disabled())
- return 0;
- if (!check_whether_dirport_reachable())
- return 0;
- if (!router_get_advertised_dir_port(options, dir_port))
- return 0;
-
- /* Section two: reasons to publish or not publish that the user
- * might find surprising. These are generally config options that
- * make us choose not to publish. */
-
- if (accounting_is_enabled(options)) {
+ if (accounting_is_enabled(options) &&
+ get_options()->AccountingRule != ACCT_IN) {
/* Don't spend bytes for directory traffic if we could end up hibernating,
* but allow DirPort otherwise. Some people set AccountingMax because
- * they're confused or to get statistics. */
+ * they're confused or to get statistics. Directory traffic has a much
+ * larger effect on output than input so there is no reason to turn it
+ * off if using AccountingRule in. */
int interval_length = accounting_get_interval_length();
uint32_t effective_bw = get_effective_bwrate(options);
uint64_t acc_bytes;
@@ -1157,10 +1162,7 @@ decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port)
new_choice = 0;
reason = "AccountingMax enabled";
}
-#define MIN_BW_TO_ADVERTISE_DIRPORT 51200
- } else if (options->BandwidthRate < MIN_BW_TO_ADVERTISE_DIRPORT ||
- (options->RelayBandwidthRate > 0 &&
- options->RelayBandwidthRate < MIN_BW_TO_ADVERTISE_DIRPORT)) {
+ } else if (! router_has_bandwidth_to_be_dirserver(options)) {
/* if we're advertising a small amount */
new_choice = 0;
reason = "BandwidthRate under 50KB";
@@ -1168,15 +1170,63 @@ decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port)
if (advertising != new_choice) {
if (new_choice == 1) {
- log_notice(LD_DIR, "Advertising DirPort as %d", dir_port);
+ if (dir_port > 0)
+ log_notice(LD_DIR, "Advertising DirPort as %d", dir_port);
+ else
+ log_notice(LD_DIR, "Advertising directory service support");
} else {
tor_assert(reason);
- log_notice(LD_DIR, "Not advertising DirPort (Reason: %s)", reason);
+ log_notice(LD_DIR, "Not advertising Dir%s (Reason: %s)",
+ dir_port ? "Port" : "ectory Service support", reason);
}
advertising = new_choice;
}
- return advertising ? dir_port : 0;
+ return advertising;
+}
+
+/** Return 1 if we are configured to accept either relay or directory requests
+ * from clients and we aren't at risk of exceeding our bandwidth limits, thus
+ * we should be a directory server. If not, return 0.
+ */
+int
+dir_server_mode(const or_options_t *options)
+{
+ if (!options->DirCache)
+ return 0;
+ return options->DirPort_set ||
+ (server_mode(options) && router_has_bandwidth_to_be_dirserver(options));
+}
+
+/** Look at a variety of factors, and return 0 if we don't want to
+ * advertise the fact that we have a DirPort open, else return the
+ * DirPort we want to advertise.
+ *
+ * Log a helpful message if we change our mind about whether to publish
+ * a DirPort.
+ */
+static int
+decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port)
+{
+ /* Part one: reasons to publish or not publish that aren't
+ * worth mentioning to the user, either because they're obvious
+ * or because they're normal behavior. */
+
+ if (!dir_port) /* short circuit the rest of the function */
+ return 0;
+ if (authdir_mode(options)) /* always publish */
+ return dir_port;
+ if (net_is_disabled())
+ return 0;
+ if (!check_whether_dirport_reachable())
+ return 0;
+ if (!router_get_advertised_dir_port(options, dir_port))
+ return 0;
+
+ /* Part two: reasons to publish or not publish that the user
+ * might find surprising. router_should_be_directory_server()
+ * considers config options that make us choose not to publish. */
+ return router_should_be_directory_server(options, dir_port) ? dir_port : 0;
}
/** Allocate and return a new extend_info_t that can be used to build
@@ -1243,14 +1293,15 @@ consider_testing_reachability(int test_or, int test_dir)
extend_info_free(ei);
}
+ /* XXX IPv6 self testing */
tor_addr_from_ipv4h(&addr, me->addr);
if (test_dir && !check_whether_dirport_reachable() &&
!connection_get_by_type_addr_port_purpose(
CONN_TYPE_DIR, &addr, me->dir_port,
DIR_PURPOSE_FETCH_SERVERDESC)) {
/* ask myself, via tor, for my server descriptor. */
- directory_initiate_command(&addr,
- me->or_port, me->dir_port,
+ directory_initiate_command(&addr, me->or_port,
+ &addr, me->dir_port,
me->cache_info.identity_digest,
DIR_PURPOSE_FETCH_SERVERDESC,
ROUTER_PURPOSE_GENERAL,
@@ -1460,8 +1511,8 @@ static int server_is_advertised=0;
/** Return true iff we have published our descriptor lately.
*/
-int
-advertised_server_mode(void)
+MOCK_IMPL(int,
+advertised_server_mode,(void))
{
return server_is_advertised;
}
@@ -1689,7 +1740,8 @@ router_upload_dir_desc_to_dirservers(int force)
int
router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port)
{
- if (!router_get_my_routerinfo()) /* make sure desc_routerinfo exists */
+ const routerinfo_t *me = router_get_my_routerinfo();
+ if (!me) /* make sure routerinfo exists */
return -1;
/* make sure it's resolved to something. this way we can't get a
@@ -1697,20 +1749,21 @@ router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port)
if (tor_addr_is_null(addr))
return -1;
- /* look at desc_routerinfo->exit_policy for both the v4 and the v6
- * policies. The exit_policy field in desc_routerinfo is a bit unusual,
- * in that it contains IPv6 and IPv6 entries. We don't want to look
- * at desc_routerinfio->ipv6_exit_policy, since that's a port summary. */
+ /* look at router_get_my_routerinfo()->exit_policy for both the v4 and the
+ * v6 policies. The exit_policy field in router_get_my_routerinfo() is a
+ * bit unusual, in that it contains IPv6 and IPv6 entries. We don't want to
+ * look at router_get_my_routerinfo()->ipv6_exit_policy, since that's a port
+ * summary. */
if ((tor_addr_family(addr) == AF_INET ||
tor_addr_family(addr) == AF_INET6)) {
return compare_tor_addr_to_addr_policy(addr, port,
- desc_routerinfo->exit_policy) != ADDR_POLICY_ACCEPTED;
+ me->exit_policy) != ADDR_POLICY_ACCEPTED;
#if 0
} else if (tor_addr_family(addr) == AF_INET6) {
return get_options()->IPv6Exit &&
desc_routerinfo->ipv6_exit_policy &&
compare_tor_addr_to_short_policy(addr, port,
- desc_routerinfo->ipv6_exit_policy) != ADDR_POLICY_ACCEPTED;
+ me->ipv6_exit_policy) != ADDR_POLICY_ACCEPTED;
#endif
} else {
return -1;
@@ -1719,13 +1772,13 @@ router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port)
/** Return true iff my exit policy is reject *:*. Return -1 if we don't
* have a descriptor */
-int
-router_my_exit_policy_is_reject_star(void)
+MOCK_IMPL(int,
+router_my_exit_policy_is_reject_star,(void))
{
- if (!router_get_my_routerinfo()) /* make sure desc_routerinfo exists */
+ if (!router_get_my_routerinfo()) /* make sure routerinfo exists */
return -1;
- return desc_routerinfo->policy_is_reject_star;
+ return router_get_my_routerinfo()->policy_is_reject_star;
}
/** Return true iff I'm a server and <b>digest</b> is equal to
@@ -1784,12 +1837,13 @@ const char *
router_get_my_descriptor(void)
{
const char *body;
- if (!router_get_my_routerinfo())
+ const routerinfo_t *me = router_get_my_routerinfo();
+ if (! me)
return NULL;
+ tor_assert(me->cache_info.saved_location == SAVED_NOWHERE);
+ body = signed_descriptor_get_body(&me->cache_info);
/* Make sure this is nul-terminated. */
- tor_assert(desc_routerinfo->cache_info.saved_location == SAVED_NOWHERE);
- body = signed_descriptor_get_body(&desc_routerinfo->cache_info);
- tor_assert(!body[desc_routerinfo->cache_info.signed_descriptor_len]);
+ tor_assert(!body[me->cache_info.signed_descriptor_len]);
log_debug(LD_GENERAL,"my desc is '%s'", body);
return body;
}
@@ -1824,8 +1878,8 @@ static int router_guess_address_from_dir_headers(uint32_t *guess);
* it's configured in torrc, or because we've learned it from
* dirserver headers. Place the answer in *<b>addr</b> and return
* 0 on success, else return -1 if we have no guess. */
-int
-router_pick_published_address(const or_options_t *options, uint32_t *addr)
+MOCK_IMPL(int,
+router_pick_published_address,(const or_options_t *options, uint32_t *addr))
{
*addr = get_last_resolved_addr();
if (!*addr &&
@@ -1870,6 +1924,8 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
ri->addr = addr;
ri->or_port = router_get_advertised_or_port(options);
ri->dir_port = router_get_advertised_dir_port(options, 0);
+ ri->supports_tunnelled_dir_requests = dir_server_mode(options) &&
+ router_should_be_directory_server(options, ri->dir_port);
ri->cache_info.published_on = time(NULL);
ri->onion_pkey = crypto_pk_dup_key(get_onion_key()); /* must invoke from
* main thread */
@@ -1927,7 +1983,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
/* DNS is screwed up; don't claim to be an exit. */
policies_exit_policy_append_reject_star(&ri->exit_policy);
} else {
- policies_parse_exit_policy_from_options(options,ri->addr,&ri->ipv6_addr,1,
+ policies_parse_exit_policy_from_options(options,ri->addr,&ri->ipv6_addr,
&ri->exit_policy);
}
ri->policy_is_reject_star =
@@ -2190,10 +2246,10 @@ check_descriptor_bandwidth_changed(time_t now)
{
static time_t last_changed = 0;
uint64_t prev, cur;
- if (!desc_routerinfo)
+ if (!router_get_my_routerinfo())
return;
- prev = desc_routerinfo->bandwidthcapacity;
+ prev = router_get_my_routerinfo()->bandwidthcapacity;
cur = we_are_hibernating() ? 0 : rep_hist_bandwidth_assess();
if ((prev != cur && (!prev || !cur)) ||
cur > prev*2 ||
@@ -2247,11 +2303,11 @@ check_descriptor_ipaddress_changed(time_t now)
(void) now;
- if (!desc_routerinfo)
+ if (router_get_my_routerinfo() == NULL)
return;
/* XXXX ipv6 */
- prev = desc_routerinfo->addr;
+ prev = router_get_my_routerinfo()->addr;
if (resolve_my_address(LOG_INFO, options, &cur, &method, &hostname) < 0) {
log_info(LD_CONFIG,"options->Address didn't resolve into an IP.");
return;
@@ -2323,7 +2379,7 @@ router_new_address_suggestion(const char *suggestion,
if (tor_addr_eq(&d_conn->base_.addr, &addr)) {
/* Don't believe anybody who says our IP is their IP. */
log_debug(LD_DIR, "A directory server told us our IP address is %s, "
- "but he's just reporting his own IP address. Ignoring.",
+ "but they are just reporting their own IP address. Ignoring.",
suggestion);
return;
}
@@ -2646,6 +2702,10 @@ router_dump_router_to_string(routerinfo_t *router,
tor_free(p6);
}
+ if (router->supports_tunnelled_dir_requests) {
+ smartlist_add(chunks, tor_strdup("tunnelled-dir-server\n"));
+ }
+
/* Sign the descriptor with Ed25519 */
if (emit_ed_sigs) {
smartlist_add(chunks, tor_strdup("router-sig-ed25519 "));
@@ -2733,44 +2793,13 @@ router_dump_exit_policy_to_string(const routerinfo_t *router,
int include_ipv4,
int include_ipv6)
{
- smartlist_t *exit_policy_strings;
- char *policy_string = NULL;
-
if ((!router->exit_policy) || (router->policy_is_reject_star)) {
return tor_strdup("reject *:*");
}
- exit_policy_strings = smartlist_new();
-
- SMARTLIST_FOREACH_BEGIN(router->exit_policy, addr_policy_t *, tmpe) {
- char *pbuf;
- int bytes_written_to_pbuf;
- if ((tor_addr_family(&tmpe->addr) == AF_INET6) && (!include_ipv6)) {
- continue; /* Don't include IPv6 parts of address policy */
- }
- if ((tor_addr_family(&tmpe->addr) == AF_INET) && (!include_ipv4)) {
- continue; /* Don't include IPv4 parts of address policy */
- }
-
- pbuf = tor_malloc(POLICY_BUF_LEN);
- bytes_written_to_pbuf = policy_write_item(pbuf,POLICY_BUF_LEN, tmpe, 1);
-
- if (bytes_written_to_pbuf < 0) {
- log_warn(LD_BUG, "router_dump_exit_policy_to_string ran out of room!");
- tor_free(pbuf);
- goto done;
- }
-
- smartlist_add(exit_policy_strings,pbuf);
- } SMARTLIST_FOREACH_END(tmpe);
-
- policy_string = smartlist_join_strings(exit_policy_strings, "\n", 0, NULL);
-
- done:
- SMARTLIST_FOREACH(exit_policy_strings, char *, str, tor_free(str));
- smartlist_free(exit_policy_strings);
-
- return policy_string;
+ return policy_dump_to_string(router->exit_policy,
+ include_ipv4,
+ include_ipv6);
}
/** Copy the primary (IPv4) OR port (IP address and TCP port) for
@@ -3385,28 +3414,16 @@ router_free_all(void)
/** Return a smartlist of tor_addr_port_t's with all the OR ports of
<b>ri</b>. Note that freeing of the items in the list as well as
- the smartlist itself is the callers responsibility.
-
- XXX duplicating code from node_get_all_orports(). */
+ the smartlist itself is the callers responsibility. */
smartlist_t *
router_get_all_orports(const routerinfo_t *ri)
{
- smartlist_t *sl = smartlist_new();
tor_assert(ri);
-
- if (ri->addr != 0) {
- tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t));
- tor_addr_from_ipv4h(&ap->addr, ri->addr);
- ap->port = ri->or_port;
- smartlist_add(sl, ap);
- }
- if (!tor_addr_is_null(&ri->ipv6_addr)) {
- tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t));
- tor_addr_copy(&ap->addr, &ri->ipv6_addr);
- ap->port = ri->or_port;
- smartlist_add(sl, ap);
- }
-
- return sl;
+ node_t fake_node;
+ memset(&fake_node, 0, sizeof(fake_node));
+ /* we don't modify ri, fake_node is passed as a const node_t *
+ */
+ fake_node.ri = (routerinfo_t *)ri;
+ return node_get_all_orports(&fake_node);
}
diff --git a/src/or/router.h b/src/or/router.h
index d8fcf0a9ad..ca590e3217 100644
--- a/src/or/router.h
+++ b/src/or/router.h
@@ -22,7 +22,7 @@ int server_identity_key_is_set(void);
void set_client_identity_key(crypto_pk_t *k);
crypto_pk_t *get_tlsclient_identity_key(void);
int client_identity_key_is_set(void);
-authority_cert_t *get_my_v3_authority_cert(void);
+MOCK_DECL(authority_cert_t *, get_my_v3_authority_cert, (void));
crypto_pk_t *get_my_v3_authority_signing_key(void);
authority_cert_t *get_my_v3_legacy_cert(void);
crypto_pk_t *get_my_v3_legacy_signing_key(void);
@@ -41,6 +41,7 @@ int init_keys_client(void);
int check_whether_orport_reachable(void);
int check_whether_dirport_reachable(void);
+int dir_server_mode(const or_options_t *options);
void consider_testing_reachability(int test_or, int test_dir);
void router_orport_found_reachable(void);
void router_dirport_found_reachable(void);
@@ -67,7 +68,7 @@ uint16_t router_get_advertised_dir_port(const or_options_t *options,
MOCK_DECL(int, server_mode, (const or_options_t *options));
MOCK_DECL(int, public_server_mode, (const or_options_t *options));
-int advertised_server_mode(void);
+MOCK_DECL(int, advertised_server_mode, (void));
int proxy_mode(const or_options_t *options);
void consider_publishable_server(int force);
int should_refuse_unknown_exits(const or_options_t *options);
@@ -80,7 +81,7 @@ void check_descriptor_ipaddress_changed(time_t now);
void router_new_address_suggestion(const char *suggestion,
const dir_connection_t *d_conn);
int router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port);
-int router_my_exit_policy_is_reject_star(void);
+MOCK_DECL(int, router_my_exit_policy_is_reject_star,(void));
MOCK_DECL(const routerinfo_t *, router_get_my_routerinfo, (void));
extrainfo_t *router_get_my_extrainfo(void);
const char *router_get_my_descriptor(void);
@@ -89,7 +90,8 @@ int router_digest_is_me(const char *digest);
const uint8_t *router_get_my_id_digest(void);
int router_extrainfo_digest_is_me(const char *digest);
int router_is_me(const routerinfo_t *router);
-int router_pick_published_address(const or_options_t *options, uint32_t *addr);
+MOCK_DECL(int,router_pick_published_address,(const or_options_t *options,
+ uint32_t *addr));
int router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e);
int router_rebuild_descriptor(int force);
char *router_dump_router_to_string(routerinfo_t *router,
diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c
index d88bfca13a..210178dc1d 100644
--- a/src/or/routerkeys.c
+++ b/src/or/routerkeys.c
@@ -927,7 +927,7 @@ generate_ed_link_cert(const or_options_t *options, time_t now)
return -1;
}
- const digests_t *digests = tor_x509_cert_get_cert_digests(link);
+ const common_digests_t *digests = tor_x509_cert_get_cert_digests(link);
if (link_cert_cert &&
! EXPIRES_SOON(link_cert_cert, options->TestingLinkKeySlop) &&
@@ -972,7 +972,7 @@ should_make_new_ed_keys(const or_options_t *options, const time_t now)
if (tor_tls_get_my_certs(1, &link, &id) < 0 || link == NULL)
return 1;
- const digests_t *digests = tor_x509_cert_get_cert_digests(link);
+ const common_digests_t *digests = tor_x509_cert_get_cert_digests(link);
if (!fast_memeq(digests->d[DIGEST_SHA256],
link_cert_cert->signed_key.pubkey,
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index 79a5bb3910..d740b0d4ae 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -13,6 +13,7 @@
#define ROUTERLIST_PRIVATE
#include "or.h"
+#include "backtrace.h"
#include "crypto_ed25519.h"
#include "circuitstats.h"
#include "config.h"
@@ -67,8 +68,6 @@ typedef struct cert_list_t cert_list_t;
static int compute_weighted_bandwidths(const smartlist_t *sl,
bandwidth_weight_rule_t rule,
u64_dbl_t **bandwidths_out);
-static const routerstatus_t *router_pick_directory_server_impl(
- dirinfo_type_t auth, int flags, int *n_busy_out);
static const routerstatus_t *router_pick_trusteddirserver_impl(
const smartlist_t *sourcelist, dirinfo_type_t auth,
int flags, int *n_busy_out);
@@ -278,7 +277,7 @@ trusted_dirs_reload_certs(void)
/** Helper: return true iff we already have loaded the exact cert
* <b>cert</b>. */
-static INLINE int
+static inline int
already_have_cert(authority_cert_t *cert)
{
cert_list_t *cl = get_cert_list(cert->cache_info.identity_digest);
@@ -897,8 +896,10 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
if (smartlist_len(fps) > 1) {
resource = smartlist_join_strings(fps, "", 0, NULL);
+ /* XXX - do we want certs from authorities or mirrors? - teor */
directory_get_from_dirserver(DIR_PURPOSE_FETCH_CERTIFICATE, 0,
- resource, PDS_RETRY_IF_NO_SERVERS);
+ resource, PDS_RETRY_IF_NO_SERVERS,
+ DL_WANT_ANY_DIRSERVER);
tor_free(resource);
}
/* else we didn't add any: they were all pending */
@@ -941,8 +942,10 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
if (smartlist_len(fp_pairs) > 1) {
resource = smartlist_join_strings(fp_pairs, "", 0, NULL);
+ /* XXX - do we want certs from authorities or mirrors? - teor */
directory_get_from_dirserver(DIR_PURPOSE_FETCH_CERTIFICATE, 0,
- resource, PDS_RETRY_IF_NO_SERVERS);
+ resource, PDS_RETRY_IF_NO_SERVERS,
+ DL_WANT_ANY_DIRSERVER);
tor_free(resource);
}
/* else they were all pending */
@@ -985,7 +988,7 @@ router_should_rebuild_store(desc_store_t *store)
/** Return the desc_store_t in <b>rl</b> that should be used to store
* <b>sd</b>. */
-static INLINE desc_store_t *
+static inline desc_store_t *
desc_get_store(routerlist_t *rl, const signed_descriptor_t *sd)
{
if (sd->is_extrainfo)
@@ -1295,8 +1298,8 @@ router_get_fallback_dir_servers(void)
/** Try to find a running dirserver that supports operations of <b>type</b>.
*
* If there are no running dirservers in our routerlist and the
- * <b>PDS_RETRY_IF_NO_SERVERS</b> flag is set, set all the authoritative ones
- * as running again, and pick one.
+ * <b>PDS_RETRY_IF_NO_SERVERS</b> flag is set, set all the fallback ones
+ * (including authorities) as running again, and pick one.
*
* If the <b>PDS_IGNORE_FASCISTFIREWALL</b> flag is set, then include
* dirservers that we can't reach.
@@ -1304,8 +1307,9 @@ router_get_fallback_dir_servers(void)
* If the <b>PDS_ALLOW_SELF</b> flag is not set, then don't include ourself
* (if we're a dirserver).
*
- * Don't pick an authority if any non-authority is viable; try to avoid using
- * servers that have returned 503 recently.
+ * Don't pick a fallback directory mirror if any non-fallback is viable;
+ * (the fallback directory mirrors include the authorities)
+ * try to avoid using servers that have returned 503 recently.
*/
const routerstatus_t *
router_pick_directory_server(dirinfo_type_t type, int flags)
@@ -1332,7 +1336,7 @@ router_pick_directory_server(dirinfo_type_t type, int flags)
log_info(LD_DIR,
"No reachable router entries for dirservers. "
"Trying them all again.");
- /* mark all authdirservers as up again */
+ /* mark all fallback directory mirrors as up again */
mark_all_dirservers_up(fallback_dir_servers);
/* try again */
choice = router_pick_directory_server_impl(type, flags, NULL);
@@ -1358,15 +1362,20 @@ router_get_trusteddirserver_by_digest(const char *digest)
}
/** Return the dir_server_t for the fallback dirserver whose identity
- * key hashes to <b>digest</b>, or NULL if no such authority is known.
+ * key hashes to <b>digest</b>, or NULL if no such fallback is in the list of
+ * fallback_dir_servers. (fallback_dir_servers is affected by the FallbackDir
+ * and UseDefaultFallbackDirs torrc options.)
*/
dir_server_t *
router_get_fallback_dirserver_by_digest(const char *digest)
{
- if (!trusted_dir_servers)
+ if (!fallback_dir_servers)
return NULL;
- SMARTLIST_FOREACH(trusted_dir_servers, dir_server_t *, ds,
+ if (!digest)
+ return NULL;
+
+ SMARTLIST_FOREACH(fallback_dir_servers, dir_server_t *, ds,
{
if (tor_memeq(ds->digest, digest, DIGEST_LEN))
return ds;
@@ -1375,6 +1384,17 @@ router_get_fallback_dirserver_by_digest(const char *digest)
return NULL;
}
+/** Return 1 if any fallback dirserver's identity key hashes to <b>digest</b>,
+ * or 0 if no such fallback is in the list of fallback_dir_servers.
+ * (fallback_dir_servers is affected by the FallbackDir and
+ * UseDefaultFallbackDirs torrc options.)
+ */
+int
+router_digest_is_fallback_dir(const char *digest)
+{
+ return (router_get_fallback_dirserver_by_digest(digest) != NULL);
+}
+
/** Return the dir_server_t for the directory authority whose
* v3 identity key hashes to <b>digest</b>, or NULL if no such authority
* is known.
@@ -1441,9 +1461,190 @@ router_pick_dirserver_generic(smartlist_t *sourcelist,
return router_pick_trusteddirserver_impl(sourcelist, type, flags, NULL);
}
+/* Check if we already have a directory fetch from ap, for serverdesc
+ * (including extrainfo) or microdesc documents.
+ * If so, return 1, if not, return 0.
+ * Also returns 0 if addr is NULL, tor_addr_is_null(addr), or dir_port is 0.
+ */
+STATIC int
+router_is_already_dir_fetching(const tor_addr_port_t *ap, int serverdesc,
+ int microdesc)
+{
+ if (!ap || tor_addr_is_null(&ap->addr) || !ap->port) {
+ return 0;
+ }
+
+ /* XX/teor - we're not checking tunnel connections here, see #17848
+ */
+ if (serverdesc && (
+ connection_get_by_type_addr_port_purpose(
+ CONN_TYPE_DIR, &ap->addr, ap->port, DIR_PURPOSE_FETCH_SERVERDESC)
+ || connection_get_by_type_addr_port_purpose(
+ CONN_TYPE_DIR, &ap->addr, ap->port, DIR_PURPOSE_FETCH_EXTRAINFO))) {
+ return 1;
+ }
+
+ if (microdesc && (
+ connection_get_by_type_addr_port_purpose(
+ CONN_TYPE_DIR, &ap->addr, ap->port, DIR_PURPOSE_FETCH_MICRODESC))) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Check if we already have a directory fetch from ds, for serverdesc
+ * (including extrainfo) or microdesc documents.
+ * If so, return 1, if not, return 0.
+ */
+static int
+router_is_already_dir_fetching_ds(const dir_server_t *ds,
+ int serverdesc,
+ int microdesc)
+{
+ tor_addr_port_t ipv4_dir_ap, ipv6_dir_ap;
+
+ /* Assume IPv6 DirPort is the same as IPv4 DirPort */
+ tor_addr_from_ipv4h(&ipv4_dir_ap.addr, ds->addr);
+ ipv4_dir_ap.port = ds->dir_port;
+ tor_addr_copy(&ipv6_dir_ap.addr, &ds->ipv6_addr);
+ ipv6_dir_ap.port = ds->dir_port;
+
+ return (router_is_already_dir_fetching(&ipv4_dir_ap, serverdesc, microdesc)
+ || router_is_already_dir_fetching(&ipv6_dir_ap, serverdesc, microdesc));
+}
+
+/* Check if we already have a directory fetch from rs, for serverdesc
+ * (including extrainfo) or microdesc documents.
+ * If so, return 1, if not, return 0.
+ */
+static int
+router_is_already_dir_fetching_rs(const routerstatus_t *rs,
+ int serverdesc,
+ int microdesc)
+{
+ tor_addr_port_t ipv4_dir_ap, ipv6_dir_ap;
+
+ /* Assume IPv6 DirPort is the same as IPv4 DirPort */
+ tor_addr_from_ipv4h(&ipv4_dir_ap.addr, rs->addr);
+ ipv4_dir_ap.port = rs->dir_port;
+ tor_addr_copy(&ipv6_dir_ap.addr, &rs->ipv6_addr);
+ ipv6_dir_ap.port = rs->dir_port;
+
+ return (router_is_already_dir_fetching(&ipv4_dir_ap, serverdesc, microdesc)
+ || router_is_already_dir_fetching(&ipv6_dir_ap, serverdesc, microdesc));
+}
+
+#ifndef LOG_FALSE_POSITIVES_DURING_BOOTSTRAP
+#define LOG_FALSE_POSITIVES_DURING_BOOTSTRAP 0
+#endif
+
+/* Log a message if rs is not found or not a preferred address */
+static void
+router_picked_poor_directory_log(const routerstatus_t *rs)
+{
+ const networkstatus_t *usable_consensus;
+ usable_consensus = networkstatus_get_reasonably_live_consensus(time(NULL),
+ usable_consensus_flavor());
+
+#if !LOG_FALSE_POSITIVES_DURING_BOOTSTRAP
+ /* Don't log early in the bootstrap process, it's normal to pick from a
+ * small pool of nodes. Of course, this won't help if we're trying to
+ * diagnose bootstrap issues. */
+ if (!smartlist_len(nodelist_get_list()) || !usable_consensus
+ || !router_have_minimum_dir_info()) {
+ return;
+ }
+#endif
+
+ /* We couldn't find a node, or the one we have doesn't fit our preferences.
+ * This might be a bug. */
+ if (!rs) {
+ log_warn(LD_BUG, "Firewall denied all OR and Dir addresses for all relays "
+ "when searching for a directory.");
+ log_backtrace(LOG_WARN, LD_BUG, "Node search initiated by");
+ } else if (!fascist_firewall_allows_rs(rs, FIREWALL_OR_CONNECTION, 1)
+ && !fascist_firewall_allows_rs(rs, FIREWALL_DIR_CONNECTION, 1)
+ ) {
+ log_info(LD_BUG, "Selected a directory %s with non-preferred OR and Dir "
+ "addresses for launching a connection: "
+ "IPv4 %s OR %d Dir %d IPv6 %s OR %d Dir %d",
+ routerstatus_describe(rs),
+ fmt_addr32(rs->addr), rs->or_port,
+ rs->dir_port, fmt_addr(&rs->ipv6_addr),
+ rs->ipv6_orport, rs->dir_port);
+ log_backtrace(LOG_INFO, LD_BUG, "Node search initiated by");
+ }
+}
+
+#undef LOG_FALSE_POSITIVES_DURING_BOOTSTRAP
+
/** How long do we avoid using a directory server after it's given us a 503? */
#define DIR_503_TIMEOUT (60*60)
+/* Common retry code for router_pick_directory_server_impl and
+ * router_pick_trusteddirserver_impl. Retry with the non-preferred IP version.
+ * Must be called before RETRY_WITHOUT_EXCLUDE().
+ *
+ * If we got no result, and we are applying IP preferences, and we are a
+ * client that could use an alternate IP version, try again with the
+ * opposite preferences. */
+#define RETRY_ALTERNATE_IP_VERSION(retry_label) \
+ STMT_BEGIN \
+ if (result == NULL && try_ip_pref && options->ClientUseIPv4 \
+ && fascist_firewall_use_ipv6(options) && !server_mode(options) \
+ && n_not_preferred && !n_busy) { \
+ n_excluded = 0; \
+ n_busy = 0; \
+ try_ip_pref = 0; \
+ n_not_preferred = 0; \
+ goto retry_label; \
+ } \
+ STMT_END \
+
+/* Common retry code for router_pick_directory_server_impl and
+ * router_pick_trusteddirserver_impl. Retry without excluding nodes, but with
+ * the preferred IP version. Must be called after RETRY_ALTERNATE_IP_VERSION().
+ *
+ * If we got no result, and we are excluding nodes, and StrictNodes is
+ * not set, try again without excluding nodes. */
+#define RETRY_WITHOUT_EXCLUDE(retry_label) \
+ STMT_BEGIN \
+ if (result == NULL && try_excluding && !options->StrictNodes \
+ && n_excluded && !n_busy) { \
+ try_excluding = 0; \
+ n_excluded = 0; \
+ n_busy = 0; \
+ try_ip_pref = 1; \
+ n_not_preferred = 0; \
+ goto retry_label; \
+ } \
+ STMT_END
+
+/* When iterating through the routerlist, can OR address/port preference
+ * and reachability checks be skipped?
+ */
+static int
+router_skip_or_reachability(const or_options_t *options, int try_ip_pref)
+{
+ /* Servers always have and prefer IPv4.
+ * And if clients are checking against the firewall for reachability only,
+ * but there's no firewall, don't bother checking */
+ return server_mode(options) || (!try_ip_pref && !firewall_is_fascist_or());
+}
+
+/* When iterating through the routerlist, can Dir address/port preference
+ * and reachability checks be skipped?
+ */
+static int
+router_skip_dir_reachability(const or_options_t *options, int try_ip_pref)
+{
+ /* Servers always have and prefer IPv4.
+ * And if clients are checking against the firewall for reachability only,
+ * but there's no firewall, don't bother checking */
+ return server_mode(options) || (!try_ip_pref && !firewall_is_fascist_dir());
+}
+
/** Pick a random running valid directory server/mirror from our
* routerlist. Arguments are as for router_pick_directory_server(), except:
*
@@ -1451,7 +1652,7 @@ router_pick_dirserver_generic(smartlist_t *sourcelist,
* directories that we excluded for no other reason than
* PDS_NO_EXISTING_SERVERDESC_FETCH or PDS_NO_EXISTING_MICRODESC_FETCH.
*/
-static const routerstatus_t *
+STATIC const routerstatus_t *
router_pick_directory_server_impl(dirinfo_type_t type, int flags,
int *n_busy_out)
{
@@ -1468,11 +1669,12 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags,
const int no_microdesc_fetching = (flags & PDS_NO_EXISTING_MICRODESC_FETCH);
const int for_guard = (flags & PDS_FOR_GUARD);
int try_excluding = 1, n_excluded = 0, n_busy = 0;
+ int try_ip_pref = 1, n_not_preferred = 0;
if (!consensus)
return NULL;
- retry_without_exclude:
+ retry_search:
direct = smartlist_new();
tunnel = smartlist_new();
@@ -1481,17 +1683,19 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags,
overloaded_direct = smartlist_new();
overloaded_tunnel = smartlist_new();
+ const int skip_or = router_skip_or_reachability(options, try_ip_pref);
+ const int skip_dir = router_skip_dir_reachability(options, try_ip_pref);
+
/* Find all the running dirservers we know about. */
SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) {
int is_trusted, is_trusted_extrainfo;
int is_overloaded;
- tor_addr_t addr;
const routerstatus_t *status = node->rs;
const country_t country = node->country;
if (!status)
continue;
- if (!node->is_running || !status->dir_port || !node->is_valid)
+ if (!node->is_running || !node_is_dir(node) || !node->is_valid)
continue;
if (requireother && router_digest_is_me(node->identity))
continue;
@@ -1516,36 +1720,34 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags,
continue;
}
- /* XXXX IP6 proposal 118 */
- tor_addr_from_ipv4h(&addr, status->addr);
-
- if (no_serverdesc_fetching && (
- connection_get_by_type_addr_port_purpose(
- CONN_TYPE_DIR, &addr, status->dir_port, DIR_PURPOSE_FETCH_SERVERDESC)
- || connection_get_by_type_addr_port_purpose(
- CONN_TYPE_DIR, &addr, status->dir_port, DIR_PURPOSE_FETCH_EXTRAINFO)
- )) {
- ++n_busy;
- continue;
- }
-
- if (no_microdesc_fetching && connection_get_by_type_addr_port_purpose(
- CONN_TYPE_DIR, &addr, status->dir_port, DIR_PURPOSE_FETCH_MICRODESC)
- ) {
+ if (router_is_already_dir_fetching_rs(status,
+ no_serverdesc_fetching,
+ no_microdesc_fetching)) {
++n_busy;
continue;
}
is_overloaded = status->last_dir_503_at + DIR_503_TIMEOUT > now;
- if ((!fascistfirewall ||
- fascist_firewall_allows_address_or(&addr, status->or_port)))
+ /* Clients use IPv6 addresses if the server has one and the client
+ * prefers IPv6.
+ * Add the router if its preferred address and port are reachable.
+ * If we don't get any routers, we'll try again with the non-preferred
+ * address for each router (if any). (To ensure correct load-balancing
+ * we try routers that only have one address both times.)
+ */
+ if (!fascistfirewall || skip_or ||
+ fascist_firewall_allows_rs(status, FIREWALL_OR_CONNECTION,
+ try_ip_pref))
smartlist_add(is_trusted ? trusted_tunnel :
is_overloaded ? overloaded_tunnel : tunnel, (void*)node);
- else if (!fascistfirewall ||
- fascist_firewall_allows_address_dir(&addr, status->dir_port))
+ else if (skip_dir ||
+ fascist_firewall_allows_rs(status, FIREWALL_DIR_CONNECTION,
+ try_ip_pref))
smartlist_add(is_trusted ? trusted_direct :
is_overloaded ? overloaded_direct : direct, (void*)node);
+ else if (!tor_addr_is_null(&status->ipv6_addr))
+ ++n_not_preferred;
} SMARTLIST_FOREACH_END(node);
if (smartlist_len(tunnel)) {
@@ -1574,19 +1776,15 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags,
smartlist_free(overloaded_direct);
smartlist_free(overloaded_tunnel);
- if (result == NULL && try_excluding && !options->StrictNodes && n_excluded
- && !n_busy) {
- /* If we got no result, and we are excluding nodes, and StrictNodes is
- * not set, try again without excluding nodes. */
- try_excluding = 0;
- n_excluded = 0;
- n_busy = 0;
- goto retry_without_exclude;
- }
+ RETRY_ALTERNATE_IP_VERSION(retry_search);
+
+ RETRY_WITHOUT_EXCLUDE(retry_search);
if (n_busy_out)
*n_busy_out = n_busy;
+ router_picked_poor_directory_log(result ? result->rs : NULL);
+
return result ? result->rs : NULL;
}
@@ -1637,22 +1835,25 @@ router_pick_trusteddirserver_impl(const smartlist_t *sourcelist,
smartlist_t *pick_from;
int n_busy = 0;
int try_excluding = 1, n_excluded = 0;
+ int try_ip_pref = 1, n_not_preferred = 0;
if (!sourcelist)
return NULL;
- retry_without_exclude:
+ retry_search:
direct = smartlist_new();
tunnel = smartlist_new();
overloaded_direct = smartlist_new();
overloaded_tunnel = smartlist_new();
+ const int skip_or = router_skip_or_reachability(options, try_ip_pref);
+ const int skip_dir = router_skip_dir_reachability(options, try_ip_pref);
+
SMARTLIST_FOREACH_BEGIN(sourcelist, const dir_server_t *, d)
{
int is_overloaded =
d->fake_status.last_dir_503_at + DIR_503_TIMEOUT > now;
- tor_addr_t addr;
if (!d->is_running) continue;
if ((type & d->type) == 0)
continue;
@@ -1668,35 +1869,29 @@ router_pick_trusteddirserver_impl(const smartlist_t *sourcelist,
continue;
}
- /* XXXX IP6 proposal 118 */
- tor_addr_from_ipv4h(&addr, d->addr);
-
- if (no_serverdesc_fetching) {
- if (connection_get_by_type_addr_port_purpose(
- CONN_TYPE_DIR, &addr, d->dir_port, DIR_PURPOSE_FETCH_SERVERDESC)
- || connection_get_by_type_addr_port_purpose(
- CONN_TYPE_DIR, &addr, d->dir_port, DIR_PURPOSE_FETCH_EXTRAINFO)) {
- //log_debug(LD_DIR, "We have an existing connection to fetch "
- // "descriptor from %s; delaying",d->description);
- ++n_busy;
- continue;
- }
- }
- if (no_microdesc_fetching) {
- if (connection_get_by_type_addr_port_purpose(
- CONN_TYPE_DIR, &addr, d->dir_port, DIR_PURPOSE_FETCH_MICRODESC)) {
- ++n_busy;
- continue;
- }
+ if (router_is_already_dir_fetching_ds(d, no_serverdesc_fetching,
+ no_microdesc_fetching)) {
+ ++n_busy;
+ continue;
}
- if (d->or_port &&
- (!fascistfirewall ||
- fascist_firewall_allows_address_or(&addr, d->or_port)))
+ /* Clients use IPv6 addresses if the server has one and the client
+ * prefers IPv6.
+ * Add the router if its preferred address and port are reachable.
+ * If we don't get any routers, we'll try again with the non-preferred
+ * address for each router (if any). (To ensure correct load-balancing
+ * we try routers that only have one address both times.)
+ */
+ if (!fascistfirewall || skip_or ||
+ fascist_firewall_allows_dir_server(d, FIREWALL_OR_CONNECTION,
+ try_ip_pref))
smartlist_add(is_overloaded ? overloaded_tunnel : tunnel, (void*)d);
- else if (!fascistfirewall ||
- fascist_firewall_allows_address_dir(&addr, d->dir_port))
+ else if (skip_dir ||
+ fascist_firewall_allows_dir_server(d, FIREWALL_DIR_CONNECTION,
+ try_ip_pref))
smartlist_add(is_overloaded ? overloaded_direct : direct, (void*)d);
+ else if (!tor_addr_is_null(&d->ipv6_addr))
+ ++n_not_preferred;
}
SMARTLIST_FOREACH_END(d);
@@ -1718,22 +1913,19 @@ router_pick_trusteddirserver_impl(const smartlist_t *sourcelist,
result = &selection->fake_status;
}
- if (n_busy_out)
- *n_busy_out = n_busy;
-
smartlist_free(direct);
smartlist_free(tunnel);
smartlist_free(overloaded_direct);
smartlist_free(overloaded_tunnel);
- if (result == NULL && try_excluding && !options->StrictNodes && n_excluded) {
- /* If we got no result, and we are excluding nodes, and StrictNodes is
- * not set, try again without excluding nodes. */
- try_excluding = 0;
- n_excluded = 0;
- goto retry_without_exclude;
- }
+ RETRY_ALTERNATE_IP_VERSION(retry_search);
+
+ RETRY_WITHOUT_EXCLUDE(retry_search);
+ router_picked_poor_directory_log(result);
+
+ if (n_busy_out)
+ *n_busy_out = n_busy;
return result;
}
@@ -1803,8 +1995,12 @@ routerlist_add_node_and_family(smartlist_t *sl, const routerinfo_t *router)
void
router_add_running_nodes_to_smartlist(smartlist_t *sl, int allow_invalid,
int need_uptime, int need_capacity,
- int need_guard, int need_desc)
-{ /* XXXX MOVE */
+ int need_guard, int need_desc,
+ int pref_addr)
+{
+ const int check_reach = !router_skip_or_reachability(get_options(),
+ pref_addr);
+ /* XXXX MOVE */
SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) {
if (!node->is_running ||
(!node->is_valid && !allow_invalid))
@@ -1815,6 +2011,11 @@ router_add_running_nodes_to_smartlist(smartlist_t *sl, int allow_invalid,
continue;
if (node_is_unreliable(node, need_uptime, need_capacity, need_guard))
continue;
+ /* Choose a node with an OR address that matches the firewall rules */
+ if (check_reach && !fascist_firewall_allows_node(node,
+ FIREWALL_OR_CONNECTION,
+ pref_addr))
+ continue;
smartlist_add(sl, (void *)node);
} SMARTLIST_FOREACH_END(node);
@@ -1897,7 +2098,7 @@ scale_array_elements_to_u64(u64_dbl_t *entries, int n_entries,
#if SIZEOF_VOID_P == 8
#define gt_i64_timei(a,b) ((a) > (b))
#else
-static INLINE int
+static inline int
gt_i64_timei(uint64_t a, uint64_t b)
{
int64_t diff = (int64_t) (b - a);
@@ -1975,7 +2176,7 @@ bridge_get_advertised_bandwidth_bounded(routerinfo_t *router)
/** Return bw*1000, unless bw*1000 would overflow, in which case return
* INT32_MAX. */
-static INLINE int32_t
+static inline int32_t
kb_to_bytes(uint32_t bw)
{
return (bw > (INT32_MAX/1000)) ? INT32_MAX : bw*1000;
@@ -2276,6 +2477,10 @@ node_sl_choose_by_bandwidth(const smartlist_t *sl,
* If <b>CRN_NEED_DESC</b> is set in flags, we only consider nodes that
* have a routerinfo or microdescriptor -- that is, enough info to be
* used to build a circuit.
+ * If <b>CRN_PREF_ADDR</b> is set in flags, we only consider nodes that
+ * have an address that is preferred by the ClientPreferIPv6ORPort setting
+ * (regardless of this flag, we exclude nodes that aren't allowed by the
+ * firewall, including ClientUseIPv4 0 and fascist_firewall_use_ipv6() == 0).
*/
const node_t *
router_choose_random_node(smartlist_t *excludedsmartlist,
@@ -2288,6 +2493,7 @@ router_choose_random_node(smartlist_t *excludedsmartlist,
const int allow_invalid = (flags & CRN_ALLOW_INVALID) != 0;
const int weight_for_exit = (flags & CRN_WEIGHT_AS_EXIT) != 0;
const int need_desc = (flags & CRN_NEED_DESC) != 0;
+ const int pref_addr = (flags & CRN_PREF_ADDR) != 0;
smartlist_t *sl=smartlist_new(),
*excludednodes=smartlist_new();
@@ -2313,7 +2519,7 @@ router_choose_random_node(smartlist_t *excludedsmartlist,
router_add_running_nodes_to_smartlist(sl, allow_invalid,
need_uptime, need_capacity,
- need_guard, need_desc);
+ need_guard, need_desc, pref_addr);
log_debug(LD_CIRC,
"We found %d running nodes.",
smartlist_len(sl));
@@ -2342,7 +2548,7 @@ router_choose_random_node(smartlist_t *excludedsmartlist,
choice = node_sl_choose_by_bandwidth(sl, rule);
smartlist_free(sl);
- if (!choice && (need_uptime || need_capacity || need_guard)) {
+ if (!choice && (need_uptime || need_capacity || need_guard || pref_addr)) {
/* try once more -- recurse but with fewer restrictions. */
log_info(LD_CIRC,
"We couldn't find any live%s%s%s routers; falling back "
@@ -2350,7 +2556,8 @@ router_choose_random_node(smartlist_t *excludedsmartlist,
need_capacity?", fast":"",
need_uptime?", stable":"",
need_guard?", guard":"");
- flags &= ~ (CRN_NEED_UPTIME|CRN_NEED_CAPACITY|CRN_NEED_GUARD);
+ flags &= ~ (CRN_NEED_UPTIME|CRN_NEED_CAPACITY|CRN_NEED_GUARD|
+ CRN_PREF_ADDR);
choice = router_choose_random_node(
excludedsmartlist, excludedset, flags);
}
@@ -2790,7 +2997,7 @@ dump_routerlist_mem_usage(int severity)
* in <b>sl</b> at position <b>idx</b>. Otherwise, search <b>sl</b> for
* <b>ri</b>. Return the index of <b>ri</b> in <b>sl</b>, or -1 if <b>ri</b>
* is not in <b>sl</b>. */
-static INLINE int
+static inline int
routerlist_find_elt_(smartlist_t *sl, void *ri, int idx)
{
if (idx < 0) {
@@ -3215,7 +3422,11 @@ routerlist_reparse_old(routerlist_t *rl, signed_descriptor_t *sd)
return ri;
}
-/** Free all memory held by the routerlist module. */
+/** Free all memory held by the routerlist module.
+ * Note: Calling routerlist_free_all() should always be paired with
+ * a call to nodelist_free_all(). These should only be called during
+ * cleanup.
+ */
void
routerlist_free_all(void)
{
@@ -4034,15 +4245,16 @@ router_exit_policy_rejects_all(const routerinfo_t *router)
}
/** Create an directory server at <b>address</b>:<b>port</b>, with OR identity
- * key <b>digest</b>. If <b>address</b> is NULL, add ourself. If
- * <b>is_authority</b>, this is a directory authority. Return the new
- * directory server entry on success or NULL on failure. */
+ * key <b>digest</b> which has DIGEST_LEN bytes. If <b>address</b> is NULL,
+ * add ourself. If <b>is_authority</b>, this is a directory authority. Return
+ * the new directory server entry on success or NULL on failure. */
static dir_server_t *
dir_server_new(int is_authority,
const char *nickname,
const tor_addr_t *addr,
const char *hostname,
uint16_t dir_port, uint16_t or_port,
+ const tor_addr_port_t *addrport_ipv6,
const char *digest, const char *v3_auth_digest,
dirinfo_type_t type,
double weight)
@@ -4051,13 +4263,15 @@ dir_server_new(int is_authority,
uint32_t a;
char *hostname_ = NULL;
+ tor_assert(digest);
+
if (weight < 0)
return NULL;
if (tor_addr_family(addr) == AF_INET)
a = tor_addr_to_ipv4h(addr);
else
- return NULL; /*XXXX Support IPv6 */
+ return NULL;
if (!hostname)
hostname_ = tor_dup_addr(addr);
@@ -4074,6 +4288,18 @@ dir_server_new(int is_authority,
ent->is_authority = is_authority;
ent->type = type;
ent->weight = weight;
+ if (addrport_ipv6) {
+ if (tor_addr_family(&addrport_ipv6->addr) != AF_INET6) {
+ log_warn(LD_BUG, "Hey, I got a non-ipv6 addr as addrport_ipv6.");
+ tor_addr_make_unspec(&ent->ipv6_addr);
+ } else {
+ tor_addr_copy(&ent->ipv6_addr, &addrport_ipv6->addr);
+ ent->ipv6_orport = addrport_ipv6->port;
+ }
+ } else {
+ tor_addr_make_unspec(&ent->ipv6_addr);
+ }
+
memcpy(ent->digest, digest, DIGEST_LEN);
if (v3_auth_digest && (type & V3_DIRINFO))
memcpy(ent->v3_identity_digest, v3_auth_digest, DIGEST_LEN);
@@ -4086,6 +4312,7 @@ dir_server_new(int is_authority,
hostname, (int)dir_port);
ent->fake_status.addr = ent->addr;
+ tor_addr_copy(&ent->fake_status.ipv6_addr, &ent->ipv6_addr);
memcpy(ent->fake_status.identity_digest, digest, DIGEST_LEN);
if (nickname)
strlcpy(ent->fake_status.nickname, nickname,
@@ -4094,6 +4321,7 @@ dir_server_new(int is_authority,
ent->fake_status.nickname[0] = '\0';
ent->fake_status.dir_port = ent->dir_port;
ent->fake_status.or_port = ent->or_port;
+ ent->fake_status.ipv6_orport = ent->ipv6_orport;
return ent;
}
@@ -4105,6 +4333,7 @@ dir_server_new(int is_authority,
dir_server_t *
trusted_dir_server_new(const char *nickname, const char *address,
uint16_t dir_port, uint16_t or_port,
+ const tor_addr_port_t *ipv6_addrport,
const char *digest, const char *v3_auth_digest,
dirinfo_type_t type, double weight)
{
@@ -4135,7 +4364,9 @@ trusted_dir_server_new(const char *nickname, const char *address,
tor_addr_from_ipv4h(&addr, a);
result = dir_server_new(1, nickname, &addr, hostname,
- dir_port, or_port, digest,
+ dir_port, or_port,
+ ipv6_addrport,
+ digest,
v3_auth_digest, type, weight);
tor_free(hostname);
return result;
@@ -4147,9 +4378,12 @@ trusted_dir_server_new(const char *nickname, const char *address,
dir_server_t *
fallback_dir_server_new(const tor_addr_t *addr,
uint16_t dir_port, uint16_t or_port,
+ const tor_addr_port_t *addrport_ipv6,
const char *id_digest, double weight)
{
- return dir_server_new(0, NULL, addr, NULL, dir_port, or_port, id_digest,
+ return dir_server_new(0, NULL, addr, NULL, dir_port, or_port,
+ addrport_ipv6,
+ id_digest,
NULL, ALL_DIRINFO, weight);
}
@@ -4218,7 +4452,7 @@ clear_dir_servers(void)
/** For every current directory connection whose purpose is <b>purpose</b>,
* and where the resource being downloaded begins with <b>prefix</b>, split
* rest of the resource into base16 fingerprints (or base64 fingerprints if
- * purpose==DIR_PURPPOSE_FETCH_MICRODESC), decode them, and set the
+ * purpose==DIR_PURPOSE_FETCH_MICRODESC), decode them, and set the
* corresponding elements of <b>result</b> to a nonzero value.
*/
static void
@@ -4374,14 +4608,14 @@ MOCK_IMPL(STATIC void, initiate_descriptor_downloads,
tor_free(cp);
if (source) {
- /* We know which authority we want. */
+ /* We know which authority or directory mirror we want. */
directory_initiate_command_routerstatus(source, purpose,
ROUTER_PURPOSE_GENERAL,
DIRIND_ONEHOP,
resource, NULL, 0, 0);
} else {
directory_get_from_dirserver(purpose, ROUTER_PURPOSE_GENERAL, resource,
- pds_flags);
+ pds_flags, DL_WANT_ANY_DIRSERVER);
}
tor_free(resource);
}
@@ -4392,13 +4626,20 @@ static int
max_dl_per_request(const or_options_t *options, int purpose)
{
/* Since squid does not like URLs >= 4096 bytes we limit it to 96.
- * 4096 - strlen(http://255.255.255.255/tor/server/d/.z) == 4058
- * 4058/41 (40 for the hash and 1 for the + that separates them) => 98
+ * 4096 - strlen(http://[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ * /tor/server/d/.z) == 4026
+ * 4026/41 (40 for the hash and 1 for the + that separates them) => 98
* So use 96 because it's a nice number.
+ *
+ * For microdescriptors, the calculation is
+ * 4096 - strlen(http://[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ * /tor/micro/d/.z) == 4027
+ * 4027/44 (43 for the hash and 1 for the - that separates them) => 91
+ * So use 90 because it's a nice number.
*/
int max = 96;
if (purpose == DIR_PURPOSE_FETCH_MICRODESC) {
- max = 92;
+ max = 90;
}
/* If we're going to tunnel our connections, we can ask for a lot more
* in a request. */
@@ -4663,9 +4904,14 @@ launch_dummy_descriptor_download_as_needed(time_t now,
last_descriptor_download_attempted + DUMMY_DOWNLOAD_INTERVAL < now &&
last_dummy_download + DUMMY_DOWNLOAD_INTERVAL < now) {
last_dummy_download = now;
+ /* XX/teor - do we want an authority here, because they are less likely
+ * to give us the wrong address? (See #17782)
+ * I'm leaving the previous behaviour intact, because I don't like
+ * the idea of some relays contacting an authority every 20 minutes. */
directory_get_from_dirserver(DIR_PURPOSE_FETCH_SERVERDESC,
ROUTER_PURPOSE_GENERAL, "authority.z",
- PDS_RETRY_IF_NO_SERVERS);
+ PDS_RETRY_IF_NO_SERVERS,
+ DL_WANT_ANY_DIRSERVER);
}
}
@@ -4851,7 +5097,9 @@ router_differences_are_cosmetic(const routerinfo_t *r1, const routerinfo_t *r2)
(r1->contact_info && r2->contact_info &&
strcasecmp(r1->contact_info, r2->contact_info)) ||
r1->is_hibernating != r2->is_hibernating ||
- cmp_addr_policies(r1->exit_policy, r2->exit_policy))
+ cmp_addr_policies(r1->exit_policy, r2->exit_policy) ||
+ (r1->supports_tunnelled_dir_requests !=
+ r2->supports_tunnelled_dir_requests))
return 0;
if ((r1->declared_family == NULL) != (r2->declared_family == NULL))
return 0;
@@ -5196,8 +5444,8 @@ hid_serv_acting_as_directory(void)
/** Return true if this node is responsible for storing the descriptor ID
* in <b>query</b> and false otherwise. */
-int
-hid_serv_responsible_for_desc_id(const char *query)
+MOCK_IMPL(int, hid_serv_responsible_for_desc_id,
+ (const char *query))
{
const routerinfo_t *me;
routerstatus_t *last_rs;
diff --git a/src/or/routerlist.h b/src/or/routerlist.h
index 200533fe91..483dd06039 100644
--- a/src/or/routerlist.h
+++ b/src/or/routerlist.h
@@ -50,6 +50,7 @@ const routerstatus_t *router_pick_directory_server(dirinfo_type_t type,
dir_server_t *router_get_trusteddirserver_by_digest(const char *d);
dir_server_t *router_get_fallback_dirserver_by_digest(
const char *digest);
+int router_digest_is_fallback_dir(const char *digest);
dir_server_t *trusteddirserver_get_by_v3_auth_digest(const char *d);
const routerstatus_t *router_pick_trusteddirserver(dirinfo_type_t type,
int flags);
@@ -60,7 +61,8 @@ void router_reset_status_download_failures(void);
int routers_have_same_or_addrs(const routerinfo_t *r1, const routerinfo_t *r2);
void router_add_running_nodes_to_smartlist(smartlist_t *sl, int allow_invalid,
int need_uptime, int need_capacity,
- int need_guard, int need_desc);
+ int need_guard, int need_desc,
+ int pref_addr);
const routerinfo_t *routerlist_find_my_routerinfo(void);
uint32_t router_get_advertised_bandwidth(const routerinfo_t *router);
@@ -109,7 +111,7 @@ static int WRA_NEVER_DOWNLOADABLE(was_router_added_t s);
* was added. It might still be necessary to check whether the descriptor
* generator should be notified.
*/
-static INLINE int
+static inline int
WRA_WAS_ADDED(was_router_added_t s) {
return s == ROUTER_ADDED_SUCCESSFULLY || s == ROUTER_ADDED_NOTIFY_GENERATOR;
}
@@ -120,7 +122,7 @@ WRA_WAS_ADDED(was_router_added_t s) {
* - it was outdated.
* - its certificates were expired.
*/
-static INLINE int WRA_WAS_OUTDATED(was_router_added_t s)
+static inline int WRA_WAS_OUTDATED(was_router_added_t s)
{
return (s == ROUTER_WAS_TOO_OLD ||
s == ROUTER_IS_ALREADY_KNOWN ||
@@ -130,13 +132,13 @@ static INLINE int WRA_WAS_OUTDATED(was_router_added_t s)
}
/** Return true iff the outcome code in <b>s</b> indicates that the descriptor
* was flat-out rejected. */
-static INLINE int WRA_WAS_REJECTED(was_router_added_t s)
+static inline int WRA_WAS_REJECTED(was_router_added_t s)
{
return (s == ROUTER_AUTHDIR_REJECTS);
}
/** Return true iff the outcome code in <b>s</b> indicates that the descriptor
* was flat-out rejected. */
-static INLINE int WRA_NEVER_DOWNLOADABLE(was_router_added_t s)
+static inline int WRA_NEVER_DOWNLOADABLE(was_router_added_t s)
{
return (s == ROUTER_AUTHDIR_REJECTS ||
s == ROUTER_BAD_EI ||
@@ -170,10 +172,12 @@ int router_exit_policy_rejects_all(const routerinfo_t *router);
dir_server_t *trusted_dir_server_new(const char *nickname, const char *address,
uint16_t dir_port, uint16_t or_port,
+ const tor_addr_port_t *addrport_ipv6,
const char *digest, const char *v3_auth_digest,
dirinfo_type_t type, double weight);
dir_server_t *fallback_dir_server_new(const tor_addr_t *addr,
uint16_t dir_port, uint16_t or_port,
+ const tor_addr_port_t *addrport_ipv6,
const char *id_digest, double weight);
void dir_server_add(dir_server_t *ent);
@@ -201,7 +205,7 @@ void refresh_all_country_info(void);
int hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
const char *id);
int hid_serv_acting_as_directory(void);
-int hid_serv_responsible_for_desc_id(const char *id);
+MOCK_DECL(int, hid_serv_responsible_for_desc_id, (const char *id));
void list_pending_microdesc_downloads(digest256map_t *result);
void launch_descriptor_downloads(int purpose,
@@ -230,6 +234,9 @@ STATIC int choose_array_element_by_weight(const u64_dbl_t *entries,
int n_entries);
STATIC void scale_array_elements_to_u64(u64_dbl_t *entries, int n_entries,
uint64_t *total_out);
+STATIC const routerstatus_t *router_pick_directory_server_impl(
+ dirinfo_type_t auth, int flags,
+ int *n_busy_out);
MOCK_DECL(int, router_descriptor_is_older_than, (const routerinfo_t *router,
int seconds));
@@ -239,6 +246,8 @@ MOCK_DECL(STATIC was_router_added_t, extrainfo_insert,
MOCK_DECL(STATIC void, initiate_descriptor_downloads,
(const routerstatus_t *source, int purpose, smartlist_t *digests,
int lo, int hi, int pds_flags));
+STATIC int router_is_already_dir_fetching(const tor_addr_port_t *ap,
+ int serverdesc, int microdesc);
#endif
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index f898ef8aef..43c30a19ee 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -35,8 +35,9 @@
/****************************************************************************/
/** Enumeration of possible token types. The ones starting with K_ correspond
- * to directory 'keywords'. ERR_ is an error in the tokenizing process, EOF_
- * is an end-of-file marker, and NIL_ is used to encode not-a-token.
+ * to directory 'keywords'. A_ is for an annotation, R or C is related to
+ * hidden services, ERR_ is an error in the tokenizing process, EOF_ is an
+ * end-of-file marker, and NIL_ is used to encode not-a-token.
*/
typedef enum {
K_ACCEPT = 0,
@@ -125,6 +126,7 @@ typedef enum {
K_DIR_KEY_CERTIFICATION,
K_DIR_KEY_CROSSCERT,
K_DIR_ADDRESS,
+ K_DIR_TUNNELLED,
K_VOTE_STATUS,
K_VALID_AFTER,
@@ -318,6 +320,7 @@ static token_rule_t routerdesc_token_table[] = {
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
T1( "bandwidth", K_BANDWIDTH, GE(3), NO_OBJ ),
A01("@purpose", A_PURPOSE, GE(1), NO_OBJ ),
+ T01("tunnelled-dir-server",K_DIR_TUNNELLED, NO_ARGS, NO_OBJ ),
END_OF_TABLE
};
@@ -535,7 +538,7 @@ static int router_get_hash_impl(const char *s, size_t s_len, char *digest,
char end_char,
digest_algorithm_t alg);
static int router_get_hashes_impl(const char *s, size_t s_len,
- digests_t *digests,
+ common_digests_t *digests,
const char *start_str, const char *end_str,
char end_char);
static void token_clear(directory_token_t *tok);
@@ -635,7 +638,7 @@ router_get_router_hash(const char *s, size_t s_len, char *digest)
/** 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, digests_t *digests)
+router_get_networkstatus_v3_hashes(const char *s, common_digests_t *digests)
{
return router_get_hashes_impl(s,strlen(s),digests,
"network-status-version",
@@ -1609,6 +1612,12 @@ router_parse_entry_from_string(const char *s, const char *end,
router->wants_to_be_hs_dir = 1;
}
+ /* This router accepts tunnelled directory requests via begindir if it has
+ * an open dirport or it included "tunnelled-dir-server". */
+ if (find_opt_by_keyword(tokens, K_DIR_TUNNELLED) || router->dir_port > 0) {
+ router->supports_tunnelled_dir_requests = 1;
+ }
+
tok = find_by_keyword(tokens, K_ROUTER_SIGNATURE);
note_crypto_pk_op(VERIFY_RTR);
#ifdef COUNT_DISTINCT_DIGESTS
@@ -2061,7 +2070,7 @@ authority_cert_parse_from_string(const char *s, const char **end_of_string)
* object (starting with "r " at the start of a line). If none is found,
* 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 *
+static inline const char *
find_start_of_next_routerstatus(const char *s)
{
const char *eos, *footer, *sig;
@@ -2294,6 +2303,8 @@ routerstatus_parse_entry_from_string(memarea_t *area,
rs->is_unnamed = 1;
} else if (!strcmp(tok->args[i], "HSDir")) {
rs->is_hs_dir = 1;
+ } else if (!strcmp(tok->args[i], "V2Dir")) {
+ rs->is_v2_dir = 1;
}
}
}
@@ -2836,7 +2847,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
smartlist_t *rs_tokens = NULL, *footer_tokens = NULL;
networkstatus_voter_info_t *voter = NULL;
networkstatus_t *ns = NULL;
- digests_t ns_digests;
+ common_digests_t ns_digests;
const char *cert, *end_of_header, *end_of_footer, *s_dup = s;
directory_token_t *tok;
int ok;
@@ -3432,15 +3443,16 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
return ns;
}
-/** Return the digests_t that holds the digests of the
+/** Return the common_digests_t that holds the digests of the
* <b>flavor_name</b>-flavored networkstatus according to the detached
- * signatures document <b>sigs</b>, allocating a new digests_t as neeeded. */
-static digests_t *
+ * signatures document <b>sigs</b>, allocating a new common_digests_t as
+ * neeeded. */
+static common_digests_t *
detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name)
{
- digests_t *d = strmap_get(sigs->digests, flavor_name);
+ common_digests_t *d = strmap_get(sigs->digests, flavor_name);
if (!d) {
- d = tor_malloc_zero(sizeof(digests_t));
+ d = tor_malloc_zero(sizeof(common_digests_t));
strmap_set(sigs->digests, flavor_name, d);
}
return d;
@@ -3448,7 +3460,7 @@ detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name)
/** Return the list of signatures of the <b>flavor_name</b>-flavored
* networkstatus according to the detached signatures document <b>sigs</b>,
- * allocating a new digests_t as neeeded. */
+ * allocating a new common_digests_t as neeeded. */
static smartlist_t *
detached_get_signatures(ns_detached_signatures_t *sigs,
const char *flavor_name)
@@ -3470,7 +3482,7 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
* networkstatus_parse_vote_from_string(). */
directory_token_t *tok;
memarea_t *area = NULL;
- digests_t *digests;
+ common_digests_t *digests;
smartlist_t *tokens = smartlist_new();
ns_detached_signatures_t *sigs =
@@ -3668,10 +3680,10 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
*
* Returns NULL on policy errors.
*
- * If there is a policy error, malformed_list is set to true if the entire
- * policy list should be discarded. Otherwise, it is set to false, and only
- * this item should be ignored - the rest of the policy list can continue to
- * be processed and used.
+ * Set *<b>malformed_list</b> to true if the entire policy list should be
+ * discarded. Otherwise, set it to false, and only this item should be ignored
+ * on error - the rest of the policy list can continue to be processed and
+ * used.
*
* The addr_policy_t returned by this function can have its address set to
* AF_UNSPEC for '*'. Use policy_expand_unspec() to turn this into a pair
@@ -3684,8 +3696,8 @@ router_parse_addr_policy_item_from_string,(const char *s, int assume_action,
directory_token_t *tok = NULL;
const char *cp, *eos;
/* Longest possible policy is
- * "accept6 ffff:ffff:..255/128:10000-65535",
- * which contains a max-length IPv6 address, plus 24 characters.
+ * "accept6 [ffff:ffff:..255]/128:10000-65535",
+ * which contains a max-length IPv6 address, plus 26 characters.
* But note that there can be an arbitrary amount of space between the
* accept and the address:mask/port element.
* We don't need to multiply TOR_ADDR_BUF_LEN by 2, as there is only one
@@ -3697,9 +3709,12 @@ router_parse_addr_policy_item_from_string,(const char *s, int assume_action,
memarea_t *area = NULL;
tor_assert(malformed_list);
+ *malformed_list = 0;
s = eat_whitespace(s);
- if ((*s == '*' || TOR_ISDIGIT(*s)) && assume_action >= 0) {
+ /* We can only do assume_action on []-quoted IPv6, as "a" (accept)
+ * and ":" (port separator) are ambiguous */
+ if ((*s == '*' || *s == '[' || TOR_ISDIGIT(*s)) && assume_action >= 0) {
if (tor_snprintf(line, sizeof(line), "%s %s",
assume_action == ADDR_POLICY_ACCEPT?"accept":"reject", s)<0) {
log_warn(LD_DIR, "Policy %s is too long.", escaped(s));
@@ -3930,7 +3945,7 @@ token_clear(directory_token_t *tok)
* Return <b>tok</b> on success, or a new ERR_ token if the token didn't
* conform to the syntax we wanted.
**/
-static INLINE directory_token_t *
+static inline directory_token_t *
token_check_object(memarea_t *area, const char *kwd,
directory_token_t *tok, obj_syntax o_syn)
{
@@ -3995,7 +4010,7 @@ token_check_object(memarea_t *area, const char *kwd,
* number of parsed elements into the n_args field of <b>tok</b>. Allocate
* all storage in <b>area</b>. Return the number of arguments parsed, or
* return -1 if there was an insanely high number of arguments. */
-static INLINE int
+static inline int
get_token_arguments(memarea_t *area, directory_token_t *tok,
const char *s, const char *eol)
{
@@ -4430,7 +4445,7 @@ router_get_hash_impl(const char *s, size_t s_len, char *digest,
/** As router_get_hash_impl, but compute all hashes. */
static int
-router_get_hashes_impl(const char *s, size_t s_len, digests_t *digests,
+router_get_hashes_impl(const char *s, size_t s_len, common_digests_t *digests,
const char *start_str,
const char *end_str, char end_c)
{
@@ -4439,7 +4454,7 @@ router_get_hashes_impl(const char *s, size_t s_len, digests_t *digests,
&start,&end)<0)
return -1;
- if (crypto_digest_all(digests, start, end-start)) {
+ if (crypto_common_digests(digests, start, end-start)) {
log_warn(LD_BUG,"couldn't compute digests");
return -1;
}
diff --git a/src/or/routerparse.h b/src/or/routerparse.h
index 99fd52866c..723d5d8945 100644
--- a/src/or/routerparse.h
+++ b/src/or/routerparse.h
@@ -14,7 +14,8 @@
int router_get_router_hash(const char *s, size_t s_len, char *digest);
int router_get_dir_hash(const char *s, char *digest);
-int router_get_networkstatus_v3_hashes(const char *s, digests_t *digests);
+int router_get_networkstatus_v3_hashes(const char *s,
+ common_digests_t *digests);
int router_get_extrainfo_hash(const char *s, size_t s_len, char *digest);
#define DIROBJ_MAX_SIG_LEN 256
char *router_get_dirobj_signature(const char *digest,
diff --git a/src/or/routerset.c b/src/or/routerset.c
index 3be55d3404..debe9ec6e1 100644
--- a/src/or/routerset.c
+++ b/src/or/routerset.c
@@ -107,10 +107,12 @@ routerset_parse(routerset_t *target, const char *s, const char *description)
description);
smartlist_add(target->country_names, countryname);
added_countries = 1;
- } else if ((strchr(nick,'.') || strchr(nick, '*')) &&
- (p = router_parse_addr_policy_item_from_string(
+ } else if ((strchr(nick,'.') || strchr(nick, ':') || strchr(nick, '*'))
+ && (p = router_parse_addr_policy_item_from_string(
nick, ADDR_POLICY_REJECT,
&malformed_list))) {
+ /* IPv4 addresses contain '.', IPv6 addresses contain ':',
+ * and wildcard addresses contain '*'. */
log_debug(LD_CONFIG, "Adding address %s to %s", nick, description);
smartlist_add(target->policies, p);
} else if (malformed_list) {
diff --git a/src/or/statefile.c b/src/or/statefile.c
index dd1894beb7..7fe8dc5077 100644
--- a/src/or/statefile.c
+++ b/src/or/statefile.c
@@ -9,6 +9,7 @@
#include "circuitstats.h"
#include "config.h"
#include "confparse.h"
+#include "connection.h"
#include "entrynodes.h"
#include "hibernate.h"
#include "rephist.h"
@@ -372,6 +373,12 @@ or_state_load(void)
new_state = or_state_new();
} else if (contents) {
log_info(LD_GENERAL, "Loaded state from \"%s\"", fname);
+ /* Warn the user if their clock has been set backwards,
+ * they could be tricked into using old consensuses */
+ time_t apparent_skew = new_state->LastWritten - time(NULL);
+ if (apparent_skew > 0)
+ clock_skew_warning(NULL, (long)apparent_skew, 1, LD_GENERAL,
+ "local state file", fname);
} else {
log_info(LD_GENERAL, "Initialized state");
}
diff --git a/src/or/status.c b/src/or/status.c
index 8f7be0aa3c..69d10721d2 100644
--- a/src/or/status.c
+++ b/src/or/status.c
@@ -164,24 +164,38 @@ log_accounting(const time_t now, const or_options_t *options)
or_state_t *state = get_or_state();
char *acc_rcvd = bytes_to_usage(state->AccountingBytesReadInInterval);
char *acc_sent = bytes_to_usage(state->AccountingBytesWrittenInInterval);
+ char *acc_used = bytes_to_usage(get_accounting_bytes());
uint64_t acc_bytes = options->AccountingMax;
char *acc_max;
time_t interval_end = accounting_get_end_time();
char end_buf[ISO_TIME_LEN + 1];
char *remaining = NULL;
- if (options->AccountingRule == ACCT_SUM)
- acc_bytes *= 2;
acc_max = bytes_to_usage(acc_bytes);
format_local_iso_time(end_buf, interval_end);
remaining = secs_to_uptime(interval_end - now);
+ const char *acc_rule;
+ switch (options->AccountingRule) {
+ case ACCT_MAX: acc_rule = "max";
+ break;
+ case ACCT_SUM: acc_rule = "sum";
+ break;
+ case ACCT_OUT: acc_rule = "out";
+ break;
+ case ACCT_IN: acc_rule = "in";
+ break;
+ default: acc_rule = "max";
+ break;
+ }
+
log_notice(LD_HEARTBEAT, "Heartbeat: Accounting enabled. "
- "Sent: %s / %s, Received: %s / %s. The "
+ "Sent: %s, Received: %s, Used: %s / %s, Rule: %s. The "
"current accounting interval ends on %s, in %s.",
- acc_sent, acc_max, acc_rcvd, acc_max, end_buf, remaining);
+ acc_sent, acc_rcvd, acc_used, acc_max, acc_rule, end_buf, remaining);
tor_free(acc_rcvd);
tor_free(acc_sent);
+ tor_free(acc_used);
tor_free(acc_max);
tor_free(remaining);
}
diff --git a/src/or/transports.c b/src/or/transports.c
index ba2c784c2c..5a3af85be8 100644
--- a/src/or/transports.c
+++ b/src/or/transports.c
@@ -105,7 +105,7 @@
static process_environment_t *
create_managed_proxy_environment(const managed_proxy_t *mp);
-static INLINE int proxy_configuration_finished(const managed_proxy_t *mp);
+static inline int proxy_configuration_finished(const managed_proxy_t *mp);
static void handle_finished_proxy(managed_proxy_t *mp);
static void parse_method_error(const char *line, int is_server_method);
@@ -713,7 +713,7 @@ register_client_proxy(const managed_proxy_t *mp)
}
/** Register the transports of managed proxy <b>mp</b>. */
-static INLINE void
+static inline void
register_proxy(const managed_proxy_t *mp)
{
if (mp->is_server)
@@ -828,7 +828,7 @@ handle_finished_proxy(managed_proxy_t *mp)
/** Return true if the configuration of the managed proxy <b>mp</b> is
finished. */
-static INLINE int
+static inline int
proxy_configuration_finished(const managed_proxy_t *mp)
{
return (mp->conf_state == PT_PROTO_CONFIGURED ||
@@ -1100,7 +1100,7 @@ parse_smethod_line(const char *line, managed_proxy_t *mp)
smartlist_add(mp->transports, transport);
- /* For now, notify the user so that he knows where the server
+ /* For now, notify the user so that they know where the server
transport is listening. */
log_info(LD_CONFIG, "Server transport %s at %s:%d.",
method_name, address, (int)port);
diff --git a/src/test/Makefile.nmake b/src/test/Makefile.nmake
index 0435617683..0ba56d7036 100644
--- a/src/test/Makefile.nmake
+++ b/src/test/Makefile.nmake
@@ -14,7 +14,8 @@ LIBS = ..\..\..\build-alpha\lib\libevent.lib \
TEST_OBJECTS = test.obj test_addr.obj test_channel.obj test_channeltls.obj \
test_containers.obj \
test_controller_events.obj test_crypto.obj test_data.obj test_dir.obj \
- test_checkdir.obj test_microdesc.obj test_pt.obj test_util.obj test_config.obj \
+ test_checkdir.obj test_microdesc.obj test_pt.obj test_util.obj \
+ test_config.obj test_connection.obj \
test_cell_formats.obj test_relay.obj test_replay.obj \
test_scheduler.obj test_introduce.obj test_hs.obj tinytest.obj
diff --git a/src/test/bench.c b/src/test/bench.c
index 2a27377c80..1ef54042b2 100644
--- a/src/test/bench.c
+++ b/src/test/bench.c
@@ -443,6 +443,45 @@ bench_siphash(void)
}
static void
+bench_digest(void)
+{
+ char buf[8192];
+ char out[DIGEST512_LEN];
+ const int lens[] = { 1, 16, 32, 64, 128, 512, 1024, 2048, -1 };
+ const int N = 300000;
+ uint64_t start, end;
+ crypto_rand(buf, sizeof(buf));
+
+ for (int alg = 0; alg < N_DIGEST_ALGORITHMS; alg++) {
+ for (int i = 0; lens[i] > 0; ++i) {
+ reset_perftime();
+ start = perftime();
+ for (int j = 0; j < N; ++j) {
+ switch (alg) {
+ case DIGEST_SHA1:
+ crypto_digest(out, buf, lens[i]);
+ break;
+ case DIGEST_SHA256:
+ case DIGEST_SHA3_256:
+ crypto_digest256(out, buf, lens[i], alg);
+ break;
+ case DIGEST_SHA512:
+ case DIGEST_SHA3_512:
+ crypto_digest512(out, buf, lens[i], alg);
+ break;
+ default:
+ tor_assert(0);
+ }
+ }
+ end = perftime();
+ printf("%s(%d): %.2f ns per call\n",
+ crypto_digest_algorithm_get_name(alg),
+ lens[i], NANOCOUNT(start,end,N));
+ }
+ }
+}
+
+static void
bench_cell_ops(void)
{
const int iters = 1<<16;
@@ -589,6 +628,7 @@ typedef struct benchmark_t {
static struct benchmark_t benchmarks[] = {
ENT(dmap),
ENT(siphash),
+ ENT(digest),
ENT(aes),
ENT(onion_TAP),
ENT(onion_ntor),
@@ -643,7 +683,10 @@ main(int argc, const char **argv)
reset_perftime();
- crypto_seed_rng();
+ if (crypto_seed_rng() < 0) {
+ printf("Couldn't seed RNG; exiting.\n");
+ return 1;
+ }
crypto_init_siphash_key();
options = options_new();
init_logging(1);
diff --git a/src/test/bt_test.py b/src/test/bt_test.py
index e694361703..dab02d7699 100755
--- a/src/test/bt_test.py
+++ b/src/test/bt_test.py
@@ -15,6 +15,7 @@ OK
"""
+from __future__ import print_function
import sys
@@ -37,6 +38,8 @@ for I in range(len(LINES)):
if matches(LINES[I:], FUNCNAMES):
print("OK")
sys.exit(0)
-else:
- print("BAD")
- sys.exit(1)
+
+for l in LINES:
+ print("{}".format(l), end="")
+
+sys.exit(1)
diff --git a/src/test/include.am b/src/test/include.am
index 7a02a4e2f2..786fb77dd0 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -8,14 +8,16 @@ TESTS_ENVIRONMENT = \
export builddir="$(builddir)"; \
export TESTING_TOR_BINARY="$(TESTING_TOR_BINARY)";
-TESTSCRIPTS = src/test/test_zero_length_keys.sh
+TESTSCRIPTS = src/test/test_zero_length_keys.sh \
+ src/test/test_switch_id.sh
if USEPYTHON
TESTSCRIPTS += src/test/test_ntor.sh src/test/test_bt.sh
endif
TESTS += src/test/test src/test/test-slow src/test/test-memwipe \
- src/test/test_workqueue src/test/test_keygen.sh $(TESTSCRIPTS)
+ src/test/test_workqueue src/test/test_keygen.sh \
+ $(TESTSCRIPTS)
# These flavors are run using automake's test-driver and test-network.sh
TEST_CHUTNEY_FLAVORS = basic-min bridges-min hs-min bridges+hs
@@ -37,7 +39,8 @@ noinst_PROGRAMS+= \
src/test/test-slow \
src/test/test-memwipe \
src/test/test-child \
- src/test/test_workqueue
+ src/test/test_workqueue \
+ src/test/test-switch-id
endif
src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
@@ -53,6 +56,8 @@ src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
# matters a lot there, and is quite hard to debug if you forget to do it.
src_test_test_SOURCES = \
+ src/test/log_test_helpers.c \
+ src/test/rend_test_helpers.c \
src/test/test.c \
src/test/test_accounting.c \
src/test/test_addr.c \
@@ -65,13 +70,17 @@ src_test_test_SOURCES = \
src/test/test_checkdir.c \
src/test/test_circuitlist.c \
src/test/test_circuitmux.c \
+ src/test/test_compat_libevent.c \
src/test/test_config.c \
+ src/test/test_connection.c \
src/test/test_containers.c \
src/test/test_controller.c \
src/test/test_controller_events.c \
src/test/test_crypto.c \
src/test/test_data.c \
src/test/test_dir.c \
+ src/test/test_dir_common.c \
+ src/test/test_dir_handle_get.c \
src/test/test_entryconn.c \
src/test/test_entrynodes.c \
src/test/test_guardfraction.c \
@@ -86,9 +95,11 @@ src_test_test_SOURCES = \
src/test/test_oom.c \
src/test/test_options.c \
src/test/test_policy.c \
+ src/test/test_procmon.c \
src/test/test_pt.c \
src/test/test_relay.c \
src/test/test_relaycell.c \
+ src/test/test_rendcache.c \
src/test/test_replay.c \
src/test/test_routerkeys.c \
src/test/test_routerlist.c \
@@ -97,9 +108,12 @@ src_test_test_SOURCES = \
src/test/test_socks.c \
src/test/test_status.c \
src/test/test_threads.c \
+ src/test/test_tortls.c \
src/test/test_util.c \
+ src/test/test_util_format.c \
+ src/test/test_util_process.c \
src/test/test_helpers.c \
- src/test/test_dns.c \
+ src/test/test_dns.c \
src/test/testing_common.c \
src/ext/tinytest.c
@@ -126,11 +140,21 @@ src_test_test_workqueue_SOURCES = \
src_test_test_workqueue_CPPFLAGS= $(src_test_AM_CPPFLAGS)
src_test_test_workqueue_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+src_test_test_switch_id_SOURCES = \
+ src/test/test_switch_id.c
+src_test_test_switch_id_CPPFLAGS= $(src_test_AM_CPPFLAGS)
+src_test_test_switch_id_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+src_test_test_switch_id_LDFLAGS = @TOR_LDFLAGS_zlib@
+src_test_test_switch_id_LDADD = \
+ src/common/libor-testing.a \
+ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@
+
src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
@TOR_LDFLAGS_libevent@
src_test_test_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \
- src/common/libor-crypto-testing.a $(LIBDONNA) src/common/libor.a \
- src/common/libor-event-testing.a src/trunnel/libor-trunnel-testing.a \
+ src/common/libor-crypto-testing.a $(LIBKECCAK_TINY) $(LIBDONNA) \
+ src/common/libor.a src/common/libor-event-testing.a \
+ src/trunnel/libor-trunnel-testing.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \
@TOR_SYSTEMD_LIBS@
@@ -148,7 +172,7 @@ src_test_test_memwipe_LDFLAGS = $(src_test_test_LDFLAGS)
src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
@TOR_LDFLAGS_libevent@
src_test_bench_LDADD = src/or/libtor.a src/common/libor.a \
- src/common/libor-crypto.a $(LIBDONNA) \
+ src/common/libor-crypto.a $(LIBKECCAK_TINY) $(LIBDONNA) \
src/common/libor-event.a src/trunnel/libor-trunnel.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \
@@ -158,26 +182,30 @@ src_test_test_workqueue_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
@TOR_LDFLAGS_libevent@
src_test_test_workqueue_LDADD = src/or/libtor-testing.a \
src/common/libor-testing.a \
- src/common/libor-crypto-testing.a $(LIBDONNA) \
+ src/common/libor-crypto-testing.a $(LIBKECCAK_TINY) $(LIBDONNA) \
src/common/libor-event-testing.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
noinst_HEADERS+= \
src/test/fakechans.h \
+ src/test/log_test_helpers.h \
+ src/test/rend_test_helpers.h \
src/test/test.h \
src/test/test_helpers.h \
+ src/test/test_dir_common.h \
src/test/test_descriptors.inc \
src/test/example_extrainfo.inc \
src/test/failing_routerdescs.inc \
src/test/ed25519_vectors.inc \
- src/test/test_descriptors.inc
+ src/test/test_descriptors.inc \
+ src/test/vote_descriptors.inc
noinst_PROGRAMS+= src/test/test-ntor-cl
src_test_test_ntor_cl_SOURCES = src/test/test_ntor_cl.c
src_test_test_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
src_test_test_ntor_cl_LDADD = src/or/libtor.a src/common/libor.a \
- src/common/libor-crypto.a $(LIBDONNA) \
+ src/common/libor-crypto.a $(LIBKECCAK_TINY) $(LIBDONNA) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
src_test_test_ntor_cl_AM_CPPFLAGS = \
@@ -199,4 +227,5 @@ EXTRA_DIST += \
src/test/test_keygen.sh \
src/test/test_zero_length_keys.sh \
src/test/test_ntor.sh src/test/test_bt.sh \
- src/test/test-network.sh
+ src/test/test-network.sh \
+ src/test/test_switch_id.sh
diff --git a/src/test/log_test_helpers.c b/src/test/log_test_helpers.c
new file mode 100644
index 0000000000..bcf73a8437
--- /dev/null
+++ b/src/test/log_test_helpers.c
@@ -0,0 +1,113 @@
+/* Copyright (c) 2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+#define LOG_PRIVATE
+#include "torlog.h"
+#include "log_test_helpers.h"
+
+static smartlist_t *saved_logs = NULL;
+
+int
+setup_capture_of_logs(int new_level)
+{
+ int previous_log = log_global_min_severity_;
+ log_global_min_severity_ = new_level;
+ mock_clean_saved_logs();
+ MOCK(logv, mock_saving_logv);
+ return previous_log;
+}
+
+void
+teardown_capture_of_logs(int prev)
+{
+ UNMOCK(logv);
+ log_global_min_severity_ = prev;
+ mock_clean_saved_logs();
+}
+
+void
+mock_clean_saved_logs(void)
+{
+ if (!saved_logs)
+ return;
+ SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
+ { tor_free(m->generated_msg); tor_free(m); });
+ smartlist_free(saved_logs);
+ saved_logs = NULL;
+}
+
+const smartlist_t *
+mock_saved_logs(void)
+{
+ return saved_logs;
+}
+
+int
+mock_saved_log_has_message(const char *msg)
+{
+ int has_msg = 0;
+ if (saved_logs) {
+ SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
+ {
+ if (msg && m->generated_msg &&
+ !strcmp(msg, m->generated_msg)) {
+ has_msg = 1;
+ }
+ });
+ }
+
+ return has_msg;
+}
+
+/* Do the saved logs have any messages with severity? */
+int
+mock_saved_log_has_severity(int severity)
+{
+ int has_sev = 0;
+ if (saved_logs) {
+ SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
+ {
+ if (m->severity == severity) {
+ has_sev = 1;
+ }
+ });
+ }
+
+ return has_sev;
+}
+
+/* Do the saved logs have any messages? */
+int
+mock_saved_log_has_entry(void)
+{
+ if (saved_logs) {
+ return smartlist_len(saved_logs) > 0;
+ }
+ return 0;
+}
+
+void
+mock_saving_logv(int severity, log_domain_mask_t domain,
+ const char *funcname, const char *suffix,
+ const char *format, va_list ap)
+{
+ (void)domain;
+ char *buf = tor_malloc_zero(10240);
+ int n;
+ n = tor_vsnprintf(buf,10240,format,ap);
+ tor_assert(n < 10240-1);
+ buf[n]='\n';
+ buf[n+1]='\0';
+
+ mock_saved_log_entry_t *e = tor_malloc_zero(sizeof(mock_saved_log_entry_t));
+ e->severity = severity;
+ e->funcname = funcname;
+ e->suffix = suffix;
+ e->format = format;
+ e->generated_msg = tor_strdup(buf);
+ tor_free(buf);
+
+ if (!saved_logs)
+ saved_logs = smartlist_new();
+ smartlist_add(saved_logs, e);
+}
+
diff --git a/src/test/log_test_helpers.h b/src/test/log_test_helpers.h
new file mode 100644
index 0000000000..02f31a5220
--- /dev/null
+++ b/src/test/log_test_helpers.h
@@ -0,0 +1,56 @@
+/* Copyright (c) 2014-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+
+#ifndef TOR_LOG_TEST_HELPERS_H
+#define TOR_LOG_TEST_HELPERS_H
+
+typedef struct mock_saved_log_entry_t {
+ int severity;
+ const char *funcname;
+ const char *suffix;
+ const char *format;
+ char *generated_msg;
+ struct mock_saved_log_entry_t *next;
+} mock_saved_log_entry_t;
+
+void mock_saving_logv(int severity, log_domain_mask_t domain,
+ const char *funcname, const char *suffix,
+ const char *format, va_list ap)
+ CHECK_PRINTF(5, 0);
+void mock_clean_saved_logs(void);
+const smartlist_t *mock_saved_logs(void);
+int setup_capture_of_logs(int new_level);
+void teardown_capture_of_logs(int prev);
+
+int mock_saved_log_has_message(const char *msg);
+int mock_saved_log_has_severity(int severity);
+int mock_saved_log_has_entry(void);
+
+#define expect_log_msg(str) \
+ tt_assert_msg(mock_saved_log_has_message(str), \
+ "expected log to contain " # str);
+
+#define expect_no_log_msg(str) \
+ tt_assert_msg(!mock_saved_log_has_message(str), \
+ "expected log to not contain " # str);
+
+#define expect_log_severity(severity) \
+ tt_assert_msg(mock_saved_log_has_severity(severity), \
+ "expected log to contain severity " # severity);
+
+#define expect_no_log_severity(severity) \
+ tt_assert_msg(!mock_saved_log_has_severity(severity), \
+ "expected log to not contain severity " # severity);
+
+#define expect_log_entry() \
+ tt_assert_msg(mock_saved_log_has_entry(), \
+ "expected log to contain entries");
+
+#define expect_no_log_entry() \
+ tt_assert_msg(!mock_saved_log_has_entry(), \
+ "expected log to not contain entries");
+
+#endif
+
diff --git a/src/test/rend_test_helpers.c b/src/test/rend_test_helpers.c
new file mode 100644
index 0000000000..f16d67fa1a
--- /dev/null
+++ b/src/test/rend_test_helpers.c
@@ -0,0 +1,73 @@
+/* Copyright (c) 2014-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "test.h"
+#include "rendcommon.h"
+#include "rend_test_helpers.h"
+
+void
+generate_desc(int time_diff, rend_encoded_v2_service_descriptor_t **desc,
+ char **service_id, int intro_points)
+{
+ rend_service_descriptor_t *generated = NULL;
+ smartlist_t *descs = smartlist_new();
+ time_t now;
+
+ now = time(NULL) + time_diff;
+ create_descriptor(&generated, service_id, intro_points);
+ generated->timestamp = now;
+
+ rend_encode_v2_descriptors(descs, generated, now, 0, REND_NO_AUTH, NULL,
+ NULL);
+ tor_assert(smartlist_len(descs) > 1);
+ *desc = smartlist_get(descs, 0);
+ smartlist_set(descs, 0, NULL);
+
+ SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
+ rend_encoded_v2_service_descriptor_free(d));
+ smartlist_free(descs);
+ rend_service_descriptor_free(generated);
+}
+
+void
+create_descriptor(rend_service_descriptor_t **generated, char **service_id,
+ int intro_points)
+{
+ crypto_pk_t *pk1 = NULL;
+ crypto_pk_t *pk2 = NULL;
+ int i;
+
+ *service_id = tor_malloc(REND_SERVICE_ID_LEN_BASE32+1);
+ pk1 = pk_generate(0);
+ pk2 = pk_generate(1);
+
+ *generated = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ (*generated)->pk = crypto_pk_dup_key(pk1);
+ rend_get_service_id((*generated)->pk, *service_id);
+
+ (*generated)->version = 2;
+ (*generated)->protocols = 42;
+ (*generated)->intro_nodes = smartlist_new();
+
+ for (i = 0; i < intro_points; i++) {
+ rend_intro_point_t *intro = tor_malloc_zero(sizeof(rend_intro_point_t));
+ crypto_pk_t *okey = pk_generate(2 + i);
+ intro->extend_info = tor_malloc_zero(sizeof(extend_info_t));
+ intro->extend_info->onion_key = okey;
+ crypto_pk_get_digest(intro->extend_info->onion_key,
+ intro->extend_info->identity_digest);
+ intro->extend_info->nickname[0] = '$';
+ base16_encode(intro->extend_info->nickname + 1,
+ sizeof(intro->extend_info->nickname) - 1,
+ intro->extend_info->identity_digest, DIGEST_LEN);
+ tor_addr_from_ipv4h(&intro->extend_info->addr, crypto_rand_int(65536));
+ intro->extend_info->port = 1 + crypto_rand_int(65535);
+ intro->intro_key = crypto_pk_dup_key(pk2);
+ smartlist_add((*generated)->intro_nodes, intro);
+ }
+
+ crypto_pk_free(pk1);
+ crypto_pk_free(pk2);
+}
+
diff --git a/src/test/rend_test_helpers.h b/src/test/rend_test_helpers.h
new file mode 100644
index 0000000000..1ef03747d7
--- /dev/null
+++ b/src/test/rend_test_helpers.h
@@ -0,0 +1,15 @@
+/* Copyright (c) 2014-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+
+#ifndef TOR_REND_TEST_HELPERS_H
+#define TOR_REND_TEST_HELPERS_H
+
+void generate_desc(int time_diff, rend_encoded_v2_service_descriptor_t **desc,
+ char **service_id, int intro_points);
+void create_descriptor(rend_service_descriptor_t **generated,
+ char **service_id, int intro_points);
+
+#endif
+
diff --git a/src/test/test-memwipe.c b/src/test/test-memwipe.c
index a39bad1540..5d4fcec664 100644
--- a/src/test/test-memwipe.c
+++ b/src/test/test-memwipe.c
@@ -62,7 +62,7 @@ fill_a_buffer_nothing(void)
return sum;
}
-static INLINE int
+static inline int
vmemeq(volatile char *a, const char *b, size_t n)
{
while (n--) {
diff --git a/src/test/test.c b/src/test/test.c
index e10e260266..d671ac896e 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -28,6 +28,7 @@
#define ROUTER_PRIVATE
#define CIRCUITSTATS_PRIVATE
#define CIRCUITLIST_PRIVATE
+#define MAIN_PRIVATE
#define STATEFILE_PRIVATE
/*
@@ -47,8 +48,10 @@ double fabs(double x);
#include "connection_edge.h"
#include "geoip.h"
#include "rendcommon.h"
+#include "rendcache.h"
#include "test.h"
#include "torgzip.h"
+#include "main.h"
#include "memarea.h"
#include "onion.h"
#include "onion_ntor.h"
@@ -316,6 +319,9 @@ test_circuit_timeout(void *arg)
int i, runs;
double close_ms;
(void)arg;
+
+ initialize_periodic_events();
+
circuit_build_times_init(&initial);
circuit_build_times_init(&estimate);
circuit_build_times_init(&final);
@@ -455,6 +461,7 @@ test_circuit_timeout(void *arg)
circuit_build_times_free_timeouts(&estimate);
circuit_build_times_free_timeouts(&final);
or_state_free(state);
+ teardown_periodic_events();
}
/** Test encoding and parsing of rendezvous service descriptors. */
@@ -494,6 +501,9 @@ test_rend_fns(void *arg)
tt_str_op(address6,OP_EQ, "abcdefghijklmnop");
tt_assert(BAD_HOSTNAME == parse_extended_hostname(address7));
+ /* Initialize the service cache. */
+ rend_cache_init();
+
pk1 = pk_generate(0);
pk2 = pk_generate(1);
generated = tor_malloc_zero(sizeof(rend_service_descriptor_t));
@@ -1105,8 +1115,8 @@ static struct testcase_t test_array[] = {
{ "bad_onion_handshake", test_bad_onion_handshake, 0, NULL, NULL },
ENT(onion_queues),
{ "ntor_handshake", test_ntor_handshake, 0, NULL, NULL },
- ENT(circuit_timeout),
- ENT(rend_fns),
+ FORK(circuit_timeout),
+ FORK(rend_fns),
ENT(geoip),
FORK(geoip_with_pt),
FORK(stats),
@@ -1125,12 +1135,15 @@ extern struct testcase_t channeltls_tests[];
extern struct testcase_t checkdir_tests[];
extern struct testcase_t circuitlist_tests[];
extern struct testcase_t circuitmux_tests[];
+extern struct testcase_t compat_libevent_tests[];
extern struct testcase_t config_tests[];
+extern struct testcase_t connection_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 dir_tests[];
+extern struct testcase_t dir_handle_get_tests[];
extern struct testcase_t entryconn_tests[];
extern struct testcase_t entrynodes_tests[];
extern struct testcase_t guardfraction_tests[];
@@ -1145,9 +1158,11 @@ extern struct testcase_t nodelist_tests[];
extern struct testcase_t oom_tests[];
extern struct testcase_t options_tests[];
extern struct testcase_t policy_tests[];
+extern struct testcase_t procmon_tests[];
extern struct testcase_t pt_tests[];
extern struct testcase_t relay_tests[];
extern struct testcase_t relaycell_tests[];
+extern struct testcase_t rend_cache_tests[];
extern struct testcase_t replaycache_tests[];
extern struct testcase_t router_tests[];
extern struct testcase_t routerkeys_tests[];
@@ -1157,7 +1172,10 @@ extern struct testcase_t scheduler_tests[];
extern struct testcase_t socks_tests[];
extern struct testcase_t status_tests[];
extern struct testcase_t thread_tests[];
+extern struct testcase_t tortls_tests[];
extern struct testcase_t util_tests[];
+extern struct testcase_t util_format_tests[];
+extern struct testcase_t util_process_tests[];
extern struct testcase_t dns_tests[];
struct testgroup_t testgroups[] = {
@@ -1173,12 +1191,15 @@ struct testgroup_t testgroups[] = {
{ "checkdir/", checkdir_tests },
{ "circuitlist/", circuitlist_tests },
{ "circuitmux/", circuitmux_tests },
+ { "compat/libevent/", compat_libevent_tests },
{ "config/", config_tests },
+ { "connection/", connection_tests },
{ "container/", container_tests },
{ "control/", controller_tests },
{ "control/event/", controller_event_tests },
{ "crypto/", crypto_tests },
{ "dir/", dir_tests },
+ { "dir_handle_get/", dir_handle_get_tests },
{ "dir/md/", microdesc_tests },
{ "entryconn/", entryconn_tests },
{ "entrynodes/", entrynodes_tests },
@@ -1192,9 +1213,11 @@ struct testgroup_t testgroups[] = {
{ "oom/", oom_tests },
{ "options/", options_tests },
{ "policy/" , policy_tests },
+ { "procmon/", procmon_tests },
{ "pt/", pt_tests },
{ "relay/" , relay_tests },
{ "relaycell/", relaycell_tests },
+ { "rend_cache/", rend_cache_tests },
{ "replaycache/", replaycache_tests },
{ "routerkeys/", routerkeys_tests },
{ "routerlist/", routerlist_tests },
@@ -1202,8 +1225,11 @@ struct testgroup_t testgroups[] = {
{ "scheduler/", scheduler_tests },
{ "socks/", socks_tests },
{ "status/" , status_tests },
+ { "tortls/", tortls_tests },
{ "util/", util_tests },
+ { "util/format/", util_format_tests },
{ "util/logging/", logging_tests },
+ { "util/process/", util_process_tests },
{ "util/thread/", thread_tests },
{ "dns/", dns_tests },
END_OF_GROUPS
diff --git a/src/test/test_accounting.c b/src/test/test_accounting.c
index 25908e942c..7edba988a6 100644
--- a/src/test/test_accounting.c
+++ b/src/test/test_accounting.c
@@ -61,6 +61,32 @@ test_accounting_limits(void *arg)
fake_time += 1;
consider_hibernation(fake_time);
tor_assert(we_are_hibernating() == 1);
+
+ options->AccountingRule = ACCT_OUT;
+
+ accounting_add_bytes(100, 10, 1);
+ fake_time += 1;
+ consider_hibernation(fake_time);
+ tor_assert(we_are_hibernating() == 0);
+
+ accounting_add_bytes(0, 90, 1);
+ fake_time += 1;
+ consider_hibernation(fake_time);
+ tor_assert(we_are_hibernating() == 1);
+
+ options->AccountingMax = 300;
+ options->AccountingRule = ACCT_IN;
+
+ accounting_add_bytes(10, 100, 1);
+ fake_time += 1;
+ consider_hibernation(fake_time);
+ tor_assert(we_are_hibernating() == 0);
+
+ accounting_add_bytes(90, 0, 1);
+ fake_time += 1;
+ consider_hibernation(fake_time);
+ tor_assert(we_are_hibernating() == 1);
+
goto done;
done:
NS_UNMOCK(get_or_state);
diff --git a/src/test/test_addr.c b/src/test/test_addr.c
index 2c25c1ef7d..85130db4fe 100644
--- a/src/test/test_addr.c
+++ b/src/test/test_addr.c
@@ -302,6 +302,7 @@ test_addr_ip6_helpers(void *arg)
//test_ntop6_reduces("0:0:0:0:0:0:c0a8:0101", "::192.168.1.1");
test_ntop6_reduces("0:0:0:0:0:ffff:c0a8:0101", "::ffff:192.168.1.1");
+ test_ntop6_reduces("0:0:0:0:0:0:c0a8:0101", "::192.168.1.1");
test_ntop6_reduces("002:0:0000:0:3::4", "2::3:0:0:4");
test_ntop6_reduces("0:0::1:0:3", "::1:0:3");
test_ntop6_reduces("008:0::0", "8::");
diff --git a/src/test/test_address.c b/src/test/test_address.c
index 4cf3a5a3a6..748dfc5a68 100644
--- a/src/test/test_address.c
+++ b/src/test/test_address.c
@@ -119,6 +119,21 @@ smartlist_contains_internal_tor_addr(smartlist_t *smartlist)
}
/** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
+ * that is NULL or the null tor_addr_t. Otherwise, return 0.
+ */
+static int
+smartlist_contains_null_tor_addr(smartlist_t *smartlist)
+{
+ SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) {
+ if (tor_addr == NULL || tor_addr_is_null(tor_addr)) {
+ return 1;
+ }
+ } SMARTLIST_FOREACH_END(tor_addr);
+
+ return 0;
+}
+
+/** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
* that is an IPv4 address. Otherwise, return 0.
*/
static int
@@ -205,7 +220,7 @@ test_address_ifaddrs_to_smartlist(void *arg)
ifa_ipv6->ifa_dstaddr = NULL;
ifa_ipv6->ifa_data = NULL;
- smartlist = ifaddrs_to_smartlist(ifa);
+ smartlist = ifaddrs_to_smartlist(ifa, AF_UNSPEC);
tt_assert(smartlist);
tt_assert(smartlist_len(smartlist) == 3);
@@ -266,15 +281,25 @@ test_address_get_if_addrs_ifaddrs(void *arg)
(void)arg;
- results = get_interface_addresses_ifaddrs(LOG_ERR);
+ results = get_interface_addresses_ifaddrs(LOG_ERR, AF_UNSPEC);
+
+ tt_assert(results);
+ /* Some FreeBSD jails don't have localhost IP address. Instead, they only
+ * have the address assigned to the jail (whatever that may be).
+ * And a jail without a network connection might not have any addresses at
+ * all. */
+ tt_assert(!smartlist_contains_null_tor_addr(results));
+
+ /* If there are addresses, they must be IPv4 or IPv6 */
+ if (smartlist_len(results) > 0) {
+ tt_assert(smartlist_contains_ipv4_tor_addr(results)
+ || smartlist_contains_ipv6_tor_addr(results));
+ }
- tt_int_op(smartlist_len(results),>=,1);
-#ifndef __FreeBSD__
- /* FreeBSD doesn't have a localhost in jails sometimes. */
- tt_assert(smartlist_contains_localhost_tor_addr(results));
-#endif
done:
- SMARTLIST_FOREACH(results, tor_addr_t *, t, tor_free(t));
+ if (results) {
+ SMARTLIST_FOREACH(results, tor_addr_t *, t, tor_free(t));
+ }
smartlist_free(results);
return;
}
@@ -291,10 +316,17 @@ test_address_get_if_addrs_win32(void *arg)
(void)arg;
- results = get_interface_addresses_win32(LOG_ERR);
+ results = get_interface_addresses_win32(LOG_ERR, AF_UNSPEC);
tt_int_op(smartlist_len(results),>=,1);
tt_assert(smartlist_contains_localhost_tor_addr(results));
+ tt_assert(!smartlist_contains_null_tor_addr(results));
+
+ /* If there are addresses, they must be IPv4 or IPv6 */
+ if (smartlist_len(results) > 0) {
+ tt_assert(smartlist_contains_ipv4_tor_addr(results)
+ || smartlist_contains_ipv6_tor_addr(results));
+ }
done:
SMARTLIST_FOREACH(results, tor_addr_t *, t, tor_free(t));
@@ -481,16 +513,28 @@ test_address_get_if_addrs_ioctl(void *arg)
(void)arg;
- result = get_interface_addresses_ioctl(LOG_ERR);
+ result = get_interface_addresses_ioctl(LOG_ERR, AF_INET);
+ /* On an IPv6-only system, this will fail and return NULL
tt_assert(result);
- tt_int_op(smartlist_len(result),>=,1);
+ */
-#ifndef __FreeBSD__
- /* FreeBSD doesn't have a localhost in jails sometimes. */
- tt_assert(smartlist_contains_localhost_tor_addr(result));
-#endif
- done:
+ /* Some FreeBSD jails don't have localhost IP address. Instead, they only
+ * have the address assigned to the jail (whatever that may be).
+ * And a jail without a network connection might not have any addresses at
+ * all. */
+ if (result) {
+ tt_assert(!smartlist_contains_null_tor_addr(result));
+
+ /* If there are addresses, they must be IPv4 or IPv6.
+ * (AIX supports IPv6 from SIOCGIFCONF.) */
+ if (smartlist_len(result) > 0) {
+ tt_assert(smartlist_contains_ipv4_tor_addr(result)
+ || smartlist_contains_ipv6_tor_addr(result));
+ }
+ }
+
+ done:
if (result) {
SMARTLIST_FOREACH(result, tor_addr_t *, t, tor_free(t));
smartlist_free(result);
@@ -700,12 +744,13 @@ test_address_get_if_addrs_list_internal(void *arg)
tt_assert(!smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_multicast_tor_addr(results));
/* The list may or may not contain internal addresses */
+ tt_assert(!smartlist_contains_null_tor_addr(results));
- /* Allow unit tests to pass on IPv6-only machines */
+ /* if there are any addresses, they must be IPv4 */
if (smartlist_len(results) > 0) {
- tt_assert(smartlist_contains_ipv4_tor_addr(results)
- || smartlist_contains_ipv6_tor_addr(results));
+ tt_assert(smartlist_contains_ipv4_tor_addr(results));
}
+ tt_assert(!smartlist_contains_ipv6_tor_addr(results));
done:
free_interface_address_list(results);
@@ -728,6 +773,7 @@ test_address_get_if_addrs_list_no_internal(void *arg)
tt_assert(!smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_multicast_tor_addr(results));
tt_assert(!smartlist_contains_internal_tor_addr(results));
+ tt_assert(!smartlist_contains_null_tor_addr(results));
/* if there are any addresses, they must be IPv4 */
if (smartlist_len(results) > 0) {
@@ -756,6 +802,7 @@ test_address_get_if_addrs6_list_internal(void *arg)
tt_assert(!smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_multicast_tor_addr(results));
/* The list may or may not contain internal addresses */
+ tt_assert(!smartlist_contains_null_tor_addr(results));
/* if there are any addresses, they must be IPv6 */
tt_assert(!smartlist_contains_ipv4_tor_addr(results));
@@ -784,7 +831,9 @@ test_address_get_if_addrs6_list_no_internal(void *arg)
tt_assert(!smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_multicast_tor_addr(results));
tt_assert(!smartlist_contains_internal_tor_addr(results));
+ tt_assert(!smartlist_contains_null_tor_addr(results));
+ /* if there are any addresses, they must be IPv6 */
tt_assert(!smartlist_contains_ipv4_tor_addr(results));
if (smartlist_len(results) > 0) {
tt_assert(smartlist_contains_ipv6_tor_addr(results));
@@ -798,9 +847,10 @@ test_address_get_if_addrs6_list_no_internal(void *arg)
static int called_get_interface_addresses_raw = 0;
static smartlist_t *
-mock_get_interface_addresses_raw_fail(int severity)
+mock_get_interface_addresses_raw_fail(int severity, sa_family_t family)
{
(void)severity;
+ (void)family;
called_get_interface_addresses_raw++;
return smartlist_new();
@@ -852,7 +902,7 @@ test_address_get_if_addrs_internal_fail(void *arg)
rv = get_interface_address(LOG_ERR, &ipv4h_addr);
tt_assert(rv == -1);
-done:
+ done:
UNMOCK(get_interface_addresses_raw);
UNMOCK(get_interface_address6_via_udp_socket_hack);
free_interface_address6_list(results1);
@@ -880,7 +930,7 @@ test_address_get_if_addrs_no_internal_fail(void *arg)
tt_assert(results2 != NULL);
tt_int_op(smartlist_len(results2),==,0);
-done:
+ done:
UNMOCK(get_interface_addresses_raw);
UNMOCK(get_interface_address6_via_udp_socket_hack);
free_interface_address6_list(results1);
@@ -939,6 +989,118 @@ test_address_get_if_addrs6(void *arg)
return;
}
+static void
+test_address_tor_addr_to_in6(void *ignored)
+{
+ (void)ignored;
+ tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t));
+ const struct in6_addr *res;
+ uint8_t expected[16] = {42, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15};
+
+ a->family = AF_INET;
+ res = tor_addr_to_in6(a);
+ tt_assert(!res);
+
+ a->family = AF_INET6;
+ memcpy(a->addr.in6_addr.s6_addr, expected, 16);
+ res = tor_addr_to_in6(a);
+ tt_assert(res);
+ tt_mem_op(res->s6_addr, OP_EQ, expected, 16);
+
+ done:
+ tor_free(a);
+}
+
+static void
+test_address_tor_addr_to_in(void *ignored)
+{
+ (void)ignored;
+ tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t));
+ const struct in_addr *res;
+
+ a->family = AF_INET6;
+ res = tor_addr_to_in(a);
+ tt_assert(!res);
+
+ a->family = AF_INET;
+ a->addr.in_addr.s_addr = 44;
+ res = tor_addr_to_in(a);
+ tt_assert(res);
+ tt_int_op(res->s_addr, OP_EQ, 44);
+
+ done:
+ tor_free(a);
+}
+
+static void
+test_address_tor_addr_to_ipv4n(void *ignored)
+{
+ (void)ignored;
+ tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t));
+ uint32_t res;
+
+ a->family = AF_INET6;
+ res = tor_addr_to_ipv4n(a);
+ tt_assert(!res);
+
+ a->family = AF_INET;
+ a->addr.in_addr.s_addr = 43;
+ res = tor_addr_to_ipv4n(a);
+ tt_assert(res);
+ tt_int_op(res, OP_EQ, 43);
+
+ done:
+ tor_free(a);
+}
+
+static void
+test_address_tor_addr_to_mapped_ipv4h(void *ignored)
+{
+ (void)ignored;
+ tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t));
+ uint32_t res;
+ uint8_t toset[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 42};
+
+ a->family = AF_INET;
+ res = tor_addr_to_mapped_ipv4h(a);
+ tt_assert(!res);
+
+ a->family = AF_INET6;
+
+ memcpy(a->addr.in6_addr.s6_addr, toset, 16);
+ res = tor_addr_to_mapped_ipv4h(a);
+ tt_assert(res);
+ tt_int_op(res, OP_EQ, 42);
+
+ done:
+ tor_free(a);
+}
+
+static void
+test_address_tor_addr_eq_ipv4h(void *ignored)
+{
+ (void)ignored;
+ tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t));
+ int res;
+
+ a->family = AF_INET6;
+ res = tor_addr_eq_ipv4h(a, 42);
+ tt_assert(!res);
+
+ a->family = AF_INET;
+ a->addr.in_addr.s_addr = 52;
+ res = tor_addr_eq_ipv4h(a, 42);
+ tt_assert(!res);
+
+ a->addr.in_addr.s_addr = 52;
+ res = tor_addr_eq_ipv4h(a, ntohl(52));
+ tt_assert(res);
+
+ done:
+ tor_free(a);
+}
+
#define ADDRESS_TEST(name, flags) \
{ #name, test_address_ ## name, flags, NULL, NULL }
@@ -965,6 +1127,11 @@ struct testcase_t address_tests[] = {
ADDRESS_TEST(get_if_addrs_ioctl, TT_FORK),
ADDRESS_TEST(ifreq_to_smartlist, 0),
#endif
+ ADDRESS_TEST(tor_addr_to_in6, 0),
+ ADDRESS_TEST(tor_addr_to_in, 0),
+ ADDRESS_TEST(tor_addr_to_ipv4n, 0),
+ ADDRESS_TEST(tor_addr_to_mapped_ipv4h, 0),
+ ADDRESS_TEST(tor_addr_eq_ipv4h, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_bt_cl.c b/src/test/test_bt_cl.c
index dabaee6e0a..c43143ffe9 100644
--- a/src/test/test_bt_cl.c
+++ b/src/test/test_bt_cl.c
@@ -119,6 +119,7 @@ main(int argc, char **argv)
printf("%d\n", we_weave(2));
clean_up_backtrace_handler();
+ logs_free_all();
return 0;
}
diff --git a/src/test/test_channeltls.c b/src/test/test_channeltls.c
index 016e504ab3..dff1dde87e 100644
--- a/src/test/test_channeltls.c
+++ b/src/test/test_channeltls.c
@@ -123,7 +123,7 @@ test_channeltls_num_bytes_queued(void *arg)
/*
* Next, we have to test ch->num_bytes_queued, which is
* channel_tls_num_bytes_queued_method. We can't mock
- * connection_get_outbuf_len() directly because it's static INLINE
+ * connection_get_outbuf_len() directly because it's static inline
* in connection.h, but we can mock buf_datalen(). Note that
* if bufferevents ever work, this will break with them enabled.
*/
diff --git a/src/test/test_circuitmux.c b/src/test/test_circuitmux.c
index 6d93731eea..5c72fc656d 100644
--- a/src/test/test_circuitmux.c
+++ b/src/test/test_circuitmux.c
@@ -36,11 +36,7 @@ test_cmux_destroy_cell_queue(void *arg)
circuit_t *circ = NULL;
cell_queue_t *cq = NULL;
packed_cell_t *pc = NULL;
- tor_libevent_cfg cfg;
- memset(&cfg, 0, sizeof(cfg));
-
- tor_libevent_initialize(&cfg);
scheduler_init();
(void) arg;
diff --git a/src/test/test_compat_libevent.c b/src/test/test_compat_libevent.c
new file mode 100644
index 0000000000..f3fe113012
--- /dev/null
+++ b/src/test/test_compat_libevent.c
@@ -0,0 +1,224 @@
+/* Copyright (c) 2010-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define COMPAT_LIBEVENT_PRIVATE
+#include "orconfig.h"
+#include "or.h"
+
+#include "test.h"
+
+#include "compat_libevent.h"
+
+#ifdef HAVE_EVENT2_EVENT_H
+#include <event2/event.h>
+#include <event2/thread.h>
+#ifdef USE_BUFFEREVENTS
+#include <event2/bufferevent.h>
+#endif
+#else
+#include <event.h>
+#endif
+
+#include "log_test_helpers.h"
+
+#define NS_MODULE compat_libevent
+
+static void
+test_compat_libevent_logging_callback(void *ignored)
+{
+ (void)ignored;
+ int previous_log = setup_capture_of_logs(LOG_DEBUG);
+
+ libevent_logging_callback(_EVENT_LOG_DEBUG, "hello world");
+ expect_log_msg("Message from libevent: hello world\n");
+ expect_log_severity(LOG_DEBUG);
+
+ mock_clean_saved_logs();
+ libevent_logging_callback(_EVENT_LOG_MSG, "hello world another time");
+ expect_log_msg("Message from libevent: hello world another time\n");
+ expect_log_severity(LOG_INFO);
+
+ mock_clean_saved_logs();
+ libevent_logging_callback(_EVENT_LOG_WARN, "hello world a third time");
+ expect_log_msg("Warning from libevent: hello world a third time\n");
+ expect_log_severity(LOG_WARN);
+
+ mock_clean_saved_logs();
+ libevent_logging_callback(_EVENT_LOG_ERR, "hello world a fourth time");
+ expect_log_msg("Error from libevent: hello world a fourth time\n");
+ expect_log_severity(LOG_ERR);
+
+ mock_clean_saved_logs();
+ libevent_logging_callback(42, "hello world a fifth time");
+ expect_log_msg("Message [42] from libevent: hello world a fifth time\n");
+ expect_log_severity(LOG_WARN);
+
+ mock_clean_saved_logs();
+ libevent_logging_callback(_EVENT_LOG_DEBUG,
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ );
+ expect_log_msg("Message from libevent: "
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789"
+ "012345678901234567890123456789\n");
+ expect_log_severity(LOG_DEBUG);
+
+ mock_clean_saved_logs();
+ libevent_logging_callback(42, "xxx\n");
+ expect_log_msg("Message [42] from libevent: xxx\n");
+ expect_log_severity(LOG_WARN);
+
+ suppress_libevent_log_msg("something");
+ mock_clean_saved_logs();
+ libevent_logging_callback(_EVENT_LOG_MSG, "hello there");
+ expect_log_msg("Message from libevent: hello there\n");
+ expect_log_severity(LOG_INFO);
+
+ mock_clean_saved_logs();
+ libevent_logging_callback(_EVENT_LOG_MSG, "hello there something else");
+ expect_no_log_msg("hello there something else");
+
+ // No way of verifying the result of this, it seems =/
+ configure_libevent_logging();
+
+ done:
+ suppress_libevent_log_msg(NULL);
+ teardown_capture_of_logs(previous_log);
+}
+
+static void
+test_compat_libevent_le_versions_compatibility(void *ignored)
+{
+ (void)ignored;
+ int res;
+
+ res = le_versions_compatibility(LE_OTHER);
+ tt_int_op(res, OP_EQ, 0);
+
+ res = le_versions_compatibility(V_OLD(0,9,'c'));
+ tt_int_op(res, OP_EQ, 1);
+
+ res = le_versions_compatibility(V(1,3,98));
+ tt_int_op(res, OP_EQ, 2);
+
+ res = le_versions_compatibility(V(1,4,98));
+ tt_int_op(res, OP_EQ, 3);
+
+ res = le_versions_compatibility(V(1,5,0));
+ tt_int_op(res, OP_EQ, 4);
+
+ res = le_versions_compatibility(V(2,0,0));
+ tt_int_op(res, OP_EQ, 4);
+
+ res = le_versions_compatibility(V(2,0,2));
+ tt_int_op(res, OP_EQ, 5);
+
+ done:
+ (void)0;
+}
+
+static void
+test_compat_libevent_tor_decode_libevent_version(void *ignored)
+{
+ (void)ignored;
+ le_version_t res;
+
+ res = tor_decode_libevent_version("SOMETHING WRONG");
+ tt_int_op(res, OP_EQ, LE_OTHER);
+
+ res = tor_decode_libevent_version("1.4.11");
+ tt_int_op(res, OP_EQ, V(1,4,11));
+
+ res = tor_decode_libevent_version("1.4.12b-stable");
+ tt_int_op(res, OP_EQ, V(1,4,12));
+
+ res = tor_decode_libevent_version("1.4.17b_stable");
+ tt_int_op(res, OP_EQ, V(1,4,17));
+
+ res = tor_decode_libevent_version("1.4.12!stable");
+ tt_int_op(res, OP_EQ, LE_OTHER);
+
+ res = tor_decode_libevent_version("1.4.12b!stable");
+ tt_int_op(res, OP_EQ, LE_OTHER);
+
+ res = tor_decode_libevent_version("1.4.13-");
+ tt_int_op(res, OP_EQ, V(1,4,13));
+
+ res = tor_decode_libevent_version("1.4.14_");
+ tt_int_op(res, OP_EQ, V(1,4,14));
+
+ res = tor_decode_libevent_version("1.4.15c-");
+ tt_int_op(res, OP_EQ, V(1,4,15));
+
+ res = tor_decode_libevent_version("1.4.16c_");
+ tt_int_op(res, OP_EQ, V(1,4,16));
+
+ res = tor_decode_libevent_version("1.4.17-s");
+ tt_int_op(res, OP_EQ, V(1,4,17));
+
+ res = tor_decode_libevent_version("1.5");
+ tt_int_op(res, OP_EQ, V(1,5,0));
+
+ res = tor_decode_libevent_version("1.2");
+ tt_int_op(res, OP_EQ, V(1,2,0));
+
+ res = tor_decode_libevent_version("1.2-");
+ tt_int_op(res, OP_EQ, LE_OTHER);
+
+ res = tor_decode_libevent_version("1.6e");
+ tt_int_op(res, OP_EQ, V_OLD(1,6,'e'));
+
+ done:
+ (void)0;
+}
+
+#if defined(LIBEVENT_VERSION)
+#define HEADER_VERSION LIBEVENT_VERSION
+#elif defined(_EVENT_VERSION)
+#define HEADER_VERSION _EVENT_VERSION
+#endif
+
+static void
+test_compat_libevent_header_version(void *ignored)
+{
+ (void)ignored;
+ const char *res;
+
+ res = tor_libevent_get_header_version_str();
+ tt_str_op(res, OP_EQ, HEADER_VERSION);
+
+ done:
+ (void)0;
+}
+
+struct testcase_t compat_libevent_tests[] = {
+ { "logging_callback", test_compat_libevent_logging_callback,
+ TT_FORK, NULL, NULL },
+ { "le_versions_compatibility",
+ test_compat_libevent_le_versions_compatibility, 0, NULL, NULL },
+ { "tor_decode_libevent_version",
+ test_compat_libevent_tor_decode_libevent_version, 0, NULL, NULL },
+ { "header_version", test_compat_libevent_header_version, 0, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_config.c b/src/test/test_config.c
index 28e9fa0f32..6be503abc5 100644
--- a/src/test/test_config.c
+++ b/src/test/test_config.c
@@ -7,17 +7,43 @@
#define CONFIG_PRIVATE
#define PT_PRIVATE
+#define ROUTERSET_PRIVATE
#include "or.h"
+#include "address.h"
#include "addressmap.h"
+#include "circuitmux_ewma.h"
+#include "circuitbuild.h"
#include "config.h"
#include "confparse.h"
+#include "connection.h"
#include "connection_edge.h"
#include "test.h"
#include "util.h"
#include "address.h"
+#include "connection_or.h"
+#include "control.h"
+#include "cpuworker.h"
+#include "dirserv.h"
+#include "dirvote.h"
+#include "dns.h"
#include "entrynodes.h"
#include "transports.h"
+#include "ext_orport.h"
+#include "geoip.h"
+#include "hibernate.h"
+#include "main.h"
+#include "networkstatus.h"
+#include "nodelist.h"
+#include "policies.h"
+#include "rendclient.h"
+#include "rendservice.h"
+#include "router.h"
#include "routerlist.h"
+#include "routerset.h"
+#include "statefile.h"
+#include "test.h"
+#include "transports.h"
+#include "util.h"
static void
test_config_addressmap(void *arg)
@@ -1444,6 +1470,176 @@ test_config_resolve_my_address(void *arg)
UNMOCK(tor_gethostname);
}
+static void
+test_config_adding_trusted_dir_server(void *arg)
+{
+ (void)arg;
+
+ const char digest[DIGEST_LEN] = "";
+ dir_server_t *ds = NULL;
+ tor_addr_port_t ipv6;
+ int rv = -1;
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ /* create a trusted ds without an IPv6 address and port */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest,
+ NULL, V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+ tt_assert(get_n_authorities(V3_DIRINFO) == 1);
+ tt_assert(smartlist_len(router_get_fallback_dir_servers()) == 1);
+
+ /* create a trusted ds with an IPv6 address and port */
+ rv = tor_addr_port_parse(LOG_WARN, "[::1]:9061", &ipv6.addr, &ipv6.port, -1);
+ tt_assert(rv == 0);
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, &ipv6, digest,
+ NULL, V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+ tt_assert(get_n_authorities(V3_DIRINFO) == 2);
+ tt_assert(smartlist_len(router_get_fallback_dir_servers()) == 2);
+
+ done:
+ clear_dir_servers();
+ routerlist_free_all();
+}
+
+static void
+test_config_adding_fallback_dir_server(void *arg)
+{
+ (void)arg;
+
+ const char digest[DIGEST_LEN] = "";
+ dir_server_t *ds = NULL;
+ tor_addr_t ipv4;
+ tor_addr_port_t ipv6;
+ int rv = -1;
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ rv = tor_addr_parse(&ipv4, "127.0.0.1");
+ tt_assert(rv == AF_INET);
+
+ /* create a trusted ds without an IPv6 address and port */
+ ds = fallback_dir_server_new(&ipv4, 9059, 9060, NULL, digest, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+ tt_assert(smartlist_len(router_get_fallback_dir_servers()) == 1);
+
+ /* create a trusted ds with an IPv6 address and port */
+ rv = tor_addr_port_parse(LOG_WARN, "[::1]:9061", &ipv6.addr, &ipv6.port, -1);
+ tt_assert(rv == 0);
+ ds = fallback_dir_server_new(&ipv4, 9059, 9060, &ipv6, digest, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+ tt_assert(smartlist_len(router_get_fallback_dir_servers()) == 2);
+
+ done:
+ clear_dir_servers();
+ routerlist_free_all();
+}
+
+/* No secrets here:
+ * v3ident is `echo "onion" | shasum | cut -d" " -f1 | tr "a-f" "A-F"`
+ * fingerprint is `echo "unionem" | shasum | cut -d" " -f1 | tr "a-f" "A-F"`
+ * with added spaces
+ */
+#define TEST_DIR_AUTH_LINE_START \
+ "foobar orport=12345 " \
+ "v3ident=14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 "
+#define TEST_DIR_AUTH_LINE_END \
+ "1.2.3.4:54321 " \
+ "FDB2 FBD2 AAA5 25FA 2999 E617 5091 5A32 C777 3B17"
+#define TEST_DIR_AUTH_IPV6_FLAG \
+ "ipv6=[feed::beef]:9 "
+
+static void
+test_config_parsing_trusted_dir_server(void *arg)
+{
+ (void)arg;
+ int rv = -1;
+
+ /* parse a trusted dir server without an IPv6 address and port */
+ rv = parse_dir_authority_line(TEST_DIR_AUTH_LINE_START
+ TEST_DIR_AUTH_LINE_END,
+ V3_DIRINFO, 1);
+ tt_assert(rv == 0);
+
+ /* parse a trusted dir server with an IPv6 address and port */
+ rv = parse_dir_authority_line(TEST_DIR_AUTH_LINE_START
+ TEST_DIR_AUTH_IPV6_FLAG
+ TEST_DIR_AUTH_LINE_END,
+ V3_DIRINFO, 1);
+ tt_assert(rv == 0);
+
+ /* Since we are only validating, there is no cleanup. */
+ done:
+ ;
+}
+
+#undef TEST_DIR_AUTH_LINE_START
+#undef TEST_DIR_AUTH_LINE_END
+#undef TEST_DIR_AUTH_IPV6_FLAG
+
+/* No secrets here:
+ * id is `echo "syn-propanethial-S-oxide" | shasum | cut -d" " -f1`
+ */
+#define TEST_DIR_FALLBACK_LINE \
+ "1.2.3.4:54321 orport=12345 " \
+ "id=50e643986f31ea1235bcc1af17a1c5c5cfc0ee54 "
+#define TEST_DIR_FALLBACK_IPV6_FLAG \
+ "ipv6=[2015:c0de::deed]:9"
+
+static void
+test_config_parsing_fallback_dir_server(void *arg)
+{
+ (void)arg;
+ int rv = -1;
+
+ /* parse a trusted dir server without an IPv6 address and port */
+ rv = parse_dir_fallback_line(TEST_DIR_FALLBACK_LINE, 1);
+ tt_assert(rv == 0);
+
+ /* parse a trusted dir server with an IPv6 address and port */
+ rv = parse_dir_fallback_line(TEST_DIR_FALLBACK_LINE
+ TEST_DIR_FALLBACK_IPV6_FLAG,
+ 1);
+ tt_assert(rv == 0);
+
+ /* Since we are only validating, there is no cleanup. */
+ done:
+ ;
+}
+
+#undef TEST_DIR_FALLBACK_LINE
+#undef TEST_DIR_FALLBACK_IPV6_FLAG
+
+static void
+test_config_adding_default_trusted_dir_servers(void *arg)
+{
+ (void)arg;
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ /* Assume we only have one bridge authority */
+ add_default_trusted_dir_authorities(BRIDGE_DIRINFO);
+ tt_assert(get_n_authorities(BRIDGE_DIRINFO) == 1);
+ tt_assert(smartlist_len(router_get_fallback_dir_servers()) == 1);
+
+ /* Assume we have nine V3 authorities */
+ add_default_trusted_dir_authorities(V3_DIRINFO);
+ tt_assert(get_n_authorities(V3_DIRINFO) == 9);
+ tt_assert(smartlist_len(router_get_fallback_dir_servers()) == 10);
+
+ done:
+ clear_dir_servers();
+ routerlist_free_all();
+}
+
static int n_add_default_fallback_dir_servers_known_default = 0;
/**
@@ -1471,13 +1667,14 @@ add_default_fallback_dir_servers_known_default(void)
n_add_default_fallback_dir_servers_known_default++;
}
+/* Test all the different combinations of adding dir servers */
static void
test_config_adding_dir_servers(void *arg)
{
(void)arg;
/* allocate options */
- or_options_t *options = tor_malloc(sizeof(or_options_t));
+ or_options_t *options = tor_malloc_zero(sizeof(or_options_t));
/* Allocate and populate configuration lines:
*
@@ -1486,8 +1683,7 @@ test_config_adding_dir_servers(void *arg)
* Zeroing the structure has the same effect as initialising to:
* { NULL, NULL, NULL, CONFIG_LINE_NORMAL, 0};
*/
- config_line_t *test_dir_authority = tor_malloc(sizeof(config_line_t));
- memset(test_dir_authority, 0, sizeof(config_line_t));
+ config_line_t *test_dir_authority = tor_malloc_zero(sizeof(config_line_t));
test_dir_authority->key = tor_strdup("DirAuthority");
test_dir_authority->value = tor_strdup(
"D0 orport=9000 "
@@ -1495,16 +1691,16 @@ test_config_adding_dir_servers(void *arg)
"127.0.0.1:60090 0123 4567 8901 2345 6789 0123 4567 8901 2345 6789"
);
- config_line_t *test_alt_bridge_authority = tor_malloc(sizeof(config_line_t));
- memset(test_alt_bridge_authority, 0, sizeof(config_line_t));
+ config_line_t *test_alt_bridge_authority = tor_malloc_zero(
+ sizeof(config_line_t));
test_alt_bridge_authority->key = tor_strdup("AlternateBridgeAuthority");
test_alt_bridge_authority->value = tor_strdup(
"B1 orport=9001 bridge "
"127.0.0.1:60091 1123 4567 8901 2345 6789 0123 4567 8901 2345 6789"
);
- config_line_t *test_alt_dir_authority = tor_malloc(sizeof(config_line_t));
- memset(test_alt_dir_authority, 0, sizeof(config_line_t));
+ config_line_t *test_alt_dir_authority = tor_malloc_zero(
+ sizeof(config_line_t));
test_alt_dir_authority->key = tor_strdup("AlternateDirAuthority");
test_alt_dir_authority->value = tor_strdup(
"A2 orport=9002 "
@@ -1513,23 +1709,23 @@ test_config_adding_dir_servers(void *arg)
);
/* Use the format specified in the manual page */
- config_line_t *test_fallback_directory = tor_malloc(sizeof(config_line_t));
- memset(test_fallback_directory, 0, sizeof(config_line_t));
+ config_line_t *test_fallback_directory = tor_malloc_zero(
+ sizeof(config_line_t));
test_fallback_directory->key = tor_strdup("FallbackDir");
test_fallback_directory->value = tor_strdup(
"127.0.0.1:60093 orport=9003 id=0323456789012345678901234567890123456789"
);
/* We need to know if add_default_fallback_dir_servers is called,
+ * whatever the size of the list in fallback_dirs.inc,
* so we use a version of add_default_fallback_dir_servers that adds
- * one known default fallback directory.
- * There doesn't appear to be any need to test it unmocked. */
+ * one known default fallback directory. */
MOCK(add_default_fallback_dir_servers,
add_default_fallback_dir_servers_known_default);
/* There are 16 different cases, covering each combination of set/NULL for:
* DirAuthorities, AlternateBridgeAuthority, AlternateDirAuthority &
- * FallbackDir.
+ * FallbackDir. (We always set UseDefaultFallbackDirs to 1.)
* But validate_dir_servers() ensures that:
* "You cannot set both DirAuthority and Alternate*Authority."
* This reduces the number of cases to 10.
@@ -1543,8 +1739,6 @@ test_config_adding_dir_servers(void *arg)
* The valid cases are cases 0-9 counting using this method, as every case
* greater than or equal to 10 = 1010 is invalid.
*
- * After #15642 - Disable default fallback dirs when any custom dirs set
- *
* 1. Outcome: Use Set Directory Authorities
* - No Default Authorities
* - Use AlternateBridgeAuthority, AlternateDirAuthority, and FallbackDir
@@ -1581,20 +1775,6 @@ test_config_adding_dir_servers(void *arg)
* Cases expected to yield this outcome:
* 0 (DirAuthorities, AlternateBridgeAuthority, AlternateDirAuthority
* and FallbackDir are all NULL)
- *
- * Before #15642 but after #13163 - Stop using default authorities when both
- * Alternate Dir and Bridge Authority are set
- * (#13163 was committed in 0.2.6 as c1dd43d823c7)
- *
- * The behaviour is different in the following cases
- * where FallbackDir is NULL:
- * 2, 6, 8
- *
- * In these cases, the Default Fallback Directories are applied, even when
- * DirAuthorities or AlternateDirAuthority are set.
- *
- * However, as the list of default fallback directories is currently empty,
- * this change doesn't modify any user-visible behaviour.
*/
/*
@@ -1628,6 +1808,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = NULL;
options->AlternateDirAuthority = NULL;
options->FallbackDir = NULL;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -1637,6 +1818,9 @@ test_config_adding_dir_servers(void *arg)
/* we must have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 1);
+ /* we have more fallbacks than just the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
+
{
/* fallback_dir_servers */
const smartlist_t *fallback_servers = router_get_fallback_dir_servers();
@@ -1669,7 +1853,10 @@ test_config_adding_dir_servers(void *arg)
n_default_fallback_dir = (smartlist_len(fallback_servers) -
n_default_alt_bridge_authority -
n_default_alt_dir_authority);
- /* If we have a negative count, something has gone really wrong */
+ /* If we have a negative count, something has gone really wrong,
+ * or some authorities aren't being added as fallback directories.
+ * (networkstatus_consensus_can_use_extra_fallbacks depends on all
+ * authorities being fallback directories.) */
tt_assert(n_default_fallback_dir >= 0);
}
}
@@ -1703,6 +1890,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = NULL;
options->AlternateDirAuthority = NULL;
options->FallbackDir = test_fallback_directory;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -1712,6 +1900,9 @@ test_config_adding_dir_servers(void *arg)
/* we must not have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ /* we have more fallbacks than just the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
+
{
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
@@ -1840,6 +2031,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = NULL;
options->AlternateDirAuthority = NULL;
options->FallbackDir = NULL;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -1849,6 +2041,9 @@ test_config_adding_dir_servers(void *arg)
/* we must not have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ /* we just have the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 0);
+
{
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
@@ -1977,6 +2172,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = test_alt_bridge_authority;
options->AlternateDirAuthority = test_alt_dir_authority;
options->FallbackDir = test_fallback_directory;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -1986,6 +2182,9 @@ test_config_adding_dir_servers(void *arg)
/* we must not have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ /* we have more fallbacks than just the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
+
{
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
@@ -2115,6 +2314,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = test_alt_bridge_authority;
options->AlternateDirAuthority = test_alt_dir_authority;
options->FallbackDir = NULL;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -2124,6 +2324,9 @@ test_config_adding_dir_servers(void *arg)
/* we must not have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ /* we have more fallbacks than just the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 0);
+
{
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
@@ -2263,6 +2466,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = test_alt_bridge_authority;
options->AlternateDirAuthority = NULL;
options->FallbackDir = test_fallback_directory;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -2272,6 +2476,9 @@ test_config_adding_dir_servers(void *arg)
/* we must not have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ /* we have more fallbacks than just the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
+
{
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
@@ -2413,6 +2620,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = test_alt_bridge_authority;
options->AlternateDirAuthority = NULL;
options->FallbackDir = NULL;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -2422,6 +2630,9 @@ test_config_adding_dir_servers(void *arg)
/* we must have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 1);
+ /* we have more fallbacks than just the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
+
{
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
@@ -2572,6 +2783,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = NULL;
options->AlternateDirAuthority = test_alt_dir_authority;
options->FallbackDir = test_fallback_directory;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -2581,6 +2793,9 @@ test_config_adding_dir_servers(void *arg)
/* we must not have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ /* we have more fallbacks than just the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
+
{
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
@@ -2725,6 +2940,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = NULL;
options->AlternateDirAuthority = test_alt_dir_authority;
options->FallbackDir = NULL;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -2734,6 +2950,9 @@ test_config_adding_dir_servers(void *arg)
/* we must not have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ /* we just have the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 0);
+
{
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
@@ -2887,6 +3106,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = NULL;
options->AlternateDirAuthority = NULL;
options->FallbackDir = test_fallback_directory;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -2896,6 +3116,9 @@ test_config_adding_dir_servers(void *arg)
/* we must not have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ /* we have more fallbacks than just the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
+
{
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
@@ -3046,6 +3269,7 @@ test_config_adding_dir_servers(void *arg)
options->AlternateBridgeAuthority = NULL;
options->AlternateDirAuthority = NULL;
options->FallbackDir = NULL;
+ options->UseDefaultFallbackDirs = 1;
/* parse options - ensure we always update by passing NULL old_options */
consider_adding_dir_servers(options, NULL);
@@ -3055,6 +3279,9 @@ test_config_adding_dir_servers(void *arg)
/* we must have added the default fallback dirs */
tt_assert(n_add_default_fallback_dir_servers_known_default == 1);
+ /* we have more fallbacks than just the authorities */
+ tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
+
{
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
@@ -3209,11 +3436,1130 @@ test_config_adding_dir_servers(void *arg)
UNMOCK(add_default_fallback_dir_servers);
}
+static void
+test_config_default_dir_servers(void *arg)
+{
+ or_options_t *opts = NULL;
+ (void)arg;
+ int trusted_count = 0;
+ int fallback_count = 0;
+
+ /* new set of options should stop fallback parsing */
+ opts = tor_malloc_zero(sizeof(or_options_t));
+ opts->UseDefaultFallbackDirs = 0;
+ /* set old_options to NULL to force dir update */
+ consider_adding_dir_servers(opts, NULL);
+ trusted_count = smartlist_len(router_get_trusted_dir_servers());
+ fallback_count = smartlist_len(router_get_fallback_dir_servers());
+ or_options_free(opts);
+ opts = NULL;
+
+ /* assume a release will never go out with less than 7 authorities */
+ tt_assert(trusted_count >= 7);
+ /* if we disable the default fallbacks, there must not be any extra */
+ tt_assert(fallback_count == trusted_count);
+
+ opts = tor_malloc_zero(sizeof(or_options_t));
+ opts->UseDefaultFallbackDirs = 1;
+ consider_adding_dir_servers(opts, opts);
+ trusted_count = smartlist_len(router_get_trusted_dir_servers());
+ fallback_count = smartlist_len(router_get_fallback_dir_servers());
+ or_options_free(opts);
+ opts = NULL;
+
+ /* assume a release will never go out with less than 7 authorities */
+ tt_assert(trusted_count >= 7);
+ /* XX/teor - allow for default fallbacks to be added without breaking
+ * the unit tests. Set a minimum fallback count once the list is stable. */
+ tt_assert(fallback_count >= trusted_count);
+
+ done:
+ or_options_free(opts);
+}
+
+static int mock_router_pick_published_address_result = 0;
+
+static int
+mock_router_pick_published_address(const or_options_t *options, uint32_t *addr)
+{
+ (void)options;
+ (void)addr;
+ return mock_router_pick_published_address_result;
+}
+
+static int mock_router_my_exit_policy_is_reject_star_result = 0;
+
+static int
+mock_router_my_exit_policy_is_reject_star(void)
+{
+ return mock_router_my_exit_policy_is_reject_star_result;
+}
+
+static int mock_advertised_server_mode_result = 0;
+
+static int
+mock_advertised_server_mode(void)
+{
+ return mock_advertised_server_mode_result;
+}
+
+static routerinfo_t *mock_router_get_my_routerinfo_result = NULL;
+
+static const routerinfo_t *
+mock_router_get_my_routerinfo(void)
+{
+ return mock_router_get_my_routerinfo_result;
+}
+
+static void
+test_config_directory_fetch(void *arg)
+{
+ (void)arg;
+
+ /* Test Setup */
+ or_options_t *options = tor_malloc_zero(sizeof(or_options_t));
+ routerinfo_t routerinfo;
+ memset(&routerinfo, 0, sizeof(routerinfo));
+ mock_router_pick_published_address_result = -1;
+ mock_router_my_exit_policy_is_reject_star_result = 1;
+ mock_advertised_server_mode_result = 0;
+ mock_router_get_my_routerinfo_result = NULL;
+ MOCK(router_pick_published_address, mock_router_pick_published_address);
+ MOCK(router_my_exit_policy_is_reject_star,
+ mock_router_my_exit_policy_is_reject_star);
+ MOCK(advertised_server_mode, mock_advertised_server_mode);
+ MOCK(router_get_my_routerinfo, mock_router_get_my_routerinfo);
+
+ /* Clients can use multiple directory mirrors for bootstrap */
+ memset(options, 0, sizeof(or_options_t));
+ options->ClientOnly = 1;
+ tt_assert(server_mode(options) == 0);
+ tt_assert(public_server_mode(options) == 0);
+ tt_assert(directory_fetches_from_authorities(options) == 0);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 1);
+
+ /* Bridge Clients can use multiple directory mirrors for bootstrap */
+ memset(options, 0, sizeof(or_options_t));
+ options->UseBridges = 1;
+ tt_assert(server_mode(options) == 0);
+ tt_assert(public_server_mode(options) == 0);
+ tt_assert(directory_fetches_from_authorities(options) == 0);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 1);
+
+ /* Bridge Relays (Bridges) must act like clients, and use multiple
+ * directory mirrors for bootstrap */
+ memset(options, 0, sizeof(or_options_t));
+ options->BridgeRelay = 1;
+ options->ORPort_set = 1;
+ tt_assert(server_mode(options) == 1);
+ tt_assert(public_server_mode(options) == 0);
+ tt_assert(directory_fetches_from_authorities(options) == 0);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 1);
+
+ /* Clients set to FetchDirInfoEarly must fetch it from the authorities,
+ * but can use multiple authorities for bootstrap */
+ memset(options, 0, sizeof(or_options_t));
+ options->FetchDirInfoEarly = 1;
+ tt_assert(server_mode(options) == 0);
+ tt_assert(public_server_mode(options) == 0);
+ tt_assert(directory_fetches_from_authorities(options) == 1);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 1);
+
+ /* OR servers only fetch the consensus from the authorities when they don't
+ * know their own address, but never use multiple directories for bootstrap
+ */
+ memset(options, 0, sizeof(or_options_t));
+ options->ORPort_set = 1;
+
+ mock_router_pick_published_address_result = -1;
+ tt_assert(server_mode(options) == 1);
+ tt_assert(public_server_mode(options) == 1);
+ tt_assert(directory_fetches_from_authorities(options) == 1);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 0);
+
+ mock_router_pick_published_address_result = 0;
+ tt_assert(server_mode(options) == 1);
+ tt_assert(public_server_mode(options) == 1);
+ tt_assert(directory_fetches_from_authorities(options) == 0);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 0);
+
+ /* Exit OR servers only fetch the consensus from the authorities when they
+ * refuse unknown exits, but never use multiple directories for bootstrap
+ */
+ memset(options, 0, sizeof(or_options_t));
+ options->ORPort_set = 1;
+ options->ExitRelay = 1;
+ mock_router_pick_published_address_result = 0;
+ mock_router_my_exit_policy_is_reject_star_result = 0;
+ mock_advertised_server_mode_result = 1;
+ mock_router_get_my_routerinfo_result = &routerinfo;
+
+ routerinfo.supports_tunnelled_dir_requests = 1;
+
+ options->RefuseUnknownExits = 1;
+ tt_assert(server_mode(options) == 1);
+ tt_assert(public_server_mode(options) == 1);
+ tt_assert(directory_fetches_from_authorities(options) == 1);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 0);
+
+ options->RefuseUnknownExits = 0;
+ mock_router_pick_published_address_result = 0;
+ tt_assert(server_mode(options) == 1);
+ tt_assert(public_server_mode(options) == 1);
+ tt_assert(directory_fetches_from_authorities(options) == 0);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 0);
+
+ /* Dir servers fetch the consensus from the authorities, unless they are not
+ * advertising themselves (hibernating) or have no routerinfo or are not
+ * advertising their dirport, and never use multiple directories for
+ * bootstrap. This only applies if they are also OR servers.
+ * (We don't care much about the behaviour of non-OR directory servers.) */
+ memset(options, 0, sizeof(or_options_t));
+ options->DirPort_set = 1;
+ options->ORPort_set = 1;
+ options->DirCache = 1;
+ mock_router_pick_published_address_result = 0;
+ mock_router_my_exit_policy_is_reject_star_result = 1;
+
+ mock_advertised_server_mode_result = 1;
+ routerinfo.dir_port = 1;
+ mock_router_get_my_routerinfo_result = &routerinfo;
+ tt_assert(server_mode(options) == 1);
+ tt_assert(public_server_mode(options) == 1);
+ tt_assert(directory_fetches_from_authorities(options) == 1);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 0);
+
+ mock_advertised_server_mode_result = 0;
+ routerinfo.dir_port = 1;
+ mock_router_get_my_routerinfo_result = &routerinfo;
+ tt_assert(server_mode(options) == 1);
+ tt_assert(public_server_mode(options) == 1);
+ tt_assert(directory_fetches_from_authorities(options) == 0);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 0);
+
+ mock_advertised_server_mode_result = 1;
+ mock_router_get_my_routerinfo_result = NULL;
+ tt_assert(server_mode(options) == 1);
+ tt_assert(public_server_mode(options) == 1);
+ tt_assert(directory_fetches_from_authorities(options) == 0);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 0);
+
+ mock_advertised_server_mode_result = 1;
+ routerinfo.dir_port = 0;
+ routerinfo.supports_tunnelled_dir_requests = 0;
+ mock_router_get_my_routerinfo_result = &routerinfo;
+ tt_assert(server_mode(options) == 1);
+ tt_assert(public_server_mode(options) == 1);
+ tt_assert(directory_fetches_from_authorities(options) == 0);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 0);
+
+ mock_advertised_server_mode_result = 1;
+ routerinfo.dir_port = 1;
+ routerinfo.supports_tunnelled_dir_requests = 1;
+ mock_router_get_my_routerinfo_result = &routerinfo;
+ tt_assert(server_mode(options) == 1);
+ tt_assert(public_server_mode(options) == 1);
+ tt_assert(directory_fetches_from_authorities(options) == 1);
+ tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
+ == 0);
+
+ done:
+ tor_free(options);
+ UNMOCK(router_pick_published_address);
+ UNMOCK(router_get_my_routerinfo);
+ UNMOCK(advertised_server_mode);
+ UNMOCK(router_my_exit_policy_is_reject_star);
+}
+
+static void
+test_config_default_fallback_dirs(void *arg)
+{
+ const char *fallback[] = {
+#include "../or/fallback_dirs.inc"
+ NULL
+ };
+
+ int n_included_fallback_dirs = 0;
+ int n_added_fallback_dirs = 0;
+
+ (void)arg;
+ clear_dir_servers();
+
+ while (fallback[n_included_fallback_dirs])
+ n_included_fallback_dirs++;
+
+ add_default_fallback_dir_servers();
+
+ n_added_fallback_dirs = smartlist_len(router_get_fallback_dir_servers());
+
+ tt_assert(n_included_fallback_dirs == n_added_fallback_dirs);
+
+ done:
+ clear_dir_servers();
+}
+
+static config_line_t *
+mock_config_line(const char *key, const char *val)
+{
+ config_line_t *config_line = tor_malloc(sizeof(config_line_t));
+ memset(config_line, 0, sizeof(config_line_t));
+ config_line->key = tor_strdup(key);
+ config_line->value = tor_strdup(val);
+ return config_line;
+}
+
+static void
+test_config_parse_port_config__listenaddress(void *data)
+{
+ (void)data;
+ int ret;
+ config_line_t *config_listen_address = NULL, *config_listen_address2 = NULL,
+ *config_listen_address3 = NULL;
+ config_line_t *config_port1 = NULL, *config_port2 = NULL,
+ *config_port3 = NULL, *config_port4 = NULL, *config_port5 = NULL;
+ smartlist_t *slout = NULL;
+ port_cfg_t *port_cfg = NULL;
+
+ // Test basic invocation with no arguments
+ ret = parse_port_config(NULL, NULL, NULL, NULL, 0, NULL, 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Setup some test data
+ config_listen_address = mock_config_line("DNSListenAddress", "127.0.0.1");
+ config_listen_address2 = mock_config_line("DNSListenAddress", "x$$$:::345");
+ config_listen_address3 = mock_config_line("DNSListenAddress",
+ "127.0.0.1:1442");
+ config_port1 = mock_config_line("DNSPort", "42");
+ config_port2 = mock_config_line("DNSPort", "43");
+ config_port1->next = config_port2;
+ config_port3 = mock_config_line("DNSPort", "auto");
+ config_port4 = mock_config_line("DNSPort", "55542");
+ config_port5 = mock_config_line("DNSPort", "666777");
+
+ // Test failure when we have a ListenAddress line and several
+ // Port lines for the same portname
+ ret = parse_port_config(NULL, config_port1, config_listen_address, "DNS", 0,
+ NULL, 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test case when we have a listen address, no default port and allow
+ // spurious listen address lines
+ ret = parse_port_config(NULL, NULL, config_listen_address, "DNS", 0, NULL,
+ 0, CL_PORT_ALLOW_EXTRA_LISTENADDR);
+ tt_int_op(ret, OP_EQ, 1);
+
+ // Test case when we have a listen address, no default port but doesn't
+ // allow spurious listen address lines
+ ret = parse_port_config(NULL, NULL, config_listen_address, "DNS", 0, NULL,
+ 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test case when we have a listen address, and a port that points to auto,
+ // should use the AUTO port
+ slout = smartlist_new();
+ ret = parse_port_config(slout, config_port3, config_listen_address, "DNS",
+ 0, NULL, 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->port, OP_EQ, CFG_AUTO_PORT);
+
+ // Test when we have a listen address and a custom port
+ ret = parse_port_config(slout, config_port4, config_listen_address, "DNS",
+ 0, NULL, 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 2);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 1);
+ tt_int_op(port_cfg->port, OP_EQ, 55542);
+
+ // Test when we have a listen address and an invalid custom port
+ ret = parse_port_config(slout, config_port5, config_listen_address, "DNS",
+ 0, NULL, 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test we get a server port configuration when asked for it
+ ret = parse_port_config(slout, NULL, config_listen_address, "DNS", 0, NULL,
+ 123, CL_PORT_SERVER_OPTIONS);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 4);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 2);
+ tt_int_op(port_cfg->port, OP_EQ, 123);
+ tt_int_op(port_cfg->server_cfg.no_listen, OP_EQ, 1);
+ tt_int_op(port_cfg->server_cfg.bind_ipv4_only, OP_EQ, 1);
+
+ // Test an invalid ListenAddress configuration
+ ret = parse_port_config(NULL, NULL, config_listen_address2, "DNS", 0, NULL,
+ 222, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test default to the port in the listen address if available
+ ret = parse_port_config(slout, config_port2, config_listen_address3, "DNS",
+ 0, NULL, 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 5);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 4);
+ tt_int_op(port_cfg->port, OP_EQ, 1442);
+
+ // Test we work correctly without an out, but with a listen address
+ // and a port
+ ret = parse_port_config(NULL, config_port2, config_listen_address, "DNS",
+ 0, NULL, 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test warning nonlocal control
+ ret = parse_port_config(slout, config_port2, config_listen_address, "DNS",
+ CONN_TYPE_CONTROL_LISTENER, NULL, 0,
+ CL_PORT_WARN_NONLOCAL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test warning nonlocal ext or listener
+ ret = parse_port_config(slout, config_port2, config_listen_address, "DNS",
+ CONN_TYPE_EXT_OR_LISTENER, NULL, 0,
+ CL_PORT_WARN_NONLOCAL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test warning nonlocal other
+ ret = parse_port_config(slout, config_port2, config_listen_address, "DNS",
+ 0, NULL, 0, CL_PORT_WARN_NONLOCAL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test warning nonlocal control without an out
+ ret = parse_port_config(NULL, config_port2, config_listen_address, "DNS",
+ CONN_TYPE_CONTROL_LISTENER, NULL, 0,
+ CL_PORT_WARN_NONLOCAL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ done:
+ tor_free(config_listen_address);
+ tor_free(config_listen_address2);
+ tor_free(config_listen_address3);
+ tor_free(config_port1);
+ tor_free(config_port2);
+ tor_free(config_port3);
+ tor_free(config_port4);
+ tor_free(config_port5);
+ smartlist_free(slout);
+}
+
+static void
+test_config_parse_port_config__ports__no_ports_given(void *data)
+{
+ (void)data;
+ int ret;
+ smartlist_t *slout = NULL;
+ port_cfg_t *port_cfg = NULL;
+
+ slout = smartlist_new();
+
+ // Test no defaultport, no defaultaddress and no out
+ ret = parse_port_config(NULL, NULL, NULL, "DNS", 0, NULL, 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test with defaultport, no defaultaddress and no out
+ ret = parse_port_config(NULL, NULL, NULL, "DNS", 0, NULL, 42, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test no defaultport, with defaultaddress and no out
+ ret = parse_port_config(NULL, NULL, NULL, "DNS", 0, "127.0.0.2", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test with defaultport, with defaultaddress and no out
+ ret = parse_port_config(NULL, NULL, NULL, "DNS", 0, "127.0.0.2", 42, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test no defaultport, no defaultaddress and with out
+ ret = parse_port_config(slout, NULL, NULL, "DNS", 0, NULL, 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 0);
+
+ // Test with defaultport, no defaultaddress and with out
+ ret = parse_port_config(slout, NULL, NULL, "DNS", 0, NULL, 42, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 0);
+
+ // Test no defaultport, with defaultaddress and with out
+ ret = parse_port_config(slout, NULL, NULL, "DNS", 0, "127.0.0.2", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 0);
+
+ // Test with defaultport, with defaultaddress and out, adds a new port cfg
+ smartlist_clear(slout);
+ ret = parse_port_config(slout, NULL, NULL, "DNS", 0, "127.0.0.2", 42, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->port, OP_EQ, 42);
+ tt_int_op(port_cfg->is_unix_addr, OP_EQ, 0);
+
+ // Test with defaultport, with defaultaddress and out, adds a new port cfg
+ // for a unix address
+ smartlist_clear(slout);
+ ret = parse_port_config(slout, NULL, NULL, "DNS", 0, "/foo/bar/unixdomain",
+ 42, CL_PORT_IS_UNIXSOCKET);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->port, OP_EQ, 0);
+ tt_int_op(port_cfg->is_unix_addr, OP_EQ, 1);
+ tt_str_op(port_cfg->unix_addr, OP_EQ, "/foo/bar/unixdomain");
+
+ done:
+ smartlist_free(slout);
+}
+
+static void
+test_config_parse_port_config__ports__ports_given(void *data)
+{
+ (void)data;
+ int ret;
+ smartlist_t *slout = NULL;
+ port_cfg_t *port_cfg = NULL;
+ config_line_t *config_port_invalid = NULL, *config_port_valid = NULL;
+ tor_addr_t addr;
+
+ slout = smartlist_new();
+
+ // Test error when encounters an invalid Port specification
+ config_port_invalid = mock_config_line("DNSPort", "");
+ ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS", 0, NULL,
+ 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test error when encounters an empty unix domain specification
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ config_port_invalid = mock_config_line("DNSPort", "unix:");
+ ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS", 0, NULL,
+ 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test error when encounters a unix domain specification but the listener
+ // doesnt support domain sockets
+ config_port_valid = mock_config_line("DNSPort", "unix:/tmp/foo/bar");
+ ret = parse_port_config(NULL, config_port_valid, NULL, "DNS",
+ CONN_TYPE_AP_DNS_LISTENER, NULL, 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test valid unix domain
+ smartlist_clear(slout);
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ CONN_TYPE_AP_LISTENER, NULL, 0, 0);
+#ifdef _WIN32
+ tt_int_op(ret, OP_EQ, -1);
+#else
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->port, OP_EQ, 0);
+ tt_int_op(port_cfg->is_unix_addr, OP_EQ, 1);
+ tt_str_op(port_cfg->unix_addr, OP_EQ, "/tmp/foo/bar");
+#endif
+
+ // Test failure if we have no ipv4 and no ipv6 (for unix domain sockets,
+ // this makes no sense - it should be fixed)
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ config_port_invalid = mock_config_line("DNSPort",
+ "unix:/tmp/foo/bar NoIPv4Traffic");
+ ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS",
+ CONN_TYPE_AP_LISTENER, NULL, 0,
+ CL_PORT_TAKES_HOSTNAMES);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test success with no ipv4 but take ipv6 (for unix domain sockets, this
+ // makes no sense - it should be fixed)
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "unix:/tmp/foo/bar "
+ "NoIPv4Traffic IPv6Traffic");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ CONN_TYPE_AP_LISTENER, NULL, 0,
+ CL_PORT_TAKES_HOSTNAMES);
+#ifdef _WIN32
+ tt_int_op(ret, OP_EQ, -1);
+#else
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 0);
+ tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 1);
+#endif
+
+ // Test success with both ipv4 and ipv6 (for unix domain sockets,
+ // this makes no sense - it should be fixed)
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "unix:/tmp/foo/bar "
+ "IPv4Traffic IPv6Traffic");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ CONN_TYPE_AP_LISTENER, NULL, 0,
+ CL_PORT_TAKES_HOSTNAMES);
+#ifdef _WIN32
+ tt_int_op(ret, OP_EQ, -1);
+#else
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 1);
+ tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 1);
+#endif
+
+ // Test failure if we specify world writable for an IP Port
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ config_port_invalid = mock_config_line("DNSPort", "42 WorldWritable");
+ ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test failure if we specify group writable for an IP Port
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ config_port_invalid = mock_config_line("DNSPort", "42 GroupWritable");
+ ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test success with only a port (this will fail without a default address)
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ config_port_valid = mock_config_line("DNSPort", "42");
+ ret = parse_port_config(NULL, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test success with only a port and isolate destination port
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 IsolateDestPort");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.isolation_flags, OP_EQ,
+ ISO_DEFAULT | ISO_DESTPORT);
+
+ // Test success with a negative isolate destination port, and plural
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 NoIsolateDestPorts");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.isolation_flags, OP_EQ,
+ ISO_DEFAULT & ~ISO_DESTPORT);
+
+ // Test success with isolate destination address
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 IsolateDestAddr");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.isolation_flags, OP_EQ,
+ ISO_DEFAULT | ISO_DESTADDR);
+
+ // Test success with isolate socks AUTH
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 IsolateSOCKSAuth");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.isolation_flags, OP_EQ,
+ ISO_DEFAULT | ISO_SOCKSAUTH);
+
+ // Test success with isolate client protocol
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 IsolateClientProtocol");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.isolation_flags, OP_EQ,
+ ISO_DEFAULT | ISO_CLIENTPROTO);
+
+ // Test success with isolate client address
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 IsolateClientAddr");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.isolation_flags, OP_EQ,
+ ISO_DEFAULT | ISO_CLIENTADDR);
+
+ // Test success with ignored unknown options
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ config_port_valid = mock_config_line("DNSPort", "42 ThisOptionDoesntExist");
+ ret = parse_port_config(NULL, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test success with no isolate socks AUTH
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 NoIsolateSOCKSAuth");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.3", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.socks_prefer_no_auth, OP_EQ, 1);
+
+ // Test success with prefer ipv6
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 IPv6Traffic PreferIPv6");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ CONN_TYPE_AP_LISTENER, "127.0.0.42", 0,
+ CL_PORT_TAKES_HOSTNAMES);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.prefer_ipv6, OP_EQ, 1);
+
+ // Test success with cache ipv4 DNS
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 CacheIPv4DNS");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.cache_ipv4_answers, OP_EQ, 1);
+ tt_int_op(port_cfg->entry_cfg.cache_ipv6_answers, OP_EQ, 0);
+
+ // Test success with cache ipv6 DNS
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 CacheIPv6DNS");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.cache_ipv4_answers, OP_EQ, 1);
+ tt_int_op(port_cfg->entry_cfg.cache_ipv6_answers, OP_EQ, 1);
+
+ // Test success with no cache ipv4 DNS
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 NoCacheIPv4DNS");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.cache_ipv4_answers, OP_EQ, 0);
+ tt_int_op(port_cfg->entry_cfg.cache_ipv6_answers, OP_EQ, 0);
+
+ // Test success with cache DNS
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 CacheDNS");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, CL_PORT_TAKES_HOSTNAMES);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.cache_ipv4_answers, OP_EQ, 1);
+ tt_int_op(port_cfg->entry_cfg.cache_ipv6_answers, OP_EQ, 1);
+
+ // Test success with use cached ipv4 DNS
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 UseIPv4Cache");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.use_cached_ipv4_answers, OP_EQ, 1);
+ tt_int_op(port_cfg->entry_cfg.use_cached_ipv6_answers, OP_EQ, 0);
+
+ // Test success with use cached ipv6 DNS
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 UseIPv6Cache");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.use_cached_ipv4_answers, OP_EQ, 0);
+ tt_int_op(port_cfg->entry_cfg.use_cached_ipv6_answers, OP_EQ, 1);
+
+ // Test success with use cached DNS
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 UseDNSCache");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.use_cached_ipv4_answers, OP_EQ, 1);
+ tt_int_op(port_cfg->entry_cfg.use_cached_ipv6_answers, OP_EQ, 1);
+
+ // Test success with not preferring ipv6 automap
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 NoPreferIPv6Automap");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.prefer_ipv6_virtaddr, OP_EQ, 0);
+
+ // Test success with prefer SOCKS no auth
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 PreferSOCKSNoAuth");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.socks_prefer_no_auth, OP_EQ, 1);
+
+ // Test failure with both a zero port and a non-zero port
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_invalid = mock_config_line("DNSPort", "0");
+ config_port_valid = mock_config_line("DNSPort", "42");
+ config_port_invalid->next = config_port_valid;
+ ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0,
+ "127.0.0.42", 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test success with warn non-local control
+ smartlist_clear(slout);
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ CONN_TYPE_CONTROL_LISTENER, "127.0.0.42", 0,
+ CL_PORT_WARN_NONLOCAL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test success with warn non-local listener
+ smartlist_clear(slout);
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ CONN_TYPE_EXT_OR_LISTENER, "127.0.0.42", 0,
+ CL_PORT_WARN_NONLOCAL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test success with warn non-local other
+ smartlist_clear(slout);
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, CL_PORT_WARN_NONLOCAL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test success with warn non-local other without out
+ ret = parse_port_config(NULL, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.42", 0, CL_PORT_WARN_NONLOCAL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test success with both ipv4 and ipv6 but without stream options
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 IPv4Traffic "
+ "IPv6Traffic");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.44", 0,
+ CL_PORT_TAKES_HOSTNAMES |
+ CL_PORT_NO_STREAM_OPTIONS);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 1);
+ tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 0);
+
+ // Test failure for a SessionGroup argument with invalid value
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_invalid = mock_config_line("DNSPort", "42 SessionGroup=invalid");
+ ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0,
+ "127.0.0.44", 0, CL_PORT_NO_STREAM_OPTIONS);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // TODO: this seems wrong. Shouldn't it be the other way around?
+ // Potential bug.
+ // Test failure for a SessionGroup argument with valid value but with stream
+ // options allowed
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ smartlist_clear(slout);
+ config_port_invalid = mock_config_line("DNSPort", "42 SessionGroup=123");
+ ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0,
+ "127.0.0.44", 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test failure for more than one SessionGroup argument
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ smartlist_clear(slout);
+ config_port_invalid = mock_config_line("DNSPort", "42 SessionGroup=123 "
+ "SessionGroup=321");
+ ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0,
+ "127.0.0.44", 0, CL_PORT_NO_STREAM_OPTIONS);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test success with a sessiongroup options
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "42 SessionGroup=1111122");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.44", 0, CL_PORT_NO_STREAM_OPTIONS);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.session_group, OP_EQ, 1111122);
+
+ // Test success with a zero unix domain socket, and doesnt add it to out
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "0");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.45", 0, CL_PORT_IS_UNIXSOCKET);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 0);
+
+ // Test success with a one unix domain socket, and doesnt add it to out
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "something");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.45", 0, CL_PORT_IS_UNIXSOCKET);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->is_unix_addr, OP_EQ, 1);
+ tt_str_op(port_cfg->unix_addr, OP_EQ, "something");
+
+ // Test success with a port of auto - it uses the default address
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "auto");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.46", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->port, OP_EQ, CFG_AUTO_PORT);
+ tor_addr_parse(&addr, "127.0.0.46");
+ tt_assert(tor_addr_eq(&port_cfg->addr, &addr))
+
+ // Test success with parsing both an address and an auto port
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "127.0.0.122:auto");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.46", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->port, OP_EQ, CFG_AUTO_PORT);
+ tor_addr_parse(&addr, "127.0.0.122");
+ tt_assert(tor_addr_eq(&port_cfg->addr, &addr))
+
+ // Test failure when asked to parse an invalid address followed by auto
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ config_port_invalid = mock_config_line("DNSPort", "invalidstuff!!:auto");
+ ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS", 0,
+ "127.0.0.46", 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test success with parsing both an address and a real port
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "127.0.0.123:656");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0,
+ "127.0.0.46", 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->port, OP_EQ, 656);
+ tor_addr_parse(&addr, "127.0.0.123");
+ tt_assert(tor_addr_eq(&port_cfg->addr, &addr))
+
+ // Test failure if we can't parse anything at all
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_invalid = mock_config_line("DNSPort", "something wrong");
+ ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0,
+ "127.0.0.46", 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test failure if we find both an address, a port and an auto
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ smartlist_clear(slout);
+ config_port_invalid = mock_config_line("DNSPort", "127.0.1.0:123:auto");
+ ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0,
+ "127.0.0.46", 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test that default to group writeable default sets group writeable for
+ // domain socket
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "unix:/tmp/somewhere");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ CONN_TYPE_AP_LISTENER, "127.0.0.46", 0,
+ CL_PORT_DFLT_GROUP_WRITABLE);
+#ifdef _WIN32
+ tt_int_op(ret, OP_EQ, -1);
+#else
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->is_group_writable, OP_EQ, 1);
+#endif
+
+ done:
+ smartlist_free(slout);
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+}
+
+static void
+test_config_parse_port_config__ports__server_options(void *data)
+{
+ (void)data;
+ int ret;
+ smartlist_t *slout = NULL;
+ port_cfg_t *port_cfg = NULL;
+ config_line_t *config_port_invalid = NULL, *config_port_valid = NULL;
+
+ slout = smartlist_new();
+
+ // Test success with NoAdvertise option
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ config_port_valid = mock_config_line("DNSPort",
+ "127.0.0.124:656 NoAdvertise");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, NULL, 0,
+ CL_PORT_SERVER_OPTIONS);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->server_cfg.no_advertise, OP_EQ, 1);
+ tt_int_op(port_cfg->server_cfg.no_listen, OP_EQ, 0);
+
+ // Test success with NoListen option
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "127.0.0.124:656 NoListen");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, NULL, 0,
+ CL_PORT_SERVER_OPTIONS);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->server_cfg.no_advertise, OP_EQ, 0);
+ tt_int_op(port_cfg->server_cfg.no_listen, OP_EQ, 1);
+
+ // Test failure with both NoAdvertise and NoListen option
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ smartlist_clear(slout);
+ config_port_invalid = mock_config_line("DNSPort", "127.0.0.124:656 NoListen "
+ "NoAdvertise");
+ ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0, NULL,
+ 0, CL_PORT_SERVER_OPTIONS);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test success with IPv4Only
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "127.0.0.124:656 IPv4Only");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, NULL, 0,
+ CL_PORT_SERVER_OPTIONS);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->server_cfg.bind_ipv4_only, OP_EQ, 1);
+ tt_int_op(port_cfg->server_cfg.bind_ipv6_only, OP_EQ, 0);
+
+ // Test success with IPv6Only
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "[::1]:656 IPv6Only");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, NULL, 0,
+ CL_PORT_SERVER_OPTIONS);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->server_cfg.bind_ipv4_only, OP_EQ, 0);
+ tt_int_op(port_cfg->server_cfg.bind_ipv6_only, OP_EQ, 1);
+
+ // Test failure with both IPv4Only and IPv6Only
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ smartlist_clear(slout);
+ config_port_invalid = mock_config_line("DNSPort", "127.0.0.124:656 IPv6Only "
+ "IPv4Only");
+ ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0, NULL,
+ 0, CL_PORT_SERVER_OPTIONS);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test success with invalid parameter
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "127.0.0.124:656 unknown");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS", 0, NULL, 0,
+ CL_PORT_SERVER_OPTIONS);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+
+ // Test failure when asked to bind only to ipv6 but gets an ipv4 address
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ smartlist_clear(slout);
+ config_port_invalid = mock_config_line("DNSPort",
+ "127.0.0.124:656 IPv6Only");
+ ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0, NULL,
+ 0, CL_PORT_SERVER_OPTIONS);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test failure when asked to bind only to ipv4 but gets an ipv6 address
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ smartlist_clear(slout);
+ config_port_invalid = mock_config_line("DNSPort", "[::1]:656 IPv4Only");
+ ret = parse_port_config(slout, config_port_invalid, NULL, "DNS", 0, NULL,
+ 0, CL_PORT_SERVER_OPTIONS);
+ tt_int_op(ret, OP_EQ, -1);
+
+ done:
+ smartlist_free(slout);
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+}
+
#define CONFIG_TEST(name, flags) \
{ #name, test_config_ ## name, flags, NULL, NULL }
struct testcase_t config_tests[] = {
+ CONFIG_TEST(adding_trusted_dir_server, TT_FORK),
+ CONFIG_TEST(adding_fallback_dir_server, TT_FORK),
+ CONFIG_TEST(parsing_trusted_dir_server, 0),
+ CONFIG_TEST(parsing_fallback_dir_server, 0),
+ CONFIG_TEST(adding_default_trusted_dir_servers, TT_FORK),
CONFIG_TEST(adding_dir_servers, TT_FORK),
+ CONFIG_TEST(default_dir_servers, TT_FORK),
+ CONFIG_TEST(default_fallback_dirs, 0),
CONFIG_TEST(resolve_my_address, TT_FORK),
CONFIG_TEST(addressmap, 0),
CONFIG_TEST(parse_bridge_line, 0),
@@ -3222,6 +4568,11 @@ struct testcase_t config_tests[] = {
CONFIG_TEST(check_or_create_data_subdir, TT_FORK),
CONFIG_TEST(write_to_data_subdir, TT_FORK),
CONFIG_TEST(fix_my_family, 0),
+ CONFIG_TEST(directory_fetch, 0),
+ CONFIG_TEST(parse_port_config__listenaddress, 0),
+ CONFIG_TEST(parse_port_config__ports__no_ports_given, 0),
+ CONFIG_TEST(parse_port_config__ports__server_options, 0),
+ CONFIG_TEST(parse_port_config__ports__ports_given, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_connection.c b/src/test/test_connection.c
new file mode 100644
index 0000000000..c5ef92931d
--- /dev/null
+++ b/src/test/test_connection.c
@@ -0,0 +1,761 @@
+/* Copyright (c) 2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+
+#define CONNECTION_PRIVATE
+#define MAIN_PRIVATE
+
+#include "or.h"
+#include "test.h"
+
+#include "connection.h"
+#include "main.h"
+#include "networkstatus.h"
+#include "rendcache.h"
+#include "directory.h"
+
+static void test_conn_lookup_addr_helper(const char *address,
+ int family,
+ tor_addr_t *addr);
+
+static void * test_conn_get_basic_setup(const struct testcase_t *tc);
+static int test_conn_get_basic_teardown(const struct testcase_t *tc,
+ void *arg);
+
+static void * test_conn_get_rend_setup(const struct testcase_t *tc);
+static int test_conn_get_rend_teardown(const struct testcase_t *tc,
+ void *arg);
+
+static void * test_conn_get_rsrc_setup(const struct testcase_t *tc);
+static int test_conn_get_rsrc_teardown(const struct testcase_t *tc,
+ void *arg);
+
+/* Arbitrary choice - IPv4 Directory Connection to localhost */
+#define TEST_CONN_TYPE (CONN_TYPE_DIR)
+/* We assume every machine has IPv4 localhost, is that ok? */
+#define TEST_CONN_ADDRESS "127.0.0.1"
+#define TEST_CONN_PORT (12345)
+#define TEST_CONN_ADDRESS_PORT "127.0.0.1:12345"
+#define TEST_CONN_FAMILY (AF_INET)
+#define TEST_CONN_STATE (DIR_CONN_STATE_MIN_)
+#define TEST_CONN_ADDRESS_2 "127.0.0.2"
+
+#define TEST_CONN_BASIC_PURPOSE (DIR_PURPOSE_MIN_)
+
+#define TEST_CONN_REND_ADDR "cfs3rltphxxvabci"
+#define TEST_CONN_REND_PURPOSE (DIR_PURPOSE_FETCH_RENDDESC_V2)
+#define TEST_CONN_REND_PURPOSE_SUCCESSFUL (DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2)
+#define TEST_CONN_REND_TYPE_2 (CONN_TYPE_AP)
+#define TEST_CONN_REND_ADDR_2 "icbavxxhptlr3sfc"
+
+#define TEST_CONN_RSRC (networkstatus_get_flavor_name(FLAV_MICRODESC))
+#define TEST_CONN_RSRC_PURPOSE (DIR_PURPOSE_FETCH_CONSENSUS)
+#define TEST_CONN_RSRC_STATE_SUCCESSFUL (DIR_CONN_STATE_CLIENT_FINISHED)
+#define TEST_CONN_RSRC_2 (networkstatus_get_flavor_name(FLAV_NS))
+
+#define TEST_CONN_DL_STATE (DIR_CONN_STATE_CLIENT_SENDING)
+
+#define TEST_CONN_FD_INIT 50
+static int mock_connection_connect_sockaddr_called = 0;
+static int fake_socket_number = TEST_CONN_FD_INIT;
+
+static int
+mock_connection_connect_sockaddr(connection_t *conn,
+ const struct sockaddr *sa,
+ socklen_t sa_len,
+ const struct sockaddr *bindaddr,
+ socklen_t bindaddr_len,
+ int *socket_error)
+{
+ (void)sa_len;
+ (void)bindaddr;
+ (void)bindaddr_len;
+
+ tor_assert(conn);
+ tor_assert(sa);
+ tor_assert(socket_error);
+
+ mock_connection_connect_sockaddr_called++;
+
+ conn->s = fake_socket_number++;
+ tt_assert(SOCKET_OK(conn->s));
+ /* We really should call tor_libevent_initialize() here. Because we don't,
+ * we are relying on other parts of the code not checking if the_event_base
+ * (and therefore event->ev_base) is NULL. */
+ tt_assert(connection_add_connecting(conn) == 0);
+
+ done:
+ /* Fake "connected" status */
+ return 1;
+}
+
+static void
+test_conn_lookup_addr_helper(const char *address, int family, tor_addr_t *addr)
+{
+ int rv = 0;
+
+ tt_assert(addr);
+
+ rv = tor_addr_lookup(address, family, addr);
+ /* XXXX - should we retry on transient failure? */
+ tt_assert(rv == 0);
+ tt_assert(tor_addr_is_loopback(addr));
+ tt_assert(tor_addr_is_v4(addr));
+
+ return;
+
+ done:
+ tor_addr_make_null(addr, TEST_CONN_FAMILY);
+}
+
+static void *
+test_conn_get_basic_setup(const struct testcase_t *tc)
+{
+ connection_t *conn = NULL;
+ tor_addr_t addr;
+ int socket_err = 0;
+ int in_progress = 0;
+ (void)tc;
+
+ MOCK(connection_connect_sockaddr,
+ mock_connection_connect_sockaddr);
+
+ init_connection_lists();
+
+ conn = connection_new(TEST_CONN_TYPE, TEST_CONN_FAMILY);
+ tt_assert(conn);
+
+ test_conn_lookup_addr_helper(TEST_CONN_ADDRESS, TEST_CONN_FAMILY, &addr);
+ tt_assert(!tor_addr_is_null(&addr));
+
+ /* XXXX - connection_connect doesn't set these, should it? */
+ tor_addr_copy_tight(&conn->addr, &addr);
+ conn->port = TEST_CONN_PORT;
+ mock_connection_connect_sockaddr_called = 0;
+ in_progress = connection_connect(conn, TEST_CONN_ADDRESS_PORT, &addr,
+ TEST_CONN_PORT, &socket_err);
+ tt_assert(mock_connection_connect_sockaddr_called == 1);
+ tt_assert(!socket_err);
+ tt_assert(in_progress == 0 || in_progress == 1);
+
+ /* fake some of the attributes so the connection looks OK */
+ conn->state = TEST_CONN_STATE;
+ conn->purpose = TEST_CONN_BASIC_PURPOSE;
+ assert_connection_ok(conn, time(NULL));
+
+ UNMOCK(connection_connect_sockaddr);
+
+ return conn;
+
+ /* On failure */
+ done:
+ UNMOCK(connection_connect_sockaddr);
+ test_conn_get_basic_teardown(tc, conn);
+
+ /* Returning NULL causes the unit test to fail */
+ return NULL;
+}
+
+static int
+test_conn_get_basic_teardown(const struct testcase_t *tc, void *arg)
+{
+ (void)tc;
+ connection_t *conn = arg;
+
+ tt_assert(conn);
+ assert_connection_ok(conn, time(NULL));
+
+ /* teardown the connection as fast as possible */
+ if (conn->linked_conn) {
+ assert_connection_ok(conn->linked_conn, time(NULL));
+
+ /* We didn't call tor_libevent_initialize(), so event_base was NULL,
+ * so we can't rely on connection_unregister_events() use of event_del().
+ */
+ if (conn->linked_conn->read_event) {
+ tor_free(conn->linked_conn->read_event);
+ conn->linked_conn->read_event = NULL;
+ }
+ if (conn->linked_conn->write_event) {
+ tor_free(conn->linked_conn->write_event);
+ conn->linked_conn->write_event = NULL;
+ }
+
+ if (!conn->linked_conn->marked_for_close) {
+ connection_close_immediate(conn->linked_conn);
+ connection_mark_for_close(conn->linked_conn);
+ }
+ conn->linked_conn->linked_conn = NULL;
+ connection_free(conn->linked_conn);
+ conn->linked_conn = NULL;
+ }
+
+ /* We didn't set the events up properly, so we can't use event_del() in
+ * close_closeable_connections() > connection_free()
+ * > connection_unregister_events() */
+ if (conn->read_event) {
+ tor_free(conn->read_event);
+ conn->read_event = NULL;
+ }
+ if (conn->write_event) {
+ tor_free(conn->write_event);
+ conn->write_event = NULL;
+ }
+
+ if (!conn->marked_for_close) {
+ connection_close_immediate(conn);
+ connection_mark_for_close(conn);
+ }
+
+ close_closeable_connections();
+
+ /* The unit test will fail if we return 0 */
+ return 1;
+
+ /* When conn == NULL, we can't cleanup anything */
+ done:
+ return 0;
+}
+
+static void *
+test_conn_get_rend_setup(const struct testcase_t *tc)
+{
+ dir_connection_t *conn = DOWNCAST(dir_connection_t,
+ test_conn_get_basic_setup(tc));
+ tt_assert(conn);
+ assert_connection_ok(&conn->base_, time(NULL));
+
+ rend_cache_init();
+
+ /* TODO: use directory_initiate_command_rend() to do this - maybe? */
+ conn->rend_data = tor_malloc_zero(sizeof(rend_data_t));
+ tor_assert(strlen(TEST_CONN_REND_ADDR) == REND_SERVICE_ID_LEN_BASE32);
+ memcpy(conn->rend_data->onion_address,
+ TEST_CONN_REND_ADDR,
+ REND_SERVICE_ID_LEN_BASE32+1);
+ conn->rend_data->hsdirs_fp = smartlist_new();
+ conn->base_.purpose = TEST_CONN_REND_PURPOSE;
+
+ assert_connection_ok(&conn->base_, time(NULL));
+ return conn;
+
+ /* On failure */
+ done:
+ test_conn_get_rend_teardown(tc, conn);
+ /* Returning NULL causes the unit test to fail */
+ return NULL;
+}
+
+static int
+test_conn_get_rend_teardown(const struct testcase_t *tc, void *arg)
+{
+ dir_connection_t *conn = DOWNCAST(dir_connection_t, arg);
+ int rv = 0;
+
+ tt_assert(conn);
+ assert_connection_ok(&conn->base_, time(NULL));
+
+ /* avoid a last-ditch attempt to refetch the descriptor */
+ conn->base_.purpose = TEST_CONN_REND_PURPOSE_SUCCESSFUL;
+
+ /* connection_free_() cleans up rend_data */
+ rv = test_conn_get_basic_teardown(tc, arg);
+ done:
+ rend_cache_free_all();
+ return rv;
+}
+
+static void *
+test_conn_get_rsrc_setup(const struct testcase_t *tc)
+{
+ dir_connection_t *conn = DOWNCAST(dir_connection_t,
+ test_conn_get_basic_setup(tc));
+ tt_assert(conn);
+ assert_connection_ok(&conn->base_, time(NULL));
+
+ /* TODO: use the canonical function to do this - maybe? */
+ conn->requested_resource = tor_strdup(TEST_CONN_RSRC);
+ conn->base_.purpose = TEST_CONN_RSRC_PURPOSE;
+
+ assert_connection_ok(&conn->base_, time(NULL));
+ return conn;
+
+ /* On failure */
+ done:
+ test_conn_get_rend_teardown(tc, conn);
+ /* Returning NULL causes the unit test to fail */
+ return NULL;
+}
+
+static int
+test_conn_get_rsrc_teardown(const struct testcase_t *tc, void *arg)
+{
+ dir_connection_t *conn = DOWNCAST(dir_connection_t, arg);
+ int rv = 0;
+
+ tt_assert(conn);
+ assert_connection_ok(&conn->base_, time(NULL));
+
+ /* avoid a last-ditch attempt to refetch the consensus */
+ conn->base_.state = TEST_CONN_RSRC_STATE_SUCCESSFUL;
+
+ /* connection_free_() cleans up requested_resource */
+ rv = test_conn_get_basic_teardown(tc, arg);
+ done:
+ return rv;
+}
+
+static void *
+test_conn_download_status_setup(const struct testcase_t *tc)
+{
+ (void)tc;
+
+ /* Don't return NULL, that causes the test to fail */
+ return (void*)"ok";
+}
+
+static int
+test_conn_download_status_teardown(const struct testcase_t *tc, void *arg)
+{
+ (void)arg;
+ int rv = 0;
+
+ /* Ignore arg, and just loop through the connection array */
+ SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) {
+ if (conn) {
+ assert_connection_ok(conn, time(NULL));
+
+ /* connection_free_() cleans up requested_resource */
+ rv = test_conn_get_rsrc_teardown(tc, conn);
+ tt_assert(rv == 1);
+ }
+ } SMARTLIST_FOREACH_END(conn);
+
+ done:
+ return rv;
+}
+
+static dir_connection_t *
+test_conn_download_status_add_a_connection(void)
+{
+ dir_connection_t *conn = DOWNCAST(dir_connection_t,
+ test_conn_get_rsrc_setup(NULL));
+
+ tt_assert(conn);
+ assert_connection_ok(&conn->base_, time(NULL));
+
+ return conn;
+
+ done:
+ test_conn_download_status_teardown(NULL, NULL);
+ return NULL;
+}
+
+static struct testcase_setup_t test_conn_get_basic_st = {
+ test_conn_get_basic_setup, test_conn_get_basic_teardown
+};
+
+static struct testcase_setup_t test_conn_get_rend_st = {
+ test_conn_get_rend_setup, test_conn_get_rend_teardown
+};
+
+static struct testcase_setup_t test_conn_get_rsrc_st = {
+ test_conn_get_rsrc_setup, test_conn_get_rsrc_teardown
+};
+
+static struct testcase_setup_t test_conn_download_status_st = {
+ test_conn_download_status_setup, test_conn_download_status_teardown
+};
+
+static void
+test_conn_get_basic(void *arg)
+{
+ connection_t *conn = (connection_t*)arg;
+ tor_addr_t addr, addr2;
+
+ tt_assert(conn);
+ assert_connection_ok(conn, time(NULL));
+
+ test_conn_lookup_addr_helper(TEST_CONN_ADDRESS, TEST_CONN_FAMILY, &addr);
+ tt_assert(!tor_addr_is_null(&addr));
+ test_conn_lookup_addr_helper(TEST_CONN_ADDRESS_2, TEST_CONN_FAMILY, &addr2);
+ tt_assert(!tor_addr_is_null(&addr2));
+
+ /* Check that we get this connection back when we search for it by
+ * its attributes, but get NULL when we supply a different value. */
+
+ tt_assert(connection_get_by_global_id(conn->global_identifier) == conn);
+ tt_assert(connection_get_by_global_id(!conn->global_identifier) == NULL);
+
+ tt_assert(connection_get_by_type(conn->type) == conn);
+ tt_assert(connection_get_by_type(TEST_CONN_TYPE) == conn);
+ tt_assert(connection_get_by_type(!conn->type) == NULL);
+ tt_assert(connection_get_by_type(!TEST_CONN_TYPE) == NULL);
+
+ tt_assert(connection_get_by_type_state(conn->type, conn->state)
+ == conn);
+ tt_assert(connection_get_by_type_state(TEST_CONN_TYPE, TEST_CONN_STATE)
+ == conn);
+ tt_assert(connection_get_by_type_state(!conn->type, !conn->state)
+ == NULL);
+ tt_assert(connection_get_by_type_state(!TEST_CONN_TYPE, !TEST_CONN_STATE)
+ == NULL);
+
+ /* Match on the connection fields themselves */
+ tt_assert(connection_get_by_type_addr_port_purpose(conn->type,
+ &conn->addr,
+ conn->port,
+ conn->purpose)
+ == conn);
+ /* Match on the original inputs to the connection */
+ tt_assert(connection_get_by_type_addr_port_purpose(TEST_CONN_TYPE,
+ &conn->addr,
+ conn->port,
+ conn->purpose)
+ == conn);
+ tt_assert(connection_get_by_type_addr_port_purpose(conn->type,
+ &addr,
+ conn->port,
+ conn->purpose)
+ == conn);
+ tt_assert(connection_get_by_type_addr_port_purpose(conn->type,
+ &conn->addr,
+ TEST_CONN_PORT,
+ conn->purpose)
+ == conn);
+ tt_assert(connection_get_by_type_addr_port_purpose(conn->type,
+ &conn->addr,
+ conn->port,
+ TEST_CONN_BASIC_PURPOSE)
+ == conn);
+ tt_assert(connection_get_by_type_addr_port_purpose(TEST_CONN_TYPE,
+ &addr,
+ TEST_CONN_PORT,
+ TEST_CONN_BASIC_PURPOSE)
+ == conn);
+ /* Then try each of the not-matching combinations */
+ tt_assert(connection_get_by_type_addr_port_purpose(!conn->type,
+ &conn->addr,
+ conn->port,
+ conn->purpose)
+ == NULL);
+ tt_assert(connection_get_by_type_addr_port_purpose(conn->type,
+ &addr2,
+ conn->port,
+ conn->purpose)
+ == NULL);
+ tt_assert(connection_get_by_type_addr_port_purpose(conn->type,
+ &conn->addr,
+ !conn->port,
+ conn->purpose)
+ == NULL);
+ tt_assert(connection_get_by_type_addr_port_purpose(conn->type,
+ &conn->addr,
+ conn->port,
+ !conn->purpose)
+ == NULL);
+ /* Then try everything not-matching */
+ tt_assert(connection_get_by_type_addr_port_purpose(!conn->type,
+ &addr2,
+ !conn->port,
+ !conn->purpose)
+ == NULL);
+ tt_assert(connection_get_by_type_addr_port_purpose(!TEST_CONN_TYPE,
+ &addr2,
+ !TEST_CONN_PORT,
+ !TEST_CONN_BASIC_PURPOSE)
+ == NULL);
+
+ done:
+ ;
+}
+
+static void
+test_conn_get_rend(void *arg)
+{
+ dir_connection_t *conn = DOWNCAST(dir_connection_t, arg);
+ tt_assert(conn);
+ assert_connection_ok(&conn->base_, time(NULL));
+
+ tt_assert(connection_get_by_type_state_rendquery(
+ conn->base_.type,
+ conn->base_.state,
+ conn->rend_data->onion_address)
+ == TO_CONN(conn));
+ tt_assert(connection_get_by_type_state_rendquery(
+ TEST_CONN_TYPE,
+ TEST_CONN_STATE,
+ TEST_CONN_REND_ADDR)
+ == TO_CONN(conn));
+ tt_assert(connection_get_by_type_state_rendquery(TEST_CONN_REND_TYPE_2,
+ !conn->base_.state,
+ "")
+ == NULL);
+ tt_assert(connection_get_by_type_state_rendquery(TEST_CONN_REND_TYPE_2,
+ !TEST_CONN_STATE,
+ TEST_CONN_REND_ADDR_2)
+ == NULL);
+
+ done:
+ ;
+}
+
+#define sl_is_conn_assert(sl_input, conn) \
+ do { \
+ the_sl = (sl_input); \
+ tt_assert(smartlist_len((the_sl)) == 1); \
+ tt_assert(smartlist_get((the_sl), 0) == (conn)); \
+ smartlist_free(the_sl); the_sl = NULL; \
+ } while (0)
+
+#define sl_no_conn_assert(sl_input) \
+ do { \
+ the_sl = (sl_input); \
+ tt_assert(smartlist_len((the_sl)) == 0); \
+ smartlist_free(the_sl); the_sl = NULL; \
+ } while (0)
+
+static void
+test_conn_get_rsrc(void *arg)
+{
+ dir_connection_t *conn = DOWNCAST(dir_connection_t, arg);
+ smartlist_t *the_sl = NULL;
+ tt_assert(conn);
+ assert_connection_ok(&conn->base_, time(NULL));
+
+ tt_assert(connection_dir_get_by_purpose_and_resource(
+ conn->base_.purpose,
+ conn->requested_resource)
+ == conn);
+ tt_assert(connection_dir_get_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC)
+ == conn);
+ tt_assert(connection_dir_get_by_purpose_and_resource(
+ !conn->base_.purpose,
+ "")
+ == NULL);
+ tt_assert(connection_dir_get_by_purpose_and_resource(
+ !TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC_2)
+ == NULL);
+
+ tt_assert(connection_dir_get_by_purpose_resource_and_state(
+ conn->base_.purpose,
+ conn->requested_resource,
+ conn->base_.state)
+ == conn);
+ tt_assert(connection_dir_get_by_purpose_resource_and_state(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC,
+ TEST_CONN_STATE)
+ == conn);
+ tt_assert(connection_dir_get_by_purpose_resource_and_state(
+ !conn->base_.purpose,
+ "",
+ !conn->base_.state)
+ == NULL);
+ tt_assert(connection_dir_get_by_purpose_resource_and_state(
+ !TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC_2,
+ !TEST_CONN_STATE)
+ == NULL);
+
+ sl_is_conn_assert(connection_dir_list_by_purpose_and_resource(
+ conn->base_.purpose,
+ conn->requested_resource),
+ conn);
+ sl_is_conn_assert(connection_dir_list_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC),
+ conn);
+ sl_no_conn_assert(connection_dir_list_by_purpose_and_resource(
+ !conn->base_.purpose,
+ ""));
+ sl_no_conn_assert(connection_dir_list_by_purpose_and_resource(
+ !TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC_2));
+
+ sl_is_conn_assert(connection_dir_list_by_purpose_resource_and_state(
+ conn->base_.purpose,
+ conn->requested_resource,
+ conn->base_.state),
+ conn);
+ sl_is_conn_assert(connection_dir_list_by_purpose_resource_and_state(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC,
+ TEST_CONN_STATE),
+ conn);
+ sl_no_conn_assert(connection_dir_list_by_purpose_resource_and_state(
+ !conn->base_.purpose,
+ "",
+ !conn->base_.state));
+ sl_no_conn_assert(connection_dir_list_by_purpose_resource_and_state(
+ !TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC_2,
+ !TEST_CONN_STATE));
+
+ tt_assert(connection_dir_count_by_purpose_and_resource(
+ conn->base_.purpose,
+ conn->requested_resource)
+ == 1);
+ tt_assert(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC)
+ == 1);
+ tt_assert(connection_dir_count_by_purpose_and_resource(
+ !conn->base_.purpose,
+ "")
+ == 0);
+ tt_assert(connection_dir_count_by_purpose_and_resource(
+ !TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC_2)
+ == 0);
+
+ tt_assert(connection_dir_count_by_purpose_resource_and_state(
+ conn->base_.purpose,
+ conn->requested_resource,
+ conn->base_.state)
+ == 1);
+ tt_assert(connection_dir_count_by_purpose_resource_and_state(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC,
+ TEST_CONN_STATE)
+ == 1);
+ tt_assert(connection_dir_count_by_purpose_resource_and_state(
+ !conn->base_.purpose,
+ "",
+ !conn->base_.state)
+ == 0);
+ tt_assert(connection_dir_count_by_purpose_resource_and_state(
+ !TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC_2,
+ !TEST_CONN_STATE)
+ == 0);
+
+ done:
+ smartlist_free(the_sl);
+}
+
+static void
+test_conn_download_status(void *arg)
+{
+ (void)arg;
+ dir_connection_t *conn = NULL;
+ dir_connection_t *conn2 = NULL;
+ dir_connection_t *conn3 = NULL;
+
+ /* no connections, no excess, not downloading */
+ tt_assert(networkstatus_consensus_has_excess_connections() == 0);
+ tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 0);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 0);
+
+ /* one connection, no excess, not downloading */
+ conn = test_conn_download_status_add_a_connection();
+ tt_assert(networkstatus_consensus_has_excess_connections() == 0);
+ tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 0);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 0);
+
+ /* one connection, no excess, but downloading */
+ conn->base_.state = TEST_CONN_DL_STATE;
+ tt_assert(networkstatus_consensus_has_excess_connections() == 0);
+ tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 1);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 1);
+ conn->base_.state = TEST_CONN_STATE;
+
+ /* two connections, excess, but not downloading */
+ conn2 = test_conn_download_status_add_a_connection();
+ tt_assert(networkstatus_consensus_has_excess_connections() == 1);
+ tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 0);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 0);
+
+ /* two connections, excess, downloading */
+ conn2->base_.state = TEST_CONN_DL_STATE;
+ tt_assert(networkstatus_consensus_has_excess_connections() == 1);
+ tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 1);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 1);
+ conn2->base_.state = TEST_CONN_STATE;
+
+ /* more connections, excess, but not downloading */
+ conn3 = test_conn_download_status_add_a_connection();
+ tt_assert(networkstatus_consensus_has_excess_connections() == 1);
+ tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 0);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 0);
+
+ /* more connections, excess, downloading */
+ conn3->base_.state = TEST_CONN_DL_STATE;
+ tt_assert(networkstatus_consensus_has_excess_connections() == 1);
+ tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 1);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 1);
+
+ /* more connections, more downloading */
+ conn2->base_.state = TEST_CONN_DL_STATE;
+ tt_assert(networkstatus_consensus_has_excess_connections() == 1);
+ tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 1);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 1);
+
+ /* now try closing the one that isn't downloading:
+ * these tests won't work unless tor thinks it is bootstrapping */
+ tt_assert(networkstatus_consensus_is_boostrapping(time(NULL)));
+
+ tt_assert(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC) == 3);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 1);
+ tt_assert(connection_dir_close_consensus_conn_if_extra(conn) == -1);
+ tt_assert(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC) == 2);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 1);
+
+ /* now try closing one that is already closed - nothing happens */
+ tt_assert(connection_dir_close_consensus_conn_if_extra(conn) == 0);
+ tt_assert(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC) == 2);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 1);
+
+ /* now try closing one that is downloading - it stays open */
+ tt_assert(connection_dir_close_consensus_conn_if_extra(conn2) == 0);
+ tt_assert(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC) == 2);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 1);
+
+ /* now try closing all excess connections */
+ connection_dir_close_extra_consensus_conns();
+ tt_assert(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE,
+ TEST_CONN_RSRC) == 1);
+ tt_assert(connection_dir_avoid_extra_connection_for_purpose(
+ TEST_CONN_RSRC_PURPOSE) == 1);
+
+ done:
+ /* the teardown function removes all the connections */;
+}
+
+#define CONNECTION_TESTCASE(name, fork, setup) \
+ { #name, test_conn_##name, fork, &setup, NULL }
+
+struct testcase_t connection_tests[] = {
+ CONNECTION_TESTCASE(get_basic, TT_FORK, test_conn_get_basic_st),
+ CONNECTION_TESTCASE(get_rend, TT_FORK, test_conn_get_rend_st),
+ CONNECTION_TESTCASE(get_rsrc, TT_FORK, test_conn_get_rsrc_st),
+ CONNECTION_TESTCASE(download_status, TT_FORK, test_conn_download_status_st),
+//CONNECTION_TESTCASE(func_suffix, TT_FORK, setup_func_pair),
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c
index dbaec61ee9..c6684e285e 100644
--- a/src/test/test_crypto.c
+++ b/src/test/test_crypto.c
@@ -5,6 +5,7 @@
#include "orconfig.h"
#define CRYPTO_CURVE25519_PRIVATE
+#define CRYPTO_PRIVATE
#include "or.h"
#include "test.h"
#include "aes.h"
@@ -15,6 +16,7 @@
#include "ed25519_vectors.inc"
#include <openssl/evp.h>
+#include <openssl/rand.h>
extern const char AUTHORITY_SIGNKEY_3[];
extern const char AUTHORITY_SIGNKEY_A_DIGEST[];
@@ -131,6 +133,38 @@ test_crypto_rng_range(void *arg)
;
}
+/* Test for rectifying openssl RAND engine. */
+static void
+test_crypto_rng_engine(void *arg)
+{
+ (void)arg;
+ RAND_METHOD dummy_method;
+ memset(&dummy_method, 0, sizeof(dummy_method));
+
+ /* We should be a no-op if we're already on RAND_OpenSSL */
+ tt_int_op(0, ==, crypto_force_rand_ssleay());
+ tt_assert(RAND_get_rand_method() == RAND_OpenSSL());
+
+ /* We should correct the method if it's a dummy. */
+ RAND_set_rand_method(&dummy_method);
+#ifdef LIBRESSL_VERSION_NUMBER
+ /* On libressl, you can't override the RNG. */
+ tt_assert(RAND_get_rand_method() == RAND_OpenSSL());
+ tt_int_op(0, ==, crypto_force_rand_ssleay());
+#else
+ tt_assert(RAND_get_rand_method() == &dummy_method);
+ tt_int_op(1, ==, crypto_force_rand_ssleay());
+#endif
+ tt_assert(RAND_get_rand_method() == RAND_OpenSSL());
+
+ /* Make sure we aren't calling dummy_method */
+ crypto_rand((void *) &dummy_method, sizeof(dummy_method));
+ crypto_rand((void *) &dummy_method, sizeof(dummy_method));
+
+ done:
+ ;
+}
+
/** Run unit tests for our AES functionality */
static void
test_crypto_aes(void *arg)
@@ -284,10 +318,11 @@ test_crypto_sha(void *arg)
{
crypto_digest_t *d1 = NULL, *d2 = NULL;
int i;
- char key[160];
- char digest[32];
- char data[50];
- char d_out1[DIGEST_LEN], d_out2[DIGEST256_LEN];
+#define RFC_4231_MAX_KEY_SIZE 131
+ char key[RFC_4231_MAX_KEY_SIZE];
+ char digest[DIGEST256_LEN];
+ char data[DIGEST512_LEN];
+ char d_out1[DIGEST512_LEN], d_out2[DIGEST512_LEN];
char *mem_op_hex_tmp=NULL;
/* Test SHA-1 with a test vector from the specification. */
@@ -302,6 +337,13 @@ test_crypto_sha(void *arg)
"96177A9CB410FF61F20015AD");
tt_int_op(i, OP_EQ, 0);
+ /* Test SHA-512 with a test vector from the specification. */
+ i = crypto_digest512(data, "abc", 3, DIGEST_SHA512);
+ test_memeq_hex(data, "ddaf35a193617abacc417349ae20413112e6fa4e89a97"
+ "ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3"
+ "feebbd454d4423643ce80e2a9ac94fa54ca49f");
+ tt_int_op(i, OP_EQ, 0);
+
/* Test HMAC-SHA256 with test cases from wikipedia and RFC 4231 */
/* Case empty (wikipedia) */
@@ -378,15 +420,15 @@ test_crypto_sha(void *arg)
d2 = crypto_digest_dup(d1);
tt_assert(d2);
crypto_digest_add_bytes(d2, "ghijkl", 6);
- crypto_digest_get_digest(d2, d_out1, sizeof(d_out1));
+ crypto_digest_get_digest(d2, d_out1, DIGEST_LEN);
crypto_digest(d_out2, "abcdefghijkl", 12);
tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST_LEN);
crypto_digest_assign(d2, d1);
crypto_digest_add_bytes(d2, "mno", 3);
- crypto_digest_get_digest(d2, d_out1, sizeof(d_out1));
+ crypto_digest_get_digest(d2, d_out1, DIGEST_LEN);
crypto_digest(d_out2, "abcdefmno", 9);
tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST_LEN);
- crypto_digest_get_digest(d1, d_out1, sizeof(d_out1));
+ crypto_digest_get_digest(d1, d_out1, DIGEST_LEN);
crypto_digest(d_out2, "abcdef", 6);
tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST_LEN);
crypto_digest_free(d1);
@@ -399,17 +441,38 @@ test_crypto_sha(void *arg)
d2 = crypto_digest_dup(d1);
tt_assert(d2);
crypto_digest_add_bytes(d2, "ghijkl", 6);
- crypto_digest_get_digest(d2, d_out1, sizeof(d_out1));
+ crypto_digest_get_digest(d2, d_out1, DIGEST256_LEN);
crypto_digest256(d_out2, "abcdefghijkl", 12, DIGEST_SHA256);
- tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST_LEN);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST256_LEN);
crypto_digest_assign(d2, d1);
crypto_digest_add_bytes(d2, "mno", 3);
- crypto_digest_get_digest(d2, d_out1, sizeof(d_out1));
+ crypto_digest_get_digest(d2, d_out1, DIGEST256_LEN);
crypto_digest256(d_out2, "abcdefmno", 9, DIGEST_SHA256);
- tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST_LEN);
- crypto_digest_get_digest(d1, d_out1, sizeof(d_out1));
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST256_LEN);
+ crypto_digest_get_digest(d1, d_out1, DIGEST256_LEN);
crypto_digest256(d_out2, "abcdef", 6, DIGEST_SHA256);
- tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST_LEN);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST256_LEN);
+ crypto_digest_free(d1);
+ crypto_digest_free(d2);
+
+ /* Incremental digest code with sha512 */
+ d1 = crypto_digest512_new(DIGEST_SHA512);
+ tt_assert(d1);
+ crypto_digest_add_bytes(d1, "abcdef", 6);
+ d2 = crypto_digest_dup(d1);
+ tt_assert(d2);
+ crypto_digest_add_bytes(d2, "ghijkl", 6);
+ crypto_digest_get_digest(d2, d_out1, DIGEST512_LEN);
+ crypto_digest512(d_out2, "abcdefghijkl", 12, DIGEST_SHA512);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST512_LEN);
+ crypto_digest_assign(d2, d1);
+ crypto_digest_add_bytes(d2, "mno", 3);
+ crypto_digest_get_digest(d2, d_out1, DIGEST512_LEN);
+ crypto_digest512(d_out2, "abcdefmno", 9, DIGEST_SHA512);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST512_LEN);
+ crypto_digest_get_digest(d1, d_out1, DIGEST512_LEN);
+ crypto_digest512(d_out2, "abcdef", 6, DIGEST_SHA512);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST512_LEN);
done:
if (d1)
@@ -419,6 +482,394 @@ test_crypto_sha(void *arg)
tor_free(mem_op_hex_tmp);
}
+static void
+test_crypto_sha3(void *arg)
+{
+ crypto_digest_t *d1 = NULL, *d2 = NULL;
+ int i;
+ char data[DIGEST512_LEN];
+ char d_out1[DIGEST512_LEN], d_out2[DIGEST512_LEN];
+ char *mem_op_hex_tmp=NULL;
+ char *large = NULL;
+
+ (void)arg;
+
+ /* Test SHA3-[256,512] with a test vectors from the Keccak Code Package.
+ *
+ * NB: The code package's test vectors have length expressed in bits.
+ */
+
+ /* Len = 8, Msg = CC */
+ const uint8_t keccak_kat_msg8[] = { 0xcc };
+ i = crypto_digest256(data, (const char*)keccak_kat_msg8, 1, DIGEST_SHA3_256);
+ test_memeq_hex(data, "677035391CD3701293D385F037BA3279"
+ "6252BB7CE180B00B582DD9B20AAAD7F0");
+ tt_int_op(i, OP_EQ, 0);
+ i = crypto_digest512(data, (const char*)keccak_kat_msg8, 1, DIGEST_SHA3_512);
+ test_memeq_hex(data, "3939FCC8B57B63612542DA31A834E5DC"
+ "C36E2EE0F652AC72E02624FA2E5ADEEC"
+ "C7DD6BB3580224B4D6138706FC6E8059"
+ "7B528051230B00621CC2B22999EAA205");
+ tt_int_op(i, OP_EQ, 0);
+
+ /* Len = 24, Msg = 1F877C */
+ const uint8_t keccak_kat_msg24[] = { 0x1f, 0x87, 0x7c };
+ i = crypto_digest256(data, (const char*)keccak_kat_msg24, 3,
+ DIGEST_SHA3_256);
+ test_memeq_hex(data, "BC22345E4BD3F792A341CF18AC0789F1"
+ "C9C966712A501B19D1B6632CCD408EC5");
+ tt_int_op(i, OP_EQ, 0);
+ i = crypto_digest512(data, (const char*)keccak_kat_msg24, 3,
+ DIGEST_SHA3_512);
+ test_memeq_hex(data, "CB20DCF54955F8091111688BECCEF48C"
+ "1A2F0D0608C3A575163751F002DB30F4"
+ "0F2F671834B22D208591CFAF1F5ECFE4"
+ "3C49863A53B3225BDFD7C6591BA7658B");
+ tt_int_op(i, OP_EQ, 0);
+
+ /* Len = 1080, Msg = B771D5CEF... ...C35AC81B5 (SHA3-256 rate - 1) */
+ const uint8_t keccak_kat_msg1080[] = {
+ 0xB7, 0x71, 0xD5, 0xCE, 0xF5, 0xD1, 0xA4, 0x1A, 0x93, 0xD1,
+ 0x56, 0x43, 0xD7, 0x18, 0x1D, 0x2A, 0x2E, 0xF0, 0xA8, 0xE8,
+ 0x4D, 0x91, 0x81, 0x2F, 0x20, 0xED, 0x21, 0xF1, 0x47, 0xBE,
+ 0xF7, 0x32, 0xBF, 0x3A, 0x60, 0xEF, 0x40, 0x67, 0xC3, 0x73,
+ 0x4B, 0x85, 0xBC, 0x8C, 0xD4, 0x71, 0x78, 0x0F, 0x10, 0xDC,
+ 0x9E, 0x82, 0x91, 0xB5, 0x83, 0x39, 0xA6, 0x77, 0xB9, 0x60,
+ 0x21, 0x8F, 0x71, 0xE7, 0x93, 0xF2, 0x79, 0x7A, 0xEA, 0x34,
+ 0x94, 0x06, 0x51, 0x28, 0x29, 0x06, 0x5D, 0x37, 0xBB, 0x55,
+ 0xEA, 0x79, 0x6F, 0xA4, 0xF5, 0x6F, 0xD8, 0x89, 0x6B, 0x49,
+ 0xB2, 0xCD, 0x19, 0xB4, 0x32, 0x15, 0xAD, 0x96, 0x7C, 0x71,
+ 0x2B, 0x24, 0xE5, 0x03, 0x2D, 0x06, 0x52, 0x32, 0xE0, 0x2C,
+ 0x12, 0x74, 0x09, 0xD2, 0xED, 0x41, 0x46, 0xB9, 0xD7, 0x5D,
+ 0x76, 0x3D, 0x52, 0xDB, 0x98, 0xD9, 0x49, 0xD3, 0xB0, 0xFE,
+ 0xD6, 0xA8, 0x05, 0x2F, 0xBB,
+ };
+ i = crypto_digest256(data, (const char*)keccak_kat_msg1080, 135,
+ DIGEST_SHA3_256);
+ test_memeq_hex(data, "A19EEE92BB2097B64E823D597798AA18"
+ "BE9B7C736B8059ABFD6779AC35AC81B5");
+ tt_int_op(i, OP_EQ, 0);
+ i = crypto_digest512(data, (const char*)keccak_kat_msg1080, 135,
+ DIGEST_SHA3_512);
+ test_memeq_hex(data, "7575A1FB4FC9A8F9C0466BD5FCA496D1"
+ "CB78696773A212A5F62D02D14E3259D1"
+ "92A87EBA4407DD83893527331407B6DA"
+ "DAAD920DBC46489B677493CE5F20B595");
+ tt_int_op(i, OP_EQ, 0);
+
+ /* Len = 1088, Msg = B32D95B0... ...8E380C04 (SHA3-256 rate) */
+ const uint8_t keccak_kat_msg1088[] = {
+ 0xB3, 0x2D, 0x95, 0xB0, 0xB9, 0xAA, 0xD2, 0xA8, 0x81, 0x6D,
+ 0xE6, 0xD0, 0x6D, 0x1F, 0x86, 0x00, 0x85, 0x05, 0xBD, 0x8C,
+ 0x14, 0x12, 0x4F, 0x6E, 0x9A, 0x16, 0x3B, 0x5A, 0x2A, 0xDE,
+ 0x55, 0xF8, 0x35, 0xD0, 0xEC, 0x38, 0x80, 0xEF, 0x50, 0x70,
+ 0x0D, 0x3B, 0x25, 0xE4, 0x2C, 0xC0, 0xAF, 0x05, 0x0C, 0xCD,
+ 0x1B, 0xE5, 0xE5, 0x55, 0xB2, 0x30, 0x87, 0xE0, 0x4D, 0x7B,
+ 0xF9, 0x81, 0x36, 0x22, 0x78, 0x0C, 0x73, 0x13, 0xA1, 0x95,
+ 0x4F, 0x87, 0x40, 0xB6, 0xEE, 0x2D, 0x3F, 0x71, 0xF7, 0x68,
+ 0xDD, 0x41, 0x7F, 0x52, 0x04, 0x82, 0xBD, 0x3A, 0x08, 0xD4,
+ 0xF2, 0x22, 0xB4, 0xEE, 0x9D, 0xBD, 0x01, 0x54, 0x47, 0xB3,
+ 0x35, 0x07, 0xDD, 0x50, 0xF3, 0xAB, 0x42, 0x47, 0xC5, 0xDE,
+ 0x9A, 0x8A, 0xBD, 0x62, 0xA8, 0xDE, 0xCE, 0xA0, 0x1E, 0x3B,
+ 0x87, 0xC8, 0xB9, 0x27, 0xF5, 0xB0, 0x8B, 0xEB, 0x37, 0x67,
+ 0x4C, 0x6F, 0x8E, 0x38, 0x0C, 0x04,
+ };
+ i = crypto_digest256(data, (const char*)keccak_kat_msg1088, 136,
+ DIGEST_SHA3_256);
+ test_memeq_hex(data, "DF673F4105379FF6B755EEAB20CEB0DC"
+ "77B5286364FE16C59CC8A907AFF07732");
+ tt_int_op(i, OP_EQ, 0);
+ i = crypto_digest512(data, (const char*)keccak_kat_msg1088, 136,
+ DIGEST_SHA3_512);
+ test_memeq_hex(data, "2E293765022D48996CE8EFF0BE54E87E"
+ "FB94A14C72DE5ACD10D0EB5ECE029CAD"
+ "FA3BA17A40B2FFA2163991B17786E51C"
+ "ABA79E5E0FFD34CF085E2A098BE8BACB");
+ tt_int_op(i, OP_EQ, 0);
+
+ /* Len = 1096, Msg = 04410E310... ...601016A0D (SHA3-256 rate + 1) */
+ const uint8_t keccak_kat_msg1096[] = {
+ 0x04, 0x41, 0x0E, 0x31, 0x08, 0x2A, 0x47, 0x58, 0x4B, 0x40,
+ 0x6F, 0x05, 0x13, 0x98, 0xA6, 0xAB, 0xE7, 0x4E, 0x4D, 0xA5,
+ 0x9B, 0xB6, 0xF8, 0x5E, 0x6B, 0x49, 0xE8, 0xA1, 0xF7, 0xF2,
+ 0xCA, 0x00, 0xDF, 0xBA, 0x54, 0x62, 0xC2, 0xCD, 0x2B, 0xFD,
+ 0xE8, 0xB6, 0x4F, 0xB2, 0x1D, 0x70, 0xC0, 0x83, 0xF1, 0x13,
+ 0x18, 0xB5, 0x6A, 0x52, 0xD0, 0x3B, 0x81, 0xCA, 0xC5, 0xEE,
+ 0xC2, 0x9E, 0xB3, 0x1B, 0xD0, 0x07, 0x8B, 0x61, 0x56, 0x78,
+ 0x6D, 0xA3, 0xD6, 0xD8, 0xC3, 0x30, 0x98, 0xC5, 0xC4, 0x7B,
+ 0xB6, 0x7A, 0xC6, 0x4D, 0xB1, 0x41, 0x65, 0xAF, 0x65, 0xB4,
+ 0x45, 0x44, 0xD8, 0x06, 0xDD, 0xE5, 0xF4, 0x87, 0xD5, 0x37,
+ 0x3C, 0x7F, 0x97, 0x92, 0xC2, 0x99, 0xE9, 0x68, 0x6B, 0x7E,
+ 0x58, 0x21, 0xE7, 0xC8, 0xE2, 0x45, 0x83, 0x15, 0xB9, 0x96,
+ 0xB5, 0x67, 0x7D, 0x92, 0x6D, 0xAC, 0x57, 0xB3, 0xF2, 0x2D,
+ 0xA8, 0x73, 0xC6, 0x01, 0x01, 0x6A, 0x0D,
+ };
+ i = crypto_digest256(data, (const char*)keccak_kat_msg1096, 137,
+ DIGEST_SHA3_256);
+ test_memeq_hex(data, "D52432CF3B6B4B949AA848E058DCD62D"
+ "735E0177279222E7AC0AF8504762FAA0");
+ tt_int_op(i, OP_EQ, 0);
+ i = crypto_digest512(data, (const char*)keccak_kat_msg1096, 137,
+ DIGEST_SHA3_512);
+ test_memeq_hex(data, "BE8E14B6757FFE53C9B75F6DDE9A7B6C"
+ "40474041DE83D4A60645A826D7AF1ABE"
+ "1EEFCB7B74B62CA6A514E5F2697D585B"
+ "FECECE12931BBE1D4ED7EBF7B0BE660E");
+ tt_int_op(i, OP_EQ, 0);
+
+ /* Len = 1144, Msg = EA40E83C... ...66DFAFEC (SHA3-512 rate *2 - 1) */
+ const uint8_t keccak_kat_msg1144[] = {
+ 0xEA, 0x40, 0xE8, 0x3C, 0xB1, 0x8B, 0x3A, 0x24, 0x2C, 0x1E,
+ 0xCC, 0x6C, 0xCD, 0x0B, 0x78, 0x53, 0xA4, 0x39, 0xDA, 0xB2,
+ 0xC5, 0x69, 0xCF, 0xC6, 0xDC, 0x38, 0xA1, 0x9F, 0x5C, 0x90,
+ 0xAC, 0xBF, 0x76, 0xAE, 0xF9, 0xEA, 0x37, 0x42, 0xFF, 0x3B,
+ 0x54, 0xEF, 0x7D, 0x36, 0xEB, 0x7C, 0xE4, 0xFF, 0x1C, 0x9A,
+ 0xB3, 0xBC, 0x11, 0x9C, 0xFF, 0x6B, 0xE9, 0x3C, 0x03, 0xE2,
+ 0x08, 0x78, 0x33, 0x35, 0xC0, 0xAB, 0x81, 0x37, 0xBE, 0x5B,
+ 0x10, 0xCD, 0xC6, 0x6F, 0xF3, 0xF8, 0x9A, 0x1B, 0xDD, 0xC6,
+ 0xA1, 0xEE, 0xD7, 0x4F, 0x50, 0x4C, 0xBE, 0x72, 0x90, 0x69,
+ 0x0B, 0xB2, 0x95, 0xA8, 0x72, 0xB9, 0xE3, 0xFE, 0x2C, 0xEE,
+ 0x9E, 0x6C, 0x67, 0xC4, 0x1D, 0xB8, 0xEF, 0xD7, 0xD8, 0x63,
+ 0xCF, 0x10, 0xF8, 0x40, 0xFE, 0x61, 0x8E, 0x79, 0x36, 0xDA,
+ 0x3D, 0xCA, 0x5C, 0xA6, 0xDF, 0x93, 0x3F, 0x24, 0xF6, 0x95,
+ 0x4B, 0xA0, 0x80, 0x1A, 0x12, 0x94, 0xCD, 0x8D, 0x7E, 0x66,
+ 0xDF, 0xAF, 0xEC,
+ };
+ i = crypto_digest512(data, (const char*)keccak_kat_msg1144, 143,
+ DIGEST_SHA3_512);
+ test_memeq_hex(data, "3A8E938C45F3F177991296B24565D9A6"
+ "605516615D96A062C8BE53A0D6C5A648"
+ "7BE35D2A8F3CF6620D0C2DBA2C560D68"
+ "295F284BE7F82F3B92919033C9CE5D80");
+ tt_int_op(i, OP_EQ, 0);
+ i = crypto_digest256(data, (const char*)keccak_kat_msg1144, 143,
+ DIGEST_SHA3_256);
+ test_memeq_hex(data, "E58A947E98D6DD7E932D2FE02D9992E6"
+ "118C0C2C606BDCDA06E7943D2C95E0E5");
+ tt_int_op(i, OP_EQ, 0);
+
+ /* Len = 1152, Msg = 157D5B7E... ...79EE00C63 (SHA3-512 rate * 2) */
+ const uint8_t keccak_kat_msg1152[] = {
+ 0x15, 0x7D, 0x5B, 0x7E, 0x45, 0x07, 0xF6, 0x6D, 0x9A, 0x26,
+ 0x74, 0x76, 0xD3, 0x38, 0x31, 0xE7, 0xBB, 0x76, 0x8D, 0x4D,
+ 0x04, 0xCC, 0x34, 0x38, 0xDA, 0x12, 0xF9, 0x01, 0x02, 0x63,
+ 0xEA, 0x5F, 0xCA, 0xFB, 0xDE, 0x25, 0x79, 0xDB, 0x2F, 0x6B,
+ 0x58, 0xF9, 0x11, 0xD5, 0x93, 0xD5, 0xF7, 0x9F, 0xB0, 0x5F,
+ 0xE3, 0x59, 0x6E, 0x3F, 0xA8, 0x0F, 0xF2, 0xF7, 0x61, 0xD1,
+ 0xB0, 0xE5, 0x70, 0x80, 0x05, 0x5C, 0x11, 0x8C, 0x53, 0xE5,
+ 0x3C, 0xDB, 0x63, 0x05, 0x52, 0x61, 0xD7, 0xC9, 0xB2, 0xB3,
+ 0x9B, 0xD9, 0x0A, 0xCC, 0x32, 0x52, 0x0C, 0xBB, 0xDB, 0xDA,
+ 0x2C, 0x4F, 0xD8, 0x85, 0x6D, 0xBC, 0xEE, 0x17, 0x31, 0x32,
+ 0xA2, 0x67, 0x91, 0x98, 0xDA, 0xF8, 0x30, 0x07, 0xA9, 0xB5,
+ 0xC5, 0x15, 0x11, 0xAE, 0x49, 0x76, 0x6C, 0x79, 0x2A, 0x29,
+ 0x52, 0x03, 0x88, 0x44, 0x4E, 0xBE, 0xFE, 0x28, 0x25, 0x6F,
+ 0xB3, 0x3D, 0x42, 0x60, 0x43, 0x9C, 0xBA, 0x73, 0xA9, 0x47,
+ 0x9E, 0xE0, 0x0C, 0x63,
+ };
+ i = crypto_digest512(data, (const char*)keccak_kat_msg1152, 144,
+ DIGEST_SHA3_512);
+ test_memeq_hex(data, "FE45289874879720CE2A844AE34BB735"
+ "22775DCB6019DCD22B8885994672A088"
+ "9C69E8115C641DC8B83E39F7311815A1"
+ "64DC46E0BA2FCA344D86D4BC2EF2532C");
+ tt_int_op(i, OP_EQ, 0);
+ i = crypto_digest256(data, (const char*)keccak_kat_msg1152, 144,
+ DIGEST_SHA3_256);
+ test_memeq_hex(data, "A936FB9AF87FB67857B3EAD5C76226AD"
+ "84DA47678F3C2FFE5A39FDB5F7E63FFB");
+ tt_int_op(i, OP_EQ, 0);
+
+ /* Len = 1160, Msg = 836B34B5... ...11044C53 (SHA3-512 rate * 2 + 1) */
+ const uint8_t keccak_kat_msg1160[] = {
+ 0x83, 0x6B, 0x34, 0xB5, 0x15, 0x47, 0x6F, 0x61, 0x3F, 0xE4,
+ 0x47, 0xA4, 0xE0, 0xC3, 0xF3, 0xB8, 0xF2, 0x09, 0x10, 0xAC,
+ 0x89, 0xA3, 0x97, 0x70, 0x55, 0xC9, 0x60, 0xD2, 0xD5, 0xD2,
+ 0xB7, 0x2B, 0xD8, 0xAC, 0xC7, 0x15, 0xA9, 0x03, 0x53, 0x21,
+ 0xB8, 0x67, 0x03, 0xA4, 0x11, 0xDD, 0xE0, 0x46, 0x6D, 0x58,
+ 0xA5, 0x97, 0x69, 0x67, 0x2A, 0xA6, 0x0A, 0xD5, 0x87, 0xB8,
+ 0x48, 0x1D, 0xE4, 0xBB, 0xA5, 0x52, 0xA1, 0x64, 0x57, 0x79,
+ 0x78, 0x95, 0x01, 0xEC, 0x53, 0xD5, 0x40, 0xB9, 0x04, 0x82,
+ 0x1F, 0x32, 0xB0, 0xBD, 0x18, 0x55, 0xB0, 0x4E, 0x48, 0x48,
+ 0xF9, 0xF8, 0xCF, 0xE9, 0xEB, 0xD8, 0x91, 0x1B, 0xE9, 0x57,
+ 0x81, 0xA7, 0x59, 0xD7, 0xAD, 0x97, 0x24, 0xA7, 0x10, 0x2D,
+ 0xBE, 0x57, 0x67, 0x76, 0xB7, 0xC6, 0x32, 0xBC, 0x39, 0xB9,
+ 0xB5, 0xE1, 0x90, 0x57, 0xE2, 0x26, 0x55, 0x2A, 0x59, 0x94,
+ 0xC1, 0xDB, 0xB3, 0xB5, 0xC7, 0x87, 0x1A, 0x11, 0xF5, 0x53,
+ 0x70, 0x11, 0x04, 0x4C, 0x53,
+ };
+ i = crypto_digest512(data, (const char*)keccak_kat_msg1160, 145,
+ DIGEST_SHA3_512);
+ test_memeq_hex(data, "AFF61C6E11B98E55AC213B1A0BC7DE04"
+ "05221AC5EFB1229842E4614F4A029C9B"
+ "D14A0ED7FD99AF3681429F3F309FDB53"
+ "166AA9A3CD9F1F1223D04B4A9015E94A");
+ tt_int_op(i, OP_EQ, 0);
+ i = crypto_digest256(data, (const char*)keccak_kat_msg1160, 145,
+ DIGEST_SHA3_256);
+ test_memeq_hex(data, "3A654B88F88086C2751EDAE6D3924814"
+ "3CF6235C6B0B7969342C45A35194B67E");
+ tt_int_op(i, OP_EQ, 0);
+
+ /* SHA3-[256,512] Empty case (wikipedia) */
+ i = crypto_digest256(data, "", 0, DIGEST_SHA3_256);
+ test_memeq_hex(data, "a7ffc6f8bf1ed76651c14756a061d662"
+ "f580ff4de43b49fa82d80a4b80f8434a");
+ tt_int_op(i, OP_EQ, 0);
+ i = crypto_digest512(data, "", 0, DIGEST_SHA3_512);
+ test_memeq_hex(data, "a69f73cca23a9ac5c8b567dc185a756e"
+ "97c982164fe25859e0d1dcc1475c80a6"
+ "15b2123af1f5f94c11e3e9402c3ac558"
+ "f500199d95b6d3e301758586281dcd26");
+ tt_int_op(i, OP_EQ, 0);
+
+ /* Incremental digest code with SHA3-256 */
+ d1 = crypto_digest256_new(DIGEST_SHA3_256);
+ tt_assert(d1);
+ crypto_digest_add_bytes(d1, "abcdef", 6);
+ d2 = crypto_digest_dup(d1);
+ tt_assert(d2);
+ crypto_digest_add_bytes(d2, "ghijkl", 6);
+ crypto_digest_get_digest(d2, d_out1, DIGEST256_LEN);
+ crypto_digest256(d_out2, "abcdefghijkl", 12, DIGEST_SHA3_256);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST256_LEN);
+ crypto_digest_assign(d2, d1);
+ crypto_digest_add_bytes(d2, "mno", 3);
+ crypto_digest_get_digest(d2, d_out1, DIGEST256_LEN);
+ crypto_digest256(d_out2, "abcdefmno", 9, DIGEST_SHA3_256);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST256_LEN);
+ crypto_digest_get_digest(d1, d_out1, DIGEST256_LEN);
+ crypto_digest256(d_out2, "abcdef", 6, DIGEST_SHA3_256);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST256_LEN);
+ crypto_digest_free(d1);
+ crypto_digest_free(d2);
+
+ /* Incremental digest code with SHA3-512 */
+ d1 = crypto_digest512_new(DIGEST_SHA3_512);
+ tt_assert(d1);
+ crypto_digest_add_bytes(d1, "abcdef", 6);
+ d2 = crypto_digest_dup(d1);
+ tt_assert(d2);
+ crypto_digest_add_bytes(d2, "ghijkl", 6);
+ crypto_digest_get_digest(d2, d_out1, DIGEST512_LEN);
+ crypto_digest512(d_out2, "abcdefghijkl", 12, DIGEST_SHA3_512);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST512_LEN);
+ crypto_digest_assign(d2, d1);
+ crypto_digest_add_bytes(d2, "mno", 3);
+ crypto_digest_get_digest(d2, d_out1, DIGEST512_LEN);
+ crypto_digest512(d_out2, "abcdefmno", 9, DIGEST_SHA3_512);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST512_LEN);
+ crypto_digest_get_digest(d1, d_out1, DIGEST512_LEN);
+ crypto_digest512(d_out2, "abcdef", 6, DIGEST_SHA3_512);
+ tt_mem_op(d_out1,OP_EQ, d_out2, DIGEST512_LEN);
+ crypto_digest_free(d1);
+
+ /* Attempt to exercise the incremental hashing code by creating a randomized
+ * 100 KiB buffer, and hashing rand[1, 5 * Rate] bytes at a time. SHA3-512
+ * is used because it has a lowest rate of the family (the code is common,
+ * but the slower rate exercises more of it).
+ */
+ const size_t bufsz = 100 * 1024;
+ size_t j = 0;
+ large = tor_malloc(bufsz);
+ crypto_rand(large, bufsz);
+ d1 = crypto_digest512_new(DIGEST_SHA3_512); /* Running digest. */
+ while (j < bufsz) {
+ /* Pick how much data to add to the running digest. */
+ size_t incr = (size_t)crypto_rand_int_range(1, 72 * 5);
+ incr = MIN(bufsz - j, incr);
+
+ /* Add the data, and calculate the hash. */
+ crypto_digest_add_bytes(d1, large + j, incr);
+ crypto_digest_get_digest(d1, d_out1, DIGEST512_LEN);
+
+ /* One-shot hash the buffer up to the data that was just added,
+ * and ensure that the values match up.
+ *
+ * XXX/yawning: If this actually fails, it'll be rather difficult to
+ * reproduce. Improvements welcome.
+ */
+ i = crypto_digest512(d_out2, large, j + incr, DIGEST_SHA3_512);
+ tt_int_op(i, OP_EQ, 0);
+ tt_mem_op(d_out1, OP_EQ, d_out2, DIGEST512_LEN);
+
+ j += incr;
+ }
+
+ done:
+ if (d1)
+ crypto_digest_free(d1);
+ if (d2)
+ crypto_digest_free(d2);
+ tor_free(large);
+ tor_free(mem_op_hex_tmp);
+}
+
+/** Run unit tests for our XOF. */
+static void
+test_crypto_sha3_xof(void *arg)
+{
+ uint8_t msg[255];
+ uint8_t out[512];
+ crypto_xof_t *xof;
+ char *mem_op_hex_tmp=NULL;
+
+ (void)arg;
+
+ /* SHAKE256 test vector (Len = 2040) from the Keccak Code Package. */
+ base16_decode((char *)msg, 255,
+ "3A3A819C48EFDE2AD914FBF00E18AB6BC4F14513AB27D0C178A188B61431"
+ "E7F5623CB66B23346775D386B50E982C493ADBBFC54B9A3CD383382336A1"
+ "A0B2150A15358F336D03AE18F666C7573D55C4FD181C29E6CCFDE63EA35F"
+ "0ADF5885CFC0A3D84A2B2E4DD24496DB789E663170CEF74798AA1BBCD457"
+ "4EA0BBA40489D764B2F83AADC66B148B4A0CD95246C127D5871C4F114186"
+ "90A5DDF01246A0C80A43C70088B6183639DCFDA4125BD113A8F49EE23ED3"
+ "06FAAC576C3FB0C1E256671D817FC2534A52F5B439F72E424DE376F4C565"
+ "CCA82307DD9EF76DA5B7C4EB7E085172E328807C02D011FFBF33785378D7"
+ "9DC266F6A5BE6BB0E4A92ECEEBAEB1", 510);
+ const char *squeezed_hex =
+ "8A5199B4A7E133E264A86202720655894D48CFF344A928CF8347F48379CE"
+ "F347DFC5BCFFAB99B27B1F89AA2735E23D30088FFA03B9EDB02B9635470A"
+ "B9F1038985D55F9CA774572DD006470EA65145469609F9FA0831BF1FFD84"
+ "2DC24ACADE27BD9816E3B5BF2876CB112232A0EB4475F1DFF9F5C713D9FF"
+ "D4CCB89AE5607FE35731DF06317949EEF646E9591CF3BE53ADD6B7DD2B60"
+ "96E2B3FB06E662EC8B2D77422DAAD9463CD155204ACDBD38E319613F39F9"
+ "9B6DFB35CA9365160066DB19835888C2241FF9A731A4ACBB5663727AAC34"
+ "A401247FBAA7499E7D5EE5B69D31025E63D04C35C798BCA1262D5673A9CF"
+ "0930B5AD89BD485599DC184528DA4790F088EBD170B635D9581632D2FF90"
+ "DB79665CED430089AF13C9F21F6D443A818064F17AEC9E9C5457001FA8DC"
+ "6AFBADBE3138F388D89D0E6F22F66671255B210754ED63D81DCE75CE8F18"
+ "9B534E6D6B3539AA51E837C42DF9DF59C71E6171CD4902FE1BDC73FB1775"
+ "B5C754A1ED4EA7F3105FC543EE0418DAD256F3F6118EA77114A16C15355B"
+ "42877A1DB2A7DF0E155AE1D8670ABCEC3450F4E2EEC9838F895423EF63D2"
+ "61138BAAF5D9F104CB5A957AEA06C0B9B8C78B0D441796DC0350DDEABB78"
+ "A33B6F1F9E68EDE3D1805C7B7E2CFD54E0FAD62F0D8CA67A775DC4546AF9"
+ "096F2EDB221DB42843D65327861282DC946A0BA01A11863AB2D1DFD16E39"
+ "73D4";
+
+ /* Test oneshot absorb/squeeze. */
+ xof = crypto_xof_new();
+ tt_assert(xof);
+ crypto_xof_add_bytes(xof, msg, sizeof(msg));
+ crypto_xof_squeeze_bytes(xof, out, sizeof(out));
+ test_memeq_hex(out, squeezed_hex);
+ crypto_xof_free(xof);
+ memset(out, 0, sizeof(out));
+
+ /* Test incremental absorb/squeeze. */
+ xof = crypto_xof_new();
+ tt_assert(xof);
+ for (size_t i = 0; i < sizeof(msg); i++)
+ crypto_xof_add_bytes(xof, msg + i, 1);
+ for (size_t i = 0; i < sizeof(out); i++)
+ crypto_xof_squeeze_bytes(xof, out + i, 1);
+ test_memeq_hex(out, squeezed_hex);
+
+ done:
+ if (xof)
+ crypto_xof_free(xof);
+ tor_free(mem_op_hex_tmp);
+}
+
/** Run unit tests for our public key crypto functions */
static void
test_crypto_pk(void *arg)
@@ -639,7 +1090,7 @@ test_crypto_digests(void *arg)
{
crypto_pk_t *k = NULL;
ssize_t r;
- digests_t pkey_digests;
+ common_digests_t pkey_digests;
char digest[DIGEST_LEN];
(void)arg;
@@ -653,7 +1104,7 @@ test_crypto_digests(void *arg)
tt_mem_op(hex_str(digest, DIGEST_LEN),OP_EQ,
AUTHORITY_SIGNKEY_A_DIGEST, HEX_DIGEST_LEN);
- r = crypto_pk_get_all_digests(k, &pkey_digests);
+ r = crypto_pk_get_common_digests(k, &pkey_digests);
tt_mem_op(hex_str(pkey_digests.d[DIGEST_SHA1], DIGEST_LEN),OP_EQ,
AUTHORITY_SIGNKEY_A_DIGEST, HEX_DIGEST_LEN);
@@ -663,6 +1114,11 @@ test_crypto_digests(void *arg)
crypto_pk_free(k);
}
+#ifndef OPENSSL_1_1_API
+#define EVP_ENCODE_CTX_new() tor_malloc_zero(sizeof(EVP_ENCODE_CTX))
+#define EVP_ENCODE_CTX_free(ctx) tor_free(ctx)
+#endif
+
/** Encode src into dest with OpenSSL's EVP Encode interface, returning the
* length of the encoded data in bytes.
*/
@@ -670,12 +1126,13 @@ static int
base64_encode_evp(char *dest, char *src, size_t srclen)
{
const unsigned char *s = (unsigned char*)src;
- EVP_ENCODE_CTX ctx;
+ EVP_ENCODE_CTX *ctx = EVP_ENCODE_CTX_new();
int len, ret;
- EVP_EncodeInit(&ctx);
- EVP_EncodeUpdate(&ctx, (unsigned char *)dest, &len, s, (int)srclen);
- EVP_EncodeFinal(&ctx, (unsigned char *)(dest + len), &ret);
+ EVP_EncodeInit(ctx);
+ EVP_EncodeUpdate(ctx, (unsigned char *)dest, &len, s, (int)srclen);
+ EVP_EncodeFinal(ctx, (unsigned char *)(dest + len), &ret);
+ EVP_ENCODE_CTX_free(ctx);
return ret+ len;
}
@@ -1271,6 +1728,24 @@ test_crypto_curve25519_persist(void *arg)
tor_free(tag);
}
+static void *
+ed25519_testcase_setup(const struct testcase_t *testcase)
+{
+ crypto_ed25519_testing_force_impl(testcase->setup_data);
+ return testcase->setup_data;
+}
+static int
+ed25519_testcase_cleanup(const struct testcase_t *testcase, void *ptr)
+{
+ (void)testcase;
+ (void)ptr;
+ crypto_ed25519_testing_restore_impl();
+ return 1;
+}
+static const struct testcase_setup_t ed25519_test_setup = {
+ ed25519_testcase_setup, ed25519_testcase_cleanup
+};
+
static void
test_crypto_ed25519_simple(void *arg)
{
@@ -1803,13 +2278,126 @@ test_crypto_siphash(void *arg)
;
}
+/* We want the likelihood that the random buffer exhibits any regular pattern
+ * to be far less than the memory bit error rate in the int return value.
+ * Using 2048 bits provides a failure rate of 1/(3 * 10^616), and we call
+ * 3 functions, leading to an overall error rate of 1/10^616.
+ * This is comparable with the 1/10^603 failure rate of test_crypto_rng_range.
+ */
+#define FAILURE_MODE_BUFFER_SIZE (2048/8)
+
+/** Check crypto_rand for a failure mode where it does nothing to the buffer,
+ * or it sets the buffer to all zeroes. Return 0 when the check passes,
+ * or -1 when it fails. */
+static int
+crypto_rand_check_failure_mode_zero(void)
+{
+ char buf[FAILURE_MODE_BUFFER_SIZE];
+
+ memset(buf, 0, FAILURE_MODE_BUFFER_SIZE);
+ crypto_rand(buf, FAILURE_MODE_BUFFER_SIZE);
+
+ for (size_t i = 0; i < FAILURE_MODE_BUFFER_SIZE; i++) {
+ if (buf[i] != 0) {
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/** Check crypto_rand for a failure mode where every int64_t in the buffer is
+ * the same. Return 0 when the check passes, or -1 when it fails. */
+static int
+crypto_rand_check_failure_mode_identical(void)
+{
+ /* just in case the buffer size isn't a multiple of sizeof(int64_t) */
+#define FAILURE_MODE_BUFFER_SIZE_I64 \
+ (FAILURE_MODE_BUFFER_SIZE/SIZEOF_INT64_T)
+#define FAILURE_MODE_BUFFER_SIZE_I64_BYTES \
+ (FAILURE_MODE_BUFFER_SIZE_I64*SIZEOF_INT64_T)
+
+#if FAILURE_MODE_BUFFER_SIZE_I64 < 2
+#error FAILURE_MODE_BUFFER_SIZE needs to be at least 2*SIZEOF_INT64_T
+#endif
+
+ int64_t buf[FAILURE_MODE_BUFFER_SIZE_I64];
+
+ memset(buf, 0, FAILURE_MODE_BUFFER_SIZE_I64_BYTES);
+ crypto_rand((char *)buf, FAILURE_MODE_BUFFER_SIZE_I64_BYTES);
+
+ for (size_t i = 1; i < FAILURE_MODE_BUFFER_SIZE_I64; i++) {
+ if (buf[i] != buf[i-1]) {
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/** Check crypto_rand for a failure mode where it increments the "random"
+ * value by 1 for every byte in the buffer. (This is OpenSSL's PREDICT mode.)
+ * Return 0 when the check passes, or -1 when it fails. */
+static int
+crypto_rand_check_failure_mode_predict(void)
+{
+ unsigned char buf[FAILURE_MODE_BUFFER_SIZE];
+
+ memset(buf, 0, FAILURE_MODE_BUFFER_SIZE);
+ crypto_rand((char *)buf, FAILURE_MODE_BUFFER_SIZE);
+
+ for (size_t i = 1; i < FAILURE_MODE_BUFFER_SIZE; i++) {
+ /* check if the last byte was incremented by 1, including integer
+ * wrapping */
+ if (buf[i] - buf[i-1] != 1 && buf[i-1] - buf[i] != 255) {
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+#undef FAILURE_MODE_BUFFER_SIZE
+
+static void
+test_crypto_failure_modes(void *arg)
+{
+ int rv = 0;
+ (void)arg;
+
+ rv = crypto_early_init();
+ tt_assert(rv == 0);
+
+ /* Check random works */
+ rv = crypto_rand_check_failure_mode_zero();
+ tt_assert(rv == 0);
+
+ rv = crypto_rand_check_failure_mode_identical();
+ tt_assert(rv == 0);
+
+ rv = crypto_rand_check_failure_mode_predict();
+ tt_assert(rv == 0);
+
+ done:
+ ;
+}
+
#define CRYPTO_LEGACY(name) \
{ #name, test_crypto_ ## name , 0, NULL, NULL }
+#define ED25519_TEST_ONE(name, fl, which) \
+ { #name "/ed25519_" which, test_crypto_ed25519_ ## name, (fl), \
+ &ed25519_test_setup, (void*)which }
+
+#define ED25519_TEST(name, fl) \
+ ED25519_TEST_ONE(name, (fl), "donna"), \
+ ED25519_TEST_ONE(name, (fl), "ref10")
+
struct testcase_t crypto_tests[] = {
CRYPTO_LEGACY(formats),
CRYPTO_LEGACY(rng),
{ "rng_range", test_crypto_rng_range, 0, NULL, NULL },
+ { "rng_engine", test_crypto_rng_engine, TT_FORK, NULL, NULL },
{ "aes_AES", test_crypto_aes, TT_FORK, &passthrough_setup, (void*)"aes" },
{ "aes_EVP", test_crypto_aes, TT_FORK, &passthrough_setup, (void*)"evp" },
CRYPTO_LEGACY(sha),
@@ -1817,6 +2405,8 @@ struct testcase_t crypto_tests[] = {
{ "pk_fingerprints", test_crypto_pk_fingerprints, TT_FORK, NULL, NULL },
{ "pk_base64", test_crypto_pk_base64, TT_FORK, NULL, NULL },
CRYPTO_LEGACY(digests),
+ { "sha3", test_crypto_sha3, TT_FORK, NULL, NULL},
+ { "sha3_xof", test_crypto_sha3_xof, TT_FORK, NULL, NULL},
CRYPTO_LEGACY(dh),
{ "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &passthrough_setup,
(void*)"aes" },
@@ -1832,15 +2422,15 @@ struct testcase_t crypto_tests[] = {
{ "curve25519_wrappers", test_crypto_curve25519_wrappers, 0, NULL, NULL },
{ "curve25519_encode", test_crypto_curve25519_encode, 0, NULL, NULL },
{ "curve25519_persist", test_crypto_curve25519_persist, 0, NULL, NULL },
- { "ed25519_simple", test_crypto_ed25519_simple, 0, NULL, NULL },
- { "ed25519_test_vectors", test_crypto_ed25519_test_vectors, 0, NULL, NULL },
- { "ed25519_encode", test_crypto_ed25519_encode, 0, NULL, NULL },
- { "ed25519_convert", test_crypto_ed25519_convert, 0, NULL, NULL },
- { "ed25519_blinding", test_crypto_ed25519_blinding, 0, NULL, NULL },
- { "ed25519_testvectors", test_crypto_ed25519_testvectors, 0, NULL, NULL },
- { "ed25519_fuzz_donna", test_crypto_ed25519_fuzz_donna, TT_FORK, NULL,
- NULL },
+ ED25519_TEST(simple, 0),
+ ED25519_TEST(test_vectors, 0),
+ ED25519_TEST(encode, 0),
+ ED25519_TEST(convert, 0),
+ ED25519_TEST(blinding, 0),
+ ED25519_TEST(testvectors, 0),
+ ED25519_TEST(fuzz_donna, TT_FORK),
{ "siphash", test_crypto_siphash, 0, NULL, NULL },
+ { "failure_modes", test_crypto_failure_modes, TT_FORK, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_dir.c b/src/test/test_dir.c
index 855746e749..72e619f9cd 100644
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@ -6,19 +6,24 @@
#include "orconfig.h"
#include <math.h>
+#define CONFIG_PRIVATE
#define DIRSERV_PRIVATE
#define DIRVOTE_PRIVATE
#define ROUTER_PRIVATE
#define ROUTERLIST_PRIVATE
#define HIBERNATE_PRIVATE
#define NETWORKSTATUS_PRIVATE
+#define RELAY_PRIVATE
+
#include "or.h"
+#include "confparse.h"
#include "config.h"
#include "crypto_ed25519.h"
#include "directory.h"
#include "dirserv.h"
#include "dirvote.h"
#include "hibernate.h"
+#include "memarea.h"
#include "networkstatus.h"
#include "router.h"
#include "routerkeys.h"
@@ -26,7 +31,11 @@
#include "routerparse.h"
#include "routerset.h"
#include "test.h"
+#include "test_dir_common.h"
#include "torcert.h"
+#include "relay.h"
+
+#define NS_MODULE dir
static void
test_dir_nicknames(void *arg)
@@ -110,6 +119,7 @@ test_dir_formats(void *arg)
r1->cache_info.published_on = 0;
r1->or_port = 9000;
r1->dir_port = 9003;
+ r1->supports_tunnelled_dir_requests = 1;
tor_addr_parse(&r1->ipv6_addr, "1:2:3:4::");
r1->ipv6_orport = 9999;
r1->onion_pkey = crypto_pk_dup_key(pk1);
@@ -154,6 +164,7 @@ test_dir_formats(void *arg)
r2->cache_info.published_on = 5;
r2->or_port = 9005;
r2->dir_port = 0;
+ r2->supports_tunnelled_dir_requests = 1;
r2->onion_pkey = crypto_pk_dup_key(pk2);
curve25519_keypair_t r2_onion_keypair;
curve25519_keypair_generate(&r2_onion_keypair, 0);
@@ -174,7 +185,9 @@ test_dir_formats(void *arg)
/* XXXX025 router_dump_to_string should really take this from ri.*/
options->ContactInfo = tor_strdup("Magri White "
"<magri@elsewhere.example.com>");
+
buf = router_dump_router_to_string(r1, pk2, NULL, NULL, NULL);
+
tor_free(options->ContactInfo);
tt_assert(buf);
@@ -200,7 +213,8 @@ test_dir_formats(void *arg)
strlcat(buf2, "hidden-service-dir\n", sizeof(buf2));
strlcat(buf2, "contact Magri White <magri@elsewhere.example.com>\n",
sizeof(buf2));
- strlcat(buf2, "reject *:*\nrouter-signature\n", sizeof(buf2));
+ strlcat(buf2, "reject *:*\n", sizeof(buf2));
+ strlcat(buf2, "tunnelled-dir-server\nrouter-signature\n", sizeof(buf2));
buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same
* twice */
@@ -214,12 +228,13 @@ test_dir_formats(void *arg)
tt_assert(rp1);
tt_int_op(rp1->addr,OP_EQ, r1->addr);
tt_int_op(rp1->or_port,OP_EQ, r1->or_port);
- //test_eq(rp1->dir_port, r1->dir_port);
+ tt_int_op(rp1->dir_port,OP_EQ, r1->dir_port);
tt_int_op(rp1->bandwidthrate,OP_EQ, r1->bandwidthrate);
tt_int_op(rp1->bandwidthburst,OP_EQ, r1->bandwidthburst);
tt_int_op(rp1->bandwidthcapacity,OP_EQ, r1->bandwidthcapacity);
tt_assert(crypto_pk_cmp_keys(rp1->onion_pkey, pk1) == 0);
tt_assert(crypto_pk_cmp_keys(rp1->identity_pkey, pk2) == 0);
+ tt_assert(rp1->supports_tunnelled_dir_requests);
//tt_assert(rp1->exit_policy == NULL);
tor_free(buf);
@@ -290,6 +305,7 @@ test_dir_formats(void *arg)
BASE64_ENCODE_MULTILINE);
strlcat(buf2, cert_buf, sizeof(buf2));
strlcat(buf2, "accept *:80\nreject 18.0.0.0/8:24\n", sizeof(buf2));
+ strlcat(buf2, "tunnelled-dir-server\n", sizeof(buf2));
strlcat(buf2, "router-sig-ed25519 ", sizeof(buf2));
buf = router_dump_router_to_string(r2, pk1, pk2, &r2_onion_keypair, &kp2);
@@ -301,6 +317,8 @@ test_dir_formats(void *arg)
tor_free(buf);
buf = router_dump_router_to_string(r2, pk1, NULL, NULL, NULL);
+
+ /* Reset for later */
cp = buf;
rp2 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL);
tt_assert(rp2);
@@ -315,6 +333,7 @@ test_dir_formats(void *arg)
CURVE25519_PUBKEY_LEN);
tt_assert(crypto_pk_cmp_keys(rp2->onion_pkey, pk2) == 0);
tt_assert(crypto_pk_cmp_keys(rp2->identity_pkey, pk1) == 0);
+ tt_assert(rp2->supports_tunnelled_dir_requests);
tt_int_op(smartlist_len(rp2->exit_policy),OP_EQ, 2);
@@ -478,6 +497,7 @@ test_dir_routerinfo_parsing(void *arg)
#undef CHECK_FAIL
#undef CHECK_OK
done:
+ memarea_clear_freelist();
routerinfo_free(ri);
}
@@ -580,6 +600,8 @@ test_dir_extrainfo_parsing(void *arg)
#undef CHECK_FAIL
done:
+ escaped(NULL);
+ memarea_clear_freelist();
extrainfo_free(ei);
routerinfo_free(ri);
digestmap_free((digestmap_t*)map, routerinfo_free_wrapper_);
@@ -1477,13 +1499,6 @@ test_dir_param_voting(void *arg)
return;
}
-extern const char AUTHORITY_CERT_1[];
-extern const char AUTHORITY_SIGNKEY_1[];
-extern const char AUTHORITY_CERT_2[];
-extern const char AUTHORITY_SIGNKEY_2[];
-extern const char AUTHORITY_CERT_3[];
-extern const char AUTHORITY_SIGNKEY_3[];
-
/** Helper: Test that two networkstatus_voter_info_t do in fact represent the
* same voting authority, and that they do in fact have all the same
* information. */
@@ -1503,42 +1518,6 @@ test_same_voter(networkstatus_voter_info_t *v1,
;
}
-/** Helper: Make a new routerinfo containing the right information for a
- * given vote_routerstatus_t. */
-static routerinfo_t *
-generate_ri_from_rs(const vote_routerstatus_t *vrs)
-{
- routerinfo_t *r;
- const routerstatus_t *rs = &vrs->status;
- static time_t published = 0;
-
- r = tor_malloc_zero(sizeof(routerinfo_t));
- r->cert_expiration_time = TIME_MAX;
- memcpy(r->cache_info.identity_digest, rs->identity_digest, DIGEST_LEN);
- memcpy(r->cache_info.signed_descriptor_digest, rs->descriptor_digest,
- DIGEST_LEN);
- r->cache_info.do_not_cache = 1;
- r->cache_info.routerlist_index = -1;
- r->cache_info.signed_descriptor_body =
- tor_strdup("123456789012345678901234567890123");
- r->cache_info.signed_descriptor_len =
- strlen(r->cache_info.signed_descriptor_body);
- r->exit_policy = smartlist_new();
- r->cache_info.published_on = ++published + time(NULL);
- if (rs->has_bandwidth) {
- /*
- * Multiply by 1000 because the routerinfo_t and the routerstatus_t
- * seem to use different units (*sigh*) and because we seem stuck on
- * icky and perverse decimal kilobytes (*double sigh*) - see
- * router_get_advertised_bandwidth_capped() of routerlist.c and
- * routerstatus_format_entry() of dirserv.c.
- */
- r->bandwidthrate = rs->bandwidth_kb * 1000;
- r->bandwidthcapacity = rs->bandwidth_kb * 1000;
- }
- return r;
-}
-
/** Helper: get a detached signatures document for one or two
* consensuses. */
static char *
@@ -1556,100 +1535,6 @@ get_detached_sigs(networkstatus_t *ns, networkstatus_t *ns2)
return r;
}
-/**
- * Generate a routerstatus for v3_networkstatus test
- */
-static vote_routerstatus_t *
-gen_routerstatus_for_v3ns(int idx, time_t now)
-{
- vote_routerstatus_t *vrs=NULL;
- routerstatus_t *rs;
- tor_addr_t addr_ipv6;
-
- switch (idx) {
- case 0:
- /* Generate the first routerstatus. */
- vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
- rs = &vrs->status;
- vrs->version = tor_strdup("0.1.2.14");
- rs->published_on = now-1500;
- strlcpy(rs->nickname, "router2", sizeof(rs->nickname));
- memset(rs->identity_digest, 3, DIGEST_LEN);
- memset(rs->descriptor_digest, 78, DIGEST_LEN);
- rs->addr = 0x99008801;
- rs->or_port = 443;
- rs->dir_port = 8000;
- /* all flags but running cleared */
- rs->is_flagged_running = 1;
- break;
- case 1:
- /* Generate the second routerstatus. */
- vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
- rs = &vrs->status;
- vrs->version = tor_strdup("0.2.0.5");
- rs->published_on = now-1000;
- strlcpy(rs->nickname, "router1", sizeof(rs->nickname));
- memset(rs->identity_digest, 5, DIGEST_LEN);
- memset(rs->descriptor_digest, 77, DIGEST_LEN);
- rs->addr = 0x99009901;
- rs->or_port = 443;
- rs->dir_port = 0;
- tor_addr_parse(&addr_ipv6, "[1:2:3::4]");
- tor_addr_copy(&rs->ipv6_addr, &addr_ipv6);
- rs->ipv6_orport = 4711;
- rs->is_exit = rs->is_stable = rs->is_fast = rs->is_flagged_running =
- rs->is_valid = rs->is_possible_guard = 1;
- break;
- case 2:
- /* Generate the third routerstatus. */
- vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
- rs = &vrs->status;
- vrs->version = tor_strdup("0.1.0.3");
- rs->published_on = now-1000;
- strlcpy(rs->nickname, "router3", sizeof(rs->nickname));
- memset(rs->identity_digest, 33, DIGEST_LEN);
- memset(rs->descriptor_digest, 79, DIGEST_LEN);
- rs->addr = 0xAA009901;
- rs->or_port = 400;
- rs->dir_port = 9999;
- rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast =
- rs->is_flagged_running = rs->is_valid =
- rs->is_possible_guard = 1;
- break;
- case 3:
- /* Generate a fourth routerstatus that is not running. */
- vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
- rs = &vrs->status;
- vrs->version = tor_strdup("0.1.6.3");
- rs->published_on = now-1000;
- strlcpy(rs->nickname, "router4", sizeof(rs->nickname));
- memset(rs->identity_digest, 34, DIGEST_LEN);
- memset(rs->descriptor_digest, 47, DIGEST_LEN);
- rs->addr = 0xC0000203;
- rs->or_port = 500;
- rs->dir_port = 1999;
- /* Running flag (and others) cleared */
- break;
- case 4:
- /* No more for this test; return NULL */
- vrs = NULL;
- break;
- default:
- /* Shouldn't happen */
- tt_assert(0);
- }
- if (vrs) {
- vrs->microdesc = tor_malloc_zero(sizeof(vote_microdesc_hash_t));
- tor_asprintf(&vrs->microdesc->microdesc_hash_line,
- "m 9,10,11,12,13,14,15,16,17 "
- "sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa%d\n",
- idx);
- }
-
- done:
- return vrs;
-}
-
/** Apply tweaks to the vote list for each voter */
static int
vote_tweaks_for_v3ns(networkstatus_t *v, int voter, time_t now)
@@ -1681,7 +1566,7 @@ vote_tweaks_for_v3ns(networkstatus_t *v, int voter, time_t now)
vrs = smartlist_get(v->routerstatus_list, 0);
memset(vrs->status.descriptor_digest, (int)'Z', DIGEST_LEN);
tt_assert(router_add_to_routerlist(
- generate_ri_from_rs(vrs), &msg,0,0) >= 0);
+ dir_common_generate_ri_from_rs(vrs), &msg,0,0) >= 0);
}
}
@@ -1746,11 +1631,11 @@ test_vrs_for_v3ns(vote_routerstatus_t *vrs, int voter, time_t now)
tt_assert(tor_addr_eq(&rs->ipv6_addr, &addr_ipv6));
tt_int_op(rs->ipv6_orport,OP_EQ, 4711);
if (voter == 1) {
- /* all except "authority" (1) and "v2dir" (64) */
- tt_u64_op(vrs->flags, OP_EQ, U64_LITERAL(190));
+ /* all except "authority" (1) */
+ tt_u64_op(vrs->flags, OP_EQ, U64_LITERAL(254));
} else {
- /* 1023 - authority(1) - madeofcheese(16) - madeoftin(32) - v2dir(256) */
- tt_u64_op(vrs->flags, OP_EQ, U64_LITERAL(718));
+ /* 1023 - authority(1) - madeofcheese(16) - madeoftin(32) */
+ tt_u64_op(vrs->flags, OP_EQ, U64_LITERAL(974));
}
} else if (tor_memeq(rs->identity_digest,
"\x33\x33\x33\x33\x33\x33\x33\x33\x33\x33"
@@ -1820,6 +1705,7 @@ test_routerstatus_for_v3ns(routerstatus_t *rs, time_t now)
tt_assert(rs->is_flagged_running);
tt_assert(!rs->is_valid);
tt_assert(!rs->is_named);
+ tt_assert(rs->is_v2_dir);
/* XXXX check version */
} else if (tor_memeq(rs->identity_digest,
"\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5"
@@ -1845,6 +1731,7 @@ test_routerstatus_for_v3ns(routerstatus_t *rs, time_t now)
tt_assert(rs->is_stable);
tt_assert(rs->is_flagged_running);
tt_assert(rs->is_valid);
+ tt_assert(rs->is_v2_dir);
tt_assert(!rs->is_named);
/* XXXX check version */
} else {
@@ -1869,7 +1756,6 @@ test_a_networkstatus(
authority_cert_t *cert1=NULL, *cert2=NULL, *cert3=NULL;
crypto_pk_t *sign_skey_1=NULL, *sign_skey_2=NULL, *sign_skey_3=NULL;
crypto_pk_t *sign_skey_leg1=NULL;
- const char *msg=NULL;
/*
* Sum the non-zero returns from vote_tweaks() we've seen; if vote_tweaks()
* returns non-zero, it changed net_params and we should skip the tests for
@@ -1885,8 +1771,7 @@ test_a_networkstatus(
vote_routerstatus_t *vrs;
routerstatus_t *rs;
int idx, n_rs, n_vrs;
- char *v1_text=NULL, *v2_text=NULL, *v3_text=NULL, *consensus_text=NULL,
- *cp=NULL;
+ char *consensus_text=NULL, *cp=NULL;
smartlist_t *votes = smartlist_new();
/* For generating the two other consensuses. */
@@ -1901,79 +1786,13 @@ test_a_networkstatus(
tt_assert(rs_test);
tt_assert(vrs_test);
- /* Parse certificates and keys. */
- cert1 = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
- tt_assert(cert1);
- cert2 = authority_cert_parse_from_string(AUTHORITY_CERT_2, NULL);
- tt_assert(cert2);
- cert3 = authority_cert_parse_from_string(AUTHORITY_CERT_3, NULL);
- tt_assert(cert3);
- sign_skey_1 = crypto_pk_new();
- sign_skey_2 = crypto_pk_new();
- sign_skey_3 = crypto_pk_new();
+ tt_assert(!dir_common_authority_pk_init(&cert1, &cert2, &cert3,
+ &sign_skey_1, &sign_skey_2,
+ &sign_skey_3));
sign_skey_leg1 = pk_generate(4);
- tt_assert(!crypto_pk_read_private_key_from_string(sign_skey_1,
- AUTHORITY_SIGNKEY_1, -1));
- tt_assert(!crypto_pk_read_private_key_from_string(sign_skey_2,
- AUTHORITY_SIGNKEY_2, -1));
- tt_assert(!crypto_pk_read_private_key_from_string(sign_skey_3,
- AUTHORITY_SIGNKEY_3, -1));
-
- tt_assert(!crypto_pk_cmp_keys(sign_skey_1, cert1->signing_key));
- tt_assert(!crypto_pk_cmp_keys(sign_skey_2, cert2->signing_key));
-
- /*
- * Set up a vote; generate it; try to parse it.
- */
- vote = tor_malloc_zero(sizeof(networkstatus_t));
- vote->type = NS_TYPE_VOTE;
- vote->published = now;
- vote->valid_after = now+1000;
- vote->fresh_until = now+2000;
- vote->valid_until = now+3000;
- vote->vote_seconds = 100;
- vote->dist_seconds = 200;
- vote->supported_methods = smartlist_new();
- smartlist_split_string(vote->supported_methods, "1 2 3", NULL, 0, -1);
- vote->client_versions = tor_strdup("0.1.2.14,0.1.2.15");
- vote->server_versions = tor_strdup("0.1.2.14,0.1.2.15,0.1.2.16");
- vote->known_flags = smartlist_new();
- smartlist_split_string(vote->known_flags,
- "Authority Exit Fast Guard Running Stable V2Dir Valid",
- 0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- vote->voters = smartlist_new();
- voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
- voter->nickname = tor_strdup("Voter1");
- voter->address = tor_strdup("1.2.3.4");
- voter->addr = 0x01020304;
- voter->dir_port = 80;
- voter->or_port = 9000;
- voter->contact = tor_strdup("voter@example.com");
- crypto_pk_get_digest(cert1->identity_key, voter->identity_digest);
- smartlist_add(vote->voters, voter);
- vote->cert = authority_cert_dup(cert1);
- vote->net_params = smartlist_new();
- smartlist_split_string(vote->net_params, "circuitwindow=101 foo=990",
- NULL, 0, 0);
- vote->routerstatus_list = smartlist_new();
- /* add routerstatuses */
- idx = 0;
- do {
- vrs = vrs_gen(idx, now);
- if (vrs) {
- smartlist_add(vote->routerstatus_list, vrs);
- tt_assert(router_add_to_routerlist(generate_ri_from_rs(vrs),
- &msg,0,0)>=0);
- ++idx;
- }
- } while (vrs);
- n_vrs = idx;
-
- /* dump the vote and try to parse it. */
- v1_text = format_networkstatus_vote(sign_skey_1, vote);
- tt_assert(v1_text);
- v1 = networkstatus_parse_vote_from_string(v1_text, NULL, NS_TYPE_VOTE);
+ tt_assert(!dir_common_construct_vote_1(&vote, cert1, sign_skey_1, vrs_gen,
+ &v1, &n_vrs, now, 1));
tt_assert(v1);
/* Make sure the parsed thing was right. */
@@ -2000,6 +1819,8 @@ test_a_networkstatus(
tt_str_op(cp,OP_EQ, "Authority:Exit:Fast:Guard:Running:Stable:V2Dir:Valid");
tor_free(cp);
tt_int_op(smartlist_len(v1->routerstatus_list),OP_EQ, n_vrs);
+ networkstatus_vote_free(vote);
+ vote = NULL;
if (vote_tweaks) params_tweaked += vote_tweaks(v1, 1, now);
@@ -2011,33 +1832,10 @@ test_a_networkstatus(
}
/* Generate second vote. It disagrees on some of the times,
- * and doesn't list versions, and knows some crazy flags */
- vote->published = now+1;
- vote->fresh_until = now+3005;
- vote->dist_seconds = 300;
- authority_cert_free(vote->cert);
- vote->cert = authority_cert_dup(cert2);
- SMARTLIST_FOREACH(vote->net_params, char *, c, tor_free(c));
- smartlist_clear(vote->net_params);
- smartlist_split_string(vote->net_params, "bar=2000000000 circuitwindow=20",
- NULL, 0, 0);
- tor_free(vote->client_versions);
- tor_free(vote->server_versions);
- voter = smartlist_get(vote->voters, 0);
- tor_free(voter->nickname);
- tor_free(voter->address);
- voter->nickname = tor_strdup("Voter2");
- voter->address = tor_strdup("2.3.4.5");
- voter->addr = 0x02030405;
- crypto_pk_get_digest(cert2->identity_key, voter->identity_digest);
- smartlist_add(vote->known_flags, tor_strdup("MadeOfCheese"));
- smartlist_add(vote->known_flags, tor_strdup("MadeOfTin"));
- smartlist_sort_strings(vote->known_flags);
-
- /* generate and parse v2. */
- v2_text = format_networkstatus_vote(sign_skey_2, vote);
- tt_assert(v2_text);
- v2 = networkstatus_parse_vote_from_string(v2_text, NULL, NS_TYPE_VOTE);
+ * and doesn't list versions, and knows some crazy flags.
+ * Generate and parse v2. */
+ tt_assert(!dir_common_construct_vote_2(&vote, cert2, sign_skey_2, vrs_gen,
+ &v2, &n_vrs, now, 1));
tt_assert(v2);
if (vote_tweaks) params_tweaked += vote_tweaks(v2, 2, now);
@@ -2055,34 +1853,12 @@ test_a_networkstatus(
tt_assert(vrs);
vrs_test(vrs, 2, now);
}
+ networkstatus_vote_free(vote);
+ vote = NULL;
- /* Generate the third vote. */
- vote->published = now;
- vote->fresh_until = now+2003;
- vote->dist_seconds = 250;
- authority_cert_free(vote->cert);
- vote->cert = authority_cert_dup(cert3);
- SMARTLIST_FOREACH(vote->net_params, char *, c, tor_free(c));
- smartlist_clear(vote->net_params);
- smartlist_split_string(vote->net_params, "circuitwindow=80 foo=660",
- NULL, 0, 0);
- smartlist_add(vote->supported_methods, tor_strdup("4"));
- vote->client_versions = tor_strdup("0.1.2.14,0.1.2.17");
- vote->server_versions = tor_strdup("0.1.2.10,0.1.2.15,0.1.2.16");
- voter = smartlist_get(vote->voters, 0);
- tor_free(voter->nickname);
- tor_free(voter->address);
- voter->nickname = tor_strdup("Voter3");
- voter->address = tor_strdup("3.4.5.6");
- voter->addr = 0x03040506;
- crypto_pk_get_digest(cert3->identity_key, voter->identity_digest);
- /* This one has a legacy id. */
- memset(voter->legacy_id_digest, (int)'A', DIGEST_LEN);
-
- v3_text = format_networkstatus_vote(sign_skey_3, vote);
- tt_assert(v3_text);
-
- v3 = networkstatus_parse_vote_from_string(v3_text, NULL, NS_TYPE_VOTE);
+ /* Generate the third vote with a legacy id. */
+ tt_assert(!dir_common_construct_vote_3(&vote, cert3, sign_skey_3, vrs_gen,
+ &v3, &n_vrs, now, 1));
tt_assert(v3);
if (vote_tweaks) params_tweaked += vote_tweaks(v3, 3, now);
@@ -2153,12 +1929,20 @@ test_a_networkstatus(
/* Check the routerstatuses. */
n_rs = smartlist_len(con->routerstatus_list);
+ tt_assert(n_rs);
for (idx = 0; idx < n_rs; ++idx) {
rs = smartlist_get(con->routerstatus_list, idx);
tt_assert(rs);
rs_test(rs, now);
}
+ n_rs = smartlist_len(con_md->routerstatus_list);
+ tt_assert(n_rs);
+ for (idx = 0; idx < n_rs; ++idx) {
+ rs = smartlist_get(con_md->routerstatus_list, idx);
+ tt_assert(rs);
+ }
+
/* Check signatures. the first voter is a pseudo-entry with a legacy key.
* The second one hasn't signed. The fourth one has signed: validate it. */
voter = smartlist_get(con->voters, 1);
@@ -2215,11 +1999,13 @@ test_a_networkstatus(
tt_assert(con_md3);
/* All three should have the same digest. */
- tt_mem_op(&con->digests,OP_EQ, &con2->digests, sizeof(digests_t));
- tt_mem_op(&con->digests,OP_EQ, &con3->digests, sizeof(digests_t));
+ tt_mem_op(&con->digests,OP_EQ, &con2->digests, sizeof(common_digests_t));
+ tt_mem_op(&con->digests,OP_EQ, &con3->digests, sizeof(common_digests_t));
- tt_mem_op(&con_md->digests,OP_EQ, &con_md2->digests, sizeof(digests_t));
- tt_mem_op(&con_md->digests,OP_EQ, &con_md3->digests, sizeof(digests_t));
+ tt_mem_op(&con_md->digests,OP_EQ, &con_md2->digests,
+ sizeof(common_digests_t));
+ tt_mem_op(&con_md->digests,OP_EQ, &con_md3->digests,
+ sizeof(common_digests_t));
/* Extract a detached signature from con3. */
detached_text1 = get_detached_sigs(con3, con_md3);
@@ -2233,7 +2019,7 @@ test_a_networkstatus(
tt_int_op(dsig1->fresh_until,OP_EQ, con3->fresh_until);
tt_int_op(dsig1->valid_until,OP_EQ, con3->valid_until);
{
- digests_t *dsig_digests = strmap_get(dsig1->digests, "ns");
+ common_digests_t *dsig_digests = strmap_get(dsig1->digests, "ns");
tt_assert(dsig_digests);
tt_mem_op(dsig_digests->d[DIGEST_SHA1], OP_EQ,
con3->digests.d[DIGEST_SHA1], DIGEST_LEN);
@@ -2309,38 +2095,22 @@ test_a_networkstatus(
done:
tor_free(cp);
smartlist_free(votes);
- tor_free(v1_text);
- tor_free(v2_text);
- tor_free(v3_text);
tor_free(consensus_text);
tor_free(consensus_text_md);
- if (vote)
- networkstatus_vote_free(vote);
- if (v1)
- networkstatus_vote_free(v1);
- if (v2)
- networkstatus_vote_free(v2);
- if (v3)
- networkstatus_vote_free(v3);
- if (con)
- networkstatus_vote_free(con);
- if (con_md)
- networkstatus_vote_free(con_md);
- if (sign_skey_1)
- crypto_pk_free(sign_skey_1);
- if (sign_skey_2)
- crypto_pk_free(sign_skey_2);
- if (sign_skey_3)
- crypto_pk_free(sign_skey_3);
- if (sign_skey_leg1)
- crypto_pk_free(sign_skey_leg1);
- if (cert1)
- authority_cert_free(cert1);
- if (cert2)
- authority_cert_free(cert2);
- if (cert3)
- authority_cert_free(cert3);
+ networkstatus_vote_free(vote);
+ networkstatus_vote_free(v1);
+ networkstatus_vote_free(v2);
+ networkstatus_vote_free(v3);
+ networkstatus_vote_free(con);
+ networkstatus_vote_free(con_md);
+ crypto_pk_free(sign_skey_1);
+ crypto_pk_free(sign_skey_2);
+ crypto_pk_free(sign_skey_3);
+ crypto_pk_free(sign_skey_leg1);
+ authority_cert_free(cert1);
+ authority_cert_free(cert2);
+ authority_cert_free(cert3);
tor_free(consensus_text2);
tor_free(consensus_text3);
@@ -2348,18 +2118,13 @@ test_a_networkstatus(
tor_free(consensus_text_md3);
tor_free(detached_text1);
tor_free(detached_text2);
- if (con2)
- networkstatus_vote_free(con2);
- if (con3)
- networkstatus_vote_free(con3);
- if (con_md2)
- networkstatus_vote_free(con_md2);
- if (con_md3)
- networkstatus_vote_free(con_md3);
- if (dsig1)
- ns_detached_signatures_free(dsig1);
- if (dsig2)
- ns_detached_signatures_free(dsig2);
+
+ networkstatus_vote_free(con2);
+ networkstatus_vote_free(con3);
+ networkstatus_vote_free(con_md2);
+ networkstatus_vote_free(con_md3);
+ ns_detached_signatures_free(dsig1);
+ ns_detached_signatures_free(dsig2);
}
/** Run unit tests for generating and parsing V3 consensus networkstatus
@@ -2368,7 +2133,7 @@ static void
test_dir_v3_networkstatus(void *arg)
{
(void)arg;
- test_a_networkstatus(gen_routerstatus_for_v3ns,
+ test_a_networkstatus(dir_common_gen_routerstatus_for_v3ns,
vote_tweaks_for_v3ns,
test_vrs_for_v3ns,
test_consensus_for_v3ns,
@@ -2965,6 +2730,7 @@ test_dir_fmt_control_ns(void *arg)
rs.is_fast = 1;
rs.is_flagged_running = 1;
rs.has_bandwidth = 1;
+ rs.is_v2_dir = 1;
rs.bandwidth_kb = 1000;
s = networkstatus_getinfo_helper_single(&rs);
@@ -3340,12 +3106,33 @@ static void
test_dir_fetch_type(void *arg)
{
(void)arg;
- tt_assert(dir_fetch_type(DIR_PURPOSE_FETCH_MICRODESC, ROUTER_PURPOSE_GENERAL,
- NULL) == MICRODESC_DIRINFO);
- tt_assert(dir_fetch_type(DIR_PURPOSE_FETCH_SERVERDESC, ROUTER_PURPOSE_BRIDGE,
- NULL) == BRIDGE_DIRINFO);
- tt_assert(dir_fetch_type(DIR_PURPOSE_FETCH_CONSENSUS, ROUTER_PURPOSE_GENERAL,
- "microdesc") == (V3_DIRINFO | MICRODESC_DIRINFO));
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_EXTRAINFO, ROUTER_PURPOSE_BRIDGE,
+ NULL), OP_EQ, EXTRAINFO_DIRINFO | BRIDGE_DIRINFO);
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_EXTRAINFO, ROUTER_PURPOSE_GENERAL,
+ NULL), OP_EQ, EXTRAINFO_DIRINFO | V3_DIRINFO);
+
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_SERVERDESC, ROUTER_PURPOSE_BRIDGE,
+ NULL), OP_EQ, BRIDGE_DIRINFO);
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_SERVERDESC,
+ ROUTER_PURPOSE_GENERAL, NULL), OP_EQ, V3_DIRINFO);
+
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_STATUS_VOTE,
+ ROUTER_PURPOSE_GENERAL, NULL), OP_EQ, V3_DIRINFO);
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_DETACHED_SIGNATURES,
+ ROUTER_PURPOSE_GENERAL, NULL), OP_EQ, V3_DIRINFO);
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_CERTIFICATE,
+ ROUTER_PURPOSE_GENERAL, NULL), OP_EQ, V3_DIRINFO);
+
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_CONSENSUS, ROUTER_PURPOSE_GENERAL,
+ "microdesc"), OP_EQ, V3_DIRINFO|MICRODESC_DIRINFO);
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_CONSENSUS, ROUTER_PURPOSE_GENERAL,
+ NULL), OP_EQ, V3_DIRINFO);
+
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_MICRODESC, ROUTER_PURPOSE_GENERAL,
+ NULL), OP_EQ, MICRODESC_DIRINFO);
+
+ tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_RENDDESC_V2,
+ ROUTER_PURPOSE_GENERAL, NULL), OP_EQ, NO_DIRINFO);
done: ;
}
@@ -3453,7 +3240,7 @@ test_dir_packages(void *arg)
ADD(4, "clownshoes 22alpha4 http://quumble.example.cam/ blake2=fooa");
ADD(5, "clownshoes 22alpha4 http://quumble.example.cam/ blake2=fooa");
- /* Five votes for A ... all from the same guy. Three for B. */
+ /* Five votes for A ... all from the same authority. Three for B. */
ADD(0, "cbc 99.1.11.1.1 http://example.com/cbc/ cubehash=ahooy sha512=m");
ADD(1, "cbc 99.1.11.1.1 http://example.com/cbc/ cubehash=ahooy sha512=m");
ADD(3, "cbc 99.1.11.1.1 http://example.com/cbc/ cubehash=ahooy sha512=m");
@@ -3494,6 +3281,768 @@ test_dir_packages(void *arg)
tor_free(res);
}
+static void
+test_dir_download_status_schedule(void *arg)
+{
+ (void)arg;
+ download_status_t dls_failure = { 0, 0, 0, DL_SCHED_GENERIC,
+ DL_WANT_AUTHORITY,
+ DL_SCHED_INCREMENT_FAILURE };
+ download_status_t dls_attempt = { 0, 0, 0, DL_SCHED_CONSENSUS,
+ DL_WANT_ANY_DIRSERVER,
+ DL_SCHED_INCREMENT_ATTEMPT};
+ download_status_t dls_bridge = { 0, 0, 0, DL_SCHED_BRIDGE,
+ DL_WANT_AUTHORITY,
+ DL_SCHED_INCREMENT_FAILURE};
+ int increment = -1;
+ int expected_increment = -1;
+ time_t current_time = time(NULL);
+ int delay1 = -1;
+ int delay2 = -1;
+ smartlist_t *schedule = smartlist_new();
+
+ /* Make a dummy schedule */
+ smartlist_add(schedule, (void *)&delay1);
+ smartlist_add(schedule, (void *)&delay2);
+
+ /* check a range of values */
+ delay1 = 1000;
+ increment = download_status_schedule_get_delay(&dls_failure,
+ schedule,
+ TIME_MIN);
+ expected_increment = delay1;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_failure.next_attempt_at == TIME_MIN + expected_increment);
+
+ delay1 = INT_MAX;
+ increment = download_status_schedule_get_delay(&dls_failure,
+ schedule,
+ -1);
+ expected_increment = delay1;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_failure.next_attempt_at == TIME_MAX);
+
+ delay1 = 0;
+ increment = download_status_schedule_get_delay(&dls_attempt,
+ schedule,
+ 0);
+ expected_increment = delay1;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_attempt.next_attempt_at == 0 + expected_increment);
+
+ delay1 = 1000;
+ increment = download_status_schedule_get_delay(&dls_attempt,
+ schedule,
+ 1);
+ expected_increment = delay1;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_attempt.next_attempt_at == 1 + expected_increment);
+
+ delay1 = INT_MAX;
+ increment = download_status_schedule_get_delay(&dls_bridge,
+ schedule,
+ current_time);
+ expected_increment = delay1;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_bridge.next_attempt_at == TIME_MAX);
+
+ delay1 = 1;
+ increment = download_status_schedule_get_delay(&dls_bridge,
+ schedule,
+ TIME_MAX);
+ expected_increment = delay1;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_bridge.next_attempt_at == TIME_MAX);
+
+ /* see what happens when we reach the end */
+ dls_attempt.n_download_attempts++;
+ dls_bridge.n_download_failures++;
+
+ delay2 = 100;
+ increment = download_status_schedule_get_delay(&dls_attempt,
+ schedule,
+ current_time);
+ expected_increment = delay2;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_attempt.next_attempt_at == current_time + delay2);
+
+ delay2 = 1;
+ increment = download_status_schedule_get_delay(&dls_bridge,
+ schedule,
+ current_time);
+ expected_increment = delay2;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_bridge.next_attempt_at == current_time + delay2);
+
+ /* see what happens when we try to go off the end */
+ dls_attempt.n_download_attempts++;
+ dls_bridge.n_download_failures++;
+
+ delay2 = 5;
+ increment = download_status_schedule_get_delay(&dls_attempt,
+ schedule,
+ current_time);
+ expected_increment = delay2;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_attempt.next_attempt_at == current_time + delay2);
+
+ delay2 = 17;
+ increment = download_status_schedule_get_delay(&dls_bridge,
+ schedule,
+ current_time);
+ expected_increment = delay2;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_bridge.next_attempt_at == current_time + delay2);
+
+ /* see what happens when we reach IMPOSSIBLE_TO_DOWNLOAD */
+ dls_attempt.n_download_attempts = IMPOSSIBLE_TO_DOWNLOAD;
+ dls_bridge.n_download_failures = IMPOSSIBLE_TO_DOWNLOAD;
+
+ delay2 = 35;
+ increment = download_status_schedule_get_delay(&dls_attempt,
+ schedule,
+ current_time);
+ expected_increment = INT_MAX;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_attempt.next_attempt_at == TIME_MAX);
+
+ delay2 = 99;
+ increment = download_status_schedule_get_delay(&dls_bridge,
+ schedule,
+ current_time);
+ expected_increment = INT_MAX;
+ tt_assert(increment == expected_increment);
+ tt_assert(dls_bridge.next_attempt_at == TIME_MAX);
+
+ done:
+ /* the pointers in schedule are allocated on the stack */
+ smartlist_free(schedule);
+}
+
+static void
+test_dir_download_status_increment(void *arg)
+{
+ (void)arg;
+ download_status_t dls_failure = { 0, 0, 0, DL_SCHED_GENERIC,
+ DL_WANT_AUTHORITY,
+ DL_SCHED_INCREMENT_FAILURE };
+ download_status_t dls_attempt = { 0, 0, 0, DL_SCHED_BRIDGE,
+ DL_WANT_ANY_DIRSERVER,
+ DL_SCHED_INCREMENT_ATTEMPT};
+ int delay0 = -1;
+ int delay1 = -1;
+ int delay2 = -1;
+ smartlist_t *schedule = smartlist_new();
+ or_options_t test_options;
+ time_t next_at = TIME_MAX;
+ time_t current_time = time(NULL);
+
+ /* Provide some values for the schedule */
+ delay0 = 10;
+ delay1 = 99;
+ delay2 = 20;
+
+ /* Make the schedule */
+ smartlist_add(schedule, (void *)&delay0);
+ smartlist_add(schedule, (void *)&delay1);
+ smartlist_add(schedule, (void *)&delay2);
+
+ /* Put it in the options */
+ mock_options = &test_options;
+ reset_options(mock_options, &mock_get_options_calls);
+ mock_options->TestingClientDownloadSchedule = schedule;
+ mock_options->TestingBridgeDownloadSchedule = schedule;
+
+ MOCK(get_options, mock_get_options);
+
+ /* Check that a failure reset works */
+ mock_get_options_calls = 0;
+ download_status_reset(&dls_failure);
+ /* we really want to test that it's equal to time(NULL) + delay0, but that's
+ * an unrealiable test, because time(NULL) might change. */
+ tt_assert(download_status_get_next_attempt_at(&dls_failure)
+ >= current_time + delay0);
+ tt_assert(download_status_get_next_attempt_at(&dls_failure)
+ != TIME_MAX);
+ tt_assert(download_status_get_n_failures(&dls_failure) == 0);
+ tt_assert(download_status_get_n_attempts(&dls_failure) == 0);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* avoid timing inconsistencies */
+ dls_failure.next_attempt_at = current_time + delay0;
+
+ /* check that a reset schedule becomes ready at the right time */
+ tt_assert(download_status_is_ready(&dls_failure,
+ current_time + delay0 - 1,
+ 1) == 0);
+ tt_assert(download_status_is_ready(&dls_failure,
+ current_time + delay0,
+ 1) == 1);
+ tt_assert(download_status_is_ready(&dls_failure,
+ current_time + delay0 + 1,
+ 1) == 1);
+
+ /* Check that a failure increment works */
+ mock_get_options_calls = 0;
+ next_at = download_status_increment_failure(&dls_failure, 404, "test", 0,
+ current_time);
+ tt_assert(next_at == current_time + delay1);
+ tt_assert(download_status_get_n_failures(&dls_failure) == 1);
+ tt_assert(download_status_get_n_attempts(&dls_failure) == 1);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* check that an incremented schedule becomes ready at the right time */
+ tt_assert(download_status_is_ready(&dls_failure,
+ current_time + delay1 - 1,
+ 1) == 0);
+ tt_assert(download_status_is_ready(&dls_failure,
+ current_time + delay1,
+ 1) == 1);
+ tt_assert(download_status_is_ready(&dls_failure,
+ current_time + delay1 + 1,
+ 1) == 1);
+
+ /* check that a schedule isn't ready if it's had too many failures */
+ tt_assert(download_status_is_ready(&dls_failure,
+ current_time + delay1 + 10,
+ 0) == 0);
+
+ /* Check that failure increments don't happen on 503 for clients, but that
+ * attempt increments do. */
+ mock_get_options_calls = 0;
+ next_at = download_status_increment_failure(&dls_failure, 503, "test", 0,
+ current_time);
+ tt_assert(next_at == current_time + delay1);
+ tt_assert(download_status_get_n_failures(&dls_failure) == 1);
+ tt_assert(download_status_get_n_attempts(&dls_failure) == 2);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* Check that failure increments do happen on 503 for servers */
+ mock_get_options_calls = 0;
+ next_at = download_status_increment_failure(&dls_failure, 503, "test", 1,
+ current_time);
+ tt_assert(next_at == current_time + delay2);
+ tt_assert(download_status_get_n_failures(&dls_failure) == 2);
+ tt_assert(download_status_get_n_attempts(&dls_failure) == 3);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* Check what happens when we run off the end of the schedule */
+ mock_get_options_calls = 0;
+ next_at = download_status_increment_failure(&dls_failure, 404, "test", 0,
+ current_time);
+ tt_assert(next_at == current_time + delay2);
+ tt_assert(download_status_get_n_failures(&dls_failure) == 3);
+ tt_assert(download_status_get_n_attempts(&dls_failure) == 4);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* Check what happens when we hit the failure limit */
+ mock_get_options_calls = 0;
+ download_status_mark_impossible(&dls_failure);
+ next_at = download_status_increment_failure(&dls_failure, 404, "test", 0,
+ current_time);
+ tt_assert(next_at == TIME_MAX);
+ tt_assert(download_status_get_n_failures(&dls_failure)
+ == IMPOSSIBLE_TO_DOWNLOAD);
+ tt_assert(download_status_get_n_attempts(&dls_failure)
+ == IMPOSSIBLE_TO_DOWNLOAD);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* Check that a failure reset doesn't reset at the limit */
+ mock_get_options_calls = 0;
+ download_status_reset(&dls_failure);
+ tt_assert(download_status_get_next_attempt_at(&dls_failure)
+ == TIME_MAX);
+ tt_assert(download_status_get_n_failures(&dls_failure)
+ == IMPOSSIBLE_TO_DOWNLOAD);
+ tt_assert(download_status_get_n_attempts(&dls_failure)
+ == IMPOSSIBLE_TO_DOWNLOAD);
+ tt_assert(mock_get_options_calls == 0);
+
+ /* Check that a failure reset resets just before the limit */
+ mock_get_options_calls = 0;
+ dls_failure.n_download_failures = IMPOSSIBLE_TO_DOWNLOAD - 1;
+ dls_failure.n_download_attempts = IMPOSSIBLE_TO_DOWNLOAD - 1;
+ download_status_reset(&dls_failure);
+ /* we really want to test that it's equal to time(NULL) + delay0, but that's
+ * an unrealiable test, because time(NULL) might change. */
+ tt_assert(download_status_get_next_attempt_at(&dls_failure)
+ >= current_time + delay0);
+ tt_assert(download_status_get_next_attempt_at(&dls_failure)
+ != TIME_MAX);
+ tt_assert(download_status_get_n_failures(&dls_failure) == 0);
+ tt_assert(download_status_get_n_attempts(&dls_failure) == 0);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* Check that failure increments do happen on attempt-based schedules,
+ * but that the retry is set at the end of time */
+ mock_get_options_calls = 0;
+ next_at = download_status_increment_failure(&dls_attempt, 404, "test", 0,
+ current_time);
+ tt_assert(next_at == TIME_MAX);
+ tt_assert(download_status_get_n_failures(&dls_attempt) == 1);
+ tt_assert(download_status_get_n_attempts(&dls_attempt) == 0);
+ tt_assert(mock_get_options_calls == 0);
+
+ /* Check that an attempt reset works */
+ mock_get_options_calls = 0;
+ download_status_reset(&dls_attempt);
+ /* we really want to test that it's equal to time(NULL) + delay0, but that's
+ * an unrealiable test, because time(NULL) might change. */
+ tt_assert(download_status_get_next_attempt_at(&dls_attempt)
+ >= current_time + delay0);
+ tt_assert(download_status_get_next_attempt_at(&dls_attempt)
+ != TIME_MAX);
+ tt_assert(download_status_get_n_failures(&dls_attempt) == 0);
+ tt_assert(download_status_get_n_attempts(&dls_attempt) == 0);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* avoid timing inconsistencies */
+ dls_attempt.next_attempt_at = current_time + delay0;
+
+ /* check that a reset schedule becomes ready at the right time */
+ tt_assert(download_status_is_ready(&dls_attempt,
+ current_time + delay0 - 1,
+ 1) == 0);
+ tt_assert(download_status_is_ready(&dls_attempt,
+ current_time + delay0,
+ 1) == 1);
+ tt_assert(download_status_is_ready(&dls_attempt,
+ current_time + delay0 + 1,
+ 1) == 1);
+
+ /* Check that an attempt increment works */
+ mock_get_options_calls = 0;
+ next_at = download_status_increment_attempt(&dls_attempt, "test",
+ current_time);
+ tt_assert(next_at == current_time + delay1);
+ tt_assert(download_status_get_n_failures(&dls_attempt) == 0);
+ tt_assert(download_status_get_n_attempts(&dls_attempt) == 1);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* check that an incremented schedule becomes ready at the right time */
+ tt_assert(download_status_is_ready(&dls_attempt,
+ current_time + delay1 - 1,
+ 1) == 0);
+ tt_assert(download_status_is_ready(&dls_attempt,
+ current_time + delay1,
+ 1) == 1);
+ tt_assert(download_status_is_ready(&dls_attempt,
+ current_time + delay1 + 1,
+ 1) == 1);
+
+ /* check that a schedule isn't ready if it's had too many attempts */
+ tt_assert(download_status_is_ready(&dls_attempt,
+ current_time + delay1 + 10,
+ 0) == 0);
+
+ /* Check what happens when we reach then run off the end of the schedule */
+ mock_get_options_calls = 0;
+ next_at = download_status_increment_attempt(&dls_attempt, "test",
+ current_time);
+ tt_assert(next_at == current_time + delay2);
+ tt_assert(download_status_get_n_failures(&dls_attempt) == 0);
+ tt_assert(download_status_get_n_attempts(&dls_attempt) == 2);
+ tt_assert(mock_get_options_calls >= 1);
+
+ mock_get_options_calls = 0;
+ next_at = download_status_increment_attempt(&dls_attempt, "test",
+ current_time);
+ tt_assert(next_at == current_time + delay2);
+ tt_assert(download_status_get_n_failures(&dls_attempt) == 0);
+ tt_assert(download_status_get_n_attempts(&dls_attempt) == 3);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* Check what happens when we hit the attempt limit */
+ mock_get_options_calls = 0;
+ download_status_mark_impossible(&dls_attempt);
+ next_at = download_status_increment_attempt(&dls_attempt, "test",
+ current_time);
+ tt_assert(next_at == TIME_MAX);
+ tt_assert(download_status_get_n_failures(&dls_attempt)
+ == IMPOSSIBLE_TO_DOWNLOAD);
+ tt_assert(download_status_get_n_attempts(&dls_attempt)
+ == IMPOSSIBLE_TO_DOWNLOAD);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* Check that an attempt reset doesn't reset at the limit */
+ mock_get_options_calls = 0;
+ download_status_reset(&dls_attempt);
+ tt_assert(download_status_get_next_attempt_at(&dls_attempt)
+ == TIME_MAX);
+ tt_assert(download_status_get_n_failures(&dls_attempt)
+ == IMPOSSIBLE_TO_DOWNLOAD);
+ tt_assert(download_status_get_n_attempts(&dls_attempt)
+ == IMPOSSIBLE_TO_DOWNLOAD);
+ tt_assert(mock_get_options_calls == 0);
+
+ /* Check that an attempt reset resets just before the limit */
+ mock_get_options_calls = 0;
+ dls_attempt.n_download_failures = IMPOSSIBLE_TO_DOWNLOAD - 1;
+ dls_attempt.n_download_attempts = IMPOSSIBLE_TO_DOWNLOAD - 1;
+ download_status_reset(&dls_attempt);
+ /* we really want to test that it's equal to time(NULL) + delay0, but that's
+ * an unrealiable test, because time(NULL) might change. */
+ tt_assert(download_status_get_next_attempt_at(&dls_attempt)
+ >= current_time + delay0);
+ tt_assert(download_status_get_next_attempt_at(&dls_attempt)
+ != TIME_MAX);
+ tt_assert(download_status_get_n_failures(&dls_attempt) == 0);
+ tt_assert(download_status_get_n_attempts(&dls_attempt) == 0);
+ tt_assert(mock_get_options_calls >= 1);
+
+ /* Check that attempt increments don't happen on failure-based schedules,
+ * and that the attempt is set at the end of time */
+ mock_get_options_calls = 0;
+ next_at = download_status_increment_attempt(&dls_failure, "test",
+ current_time);
+ tt_assert(next_at == TIME_MAX);
+ tt_assert(download_status_get_n_failures(&dls_failure) == 0);
+ tt_assert(download_status_get_n_attempts(&dls_failure) == 0);
+ tt_assert(mock_get_options_calls == 0);
+
+ done:
+ /* the pointers in schedule are allocated on the stack */
+ smartlist_free(schedule);
+ UNMOCK(get_options);
+ mock_options = NULL;
+ mock_get_options_calls = 0;
+}
+
+static void
+test_dir_authdir_type_to_string(void *data)
+{
+ (void)data;
+ char *res;
+
+ tt_str_op(res = authdir_type_to_string(NO_DIRINFO), OP_EQ,
+ "[Not an authority]");
+ tor_free(res);
+
+ tt_str_op(res = authdir_type_to_string(EXTRAINFO_DIRINFO), OP_EQ,
+ "[Not an authority]");
+ tor_free(res);
+
+ tt_str_op(res = authdir_type_to_string(MICRODESC_DIRINFO), OP_EQ,
+ "[Not an authority]");
+ tor_free(res);
+
+ tt_str_op(res = authdir_type_to_string(V3_DIRINFO), OP_EQ, "V3");
+ tor_free(res);
+
+ tt_str_op(res = authdir_type_to_string(BRIDGE_DIRINFO), OP_EQ, "Bridge");
+ tor_free(res);
+
+ tt_str_op(res = authdir_type_to_string(
+ V3_DIRINFO | BRIDGE_DIRINFO | EXTRAINFO_DIRINFO), OP_EQ,
+ "V3, Bridge");
+ done:
+ tor_free(res);
+}
+
+static void
+test_dir_conn_purpose_to_string(void *data)
+{
+ (void)data;
+
+#define EXPECT_CONN_PURPOSE(purpose, expected) \
+ tt_str_op(dir_conn_purpose_to_string(purpose), OP_EQ, expected);
+
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_UPLOAD_DIR, "server descriptor upload");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_UPLOAD_VOTE, "server vote upload");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_UPLOAD_SIGNATURES,
+ "consensus signature upload");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_SERVERDESC, "server descriptor fetch");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_EXTRAINFO, "extra-info fetch");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_CONSENSUS,
+ "consensus network-status fetch");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_CERTIFICATE, "authority cert fetch");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_STATUS_VOTE, "status vote fetch");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_DETACHED_SIGNATURES,
+ "consensus signature fetch");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_RENDDESC_V2,
+ "hidden-service v2 descriptor fetch");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_UPLOAD_RENDDESC_V2,
+ "hidden-service v2 descriptor upload");
+ EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_MICRODESC, "microdescriptor fetch");
+ EXPECT_CONN_PURPOSE(1024, "(unknown)");
+
+ done: ;
+}
+
+NS_DECL(int,
+public_server_mode, (const or_options_t *options));
+
+static int
+NS(public_server_mode)(const or_options_t *options)
+{
+ (void)options;
+
+ if (CALLED(public_server_mode)++ == 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+test_dir_should_use_directory_guards(void *data)
+{
+ or_options_t *options;
+ char *errmsg = NULL;
+ (void)data;
+
+ NS_MOCK(public_server_mode);
+
+ options = options_new();
+ options_init(options);
+
+ tt_int_op(should_use_directory_guards(options), OP_EQ, 0);
+ tt_int_op(CALLED(public_server_mode), OP_EQ, 1);
+
+ options->UseEntryGuardsAsDirGuards = 1;
+ options->UseEntryGuards = 1;
+ options->DownloadExtraInfo = 0;
+ options->FetchDirInfoEarly = 0;
+ options->FetchDirInfoExtraEarly = 0;
+ options->FetchUselessDescriptors = 0;
+ tt_int_op(should_use_directory_guards(options), OP_EQ, 1);
+ tt_int_op(CALLED(public_server_mode), OP_EQ, 2);
+
+ options->UseEntryGuards = 0;
+ tt_int_op(should_use_directory_guards(options), OP_EQ, 0);
+ tt_int_op(CALLED(public_server_mode), OP_EQ, 3);
+ options->UseEntryGuards = 1;
+
+ options->UseEntryGuardsAsDirGuards = 0;
+ tt_int_op(should_use_directory_guards(options), OP_EQ, 0);
+ tt_int_op(CALLED(public_server_mode), OP_EQ, 4);
+ options->UseEntryGuardsAsDirGuards = 1;
+
+ options->DownloadExtraInfo = 1;
+ tt_int_op(should_use_directory_guards(options), OP_EQ, 0);
+ tt_int_op(CALLED(public_server_mode), OP_EQ, 5);
+ options->DownloadExtraInfo = 0;
+
+ options->FetchDirInfoEarly = 1;
+ tt_int_op(should_use_directory_guards(options), OP_EQ, 0);
+ tt_int_op(CALLED(public_server_mode), OP_EQ, 6);
+ options->FetchDirInfoEarly = 0;
+
+ options->FetchDirInfoExtraEarly = 1;
+ tt_int_op(should_use_directory_guards(options), OP_EQ, 0);
+ tt_int_op(CALLED(public_server_mode), OP_EQ, 7);
+ options->FetchDirInfoExtraEarly = 0;
+
+ options->FetchUselessDescriptors = 1;
+ tt_int_op(should_use_directory_guards(options), OP_EQ, 0);
+ tt_int_op(CALLED(public_server_mode), OP_EQ, 8);
+ options->FetchUselessDescriptors = 0;
+
+ done:
+ NS_UNMOCK(public_server_mode);
+ or_options_free(options);
+ tor_free(errmsg);
+}
+
+NS_DECL(void,
+directory_initiate_command_routerstatus, (const routerstatus_t *status,
+ uint8_t dir_purpose,
+ uint8_t router_purpose,
+ dir_indirection_t indirection,
+ const char *resource,
+ const char *payload,
+ size_t payload_len,
+ time_t if_modified_since));
+
+static void
+test_dir_should_not_init_request_to_ourselves(void *data)
+{
+ char digest[DIGEST_LEN];
+ dir_server_t *ourself = NULL;
+ crypto_pk_t *key = pk_generate(2);
+ (void) data;
+
+ NS_MOCK(directory_initiate_command_routerstatus);
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ set_server_identity_key(key);
+ crypto_pk_get_digest(key, (char*) &digest);
+ ourself = trusted_dir_server_new("ourself", "127.0.0.1", 9059, 9060,
+ NULL, digest,
+ NULL, V3_DIRINFO, 1.0);
+
+ tt_assert(ourself);
+ dir_server_add(ourself);
+
+ directory_get_from_all_authorities(DIR_PURPOSE_FETCH_STATUS_VOTE, 0, NULL);
+ tt_int_op(CALLED(directory_initiate_command_routerstatus), OP_EQ, 0);
+
+ directory_get_from_all_authorities(DIR_PURPOSE_FETCH_DETACHED_SIGNATURES, 0,
+ NULL);
+
+ tt_int_op(CALLED(directory_initiate_command_routerstatus), OP_EQ, 0);
+
+ done:
+ NS_UNMOCK(directory_initiate_command_routerstatus);
+ clear_dir_servers();
+ routerlist_free_all();
+ crypto_pk_free(key);
+}
+
+static void
+test_dir_should_not_init_request_to_dir_auths_without_v3_info(void *data)
+{
+ dir_server_t *ds = NULL;
+ dirinfo_type_t dirinfo_type = BRIDGE_DIRINFO | EXTRAINFO_DIRINFO \
+ | MICRODESC_DIRINFO;
+ (void) data;
+
+ NS_MOCK(directory_initiate_command_routerstatus);
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ ds = trusted_dir_server_new("ds", "10.0.0.1", 9059, 9060, NULL,
+ "12345678901234567890", NULL, dirinfo_type, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+
+ directory_get_from_all_authorities(DIR_PURPOSE_FETCH_STATUS_VOTE, 0, NULL);
+ tt_int_op(CALLED(directory_initiate_command_routerstatus), OP_EQ, 0);
+
+ directory_get_from_all_authorities(DIR_PURPOSE_FETCH_DETACHED_SIGNATURES, 0,
+ NULL);
+ tt_int_op(CALLED(directory_initiate_command_routerstatus), OP_EQ, 0);
+
+ done:
+ NS_UNMOCK(directory_initiate_command_routerstatus);
+ clear_dir_servers();
+ routerlist_free_all();
+}
+
+static void
+test_dir_should_init_request_to_dir_auths(void *data)
+{
+ dir_server_t *ds = NULL;
+ (void) data;
+
+ NS_MOCK(directory_initiate_command_routerstatus);
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ ds = trusted_dir_server_new("ds", "10.0.0.1", 9059, 9060, NULL,
+ "12345678901234567890", NULL, V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+
+ directory_get_from_all_authorities(DIR_PURPOSE_FETCH_STATUS_VOTE, 0, NULL);
+ tt_int_op(CALLED(directory_initiate_command_routerstatus), OP_EQ, 1);
+
+ directory_get_from_all_authorities(DIR_PURPOSE_FETCH_DETACHED_SIGNATURES, 0,
+ NULL);
+ tt_int_op(CALLED(directory_initiate_command_routerstatus), OP_EQ, 2);
+
+ done:
+ NS_UNMOCK(directory_initiate_command_routerstatus);
+ clear_dir_servers();
+ routerlist_free_all();
+}
+
+void
+NS(directory_initiate_command_routerstatus)(const routerstatus_t *status,
+ uint8_t dir_purpose,
+ uint8_t router_purpose,
+ dir_indirection_t indirection,
+ const char *resource,
+ const char *payload,
+ size_t payload_len,
+ time_t if_modified_since)
+{
+ (void)status;
+ (void)dir_purpose;
+ (void)router_purpose;
+ (void)indirection;
+ (void)resource;
+ (void)payload;
+ (void)payload_len;
+ (void)if_modified_since;
+ CALLED(directory_initiate_command_routerstatus)++;
+}
+
+static void
+test_dir_choose_compression_level(void* data)
+{
+ (void)data;
+
+ /* It starts under_memory_pressure */
+ tt_int_op(have_been_under_memory_pressure(), OP_EQ, 1);
+
+ tt_assert(HIGH_COMPRESSION == choose_compression_level(-1));
+ tt_assert(LOW_COMPRESSION == choose_compression_level(1024-1));
+ tt_assert(MEDIUM_COMPRESSION == choose_compression_level(2048-1));
+ tt_assert(HIGH_COMPRESSION == choose_compression_level(2048));
+
+ /* Reset under_memory_pressure timer */
+ cell_queues_check_size();
+ tt_int_op(have_been_under_memory_pressure(), OP_EQ, 0);
+
+ tt_assert(HIGH_COMPRESSION == choose_compression_level(-1));
+ tt_assert(HIGH_COMPRESSION == choose_compression_level(1024-1));
+ tt_assert(HIGH_COMPRESSION == choose_compression_level(2048-1));
+ tt_assert(HIGH_COMPRESSION == choose_compression_level(2048));
+
+ done: ;
+}
+
+static void
+test_dir_find_dl_schedule(void* data)
+{
+ download_status_t dls;
+ smartlist_t server, client, server_cons, client_cons, bridge;
+ (void)data;
+
+ mock_options = malloc(sizeof(or_options_t));
+ reset_options(mock_options, &mock_get_options_calls);
+ MOCK(get_options, mock_get_options);
+
+ mock_options->TestingServerDownloadSchedule = &server;
+ mock_options->TestingClientDownloadSchedule = &client;
+ mock_options->TestingServerConsensusDownloadSchedule = &server_cons;
+ mock_options->TestingClientConsensusDownloadSchedule = &client_cons;
+ mock_options->TestingBridgeDownloadSchedule = &bridge;
+
+ dls.schedule = DL_SCHED_GENERIC;
+ mock_options->ClientOnly = 1;
+ tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &client);
+ mock_options->ClientOnly = 0;
+ mock_options->DirPort_set = 1;
+ mock_options->ORPort_set = 1;
+ mock_options->DirCache = 1;
+ tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &server);
+
+#if 0
+ dls.schedule = DL_SCHED_CONSENSUS;
+ mock_options->ClientOnly = 1;
+ mock_options->DirCache = 0;
+ tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &client_cons);
+ mock_options->ClientOnly = 0;
+ mock_options->DirCache = 1;
+ tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &server_cons);
+#endif
+
+ dls.schedule = DL_SCHED_BRIDGE;
+ mock_options->ClientOnly = 1;
+ tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &bridge);
+ mock_options->ClientOnly = 0;
+ tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &bridge);
+
+ done:
+ UNMOCK(get_options);
+}
+
#define DIR_LEGACY(name) \
{ #name, test_dir_ ## name , TT_FORK, NULL, NULL }
@@ -3525,6 +4074,16 @@ struct testcase_t dir_tests[] = {
DIR(purpose_needs_anonymity, 0),
DIR(fetch_type, 0),
DIR(packages, 0),
+ DIR(download_status_schedule, 0),
+ DIR(download_status_increment, 0),
+ DIR(authdir_type_to_string, 0),
+ DIR(conn_purpose_to_string, 0),
+ DIR(should_use_directory_guards, 0),
+ DIR(should_not_init_request_to_ourselves, TT_FORK),
+ DIR(should_not_init_request_to_dir_auths_without_v3_info, 0),
+ DIR(should_init_request_to_dir_auths, 0),
+ DIR(choose_compression_level, 0),
+ DIR(find_dl_schedule, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_dir_common.c b/src/test/test_dir_common.c
new file mode 100644
index 0000000000..f9e97c973c
--- /dev/null
+++ b/src/test/test_dir_common.c
@@ -0,0 +1,425 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#define DIRVOTE_PRIVATE
+#include "crypto.h"
+#include "test.h"
+#include "container.h"
+#include "or.h"
+#include "dirvote.h"
+#include "nodelist.h"
+#include "routerlist.h"
+#include "test_dir_common.h"
+
+void dir_common_setup_vote(networkstatus_t **vote, time_t now);
+networkstatus_t * dir_common_add_rs_and_parse(networkstatus_t *vote,
+ networkstatus_t **vote_out,
+ vote_routerstatus_t * (*vrs_gen)(int idx, time_t now),
+ crypto_pk_t *sign_skey, int *n_vrs,
+ time_t now, int clear_rl);
+
+extern const char AUTHORITY_CERT_1[];
+extern const char AUTHORITY_SIGNKEY_1[];
+extern const char AUTHORITY_CERT_2[];
+extern const char AUTHORITY_SIGNKEY_2[];
+extern const char AUTHORITY_CERT_3[];
+extern const char AUTHORITY_SIGNKEY_3[];
+
+/** Initialize and set auth certs and keys
+ * Returns 0 on success, -1 on failure. Clean up handled by caller.
+ */
+int
+dir_common_authority_pk_init(authority_cert_t **cert1,
+ authority_cert_t **cert2,
+ authority_cert_t **cert3,
+ crypto_pk_t **sign_skey_1,
+ crypto_pk_t **sign_skey_2,
+ crypto_pk_t **sign_skey_3)
+{
+ /* Parse certificates and keys. */
+ authority_cert_t *cert;
+ cert = authority_cert_parse_from_string(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);
+ tt_assert(*cert2);
+ *cert3 = authority_cert_parse_from_string(AUTHORITY_CERT_3, NULL);
+ tt_assert(*cert3);
+ *sign_skey_1 = crypto_pk_new();
+ *sign_skey_2 = crypto_pk_new();
+ *sign_skey_3 = crypto_pk_new();
+
+ tt_assert(!crypto_pk_read_private_key_from_string(*sign_skey_1,
+ AUTHORITY_SIGNKEY_1, -1));
+ tt_assert(!crypto_pk_read_private_key_from_string(*sign_skey_2,
+ AUTHORITY_SIGNKEY_2, -1));
+ tt_assert(!crypto_pk_read_private_key_from_string(*sign_skey_3,
+ AUTHORITY_SIGNKEY_3, -1));
+
+ tt_assert(!crypto_pk_cmp_keys(*sign_skey_1, (*cert1)->signing_key));
+ tt_assert(!crypto_pk_cmp_keys(*sign_skey_2, (*cert2)->signing_key));
+
+ return 0;
+ done:
+ return -1;
+}
+
+/**
+ * Generate a routerstatus for v3_networkstatus test.
+ */
+vote_routerstatus_t *
+dir_common_gen_routerstatus_for_v3ns(int idx, time_t now)
+{
+ vote_routerstatus_t *vrs=NULL;
+ routerstatus_t *rs = NULL;
+ tor_addr_t addr_ipv6;
+ char *method_list = NULL;
+
+ switch (idx) {
+ case 0:
+ /* Generate the first routerstatus. */
+ vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
+ rs = &vrs->status;
+ vrs->version = tor_strdup("0.1.2.14");
+ rs->published_on = now-1500;
+ strlcpy(rs->nickname, "router2", sizeof(rs->nickname));
+ memset(rs->identity_digest, TEST_DIR_ROUTER_ID_1, DIGEST_LEN);
+ memset(rs->descriptor_digest, TEST_DIR_ROUTER_DD_1, DIGEST_LEN);
+ rs->addr = 0x99008801;
+ rs->or_port = 443;
+ rs->dir_port = 8000;
+ /* all flags but running and v2dir cleared */
+ rs->is_flagged_running = 1;
+ rs->is_v2_dir = 1;
+ break;
+ case 1:
+ /* Generate the second routerstatus. */
+ vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
+ rs = &vrs->status;
+ vrs->version = tor_strdup("0.2.0.5");
+ rs->published_on = now-1000;
+ strlcpy(rs->nickname, "router1", sizeof(rs->nickname));
+ memset(rs->identity_digest, TEST_DIR_ROUTER_ID_2, DIGEST_LEN);
+ memset(rs->descriptor_digest, TEST_DIR_ROUTER_DD_2, DIGEST_LEN);
+ rs->addr = 0x99009901;
+ rs->or_port = 443;
+ rs->dir_port = 0;
+ tor_addr_parse(&addr_ipv6, "[1:2:3::4]");
+ tor_addr_copy(&rs->ipv6_addr, &addr_ipv6);
+ rs->ipv6_orport = 4711;
+ rs->is_exit = rs->is_stable = rs->is_fast = rs->is_flagged_running =
+ rs->is_valid = rs->is_possible_guard = rs->is_v2_dir = 1;
+ break;
+ case 2:
+ /* Generate the third routerstatus. */
+ vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
+ rs = &vrs->status;
+ vrs->version = tor_strdup("0.1.0.3");
+ rs->published_on = now-1000;
+ strlcpy(rs->nickname, "router3", sizeof(rs->nickname));
+ memset(rs->identity_digest, TEST_DIR_ROUTER_ID_3, DIGEST_LEN);
+ memset(rs->descriptor_digest, TEST_DIR_ROUTER_DD_3, DIGEST_LEN);
+ rs->addr = 0xAA009901;
+ rs->or_port = 400;
+ rs->dir_port = 9999;
+ rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast =
+ rs->is_flagged_running = rs->is_valid = rs->is_v2_dir =
+ rs->is_possible_guard = 1;
+ break;
+ case 3:
+ /* Generate a fourth routerstatus that is not running. */
+ vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
+ rs = &vrs->status;
+ vrs->version = tor_strdup("0.1.6.3");
+ rs->published_on = now-1000;
+ strlcpy(rs->nickname, "router4", sizeof(rs->nickname));
+ memset(rs->identity_digest, TEST_DIR_ROUTER_ID_4, DIGEST_LEN);
+ memset(rs->descriptor_digest, TEST_DIR_ROUTER_DD_4, DIGEST_LEN);
+ rs->addr = 0xC0000203;
+ rs->or_port = 500;
+ rs->dir_port = 1999;
+ rs->is_v2_dir = 1;
+ /* Running flag (and others) cleared */
+ break;
+ case 4:
+ /* No more for this test; return NULL */
+ vrs = NULL;
+ break;
+ default:
+ /* Shouldn't happen */
+ tt_assert(0);
+ }
+ if (vrs) {
+ vrs->microdesc = tor_malloc_zero(sizeof(vote_microdesc_hash_t));
+ method_list = make_consensus_method_list(MIN_SUPPORTED_CONSENSUS_METHOD,
+ MAX_SUPPORTED_CONSENSUS_METHOD,
+ ",");
+ tor_asprintf(&vrs->microdesc->microdesc_hash_line,
+ "m %s "
+ "sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa%d\n",
+ method_list, idx);
+ }
+
+ done:
+ tor_free(method_list);
+ return vrs;
+}
+
+/** Initialize networkstatus vote object attributes. */
+void
+dir_common_setup_vote(networkstatus_t **vote, time_t now)
+{
+ *vote = tor_malloc_zero(sizeof(networkstatus_t));
+ (*vote)->type = NS_TYPE_VOTE;
+ (*vote)->published = now;
+ (*vote)->supported_methods = smartlist_new();
+ (*vote)->known_flags = smartlist_new();
+ (*vote)->net_params = smartlist_new();
+ (*vote)->routerstatus_list = smartlist_new();
+ (*vote)->voters = smartlist_new();
+}
+
+/** Helper: Make a new routerinfo containing the right information for a
+ * given vote_routerstatus_t. */
+routerinfo_t *
+dir_common_generate_ri_from_rs(const vote_routerstatus_t *vrs)
+{
+ routerinfo_t *r;
+ const routerstatus_t *rs = &vrs->status;
+ static time_t published = 0;
+
+ r = tor_malloc_zero(sizeof(routerinfo_t));
+ r->cert_expiration_time = TIME_MAX;
+ memcpy(r->cache_info.identity_digest, rs->identity_digest, DIGEST_LEN);
+ memcpy(r->cache_info.signed_descriptor_digest, rs->descriptor_digest,
+ DIGEST_LEN);
+ r->cache_info.do_not_cache = 1;
+ r->cache_info.routerlist_index = -1;
+ r->cache_info.signed_descriptor_body =
+ tor_strdup("123456789012345678901234567890123");
+ r->cache_info.signed_descriptor_len =
+ strlen(r->cache_info.signed_descriptor_body);
+ r->exit_policy = smartlist_new();
+ r->cache_info.published_on = ++published + time(NULL);
+ if (rs->has_bandwidth) {
+ /*
+ * Multiply by 1000 because the routerinfo_t and the routerstatus_t
+ * seem to use different units (*sigh*) and because we seem stuck on
+ * icky and perverse decimal kilobytes (*double sigh*) - see
+ * router_get_advertised_bandwidth_capped() of routerlist.c and
+ * routerstatus_format_entry() of dirserv.c.
+ */
+ r->bandwidthrate = rs->bandwidth_kb * 1000;
+ r->bandwidthcapacity = rs->bandwidth_kb * 1000;
+ }
+ return r;
+}
+
+/** Create routerstatuses and signed vote.
+ * Create routerstatuses using *vrs_gen* and add them to global routerlist.
+ * Next, create signed vote using *sign_skey* and *vote*, which should have
+ * predefined header fields.
+ * Setting *clear_rl* clears the global routerlist before adding the new
+ * routers.
+ * Return the signed vote, same as *vote_out*. Save the number of routers added
+ * in *n_vrs*.
+ */
+networkstatus_t *
+dir_common_add_rs_and_parse(networkstatus_t *vote, networkstatus_t **vote_out,
+ vote_routerstatus_t * (*vrs_gen)(int idx, time_t now),
+ crypto_pk_t *sign_skey, int *n_vrs, time_t now,
+ int clear_rl)
+{
+ vote_routerstatus_t *vrs;
+ char *v_text=NULL;
+ const char *msg=NULL;
+ int idx;
+ was_router_added_t router_added = -1;
+ *vote_out = NULL;
+
+ if (clear_rl) {
+ nodelist_free_all();
+ routerlist_free_all();
+ }
+
+ idx = 0;
+ do {
+ vrs = vrs_gen(idx, now);
+ if (vrs) {
+ smartlist_add(vote->routerstatus_list, vrs);
+ router_added =
+ router_add_to_routerlist(dir_common_generate_ri_from_rs(vrs),
+ &msg,0,0);
+ tt_assert(router_added >= 0);
+ ++idx;
+ }
+ } while (vrs);
+ *n_vrs = idx;
+
+ /* 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);
+
+ done:
+ if (v_text)
+ tor_free(v_text);
+
+ return *vote_out;
+}
+
+/** Create a fake *vote* where *cert* describes the signer, *sign_skey*
+ * is the signing key, and *vrs_gen* is the function we'll use to create the
+ * routers on which we're voting.
+ * We pass *vote_out*, *n_vrs*, and *clear_rl* directly to vrs_gen().
+ * Return 0 on success, return -1 on failure.
+ */
+int
+dir_common_construct_vote_1(networkstatus_t **vote, authority_cert_t *cert,
+ crypto_pk_t *sign_skey,
+ vote_routerstatus_t * (*vrs_gen)(int idx, time_t now),
+ networkstatus_t **vote_out, int *n_vrs,
+ time_t now, int clear_rl)
+{
+ networkstatus_voter_info_t *voter;
+
+ dir_common_setup_vote(vote, now);
+ (*vote)->valid_after = now+1000;
+ (*vote)->fresh_until = now+2000;
+ (*vote)->valid_until = now+3000;
+ (*vote)->vote_seconds = 100;
+ (*vote)->dist_seconds = 200;
+ smartlist_split_string((*vote)->supported_methods, "1 2 3", NULL, 0, -1);
+ (*vote)->client_versions = tor_strdup("0.1.2.14,0.1.2.15");
+ (*vote)->server_versions = tor_strdup("0.1.2.14,0.1.2.15,0.1.2.16");
+ smartlist_split_string((*vote)->known_flags,
+ "Authority Exit Fast Guard Running Stable V2Dir Valid",
+ 0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
+ voter->nickname = tor_strdup("Voter1");
+ voter->address = tor_strdup("1.2.3.4");
+ voter->addr = 0x01020304;
+ voter->dir_port = 80;
+ voter->or_port = 9000;
+ voter->contact = tor_strdup("voter@example.com");
+ crypto_pk_get_digest(cert->identity_key, voter->identity_digest);
+ /*
+ * Set up a vote; generate it; try to parse it.
+ */
+ smartlist_add((*vote)->voters, voter);
+ (*vote)->cert = authority_cert_dup(cert);
+ smartlist_split_string((*vote)->net_params, "circuitwindow=101 foo=990",
+ NULL, 0, 0);
+ *n_vrs = 0;
+ /* add routerstatuses */
+ if (!dir_common_add_rs_and_parse(*vote, vote_out, vrs_gen, sign_skey,
+ n_vrs, now, clear_rl))
+ return -1;
+
+ return 0;
+}
+
+/** See dir_common_construct_vote_1.
+ * Produces a vote with slightly different values.
+ */
+int
+dir_common_construct_vote_2(networkstatus_t **vote, authority_cert_t *cert,
+ crypto_pk_t *sign_skey,
+ vote_routerstatus_t * (*vrs_gen)(int idx, time_t now),
+ networkstatus_t **vote_out, int *n_vrs,
+ time_t now, int clear_rl)
+{
+ networkstatus_voter_info_t *voter;
+
+ dir_common_setup_vote(vote, now);
+ (*vote)->type = NS_TYPE_VOTE;
+ (*vote)->published += 1;
+ (*vote)->valid_after = now+1000;
+ (*vote)->fresh_until = now+3005;
+ (*vote)->valid_until = now+3000;
+ (*vote)->vote_seconds = 100;
+ (*vote)->dist_seconds = 300;
+ smartlist_split_string((*vote)->supported_methods, "1 2 3", NULL, 0, -1);
+ smartlist_split_string((*vote)->known_flags,
+ "Authority Exit Fast Guard MadeOfCheese MadeOfTin "
+ "Running Stable V2Dir Valid", 0,
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
+ voter->nickname = tor_strdup("Voter2");
+ voter->address = tor_strdup("2.3.4.5");
+ voter->addr = 0x02030405;
+ voter->dir_port = 80;
+ voter->or_port = 9000;
+ voter->contact = tor_strdup("voter@example.com");
+ crypto_pk_get_digest(cert->identity_key, voter->identity_digest);
+ /*
+ * Set up a vote; generate it; try to parse it.
+ */
+ smartlist_add((*vote)->voters, voter);
+ (*vote)->cert = authority_cert_dup(cert);
+ if (! (*vote)->net_params)
+ (*vote)->net_params = smartlist_new();
+ smartlist_split_string((*vote)->net_params,
+ "bar=2000000000 circuitwindow=20",
+ NULL, 0, 0);
+ /* add routerstatuses */
+ /* dump the vote and try to parse it. */
+ dir_common_add_rs_and_parse(*vote, vote_out, vrs_gen, sign_skey,
+ n_vrs, now, clear_rl);
+
+ return 0;
+}
+
+/** See dir_common_construct_vote_1.
+ * Produces a vote with slightly different values. Adds a legacy key.
+ */
+int
+dir_common_construct_vote_3(networkstatus_t **vote, authority_cert_t *cert,
+ crypto_pk_t *sign_skey,
+ vote_routerstatus_t * (*vrs_gen)(int idx, time_t now),
+ networkstatus_t **vote_out, int *n_vrs,
+ time_t now, int clear_rl)
+{
+ networkstatus_voter_info_t *voter;
+
+ dir_common_setup_vote(vote, now);
+ (*vote)->valid_after = now+1000;
+ (*vote)->fresh_until = now+2003;
+ (*vote)->valid_until = now+3000;
+ (*vote)->vote_seconds = 100;
+ (*vote)->dist_seconds = 250;
+ smartlist_split_string((*vote)->supported_methods, "1 2 3 4", NULL, 0, -1);
+ (*vote)->client_versions = tor_strdup("0.1.2.14,0.1.2.17");
+ (*vote)->server_versions = tor_strdup("0.1.2.10,0.1.2.15,0.1.2.16");
+ smartlist_split_string((*vote)->known_flags,
+ "Authority Exit Fast Guard Running Stable V2Dir Valid",
+ 0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
+ voter->nickname = tor_strdup("Voter2");
+ voter->address = tor_strdup("3.4.5.6");
+ voter->addr = 0x03040506;
+ voter->dir_port = 80;
+ voter->or_port = 9000;
+ voter->contact = tor_strdup("voter@example.com");
+ crypto_pk_get_digest(cert->identity_key, voter->identity_digest);
+ memset(voter->legacy_id_digest, (int)'A', DIGEST_LEN);
+ /*
+ * Set up a vote; generate it; try to parse it.
+ */
+ smartlist_add((*vote)->voters, voter);
+ (*vote)->cert = authority_cert_dup(cert);
+ smartlist_split_string((*vote)->net_params, "circuitwindow=80 foo=660",
+ NULL, 0, 0);
+ /* add routerstatuses */
+ /* dump the vote and try to parse it. */
+ dir_common_add_rs_and_parse(*vote, vote_out, vrs_gen, sign_skey,
+ n_vrs, now, clear_rl);
+
+ return 0;
+}
+
diff --git a/src/test/test_dir_common.h b/src/test/test_dir_common.h
new file mode 100644
index 0000000000..9557cb7157
--- /dev/null
+++ b/src/test/test_dir_common.h
@@ -0,0 +1,52 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "networkstatus.h"
+#include "routerparse.h"
+
+#define TEST_DIR_ROUTER_ID_1 3
+#define TEST_DIR_ROUTER_ID_2 5
+#define TEST_DIR_ROUTER_ID_3 33
+#define TEST_DIR_ROUTER_ID_4 34
+
+#define TEST_DIR_ROUTER_DD_1 78
+#define TEST_DIR_ROUTER_DD_2 77
+#define TEST_DIR_ROUTER_DD_3 79
+#define TEST_DIR_ROUTER_DD_4 44
+
+int dir_common_authority_pk_init(authority_cert_t **cert1,
+ authority_cert_t **cert2,
+ authority_cert_t **cert3,
+ crypto_pk_t **sign_skey_1,
+ crypto_pk_t **sign_skey_2,
+ crypto_pk_t **sign_skey_3);
+
+routerinfo_t * dir_common_generate_ri_from_rs(const vote_routerstatus_t *vrs);
+
+vote_routerstatus_t * dir_common_gen_routerstatus_for_v3ns(int idx,
+ time_t now);
+
+int dir_common_construct_vote_1(networkstatus_t **vote,
+ authority_cert_t *cert1,
+ crypto_pk_t *sign_skey,
+ vote_routerstatus_t * (*vrs_gen)(int idx, time_t now),
+ networkstatus_t **vote_out, int *n_vrs, time_t now,
+ int clear_rl);
+
+int dir_common_construct_vote_2(networkstatus_t **vote,
+ authority_cert_t *cert2,
+ crypto_pk_t *sign_skey,
+ vote_routerstatus_t * (*vrs_gen)(int idx, time_t now),
+ networkstatus_t **vote_out, int *n_vrs, time_t now,
+ int clear_rl);
+
+int dir_common_construct_vote_3(networkstatus_t **vote,
+ authority_cert_t *cert3,
+ crypto_pk_t *sign_skey,
+ vote_routerstatus_t * (*vrs_gen)(int idx, time_t now),
+ networkstatus_t **vote_out, int *n_vrs, time_t now,
+ int clear_rl);
+
diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c
new file mode 100644
index 0000000000..46d76734e0
--- /dev/null
+++ b/src/test/test_dir_handle_get.c
@@ -0,0 +1,2552 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define RENDCOMMON_PRIVATE
+#define GEOIP_PRIVATE
+#define CONNECTION_PRIVATE
+#define CONFIG_PRIVATE
+#define RENDCACHE_PRIVATE
+
+#include "or.h"
+#include "config.h"
+#include "connection.h"
+#include "directory.h"
+#include "test.h"
+#include "connection.h"
+#include "rendcommon.h"
+#include "rendcache.h"
+#include "router.h"
+#include "routerlist.h"
+#include "rend_test_helpers.h"
+#include "microdesc.h"
+#include "test_helpers.h"
+#include "nodelist.h"
+#include "entrynodes.h"
+#include "routerparse.h"
+#include "networkstatus.h"
+#include "geoip.h"
+#include "dirserv.h"
+#include "torgzip.h"
+#include "dirvote.h"
+
+#ifdef _WIN32
+/* For mkdir() */
+#include <direct.h>
+#else
+#include <dirent.h>
+#endif
+
+#include "vote_descriptors.inc"
+
+#define NS_MODULE dir_handle_get
+
+static void
+connection_write_to_buf_mock(const char *string, size_t len,
+ connection_t *conn, int zlib)
+{
+ (void) zlib;
+
+ tor_assert(string);
+ tor_assert(conn);
+
+ write_to_buf(string, len, conn->outbuf);
+}
+
+#define GET(path) "GET " path " HTTP/1.0\r\n\r\n"
+#define NOT_FOUND "HTTP/1.0 404 Not found\r\n\r\n"
+#define BAD_REQUEST "HTTP/1.0 400 Bad request\r\n\r\n"
+#define SERVER_BUSY "HTTP/1.0 503 Directory busy, try again later\r\n\r\n"
+#define NOT_ENOUGH_CONSENSUS_SIGNATURES "HTTP/1.0 404 " \
+ "Consensus not signed by sufficient number of requested authorities\r\n\r\n"
+
+static tor_addr_t MOCK_TOR_ADDR;
+
+static void
+test_dir_handle_get_bad_request(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(directory_handle_command_get(conn, "", NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(header, OP_EQ, BAD_REQUEST);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_v1_command_not_found(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ // no frontpage configured
+ tt_ptr_op(get_dirportfrontpage(), OP_EQ, NULL);
+
+ /* V1 path */
+ tt_int_op(directory_handle_command_get(conn, GET("/tor/"), NULL, 0),
+ OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static const char*
+mock_get_dirportfrontpage(void)
+{
+ return "HELLO FROM FRONTPAGE";
+}
+
+static void
+test_dir_handle_get_v1_command(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0, body_len = 0;
+ const char *exp_body = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+ MOCK(get_dirportfrontpage, mock_get_dirportfrontpage);
+
+ exp_body = get_dirportfrontpage();
+ body_len = strlen(exp_body);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(directory_handle_command_get(conn, GET("/tor/"), NULL, 0),
+ OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, body_len+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/html\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 20\r\n"));
+
+ tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_str_op(body, OP_EQ, exp_body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ UNMOCK(get_dirportfrontpage);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+}
+
+static void
+test_dir_handle_get_not_found(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ /* Unrecognized path */
+ tt_int_op(directory_handle_command_get(conn, GET("/anything"), NULL, 0),
+ OP_EQ, 0);
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_robots_txt(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ tt_int_op(directory_handle_command_get(conn, GET("/tor/robots.txt"),
+ NULL, 0), OP_EQ, 0);
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, 29, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 28\r\n"));
+
+ tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_str_op(body, OP_EQ, "User-agent: *\r\nDisallow: /\r\n");
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+}
+
+static void
+test_dir_handle_get_bytes_txt(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0, body_len = 0;
+ char buff[30];
+ char *exp_body = NULL;
+ (void) data;
+
+ exp_body = directory_dump_request_log();
+ body_len = strlen(exp_body);
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ tt_int_op(directory_handle_command_get(conn, GET("/tor/bytes.txt"), NULL, 0),
+ OP_EQ, 0);
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, body_len+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Pragma: no-cache\r\n"));
+
+ sprintf(buff, "Content-Length: %ld\r\n", (long) body_len);
+ tt_assert(strstr(header, buff));
+
+ tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_str_op(body, OP_EQ, exp_body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ tor_free(exp_body);
+}
+
+#define RENDEZVOUS2_GET(descid) GET("/tor/rendezvous2/" descid)
+static void
+test_dir_handle_get_rendezvous2_not_found_if_not_encrypted(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ // connection is not encrypted
+ tt_assert(!connection_dir_is_encrypted(conn))
+
+ tt_int_op(directory_handle_command_get(conn, RENDEZVOUS2_GET(), NULL, 0),
+ OP_EQ, 0);
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_rendezvous2_on_encrypted_conn_with_invalid_desc_id(
+ void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ // connection is encrypted
+ TO_CONN(conn)->linked = 1;
+ tt_assert(connection_dir_is_encrypted(conn));
+
+ tt_int_op(directory_handle_command_get(conn,
+ RENDEZVOUS2_GET("invalid-desc-id"), NULL, 0), OP_EQ, 0);
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(header, OP_EQ, BAD_REQUEST);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_rendezvous2_on_encrypted_conn_not_well_formed(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ // connection is encrypted
+ TO_CONN(conn)->linked = 1;
+ tt_assert(connection_dir_is_encrypted(conn));
+
+ //TODO: this cant be reached because rend_valid_descriptor_id() prevents this
+ //case to happen. This test is the same as
+ //test_dir_handle_get_rendezvous2_on_encrypted_conn_with_invalid_desc_id
+ //We should refactor to remove the case from the switch.
+
+ const char *req = RENDEZVOUS2_GET("1bababababababababababababababab");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(header, OP_EQ, BAD_REQUEST);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_rendezvous2_not_found(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ rend_cache_init();
+
+ // connection is encrypted
+ TO_CONN(conn)->linked = 1;
+ tt_assert(connection_dir_is_encrypted(conn));
+
+ const char *req = RENDEZVOUS2_GET("3xqunszqnaolrrfmtzgaki7mxelgvkje");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ rend_cache_free_all();
+}
+
+NS_DECL(const routerinfo_t *, router_get_my_routerinfo, (void));
+NS_DECL(int, hid_serv_responsible_for_desc_id, (const char *id));
+
+static routerinfo_t *mock_routerinfo;
+static int hid_serv_responsible_for_desc_id_response;
+
+static const routerinfo_t *
+NS(router_get_my_routerinfo)(void)
+{
+ if (!mock_routerinfo) {
+ mock_routerinfo = tor_malloc_zero(sizeof(routerinfo_t));
+ }
+
+ return mock_routerinfo;
+}
+
+static int
+NS(hid_serv_responsible_for_desc_id)(const char *id)
+{
+ (void)id;
+ return hid_serv_responsible_for_desc_id_response;
+}
+
+static void
+test_dir_handle_get_rendezvous2_on_encrypted_conn_success(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ char buff[30];
+ char req[70];
+ rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
+ char *service_id = NULL;
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ size_t body_len = 0;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+ NS_MOCK(router_get_my_routerinfo);
+ NS_MOCK(hid_serv_responsible_for_desc_id);
+
+ rend_cache_init();
+ hid_serv_responsible_for_desc_id_response = 1;
+
+ /* create a valid rend service descriptor */
+ #define RECENT_TIME -10
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+
+ tt_int_op(rend_cache_store_v2_desc_as_dir(desc_holder->desc_str),
+ OP_EQ, RCS_OKAY);
+
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ // connection is encrypted
+ TO_CONN(conn)->linked = 1;
+ tt_assert(connection_dir_is_encrypted(conn));
+
+ sprintf(req, RENDEZVOUS2_GET("%s"), desc_id_base32);
+
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ body_len = strlen(desc_holder->desc_str);
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, body_len+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Pragma: no-cache\r\n"));
+ sprintf(buff, "Content-Length: %ld\r\n", (long) body_len);
+ tt_assert(strstr(header, buff));
+
+ tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_str_op(body, OP_EQ, desc_holder->desc_str);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ NS_UNMOCK(router_get_my_routerinfo);
+ NS_UNMOCK(hid_serv_responsible_for_desc_id);
+ tor_free(mock_routerinfo->cache_info.signed_descriptor_body);
+ tor_free(mock_routerinfo);
+
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_cache_free_all();
+}
+
+#define MICRODESC_GET(digest) GET("/tor/micro/d/" digest)
+static void
+test_dir_handle_get_micro_d_not_found(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ #define B64_256_1 "8/Pz8/u7vz8/Pz+7vz8/Pz+7u/Pz8/P7u/Pz8/P7u78"
+ #define B64_256_2 "zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMw"
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = MICRODESC_GET(B64_256_1 "-" B64_256_2);
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static or_options_t *mock_options = NULL;
+static void
+init_mock_options(void)
+{
+ mock_options = malloc(sizeof(or_options_t));
+ memset(mock_options, 0, sizeof(or_options_t));
+ mock_options->TestingTorNetwork = 1;
+}
+
+static const or_options_t *
+mock_get_options(void)
+{
+ tor_assert(mock_options);
+ return mock_options;
+}
+
+static const char microdesc[] =
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMjlHH/daN43cSVRaHBwgUfnszzAhg98EvivJ9Qxfv51mvQUxPjQ07es\n"
+ "gV/3n8fyh3Kqr/ehi9jxkdgSRfSnmF7giaHL1SLZ29kA7KtST+pBvmTpDtHa3ykX\n"
+ "Xorc7hJvIyTZoc1HU+5XSynj3gsBE5IGK1ZRzrNS688LnuZMVp1tAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n";
+
+static void
+test_dir_handle_get_micro_d(void *data)
+{
+ dir_connection_t *conn = NULL;
+ microdesc_cache_t *mc = NULL ;
+ smartlist_t *list = NULL;
+ char digest[DIGEST256_LEN];
+ char digest_base64[128];
+ char path[80];
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* SETUP */
+ init_mock_options();
+ const char *fn = get_fname("dir_handle_datadir_test1");
+ mock_options->DataDirectory = tor_strdup(fn);
+
+#ifdef _WIN32
+ tt_int_op(0, OP_EQ, mkdir(mock_options->DataDirectory));
+#else
+ tt_int_op(0, OP_EQ, mkdir(mock_options->DataDirectory, 0700));
+#endif
+
+ /* Add microdesc to cache */
+ crypto_digest256(digest, microdesc, strlen(microdesc), DIGEST_SHA256);
+ base64_encode_nopad(digest_base64, sizeof(digest_base64),
+ (uint8_t *) digest, DIGEST256_LEN);
+
+ mc = get_microdesc_cache();
+ list = microdescs_add_to_cache(mc, microdesc, NULL, SAVED_NOWHERE, 0,
+ time(NULL), NULL);
+ tt_int_op(1, OP_EQ, smartlist_len(list));
+
+ /* Make the request */
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ sprintf(path, MICRODESC_GET("%s"), digest_base64);
+ tt_int_op(directory_handle_command_get(conn, path, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(microdesc)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+
+ tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_str_op(body, OP_EQ, microdesc);
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+
+ or_options_free(mock_options); mock_options = NULL;
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ smartlist_free(list);
+ microdesc_free_all();
+}
+
+static void
+test_dir_handle_get_micro_d_server_busy(void *data)
+{
+ dir_connection_t *conn = NULL;
+ microdesc_cache_t *mc = NULL ;
+ smartlist_t *list = NULL;
+ char digest[DIGEST256_LEN];
+ char digest_base64[128];
+ char path[80];
+ char *header = NULL;
+ (void) data;
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* SETUP */
+ init_mock_options();
+ const char *fn = get_fname("dir_handle_datadir_test2");
+ mock_options->DataDirectory = tor_strdup(fn);
+
+#ifdef _WIN32
+ tt_int_op(0, OP_EQ, mkdir(mock_options->DataDirectory));
+#else
+ tt_int_op(0, OP_EQ, mkdir(mock_options->DataDirectory, 0700));
+#endif
+
+ /* Add microdesc to cache */
+ crypto_digest256(digest, microdesc, strlen(microdesc), DIGEST_SHA256);
+ base64_encode_nopad(digest_base64, sizeof(digest_base64),
+ (uint8_t *) digest, DIGEST256_LEN);
+
+ mc = get_microdesc_cache();
+ list = microdescs_add_to_cache(mc, microdesc, NULL, SAVED_NOWHERE, 0,
+ time(NULL), NULL);
+ tt_int_op(1, OP_EQ, smartlist_len(list));
+
+ //Make it busy
+ mock_options->CountPrivateBandwidth = 1;
+
+ /* Make the request */
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ sprintf(path, MICRODESC_GET("%s"), digest_base64);
+ tt_int_op(directory_handle_command_get(conn, path, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+
+ or_options_free(mock_options); mock_options = NULL;
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ smartlist_free(list);
+ microdesc_free_all();
+}
+
+#define BRIDGES_PATH "/tor/networkstatus-bridges"
+static void
+test_dir_handle_get_networkstatus_bridges_not_found_without_auth(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* SETUP */
+ init_mock_options();
+ mock_options->BridgeAuthoritativeDir = 1;
+ mock_options->BridgePassword_AuthDigest_ = tor_strdup("digest");
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ TO_CONN(conn)->linked = 1;
+
+ const char *req = GET(BRIDGES_PATH);
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+ or_options_free(mock_options); mock_options = NULL;
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_networkstatus_bridges(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* SETUP */
+ init_mock_options();
+ mock_options->BridgeAuthoritativeDir = 1;
+ mock_options->BridgePassword_AuthDigest_ = tor_malloc(DIGEST256_LEN);
+ crypto_digest256(mock_options->BridgePassword_AuthDigest_,
+ "abcdefghijklm12345", 18, DIGEST_SHA256);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ TO_CONN(conn)->linked = 1;
+
+ const char *req = "GET " BRIDGES_PATH " HTTP/1.0\r\n"
+ "Authorization: Basic abcdefghijklm12345\r\n\r\n";
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 0\r\n"));
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+ or_options_free(mock_options); mock_options = NULL;
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_networkstatus_bridges_not_found_wrong_auth(void *data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* SETUP */
+ init_mock_options();
+ mock_options->BridgeAuthoritativeDir = 1;
+ mock_options->BridgePassword_AuthDigest_ = tor_malloc(DIGEST256_LEN);
+ crypto_digest256(mock_options->BridgePassword_AuthDigest_,
+ "abcdefghijklm12345", 18, DIGEST_SHA256);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ TO_CONN(conn)->linked = 1;
+
+ const char *req = "GET " BRIDGES_PATH " HTTP/1.0\r\n"
+ "Authorization: Basic NOTSAMEDIGEST\r\n\r\n";
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+ or_options_free(mock_options); mock_options = NULL;
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+#define SERVER_DESC_GET(id) GET("/tor/server/" id)
+static void
+test_dir_handle_get_server_descriptors_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = SERVER_DESC_GET("invalid");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+ tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_SERVER_BY_FP);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ or_options_free(mock_options); mock_options = NULL;
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_server_descriptors_all(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ /* Setup fake routerlist. */
+ helper_setup_fake_routerlist();
+
+ //TODO: change to router_get_my_extrainfo when testing "extra" path
+ NS_MOCK(router_get_my_routerinfo);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ // We are one of the routers
+ routerlist_t *our_routerlist = router_get_routerlist();
+ tt_int_op(smartlist_len(our_routerlist->routers), OP_GE, 1);
+ mock_routerinfo = smartlist_get(our_routerlist->routers, 0);
+ set_server_identity_key(mock_routerinfo->identity_pkey);
+
+ /* Treat "all" requests as if they were unencrypted */
+ mock_routerinfo->cache_info.send_unencrypted = 1;
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = SERVER_DESC_GET("all");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ //TODO: Is this a BUG?
+ //It requires strlen(signed_descriptor_len)+1 as body_len but returns a body
+ //which is smaller than that by annotation_len bytes
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used,
+ mock_routerinfo->cache_info.signed_descriptor_len+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+
+ //TODO: Is this a BUG?
+ //This is what should be expected: tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_int_op(body_used, OP_EQ,
+ mock_routerinfo->cache_info.signed_descriptor_len);
+
+ tt_str_op(body, OP_EQ, mock_routerinfo->cache_info.signed_descriptor_body +
+ mock_routerinfo->cache_info.annotations_len);
+ tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+
+ routerlist_free_all();
+ nodelist_free_all();
+ entry_guards_free_all();
+}
+
+static char
+TEST_DESCRIPTOR[] =
+"@uploaded-at 2014-06-08 19:20:11\n"
+"@source \"127.0.0.1\"\n"
+"router test000a 127.0.0.1 5000 0 7000\n"
+"platform Tor 0.2.5.3-alpha-dev on Linux\n"
+"protocols Link 1 2 Circuit 1\n"
+"published 2014-06-08 19:20:11\n"
+"fingerprint C7E7 CCB8 179F 8CC3 7F5C 8A04 2B3A 180B 934B 14BA\n"
+"uptime 0\n"
+"bandwidth 1073741824 1073741824 0\n"
+"extra-info-digest 67A152A4C7686FB07664F872620635F194D76D95\n"
+"caches-extra-info\n"
+"onion-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIGJAoGBAOuBUIEBARMkkka/TGyaQNgUEDLP0KG7sy6KNQTNOlZHUresPr/vlVjo\n"
+"HPpLMfu9M2z18c51YX/muWwY9x4MyQooD56wI4+AqXQcJRwQfQlPn3Ay82uZViA9\n"
+"DpBajRieLlKKkl145KjArpD7F5BVsqccvjErgFYXvhhjSrx7BVLnAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"signing-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIGJAoGBAN6NLnSxWQnFXxqZi5D3b0BMgV6y9NJLGjYQVP+eWtPZWgqyv4zeYsqv\n"
+"O9y6c5lvxyUxmNHfoAbe/s8f2Vf3/YaC17asAVSln4ktrr3e9iY74a9RMWHv1Gzk\n"
+"3042nMcqj3PEhRN0PoLkcOZNjjmNbaqki6qy9bWWZDNTdo+uI44dAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"hidden-service-dir\n"
+"contact auth0@test.test\n"
+"ntor-onion-key pK4bs08ERYN591jj7ca17Rn9Q02TIEfhnjR6hSq+fhU=\n"
+"reject *:*\n"
+"router-signature\n"
+"-----BEGIN SIGNATURE-----\n"
+"rx88DuM3Y7tODlHNDDEVzKpwh3csaG1or+T4l2Xs1oq3iHHyPEtB6QTLYrC60trG\n"
+"aAPsj3DEowGfjga1b248g2dtic8Ab+0exfjMm1RHXfDam5TXXZU3A0wMyoHjqHuf\n"
+"eChGPgFNUvEc+5YtD27qEDcUjcinYztTs7/dzxBT4PE=\n"
+"-----END SIGNATURE-----\n";
+
+static void
+test_dir_handle_get_server_descriptors_authority(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ crypto_pk_t *identity_pkey = pk_generate(0);
+ (void) data;
+
+ NS_MOCK(router_get_my_routerinfo);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* init mock */
+ router_get_my_routerinfo();
+ crypto_pk_get_digest(identity_pkey,
+ mock_routerinfo->cache_info.identity_digest);
+
+ // the digest is mine (the channel is unnecrypted, so we must allow sending)
+ set_server_identity_key(identity_pkey);
+ mock_routerinfo->cache_info.send_unencrypted = 1;
+
+ /* Setup descriptor */
+ long annotation_len = strstr(TEST_DESCRIPTOR, "router ") - TEST_DESCRIPTOR;
+ mock_routerinfo->cache_info.signed_descriptor_body =
+ tor_strdup(TEST_DESCRIPTOR);
+ mock_routerinfo->cache_info.signed_descriptor_len =
+ strlen(TEST_DESCRIPTOR) - annotation_len;;
+ mock_routerinfo->cache_info.annotations_len = annotation_len;
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = SERVER_DESC_GET("authority");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ //TODO: Is this a BUG?
+ //It requires strlen(TEST_DESCRIPTOR)+1 as body_len but returns a body which
+ //is smaller than that by annotation_len bytes
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(TEST_DESCRIPTOR)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+
+ tt_int_op(body_used, OP_EQ, strlen(body));
+
+ tt_str_op(body, OP_EQ, TEST_DESCRIPTOR + annotation_len);
+ tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+ UNMOCK(connection_write_to_buf_impl_);
+ tor_free(mock_routerinfo->cache_info.signed_descriptor_body);
+ tor_free(mock_routerinfo);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ crypto_pk_free(identity_pkey);
+}
+
+static void
+test_dir_handle_get_server_descriptors_fp(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ crypto_pk_t *identity_pkey = pk_generate(0);
+ (void) data;
+
+ NS_MOCK(router_get_my_routerinfo);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* init mock */
+ router_get_my_routerinfo();
+ crypto_pk_get_digest(identity_pkey,
+ mock_routerinfo->cache_info.identity_digest);
+
+ // the digest is mine (the channel is unnecrypted, so we must allow sending)
+ set_server_identity_key(identity_pkey);
+ mock_routerinfo->cache_info.send_unencrypted = 1;
+
+ /* Setup descriptor */
+ long annotation_len = strstr(TEST_DESCRIPTOR, "router ") - TEST_DESCRIPTOR;
+ mock_routerinfo->cache_info.signed_descriptor_body =
+ tor_strdup(TEST_DESCRIPTOR);
+ mock_routerinfo->cache_info.signed_descriptor_len =
+ strlen(TEST_DESCRIPTOR) - annotation_len;
+ mock_routerinfo->cache_info.annotations_len = annotation_len;
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ #define HEX1 "Fe0daff89127389bc67558691231234551193EEE"
+ #define HEX2 "Deadbeef99999991111119999911111111f00ba4"
+ const char *hex_digest = hex_str(mock_routerinfo->cache_info.identity_digest,
+ DIGEST_LEN);
+
+ char req[155];
+ sprintf(req, SERVER_DESC_GET("fp/%s+" HEX1 "+" HEX2), hex_digest);
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ //TODO: Is this a BUG?
+ //It requires strlen(TEST_DESCRIPTOR)+1 as body_len but returns a body which
+ //is smaller than that by annotation_len bytes
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(TEST_DESCRIPTOR)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+
+ tt_int_op(body_used, OP_EQ, strlen(body));
+
+ tt_str_op(body, OP_EQ, TEST_DESCRIPTOR + annotation_len);
+ tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+ UNMOCK(connection_write_to_buf_impl_);
+ tor_free(mock_routerinfo->cache_info.signed_descriptor_body);
+ tor_free(mock_routerinfo);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ crypto_pk_free(identity_pkey);
+}
+
+#define HEX1 "Fe0daff89127389bc67558691231234551193EEE"
+#define HEX2 "Deadbeef99999991111119999911111111f00ba4"
+
+static void
+test_dir_handle_get_server_descriptors_d(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ crypto_pk_t *identity_pkey = pk_generate(0);
+ (void) data;
+
+ /* Setup fake routerlist. */
+ helper_setup_fake_routerlist();
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* Get one router's signed_descriptor_digest */
+ routerlist_t *our_routerlist = router_get_routerlist();
+ tt_int_op(smartlist_len(our_routerlist->routers), OP_GE, 1);
+ routerinfo_t *router = smartlist_get(our_routerlist->routers, 0);
+ const char *hex_digest = hex_str(router->cache_info.signed_descriptor_digest,
+ DIGEST_LEN);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ char req_header[155];
+ sprintf(req_header, SERVER_DESC_GET("d/%s+" HEX1 "+" HEX2), hex_digest);
+ tt_int_op(directory_handle_command_get(conn, req_header, NULL, 0), OP_EQ, 0);
+
+ //TODO: Is this a BUG?
+ //It requires strlen(signed_descriptor_len)+1 as body_len but returns a body
+ //which is smaller than that by annotation_len bytes
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used,
+ router->cache_info.signed_descriptor_len+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+
+ //TODO: Is this a BUG?
+ //This is what should be expected:
+ //tt_int_op(body_used, OP_EQ, strlen(body));
+ tt_int_op(body_used, OP_EQ, router->cache_info.signed_descriptor_len);
+
+ tt_str_op(body, OP_EQ, router->cache_info.signed_descriptor_body +
+ router->cache_info.annotations_len);
+ tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ tor_free(mock_routerinfo);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ crypto_pk_free(identity_pkey);
+
+ routerlist_free_all();
+ nodelist_free_all();
+ entry_guards_free_all();
+}
+
+static void
+test_dir_handle_get_server_descriptors_busy(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ crypto_pk_t *identity_pkey = pk_generate(0);
+ (void) data;
+
+ /* Setup fake routerlist. */
+ helper_setup_fake_routerlist();
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ //Make it busy
+ MOCK(get_options, mock_get_options);
+ init_mock_options();
+ mock_options->CountPrivateBandwidth = 1;
+
+ /* Get one router's signed_descriptor_digest */
+ routerlist_t *our_routerlist = router_get_routerlist();
+ tt_int_op(smartlist_len(our_routerlist->routers), OP_GE, 1);
+ routerinfo_t *router = smartlist_get(our_routerlist->routers, 0);
+ const char *hex_digest = hex_str(router->cache_info.signed_descriptor_digest,
+ DIGEST_LEN);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ #define HEX1 "Fe0daff89127389bc67558691231234551193EEE"
+ #define HEX2 "Deadbeef99999991111119999911111111f00ba4"
+ char req_header[155];
+ sprintf(req_header, SERVER_DESC_GET("d/%s+" HEX1 "+" HEX2), hex_digest);
+ tt_int_op(directory_handle_command_get(conn, req_header, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+ tt_int_op(conn->dir_spool_src, OP_EQ, DIR_SPOOL_NONE);
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+ tor_free(mock_routerinfo);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ crypto_pk_free(identity_pkey);
+
+ routerlist_free_all();
+ nodelist_free_all();
+ entry_guards_free_all();
+}
+
+static void
+test_dir_handle_get_server_keys_bad_req(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(BAD_REQUEST, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_server_keys_all_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/all");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+#define TEST_CERTIFICATE AUTHORITY_CERT_3
+#define TEST_SIGNING_KEY AUTHORITY_SIGNKEY_A_DIGEST
+extern const char AUTHORITY_CERT_3[];
+extern const char AUTHORITY_SIGNKEY_A_DIGEST[];
+
+static const char TEST_CERT_IDENT_KEY[] =
+ "D867ACF56A9D229B35C25F0090BC9867E906BE69";
+
+static void
+test_dir_handle_get_server_keys_all(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ const char digest[DIGEST_LEN] = "";
+
+ dir_server_t *ds = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest,
+ NULL, V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/all");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(TEST_CERTIFICATE)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 1883\r\n"));
+
+ tt_str_op(TEST_CERTIFICATE, OP_EQ, body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+
+ clear_dir_servers();
+ routerlist_free_all();
+}
+
+static void
+test_dir_handle_get_server_keys_authority_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/authority");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static authority_cert_t * mock_cert = NULL;
+
+static authority_cert_t *
+get_my_v3_authority_cert_m(void)
+{
+ tor_assert(mock_cert);
+ return mock_cert;
+}
+
+static void
+test_dir_handle_get_server_keys_authority(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ mock_cert = authority_cert_parse_from_string(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);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/authority");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(TEST_CERTIFICATE)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 1883\r\n"));
+
+ tt_str_op(TEST_CERTIFICATE, OP_EQ, body);
+
+ done:
+ UNMOCK(get_my_v3_authority_cert);
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ authority_cert_free(mock_cert); mock_cert = NULL;
+}
+
+static void
+test_dir_handle_get_server_keys_fp_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/fp/somehex");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_server_keys_fp(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ dir_server_t *ds = NULL;
+ const char digest[DIGEST_LEN] = "";
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest,
+ NULL, V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ char req[71];
+ sprintf(req, GET("/tor/keys/fp/%s"), TEST_CERT_IDENT_KEY);
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(TEST_CERTIFICATE)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 1883\r\n"));
+
+ tt_str_op(TEST_CERTIFICATE, OP_EQ, body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ clear_dir_servers();
+ routerlist_free_all();
+}
+
+static void
+test_dir_handle_get_server_keys_sk_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/sk/somehex");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_server_keys_sk(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ mock_cert = authority_cert_parse_from_string(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);
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ char req[71];
+ sprintf(req, GET("/tor/keys/sk/%s"), TEST_SIGNING_KEY);
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(TEST_CERTIFICATE)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 1883\r\n"));
+
+ tt_str_op(TEST_CERTIFICATE, OP_EQ, body);
+
+ done:
+ UNMOCK(get_my_v3_authority_cert);
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ authority_cert_free(mock_cert); mock_cert = NULL;
+ tor_free(header);
+ tor_free(body);
+}
+
+static void
+test_dir_handle_get_server_keys_fpsk_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ const char *req = GET("/tor/keys/fp-sk/somehex");
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_server_keys_fpsk(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ dir_server_t *ds = NULL;
+ const char digest[DIGEST_LEN] = "";
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest,
+ NULL, V3_DIRINFO, 1.0);
+ tt_assert(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+ dir_server_add(ds);
+
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ char req[115];
+ sprintf(req, GET("/tor/keys/fp-sk/%s-%s"),
+ TEST_CERT_IDENT_KEY, TEST_SIGNING_KEY);
+
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(TEST_CERTIFICATE)+1, 0);
+
+ tt_assert(header);
+ tt_assert(body);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 1883\r\n"));
+
+ tt_str_op(TEST_CERTIFICATE, OP_EQ, body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+
+ clear_dir_servers();
+ routerlist_free_all();
+}
+
+static void
+test_dir_handle_get_server_keys_busy(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ dir_server_t *ds = NULL;
+ const char digest[DIGEST_LEN] = "";
+ (void) data;
+
+ clear_dir_servers();
+ routerlist_free_all();
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest,
+ NULL, V3_DIRINFO, 1.0);
+ tt_assert(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+ dir_server_add(ds);
+
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* setup busy server */
+ init_mock_options();
+ mock_options->CountPrivateBandwidth = 1;
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ char req[71];
+ sprintf(req, GET("/tor/keys/fp/%s"), TEST_CERT_IDENT_KEY);
+ tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+ done:
+ UNMOCK(get_options);
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ or_options_free(mock_options); mock_options = NULL;
+
+ clear_dir_servers();
+ routerlist_free_all();
+}
+
+static networkstatus_t *mock_ns_val = NULL;
+static networkstatus_t *
+mock_ns_get_by_flavor(consensus_flavor_t f)
+{
+ (void)f;
+ return mock_ns_val;
+}
+
+static void
+test_dir_handle_get_status_vote_current_consensus_ns_not_enough_sigs(void* d)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *stats = NULL;
+ (void) d;
+
+ /* init mock */
+ mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t));
+ mock_ns_val->flavor = FLAV_NS;
+ mock_ns_val->voters = smartlist_new();
+
+ /* init mock */
+ init_mock_options();
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+ MOCK(networkstatus_get_latest_consensus_by_flavor, mock_ns_get_by_flavor);
+
+ /* start gathering stats */
+ mock_options->DirReqStatistics = 1;
+ geoip_dirreq_stats_init(time(NULL));
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/consensus-ns/" HEX1 "+" HEX2), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+
+ tt_assert(header);
+ tt_str_op(NOT_ENOUGH_CONSENSUS_SIGNATURES, OP_EQ, header);
+
+ stats = geoip_format_dirreq_stats(time(NULL));
+ tt_assert(stats);
+ tt_assert(strstr(stats, "not-enough-sigs=8"));
+
+ done:
+ UNMOCK(networkstatus_get_latest_consensus_by_flavor);
+ UNMOCK(connection_write_to_buf_impl_);
+ UNMOCK(get_options);
+
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(stats);
+ smartlist_free(mock_ns_val->voters);
+ tor_free(mock_ns_val);
+ or_options_free(mock_options); mock_options = NULL;
+}
+
+static void
+test_dir_handle_get_status_vote_current_consensus_ns_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ char *stats = NULL;
+ (void) data;
+
+ init_mock_options();
+
+ MOCK(get_options, mock_get_options);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ /* start gathering stats */
+ mock_options->DirReqStatistics = 1;
+ geoip_dirreq_stats_init(time(NULL));
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/consensus-ns"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ stats = geoip_format_dirreq_stats(time(NULL));
+ tt_assert(stats);
+ tt_assert(strstr(stats, "not-found=8"));
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ UNMOCK(get_options);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(stats);
+ or_options_free(mock_options); mock_options = NULL;
+}
+
+NS_DECL(int, geoip_get_country_by_addr, (const tor_addr_t *addr));
+
+int
+NS(geoip_get_country_by_addr)(const tor_addr_t *addr)
+{
+ (void)addr;
+ CALLED(geoip_get_country_by_addr)++;
+ return 1;
+}
+
+static void
+status_vote_current_consensus_ns_test(char **header, char **body,
+ size_t *body_len)
+{
+ common_digests_t digests;
+ dir_connection_t *conn = NULL;
+
+ #define NETWORK_STATUS "some network status string"
+ dirserv_set_cached_consensus_networkstatus(NETWORK_STATUS, "ns", &digests,
+ time(NULL));
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ tt_assert(mock_options);
+ mock_options->DirReqStatistics = 1;
+ geoip_dirreq_stats_init(time(NULL));
+
+ /* init geoip database */
+ geoip_parse_entry("10,50,AB", AF_INET);
+ tt_str_op("ab", OP_EQ, geoip_get_country_name(1));
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ TO_CONN(conn)->address = tor_strdup("127.0.0.1");
+
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/consensus-ns"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, header, MAX_HEADERS_SIZE,
+ body, body_len, strlen(NETWORK_STATUS)+7, 0);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+}
+
+static void
+test_dir_handle_get_status_vote_current_consensus_ns(void* data)
+{
+ char *header = NULL;
+ char *body = NULL, *comp_body = NULL;
+ size_t body_used = 0, comp_body_used = 0;
+ char *stats = NULL, *hist = NULL;
+ (void) data;
+
+ dirserv_free_all();
+ clear_geoip_db();
+
+ NS_MOCK(geoip_get_country_by_addr);
+ MOCK(get_options, mock_get_options);
+
+ init_mock_options();
+
+ status_vote_current_consensus_ns_test(&header, &comp_body, &comp_body_used);
+ tt_assert(header);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Pragma: no-cache\r\n"));
+
+ compress_method_t compression = detect_compression_method(comp_body,
+ comp_body_used);
+ tt_int_op(ZLIB_METHOD, OP_EQ, compression);
+
+ tor_gzip_uncompress(&body, &body_used, comp_body, comp_body_used,
+ compression, 0, LOG_PROTOCOL_WARN);
+
+ tt_str_op(NETWORK_STATUS, OP_EQ, body);
+ tt_int_op(strlen(NETWORK_STATUS), OP_EQ, body_used);
+
+ stats = geoip_format_dirreq_stats(time(NULL));
+ tt_assert(stats);
+
+ tt_assert(strstr(stats, "ok=8"));
+ tt_assert(strstr(stats, "dirreq-v3-ips ab=8"));
+ tt_assert(strstr(stats, "dirreq-v3-reqs ab=8"));
+ tt_assert(strstr(stats, "dirreq-v3-direct-dl"
+ " complete=0,timeout=0,running=4"));
+
+ hist = geoip_get_request_history();
+ tt_assert(hist);
+ tt_str_op("ab=8", OP_EQ, hist);
+
+ done:
+ NS_UNMOCK(geoip_get_country_by_addr);
+ UNMOCK(get_options);
+ tor_free(header);
+ tor_free(comp_body);
+ tor_free(body);
+ tor_free(stats);
+ tor_free(hist);
+ or_options_free(mock_options); mock_options = NULL;
+
+ dirserv_free_all();
+ clear_geoip_db();
+}
+
+static void
+test_dir_handle_get_status_vote_current_consensus_ns_busy(void* data)
+{
+ char *header = NULL;
+ char *body = NULL;
+ size_t body_used = 0;
+ char *stats = NULL;
+ (void) data;
+
+ dirserv_free_all();
+ clear_geoip_db();
+
+ MOCK(get_options, mock_get_options);
+
+ // Make it busy
+ init_mock_options();
+ mock_options->CountPrivateBandwidth = 1;
+
+ status_vote_current_consensus_ns_test(&header, &body, &body_used);
+ tt_assert(header);
+
+ tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+ stats = geoip_format_dirreq_stats(time(NULL));
+ tt_assert(stats);
+ tt_assert(strstr(stats, "busy=8"));
+
+ done:
+ UNMOCK(get_options);
+ tor_free(header);
+ tor_free(body);
+ or_options_free(mock_options); mock_options = NULL;
+
+ tor_free(stats);
+ dirserv_free_all();
+ clear_geoip_db();
+}
+
+static void
+test_dir_handle_get_status_vote_current_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/" HEX1), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+#define VOTE_DIGEST "312A4890D4D832597ABBD3089C782DBBFB81E48D"
+
+static void
+status_vote_current_d_test(char **header, char **body, size_t *body_l)
+{
+ dir_connection_t *conn = NULL;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/d/" VOTE_DIGEST), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, header, MAX_HEADERS_SIZE,
+ body, body_l, strlen(VOTE_BODY_V3)+1, 0);
+ tt_assert(header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+}
+
+static void
+status_vote_next_d_test(char **header, char **body, size_t *body_l)
+{
+ dir_connection_t *conn = NULL;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/next/d/" VOTE_DIGEST), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, header, MAX_HEADERS_SIZE,
+ body, body_l, strlen(VOTE_BODY_V3)+1, 0);
+ tt_assert(header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+}
+
+static void
+test_dir_handle_get_status_vote_current_d_not_found(void* data)
+{
+ char *header = NULL;
+ (void) data;
+
+ status_vote_current_d_test(&header, NULL, NULL);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_status_vote_next_d_not_found(void* data)
+{
+ char *header = NULL;
+ (void) data;
+
+ status_vote_next_d_test(&header, NULL, NULL);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_status_vote_d(void* data)
+{
+ char *header = NULL, *body = NULL;
+ size_t body_used = 0;
+ dir_server_t *ds = NULL;
+ const char digest[DIGEST_LEN] = "";
+ (void) data;
+
+ clear_dir_servers();
+ dirvote_free_all();
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest,
+ NULL, V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+
+ init_mock_options();
+ mock_options->AuthoritativeDir = 1;
+ mock_options->V3AuthoritativeDir = 1;
+ mock_options->TestingV3AuthVotingStartOffset = 0;
+ mock_options->TestingV3AuthInitialVotingInterval = 1;
+ mock_options->TestingV3AuthInitialVoteDelay = 1;
+ mock_options->TestingV3AuthInitialDistDelay = 1;
+
+ time_t now = 1441223455 -1;
+ dirvote_recalculate_timing(mock_options, now);
+
+ const char *msg_out = NULL;
+ int status_out = 0;
+ struct pending_vote_t *pv = dirvote_add_vote(VOTE_BODY_V3, &msg_out,
+ &status_out);
+ tt_assert(pv);
+
+ status_vote_current_d_test(&header, &body, &body_used);
+
+ tt_assert(header);
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 4135\r\n"));
+
+ tt_str_op(VOTE_BODY_V3, OP_EQ, body);
+
+ tor_free(header);
+ tor_free(body);
+
+ status_vote_next_d_test(&header, &body, &body_used);
+
+ tt_assert(header);
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 4135\r\n"));
+
+ tt_str_op(VOTE_BODY_V3, OP_EQ, body);
+
+ done:
+ tor_free(header);
+ tor_free(body);
+ or_options_free(mock_options); mock_options = NULL;
+
+ clear_dir_servers();
+ dirvote_free_all();
+}
+
+static void
+test_dir_handle_get_status_vote_next_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/next/" HEX1), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+status_vote_next_consensus_test(char **header, char **body, size_t *body_used)
+{
+ dir_connection_t *conn = NULL;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/next/consensus"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, header, MAX_HEADERS_SIZE,
+ body, body_used, 18, 0);
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus_not_found(void* data)
+{
+ char *header = NULL, *body = NULL;
+ size_t body_used;
+ (void) data;
+
+ status_vote_next_consensus_test(&header, &body, &body_used);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ tor_free(header);
+ tor_free(body);
+}
+
+static void
+test_dir_handle_get_status_vote_current_authority_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/authority"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+static void
+test_dir_handle_get_status_vote_next_authority_not_found(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL;
+ (void) data;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/next/authority"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ NULL, NULL, 1, 0);
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+}
+
+NS_DECL(const char*,
+dirvote_get_pending_consensus, (consensus_flavor_t flav));
+
+const char*
+NS(dirvote_get_pending_consensus)(consensus_flavor_t flav)
+{
+ (void)flav;
+ return "pending consensus";
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus(void* data)
+{
+ char *header = NULL, *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ NS_MOCK(dirvote_get_pending_consensus);
+
+ status_vote_next_consensus_test(&header, &body, &body_used);
+ tt_assert(header);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 17\r\n"));
+
+ tt_str_op("pending consensus", OP_EQ, body);
+
+ done:
+ NS_UNMOCK(dirvote_get_pending_consensus);
+ tor_free(header);
+ tor_free(body);
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus_busy(void* data)
+{
+ char *header = NULL, *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ MOCK(get_options, mock_get_options);
+ NS_MOCK(dirvote_get_pending_consensus);
+
+ //Make it busy
+ init_mock_options();
+ mock_options->CountPrivateBandwidth = 1;
+
+ status_vote_next_consensus_test(&header, &body, &body_used);
+
+ tt_assert(header);
+ tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+ done:
+ NS_UNMOCK(dirvote_get_pending_consensus);
+ UNMOCK(get_options);
+ tor_free(header);
+ tor_free(body);
+ or_options_free(mock_options); mock_options = NULL;
+}
+
+static void
+status_vote_next_consensus_signatures_test(char **header, char **body,
+ size_t *body_used)
+{
+ dir_connection_t *conn = NULL;
+
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/next/consensus-signatures"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, header, MAX_HEADERS_SIZE,
+ body, body_used, 22, 0);
+
+ done:
+ connection_free_(TO_CONN(conn));
+ UNMOCK(connection_write_to_buf_impl_);
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus_signatures_not_found(void* data)
+{
+ char *header = NULL, *body = NULL;
+ size_t body_used;
+ (void) data;
+
+ status_vote_next_consensus_signatures_test(&header, &body, &body_used);
+
+ tt_assert(header);
+ tt_str_op(NOT_FOUND, OP_EQ, header);
+
+ done:
+ tor_free(header);
+ tor_free(body);
+}
+
+NS_DECL(const char*,
+dirvote_get_pending_detached_signatures, (void));
+
+const char*
+NS(dirvote_get_pending_detached_signatures)(void)
+{
+ return "pending detached sigs";
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus_signatures(void* data)
+{
+ char *header = NULL, *body = NULL;
+ size_t body_used = 0;
+ (void) data;
+
+ NS_MOCK(dirvote_get_pending_detached_signatures);
+
+ status_vote_next_consensus_signatures_test(&header, &body, &body_used);
+ tt_assert(header);
+
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 21\r\n"));
+
+ tt_str_op("pending detached sigs", OP_EQ, body);
+
+ done:
+ NS_UNMOCK(dirvote_get_pending_detached_signatures);
+ tor_free(header);
+ tor_free(body);
+}
+
+static void
+test_dir_handle_get_status_vote_next_consensus_signatures_busy(void* data)
+{
+ char *header = NULL, *body = NULL;
+ size_t body_used;
+ (void) data;
+
+ NS_MOCK(dirvote_get_pending_detached_signatures);
+ MOCK(get_options, mock_get_options);
+
+ //Make it busy
+ init_mock_options();
+ mock_options->CountPrivateBandwidth = 1;
+
+ status_vote_next_consensus_signatures_test(&header, &body, &body_used);
+
+ tt_assert(header);
+ tt_str_op(SERVER_BUSY, OP_EQ, header);
+
+ done:
+ UNMOCK(get_options);
+ NS_UNMOCK(dirvote_get_pending_detached_signatures);
+ tor_free(header);
+ tor_free(body);
+ or_options_free(mock_options); mock_options = NULL;
+}
+
+static void
+test_dir_handle_get_status_vote_next_authority(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL, *body = NULL;
+ const char *msg_out = NULL;
+ int status_out = 0;
+ size_t body_used = 0;
+ dir_server_t *ds = NULL;
+ const char digest[DIGEST_LEN] = "";
+ (void) data;
+
+ clear_dir_servers();
+ routerlist_free_all();
+ dirvote_free_all();
+
+ mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest,
+ NULL, V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+ init_mock_options();
+ mock_options->AuthoritativeDir = 1;
+ mock_options->V3AuthoritativeDir = 1;
+ mock_options->TestingV3AuthVotingStartOffset = 0;
+ mock_options->TestingV3AuthInitialVotingInterval = 1;
+ mock_options->TestingV3AuthInitialVoteDelay = 1;
+ mock_options->TestingV3AuthInitialDistDelay = 1;
+
+ time_t now = 1441223455 -1;
+ dirvote_recalculate_timing(mock_options, now);
+
+ struct pending_vote_t *vote = dirvote_add_vote(VOTE_BODY_V3, &msg_out,
+ &status_out);
+ tt_assert(vote);
+
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/next/authority"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(VOTE_BODY_V3)+1, 0);
+
+ tt_assert(header);
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 4135\r\n"));
+
+ tt_str_op(VOTE_BODY_V3, OP_EQ, body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ UNMOCK(get_my_v3_authority_cert);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ authority_cert_free(mock_cert); mock_cert = NULL;
+ or_options_free(mock_options); mock_options = NULL;
+
+ clear_dir_servers();
+ routerlist_free_all();
+ dirvote_free_all();
+}
+
+static void
+test_dir_handle_get_status_vote_current_authority(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL, *body = NULL;
+ const char *msg_out = NULL;
+ int status_out = 0;
+ size_t body_used = 0;
+ const char digest[DIGEST_LEN] = "";
+
+ dir_server_t *ds = NULL;
+ (void) data;
+
+ clear_dir_servers();
+ routerlist_free_all();
+ dirvote_free_all();
+
+ mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE, NULL);
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest,
+ NULL, V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+
+ init_mock_options();
+ mock_options->AuthoritativeDir = 1;
+ mock_options->V3AuthoritativeDir = 1;
+ mock_options->TestingV3AuthVotingStartOffset = 0;
+ mock_options->TestingV3AuthInitialVotingInterval = 1;
+ mock_options->TestingV3AuthInitialVoteDelay = 1;
+ mock_options->TestingV3AuthInitialDistDelay = 1;
+
+ time_t now = 1441223455;
+ dirvote_recalculate_timing(mock_options, now-1);
+
+ struct pending_vote_t *vote = dirvote_add_vote(VOTE_BODY_V3, &msg_out,
+ &status_out);
+ tt_assert(vote);
+
+ // move the pending vote to previous vote
+ dirvote_act(mock_options, now+1);
+
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/authority"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(VOTE_BODY_V3)+1, 0);
+
+ tt_assert(header);
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 4135\r\n"));
+
+ tt_str_op(VOTE_BODY_V3, OP_EQ, body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ UNMOCK(get_my_v3_authority_cert);
+ connection_free_(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ authority_cert_free(mock_cert); mock_cert = NULL;
+ or_options_free(mock_options); mock_options = NULL;
+
+ clear_dir_servers();
+ routerlist_free_all();
+ dirvote_free_all();
+}
+
+#define DIR_HANDLE_CMD(name,flags) \
+ { #name, test_dir_handle_get_##name, (flags), NULL, NULL }
+
+struct testcase_t dir_handle_get_tests[] = {
+ DIR_HANDLE_CMD(not_found, 0),
+ DIR_HANDLE_CMD(bad_request, 0),
+ DIR_HANDLE_CMD(v1_command_not_found, 0),
+ DIR_HANDLE_CMD(v1_command, 0),
+ DIR_HANDLE_CMD(robots_txt, 0),
+ DIR_HANDLE_CMD(bytes_txt, 0),
+ DIR_HANDLE_CMD(rendezvous2_not_found_if_not_encrypted, 0),
+ DIR_HANDLE_CMD(rendezvous2_not_found, 0),
+ DIR_HANDLE_CMD(rendezvous2_on_encrypted_conn_with_invalid_desc_id, 0),
+ DIR_HANDLE_CMD(rendezvous2_on_encrypted_conn_not_well_formed, 0),
+ DIR_HANDLE_CMD(rendezvous2_on_encrypted_conn_success, 0),
+ DIR_HANDLE_CMD(micro_d_not_found, 0),
+ DIR_HANDLE_CMD(micro_d_server_busy, 0),
+ DIR_HANDLE_CMD(micro_d, 0),
+ DIR_HANDLE_CMD(networkstatus_bridges_not_found_without_auth, 0),
+ DIR_HANDLE_CMD(networkstatus_bridges_not_found_wrong_auth, 0),
+ DIR_HANDLE_CMD(networkstatus_bridges, 0),
+ DIR_HANDLE_CMD(server_descriptors_not_found, 0),
+ DIR_HANDLE_CMD(server_descriptors_busy, TT_FORK),
+ DIR_HANDLE_CMD(server_descriptors_all, TT_FORK),
+ DIR_HANDLE_CMD(server_descriptors_authority, TT_FORK),
+ DIR_HANDLE_CMD(server_descriptors_fp, TT_FORK),
+ DIR_HANDLE_CMD(server_descriptors_d, TT_FORK),
+ DIR_HANDLE_CMD(server_keys_bad_req, 0),
+ DIR_HANDLE_CMD(server_keys_busy, 0),
+ DIR_HANDLE_CMD(server_keys_all_not_found, 0),
+ DIR_HANDLE_CMD(server_keys_all, 0),
+ DIR_HANDLE_CMD(server_keys_authority_not_found, 0),
+ DIR_HANDLE_CMD(server_keys_authority, 0),
+ DIR_HANDLE_CMD(server_keys_fp_not_found, 0),
+ DIR_HANDLE_CMD(server_keys_fp, 0),
+ DIR_HANDLE_CMD(server_keys_sk_not_found, 0),
+ DIR_HANDLE_CMD(server_keys_sk, 0),
+ DIR_HANDLE_CMD(server_keys_fpsk_not_found, 0),
+ DIR_HANDLE_CMD(server_keys_fpsk, 0),
+ DIR_HANDLE_CMD(status_vote_current_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_next_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_current_authority_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_current_authority, 0),
+ DIR_HANDLE_CMD(status_vote_next_authority_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_next_authority, 0),
+ DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_enough_sigs, 0),
+ DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_current_consensus_ns_busy, 0),
+ DIR_HANDLE_CMD(status_vote_current_consensus_ns, 0),
+ DIR_HANDLE_CMD(status_vote_current_d_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_next_d_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_d, 0),
+ DIR_HANDLE_CMD(status_vote_next_consensus_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_next_consensus_busy, 0),
+ DIR_HANDLE_CMD(status_vote_next_consensus, 0),
+ DIR_HANDLE_CMD(status_vote_next_consensus_signatures_not_found, 0),
+ DIR_HANDLE_CMD(status_vote_next_consensus_signatures_busy, 0),
+ DIR_HANDLE_CMD(status_vote_next_consensus_signatures, 0),
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_dns.c b/src/test/test_dns.c
index ad81914ccb..5289ca58ff 100644
--- a/src/test/test_dns.c
+++ b/src/test/test_dns.c
@@ -5,9 +5,14 @@
#include "dns.h"
#include "connection.h"
+#include "router.h"
+
+#define NS_MODULE dns
+
+#define NS_SUBMODULE clip_ttl
static void
-test_dns_clip_ttl(void *arg)
+NS(test_main)(void *arg)
{
(void)arg;
@@ -21,8 +26,12 @@ test_dns_clip_ttl(void *arg)
return;
}
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE expiry_ttl
+
static void
-test_dns_expiry_ttl(void *arg)
+NS(test_main)(void *arg)
{
(void)arg;
@@ -36,6 +45,10 @@ test_dns_expiry_ttl(void *arg)
return;
}
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE resolve
+
static int resolve_retval = 0;
static int resolve_made_conn_pending = 0;
static char *resolved_name = NULL;
@@ -43,6 +56,11 @@ static cached_resolve_t *cache_entry = NULL;
static int n_fake_impl = 0;
+NS_DECL(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));
+
/** This will be our configurable substitute for <b>dns_resolve_impl</b> in
* dns.c. It will return <b>resolve_retval</b>,
* and set <b>resolve_made_conn_pending</b> to
@@ -52,10 +70,10 @@ static int n_fake_impl = 0;
* 1.
*/
static int
-dns_resolve_fake_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)
+NS(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)
{
(void)oncirc;
(void)exitconn;
@@ -82,8 +100,8 @@ static uint8_t last_answer_type = 0;
static cached_resolve_t *last_resolved;
static void
-send_resolved_cell_replacement(edge_connection_t *conn, uint8_t answer_type,
- const cached_resolve_t *resolved)
+NS(send_resolved_cell)(edge_connection_t *conn, uint8_t answer_type,
+ const cached_resolve_t *resolved)
{
conn_for_resolved_cell = conn;
@@ -98,8 +116,8 @@ static int n_send_resolved_hostname_cell_replacement = 0;
static char *last_resolved_hostname = NULL;
static void
-send_resolved_hostname_cell_replacement(edge_connection_t *conn,
- const char *hostname)
+NS(send_resolved_hostname_cell)(edge_connection_t *conn,
+ const char *hostname)
{
conn_for_resolved_cell = conn;
@@ -112,7 +130,7 @@ send_resolved_hostname_cell_replacement(edge_connection_t *conn,
static int n_dns_cancel_pending_resolve_replacement = 0;
static void
-dns_cancel_pending_resolve_replacement(const char *address)
+NS(dns_cancel_pending_resolve)(const char *address)
{
(void) address;
n_dns_cancel_pending_resolve_replacement++;
@@ -122,7 +140,7 @@ static int n_connection_free = 0;
static connection_t *last_freed_conn = NULL;
static void
-connection_free_replacement(connection_t *conn)
+NS(connection_free)(connection_t *conn)
{
n_connection_free++;
@@ -130,7 +148,7 @@ connection_free_replacement(connection_t *conn)
}
static void
-test_dns_resolve_outer(void *arg)
+NS(test_main)(void *arg)
{
(void) arg;
int retval;
@@ -149,9 +167,9 @@ test_dns_resolve_outer(void *arg)
memset(exitconn,0,sizeof(edge_connection_t));
memset(nextconn,0,sizeof(edge_connection_t));
- MOCK(dns_resolve_impl,dns_resolve_fake_impl);
- MOCK(send_resolved_cell,send_resolved_cell_replacement);
- MOCK(send_resolved_hostname_cell,send_resolved_hostname_cell_replacement);
+ NS_MOCK(dns_resolve_impl);
+ NS_MOCK(send_resolved_cell);
+ NS_MOCK(send_resolved_hostname_cell);
/*
* CASE 1: dns_resolve_impl returns 1 and sets a hostname. purpose is
@@ -264,8 +282,8 @@ test_dns_resolve_outer(void *arg)
* on exitconn with type being RESOLVED_TYPE_ERROR.
*/
- MOCK(dns_cancel_pending_resolve,dns_cancel_pending_resolve_replacement);
- MOCK(connection_free,connection_free_replacement);
+ NS_MOCK(dns_cancel_pending_resolve);
+ NS_MOCK(connection_free);
exitconn->on_circuit = &(on_circuit->base_);
exitconn->base_.purpose = EXIT_PURPOSE_RESOLVE;
@@ -288,11 +306,11 @@ test_dns_resolve_outer(void *arg)
tt_assert(last_freed_conn == TO_CONN(exitconn));
done:
- UNMOCK(dns_resolve_impl);
- UNMOCK(send_resolved_cell);
- UNMOCK(send_resolved_hostname_cell);
- UNMOCK(dns_cancel_pending_resolve);
- UNMOCK(connection_free);
+ NS_UNMOCK(dns_resolve_impl);
+ NS_UNMOCK(send_resolved_cell);
+ NS_UNMOCK(send_resolved_hostname_cell);
+ NS_UNMOCK(dns_cancel_pending_resolve);
+ NS_UNMOCK(connection_free);
tor_free(on_circuit);
tor_free(exitconn);
tor_free(nextconn);
@@ -302,10 +320,446 @@ test_dns_resolve_outer(void *arg)
return;
}
+#undef NS_SUBMODULE
+
+/** Create an <b>edge_connection_t</b> instance that is considered a
+ * valid exit connection by asserts in dns_resolve_impl.
+ */
+static edge_connection_t *
+create_valid_exitconn(void)
+{
+ edge_connection_t *exitconn = tor_malloc_zero(sizeof(edge_connection_t));
+ TO_CONN(exitconn)->type = CONN_TYPE_EXIT;
+ TO_CONN(exitconn)->magic = EDGE_CONNECTION_MAGIC;
+ TO_CONN(exitconn)->purpose = EXIT_PURPOSE_RESOLVE;
+ TO_CONN(exitconn)->state = EXIT_CONN_STATE_RESOLVING;
+ exitconn->base_.s = TOR_INVALID_SOCKET;
+
+ return exitconn;
+}
+
+#define NS_SUBMODULE ASPECT(resolve_impl, addr_is_ip_no_need_to_resolve)
+
+/*
+ * Given that <b>exitconn->base_.address</b> is IP address string, we
+ * want dns_resolve_impl() to parse it and store in
+ * <b>exitconn->base_.addr</b>. We expect dns_resolve_impl to return 1.
+ * Lastly, we want it to set the TTL value to default one for DNS queries.
+ */
+
+static void
+NS(test_main)(void *arg)
+{
+ int retval;
+ int made_pending;
+ const tor_addr_t *resolved_addr;
+ tor_addr_t addr_to_compare;
+
+ (void)arg;
+
+ tor_addr_parse(&addr_to_compare, "8.8.8.8");
+
+ or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
+
+ edge_connection_t *exitconn = create_valid_exitconn();
+
+ TO_CONN(exitconn)->address = tor_strdup("8.8.8.8");
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ NULL);
+
+ resolved_addr = &(exitconn->base_.addr);
+
+ tt_int_op(retval,==,1);
+ tt_assert(tor_addr_eq(resolved_addr, (const tor_addr_t *)&addr_to_compare));
+ tt_int_op(exitconn->address_ttl,==,DEFAULT_DNS_TTL);
+
+ done:
+ tor_free(on_circ);
+ tor_free(TO_CONN(exitconn)->address);
+ tor_free(exitconn);
+ return;
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE ASPECT(resolve_impl, non_exit)
+
+/** Given that Tor instance is not configured as an exit node, we want
+ * dns_resolve_impl() to fail with return value -1.
+ */
+static int
+NS(router_my_exit_policy_is_reject_star)(void)
+{
+ return 1;
+}
+
+static void
+NS(test_main)(void *arg)
+{
+ int retval;
+ int made_pending;
+
+ edge_connection_t *exitconn = create_valid_exitconn();
+ or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
+
+ (void)arg;
+
+ TO_CONN(exitconn)->address = tor_strdup("torproject.org");
+
+ NS_MOCK(router_my_exit_policy_is_reject_star);
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ NULL);
+
+ tt_int_op(retval,==,-1);
+
+ done:
+ tor_free(TO_CONN(exitconn)->address);
+ tor_free(exitconn);
+ tor_free(on_circ);
+ NS_UNMOCK(router_my_exit_policy_is_reject_star);
+ return;
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE ASPECT(resolve_impl, addr_is_invalid_dest)
+
+/** Given that address is not a valid destination (as judged by
+ * address_is_invalid_destination() function), we want dns_resolve_impl()
+ * function to fail with return value -1.
+ */
+
+static int
+NS(router_my_exit_policy_is_reject_star)(void)
+{
+ return 0;
+}
+
+static void
+NS(test_main)(void *arg)
+{
+ int retval;
+ int made_pending;
+
+ edge_connection_t *exitconn = create_valid_exitconn();
+ or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
+
+ (void)arg;
+
+ NS_MOCK(router_my_exit_policy_is_reject_star);
+
+ TO_CONN(exitconn)->address = tor_strdup("invalid#@!.org");
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ NULL);
+
+ tt_int_op(retval,==,-1);
+
+ done:
+ NS_UNMOCK(router_my_exit_policy_is_reject_star);
+ tor_free(TO_CONN(exitconn)->address);
+ tor_free(exitconn);
+ tor_free(on_circ);
+ return;
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE ASPECT(resolve_impl, malformed_ptr)
+
+/** Given that address is a malformed PTR name, we want dns_resolve_impl to
+ * fail.
+ */
+
+static int
+NS(router_my_exit_policy_is_reject_star)(void)
+{
+ return 0;
+}
+
+static void
+NS(test_main)(void *arg)
+{
+ int retval;
+ int made_pending;
+
+ edge_connection_t *exitconn = create_valid_exitconn();
+ or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
+
+ (void)arg;
+
+ TO_CONN(exitconn)->address = tor_strdup("1.0.0.127.in-addr.arpa");
+
+ NS_MOCK(router_my_exit_policy_is_reject_star);
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ NULL);
+
+ tt_int_op(retval,==,-1);
+
+ tor_free(TO_CONN(exitconn)->address);
+
+ TO_CONN(exitconn)->address =
+ tor_strdup("z01234567890123456789.in-addr.arpa");
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ NULL);
+
+ tt_int_op(retval,==,-1);
+
+ done:
+ NS_UNMOCK(router_my_exit_policy_is_reject_star);
+ tor_free(TO_CONN(exitconn)->address);
+ tor_free(exitconn);
+ tor_free(on_circ);
+ return;
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE ASPECT(resolve_impl, cache_hit_pending)
+
+/* Given that there is already a pending resolve for the given address,
+ * we want dns_resolve_impl to append our exit connection to list
+ * of pending connections for the pending DNS request and return 0.
+ */
+
+static int
+NS(router_my_exit_policy_is_reject_star)(void)
+{
+ return 0;
+}
+
+static void
+NS(test_main)(void *arg)
+{
+ int retval;
+ int made_pending = 0;
+
+ pending_connection_t *pending_conn = NULL;
+
+ edge_connection_t *exitconn = create_valid_exitconn();
+ or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
+
+ cached_resolve_t *cache_entry = tor_malloc_zero(sizeof(cached_resolve_t));
+ cache_entry->magic = CACHED_RESOLVE_MAGIC;
+ cache_entry->state = CACHE_STATE_PENDING;
+ cache_entry->minheap_idx = -1;
+ cache_entry->expire = time(NULL) + 60 * 60;
+
+ (void)arg;
+
+ TO_CONN(exitconn)->address = tor_strdup("torproject.org");
+
+ strlcpy(cache_entry->address, TO_CONN(exitconn)->address,
+ sizeof(cache_entry->address));
+
+ NS_MOCK(router_my_exit_policy_is_reject_star);
+
+ dns_init();
+
+ dns_insert_cache_entry(cache_entry);
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ NULL);
+
+ tt_int_op(retval,==,0);
+ tt_int_op(made_pending,==,1);
+
+ pending_conn = cache_entry->pending_connections;
+
+ tt_assert(pending_conn != NULL);
+ tt_assert(pending_conn->conn == exitconn);
+
+ done:
+ NS_UNMOCK(router_my_exit_policy_is_reject_star);
+ tor_free(on_circ);
+ tor_free(TO_CONN(exitconn)->address);
+ tor_free(cache_entry->pending_connections);
+ tor_free(cache_entry);
+ tor_free(exitconn);
+ return;
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE ASPECT(resolve_impl, cache_hit_cached)
+
+/* Given that a finished DNS resolve is available in our cache, we want
+ * dns_resolve_impl() return it to called via resolve_out and pass the
+ * handling to set_exitconn_info_from_resolve function.
+ */
+static int
+NS(router_my_exit_policy_is_reject_star)(void)
+{
+ return 0;
+}
+
+static edge_connection_t *last_exitconn = NULL;
+static cached_resolve_t *last_resolve = NULL;
+
+static int
+NS(set_exitconn_info_from_resolve)(edge_connection_t *exitconn,
+ const cached_resolve_t *resolve,
+ char **hostname_out)
+{
+ last_exitconn = exitconn;
+ last_resolve = (cached_resolve_t *)resolve;
+
+ (void)hostname_out;
+
+ return 0;
+}
+
+static void
+NS(test_main)(void *arg)
+{
+ int retval;
+ int made_pending = 0;
+
+ edge_connection_t *exitconn = create_valid_exitconn();
+ or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
+
+ cached_resolve_t *resolve_out = NULL;
+
+ cached_resolve_t *cache_entry = tor_malloc_zero(sizeof(cached_resolve_t));
+ cache_entry->magic = CACHED_RESOLVE_MAGIC;
+ cache_entry->state = CACHE_STATE_CACHED;
+ cache_entry->minheap_idx = -1;
+ cache_entry->expire = time(NULL) + 60 * 60;
+
+ (void)arg;
+
+ TO_CONN(exitconn)->address = tor_strdup("torproject.org");
+
+ strlcpy(cache_entry->address, TO_CONN(exitconn)->address,
+ sizeof(cache_entry->address));
+
+ NS_MOCK(router_my_exit_policy_is_reject_star);
+ NS_MOCK(set_exitconn_info_from_resolve);
+
+ dns_init();
+
+ dns_insert_cache_entry(cache_entry);
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ &resolve_out);
+
+ tt_int_op(retval,==,0);
+ tt_int_op(made_pending,==,0);
+ tt_assert(resolve_out == cache_entry);
+
+ tt_assert(last_exitconn == exitconn);
+ tt_assert(last_resolve == cache_entry);
+
+ done:
+ NS_UNMOCK(router_my_exit_policy_is_reject_star);
+ NS_UNMOCK(set_exitconn_info_from_resolve);
+ tor_free(on_circ);
+ tor_free(TO_CONN(exitconn)->address);
+ tor_free(cache_entry->pending_connections);
+ tor_free(cache_entry);
+ return;
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE ASPECT(resolve_impl, cache_miss)
+
+/* Given that there are neither pending nor pre-cached resolve for a given
+ * address, we want dns_resolve_impl() to create a new cached_resolve_t
+ * object, mark it as pending, insert it into the cache, attach the exit
+ * connection to list of pending connections and call launch_resolve()
+ * with the cached_resolve_t object it created.
+ */
+static int
+NS(router_my_exit_policy_is_reject_star)(void)
+{
+ return 0;
+}
+
+static cached_resolve_t *last_launched_resolve = NULL;
+
+static int
+NS(launch_resolve)(cached_resolve_t *resolve)
+{
+ last_launched_resolve = resolve;
+
+ return 0;
+}
+
+static void
+NS(test_main)(void *arg)
+{
+ int retval;
+ int made_pending = 0;
+
+ pending_connection_t *pending_conn = NULL;
+
+ edge_connection_t *exitconn = create_valid_exitconn();
+ or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
+
+ cached_resolve_t *cache_entry = NULL;
+ cached_resolve_t query;
+
+ (void)arg;
+
+ TO_CONN(exitconn)->address = tor_strdup("torproject.org");
+
+ strlcpy(query.address, TO_CONN(exitconn)->address, sizeof(query.address));
+
+ NS_MOCK(router_my_exit_policy_is_reject_star);
+ NS_MOCK(launch_resolve);
+
+ dns_init();
+
+ retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
+ NULL);
+
+ tt_int_op(retval,==,0);
+ tt_int_op(made_pending,==,1);
+
+ cache_entry = dns_get_cache_entry(&query);
+
+ tt_assert(cache_entry);
+
+ pending_conn = cache_entry->pending_connections;
+
+ tt_assert(pending_conn != NULL);
+ tt_assert(pending_conn->conn == exitconn);
+
+ tt_assert(last_launched_resolve == cache_entry);
+ tt_str_op(cache_entry->address,==,TO_CONN(exitconn)->address);
+
+ done:
+ NS_UNMOCK(router_my_exit_policy_is_reject_star);
+ NS_UNMOCK(launch_resolve);
+ tor_free(on_circ);
+ tor_free(TO_CONN(exitconn)->address);
+ if (cache_entry)
+ tor_free(cache_entry->pending_connections);
+ tor_free(cache_entry);
+ tor_free(exitconn);
+ return;
+}
+
+#undef NS_SUBMODULE
+
struct testcase_t dns_tests[] = {
- { "clip_ttl", test_dns_clip_ttl, 0, NULL, NULL },
- { "expiry_ttl", test_dns_expiry_ttl, 0, NULL, NULL },
- { "resolve_outer", test_dns_resolve_outer, TT_FORK, NULL, NULL },
+ TEST_CASE(clip_ttl),
+ TEST_CASE(expiry_ttl),
+ TEST_CASE(resolve),
+ TEST_CASE_ASPECT(resolve_impl, addr_is_ip_no_need_to_resolve),
+ TEST_CASE_ASPECT(resolve_impl, non_exit),
+ TEST_CASE_ASPECT(resolve_impl, addr_is_invalid_dest),
+ TEST_CASE_ASPECT(resolve_impl, malformed_ptr),
+ TEST_CASE_ASPECT(resolve_impl, cache_hit_pending),
+ TEST_CASE_ASPECT(resolve_impl, cache_hit_cached),
+ TEST_CASE_ASPECT(resolve_impl, cache_miss),
END_OF_TESTCASES
};
+#undef NS_MODULE
+
diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c
index 0011d3698a..fd19db095d 100644
--- a/src/test/test_entrynodes.c
+++ b/src/test/test_entrynodes.c
@@ -9,14 +9,16 @@
#include "or.h"
#include "test.h"
+
+#include "config.h"
#include "entrynodes.h"
-#include "routerparse.h"
#include "nodelist.h"
-#include "util.h"
+#include "policies.h"
#include "routerlist.h"
+#include "routerparse.h"
#include "routerset.h"
#include "statefile.h"
-#include "config.h"
+#include "util.h"
#include "test_helpers.h"
@@ -70,6 +72,14 @@ fake_network_setup(const struct testcase_t *testcase)
return dummy_state;
}
+static or_options_t mocked_options;
+
+static const or_options_t *
+mock_get_options(void)
+{
+ return &mocked_options;
+}
+
/** Test choose_random_entry() with none of our routers being guard nodes. */
static void
test_choose_random_entry_no_guards(void *arg)
@@ -78,6 +88,14 @@ test_choose_random_entry_no_guards(void *arg)
(void) arg;
+ MOCK(get_options, mock_get_options);
+
+ /* Check that we get a guard if it passes preferred
+ * address settings */
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ mocked_options.ClientUseIPv4 = 1;
+ mocked_options.ClientPreferIPv6ORPort = 0;
+
/* Try to pick an entry even though none of our routers are guards. */
chosen_entry = choose_random_entry(NULL);
@@ -86,8 +104,55 @@ test_choose_random_entry_no_guards(void *arg)
can't find a proper entry guard. */
tt_assert(chosen_entry);
+ /* And with the other IP version active */
+ mocked_options.ClientUseIPv6 = 1;
+ chosen_entry = choose_random_entry(NULL);
+ tt_assert(chosen_entry);
+
+ /* And with the preference on auto */
+ mocked_options.ClientPreferIPv6ORPort = -1;
+ chosen_entry = choose_random_entry(NULL);
+ tt_assert(chosen_entry);
+
+ /* Check that we don't get a guard if it doesn't pass mandatory address
+ * settings */
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ mocked_options.ClientUseIPv4 = 0;
+ mocked_options.ClientPreferIPv6ORPort = 0;
+
+ chosen_entry = choose_random_entry(NULL);
+
+ /* If we don't allow IPv4 at all, we don't get a guard*/
+ tt_assert(!chosen_entry);
+
+ /* Check that we get a guard if it passes allowed but not preferred address
+ * settings */
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ mocked_options.ClientUseIPv4 = 1;
+ mocked_options.ClientUseIPv6 = 1;
+ mocked_options.ClientPreferIPv6ORPort = 1;
+
+ chosen_entry = choose_random_entry(NULL);
+ tt_assert(chosen_entry);
+
+ /* Check that we get a guard if it passes preferred address settings when
+ * they're auto */
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ mocked_options.ClientUseIPv4 = 1;
+ mocked_options.ClientPreferIPv6ORPort = -1;
+
+ chosen_entry = choose_random_entry(NULL);
+ tt_assert(chosen_entry);
+
+ /* And with IPv6 active */
+ mocked_options.ClientUseIPv6 = 1;
+
+ chosen_entry = choose_random_entry(NULL);
+ tt_assert(chosen_entry);
+
done:
- ;
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ UNMOCK(get_options);
}
/** Test choose_random_entry() with only one of our routers being a
@@ -101,17 +166,78 @@ test_choose_random_entry_one_possible_guard(void *arg)
(void) arg;
+ MOCK(get_options, mock_get_options);
+
/* Set one of the nodes to be a guard. */
our_nodelist = nodelist_get_list();
the_guard = smartlist_get(our_nodelist, 4); /* chosen by fair dice roll */
the_guard->is_possible_guard = 1;
+ /* Check that we get the guard if it passes preferred
+ * address settings */
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ mocked_options.ClientUseIPv4 = 1;
+ mocked_options.ClientPreferIPv6ORPort = 0;
+
/* Pick an entry. Make sure we pick the node we marked as guard. */
chosen_entry = choose_random_entry(NULL);
tt_ptr_op(chosen_entry, OP_EQ, the_guard);
+ /* And with the other IP version active */
+ mocked_options.ClientUseIPv6 = 1;
+ chosen_entry = choose_random_entry(NULL);
+ tt_ptr_op(chosen_entry, OP_EQ, the_guard);
+
+ /* And with the preference on auto */
+ mocked_options.ClientPreferIPv6ORPort = -1;
+ chosen_entry = choose_random_entry(NULL);
+ tt_ptr_op(chosen_entry, OP_EQ, the_guard);
+
+ /* Check that we don't get a guard if it doesn't pass mandatory address
+ * settings */
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ mocked_options.ClientUseIPv4 = 0;
+ mocked_options.ClientPreferIPv6ORPort = 0;
+
+ chosen_entry = choose_random_entry(NULL);
+
+ /* If we don't allow IPv4 at all, we don't get a guard*/
+ tt_assert(!chosen_entry);
+
+ /* Check that we get a node if it passes allowed but not preferred
+ * address settings */
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ mocked_options.ClientUseIPv4 = 1;
+ mocked_options.ClientUseIPv6 = 1;
+ mocked_options.ClientPreferIPv6ORPort = 1;
+
+ chosen_entry = choose_random_entry(NULL);
+
+ /* We disable the guard check and the preferred address check at the same
+ * time, so we can't be sure we get the guard */
+ tt_assert(chosen_entry);
+
+ /* Check that we get a node if it is allowed but not preferred when settings
+ * are auto */
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ mocked_options.ClientUseIPv4 = 1;
+ mocked_options.ClientPreferIPv6ORPort = -1;
+
+ chosen_entry = choose_random_entry(NULL);
+
+ /* We disable the guard check and the preferred address check at the same
+ * time, so we can't be sure we get the guard */
+ tt_assert(chosen_entry);
+
+ /* and with IPv6 active */
+ mocked_options.ClientUseIPv6 = 1;
+
+ chosen_entry = choose_random_entry(NULL);
+ tt_assert(chosen_entry);
+
done:
- ;
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ UNMOCK(get_options);
}
/** Helper to conduct tests for populate_live_entry_guards().
@@ -624,6 +750,93 @@ test_entry_is_live(void *arg)
; /* XXX */
}
+#define TEST_IPV4_ADDR "123.45.67.89"
+#define TEST_IPV6_ADDR "[1234:5678:90ab:cdef::]"
+
+static void
+test_node_preferred_orport(void *arg)
+{
+ (void)arg;
+ tor_addr_t ipv4_addr;
+ const uint16_t ipv4_port = 4444;
+ tor_addr_t ipv6_addr;
+ const uint16_t ipv6_port = 6666;
+ routerinfo_t node_ri;
+ node_t node;
+ tor_addr_port_t ap;
+
+ /* Setup options */
+ memset(&mocked_options, 0, sizeof(mocked_options));
+ /* We don't test ClientPreferIPv6ORPort here, because it's used in
+ * nodelist_set_consensus to setup node.ipv6_preferred, which we set
+ * directly. */
+ MOCK(get_options, mock_get_options);
+
+ /* Setup IP addresses */
+ tor_addr_parse(&ipv4_addr, TEST_IPV4_ADDR);
+ tor_addr_parse(&ipv6_addr, TEST_IPV6_ADDR);
+
+ /* Setup node_ri */
+ memset(&node_ri, 0, sizeof(node_ri));
+ node_ri.addr = tor_addr_to_ipv4h(&ipv4_addr);
+ node_ri.or_port = ipv4_port;
+ tor_addr_copy(&node_ri.ipv6_addr, &ipv6_addr);
+ node_ri.ipv6_orport = ipv6_port;
+
+ /* Setup node */
+ memset(&node, 0, sizeof(node));
+ node.ri = &node_ri;
+
+ /* Check the preferred address is IPv4 if we're only using IPv4, regardless
+ * of whether we prefer it or not */
+ mocked_options.ClientUseIPv4 = 1;
+ mocked_options.ClientUseIPv6 = 0;
+ node.ipv6_preferred = 0;
+ node_get_pref_orport(&node, &ap);
+ tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr));
+ tt_assert(ap.port == ipv4_port);
+
+ node.ipv6_preferred = 1;
+ node_get_pref_orport(&node, &ap);
+ tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr));
+ tt_assert(ap.port == ipv4_port);
+
+ /* Check the preferred address is IPv4 if we're using IPv4 and IPv6, but
+ * don't prefer the IPv6 address */
+ mocked_options.ClientUseIPv4 = 1;
+ mocked_options.ClientUseIPv6 = 1;
+ node.ipv6_preferred = 0;
+ node_get_pref_orport(&node, &ap);
+ tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr));
+ tt_assert(ap.port == ipv4_port);
+
+ /* Check the preferred address is IPv6 if we prefer it and
+ * ClientUseIPv6 is 1, regardless of ClientUseIPv4 */
+ mocked_options.ClientUseIPv4 = 1;
+ mocked_options.ClientUseIPv6 = 1;
+ node.ipv6_preferred = 1;
+ node_get_pref_orport(&node, &ap);
+ tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr));
+ tt_assert(ap.port == ipv6_port);
+
+ mocked_options.ClientUseIPv4 = 0;
+ node_get_pref_orport(&node, &ap);
+ tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr));
+ tt_assert(ap.port == ipv6_port);
+
+ /* Check the preferred address is IPv6 if we don't prefer it, but
+ * ClientUseIPv4 is 0 */
+ mocked_options.ClientUseIPv4 = 0;
+ mocked_options.ClientUseIPv6 = 1;
+ node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(&mocked_options);
+ node_get_pref_orport(&node, &ap);
+ tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr));
+ tt_assert(ap.port == ipv6_port);
+
+ done:
+ UNMOCK(get_options);
+}
+
static const struct testcase_setup_t fake_network = {
fake_network_setup, fake_network_cleanup
};
@@ -654,6 +867,9 @@ struct testcase_t entrynodes_tests[] = {
{ "entry_is_live",
test_entry_is_live,
TT_FORK, &fake_network, NULL },
+ { "node_preferred_orport",
+ test_node_preferred_orport,
+ 0, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c
index 2e5a32eef3..5d38ed8fa2 100644
--- a/src/test/test_extorport.c
+++ b/src/test/test_extorport.c
@@ -309,15 +309,14 @@ test_ext_or_cookie_auth(void *arg)
tor_free(client_hash2);
}
-static int
+static void
crypto_rand_return_tse_str(char *to, size_t n)
{
if (n != 32) {
TT_FAIL(("Asked for %d bytes, not 32", (int)n));
- return -1;
+ return;
}
memcpy(to, "te road There is always another ", 32);
- return 0;
}
static void
diff --git a/src/test/test_nodelist.c b/src/test/test_nodelist.c
index a8693ec9b5..b0cb87dc60 100644
--- a/src/test/test_nodelist.c
+++ b/src/test/test_nodelist.c
@@ -60,12 +60,53 @@ test_nodelist_node_get_verbose_nickname_not_named(void *arg)
return;
}
+/** A node should be considered a directory server if it has an open dirport
+ * of it accepts tunnelled directory requests.
+ */
+static void
+test_nodelist_node_is_dir(void *arg)
+{
+ (void)arg;
+
+ routerstatus_t rs;
+ routerinfo_t ri;
+ node_t node;
+ memset(&node, 0, sizeof(node_t));
+ memset(&rs, 0, sizeof(routerstatus_t));
+ memset(&ri, 0, sizeof(routerinfo_t));
+
+ tt_assert(!node_is_dir(&node));
+
+ node.rs = &rs;
+ tt_assert(!node_is_dir(&node));
+
+ rs.is_v2_dir = 1;
+ tt_assert(node_is_dir(&node));
+
+ rs.is_v2_dir = 0;
+ rs.dir_port = 1;
+ tt_assert(! node_is_dir(&node));
+
+ node.rs = NULL;
+ tt_assert(!node_is_dir(&node));
+ node.ri = &ri;
+ ri.supports_tunnelled_dir_requests = 1;
+ tt_assert(node_is_dir(&node));
+ ri.supports_tunnelled_dir_requests = 0;
+ ri.dir_port = 1;
+ tt_assert(! node_is_dir(&node));
+
+ done:
+ return;
+}
+
#define NODE(name, flags) \
{ #name, test_nodelist_##name, (flags), NULL, NULL }
struct testcase_t nodelist_tests[] = {
NODE(node_get_verbose_nickname_by_id_null_node, TT_FORK),
NODE(node_get_verbose_nickname_not_named, TT_FORK),
+ NODE(node_is_dir, TT_FORK),
END_OF_TESTCASES
};
diff --git a/src/test/test_ntor_cl.c b/src/test/test_ntor_cl.c
index bfbf13a476..915a5d0a7c 100644
--- a/src/test/test_ntor_cl.c
+++ b/src/test/test_ntor_cl.c
@@ -106,6 +106,7 @@ server1(int argc, char **argv)
done:
tor_free(keys);
tor_free(hexkeys);
+ dimap_free(keymap, NULL);
return result;
}
diff --git a/src/test/test_options.c b/src/test/test_options.c
index a8ebadb14b..b2be8a9c65 100644
--- a/src/test/test_options.c
+++ b/src/test/test_options.c
@@ -8,6 +8,18 @@
#include "confparse.h"
#include "config.h"
#include "test.h"
+#include "geoip.h"
+
+#define ROUTERSET_PRIVATE
+#include "routerset.h"
+
+#include "log_test_helpers.h"
+
+#include "sandbox.h"
+#include "memarea.h"
+#include "policies.h"
+
+#define NS_MODULE test_options
typedef struct {
int severity;
@@ -38,6 +50,7 @@ setup_log_callback(void)
lst.masks[LOG_WARN - LOG_ERR] = ~0;
lst.masks[LOG_NOTICE - LOG_ERR] = ~0;
add_callback_log(&lst, log_cback);
+ mark_logs_temp();
}
static char *
@@ -69,22 +82,41 @@ clear_log_messages(void)
messages = NULL;
}
+#define setup_options(opt,dflt) \
+ do { \
+ opt = options_new(); \
+ opt->command = CMD_RUN_TOR; \
+ options_init(opt); \
+ \
+ dflt = config_dup(&options_format, opt); \
+ clear_log_messages(); \
+ } while (0)
+
+#define VALID_DIR_AUTH "DirAuthority dizum orport=443 v3ident=E8A9C45" \
+ "EDE6D711294FADF8E7951F4DE6CA56B58 194.109.206.212:80 7EA6 EAD6 FD83" \
+ " 083C 538F 4403 8BBF A077 587D D755\n"
+#define VALID_ALT_BRIDGE_AUTH \
+ "AlternateBridgeAuthority dizum orport=443 v3ident=E8A9C45" \
+ "EDE6D711294FADF8E7951F4DE6CA56B58 194.109.206.212:80 7EA6 EAD6 FD83" \
+ " 083C 538F 4403 8BBF A077 587D D755\n"
+#define VALID_ALT_DIR_AUTH \
+ "AlternateDirAuthority dizum orport=443 v3ident=E8A9C45" \
+ "EDE6D711294FADF8E7951F4DE6CA56B58 194.109.206.212:80 7EA6 EAD6 FD83" \
+ " 083C 538F 4403 8BBF A077 587D D755\n"
+
static void
test_options_validate_impl(const char *configuration,
const char *expect_errmsg,
int expect_log_severity,
const char *expect_log)
{
- or_options_t *opt = options_new();
+ or_options_t *opt=NULL;
or_options_t *dflt;
config_line_t *cl=NULL;
char *msg=NULL;
int r;
- opt->command = CMD_RUN_TOR;
- options_init(opt);
- dflt = config_dup(&options_format, opt);
- clear_log_messages();
+ setup_options(opt, dflt);
r = config_get_lines(configuration, &cl, 1);
tt_int_op(r, OP_EQ, 0);
@@ -126,6 +158,9 @@ test_options_validate_impl(const char *configuration,
}
done:
+ memarea_clear_freelist();
+ escaped(NULL);
+ policies_free_all();
config_free_lines(cl);
or_options_free(opt);
or_options_free(dflt);
@@ -147,6 +182,7 @@ test_options_validate(void *arg)
{
(void)arg;
setup_log_callback();
+ sandbox_disable_getaddrinfo_cache();
WANT_ERR("ExtORPort 500000", "Invalid ExtORPort");
@@ -159,12 +195,4224 @@ test_options_validate(void *arg)
"ServerTransportOptions did not parse",
LOG_WARN, "\"slingsnappy\" is not a k=v");
+ WANT_ERR("DirPort 8080\nDirCache 0",
+ "DirPort configured but DirCache disabled.");
+ WANT_ERR("BridgeRelay 1\nDirCache 0",
+ "We're a bridge but DirCache is disabled.");
+
+ close_temp_logs();
+ clear_log_messages();
+ return;
+}
+
+#define MEGABYTEIFY(mb) (U64_LITERAL(mb) << 20)
+static void
+test_have_enough_mem_for_dircache(void *arg)
+{
+ (void)arg;
+ or_options_t *opt=NULL;
+ or_options_t *dflt;
+ config_line_t *cl=NULL;
+ char *msg=NULL;;
+ int r;
+ const char *configuration = "ORPort 8080\nDirCache 1", *expect_errmsg;
+
+ setup_options(opt, dflt);
+ setup_log_callback();
+ (void)dflt;
+
+ r = config_get_lines(configuration, &cl, 1);
+ tt_int_op(r, OP_EQ, 0);
+
+ r = config_assign(&options_format, opt, cl, 0, 0, &msg);
+ tt_int_op(r, OP_EQ, 0);
+
+ /* 300 MB RAM available, DirCache enabled */
+ r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(300), &msg);
+ tt_int_op(r, OP_EQ, 0);
+ tt_assert(!msg);
+
+ /* 200 MB RAM available, DirCache enabled */
+ r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(200), &msg);
+ tt_int_op(r, OP_EQ, -1);
+ expect_errmsg = "Being a directory cache (default) with less than ";
+ if (!strstr(msg, expect_errmsg)) {
+ TT_DIE(("Expected error message <%s> from <%s>, but got <%s>.",
+ expect_errmsg, configuration, msg));
+ }
+ tor_free(msg);
+
+ config_free_lines(cl); cl = NULL;
+ configuration = "ORPort 8080\nDirCache 1\nBridgeRelay 1";
+ r = config_get_lines(configuration, &cl, 1);
+ tt_int_op(r, OP_EQ, 0);
+
+ r = config_assign(&options_format, opt, cl, 0, 0, &msg);
+ tt_int_op(r, OP_EQ, 0);
+
+ /* 300 MB RAM available, DirCache enabled, Bridge */
+ r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(300), &msg);
+ tt_int_op(r, OP_EQ, 0);
+ tt_assert(!msg);
+
+ /* 200 MB RAM available, DirCache enabled, Bridge */
+ r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(200), &msg);
+ tt_int_op(r, OP_EQ, -1);
+ expect_errmsg = "Running a Bridge with less than ";
+ if (!strstr(msg, expect_errmsg)) {
+ TT_DIE(("Expected error message <%s> from <%s>, but got <%s>.",
+ expect_errmsg, configuration, msg));
+ }
+ tor_free(msg);
+
+ config_free_lines(cl); cl = NULL;
+ configuration = "ORPort 8080\nDirCache 0";
+ r = config_get_lines(configuration, &cl, 1);
+ tt_int_op(r, OP_EQ, 0);
+
+ r = config_assign(&options_format, opt, cl, 0, 0, &msg);
+ tt_int_op(r, OP_EQ, 0);
+
+ /* 200 MB RAM available, DirCache disabled */
+ r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(200), &msg);
+ tt_int_op(r, OP_EQ, 0);
+ tt_assert(!msg);
+
+ /* 300 MB RAM available, DirCache disabled */
+ r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(300), &msg);
+ tt_int_op(r, OP_EQ, -1);
+ expect_errmsg = "DirCache is disabled and we are configured as a ";
+ if (!strstr(msg, expect_errmsg)) {
+ TT_DIE(("Expected error message <%s> from <%s>, but got <%s>.",
+ expect_errmsg, configuration, msg));
+ }
+ tor_free(msg);
+
clear_log_messages();
+
+ done:
+ if (msg)
+ tor_free(msg);
+ tor_free(dflt);
+ or_options_free(opt);
+ config_free_lines(cl);
return;
}
+static const char *fixed_get_uname_result = NULL;
+
+static const char *
+fixed_get_uname(void)
+{
+ return fixed_get_uname_result;
+}
+
+#define TEST_OPTIONS_OLD_VALUES "TestingV3AuthInitialVotingInterval 1800\n" \
+ "TestingClientBootstrapConsensusMaxDownloadTries 7\n" \
+ "TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries 4\n" \
+ "TestingClientBootstrapConsensusMaxInProgressTries 3\n" \
+ "TestingV3AuthInitialVoteDelay 300\n" \
+ "TestingV3AuthInitialDistDelay 300\n" \
+ "TestingClientMaxIntervalWithoutRequest 600\n" \
+ "TestingDirConnectionMaxStall 600\n" \
+ "TestingConsensusMaxDownloadTries 8\n" \
+ "TestingDescriptorMaxDownloadTries 8\n" \
+ "TestingMicrodescMaxDownloadTries 8\n" \
+ "TestingCertMaxDownloadTries 8\n"
+
+#define TEST_OPTIONS_DEFAULT_VALUES TEST_OPTIONS_OLD_VALUES \
+ "MaxClientCircuitsPending 1\n" \
+ "RendPostPeriod 1000\n" \
+ "KeepAlivePeriod 1\n" \
+ "ConnLimit 1\n" \
+ "V3AuthVotingInterval 300\n" \
+ "V3AuthVoteDelay 20\n" \
+ "V3AuthDistDelay 20\n" \
+ "V3AuthNIntervalsValid 3\n" \
+ "ClientUseIPv4 1\n" \
+ "VirtualAddrNetworkIPv4 127.192.0.0/10\n" \
+ "VirtualAddrNetworkIPv6 [FE80::]/10\n" \
+ "SchedulerHighWaterMark__ 42\n" \
+ "SchedulerLowWaterMark__ 10\n"
+
+typedef struct {
+ or_options_t *old_opt;
+ or_options_t *opt;
+ or_options_t *def_opt;
+} options_test_data_t;
+
+static void free_options_test_data(options_test_data_t *td);
+
+static options_test_data_t *
+get_options_test_data(const char *conf)
+{
+ int rv = -1;
+ char *msg = NULL;
+ config_line_t *cl=NULL;
+ options_test_data_t *result = tor_malloc(sizeof(options_test_data_t));
+ result->opt = options_new();
+ result->old_opt = options_new();
+ result->def_opt = options_new();
+ rv = config_get_lines(conf, &cl, 1);
+ tt_assert(rv == 0);
+ rv = config_assign(&options_format, result->opt, cl, 0, 0, &msg);
+ if (msg) {
+ /* Display the parse error message by comparing it with an empty string */
+ tt_str_op(msg, OP_EQ, "");
+ }
+ tt_assert(rv == 0);
+ config_free_lines(cl);
+ result->opt->LogTimeGranularity = 1;
+ result->opt->TokenBucketRefillInterval = 1;
+ rv = config_get_lines(TEST_OPTIONS_OLD_VALUES, &cl, 1);
+ tt_assert(rv == 0);
+ rv = config_assign(&options_format, result->def_opt, cl, 0, 0, &msg);
+ if (msg) {
+ /* Display the parse error message by comparing it with an empty string */
+ tt_str_op(msg, OP_EQ, "");
+ }
+ tt_assert(rv == 0);
+
+ done:
+ config_free_lines(cl);
+ if (rv != 0) {
+ free_options_test_data(result);
+ result = NULL;
+ /* Callers expect a non-NULL result, so just die if we can't provide one.
+ */
+ tor_assert(0);
+ }
+ return result;
+}
+
+static void
+free_options_test_data(options_test_data_t *td)
+{
+ if (!td) return;
+ or_options_free(td->old_opt);
+ or_options_free(td->opt);
+ or_options_free(td->def_opt);
+ tor_free(td);
+}
+
+#define expect_log_msg(str) \
+ tt_assert_msg(mock_saved_log_has_message(str), \
+ "expected log to contain " # str);
+
+#define expect_no_log_msg(str) \
+ tt_assert_msg(!mock_saved_log_has_message(str), \
+ "expected log to not contain " # str);
+
+static void
+test_options_validate__uname_for_server(void *ignored)
+{
+ (void)ignored;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "ORListenAddress 127.0.0.1:5555");
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ MOCK(get_uname, fixed_get_uname);
+ fixed_get_uname_result = "Windows 95";
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_msg("Tor is running as a server, but you"
+ " are running Windows 95; this probably won't work. See https://www"
+ ".torproject.org/docs/faq.html#BestOSForRelay for details.\n");
+ tor_free(msg);
+
+ fixed_get_uname_result = "Windows 98";
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_msg("Tor is running as a server, but you"
+ " are running Windows 98; this probably won't work. See https://www"
+ ".torproject.org/docs/faq.html#BestOSForRelay for details.\n");
+ tor_free(msg);
+
+ fixed_get_uname_result = "Windows Me";
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_msg("Tor is running as a server, but you"
+ " are running Windows Me; this probably won't work. See https://www"
+ ".torproject.org/docs/faq.html#BestOSForRelay for details.\n");
+ tor_free(msg);
+
+ fixed_get_uname_result = "Windows 2000";
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_entry();
+ tor_free(msg);
+
+ done:
+ UNMOCK(get_uname);
+ free_options_test_data(tdata);
+ tor_free(msg);
+ teardown_capture_of_logs(previous_log);
+}
+
+static void
+test_options_validate__outbound_addresses(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "OutboundBindAddress xxyy!!!sdfaf");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__data_directory(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "DataDirectory longreallyl"
+ "ongLONGLONGlongreallylong"
+ "LONGLONGlongreallylongLON"
+ "GLONGlongreallylongLONGLO"
+ "NGlongreallylongLONGLONGl"
+ "ongreallylongLONGLONGlong"
+ "reallylongLONGLONGlongrea"
+ "llylongLONGLONGlongreally"
+ "longLONGLONGlongreallylon"
+ "gLONGLONGlongreallylongLO"
+ "NGLONGlongreallylongLONGL"
+ "ONGlongreallylongLONGLONG"
+ "longreallylongLONGLONGlon"
+ "greallylongLONGLONGlongre"
+ "allylongLONGLONGlongreall"
+ "ylongLONGLONGlongreallylo"
+ "ngLONGLONGlongreallylongL"
+ "ONGLONGlongreallylongLONG"
+ "LONG"); // 440 characters
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Invalid DataDirectory");
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__nickname(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "Nickname ThisNickNameIsABitTooLong");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Nickname 'ThisNickNameIsABitTooLong' is wrong length or"
+ " contains illegal characters.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("Nickname AMoreValidNick");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(!msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("DataDirectory /tmp/somewhere");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(!msg);
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__contactinfo(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "ORListenAddress 127.0.0.1:5555\nORPort 955");
+ int previous_log = setup_capture_of_logs(LOG_DEBUG);
+ tdata->opt->ContactInfo = NULL;
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg(
+ "Your ContactInfo config option is not"
+ " set. Please consider setting it, so we can contact you if your"
+ " server is misconfigured or something else goes wrong.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("ORListenAddress 127.0.0.1:5555\nORPort 955\n"
+ "ContactInfo hella@example.org");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_no_log_msg(
+ "Your ContactInfo config option is not"
+ " set. Please consider setting it, so we can contact you if your"
+ " server is misconfigured or something else goes wrong.\n");
+ tor_free(msg);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+extern int quiet_level;
+
+static void
+test_options_validate__logs(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ (void)ret;
+ char *msg;
+ int orig_quiet_level = quiet_level;
+ options_test_data_t *tdata = get_options_test_data("");
+ tdata->opt->Logs = NULL;
+ tdata->opt->RunAsDaemon = 0;
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(tdata->opt->Logs->key, OP_EQ, "Log");
+ tt_str_op(tdata->opt->Logs->value, OP_EQ, "notice stdout");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("");
+ tdata->opt->Logs = NULL;
+ tdata->opt->RunAsDaemon = 0;
+ quiet_level = 1;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(tdata->opt->Logs->key, OP_EQ, "Log");
+ tt_str_op(tdata->opt->Logs->value, OP_EQ, "warn stdout");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("");
+ tdata->opt->Logs = NULL;
+ tdata->opt->RunAsDaemon = 0;
+ quiet_level = 2;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_assert(!tdata->opt->Logs);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("");
+ tdata->opt->Logs = NULL;
+ tdata->opt->RunAsDaemon = 0;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 1, &msg);
+ tt_assert(!tdata->opt->Logs);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("");
+ tdata->opt->Logs = NULL;
+ tdata->opt->RunAsDaemon = 1;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_assert(!tdata->opt->Logs);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("");
+ tdata->opt->RunAsDaemon = 0;
+ config_line_t *cl=NULL;
+ config_get_lines("Log foo", &cl, 1);
+ tdata->opt->Logs = cl;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op((intptr_t)tdata->opt->Logs, OP_EQ, (intptr_t)cl);
+
+ done:
+ quiet_level = orig_quiet_level;
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+/* static config_line_t * */
+/* mock_config_line(const char *key, const char *val) */
+/* { */
+/* config_line_t *config_line = tor_malloc(sizeof(config_line_t)); */
+/* memset(config_line, 0, sizeof(config_line_t)); */
+/* config_line->key = tor_strdup(key); */
+/* config_line->value = tor_strdup(val); */
+/* return config_line; */
+/* } */
+
+static void
+test_options_validate__authdir(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_INFO);
+ options_test_data_t *tdata = get_options_test_data(
+ "AuthoritativeDirectory 1\n"
+ "Address this.should.not_exist.example.org");
+
+ sandbox_disable_getaddrinfo_cache();
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Failed to resolve/guess local address. See logs for"
+ " details.");
+ expect_log_msg("Could not resolve local Address "
+ "'this.should.not_exist.example.org'. Failing.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(!msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Authoritative directory servers must set ContactInfo");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "TestingTorNetwork 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "AuthoritativeDir is set, but none of (Bridge/V3)"
+ "AuthoritativeDir is set.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "AuthoritativeDir is set, but none of (Bridge/V3)"
+ "AuthoritativeDir is set.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "RecommendedVersions 1.2, 3.14\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(tdata->opt->RecommendedClientVersions->value, OP_EQ, "1.2, 3.14");
+ tt_str_op(tdata->opt->RecommendedServerVersions->value, OP_EQ, "1.2, 3.14");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "RecommendedVersions 1.2, 3.14\n"
+ "RecommendedClientVersions 25\n"
+ "RecommendedServerVersions 4.18\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(tdata->opt->RecommendedClientVersions->value, OP_EQ, "25");
+ tt_str_op(tdata->opt->RecommendedServerVersions->value, OP_EQ, "4.18");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "VersioningAuthoritativeDirectory 1\n"
+ "RecommendedVersions 1.2, 3.14\n"
+ "RecommendedClientVersions 25\n"
+ "RecommendedServerVersions 4.18\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(msg, OP_EQ, "AuthoritativeDir is set, but none of (Bridge/V3)"
+ "AuthoritativeDir is set.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "VersioningAuthoritativeDirectory 1\n"
+ "RecommendedServerVersions 4.18\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(msg, OP_EQ, "Versioning authoritative dir servers must set "
+ "Recommended*Versions.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "VersioningAuthoritativeDirectory 1\n"
+ "RecommendedClientVersions 4.18\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(msg, OP_EQ, "Versioning authoritative dir servers must set "
+ "Recommended*Versions.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "UseEntryGuards 1\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_msg("Authoritative directory servers "
+ "can't set UseEntryGuards. Disabling.\n");
+ tt_int_op(tdata->opt->UseEntryGuards, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "V3AuthoritativeDir 1\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_msg("Authoritative directories always try"
+ " to download extra-info documents. Setting DownloadExtraInfo.\n");
+ tt_int_op(tdata->opt->DownloadExtraInfo, OP_EQ, 1);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "DownloadExtraInfo 1\n"
+ "V3AuthoritativeDir 1\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_no_log_msg("Authoritative directories always try"
+ " to download extra-info documents. Setting DownloadExtraInfo.\n");
+ tt_int_op(tdata->opt->DownloadExtraInfo, OP_EQ, 1);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(msg, OP_EQ, "AuthoritativeDir is set, but none of (Bridge/V3)"
+ "AuthoritativeDir is set.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "BridgeAuthoritativeDir 1\n"
+ "ContactInfo hello@hello.com\n"
+ "V3BandwidthsFile non-existant-file\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(msg, OP_EQ,
+ "Running as authoritative directory, but no DirPort set.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "BridgeAuthoritativeDir 1\n"
+ "ContactInfo hello@hello.com\n"
+ "V3BandwidthsFile non-existant-file\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(NULL, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(msg, OP_EQ,
+ "Running as authoritative directory, but no DirPort set.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "BridgeAuthoritativeDir 1\n"
+ "ContactInfo hello@hello.com\n"
+ "GuardfractionFile non-existant-file\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(msg, OP_EQ,
+ "Running as authoritative directory, but no DirPort set.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "BridgeAuthoritativeDir 1\n"
+ "ContactInfo hello@hello.com\n"
+ "GuardfractionFile non-existant-file\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ options_validate(NULL, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_str_op(msg, OP_EQ,
+ "Running as authoritative directory, but no DirPort set.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "BridgeAuthoritativeDir 1\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Running as authoritative directory, but no DirPort set.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AuthoritativeDirectory 1\n"
+ "Address 100.200.10.1\n"
+ "DirPort 999\n"
+ "BridgeAuthoritativeDir 1\n"
+ "ContactInfo hello@hello.com\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Running as authoritative directory, but no ORPort set.");
+ tor_free(msg);
+
+ // TODO: This case can't be reached, since clientonly is used to
+ // check when parsing port lines as well.
+ /* free_options_test_data(tdata); */
+ /* tdata = get_options_test_data("AuthoritativeDirectory 1\n" */
+ /* "Address 100.200.10.1\n" */
+ /* "DirPort 999\n" */
+ /* "ORPort 888\n" */
+ /* "ClientOnly 1\n" */
+ /* "BridgeAuthoritativeDir 1\n" */
+ /* "ContactInfo hello@hello.com\n" */
+ /* "SchedulerHighWaterMark__ 42\n" */
+ /* "SchedulerLowWaterMark__ 10\n"); */
+ /* mock_clean_saved_logs(); */
+ /* ret = options_validate(tdata->old_opt, tdata->opt, */
+ /* tdata->def_opt, 0, &msg); */
+ /* tt_int_op(ret, OP_EQ, -1); */
+ /* tt_str_op(msg, OP_EQ, "Running as authoritative directory, " */
+ /* "but ClientOnly also set."); */
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ // sandbox_free_getaddrinfo_cache();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__relay_with_hidden_services(void *ignored)
+{
+ (void)ignored;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_DEBUG);
+ options_test_data_t *tdata = get_options_test_data(
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "HiddenServiceDir "
+ "/Library/Tor/var/lib/tor/hidden_service/\n"
+ "HiddenServicePort 80 127.0.0.1:8080\n"
+ );
+
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_msg(
+ "Tor is currently configured as a relay and a hidden service. "
+ "That's not very secure: you should probably run your hidden servi"
+ "ce in a separate Tor process, at least -- see "
+ "https://trac.torproject.org/8742\n");
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+// TODO: it doesn't seem possible to hit the case of having no port lines at
+// all, since there will be a default created for SocksPort
+/* static void */
+/* test_options_validate__ports(void *ignored) */
+/* { */
+/* (void)ignored; */
+/* int ret; */
+/* char *msg; */
+/* int previous_log = setup_capture_of_logs(LOG_WARN); */
+/* options_test_data_t *tdata = get_options_test_data(""); */
+/* ret = options_validate(tdata->old_opt, tdata->opt, */
+/* tdata->def_opt, 0, &msg); */
+/* expect_log_msg("SocksPort, TransPort, NATDPort, DNSPort, and ORPort " */
+/* "are all undefined, and there aren't any hidden services " */
+/* "configured. " */
+/* " Tor will still run, but probably won't do anything.\n"); */
+/* done: */
+/* teardown_capture_of_logs(previous_log); */
+/* free_options_test_data(tdata); */
+/* tor_free(msg); */
+/* } */
+
+static void
+test_options_validate__transproxy(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata;
+
+#ifdef USE_TRANSPARENT
+ // Test default trans proxy
+ tdata = get_options_test_data("TransProxyType default\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(tdata->opt->TransProxyType_parsed, OP_EQ, TPT_DEFAULT);
+ tor_free(msg);
+
+ // Test pf-divert trans proxy
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("TransProxyType pf-divert\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+
+#if !defined(__OpenBSD__) && !defined( DARWIN )
+ tt_str_op(msg, OP_EQ,
+ "pf-divert is a OpenBSD-specific and OS X/Darwin-specific feature.");
+#else
+ tt_int_op(tdata->opt->TransProxyType_parsed, OP_EQ, TPT_PF_DIVERT);
+ tt_str_op(msg, OP_EQ, "Cannot use TransProxyType without "
+ "any valid TransPort or TransListenAddress.");
+#endif
+ tor_free(msg);
+
+ // Test tproxy trans proxy
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("TransProxyType tproxy\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+
+#if !defined(__linux__)
+ tt_str_op(msg, OP_EQ, "TPROXY is a Linux-specific feature.");
+#else
+ tt_int_op(tdata->opt->TransProxyType_parsed, OP_EQ, TPT_TPROXY);
+ tt_str_op(msg, OP_EQ, "Cannot use TransProxyType without any valid "
+ "TransPort or TransListenAddress.");
+#endif
+ tor_free(msg);
+
+ // Test ipfw trans proxy
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("TransProxyType ipfw\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+
+#if !defined(__FreeBSD__) && !defined( DARWIN )
+ tt_str_op(msg, OP_EQ, "ipfw is a FreeBSD-specificand OS X/Darwin-specific "
+ "feature.");
+#else
+ tt_int_op(tdata->opt->TransProxyType_parsed, OP_EQ, TPT_IPFW);
+ tt_str_op(msg, OP_EQ, "Cannot use TransProxyType without any valid "
+ "TransPort or TransListenAddress.");
+#endif
+ tor_free(msg);
+
+ // Test unknown trans proxy
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("TransProxyType non-existant\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Unrecognized value for TransProxyType");
+ tor_free(msg);
+
+ // Test trans proxy success
+ free_options_test_data(tdata);
+
+#if defined(linux)
+ tdata = get_options_test_data("TransProxyType tproxy\n"
+ "TransPort 127.0.0.1:123\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(!msg);
+#endif
+#if defined(__FreeBSD__) || defined( DARWIN )
+ tdata = get_options_test_data("TransProxyType ipfw\n"
+ "TransPort 127.0.0.1:123\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(!msg);
+#endif
+#if defined(__OpenBSD__)
+ tdata = get_options_test_data("TransProxyType pf-divert\n"
+ "TransPort 127.0.0.1:123\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(!msg);
+#endif
+
+#else
+ tdata = get_options_test_data("TransPort 127.0.0.1:555\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "TransPort and TransListenAddress are disabled in "
+ "this build.");
+ tor_free(msg);
+#endif
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+NS_DECL(country_t, geoip_get_country, (const char *country));
+
+static country_t
+NS(geoip_get_country)(const char *countrycode)
+{
+ (void)countrycode;
+ CALLED(geoip_get_country)++;
+
+ return 1;
+}
+
+static void
+test_options_validate__exclude_nodes(void *ignored)
+{
+ (void)ignored;
+
+ NS_MOCK(geoip_get_country);
+
+ int ret;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+ options_test_data_t *tdata = get_options_test_data(
+ "ExcludeExitNodes {us}\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(smartlist_len(tdata->opt->ExcludeExitNodesUnion_->list), OP_EQ, 1);
+ tt_str_op((char *)
+ (smartlist_get(tdata->opt->ExcludeExitNodesUnion_->list, 0)),
+ OP_EQ, "{us}");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("ExcludeNodes {cn}\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(smartlist_len(tdata->opt->ExcludeExitNodesUnion_->list), OP_EQ, 1);
+ tt_str_op((char *)
+ (smartlist_get(tdata->opt->ExcludeExitNodesUnion_->list, 0)),
+ OP_EQ, "{cn}");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("ExcludeNodes {cn}\n"
+ "ExcludeExitNodes {us} {cn}\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(smartlist_len(tdata->opt->ExcludeExitNodesUnion_->list), OP_EQ, 2);
+ tt_str_op((char *)
+ (smartlist_get(tdata->opt->ExcludeExitNodesUnion_->list, 0)),
+ OP_EQ, "{us} {cn}");
+ tt_str_op((char *)
+ (smartlist_get(tdata->opt->ExcludeExitNodesUnion_->list, 1)),
+ OP_EQ, "{cn}");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("ExcludeNodes {cn}\n"
+ "StrictNodes 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg(
+ "You have asked to exclude certain relays from all positions "
+ "in your circuits. Expect hidden services and other Tor "
+ "features to be broken in unpredictable ways.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("ExcludeNodes {cn}\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_no_log_msg(
+ "You have asked to exclude certain relays from all positions "
+ "in your circuits. Expect hidden services and other Tor "
+ "features to be broken in unpredictable ways.\n");
+ tor_free(msg);
+
+ done:
+ NS_UNMOCK(geoip_get_country);
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__scheduler(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_DEBUG);
+ options_test_data_t *tdata = get_options_test_data(
+ "SchedulerLowWaterMark__ 0\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg("Bad SchedulerLowWaterMark__ option\n");
+ tor_free(msg);
+
+ // TODO: this test cannot run on platforms where UINT32_MAX == UINT64_MAX.
+ // I suspect it's unlikely this branch can actually happen
+ /* free_options_test_data(tdata); */
+ /* tdata = get_options_test_data( */
+ /* "SchedulerLowWaterMark 10000000000000000000\n"); */
+ /* tdata->opt->SchedulerLowWaterMark__ = (uint64_t)UINT32_MAX; */
+ /* tdata->opt->SchedulerLowWaterMark__++; */
+ /* mock_clean_saved_logs(); */
+ /* ret = options_validate(tdata->old_opt, tdata->opt, */
+ /* tdata->def_opt, 0, &msg); */
+ /* tt_int_op(ret, OP_EQ, -1); */
+ /* expect_log_msg("Bad SchedulerLowWaterMark__ option\n"); */
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("SchedulerLowWaterMark__ 42\n"
+ "SchedulerHighWaterMark__ 42\n");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg("Bad SchedulerHighWaterMark option\n");
+ tor_free(msg);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__node_families(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "NodeFamily flux, flax\n"
+ "NodeFamily somewhere\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(tdata->opt->NodeFamilySets);
+ tt_int_op(smartlist_len(tdata->opt->NodeFamilySets), OP_EQ, 2);
+ tt_str_op((char *)(smartlist_get(
+ ((routerset_t *)smartlist_get(tdata->opt->NodeFamilySets, 0))->list, 0)),
+ OP_EQ, "flux");
+ tt_str_op((char *)(smartlist_get(
+ ((routerset_t *)smartlist_get(tdata->opt->NodeFamilySets, 0))->list, 1)),
+ OP_EQ, "flax");
+ tt_str_op((char *)(smartlist_get(
+ ((routerset_t *)smartlist_get(tdata->opt->NodeFamilySets, 1))->list, 0)),
+ OP_EQ, "somewhere");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(!tdata->opt->NodeFamilySets);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("NodeFamily !flux\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(tdata->opt->NodeFamilySets);
+ tt_int_op(smartlist_len(tdata->opt->NodeFamilySets), OP_EQ, 0);
+ tor_free(msg);
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__tlsec(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_DEBUG);
+ options_test_data_t *tdata = get_options_test_data(
+ "TLSECGroup ed25519\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg("Unrecognized TLSECGroup: Falling back to the default.\n");
+ tt_assert(!tdata->opt->TLSECGroup);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("TLSECGroup P224\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_no_log_msg(
+ "Unrecognized TLSECGroup: Falling back to the default.\n");
+ tt_assert(tdata->opt->TLSECGroup);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("TLSECGroup P256\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_no_log_msg(
+ "Unrecognized TLSECGroup: Falling back to the default.\n");
+ tt_assert(tdata->opt->TLSECGroup);
+ tor_free(msg);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__token_bucket(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data("");
+
+ tdata->opt->TokenBucketRefillInterval = 0;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "TokenBucketRefillInterval must be between 1 and 1000 inclusive.");
+ tor_free(msg);
+
+ tdata->opt->TokenBucketRefillInterval = 1001;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "TokenBucketRefillInterval must be between 1 and 1000 inclusive.");
+ tor_free(msg);
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__recommended_packages(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+ options_test_data_t *tdata = get_options_test_data(
+ "RecommendedPackages foo 1.2 http://foo.com sha1=123123123123\n"
+ "RecommendedPackages invalid-package-line\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_no_log_msg("Invalid RecommendedPackage line "
+ "invalid-package-line will be ignored\n");
+
+ done:
+ escaped(NULL); // This will free the leaking memory from the previous escaped
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__fetch_dir(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "FetchDirInfoExtraEarly 1\n"
+ "FetchDirInfoEarly 0\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "FetchDirInfoExtraEarly requires that you"
+ " also set FetchDirInfoEarly");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("FetchDirInfoExtraEarly 1\n"
+ "FetchDirInfoEarly 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_NE, "FetchDirInfoExtraEarly requires that you"
+ " also set FetchDirInfoEarly");
+ tor_free(msg);
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__conn_limit(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "ConnLimit 0\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "ConnLimit must be greater than 0, but was set to 0");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "MaxClientCircuitsPending must be between 1 and 1024, "
+ "but was set to 0");
+ tor_free(msg);
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__paths_needed(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+ options_test_data_t *tdata = get_options_test_data(
+ "PathsNeededToBuildCircuits 0.1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(tdata->opt->PathsNeededToBuildCircuits > 0.24 &&
+ tdata->opt->PathsNeededToBuildCircuits < 0.26);
+ expect_log_msg("PathsNeededToBuildCircuits is too low. "
+ "Increasing to 0.25\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data("PathsNeededToBuildCircuits 0.99\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(tdata->opt->PathsNeededToBuildCircuits > 0.94 &&
+ tdata->opt->PathsNeededToBuildCircuits < 0.96);
+ expect_log_msg("PathsNeededToBuildCircuits is "
+ "too high. Decreasing to 0.95\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data("PathsNeededToBuildCircuits 0.91\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(tdata->opt->PathsNeededToBuildCircuits > 0.90 &&
+ tdata->opt->PathsNeededToBuildCircuits < 0.92);
+ expect_no_log_entry();
+ tor_free(msg);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__max_client_circuits(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "MaxClientCircuitsPending 0\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "MaxClientCircuitsPending must be between 1 and 1024,"
+ " but was set to 0");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("MaxClientCircuitsPending 1025\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "MaxClientCircuitsPending must be between 1 and 1024,"
+ " but was set to 1025");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "KeepalivePeriod option must be positive.");
+ tor_free(msg);
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__ports(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "FirewallPorts 65537\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Port '65537' out of range in FirewallPorts");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("FirewallPorts 1\n"
+ "LongLivedPorts 124444\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Port '124444' out of range in LongLivedPorts");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("FirewallPorts 1\n"
+ "LongLivedPorts 2\n"
+ "RejectPlaintextPorts 112233\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Port '112233' out of range in RejectPlaintextPorts");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("FirewallPorts 1\n"
+ "LongLivedPorts 2\n"
+ "RejectPlaintextPorts 3\n"
+ "WarnPlaintextPorts 65536\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Port '65536' out of range in WarnPlaintextPorts");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("FirewallPorts 1\n"
+ "LongLivedPorts 2\n"
+ "RejectPlaintextPorts 3\n"
+ "WarnPlaintextPorts 4\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "KeepalivePeriod option must be positive.");
+ tor_free(msg);
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__reachable_addresses(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_NOTICE);
+ options_test_data_t *tdata = get_options_test_data(
+ "FascistFirewall 1\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg("Converting FascistFirewall config "
+ "option to new format: \"ReachableDirAddresses *:80\"\n");
+ tt_str_op(tdata->opt->ReachableDirAddresses->value, OP_EQ, "*:80");
+ expect_log_msg("Converting FascistFirewall config "
+ "option to new format: \"ReachableORAddresses *:443\"\n");
+ tt_str_op(tdata->opt->ReachableORAddresses->value, OP_EQ, "*:443");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data("FascistFirewall 1\n"
+ "ReachableDirAddresses *:81\n"
+ "ReachableORAddresses *:444\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+ tdata->opt->FirewallPorts = smartlist_new();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_entry();
+ tt_str_op(tdata->opt->ReachableDirAddresses->value, OP_EQ, "*:81");
+ tt_str_op(tdata->opt->ReachableORAddresses->value, OP_EQ, "*:444");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data("FascistFirewall 1\n"
+ "FirewallPort 123\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg("Converting FascistFirewall and "
+ "FirewallPorts config options to new format: "
+ "\"ReachableAddresses *:123\"\n");
+ tt_str_op(tdata->opt->ReachableAddresses->value, OP_EQ, "*:123");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data("FascistFirewall 1\n"
+ "ReachableAddresses *:82\n"
+ "ReachableAddresses *:83\n"
+ "ReachableAddresses reject *:*\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_entry();
+ tt_str_op(tdata->opt->ReachableAddresses->value, OP_EQ, "*:82");
+ tor_free(msg);
+
+#define SERVERS_REACHABLE_MSG "Servers must be able to freely connect to" \
+ " the rest of the Internet, so they must not set Reachable*Addresses or" \
+ " FascistFirewall or FirewallPorts or ClientUseIPv4 0."
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("ReachableAddresses *:82\n"
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, SERVERS_REACHABLE_MSG);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("ReachableORAddresses *:82\n"
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, SERVERS_REACHABLE_MSG);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("ReachableDirAddresses *:82\n"
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, SERVERS_REACHABLE_MSG);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("ClientUseIPv4 0\n"
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, SERVERS_REACHABLE_MSG);
+ tor_free(msg);
+
+ /* Test IPv4-only clients setting IPv6 preferences */
+
+#define WARN_PLEASE_USE_IPV6_OR_LOG_MSG \
+ "ClientPreferIPv6ORPort 1 is ignored unless tor is using IPv6. " \
+ "Please set ClientUseIPv6 1, ClientUseIPv4 0, or configure bridges.\n"
+
+#define WARN_PLEASE_USE_IPV6_DIR_LOG_MSG \
+ "ClientPreferIPv6DirPort 1 is ignored unless tor is using IPv6. " \
+ "Please set ClientUseIPv6 1, ClientUseIPv4 0, or configure bridges.\n"
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ClientUseIPv4 1\n"
+ "ClientUseIPv6 0\n"
+ "UseBridges 0\n"
+ "ClientPreferIPv6ORPort 1\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg(WARN_PLEASE_USE_IPV6_OR_LOG_MSG);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ClientUseIPv4 1\n"
+ "ClientUseIPv6 0\n"
+ "UseBridges 0\n"
+ "ClientPreferIPv6DirPort 1\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg(WARN_PLEASE_USE_IPV6_DIR_LOG_MSG);
+ tor_free(msg);
+
+ /* Now test an IPv4/IPv6 client setting IPv6 preferences */
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ClientUseIPv4 1\n"
+ "ClientUseIPv6 1\n"
+ "ClientPreferIPv6ORPort 1\n"
+ "ClientPreferIPv6DirPort 1\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+
+ /* Now test an IPv6 client setting IPv6 preferences */
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ClientUseIPv6 1\n"
+ "ClientPreferIPv6ORPort 1\n"
+ "ClientPreferIPv6DirPort 1\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+
+ /* And an implicit (IPv4 disabled) IPv6 client setting IPv6 preferences */
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ClientUseIPv4 0\n"
+ "ClientPreferIPv6ORPort 1\n"
+ "ClientPreferIPv6DirPort 1\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+
+ /* And an implicit (bridge) client setting IPv6 preferences */
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "UseBridges 1\n"
+ "Bridge 127.0.0.1:12345\n"
+ "ClientPreferIPv6ORPort 1\n"
+ "ClientPreferIPv6DirPort 1\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__use_bridges(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "UseBridges 1\n"
+ "ClientUseIPv4 1\n"
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Servers must be able to freely connect to the rest of"
+ " the Internet, so they must not set UseBridges.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("UseBridges 1\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_NE, "Servers must be able to freely connect to the rest of"
+ " the Internet, so they must not set UseBridges.");
+ tor_free(msg);
+
+ NS_MOCK(geoip_get_country);
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("UseBridges 1\n"
+ "EntryNodes {cn}\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "You cannot set both UseBridges and EntryNodes.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "UseBridges 1\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "If you set UseBridges, you must specify at least one bridge.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "UseBridges 1\n"
+ "Bridge 10.0.0.1\n"
+ "Bridge !!!\n"
+ );
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Bridge line did not parse. See logs for details.");
+ tor_free(msg);
+
+ done:
+ NS_UNMOCK(geoip_get_country);
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__entry_nodes(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ NS_MOCK(geoip_get_country);
+ options_test_data_t *tdata = get_options_test_data(
+ "EntryNodes {cn}\n"
+ "UseEntryGuards 0\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "If EntryNodes is set, UseEntryGuards must be enabled.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("EntryNodes {cn}\n"
+ "UseEntryGuards 1\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "KeepalivePeriod option must be positive.");
+ tor_free(msg);
+
+ done:
+ NS_UNMOCK(geoip_get_country);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__invalid_nodes(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "AllowInvalidNodes something_stupid\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Unrecognized value 'something_stupid' in AllowInvalidNodes");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AllowInvalidNodes entry, middle, exit\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(tdata->opt->AllowInvalid_, OP_EQ, ALLOW_INVALID_ENTRY |
+ ALLOW_INVALID_EXIT | ALLOW_INVALID_MIDDLE);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("AllowInvalidNodes introduction, rendezvous\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(tdata->opt->AllowInvalid_, OP_EQ, ALLOW_INVALID_INTRODUCTION |
+ ALLOW_INVALID_RENDEZVOUS);
+ tor_free(msg);
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__safe_logging(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = get_options_test_data(
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(tdata->opt->SafeLogging_, OP_EQ, SAFELOG_SCRUB_NONE);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("SafeLogging 0\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(tdata->opt->SafeLogging_, OP_EQ, SAFELOG_SCRUB_NONE);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("SafeLogging Relay\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(tdata->opt->SafeLogging_, OP_EQ, SAFELOG_SCRUB_RELAY);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("SafeLogging 1\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_int_op(tdata->opt->SafeLogging_, OP_EQ, SAFELOG_SCRUB_ALL);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("SafeLogging stuffy\n"
+ "MaxClientCircuitsPending 1\n"
+ "ConnLimit 1\n"
+ "SchedulerHighWaterMark__ 42\n"
+ "SchedulerLowWaterMark__ 10\n");
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Unrecognized value '\"stuffy\"' in SafeLogging");
+ tor_free(msg);
+
+ done:
+ escaped(NULL); // This will free the leaking memory from the previous escaped
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__publish_server_descriptor(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+ options_test_data_t *tdata = get_options_test_data(
+ "PublishServerDescriptor bridge\n" TEST_OPTIONS_DEFAULT_VALUES
+ );
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(!msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("PublishServerDescriptor humma\n"
+ TEST_OPTIONS_DEFAULT_VALUES);
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Unrecognized value in PublishServerDescriptor");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("PublishServerDescriptor bridge, v3\n"
+ TEST_OPTIONS_DEFAULT_VALUES);
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Bridges are not supposed to publish router "
+ "descriptors to the directory authorities. Please correct your "
+ "PublishServerDescriptor line.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("BridgeRelay 1\n"
+ "PublishServerDescriptor v3\n"
+ TEST_OPTIONS_DEFAULT_VALUES);
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Bridges are not supposed to publish router "
+ "descriptors to the directory authorities. Please correct your "
+ "PublishServerDescriptor line.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("BridgeRelay 1\n" TEST_OPTIONS_DEFAULT_VALUES);
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_NE, "Bridges are not supposed to publish router "
+ "descriptors to the directory authorities. Please correct your "
+ "PublishServerDescriptor line.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data("BridgeRelay 1\n"
+ "DirPort 999\n" TEST_OPTIONS_DEFAULT_VALUES);
+
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg("Can't set a DirPort on a bridge "
+ "relay; disabling DirPort\n");
+ tt_assert(!tdata->opt->DirPort_lines);
+ tt_assert(!tdata->opt->DirPort_set);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__testing(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+
+#define ENSURE_DEFAULT(varname, varval) \
+ STMT_BEGIN \
+ free_options_test_data(tdata); \
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES \
+ #varname " " #varval "\n"); \
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);\
+ tt_str_op(msg, OP_EQ, \
+ #varname " may only be changed in testing Tor networks!"); \
+ tt_int_op(ret, OP_EQ, -1); \
+ tor_free(msg); \
+ \
+ free_options_test_data(tdata); \
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES \
+ #varname " " #varval "\n" \
+ VALID_DIR_AUTH \
+ "TestingTorNetwork 1\n"); \
+ \
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);\
+ if (msg) { \
+ tt_str_op(msg, OP_NE, \
+ #varname " may only be changed in testing Tor networks!"); \
+ tor_free(msg); \
+ } \
+ \
+ free_options_test_data(tdata); \
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES \
+ #varname " " #varval "\n" \
+ "___UsingTestNetworkDefaults 1\n"); \
+ \
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);\
+ if (msg) { \
+ tt_str_op(msg, OP_NE, \
+ #varname " may only be changed in testing Tor networks!"); \
+ tor_free(msg); \
+ } \
+ STMT_END
+
+ ENSURE_DEFAULT(TestingV3AuthInitialVotingInterval, 3600);
+ ENSURE_DEFAULT(TestingV3AuthInitialVoteDelay, 3000);
+ ENSURE_DEFAULT(TestingV3AuthInitialDistDelay, 3000);
+ ENSURE_DEFAULT(TestingV3AuthVotingStartOffset, 3000);
+ ENSURE_DEFAULT(TestingAuthDirTimeToLearnReachability, 3000);
+ ENSURE_DEFAULT(TestingEstimatedDescriptorPropagationTime, 3000);
+ ENSURE_DEFAULT(TestingServerDownloadSchedule, 3000);
+ ENSURE_DEFAULT(TestingClientDownloadSchedule, 3000);
+ ENSURE_DEFAULT(TestingServerConsensusDownloadSchedule, 3000);
+ ENSURE_DEFAULT(TestingClientConsensusDownloadSchedule, 3000);
+ ENSURE_DEFAULT(TestingBridgeDownloadSchedule, 3000);
+ ENSURE_DEFAULT(TestingClientMaxIntervalWithoutRequest, 3000);
+ ENSURE_DEFAULT(TestingDirConnectionMaxStall, 3000);
+ ENSURE_DEFAULT(TestingConsensusMaxDownloadTries, 3000);
+ ENSURE_DEFAULT(TestingDescriptorMaxDownloadTries, 3000);
+ ENSURE_DEFAULT(TestingMicrodescMaxDownloadTries, 3000);
+ ENSURE_DEFAULT(TestingCertMaxDownloadTries, 3000);
+ ENSURE_DEFAULT(TestingAuthKeyLifetime, 3000);
+ ENSURE_DEFAULT(TestingLinkCertLifetime, 3000);
+ ENSURE_DEFAULT(TestingSigningKeySlop, 3000);
+ ENSURE_DEFAULT(TestingAuthKeySlop, 3000);
+ ENSURE_DEFAULT(TestingLinkKeySlop, 3000);
+
+ done:
+ escaped(NULL); // This will free the leaking memory from the previous escaped
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__hidserv(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ options_test_data_t *tdata = get_options_test_data(
+ TEST_OPTIONS_DEFAULT_VALUES);
+ tdata->opt->MinUptimeHidServDirectoryV2 = -1;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("MinUptimeHidServDirectoryV2 "
+ "option must be at least 0 seconds. Changing to 0.\n");
+ tt_int_op(tdata->opt->MinUptimeHidServDirectoryV2, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "RendPostPeriod 1\n" );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("RendPostPeriod option is too short;"
+ " raising to 600 seconds.\n");
+ tt_int_op(tdata->opt->RendPostPeriod, OP_EQ, 600);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "RendPostPeriod 302401\n" );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("RendPostPeriod is too large; "
+ "clipping to 302400s.\n");
+ tt_int_op(tdata->opt->RendPostPeriod, OP_EQ, 302400);
+ tor_free(msg);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__predicted_ports(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ options_test_data_t *tdata = get_options_test_data(
+ "PredictedPortsRelevanceTime 100000000\n"
+ TEST_OPTIONS_DEFAULT_VALUES);
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("PredictedPortsRelevanceTime is too "
+ "large; clipping to 3600s.\n");
+ tt_int_op(tdata->opt->PredictedPortsRelevanceTime, OP_EQ, 3600);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__path_bias(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+
+ options_test_data_t *tdata = get_options_test_data(
+ TEST_OPTIONS_DEFAULT_VALUES
+ "PathBiasNoticeRate 1.1\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "PathBiasNoticeRate is too high. It must be between 0 and 1.0");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "PathBiasWarnRate 1.1\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "PathBiasWarnRate is too high. It must be between 0 and 1.0");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "PathBiasExtremeRate 1.1\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "PathBiasExtremeRate is too high. It must be between 0 and 1.0");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "PathBiasNoticeUseRate 1.1\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "PathBiasNoticeUseRate is too high. It must be between 0 and 1.0");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "PathBiasExtremeUseRate 1.1\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "PathBiasExtremeUseRate is too high. It must be between 0 and 1.0");
+ tor_free(msg);
+
+ done:
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__bandwidth(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+
+#define ENSURE_BANDWIDTH_PARAM(p) \
+ STMT_BEGIN \
+ free_options_test_data(tdata); \
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES #p " 3Gb\n"); \
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);\
+ tt_int_op(ret, OP_EQ, -1); \
+ tt_mem_op(msg, OP_EQ, #p " (3221225471) must be at most 2147483647", 40); \
+ tor_free(msg); \
+ STMT_END
+
+ ENSURE_BANDWIDTH_PARAM(BandwidthRate);
+ ENSURE_BANDWIDTH_PARAM(BandwidthBurst);
+ ENSURE_BANDWIDTH_PARAM(MaxAdvertisedBandwidth);
+ ENSURE_BANDWIDTH_PARAM(RelayBandwidthRate);
+ ENSURE_BANDWIDTH_PARAM(RelayBandwidthBurst);
+ ENSURE_BANDWIDTH_PARAM(PerConnBWRate);
+ ENSURE_BANDWIDTH_PARAM(PerConnBWBurst);
+ ENSURE_BANDWIDTH_PARAM(AuthDirFastGuarantee);
+ ENSURE_BANDWIDTH_PARAM(AuthDirGuardBWGuarantee);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "RelayBandwidthRate 1000\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_u64_op(tdata->opt->RelayBandwidthBurst, OP_EQ, 1000);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "RelayBandwidthBurst 1001\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_u64_op(tdata->opt->RelayBandwidthRate, OP_EQ, 1001);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "RelayBandwidthRate 1001\n"
+ "RelayBandwidthBurst 1000\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "RelayBandwidthBurst must be at least equal to "
+ "RelayBandwidthRate.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "BandwidthRate 1001\n"
+ "BandwidthBurst 1000\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "BandwidthBurst must be at least equal to BandwidthRate.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "RelayBandwidthRate 1001\n"
+ "BandwidthRate 1000\n"
+ "BandwidthBurst 1000\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_u64_op(tdata->opt->BandwidthRate, OP_EQ, 1001);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "RelayBandwidthRate 1001\n"
+ "BandwidthRate 1000\n"
+ "RelayBandwidthBurst 1001\n"
+ "BandwidthBurst 1000\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_u64_op(tdata->opt->BandwidthBurst, OP_EQ, 1001);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "BandwidthRate 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "BandwidthRate is set to 1 bytes/second. For servers,"
+ " it must be at least 76800.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "BandwidthRate 76800\n"
+ "MaxAdvertisedBandwidth 30000\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "MaxAdvertisedBandwidth is set to 30000 bytes/second."
+ " For servers, it must be at least 38400.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "BandwidthRate 76800\n"
+ "RelayBandwidthRate 1\n"
+ "MaxAdvertisedBandwidth 38400\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "RelayBandwidthRate is set to 1 bytes/second. For "
+ "servers, it must be at least 76800.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "BandwidthRate 76800\n"
+ "BandwidthBurst 76800\n"
+ "RelayBandwidthRate 76800\n"
+ "MaxAdvertisedBandwidth 38400\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__circuits(void *ignored)
+{
+ (void)ignored;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "MaxCircuitDirtiness 2592001\n");
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_msg("MaxCircuitDirtiness option is too "
+ "high; setting to 30 days.\n");
+ tt_int_op(tdata->opt->MaxCircuitDirtiness, OP_EQ, 2592000);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "CircuitStreamTimeout 1\n");
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_msg("CircuitStreamTimeout option is too"
+ " short; raising to 10 seconds.\n");
+ tt_int_op(tdata->opt->CircuitStreamTimeout, OP_EQ, 10);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "CircuitStreamTimeout 111\n");
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_no_log_msg("CircuitStreamTimeout option is too"
+ " short; raising to 10 seconds.\n");
+ tt_int_op(tdata->opt->CircuitStreamTimeout, OP_EQ, 111);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HeartbeatPeriod 1\n");
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_msg("HeartbeatPeriod option is too short;"
+ " raising to 1800 seconds.\n");
+ tt_int_op(tdata->opt->HeartbeatPeriod, OP_EQ, 1800);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HeartbeatPeriod 1982\n");
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_no_log_msg("HeartbeatPeriod option is too short;"
+ " raising to 1800 seconds.\n");
+ tt_int_op(tdata->opt->HeartbeatPeriod, OP_EQ, 1982);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "CircuitBuildTimeout 1\n"
+ );
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_log_msg("CircuitBuildTimeout is shorter (1"
+ " seconds) than the recommended minimum (10 seconds), and "
+ "LearnCircuitBuildTimeout is disabled. If tor isn't working, "
+ "raise this value or enable LearnCircuitBuildTimeout.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ mock_clean_saved_logs();
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "CircuitBuildTimeout 11\n"
+ );
+ options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ expect_no_log_msg("CircuitBuildTimeout is shorter (1 "
+ "seconds) than the recommended minimum (10 seconds), and "
+ "LearnCircuitBuildTimeout is disabled. If tor isn't working, "
+ "raise this value or enable LearnCircuitBuildTimeout.\n");
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__port_forwarding(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "PortForwarding 1\nSandbox 1\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "PortForwarding is not compatible with Sandbox;"
+ " at most one can be set");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "PortForwarding 1\nSandbox 0\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(!msg);
+ tor_free(msg);
+
+ done:
+ free_options_test_data(tdata);
+ memarea_clear_freelist();
+ policies_free_all();
+ tor_free(msg);
+}
+
+static void
+test_options_validate__tor2web(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Tor2webRendezvousPoints 1\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Tor2webRendezvousPoints cannot be set without Tor2webMode.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Tor2webRendezvousPoints 1\nTor2webMode 1\n");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__rend(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "UseEntryGuards 0\n"
+ "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n"
+ "HiddenServicePort 80 127.0.0.1:8080\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("UseEntryGuards is disabled, but you"
+ " have configured one or more hidden services on this Tor "
+ "instance. Your hidden services will be very easy to locate using"
+ " a well-known attack -- see http://freehaven.net/anonbib/#hs-"
+ "attack06 for details.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(
+ TEST_OPTIONS_DEFAULT_VALUES
+ "UseEntryGuards 1\n"
+ "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n"
+ "HiddenServicePort 80 127.0.0.1:8080\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg("UseEntryGuards is disabled, but you"
+ " have configured one or more hidden services on this Tor "
+ "instance. Your hidden services will be very easy to locate using"
+ " a well-known attack -- see http://freehaven.net/anonbib/#hs-"
+ "attack06 for details.\n");
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HiddenServicePort 80 127.0.0.1:8080\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Failed to configure rendezvous options. See logs for details.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HidServAuth failed\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Failed to configure client authorization for hidden "
+ "services. See logs for details.");
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__accounting(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AccountingRule something_bad\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "AccountingRule must be 'sum', 'max', 'in', or 'out'");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AccountingRule sum\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->AccountingRule, OP_EQ, ACCT_SUM);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AccountingRule max\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->AccountingRule, OP_EQ, ACCT_MAX);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AccountingStart fail\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Failed to parse accounting options. See logs for details.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AccountingMax 10\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(
+ TEST_OPTIONS_DEFAULT_VALUES
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "BandwidthRate 76800\n"
+ "BandwidthBurst 76800\n"
+ "MaxAdvertisedBandwidth 38400\n"
+ "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n"
+ "HiddenServicePort 80 127.0.0.1:8080\n"
+ "AccountingMax 10\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("Using accounting with a hidden "
+ "service and an ORPort is risky: your hidden service(s) and "
+ "your public address will all turn off at the same time, "
+ "which may alert observers that they are being run by the "
+ "same party.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(
+ TEST_OPTIONS_DEFAULT_VALUES
+ "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n"
+ "HiddenServicePort 80 127.0.0.1:8080\n"
+ "AccountingMax 10\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg("Using accounting with a hidden "
+ "service and an ORPort is risky: your hidden service(s) and "
+ "your public address will all turn off at the same time, "
+ "which may alert observers that they are being run by the "
+ "same party.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(
+ TEST_OPTIONS_DEFAULT_VALUES
+ "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n"
+ "HiddenServicePort 80 127.0.0.1:8080\n"
+ "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service2/\n"
+ "HiddenServicePort 81 127.0.0.1:8081\n"
+ "AccountingMax 10\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("Using accounting with multiple "
+ "hidden services is risky: they will all turn off at the same"
+ " time, which may alert observers that they are being run by "
+ "the same party.\n");
+ tor_free(msg);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__proxy(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ sandbox_disable_getaddrinfo_cache();
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpProxy 127.0.42.1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->HTTPProxyPort, OP_EQ, 80);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpProxy 127.0.42.1:444\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->HTTPProxyPort, OP_EQ, 444);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpProxy not_so_valid!\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "HTTPProxy failed to parse or resolve. Please fix.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpProxyAuthenticator "
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreonetwothreonetwothreonetwothre"
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreonetwothreonetwothreonetwothre"
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreonetwothreonetwothreonetwothre"
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreonetwothreonetwothreonetwothre"
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreonetwothreonetwothreonetwothre"
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreeonetwothreeonetwothree"
+
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "HTTPProxyAuthenticator is too long (>= 512 chars).");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpProxyAuthenticator validauth\n"
+
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpsProxy 127.0.42.1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->HTTPSProxyPort, OP_EQ, 443);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpsProxy 127.0.42.1:444\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->HTTPSProxyPort, OP_EQ, 444);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpsProxy not_so_valid!\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "HTTPSProxy failed to parse or resolve. Please fix.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpsProxyAuthenticator "
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreonetwothreonetwothreonetwothre"
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreonetwothreonetwothreonetwothre"
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreonetwothreonetwothreonetwothre"
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreonetwothreonetwothreonetwothre"
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreonetwothreonetwothreonetwothre"
+ "onetwothreonetwothreonetwothreonetwothreonetw"
+ "othreonetwothreeonetwothreeonetwothree"
+
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "HTTPSProxyAuthenticator is too long (>= 512 chars).");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpsProxyAuthenticator validauth\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks4Proxy 127.0.42.1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->Socks4ProxyPort, OP_EQ, 1080);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks4Proxy 127.0.42.1:444\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->Socks4ProxyPort, OP_EQ, 444);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks4Proxy not_so_valid!\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Socks4Proxy failed to parse or resolve. Please fix.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks5Proxy 127.0.42.1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->Socks5ProxyPort, OP_EQ, 1080);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks5Proxy 127.0.42.1:444\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->Socks5ProxyPort, OP_EQ, 444);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks5Proxy not_so_valid!\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Socks5Proxy failed to parse or resolve. Please fix.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks4Proxy 215.1.1.1\n"
+ "Socks5Proxy 215.1.1.2\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "You have configured more than one proxy type. "
+ "(Socks4Proxy|Socks5Proxy|HTTPSProxy)");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpProxy 215.1.1.1\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("HTTPProxy configured, but no SOCKS "
+ "proxy or HTTPS proxy configured. Watch out: this configuration "
+ "will proxy unencrypted directory connections only.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpProxy 215.1.1.1\n"
+ "Socks4Proxy 215.1.1.1\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg("HTTPProxy configured, but no SOCKS "
+ "proxy or HTTPS proxy configured. Watch out: this configuration "
+ "will proxy unencrypted directory connections only.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpProxy 215.1.1.1\n"
+ "Socks5Proxy 215.1.1.1\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg("HTTPProxy configured, but no SOCKS "
+ "proxy or HTTPS proxy configured. Watch out: this configuration "
+ "will proxy unencrypted directory connections only.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HttpProxy 215.1.1.1\n"
+ "HttpsProxy 215.1.1.1\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "HTTPProxy configured, but no SOCKS proxy or HTTPS proxy "
+ "configured. Watch out: this configuration will proxy "
+ "unencrypted directory connections only.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ );
+ tdata->opt->Socks5ProxyUsername = tor_strdup("");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Socks5ProxyUsername must be between 1 and 255 characters.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ );
+ tdata->opt->Socks5ProxyUsername =
+ tor_strdup("ABCDEABCDE0123456789ABCDEABCDE0123456789ABCDEABCDE0123456789AB"
+ "CDEABCDE0123456789ABCDEABCDE0123456789ABCDEABCDE0123456789ABCD"
+ "EABCDE0123456789ABCDEABCDE0123456789ABCDEABCDE0123456789ABCDEA"
+ "BCDE0123456789ABCDEABCDE0123456789ABCDEABCDE0123456789ABCDEABC"
+ "DE0123456789ABCDEABCDE0123456789ABCDEABCDE0123456789");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Socks5ProxyUsername must be between 1 and 255 characters.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks5ProxyUsername hello_world\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Socks5ProxyPassword must be included with "
+ "Socks5ProxyUsername.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks5ProxyUsername hello_world\n"
+ );
+ tdata->opt->Socks5ProxyPassword = tor_strdup("");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Socks5ProxyPassword must be between 1 and 255 characters.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks5ProxyUsername hello_world\n"
+ );
+ tdata->opt->Socks5ProxyPassword =
+ tor_strdup("ABCDEABCDE0123456789ABCDEABCDE0123456789ABCDEABCDE0123456789AB"
+ "CDEABCDE0123456789ABCDEABCDE0123456789ABCDEABCDE0123456789ABCD"
+ "EABCDE0123456789ABCDEABCDE0123456789ABCDEABCDE0123456789ABCDEA"
+ "BCDE0123456789ABCDEABCDE0123456789ABCDEABCDE0123456789ABCDEABC"
+ "DE0123456789ABCDEABCDE0123456789ABCDEABCDE0123456789");
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Socks5ProxyPassword must be between 1 and 255 characters.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks5ProxyUsername hello_world\n"
+ "Socks5ProxyPassword world_hello\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "Socks5ProxyPassword hello_world\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Socks5ProxyPassword must be included with "
+ "Socks5ProxyUsername.");
+ tor_free(msg);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ memarea_clear_freelist();
+ policies_free_all();
+ // sandbox_free_getaddrinfo_cache();
+ tor_free(msg);
+}
+
+static void
+test_options_validate__control(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HashedControlPassword something_incorrect\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Bad HashedControlPassword: wrong length or bad encoding");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HashedControlPassword 16:872860B76453A77D60CA"
+ "2BB8C1A7042072093276A3D701AD684053EC4C\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(
+ TEST_OPTIONS_DEFAULT_VALUES
+ "__HashedControlSessionPassword something_incorrect\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Bad HashedControlSessionPassword: wrong length or "
+ "bad encoding");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "__HashedControlSessionPassword 16:872860B7645"
+ "3A77D60CA2BB8C1A7042072093276A3D701AD684053EC"
+ "4C\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(
+ TEST_OPTIONS_DEFAULT_VALUES
+ "__OwningControllerProcess something_incorrect\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Bad OwningControllerProcess: invalid PID");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "__OwningControllerProcess 123\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ControlPort 127.0.0.1:1234\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg(
+ "ControlPort is open, but no authentication method has been "
+ "configured. This means that any program on your computer can "
+ "reconfigure your Tor. That's bad! You should upgrade your Tor"
+ " controller as soon as possible.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ControlPort 127.0.0.1:1234\n"
+ "HashedControlPassword 16:872860B76453A77D60CA"
+ "2BB8C1A7042072093276A3D701AD684053EC4C\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "ControlPort is open, but no authentication method has been "
+ "configured. This means that any program on your computer can "
+ "reconfigure your Tor. That's bad! You should upgrade your Tor "
+ "controller as soon as possible.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ControlPort 127.0.0.1:1234\n"
+ "__HashedControlSessionPassword 16:872860B7645"
+ "3A77D60CA2BB8C1A7042072093276A3D701AD684053EC"
+ "4C\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "ControlPort is open, but no authentication method has been "
+ "configured. This means that any program on your computer can "
+ "reconfigure your Tor. That's bad! You should upgrade your Tor "
+ "controller as soon as possible.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ControlPort 127.0.0.1:1234\n"
+ "CookieAuthentication 1\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "ControlPort is open, but no authentication method has been "
+ "configured. This means that any program on your computer can "
+ "reconfigure your Tor. That's bad! You should upgrade your Tor "
+ "controller as soon as possible.\n");
+ tor_free(msg);
+
+#ifdef HAVE_SYS_UN_H
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ControlSocket unix:/tmp WorldWritable\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg(
+ "ControlSocket is world writable, but no authentication method has"
+ " been configured. This means that any program on your computer "
+ "can reconfigure your Tor. That's bad! You should upgrade your "
+ "Tor controller as soon as possible.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ControlSocket unix:/tmp WorldWritable\n"
+ "HashedControlPassword 16:872860B76453A77D60CA"
+ "2BB8C1A7042072093276A3D701AD684053EC4C\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "ControlSocket is world writable, but no authentication method has"
+ " been configured. This means that any program on your computer "
+ "can reconfigure your Tor. That's bad! You should upgrade your "
+ "Tor controller as soon as possible.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ControlSocket unix:/tmp WorldWritable\n"
+ "__HashedControlSessionPassword 16:872860B7645"
+ "3A77D60CA2BB8C1A7042072093276A3D701AD684053EC"
+ "4C\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "ControlSocket is world writable, but no authentication method has"
+ " been configured. This means that any program on your computer "
+ "can reconfigure your Tor. That's bad! You should upgrade your "
+ "Tor controller as soon as possible.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ControlSocket unix:/tmp WorldWritable\n"
+ "CookieAuthentication 1\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "ControlSocket is world writable, but no authentication method has"
+ " been configured. This means that any program on your computer "
+ "can reconfigure your Tor. That's bad! You should upgrade your "
+ "Tor controller as soon as possible.\n");
+ tor_free(msg);
+#endif
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "CookieAuthFileGroupReadable 1\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg(
+ "CookieAuthFileGroupReadable is set, but will have no effect: you "
+ "must specify an explicit CookieAuthFile to have it "
+ "group-readable.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "CookieAuthFileGroupReadable 1\n"
+ "CookieAuthFile /tmp/somewhere\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "CookieAuthFileGroupReadable is set, but will have no effect: you "
+ "must specify an explicit CookieAuthFile to have it "
+ "group-readable.\n");
+ tor_free(msg);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__families(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "MyFamily home\n"
+ "BridgeRelay 1\n"
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "BandwidthRate 51300\n"
+ "BandwidthBurst 51300\n"
+ "MaxAdvertisedBandwidth 25700\n"
+ "DirCache 1\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg(
+ "Listing a family for a bridge relay is not supported: it can "
+ "reveal bridge fingerprints to censors. You should also make sure "
+ "you aren't listing this bridge's fingerprint in any other "
+ "MyFamily.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "MyFamily home\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "Listing a family for a bridge relay is not supported: it can "
+ "reveal bridge fingerprints to censors. You should also make sure "
+ "you aren't listing this bridge's fingerprint in any other "
+ "MyFamily.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "MyFamily !\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Invalid nickname '!' in MyFamily line");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "NodeFamily foo\n"
+ "NodeFamily !\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_assert(!msg);
+ tor_free(msg);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__addr_policies(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ExitPolicy !!!\n"
+ "ExitRelay 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Error in ExitPolicy entry.");
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__dir_auth(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ VALID_DIR_AUTH
+ VALID_ALT_DIR_AUTH
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Directory authority/fallback line did not parse. See logs for "
+ "details.");
+ expect_log_msg(
+ "You cannot set both DirAuthority and Alternate*Authority.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingTorNetwork 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "TestingTorNetwork may only be configured in combination with a "
+ "non-default set of DirAuthority or both of AlternateDirAuthority "
+ "and AlternateBridgeAuthority configured.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ VALID_DIR_AUTH
+ "TestingTorNetwork 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingTorNetwork 1\n"
+ VALID_ALT_DIR_AUTH
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "TestingTorNetwork may only be configured in combination with a "
+ "non-default set of DirAuthority or both of AlternateDirAuthority "
+ "and AlternateBridgeAuthority configured.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingTorNetwork 1\n"
+ VALID_ALT_BRIDGE_AUTH
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "TestingTorNetwork may only be configured in "
+ "combination with a non-default set of DirAuthority or both of "
+ "AlternateDirAuthority and AlternateBridgeAuthority configured.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ VALID_ALT_DIR_AUTH
+ VALID_ALT_BRIDGE_AUTH
+ "TestingTorNetwork 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__transport(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_NOTICE);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ClientTransportPlugin !!\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Invalid client transport line. See logs for details.");
+ expect_log_msg(
+ "Too few arguments on ClientTransportPlugin line.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ClientTransportPlugin foo exec bar\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ServerTransportPlugin !!\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Invalid server transport line. See logs for details.");
+ expect_log_msg(
+ "Too few arguments on ServerTransportPlugin line.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ServerTransportPlugin foo exec bar\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg(
+ "Tor is not configured as a relay but you specified a "
+ "ServerTransportPlugin line (\"foo exec bar\"). The "
+ "ServerTransportPlugin line will be ignored.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ServerTransportPlugin foo exec bar\n"
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "BandwidthRate 76900\n"
+ "BandwidthBurst 76900\n"
+ "MaxAdvertisedBandwidth 38500\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "Tor is not configured as a relay but you specified a "
+ "ServerTransportPlugin line (\"foo exec bar\"). The "
+ "ServerTransportPlugin line will be ignored.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ServerTransportListenAddr foo 127.0.0.42:55\n"
+ "ServerTransportListenAddr !\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "ServerTransportListenAddr did not parse. See logs for details.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ServerTransportListenAddr foo 127.0.0.42:55\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg(
+ "You need at least a single managed-proxy to specify a transport "
+ "listen address. The ServerTransportListenAddr line will be "
+ "ignored.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ServerTransportListenAddr foo 127.0.0.42:55\n"
+ "ServerTransportPlugin foo exec bar\n"
+ "ORListenAddress 127.0.0.1:5555\n"
+ "ORPort 955\n"
+ "BandwidthRate 76900\n"
+ "BandwidthBurst 76900\n"
+ "MaxAdvertisedBandwidth 38500\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "You need at least a single managed-proxy to specify a transport "
+ "listen address. The ServerTransportListenAddr line will be "
+ "ignored.\n");
+
+ done:
+ escaped(NULL); // This will free the leaking memory from the previous escaped
+ memarea_clear_freelist();
+ policies_free_all();
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__constrained_sockets(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ConstrainedSockets 1\n"
+ "ConstrainedSockSize 0\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "ConstrainedSockSize is invalid. Must be a value "
+ "between 2048 and 262144 in 1024 byte increments.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ConstrainedSockets 1\n"
+ "ConstrainedSockSize 263168\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "ConstrainedSockSize is invalid. Must be a value "
+ "between 2048 and 262144 in 1024 byte increments.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ConstrainedSockets 1\n"
+ "ConstrainedSockSize 2047\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "ConstrainedSockSize is invalid. Must be a value "
+ "between 2048 and 262144 in 1024 byte increments.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ConstrainedSockets 1\n"
+ "ConstrainedSockSize 2048\n"
+ "DirPort 999\n"
+ "DirCache 1\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("You have requested constrained "
+ "socket buffers while also serving directory entries via DirPort."
+ " It is strongly suggested that you disable serving directory"
+ " requests when system TCP buffer resources are scarce.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "ConstrainedSockets 1\n"
+ "ConstrainedSockSize 2048\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg(
+ "You have requested constrained socket buffers while also serving"
+ " directory entries via DirPort. It is strongly suggested that "
+ "you disable serving directory requests when system TCP buffer "
+ "resources are scarce.\n");
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__v3_auth(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthVoteDelay 1000\n"
+ "V3AuthDistDelay 1000\n"
+ "V3AuthVotingInterval 1000\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "V3AuthVoteDelay plus V3AuthDistDelay must be less than half "
+ "V3AuthVotingInterval");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthVoteDelay 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "V3AuthVoteDelay is way too low.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthVoteDelay 1\n"
+ "TestingTorNetwork 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "V3AuthVoteDelay is way too low.");
+ tor_free(msg);
+
+ // TODO: we can't reach the case of v3authvotedelay lower
+ // than MIN_VOTE_SECONDS but not lower than MIN_VOTE_SECONDS_TESTING,
+ // since they are the same
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthDistDelay 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "V3AuthDistDelay is way too low.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthDistDelay 1\n"
+ "TestingTorNetwork 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "V3AuthDistDelay is way too low.");
+ tor_free(msg);
+
+ // TODO: we can't reach the case of v3authdistdelay lower than
+ // MIN_DIST_SECONDS but not lower than MIN_DIST_SECONDS_TESTING,
+ // since they are the same
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthNIntervalsValid 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "V3AuthNIntervalsValid must be at least 2.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthVoteDelay 49\n"
+ "V3AuthDistDelay 49\n"
+ "V3AuthVotingInterval 200\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "V3AuthVotingInterval is insanely low.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthVoteDelay 49\n"
+ "V3AuthDistDelay 49\n"
+ "V3AuthVotingInterval 200000\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "V3AuthVotingInterval is insanely high.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthVoteDelay 49\n"
+ "V3AuthDistDelay 49\n"
+ "V3AuthVotingInterval 1441\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("V3AuthVotingInterval does not divide"
+ " evenly into 24 hours.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthVoteDelay 49\n"
+ "V3AuthDistDelay 49\n"
+ "V3AuthVotingInterval 1440\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg("V3AuthVotingInterval does not divide"
+ " evenly into 24 hours.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "V3AuthVoteDelay 49\n"
+ "V3AuthDistDelay 49\n"
+ "V3AuthVotingInterval 299\n"
+ VALID_DIR_AUTH
+ "TestingTorNetwork 1\n"
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("V3AuthVotingInterval is very low. "
+ "This may lead to failure to synchronise for a consensus.\n");
+ tor_free(msg);
+
+ // TODO: It is impossible to reach the case of testingtor network, with
+ // v3authvotinginterval too low
+ /* free_options_test_data(tdata); */
+ /* tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES */
+ /* "V3AuthVoteDelay 1\n" */
+ /* "V3AuthDistDelay 1\n" */
+ /* "V3AuthVotingInterval 9\n" */
+ /* VALID_DIR_AUTH */
+ /* "TestingTorNetwork 1\n" */
+ /* ); */
+ /* ret = options_validate(tdata->old_opt, tdata->opt, */
+ /* tdata->def_opt, 0, &msg); */
+ /* tt_int_op(ret, OP_EQ, -1); */
+ /* tt_str_op(msg, OP_EQ, "V3AuthVotingInterval is insanely low."); */
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingV3AuthInitialVoteDelay 1\n"
+ VALID_DIR_AUTH
+ "TestingTorNetwork 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "TestingV3AuthInitialVoteDelay is way too low.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingV3AuthInitialDistDelay 1\n"
+ VALID_DIR_AUTH
+ "TestingTorNetwork 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "TestingV3AuthInitialDistDelay is way too low.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ VALID_DIR_AUTH
+ "TestingTorNetwork 1\n"
+ );
+ tdata->opt->TestingV3AuthVotingStartOffset = 100000;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "TestingV3AuthVotingStartOffset is higher than the "
+ "voting interval.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ VALID_DIR_AUTH
+ "TestingTorNetwork 1\n"
+ );
+ tdata->opt->TestingV3AuthVotingStartOffset = -1;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "TestingV3AuthVotingStartOffset must be non-negative.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ VALID_DIR_AUTH
+ "TestingTorNetwork 1\n"
+ "TestingV3AuthInitialVotingInterval 4\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "TestingV3AuthInitialVotingInterval is insanely low.");
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__virtual_addr(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "VirtualAddrNetworkIPv4 !!"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Error parsing VirtualAddressNetwork !!");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "VirtualAddrNetworkIPv6 !!"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Error parsing VirtualAddressNetworkIPv6 !!");
+ tor_free(msg);
+
+ done:
+ escaped(NULL); // This will free the leaking memory from the previous escaped
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__exits(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AllowSingleHopExits 1"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg("You have set AllowSingleHopExits; "
+ "now your relay will allow others to make one-hop exits. However,"
+ " since by default most clients avoid relays that set this option,"
+ " most clients will ignore you.\n");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AllowSingleHopExits 1\n"
+ VALID_DIR_AUTH
+ );
+ mock_clean_saved_logs();
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_msg("You have set AllowSingleHopExits; "
+ "now your relay will allow others to make one-hop exits. However,"
+ " since by default most clients avoid relays that set this option,"
+ " most clients will ignore you.\n");
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__testing_options(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+#define TEST_TESTING_OPTION(name, low_val, high_val, err_low) \
+ STMT_BEGIN \
+ free_options_test_data(tdata); \
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES \
+ VALID_DIR_AUTH \
+ "TestingTorNetwork 1\n" \
+ ); \
+ tdata->opt-> name = low_val; \
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);\
+ tt_int_op(ret, OP_EQ, -1); \
+ tt_str_op(msg, OP_EQ, #name " " err_low); \
+ tor_free(msg); \
+ \
+ free_options_test_data(tdata); \
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES \
+ VALID_DIR_AUTH \
+ "TestingTorNetwork 1\n" \
+ ); \
+ tdata->opt-> name = high_val; \
+ mock_clean_saved_logs(); \
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);\
+ tt_int_op(ret, OP_EQ, 0); \
+ expect_log_msg( #name " is insanely high.\n"); \
+ tor_free(msg); \
+ STMT_END
+
+ TEST_TESTING_OPTION(TestingAuthDirTimeToLearnReachability, -1, 8000,
+ "must be non-negative.");
+ TEST_TESTING_OPTION(TestingEstimatedDescriptorPropagationTime, -1, 3601,
+ "must be non-negative.");
+ TEST_TESTING_OPTION(TestingClientMaxIntervalWithoutRequest, -1, 3601,
+ "is way too low.");
+ TEST_TESTING_OPTION(TestingDirConnectionMaxStall, 1, 3601,
+ "is way too low.");
+ // TODO: I think this points to a bug/regression in options_validate
+ TEST_TESTING_OPTION(TestingConsensusMaxDownloadTries, 1, 801,
+ "must be greater than 2.");
+ TEST_TESTING_OPTION(TestingDescriptorMaxDownloadTries, 1, 801,
+ "must be greater than 1.");
+ TEST_TESTING_OPTION(TestingMicrodescMaxDownloadTries, 1, 801,
+ "must be greater than 1.");
+ TEST_TESTING_OPTION(TestingCertMaxDownloadTries, 1, 801,
+ "must be greater than 1.");
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingEnableConnBwEvent 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "TestingEnableConnBwEvent may only be changed in "
+ "testing Tor networks!");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingEnableConnBwEvent 1\n"
+ VALID_DIR_AUTH
+ "TestingTorNetwork 1\n"
+ "___UsingTestNetworkDefaults 0\n"
+ );
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(!msg);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingEnableConnBwEvent 1\n"
+ VALID_DIR_AUTH
+ "TestingTorNetwork 0\n"
+ "___UsingTestNetworkDefaults 1\n"
+ );
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(!msg);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingEnableCellStatsEvent 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "TestingEnableCellStatsEvent may only be changed in "
+ "testing Tor networks!");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingEnableCellStatsEvent 1\n"
+ VALID_DIR_AUTH
+ "TestingTorNetwork 1\n"
+ "___UsingTestNetworkDefaults 0\n"
+ );
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(!msg);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingEnableCellStatsEvent 1\n"
+ VALID_DIR_AUTH
+ "TestingTorNetwork 0\n"
+ "___UsingTestNetworkDefaults 1\n"
+ );
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(!msg);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingEnableTbEmptyEvent 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "TestingEnableTbEmptyEvent may only be changed "
+ "in testing Tor networks!");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingEnableTbEmptyEvent 1\n"
+ VALID_DIR_AUTH
+ "TestingTorNetwork 1\n"
+ "___UsingTestNetworkDefaults 0\n"
+ );
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(!msg);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "TestingEnableTbEmptyEvent 1\n"
+ VALID_DIR_AUTH
+ "TestingTorNetwork 0\n"
+ "___UsingTestNetworkDefaults 1\n"
+ );
+
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(!msg);
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__accel(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AccelName foo\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->HardwareAccel, OP_EQ, 1);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AccelName foo\n"
+ );
+ tdata->opt->HardwareAccel = 2;
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tdata->opt->HardwareAccel, OP_EQ, 2);
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AccelDir 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ,
+ "Can't use hardware crypto accelerator dir without engine name.");
+ tor_free(msg);
+
+ free_options_test_data(tdata);
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "AccelDir 1\n"
+ "AccelName something\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(msg);
+
+ done:
+ memarea_clear_freelist();
+ policies_free_all();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+#define LOCAL_VALIDATE_TEST(name) \
+ { "validate__" #name, test_options_validate__ ## name, TT_FORK, NULL, NULL }
+
struct testcase_t options_tests[] = {
{ "validate", test_options_validate, TT_FORK, NULL, NULL },
- END_OF_TESTCASES
+ { "mem_dircache", test_have_enough_mem_for_dircache, TT_FORK, NULL, NULL },
+ LOCAL_VALIDATE_TEST(uname_for_server),
+ LOCAL_VALIDATE_TEST(outbound_addresses),
+ LOCAL_VALIDATE_TEST(data_directory),
+ LOCAL_VALIDATE_TEST(nickname),
+ LOCAL_VALIDATE_TEST(contactinfo),
+ LOCAL_VALIDATE_TEST(logs),
+ LOCAL_VALIDATE_TEST(authdir),
+ LOCAL_VALIDATE_TEST(relay_with_hidden_services),
+ LOCAL_VALIDATE_TEST(transproxy),
+ LOCAL_VALIDATE_TEST(exclude_nodes),
+ LOCAL_VALIDATE_TEST(scheduler),
+ LOCAL_VALIDATE_TEST(node_families),
+ LOCAL_VALIDATE_TEST(tlsec),
+ LOCAL_VALIDATE_TEST(token_bucket),
+ LOCAL_VALIDATE_TEST(recommended_packages),
+ LOCAL_VALIDATE_TEST(fetch_dir),
+ LOCAL_VALIDATE_TEST(conn_limit),
+ LOCAL_VALIDATE_TEST(paths_needed),
+ LOCAL_VALIDATE_TEST(max_client_circuits),
+ LOCAL_VALIDATE_TEST(ports),
+ LOCAL_VALIDATE_TEST(reachable_addresses),
+ LOCAL_VALIDATE_TEST(use_bridges),
+ LOCAL_VALIDATE_TEST(entry_nodes),
+ LOCAL_VALIDATE_TEST(invalid_nodes),
+ LOCAL_VALIDATE_TEST(safe_logging),
+ LOCAL_VALIDATE_TEST(publish_server_descriptor),
+ LOCAL_VALIDATE_TEST(testing),
+ LOCAL_VALIDATE_TEST(hidserv),
+ LOCAL_VALIDATE_TEST(predicted_ports),
+ LOCAL_VALIDATE_TEST(path_bias),
+ LOCAL_VALIDATE_TEST(bandwidth),
+ LOCAL_VALIDATE_TEST(circuits),
+ LOCAL_VALIDATE_TEST(port_forwarding),
+ LOCAL_VALIDATE_TEST(tor2web),
+ LOCAL_VALIDATE_TEST(rend),
+ LOCAL_VALIDATE_TEST(accounting),
+ LOCAL_VALIDATE_TEST(proxy),
+ LOCAL_VALIDATE_TEST(control),
+ LOCAL_VALIDATE_TEST(families),
+ LOCAL_VALIDATE_TEST(addr_policies),
+ LOCAL_VALIDATE_TEST(dir_auth),
+ LOCAL_VALIDATE_TEST(transport),
+ LOCAL_VALIDATE_TEST(constrained_sockets),
+ LOCAL_VALIDATE_TEST(v3_auth),
+ LOCAL_VALIDATE_TEST(virtual_addr),
+ LOCAL_VALIDATE_TEST(exits),
+ LOCAL_VALIDATE_TEST(testing_options),
+ LOCAL_VALIDATE_TEST(accel),
+ END_OF_TESTCASES /* */
};
diff --git a/src/test/test_policy.c b/src/test/test_policy.c
index 37c36fed99..84a7081588 100644
--- a/src/test/test_policy.c
+++ b/src/test/test_policy.c
@@ -2,8 +2,11 @@
/* See LICENSE for licensing information */
#include "or.h"
+#define CONFIG_PRIVATE
+#include "config.h"
#include "router.h"
#include "routerparse.h"
+#define POLICIES_PRIVATE
#include "policies.h"
#include "test.h"
@@ -49,7 +52,7 @@ test_policy_summary_helper(const char *policy_str,
r = policies_parse_exit_policy(&line, &policy,
EXIT_POLICY_IPV6_ENABLED |
- EXIT_POLICY_ADD_DEFAULT, 0, NULL, 0);
+ EXIT_POLICY_ADD_DEFAULT, NULL);
tt_int_op(r,OP_EQ, 0);
summary = policy_summarize(policy, AF_INET);
@@ -80,7 +83,8 @@ test_policies_general(void *arg)
*policy7 = NULL, *policy8 = NULL, *policy9 = NULL,
*policy10 = NULL, *policy11 = NULL, *policy12 = NULL;
addr_policy_t *p;
- tor_addr_t tar;
+ tor_addr_t tar, tar2;
+ smartlist_t *addr_list = NULL;
config_line_t line;
smartlist_t *sm = NULL;
char *policy_str = NULL;
@@ -115,17 +119,22 @@ test_policies_general(void *arg)
tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy2,
EXIT_POLICY_IPV6_ENABLED |
EXIT_POLICY_REJECT_PRIVATE |
- EXIT_POLICY_ADD_DEFAULT, 0,
- NULL, 0));
+ EXIT_POLICY_ADD_DEFAULT, NULL));
tt_assert(policy2);
- tor_addr_parse(&tar, "[2000::1234]");
+ tor_addr_from_ipv4h(&tar, 0x0306090cu);
+ tor_addr_parse(&tar2, "[2000::1234]");
+ addr_list = smartlist_new();
+ smartlist_add(addr_list, &tar);
+ smartlist_add(addr_list, &tar2);
tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy12,
EXIT_POLICY_IPV6_ENABLED |
EXIT_POLICY_REJECT_PRIVATE |
EXIT_POLICY_ADD_DEFAULT,
- 0x0306090cu, &tar, 1));
+ addr_list));
+ smartlist_free(addr_list);
+ addr_list = NULL;
tt_assert(policy12);
@@ -206,15 +215,15 @@ test_policies_general(void *arg)
tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy8,
EXIT_POLICY_IPV6_ENABLED |
EXIT_POLICY_REJECT_PRIVATE |
- EXIT_POLICY_ADD_DEFAULT, 0,
- NULL, 0));
+ EXIT_POLICY_ADD_DEFAULT,
+ NULL));
tt_assert(policy8);
tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy9,
EXIT_POLICY_REJECT_PRIVATE |
- EXIT_POLICY_ADD_DEFAULT, 0,
- NULL, 0));
+ EXIT_POLICY_ADD_DEFAULT,
+ NULL));
tt_assert(policy9);
@@ -261,6 +270,93 @@ test_policies_general(void *arg)
addr_policy_list_free(policy);
policy = NULL;
+ /* make sure assume_action works */
+ malformed_list = 0;
+ p = router_parse_addr_policy_item_from_string("127.0.0.1",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(p);
+ addr_policy_free(p);
+ tt_assert(!malformed_list);
+
+ p = router_parse_addr_policy_item_from_string("127.0.0.1:*",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(p);
+ addr_policy_free(p);
+ tt_assert(!malformed_list);
+
+ p = router_parse_addr_policy_item_from_string("[::]",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(p);
+ addr_policy_free(p);
+ tt_assert(!malformed_list);
+
+ p = router_parse_addr_policy_item_from_string("[::]:*",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(p);
+ addr_policy_free(p);
+ tt_assert(!malformed_list);
+
+ p = router_parse_addr_policy_item_from_string("[face::b]",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(p);
+ addr_policy_free(p);
+ tt_assert(!malformed_list);
+
+ p = router_parse_addr_policy_item_from_string("[b::aaaa]",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(p);
+ addr_policy_free(p);
+ tt_assert(!malformed_list);
+
+ p = router_parse_addr_policy_item_from_string("*",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(p);
+ addr_policy_free(p);
+ tt_assert(!malformed_list);
+
+ p = router_parse_addr_policy_item_from_string("*4",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(p);
+ addr_policy_free(p);
+ tt_assert(!malformed_list);
+
+ p = router_parse_addr_policy_item_from_string("*6",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(p);
+ addr_policy_free(p);
+ tt_assert(!malformed_list);
+
+ /* These are all ambiguous IPv6 addresses, it's good that we reject them */
+ p = router_parse_addr_policy_item_from_string("acce::abcd",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(!p);
+ tt_assert(malformed_list);
+ malformed_list = 0;
+
+ p = router_parse_addr_policy_item_from_string("7:1234",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(!p);
+ tt_assert(malformed_list);
+ malformed_list = 0;
+
+ p = router_parse_addr_policy_item_from_string("::",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(!p);
+ tt_assert(malformed_list);
+ malformed_list = 0;
+
/* make sure compacting logic works. */
policy = NULL;
line.key = (char*)"foo";
@@ -268,8 +364,7 @@ test_policies_general(void *arg)
line.next = NULL;
tt_int_op(0, OP_EQ, policies_parse_exit_policy(&line,&policy,
EXIT_POLICY_IPV6_ENABLED |
- EXIT_POLICY_ADD_DEFAULT, 0,
- NULL, 0));
+ EXIT_POLICY_ADD_DEFAULT, NULL));
tt_assert(policy);
//test_streq(policy->string, "accept *:80");
@@ -489,6 +584,324 @@ test_policies_general(void *arg)
short_policy_free(short_parsed);
}
+/** Helper: Check that policy_list contains address */
+static int
+test_policy_has_address_helper(const smartlist_t *policy_list,
+ const tor_addr_t *addr)
+{
+ int found = 0;
+
+ tt_assert(policy_list);
+ tt_assert(addr);
+
+ SMARTLIST_FOREACH_BEGIN(policy_list, addr_policy_t*, p) {
+ if (tor_addr_eq(&p->addr, addr)) {
+ found = 1;
+ }
+ } SMARTLIST_FOREACH_END(p);
+
+ return found;
+
+ done:
+ return 0;
+}
+
+#define TEST_IPV4_ADDR (0x01020304)
+#define TEST_IPV6_ADDR ("2002::abcd")
+
+/** Run unit tests for rejecting the configured addresses on this exit relay
+ * using policies_parse_exit_policy_reject_private */
+static void
+test_policies_reject_exit_address(void *arg)
+{
+ smartlist_t *policy = NULL;
+ tor_addr_t ipv4_addr, ipv6_addr;
+ smartlist_t *ipv4_list, *ipv6_list, *both_list, *dupl_list;
+ (void)arg;
+
+ tor_addr_from_ipv4h(&ipv4_addr, TEST_IPV4_ADDR);
+ tor_addr_parse(&ipv6_addr, TEST_IPV6_ADDR);
+
+ ipv4_list = smartlist_new();
+ ipv6_list = smartlist_new();
+ both_list = smartlist_new();
+ dupl_list = smartlist_new();
+
+ smartlist_add(ipv4_list, &ipv4_addr);
+ smartlist_add(both_list, &ipv4_addr);
+ smartlist_add(dupl_list, &ipv4_addr);
+ smartlist_add(dupl_list, &ipv4_addr);
+ smartlist_add(dupl_list, &ipv4_addr);
+
+ smartlist_add(ipv6_list, &ipv6_addr);
+ smartlist_add(both_list, &ipv6_addr);
+ smartlist_add(dupl_list, &ipv6_addr);
+ smartlist_add(dupl_list, &ipv6_addr);
+
+ /* IPv4-Only Exits */
+
+ /* test that IPv4 addresses are rejected on an IPv4-only exit */
+ policies_parse_exit_policy_reject_private(&policy, 0, ipv4_list, 0, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 1);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* test that IPv6 addresses are NOT rejected on an IPv4-only exit
+ * (all IPv6 addresses are rejected by policies_parse_exit_policy_internal
+ * on IPv4-only exits, so policies_parse_exit_policy_reject_private doesn't
+ * need to do anything) */
+ policies_parse_exit_policy_reject_private(&policy, 0, ipv6_list, 0, 0);
+ tt_assert(policy == NULL);
+
+ /* test that only IPv4 addresses are rejected on an IPv4-only exit */
+ policies_parse_exit_policy_reject_private(&policy, 0, both_list, 0, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 1);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* Test that lists with duplicate entries produce the same results */
+ policies_parse_exit_policy_reject_private(&policy, 0, dupl_list, 0, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 1);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* IPv4/IPv6 Exits */
+
+ /* test that IPv4 addresses are rejected on an IPv4/IPv6 exit */
+ policies_parse_exit_policy_reject_private(&policy, 1, ipv4_list, 0, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 1);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* test that IPv6 addresses are rejected on an IPv4/IPv6 exit */
+ policies_parse_exit_policy_reject_private(&policy, 1, ipv6_list, 0, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 1);
+ tt_assert(test_policy_has_address_helper(policy, &ipv6_addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* test that IPv4 and IPv6 addresses are rejected on an IPv4/IPv6 exit */
+ policies_parse_exit_policy_reject_private(&policy, 1, both_list, 0, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 2);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
+ tt_assert(test_policy_has_address_helper(policy, &ipv6_addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* Test that lists with duplicate entries produce the same results */
+ policies_parse_exit_policy_reject_private(&policy, 1, dupl_list, 0, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 2);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
+ tt_assert(test_policy_has_address_helper(policy, &ipv6_addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ done:
+ addr_policy_list_free(policy);
+ smartlist_free(ipv4_list);
+ smartlist_free(ipv6_list);
+ smartlist_free(both_list);
+ smartlist_free(dupl_list);
+}
+
+static smartlist_t *test_configured_ports = NULL;
+const smartlist_t *mock_get_configured_ports(void);
+
+/** Returns test_configured_ports */
+const smartlist_t *
+mock_get_configured_ports(void)
+{
+ return test_configured_ports;
+}
+
+/** Run unit tests for rejecting publicly routable configured port addresses
+ * on this exit relay using policies_parse_exit_policy_reject_private */
+static void
+test_policies_reject_port_address(void *arg)
+{
+ smartlist_t *policy = NULL;
+ port_cfg_t *ipv4_port = NULL;
+ port_cfg_t *ipv6_port = NULL;
+ (void)arg;
+
+ test_configured_ports = smartlist_new();
+
+ ipv4_port = port_cfg_new(0);
+ tor_addr_from_ipv4h(&ipv4_port->addr, TEST_IPV4_ADDR);
+ smartlist_add(test_configured_ports, ipv4_port);
+
+ ipv6_port = port_cfg_new(0);
+ tor_addr_parse(&ipv6_port->addr, TEST_IPV6_ADDR);
+ smartlist_add(test_configured_ports, ipv6_port);
+
+ MOCK(get_configured_ports, mock_get_configured_ports);
+
+ /* test that an IPv4 port is rejected on an IPv4-only exit, but an IPv6 port
+ * is NOT rejected (all IPv6 addresses are rejected by
+ * policies_parse_exit_policy_internal on IPv4-only exits, so
+ * policies_parse_exit_policy_reject_private doesn't need to do anything
+ * with IPv6 addresses on IPv4-only exits) */
+ policies_parse_exit_policy_reject_private(&policy, 0, NULL, 0, 1);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 1);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_port->addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* test that IPv4 and IPv6 ports are rejected on an IPv4/IPv6 exit */
+ policies_parse_exit_policy_reject_private(&policy, 1, NULL, 0, 1);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == 2);
+ tt_assert(test_policy_has_address_helper(policy, &ipv4_port->addr));
+ tt_assert(test_policy_has_address_helper(policy, &ipv6_port->addr));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ done:
+ addr_policy_list_free(policy);
+ if (test_configured_ports) {
+ SMARTLIST_FOREACH(test_configured_ports,
+ port_cfg_t *, p, port_cfg_free(p));
+ smartlist_free(test_configured_ports);
+ test_configured_ports = NULL;
+ }
+ UNMOCK(get_configured_ports);
+}
+
+smartlist_t *mock_ipv4_addrs = NULL;
+smartlist_t *mock_ipv6_addrs = NULL;
+
+/* mock get_interface_address6_list, returning a deep copy of the template
+ * address list ipv4_interface_address_list or ipv6_interface_address_list */
+static smartlist_t *
+mock_get_interface_address6_list(int severity,
+ sa_family_t family,
+ int include_internal)
+{
+ (void)severity;
+ (void)include_internal;
+ smartlist_t *clone_list = smartlist_new();
+ smartlist_t *template_list = NULL;
+
+ if (family == AF_INET) {
+ template_list = mock_ipv4_addrs;
+ } else if (family == AF_INET6) {
+ template_list = mock_ipv6_addrs;
+ } else {
+ return NULL;
+ }
+
+ tt_assert(template_list);
+
+ SMARTLIST_FOREACH_BEGIN(template_list, tor_addr_t *, src_addr) {
+ tor_addr_t *dest_addr = malloc(sizeof(tor_addr_t));
+ memset(dest_addr, 0, sizeof(*dest_addr));
+ tor_addr_copy_tight(dest_addr, src_addr);
+ smartlist_add(clone_list, dest_addr);
+ } SMARTLIST_FOREACH_END(src_addr);
+
+ return clone_list;
+
+ done:
+ free_interface_address6_list(clone_list);
+ return NULL;
+}
+
+/** Run unit tests for rejecting publicly routable interface addresses on this
+ * exit relay using policies_parse_exit_policy_reject_private */
+static void
+test_policies_reject_interface_address(void *arg)
+{
+ smartlist_t *policy = NULL;
+ smartlist_t *public_ipv4_addrs =
+ get_interface_address6_list(LOG_INFO, AF_INET, 0);
+ smartlist_t *public_ipv6_addrs =
+ get_interface_address6_list(LOG_INFO, AF_INET6, 0);
+ tor_addr_t ipv4_addr, ipv6_addr;
+ (void)arg;
+
+ /* test that no addresses are rejected when none are supplied/requested */
+ policies_parse_exit_policy_reject_private(&policy, 0, NULL, 0, 0);
+ tt_assert(policy == NULL);
+
+ /* test that only IPv4 interface addresses are rejected on an IPv4-only exit
+ * (and allow for duplicates)
+ */
+ policies_parse_exit_policy_reject_private(&policy, 0, NULL, 1, 0);
+ if (policy) {
+ tt_assert(smartlist_len(policy) <= smartlist_len(public_ipv4_addrs));
+ addr_policy_list_free(policy);
+ policy = NULL;
+ }
+
+ /* test that IPv4 and IPv6 interface addresses are rejected on an IPv4/IPv6
+ * exit (and allow for duplicates) */
+ policies_parse_exit_policy_reject_private(&policy, 1, NULL, 1, 0);
+ if (policy) {
+ tt_assert(smartlist_len(policy) <= (smartlist_len(public_ipv4_addrs)
+ + smartlist_len(public_ipv6_addrs)));
+ addr_policy_list_free(policy);
+ policy = NULL;
+ }
+
+ /* Now do it all again, but mocked */
+ tor_addr_from_ipv4h(&ipv4_addr, TEST_IPV4_ADDR);
+ mock_ipv4_addrs = smartlist_new();
+ smartlist_add(mock_ipv4_addrs, (void *)&ipv4_addr);
+
+ tor_addr_parse(&ipv6_addr, TEST_IPV6_ADDR);
+ mock_ipv6_addrs = smartlist_new();
+ smartlist_add(mock_ipv6_addrs, (void *)&ipv6_addr);
+
+ MOCK(get_interface_address6_list, mock_get_interface_address6_list);
+
+ /* test that no addresses are rejected when none are supplied/requested */
+ policies_parse_exit_policy_reject_private(&policy, 0, NULL, 0, 0);
+ tt_assert(policy == NULL);
+
+ /* test that only IPv4 interface addresses are rejected on an IPv4-only exit
+ */
+ policies_parse_exit_policy_reject_private(&policy, 0, NULL, 1, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == smartlist_len(mock_ipv4_addrs));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ /* test that IPv4 and IPv6 interface addresses are rejected on an IPv4/IPv6
+ * exit */
+ policies_parse_exit_policy_reject_private(&policy, 1, NULL, 1, 0);
+ tt_assert(policy);
+ tt_assert(smartlist_len(policy) == (smartlist_len(mock_ipv4_addrs)
+ + smartlist_len(mock_ipv6_addrs)));
+ addr_policy_list_free(policy);
+ policy = NULL;
+
+ done:
+ addr_policy_list_free(policy);
+ free_interface_address6_list(public_ipv4_addrs);
+ free_interface_address6_list(public_ipv6_addrs);
+
+ UNMOCK(get_interface_address6_list);
+ /* we don't use free_interface_address6_list on these lists because their
+ * address pointers are stack-based */
+ smartlist_free(mock_ipv4_addrs);
+ smartlist_free(mock_ipv6_addrs);
+}
+
+#undef TEST_IPV4_ADDR
+#undef TEST_IPV6_ADDR
+
static void
test_dump_exit_policy_to_string(void *arg)
{
@@ -578,10 +991,861 @@ test_dump_exit_policy_to_string(void *arg)
tor_free(ep);
}
+static routerinfo_t *mock_desc_routerinfo = NULL;
+static const routerinfo_t *
+mock_router_get_my_routerinfo(void)
+{
+ return mock_desc_routerinfo;
+}
+
+#define DEFAULT_POLICY_STRING "reject *:*"
+#define TEST_IPV4_ADDR (0x02040608)
+#define TEST_IPV6_ADDR ("2003::ef01")
+
+static or_options_t mock_options;
+
+static const or_options_t *
+mock_get_options(void)
+{
+ return &mock_options;
+}
+
+/** Run unit tests for generating summary lines of exit policies */
+static void
+test_policies_getinfo_helper_policies(void *arg)
+{
+ (void)arg;
+ int rv = 0;
+ size_t ipv4_len = 0, ipv6_len = 0;
+ char *answer = NULL;
+ const char *errmsg = NULL;
+ routerinfo_t mock_my_routerinfo;
+
+ memset(&mock_my_routerinfo, 0, sizeof(mock_my_routerinfo));
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/default", &answer, &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ tt_assert(strlen(answer) > 0);
+ tor_free(answer);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/default",
+ &answer, &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ tt_assert(strlen(answer) > 0);
+ tor_free(answer);
+
+ memset(&mock_my_routerinfo, 0, sizeof(routerinfo_t));
+ MOCK(router_get_my_routerinfo, mock_router_get_my_routerinfo);
+ mock_my_routerinfo.exit_policy = smartlist_new();
+ mock_desc_routerinfo = &mock_my_routerinfo;
+
+ memset(&mock_options, 0, sizeof(or_options_t));
+ MOCK(get_options, mock_get_options);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay",
+ &answer, &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ tt_assert(strlen(answer) == 0);
+ tor_free(answer);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/ipv4", &answer,
+ &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ ipv4_len = strlen(answer);
+ tt_assert(ipv4_len == 0 || ipv4_len == strlen(DEFAULT_POLICY_STRING));
+ tt_assert(ipv4_len == 0 || !strcasecmp(answer, DEFAULT_POLICY_STRING));
+ tor_free(answer);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/ipv6", &answer,
+ &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ ipv6_len = strlen(answer);
+ tt_assert(ipv6_len == 0 || ipv6_len == strlen(DEFAULT_POLICY_STRING));
+ tt_assert(ipv6_len == 0 || !strcasecmp(answer, DEFAULT_POLICY_STRING));
+ tor_free(answer);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/full", &answer,
+ &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ /* It's either empty or it's the default */
+ tt_assert(strlen(answer) == 0 || !strcasecmp(answer, DEFAULT_POLICY_STRING));
+ tor_free(answer);
+
+ mock_my_routerinfo.addr = TEST_IPV4_ADDR;
+ tor_addr_parse(&mock_my_routerinfo.ipv6_addr, TEST_IPV6_ADDR);
+ append_exit_policy_string(&mock_my_routerinfo.exit_policy, "accept *4:*");
+ append_exit_policy_string(&mock_my_routerinfo.exit_policy, "reject *6:*");
+
+ mock_options.IPv6Exit = 1;
+ mock_options.ExitPolicyRejectPrivate = 1;
+ tor_addr_from_ipv4h(&mock_options.OutboundBindAddressIPv4_, TEST_IPV4_ADDR);
+ tor_addr_parse(&mock_options.OutboundBindAddressIPv6_, TEST_IPV6_ADDR);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay",
+ &answer, &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ tt_assert(strlen(answer) > 0);
+ tor_free(answer);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/ipv4", &answer,
+ &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ ipv4_len = strlen(answer);
+ tt_assert(ipv4_len > 0);
+ tor_free(answer);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/ipv6", &answer,
+ &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ ipv6_len = strlen(answer);
+ tt_assert(ipv6_len > 0);
+ tor_free(answer);
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/full", &answer,
+ &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ tt_assert(strlen(answer) > 0);
+ tt_assert(strlen(answer) == ipv4_len + ipv6_len + 1);
+ tor_free(answer);
+
+ done:
+ tor_free(answer);
+ UNMOCK(get_options);
+ UNMOCK(router_get_my_routerinfo);
+ addr_policy_list_free(mock_my_routerinfo.exit_policy);
+}
+
+#undef DEFAULT_POLICY_STRING
+#undef TEST_IPV4_ADDR
+#undef TEST_IPV6_ADDR
+
+#define TEST_IPV4_ADDR_STR "1.2.3.4"
+#define TEST_IPV6_ADDR_STR "[1002::4567]"
+#define REJECT_IPv4_FINAL_STR "reject 0.0.0.0/0:*"
+#define REJECT_IPv6_FINAL_STR "reject [::]/0:*"
+
+#define OTHER_IPV4_ADDR_STR "6.7.8.9"
+#define OTHER_IPV6_ADDR_STR "[afff::]"
+
+/** Run unit tests for fascist_firewall_allows_address */
+static void
+test_policies_fascist_firewall_allows_address(void *arg)
+{
+ (void)arg;
+ tor_addr_t ipv4_addr, ipv6_addr, r_ipv4_addr, r_ipv6_addr;
+ tor_addr_t n_ipv4_addr, n_ipv6_addr;
+ const uint16_t port = 1234;
+ smartlist_t *policy = NULL;
+ smartlist_t *e_policy = NULL;
+ addr_policy_t *item = NULL;
+ int malformed_list = 0;
+
+ /* Setup the options and the items in the policies */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ MOCK(get_options, mock_get_options);
+
+ policy = smartlist_new();
+ item = router_parse_addr_policy_item_from_string("accept "
+ TEST_IPV4_ADDR_STR ":*",
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(item);
+ tt_assert(!malformed_list);
+ smartlist_add(policy, item);
+ item = router_parse_addr_policy_item_from_string("accept "
+ TEST_IPV6_ADDR_STR,
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(item);
+ tt_assert(!malformed_list);
+ smartlist_add(policy, item);
+ /* Normally, policy_expand_unspec would do this for us */
+ item = router_parse_addr_policy_item_from_string(REJECT_IPv4_FINAL_STR,
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(item);
+ tt_assert(!malformed_list);
+ smartlist_add(policy, item);
+ item = router_parse_addr_policy_item_from_string(REJECT_IPv6_FINAL_STR,
+ ADDR_POLICY_ACCEPT,
+ &malformed_list);
+ tt_assert(item);
+ tt_assert(!malformed_list);
+ smartlist_add(policy, item);
+ item = NULL;
+
+ e_policy = smartlist_new();
+
+ /*
+ char *polstr = policy_dump_to_string(policy, 1, 1);
+ printf("%s\n", polstr);
+ tor_free(polstr);
+ */
+
+ /* Parse the addresses */
+ tor_addr_parse(&ipv4_addr, TEST_IPV4_ADDR_STR);
+ tor_addr_parse(&ipv6_addr, TEST_IPV6_ADDR_STR);
+ tor_addr_parse(&r_ipv4_addr, OTHER_IPV4_ADDR_STR);
+ tor_addr_parse(&r_ipv6_addr, OTHER_IPV6_ADDR_STR);
+ tor_addr_make_null(&n_ipv4_addr, AF_INET);
+ tor_addr_make_null(&n_ipv6_addr, AF_INET6);
+
+ /* Test the function's address matching with IPv4 and IPv6 on */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 1;
+ mock_options.UseBridges = 0;
+
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0)
+ == 0);
+
+ /* Preferring IPv4 */
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 1, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 1, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 1, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 1, 0)
+ == 0);
+
+ /* Preferring IPv6 */
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 1, 1)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 1, 1)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 1, 1)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 1, 1)
+ == 0);
+
+ /* Test the function's address matching with UseBridges on */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 1;
+ mock_options.UseBridges = 1;
+
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0)
+ == 0);
+
+ /* Preferring IPv4 */
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 1, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 1, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 1, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 1, 0)
+ == 0);
+
+ /* Preferring IPv6 */
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 1, 1)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 1, 1)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 1, 1)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 1, 1)
+ == 0);
+
+ /* bridge clients always use IPv6, regardless of ClientUseIPv6 */
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 0;
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0)
+ == 0);
+
+ /* Test the function's address matching with IPv4 on */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 0;
+ mock_options.UseBridges = 0;
+
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0)
+ == 0);
+
+ /* Test the function's address matching with IPv6 on */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 0;
+ mock_options.ClientUseIPv6 = 1;
+ mock_options.UseBridges = 0;
+
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0)
+ == 0);
+
+ /* Test the function's address matching with ClientUseIPv4 0.
+ * This means "use IPv6" regardless of the other settings. */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 0;
+ mock_options.ClientUseIPv6 = 0;
+ mock_options.UseBridges = 0;
+
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0)
+ == 0);
+
+ /* Test the function's address matching for unusual inputs */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 1;
+ mock_options.UseBridges = 1;
+
+ /* NULL and tor_addr_is_null addresses are rejected */
+ tt_assert(fascist_firewall_allows_address(NULL, port, policy, 0, 0) == 0);
+ tt_assert(fascist_firewall_allows_address(&n_ipv4_addr, port, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&n_ipv6_addr, port, policy, 0, 0)
+ == 0);
+
+ /* zero ports are rejected */
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, 0, policy, 0, 0)
+ == 0);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, 0, policy, 0, 0)
+ == 0);
+
+ /* NULL and empty policies accept everything */
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, NULL, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, NULL, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, e_policy, 0, 0)
+ == 1);
+ tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, e_policy, 0, 0)
+ == 1);
+
+ done:
+ addr_policy_free(item);
+ addr_policy_list_free(policy);
+ addr_policy_list_free(e_policy);
+ UNMOCK(get_options);
+}
+
+#undef REJECT_IPv4_FINAL_STR
+#undef REJECT_IPv6_FINAL_STR
+#undef OTHER_IPV4_ADDR_STR
+#undef OTHER_IPV6_ADDR_STR
+
+#define TEST_IPV4_OR_PORT 1234
+#define TEST_IPV4_DIR_PORT 2345
+#define TEST_IPV6_OR_PORT 61234
+#define TEST_IPV6_DIR_PORT 62345
+
+/* Check that fascist_firewall_choose_address_rs() returns the expected
+ * results. */
+#define CHECK_CHOSEN_ADDR_RS(fake_rs, fw_connection, pref_only, expect_rv, \
+ expect_ap) \
+ STMT_BEGIN \
+ tor_addr_port_t chosen_rs_ap; \
+ tor_addr_make_null(&chosen_rs_ap.addr, AF_INET); \
+ chosen_rs_ap.port = 0; \
+ tt_int_op(fascist_firewall_choose_address_rs(&(fake_rs), \
+ (fw_connection), \
+ (pref_only), \
+ &chosen_rs_ap), \
+ OP_EQ, (expect_rv)); \
+ tt_assert(tor_addr_eq(&(expect_ap).addr, &chosen_rs_ap.addr)); \
+ tt_int_op((expect_ap).port, OP_EQ, chosen_rs_ap.port); \
+ STMT_END
+
+/* Check that fascist_firewall_choose_address_node() returns the expected
+ * results. */
+#define CHECK_CHOSEN_ADDR_NODE(fake_node, fw_connection, pref_only, \
+ expect_rv, expect_ap) \
+ STMT_BEGIN \
+ tor_addr_port_t chosen_node_ap; \
+ tor_addr_make_null(&chosen_node_ap.addr, AF_INET); \
+ chosen_node_ap.port = 0; \
+ tt_int_op(fascist_firewall_choose_address_node(&(fake_node), \
+ (fw_connection), \
+ (pref_only), \
+ &chosen_node_ap), \
+ OP_EQ, (expect_rv)); \
+ tt_assert(tor_addr_eq(&(expect_ap).addr, &chosen_node_ap.addr)); \
+ tt_int_op((expect_ap).port, OP_EQ, chosen_node_ap.port); \
+ STMT_END
+
+/* Check that fascist_firewall_choose_address_rs and
+ * fascist_firewall_choose_address_node() both return the expected results. */
+#define CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, fw_connection, pref_only, \
+ expect_rv, expect_ap) \
+ STMT_BEGIN \
+ CHECK_CHOSEN_ADDR_RS(fake_rs, fw_connection, pref_only, expect_rv, \
+ expect_ap); \
+ CHECK_CHOSEN_ADDR_NODE(fake_node, fw_connection, pref_only, expect_rv, \
+ expect_ap); \
+ STMT_END
+
+/** Run unit tests for fascist_firewall_choose_address */
+static void
+test_policies_fascist_firewall_choose_address(void *arg)
+{
+ (void)arg;
+ tor_addr_port_t ipv4_or_ap, ipv4_dir_ap, ipv6_or_ap, ipv6_dir_ap;
+ tor_addr_port_t n_ipv4_ap, n_ipv6_ap;
+
+ /* Setup the options */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ MOCK(get_options, mock_get_options);
+
+ /* Parse the addresses */
+ tor_addr_parse(&ipv4_or_ap.addr, TEST_IPV4_ADDR_STR);
+ ipv4_or_ap.port = TEST_IPV4_OR_PORT;
+ tor_addr_parse(&ipv4_dir_ap.addr, TEST_IPV4_ADDR_STR);
+ ipv4_dir_ap.port = TEST_IPV4_DIR_PORT;
+
+ tor_addr_parse(&ipv6_or_ap.addr, TEST_IPV6_ADDR_STR);
+ ipv6_or_ap.port = TEST_IPV6_OR_PORT;
+ tor_addr_parse(&ipv6_dir_ap.addr, TEST_IPV6_ADDR_STR);
+ ipv6_dir_ap.port = TEST_IPV6_DIR_PORT;
+
+ tor_addr_make_null(&n_ipv4_ap.addr, AF_INET);
+ n_ipv4_ap.port = 0;
+ tor_addr_make_null(&n_ipv6_ap.addr, AF_INET6);
+ n_ipv6_ap.port = 0;
+
+ /* Sanity check fascist_firewall_choose_address with IPv4 and IPv6 on */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 1;
+ mock_options.UseBridges = 0;
+
+ /* Prefer IPv4 */
+ tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 1,
+ FIREWALL_OR_CONNECTION, 0, 0)
+ == &ipv4_or_ap);
+ tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 1,
+ FIREWALL_OR_CONNECTION, 1, 0)
+ == &ipv4_or_ap);
+ tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 1,
+ FIREWALL_DIR_CONNECTION, 0, 0)
+ == &ipv4_dir_ap);
+ tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 1,
+ FIREWALL_DIR_CONNECTION, 1, 0)
+ == &ipv4_dir_ap);
+
+ /* Prefer IPv6 */
+ tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0,
+ FIREWALL_OR_CONNECTION, 0, 1)
+ == &ipv6_or_ap);
+ tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0,
+ FIREWALL_OR_CONNECTION, 1, 1)
+ == &ipv6_or_ap);
+ tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 0,
+ FIREWALL_DIR_CONNECTION, 0, 1)
+ == &ipv6_dir_ap);
+ tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &ipv6_dir_ap, 0,
+ FIREWALL_DIR_CONNECTION, 1, 1)
+ == &ipv6_dir_ap);
+
+ /* Unusual inputs */
+
+ /* null preferred OR addresses */
+ tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &n_ipv6_ap, 0,
+ FIREWALL_OR_CONNECTION, 0, 1)
+ == &ipv4_or_ap);
+ tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &ipv6_or_ap, 1,
+ FIREWALL_OR_CONNECTION, 0, 0)
+ == &ipv6_or_ap);
+
+ /* null both OR addresses */
+ tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 0,
+ FIREWALL_OR_CONNECTION, 0, 1)
+ == NULL);
+ tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 1,
+ FIREWALL_OR_CONNECTION, 0, 0)
+ == NULL);
+
+ /* null preferred Dir addresses */
+ tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &n_ipv6_ap, 0,
+ FIREWALL_DIR_CONNECTION, 0, 1)
+ == &ipv4_dir_ap);
+ tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &ipv6_dir_ap, 1,
+ FIREWALL_DIR_CONNECTION, 0, 0)
+ == &ipv6_dir_ap);
+
+ /* null both Dir addresses */
+ tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 0,
+ FIREWALL_DIR_CONNECTION, 0, 1)
+ == NULL);
+ tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 1,
+ FIREWALL_DIR_CONNECTION, 0, 0)
+ == NULL);
+
+ /* Prefer IPv4 but want IPv6 (contradictory) */
+ tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0,
+ FIREWALL_OR_CONNECTION, 0, 0)
+ == &ipv4_or_ap);
+ tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0,
+ FIREWALL_OR_CONNECTION, 1, 0)
+ == &ipv4_or_ap);
+
+ /* Prefer IPv6 but want IPv4 (contradictory) */
+ tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 1,
+ FIREWALL_OR_CONNECTION, 0, 1)
+ == &ipv6_or_ap);
+ tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 1,
+ FIREWALL_OR_CONNECTION, 1, 1)
+ == &ipv6_or_ap);
+
+ /* Make a fake rs. There will be no corresponding node.
+ * This is what happens when there's no consensus and we're bootstrapping
+ * from authorities / fallbacks. */
+ routerstatus_t fake_rs;
+ memset(&fake_rs, 0, sizeof(routerstatus_t));
+ /* In a routerstatus, the OR and Dir addresses are the same */
+ fake_rs.addr = tor_addr_to_ipv4h(&ipv4_or_ap.addr);
+ fake_rs.or_port = ipv4_or_ap.port;
+ fake_rs.dir_port = ipv4_dir_ap.port;
+
+ tor_addr_copy(&fake_rs.ipv6_addr, &ipv6_or_ap.addr);
+ fake_rs.ipv6_orport = ipv6_or_ap.port;
+ /* In a routerstatus, the IPv4 and IPv6 DirPorts are the same.*/
+ ipv6_dir_ap.port = TEST_IPV4_DIR_PORT;
+
+ /* Make a fake node. Even though it contains the fake_rs, a lookup won't
+ * find the node from the rs, because they're not in the hash table. */
+ node_t fake_node;
+ memset(&fake_node, 0, sizeof(node_t));
+ fake_node.rs = &fake_rs;
+
+ /* Choose an address with IPv4 and IPv6 on */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 1;
+ mock_options.UseBridges = 0;
+
+ /* Preferring IPv4 */
+ mock_options.ClientPreferIPv6ORPort = 0;
+ mock_options.ClientPreferIPv6DirPort = 0;
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv4_dir_ap);
+
+ /* Auto (Preferring IPv4) */
+ mock_options.ClientPreferIPv6ORPort = -1;
+ mock_options.ClientPreferIPv6DirPort = -1;
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv4_dir_ap);
+
+ /* Preferring IPv6 */
+ mock_options.ClientPreferIPv6ORPort = 1;
+ mock_options.ClientPreferIPv6DirPort = 1;
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv6_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv6_dir_ap);
+
+ /* Preferring IPv4 OR / IPv6 Dir */
+ mock_options.ClientPreferIPv6ORPort = 0;
+ mock_options.ClientPreferIPv6DirPort = 1;
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv6_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv6_dir_ap);
+
+ /* Preferring IPv6 OR / IPv4 Dir */
+ mock_options.ClientPreferIPv6ORPort = 1;
+ mock_options.ClientPreferIPv6DirPort = 0;
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv4_dir_ap);
+
+ /* Choose an address with UseBridges on */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.UseBridges = 1;
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 1;
+
+ /* Preferring IPv4 */
+ mock_options.ClientPreferIPv6ORPort = 0;
+ mock_options.ClientPreferIPv6DirPort = 0;
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv4_dir_ap);
+
+ /* Auto:
+ * - bridge clients prefer the configured bridge OR address from the node,
+ * (the configured address family sets node.ipv6_preferred)
+ * - other clients prefer IPv4 OR by default (see above),
+ * - all clients, including bridge clients, prefer IPv4 Dir by default.
+ */
+ mock_options.ClientPreferIPv6ORPort = -1;
+ mock_options.ClientPreferIPv6DirPort = -1;
+
+ /* Simulate the initialisation of fake_node.ipv6_preferred with a bridge
+ * configured with an IPv4 address */
+ fake_node.ipv6_preferred = 0;
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 0, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 1, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv4_dir_ap);
+
+ /* Simulate the initialisation of fake_node.ipv6_preferred with a bridge
+ * configured with an IPv6 address */
+ fake_node.ipv6_preferred = 1;
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 0, 1, ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 1, 1, ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv4_dir_ap);
+
+ /* When a rs has no node, it defaults to IPv4 under auto. */
+ CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_OR_CONNECTION, 0, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_OR_CONNECTION, 1, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_DIR_CONNECTION, 0, 1, ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_DIR_CONNECTION, 1, 1, ipv4_dir_ap);
+
+ /* Preferring IPv6 */
+ mock_options.ClientPreferIPv6ORPort = 1;
+ mock_options.ClientPreferIPv6DirPort = 1;
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv6_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv6_dir_ap);
+
+ /* In the default configuration (Auto / IPv6 off), bridge clients should
+ * use both IPv4 and IPv6, but only prefer IPv6 for bridges configured with
+ * an IPv6 address, regardless of ClientUseIPv6. (See above.) */
+ mock_options.ClientUseIPv6 = 0;
+ mock_options.ClientPreferIPv6ORPort = -1;
+ mock_options.ClientPreferIPv6DirPort = -1;
+ /* Simulate the initialisation of fake_node.ipv6_preferred with a bridge
+ * configured with an IPv4 address */
+ fake_node.ipv6_preferred = 0;
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 0, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 1, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv4_dir_ap);
+
+ /* Simulate the initialisation of fake_node.ipv6_preferred with a bridge
+ * configured with an IPv6 address */
+ fake_node.ipv6_preferred = 1;
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 0, 1, ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_OR_CONNECTION, 1, 1, ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_NODE(fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv4_dir_ap);
+
+ /* When a rs has no node, it defaults to IPv4 under auto. */
+ CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_OR_CONNECTION, 0, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_OR_CONNECTION, 1, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_DIR_CONNECTION, 0, 1, ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_RS(fake_rs, FIREWALL_DIR_CONNECTION, 1, 1, ipv4_dir_ap);
+
+ /* Choose an address with IPv4 on */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 0;
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv4_dir_ap);
+
+ /* Choose an address with IPv6 on */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 0;
+ mock_options.ClientUseIPv6 = 1;
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv6_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv6_dir_ap);
+
+ /* Choose an address with ClientUseIPv4 0.
+ * This means "use IPv6" regardless of the other settings. */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 0;
+ mock_options.ClientUseIPv6 = 0;
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv6_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv6_dir_ap);
+
+ /* Choose an address with ORPort_set 1 (server mode).
+ * This means "use IPv4" regardless of the other settings. */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ORPort_set = 1;
+ mock_options.ClientUseIPv4 = 0;
+ mock_options.ClientUseIPv6 = 1;
+ mock_options.ClientPreferIPv6ORPort = 1;
+ mock_options.ClientPreferIPv6DirPort = 1;
+
+ /* Simulate the initialisation of fake_node.ipv6_preferred */
+ fake_node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(
+ &mock_options);
+
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 0, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_OR_CONNECTION, 1, 1,
+ ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 0, 1,
+ ipv4_dir_ap);
+ CHECK_CHOSEN_ADDR_RN(fake_rs, fake_node, FIREWALL_DIR_CONNECTION, 1, 1,
+ ipv4_dir_ap);
+
+ done:
+ UNMOCK(get_options);
+}
+
+#undef TEST_IPV4_ADDR_STR
+#undef TEST_IPV6_ADDR_STR
+#undef TEST_IPV4_OR_PORT
+#undef TEST_IPV4_DIR_PORT
+#undef TEST_IPV6_OR_PORT
+#undef TEST_IPV6_DIR_PORT
+
+#undef CHECK_CHOSEN_ADDR_RS
+#undef CHECK_CHOSEN_ADDR_NODE
+#undef CHECK_CHOSEN_ADDR_RN
+
struct testcase_t policy_tests[] = {
{ "router_dump_exit_policy_to_string", test_dump_exit_policy_to_string, 0,
NULL, NULL },
{ "general", test_policies_general, 0, NULL, NULL },
+ { "getinfo_helper_policies", test_policies_getinfo_helper_policies, 0, NULL,
+ NULL },
+ { "reject_exit_address", test_policies_reject_exit_address, 0, NULL, NULL },
+ { "reject_interface_address", test_policies_reject_interface_address, 0,
+ NULL, NULL },
+ { "reject_port_address", test_policies_reject_port_address, 0, NULL, NULL },
+ { "fascist_firewall_allows_address",
+ test_policies_fascist_firewall_allows_address, 0, NULL, NULL },
+ { "fascist_firewall_choose_address",
+ test_policies_fascist_firewall_choose_address, 0, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_procmon.c b/src/test/test_procmon.c
new file mode 100644
index 0000000000..2855178788
--- /dev/null
+++ b/src/test/test_procmon.c
@@ -0,0 +1,58 @@
+/* Copyright (c) 2010-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define PROCMON_PRIVATE
+#include "orconfig.h"
+#include "or.h"
+#include "test.h"
+
+#include "procmon.h"
+
+#include "log_test_helpers.h"
+
+#define NS_MODULE procmon
+
+struct event_base;
+
+static void
+test_procmon_tor_process_monitor_new(void *ignored)
+{
+ (void)ignored;
+ tor_process_monitor_t *res;
+ const char *msg;
+
+ res = tor_process_monitor_new(NULL, "probably invalid", 0, NULL, NULL, &msg);
+ tt_assert(!res);
+ tt_str_op(msg, OP_EQ, "invalid PID");
+
+ res = tor_process_monitor_new(NULL, "243443535345454", 0, NULL, NULL, &msg);
+ tt_assert(!res);
+ tt_str_op(msg, OP_EQ, "invalid PID");
+
+ res = tor_process_monitor_new(tor_libevent_get_base(), "43", 0,
+ NULL, NULL, &msg);
+ tt_assert(res);
+ tt_assert(!msg);
+ tor_process_monitor_free(res);
+
+ res = tor_process_monitor_new(tor_libevent_get_base(), "44 hello", 0,
+ NULL, NULL, &msg);
+ tt_assert(res);
+ tt_assert(!msg);
+ tor_process_monitor_free(res);
+
+ res = tor_process_monitor_new(tor_libevent_get_base(), "45:hello", 0,
+ NULL, NULL, &msg);
+ tt_assert(res);
+ tt_assert(!msg);
+
+ done:
+ tor_process_monitor_free(res);
+}
+
+struct testcase_t procmon_tests[] = {
+ { "tor_process_monitor_new", test_procmon_tor_process_monitor_new,
+ TT_FORK, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_rendcache.c b/src/test/test_rendcache.c
new file mode 100644
index 0000000000..7e04799db2
--- /dev/null
+++ b/src/test/test_rendcache.c
@@ -0,0 +1,1340 @@
+/* Copyright (c) 2010-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "or.h"
+
+#include "test.h"
+#define RENDCACHE_PRIVATE
+#include "rendcache.h"
+#include "router.h"
+#include "routerlist.h"
+#include "config.h"
+#include <openssl/rsa.h>
+#include "rend_test_helpers.h"
+
+#define NS_MODULE rend_cache
+
+static const int RECENT_TIME = -10;
+static const int TIME_IN_THE_PAST = -(REND_CACHE_MAX_AGE + \
+ REND_CACHE_MAX_SKEW + 10);
+static const int TIME_IN_THE_FUTURE = REND_CACHE_MAX_SKEW + 10;
+
+extern strmap_t *rend_cache;
+extern digestmap_t *rend_cache_v2_dir;
+extern strmap_t *rend_cache_failure;
+extern size_t rend_cache_total_allocation;
+
+static rend_data_t *
+mock_rend_data(const char *onion_address)
+{
+ rend_data_t *rend_query = tor_malloc_zero(sizeof(rend_data_t));
+
+ strlcpy(rend_query->onion_address, onion_address,
+ sizeof(rend_query->onion_address));
+ rend_query->auth_type = REND_NO_AUTH;
+ rend_query->hsdirs_fp = smartlist_new();
+ smartlist_add(rend_query->hsdirs_fp, tor_memdup("aaaaaaaaaaaaaaaaaaaaaaaa",
+ DIGEST_LEN));
+
+ return rend_query;
+}
+
+static void
+test_rend_cache_lookup_entry(void *data)
+{
+ int ret;
+ rend_data_t *mock_rend_query = NULL;
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ rend_cache_entry_t *entry = NULL;
+ rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
+ char *service_id = NULL;
+ (void)data;
+
+ rend_cache_init();
+
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+
+ ret = rend_cache_lookup_entry("abababababababab", 0, NULL);
+ tt_int_op(ret, OP_EQ, -ENOENT);
+
+ ret = rend_cache_lookup_entry("invalid query", 2, NULL);
+ tt_int_op(ret, OP_EQ, -EINVAL);
+
+ ret = rend_cache_lookup_entry("abababababababab", 2, NULL);
+ tt_int_op(ret, OP_EQ, -ENOENT);
+
+ ret = rend_cache_lookup_entry("abababababababab", 4224, NULL);
+ tt_int_op(ret, OP_EQ, -ENOENT);
+
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+ rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32,
+ mock_rend_query, NULL);
+
+ ret = rend_cache_lookup_entry(service_id, 2, NULL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ret = rend_cache_lookup_entry(service_id, 2, &entry);
+ tt_assert(entry);
+ tt_int_op(entry->len, OP_EQ, strlen(desc_holder->desc_str));
+ tt_str_op(entry->desc, OP_EQ, desc_holder->desc_str);
+
+ done:
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_cache_free_all();
+ rend_data_free(mock_rend_query);
+}
+
+static void
+test_rend_cache_store_v2_desc_as_client(void *data)
+{
+ rend_cache_store_status_t ret;
+ rend_data_t *mock_rend_query;
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ rend_cache_entry_t *entry = NULL;
+ rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
+ char *service_id = NULL;
+ char client_cookie[REND_DESC_COOKIE_LEN];
+ (void)data;
+
+ rend_cache_init();
+
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+
+ // Test success
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ &entry);
+
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+ tt_assert(entry);
+ tt_int_op(entry->len, OP_EQ, strlen(desc_holder->desc_str));
+ tt_str_op(entry->desc, OP_EQ, desc_holder->desc_str);
+
+ // Test various failure modes
+
+ // TODO: a too long desc_id_base32 argument crashes the function
+ /* ret = rend_cache_store_v2_desc_as_client( */
+ /* desc_holder->desc_str, */
+ /* "3TOOLONG3TOOLONG3TOOLONG3TOOLONG3TOOLONG3TOOLONG", */
+ /* &mock_rend_query, NULL); */
+ /* tt_int_op(ret, OP_EQ, RCS_BADDESC); */
+
+ // Test bad base32 failure
+ // This causes an assertion failure if we're running with assertions.
+ // But when building without asserts, we can test it.
+#ifdef DISABLE_ASSERTS_IN_UNIT_TESTS
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ "!xqunszqnaolrrfmtzgaki7mxelgvkj", mock_rend_query, NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+#endif
+
+ // Test invalid descriptor
+ ret = rend_cache_store_v2_desc_as_client("invalid descriptor",
+ "3xqunszqnaolrrfmtzgaki7mxelgvkje", mock_rend_query, NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+
+ // TODO: it doesn't seem to be possible to test invalid service ID condition.
+ // that means it is likely not possible to have that condition without
+ // earlier conditions failing first (such as signature checking of the desc)
+
+ rend_cache_free_all();
+
+ // Test mismatch between service ID and onion address
+ rend_cache_init();
+ strncpy(mock_rend_query->onion_address, "abc", REND_SERVICE_ID_LEN_BASE32+1);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32,
+ mock_rend_query, NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+ rend_cache_free_all();
+ rend_data_free(mock_rend_query);
+
+ // Test incorrect descriptor ID
+ rend_cache_init();
+ mock_rend_query = mock_rend_data(service_id);
+ desc_id_base32[0]++;
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+ desc_id_base32[0]--;
+ rend_cache_free_all();
+
+ // Test too old descriptor
+ rend_cache_init();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_data_free(mock_rend_query);
+
+ generate_desc(TIME_IN_THE_PAST, &desc_holder, &service_id, 3);
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32,
+ mock_rend_query, NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+ rend_cache_free_all();
+
+ // Test too new descriptor (in the future)
+ rend_cache_init();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_data_free(mock_rend_query);
+
+ generate_desc(TIME_IN_THE_FUTURE, &desc_holder, &service_id, 3);
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+ rend_cache_free_all();
+
+ // Test when a descriptor is already in the cache
+ rend_cache_init();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_data_free(mock_rend_query);
+
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+
+ rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32,
+ mock_rend_query, NULL);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ &entry);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+ tt_assert(entry);
+ rend_cache_free_all();
+
+ // Test unsuccessful decrypting of introduction points
+ rend_cache_init();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_data_free(mock_rend_query);
+
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+ mock_rend_query = mock_rend_data(service_id);
+ mock_rend_query->auth_type = REND_BASIC_AUTH;
+ client_cookie[0] = 'A';
+ memcpy(mock_rend_query->descriptor_cookie, client_cookie,
+ REND_DESC_COOKIE_LEN);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+ rend_cache_free_all();
+
+ // Test successful run when we have REND_BASIC_AUTH but not cookie
+ rend_cache_init();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_data_free(mock_rend_query);
+
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+ mock_rend_query = mock_rend_data(service_id);
+ mock_rend_query->auth_type = REND_BASIC_AUTH;
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ rend_cache_free_all();
+
+ // Test when we have no introduction points
+ rend_cache_init();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_data_free(mock_rend_query);
+
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 0);
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+ rend_cache_free_all();
+
+ // Test when we have too many intro points
+ rend_cache_init();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_data_free(mock_rend_query);
+
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, MAX_INTRO_POINTS+1);
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+
+ done:
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_cache_free_all();
+ rend_data_free(mock_rend_query);
+}
+
+static void
+test_rend_cache_store_v2_desc_as_client_with_different_time(void *data)
+{
+ rend_cache_store_status_t ret;
+ rend_data_t *mock_rend_query;
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ rend_service_descriptor_t *generated = NULL;
+ smartlist_t *descs = smartlist_new();
+ time_t t;
+ char *service_id = NULL;
+ rend_encoded_v2_service_descriptor_t *desc_holder_newer;
+ rend_encoded_v2_service_descriptor_t *desc_holder_older;
+
+ t = time(NULL);
+ rend_cache_init();
+
+ create_descriptor(&generated, &service_id, 3);
+
+ generated->timestamp = t + RECENT_TIME;
+ rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0,
+ REND_NO_AUTH, NULL, NULL);
+ desc_holder_newer = ((rend_encoded_v2_service_descriptor_t *)
+ smartlist_get(descs, 0));
+ smartlist_set(descs, 0, NULL);
+
+ SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
+ rend_encoded_v2_service_descriptor_free(d));
+ smartlist_free(descs);
+ descs = smartlist_new();
+
+ generated->timestamp = (t + RECENT_TIME) - 20;
+ rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0,
+ REND_NO_AUTH, NULL, NULL);
+ desc_holder_older = ((rend_encoded_v2_service_descriptor_t *)
+ smartlist_get(descs, 0));
+ smartlist_set(descs, 0, NULL);
+ (void)data;
+
+ // Test when a descriptor is already in the cache and it is newer than the
+ // one we submit
+ mock_rend_query = mock_rend_data(service_id);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32),
+ desc_holder_newer->desc_id, DIGEST_LEN);
+ rend_cache_store_v2_desc_as_client(desc_holder_newer->desc_str,
+ desc_id_base32, mock_rend_query, NULL);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder_older->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ rend_cache_free_all();
+
+ // Test when an old descriptor is in the cache and we submit a newer one
+ rend_cache_init();
+ rend_cache_store_v2_desc_as_client(desc_holder_older->desc_str,
+ desc_id_base32, mock_rend_query, NULL);
+ ret = rend_cache_store_v2_desc_as_client(desc_holder_newer->desc_str,
+ desc_id_base32, mock_rend_query,
+ NULL);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ done:
+ rend_encoded_v2_service_descriptor_free(desc_holder_newer);
+ rend_encoded_v2_service_descriptor_free(desc_holder_older);
+ SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
+ rend_encoded_v2_service_descriptor_free(d));
+ smartlist_free(descs);
+ rend_service_descriptor_free(generated);
+ tor_free(service_id);
+ rend_cache_free_all();
+ rend_data_free(mock_rend_query);
+}
+
+#define NS_SUBMODULE lookup_v2_desc_as_dir
+NS_DECL(const routerinfo_t *, router_get_my_routerinfo, (void));
+NS_DECL(int, hid_serv_responsible_for_desc_id, (const char *id));
+
+static routerinfo_t *mock_routerinfo;
+static int hid_serv_responsible_for_desc_id_response;
+
+static const routerinfo_t *
+NS(router_get_my_routerinfo)(void)
+{
+ if (!mock_routerinfo) {
+ mock_routerinfo = tor_malloc(sizeof(routerinfo_t));
+ }
+
+ return mock_routerinfo;
+}
+
+static int
+NS(hid_serv_responsible_for_desc_id)(const char *id)
+{
+ (void)id;
+ return hid_serv_responsible_for_desc_id_response;
+}
+
+static void
+test_rend_cache_lookup_v2_desc_as_dir(void *data)
+{
+ int ret;
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
+ char *service_id = NULL;
+ const char *ret_desc = NULL;
+
+ (void)data;
+
+ NS_MOCK(router_get_my_routerinfo);
+ NS_MOCK(hid_serv_responsible_for_desc_id);
+
+ rend_cache_init();
+
+ // Test invalid base32
+ ret = rend_cache_lookup_v2_desc_as_dir("!bababababababab", NULL);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test non-existent descriptor but well formed
+ ret = rend_cache_lookup_v2_desc_as_dir("3xqunszqnaolrrfmtzgaki7mxelgvkje",
+ NULL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test existing descriptor
+ hid_serv_responsible_for_desc_id_response = 1;
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+ rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
+ DIGEST_LEN);
+ ret = rend_cache_lookup_v2_desc_as_dir(desc_id_base32, &ret_desc);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_assert(ret_desc);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+ NS_UNMOCK(hid_serv_responsible_for_desc_id);
+ tor_free(mock_routerinfo);
+ rend_cache_free_all();
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE store_v2_desc_as_dir
+NS_DECL(const routerinfo_t *, router_get_my_routerinfo, (void));
+NS_DECL(int, hid_serv_responsible_for_desc_id, (const char *id));
+
+static const routerinfo_t *
+NS(router_get_my_routerinfo)(void)
+{
+ return mock_routerinfo;
+}
+
+static int
+NS(hid_serv_responsible_for_desc_id)(const char *id)
+{
+ (void)id;
+ return hid_serv_responsible_for_desc_id_response;
+}
+
+static void
+test_rend_cache_store_v2_desc_as_dir(void *data)
+{
+ (void)data;
+ rend_cache_store_status_t ret;
+ rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
+ char *service_id = NULL;
+
+ NS_MOCK(router_get_my_routerinfo);
+ NS_MOCK(hid_serv_responsible_for_desc_id);
+
+ rend_cache_init();
+
+ // Test when we are not an HS dir
+ mock_routerinfo = NULL;
+ ret = rend_cache_store_v2_desc_as_dir("");
+ tt_int_op(ret, OP_EQ, RCS_NOTDIR);
+
+ // Test when we can't parse the descriptor
+ mock_routerinfo = tor_malloc(sizeof(routerinfo_t));
+ hid_serv_responsible_for_desc_id_response = 1;
+ ret = rend_cache_store_v2_desc_as_dir("unparseable");
+ tt_int_op(ret, OP_EQ, RCS_BADDESC);
+
+ // Test when we are not responsible for an HS
+ hid_serv_responsible_for_desc_id_response = 0;
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+
+ // Test when we have an old descriptor
+ hid_serv_responsible_for_desc_id_response = 1;
+ generate_desc(TIME_IN_THE_PAST, &desc_holder, &service_id, 3);
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+
+ // Test when we have a descriptor in the future
+ generate_desc(TIME_IN_THE_FUTURE, &desc_holder, &service_id, 3);
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+
+ // Test when two descriptors
+ generate_desc(TIME_IN_THE_FUTURE, &desc_holder, &service_id, 3);
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+
+ // Test when asking for hidden service statistics HiddenServiceStatistics
+ rend_cache_purge();
+ generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+ get_options_mutable()->HiddenServiceStatistics = 1;
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+ NS_UNMOCK(hid_serv_responsible_for_desc_id);
+ rend_encoded_v2_service_descriptor_free(desc_holder);
+ tor_free(service_id);
+ rend_cache_free_all();
+ tor_free(mock_routerinfo);
+}
+
+static void
+test_rend_cache_store_v2_desc_as_dir_with_different_time(void *data)
+{
+ (void)data;
+
+ rend_cache_store_status_t ret;
+ rend_service_descriptor_t *generated = NULL;
+ smartlist_t *descs = smartlist_new();
+ time_t t;
+ char *service_id = NULL;
+ rend_encoded_v2_service_descriptor_t *desc_holder_newer;
+ rend_encoded_v2_service_descriptor_t *desc_holder_older;
+
+ NS_MOCK(router_get_my_routerinfo);
+ NS_MOCK(hid_serv_responsible_for_desc_id);
+
+ rend_cache_init();
+
+ t = time(NULL);
+
+ create_descriptor(&generated, &service_id, 3);
+ generated->timestamp = t + RECENT_TIME;
+ rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0,
+ REND_NO_AUTH, NULL, NULL);
+ desc_holder_newer = ((rend_encoded_v2_service_descriptor_t *)
+ smartlist_get(descs, 0));
+ smartlist_set(descs, 0, NULL);
+ SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
+ rend_encoded_v2_service_descriptor_free(d));
+ smartlist_free(descs);
+ descs = smartlist_new();
+
+ generated->timestamp = (t + RECENT_TIME) - 20;
+ rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0,
+ REND_NO_AUTH, NULL, NULL);
+ desc_holder_older = ((rend_encoded_v2_service_descriptor_t *)
+ smartlist_get(descs, 0));
+ smartlist_set(descs, 0, NULL);
+
+ // Test when we have a newer descriptor stored
+ mock_routerinfo = tor_malloc(sizeof(routerinfo_t));
+ hid_serv_responsible_for_desc_id_response = 1;
+ rend_cache_store_v2_desc_as_dir(desc_holder_newer->desc_str);
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder_older->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ // Test when we have an old descriptor stored
+ rend_cache_purge();
+ rend_cache_store_v2_desc_as_dir(desc_holder_older->desc_str);
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder_newer->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+ NS_UNMOCK(hid_serv_responsible_for_desc_id);
+ rend_cache_free_all();
+ rend_service_descriptor_free(generated);
+ tor_free(service_id);
+ SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
+ rend_encoded_v2_service_descriptor_free(d));
+ smartlist_free(descs);
+ rend_encoded_v2_service_descriptor_free(desc_holder_newer);
+ rend_encoded_v2_service_descriptor_free(desc_holder_older);
+ tor_free(mock_routerinfo);
+}
+
+static void
+test_rend_cache_store_v2_desc_as_dir_with_different_content(void *data)
+{
+ (void)data;
+
+ rend_cache_store_status_t ret;
+ rend_service_descriptor_t *generated = NULL;
+ smartlist_t *descs = smartlist_new();
+ time_t t;
+ char *service_id = NULL;
+ rend_encoded_v2_service_descriptor_t *desc_holder_one = NULL;
+ rend_encoded_v2_service_descriptor_t *desc_holder_two = NULL;
+
+ NS_MOCK(router_get_my_routerinfo);
+ NS_MOCK(hid_serv_responsible_for_desc_id);
+
+ rend_cache_init();
+
+ t = time(NULL);
+
+ create_descriptor(&generated, &service_id, 3);
+ generated->timestamp = t + RECENT_TIME;
+ rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0,
+ REND_NO_AUTH, NULL, NULL);
+ desc_holder_one = ((rend_encoded_v2_service_descriptor_t *)
+ smartlist_get(descs, 0));
+ smartlist_set(descs, 0, NULL);
+
+ SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
+ rend_encoded_v2_service_descriptor_free(d));
+ smartlist_free(descs);
+ descs = smartlist_new();
+
+ generated->timestamp = t + RECENT_TIME;
+ generated->protocols = 41;
+ rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0,
+ REND_NO_AUTH, NULL, NULL);
+ desc_holder_two = ((rend_encoded_v2_service_descriptor_t *)
+ smartlist_get(descs, 0));
+ smartlist_set(descs, 0, NULL);
+
+ // Test when we have another descriptor stored, with a different descriptor
+ mock_routerinfo = tor_malloc(sizeof(routerinfo_t));
+ hid_serv_responsible_for_desc_id_response = 1;
+ rend_cache_store_v2_desc_as_dir(desc_holder_one->desc_str);
+ ret = rend_cache_store_v2_desc_as_dir(desc_holder_two->desc_str);
+ tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+ NS_UNMOCK(hid_serv_responsible_for_desc_id);
+ rend_cache_free_all();
+ rend_service_descriptor_free(generated);
+ tor_free(service_id);
+ SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
+ rend_encoded_v2_service_descriptor_free(d));
+ smartlist_free(descs);
+ rend_encoded_v2_service_descriptor_free(desc_holder_one);
+ rend_encoded_v2_service_descriptor_free(desc_holder_two);
+}
+
+#undef NS_SUBMODULE
+
+static void
+test_rend_cache_init(void *data)
+{
+ (void)data;
+
+ tt_assert_msg(!rend_cache, "rend_cache should be NULL when starting");
+ tt_assert_msg(!rend_cache_v2_dir, "rend_cache_v2_dir should be NULL "
+ "when starting");
+ tt_assert_msg(!rend_cache_failure, "rend_cache_failure should be NULL when "
+ "starting");
+
+ rend_cache_init();
+
+ tt_assert_msg(rend_cache, "rend_cache should not be NULL after initing");
+ tt_assert_msg(rend_cache_v2_dir, "rend_cache_v2_dir should not be NULL "
+ "after initing");
+ tt_assert_msg(rend_cache_failure, "rend_cache_failure should not be NULL "
+ "after initing");
+
+ tt_int_op(strmap_size(rend_cache), OP_EQ, 0);
+ tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
+ tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 0);
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_decrement_allocation(void *data)
+{
+ (void)data;
+
+ // Test when the cache has enough allocations
+ rend_cache_total_allocation = 10;
+ rend_cache_decrement_allocation(3);
+ tt_int_op(rend_cache_total_allocation, OP_EQ, 7);
+
+ // Test when there are not enough allocations
+ rend_cache_total_allocation = 1;
+ rend_cache_decrement_allocation(2);
+ tt_int_op(rend_cache_total_allocation, OP_EQ, 0);
+
+ // And again
+ rend_cache_decrement_allocation(2);
+ tt_int_op(rend_cache_total_allocation, OP_EQ, 0);
+
+ done:
+ (void)0;
+}
+
+static void
+test_rend_cache_increment_allocation(void *data)
+{
+ (void)data;
+
+ // Test when the cache is not overflowing
+ rend_cache_total_allocation = 5;
+ rend_cache_increment_allocation(3);
+ tt_int_op(rend_cache_total_allocation, OP_EQ, 8);
+
+ // Test when there are too many allocations
+ rend_cache_total_allocation = SIZE_MAX-1;
+ rend_cache_increment_allocation(2);
+ tt_u64_op(rend_cache_total_allocation, OP_EQ, SIZE_MAX);
+
+ // And again
+ rend_cache_increment_allocation(2);
+ tt_u64_op(rend_cache_total_allocation, OP_EQ, SIZE_MAX);
+
+ done:
+ (void)0;
+}
+
+static void
+test_rend_cache_failure_intro_entry_new(void *data)
+{
+ time_t now;
+ rend_cache_failure_intro_t *entry;
+ rend_intro_point_failure_t failure;
+
+ (void)data;
+
+ failure = INTRO_POINT_FAILURE_TIMEOUT;
+ now = time(NULL);
+ entry = rend_cache_failure_intro_entry_new(failure);
+
+ tt_int_op(entry->failure_type, OP_EQ, INTRO_POINT_FAILURE_TIMEOUT);
+ tt_int_op(entry->created_ts, OP_GE, now-5);
+ tt_int_op(entry->created_ts, OP_LE, now+5);
+
+ done:
+ tor_free(entry);
+}
+
+static void
+test_rend_cache_failure_intro_lookup(void *data)
+{
+ (void)data;
+ int ret;
+ rend_cache_failure_t *failure;
+ rend_cache_failure_intro_t *ip;
+ rend_cache_failure_intro_t *entry;
+ const char key_ip_one[DIGEST_LEN] = "ip1";
+ const char key_ip_two[DIGEST_LEN] = "ip2";
+ const char key_foo[DIGEST_LEN] = "foo1";
+
+ rend_cache_init();
+
+ failure = rend_cache_failure_entry_new();
+ ip = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+ digestmap_set(failure->intro_failures, key_ip_one, ip);
+ strmap_set_lc(rend_cache_failure, "foo1", failure);
+
+ // Test not found
+ ret = cache_failure_intro_lookup((const uint8_t *) key_foo, "foo2", NULL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test found with no intro failures in it
+ ret = cache_failure_intro_lookup((const uint8_t *) key_ip_two, "foo1", NULL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Test found
+ ret = cache_failure_intro_lookup((const uint8_t *) key_ip_one, "foo1", NULL);
+ tt_int_op(ret, OP_EQ, 1);
+
+ // Test found and asking for entry
+ cache_failure_intro_lookup((const uint8_t *) key_ip_one, "foo1", &entry);
+ tt_assert(entry);
+ tt_assert(entry == ip);
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_clean(void *data)
+{
+ rend_cache_entry_t *one, *two;
+ rend_service_descriptor_t *desc_one, *desc_two;
+ strmap_iter_t *iter = NULL;
+ const char *key;
+ void *val;
+
+ (void)data;
+
+ rend_cache_init();
+
+ // Test with empty rendcache
+ rend_cache_clean(time(NULL), REND_CACHE_TYPE_CLIENT);
+ tt_int_op(strmap_size(rend_cache), OP_EQ, 0);
+
+ // Test with two old entries
+ one = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ two = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ desc_one = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ desc_two = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ one->parsed = desc_one;
+ two->parsed = desc_two;
+
+ desc_one->timestamp = time(NULL) + TIME_IN_THE_PAST;
+ desc_two->timestamp = (time(NULL) + TIME_IN_THE_PAST) - 10;
+ desc_one->pk = pk_generate(0);
+ desc_two->pk = pk_generate(1);
+
+ strmap_set_lc(rend_cache, "foo1", one);
+ strmap_set_lc(rend_cache, "foo2", two);
+
+ rend_cache_clean(time(NULL), REND_CACHE_TYPE_CLIENT);
+ tt_int_op(strmap_size(rend_cache), OP_EQ, 0);
+
+ // Test with one old entry and one newer entry
+ one = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ two = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ desc_one = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ desc_two = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ one->parsed = desc_one;
+ two->parsed = desc_two;
+
+ desc_one->timestamp = (time(NULL) + TIME_IN_THE_PAST) - 10;
+ desc_two->timestamp = time(NULL) - 100;
+ desc_one->pk = pk_generate(0);
+ desc_two->pk = pk_generate(1);
+
+ strmap_set_lc(rend_cache, "foo1", one);
+ strmap_set_lc(rend_cache, "foo2", two);
+
+ rend_cache_clean(time(NULL), REND_CACHE_TYPE_CLIENT);
+ tt_int_op(strmap_size(rend_cache), OP_EQ, 1);
+
+ iter = strmap_iter_init(rend_cache);
+ strmap_iter_get(iter, &key, &val);
+ tt_str_op(key, OP_EQ, "foo2");
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_failure_entry_new(void *data)
+{
+ rend_cache_failure_t *failure;
+
+ (void)data;
+
+ failure = rend_cache_failure_entry_new();
+ tt_assert(failure);
+ tt_int_op(digestmap_size(failure->intro_failures), OP_EQ, 0);
+
+ done:
+ rend_cache_failure_entry_free(failure);
+}
+
+static void
+test_rend_cache_failure_entry_free(void *data)
+{
+ (void)data;
+
+ // Test that it can deal with a NULL argument
+ rend_cache_failure_entry_free(NULL);
+
+ /* done: */
+ /* (void)0; */
+}
+
+static void
+test_rend_cache_failure_clean(void *data)
+{
+ rend_cache_failure_t *failure;
+ rend_cache_failure_intro_t *ip_one, *ip_two;
+
+ const char key_one[DIGEST_LEN] = "ip1";
+ const char key_two[DIGEST_LEN] = "ip2";
+
+ (void)data;
+
+ rend_cache_init();
+
+ // Test with empty failure cache
+ rend_cache_failure_clean(time(NULL));
+ tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 0);
+
+ // Test with one empty failure entry
+ failure = rend_cache_failure_entry_new();
+ strmap_set_lc(rend_cache_failure, "foo1", failure);
+ rend_cache_failure_clean(time(NULL));
+ tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 0);
+
+ // Test with one new intro point
+ failure = rend_cache_failure_entry_new();
+ ip_one = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+ digestmap_set(failure->intro_failures, key_one, ip_one);
+ strmap_set_lc(rend_cache_failure, "foo1", failure);
+ rend_cache_failure_clean(time(NULL));
+ tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 1);
+
+ // Test with one old intro point
+ rend_cache_failure_purge();
+ failure = rend_cache_failure_entry_new();
+ ip_one = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+ ip_one->created_ts = time(NULL) - 7*60;
+ digestmap_set(failure->intro_failures, key_one, ip_one);
+ strmap_set_lc(rend_cache_failure, "foo1", failure);
+ rend_cache_failure_clean(time(NULL));
+ tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 0);
+
+ // Test with one old intro point and one new one
+ rend_cache_failure_purge();
+ failure = rend_cache_failure_entry_new();
+ ip_one = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+ ip_one->created_ts = time(NULL) - 7*60;
+ digestmap_set(failure->intro_failures, key_one, ip_one);
+ ip_two = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+ ip_two->created_ts = time(NULL) - 2*60;
+ digestmap_set(failure->intro_failures, key_two, ip_two);
+ strmap_set_lc(rend_cache_failure, "foo1", failure);
+ rend_cache_failure_clean(time(NULL));
+ tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 1);
+ tt_int_op(digestmap_size(failure->intro_failures), OP_EQ, 1);
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_failure_remove(void *data)
+{
+ rend_service_descriptor_t *desc;
+ (void)data;
+
+ rend_cache_init();
+
+ // Test that it deals well with a NULL desc
+ rend_cache_failure_remove(NULL);
+
+ // Test a descriptor that isn't in the cache
+ desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ desc->pk = pk_generate(0);
+ rend_cache_failure_remove(desc);
+
+ // There seems to not exist any way of getting rend_cache_failure_remove()
+ // to fail because of a problem with rend_get_service_id from here
+ rend_cache_free_all();
+
+ rend_service_descriptor_free(desc);
+ /* done: */
+ /* (void)0; */
+}
+
+static void
+test_rend_cache_free_all(void *data)
+{
+ rend_cache_failure_t *failure;
+ rend_cache_entry_t *one;
+ rend_service_descriptor_t *desc_one;
+
+ (void)data;
+
+ rend_cache_init();
+
+ failure = rend_cache_failure_entry_new();
+ strmap_set_lc(rend_cache_failure, "foo1", failure);
+
+ one = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ desc_one = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ one->parsed = desc_one;
+ desc_one->timestamp = time(NULL) + TIME_IN_THE_PAST;
+ desc_one->pk = pk_generate(0);
+ strmap_set_lc(rend_cache, "foo1", one);
+
+ rend_cache_free_all();
+
+ tt_assert(!rend_cache);
+ tt_assert(!rend_cache_v2_dir);
+ tt_assert(!rend_cache_failure);
+ tt_assert(!rend_cache_total_allocation);
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_entry_free(void *data)
+{
+ (void)data;
+ rend_cache_entry_t *e;
+
+ // Handles NULL correctly
+ rend_cache_entry_free(NULL);
+
+ // Handles NULL descriptor correctly
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ rend_cache_entry_free(e);
+
+ // Handles non-NULL descriptor correctly
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ e->desc = (char *)malloc(10);
+ rend_cache_entry_free(e);
+
+ /* done: */
+ /* (void)0; */
+}
+
+static void
+test_rend_cache_purge(void *data)
+{
+ (void)data;
+
+ // Deals with a NULL rend_cache
+ rend_cache_purge();
+ tt_assert(rend_cache);
+ tt_assert(strmap_size(rend_cache) == 0);
+
+ // Deals with existing rend_cache
+ rend_cache_free_all();
+ rend_cache_init();
+ tt_assert(rend_cache);
+ tt_assert(strmap_size(rend_cache) == 0);
+
+ rend_cache_purge();
+ tt_assert(rend_cache);
+ tt_assert(strmap_size(rend_cache) == 0);
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_failure_intro_add(void *data)
+{
+ (void)data;
+ rend_cache_failure_t *fail_entry;
+ rend_cache_failure_intro_t *entry;
+ const char identity[DIGEST_LEN] = "foo1";
+
+ rend_cache_init();
+
+ // Adds non-existing entry
+ cache_failure_intro_add((const uint8_t *) identity, "foo2",
+ INTRO_POINT_FAILURE_TIMEOUT);
+ fail_entry = strmap_get_lc(rend_cache_failure, "foo2");
+ tt_assert(fail_entry);
+ tt_int_op(digestmap_size(fail_entry->intro_failures), OP_EQ, 1);
+ entry = digestmap_get(fail_entry->intro_failures, identity);
+ tt_assert(entry);
+
+ // Adds existing entry
+ cache_failure_intro_add((const uint8_t *) identity, "foo2",
+ INTRO_POINT_FAILURE_TIMEOUT);
+ fail_entry = strmap_get_lc(rend_cache_failure, "foo2");
+ tt_assert(fail_entry);
+ tt_int_op(digestmap_size(fail_entry->intro_failures), OP_EQ, 1);
+ entry = digestmap_get(fail_entry->intro_failures, identity);
+ tt_assert(entry);
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_intro_failure_note(void *data)
+{
+ (void)data;
+ rend_cache_failure_t *fail_entry;
+ rend_cache_failure_intro_t *entry;
+ const char key[DIGEST_LEN] = "foo1";
+
+ rend_cache_init();
+
+ // Test not found
+ rend_cache_intro_failure_note(INTRO_POINT_FAILURE_TIMEOUT,
+ (const uint8_t *) key, "foo2");
+ fail_entry = strmap_get_lc(rend_cache_failure, "foo2");
+ tt_assert(fail_entry);
+ tt_int_op(digestmap_size(fail_entry->intro_failures), OP_EQ, 1);
+ entry = digestmap_get(fail_entry->intro_failures, key);
+ tt_assert(entry);
+ tt_int_op(entry->failure_type, OP_EQ, INTRO_POINT_FAILURE_TIMEOUT);
+
+ // Test found
+ rend_cache_intro_failure_note(INTRO_POINT_FAILURE_UNREACHABLE,
+ (const uint8_t *) key, "foo2");
+ tt_int_op(entry->failure_type, OP_EQ, INTRO_POINT_FAILURE_UNREACHABLE);
+
+ done:
+ rend_cache_free_all();
+}
+
+#define NS_SUBMODULE clean_v2_descs_as_dir
+NS_DECL(int, hid_serv_responsible_for_desc_id, (const char *id));
+
+static int
+NS(hid_serv_responsible_for_desc_id)(const char *id)
+{
+ (void)id;
+ return hid_serv_responsible_for_desc_id_response;
+}
+
+static void
+test_rend_cache_clean_v2_descs_as_dir(void *data)
+{
+ rend_cache_entry_t *e;
+ time_t now;
+ rend_service_descriptor_t *desc;
+ now = time(NULL);
+ const char key[DIGEST_LEN] = "abcde";
+
+ (void)data;
+
+ NS_MOCK(hid_serv_responsible_for_desc_id);
+ rend_cache_init();
+
+ // Test running with an empty cache
+ rend_cache_clean_v2_descs_as_dir(now, 0);
+ tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
+
+ // Test with only one new entry
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ e->last_served = now;
+ desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ desc->timestamp = now;
+ desc->pk = pk_generate(0);
+ e->parsed = desc;
+ digestmap_set(rend_cache_v2_dir, key, e);
+
+ hid_serv_responsible_for_desc_id_response = 1;
+ rend_cache_clean_v2_descs_as_dir(now, 0);
+ tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 1);
+
+ // Test with one old entry
+ desc->timestamp = now - (REND_CACHE_MAX_AGE + REND_CACHE_MAX_SKEW + 1000);
+ rend_cache_clean_v2_descs_as_dir(now, 0);
+ tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
+
+ // Test with one entry that is not under the responsibility of this
+ // hidden service
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ e->last_served = now;
+ desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ desc->timestamp = now;
+ desc->pk = pk_generate(0);
+ e->parsed = desc;
+ digestmap_set(rend_cache_v2_dir, key, e);
+
+ hid_serv_responsible_for_desc_id_response = 0;
+ rend_cache_clean_v2_descs_as_dir(now, 0);
+ tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
+
+ // Test with one entry that has an old last served
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ e->last_served = now - (REND_CACHE_MAX_AGE + REND_CACHE_MAX_SKEW + 1000);
+ desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ desc->timestamp = now;
+ desc->pk = pk_generate(0);
+ e->parsed = desc;
+ digestmap_set(rend_cache_v2_dir, key, e);
+
+ hid_serv_responsible_for_desc_id_response = 1;
+ rend_cache_clean_v2_descs_as_dir(now, 0);
+ tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
+
+ // Test a run through asking for a large force_remove
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ e->last_served = now;
+ desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+ desc->timestamp = now;
+ desc->pk = pk_generate(0);
+ e->parsed = desc;
+ digestmap_set(rend_cache_v2_dir, key, e);
+
+ hid_serv_responsible_for_desc_id_response = 1;
+ rend_cache_clean_v2_descs_as_dir(now, 20000);
+ tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 1);
+
+ done:
+ NS_UNMOCK(hid_serv_responsible_for_desc_id);
+ rend_cache_free_all();
+}
+
+#undef NS_SUBMODULE
+
+static void
+test_rend_cache_entry_allocation(void *data)
+{
+ (void)data;
+
+ size_t ret;
+ rend_cache_entry_t *e = NULL;
+
+ // Handles a null argument
+ ret = rend_cache_entry_allocation(NULL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ // Handles a non-null argument
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ ret = rend_cache_entry_allocation(e);
+ tt_int_op(ret, OP_GT, sizeof(rend_cache_entry_t));
+
+ done:
+ tor_free(e);
+}
+
+static void
+test_rend_cache_failure_intro_entry_free(void *data)
+{
+ (void)data;
+ rend_cache_failure_intro_t *entry;
+
+ // Handles a null argument
+ rend_cache_failure_intro_entry_free(NULL);
+
+ // Handles a non-null argument
+ entry = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+ rend_cache_failure_intro_entry_free(entry);
+}
+
+static void
+test_rend_cache_failure_purge(void *data)
+{
+ (void)data;
+
+ // Handles a null failure cache
+ strmap_free(rend_cache_failure, rend_cache_failure_entry_free_);
+ rend_cache_failure = NULL;
+
+ rend_cache_failure_purge();
+
+ tt_ptr_op(rend_cache_failure, OP_NE, NULL);
+ tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 0);
+
+ done:
+ rend_cache_free_all();
+}
+
+static void
+test_rend_cache_validate_intro_point_failure(void *data)
+{
+ (void)data;
+ rend_service_descriptor_t *desc = NULL;
+ char *service_id = NULL;
+ rend_intro_point_t *intro = NULL;
+ const char *identity = NULL;
+ rend_cache_failure_t *failure;
+ rend_cache_failure_intro_t *ip;
+
+ rend_cache_init();
+
+ create_descriptor(&desc, &service_id, 3);
+ desc->timestamp = time(NULL) + RECENT_TIME;
+
+ intro = (rend_intro_point_t *)smartlist_get(desc->intro_nodes, 0);
+ identity = intro->extend_info->identity_digest;
+
+ failure = rend_cache_failure_entry_new();
+ ip = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+ digestmap_set(failure->intro_failures, identity, ip);
+ strmap_set_lc(rend_cache_failure, service_id, failure);
+
+ // Test when we have an intro point in our cache
+ validate_intro_point_failure(desc, service_id);
+ tt_int_op(smartlist_len(desc->intro_nodes), OP_EQ, 2);
+
+ done:
+ rend_cache_free_all();
+ rend_service_descriptor_free(desc);
+ tor_free(service_id);
+}
+
+struct testcase_t rend_cache_tests[] = {
+ { "init", test_rend_cache_init, 0, NULL, NULL },
+ { "decrement_allocation", test_rend_cache_decrement_allocation, 0,
+ NULL, NULL },
+ { "increment_allocation", test_rend_cache_increment_allocation, 0,
+ NULL, NULL },
+ { "clean", test_rend_cache_clean, TT_FORK, NULL, NULL },
+ { "clean_v2_descs_as_dir", test_rend_cache_clean_v2_descs_as_dir, 0,
+ NULL, NULL },
+ { "entry_allocation", test_rend_cache_entry_allocation, 0, NULL, NULL },
+ { "entry_free", test_rend_cache_entry_free, 0, NULL, NULL },
+ { "failure_intro_entry_free", test_rend_cache_failure_intro_entry_free, 0,
+ NULL, NULL },
+ { "free_all", test_rend_cache_free_all, 0, NULL, NULL },
+ { "purge", test_rend_cache_purge, 0, NULL, NULL },
+ { "failure_clean", test_rend_cache_failure_clean, 0, NULL, NULL },
+ { "failure_entry_new", test_rend_cache_failure_entry_new, 0, NULL, NULL },
+ { "failure_entry_free", test_rend_cache_failure_entry_free, 0, NULL, NULL },
+ { "failure_intro_add", test_rend_cache_failure_intro_add, 0, NULL, NULL },
+ { "failure_intro_entry_new", test_rend_cache_failure_intro_entry_new, 0,
+ NULL, NULL },
+ { "failure_intro_lookup", test_rend_cache_failure_intro_lookup, 0,
+ NULL, NULL },
+ { "failure_purge", test_rend_cache_failure_purge, 0, NULL, NULL },
+ { "failure_remove", test_rend_cache_failure_remove, 0, NULL, NULL },
+ { "intro_failure_note", test_rend_cache_intro_failure_note, 0, NULL, NULL },
+ { "lookup", test_rend_cache_lookup_entry, 0, NULL, NULL },
+ { "lookup_v2_desc_as_dir", test_rend_cache_lookup_v2_desc_as_dir, 0,
+ NULL, NULL },
+ { "store_v2_desc_as_client", test_rend_cache_store_v2_desc_as_client, 0,
+ NULL, NULL },
+ { "store_v2_desc_as_client_with_different_time",
+ test_rend_cache_store_v2_desc_as_client_with_different_time, 0,
+ NULL, NULL },
+ { "store_v2_desc_as_dir", test_rend_cache_store_v2_desc_as_dir, 0,
+ NULL, NULL },
+ { "store_v2_desc_as_dir_with_different_time",
+ test_rend_cache_store_v2_desc_as_dir_with_different_time, 0, NULL, NULL },
+ { "store_v2_desc_as_dir_with_different_content",
+ test_rend_cache_store_v2_desc_as_dir_with_different_content, 0,
+ NULL, NULL },
+ { "validate_intro_point_failure",
+ test_rend_cache_validate_intro_point_failure, 0, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_replay.c b/src/test/test_replay.c
index a02c160365..7a0f098776 100644
--- a/src/test/test_replay.c
+++ b/src/test/test_replay.c
@@ -17,6 +17,20 @@ static const char *test_buffer =
" occaecat cupidatat non proident, sunt in culpa qui officia deserunt"
" mollit anim id est laborum.";
+static const char *test_buffer_2 =
+ "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis"
+ " praesentium voluptatum deleniti atque corrupti quos dolores et quas"
+ " molestias excepturi sint occaecati cupiditate non provident, similique"
+ " sunt in culpa qui officia deserunt mollitia animi, id est laborum et"
+ " dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio."
+ " Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil"
+ " impedit quo minus id quod maxime placeat facere possimus, omnis voluptas"
+ " assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut"
+ " officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates"
+ " repudiandae sint et molestiae non recusandae. Itaque earum rerum hic"
+ " tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias"
+ " consequatur aut perferendis doloribus asperiores repellat.";
+
static void
test_replaycache_alloc(void *arg)
{
@@ -83,6 +97,12 @@ test_replaycache_miss(void *arg)
strlen(test_buffer), NULL);
tt_int_op(result,OP_EQ, 0);
+ /* make sure a different buffer misses as well */
+ result =
+ replaycache_add_and_test_internal(1200, NULL, test_buffer_2,
+ strlen(test_buffer_2), NULL);
+ tt_int_op(result,OP_EQ, 0);
+
/* poke the bad-parameter error case too */
result =
replaycache_add_and_test_internal(1200, NULL, test_buffer,
@@ -115,6 +135,18 @@ test_replaycache_hit(void *arg)
strlen(test_buffer), NULL);
tt_int_op(result,OP_EQ, 1);
+ /* make sure a different buffer misses then hits as well */
+
+ result =
+ replaycache_add_and_test_internal(1200, r, test_buffer_2,
+ strlen(test_buffer_2), NULL);
+ tt_int_op(result,OP_EQ, 0);
+
+ result =
+ replaycache_add_and_test_internal(1300, r, test_buffer_2,
+ strlen(test_buffer_2), NULL);
+ tt_int_op(result,OP_EQ, 1);
+
done:
if (r) replaycache_free(r);
@@ -245,7 +277,7 @@ test_replaycache_scrub(void *arg)
/* Make sure we hit the aging-out case too */
replaycache_scrub_if_needed_internal(1500, r);
/* Assert that we aged it */
- tt_int_op(digestmap_size(r->digests_seen),OP_EQ, 0);
+ tt_int_op(digest256map_size(r->digests_seen),OP_EQ, 0);
done:
if (r) replaycache_free(r);
diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c
index 381a592c5b..31533d070f 100644
--- a/src/test/test_routerlist.c
+++ b/src/test/test_routerlist.c
@@ -1,22 +1,49 @@
/* Copyright (c) 2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+#include "orconfig.h"
+#include <math.h>
+#include <time.h>
+
+#define DIRVOTE_PRIVATE
+#define NETWORKSTATUS_PRIVATE
#define ROUTERLIST_PRIVATE
+#define TOR_UNIT_TESTING
#include "or.h"
-#include "routerlist.h"
+#include "config.h"
+#include "connection.h"
+#include "container.h"
#include "directory.h"
+#include "dirvote.h"
+#include "networkstatus.h"
+#include "nodelist.h"
+#include "policies.h"
+#include "routerlist.h"
+#include "routerparse.h"
#include "test.h"
+#include "test_dir_common.h"
+
+extern const char AUTHORITY_CERT_1[];
+extern const char AUTHORITY_SIGNKEY_1[];
+extern const char AUTHORITY_CERT_2[];
+extern const char AUTHORITY_SIGNKEY_2[];
+extern const char AUTHORITY_CERT_3[];
+extern const char AUTHORITY_SIGNKEY_3[];
+
+void construct_consensus(const char **consensus_text_md);
/* 4 digests + 3 sep + pre + post + NULL */
static char output[4*BASE64_DIGEST256_LEN+3+2+2+1];
static void
mock_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
- const char *resource, int pds_flags)
+ const char *resource, int pds_flags,
+ download_want_authority_t want_authority)
{
(void)dir_purpose;
(void)router_purpose;
(void)pds_flags;
+ (void)want_authority;
tt_assert(resource);
strlcpy(output, resource, sizeof(output));
done:
@@ -92,12 +119,335 @@ test_routerlist_launch_descriptor_downloads(void *arg)
smartlist_free(downloadable);
}
+void
+construct_consensus(const char **consensus_text_md)
+{
+ networkstatus_t *vote = NULL;
+ networkstatus_t *v1 = NULL, *v2 = NULL, *v3 = NULL;
+ networkstatus_voter_info_t *voter = NULL;
+ authority_cert_t *cert1=NULL, *cert2=NULL, *cert3=NULL;
+ crypto_pk_t *sign_skey_1=NULL, *sign_skey_2=NULL, *sign_skey_3=NULL;
+ crypto_pk_t *sign_skey_leg=NULL;
+ time_t now = time(NULL);
+ smartlist_t *votes = NULL;
+ int n_vrs;
+
+ tt_assert(!dir_common_authority_pk_init(&cert1, &cert2, &cert3,
+ &sign_skey_1, &sign_skey_2,
+ &sign_skey_3));
+ sign_skey_leg = pk_generate(4);
+
+ dir_common_construct_vote_1(&vote, cert1, sign_skey_1,
+ &dir_common_gen_routerstatus_for_v3ns,
+ &v1, &n_vrs, now, 1);
+ networkstatus_vote_free(vote);
+ tt_assert(v1);
+ tt_int_op(n_vrs, ==, 4);
+ tt_int_op(smartlist_len(v1->routerstatus_list), ==, 4);
+
+ dir_common_construct_vote_2(&vote, cert2, sign_skey_2,
+ &dir_common_gen_routerstatus_for_v3ns,
+ &v2, &n_vrs, now, 1);
+ networkstatus_vote_free(vote);
+ tt_assert(v2);
+ tt_int_op(n_vrs, ==, 4);
+ tt_int_op(smartlist_len(v2->routerstatus_list), ==, 4);
+
+ dir_common_construct_vote_3(&vote, cert3, sign_skey_3,
+ &dir_common_gen_routerstatus_for_v3ns,
+ &v3, &n_vrs, now, 1);
+
+ tt_assert(v3);
+ tt_int_op(n_vrs, ==, 4);
+ tt_int_op(smartlist_len(v3->routerstatus_list), ==, 4);
+ networkstatus_vote_free(vote);
+ votes = smartlist_new();
+ smartlist_add(votes, v1);
+ smartlist_add(votes, v2);
+ smartlist_add(votes, v3);
+
+ *consensus_text_md = networkstatus_compute_consensus(votes, 3,
+ cert1->identity_key,
+ sign_skey_1,
+ "AAAAAAAAAAAAAAAAAAAA",
+ sign_skey_leg,
+ FLAV_MICRODESC);
+
+ tt_assert(*consensus_text_md);
+
+ done:
+ tor_free(voter);
+ networkstatus_vote_free(v1);
+ networkstatus_vote_free(v2);
+ networkstatus_vote_free(v3);
+}
+
+static void
+test_router_pick_directory_server_impl(void *arg)
+{
+ (void)arg;
+
+ networkstatus_t *con_md = NULL;
+ const char *consensus_text_md = NULL;
+ int flags = PDS_IGNORE_FASCISTFIREWALL|PDS_RETRY_IF_NO_SERVERS;
+ or_options_t *options = get_options_mutable();
+ const routerstatus_t *rs = NULL;
+ options->UseMicrodescriptors = 1;
+ char *router1_id = NULL, *router2_id = NULL, *router3_id = NULL;
+ node_t *node_router1 = NULL, *node_router2 = NULL, *node_router3 = NULL;
+ config_line_t *policy_line = NULL;
+ time_t now = time(NULL);
+ int tmp_dirport1, tmp_dirport3;
+
+ (void)arg;
+
+ /* No consensus available, fail early */
+ rs = router_pick_directory_server_impl(V3_DIRINFO, (const int) 0, NULL);
+ tt_assert(rs == NULL);
+
+ construct_consensus(&consensus_text_md);
+ tt_assert(consensus_text_md);
+ con_md = networkstatus_parse_vote_from_string(consensus_text_md, NULL,
+ NS_TYPE_CONSENSUS);
+ tt_assert(con_md);
+ tt_int_op(con_md->flavor,==, FLAV_MICRODESC);
+ tt_assert(con_md->routerstatus_list);
+ tt_int_op(smartlist_len(con_md->routerstatus_list), ==, 3);
+ tt_assert(!networkstatus_set_current_consensus_from_ns(con_md,
+ "microdesc"));
+ nodelist_set_consensus(con_md);
+ nodelist_assert_ok();
+
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ /* We should not fail now we have a consensus and routerstatus_list
+ * and nodelist are populated. */
+ tt_assert(rs != NULL);
+
+ /* Manipulate the nodes so we get the dir server we expect */
+ router1_id = tor_malloc(DIGEST_LEN);
+ memset(router1_id, TEST_DIR_ROUTER_ID_1, DIGEST_LEN);
+ router2_id = tor_malloc(DIGEST_LEN);
+ memset(router2_id, TEST_DIR_ROUTER_ID_2, DIGEST_LEN);
+ router3_id = tor_malloc(DIGEST_LEN);
+ memset(router3_id, TEST_DIR_ROUTER_ID_3, DIGEST_LEN);
+
+ node_router1 = node_get_mutable_by_id(router1_id);
+ node_router2 = node_get_mutable_by_id(router2_id);
+ node_router3 = node_get_mutable_by_id(router3_id);
+
+ node_router1->is_possible_guard = 1;
+
+ node_router1->is_running = 0;
+ node_router3->is_running = 0;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs != NULL);
+ tt_assert(tor_memeq(rs->identity_digest, router2_id, DIGEST_LEN));
+ rs = NULL;
+ node_router1->is_running = 1;
+ node_router3->is_running = 1;
+
+ node_router1->rs->is_v2_dir = 0;
+ node_router3->rs->is_v2_dir = 0;
+ tmp_dirport1 = node_router1->rs->dir_port;
+ tmp_dirport3 = node_router3->rs->dir_port;
+ node_router1->rs->dir_port = 0;
+ node_router3->rs->dir_port = 0;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs != NULL);
+ tt_assert(tor_memeq(rs->identity_digest, router2_id, DIGEST_LEN));
+ rs = NULL;
+ node_router1->rs->is_v2_dir = 1;
+ node_router3->rs->is_v2_dir = 1;
+ node_router1->rs->dir_port = tmp_dirport1;
+ node_router3->rs->dir_port = tmp_dirport3;
+
+ node_router1->is_valid = 0;
+ node_router3->is_valid = 0;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs != NULL);
+ tt_assert(tor_memeq(rs->identity_digest, router2_id, DIGEST_LEN));
+ rs = NULL;
+ node_router1->is_valid = 1;
+ node_router3->is_valid = 1;
+
+ flags |= PDS_FOR_GUARD;
+ node_router1->using_as_guard = 1;
+ node_router2->using_as_guard = 1;
+ node_router3->using_as_guard = 1;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs == NULL);
+ node_router1->using_as_guard = 0;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs != NULL);
+ tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN));
+ rs = NULL;
+ node_router2->using_as_guard = 0;
+ node_router3->using_as_guard = 0;
+
+ /* One not valid, one guard. This should leave one remaining */
+ node_router1->is_valid = 0;
+ node_router2->using_as_guard = 1;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs != NULL);
+ tt_assert(tor_memeq(rs->identity_digest, router3_id, DIGEST_LEN));
+ rs = NULL;
+ node_router1->is_valid = 1;
+ node_router2->using_as_guard = 0;
+
+ /* Manipulate overloaded */
+
+ node_router2->rs->last_dir_503_at = now;
+ node_router3->rs->last_dir_503_at = now;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs != NULL);
+ tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN));
+ node_router2->rs->last_dir_503_at = 0;
+ node_router3->rs->last_dir_503_at = 0;
+
+ /* Set a Fascist firewall */
+ flags &= ~ PDS_IGNORE_FASCISTFIREWALL;
+ policy_line = tor_malloc_zero(sizeof(config_line_t));
+ policy_line->key = tor_strdup("ReachableORAddresses");
+ policy_line->value = tor_strdup("accept *:442, reject *:*");
+ options->ReachableORAddresses = policy_line;
+ policies_parse_from_options(options);
+
+ node_router1->rs->or_port = 444;
+ node_router2->rs->or_port = 443;
+ node_router3->rs->or_port = 442;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs != NULL);
+ tt_assert(tor_memeq(rs->identity_digest, router3_id, DIGEST_LEN));
+ node_router1->rs->or_port = 442;
+ node_router2->rs->or_port = 443;
+ node_router3->rs->or_port = 444;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs != NULL);
+ tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN));
+
+ /* Fascist firewall and overloaded */
+ node_router1->rs->or_port = 442;
+ node_router2->rs->or_port = 443;
+ node_router3->rs->or_port = 442;
+ node_router3->rs->last_dir_503_at = now;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs != NULL);
+ tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN));
+ node_router3->rs->last_dir_503_at = 0;
+
+ /* Fascists against OR and Dir */
+ policy_line = tor_malloc_zero(sizeof(config_line_t));
+ policy_line->key = tor_strdup("ReachableAddresses");
+ policy_line->value = tor_strdup("accept *:80, reject *:*");
+ options->ReachableDirAddresses = policy_line;
+ policies_parse_from_options(options);
+ node_router1->rs->or_port = 442;
+ node_router2->rs->or_port = 441;
+ node_router3->rs->or_port = 443;
+ node_router1->rs->dir_port = 80;
+ node_router2->rs->dir_port = 80;
+ node_router3->rs->dir_port = 81;
+ node_router1->rs->last_dir_503_at = now;
+ rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
+ tt_assert(rs != NULL);
+ tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN));
+ node_router1->rs->last_dir_503_at = 0;
+
+ done:
+ if (router1_id)
+ tor_free(router1_id);
+ if (router2_id)
+ tor_free(router2_id);
+ if (router3_id)
+ tor_free(router3_id);
+ if (options->ReachableORAddresses ||
+ options->ReachableDirAddresses)
+ policies_free_all();
+}
+
+connection_t *mocked_connection = NULL;
+
+/* Mock connection_get_by_type_addr_port_purpose by returning
+ * mocked_connection. */
+static connection_t *
+mock_connection_get_by_type_addr_port_purpose(int type,
+ const tor_addr_t *addr,
+ uint16_t port, int purpose)
+{
+ (void)type;
+ (void)addr;
+ (void)port;
+ (void)purpose;
+
+ return mocked_connection;
+}
+
+#define TEST_ADDR_STR "127.0.0.1"
+#define TEST_DIR_PORT 12345
+
+static void
+test_routerlist_router_is_already_dir_fetching(void *arg)
+{
+ (void)arg;
+ tor_addr_port_t test_ap, null_addr_ap, zero_port_ap;
+
+ /* Setup */
+ tor_addr_parse(&test_ap.addr, TEST_ADDR_STR);
+ test_ap.port = TEST_DIR_PORT;
+ tor_addr_make_null(&null_addr_ap.addr, AF_INET6);
+ null_addr_ap.port = TEST_DIR_PORT;
+ tor_addr_parse(&zero_port_ap.addr, TEST_ADDR_STR);
+ zero_port_ap.port = 0;
+ MOCK(connection_get_by_type_addr_port_purpose,
+ mock_connection_get_by_type_addr_port_purpose);
+
+ /* Test that we never get 1 from a NULL connection */
+ mocked_connection = NULL;
+ tt_assert(router_is_already_dir_fetching(&test_ap, 1, 1) == 0);
+ tt_assert(router_is_already_dir_fetching(&test_ap, 1, 0) == 0);
+ tt_assert(router_is_already_dir_fetching(&test_ap, 0, 1) == 0);
+ /* We always expect 0 in these cases */
+ tt_assert(router_is_already_dir_fetching(&test_ap, 0, 0) == 0);
+ tt_assert(router_is_already_dir_fetching(NULL, 1, 1) == 0);
+ tt_assert(router_is_already_dir_fetching(&null_addr_ap, 1, 1) == 0);
+ tt_assert(router_is_already_dir_fetching(&zero_port_ap, 1, 1) == 0);
+
+ /* Test that we get 1 with a connection in the appropriate circumstances */
+ mocked_connection = connection_new(CONN_TYPE_DIR, AF_INET);
+ tt_assert(router_is_already_dir_fetching(&test_ap, 1, 1) == 1);
+ tt_assert(router_is_already_dir_fetching(&test_ap, 1, 0) == 1);
+ tt_assert(router_is_already_dir_fetching(&test_ap, 0, 1) == 1);
+
+ /* Test that we get 0 even with a connection in the appropriate
+ * circumstances */
+ tt_assert(router_is_already_dir_fetching(&test_ap, 0, 0) == 0);
+ tt_assert(router_is_already_dir_fetching(NULL, 1, 1) == 0);
+ tt_assert(router_is_already_dir_fetching(&null_addr_ap, 1, 1) == 0);
+ tt_assert(router_is_already_dir_fetching(&zero_port_ap, 1, 1) == 0);
+
+ done:
+ /* If a connection is never set up, connection_free chokes on it. */
+ if (mocked_connection) {
+ buf_free(mocked_connection->inbuf);
+ buf_free(mocked_connection->outbuf);
+ }
+ tor_free(mocked_connection);
+ UNMOCK(connection_get_by_type_addr_port_purpose);
+}
+
+#undef TEST_ADDR_STR
+#undef TEST_DIR_PORT
+
#define NODE(name, flags) \
{ #name, test_routerlist_##name, (flags), NULL, NULL }
+#define ROUTER(name,flags) \
+ { #name, test_router_##name, (flags), NULL, NULL }
struct testcase_t routerlist_tests[] = {
NODE(initiate_descriptor_downloads, 0),
NODE(launch_descriptor_downloads, 0),
+ NODE(router_is_already_dir_fetching, TT_FORK),
+ ROUTER(pick_directory_server_impl, TT_FORK),
END_OF_TESTCASES
};
diff --git a/src/test/test_routerset.c b/src/test/test_routerset.c
index 90dfb28c6b..74b39c0486 100644
--- a/src/test/test_routerset.c
+++ b/src/test/test_routerset.c
@@ -423,10 +423,10 @@ NS(test_main)(void *arg)
}
#undef NS_SUBMODULE
-#define NS_SUBMODULE ASPECT(routerset_parse, policy)
+#define NS_SUBMODULE ASPECT(routerset_parse, policy_wildcard)
/*
- * Structural test for routerset_parse, when given a valid policy.
+ * Structural test for routerset_parse, when given a valid wildcard policy.
*/
NS_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string,
@@ -470,6 +470,100 @@ NS(router_parse_addr_policy_item_from_string)(const char *s,
}
#undef NS_SUBMODULE
+#define NS_SUBMODULE ASPECT(routerset_parse, policy_ipv4)
+
+/*
+ * Structural test for routerset_parse, when given a valid IPv4 address
+ * literal policy.
+ */
+
+NS_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string,
+ (const char *s, int assume_action, int *bogus));
+
+addr_policy_t *NS(mock_addr_policy);
+
+static void
+NS(test_main)(void *arg)
+{
+ routerset_t *set;
+ const char *s;
+ int r;
+ (void)arg;
+
+ NS_MOCK(router_parse_addr_policy_item_from_string);
+ NS(mock_addr_policy) = tor_malloc_zero(sizeof(addr_policy_t));
+
+ set = routerset_new();
+ s = "127.0.0.1";
+ r = routerset_parse(set, s, "");
+ tt_int_op(r, OP_EQ, 0);
+ tt_int_op(smartlist_len(set->policies), OP_NE, 0);
+ tt_int_op(CALLED(router_parse_addr_policy_item_from_string), OP_EQ, 1);
+
+ done:
+ routerset_free(set);
+}
+
+addr_policy_t *
+NS(router_parse_addr_policy_item_from_string)(const char *s, int assume_action,
+ int *bogus)
+{
+ (void)s;
+ (void)assume_action;
+ CALLED(router_parse_addr_policy_item_from_string)++;
+ *bogus = 0;
+
+ return NS(mock_addr_policy);
+}
+
+#undef NS_SUBMODULE
+#define NS_SUBMODULE ASPECT(routerset_parse, policy_ipv6)
+
+/*
+ * Structural test for routerset_parse, when given a valid IPv6 address
+ * literal policy.
+ */
+
+NS_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string,
+ (const char *s, int assume_action, int *bad));
+
+addr_policy_t *NS(mock_addr_policy);
+
+static void
+NS(test_main)(void *arg)
+{
+ routerset_t *set;
+ const char *s;
+ int r;
+ (void)arg;
+
+ NS_MOCK(router_parse_addr_policy_item_from_string);
+ NS(mock_addr_policy) = tor_malloc_zero(sizeof(addr_policy_t));
+
+ set = routerset_new();
+ s = "::1";
+ r = routerset_parse(set, s, "");
+ tt_int_op(r, OP_EQ, 0);
+ tt_int_op(smartlist_len(set->policies), OP_NE, 0);
+ tt_int_op(CALLED(router_parse_addr_policy_item_from_string), OP_EQ, 1);
+
+ done:
+ routerset_free(set);
+}
+
+addr_policy_t *
+NS(router_parse_addr_policy_item_from_string)(const char *s,
+ int assume_action, int *bad)
+{
+ (void)s;
+ (void)assume_action;
+ CALLED(router_parse_addr_policy_item_from_string)++;
+ *bad = 0;
+
+ return NS(mock_addr_policy);
+}
+
+#undef NS_SUBMODULE
#define NS_SUBMODULE ASPECT(routerset_union, source_bad)
/*
@@ -2109,7 +2203,9 @@ struct testcase_t routerset_tests[] = {
TEST_CASE_ASPECT(routerset_parse, valid_hexdigest),
TEST_CASE_ASPECT(routerset_parse, valid_nickname),
TEST_CASE_ASPECT(routerset_parse, get_countryname),
- TEST_CASE_ASPECT(routerset_parse, policy),
+ TEST_CASE_ASPECT(routerset_parse, policy_wildcard),
+ TEST_CASE_ASPECT(routerset_parse, policy_ipv4),
+ TEST_CASE_ASPECT(routerset_parse, policy_ipv6),
TEST_CASE(routerset_subtract_nodes),
TEST_CASE_ASPECT(routerset_subtract_nodes, null_routerset),
TEST_CASE(routerset_to_string),
diff --git a/src/test/test_status.c b/src/test/test_status.c
index cbc8af188c..84a0f6c024 100644
--- a/src/test/test_status.c
+++ b/src/test/test_status.c
@@ -707,15 +707,18 @@ NS(logv)(int severity, log_domain_mask_t domain,
tt_ptr_op(strstr(funcname, "log_accounting"), OP_NE, NULL);
tt_ptr_op(suffix, OP_EQ, NULL);
tt_str_op(format, OP_EQ,
- "Heartbeat: Accounting enabled. Sent: %s / %s, Received: %s / %s. "
- "The current accounting interval ends on %s, in %s.");
+ "Heartbeat: Accounting enabled. Sent: %s, Received: %s, Used: %s / "
+ "%s, Rule: %s. The current accounting interval ends on %s, in %s.");
tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* acc_sent */
- tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* acc_max */
tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* acc_rcvd */
+ tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* acc_used */
tt_str_op(va_arg(ap, char *), OP_EQ, "0 kB"); /* acc_max */
- /* format_local_iso_time uses local tz, just check mins and secs. */
- tt_ptr_op(strstr(va_arg(ap, char *), ":01:00"),
- OP_NE, NULL); /* end_buf */
+ tt_str_op(va_arg(ap, char *), OP_EQ, "max"); /* acc_rule */
+ /* format_local_iso_time uses local tz, so we can't just compare
+ * the string against a constant */
+ char datetime[ISO_TIME_LEN+1];
+ format_local_iso_time(datetime, 60);
+ tt_str_op(va_arg(ap, char *), OP_EQ, datetime); /* end_buf */
tt_str_op(va_arg(ap, char *), OP_EQ, "0:01 hours"); /* remaining */
break;
case 2:
diff --git a/src/test/test_switch_id.c b/src/test/test_switch_id.c
new file mode 100644
index 0000000000..3d9c1c8bba
--- /dev/null
+++ b/src/test/test_switch_id.c
@@ -0,0 +1,191 @@
+/* Copyright (c) 2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/capability.h>
+#endif
+
+#define TEST_BUILT_WITH_CAPS 0
+#define TEST_HAVE_CAPS 1
+#define TEST_ROOT_CAN_BIND_LOW 2
+#define TEST_SETUID 3
+#define TEST_SETUID_KEEPCAPS 4
+#define TEST_SETUID_STRICT 5
+
+static const struct {
+ const char *name;
+ int test_id;
+} which_test[] = {
+ { "built-with-caps", TEST_BUILT_WITH_CAPS },
+ { "have-caps", TEST_HAVE_CAPS },
+ { "root-bind-low", TEST_ROOT_CAN_BIND_LOW },
+ { "setuid", TEST_SETUID },
+ { "setuid-keepcaps", TEST_SETUID_KEEPCAPS },
+ { "setuid-strict", TEST_SETUID_STRICT },
+ { NULL, 0 }
+};
+
+#if !defined(_WIN32)
+/* 0 on no, 1 on yes, -1 on failure. */
+static int
+check_can_bind_low_ports(void)
+{
+ int port;
+ struct sockaddr_in sin;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+
+ for (port = 600; port < 1024; ++port) {
+ sin.sin_port = htons(port);
+ tor_socket_t fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (! SOCKET_OK(fd)) {
+ perror("socket");
+ return -1;
+ }
+
+ int one = 1;
+ if (setsockopt(fd, SOL_SOCKET,SO_REUSEADDR, (void*)&one,
+ (socklen_t)sizeof(one))) {
+ perror("setsockopt");
+ tor_close_socket_simple(fd);
+ return -1;
+ }
+
+ int res = bind(fd, (struct sockaddr *)&sin, sizeof(sin));
+ tor_close_socket_simple(fd);
+
+ if (res == 0) {
+ /* bind was successful */
+ return 1;
+ } else if (errno == EACCES || errno == EPERM) {
+ /* Got a permission-denied error. */
+ return 0;
+ } else if (errno == EADDRINUSE) {
+ /* Huh; somebody is using that port. */
+ } else {
+ perror("bind");
+ }
+ }
+
+ return -1;
+}
+#endif
+
+int
+main(int argc, char **argv)
+{
+#if defined(_WIN32)
+ (void) argc;
+ (void) argv;
+
+ fprintf(stderr, "This test is not supported on your OS.\n");
+ return 77;
+#else
+ const char *username;
+ const char *testname;
+ if (argc != 3) {
+ fprintf(stderr, "I want 2 arguments: a username and a command.\n");
+ return 1;
+ }
+ if (getuid() != 0) {
+ fprintf(stderr, "This test only works when it's run as root.\n");
+ return 1;
+ }
+ username = argv[1];
+ testname = argv[2];
+ int test_id = -1;
+ int i;
+ for (i = 0; which_test[i].name; ++i) {
+ if (!strcmp(which_test[i].name, testname)) {
+ test_id = which_test[i].test_id;
+ break;
+ }
+ }
+ if (test_id == -1) {
+ fprintf(stderr, "Unrecognized test '%s'\n", testname);
+ return 1;
+ }
+
+#ifdef HAVE_LINUX_CAPABILITIES
+ const int have_cap_support = 1;
+#else
+ const int have_cap_support = 0;
+#endif
+
+ int okay;
+
+ init_logging(1);
+ log_severity_list_t sev;
+ memset(&sev, 0, sizeof(sev));
+ set_log_severity_config(LOG_WARN, LOG_ERR, &sev);
+ add_stream_log(&sev, "", fileno(stderr));
+
+ switch (test_id)
+ {
+ case TEST_BUILT_WITH_CAPS:
+ /* Succeed if we were built with capability support. */
+ okay = have_cap_support;
+ break;
+ case TEST_HAVE_CAPS:
+ /* Succeed if "capabilities work" == "we were built with capability
+ * support." */
+ okay = have_cap_support == have_capability_support();
+ break;
+ case TEST_ROOT_CAN_BIND_LOW:
+ /* Succeed if root can bind low ports. */
+ okay = check_can_bind_low_ports() == 1;
+ break;
+ case TEST_SETUID:
+ /* Succeed if we can do a setuid with no capability retention, and doing
+ * so makes us lose the ability to bind low ports */
+ case TEST_SETUID_KEEPCAPS:
+ /* Succeed if we can do a setuid with capability retention, and doing so
+ * does not make us lose the ability to bind low ports */
+ {
+ int keepcaps = (test_id == TEST_SETUID_KEEPCAPS);
+ okay = switch_id(username, keepcaps ? SWITCH_ID_KEEP_BINDLOW : 0) == 0;
+ if (okay) {
+ okay = check_can_bind_low_ports() == keepcaps;
+ }
+ break;
+ }
+ case TEST_SETUID_STRICT:
+ /* Succeed if, after a setuid, we cannot setuid back, and we cannot
+ * re-grab any capabilities. */
+ okay = switch_id(username, SWITCH_ID_KEEP_BINDLOW) == 0;
+ if (okay) {
+ /* We'd better not be able to setuid back! */
+ if (setuid(0) == 0 || errno != EPERM) {
+ okay = 0;
+ }
+ }
+#ifdef HAVE_LINUX_CAPABILITIES
+ if (okay) {
+ cap_t caps = cap_get_proc();
+ const cap_value_t caplist[] = {
+ CAP_SETUID,
+ };
+ cap_set_flag(caps, CAP_PERMITTED, 1, caplist, CAP_SET);
+ if (cap_set_proc(caps) == 0 || errno != EPERM) {
+ okay = 0;
+ }
+ cap_free(caps);
+ }
+#endif
+ break;
+ default:
+ fprintf(stderr, "Unsupported test '%s'\n", testname);
+ okay = 0;
+ break;
+ }
+
+ if (!okay) {
+ fprintf(stderr, "Test %s failed!\n", testname);
+ }
+
+ return (okay ? 0 : 1);
+#endif
+}
+
diff --git a/src/test/test_switch_id.sh b/src/test/test_switch_id.sh
new file mode 100755
index 0000000000..1b4e0998b5
--- /dev/null
+++ b/src/test/test_switch_id.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+if test "`id -u`" != '0'; then
+ echo "This test only works when run as root. Skipping." >&2
+ exit 77
+fi
+
+if test "`id -u nobody`" = ""; then
+ echo "This test requires that your system have a 'nobody' user. Sorry." >&2
+ exit 1
+fi
+
+"${builddir:-.}/src/test/test-switch-id" nobody setuid || exit 1
+"${builddir:-.}/src/test/test-switch-id" nobody root-bind-low || exit 1
+"${builddir:-.}/src/test/test-switch-id" nobody setuid-strict || exit 1
+"${builddir:-.}/src/test/test-switch-id" nobody built-with-caps || exit 0
+# ... Go beyond this point only if we were built with capability support.
+
+"${builddir:-.}/src/test/test-switch-id" nobody have-caps || exit 1
+"${builddir:-.}/src/test/test-switch-id" nobody setuid-keepcaps || exit 1
+
+
+echo "All okay"
+
+exit 0
diff --git a/src/test/test_threads.c b/src/test/test_threads.c
index 35f5dc8ea3..fe88c9470f 100644
--- a/src/test/test_threads.c
+++ b/src/test/test_threads.c
@@ -73,6 +73,8 @@ thread_test_func_(void* _s)
++thread_fns_failed;
tor_mutex_release(thread_test_mutex_);
+ tor_free(mycount);
+
tor_mutex_release(m);
spawn_exit();
diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c
new file mode 100644
index 0000000000..28847d4611
--- /dev/null
+++ b/src/test/test_tortls.c
@@ -0,0 +1,2834 @@
+/* Copyright (c) 2010-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define TORTLS_PRIVATE
+#define LOG_PRIVATE
+#include "orconfig.h"
+
+#ifdef _WIN32
+#include <winsock2.h>
+#endif
+
+#ifdef __GNUC__
+#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+#endif
+
+#if __GNUC__ && GCC_VERSION >= 402
+#if GCC_VERSION >= 406
+#pragma GCC diagnostic push
+#endif
+/* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in
+ * srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
+#include <openssl/opensslv.h>
+
+#include <openssl/ssl.h>
+#include <openssl/ssl3.h>
+#include <openssl/err.h>
+#include <openssl/asn1t.h>
+#include <openssl/x509.h>
+#include <openssl/rsa.h>
+#include <openssl/evp.h>
+#include <openssl/bn.h>
+
+#if __GNUC__ && GCC_VERSION >= 402
+#if GCC_VERSION >= 406
+#pragma GCC diagnostic pop
+#else
+#pragma GCC diagnostic warning "-Wredundant-decls"
+#endif
+#endif
+
+#include "or.h"
+#include "torlog.h"
+#include "config.h"
+#include "tortls.h"
+
+#include "test.h"
+#include "log_test_helpers.h"
+#define NS_MODULE tortls
+
+extern tor_tls_context_t *server_tls_context;
+extern tor_tls_context_t *client_tls_context;
+
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) \
+ && !defined(LIBRESSL_VERSION_NUMBER)
+#define OPENSSL_OPAQUE
+#define SSL_STATE_STR "before SSL initialization"
+#else
+#define SSL_STATE_STR "before/accept initialization"
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static SSL_METHOD *
+give_me_a_test_method(void)
+{
+ SSL_METHOD *method = tor_malloc_zero(sizeof(SSL_METHOD));
+ memcpy(method, TLSv1_method(), sizeof(SSL_METHOD));
+ return method;
+}
+
+static int
+fake_num_ciphers(void)
+{
+ return 0;
+}
+#endif
+
+static void
+test_tortls_errno_to_tls_error(void *data)
+{
+ (void) data;
+ tt_int_op(tor_errno_to_tls_error(SOCK_ERRNO(ECONNRESET)),OP_EQ,
+ TOR_TLS_ERROR_CONNRESET);
+ tt_int_op(tor_errno_to_tls_error(SOCK_ERRNO(ETIMEDOUT)),OP_EQ,
+ TOR_TLS_ERROR_TIMEOUT);
+ tt_int_op(tor_errno_to_tls_error(SOCK_ERRNO(EHOSTUNREACH)),OP_EQ,
+ TOR_TLS_ERROR_NO_ROUTE);
+ tt_int_op(tor_errno_to_tls_error(SOCK_ERRNO(ENETUNREACH)),OP_EQ,
+ TOR_TLS_ERROR_NO_ROUTE);
+ tt_int_op(tor_errno_to_tls_error(SOCK_ERRNO(ECONNREFUSED)),OP_EQ,
+ TOR_TLS_ERROR_CONNREFUSED);
+ tt_int_op(tor_errno_to_tls_error(0),OP_EQ,TOR_TLS_ERROR_MISC);
+ done:
+ (void)1;
+}
+
+static void
+test_tortls_err_to_string(void *data)
+{
+ (void) data;
+ tt_str_op(tor_tls_err_to_string(1),OP_EQ,"[Not an error.]");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_ERROR_MISC),OP_EQ,"misc error");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_ERROR_IO),OP_EQ,"unexpected close");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_ERROR_CONNREFUSED),OP_EQ,
+ "connection refused");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_ERROR_CONNRESET),OP_EQ,
+ "connection reset");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_ERROR_NO_ROUTE),OP_EQ,
+ "host unreachable");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_ERROR_TIMEOUT),OP_EQ,
+ "connection timed out");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_CLOSE),OP_EQ,"closed");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_WANTREAD),OP_EQ,"want to read");
+ tt_str_op(tor_tls_err_to_string(TOR_TLS_WANTWRITE),OP_EQ,"want to write");
+ tt_str_op(tor_tls_err_to_string(-100),OP_EQ,"(unknown error code)");
+ done:
+ (void)1;
+}
+
+static int
+mock_tls_cert_matches_key(const tor_tls_t *tls, const tor_x509_cert_t *cert)
+{
+ (void) tls;
+ (void) cert; // XXXX look at this.
+ return 1;
+}
+
+static void
+test_tortls_tor_tls_new(void *data)
+{
+ (void) data;
+ MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key);
+ crypto_pk_t *key1 = NULL, *key2 = NULL;
+ SSL_METHOD *method = NULL;
+
+ key1 = pk_generate(2);
+ key2 = pk_generate(3);
+
+ tor_tls_t *tls = NULL;
+ tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER,
+ key1, key2, 86400), OP_EQ, 0);
+ tls = tor_tls_new(-1, 0);
+ tt_want(tls);
+ tor_tls_free(tls); tls = NULL;
+
+ SSL_CTX_free(client_tls_context->ctx);
+ client_tls_context->ctx = NULL;
+ tls = tor_tls_new(-1, 0);
+ tt_assert(!tls);
+
+#ifndef OPENSSL_OPAQUE
+ method = give_me_a_test_method();
+ SSL_CTX *ctx = SSL_CTX_new(method);
+ method->num_ciphers = fake_num_ciphers;
+ client_tls_context->ctx = ctx;
+ tls = tor_tls_new(-1, 0);
+ tt_assert(!tls);
+#endif
+
+ done:
+ UNMOCK(tor_tls_cert_matches_key);
+ crypto_pk_free(key1);
+ crypto_pk_free(key2);
+ tor_tls_free(tls);
+ tor_free(method);
+ tor_tls_free_all();
+}
+
+#define NS_MODULE tortls
+NS_DECL(void, logv, (int severity, log_domain_mask_t domain,
+ const char *funcname, const char *suffix,
+ const char *format, va_list ap));
+
+static void
+NS(logv)(int severity, log_domain_mask_t domain,
+ const char *funcname, const char *suffix, const char *format,
+ va_list ap)
+{
+ (void) severity;
+ (void) domain;
+ (void) funcname;
+ (void) suffix;
+ (void) format;
+ (void) ap; // XXXX look at this.
+ CALLED(logv)++;
+}
+
+static void
+test_tortls_tor_tls_get_error(void *data)
+{
+ (void) data;
+ MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key);
+ crypto_pk_t *key1 = NULL, *key2 = NULL;
+ key1 = pk_generate(2);
+ key2 = pk_generate(3);
+
+ tor_tls_t *tls = NULL;
+ tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER,
+ key1, key2, 86400), OP_EQ, 0);
+ tls = tor_tls_new(-1, 0);
+ NS_MOCK(logv);
+ tt_int_op(CALLED(logv), OP_EQ, 0);
+ tor_tls_get_error(tls, 0, 0,
+ (const char *)"test", 0, 0);
+ tt_int_op(CALLED(logv), OP_EQ, 1);
+
+ done:
+ UNMOCK(tor_tls_cert_matches_key);
+ NS_UNMOCK(logv);
+ crypto_pk_free(key1);
+ crypto_pk_free(key2);
+ tor_tls_free(tls);
+}
+
+static void
+test_tortls_get_state_description(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ char *buf;
+ SSL_CTX *ctx;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(SSLv23_method());
+
+ buf = tor_malloc_zero(1000);
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ tor_tls_get_state_description(NULL, buf, 20);
+ tt_str_op(buf, OP_EQ, "(No SSL object)");
+
+ SSL_free(tls->ssl);
+ tls->ssl = NULL;
+ tor_tls_get_state_description(tls, buf, 20);
+ tt_str_op(buf, OP_EQ, "(No SSL object)");
+
+ tls->ssl = SSL_new(ctx);
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in HANDSHAKE");
+
+ tls->state = TOR_TLS_ST_OPEN;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in OPEN");
+
+ tls->state = TOR_TLS_ST_GOTCLOSE;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in GOTCLOSE");
+
+ tls->state = TOR_TLS_ST_SENTCLOSE;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in SENTCLOSE");
+
+ tls->state = TOR_TLS_ST_CLOSED;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in CLOSED");
+
+ tls->state = TOR_TLS_ST_RENEGOTIATE;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in RENEGOTIATE");
+
+ tls->state = TOR_TLS_ST_BUFFEREVENT;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR);
+
+ tls->state = 7;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in unknown TLS state");
+
+ done:
+ SSL_CTX_free(ctx);
+ SSL_free(tls->ssl);
+ tor_free(buf);
+ tor_free(tls);
+}
+
+extern int tor_tls_object_ex_data_index;
+
+static void
+test_tortls_get_by_ssl(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ tor_tls_t *res;
+ SSL_CTX *ctx;
+ SSL *ssl;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
+ ctx = SSL_CTX_new(SSLv23_method());
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->magic = TOR_TLS_MAGIC;
+
+ ssl = SSL_new(ctx);
+
+ res = tor_tls_get_by_ssl(ssl);
+ tt_assert(!res);
+
+ SSL_set_ex_data(ssl, tor_tls_object_ex_data_index, tls);
+
+ res = tor_tls_get_by_ssl(ssl);
+ tt_assert(res == tls);
+
+ done:
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+ tor_free(tls);
+}
+
+static void
+test_tortls_allocate_tor_tls_object_ex_data_index(void *ignored)
+{
+ (void)ignored;
+ int first;
+
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
+ first = tor_tls_object_ex_data_index;
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+ tt_int_op(first, OP_EQ, tor_tls_object_ex_data_index);
+
+ done:
+ (void)0;
+}
+
+static void
+test_tortls_log_one_error(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ SSL_CTX *ctx;
+ SSL *ssl = NULL;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(SSLv23_method());
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ int previous_log = setup_capture_of_logs(LOG_INFO);
+
+ tor_tls_log_one_error(NULL, 0, LOG_WARN, 0, "something");
+ expect_log_msg("TLS error while something: "
+ "(null) (in (null):(null):---)\n");
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, 0, LOG_WARN, 0, NULL);
+ expect_log_msg("TLS error: (null) "
+ "(in (null):(null):---)\n");
+
+ mock_clean_saved_logs();
+ tls->address = tor_strdup("127.hello");
+ tor_tls_log_one_error(tls, 0, LOG_WARN, 0, NULL);
+ expect_log_msg("TLS error with 127.hello: "
+ "(null) (in (null):(null):---)\n");
+ tor_free(tls->address);
+
+ mock_clean_saved_logs();
+ tls->address = tor_strdup("127.hello");
+ tor_tls_log_one_error(tls, 0, LOG_WARN, 0, "blarg");
+ expect_log_msg("TLS error while blarg with "
+ "127.hello: (null) (in (null):(null):---)\n");
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, 3), LOG_WARN, 0, NULL);
+ expect_log_msg("TLS error with 127.hello: "
+ "BN lib (in unknown library:(null):---)\n");
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_HTTP_REQUEST),
+ LOG_WARN, 0, NULL);
+ expect_log_severity(LOG_INFO);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_HTTPS_PROXY_REQUEST),
+ LOG_WARN, 0, NULL);
+ expect_log_severity(LOG_INFO);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_RECORD_LENGTH_MISMATCH),
+ LOG_WARN, 0, NULL);
+ expect_log_severity(LOG_INFO);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_RECORD_TOO_LARGE),
+ LOG_WARN, 0, NULL);
+ expect_log_severity(LOG_INFO);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_UNKNOWN_PROTOCOL),
+ LOG_WARN, 0, NULL);
+ expect_log_severity(LOG_INFO);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_UNSUPPORTED_PROTOCOL),
+ LOG_WARN, 0, NULL);
+ expect_log_severity(LOG_INFO);
+
+ tls->ssl = SSL_new(ctx);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, 0, LOG_WARN, 0, NULL);
+ expect_log_msg("TLS error with 127.hello: (null)"
+ " (in (null):(null):" SSL_STATE_STR ")\n");
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+ if (tls && tls->ssl)
+ SSL_free(tls->ssl);
+ if (tls)
+ tor_free(tls->address);
+ tor_free(tls);
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_get_error(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ int ret;
+ SSL_CTX *ctx;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(SSLv23_method());
+ int previous_log = setup_capture_of_logs(LOG_INFO);
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = SSL_new(ctx);
+ SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL);
+
+ ret = tor_tls_get_error(tls, 0, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_IO);
+ expect_log_msg("TLS error: unexpected close while"
+ " something (before/accept initialization)\n");
+
+ mock_clean_saved_logs();
+ ret = tor_tls_get_error(tls, 2, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_entry();
+
+ mock_clean_saved_logs();
+ ret = tor_tls_get_error(tls, 0, 1, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, -11);
+ expect_no_log_entry();
+
+ mock_clean_saved_logs();
+ ERR_clear_error();
+ ERR_put_error(ERR_LIB_BN, 2, -1, "somewhere.c", 99);
+ ret = tor_tls_get_error(tls, 0, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+ expect_log_msg("TLS error while something: (null)"
+ " (in bignum routines:(null):before/accept initialization)\n");
+
+ mock_clean_saved_logs();
+ ERR_clear_error();
+ tls->ssl->rwstate = SSL_READING;
+ SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_READ;
+ ret = tor_tls_get_error(tls, -1, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, TOR_TLS_WANTREAD);
+ expect_no_log_entry();
+
+ mock_clean_saved_logs();
+ ERR_clear_error();
+ tls->ssl->rwstate = SSL_READING;
+ SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_WRITE;
+ ret = tor_tls_get_error(tls, -1, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, TOR_TLS_WANTWRITE);
+ expect_no_log_entry();
+
+ mock_clean_saved_logs();
+ ERR_clear_error();
+ tls->ssl->rwstate = 0;
+ tls->ssl->shutdown = SSL_RECEIVED_SHUTDOWN;
+ tls->ssl->s3->warn_alert =SSL_AD_CLOSE_NOTIFY;
+ ret = tor_tls_get_error(tls, 0, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, TOR_TLS_CLOSE);
+ expect_log_entry();
+
+ mock_clean_saved_logs();
+ ret = tor_tls_get_error(tls, 0, 2, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, -10);
+ expect_no_log_entry();
+
+ mock_clean_saved_logs();
+ ERR_put_error(ERR_LIB_SYS, 2, -1, "somewhere.c", 99);
+ ret = tor_tls_get_error(tls, -1, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, -9);
+ expect_log_msg("TLS error while something: (null) (in system library:"
+ "connect:before/accept initialization)\n");
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ SSL_free(tls->ssl);
+ tor_free(tls);
+ SSL_CTX_free(ctx);
+}
+#endif
+
+static void
+test_tortls_always_accept_verify_cb(void *ignored)
+{
+ (void)ignored;
+ int ret;
+
+ ret = always_accept_verify_cb(0, NULL);
+ tt_int_op(ret, OP_EQ, 1);
+
+ done:
+ (void)0;
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_x509_cert_free(void *ignored)
+{
+ (void)ignored;
+ tor_x509_cert_t *cert;
+
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ tor_x509_cert_free(cert);
+
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ cert->cert = tor_malloc_zero(sizeof(X509));
+ cert->encoded = tor_malloc_zero(1);
+ tor_x509_cert_free(cert);
+}
+#endif
+
+static void
+test_tortls_x509_cert_get_id_digests(void *ignored)
+{
+ (void)ignored;
+ tor_x509_cert_t *cert;
+ common_digests_t *d;
+ const common_digests_t *res;
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ d = tor_malloc_zero(sizeof(common_digests_t));
+ d->d[0][0] = 42;
+
+ res = tor_x509_cert_get_id_digests(cert);
+ tt_assert(!res);
+
+ cert->pkey_digests_set = 1;
+ cert->pkey_digests = *d;
+ res = tor_x509_cert_get_id_digests(cert);
+ tt_int_op(res->d[0][0], OP_EQ, 42);
+
+ done:
+ tor_free(cert);
+ tor_free(d);
+}
+
+#ifndef OPENSSL_OPAQUE
+static int
+fixed_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+ (void) a; (void) b;
+ return 1;
+}
+
+static void
+fake_x509_free(X509 *cert)
+{
+ if (cert) {
+ if (cert->cert_info) {
+ if (cert->cert_info->key) {
+ if (cert->cert_info->key->pkey) {
+ tor_free(cert->cert_info->key->pkey);
+ }
+ tor_free(cert->cert_info->key);
+ }
+ tor_free(cert->cert_info);
+ }
+ tor_free(cert);
+ }
+}
+
+static void
+test_tortls_cert_matches_key(void *ignored)
+{
+ (void)ignored;
+ int res;
+ tor_tls_t *tls;
+ tor_x509_cert_t *cert;
+ X509 *one = NULL, *two = NULL;
+ EVP_PKEY_ASN1_METHOD *meth = EVP_PKEY_asn1_new(999, 0, NULL, NULL);
+ EVP_PKEY_asn1_set_public(meth, NULL, NULL, fixed_pub_cmp, NULL, NULL, NULL);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ one = tor_malloc_zero(sizeof(X509));
+ one->references = 1;
+ two = tor_malloc_zero(sizeof(X509));
+ two->references = 1;
+
+ res = tor_tls_cert_matches_key(tls, cert);
+ tt_int_op(res, OP_EQ, 0);
+
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+ tls->ssl->session->peer = one;
+ res = tor_tls_cert_matches_key(tls, cert);
+ tt_int_op(res, OP_EQ, 0);
+
+ cert->cert = two;
+ res = tor_tls_cert_matches_key(tls, cert);
+ tt_int_op(res, OP_EQ, 0);
+
+ one->cert_info = tor_malloc_zero(sizeof(X509_CINF));
+ one->cert_info->key = tor_malloc_zero(sizeof(X509_PUBKEY));
+ one->cert_info->key->pkey = tor_malloc_zero(sizeof(EVP_PKEY));
+ one->cert_info->key->pkey->references = 1;
+ one->cert_info->key->pkey->ameth = meth;
+ one->cert_info->key->pkey->type = 1;
+
+ two->cert_info = tor_malloc_zero(sizeof(X509_CINF));
+ two->cert_info->key = tor_malloc_zero(sizeof(X509_PUBKEY));
+ two->cert_info->key->pkey = tor_malloc_zero(sizeof(EVP_PKEY));
+ two->cert_info->key->pkey->references = 1;
+ two->cert_info->key->pkey->ameth = meth;
+ two->cert_info->key->pkey->type = 2;
+
+ res = tor_tls_cert_matches_key(tls, cert);
+ tt_int_op(res, OP_EQ, 0);
+
+ one->cert_info->key->pkey->type = 1;
+ two->cert_info->key->pkey->type = 1;
+ res = tor_tls_cert_matches_key(tls, cert);
+ tt_int_op(res, OP_EQ, 1);
+
+ done:
+ EVP_PKEY_asn1_free(meth);
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ tor_free(cert);
+ fake_x509_free(one);
+ fake_x509_free(two);
+}
+
+static void
+test_tortls_cert_get_key(void *ignored)
+{
+ (void)ignored;
+ tor_x509_cert_t *cert = NULL;
+ crypto_pk_t *res = NULL;
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ X509 *key = NULL;
+ key = tor_malloc_zero(sizeof(X509));
+ key->references = 1;
+
+ res = tor_tls_cert_get_key(cert);
+ tt_assert(!res);
+
+ cert->cert = key;
+ key->cert_info = tor_malloc_zero(sizeof(X509_CINF));
+ key->cert_info->key = tor_malloc_zero(sizeof(X509_PUBKEY));
+ key->cert_info->key->pkey = tor_malloc_zero(sizeof(EVP_PKEY));
+ key->cert_info->key->pkey->references = 1;
+ key->cert_info->key->pkey->type = 2;
+ res = tor_tls_cert_get_key(cert);
+ tt_assert(!res);
+
+ done:
+ fake_x509_free(key);
+ tor_free(cert);
+ crypto_pk_free(res);
+}
+#endif
+
+static void
+test_tortls_get_my_client_auth_key(void *ignored)
+{
+ (void)ignored;
+ crypto_pk_t *ret;
+ crypto_pk_t *expected;
+ tor_tls_context_t *ctx;
+ RSA *k = tor_malloc_zero(sizeof(RSA));
+
+ ctx = tor_malloc_zero(sizeof(tor_tls_context_t));
+ expected = crypto_new_pk_from_rsa_(k);
+ ctx->auth_key = expected;
+
+ client_tls_context = NULL;
+ ret = tor_tls_get_my_client_auth_key();
+ tt_assert(!ret);
+
+ client_tls_context = ctx;
+ ret = tor_tls_get_my_client_auth_key();
+ tt_assert(ret == expected);
+
+ done:
+ tor_free(expected);
+ tor_free(k);
+ tor_free(ctx);
+}
+
+static void
+test_tortls_get_my_certs(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_context_t *ctx;
+ const tor_x509_cert_t *link_cert_out = NULL;
+ const tor_x509_cert_t *id_cert_out = NULL;
+
+ ctx = tor_malloc_zero(sizeof(tor_tls_context_t));
+
+ client_tls_context = NULL;
+ ret = tor_tls_get_my_certs(0, NULL, NULL);
+ tt_int_op(ret, OP_EQ, -1);
+
+ server_tls_context = NULL;
+ ret = tor_tls_get_my_certs(1, NULL, NULL);
+ tt_int_op(ret, OP_EQ, -1);
+
+ client_tls_context = ctx;
+ ret = tor_tls_get_my_certs(0, NULL, NULL);
+ tt_int_op(ret, OP_EQ, 0);
+
+ client_tls_context = ctx;
+ ret = tor_tls_get_my_certs(0, &link_cert_out, &id_cert_out);
+ tt_int_op(ret, OP_EQ, 0);
+
+ server_tls_context = ctx;
+ ret = tor_tls_get_my_certs(1, &link_cert_out, &id_cert_out);
+ tt_int_op(ret, OP_EQ, 0);
+
+ done:
+ (void)1;
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_get_ciphersuite_name(void *ignored)
+{
+ (void)ignored;
+ const char *ret;
+ tor_tls_t *ctx;
+ ctx = tor_malloc_zero(sizeof(tor_tls_t));
+ ctx->ssl = tor_malloc_zero(sizeof(SSL));
+
+ ret = tor_tls_get_ciphersuite_name(ctx);
+ tt_str_op(ret, OP_EQ, "(NONE)");
+
+ done:
+ tor_free(ctx->ssl);
+ tor_free(ctx);
+}
+
+static SSL_CIPHER *
+get_cipher_by_name(const char *name)
+{
+ int i;
+ const SSL_METHOD *method = SSLv23_method();
+ int num = method->num_ciphers();
+ for (i = 0; i < num; ++i) {
+ const SSL_CIPHER *cipher = method->get_cipher(i);
+ const char *ciphername = SSL_CIPHER_get_name(cipher);
+ if (!strcmp(ciphername, name)) {
+ return (SSL_CIPHER *)cipher;
+ }
+ }
+
+ return NULL;
+}
+
+static SSL_CIPHER *
+get_cipher_by_id(uint16_t id)
+{
+ int i;
+ const SSL_METHOD *method = SSLv23_method();
+ int num = method->num_ciphers();
+ for (i = 0; i < num; ++i) {
+ const SSL_CIPHER *cipher = method->get_cipher(i);
+ if (id == (SSL_CIPHER_get_id(cipher) & 0xffff)) {
+ return (SSL_CIPHER *)cipher;
+ }
+ }
+
+ return NULL;
+}
+
+extern uint16_t v2_cipher_list[];
+
+static void
+test_tortls_classify_client_ciphers(void *ignored)
+{
+ (void)ignored;
+ int i;
+ int ret;
+ SSL_CTX *ctx;
+ SSL *ssl;
+ tor_tls_t *tls;
+ STACK_OF(SSL_CIPHER) *ciphers;
+ SSL_CIPHER *tmp_cipher;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->magic = TOR_TLS_MAGIC;
+
+ ctx = SSL_CTX_new(TLSv1_method());
+ ssl = SSL_new(ctx);
+ tls->ssl = ssl;
+
+ ciphers = sk_SSL_CIPHER_new_null();
+
+ ret = tor_tls_classify_client_ciphers(ssl, NULL);
+ tt_int_op(ret, OP_EQ, -1);
+
+ SSL_set_ex_data(ssl, tor_tls_object_ex_data_index, tls);
+ tls->client_cipher_list_type = 42;
+
+ ret = tor_tls_classify_client_ciphers(ssl, NULL);
+ tt_int_op(ret, OP_EQ, 42);
+
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 1);
+
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, SSL_get_ciphers(ssl));
+ tt_int_op(ret, OP_EQ, 3);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 3);
+
+ SSL_CIPHER *one = get_cipher_by_name(TLS1_TXT_DHE_RSA_WITH_AES_128_SHA),
+ *two = get_cipher_by_name(TLS1_TXT_DHE_RSA_WITH_AES_256_SHA),
+ *three = get_cipher_by_name(SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA),
+ *four = NULL;
+ sk_SSL_CIPHER_push(ciphers, one);
+ sk_SSL_CIPHER_push(ciphers, two);
+ sk_SSL_CIPHER_push(ciphers, three);
+ sk_SSL_CIPHER_push(ciphers, four);
+
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 1);
+
+ sk_SSL_CIPHER_zero(ciphers);
+
+ one = get_cipher_by_name("ECDH-RSA-AES256-GCM-SHA384");
+ one->id = 0x00ff;
+ two = get_cipher_by_name("ECDH-RSA-AES128-GCM-SHA256");
+ two->id = 0x0000;
+ sk_SSL_CIPHER_push(ciphers, one);
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 3);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 3);
+
+ sk_SSL_CIPHER_push(ciphers, two);
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 3);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 3);
+
+ one->id = 0xC00A;
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 3);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 3);
+
+ sk_SSL_CIPHER_zero(ciphers);
+ for (i=0; v2_cipher_list[i]; i++) {
+ tmp_cipher = get_cipher_by_id(v2_cipher_list[i]);
+ tt_assert(tmp_cipher);
+ sk_SSL_CIPHER_push(ciphers, tmp_cipher);
+ }
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 2);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 2);
+
+ done:
+ sk_SSL_CIPHER_free(ciphers);
+ SSL_free(tls->ssl);
+ tor_free(tls);
+ SSL_CTX_free(ctx);
+}
+#endif
+
+static void
+test_tortls_client_is_using_v2_ciphers(void *ignored)
+{
+ (void)ignored;
+
+#ifdef HAVE_SSL_GET_CLIENT_CIPHERS
+ tt_skip();
+ done:
+ (void)1;
+#else
+ int ret;
+ SSL_CTX *ctx;
+ SSL *ssl;
+ SSL_SESSION *sess;
+ STACK_OF(SSL_CIPHER) *ciphers;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(TLSv1_method());
+ ssl = SSL_new(ctx);
+ sess = SSL_SESSION_new();
+
+ ret = tor_tls_client_is_using_v2_ciphers(ssl);
+ tt_int_op(ret, OP_EQ, -1);
+
+ ssl->session = sess;
+ ret = tor_tls_client_is_using_v2_ciphers(ssl);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ciphers = sk_SSL_CIPHER_new_null();
+ SSL_CIPHER *one = get_cipher_by_name("ECDH-RSA-AES256-GCM-SHA384");
+ one->id = 0x00ff;
+ sk_SSL_CIPHER_push(ciphers, one);
+ sess->ciphers = ciphers;
+ ret = tor_tls_client_is_using_v2_ciphers(ssl);
+ tt_int_op(ret, OP_EQ, 1);
+ done:
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+#endif
+}
+
+#ifndef OPENSSL_OPAQUE
+static X509 *fixed_try_to_extract_certs_from_tls_cert_out_result = NULL;
+static X509 *fixed_try_to_extract_certs_from_tls_id_cert_out_result = NULL;
+
+static void
+fixed_try_to_extract_certs_from_tls(int severity, tor_tls_t *tls,
+ X509 **cert_out, X509 **id_cert_out)
+{
+ (void) severity;
+ (void) tls;
+ *cert_out = fixed_try_to_extract_certs_from_tls_cert_out_result;
+ *id_cert_out = fixed_try_to_extract_certs_from_tls_id_cert_out_result;
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static const char* notCompletelyValidCertString =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICVjCCAb8CAg37MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG\n"
+ "A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE\n"
+ "MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl\n"
+ "YiBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBmcmFuazRkZC5jb20wHhcNMTIw\n"
+ "ODIyMDUyNzIzWhcNMTcwODIxMDUyNzIzWjBKMQswCQYDVQQGEwJKUDEOMAwGA1UE\n"
+ "CAwFVG9reW8xETAPBgNVBAoMCEZyYW5rNEREMRgwFgYDVQQDDA93d3cuZXhhbXBs\n"
+ "ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMYBBrx5PlP0WNI/ZdzD\n"
+ "+6Pktmurn+F2kQYbtc7XQh8/LTBvCo+P6iZoLEmUA9e7EXLRxgU1CVqeAi7QcAn9\n"
+ "MwBlc8ksFJHB0rtf9pmf8Oza9E0Bynlq/4/Kb1x+d+AyhL7oK9tQwB24uHOueHi1\n"
+ "C/iVv8CSWKiYe6hzN1txYe8rAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAASPdjigJ\n"
+ "kXCqKWpnZ/Oc75EUcMi6HztaW8abUMlYXPIgkV2F7YanHOB7K4f7OOLjiz8DTPFf\n"
+ "jC9UeuErhaA/zzWi8ewMTFZW/WshOrm3fNvcMrMLKtH534JKvcdMg6qIdjTFINIr\n"
+ "evnAhf0cwULaebn+lMs8Pdl7y37+sfluVok=\n"
+ "-----END CERTIFICATE-----\n";
+#endif
+
+static const char* validCertString = "-----BEGIN CERTIFICATE-----\n"
+ "MIIDpTCCAY0CAg3+MA0GCSqGSIb3DQEBBQUAMF4xCzAJBgNVBAYTAlVTMREwDwYD\n"
+ "VQQIDAhJbGxpbm9pczEQMA4GA1UEBwwHQ2hpY2FnbzEUMBIGA1UECgwLVG9yIFRl\n"
+ "c3RpbmcxFDASBgNVBAMMC1RvciBUZXN0aW5nMB4XDTE1MDkwNjEzMzk1OVoXDTQz\n"
+ "MDEyMjEzMzk1OVowVjELMAkGA1UEBhMCVVMxEDAOBgNVBAcMB0NoaWNhZ28xFDAS\n"
+ "BgNVBAoMC1RvciBUZXN0aW5nMR8wHQYDVQQDDBZ0ZXN0aW5nLnRvcnByb2plY3Qu\n"
+ "b3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDoT6uyVVhWyOF3wkHjjYbd\n"
+ "nKaykyRv4JVtKQdZ4OpEErmX1zw4MmyzpQNV6iR4bQnWiyLfzyVJMZDIC/WILBfX\n"
+ "w2Pza/yuLgUvDc3twMuhOACzOQVO8PrEF/aVv2+hbCCy2udXvKhnYn+CCXl3ozc8\n"
+ "XcKYvujTXDyvGWY3xwAjlQIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQCUvnhzQWuQ\n"
+ "MrN+pERkE+zcTI/9dGS90rUMMLgu8VDNqTa0TUQh8uO0EQ6uDvI8Js6e8tgwS0BR\n"
+ "UBahqb7ZHv+rejGCBr5OudqD+x4STiiuPNJVs86JTLN8SpM9CHjIBH5WCCN2KOy3\n"
+ "mevNoRcRRyYJzSFULCunIK6FGulszigMYGscrO4oiTkZiHPh9KvWT40IMiHfL+Lw\n"
+ "EtEWiLex6064LcA2YQ1AMuSZyCexks63lcfaFmQbkYOKqXa1oLkIRuDsOaSVjTfe\n"
+ "vec+X6jvf12cFTKS5WIeqkKF2Irt+dJoiHEGTe5RscUMN/f+gqHPzfFz5dR23sxo\n"
+ "g+HC6MZHlFkLAOx3wW6epPS8A/m1mw3zMPoTnb2U2YYt8T0dJMMlUn/7Y1sEAa+a\n"
+ "dSTMaeUf6VnJ//11m454EZl1to9Z7oJOgqmFffSrdD4BGIWe8f7hhW6L1Enmqe/J\n"
+ "BKL3wbzZh80O1W0bndAwhnEEhlzneFY84cbBo9pmVxpODHkUcStpr5Z7pBDrcL21\n"
+ "Ss/aB/1YrsVXhdvJdOGxl3Mnl9dUY57CympLGlT8f0pPS6GAKOelECOhFMHmJd8L\n"
+ "dj3XQSmKtYHevZ6IvuMXSlB/fJvSjSlkCuLo5+kJoaqPuRu+i/S1qxeRy3CBwmnE\n"
+ "LdSNdcX4N79GQJ996PA8+mUCQG7YRtK+WA==\n"
+ "-----END CERTIFICATE-----\n";
+
+static const char* caCertString = "-----BEGIN CERTIFICATE-----\n"
+ "MIIFjzCCA3egAwIBAgIJAKd5WgyfPMYRMA0GCSqGSIb3DQEBCwUAMF4xCzAJBgNV\n"
+ "BAYTAlVTMREwDwYDVQQIDAhJbGxpbm9pczEQMA4GA1UEBwwHQ2hpY2FnbzEUMBIG\n"
+ "A1UECgwLVG9yIFRlc3RpbmcxFDASBgNVBAMMC1RvciBUZXN0aW5nMB4XDTE1MDkw\n"
+ "NjEzMzc0MVoXDTQzMDEyMjEzMzc0MVowXjELMAkGA1UEBhMCVVMxETAPBgNVBAgM\n"
+ "CElsbGlub2lzMRAwDgYDVQQHDAdDaGljYWdvMRQwEgYDVQQKDAtUb3IgVGVzdGlu\n"
+ "ZzEUMBIGA1UEAwwLVG9yIFRlc3RpbmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw\n"
+ "ggIKAoICAQCpLMUEiLW5leUgBZoEJms2V7lZRhIAjnJBhVMHD0e3UubNknmaQoxf\n"
+ "ARz3rvqOaRd0JlV+qM9qE0DjiYcCVP1cAfqAo9d83uS1vwY3YMVJzADlaIiHfyVW\n"
+ "uEgBy0vvkeUBqaua24dYlcwsemOiXYLu41yM1wkcGHW1AhBNHppY6cznb8TyLgNM\n"
+ "2x3SGUdzc5XMyAFx51faKGBA3wjs+Hg1PLY7d30nmCgEOBavpm5I1disM/0k+Mcy\n"
+ "YmAKEo/iHJX/rQzO4b9znP69juLlR8PDBUJEVIG/CYb6+uw8MjjUyiWXYoqfVmN2\n"
+ "hm/lH8b6rXw1a2Aa3VTeD0DxaWeacMYHY/i01fd5n7hCoDTRNdSw5KJ0L3Z0SKTu\n"
+ "0lzffKzDaIfyZGlpW5qdouACkWYzsaitQOePVE01PIdO30vUfzNTFDfy42ccx3Di\n"
+ "59UCu+IXB+eMtrBfsok0Qc63vtF1linJgjHW1z/8ujk8F7/qkOfODhk4l7wngc2A\n"
+ "EmwWFIFoGaiTEZHB9qteXr4unbXZ0AHpM02uGGwZEGohjFyebEb73M+J57WKKAFb\n"
+ "PqbLcGUksL1SHNBNAJcVLttX55sO4nbidOS/kA3m+F1R04MBTyQF9qA6YDDHqdI3\n"
+ "h/3pw0Z4fxVouTYT4/NfRnX4JTP4u+7Mpcoof28VME0qWqD1LnRhFQIDAQABo1Aw\n"
+ "TjAdBgNVHQ4EFgQUMoAgIXH7pZ3QMRwTjT+DM9Yo/v0wHwYDVR0jBBgwFoAUMoAg\n"
+ "IXH7pZ3QMRwTjT+DM9Yo/v0wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC\n"
+ "AgEAUJxacjXR9sT+Xs6ISFiUsyd0T6WVKMnV46xrYJHirGfx+krWHrjxMY+ZtxYD\n"
+ "DBDGlo11Qc4v6QrclNf5QUBfIiGQsP9Cm6hHcQ+Tpg9HHCgSqG1YNPwCPReCR4br\n"
+ "BLvLfrfkcBL2IWM0PdQdCze+59DBfipsULD2mEn9fjYRXQEwb2QWtQ9qRc20Yb/x\n"
+ "Q4b/+CvUodLkaq7B8MHz0BV8HHcBoph6DYaRmO/N+hPauIuSp6XyaGYcEefGKVKj\n"
+ "G2+fcsdyXsoijNdL8vNKwm4j2gVwCBnw16J00yfFoV46YcbfqEdJB2je0XSvwXqt\n"
+ "14AOTngxso2h9k9HLtrfpO1ZG/B5AcCMs1lzbZ2fp5DPHtjvvmvA2RJqgo3yjw4W\n"
+ "4DHAuTglYFlC3mDHNfNtcGP20JvepcQNzNP2UzwcpOc94hfKikOFw+gf9Vf1qd0y\n"
+ "h/Sk6OZHn2+JVUPiWHIQV98Vtoh4RmUZDJD+b55ia3fQGTGzt4z1XFzQYSva5sfs\n"
+ "wocS/papthqWldQU7x+3wofNd5CNU1x6WKXG/yw30IT/4F8ADJD6GeygNT8QJYvt\n"
+ "u/8lAkbOy6B9xGmSvr0Kk1oq9P2NshA6kalxp1Oz/DTNDdL4AeBXV3JmM6WWCjGn\n"
+ "Yy1RT69d0rwYc5u/vnqODz1IjvT90smsrkBumGt791FAFeg=\n"
+ "-----END CERTIFICATE-----\n";
+
+static X509 *
+read_cert_from(const char *str)
+{
+ BIO *bio = BIO_new(BIO_s_mem());
+ BIO_write(bio, str, (int) strlen(str));
+ X509 *res = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ return res;
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_verify(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ crypto_pk_t *k = NULL;
+ X509 *cert1 = NULL, *cert2 = NULL, *invalidCert = NULL,
+ *validCert = NULL, *caCert = NULL;
+
+ cert1 = tor_malloc_zero(sizeof(X509));
+ cert1->references = 10;
+
+ cert2 = tor_malloc_zero(sizeof(X509));
+ cert2->references = 10;
+
+ validCert = read_cert_from(validCertString);
+ caCert = read_cert_from(caCertString);
+ invalidCert = read_cert_from(notCompletelyValidCertString);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ ret = tor_tls_verify(LOG_WARN, tls, &k);
+ tt_int_op(ret, OP_EQ, -1);
+
+ MOCK(try_to_extract_certs_from_tls, fixed_try_to_extract_certs_from_tls);
+
+ fixed_try_to_extract_certs_from_tls_cert_out_result = cert1;
+ ret = tor_tls_verify(LOG_WARN, tls, &k);
+ tt_int_op(ret, OP_EQ, -1);
+
+ fixed_try_to_extract_certs_from_tls_id_cert_out_result = cert2;
+ ret = tor_tls_verify(LOG_WARN, tls, &k);
+ tt_int_op(ret, OP_EQ, -1);
+
+ fixed_try_to_extract_certs_from_tls_cert_out_result = invalidCert;
+ fixed_try_to_extract_certs_from_tls_id_cert_out_result = invalidCert;
+
+ ret = tor_tls_verify(LOG_WARN, tls, &k);
+ tt_int_op(ret, OP_EQ, -1);
+
+ fixed_try_to_extract_certs_from_tls_cert_out_result = validCert;
+ fixed_try_to_extract_certs_from_tls_id_cert_out_result = caCert;
+
+ ret = tor_tls_verify(LOG_WARN, tls, &k);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(k);
+
+ done:
+ UNMOCK(try_to_extract_certs_from_tls);
+ tor_free(cert1);
+ tor_free(cert2);
+ tor_free(tls);
+ tor_free(k);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_check_lifetime(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ X509 *validCert = read_cert_from(validCertString);
+ time_t now = time(NULL);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ ret = tor_tls_check_lifetime(LOG_WARN, tls, 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+ tls->ssl->session->peer = validCert;
+ ret = tor_tls_check_lifetime(LOG_WARN, tls, 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ASN1_STRING_free(validCert->cert_info->validity->notBefore);
+ validCert->cert_info->validity->notBefore = ASN1_TIME_set(NULL, now-10);
+ ASN1_STRING_free(validCert->cert_info->validity->notAfter);
+ validCert->cert_info->validity->notAfter = ASN1_TIME_set(NULL, now+60);
+
+ ret = tor_tls_check_lifetime(LOG_WARN, tls, 0, -1000);
+ tt_int_op(ret, OP_EQ, -1);
+
+ ret = tor_tls_check_lifetime(LOG_WARN, tls, -1000, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ done:
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ X509_free(validCert);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static int fixed_ssl_pending_result = 0;
+
+static int
+fixed_ssl_pending(const SSL *ignored)
+{
+ (void)ignored;
+ return fixed_ssl_pending_result;
+}
+
+static void
+test_tortls_get_pending_bytes(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ SSL_METHOD *method;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ method = tor_malloc_zero(sizeof(SSL_METHOD));
+ method->ssl_pending = fixed_ssl_pending;
+ tls->ssl->method = method;
+
+ fixed_ssl_pending_result = 42;
+ ret = tor_tls_get_pending_bytes(tls);
+ tt_int_op(ret, OP_EQ, 42);
+
+ done:
+ tor_free(method);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif
+
+static void
+test_tortls_get_forced_write_size(void *ignored)
+{
+ (void)ignored;
+ long ret;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ tls->wantwrite_n = 43;
+ ret = tor_tls_get_forced_write_size(tls);
+ tt_int_op(ret, OP_EQ, 43);
+
+ done:
+ tor_free(tls);
+}
+
+extern uint64_t total_bytes_written_over_tls;
+extern uint64_t total_bytes_written_by_tls;
+
+static void
+test_tortls_get_write_overhead_ratio(void *ignored)
+{
+ (void)ignored;
+ double ret;
+
+ total_bytes_written_over_tls = 0;
+ ret = tls_get_write_overhead_ratio();
+ tt_int_op(ret, OP_EQ, 1.0);
+
+ total_bytes_written_by_tls = 10;
+ total_bytes_written_over_tls = 1;
+ ret = tls_get_write_overhead_ratio();
+ tt_int_op(ret, OP_EQ, 10.0);
+
+ total_bytes_written_by_tls = 10;
+ total_bytes_written_over_tls = 2;
+ ret = tls_get_write_overhead_ratio();
+ tt_int_op(ret, OP_EQ, 5.0);
+
+ done:
+ (void)0;
+}
+
+static void
+test_tortls_used_v1_handshake(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ // These tests assume both V2 handshake server and client are enabled
+ tls->wasV2Handshake = 0;
+ ret = tor_tls_used_v1_handshake(tls);
+ tt_int_op(ret, OP_EQ, 1);
+
+ tls->wasV2Handshake = 1;
+ ret = tor_tls_used_v1_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+
+ done:
+ tor_free(tls);
+}
+
+static void
+test_tortls_get_num_server_handshakes(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ tls->server_handshake_count = 3;
+ ret = tor_tls_get_num_server_handshakes(tls);
+ tt_int_op(ret, OP_EQ, 3);
+
+ done:
+ tor_free(tls);
+}
+
+static void
+test_tortls_server_got_renegotiate(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ tls->got_renegotiate = 1;
+ ret = tor_tls_server_got_renegotiate(tls);
+ tt_int_op(ret, OP_EQ, 1);
+
+ done:
+ tor_free(tls);
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_SSL_SESSION_get_master_key(void *ignored)
+{
+ (void)ignored;
+ size_t ret;
+ tor_tls_t *tls;
+ uint8_t *out;
+ out = tor_malloc_zero(1);
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+ tls->ssl->session->master_key_length = 1;
+
+#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
+ tls->ssl->session->master_key[0] = 43;
+ ret = SSL_SESSION_get_master_key(tls->ssl->session, out, 0);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_int_op(out[0], OP_EQ, 0);
+
+ ret = SSL_SESSION_get_master_key(tls->ssl->session, out, 1);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_int_op(out[0], OP_EQ, 43);
+
+ done:
+#endif
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ tor_free(out);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_get_tlssecrets(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ uint8_t *secret_out = tor_malloc_zero(DIGEST256_LEN);;
+ tor_tls_t *tls;
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+ tls->ssl->session->master_key_length = 1;
+ tls->ssl->s3 = tor_malloc_zero(sizeof(SSL3_STATE));
+
+ ret = tor_tls_get_tlssecrets(tls, secret_out);
+ tt_int_op(ret, OP_EQ, 0);
+
+ done:
+ tor_free(secret_out);
+ tor_free(tls->ssl->s3);
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_get_buffer_sizes(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ size_t rbuf_c=-1, rbuf_b=-1, wbuf_c=-1, wbuf_b=-1;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->s3 = tor_malloc_zero(sizeof(SSL3_STATE));
+
+ tls->ssl->s3->rbuf.buf = NULL;
+ tls->ssl->s3->rbuf.len = 1;
+ tls->ssl->s3->rbuf.offset = 0;
+ tls->ssl->s3->rbuf.left = 42;
+
+ tls->ssl->s3->wbuf.buf = NULL;
+ tls->ssl->s3->wbuf.len = 2;
+ tls->ssl->s3->wbuf.offset = 0;
+ tls->ssl->s3->wbuf.left = 43;
+
+ ret = tor_tls_get_buffer_sizes(tls, &rbuf_c, &rbuf_b, &wbuf_c, &wbuf_b);
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)
+ tt_int_op(ret, OP_EQ, -1);
+#else
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(rbuf_c, OP_EQ, 0);
+ tt_int_op(wbuf_c, OP_EQ, 0);
+ tt_int_op(rbuf_b, OP_EQ, 42);
+ tt_int_op(wbuf_b, OP_EQ, 43);
+
+ tls->ssl->s3->rbuf.buf = tor_malloc_zero(1);
+ tls->ssl->s3->wbuf.buf = tor_malloc_zero(1);
+ ret = tor_tls_get_buffer_sizes(tls, &rbuf_c, &rbuf_b, &wbuf_c, &wbuf_b);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(rbuf_c, OP_EQ, 1);
+ tt_int_op(wbuf_c, OP_EQ, 2);
+
+#endif
+
+ done:
+ tor_free(tls->ssl->s3->rbuf.buf);
+ tor_free(tls->ssl->s3->wbuf.buf);
+ tor_free(tls->ssl->s3);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif
+
+static void
+test_tortls_evaluate_ecgroup_for_tls(void *ignored)
+{
+ (void)ignored;
+ int ret;
+
+ ret = evaluate_ecgroup_for_tls(NULL);
+ tt_int_op(ret, OP_EQ, 1);
+
+ ret = evaluate_ecgroup_for_tls("foobar");
+ tt_int_op(ret, OP_EQ, 0);
+
+ ret = evaluate_ecgroup_for_tls("P256");
+ tt_int_op(ret, OP_EQ, 1);
+
+ ret = evaluate_ecgroup_for_tls("P224");
+ // tt_int_op(ret, OP_EQ, 1); This varies between machines
+
+ done:
+ (void)0;
+}
+
+#ifndef OPENSSL_OPAQUE
+typedef struct cert_pkey_st_local
+{
+ X509 *x509;
+ EVP_PKEY *privatekey;
+ const EVP_MD *digest;
+} CERT_PKEY_local;
+
+typedef struct sess_cert_st_local
+{
+ STACK_OF(X509) *cert_chain;
+ int peer_cert_type;
+ CERT_PKEY_local *peer_key;
+ CERT_PKEY_local peer_pkeys[8];
+ int references;
+} SESS_CERT_local;
+
+static void
+test_tortls_try_to_extract_certs_from_tls(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ X509 *cert = NULL, *id_cert = NULL, *c1 = NULL, *c2 = NULL;
+ SESS_CERT_local *sess = NULL;
+
+ c1 = read_cert_from(validCertString);
+ c2 = read_cert_from(caCertString);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+ sess = tor_malloc_zero(sizeof(SESS_CERT_local));
+ tls->ssl->session->sess_cert = (void *)sess;
+
+ try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert);
+ tt_assert(!cert);
+ tt_assert(!id_cert);
+
+ tls->ssl->session->peer = c1;
+ try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert);
+ tt_assert(cert == c1);
+ tt_assert(!id_cert);
+ X509_free(cert); /* decrease refcnt */
+
+ sess->cert_chain = sk_X509_new_null();
+ try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert);
+ tt_assert(cert == c1);
+ tt_assert(!id_cert);
+ X509_free(cert); /* decrease refcnt */
+
+ sk_X509_push(sess->cert_chain, c1);
+ sk_X509_push(sess->cert_chain, c2);
+
+ try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert);
+ tt_assert(cert == c1);
+ tt_assert(id_cert);
+ X509_free(cert); /* decrease refcnt */
+
+ done:
+ sk_X509_free(sess->cert_chain);
+ tor_free(sess);
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ X509_free(c1);
+ X509_free(c2);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_get_peer_cert(void *ignored)
+{
+ (void)ignored;
+ tor_x509_cert_t *ret;
+ tor_tls_t *tls;
+ X509 *cert = NULL;
+
+ cert = read_cert_from(validCertString);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+
+ ret = tor_tls_get_peer_cert(tls);
+ tt_assert(!ret);
+
+ tls->ssl->session->peer = cert;
+ ret = tor_tls_get_peer_cert(tls);
+ tt_assert(ret);
+ tt_assert(ret->cert == cert);
+
+ done:
+ tor_x509_cert_free(ret);
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ X509_free(cert);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_peer_has_cert(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ X509 *cert = NULL;
+
+ cert = read_cert_from(validCertString);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+
+ ret = tor_tls_peer_has_cert(tls);
+ tt_assert(!ret);
+
+ tls->ssl->session->peer = cert;
+ ret = tor_tls_peer_has_cert(tls);
+ tt_assert(ret);
+
+ done:
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ X509_free(cert);
+}
+#endif
+
+static void
+test_tortls_is_server(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ int ret;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->isServer = 1;
+ ret = tor_tls_is_server(tls);
+ tt_int_op(ret, OP_EQ, 1);
+
+ done:
+ tor_free(tls);
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_session_secret_cb(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ SSL_CTX *ctx;
+ STACK_OF(SSL_CIPHER) *ciphers = NULL;
+ SSL_CIPHER *one;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ tls->magic = TOR_TLS_MAGIC;
+
+ ctx = SSL_CTX_new(TLSv1_method());
+ tls->ssl = SSL_new(ctx);
+ SSL_set_ex_data(tls->ssl, tor_tls_object_ex_data_index, tls);
+
+ SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL);
+
+ tor_tls_session_secret_cb(tls->ssl, NULL, NULL, NULL, NULL, NULL);
+ tt_assert(!tls->ssl->tls_session_secret_cb);
+
+ one = get_cipher_by_name("ECDH-RSA-AES256-GCM-SHA384");
+ one->id = 0x00ff;
+ ciphers = sk_SSL_CIPHER_new_null();
+ sk_SSL_CIPHER_push(ciphers, one);
+
+ tls->client_cipher_list_type = 0;
+ tor_tls_session_secret_cb(tls->ssl, NULL, NULL, ciphers, NULL, NULL);
+ tt_assert(!tls->ssl->tls_session_secret_cb);
+
+ done:
+ sk_SSL_CIPHER_free(ciphers);
+ SSL_free(tls->ssl);
+ SSL_CTX_free(ctx);
+ tor_free(tls);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+/* TODO: It seems block_renegotiation and unblock_renegotiation and
+ * using different blags. This might not be correct */
+static void
+test_tortls_block_renegotiation(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->s3 = tor_malloc_zero(sizeof(SSL3_STATE));
+#ifndef SUPPORT_UNSAFE_RENEGOTIATION_FLAG
+#define SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0
+#endif
+
+ tls->ssl->s3->flags = SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
+
+ tor_tls_block_renegotiation(tls);
+
+#ifndef OPENSSL_1_1_API
+ tt_assert(!(tls->ssl->s3->flags &
+ SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION));
+#endif
+
+ done:
+ tor_free(tls->ssl->s3);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+
+static void
+test_tortls_unblock_renegotiation(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tor_tls_unblock_renegotiation(tls);
+
+ tt_uint_op(SSL_get_options(tls->ssl) &
+ SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, OP_EQ,
+ SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
+
+ done:
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_assert_renegotiation_unblocked(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tor_tls_unblock_renegotiation(tls);
+ tor_tls_assert_renegotiation_unblocked(tls);
+ /* No assertion here - this test will fail if tor_assert is turned on
+ * and things are bad. */
+
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif
+
+static void
+test_tortls_set_logged_address(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ tor_tls_set_logged_address(tls, "foo bar");
+
+ tt_str_op(tls->address, OP_EQ, "foo bar");
+
+ tor_tls_set_logged_address(tls, "foo bar 2");
+ tt_str_op(tls->address, OP_EQ, "foo bar 2");
+
+ done:
+ tor_free(tls->address);
+ tor_free(tls);
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+example_cb(tor_tls_t *t, void *arg)
+{
+ (void)t;
+ (void)arg;
+}
+
+static void
+test_tortls_set_renegotiate_callback(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ const char *arg = "hello";
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+
+ tor_tls_set_renegotiate_callback(tls, example_cb, (void*)arg);
+ tt_assert(tls->negotiated_callback == example_cb);
+ tt_assert(tls->callback_arg == arg);
+ tt_assert(!tls->got_renegotiate);
+
+ /* Assumes V2_HANDSHAKE_SERVER */
+ tt_assert(tls->ssl->info_callback == tor_tls_server_info_callback);
+
+ tor_tls_set_renegotiate_callback(tls, NULL, (void*)arg);
+ tt_assert(tls->ssl->info_callback == tor_tls_debug_state_callback);
+
+ done:
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static SSL_CIPHER *fixed_cipher1 = NULL;
+static SSL_CIPHER *fixed_cipher2 = NULL;
+static const SSL_CIPHER *
+fake_get_cipher(unsigned ncipher)
+{
+
+ switch (ncipher) {
+ case 1:
+ return fixed_cipher1;
+ case 2:
+ return fixed_cipher2;
+ default:
+ return NULL;
+ }
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_find_cipher_by_id(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ SSL *ssl;
+ SSL_CTX *ctx;
+ const SSL_METHOD *m = TLSv1_method();
+ SSL_METHOD *empty_method = tor_malloc_zero(sizeof(SSL_METHOD));
+
+ fixed_cipher1 = tor_malloc_zero(sizeof(SSL_CIPHER));
+ fixed_cipher2 = tor_malloc_zero(sizeof(SSL_CIPHER));
+ fixed_cipher2->id = 0xC00A;
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(m);
+ ssl = SSL_new(ctx);
+
+ ret = find_cipher_by_id(ssl, NULL, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ ret = find_cipher_by_id(ssl, m, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ ret = find_cipher_by_id(ssl, m, 0xFFFF);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ ret = find_cipher_by_id(ssl, empty_method, 0xFFFF);
+#ifdef HAVE_SSL_CIPHER_FIND
+ tt_int_op(ret, OP_EQ, 0);
+#else
+ tt_int_op(ret, OP_EQ, 1);
+#endif
+
+ empty_method->get_cipher = fake_get_cipher;
+ ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ empty_method->get_cipher = m->get_cipher;
+ empty_method->num_ciphers = m->num_ciphers;
+ ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ empty_method->get_cipher = fake_get_cipher;
+ empty_method->num_ciphers = m->num_ciphers;
+ ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ empty_method->num_ciphers = fake_num_ciphers;
+ ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
+#ifdef HAVE_SSL_CIPHER_FIND
+ tt_int_op(ret, OP_EQ, 1);
+#else
+ tt_int_op(ret, OP_EQ, 0);
+#endif
+
+ done:
+ tor_free(empty_method);
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+ tor_free(fixed_cipher1);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_debug_state_callback(void *ignored)
+{
+ (void)ignored;
+ SSL *ssl;
+ char *buf = tor_malloc_zero(1000);
+ int n;
+
+ int previous_log = setup_capture_of_logs(LOG_DEBUG);
+
+ ssl = tor_malloc_zero(sizeof(SSL));
+
+ tor_tls_debug_state_callback(ssl, 32, 45);
+
+ n = tor_snprintf(buf, 1000, "SSL %p is now in state unknown"
+ " state [type=32,val=45].\n", ssl);
+ /* tor's snprintf returns -1 on error */
+ tt_int_op(n, OP_NE, -1);
+ expect_log_msg(buf);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ tor_free(buf);
+ tor_free(ssl);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_server_info_callback(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ SSL_CTX *ctx;
+ SSL *ssl;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(TLSv1_method());
+ ssl = SSL_new(ctx);
+
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->magic = TOR_TLS_MAGIC;
+ tls->ssl = ssl;
+
+ tor_tls_server_info_callback(NULL, 0, 0);
+
+ SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_A);
+ mock_clean_saved_logs();
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ expect_log_msg("Couldn't look up the tls for an SSL*. How odd!\n");
+
+ SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_B);
+ mock_clean_saved_logs();
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ expect_log_msg("Couldn't look up the tls for an SSL*. How odd!\n");
+
+ SSL_set_state(ssl, 99);
+ mock_clean_saved_logs();
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ expect_no_log_entry();
+
+ SSL_set_ex_data(tls->ssl, tor_tls_object_ex_data_index, tls);
+ SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_B);
+ tls->negotiated_callback = 0;
+ tls->server_handshake_count = 120;
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ tt_int_op(tls->server_handshake_count, OP_EQ, 121);
+
+ tls->server_handshake_count = 127;
+ tls->negotiated_callback = (void *)1;
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ tt_int_op(tls->server_handshake_count, OP_EQ, 127);
+ tt_int_op(tls->got_renegotiate, OP_EQ, 1);
+
+ tls->ssl->session = SSL_SESSION_new();
+ tls->wasV2Handshake = 0;
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ tt_int_op(tls->wasV2Handshake, OP_EQ, 0);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+ tor_free(tls);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static int fixed_ssl_read_result_index;
+static int fixed_ssl_read_result[5];
+static int fixed_ssl_shutdown_result;
+
+static int
+fixed_ssl_read(SSL *s, void *buf, int len)
+{
+ (void)s;
+ (void)buf;
+ (void)len;
+ return fixed_ssl_read_result[fixed_ssl_read_result_index++];
+}
+
+static int
+fixed_ssl_shutdown(SSL *s)
+{
+ (void)s;
+ return fixed_ssl_shutdown_result;
+}
+
+#ifndef LIBRESSL_VERSION_NUMBER
+static int fixed_ssl_state_to_set;
+static tor_tls_t *fixed_tls;
+
+static int
+setting_version_ssl_shutdown(SSL *s)
+{
+ s->version = SSL2_VERSION;
+ return fixed_ssl_shutdown_result;
+}
+
+static int
+setting_version_and_state_ssl_shutdown(SSL *s)
+{
+ fixed_tls->state = fixed_ssl_state_to_set;
+ s->version = SSL2_VERSION;
+ return fixed_ssl_shutdown_result;
+}
+#endif
+
+static int
+dummy_handshake_func(SSL *s)
+{
+ (void)s;
+ return 1;
+}
+
+static void
+test_tortls_shutdown(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ SSL_METHOD *method = give_me_a_test_method();
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->method = method;
+ method->ssl_read = fixed_ssl_read;
+ method->ssl_shutdown = fixed_ssl_shutdown;
+
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, -9);
+
+ tls->state = TOR_TLS_ST_SENTCLOSE;
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = -1;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, -9);
+
+#ifndef LIBRESSL_VERSION_NUMBER
+ tls->ssl->handshake_func = dummy_handshake_func;
+
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = 42;
+ fixed_ssl_read_result[2] = 0;
+ fixed_ssl_shutdown_result = 1;
+ ERR_clear_error();
+ tls->ssl->version = SSL2_VERSION;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_DONE);
+ tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED);
+
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = 42;
+ fixed_ssl_read_result[2] = 0;
+ fixed_ssl_shutdown_result = 0;
+ ERR_clear_error();
+ tls->ssl->version = 0;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_DONE);
+ tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED);
+
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = 42;
+ fixed_ssl_read_result[2] = 0;
+ fixed_ssl_shutdown_result = 0;
+ ERR_clear_error();
+ tls->ssl->version = 0;
+ method->ssl_shutdown = setting_version_ssl_shutdown;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = 42;
+ fixed_ssl_read_result[2] = 0;
+ fixed_ssl_shutdown_result = 0;
+ fixed_tls = tls;
+ fixed_ssl_state_to_set = TOR_TLS_ST_GOTCLOSE;
+ ERR_clear_error();
+ tls->ssl->version = 0;
+ method->ssl_shutdown = setting_version_and_state_ssl_shutdown;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = 42;
+ fixed_ssl_read_result[2] = 0;
+ fixed_ssl_read_result[3] = -1;
+ fixed_ssl_shutdown_result = 0;
+ fixed_tls = tls;
+ fixed_ssl_state_to_set = 0;
+ ERR_clear_error();
+ tls->ssl->version = 0;
+ method->ssl_shutdown = setting_version_and_state_ssl_shutdown;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+#endif
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ tor_free(method);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+
+static int negotiated_callback_called;
+
+static void
+negotiated_callback_setter(tor_tls_t *t, void *arg)
+{
+ (void)t;
+ (void)arg;
+ negotiated_callback_called++;
+}
+
+static void
+test_tortls_read(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ char buf[100];
+ SSL_METHOD *method = give_me_a_test_method();
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->state = TOR_TLS_ST_OPEN;
+
+ ret = tor_tls_read(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, -9);
+
+ /* These tests assume that V2_HANDSHAKE_SERVER is set */
+ tls->ssl->handshake_func = dummy_handshake_func;
+ tls->ssl->method = method;
+ method->ssl_read = fixed_ssl_read;
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 42;
+ tls->state = TOR_TLS_ST_OPEN;
+ ERR_clear_error();
+ ret = tor_tls_read(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, 42);
+
+ tls->state = TOR_TLS_ST_OPEN;
+ tls->got_renegotiate = 1;
+ fixed_ssl_read_result_index = 0;
+ ERR_clear_error();
+ ret = tor_tls_read(tls, buf, 10);
+ tt_int_op(tls->got_renegotiate, OP_EQ, 0);
+
+ tls->state = TOR_TLS_ST_OPEN;
+ tls->got_renegotiate = 1;
+ negotiated_callback_called = 0;
+ tls->negotiated_callback = negotiated_callback_setter;
+ fixed_ssl_read_result_index = 0;
+ ERR_clear_error();
+ ret = tor_tls_read(tls, buf, 10);
+ tt_int_op(negotiated_callback_called, OP_EQ, 1);
+
+#ifndef LIBRESSL_VERSION_NUMBER
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 0;
+ tls->ssl->version = SSL2_VERSION;
+ ERR_clear_error();
+ ret = tor_tls_read(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, TOR_TLS_CLOSE);
+ tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED);
+#endif
+ // TODO: fill up
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ tor_free(method);
+}
+
+static int fixed_ssl_write_result;
+
+static int
+fixed_ssl_write(SSL *s, const void *buf, int len)
+{
+ (void)s;
+ (void)buf;
+ (void)len;
+ return fixed_ssl_write_result;
+}
+
+static void
+test_tortls_write(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ SSL_METHOD *method = give_me_a_test_method();
+ char buf[100];
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->state = TOR_TLS_ST_OPEN;
+
+ ret = tor_tls_write(tls, buf, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ret = tor_tls_write(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, -9);
+
+ tls->ssl->method = method;
+ tls->wantwrite_n = 1;
+ ret = tor_tls_write(tls, buf, 10);
+ tt_int_op(tls->wantwrite_n, OP_EQ, 0);
+
+ method->ssl_write = fixed_ssl_write;
+ tls->ssl->handshake_func = dummy_handshake_func;
+ fixed_ssl_write_result = 1;
+ ERR_clear_error();
+ ret = tor_tls_write(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, 1);
+
+ fixed_ssl_write_result = -1;
+ ERR_clear_error();
+ tls->ssl->rwstate = SSL_READING;
+ SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL);
+ SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_READ;
+ ret = tor_tls_write(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, TOR_TLS_WANTREAD);
+
+ ERR_clear_error();
+ tls->ssl->rwstate = SSL_READING;
+ SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL);
+ SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_WRITE;
+ ret = tor_tls_write(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, TOR_TLS_WANTWRITE);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ BIO_free(tls->ssl->rbio);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ tor_free(method);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static int fixed_ssl_accept_result;
+static int fixed_ssl_connect_result;
+
+static int
+setting_error_ssl_accept(SSL *ssl)
+{
+ (void)ssl;
+ ERR_put_error(ERR_LIB_BN, 2, -1, "somewhere.c", 99);
+ ERR_put_error(ERR_LIB_SYS, 2, -1, "somewhere.c", 99);
+ return fixed_ssl_accept_result;
+}
+
+static int
+setting_error_ssl_connect(SSL *ssl)
+{
+ (void)ssl;
+ ERR_put_error(ERR_LIB_BN, 2, -1, "somewhere.c", 99);
+ ERR_put_error(ERR_LIB_SYS, 2, -1, "somewhere.c", 99);
+ return fixed_ssl_connect_result;
+}
+
+static int
+fixed_ssl_accept(SSL *ssl)
+{
+ (void) ssl;
+ return fixed_ssl_accept_result;
+}
+
+static void
+test_tortls_handshake(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ SSL_CTX *ctx;
+ SSL_METHOD *method = give_me_a_test_method();
+ int previous_log = setup_capture_of_logs(LOG_INFO);
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(TLSv1_method());
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = SSL_new(ctx);
+ tls->state = TOR_TLS_ST_HANDSHAKE;
+
+ ret = tor_tls_handshake(tls);
+ tt_int_op(ret, OP_EQ, -9);
+
+ tls->isServer = 1;
+ tls->state = TOR_TLS_ST_HANDSHAKE;
+ ret = tor_tls_handshake(tls);
+ tt_int_op(ret, OP_EQ, -9);
+
+ tls->ssl->method = method;
+ method->ssl_accept = fixed_ssl_accept;
+ fixed_ssl_accept_result = 2;
+ ERR_clear_error();
+ tls->state = TOR_TLS_ST_HANDSHAKE;
+ ret = tor_tls_handshake(tls);
+ tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_OPEN);
+
+ method->ssl_accept = setting_error_ssl_accept;
+ fixed_ssl_accept_result = 1;
+ ERR_clear_error();
+ mock_clean_saved_logs();
+ tls->state = TOR_TLS_ST_HANDSHAKE;
+ ret = tor_tls_handshake(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+ expect_log_entry();
+ /* This fails on jessie. Investigate why! */
+#if 0
+ expect_log_msg("TLS error while handshaking: (null) (in bignum routines:"
+ "(null):SSLv3 write client hello B)\n");
+ expect_log_msg("TLS error while handshaking: (null) (in system library:"
+ "connect:SSLv3 write client hello B)\n");
+#endif
+ expect_log_severity(LOG_INFO);
+
+ tls->isServer = 0;
+ method->ssl_connect = setting_error_ssl_connect;
+ fixed_ssl_connect_result = 1;
+ ERR_clear_error();
+ mock_clean_saved_logs();
+ tls->state = TOR_TLS_ST_HANDSHAKE;
+ ret = tor_tls_handshake(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+ expect_log_entry();
+#if 0
+ /* See above */
+ expect_log_msg("TLS error while handshaking: "
+ "(null) (in bignum routines:(null):SSLv3 write client hello B)\n");
+ expect_log_msg("TLS error while handshaking: "
+ "(null) (in system library:connect:SSLv3 write client hello B)\n");
+#endif
+ expect_log_severity(LOG_WARN);
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ SSL_free(tls->ssl);
+ SSL_CTX_free(ctx);
+ tor_free(tls);
+ tor_free(method);
+}
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_finish_handshake(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ SSL_CTX *ctx;
+ SSL_METHOD *method = give_me_a_test_method();
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ X509 *c1 = read_cert_from(validCertString);
+ SESS_CERT_local *sess = NULL;
+
+ ctx = SSL_CTX_new(method);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = SSL_new(ctx);
+ tls->state = TOR_TLS_ST_OPEN;
+
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+
+ tls->isServer = 1;
+ tls->wasV2Handshake = 0;
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tls->wasV2Handshake, OP_EQ, 1);
+
+ tls->wasV2Handshake = 1;
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tls->wasV2Handshake, OP_EQ, 1);
+
+ tls->wasV2Handshake = 1;
+ tls->ssl->session = SSL_SESSION_new();
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tls->wasV2Handshake, OP_EQ, 0);
+
+ tls->isServer = 0;
+
+ sess = tor_malloc_zero(sizeof(SESS_CERT_local));
+ tls->ssl->session->sess_cert = (void *)sess;
+ sess->cert_chain = sk_X509_new_null();
+ sk_X509_push(sess->cert_chain, c1);
+ tls->ssl->session->peer = c1;
+ tls->wasV2Handshake = 0;
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tls->wasV2Handshake, OP_EQ, 1);
+
+ method->num_ciphers = fake_num_ciphers;
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, -9);
+
+ done:
+ if (sess)
+ sk_X509_free(sess->cert_chain);
+ if (tls->ssl && tls->ssl->session) {
+ tor_free(tls->ssl->session->sess_cert);
+ }
+ SSL_free(tls->ssl);
+ tor_free(tls);
+ SSL_CTX_free(ctx);
+ tor_free(method);
+}
+#endif
+
+static int fixed_crypto_pk_new_result_index;
+static crypto_pk_t *fixed_crypto_pk_new_result[5];
+
+static crypto_pk_t *
+fixed_crypto_pk_new(void)
+{
+ return fixed_crypto_pk_new_result[fixed_crypto_pk_new_result_index++];
+}
+
+#ifndef OPENSSL_OPAQUE
+static int fixed_crypto_pk_generate_key_with_bits_result_index;
+static int fixed_crypto_pk_generate_key_with_bits_result[5];
+static int fixed_tor_tls_create_certificate_result_index;
+static X509 *fixed_tor_tls_create_certificate_result[5];
+static int fixed_tor_x509_cert_new_result_index;
+static tor_x509_cert_t *fixed_tor_x509_cert_new_result[5];
+
+static int
+fixed_crypto_pk_generate_key_with_bits(crypto_pk_t *env, int bits)
+{
+ (void)env;
+ (void)bits;
+ return fixed_crypto_pk_generate_key_with_bits_result[
+ fixed_crypto_pk_generate_key_with_bits_result_index++];
+}
+
+static X509 *
+fixed_tor_tls_create_certificate(crypto_pk_t *rsa,
+ crypto_pk_t *rsa_sign,
+ const char *cname,
+ const char *cname_sign,
+ unsigned int cert_lifetime)
+{
+ (void)rsa;
+ (void)rsa_sign;
+ (void)cname;
+ (void)cname_sign;
+ (void)cert_lifetime;
+ return fixed_tor_tls_create_certificate_result[
+ fixed_tor_tls_create_certificate_result_index++];
+}
+
+static tor_x509_cert_t *
+fixed_tor_x509_cert_new(X509 *x509_cert)
+{
+ (void) x509_cert;
+ return fixed_tor_x509_cert_new_result[
+ fixed_tor_x509_cert_new_result_index++];
+}
+
+static void
+test_tortls_context_new(void *ignored)
+{
+ (void)ignored;
+ tor_tls_context_t *ret;
+ crypto_pk_t *pk1, *pk2, *pk3, *pk4, *pk5, *pk6, *pk7, *pk8, *pk9, *pk10,
+ *pk11, *pk12, *pk13, *pk14, *pk15, *pk16, *pk17, *pk18;
+
+ pk1 = crypto_pk_new();
+ pk2 = crypto_pk_new();
+ pk3 = crypto_pk_new();
+ pk4 = crypto_pk_new();
+ pk5 = crypto_pk_new();
+ pk6 = crypto_pk_new();
+ pk7 = crypto_pk_new();
+ pk8 = crypto_pk_new();
+ pk9 = crypto_pk_new();
+ pk10 = crypto_pk_new();
+ pk11 = crypto_pk_new();
+ pk12 = crypto_pk_new();
+ pk13 = crypto_pk_new();
+ pk14 = crypto_pk_new();
+ pk15 = crypto_pk_new();
+ pk16 = crypto_pk_new();
+ pk17 = crypto_pk_new();
+ pk18 = crypto_pk_new();
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = NULL;
+ MOCK(crypto_pk_new, fixed_crypto_pk_new);
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ MOCK(crypto_pk_generate_key_with_bits,
+ fixed_crypto_pk_generate_key_with_bits);
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk1;
+ fixed_crypto_pk_new_result[1] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result[0] = -1;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk2;
+ fixed_crypto_pk_new_result[1] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result[0] = 0;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk3;
+ fixed_crypto_pk_new_result[1] = pk4;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result[0] = 0;
+ fixed_crypto_pk_generate_key_with_bits_result[1] = -1;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ MOCK(tor_tls_create_certificate, fixed_tor_tls_create_certificate);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk5;
+ fixed_crypto_pk_new_result[1] = pk6;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_crypto_pk_generate_key_with_bits_result[1] = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = NULL;
+ fixed_tor_tls_create_certificate_result[1] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[2] = tor_malloc_zero(sizeof(X509));
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk7;
+ fixed_crypto_pk_new_result[1] = pk8;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[1] = NULL;
+ fixed_tor_tls_create_certificate_result[2] = tor_malloc_zero(sizeof(X509));
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk9;
+ fixed_crypto_pk_new_result[1] = pk10;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[1] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[2] = NULL;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ MOCK(tor_x509_cert_new, fixed_tor_x509_cert_new);
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk11;
+ fixed_crypto_pk_new_result[1] = pk12;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[1] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[2] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_x509_cert_new_result_index = 0;
+ fixed_tor_x509_cert_new_result[0] = NULL;
+ fixed_tor_x509_cert_new_result[1] = NULL;
+ fixed_tor_x509_cert_new_result[2] = NULL;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk13;
+ fixed_crypto_pk_new_result[1] = pk14;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[1] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[2] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_x509_cert_new_result_index = 0;
+ fixed_tor_x509_cert_new_result[0] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ fixed_tor_x509_cert_new_result[1] = NULL;
+ fixed_tor_x509_cert_new_result[2] = NULL;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk15;
+ fixed_crypto_pk_new_result[1] = pk16;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[1] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[2] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_x509_cert_new_result_index = 0;
+ fixed_tor_x509_cert_new_result[0] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ fixed_tor_x509_cert_new_result[1] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ fixed_tor_x509_cert_new_result[2] = NULL;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk17;
+ fixed_crypto_pk_new_result[1] = pk18;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[1] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_tls_create_certificate_result[2] = tor_malloc_zero(sizeof(X509));
+ fixed_tor_x509_cert_new_result_index = 0;
+ fixed_tor_x509_cert_new_result[0] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ fixed_tor_x509_cert_new_result[1] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ fixed_tor_x509_cert_new_result[2] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ done:
+ UNMOCK(tor_x509_cert_new);
+ UNMOCK(tor_tls_create_certificate);
+ UNMOCK(crypto_pk_generate_key_with_bits);
+ UNMOCK(crypto_pk_new);
+}
+#endif
+
+static int fixed_crypto_pk_get_evp_pkey_result_index = 0;
+static EVP_PKEY *fixed_crypto_pk_get_evp_pkey_result[5];
+
+static EVP_PKEY *
+fixed_crypto_pk_get_evp_pkey_(crypto_pk_t *env, int private)
+{
+ (void) env;
+ (void) private;
+ return fixed_crypto_pk_get_evp_pkey_result[
+ fixed_crypto_pk_get_evp_pkey_result_index++];
+}
+
+static void
+test_tortls_create_certificate(void *ignored)
+{
+ (void)ignored;
+ X509 *ret;
+ crypto_pk_t *pk1, *pk2;
+
+ pk1 = crypto_pk_new();
+ pk2 = crypto_pk_new();
+
+ MOCK(crypto_pk_get_evp_pkey_, fixed_crypto_pk_get_evp_pkey_);
+ fixed_crypto_pk_get_evp_pkey_result_index = 0;
+ fixed_crypto_pk_get_evp_pkey_result[0] = NULL;
+ ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_get_evp_pkey_result_index = 0;
+ fixed_crypto_pk_get_evp_pkey_result[0] = EVP_PKEY_new();
+ fixed_crypto_pk_get_evp_pkey_result[1] = NULL;
+ ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_get_evp_pkey_result_index = 0;
+ fixed_crypto_pk_get_evp_pkey_result[0] = EVP_PKEY_new();
+ fixed_crypto_pk_get_evp_pkey_result[1] = EVP_PKEY_new();
+ ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1);
+ tt_assert(!ret);
+
+ done:
+ UNMOCK(crypto_pk_get_evp_pkey_);
+ crypto_pk_free(pk1);
+ crypto_pk_free(pk2);
+}
+
+static void
+test_tortls_cert_new(void *ignored)
+{
+ (void)ignored;
+ tor_x509_cert_t *ret;
+ X509 *cert = read_cert_from(validCertString);
+
+ ret = tor_x509_cert_new(NULL);
+ tt_assert(!ret);
+
+ ret = tor_x509_cert_new(cert);
+ tt_assert(ret);
+ tor_x509_cert_free(ret);
+ ret = NULL;
+
+#if 0
+ cert = read_cert_from(validCertString);
+ /* XXX this doesn't do what you think: it alters a copy of the pubkey. */
+ X509_get_pubkey(cert)->type = EVP_PKEY_DSA;
+ ret = tor_x509_cert_new(cert);
+ tt_assert(ret);
+#endif
+
+#ifndef OPENSSL_OPAQUE
+ cert = read_cert_from(validCertString);
+ X509_CINF_free(cert->cert_info);
+ cert->cert_info = NULL;
+ ret = tor_x509_cert_new(cert);
+ tt_assert(ret);
+#endif
+
+ done:
+ tor_x509_cert_free(ret);
+}
+
+static void
+test_tortls_cert_is_valid(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_x509_cert_t *cert = NULL, *scert = NULL;
+
+ scert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(scert);
+ tor_free(cert);
+
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0);
+ tt_int_op(ret, OP_EQ, 1);
+
+#ifndef OPENSSL_OPAQUE
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ ASN1_TIME_free(cert->cert->cert_info->validity->notAfter);
+ cert->cert->cert_info->validity->notAfter =
+ ASN1_TIME_set(NULL, time(NULL)-1000000);
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ X509_PUBKEY_free(cert->cert->cert_info->key);
+ cert->cert->cert_info->key = NULL;
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 1);
+ tt_int_op(ret, OP_EQ, 0);
+#endif
+
+#if 0
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ /* This doesn't actually change the key in the cert. XXXXXX */
+ BN_one(EVP_PKEY_get1_RSA(X509_get_pubkey(cert->cert))->n);
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 1);
+ tt_int_op(ret, OP_EQ, 0);
+
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ /* This doesn't actually change the key in the cert. XXXXXX */
+ X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC;
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 1);
+ tt_int_op(ret, OP_EQ, 0);
+
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ /* This doesn't actually change the key in the cert. XXXXXX */
+ X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC;
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0);
+ tt_int_op(ret, OP_EQ, 1);
+
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ /* This doesn't actually change the key in the cert. XXXXXX */
+ X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC;
+ X509_get_pubkey(cert->cert)->ameth = NULL;
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0);
+ tt_int_op(ret, OP_EQ, 0);
+#endif
+
+ done:
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+}
+
+static void
+test_tortls_context_init_one(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_context_t *old = NULL;
+
+ MOCK(crypto_pk_new, fixed_crypto_pk_new);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = NULL;
+ ret = tor_tls_context_init_one(&old, NULL, 0, 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ done:
+ UNMOCK(crypto_pk_new);
+}
+
+#define LOCAL_TEST_CASE(name, flags) \
+ { #name, test_tortls_##name, (flags|TT_FORK), NULL, NULL }
+
+#ifdef OPENSSL_OPAQUE
+#define INTRUSIVE_TEST_CASE(name, flags) \
+ { #name, NULL, TT_SKIP, NULL, NULL }
+#else
+#define INTRUSIVE_TEST_CASE(name, flags) LOCAL_TEST_CASE(name, flags)
+#endif
+
+struct testcase_t tortls_tests[] = {
+ LOCAL_TEST_CASE(errno_to_tls_error, 0),
+ LOCAL_TEST_CASE(err_to_string, 0),
+ LOCAL_TEST_CASE(tor_tls_new, TT_FORK),
+ LOCAL_TEST_CASE(tor_tls_get_error, 0),
+ LOCAL_TEST_CASE(get_state_description, TT_FORK),
+ LOCAL_TEST_CASE(get_by_ssl, TT_FORK),
+ LOCAL_TEST_CASE(allocate_tor_tls_object_ex_data_index, TT_FORK),
+ LOCAL_TEST_CASE(log_one_error, TT_FORK),
+ INTRUSIVE_TEST_CASE(get_error, TT_FORK),
+ LOCAL_TEST_CASE(always_accept_verify_cb, 0),
+ INTRUSIVE_TEST_CASE(x509_cert_free, 0),
+ LOCAL_TEST_CASE(x509_cert_get_id_digests, 0),
+ INTRUSIVE_TEST_CASE(cert_matches_key, 0),
+ INTRUSIVE_TEST_CASE(cert_get_key, 0),
+ LOCAL_TEST_CASE(get_my_client_auth_key, TT_FORK),
+ LOCAL_TEST_CASE(get_my_certs, TT_FORK),
+ INTRUSIVE_TEST_CASE(get_ciphersuite_name, 0),
+ INTRUSIVE_TEST_CASE(classify_client_ciphers, 0),
+ LOCAL_TEST_CASE(client_is_using_v2_ciphers, 0),
+ INTRUSIVE_TEST_CASE(verify, 0),
+ INTRUSIVE_TEST_CASE(check_lifetime, 0),
+ INTRUSIVE_TEST_CASE(get_pending_bytes, 0),
+ LOCAL_TEST_CASE(get_forced_write_size, 0),
+ LOCAL_TEST_CASE(get_write_overhead_ratio, TT_FORK),
+ LOCAL_TEST_CASE(used_v1_handshake, TT_FORK),
+ LOCAL_TEST_CASE(get_num_server_handshakes, 0),
+ LOCAL_TEST_CASE(server_got_renegotiate, 0),
+ INTRUSIVE_TEST_CASE(SSL_SESSION_get_master_key, 0),
+ INTRUSIVE_TEST_CASE(get_tlssecrets, 0),
+ INTRUSIVE_TEST_CASE(get_buffer_sizes, 0),
+ LOCAL_TEST_CASE(evaluate_ecgroup_for_tls, 0),
+ INTRUSIVE_TEST_CASE(try_to_extract_certs_from_tls, 0),
+ INTRUSIVE_TEST_CASE(get_peer_cert, 0),
+ INTRUSIVE_TEST_CASE(peer_has_cert, 0),
+ INTRUSIVE_TEST_CASE(shutdown, 0),
+ INTRUSIVE_TEST_CASE(finish_handshake, 0),
+ INTRUSIVE_TEST_CASE(handshake, 0),
+ INTRUSIVE_TEST_CASE(write, 0),
+ INTRUSIVE_TEST_CASE(read, 0),
+ INTRUSIVE_TEST_CASE(server_info_callback, 0),
+ LOCAL_TEST_CASE(is_server, 0),
+ INTRUSIVE_TEST_CASE(assert_renegotiation_unblocked, 0),
+ INTRUSIVE_TEST_CASE(block_renegotiation, 0),
+ INTRUSIVE_TEST_CASE(unblock_renegotiation, 0),
+ INTRUSIVE_TEST_CASE(set_renegotiate_callback, 0),
+ LOCAL_TEST_CASE(set_logged_address, 0),
+ INTRUSIVE_TEST_CASE(find_cipher_by_id, 0),
+ INTRUSIVE_TEST_CASE(session_secret_cb, 0),
+ INTRUSIVE_TEST_CASE(debug_state_callback, 0),
+ INTRUSIVE_TEST_CASE(context_new, 0),
+ LOCAL_TEST_CASE(create_certificate, 0),
+ LOCAL_TEST_CASE(cert_new, 0),
+ LOCAL_TEST_CASE(cert_is_valid, 0),
+ LOCAL_TEST_CASE(context_init_one, 0),
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_util.c b/src/test/test_util.c
index 0a5783e9f5..37f7d938ea 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -14,11 +14,21 @@
#include "memarea.h"
#include "util_process.h"
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_SYS_UTIME_H
+#include <sys/utime.h>
+#endif
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#endif
#ifdef _WIN32
#include <tchar.h>
#endif
#include <math.h>
#include <ctype.h>
+#include <float.h>
/* XXXX this is a minimal wrapper to make the unit tests compile with the
* changed tor_timegm interface. */
@@ -4097,6 +4107,9 @@ test_util_round_to_next_multiple_of(void *arg)
tt_u64_op(round_uint64_to_next_multiple_of(99,7), ==, 105);
tt_u64_op(round_uint64_to_next_multiple_of(99,9), ==, 99);
+ tt_u64_op(round_uint64_to_next_multiple_of(UINT64_MAX,2), ==,
+ UINT64_MAX);
+
tt_i64_op(round_int64_to_next_multiple_of(0,1), ==, 0);
tt_i64_op(round_int64_to_next_multiple_of(0,7), ==, 0);
@@ -4110,7 +4123,27 @@ test_util_round_to_next_multiple_of(void *arg)
tt_i64_op(round_int64_to_next_multiple_of(INT64_MIN,2), ==, INT64_MIN);
tt_i64_op(round_int64_to_next_multiple_of(INT64_MAX,2), ==,
- INT64_MAX-INT64_MAX%2);
+ INT64_MAX);
+
+ tt_int_op(round_uint32_to_next_multiple_of(0,1), ==, 0);
+ tt_int_op(round_uint32_to_next_multiple_of(0,7), ==, 0);
+
+ tt_int_op(round_uint32_to_next_multiple_of(99,1), ==, 99);
+ tt_int_op(round_uint32_to_next_multiple_of(99,7), ==, 105);
+ tt_int_op(round_uint32_to_next_multiple_of(99,9), ==, 99);
+
+ tt_int_op(round_uint32_to_next_multiple_of(UINT32_MAX,2), ==,
+ UINT32_MAX);
+
+ tt_uint_op(round_to_next_multiple_of(0,1), ==, 0);
+ tt_uint_op(round_to_next_multiple_of(0,7), ==, 0);
+
+ tt_uint_op(round_to_next_multiple_of(99,1), ==, 99);
+ tt_uint_op(round_to_next_multiple_of(99,7), ==, 105);
+ tt_uint_op(round_to_next_multiple_of(99,9), ==, 99);
+
+ tt_uint_op(round_to_next_multiple_of(UINT_MAX,2), ==,
+ UINT_MAX);
done:
;
}
@@ -4143,6 +4176,7 @@ test_util_laplace(void *arg)
*/
tt_i64_op(INT64_MIN + 20, ==,
add_laplace_noise(20, 0.0, delta_f, epsilon));
+
tt_i64_op(-60, ==, add_laplace_noise(20, 0.1, delta_f, epsilon));
tt_i64_op(-14, ==, add_laplace_noise(20, 0.25, delta_f, epsilon));
tt_i64_op(20, ==, add_laplace_noise(20, 0.5, delta_f, epsilon));
@@ -4150,15 +4184,146 @@ test_util_laplace(void *arg)
tt_i64_op(100, ==, add_laplace_noise(20, 0.9, delta_f, epsilon));
tt_i64_op(215, ==, add_laplace_noise(20, 0.99, delta_f, epsilon));
+ /* Test extreme values of signal with maximally negative values of noise
+ * 1.0000000000000002 is the smallest number > 1
+ * 0.0000000000000002 is the double epsilon (error when calculating near 1)
+ * this is approximately 1/(2^52)
+ * per https://en.wikipedia.org/wiki/Double_precision
+ * (let's not descend into the world of subnormals)
+ * >>> laplace.ppf([0, 0.0000000000000002], loc = 0, scale = 1)
+ * array([ -inf, -35.45506713])
+ */
+ const double noscale_df = 1.0, noscale_eps = 1.0;
+
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(0, 0.0, noscale_df, noscale_eps));
+
+ /* is it clipped to INT64_MIN? */
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(-1, 0.0, noscale_df, noscale_eps));
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(INT64_MIN, 0.0,
+ noscale_df, noscale_eps));
+ /* ... even when scaled? */
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(0, 0.0, delta_f, epsilon));
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(0, 0.0,
+ DBL_MAX, 1));
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(INT64_MIN, 0.0,
+ DBL_MAX, 1));
+
+ /* does it play nice with INT64_MAX? */
+ tt_i64_op((INT64_MIN + INT64_MAX), ==,
+ add_laplace_noise(INT64_MAX, 0.0,
+ noscale_df, noscale_eps));
+
+ /* do near-zero fractional values work? */
+ const double min_dbl_error = 0.0000000000000002;
+
+ tt_i64_op(-35, ==,
+ add_laplace_noise(0, min_dbl_error,
+ noscale_df, noscale_eps));
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(INT64_MIN, min_dbl_error,
+ noscale_df, noscale_eps));
+ tt_i64_op((-35 + INT64_MAX), ==,
+ add_laplace_noise(INT64_MAX, min_dbl_error,
+ noscale_df, noscale_eps));
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(0, min_dbl_error,
+ DBL_MAX, 1));
+ tt_i64_op((INT64_MAX + INT64_MIN), ==,
+ add_laplace_noise(INT64_MAX, min_dbl_error,
+ DBL_MAX, 1));
+ tt_i64_op(INT64_MIN, ==,
+ add_laplace_noise(INT64_MIN, min_dbl_error,
+ DBL_MAX, 1));
+
+ /* does it play nice with INT64_MAX? */
+ tt_i64_op((INT64_MAX - 35), ==,
+ add_laplace_noise(INT64_MAX, min_dbl_error,
+ noscale_df, noscale_eps));
+
+ /* Test extreme values of signal with maximally positive values of noise
+ * 1.0000000000000002 is the smallest number > 1
+ * 0.9999999999999998 is the greatest number < 1 by calculation
+ * per https://en.wikipedia.org/wiki/Double_precision
+ * >>> laplace.ppf([1.0, 0.9999999999999998], loc = 0, scale = 1)
+ * array([inf, 35.35050621])
+ * but the function rejects p == 1.0, so we just use max_dbl_lt_one
+ */
+ const double max_dbl_lt_one = 0.9999999999999998;
+
+ /* do near-one fractional values work? */
+ tt_i64_op(35, ==,
+ add_laplace_noise(0, max_dbl_lt_one, noscale_df, noscale_eps));
+
+ /* is it clipped to INT64_MAX? */
+ tt_i64_op(INT64_MAX, ==,
+ add_laplace_noise(INT64_MAX - 35, max_dbl_lt_one,
+ noscale_df, noscale_eps));
+ tt_i64_op(INT64_MAX, ==,
+ add_laplace_noise(INT64_MAX - 34, max_dbl_lt_one,
+ noscale_df, noscale_eps));
+ tt_i64_op(INT64_MAX, ==,
+ add_laplace_noise(INT64_MAX, max_dbl_lt_one,
+ noscale_df, noscale_eps));
+ /* ... even when scaled? */
+ tt_i64_op(INT64_MAX, ==,
+ add_laplace_noise(INT64_MAX, max_dbl_lt_one,
+ delta_f, epsilon));
+ tt_i64_op((INT64_MIN + INT64_MAX), ==,
+ add_laplace_noise(INT64_MIN, max_dbl_lt_one,
+ DBL_MAX, 1));
+ tt_i64_op(INT64_MAX, ==,
+ add_laplace_noise(INT64_MAX, max_dbl_lt_one,
+ DBL_MAX, 1));
+ /* does it play nice with INT64_MIN? */
+ tt_i64_op((INT64_MIN + 35), ==,
+ add_laplace_noise(INT64_MIN, max_dbl_lt_one,
+ noscale_df, noscale_eps));
+
done:
;
}
-#define UTIL_LEGACY(name) \
- { #name, test_util_ ## name , 0, NULL, NULL }
+static void
+test_util_clamp_double_to_int64(void *arg)
+{
+ (void)arg;
-#define UTIL_TEST(name, flags) \
- { #name, test_util_ ## name, flags, NULL, NULL }
+ tt_i64_op(INT64_MIN, ==, clamp_double_to_int64(-INFINITY));
+ tt_i64_op(INT64_MIN, ==,
+ clamp_double_to_int64(-1.0 * pow(2.0, 64.0) - 1.0));
+ tt_i64_op(INT64_MIN, ==,
+ clamp_double_to_int64(-1.0 * pow(2.0, 63.0) - 1.0));
+ tt_i64_op(((uint64_t) -1) << 53, ==,
+ clamp_double_to_int64(-1.0 * pow(2.0, 53.0)));
+ tt_i64_op((((uint64_t) -1) << 53) + 1, ==,
+ clamp_double_to_int64(-1.0 * pow(2.0, 53.0) + 1.0));
+ tt_i64_op(-1, ==, clamp_double_to_int64(-1.0));
+ tt_i64_op(0, ==, clamp_double_to_int64(-0.9));
+ tt_i64_op(0, ==, clamp_double_to_int64(-0.1));
+ tt_i64_op(0, ==, clamp_double_to_int64(0.0));
+ tt_i64_op(0, ==, clamp_double_to_int64(NAN));
+ tt_i64_op(0, ==, clamp_double_to_int64(0.1));
+ tt_i64_op(0, ==, clamp_double_to_int64(0.9));
+ tt_i64_op(1, ==, clamp_double_to_int64(1.0));
+ tt_i64_op((((int64_t) 1) << 53) - 1, ==,
+ clamp_double_to_int64(pow(2.0, 53.0) - 1.0));
+ tt_i64_op(((int64_t) 1) << 53, ==,
+ clamp_double_to_int64(pow(2.0, 53.0)));
+ tt_i64_op(INT64_MAX, ==,
+ clamp_double_to_int64(pow(2.0, 63.0)));
+ tt_i64_op(INT64_MAX, ==,
+ clamp_double_to_int64(pow(2.0, 64.0)));
+ tt_i64_op(INT64_MAX, ==, clamp_double_to_int64(INFINITY));
+
+ done:
+ ;
+}
#ifdef FD_CLOEXEC
#define CAN_CHECK_CLOEXEC
@@ -4180,9 +4345,14 @@ fd_is_nonblocking(tor_socket_t fd)
}
#endif
+#define ERRNO_IS_EPROTO(e) (e == SOCK_ERRNO(EPROTONOSUPPORT))
+#define SOCK_ERR_IS_EPROTO(s) ERRNO_IS_EPROTO(tor_socket_errno(s))
+
+/* Test for tor_open_socket*, using IPv4 or IPv6 depending on arg. */
static void
test_util_socket(void *arg)
{
+ const int domain = !strcmp(arg, "4") ? AF_INET : AF_INET6;
tor_socket_t fd1 = TOR_INVALID_SOCKET;
tor_socket_t fd2 = TOR_INVALID_SOCKET;
tor_socket_t fd3 = TOR_INVALID_SOCKET;
@@ -4193,15 +4363,19 @@ test_util_socket(void *arg)
(void)arg;
- fd1 = tor_open_socket_with_extensions(AF_INET, SOCK_STREAM, 0, 0, 0);
- fd2 = tor_open_socket_with_extensions(AF_INET, SOCK_STREAM, 0, 0, 1);
+ fd1 = tor_open_socket_with_extensions(domain, SOCK_STREAM, 0, 0, 0);
+ if (SOCK_ERR_IS_EPROTO(fd1)) {
+ /* Assume we're on an IPv4-only or IPv6-only system, and give up now. */
+ goto done;
+ }
+ fd2 = tor_open_socket_with_extensions(domain, SOCK_STREAM, 0, 0, 1);
tt_assert(SOCKET_OK(fd1));
tt_assert(SOCKET_OK(fd2));
tt_int_op(get_n_open_sockets(), OP_EQ, n + 2);
- //fd3 = tor_open_socket_with_extensions(AF_INET, SOCK_STREAM, 0, 1, 0);
- //fd4 = tor_open_socket_with_extensions(AF_INET, SOCK_STREAM, 0, 1, 1);
- fd3 = tor_open_socket(AF_INET, SOCK_STREAM, 0);
- fd4 = tor_open_socket_nonblocking(AF_INET, SOCK_STREAM, 0);
+ //fd3 = tor_open_socket_with_extensions(domain, SOCK_STREAM, 0, 1, 0);
+ //fd4 = tor_open_socket_with_extensions(domain, SOCK_STREAM, 0, 1, 1);
+ fd3 = tor_open_socket(domain, SOCK_STREAM, 0);
+ fd4 = tor_open_socket_nonblocking(domain, SOCK_STREAM, 0);
tt_assert(SOCKET_OK(fd3));
tt_assert(SOCKET_OK(fd4));
tt_int_op(get_n_open_sockets(), OP_EQ, n + 4);
@@ -4250,8 +4424,20 @@ test_util_socketpair(void *arg)
int n = get_n_open_sockets();
tor_socket_t fds[2] = {TOR_INVALID_SOCKET, TOR_INVALID_SOCKET};
const int family = AF_UNIX;
+ int socketpair_result = 0;
+
+ socketpair_result = tor_socketpair_fn(family, SOCK_STREAM, 0, fds);
+ /* If there is no 127.0.0.1 or ::1, tor_ersatz_socketpair will and must fail.
+ * Otherwise, we risk exposing a socketpair on a routable IP address. (Some
+ * BSD jails use a routable address for localhost. Fortunately, they have
+ * the real AF_UNIX socketpair.) */
+ if (ersatz && ERRNO_IS_EPROTO(-socketpair_result)) {
+ /* In my testing, an IPv6-only FreeBSD jail without ::1 returned EINVAL.
+ * Assume we're on a machine without 127.0.0.1 or ::1 and give up now. */
+ goto done;
+ }
+ tt_int_op(0, OP_EQ, socketpair_result);
- tt_int_op(0, OP_EQ, tor_socketpair_fn(family, SOCK_STREAM, 0, fds));
tt_assert(SOCKET_OK(fds[0]));
tt_assert(SOCKET_OK(fds[1]));
tt_int_op(get_n_open_sockets(), OP_EQ, n + 2);
@@ -4271,6 +4457,8 @@ test_util_socketpair(void *arg)
tor_close_socket(fds[1]);
}
+#undef SOCKET_EPROTO
+
static void
test_util_max_mem(void *arg)
{
@@ -4414,6 +4602,92 @@ test_util_get_avail_disk_space(void *arg)
;
}
+static void
+test_util_touch_file(void *arg)
+{
+ (void) arg;
+ const char *fname = get_fname("touch");
+
+ const time_t now = time(NULL);
+ struct stat st;
+ write_bytes_to_file(fname, "abc", 3, 1);
+ tt_int_op(0, OP_EQ, stat(fname, &st));
+ /* A subtle point: the filesystem time is not necessarily equal to the
+ * system clock time, since one can be using a monotonic clock, or coarse
+ * monotonic clock, or whatever. So we might wind up with an mtime a few
+ * microseconds ago. Let's just give it a lot of wiggle room. */
+ tt_i64_op(st.st_mtime, OP_GE, now - 1);
+
+ const time_t five_sec_ago = now - 5;
+ struct utimbuf u = { five_sec_ago, five_sec_ago };
+ tt_int_op(0, OP_EQ, utime(fname, &u));
+ tt_int_op(0, OP_EQ, stat(fname, &st));
+ /* Let's hope that utime/stat give the same second as a round-trip? */
+ tt_i64_op(st.st_mtime, OP_EQ, five_sec_ago);
+
+ /* Finally we can touch the file */
+ tt_int_op(0, OP_EQ, touch_file(fname));
+ tt_int_op(0, OP_EQ, stat(fname, &st));
+ tt_i64_op(st.st_mtime, OP_GE, now-1);
+
+ done:
+ ;
+}
+
+#ifndef _WIN32
+static void
+test_util_pwdb(void *arg)
+{
+ (void) arg;
+ const struct passwd *me = NULL, *me2, *me3;
+ char *name = NULL;
+ char *dir = NULL;
+
+ /* Uncached case. */
+ /* Let's assume that we exist. */
+ me = tor_getpwuid(getuid());
+ tt_assert(me != NULL);
+ name = tor_strdup(me->pw_name);
+
+ /* Uncached case */
+ me2 = tor_getpwnam(name);
+ tt_assert(me2 != NULL);
+ tt_int_op(me2->pw_uid, OP_EQ, getuid());
+
+ /* Cached case */
+ me3 = tor_getpwuid(getuid());
+ tt_assert(me3 != NULL);
+ tt_str_op(me3->pw_name, OP_EQ, name);
+
+ me3 = tor_getpwnam(name);
+ tt_assert(me3 != NULL);
+ tt_int_op(me3->pw_uid, OP_EQ, getuid());
+
+ dir = get_user_homedir(name);
+ tt_assert(dir != NULL);
+
+ done:
+ tor_free(name);
+ tor_free(dir);
+}
+#endif
+
+#define UTIL_LEGACY(name) \
+ { #name, test_util_ ## name , 0, NULL, NULL }
+
+#define UTIL_TEST(name, flags) \
+ { #name, test_util_ ## name, flags, NULL, NULL }
+
+#ifdef _WIN32
+#define UTIL_TEST_NO_WIN(n, f) { #n, NULL, TT_SKIP, NULL, NULL }
+#define UTIL_TEST_WIN_ONLY(n, f) UTIL_TEST(n, (f))
+#define UTIL_LEGACY_NO_WIN(n) UTIL_TEST_NO_WIN(n, 0)
+#else
+#define UTIL_TEST_NO_WIN(n, f) UTIL_TEST(n, (f))
+#define UTIL_TEST_WIN_ONLY(n, f) { #n, NULL, TT_SKIP, NULL, NULL }
+#define UTIL_LEGACY_NO_WIN(n) UTIL_LEGACY(n)
+#endif
+
struct testcase_t util_tests[] = {
UTIL_LEGACY(time),
UTIL_TEST(parse_http_time, 0),
@@ -4421,9 +4695,7 @@ struct testcase_t util_tests[] = {
UTIL_LEGACY(config_line_quotes),
UTIL_LEGACY(config_line_comment_character),
UTIL_LEGACY(config_line_escaped_content),
-#ifndef _WIN32
- UTIL_LEGACY(expand_filename),
-#endif
+ UTIL_LEGACY_NO_WIN(expand_filename),
UTIL_LEGACY(escape_string_socks),
UTIL_LEGACY(string_is_key_value),
UTIL_LEGACY(strmisc),
@@ -4441,19 +4713,16 @@ struct testcase_t util_tests[] = {
UTIL_TEST(di_map, 0),
UTIL_TEST(round_to_next_multiple_of, 0),
UTIL_TEST(laplace, 0),
+ UTIL_TEST(clamp_double_to_int64, 0),
UTIL_TEST(find_str_at_start_of_line, 0),
UTIL_TEST(string_is_C_identifier, 0),
UTIL_TEST(asprintf, 0),
UTIL_TEST(listdir, 0),
UTIL_TEST(parent_dir, 0),
UTIL_TEST(ftruncate, 0),
-#ifdef _WIN32
- UTIL_TEST(load_win_lib, 0),
-#endif
-#ifndef _WIN32
- UTIL_TEST(exit_status, 0),
- UTIL_TEST(fgets_eagain, 0),
-#endif
+ UTIL_TEST_WIN_ONLY(load_win_lib, 0),
+ UTIL_TEST_NO_WIN(exit_status, 0),
+ UTIL_TEST_NO_WIN(fgets_eagain, 0),
UTIL_TEST(format_hex_number, 0),
UTIL_TEST(format_dec_number, 0),
UTIL_TEST(join_win_cmdline, 0),
@@ -4473,7 +4742,10 @@ struct testcase_t util_tests[] = {
UTIL_TEST(write_chunks_to_file, 0),
UTIL_TEST(mathlog, 0),
UTIL_TEST(weak_random, 0),
- UTIL_TEST(socket, TT_FORK),
+ { "socket_ipv4", test_util_socket, TT_FORK, &passthrough_setup,
+ (void*)"4" },
+ { "socket_ipv6", test_util_socket, TT_FORK,
+ &passthrough_setup, (void*)"6" },
{ "socketpair", test_util_socketpair, TT_FORK, &passthrough_setup,
(void*)"0" },
{ "socketpair_ersatz", test_util_socketpair, TT_FORK,
@@ -4483,6 +4755,8 @@ struct testcase_t util_tests[] = {
UTIL_TEST(ipv4_validation, 0),
UTIL_TEST(writepid, 0),
UTIL_TEST(get_avail_disk_space, 0),
+ UTIL_TEST(touch_file, 0),
+ UTIL_TEST_NO_WIN(pwdb, TT_FORK),
END_OF_TESTCASES
};
diff --git a/src/test/test_util_format.c b/src/test/test_util_format.c
new file mode 100644
index 0000000000..45a28ccaac
--- /dev/null
+++ b/src/test/test_util_format.c
@@ -0,0 +1,302 @@
+/* Copyright (c) 2010-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "or.h"
+
+#include "test.h"
+
+#define UTIL_FORMAT_PRIVATE
+#include "util_format.h"
+
+#define NS_MODULE util_format
+
+#if !defined(HAVE_HTONLL) && !defined(htonll)
+#ifdef WORDS_BIGENDIAN
+#define htonll(x) (x)
+#else
+static uint64_t
+htonll(uint64_t a)
+{
+ return htonl((uint32_t)(a>>32)) | (((uint64_t)htonl((uint32_t)a))<<32);
+}
+#endif
+#endif
+
+static void
+test_util_format_unaligned_accessors(void *ignored)
+{
+ (void)ignored;
+ char buf[9] = "onionsoup"; // 6f6e696f6e736f7570
+
+ tt_u64_op(get_uint64(buf+1), OP_EQ, htonll(U64_LITERAL(0x6e696f6e736f7570)));
+ tt_uint_op(get_uint32(buf+1), OP_EQ, htonl(0x6e696f6e));
+ tt_uint_op(get_uint16(buf+1), OP_EQ, htons(0x6e69));
+ tt_uint_op(get_uint8(buf+1), OP_EQ, 0x6e);
+
+ set_uint8(buf+7, 0x61);
+ tt_mem_op(buf, OP_EQ, "onionsoap", 9);
+
+ set_uint16(buf+6, htons(0x746f));
+ tt_mem_op(buf, OP_EQ, "onionstop", 9);
+
+ set_uint32(buf+1, htonl(0x78696465));
+ tt_mem_op(buf, OP_EQ, "oxidestop", 9);
+
+ set_uint64(buf+1, htonll(U64_LITERAL(0x6266757363617465)));
+ tt_mem_op(buf, OP_EQ, "obfuscate", 9);
+ done:
+ ;
+}
+
+static void
+test_util_format_base64_encode(void *ignored)
+{
+ (void)ignored;
+ int res;
+ int i;
+ char *src;
+ char *dst;
+
+ src = tor_malloc_zero(256);
+ dst = tor_malloc_zero(1000);
+
+ for (i=0;i<256;i++) {
+ src[i] = (char)i;
+ }
+
+ res = base64_encode(NULL, 1, src, 1, 0);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base64_encode(dst, 1, NULL, 1, 0);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base64_encode(dst, 1, src, 10, 0);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base64_encode(dst, SSIZE_MAX-1, src, 1, 0);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base64_encode(dst, SSIZE_MAX-1, src, 10, 0);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base64_encode(dst, 1000, src, 256, 0);
+ tt_int_op(res, OP_EQ, 344);
+ tt_str_op(dst, OP_EQ, "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh"
+ "8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZH"
+ "SElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3"
+ "BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeY"
+ "mZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wM"
+ "HCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp"
+ "6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==");
+
+ res = base64_encode(dst, 1000, src, 256, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 350);
+ tt_str_op(dst, OP_EQ,
+ "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v\n"
+ "MDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5f\n"
+ "YGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6P\n"
+ "kJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/\n"
+ "wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v\n"
+ "8PHy8/T19vf4+fr7/P3+/w==\n");
+
+ res = base64_encode(dst, 1000, src+1, 255, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 346);
+
+ for (i = 0;i<50;i++) {
+ src[i] = 0;
+ }
+ src[50] = 255;
+ src[51] = 255;
+ src[52] = 255;
+ src[53] = 255;
+
+ res = base64_encode(dst, 1000, src, 54, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 74);
+
+ res = base64_encode(dst, 1000, src+1, 53, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 74);
+
+ res = base64_encode(dst, 1000, src+2, 52, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 74);
+
+ res = base64_encode(dst, 1000, src+3, 51, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 70);
+
+ res = base64_encode(dst, 1000, src+4, 50, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 70);
+
+ res = base64_encode(dst, 1000, src+5, 49, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 70);
+
+ res = base64_encode(dst, 1000, src+6, 48, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 65);
+
+ res = base64_encode(dst, 1000, src+7, 47, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 65);
+
+ res = base64_encode(dst, 1000, src+8, 46, BASE64_ENCODE_MULTILINE);
+ tt_int_op(res, OP_EQ, 65);
+
+ done:
+ tor_free(src);
+ tor_free(dst);
+}
+
+static void
+test_util_format_base64_decode_nopad(void *ignored)
+{
+ (void)ignored;
+ int res;
+ int i;
+ char *src;
+ uint8_t *dst, *real_dst;
+ uint8_t expected[] = {0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65};
+ char real_src[] = "ZXhhbXBsZQ";
+
+ src = tor_malloc_zero(256);
+ dst = tor_malloc_zero(1000);
+ real_dst = tor_malloc_zero(10);
+
+ for (i=0;i<256;i++) {
+ src[i] = (char)i;
+ }
+
+ res = base64_decode_nopad(dst, 1, src, SIZE_T_CEILING);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base64_decode_nopad(dst, 1, src, 5);
+ tt_int_op(res, OP_EQ, -1);
+
+ const char *s = "SGVsbG8gd29ybGQ";
+ res = base64_decode_nopad(dst, 1000, s, strlen(s));
+ tt_int_op(res, OP_EQ, 11);
+ tt_mem_op(dst, OP_EQ, "Hello world", 11);
+
+ s = "T3BhIG11bmRv";
+ res = base64_decode_nopad(dst, 9, s, strlen(s));
+ tt_int_op(res, OP_EQ, 9);
+ tt_mem_op(dst, OP_EQ, "Opa mundo", 9);
+
+ res = base64_decode_nopad(real_dst, 10, real_src, 10);
+ tt_int_op(res, OP_EQ, 7);
+ tt_mem_op(real_dst, OP_EQ, expected, 7);
+
+ done:
+ tor_free(src);
+ tor_free(dst);
+ tor_free(real_dst);
+}
+
+static void
+test_util_format_base64_decode(void *ignored)
+{
+ (void)ignored;
+ int res;
+ int i;
+ char *src;
+ char *dst, *real_dst;
+ uint8_t expected[] = {0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65};
+ char real_src[] = "ZXhhbXBsZQ==";
+
+ src = tor_malloc_zero(256);
+ dst = tor_malloc_zero(1000);
+ real_dst = tor_malloc_zero(10);
+
+ for (i=0;i<256;i++) {
+ src[i] = (char)i;
+ }
+
+ res = base64_decode(dst, 1, src, SIZE_T_CEILING);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base64_decode(dst, SIZE_T_CEILING+1, src, 10);
+ tt_int_op(res, OP_EQ, -1);
+
+ const char *s = "T3BhIG11bmRv";
+ res = base64_decode(dst, 9, s, strlen(s));
+ tt_int_op(res, OP_EQ, 9);
+ tt_mem_op(dst, OP_EQ, "Opa mundo", 9);
+
+ memset(dst, 0, 1000);
+ res = base64_decode(dst, 100, s, strlen(s));
+ tt_int_op(res, OP_EQ, 9);
+ tt_mem_op(dst, OP_EQ, "Opa mundo", 9);
+
+ s = "SGVsbG8gd29ybGQ=";
+ res = base64_decode(dst, 100, s, strlen(s));
+ tt_int_op(res, OP_EQ, 11);
+ tt_mem_op(dst, OP_EQ, "Hello world", 11);
+
+ res = base64_decode(real_dst, 10, real_src, 10);
+ tt_int_op(res, OP_EQ, 7);
+ tt_mem_op(real_dst, OP_EQ, expected, 7);
+
+ done:
+ tor_free(src);
+ tor_free(dst);
+ tor_free(real_dst);
+}
+
+static void
+test_util_format_base16_decode(void *ignored)
+{
+ (void)ignored;
+ int res;
+ int i;
+ char *src;
+ char *dst, *real_dst;
+ char expected[] = {0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65};
+ char real_src[] = "6578616D706C65";
+
+ src = tor_malloc_zero(256);
+ dst = tor_malloc_zero(1000);
+ real_dst = tor_malloc_zero(10);
+
+ for (i=0;i<256;i++) {
+ src[i] = (char)i;
+ }
+
+ res = base16_decode(dst, 3, src, 3);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base16_decode(dst, 1, src, 10);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base16_decode(dst, SIZE_T_CEILING+2, src, 10);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base16_decode(dst, 1000, "", 0);
+ tt_int_op(res, OP_EQ, 0);
+
+ res = base16_decode(dst, 1000, "aabc", 4);
+ tt_int_op(res, OP_EQ, 0);
+ tt_mem_op(dst, OP_EQ, "\xaa\xbc", 2);
+
+ res = base16_decode(dst, 1000, "aabcd", 6);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base16_decode(dst, 1000, "axxx", 4);
+ tt_int_op(res, OP_EQ, -1);
+
+ res = base16_decode(real_dst, 10, real_src, 14);
+ tt_int_op(res, OP_EQ, 0);
+ tt_mem_op(real_dst, OP_EQ, expected, 7);
+
+ done:
+ tor_free(src);
+ tor_free(dst);
+ tor_free(real_dst);
+}
+
+struct testcase_t util_format_tests[] = {
+ { "unaligned_accessors", test_util_format_unaligned_accessors, 0,
+ NULL, NULL },
+ { "base64_encode", test_util_format_base64_encode, 0, NULL, NULL },
+ { "base64_decode_nopad", test_util_format_base64_decode_nopad, 0,
+ NULL, NULL },
+ { "base64_decode", test_util_format_base64_decode, 0, NULL, NULL },
+ { "base16_decode", test_util_format_base16_decode, 0, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_util_process.c b/src/test/test_util_process.c
new file mode 100644
index 0000000000..0a4354c043
--- /dev/null
+++ b/src/test/test_util_process.c
@@ -0,0 +1,82 @@
+/* Copyright (c) 2010-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define UTIL_PROCESS_PRIVATE
+#include "orconfig.h"
+#include "or.h"
+
+#include "test.h"
+
+#include "util_process.h"
+
+#include "log_test_helpers.h"
+
+#ifndef _WIN32
+#define NS_MODULE util_process
+
+static void
+temp_callback(int r, void *s)
+{
+ (void)r;
+ (void)s;
+}
+
+static void
+test_util_process_set_waitpid_callback(void *ignored)
+{
+ (void)ignored;
+ waitpid_callback_t *res1 = NULL, *res2 = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+ pid_t pid = (pid_t)42;
+
+ res1 = set_waitpid_callback(pid, temp_callback, NULL);
+ tt_assert(res1);
+
+ res2 = set_waitpid_callback(pid, temp_callback, NULL);
+ tt_assert(res2);
+ expect_log_msg("Replaced a waitpid monitor on pid 42. That should be "
+ "impossible.\n");
+
+ done:
+ teardown_capture_of_logs(previous_log);
+ clear_waitpid_callback(res1);
+ clear_waitpid_callback(res2);
+}
+
+static void
+test_util_process_clear_waitpid_callback(void *ignored)
+{
+ (void)ignored;
+ waitpid_callback_t *res;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+ pid_t pid = (pid_t)43;
+
+ clear_waitpid_callback(NULL);
+
+ res = set_waitpid_callback(pid, temp_callback, NULL);
+ clear_waitpid_callback(res);
+ expect_no_log_entry();
+
+#if 0
+ /* No. This is use-after-free. We don't _do_ that. XXXX */
+ clear_waitpid_callback(res);
+ expect_log_msg("Couldn't remove waitpid monitor for pid 43.\n");
+#endif
+
+ done:
+ teardown_capture_of_logs(previous_log);
+}
+#endif /* _WIN32 */
+
+#ifndef _WIN32
+#define TEST(name) { #name, test_util_process_##name, 0, NULL, NULL }
+#else
+#define TEST(name) { #name, NULL, TT_SKIP, NULL, NULL }
+#endif
+
+struct testcase_t util_process_tests[] = {
+ TEST(set_waitpid_callback),
+ TEST(clear_waitpid_callback),
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_workqueue.c b/src/test/test_workqueue.c
index 0d79733cf0..1202f80fa3 100644
--- a/src/test/test_workqueue.c
+++ b/src/test/test_workqueue.c
@@ -390,8 +390,14 @@ main(int argc, char **argv)
init_logging(1);
network_init();
- crypto_global_init(1, NULL, NULL);
- crypto_seed_rng();
+ if (crypto_global_init(1, NULL, NULL) < 0) {
+ printf("Couldn't initialize crypto subsystem; exiting.\n");
+ return 1;
+ }
+ if (crypto_seed_rng() < 0) {
+ printf("Couldn't seed RNG; exiting.\n");
+ return 1;
+ }
rq = replyqueue_new(as_flags);
tor_assert(rq);
diff --git a/src/test/testing_common.c b/src/test/testing_common.c
index 441024bd7d..da9969d8d9 100644
--- a/src/test/testing_common.c
+++ b/src/test/testing_common.c
@@ -228,6 +228,9 @@ 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);
+
#ifdef USE_DMALLOC
{
int r = CRYPTO_set_mem_ex_functions(tor_malloc_, tor_realloc_, tor_free_);
@@ -238,8 +241,12 @@ main(int c, const char **v)
update_approx_time(time(NULL));
options = options_new();
tor_threads_init();
+
+ struct tor_libevent_cfg cfg;
+ memset(&cfg, 0, sizeof(cfg));
+ tor_libevent_initialize(&cfg);
+
control_initialize_event_queue();
- init_logging(1);
configure_backtrace_handler(get_version());
for (i_out = i = 1; i < c; ++i) {
@@ -272,7 +279,10 @@ main(int c, const char **v)
return 1;
}
crypto_set_tls_dh_prime();
- crypto_seed_rng();
+ if (crypto_seed_rng() < 0) {
+ printf("Couldn't seed RNG; exiting.\n");
+ return 1;
+ }
rep_hist_init();
network_init();
setup_directory();
@@ -294,6 +304,7 @@ main(int c, const char **v)
tor_free_all(0);
dmalloc_log_unfreed();
#endif
+ crypto_global_cleanup();
if (have_failed)
return 1;
diff --git a/src/test/vote_descriptors.inc b/src/test/vote_descriptors.inc
new file mode 100644
index 0000000000..c5ce21f744
--- /dev/null
+++ b/src/test/vote_descriptors.inc
@@ -0,0 +1,94 @@
+const char* VOTE_BODY_V3 =
+"network-status-version 3\n"
+"vote-status vote\n"
+"consensus-methods 13 14 15 16 17 18 19 20 21\n"
+"published 2015-09-02 19:34:15\n"
+"valid-after 2015-09-02 19:50:55\n"
+"fresh-until 2015-09-02 20:07:38\n"
+"valid-until 2015-09-02 20:24:15\n"
+"voting-delay 100 250\n"
+"client-versions 0.1.2.14,0.1.2.17\n"
+"server-versions 0.1.2.10,0.1.2.15,0.1.2.16\n"
+"known-flags Authority Exit Fast Guard MadeOfCheese MadeOfTin Running Stable V2Dir Valid\n"
+"flag-thresholds stable-uptime=0 stable-mtbf=0 fast-speed=0 guard-wfu=0.000% guard-tk=0 guard-bw-inc-exits=0 guard-bw-exc-exits=0 enough-mtbf=0 ignoring-advertised-bws=0\n"
+"params circuitwindow=80 foo=660\n"
+"dir-source Voter3 D867ACF56A9D229B35C25F0090BC9867E906BE69 3.4.5.6 3.4.5.6 80 9000\n"
+"contact voter@example.com\n"
+"legacy-dir-key 4141414141414141414141414141414141414141\n"
+"dir-key-certificate-version 3\n"
+"fingerprint D867ACF56A9D229B35C25F0090BC9867E906BE69\n"
+"dir-key-published 2008-12-12 18:07:24\n"
+"dir-key-expires 2009-12-12 18:07:24\n"
+"dir-identity-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIIBigKCAYEAveMpKlw8oD1YqFqpJchuwSR82BDhutbqgHiez3QO9FmzOctJpV+Y\n"
+"mpTYIJLS/qC+4GBKFF1VK0C4SoBrS3zri0qdXdE+vBGcyrxrjMklpxoqSKRY2011\n"
+"4eqYPghKlo5RzuqteBclGCHyNxWjUJeRKDWgvh+U/gr2uYM6fRm5q0fCzg4aECE7\n"
+"VP6fDGZrMbQI8jHpiMSoC9gkUASNEa6chLInlnP8/H5qUEW4TB9CN/q095pefuwL\n"
+"P+F+1Nz5hnM7fa5XmeMB8iM4RriUmOQlLBZgpQBMpEfWMIPcR9F1Gh3MxERqqUcH\n"
+"tmij+IZdeXg9OkCXykcabaYIhZD3meErn9Tax4oA/THduLfgli9zM0ExwzH1OooN\n"
+"L8rIcJ+2eBo3bQiQUbdYW71sl9w7nSPtircbJUa1mUvWYLPWQxFliPiQSetgJLMj\n"
+"VQqtPmV2hvN2Xk3lLfJO50qMTK7w7Gsaw8UtV4YDM1Hcjp/hQaIB1xfwhXgl+eUU\n"
+"btUa4c+cUTjHAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"dir-signing-key\n"
+"-----BEGIN RSA PUBLIC KEY-----\n"
+"MIGJAoGBALPSUInyuEu6NV3NjozplaniIEBzQXEjv1x9/+mqnwZABpYVmuy9A8nx\n"
+"eoyY3sZFsnYwNW/IZjAgG23pEmevu3F+L4myMjjaa6ORl3MgRYQ4gmuFqpefrGdm\n"
+"ywRCleh2JerkQ4VxOuq10dn/abITzLyaZzMw30KXWp5pxKXOLtxFAgMBAAE=\n"
+"-----END RSA PUBLIC KEY-----\n"
+"dir-key-crosscert\n"
+"-----BEGIN ID SIGNATURE-----\n"
+"FTBJNR/Hlt4T53yUMp1r/QCSMCpkHJCbYBT0R0pvYqhqFfYN5qHRSICRXaFFImIF\n"
+"0DGWmwRza6DxPKNzkm5/b7I0de9zJW1jNNdQAQK5xppAtQcAafRdu8cBonnmh9KX\n"
+"k1NrAK/X00FYywju3yl/SxCn1GddVNkHYexEudmJMPM=\n"
+"-----END ID SIGNATURE-----\n"
+"dir-key-certification\n"
+"-----BEGIN SIGNATURE-----\n"
+"pjWguLFBfELZDc6DywL6Do21SCl7LcutfpM92MEn4WYeSNcTXNR6lRX7reOEJk4e\n"
+"NwEaMt+Hl7slgeR5wjnW3OmMmRPZK9bquNWbfD+sAOV9bRFZTpXIdleAQFPlwvMF\n"
+"z/Gzwspzn4i2Yh6hySShrctMmW8YL3OM8LsBXzBhp/rG2uHlsxmIsc13DA6HWt61\n"
+"ffY72uNE6KckDGsQ4wPGP9q69y6g+X+TNio1KPbsILbePv6EjbO+rS8FiS4njPlg\n"
+"SPYry1RaUvxzxTkswIzdE1tjJrUiqpbWlTGxrH9N4OszoLm45Pc784KLULrjKIoi\n"
+"Q+vRsGrcMBAa+kDowWU6H1ryKR7KOhzRTcf2uqLE/W3ezaRwmOG+ETmoVFwbhk2X\n"
+"OlbXEM9fWP+INvFkr6Z93VYL2jGkCjV7e3xXmre/Lb92fUcYi6t5dwzfV8gJnIoG\n"
+"eCHd0K8NrQK0ipVk/7zcPDKOPeo9Y5aj/f6X/pDHtb+Dd5sT+l82G/Tqy4DIYUYR\n"
+"-----END SIGNATURE-----\n"
+"r router2 AwMDAwMDAwMDAwMDAwMDAwMDAwM Tk5OTk5OTk5OTk5OTk5OTk5OTk4 2015-09-02 19:09:15 153.0.136.1 443 8000\n"
+"s Running V2Dir\n"
+"v 0.1.2.14\n"
+"w Bandwidth=30 Measured=30\n"
+"p reject 1-65535\n"
+"id ed25519 none\n"
+"m 9,10,11,12,13,14,15,16,17 sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa0\n"
+"r router1 BQUFBQUFBQUFBQUFBQUFBQUFBQU TU1NTU1NTU1NTU1NTU1NTU1NTU0 2015-09-02 19:17:35 153.0.153.1 443 0\n"
+"a [1:2:3::4]:4711\n"
+"s Exit Fast Guard Running Stable Valid\n"
+"v 0.2.0.5\n"
+"w Bandwidth=120 Measured=120\n"
+"p reject 1-65535\n"
+"id ed25519 none\n"
+"m 9,10,11,12,13,14,15,16,17 sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa1\n"
+"r router3 MzMzMzMzMzMzMzMzMzMzMzMzMzM T09PT09PT09PT09PT09PT09PT08 2015-09-02 19:17:35 170.0.153.1 400 9999\n"
+"s Authority Exit Fast Guard Running Stable V2Dir Valid\n"
+"v 0.1.0.3\n"
+"w Bandwidth=120\n"
+"p reject 1-65535\n"
+"id ed25519 none\n"
+"m 9,10,11,12,13,14,15,16,17 "
+"sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa2\n"
+"r router4 NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ Ly8vLy8vLy8vLy8vLy8vLy8vLy8 2015-09-02 19:17:35 192.0.2.3 500 1999\n"
+"s Running V2Dir\n"
+"v 0.1.6.3\n"
+"w Bandwidth=30\n"
+"p reject 1-65535\n"
+"id ed25519 none\n"
+"m 9,10,11,12,13,14,15,16,17 sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa3\n"
+"directory-footer\n"
+"directory-signature D867ACF56A9D229B35C25F0090BC9867E906BE69 CBF56A83368A5150F1A9AAADAFB4D77F8C4170E2\n"
+"-----BEGIN SIGNATURE-----\n"
+"AHiWcHe+T3XbnlQqvqSAk6RY3XmEy1+hM2u9Xk6BNi7BpQkEQM1f0vzRpgn5Dnf2\n"
+"TXQWGUq9Z7jdSVnzWT3xqPA4zjw6eZkj+DKUtwq+oEDZGlf8eHTFmr0NAWfwZbk9\n"
+"NAjbMTUXUP37N2XAZwkoCWwFCrrfMwXrL7OhZbj7ifo=\n"
+"-----END SIGNATURE-----\n";
+
diff --git a/src/tools/include.am b/src/tools/include.am
index ebdd349cb1..38ed57546f 100644
--- a/src/tools/include.am
+++ b/src/tools/include.am
@@ -20,6 +20,7 @@ endif
src_tools_tor_gencert_SOURCES = src/tools/tor-gencert.c
src_tools_tor_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
src_tools_tor_gencert_LDADD = src/common/libor.a src/common/libor-crypto.a \
+ $(LIBKECCAK_TINY) \
$(LIBDONNA) \
@TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \
@TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
@@ -31,6 +32,7 @@ src_tools_tor_cov_gencert_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
src_tools_tor_cov_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
src_tools_tor_cov_gencert_LDADD = src/common/libor-testing.a \
src/common/libor-crypto-testing.a \
+ $(LIBKECCAK_TINY) \
$(LIBDONNA) \
@TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \
@TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
@@ -39,6 +41,7 @@ endif
src_tools_tor_checkkey_SOURCES = src/tools/tor-checkkey.c
src_tools_tor_checkkey_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
src_tools_tor_checkkey_LDADD = src/common/libor.a src/common/libor-crypto.a \
+ $(LIBKECCAK_TINY) \
$(LIBDONNA) \
@TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \
@TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
diff --git a/src/trunnel/README b/src/trunnel/README
new file mode 100644
index 0000000000..e24aea0764
--- /dev/null
+++ b/src/trunnel/README
@@ -0,0 +1,21 @@
+This directory contains code for use with, and code made by, the
+automatic code generation tool "Trunnel".
+
+Trunnel generates binary parsers and formatters for simple data
+structures. It aims for human-readable, obviously-correct outputs over
+maximum efficiency or flexibility.
+
+The .trunnel files are the inputs here; the .c and .h files are the outputs.
+
+To add a new structure:
+ - Add a new .trunnel file or expand an existing one to describe the format
+ of the structure.
+ - Regenerate the .c and .h files. To do this, you run
+ "scripts/codegen/run_trunnel.sh". You'll need trunnel installed.
+ - Add the .trunnel, .c, and .h files to include.am
+
+For the Trunnel source code, and more documentation about using Trunnel,
+see https://gitweb.torproject.org/trunnel.git , especially
+ https://gitweb.torproject.org/trunnel.git/tree/README
+and https://gitweb.torproject.org/trunnel.git/tree/doc/trunnel.md
+
diff --git a/src/trunnel/ed25519_cert.c b/src/trunnel/ed25519_cert.c
index ee010dbff9..f495743667 100644
--- a/src/trunnel/ed25519_cert.c
+++ b/src/trunnel/ed25519_cert.c
@@ -1,4 +1,4 @@
-/* ed25519_cert.c -- generated by Trunnel v1.4.3.
+/* ed25519_cert.c -- generated by Trunnel v1.4.4.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
@@ -289,7 +289,8 @@ ed25519_cert_extension_encode(uint8_t *output, const size_t avail, const ed25519
trunnel_assert(written <= avail);
if (avail - written < elt_len)
goto truncated;
- memcpy(ptr, obj->un_unparsed.elts_, elt_len);
+ if (elt_len)
+ memcpy(ptr, obj->un_unparsed.elts_, elt_len);
written += elt_len; ptr += elt_len;
}
break;
@@ -374,7 +375,8 @@ ed25519_cert_extension_parse_into(ed25519_cert_extension_t *obj, const uint8_t *
/* Parse u8 un_unparsed[] */
TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->un_unparsed, remaining, {});
obj->un_unparsed.n_ = remaining;
- memcpy(obj->un_unparsed.elts_, ptr, remaining);
+ if (remaining)
+ memcpy(obj->un_unparsed.elts_, ptr, remaining);
ptr += remaining; remaining -= remaining;
break;
}
diff --git a/src/trunnel/ed25519_cert.h b/src/trunnel/ed25519_cert.h
index face810dbe..75a82d8aff 100644
--- a/src/trunnel/ed25519_cert.h
+++ b/src/trunnel/ed25519_cert.h
@@ -1,4 +1,4 @@
-/* ed25519_cert.h -- generated by by Trunnel v1.4.3.
+/* ed25519_cert.h -- generated by by Trunnel v1.4.4.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
diff --git a/src/trunnel/include.am b/src/trunnel/include.am
index 9bf37fe58b..b1448b7cb2 100644
--- a/src/trunnel/include.am
+++ b/src/trunnel/include.am
@@ -36,3 +36,7 @@ src_trunnel_libor_trunnel_testing_a_CPPFLAGS = -DTRUNNEL_LOCAL_H $(AM_CPPFLAGS)
src_trunnel_libor_trunnel_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
noinst_HEADERS+= $(TRUNNELHEADERS)
+
+EXTRA_DIST += \
+ src/trunnel/README
+
diff --git a/src/trunnel/link_handshake.c b/src/trunnel/link_handshake.c
index f9b55f0739..3ef7341ae9 100644
--- a/src/trunnel/link_handshake.c
+++ b/src/trunnel/link_handshake.c
@@ -1,4 +1,4 @@
-/* link_handshake.c -- generated by Trunnel v1.4.3.
+/* link_handshake.c -- generated by Trunnel v1.4.4.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
@@ -537,7 +537,8 @@ certs_cell_cert_encode(uint8_t *output, const size_t avail, const certs_cell_cer
trunnel_assert(written <= avail);
if (avail - written < elt_len)
goto truncated;
- memcpy(ptr, obj->body.elts_, elt_len);
+ if (elt_len)
+ memcpy(ptr, obj->body.elts_, elt_len);
written += elt_len; ptr += elt_len;
}
@@ -589,7 +590,8 @@ certs_cell_cert_parse_into(certs_cell_cert_t *obj, const uint8_t *input, const s
CHECK_REMAINING(obj->cert_len, truncated);
TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->body, obj->cert_len, {});
obj->body.n_ = obj->cert_len;
- memcpy(obj->body.elts_, ptr, obj->cert_len);
+ if (obj->cert_len)
+ memcpy(obj->body.elts_, ptr, obj->cert_len);
ptr += obj->cert_len; remaining -= obj->cert_len;
trunnel_assert(ptr + remaining == input + len_in);
return len_in - remaining;
@@ -840,7 +842,8 @@ rsa_ed_crosscert_encode(uint8_t *output, const size_t avail, const rsa_ed_crossc
trunnel_assert(written <= avail);
if (avail - written < elt_len)
goto truncated;
- memcpy(ptr, obj->sig.elts_, elt_len);
+ if (elt_len)
+ memcpy(ptr, obj->sig.elts_, elt_len);
written += elt_len; ptr += elt_len;
}
@@ -899,7 +902,8 @@ rsa_ed_crosscert_parse_into(rsa_ed_crosscert_t *obj, const uint8_t *input, const
CHECK_REMAINING(obj->sig_len, truncated);
TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->sig, obj->sig_len, {});
obj->sig.n_ = obj->sig_len;
- memcpy(obj->sig.elts_, ptr, obj->sig_len);
+ if (obj->sig_len)
+ memcpy(obj->sig.elts_, ptr, obj->sig_len);
ptr += obj->sig_len; remaining -= obj->sig_len;
trunnel_assert(ptr + remaining == input + len_in);
return len_in - remaining;
@@ -1467,7 +1471,8 @@ auth1_encode(uint8_t *output, const size_t avail, const auth1_t *obj, const auth
trunnel_assert(written <= avail);
if (avail - written < elt_len)
goto truncated;
- memcpy(ptr, obj->sig.elts_, elt_len);
+ if (elt_len)
+ memcpy(ptr, obj->sig.elts_, elt_len);
written += elt_len; ptr += elt_len;
}
@@ -1576,7 +1581,8 @@ auth1_parse_into(auth1_t *obj, const uint8_t *input, const size_t len_in, const
/* Parse u8 sig[] */
TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->sig, remaining, {});
obj->sig.n_ = remaining;
- memcpy(obj->sig.elts_, ptr, remaining);
+ if (remaining)
+ memcpy(obj->sig.elts_, ptr, remaining);
ptr += remaining; remaining -= remaining;
trunnel_assert(ptr + remaining == input + len_in);
return len_in - remaining;
diff --git a/src/trunnel/link_handshake.h b/src/trunnel/link_handshake.h
index 60bc28fa33..2749ec7dd4 100644
--- a/src/trunnel/link_handshake.h
+++ b/src/trunnel/link_handshake.h
@@ -1,4 +1,4 @@
-/* link_handshake.h -- generated by by Trunnel v1.4.3.
+/* link_handshake.h -- generated by by Trunnel v1.4.4.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
diff --git a/src/trunnel/pwbox.c b/src/trunnel/pwbox.c
index a80fbb949b..9b348a9b30 100644
--- a/src/trunnel/pwbox.c
+++ b/src/trunnel/pwbox.c
@@ -1,4 +1,4 @@
-/* pwbox.c -- generated by Trunnel v1.4.3.
+/* pwbox.c -- generated by Trunnel v1.4.4.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
@@ -362,7 +362,8 @@ pwbox_encoded_encode(uint8_t *output, size_t avail, const pwbox_encoded_t *obj)
trunnel_assert(written <= avail);
if (avail - written < elt_len)
goto truncated;
- memcpy(ptr, obj->skey_header.elts_, elt_len);
+ if (elt_len)
+ memcpy(ptr, obj->skey_header.elts_, elt_len);
written += elt_len; ptr += elt_len;
}
@@ -380,7 +381,8 @@ pwbox_encoded_encode(uint8_t *output, size_t avail, const pwbox_encoded_t *obj)
trunnel_assert(written <= avail);
if (avail - written < elt_len)
goto truncated;
- memcpy(ptr, obj->data.elts_, elt_len);
+ if (elt_len)
+ memcpy(ptr, obj->data.elts_, elt_len);
written += elt_len; ptr += elt_len;
}
trunnel_assert(written <= avail);
@@ -460,7 +462,8 @@ pwbox_encoded_parse_into(pwbox_encoded_t *obj, const uint8_t *input, const size_
CHECK_REMAINING(obj->header_len, truncated);
TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->skey_header, obj->header_len, {});
obj->skey_header.n_ = obj->header_len;
- memcpy(obj->skey_header.elts_, ptr, obj->header_len);
+ if (obj->header_len)
+ memcpy(obj->skey_header.elts_, ptr, obj->header_len);
ptr += obj->header_len; remaining -= obj->header_len;
/* Parse u8 iv[16] */
@@ -476,7 +479,8 @@ pwbox_encoded_parse_into(pwbox_encoded_t *obj, const uint8_t *input, const size_
/* Parse u8 data[] */
TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->data, remaining, {});
obj->data.n_ = remaining;
- memcpy(obj->data.elts_, ptr, remaining);
+ if (remaining)
+ memcpy(obj->data.elts_, ptr, remaining);
ptr += remaining; remaining -= remaining;
if (remaining != 0)
goto fail;
diff --git a/src/trunnel/pwbox.h b/src/trunnel/pwbox.h
index c357932681..e69e2c1a0e 100644
--- a/src/trunnel/pwbox.h
+++ b/src/trunnel/pwbox.h
@@ -1,4 +1,4 @@
-/* pwbox.h -- generated by by Trunnel v1.4.3.
+/* pwbox.h -- generated by by Trunnel v1.4.4.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h
index 8b687c8234..407ffc00fc 100644
--- a/src/win32/orconfig.h
+++ b/src/win32/orconfig.h
@@ -220,9 +220,6 @@
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS
-/* Define to 1 if time_t is signed. */
-#define TIME_T_IS_SIGNED
-
/* Define to 1 iff unaligned int access is allowed */
#define UNALIGNED_INT_ACCESS_OK
@@ -232,7 +229,7 @@
#define USING_TWOS_COMPLEMENT
/* Version number of package */
-#define VERSION "0.2.7.6-dev"
+#define VERSION "0.2.8.1-alpha-dev"