diff options
235 files changed, 5347 insertions, 2253 deletions
@@ -1,3 +1,316 @@ +Changes in version 0.4.4.1-alpha - 2020-06-16 + This is the first alpha release in the 0.4.4.x series. It improves + our guard selection algorithms, improves the amount of code that + can be disabled when running without relay support, and includes numerous + small bugfixes and enhancements. It also lays the ground for some IPv6 + features that we'll be developing more in the next (0.4.5) series. + + Here are the changes since 0.4.3.5. + + o Major features (Proposal 310, performance + security): + - Implements Proposal 310, "Bandaid on guard selection". Proposal + 310 solves load-balancing issues with older versions of the guard + selection algorithm, and improves its security. Under this new + algorithm, a newly selected guard never becomes Primary unless all + previously sampled guards are unreachable. Implements + recommendation from 32088. (Proposal 310 is linked to the CLAPS + project researching optimal client location-aware path selections. + This project is a collaboration between the UCLouvain Crypto Group, + the U.S. Naval Research Laboratory, and Princeton University.) + + o Major features (IPv6, relay): + - Consider IPv6-only EXTEND2 cells valid on relays. Log a protocol + warning if the IPv4 or IPv6 address is an internal address, and + internal addresses are not allowed. But continue to use the other + address, if it is valid. Closes ticket 33817. + - If a relay can extend over IPv4 and IPv6, and both addresses are + provided, it chooses between them uniformly at random. Closes + ticket 33817. + - Re-use existing IPv6 connections for circuit extends. Closes + ticket 33817. + - Relays may extend circuits over IPv6, if the relay has an IPv6 + ORPort, and the client supplies the other relay's IPv6 ORPort in + the EXTEND2 cell. IPv6 extends will be used by the relay IPv6 + ORPort self-tests in 33222. Closes ticket 33817. + + o Major features (v3 onion services): + - Allow v3 onion services to act as OnionBalance backend instances, + by using the HiddenServiceOnionBalanceInstance torrc option. + Closes ticket 32709. + + o Minor feature (developer tools): + - Add a script to help check the alphabetical ordering of option + names in the manual page. Closes ticket 33339. + + o Minor feature (onion service client, SOCKS5): + - Add 3 new SocksPort ExtendedErrors (F2, F3, F7) that reports back + new type of onion service connection failures. The semantics of + these error codes are documented in proposal 309. Closes + ticket 32542. + + o Minor feature (onion service v3): + - If a service cannot upload its descriptor(s), log why at INFO + level. Closes ticket 33400; bugfix on 0.3.2.1-alpha. + + o Minor feature (python scripts): + - Stop assuming that /usr/bin/python exists. Instead of using a + hardcoded path in scripts that still use Python 2, use + /usr/bin/env, similarly to the scripts that use Python 3. Fixes + bug 33192; bugfix on 0.4.2. + + o Minor features (client-only compilation): + - Disable more code related to the ext_orport protocol when + compiling without support for relay mode. Closes ticket 33368. + - Disable more of our self-testing code when support for relay mode + is disabled. Closes ticket 33370. + + o Minor features (code safety): + - Check for failures of tor_inet_ntop() and tor_inet_ntoa() + functions in DNS and IP address processing code, and adjust + codepaths to make them less likely to crash entire Tor instances. + Resolves issue 33788. + + o Minor features (compilation size): + - Most server-side DNS code is now disabled when building without + support for relay mode. Closes ticket 33366. + + o Minor features (continuous integration): + - Run unit-test and integration test (Stem, Chutney) jobs with + ALL_BUGS_ARE_FATAL macro being enabled on Travis and Appveyor. + Resolves ticket 32143. + + o Minor features (control port): + - Return a descriptive error message from the 'GETINFO status/fresh- + relay-descs' command on the control port. Previously, we returned + a generic error of "Error generating descriptor". Closes ticket + 32873. Patch by Neel Chauhan. + + o Minor features (developer tooling): + - Refrain from listing all .a files that are generated by the Tor + build in .gitignore. Add a single wildcard *.a entry that covers + all of them for present and future. Closes ticket 33642. + - Add a script ("git-install-tools.sh") to install git hooks and + helper scripts. Closes ticket 33451. + + o Minor features (directory authority, shared random): + - Refactor more authority-only parts of the shared-random scheduling + code to reside in the dirauth module, and to be disabled when + compiling with --disable-module-dirauth. Closes ticket 33436. + + o Minor features (directory): + - Remember the number of bytes we have downloaded for each directory + purpose while bootstrapping, and while fully bootstrapped. Log + this information as part of the heartbeat message. Closes + ticket 32720. + + o Minor features (IPv6 support): + - Adds IPv6 support to tor_addr_is_valid(). Adds tests for the above + changes and tor_addr_is_null(). Closes ticket 33679. Patch + by MrSquanchee. + - Allow clients and relays to send dual-stack and IPv6-only EXTEND2 + cells. Parse dual-stack and IPv6-only EXTEND2 cells on relays. + Closes ticket 33901. + + o Minor features (logging): + - When trying to find our own address, add debug-level logging to + report the sources of candidate addresses. Closes ticket 32888. + + o Minor features (testing, architecture): + - Our test scripts now double-check that subsystem initialization + order is consistent with the inter-module dependencies established + by our .may_include files. Implements ticket 31634. + - Initialize all subsystems at the beginning of our unit test + harness, to avoid crashes due to uninitialized subsystems. Follow- + up from ticket 33316. + + o Minor features (v3 onion services): + - Add v3 onion service status to the dumpstats() call which is + triggered by a SIGUSR1 signal. Previously, we only did v2 onion + services. Closes ticket 24844. Patch by Neel Chauhan. + + o Minor features (windows): + - Add support for console control signals like Ctrl+C in Windows. + Closes ticket 34211. Patch from Damon Harris (TheDcoder). + + o Minor bugfix (onion service v3): + - Prevent an assert() that would occur when cleaning the client + descriptor cache, and attempting to close circuits for a non- + decrypted descriptor (lacking client authorization). Fixes bug + 33458; bugfix on 0.4.2.1-alpha. + + o Minor bugfix (refactoring): + - Lift circuit_build_times_disabled() out of the + circuit_expire_building() loop, to save CPU time when there are + many circuits open. Fixes bug 33977; bugfix on 0.3.5.9. + + o Minor bugfixes (client performance): + - Resume use of preemptively-built circuits when UseEntryGuards is set + to 0. We accidentally disabled this feature with that config + setting, leading to slower load times. Fixes bug 34303; bugfix + on 0.3.3.2-alpha. + + o Minor bugfixes (directory authorities): + - Directory authorities now reject votes that arrive too late. In + particular, once an authority has started fetching missing votes, + it no longer accepts new votes posted by other authorities. This + change helps prevent a consensus split, where only some authorities + have the late vote. Fixes bug 4631; bugfix on 0.2.0.5-alpha. + + o Minor bugfixes (git scripts): + - Stop executing the checked-out pre-commit hook from the pre-push + hook. Instead, execute the copy in the user's git directory. Fixes + bug 33284; bugfix on 0.4.1.1-alpha. + + o Minor bugfixes (initialization): + - Initialize the subsystems in our code in an order more closely + corresponding to their dependencies, so that every system is + initialized before the ones that (theoretically) depend on it. + Fixes bug 33316; bugfix on 0.4.0.1-alpha. + + o Minor bugfixes (IPv4, relay): + - Check for invalid zero IPv4 addresses and ports when sending and + receiving extend cells. Fixes bug 33900; bugfix on 0.2.4.8-alpha. + + o Minor bugfixes (IPv6, relay): + - Consider IPv6 addresses when checking if a connection is + canonical. In 17604, relays assumed that a remote relay could + consider an IPv6 connection canonical, but did not set the + canonical flag on their side of the connection. Fixes bug 33899; + bugfix on 0.3.1.1-alpha. + - Log IPv6 addresses on connections where this relay is the + responder. Previously, responding relays would replace the remote + IPv6 address with the IPv4 address from the consensus. Fixes bug + 33899; bugfix on 0.3.1.1-alpha. + + o Minor bugfixes (linux seccomp sandbox nss): + - Fix a startup crash when tor is compiled with --enable-nss and + sandbox support is enabled. Fixes bug 34130; bugfix on + 0.3.5.1-alpha. Patch by Daniel Pinto. + + o Minor bugfixes (logging, testing): + - Make all of tor's assertion macros support the ALL_BUGS_ARE_FATAL + and DISABLE_ASSERTS_IN_UNIT_TESTS debugging modes. (IF_BUG_ONCE() + used to log a non-fatal warning, regardless of the debugging + mode.) Fixes bug 33917; bugfix on 0.2.9.1-alpha. + + o Minor bugfixes (logs): + - Remove surprising empty line in the INFO-level log about circuit + build timeout. Fixes bug 33531; bugfix on 0.3.3.1-alpha. + + o Minor bugfixes (mainloop): + - Better guard against growing a buffer past its maximum 2GB in + size. Fixes bug 33131; bugfix on 0.3.0.4-rc. + + o Minor bugfixes (manual page): + - Update the man page to reflect that MinUptimeHidServDirectoryV2 + defaults to 96 hours. Fixes bug 34299; bugfix on 0.2.6.3-alpha. + + o Minor bugfixes (onion service v3, client): + - Remove a BUG() that was causing a stacktrace when a descriptor + changed at an unexpected time. Fixes bug 28992; bugfix + on 0.3.2.1-alpha. + + o Minor bugfixes (onion service, logging): + - Fix a typo in a log message PublishHidServDescriptors is set to 0. + Fixes bug 33779; bugfix on 0.3.2.1-alpha. + + o Minor bugfixes (portability): + - Fix a portability error in the configure script, where we were + using "==" instead of "=". Fixes bug 34233; bugfix on 0.4.3.5. + + o Minor bugfixes (protocol versions): + - Sort tor's supported protocol version lists, as recommended by the + tor directory specification. Fixes bug 33285; bugfix + on 0.4.0.1-alpha. + + o Minor bugfixes (relays): + - Stop advertising incorrect IPv6 ORPorts in relay and bridge + descriptors, when the IPv6 port was configured as "auto". Fixes + bug 32588; bugfix on 0.2.3.9-alpha. + + o Code simplification and refactoring: + - Define and use a new constant TOR_ADDRPORT_BUF_LEN which is like + TOR_ADDR_BUF_LEN but includes enough space for an IP address, + brackets, separating colon, and port number. Closes ticket 33956. + Patch by Neel Chauhan. + - Merge the orconn and ocirc events into the "core" subsystem, which + manages or connections and origin circuits. Previously they were + isolated in subsystems of their own. + - Move LOG_PROTOCOL_WARN to app/config. Resolves a dependency + inversion. Closes ticket 33633. + - Move the circuit extend code to the relay module. Split the + circuit extend function into smaller functions. Closes + ticket 33633. + - Rewrite port_parse_config() to use the default port flags from + port_cfg_new(). Closes ticket 32994. Patch by MrSquanchee. + - Updated comments in 'scheduler.c' to reflect old code changes, and + simplified the scheduler channel state change code. Closes + ticket 33349. + + o Documentation: + - Document the limitations of using %include on config files with + seccomp sandbox enabled. Fixes documentation bug 34133; bugfix on + 0.3.1.1-alpha. Patch by Daniel Pinto. + - Fix several doxygen warnings related to imbalanced groups. Closes + ticket 34255. + + o Removed features: + - Remove the ClientAutoIPv6ORPort option. This option attempted to + randomly choose between IPv4 and IPv6 for client connections, and + wasn't a true implementation of Happy Eyeballs. Often, this option + failed on IPv4-only or IPv6-only connections. Closes ticket 32905. + Patch by Neel Chauhan. + - Stop shipping contrib/dist/rc.subr file, as it is not being used + on FreeBSD anymore. Closes issue 31576. + + o Testing: + - Add a basic IPv6 test to "make test-network". This test only runs + when the local machine has an IPv6 stack. Closes ticket 33300. + - Add test-network-ipv4 and test-network-ipv6 jobs to the Makefile. + These jobs run the IPv4-only and dual-stack chutney flavours from + test-network-all. Closes ticket 33280. + - Remove a redundant distcheck job. Closes ticket 33194. + - Run the test-network-ipv6 Makefile target in the Travis CI IPv6 + chutney job. This job runs on macOS, so it's a bit slow. Closes + ticket 33303. + - Sort the Travis jobs in order of speed. Putting the slowest jobs + first takes full advantage of Travis job concurrency. Closes + ticket 33194. + - Stop allowing the Chutney IPv6 Travis job to fail. This job was + previously configured to fast_finish (which requires + allow_failure), to speed up the build. Closes ticket 33195. + - Test v3 onion services to tor's mixed IPv4 chutney network. And + add a mixed IPv6 chutney network. These networks are used in the + test-network-all, test-network-ipv4, and test-network-ipv6 make + targets. Closes ticket 33334. + - Use the "bridges+hs-v23" chutney network flavour in "make test- + network". This test requires a recent version of chutney (mid- + February 2020). Closes ticket 28208. + - When a Travis chutney job fails, use chutney's new "diagnostics.sh" + tool to produce detailed diagnostic output. Closes ticket 32792. + + o Code simplification and refactoring (onion service): + - Refactor configuration parsing to use the new config subsystem + code. Closes ticket 33014. + + o Code simplification and refactoring (relay address): + - Move a series of functions related to address resolving into their + own files. Closes ticket 33789. + + o Documentation (manual page): + - Add cross reference links and a table of contents to the HTML tor + manual page. Closes ticket 33369. Work by Swati Thacker as part of + Google Season of Docs. + - Alphabetize the Denial of Service Mitigation Options, Directory + Authority Server Options, Hidden Service Options, and Testing + Network Options sections of the tor(1) manual page. Closes ticket + 33275. Work by Swati Thacker as part of Google Season of Docs. + - Refrain from mentioning nicknames in manpage section for MyFamily + torrc option. Resolves issue 33417. + - Updated the options set by TestingTorNetwork in the manual page. + Closes ticket 33778. + + Changes in version 0.4.3.5 - 2020-05-15 Tor 0.4.3.5 is the first stable release in the 0.4.3.x series. This series adds support for building without relay code enabled, and diff --git a/Doxyfile.in b/Doxyfile.in index 503c1302db..4374e54858 100644 --- a/Doxyfile.in +++ b/Doxyfile.in @@ -863,7 +863,8 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = ./src/ext \ +EXCLUDE = ./src/ext/ed25519 \ + ./src/ext/rust \ ./src/trunnel \ ./src/test \ ./src/rust/registry diff --git a/Makefile.am b/Makefile.am index 1a1fa9b658..711d2cc037 100644 --- a/Makefile.am +++ b/Makefile.am @@ -273,7 +273,6 @@ check-local: \ check-spaces \ check-changes \ check-includes \ - check-best-practices \ shellcheck \ check-cocci @@ -19,13 +19,13 @@ Documentation, including links to installation and setup instructions: https://www.torproject.org/docs/documentation.html Making applications work with Tor: - https://wiki.torproject.org/projects/tor/wiki/doc/TorifyHOWTO + https://gitlab.torproject.org/legacy/trac/-/wikis/doc/TorifyHOWTO Frequently Asked Questions: https://www.torproject.org/docs/faq.html Release timeline: - https://trac.torproject.org/projects/tor/wiki/org/teams/NetworkTeam/CoreTorReleases + https://gitlab.torproject.org/tpo/core/tor/-/wikis/NetworkTeam/CoreTorReleases To get started working on Tor development: See the doc/HACKING directory. diff --git a/changes/bug30992 b/changes/bug30992 new file mode 100644 index 0000000000..f318319016 --- /dev/null +++ b/changes/bug30992 @@ -0,0 +1,4 @@ + o Minor bugfixes (circuitpadding): + - Add a per-circuit padding machine instance counter, so we can + differentiate between shutdown requests for old machines on a circuit; + Fixes bug 30992; bugfix on 0.4.1.1-alpha. diff --git a/changes/bug32040 b/changes/bug32040 new file mode 100644 index 0000000000..1cdc0bec9a --- /dev/null +++ b/changes/bug32040 @@ -0,0 +1,7 @@ + o Minor bugfixes (circuitpadding): + - Add the abilility to keep circuit padding machines if they match a set + of circuit state or purposes. This allows us to have machines that start + up under some conditions but don't shut down under others. We now + use this mask to avoid starting up introduction circuit padding + again after the machines have already completed. Fixes bug 32040; + bugfix on 0.4.1.1-alpha. diff --git a/changes/bug32588 b/changes/bug32588 deleted file mode 100644 index f31f2ce1ad..0000000000 --- a/changes/bug32588 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (relays): - - Stop advertising incorrect IPv6 ORPorts in relay and bridge descriptors, - when the IPv6 port was configured as "auto". - Fixes bug 32588; bugfix on 0.2.3.9-alpha diff --git a/changes/bug32709 b/changes/bug32709 deleted file mode 100644 index d00b112be6..0000000000 --- a/changes/bug32709 +++ /dev/null @@ -1,4 +0,0 @@ - o Major features (v3 onion services): - - Allow v3 onion services to act as OnionBalance backend instances using - the HiddenServiceOnionBalanceInstance torrc option. Closes ticket 32709. - diff --git a/changes/bug33131 b/changes/bug33131 deleted file mode 100644 index bc5ef7bc2d..0000000000 --- a/changes/bug33131 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (mainloop): - - Better guard against growing a buffer past its maximum 2GB in size. - Fixes bug 33131; bugfix on 0.3.0.4-rc. diff --git a/changes/bug33284 b/changes/bug33284 deleted file mode 100644 index e6aed4d2d4..0000000000 --- a/changes/bug33284 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (git scripts): - - Stop executing the checked-out pre-commit hook from the pre-push hook. - Instead, execute the copy in the user's git dir. Fixes bug 33284; bugfix - on 0.4.1.1-alpha. diff --git a/changes/bug33285 b/changes/bug33285 deleted file mode 100644 index a4d06a7eb8..0000000000 --- a/changes/bug33285 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (protocol versions): - - Sort tor's supported protocol version lists, as recommended by the - tor directory specification. Fixes bug 33285; bugfix on 0.4.0.1-alpha. diff --git a/changes/bug33531 b/changes/bug33531 deleted file mode 100644 index c4284c55c9..0000000000 --- a/changes/bug33531 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (logs): - - Remove surprising empty line in info-level log about circuit build - timeout. Fixes bug 33531; bugfix on 0.3.3.1-alpha. diff --git a/changes/bug33899 b/changes/bug33899 deleted file mode 100644 index b9b7d7cf13..0000000000 --- a/changes/bug33899 +++ /dev/null @@ -1,9 +0,0 @@ - o Minor bugfixes (IPv6, relay): - - Consider IPv6 addresses when checking if a connection is canonical. - In 17604, relays assumed that a remote relay could consider an IPv6 - connection canonical, but did not set the canonical flag on their side - of the connection. Fixes bug 33899; bugfix on 0.3.1.1-alpha. - - Log IPv6 addresses on connections where this relay is the responder. - Previously, responding relays would replace the remote IPv6 address with - the IPv4 address from the consensus. - Fixes bug 33899; bugfix on 0.3.1.1-alpha. diff --git a/changes/bug33900 b/changes/bug33900 deleted file mode 100644 index c1649d2284..0000000000 --- a/changes/bug33900 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (IPv4, relay): - - Check for invalid zero IPv4 addresses and ports, when sending and - receiving extend cells. Fixes bug 33900; bugfix on 0.2.4.8-alpha. diff --git a/changes/bug33917 b/changes/bug33917 deleted file mode 100644 index 6a8daa9e26..0000000000 --- a/changes/bug33917 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (logging, testing): - - Make all of tor's assertion macros support the ALL_BUGS_ARE_FATAL and - DISABLE_ASSERTS_IN_UNIT_TESTS debugging modes. Implements these modes - for IF_BUG_ONCE(). (It used to log a non-fatal warning, regardless of - the debugging mode.) Fixes bug 33917; bugfix on 0.2.9.1-alpha. diff --git a/changes/bug33977 b/changes/bug33977 deleted file mode 100644 index b424a811a2..0000000000 --- a/changes/bug33977 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfix (refactoring): - - Lift circuit_build_times_disabled out of circuit_expire_building loop to - save CPU time with many circuits open. Fixes bug 33977; bugfix on - 0.3.5.9. diff --git a/changes/bug34065 b/changes/bug34065 new file mode 100644 index 0000000000..f81cb77c21 --- /dev/null +++ b/changes/bug34065 @@ -0,0 +1,5 @@ + o Minor features (IPv6, ExcludeNodes): + - Make routerset_contains_router() capable of handling IPv6 + addresses. This makes ExcludeNodes capable of excluding an + IPv6 adddress. Previously, ExcludeNodes ignored IPv6 + addresses. Closes ticket 34065. Patch by Neel Chauhan. diff --git a/changes/bug34130 b/changes/bug34130 deleted file mode 100644 index b1e5715fdf..0000000000 --- a/changes/bug34130 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (linux seccomp sandbox nss): - - Fix startup crash when tor is compiled with --enable-nss and - sandbox support is enabled. Fixes bug 34130; bugfix on - 0.3.5.1-alpha. Patch by Daniel Pinto. diff --git a/changes/bug34233 b/changes/bug34233 deleted file mode 100644 index 24c7869783..0000000000 --- a/changes/bug34233 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (portability): - - Fix a portability error in the configure script, where we - were using "==" instead of "=". Fixes bug 34233; bugfix on - 0.4.3.5. diff --git a/changes/bug34248 b/changes/bug34248 new file mode 100644 index 0000000000..b89df444ed --- /dev/null +++ b/changes/bug34248 @@ -0,0 +1,4 @@ + o Minor bugfixes (rust, protocol versions): + - Declare support for the onion service introduction point denial of + service extensions, when building tor with Rust. + Fixes bug 34248; bugfix on 0.4.2.1-alpha. diff --git a/changes/bug34251 b/changes/bug34251 new file mode 100644 index 0000000000..bbf0535256 --- /dev/null +++ b/changes/bug34251 @@ -0,0 +1,4 @@ + o Minor bugfixes (rust, protocol versions): + - Make Rust protocol version support checks consistent with the + undocumented error behaviour of the corresponding C code. + Fixes bug 34251; bugfix on 0.3.3.5-rc. diff --git a/changes/bug34299 b/changes/bug34299 deleted file mode 100644 index 464cf0d18a..0000000000 --- a/changes/bug34299 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (man page): - - Update the man page to reflect that MinUptimeHidServDirectoryV2 - defaults to 96 hours. Fixes bug 34299; bugfix on 0.2.6.3-alpha. diff --git a/changes/bug34303 b/changes/bug34303 deleted file mode 100644 index dce57f4646..0000000000 --- a/changes/bug34303 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (client performance): - - Resume being willing to use preemptively-built circuits when - UseEntryGuards is set to 0. We accidentally disabled this feature - with that config setting, leading to slower load times. Fixes bug - 34303; bugfix on 0.3.3.2-alpha. diff --git a/changes/bug34357 b/changes/bug34357 new file mode 100644 index 0000000000..69fada7cc0 --- /dev/null +++ b/changes/bug34357 @@ -0,0 +1,4 @@ + o Minor features (directory authorities): + - Directory authorities now reject descriptors from relays running + Tor versions from the 0.4.1 series, but still allow the 0.3.5 + series. Resolves ticket 34357. Patch by Neel Chauhan. diff --git a/changes/bug34400 b/changes/bug34400 new file mode 100644 index 0000000000..e2b56688b9 --- /dev/null +++ b/changes/bug34400 @@ -0,0 +1,5 @@ + o Minor bugfixes (v2 onion services): + - For HSFETCH commands on v2 onion services addresses, check the length of + bytes decoded, not the base32 length. This takes the behavior introduced + in commit a517daa56f5848d25ba79617a1a7b82ed2b0a7c0 into consideration. + Fixes bug 34400; bugfix on 0.4.1.1-alpha. Patch by Neel Chauhan. diff --git a/changes/bug4631 b/changes/bug4631 deleted file mode 100644 index be3dd2b43e..0000000000 --- a/changes/bug4631 +++ /dev/null @@ -1,6 +0,0 @@ - o Minor bugfixes (directory authorities): - - Directory authorities reject votes that arrive too late. In particular, - once an authority has started fetching missing votes, it no longer - accepts new votes posted by other authorities. This change helps prevent - a consensus split, where only some authorities have the late vote. - Fixes bug 4631; bugfix on 0.2.0.5-alpha. diff --git a/changes/doc33417 b/changes/doc33417 deleted file mode 100644 index 0fc868fc65..0000000000 --- a/changes/doc33417 +++ /dev/null @@ -1,3 +0,0 @@ - o Documentation (manpage): - - Refrain from mentioning nicknames in manpage section for MyFamily torrc - option. Resolves issue 33417. diff --git a/changes/doc34133 b/changes/doc34133 deleted file mode 100644 index abe9db6148..0000000000 --- a/changes/doc34133 +++ /dev/null @@ -1,6 +0,0 @@ - o Documentation: - - Correctly document that we search for a system torrc file before - Document the limitations of using %include on config files with - seccomp sandbox enabled. No new files can be added to the - %included directories. Fixes documentation bug 34133; bugfix - on 0.3.1.1-alpha. Patch by Daniel Pinto. diff --git a/changes/feature34068 b/changes/feature34068 new file mode 100644 index 0000000000..10812c8eca --- /dev/null +++ b/changes/feature34068 @@ -0,0 +1,3 @@ + o Minor features (controller, IPv6): + - Tor relays now try to report to the controller when they are launching + an IPv6 self-test. Closes ticket 34068. diff --git a/changes/ticket24844 b/changes/ticket24844 deleted file mode 100644 index da55b4cf67..0000000000 --- a/changes/ticket24844 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (v3 onion servies): - - Add v3 onion service status to the dumpstats() call which is - triggered by a SIGUSR1 signal. Previously, we only did v2 - onion services. Closes ticket 24844. Patch by Neel Chauhan. diff --git a/changes/ticket28208 b/changes/ticket28208 deleted file mode 100644 index 8818aad1d5..0000000000 --- a/changes/ticket28208 +++ /dev/null @@ -1,4 +0,0 @@ - o Testing: - - Use the "bridges+hs-v23" chutney network flavor in "make test-network". - This test requires a recent version of chutney (mid-February 2020). - Closes ticket 28208. diff --git a/changes/ticket28279 b/changes/ticket28279 new file mode 100644 index 0000000000..1c085c2a6e --- /dev/null +++ b/changes/ticket28279 @@ -0,0 +1,5 @@ + o Minor features (control port, rephist): + - Introduce GETINFO "stats/ntor/{assigned/requested}" and + "stats/tap/{assigned/requested}" to get the NTorand TAP + circuit onion handshake rephist values respectively. + Closes ticket 28279. Patch by Neel Chauhan. diff --git a/changes/ticket28992 b/changes/ticket28992 deleted file mode 100644 index 3e45d73e45..0000000000 --- a/changes/ticket28992 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (onion service v3, client): - - Remove a BUG() that is causing a stacktrace for a situation that very - rarely happens but still can. Fixes bug 28992; bugfix on 0.3.2.1-alpha. diff --git a/changes/ticket30642 b/changes/ticket30642 new file mode 100644 index 0000000000..13941b2ac5 --- /dev/null +++ b/changes/ticket30642 @@ -0,0 +1,4 @@ + o Minor features (ed25519, relay): + - Save a relay's base64-encoded ed25519 identity key to the data + directory in a file named fingerprint-ed25519. Closes ticket 30642. + Patch by Neel Chauhan. diff --git a/changes/ticket31576 b/changes/ticket31576 deleted file mode 100644 index ab984cf3d4..0000000000 --- a/changes/ticket31576 +++ /dev/null @@ -1,3 +0,0 @@ - o Removed features: - - Stop shipping contrib/dist/rc.subr file, as it is not being used on - FreeBSD anymore. Closes issue 31576. diff --git a/changes/ticket31634 b/changes/ticket31634 deleted file mode 100644 index 2777595036..0000000000 --- a/changes/ticket31634 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (testing, architeture): - - Our test scripts now double-check that subsystem initialization order - is consistent with the inter-module dependencies established by our - .may_include files. Implements ticket 31634. diff --git a/changes/ticket31699 b/changes/ticket31699 new file mode 100644 index 0000000000..1998248d57 --- /dev/null +++ b/changes/ticket31699 @@ -0,0 +1,3 @@ + o Code simplification and refactoring (autoconf): + - Remove autoconf checks for unused funcs and headers. Closes ticket + 31699; Patch by @bduszel diff --git a/changes/ticket32088 b/changes/ticket32088 deleted file mode 100644 index 0d4fc74754..0000000000 --- a/changes/ticket32088 +++ /dev/null @@ -1,13 +0,0 @@ - o Major features (Proposal 310, performance + security): - - Implements Proposal 310 - Bandaid on guard selection. - Proposal 310 solves a load-balancing issue within Prop271 which strongly - impact experimental research with Shadow. - Security improvement: Proposal 310 prevents any newly Guard relay to - have a chance to get into the primary list of older Tor clients, - except if the N first sampled guards of these clients are unreachable. - Implements recommendation from 32088. - - Proposal 310 is linked to the CLAPS project researching optimal - client location-aware path selections. This project is a collaboration - between the UCLouvain Crypto Group, the U.S. Naval Research Laboratory and - Princeton University. diff --git a/changes/ticket32143 b/changes/ticket32143 deleted file mode 100644 index 7f8a809ba5..0000000000 --- a/changes/ticket32143 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (continuous integration): - - Run unit-test and integration test (Stem, Chutney) jobs with - ALL_BUGS_ARE_FATAL macro being enabled on Travis and Appveyor. - Resolves ticket 32143. diff --git a/changes/ticket32542 b/changes/ticket32542 deleted file mode 100644 index c52335b059..0000000000 --- a/changes/ticket32542 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor feature (onion service client, SOCKS5): - - Add 3 new SocksPort ExtendedErrors (F2, F3, F7) that reports back new type - of onion service connection failures. Closes ticket 32542. diff --git a/changes/ticket32720 b/changes/ticket32720 deleted file mode 100644 index 87c540b7ff..0000000000 --- a/changes/ticket32720 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (directory): - - Remember the number of bytes we have downloaded for each directory - purpose while bootstrapping, and while fully bootstrapped. Log this - information as part of the heartbeat message. Closes ticket 32720. diff --git a/changes/ticket32792 b/changes/ticket32792 deleted file mode 100644 index 553cf0ca81..0000000000 --- a/changes/ticket32792 +++ /dev/null @@ -1,3 +0,0 @@ - o Testing: - - When a Travis chutney job fails, use chutney's new "diagnostics.sh" tool - to produce detailed diagnostic output. Closes ticket 32792. diff --git a/changes/ticket32822 b/changes/ticket32822 new file mode 100644 index 0000000000..ca62f0cc53 --- /dev/null +++ b/changes/ticket32822 @@ -0,0 +1,5 @@ + o Minor features (directory authorities, IPv6): + - Make authorities add their IPv6 ORPort (if any) to the trusted dir + servers list. Authorities currently add themselves to the trusted dir + servers list, but they only add their IPv4 address and ports to the list. + Closes ticket 32822. diff --git a/changes/ticket32873 b/changes/ticket32873 deleted file mode 100644 index 65ea1f64ad..0000000000 --- a/changes/ticket32873 +++ /dev/null @@ -1,6 +0,0 @@ - o Minor features (control port): - - Return a descriptive error message from the 'GETINFO - status/fresh-relay-descs' command on the control port. - Previously, we returned a generic error of "Error - generating descriptor". Closes ticket 32873. Patch by - Neel Chauhan. diff --git a/changes/ticket32888 b/changes/ticket32888 deleted file mode 100644 index ce7fb40b30..0000000000 --- a/changes/ticket32888 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (logging): - - When trying to find our own address, add debug-level logging - to report the sources of candidate addresses. Closes ticket - 32888. diff --git a/changes/ticket32905 b/changes/ticket32905 deleted file mode 100644 index 6f420ec693..0000000000 --- a/changes/ticket32905 +++ /dev/null @@ -1,6 +0,0 @@ - o Removed features: - - Remove the ClientAutoIPv6ORPort option. This option attempted - to randomly choose between IPv4 and IPv6 for client connections, - and isn't a true implementation of Happy Eyeballs. Often, this - option failed on IPv4-only or IPv6-only connections. Closes - ticket 32905. Patch by Neel Chauhan. diff --git a/changes/ticket32994 b/changes/ticket32994 deleted file mode 100644 index 43a32afa78..0000000000 --- a/changes/ticket32994 +++ /dev/null @@ -1,3 +0,0 @@ - o Code simplification and refactoring: - - Rewrite port_parse_config() to use the default port flags from - port_cfg_new(). Closes ticket 32994. Patch by MrSquanchee. diff --git a/changes/ticket33014 b/changes/ticket33014 deleted file mode 100644 index 885051d9cf..0000000000 --- a/changes/ticket33014 +++ /dev/null @@ -1,3 +0,0 @@ - o Code simplification and refactoring (onion service): - - Refactor configuration parsing to use the new config subsystem code. - Closes ticket 33014. diff --git a/changes/ticket33192 b/changes/ticket33192 deleted file mode 100644 index 97f976226b..0000000000 --- a/changes/ticket33192 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor feature (python): - - Stop assuming that /usr/bin/python exists. Instead of using a - hardcoded path in scripts that still use Python 2, use /usr/bin/env, - similarly to the scripts that use Python 3. Fixes bug 33192; bugfix - on 0.4.2.
\ No newline at end of file diff --git a/changes/ticket33194 b/changes/ticket33194 deleted file mode 100644 index b87e55348e..0000000000 --- a/changes/ticket33194 +++ /dev/null @@ -1,4 +0,0 @@ - o Testing: - - Remove a redundant distcheck job. Closes ticket 33194. - - Sort the Travis jobs in order of speed. Putting the slowest jobs first - takes full advantage of Travis job concurrency. Closes ticket 33194. diff --git a/changes/ticket33195 b/changes/ticket33195 deleted file mode 100644 index 11abd4816e..0000000000 --- a/changes/ticket33195 +++ /dev/null @@ -1,4 +0,0 @@ - o Testing: - - Stop allowing the Chutney IPv6 Travis job to fail. This job was - previously configured to fast_finish (which requires allow_failure), to - speed up the build. Closes ticket 33195. diff --git a/changes/ticket33222 b/changes/ticket33222 new file mode 100644 index 0000000000..f7b117d6ad --- /dev/null +++ b/changes/ticket33222 @@ -0,0 +1,8 @@ + o Major features (IPv6, relay): + - Launch IPv4 and IPv6 ORPort self-test circuits on relays and bridges. + Closes ticket 33222. + o Minor features (IPv6, relay): + - Allow relays to send IPv6-only extend cells. Closes ticket 33222. + - Declare support for the Relay=3 subprotocol version. Closes ticket 33226. + - When launching IPv6 ORPort self-test circuits, make sure that the + second-last hop can initiate an IPv6 extend. Closes ticket 33222. diff --git a/changes/ticket33224 b/changes/ticket33224 new file mode 100644 index 0000000000..3fdab7dc53 --- /dev/null +++ b/changes/ticket33224 @@ -0,0 +1,3 @@ + o Minor features (relay, IPv6): + - Add an AssumeReachableIPv6 option to disable self-checking IPv6 + reachability. Closes part of ticket 33224. diff --git a/changes/ticket33233 b/changes/ticket33233 new file mode 100644 index 0000000000..977286c323 --- /dev/null +++ b/changes/ticket33233 @@ -0,0 +1,4 @@ + o Major feature (IPv6, relay): + - The torrc option Address now supports IPv6. By doing so, we've also + unified the interface to find our address to support IPv4, IPv6 and + hostname. Closes ticket 33233; diff --git a/changes/ticket33236 b/changes/ticket33236 new file mode 100644 index 0000000000..d2b1d7e4da --- /dev/null +++ b/changes/ticket33236 @@ -0,0 +1,4 @@ + o Minor feature (relay, address discovery): + - If Address is not found in torrc, attempt to learn our address with the + configured ORPort address if any. Closes ticket 33236. + diff --git a/changes/ticket33238 b/changes/ticket33238 new file mode 100644 index 0000000000..2c4c3968cc --- /dev/null +++ b/changes/ticket33238 @@ -0,0 +1,5 @@ + o Minor feature (address discovery): + - If no Address statements are found, relays now prioritize guessing their + address by looking at the local interface instead of the local hostname. + If the interface address can't be found, the local hostname is used. + Closes ticket 33238. diff --git a/changes/ticket33275 b/changes/ticket33275 deleted file mode 100644 index bff3a7a3ad..0000000000 --- a/changes/ticket33275 +++ /dev/null @@ -1,5 +0,0 @@ - o Documentation (manpage): - - Alphabetize the Denial of Service Mitigation Options, Directory - Authority Server Options, Hidden Service Options, and Testing - Network Options sections of the tor(1) manpage. Closes ticket - 33275. Work by Swati Thacker as part of Google Season of Docs. diff --git a/changes/ticket33280 b/changes/ticket33280 deleted file mode 100644 index b90c3086ea..0000000000 --- a/changes/ticket33280 +++ /dev/null @@ -1,4 +0,0 @@ - o Testing: - - Add test-network-ipv4 and test-network-ipv6 jobs to the Makefile. - These jobs run the IPv4-only and dual-stack chutney flavours from - test-network-all. Closes ticket 33280. diff --git a/changes/ticket33300 b/changes/ticket33300 deleted file mode 100644 index 9b0bdce372..0000000000 --- a/changes/ticket33300 +++ /dev/null @@ -1,3 +0,0 @@ - o Testing: - - Add a basic IPv6 test to "make test-network". This test only runs when - the local machine has an IPv6 stack. Closes ticket 33300. diff --git a/changes/ticket33303 b/changes/ticket33303 deleted file mode 100644 index b7ac7b5067..0000000000 --- a/changes/ticket33303 +++ /dev/null @@ -1,4 +0,0 @@ - o Testing: - - Run the test-network-ipv6 Makefile target in the Travis CI IPv6 chutney - job. This job runs on macOS, so it's a bit slow. - Closes ticket 33303. diff --git a/changes/ticket33316 b/changes/ticket33316 deleted file mode 100644 index 25b0444078..0000000000 --- a/changes/ticket33316 +++ /dev/null @@ -1,15 +0,0 @@ - o Minor bugfixes (initialization): - - Initialize the subsystems in our code in an order more closely - corresponding to their dependencies, so that every system is - initialized before the ones that (theoretically) depend on it. - Fixes bug 33316; bugfix on 0.4.0.1-alpha. - - o Minor features (tests): - - Initialize all subsystems at the beginning of our unit test harness, - to avoid crashes due to uninitialized subsystems. - Follow-up from ticket 33316. - - o Code simplification and refactoring: - - Merge the orconn and ocirc events into the "core" subsystem, which - manages or connections and origin circuits. Previously they - were isolated in subsystems of their own. diff --git a/changes/ticket33334 b/changes/ticket33334 deleted file mode 100644 index ada3cb284c..0000000000 --- a/changes/ticket33334 +++ /dev/null @@ -1,5 +0,0 @@ - o Testing: - - Test v3 onion services to tor's mixed IPv4 chutney network. And add a - mixed IPv6 chutney network. These networks are used in the - test-network-all, test-network-ipv4, and test-network-ipv6 make targets. - Closes ticket 33334. diff --git a/changes/ticket33339 b/changes/ticket33339 deleted file mode 100644 index 75ccb3546f..0000000000 --- a/changes/ticket33339 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor feature (developer tools): - - Add a script to help check the alphabetical ordering of option - names in a manpage. Closes ticket 33339. diff --git a/changes/ticket33349 b/changes/ticket33349 deleted file mode 100644 index 0458a72c8d..0000000000 --- a/changes/ticket33349 +++ /dev/null @@ -1,4 +0,0 @@ - o Code simplification and refactoring: - - Updated comments in 'scheduler.c' to reflect old code changes, - and simplified the scheduler channel state change code. Closes - ticket 33349. diff --git a/changes/ticket33366 b/changes/ticket33366 deleted file mode 100644 index 1310c493c2..0000000000 --- a/changes/ticket33366 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor features (compilation size): - - Most Server-side DNS code is now disabled when building without - support for relay mode. Closes ticket 33366. diff --git a/changes/ticket33368 b/changes/ticket33368 deleted file mode 100644 index ecc6f66f4e..0000000000 --- a/changes/ticket33368 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor features (client-only compilation): - - Disable more code related to the ext_orport protocol when compiling - without support for relay mode. Closes ticket 33368. diff --git a/changes/ticket33369 b/changes/ticket33369 deleted file mode 100644 index c55335c5b7..0000000000 --- a/changes/ticket33369 +++ /dev/null @@ -1,4 +0,0 @@ - o Documentation (manpage): - - Add cross reference links and a table of contents to the HTML - tor manpage. Closes ticket 33369. Work by Swati Thacker as - part of Google Season of Docs. diff --git a/changes/ticket33370 b/changes/ticket33370 deleted file mode 100644 index 41e03357f0..0000000000 --- a/changes/ticket33370 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor features (client-only compilation): - - Disable more of our self-testing code when support for relay mode is - disabled. Closes ticket 33370. diff --git a/changes/ticket33398 b/changes/ticket33398 new file mode 100644 index 0000000000..bd175bad2f --- /dev/null +++ b/changes/ticket33398 @@ -0,0 +1,4 @@ + o Deprecated features: + - The "non-builtin" argument to the "--dump-config" command is now + deprecated. When it works, it behaves the same as "short", which + you should use instead. Closes ticket 33398. diff --git a/changes/ticket33400 b/changes/ticket33400 deleted file mode 100644 index 7603890765..0000000000 --- a/changes/ticket33400 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor feature (onion service v3): - - Log at INFO level why the service can not upload its descriptor(s). Closes - ticket 33400; bugfix on 0.3.2.1-alpha. diff --git a/changes/ticket33436 b/changes/ticket33436 deleted file mode 100644 index 69b5545c6d..0000000000 --- a/changes/ticket33436 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (directory authority, shared random): - - Refactor more authority-only parts of the shared-random scheduling code - to reside in the dirauth module, and to be disabled when compiling with - --disable-module-dirauth. Closes ticket 33436. diff --git a/changes/ticket33451 b/changes/ticket33451 deleted file mode 100644 index 74dd6d1ad8..0000000000 --- a/changes/ticket33451 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor features (developer tools): - - Add a script ("git-install-tools.sh") to install git hooks and helper - scripts. Closes ticket 33451. diff --git a/changes/ticket33458 b/changes/ticket33458 deleted file mode 100644 index 885c6dc505..0000000000 --- a/changes/ticket33458 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfix (onion service v3): - - When cleaning the client descriptor cache, an attempt at closing circuits - for a non decrypted descriptor (lacking client authorization) lead to an - assert(). Fixes bug 33458; bugfix on 0.4.2.1-alpha. diff --git a/changes/ticket33633 b/changes/ticket33633 deleted file mode 100644 index de030a6000..0000000000 --- a/changes/ticket33633 +++ /dev/null @@ -1,6 +0,0 @@ - o Code simplification and refactoring: - - Move the circuit extend code to the relay module. - Split the circuit extend function into smaller functions. - Closes ticket 33633. - - Move LOG_PROTOCOL_WARN to app/config.c. Resolves a dependency inversion. - Closes ticket 33633. diff --git a/changes/ticket33642 b/changes/ticket33642 deleted file mode 100644 index b81edf7613..0000000000 --- a/changes/ticket33642 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (developer tooling): - - Refrain from listing all .a files that are generated by Tor build in - .gitignore. Add a single wildcard *.a entry that covers all of them for - present and future. Closes ticket 33642. diff --git a/changes/ticket33679 b/changes/ticket33679 deleted file mode 100644 index d37842d065..0000000000 --- a/changes/ticket33679 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (IPv6 Support, address.c): - - Adds IPv6 support to tor_addr_is_valid(). Adds tests for the - above changes and tor_addr_is_null(). Closes ticket 33679. - Patch by MrSquanchee. diff --git a/changes/ticket33778 b/changes/ticket33778 deleted file mode 100644 index a33c647a6e..0000000000 --- a/changes/ticket33778 +++ /dev/null @@ -1,3 +0,0 @@ - o Documentation (manpage): - - Updated the options set by TestingTorNetwork in the man page. - Closes ticket 33778. diff --git a/changes/ticket33779 b/changes/ticket33779 deleted file mode 100644 index d4bc769ebb..0000000000 --- a/changes/ticket33779 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (onion service, logging): - - Typo in a log info level when PublishHidServDescriptors is set to 0. - Fixes bug 33779; bugfix on 0.3.2.1-alpha. diff --git a/changes/ticket33788 b/changes/ticket33788 deleted file mode 100644 index 236c056623..0000000000 --- a/changes/ticket33788 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (code safety): - - Check for failures of tor_inet_ntop() and tor_inet_ntoa() functions in - DNS and IP address processing code and adjust codepaths to make them - less likely to crash entire Tor instance. Resolves issue 33788. diff --git a/changes/ticket33789 b/changes/ticket33789 deleted file mode 100644 index a7e69793e6..0000000000 --- a/changes/ticket33789 +++ /dev/null @@ -1,4 +0,0 @@ - o Code simplification and refactoring (relay address): - - Move a series of functions related to address resolving into their own - files. Closes ticket 33789. - diff --git a/changes/ticket33816 b/changes/ticket33816 new file mode 100644 index 0000000000..6412e78443 --- /dev/null +++ b/changes/ticket33816 @@ -0,0 +1,4 @@ + o Code simplification and refactoring: + - When an extend cell is missing an IPv4 or IPv6 address, fill in the address + from the extend info. This is similar to what was done in ticket 33633 for + ed25519 keys. Closes ticket 33816. Patch by Neel Chauhan. diff --git a/changes/ticket33817 b/changes/ticket33817 deleted file mode 100644 index 9c22d084eb..0000000000 --- a/changes/ticket33817 +++ /dev/null @@ -1,12 +0,0 @@ - o Major features (IPv6, relay): - - Relays may extend circuits over IPv6, if the relay has an IPv6 ORPort, - and the client supplies the other relay's IPv6 ORPort in the EXTEND2 - cell. IPv6 extends will be used by the relay IPv6 ORPort self-tests in - 33222. Closes ticket 33817. - - Consider IPv6-only EXTEND2 cells valid on relays. Log a protocol warning - if the IPv4 or IPv6 address is an internal address, and internal - addresses are not allowed. But continue to use the other address, if it - is valid. Closes ticket 33817. - - If a relay can extend over IPv4 and IPv6, it chooses between them - uniformly at random. Closes ticket 33817. - - Re-use existing IPv6 connections for circuit extends. Closes ticket 33817. diff --git a/changes/ticket33901 b/changes/ticket33901 deleted file mode 100644 index b824cc5b07..0000000000 --- a/changes/ticket33901 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (IPv6, relay): - - Allow clients and relays to send dual-stack and IPv6-only EXTEND2 cells. - Parse dual-stack and IPv6-only EXTEND2 cells on relays. - Closes ticket 33901. diff --git a/changes/ticket33956 b/changes/ticket33956 deleted file mode 100644 index 7ad802797d..0000000000 --- a/changes/ticket33956 +++ /dev/null @@ -1,5 +0,0 @@ - o Code simplification and refactoring: - - Define and use a new constant TOR_ADDRPORT_BUF_LEN which is like - TOR_ADDR_BUF_LEN but includes enough space for an IP address, - brackets, seperating colon, and port number. Closes ticket 33956. - Patch by Neel Chauhan. diff --git a/changes/ticket34064 b/changes/ticket34064 new file mode 100644 index 0000000000..13ed70c8f6 --- /dev/null +++ b/changes/ticket34064 @@ -0,0 +1,5 @@ + o Minor features (relay, ipv6): + - Add new "assume-reachable" and "assume-reachable-ipv6" parameters + to be used in an emergency to tell relays that they should publish + even if they cannot complete their ORPort self-checks. + Closes ticket 34064 and part of 33224. diff --git a/changes/ticket34067 b/changes/ticket34067 new file mode 100644 index 0000000000..b67ccf6082 --- /dev/null +++ b/changes/ticket34067 @@ -0,0 +1,4 @@ + o Major features (relay self-testing, IPv6): + - Relays now track their IPv6 ORPort separately from the reachability of + their IPv4 ORPort. They will not publish a descriptor unless _both_ + ports appear to be externally reachable. Closes ticket 34067. diff --git a/changes/ticket34137 b/changes/ticket34137 new file mode 100644 index 0000000000..0982d9dd3b --- /dev/null +++ b/changes/ticket34137 @@ -0,0 +1,5 @@ + o Minor features (relay): + - Log immediately when launching a relay self-check. Previously + we would try to log before launching checks, or approximately + when we intended to launch checks, but this tended to be + error-prone. Closes ticket 34137. diff --git a/changes/ticket34200 b/changes/ticket34200 new file mode 100644 index 0000000000..b984bd83bb --- /dev/null +++ b/changes/ticket34200 @@ -0,0 +1,3 @@ + o Code simplification and refactoring: + - Refactor some common node selection code into a single function. + Closes ticket 34200. diff --git a/changes/ticket34211 b/changes/ticket34211 deleted file mode 100644 index b454873abf..0000000000 --- a/changes/ticket34211 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor features (windows): - - Add support for console control signals like Ctrl+C in Windows - Closes ticket 34211. Patch from Damon Harris (TheDcoder). diff --git a/changes/ticket34232 b/changes/ticket34232 new file mode 100644 index 0000000000..2e00465427 --- /dev/null +++ b/changes/ticket34232 @@ -0,0 +1,5 @@ + o Minor bugfixes (string handling): + - In summarize_protover_flags(), treat empty strings the same as NULL. + This prevents protocols_known from being set. Previously, we treated + empty strings as normal strings, which led to protocols_known being + set. Fixes bug 34232; bugfix on 0.3.3.2-alpha. Patch by Neel Chauhan. diff --git a/changes/ticket34255_043 b/changes/ticket34255_043 deleted file mode 100644 index 5cfec1d48d..0000000000 --- a/changes/ticket34255_043 +++ /dev/null @@ -1,3 +0,0 @@ - o Documentation: - - Fix several doxygen warnings related to imbalanced groups. - Closes ticket 34255. diff --git a/changes/ticket34445 b/changes/ticket34445 new file mode 100644 index 0000000000..111c815dac --- /dev/null +++ b/changes/ticket34445 @@ -0,0 +1,5 @@ + o Minor features (directory authority): + - The AssumeReachable option no longer stops directory authorities + from checking whether other relays are running. A new + AuthDirTestReachability option can be used to disable these checks. + Closes ticket 34445. diff --git a/changes/ticket34446 b/changes/ticket34446 new file mode 100644 index 0000000000..2ec7723129 --- /dev/null +++ b/changes/ticket34446 @@ -0,0 +1,5 @@ + o Minor features (testing configuration): + - The TestingTorNetwork no longer implicitly sets AssumeReachable to 1. + This change will allow us to test relays' self-testing mechanisms, + and eventually to test authorities' relay-testing functionality. + Closes ticket 34446. diff --git a/changes/ticket40002 b/changes/ticket40002 new file mode 100644 index 0000000000..bd40dd055a --- /dev/null +++ b/changes/ticket40002 @@ -0,0 +1,3 @@ + o Minor feature (control port): + - Add a DROPTIMEOUTS control port command to drop circuit build timeout + history and reset the timeout. Closes ticket 40002. diff --git a/changes/ticket40006 b/changes/ticket40006 new file mode 100644 index 0000000000..ad10e236c3 --- /dev/null +++ b/changes/ticket40006 @@ -0,0 +1,6 @@ + o Major bugfix (TLS, buffer): + - When attempting to read N bytes on a TLS connection, really try to read + those N bytes. Before that, Tor would stop reading after the first TLS + record which can be smaller than N bytes even though more data was waiting + on the TLS connection socket. The remaining data would have been read at + the next mainloop event. Fixes bug 40006; bugfix on 0.1.0.5-rc. diff --git a/changes/ticket40019 b/changes/ticket40019 new file mode 100644 index 0000000000..61ba171786 --- /dev/null +++ b/changes/ticket40019 @@ -0,0 +1,5 @@ + o Code simplification and refactoring (maintainer scripts): + - Disable by default the pre-commit hook. Use the environment variable + TOR_EXTRA_PRE_COMMIT_CHECKS in order to run it. Furthermore, stop running + practracker in the pre-commit hook and make check-local. Closes ticket + 40019. diff --git a/configure.ac b/configure.ac index ba5703aafe..a6df7149a0 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ dnl Copyright (c) 2007-2019, The Tor Project, Inc. dnl See LICENSE for licensing information AC_PREREQ([2.63]) -AC_INIT([tor],[0.4.4.1-alpha-dev]) +AC_INIT([tor],[0.4.5.0-alpha-dev]) AC_CONFIG_SRCDIR([src/app/main/tor_main.c]) AC_CONFIG_MACRO_DIR([m4]) @@ -16,7 +16,7 @@ configure_flags="$*" # version number changes. Tor uses it to make sure that it # only shuts down for missing "required protocols" when those protocols # are listed as required by a consensus after this date. -AC_DEFINE(APPROX_RELEASE_DATE, ["2020-06-16"], # for 0.4.4.1-alpha-dev +AC_DEFINE(APPROX_RELEASE_DATE, ["2020-06-09"], # for 0.4.5.0-alpha-dev [Approximate date when this software was released. (Updated when the version changes.)]) # "foreign" means we don't follow GNU package layout standards @@ -679,12 +679,10 @@ AC_CHECK_FUNCS( getdelim \ getifaddrs \ getline \ - getpass \ getrlimit \ gettimeofday \ gmtime_r \ gnu_get_libc_version \ - htonll \ inet_aton \ ioctl \ issetugid \ @@ -1042,7 +1040,6 @@ dnl to them. AC_CHECK_FUNCS([ \ ERR_load_KDF_strings \ EVP_PBE_scrypt \ - EVP_sha3_256 \ SSL_CIPHER_find \ SSL_CTX_set1_groups_list \ SSL_CTX_set_security_level \ @@ -1569,8 +1566,6 @@ AC_CHECK_HEADERS([errno.h \ mach/vm_inherit.h \ machine/limits.h \ malloc.h \ - malloc/malloc.h \ - malloc_np.h \ netdb.h \ netinet/in.h \ netinet/in6.h \ @@ -1591,7 +1586,6 @@ AC_CHECK_HEADERS([errno.h \ sys/statvfs.h \ sys/syscall.h \ sys/sysctl.h \ - sys/syslimits.h \ sys/time.h \ sys/types.h \ sys/un.h \ diff --git a/contrib/win32build/tor-mingw.nsi.in b/contrib/win32build/tor-mingw.nsi.in index b81ff22c89..8ca918b15b 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.4.4.1-alpha-dev" +!define VERSION "0.4.5.0-alpha-dev" !define INSTALLER "tor-${VERSION}-win32.exe" !define WEBSITE "https://www.torproject.org/" !define LICENSE "LICENSE" diff --git a/doc/HACKING/CodeStructure.md b/doc/HACKING/CodeStructure.md deleted file mode 100644 index d387018f9b..0000000000 --- a/doc/HACKING/CodeStructure.md +++ /dev/null @@ -1,123 +0,0 @@ -# Code Structure - -TODO: revise this to talk about how things are, rather than how things -have changed. - -For quite a while now, the program *tor* has been built from source -code in just two directories: **src/common** and **src/or**. - -This has become more-or-less untenable, for a few reasons -- most -notably of which is that it has led our code to become more -spaghetti-ish than I can endorse with a clean conscience. - -So to fix that, we've gone and done a huge code movement in our git -master branch, which will land in a release once Tor `0.3.5.1-alpha` is -out. - -Here's what we did: - - * **src/common** has been turned into a set of static libraries. These -all live in the **src/lib/*** directories. The dependencies between -these libraries should have no cycles. The libraries are: - - - **arch** -- Headers to handle architectural differences - - **cc** -- headers to handle differences among compilers - - **compress** -- wraps zlib, zstd, lzma - - **container** -- high-level container types - - **crypt_ops** -- Cryptographic operations. Planning to split this into -a higher and lower level library - - **ctime** -- Operations that need to run in constant-time. (Properly, -data-invariant time) - - **defs** -- miscelaneous definitions needed throughout Tor. - - **encoding** -- transforming one data type into another, and various -data types into strings. - - **err** -- lowest-level error handling, in cases where we can't use -the logs because something that the logging system needs has broken. - - **evloop** -- Generic event-loop handling logic - - **fdio** -- Low-level IO wrapper functions for file descriptors. - - **fs** -- Operations on the filesystem - - **intmath** -- low-level integer math and misc bit-twiddling hacks - - **lock** -- low-level locking code - - **log** -- Tor's logging module. This library sits roughly halfway up -the library dependency diagram, since everything it depends on has to -be carefully crafted to *not* log. - - **malloc** -- Low-level wrappers for the platform memory allocation functions. - - **math** -- Higher-level mathematical functions, and floating-point math - - **memarea** -- An arena allocator - - **meminfo** -- Functions for querying the current process's memory -status and resources - - **net** -- Networking compatibility and convenience code - - **osinfo** -- Querying information about the operating system - - **process** -- Launching and querying the status of other processes - - **sandbox** -- Backend for the linux seccomp2 sandbox - - **smartlist_core** -- The lowest-level of the smartlist_t data type. -Separated from the rest of the containers library because the logging -subsystem depends on it. - - **string** -- Compatibility and convenience functions for manipulating -C strings. - - **term** -- Terminal-related functions (currently limited to a getpass -function). - - **testsupport** -- Macros for mocking, unit tests, etc. - - **thread** -- Higher-level thread compatibility code - - **time** -- Higher-level time management code, including format -conversions and monotonic time - - **tls** -- Our wrapper around our TLS library - - **trace** -- Formerly src/trace -- a generic event tracing API - - **wallclock** -- Low-level time code, used by the log module. - - * To ensure that the dependency graph in **src/common** remains under -control, there is a tool that you can run called `make -check-includes`. It verifies that each module in Tor only includes -the headers that it is permitted to include, using a per-directory -*.may_include* file. - - * The **src/or/or.h** header has been split into numerous smaller -headers. Notably, many important structures are now declared in a -header called *foo_st.h*, where "foo" is the name of the structure. - - * The **src/or** directory, which had most of Tor's code, had been split -up into several directories. This is still a work in progress: This -code has not itself been refactored, and its dependency graph is still -a tangled web. I hope we'll be working on that over the coming -releases, but it will take a while to do. - - - The new top-level source directories are: - - **src/core** -- Code necessary to actually perform or use onion routing. - - **src/feature** -- Code used only by some onion routing -configurations, or only for a special purpose. - - **src/app** -- Top-level code to run, invoke, and configure the -lower-level code - - - The new second-level source directories are: - - **src/core/crypto** -- High-level cryptographic protocols used in Tor - - **src/core/mainloop** -- Tor's event loop, connection-handling, and -traffic-routing code. - - **src/core/or** -- Parts related to handling onion routing itself - - **src/core/proto** -- support for encoding and decoding different -wire protocols - - **src/feature/api** -- Support for making Tor embeddable - - **src/feature/client** -- Functionality which only Tor clients need - - **src/feature/control** -- Controller implementation - - **src/feature/dirauth** -- Directory authority - - **src/feature/dircache** -- Directory cache - - **src/feature/dirclient** -- Directory client - - **src/feature/dircommon** -- Shared code between the other directory modules - - **src/feature/hibernate** -- Hibernating when Tor is out of bandwidth -or shutting down - - **src/feature/hs** -- v3 onion service implementation - - **src/feature/hs_common** -- shared code between both onion service -implementations - - **src/feature/nodelist** -- storing and accessing the list of relays on -the network. - - **src/feature/relay** -- code that only relay servers and exit servers need. - - **src/feature/rend** -- v2 onion service implementation - - **src/feature/stats** -- statistics and history - - **src/app/config** -- configuration and state for Tor - - **src/app/main** -- Top-level functions to invoke the rest or Tor. - - * The `tor` executable is now built in **src/app/tor** rather than **src/or/tor**. - - * There are more static libraries than before that you need to build -into your application if you want to embed Tor. Rather than -maintaining this list yourself, I recommend that you run `make -show-libs` to have Tor emit a list of what you need to link. diff --git a/doc/include.am b/doc/include.am index 8651f845eb..5e0c90b0cd 100644 --- a/doc/include.am +++ b/doc/include.am @@ -52,7 +52,6 @@ EXTRA_DIST+= doc/asciidoc-helper.sh \ doc/HACKING/README.1st.md \ doc/HACKING/CodingStandards.md \ doc/HACKING/CodingStandardsRust.md \ - doc/HACKING/CodeStructure.md \ doc/HACKING/Fuzzing.md \ doc/HACKING/GettingStarted.md \ doc/HACKING/GettingStartedRust.md \ diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 7b3150e2a4..37db6a3050 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -97,11 +97,10 @@ The following options in this section are only recognized on the [[opt-verify-config]] **`--verify-config`**:: Verify whether the configuration file is valid. -[[opt-dump-config]] **`--dump-config`** **`short`**|**`full`**|**`non-builtin`**:: - Write a complete list of Tor's configured options to standard output. +[[opt-dump-config]] **`--dump-config`** **`short`**|**`full`**:: + Write a list of Tor's configured options to standard output. When the `short` flag is selected, only write the options that - are different from their default values. When `non-builtin` is selected, - write options that are not zero or the empty string. + are different from their default values When `full` is selected, write every option. [[opt-serviceinstall]] **`--service install`** [**`--options`** __command-line options__]:: @@ -2038,12 +2037,12 @@ different from other Tor clients: A list of identity fingerprints and country codes of nodes to use for "middle" hops in your normal circuits. Normal circuits include all circuits except for direct connections - to directory servers. Middle hops are all hops other than exit and entry. + + to directory servers. Middle hops are all hops other than exit and entry. + This is an **experimental** feature that is meant to be used by researchers and developers to test new features in the Tor network safely. Using it - without care will strongly influence your anonymity. This feature might get - removed in the future. + without care will strongly influence your anonymity. Other tor features may + not work with MiddleNodes. This feature might get removed in the future. + The HSLayer2Node and HSLayer3Node options override this option for onion service circuits, if they are set. The vanguards addon will read this @@ -2144,9 +2143,12 @@ is non-zero): [[AssumeReachable]] **AssumeReachable** **0**|**1**:: This option is used when bootstrapping a new Tor network. If set to 1, don't do self-reachability testing; just upload your server descriptor - immediately. If **AuthoritativeDirectory** is also set, this option - instructs the dirserver to bypass remote reachability testing too and list - all connected servers as running. + immediately. (Default: 0) + +[[AssumeReachableIPv6]] **AssumeReachableIPv6** **0**|**1**|**auto**:: + Like **AssumeReachable**, but affects only the relay's own IPv6 ORPort. + If this value is set to "auto", then Tor will look at **AssumeReachable** + instead. (Default: auto) [[BridgeRelay]] **BridgeRelay** **0**|**1**:: Sets the relay to act as a "bridge" with respect to relaying connections @@ -3006,6 +3008,12 @@ on the public Tor network. if there is some major bug in Ed25519 link authentication that causes us to label all the relays as not Running. (Default: 1) +[[AuthDirTestReachability]] **AuthDirTestReachability** **0**|**1**:: + Authoritative directories only. If set to 1, then we periodically + check every relay we know about to see whether it is running. + If set to 0, we vote Running for every relay, and don't perform + these tests. (Default: 1) + [[BridgePassword]] **BridgePassword** __Password__:: If set, contains an HTTP authenticator that tells a bridge authority to serve all requested bridge information. Used by the (only partially @@ -3359,7 +3367,6 @@ The following options are used for running a testing Tor network. DirAllowPrivateAddresses 1 EnforceDistinctSubnets 0 - AssumeReachable 1 AuthDirMaxServersPerAddr 0 ClientBootstrapConsensusAuthorityDownloadInitialDelay 0 ClientBootstrapConsensusFallbackDownloadInitialDelay 0 diff --git a/scripts/git/git-list-tor-branches.sh b/scripts/git/git-list-tor-branches.sh index d6b30f064f..b0c30d2e8b 100755 --- a/scripts/git/git-list-tor-branches.sh +++ b/scripts/git/git-list-tor-branches.sh @@ -139,15 +139,15 @@ finish() { branch maint-0.3.5 branch release-0.3.5 -branch maint-0.4.1 -branch release-0.4.1 - branch maint-0.4.2 branch release-0.4.2 branch maint-0.4.3 branch release-0.4.3 +branch maint-0.4.4 +branch release-0.4.4 + branch master finish diff --git a/scripts/git/pre-commit.git-hook b/scripts/git/pre-commit.git-hook index f630a242bd..5533ed0cdd 100755 --- a/scripts/git/pre-commit.git-hook +++ b/scripts/git/pre-commit.git-hook @@ -5,7 +5,12 @@ # # This is pre-commit git hook script that prevents commiting your changeset if # it fails our code formatting, changelog entry formatting, module include -# rules, or best practices tracker. +# rules, etc... + +# Run only if this environment variable is set. +if [ -z "$TOR_EXTRA_PRE_COMMIT_CHECKS" ]; then + exit 0 +fi workdir=$(git rev-parse --show-toplevel) @@ -49,13 +54,6 @@ if [ -e scripts/maint/checkShellScripts.sh ]; then scripts/maint/checkShellScripts.sh fi -# Always run the practracker unit tests -PT_DIR=scripts/maint/practracker - -if [ -e "${PT_DIR}/test_practracker.sh" ]; then - "${PT_DIR}/test_practracker.sh" -fi - if [ -e scripts/maint/checkSpaceTest.sh ]; then scripts/maint/checkSpaceTest.sh fi @@ -74,19 +72,11 @@ printf "Modified tor-owned source files:\\n%s\\n" "$CHECK_FILES" perl scripts/maint/checkSpace.pl -C \ $CHECK_FILES +# This makes sure that we are only including things we're allowed to include. if test -e scripts/maint/practracker/includes.py; then python scripts/maint/practracker/includes.py fi -# Only call practracker if ${PT_DIR}/.enable_practracker_in_hooks exists -# We do this check so that we can enable practracker in hooks in master, and -# disable it on maint branches -if [ -e "${PT_DIR}/practracker.py" ]; then - if [ -e "${PT_DIR}/.enable_practracker_in_hooks" ]; then - python3 "${PT_DIR}/practracker.py" "$workdir" - fi -fi - if [ -e scripts/coccinelle/check_cocci_parse.sh ]; then # Run a verbose cocci parse check on the changed files diff --git a/scripts/git/pre-push.git-hook b/scripts/git/pre-push.git-hook index efa45b9860..f0a3a250ec 100755 --- a/scripts/git/pre-push.git-hook +++ b/scripts/git/pre-push.git-hook @@ -83,6 +83,7 @@ do src/tools/*.[ch] \ )" + export TOR_EXTRA_PRE_COMMIT_CHECKS=1 # We want word splitting here, because file names are space # separated # shellcheck disable=SC2086 diff --git a/scripts/maint/clang-format.sh b/scripts/maint/clang-format.sh deleted file mode 100755 index 59832117b4..0000000000 --- a/scripts/maint/clang-format.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh -# Copyright 2020, The Tor Project, Inc. -# See LICENSE for licensing information. - -# -# DO NOT COMMIT OR MERGE CODE THAT IS RUN THROUGH THIS TOOL YET. -# -# WE ARE STILL DISCUSSING OUR DESIRED STYLE AND ITERATING ON IT. -# (12 Feb 2020) -# - -# This script runs "clang-format" and "codetool" in sequence over each of -# our source files, and replaces the original file only if it has changed. -# -# We can't just use clang-format -i, since we also want to use codetool to -# reformat a few things back to how we want them, and we want avoid changing -# the mtime on files that didn't actually change. - -set -e - -cd "$(dirname "$0")/../../src/" - -# Shellcheck complains that a for loop over find's output is unreliable, -# since there might be special characters in the output. But we happen -# to know that none of our C files have special characters or spaces in -# their names, so this is safe. -# -# shellcheck disable=SC2044 -for fname in $(find lib core feature app test tools -name '[^.]*.[ch]'); do - tmpfname="${fname}.clang_fmt.tmp" - rm -f "${tmpfname}" - clang-format --style=file "${fname}" > "${tmpfname}" - ../scripts/maint/codetool.py "${tmpfname}" - if cmp "${fname}" "${tmpfname}" >/dev/null 2>&1; then - echo "No change in ${fname}" - rm -f "${tmpfname}" - else - echo "Change in ${fname}" - mv "${tmpfname}" "${fname}" - fi -done diff --git a/scripts/maint/code-format.sh b/scripts/maint/code-format.sh new file mode 100755 index 0000000000..d8f597d70d --- /dev/null +++ b/scripts/maint/code-format.sh @@ -0,0 +1,232 @@ +#!/usr/bin/env bash +# Copyright 2020, The Tor Project, Inc. +# See LICENSE for licensing information. + +# +# DO NOT COMMIT OR MERGE CODE THAT IS RUN THROUGH THIS TOOL YET. +# +# WE ARE STILL DISCUSSING OUR DESIRED STYLE AND ITERATING ON IT. +# (12 Feb 2020) +# + +# This script runs "clang-format" and "codetool" in sequence over each of its +# arguments. It either replaces the original, or says whether anything has +# changed, depending on its arguments. +# +# We can't just use clang-format directly, since we also want to use codetool +# to reformat a few things back to how we want them, and we want avoid changing +# the mtime on files that didn't actually change. +# +# Use "-i" to edit the file in-place. +# Use "-c" to exit with a nonzero exit status if any file needs to change. +# Use "-d" to emit diffs. +# +# The "-a" option tells us to run over every Tor source file. +# The "-v" option tells us to be verbose. + +set -e + +ALL=0 +GITDIFF=0 +GITIDX=0 +DIFFMODE=0 +CHECKMODE=0 +CHANGEMODE=0 + +SCRIPT_NAME=$(basename "$0") +SCRIPT_DIR=$(dirname "$0") +SRC_DIR="${SCRIPT_DIR}/../../src" + +function usage() { + echo "$SCRIPT_NAME [-h] [-c|-d|-i] [-v] [-a|-G|files...]" + echo + echo " flags:" + echo " -h: show this help text" + echo " -c: check whether files are correctly formatted" + echo " -d: print a diff for the changes that would be applied" + echo " -i: change files in-place" + echo " -a: run over all the C files in Tor" + echo " -v: verbose mode" + echo " -g: look at the files that have changed in git." + echo " -G: look at the files that are staged for the git commit." + echo + echo "EXAMPLES" + echo + echo " $SCRIPT_NAME -a -i" + echo " rewrite every file in place, whether it has changed or not." + echo " $SCRIPT_NAME -a -d" + echo " as above, but only display the changes." + echo " $SCRIPT_NAME -g -i" + echo " update every file that you have changed in the git working tree." + echo " $SCRIPT_NAME -G -c" + echo " exit with an error if any staged changes are not well-formatted." +} + +FILEARGS_OK=1 + +while getopts "acdgGhiv" opt; do + case "$opt" in + h) usage + exit 0 + ;; + a) ALL=1 + FILEARGS_OK=0 + ;; + g) GITDIFF=1 + FILEARGS_OK=0 + ;; + G) GITIDX=1 + FILEARGS_OK=0 + ;; + c) CHECKMODE=1 + ;; + d) DIFFMODE=1 + ;; + i) CHANGEMODE=1 + ;; + v) VERBOSE=1 + ;; + *) echo + usage + exit 1 + ;; + esac +done +# get rid of the flags; keep the filenames. +shift $((OPTIND - 1)) + +# Define a verbose function. +if [[ $VERBOSE = 1 ]]; then + function note() + { + echo "$@" + } +else + function note() + { + true + } +fi + +# We have to be in at least one mode, or we can't do anything +if [[ $CHECKMODE = 0 && $DIFFMODE = 0 && $CHANGEMODE = 0 ]]; then + echo "Nothing to do. You need to specify -c, -d, or -i." + echo "Try $SCRIPT_NAME -h for more information." + exit 0 +fi + +# We don't want to "give an error if anything would change" if we're +# actually trying to change things. +if [[ $CHECKMODE = 1 && $CHANGEMODE = 1 ]]; then + echo "It doesn't make sense to use -c and -i together." + exit 0 +fi +# It doesn't make sense to look at "all files" and "git files" +if [[ $((ALL + GITIDX + GITDIFF)) -gt 1 ]]; then + echo "It doesn't make sense to use more than one of -a, -g, or -G together." + exit 0 +fi + +if [[ $FILEARGS_OK = 1 ]]; then + # The filenames are on the command-line. + INPUTS=("${@}") +else + if [[ "$#" != 0 ]]; then + echo "Can't use -a, -g, or -G with additional command-line arguments." + exit 1 + fi +fi + +if [[ $ALL = 1 ]]; then + # We're in "all" mode -- use find(1) to find the filenames. + mapfile -d '' INPUTS < <(find "${SRC_DIR}"/{lib,core,feature,app,test,tools} -name '[^.]*.[ch]' -print0) +elif [[ $GITIDX = 1 ]]; then + # We're in "git index" mode -- use git diff --cached to find the filenames + # that are changing in the index, then strip out the ones that + # aren't C. + mapfile INPUTS < <(git diff --name-only --cached --diff-filter=AMCR | grep '\.[ch]$') +elif [[ $GITDIFF = 1 ]]; then + # We are in 'git diff' mode -- we want everything that changed, including + # the index and the working tree. + # + # TODO: There might be a better way to do this. + mapfile INPUTS < <(git diff --name-only --cached --diff-filter=AMCR | grep '\.[ch]$'; git diff --name-only --diff-filter=AMCR | grep '\.[ch]$' ) +fi + +if [[ $GITIDX = 1 ]]; then + # If we're running in git mode, we need to stash all the changes that + # we don't want to look at. This is necessary even though we're only + # looking at the changed files, since we might have the file only + # partially staged. + note "Stashing unstaged changes" + git stash -q --keep-index + function restoregit() { + note "Restoring git state" + git stash pop -q + } +else + function restoregit() { + true + } +fi + +ANY_CHANGED=0 + +tmpfname="" + +# +# Set up a trap handler to make sure that on exit, we remove our +# tmpfile and un-stash the git environment (if appropriate) +# +trap 'if [ -n "${tmpfname}" ]; then rm -f "${tmpfname}"; fi; restoregit' 0 + +for fname in "${INPUTS[@]}"; do + note "Inspecting $fname..." + tmpfname="${fname}.$$.clang_fmt.tmp" + rm -f "${tmpfname}" + clang-format --style=file "${fname}" > "${tmpfname}" + "${SCRIPT_DIR}/codetool.py" "${tmpfname}" + + changed=not_set + + if [[ $DIFFMODE = 1 ]]; then + # If we're running diff for its output, we can also use it + # to compare the files. + if diff -u "${fname}" "${tmpfname}"; then + changed=0 + else + changed=1 + fi + else + # We aren't running diff, so we have to compare the files with cmp. + if cmp "${fname}" "${tmpfname}" >/dev/null 2>&1; then + changed=0 + else + changed=1 + fi + fi + + if [[ $changed = 1 ]]; then + note "Found a change in $fname" + ANY_CHANGED=1 + + if [[ $CHANGEMODE = 1 ]]; then + mv "${tmpfname}" "${fname}" + fi + fi + + rm -f "${tmpfname}" +done + +exitcode=0 + +if [[ $CHECKMODE = 1 ]]; then + if [[ $ANY_CHANGED = 1 ]]; then + note "Found at least one misformatted file; check failed" + exitcode=1 + else + note "No changes found." + fi +fi + +exit $exitcode diff --git a/scripts/maint/practracker/.enable_practracker_in_hooks b/scripts/maint/practracker/.enable_practracker_in_hooks new file mode 100644 index 0000000000..a9e707f5da --- /dev/null +++ b/scripts/maint/practracker/.enable_practracker_in_hooks @@ -0,0 +1 @@ +This file is present to tell our git hooks to run practracker on this branch. diff --git a/scripts/maint/practracker/exceptions.txt b/scripts/maint/practracker/exceptions.txt index 35e860d8b1..25568f03f0 100644 --- a/scripts/maint/practracker/exceptions.txt +++ b/scripts/maint/practracker/exceptions.txt @@ -46,7 +46,7 @@ problem function-size /src/app/config/config.c:parse_dir_authority_line() 150 problem function-size /src/app/config/config.c:parse_dir_fallback_line() 101 problem function-size /src/app/config/config.c:port_parse_config() 435 problem function-size /src/app/config/config.c:parse_ports() 132 -problem function-size /src/app/config/resolve_addr.c:resolve_my_address() 191 +problem function-size /src/app/config/resolve_addr.c:resolve_my_address_v4() 197 problem file-size /src/app/config/or_options_st.h 1050 problem include-count /src/app/main/main.c 68 problem function-size /src/app/main/main.c:dumpstats() 102 @@ -96,7 +96,7 @@ problem function-size /src/core/or/channeltls.c:channel_tls_process_authenticate problem dependency-violation /src/core/or/channeltls.c 11 problem include-count /src/core/or/circuitbuild.c 53 problem function-size /src/core/or/circuitbuild.c:get_unique_circ_id_by_chan() 128 -problem function-size /src/core/or/circuitbuild.c:choose_good_exit_server_general() 206 +problem function-size /src/core/or/circuitbuild.c:choose_good_exit_server_general() 196 problem dependency-violation /src/core/or/circuitbuild.c 25 problem include-count /src/core/or/circuitlist.c 55 problem function-size /src/core/or/circuitlist.c:HT_PROTOTYPE() 109 @@ -108,16 +108,16 @@ problem dependency-violation /src/core/or/circuitlist.h 1 problem function-size /src/core/or/circuitmux.c:circuitmux_set_policy() 109 problem function-size /src/core/or/circuitmux.c:circuitmux_attach_circuit() 113 problem dependency-violation /src/core/or/circuitmux_ewma.c 2 -problem file-size /src/core/or/circuitpadding.c 3101 +problem file-size /src/core/or/circuitpadding.c 3183 problem function-size /src/core/or/circuitpadding.c:circpad_machine_schedule_padding() 113 problem dependency-violation /src/core/or/circuitpadding.c 6 -problem file-size /src/core/or/circuitpadding.h 813 +problem file-size /src/core/or/circuitpadding.h 832 problem function-size /src/core/or/circuitpadding_machines.c:circpad_machine_relay_hide_intro_circuits() 103 problem function-size /src/core/or/circuitpadding_machines.c:circpad_machine_client_hide_rend_circuits() 112 problem dependency-violation /src/core/or/circuitpadding_machines.c 1 problem function-size /src/core/or/circuitstats.c:circuit_build_times_parse_state() 123 problem dependency-violation /src/core/or/circuitstats.c 11 -problem file-size /src/core/or/circuituse.c 3195 +problem file-size /src/core/or/circuituse.c 3250 problem function-size /src/core/or/circuituse.c:circuit_is_acceptable() 128 problem function-size /src/core/or/circuituse.c:circuit_expire_building() 389 problem function-size /src/core/or/circuituse.c:circuit_log_ancient_one_hop_circuits() 126 @@ -145,8 +145,9 @@ problem function-size /src/core/or/connection_or.c:connection_or_group_set_badne problem function-size /src/core/or/connection_or.c:connection_or_client_learned_peer_id() 142 problem dependency-violation /src/core/or/connection_or.c 21 problem dependency-violation /src/core/or/dos.c 6 +problem dependency-violation /src/core/or/extendinfo.c 6 problem dependency-violation /src/core/or/onion.c 2 -problem file-size /src/core/or/or.h 1105 +problem file-size /src/core/or/or.h 1150 problem include-count /src/core/or/or.h 48 problem dependency-violation /src/core/or/or.h 1 problem dependency-violation /src/core/or/or_periodic.c 1 @@ -198,7 +199,7 @@ problem function-size /src/feature/control/control_events.c:control_event_stream problem include-count /src/feature/control/control_getinfo.c 56 problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_misc() 108 problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_dir() 297 -problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_events() 234 +problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_events() 237 problem function-size /src/feature/dirauth/bwauth.c:dirserv_read_measured_bandwidths() 121 problem file-size /src/feature/dirauth/dirvote.c 4734 problem include-count /src/feature/dirauth/dirvote.c 55 @@ -255,11 +256,11 @@ problem function-size /src/feature/nodelist/microdesc.c:microdesc_cache_rebuild( problem include-count /src/feature/nodelist/networkstatus.c 65 problem function-size /src/feature/nodelist/networkstatus.c:networkstatus_check_consensus_signature() 175 problem function-size /src/feature/nodelist/networkstatus.c:networkstatus_set_current_consensus() 289 -problem function-size /src/feature/nodelist/node_select.c:router_pick_directory_server_impl() 122 +problem function-size /src/feature/nodelist/node_select.c:router_pick_directory_server_impl() 126 problem function-size /src/feature/nodelist/node_select.c:compute_weighted_bandwidths() 204 -problem function-size /src/feature/nodelist/node_select.c:router_pick_trusteddirserver_impl() 112 +problem function-size /src/feature/nodelist/node_select.c:router_pick_trusteddirserver_impl() 116 problem function-size /src/feature/nodelist/nodelist.c:compute_frac_paths_available() 190 -problem file-size /src/feature/nodelist/routerlist.c 3247 +problem file-size /src/feature/nodelist/routerlist.c 3350 problem function-size /src/feature/nodelist/routerlist.c:router_rebuild_store() 148 problem function-size /src/feature/nodelist/routerlist.c:router_add_to_routerlist() 168 problem function-size /src/feature/nodelist/routerlist.c:routerlist_remove_old_routers() 121 diff --git a/scripts/maint/rename_c_identifier.py b/scripts/maint/rename_c_identifier.py index 77802e10f3..7794689303 100755 --- a/scripts/maint/rename_c_identifier.py +++ b/scripts/maint/rename_c_identifier.py @@ -239,7 +239,7 @@ def main(argv): print("I require an even number of identifiers.", file=sys.stderr) return 1 - if any_uncommitted_changes(): + if args.commit and any_uncommitted_changes(): print("Uncommitted changes found. Not running.", file=sys.stderr) return 1 diff --git a/src/app/app.md b/src/app/app.md index 138e75b127..298bde75f5 100644 --- a/src/app/app.md +++ b/src/app/app.md @@ -4,3 +4,8 @@ The "app" directory has Tor's main entry point and configuration logic, and is responsible for initializing and managing the other modules in Tor. + +The modules in "app" are: + + - \refdir{app/config} -- configuration and state for Tor + - \refdir{app/main} -- Top-level functions to invoke the rest or Tor. diff --git a/src/app/config/config.c b/src/app/config/config.c index 71f8c18ca2..9d852e5408 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -313,7 +313,7 @@ static const config_var_t option_vars_[] = { V(AccountingMax, MEMUNIT, "0 bytes"), VAR("AccountingRule", STRING, AccountingRule_option, "max"), V(AccountingStart, STRING, NULL), - V(Address, STRING, NULL), + V(Address, LINELIST, NULL), OBSOLETE("AllowDotExit"), OBSOLETE("AllowInvalidNodes"), V(AllowNonRFC953Hostnames, BOOL, "0"), @@ -323,6 +323,7 @@ static const config_var_t option_vars_[] = { V(AlternateDirAuthority, LINELIST, NULL), OBSOLETE("AlternateHSAuthority"), V(AssumeReachable, BOOL, "0"), + V(AssumeReachableIPv6, AUTOBOOL, "auto"), OBSOLETE("AuthDirBadDir"), OBSOLETE("AuthDirBadDirCCs"), V(AuthDirBadExit, LINELIST, NULL), @@ -2771,10 +2772,6 @@ options_dump(const or_options_t *options, int how_to_dump) use_defaults = global_default_options; minimal = 1; break; - case OPTIONS_DUMP_DEFAULTS: - use_defaults = NULL; - minimal = 1; - break; case OPTIONS_DUMP_ALL: use_defaults = NULL; minimal = 0; @@ -3229,6 +3226,10 @@ options_validate_cb(const void *old_options_, void *options_, char **msg) REJECT("TokenBucketRefillInterval must be between 1 and 1000 inclusive."); } + if (options->AssumeReachable && options->AssumeReachableIPv6 == 0) { + REJECT("Cannot set AssumeReachable 1 and AssumeReachableIPv6 0."); + } + if (options->ExcludeExitNodes || options->ExcludeNodes) { options->ExcludeExitNodesUnion_ = routerset_new(); routerset_union(options->ExcludeExitNodesUnion_,options->ExcludeExitNodes); @@ -4028,7 +4029,7 @@ options_check_transition_cb(const void *old_, if (! CFG_EQ_INT(old, new_val, opt)) \ BAD_CHANGE_TO(opt," with Sandbox active") - SB_NOCHANGE_STR(Address); + SB_NOCHANGE_LINELIST(Address); SB_NOCHANGE_STR(ServerDNSResolvConfFile); SB_NOCHANGE_STR(DirPortFrontPage); SB_NOCHANGE_STR(CookieAuthFile); diff --git a/src/app/config/config.h b/src/app/config/config.h index 17caa0e3ff..f7d4e49f6f 100644 --- a/src/app/config/config.h +++ b/src/app/config/config.h @@ -58,8 +58,7 @@ setopt_err_t options_trial_assign(struct config_line_t *list, unsigned flags, void options_init(or_options_t *options); #define OPTIONS_DUMP_MINIMAL 1 -#define OPTIONS_DUMP_DEFAULTS 2 -#define OPTIONS_DUMP_ALL 3 +#define OPTIONS_DUMP_ALL 2 char *options_dump(const or_options_t *options, int how_to_dump); int options_init_from_torrc(int argc, char **argv); setopt_err_t options_init_from_string(const char *cf_defaults, const char *cf, diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h index bf58205f89..07126cc6ce 100644 --- a/src/app/config/or_options_st.h +++ b/src/app/config/or_options_st.h @@ -71,7 +71,10 @@ struct or_options_t { int CacheDirectoryGroupReadable; /**< Boolean: Is the CacheDirectory g+r? */ char *Nickname; /**< OR only: nickname of this onion router. */ - char *Address; /**< OR only: configured address for this onion router. */ + /** OR only: configured address for this onion router. Up to two times this + * options is accepted as in IPv4 and IPv6. */ + struct config_line_t *Address; + char *PidFile; /**< Where to store PID of Tor process. */ struct routerset_t *ExitNodes; /**< Structure containing nicknames, digests, @@ -192,7 +195,14 @@ struct or_options_t { unsigned int HTTPTunnelPort_set : 1; /**@}*/ - int AssumeReachable; /**< Whether to publish our descriptor regardless. */ + /** Whether to publish our descriptor regardless of all our self-tests + */ + int AssumeReachable; + /** Whether to publish our descriptor regardless of IPv6 self-tests. + * + * This is an autobool; when set to AUTO, it uses AssumeReachable. + **/ + int AssumeReachableIPv6; int AuthoritativeDir; /**< Boolean: is this an authoritative directory? */ int V3AuthoritativeDir; /**< Boolean: is this an authoritative directory * for version 3 directories? */ diff --git a/src/app/config/resolve_addr.c b/src/app/config/resolve_addr.c index 9d1a8e0260..caca5a37d9 100644 --- a/src/app/config/resolve_addr.c +++ b/src/app/config/resolve_addr.c @@ -15,300 +15,682 @@ #include "feature/control/control_events.h" +#include "lib/encoding/confline.h" #include "lib/net/gethostname.h" #include "lib/net/resolve.h" -/** Last value actually set by resolve_my_address. */ -static uint32_t last_resolved_addr = 0; +/** Maximum "Address" statement allowed in our configuration. */ +#define MAX_CONFIG_ADDRESS 2 + +/** Ease our life. Arrays containing state per address family. These are to + * add semantic to the code so we know what is accessed. */ +#define IDX_NULL 0 /* Index to zeroed address object. */ +#define IDX_IPV4 1 /* Index to AF_INET. */ +#define IDX_IPV6 2 /* Index to AF_INET6. */ +#define IDX_SIZE 3 /* How many indexes do we have. */ + +/** Function in our address function table return one of these code. */ +typedef enum { + /* The address has been found. */ + FN_RET_OK = 0, + /* The failure requirements were not met and thus it is recommended that the + * caller stops the search. */ + FN_RET_BAIL = 1, + /* The address was not found or failure is transient so the caller should go + * to the next method. */ + FN_RET_NEXT = 2, +} fn_address_ret_t; + +/** Last resolved addresses. */ +static tor_addr_t last_resolved_addrs[IDX_SIZE]; + +static inline int +af_to_idx(const int family) +{ + switch (family) { + case AF_INET: + return IDX_IPV4; + case AF_INET6: + return IDX_IPV6; + default: + /* It wouldn't be safe to just die here with an assert but we can heavily + * scream with a bug. Return the index of the NULL address. */ + tor_assert_nonfatal_unreached(); + return IDX_NULL; + } +} -/** Accessor for last_resolved_addr from outside this file. */ -uint32_t -get_last_resolved_addr(void) +/** Copy the last resolved address of family into addr_out. + * + * If not last resolved address existed, the addr_out is a null address (use + * tor_addr_is_null()). */ +void +resolved_addr_get_last(int family, tor_addr_t *addr_out) { - return last_resolved_addr; + tor_addr_copy(addr_out, &last_resolved_addrs[af_to_idx(family)]); } -/** Reset last_resolved_addr from outside this file. */ +/** Reset the last resolved address of family. + * + * This makes it null address. */ void -reset_last_resolved_addr(void) +resolved_addr_reset_last(int family) { - last_resolved_addr = 0; + tor_addr_make_null(&last_resolved_addrs[af_to_idx(family)], family); } -/** - * Attempt getting our non-local (as judged by tor_addr_is_internal() - * function) IP address using following techniques, listed in - * order from best (most desirable, try first) to worst (least - * desirable, try if everything else fails). - * - * First, attempt using <b>options-\>Address</b> to get our - * non-local IP address. - * - * If <b>options-\>Address</b> represents a non-local IP address, - * consider it ours. - * - * If <b>options-\>Address</b> is a DNS name that resolves to - * a non-local IP address, consider this IP address ours. - * - * If <b>options-\>Address</b> is NULL, fall back to getting local - * hostname and using it in above-described ways to try and - * get our IP address. - * - * In case local hostname cannot be resolved to a non-local IP - * address, try getting an IP address of network interface - * in hopes it will be non-local one. - * - * Fail if one or more of the following is true: - * - DNS name in <b>options-\>Address</b> cannot be resolved. - * - <b>options-\>Address</b> is a local host address. - * - Attempt at getting local hostname fails. - * - Attempt at getting network interface address fails. - * - * Return 0 if all is well, or -1 if we can't find a suitable - * public IP address. - * - * If we are returning 0: - * - Put our public IP address (in host order) into *<b>addr_out</b>. - * - If <b>method_out</b> is non-NULL, set *<b>method_out</b> to a static - * string describing how we arrived at our answer. - * - "CONFIGURED" - parsed from IP address string in - * <b>options-\>Address</b> - * - "RESOLVED" - resolved from DNS name in <b>options-\>Address</b> - * - "GETHOSTNAME" - resolved from a local hostname. - * - "INTERFACE" - retrieved from a network interface. - * - If <b>hostname_out</b> is non-NULL, and we resolved a hostname to - * get our address, set *<b>hostname_out</b> to a newly allocated string - * holding that hostname. (If we didn't get our address by resolving a - * hostname, set *<b>hostname_out</b> to NULL.) - * - * XXXX ipv6 +/** Errors returned by address_can_be_used() in order for the caller to know + * why the address is denied or not. */ +#define ERR_DEFAULT_DIRAUTH -1 /* Using default authorities. */ +#define ERR_ADDRESS_IS_INTERNAL -2 /* IP is internal. */ + +/** @brief Return true iff the given IP address can be used as a valid + * external resolved address. + * + * Two tests are done in this function: + * 1) If the address if NOT internal, it can be used. + * 2) If the address is internal and we have custom directory authorities + * configured then it can they be used. Important for testing networks. + * + * @param addr The IP address to validate. + * @param options Global configuration options. + * @param warn_severity Log level that should be used on error. + * @param explicit_ip Was the IP address explicitly given. + * + * @return Return 0 if it can be used. Return error code ERR_* found at the + * top of the file. */ -int -resolve_my_address(int warn_severity, const or_options_t *options, - uint32_t *addr_out, - const char **method_out, char **hostname_out) +static int +address_can_be_used(const tor_addr_t *addr, const or_options_t *options, + int warn_severity, const bool explicit_ip) { - struct in_addr in; - uint32_t addr; /* host order */ - char hostname[256]; - const char *method_used; - const char *hostname_used; - int explicit_ip=1; - int explicit_hostname=1; - int from_interface=0; - char *addr_string = NULL; - const char *address = options->Address; - int notice_severity = warn_severity <= LOG_NOTICE ? - LOG_NOTICE : warn_severity; - - tor_addr_t myaddr; + tor_assert(addr); + + /* Public address, this is fine. */ + if (!tor_addr_is_internal(addr, 0)) { + goto allow; + } + + /* We have a private IP address. It is allowed only if we set custom + * directory authorities. */ + if (using_default_dir_authorities(options)) { + log_fn(warn_severity, LD_CONFIG, + "Address '%s' is a private IP address. Tor relays that use " + "the default DirAuthorities must have public IP addresses.", + fmt_addr(addr)); + return ERR_DEFAULT_DIRAUTH; + } + + if (!explicit_ip) { + /* Even with custom directory authorities, only an explicit internal + * address is accepted. */ + log_fn(warn_severity, LD_CONFIG, + "Address %s was resolved and thus not explicitly " + "set. Even if DirAuthorities are custom, this is " + "not allowed.", fmt_addr(addr)); + return ERR_ADDRESS_IS_INTERNAL; + } + + allow: + return 0; +} + +/** @brief Get IP address from the given config line and for a specific address + * family. + * + * This can fail is more than two Address statement are found for the same + * address family. It also fails if no statement is found. + * + * @param options Global configuration options. + * @param warn_severity Log level that should be used on error. + * @param family IP address family. Only AF_INET and AF_INET6 are supported. + * @param method_out OUT: String denoting by which method the address was + * found. This is described in the control-spec.txt as + * actions for "STATUS_SERVER". + * @param hostname_out OUT: String containing the hostname gotten from the + * Address value if any. + * @param addr_out OUT: Tor address of the address found in the cline or + * resolved from the cline. + * + * @return Return 0 on success that is an address has been found or resolved + * successfully. Return error code ERR_* found at the top of the file. + */ +static fn_address_ret_t +get_address_from_config(const or_options_t *options, int warn_severity, + int family, const char **method_out, + char **hostname_out, tor_addr_t *addr_out) +{ + int ret; + bool explicit_ip = false; + int num_valid_addr = 0; + + tor_assert(options); tor_assert(addr_out); + tor_assert(method_out); + tor_assert(hostname_out); - /* - * Step one: Fill in 'hostname' to be our best guess. - */ + /* Set them to NULL for safety reasons. */ + *hostname_out = NULL; + *method_out = NULL; - if (address && *address) { - strlcpy(hostname, address, sizeof(hostname)); - log_debug(LD_CONFIG, "Trying configured Address '%s' as local hostname", - hostname); - } else { /* then we need to guess our address */ - explicit_ip = 0; /* it's implicit */ - explicit_hostname = 0; /* it's implicit */ - - if (tor_gethostname(hostname, sizeof(hostname)) < 0) { - log_fn(warn_severity, LD_NET,"Error obtaining local hostname"); - return -1; - } - log_debug(LD_CONFIG, "Guessed local host name as '%s'", hostname); + log_debug(LD_CONFIG, "Attempting to get address from configuration"); + + if (!options->Address) { + log_info(LD_CONFIG, "No Address option found in configuration."); + /* No Address statement, inform caller to try next method. */ + return FN_RET_NEXT; } - /* - * Step two: Now that we know 'hostname', parse it or resolve it. If - * it doesn't parse or resolve, look at the interface address. Set 'addr' - * to be our (host-order) 32-bit answer. - */ + for (const config_line_t *cfg = options->Address; cfg != NULL; + cfg = cfg->next) { + int af; + tor_addr_t addr; + + af = tor_addr_parse(&addr, cfg->value); + if (af == family) { + tor_addr_copy(addr_out, &addr); + *method_out = "CONFIGURED"; + explicit_ip = true; + num_valid_addr++; + continue; + } else if (af != -1) { + /* Parsable address but just not the one from the family we want. Skip + * it so we don't attempt a resolve. */ + continue; + } - if (tor_inet_aton(hostname, &in) == 0) { - /* then we have to resolve it */ - log_debug(LD_CONFIG, "Local hostname '%s' is DNS address. " - "Trying to resolve to IP address.", hostname); - explicit_ip = 0; - if (tor_lookup_hostname(hostname, &addr)) { /* failed to resolve */ - uint32_t interface_ip; /* host order */ - - if (explicit_hostname) { - log_fn(warn_severity, LD_CONFIG, - "Could not resolve local Address '%s'. Failing.", hostname); - return -1; - } - log_fn(notice_severity, LD_CONFIG, - "Could not resolve guessed local hostname '%s'. " - "Trying something else.", hostname); - if (get_interface_address(warn_severity, &interface_ip)) { - log_fn(warn_severity, LD_CONFIG, - "Could not get local interface IP address. Failing."); - return -1; - } - from_interface = 1; - addr = interface_ip; - log_fn(notice_severity, LD_CONFIG, "Learned IP address '%s' for " - "local interface. Using that.", fmt_addr32(addr)); - strlcpy(hostname, "<guessed from interfaces>", sizeof(hostname)); - } else { /* resolved hostname into addr */ - tor_addr_from_ipv4h(&myaddr, addr); - - if (!explicit_hostname && - tor_addr_is_internal(&myaddr, 0)) { - tor_addr_t interface_ip; - - log_fn(notice_severity, LD_CONFIG, "Guessed local hostname '%s' " - "resolves to a private IP address (%s). Trying something " - "else.", hostname, fmt_addr32(addr)); - - if (get_interface_address6(warn_severity, AF_INET, &interface_ip)<0) { - log_fn(warn_severity, LD_CONFIG, - "Could not get local interface IP address. Too bad."); - } else if (tor_addr_is_internal(&interface_ip, 0)) { - log_fn(notice_severity, LD_CONFIG, - "Interface IP address '%s' is a private address too. " - "Ignoring.", fmt_addr(&interface_ip)); - } else { - from_interface = 1; - addr = tor_addr_to_ipv4h(&interface_ip); - log_fn(notice_severity, LD_CONFIG, - "Learned IP address '%s' for local interface." - " Using that.", fmt_addr32(addr)); - strlcpy(hostname, "<guessed from interfaces>", sizeof(hostname)); - } + /* Not an IP address. Considering this value a hostname and attempting to + * do a DNS lookup. */ + if (!tor_addr_lookup(cfg->value, family, &addr)) { + tor_addr_copy(addr_out, &addr); + *method_out = "RESOLVED"; + if (*hostname_out) { + tor_free(*hostname_out); } + *hostname_out = tor_strdup(cfg->value); + explicit_ip = false; + num_valid_addr++; + continue; + } else { + /* Hostname that can't be resolved, this is a fatal error. */ + log_fn(warn_severity, LD_CONFIG, + "Could not resolve local Address '%s'. Failing.", cfg->value); + continue; } - } else { - log_debug(LD_CONFIG, "Local hostname '%s' is already IP address, " - "skipping DNS resolution", hostname); - addr = ntohl(in.s_addr); /* set addr so that addr_string is not - * illformed */ } - /* - * Step three: Check whether 'addr' is an internal IP address, and error - * out if it is and we don't want that. - */ + if (!num_valid_addr) { + log_fn(warn_severity, LD_CONFIG, + "No Address option found for family %s in configuration.", + fmt_af_family(family)); + /* No Address statement for family but one exists since Address is not + * NULL thus we have to stop now and not attempt to send back a guessed + * address. */ + return FN_RET_BAIL; + } - tor_addr_from_ipv4h(&myaddr,addr); + if (num_valid_addr >= MAX_CONFIG_ADDRESS) { + /* Too many Address for same family. This is a fatal error. */ + log_fn(warn_severity, LD_CONFIG, + "Found %d Address statement of address family %s. " + "Only one is allowed.", num_valid_addr, fmt_af_family(family)); + tor_free(*hostname_out); + return FN_RET_BAIL; + } - addr_string = tor_dup_ip(addr); - if (addr_string && tor_addr_is_internal(&myaddr, 0)) { - /* make sure we're ok with publishing an internal IP */ - if (using_default_dir_authorities(options)) { - /* if they are using the default authorities, disallow internal IPs - * always. For IPv6 ORPorts, this check is done in - * router_get_advertised_ipv6_or_ap(). See #33681. */ - log_fn(warn_severity, LD_CONFIG, - "Address '%s' resolves to private IP address '%s'. " - "Tor servers that use the default DirAuthorities must have " - "public IP addresses.", hostname, addr_string); - tor_free(addr_string); - return -1; - } - if (!explicit_ip) { - /* even if they've set their own authorities, require an explicit IP if - * they're using an internal address. */ - log_fn(warn_severity, LD_CONFIG, "Address '%s' resolves to private " - "IP address '%s'. Please set the Address config option to be " - "the IP address you want to use.", hostname, addr_string); - tor_free(addr_string); - return -1; - } + /* Great, we found an address. */ + ret = address_can_be_used(addr_out, options, warn_severity, explicit_ip); + if (ret != 0) { + /* One of the requirement of this interface is if an internal Address is + * used, custom authorities must be defined else it is a fatal error. + * Furthermore, if the Address was resolved to an internal interface, we + * stop immediately. */ + tor_free(*hostname_out); + return FN_RET_BAIL; } - /* - * Step four: We have a winner! 'addr' is our answer for sure, and - * 'addr_string' is its string form. Fill out the various fields to - * say how we decided it. - */ + /* Address can be used. We are done. */ + log_fn(warn_severity, LD_CONFIG, "Address found in configuration: %s", + fmt_addr(addr_out)); + return FN_RET_OK; +} - log_debug(LD_CONFIG, "Resolved Address to '%s'.", addr_string); - - if (explicit_ip) { - method_used = "CONFIGURED"; - hostname_used = NULL; - } else if (explicit_hostname) { - method_used = "RESOLVED"; - hostname_used = hostname; - } else if (from_interface) { - method_used = "INTERFACE"; - hostname_used = NULL; - } else { - method_used = "GETHOSTNAME"; - hostname_used = hostname; +/** @brief Get IP address from the local hostname by calling gethostbyname() + * and doing a DNS resolution on the hostname. + * + * @param options Global configuration options. + * @param warn_severity Log level that should be used on error. + * @param family IP address family. Only AF_INET and AF_INET6 are supported. + * @param method_out OUT: String denoting by which method the address was + * found. This is described in the control-spec.txt as + * actions for "STATUS_SERVER". + * @param hostname_out OUT: String containing the local hostname. + * @param addr_out OUT: Tor address resolved from the local hostname. + * + * @return Return 0 on success that is an address has been found and resolved + * successfully. Return error code ERR_* found at the top of the file. + */ +static fn_address_ret_t +get_address_from_hostname(const or_options_t *options, int warn_severity, + int family, const char **method_out, + char **hostname_out, tor_addr_t *addr_out) +{ + int ret; + char hostname[256]; + + tor_assert(addr_out); + tor_assert(method_out); + + /* Set them to NULL for safety reasons. */ + *hostname_out = NULL; + *method_out = NULL; + + log_debug(LD_CONFIG, "Attempting to get address from local hostname"); + + if (tor_gethostname(hostname, sizeof(hostname)) < 0) { + log_fn(warn_severity, LD_NET, "Error obtaining local hostname"); + /* Unable to obtain the local hostname is a fatal error. */ + return FN_RET_BAIL; + } + if (tor_addr_lookup(hostname, family, addr_out)) { + log_fn(warn_severity, LD_NET, + "Could not resolve local hostname '%s'. Failing.", hostname); + /* Unable to resolve, inform caller to try next method. */ + return FN_RET_NEXT; } - *addr_out = addr; - if (method_out) - *method_out = method_used; - if (hostname_out) - *hostname_out = hostname_used ? tor_strdup(hostname_used) : NULL; + ret = address_can_be_used(addr_out, options, warn_severity, false); + if (ret == ERR_DEFAULT_DIRAUTH) { + /* Non custom authorities, inform caller to try next method. */ + return FN_RET_NEXT; + } else if (ret == ERR_ADDRESS_IS_INTERNAL) { + /* Internal address is a fatal error. */ + return FN_RET_BAIL; + } - /* - * Step five: Check if the answer has changed since last time (or if - * there was no last time), and if so call various functions to keep - * us up-to-date. - */ + /* addr_out contains the address of the local hostname. */ + *method_out = "GETHOSTNAME"; + *hostname_out = tor_strdup(hostname); + + /* Found it! */ + log_fn(warn_severity, LD_CONFIG, "Address found from local hostname: %s", + fmt_addr(addr_out)); + return FN_RET_OK; +} + +/** @brief Get IP address from a network interface. + * + * @param options Global configuration options. + * @param warn_severity Log level that should be used on error. + * @param family IP address family. Only AF_INET and AF_INET6 are supported. + * @param method_out OUT: Always "INTERFACE" on success which is detailed in + * the control-spec.txt as actions for "STATUS_SERVER". + * @param hostname_out OUT: String containing the local hostname. For this + * function, it is always set to NULL. + * @param addr_out OUT: Tor address found attached to the interface. + * + * @return Return 0 on success that is an address has been found. Return + * error code ERR_* found at the top of the file. + */ +static fn_address_ret_t +get_address_from_interface(const or_options_t *options, int warn_severity, + int family, const char **method_out, + char **hostname_out, tor_addr_t *addr_out) +{ + int ret; + + tor_assert(method_out); + tor_assert(hostname_out); + tor_assert(addr_out); + + /* Set them to NULL for safety reasons. */ + *method_out = NULL; + *hostname_out = NULL; + + log_debug(LD_CONFIG, "Attempting to get address from network interface"); + + if (get_interface_address6(warn_severity, family, addr_out) < 0) { + log_fn(warn_severity, LD_CONFIG, + "Could not get local interface IP address."); + /* Unable to get IP from interface. Inform caller to try next method. */ + return FN_RET_NEXT; + } + + ret = address_can_be_used(addr_out, options, warn_severity, false); + if (ret < 0) { + /* Unable to use address. Inform caller to try next method. */ + return FN_RET_NEXT; + } - if (last_resolved_addr && last_resolved_addr != *addr_out) { + *method_out = "INTERFACE"; + + /* Found it! */ + log_fn(warn_severity, LD_CONFIG, "Address found from interface: %s", + fmt_addr(addr_out)); + return FN_RET_OK; +} + +/** @brief Get IP address from the ORPort (if any). + * + * @param options Global configuration options. + * @param warn_severity Log level that should be used on error. + * @param family IP address family. Only AF_INET and AF_INET6 are supported. + * @param method_out OUT: Always "CONFIGURED_ORPORT" on success which is + * detailed in the control-spec.txt as actions + * for "STATUS_SERVER". + * @param hostname_out OUT: String containing the ORPort hostname if any. + * @param addr_out OUT: Tor address found if any. + * + * @return Return 0 on success that is an address has been found. Return + * error code ERR_* found at the top of the file. + */ +static fn_address_ret_t +get_address_from_orport(const or_options_t *options, int warn_severity, + int family, const char **method_out, + char **hostname_out, tor_addr_t *addr_out) +{ + int ret; + const tor_addr_t *addr; + + tor_assert(method_out); + tor_assert(hostname_out); + tor_assert(addr_out); + + log_debug(LD_CONFIG, "Attempting to get address from ORPort"); + + if (!options->ORPort_set) { + log_info(LD_CONFIG, "No ORPort found in configuration."); + /* No ORPort statement, inform caller to try next method. */ + return FN_RET_NEXT; + } + + /* Get ORPort for requested family. */ + addr = get_orport_addr(family); + if (!addr) { + /* No address configured for the ORPort. Ignore. */ + return FN_RET_NEXT; + } + + /* We found the ORPort address. Just make sure it can be used. */ + ret = address_can_be_used(addr, options, warn_severity, true); + if (ret < 0) { + /* Unable to use address. Inform caller to try next method. */ + return FN_RET_NEXT; + } + + /* Found it! */ + *method_out = "CONFIGURED_ORPORT"; + tor_addr_copy(addr_out, addr); + + log_fn(warn_severity, LD_CONFIG, "Address found from ORPort: %s", + fmt_addr(addr_out)); + return FN_RET_OK; +} + +/** @brief Update the last resolved address cache using the given address. + * + * A log notice is emitted if the given address has changed from before. Not + * emitted on first resolve. + * + * Control port event "STATUS_SERVER" is emitted with the new information if + * it has changed. + * + * Finally, tor is notified that the IP address has changed. + * + * @param addr IP address to update the cache with. + * @param method_used By which method did we resolved it (for logging and + * control port). + * @param hostname_used Which hostname was used. If none were used, it is + * NULL. (for logging and control port). + */ +static void +update_resolved_cache(const tor_addr_t *addr, const char *method_used, + const char *hostname_used) +{ + /** Have we done a first resolve. This is used to control logging. */ + static bool have_resolved_once[IDX_SIZE] = { false, false, false }; + bool *done_one_resolve; + bool have_hostname = false; + tor_addr_t *last_resolved; + + tor_assert(addr); + tor_assert(method_used); + + /* Do we have an hostname. */ + have_hostname = (hostname_used != NULL); + + int idx = af_to_idx(tor_addr_family(addr)); + if (idx == IDX_NULL) { + /* Not suppose to happen and if it does, af_to_idx() screams loudly. */ + return; + } + + /* Get values from cache. */ + done_one_resolve = &have_resolved_once[idx]; + last_resolved = &last_resolved_addrs[idx]; + + /* Same address last resolved. Ignore. */ + if (tor_addr_eq(last_resolved, addr)) { + return; + } + + /* Don't log notice if this is the first resolve we do. */ + if (*done_one_resolve) { /* Leave this as a notice, regardless of the requested severity, * at least until dynamic IP address support becomes bulletproof. */ log_notice(LD_NET, "Your IP address seems to have changed to %s " "(METHOD=%s%s%s). Updating.", - addr_string, method_used, - hostname_used ? " HOSTNAME=" : "", - hostname_used ? hostname_used : ""); + fmt_addr(addr), method_used, + have_hostname ? " HOSTNAME=" : "", + have_hostname ? hostname_used : ""); ip_address_changed(0); } - if (last_resolved_addr != *addr_out) { - control_event_server_status(LOG_NOTICE, - "EXTERNAL_ADDRESS ADDRESS=%s METHOD=%s%s%s", - addr_string, method_used, - hostname_used ? " HOSTNAME=" : "", - hostname_used ? hostname_used : ""); + /* Notify control port. */ + control_event_server_status(LOG_NOTICE, + "EXTERNAL_ADDRESS ADDRESS=%s METHOD=%s%s%s", + fmt_addr(addr), method_used, + have_hostname ? " HOSTNAME=" : "", + have_hostname ? hostname_used : ""); + /* Copy address to cache. */ + tor_addr_copy(last_resolved, addr); + *done_one_resolve = true; +} + +/** Address discovery function table. The order matters as in the first one is + * executed first and so on. */ +static fn_address_ret_t + (*fn_address_table[])( + const or_options_t *options, int warn_severity, int family, + const char **method_out, char **hostname_out, tor_addr_t *addr_out) = +{ + /* These functions are in order for our find address algorithm. */ + get_address_from_config, + get_address_from_orport, + get_address_from_interface, + get_address_from_hostname, +}; +/** Length of address table as in how many functions. */ +static const size_t fn_address_table_len = ARRAY_LENGTH(fn_address_table); + +/** @brief Attempt to find our IP address that can be used as our external + * reachable address. + * + * The following describe the algorithm to find an address. Each have + * specific conditions so read carefully. + * + * On success, true is returned and depending on how the address was found, + * the out parameters can have different values. + * + * On error, false is returned and out parameters are set to NULL. + * + * 1. Look at the configuration Address option. + + * If Address is a public address, True is returned and addr_out is set + * with it, the method_out is set to "CONFIGURED" and hostname_out is set + * to NULL. + * + * If Address is an internal address but NO custom authorities are used, + * an error is returned. + * + * If Address is a hostname, that is it can't be converted to an address, + * it is resolved. On success, addr_out is set with the address, + * method_out is set to "RESOLVED" and hostname_out is set to the resolved + * hostname. On failure to resolve, an error is returned. + * + * If no given Address, fallback to the local hostname (see section 2). + * + * 2. Look at the network interface. + * + * Attempt to find the first public usable address from the list of + * network interface returned by the OS. + * + * On failure, we attempt to look at the local hostname (3). + * + * On success, addr_out is set with it, method_out is set to "INTERFACE" + * and hostname_out is set to NULL. + * + * 3. Look at the local hostname. + * + * If the local hostname resolves to a non internal address, addr_out is + * set with it, method_out is set to "GETHOSTNAME" and hostname_out is set + * to the resolved hostname. + * + * If a local hostname can NOT be found, an error is returned. + * + * If the local hostname resolves to an internal address, an error is + * returned. + * + * If the local hostname can NOT be resolved, an error is returned. + * + * @param options Global configuration options. + * @param family IP address family. Only AF_INET and AF_INET6 are supported. + * @param warn_severity Logging level. + * @param addr_out OUT: Set with the IP address found if any. + * @param method_out OUT: (optional) String denoting by which method the + * address was found. This is described in the + * control-spec.txt as actions for "STATUS_SERVER". + * @param hostname_out OUT: String containing the hostname if any was used. + * Only be set for "RESOLVED" and "GETHOSTNAME" methods. + * Else it is set to NULL. + * + * @return True if the address was found for the given family. False if not or + * on errors. + */ +bool +find_my_address(const or_options_t *options, int family, int warn_severity, + tor_addr_t *addr_out, const char **method_out, + char **hostname_out) +{ + const char *method_used = NULL; + char *hostname_used = NULL; + tor_addr_t my_addr; + + tor_assert(options); + tor_assert(addr_out); + + /* Set them to NULL for safety reasons. */ + if (method_out) *method_out = NULL; + if (hostname_out) *hostname_out = NULL; + + /* + * Step 1: Discover address by attempting 3 different methods consecutively. + */ + + /* Go over the function table. They are in order. */ + for (size_t idx = 0; idx < fn_address_table_len; idx++) { + fn_address_ret_t ret = fn_address_table[idx](options, warn_severity, + family, &method_used, + &hostname_used, &my_addr); + if (ret == FN_RET_BAIL) { + return false; + } else if (ret == FN_RET_OK) { + goto found; + } + tor_assert(ret == FN_RET_NEXT); } - last_resolved_addr = *addr_out; + /* We've exhausted our attempts. Failure. */ + log_fn(warn_severity, LD_CONFIG, "Unable to find our IP address."); + return false; + + found: /* - * And finally, clean up and return success. + * Step 2: Update last resolved address cache and inform the control port. */ + update_resolved_cache(&my_addr, method_used, hostname_used); - tor_free(addr_string); - return 0; + if (method_out) { + *method_out = method_used; + } + if (hostname_out) { + *hostname_out = hostname_used; + } else { + tor_free(hostname_used); + } + + tor_addr_copy(addr_out, &my_addr); + return true; } -/** Return true iff <b>addr</b> is judged to be on the same network as us, or - * on a private network. +/** @brief: Return true iff the given addr is judged to be local to our + * resolved address. + * + * This function is used to tell whether another address is 'remote' enough + * that we can trust it when it tells us that we are reachable, or that we + * have a certain address. + * + * The criterion to learn if the address is local are the following: + * + * 1. Internal address. + * 2. If EnforceDistinctSubnets is set then it is never local. + * 3. Network mask is compared. IPv4: /24 and IPv6 /48. This is different + * from the path selection that looks at /16 and /32 because we only + * want to learn here if the address is considered to come from the + * Internet basically. + * + * @param addr The address to test if local and also test against our resovled + * address. + * + * @return True iff address is considered local or else False. */ -MOCK_IMPL(int, -is_local_addr, (const tor_addr_t *addr)) +MOCK_IMPL(bool, +is_local_to_resolve_addr, (const tor_addr_t *addr)) { - if (tor_addr_is_internal(addr, 0)) - return 1; - /* Check whether ip is on the same /24 as we are. */ - if (get_options()->EnforceDistinctSubnets == 0) - return 0; - if (tor_addr_family(addr) == AF_INET) { - uint32_t ip = tor_addr_to_ipv4h(addr); + const int family = tor_addr_family(addr); + const tor_addr_t *last_resolved_addr = &last_resolved_addrs[family]; + /* Internal address is always local. */ + if (tor_addr_is_internal(addr, 0)) { + return true; + } + + /* Address is not local if we don't enforce subnet distinction. */ + if (get_options()->EnforceDistinctSubnets == 0) { + return false; + } + + switch (family) { + case AF_INET: /* It's possible that this next check will hit before the first time - * resolve_my_address actually succeeds. (For clients, it is likely that - * resolve_my_address will never be called at all). In those cases, - * last_resolved_addr will be 0, and so checking to see whether ip is on - * the same /24 as last_resolved_addr will be the same as checking whether - * it was on net 0, which is already done by tor_addr_is_internal. - */ - if ((last_resolved_addr & (uint32_t)0xffffff00ul) - == (ip & (uint32_t)0xffffff00ul)) - return 1; + * find_my_address actually succeeds. For clients, it is likely that + * find_my_address will never be called at all. In those cases, + * last_resolved_addr_v4 will be 0, and so checking to see whether ip is + * on the same /24 as last_resolved_addrs[AF_INET] will be the same as + * checking whether it was on net 0, which is already done by + * tor_addr_is_internal. */ + return tor_addr_compare_masked(addr, last_resolved_addr, 24, + CMP_SEMANTIC) == 0; + case AF_INET6: + /* Look at /48 because it is typically the smallest network in the global + * IPv6 routing tables, and it was previously the recommended per-customer + * network block. (See [RFC 6177: IPv6 End Site Address Assignment].) */ + return tor_addr_compare_masked(addr, last_resolved_addr, 48, + CMP_SEMANTIC) == 0; + break; + default: + /* Unknown address type so not local. */ + return false; } - return 0; } diff --git a/src/app/config/resolve_addr.h b/src/app/config/resolve_addr.h index 3747546402..e6f8a72554 100644 --- a/src/app/config/resolve_addr.h +++ b/src/app/config/resolve_addr.h @@ -9,16 +9,22 @@ #ifndef TOR_CONFIG_RESOLVE_ADDR_H #define TOR_CONFIG_RESOLVE_ADDR_H +#include "app/config/config.h" +#include "core/mainloop/connection.h" + #include "app/config/or_options_st.h" -int resolve_my_address(int warn_severity, const or_options_t *options, - uint32_t *addr_out, - const char **method_out, char **hostname_out); +#define get_orport_addr(family) \ + (get_first_advertised_addr_by_type_af(CONN_TYPE_OR_LISTENER, family)) + +bool find_my_address(const or_options_t *options, int family, + int warn_severity, tor_addr_t *addr_out, + const char **method_out, char **hostname_out); -uint32_t get_last_resolved_addr(void); -void reset_last_resolved_addr(void); +void resolved_addr_get_last(int family, tor_addr_t *addr_out); +void resolved_addr_reset_last(int family); -MOCK_DECL(int, is_local_addr, (const tor_addr_t *addr)); +MOCK_DECL(bool, is_local_to_resolve_addr, (const tor_addr_t *addr)); #ifdef RESOLVE_ADDR_PRIVATE diff --git a/src/app/config/testnet.inc b/src/app/config/testnet.inc index 907c35f97c..605943d80b 100644 --- a/src/app/config/testnet.inc +++ b/src/app/config/testnet.inc @@ -2,7 +2,6 @@ // for 'TestingTorNetwork' in 'doc/tor.1.txt' { "DirAllowPrivateAddresses", "1" }, { "EnforceDistinctSubnets", "0" }, -{ "AssumeReachable", "1" }, { "AuthDirMaxServersPerAddr", "0" }, { "ClientBootstrapConsensusAuthorityDownloadInitialDelay", "0" }, { "ClientBootstrapConsensusFallbackDownloadInitialDelay", "0" }, diff --git a/src/app/main/main.c b/src/app/main/main.c index dc39611f98..89ba787422 100644 --- a/src/app/main/main.c +++ b/src/app/main/main.c @@ -775,12 +775,14 @@ do_dump_config(void) if (!strcmp(arg, "short")) { how = OPTIONS_DUMP_MINIMAL; } else if (!strcmp(arg, "non-builtin")) { - how = OPTIONS_DUMP_DEFAULTS; + // Deprecated since 0.4.5.1-alpha. + fprintf(stderr, "'non-builtin' is deprecated; use 'short' instead.\n"); + how = OPTIONS_DUMP_MINIMAL; } else if (!strcmp(arg, "full")) { how = OPTIONS_DUMP_ALL; } else { fprintf(stderr, "No valid argument to --dump-config found!\n"); - fprintf(stderr, "Please select 'short', 'non-builtin', or 'full'.\n"); + fprintf(stderr, "Please select 'short' or 'full'.\n"); return -1; } @@ -795,8 +797,7 @@ do_dump_config(void) static void init_addrinfo(void) { - if (! server_mode(get_options()) || - (get_options()->Address && strlen(get_options()->Address) > 0)) { + if (! server_mode(get_options()) || get_options()->Address) { /* We don't need to seed our own hostname, because we won't be calling * resolve_my_address on it. */ @@ -1054,12 +1055,14 @@ sandbox_init_filter(void) OPEN_DATADIR("approved-routers"); OPEN_DATADIR_SUFFIX("fingerprint", ".tmp"); + OPEN_DATADIR_SUFFIX("fingerprint-ed25519", ".tmp"); OPEN_DATADIR_SUFFIX("hashed-fingerprint", ".tmp"); OPEN_DATADIR_SUFFIX("router-stability", ".tmp"); OPEN("/etc/resolv.conf"); RENAME_SUFFIX("fingerprint", ".tmp"); + RENAME_SUFFIX("fingerprint-ed25519", ".tmp"); RENAME_KEYDIR_SUFFIX("secret_onion_key_ntor", ".tmp"); RENAME_KEYDIR_SUFFIX("secret_id_key", ".tmp"); diff --git a/src/core/crypto/onion_crypto.c b/src/core/crypto/onion_crypto.c index 69b4dc40aa..1f34be1cc1 100644 --- a/src/core/crypto/onion_crypto.c +++ b/src/core/crypto/onion_crypto.c @@ -31,7 +31,7 @@ **/ #include "core/or/or.h" -#include "core/or/circuitbuild.h" +#include "core/or/extendinfo.h" #include "core/crypto/onion_crypto.h" #include "core/crypto/onion_fast.h" #include "core/crypto/onion_ntor.h" diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c index 792cdbf686..af823335a8 100644 --- a/src/core/mainloop/connection.c +++ b/src/core/mainloop/connection.c @@ -4871,7 +4871,7 @@ client_check_address_changed(tor_socket_t sock) smartlist_clear(outgoing_addrs); smartlist_add(outgoing_addrs, tor_memdup(&out_addr, sizeof(tor_addr_t))); /* We'll need to resolve ourselves again. */ - reset_last_resolved_addr(); + resolved_addr_reset_last(AF_INET); /* Okay, now change our keys. */ ip_address_changed(1); } diff --git a/src/core/or/channelpadding.c b/src/core/or/channelpadding.c index be2ce78a17..c754a58c42 100644 --- a/src/core/or/channelpadding.c +++ b/src/core/or/channelpadding.c @@ -90,7 +90,7 @@ static int consensus_nf_pad_single_onion; * for every single connection, every second. */ void -channelpadding_new_consensus_params(networkstatus_t *ns) +channelpadding_new_consensus_params(const networkstatus_t *ns) { #define DFLT_NETFLOW_INACTIVE_KEEPALIVE_LOW 1500 #define DFLT_NETFLOW_INACTIVE_KEEPALIVE_HIGH 9500 diff --git a/src/core/or/channelpadding.h b/src/core/or/channelpadding.h index d1c7192ffd..9246988cdc 100644 --- a/src/core/or/channelpadding.h +++ b/src/core/or/channelpadding.h @@ -37,7 +37,6 @@ int channelpadding_send_enable_command(channel_t *chan, uint16_t low_timeout, int channelpadding_get_circuits_available_timeout(void); unsigned int channelpadding_get_channel_idle_timeout(const channel_t *, int); -void channelpadding_new_consensus_params(networkstatus_t *ns); +void channelpadding_new_consensus_params(const networkstatus_t *ns); #endif /* !defined(TOR_CHANNELPADDING_H) */ - diff --git a/src/core/or/channeltls.c b/src/core/or/channeltls.c index 395fbf3455..a51fbf1dd6 100644 --- a/src/core/or/channeltls.c +++ b/src/core/or/channeltls.c @@ -63,10 +63,10 @@ #include "trunnel/channelpadding_negotiation.h" #include "trunnel/netinfo.h" #include "core/or/channelpadding.h" +#include "core/or/extendinfo.h" #include "core/or/cell_st.h" #include "core/or/cell_queue_st.h" -#include "core/or/extend_info_st.h" #include "core/or/or_connection_st.h" #include "core/or/or_handshake_certs_st.h" #include "core/or/or_handshake_state_st.h" @@ -203,7 +203,7 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port, tlschan, (chan->global_identifier)); - if (is_local_addr(addr)) { + if (is_local_to_resolve_addr(addr)) { log_debug(LD_CHANNEL, "Marking new outgoing channel %"PRIu64 " at %p as local", (chan->global_identifier), chan); @@ -340,7 +340,7 @@ channel_tls_handle_incoming(or_connection_t *orconn) tlschan->conn = orconn; orconn->chan = tlschan; - if (is_local_addr(&(TO_CONN(orconn)->addr))) { + if (is_local_to_resolve_addr(&(TO_CONN(orconn)->addr))) { log_debug(LD_CHANNEL, "Marking new incoming channel %"PRIu64 " at %p as local", (chan->global_identifier), chan); @@ -702,9 +702,9 @@ channel_tls_matches_extend_info_method(channel_t *chan, return 0; } - return (tor_addr_eq(&(extend_info->addr), - &(TO_CONN(tlschan->conn)->addr)) && - (extend_info->port == TO_CONN(tlschan->conn)->port)); + return extend_info_has_orport(extend_info, + &TO_CONN(tlschan->conn)->addr, + TO_CONN(tlschan->conn)->port); } /** @@ -1353,7 +1353,7 @@ channel_tls_update_marks(or_connection_t *conn) chan = TLS_CHAN_TO_BASE(conn->chan); - if (is_local_addr(&(TO_CONN(conn)->addr))) { + if (is_local_to_resolve_addr(&(TO_CONN(conn)->addr))) { if (!channel_is_local(chan)) { log_debug(LD_CHANNEL, "Marking channel %"PRIu64 " at %p as local", diff --git a/src/core/or/circuit_st.h b/src/core/or/circuit_st.h index 4baafb1848..35d214ce08 100644 --- a/src/core/or/circuit_st.h +++ b/src/core/or/circuit_st.h @@ -238,6 +238,12 @@ struct circuit_t { * Each element of this array corresponds to a different padding machine, * and we can have up to CIRCPAD_MAX_MACHINES such machines. */ struct circpad_machine_runtime_t *padding_info[CIRCPAD_MAX_MACHINES]; + + /** padding_machine_ctr increments each time a new padding machine + * is negotiated. It is used for shutdown conditions, to ensure + * that STOP commands actually correspond to the current machine, + * and not a previous one. */ + uint32_t padding_machine_ctr; }; #endif /* !defined(CIRCUIT_ST_H) */ diff --git a/src/core/or/circuitbuild.c b/src/core/or/circuitbuild.c index 83ce9f882b..cef70e3e76 100644 --- a/src/core/or/circuitbuild.c +++ b/src/core/or/circuitbuild.c @@ -45,6 +45,7 @@ #include "core/or/command.h" #include "core/or/connection_edge.h" #include "core/or/connection_or.h" +#include "core/or/extendinfo.h" #include "core/or/onion.h" #include "core/or/ocirc_event.h" #include "core/or/policies.h" @@ -78,9 +79,6 @@ #include "feature/nodelist/node_st.h" #include "core/or/or_circuit_st.h" #include "core/or/origin_circuit_st.h" -#include "feature/nodelist/microdesc_st.h" -#include "feature/nodelist/routerinfo_st.h" -#include "feature/nodelist/routerstatus_st.h" static int circuit_send_first_onion_skin(origin_circuit_t *circ); static int circuit_build_no_more_hops(origin_circuit_t *circ); @@ -96,13 +94,17 @@ static const node_t *choose_good_middle_server(uint8_t purpose, * callbacks. */ MOCK_IMPL(channel_t *, -channel_connect_for_circuit,(const tor_addr_t *addr, uint16_t port, - const char *id_digest, - const struct ed25519_public_key_t *ed_id)) +channel_connect_for_circuit,(const extend_info_t *ei)) { channel_t *chan; - chan = channel_connect(addr, port, id_digest, ed_id); + const tor_addr_port_t *orport = extend_info_pick_orport(ei); + if (!orport) + return NULL; + const char *id_digest = ei->identity_digest; + const ed25519_public_key_t *ed_id = &ei->ed_identity; + + chan = channel_connect(&orport->addr, orport->port, id_digest, ed_id); if (chan) command_setup_channel(chan); return chan; @@ -439,7 +441,8 @@ onion_populate_cpath(origin_circuit_t *circ) /** Create and return a new origin circuit. Initialize its purpose and * build-state based on our arguments. The <b>flags</b> argument is a - * bitfield of CIRCLAUNCH_* flags. */ + * bitfield of CIRCLAUNCH_* flags, see circuit_launch_by_extend_info() for + * more details. */ origin_circuit_t * origin_circuit_init(uint8_t purpose, int flags) { @@ -455,13 +458,16 @@ origin_circuit_init(uint8_t purpose, int flags) ((flags & CIRCLAUNCH_NEED_CAPACITY) ? 1 : 0); circ->build_state->is_internal = ((flags & CIRCLAUNCH_IS_INTERNAL) ? 1 : 0); + circ->build_state->is_ipv6_selftest = + ((flags & CIRCLAUNCH_IS_IPV6_SELFTEST) ? 1 : 0); circ->base_.purpose = purpose; return circ; } -/** Build a new circuit for <b>purpose</b>. If <b>exit</b> - * is defined, then use that as your exit router, else choose a suitable - * exit node. +/** Build a new circuit for <b>purpose</b>. If <b>exit</b> is defined, then use + * that as your exit router, else choose a suitable exit node. The <b>flags</b> + * argument is a bitfield of CIRCLAUNCH_* flags, see + * circuit_launch_by_extend_info() for more details. * * Also launch a connection to the first OR in the chosen path, if * it's not open already. @@ -546,7 +552,7 @@ circuit_handle_first_hop(origin_circuit_t *circ) * - the address is internal, and * - we're not connecting to a configured bridge, and * - we're not configured to allow extends to private addresses. */ - if (tor_addr_is_internal(&firsthop->extend_info->addr, 0) && + if (extend_info_any_orport_addr_is_internal(firsthop->extend_info) && !extend_info_is_a_configured_bridge(firsthop->extend_info) && !options->ExtendAllowPrivateAddresses) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, @@ -555,19 +561,15 @@ circuit_handle_first_hop(origin_circuit_t *circ) } /* 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, - firsthop->extend_info->port)); - - /* We'll cleanup this code in #33220, when we add an IPv6 address to - * extend_info_t. */ - const bool addr_is_ipv4 = - (tor_addr_family(&firsthop->extend_info->addr) == AF_INET); + const tor_addr_port_t *orport4 = + extend_info_get_orport(firsthop->extend_info, AF_INET); + const tor_addr_port_t *orport6 = + extend_info_get_orport(firsthop->extend_info, AF_INET6); n_chan = channel_get_for_extend( firsthop->extend_info->identity_digest, &firsthop->extend_info->ed_identity, - addr_is_ipv4 ? &firsthop->extend_info->addr : NULL, - addr_is_ipv4 ? NULL : &firsthop->extend_info->addr, + orport4 ? &orport4->addr : NULL, + orport6 ? &orport6->addr : NULL, &msg, &should_launch); @@ -579,11 +581,7 @@ circuit_handle_first_hop(origin_circuit_t *circ) circ->base_.n_hop = extend_info_dup(firsthop->extend_info); if (should_launch) { - n_chan = channel_connect_for_circuit( - &firsthop->extend_info->addr, - firsthop->extend_info->port, - firsthop->extend_info->identity_digest, - &firsthop->extend_info->ed_identity); + n_chan = channel_connect_for_circuit(firsthop->extend_info); if (!n_chan) { /* connect failed, forget the whole thing */ log_info(LD_CIRC,"connect to firsthop failed. Closing."); return -END_CIRC_REASON_CONNECTFAILED; @@ -601,7 +599,8 @@ circuit_handle_first_hop(origin_circuit_t *circ) tor_assert(!circ->base_.n_hop); circ->base_.n_chan = n_chan; circuit_chan_publish(circ, n_chan); - log_debug(LD_CIRC,"Conn open. Delivering first onion skin."); + log_debug(LD_CIRC,"Conn open for %s. Delivering first onion skin.", + safe_str_client(extend_info_describe(firsthop->extend_info))); if ((err_reason = circuit_send_next_onion_skin(circ)) < 0) { log_info(LD_CIRC,"circuit_send_next_onion_skin failed."); circ->base_.n_chan = NULL; @@ -1050,8 +1049,8 @@ circuit_build_no_more_hops(origin_circuit_t *circ) control_event_bootstrap(BOOTSTRAP_STATUS_DONE, 0); control_event_client_status(LOG_NOTICE, "CIRCUIT_ESTABLISHED"); clear_broken_connection_map(1); - if (server_mode(options) && !check_whether_orport_reachable(options)) { - inform_testing_reachability(); + if (server_mode(options) && + !router_all_orports_seem_reachable(options)) { router_do_reachability_checks(1, 1); } } @@ -1075,23 +1074,40 @@ circuit_send_intermediate_onion_skin(origin_circuit_t *circ, { int len; extend_cell_t ec; + /* Relays and bridges can send IPv6 extends. But for clients, it's an + * obvious version distinguisher. */ + const bool include_ipv6 = server_mode(get_options()); memset(&ec, 0, sizeof(ec)); + tor_addr_make_unspec(&ec.orport_ipv4.addr); + tor_addr_make_unspec(&ec.orport_ipv6.addr); log_debug(LD_CIRC,"starting to send subsequent skin."); - if (tor_addr_family(&hop->extend_info->addr) != AF_INET) { - log_warn(LD_BUG, "Trying to extend to a non-IPv4 address."); - return - END_CIRC_REASON_INTERNAL; - } - circuit_pick_extend_handshake(&ec.cell_type, &ec.create_cell.cell_type, &ec.create_cell.handshake_type, hop->extend_info); - tor_addr_copy(&ec.orport_ipv4.addr, &hop->extend_info->addr); - ec.orport_ipv4.port = hop->extend_info->port; - tor_addr_make_unspec(&ec.orport_ipv6.addr); + const tor_addr_port_t *orport4 = + extend_info_get_orport(hop->extend_info, AF_INET); + const tor_addr_port_t *orport6 = + extend_info_get_orport(hop->extend_info, AF_INET6); + int n_addrs_set = 0; + if (orport4) { + tor_addr_copy(&ec.orport_ipv4.addr, &orport4->addr); + ec.orport_ipv4.port = orport4->port; + ++n_addrs_set; + } + if (orport6 && include_ipv6) { + tor_addr_copy(&ec.orport_ipv6.addr, &orport6->addr); + ec.orport_ipv6.port = orport6->port; + ++n_addrs_set; + } + + if (n_addrs_set == 0) { + log_warn(LD_BUG, "No supported address family found in extend_info."); + return - END_CIRC_REASON_INTERNAL; + } memcpy(ec.node_id, hop->extend_info->identity_digest, DIGEST_LEN); /* Set the ED25519 identity too -- it will only get included * in the extend2 cell if we're configured to use it, though. */ @@ -1539,7 +1555,23 @@ choose_good_exit_server_general(router_crn_flags_t flags) const node_t *selected_node=NULL; const int need_uptime = (flags & CRN_NEED_UPTIME) != 0; const int need_capacity = (flags & CRN_NEED_CAPACITY) != 0; - const int direct_conn = (flags & CRN_DIRECT_CONN) != 0; + + /* We should not require guard flags on exits. */ + IF_BUG_ONCE(flags & CRN_NEED_GUARD) + return NULL; + + /* We reject single-hop exits for all node positions. */ + IF_BUG_ONCE(flags & CRN_DIRECT_CONN) + return NULL; + + /* This isn't the function for picking rendezvous nodes. */ + IF_BUG_ONCE(flags & CRN_RENDEZVOUS_V3) + return NULL; + + /* We only want exits to extend if we cannibalize the circuit. + * But we don't require IPv6 extends yet. */ + IF_BUG_ONCE(flags & CRN_INITIATE_IPV6_EXTEND) + return NULL; connections = get_connection_array(); @@ -1572,19 +1604,14 @@ choose_good_exit_server_general(router_crn_flags_t flags) */ continue; } - if (!node_has_preferred_descriptor(node, direct_conn)) { + if (!router_can_choose_node(node, flags)) { n_supported[i] = -1; continue; } - if (!node->is_running || node->is_bad_exit) { + if (node->is_bad_exit) { n_supported[i] = -1; continue; /* skip routers that are known to be down or bad exits */ } - if (node_get_purpose(node) != ROUTER_PURPOSE_GENERAL) { - /* never pick a non-general node as a random exit. */ - n_supported[i] = -1; - continue; - } if (routerset_contains_node(options->ExcludeExitNodesUnion_, node)) { n_supported[i] = -1; continue; /* user asked us not to use it, no matter what */ @@ -1594,27 +1621,6 @@ choose_good_exit_server_general(router_crn_flags_t flags) n_supported[i] = -1; continue; /* not one of our chosen exit nodes */ } - - if (node_is_unreliable(node, need_uptime, need_capacity, 0)) { - n_supported[i] = -1; - continue; /* skip routers that are not suitable. Don't worry if - * this makes us reject all the possible routers: if so, - * we'll retry later in this function with need_update and - * need_capacity set to 0. */ - } - if (!(node->is_valid)) { - /* if it's invalid and we don't want it */ - n_supported[i] = -1; -// log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- invalid router.", -// router->nickname, i); - continue; /* skip invalid routers */ - } - /* We do not allow relays that allow single hop exits by default. Option - * was deprecated in 0.2.9.2-alpha and removed in 0.3.1.0-alpha. */ - if (node_allows_single_hop_exits(node)) { - n_supported[i] = -1; - continue; - } if (node_exit_policy_rejects_all(node)) { n_supported[i] = -1; // log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- it rejects all.", @@ -1771,13 +1777,7 @@ pick_restricted_middle_node(router_crn_flags_t flags, tor_assert(pick_from); /* Add all running nodes to all_live_nodes */ - router_add_running_nodes_to_smartlist(all_live_nodes, - (flags & CRN_NEED_UPTIME) != 0, - (flags & CRN_NEED_CAPACITY) != 0, - (flags & CRN_NEED_GUARD) != 0, - (flags & CRN_NEED_DESC) != 0, - (flags & CRN_PREF_ADDR) != 0, - (flags & CRN_DIRECT_CONN) != 0); + router_add_running_nodes_to_smartlist(all_live_nodes, flags); /* Filter all_live_nodes to only add live *and* whitelisted middles * to the list whitelisted_live_middles. */ @@ -1957,6 +1957,43 @@ warn_if_last_router_excluded(origin_circuit_t *circ, return; } +/* Return a set of generic CRN_* flags based on <b>state</b>. + * + * Called for every position in the circuit. */ +STATIC int +cpath_build_state_to_crn_flags(const cpath_build_state_t *state) +{ + router_crn_flags_t flags = 0; + /* These flags apply to entry, middle, and exit nodes. + * If a flag only applies to a specific position, it should be checked in + * that function. */ + if (state->need_uptime) + flags |= CRN_NEED_UPTIME; + if (state->need_capacity) + flags |= CRN_NEED_CAPACITY; + return flags; +} + +/* Return the CRN_INITIATE_IPV6_EXTEND flag, based on <b>state</b> and + * <b>cur_len</b>. + * + * Only called for middle nodes (for now). Must not be called on single-hop + * circuits. */ +STATIC int +cpath_build_state_to_crn_ipv6_extend_flag(const cpath_build_state_t *state, + int cur_len) +{ + IF_BUG_ONCE(state->desired_path_len < 2) + return 0; + + /* The last node is the relay doing the self-test. So we want to extend over + * IPv6 from the second-last node. */ + if (state->is_ipv6_selftest && cur_len == state->desired_path_len - 2) + return CRN_INITIATE_IPV6_EXTEND; + else + return 0; +} + /** Decide a suitable length for circ's cpath, and pick an exit * router (or use <b>exit</b> if provided). Store these in the * cpath. @@ -1990,14 +2027,13 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei, exit_ei = extend_info_dup(exit_ei); } else { /* we have to decide one */ router_crn_flags_t flags = CRN_NEED_DESC; - if (state->need_uptime) - flags |= CRN_NEED_UPTIME; - if (state->need_capacity) - flags |= CRN_NEED_CAPACITY; - if (is_hs_v3_rp_circuit) - flags |= CRN_RENDEZVOUS_V3; + flags |= cpath_build_state_to_crn_flags(state); + /* Some internal exits are one hop, for example directory connections. + * (Guards are always direct, middles are never direct.) */ if (state->onehop_tunnel) flags |= CRN_DIRECT_CONN; + if (is_hs_v3_rp_circuit) + flags |= CRN_RENDEZVOUS_V3; const node_t *node = choose_good_exit_server(circ, flags, state->is_internal); if (!node) { @@ -2059,32 +2095,27 @@ circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *exit_ei) return 0; } -/** Return the number of routers in <b>routers</b> that are currently up - * and available for building circuits through. +/** Return the number of routers in <b>nodes</b> that are currently up and + * available for building circuits through. * - * (Note that this function may overcount or undercount, if we have - * descriptors that are not the type we would prefer to use for some - * particular router. See bug #25885.) + * If <b>direct</b> is true, only count nodes that are suitable for direct + * connections. Counts nodes regardless of whether their addresses are + * preferred. */ MOCK_IMPL(STATIC int, count_acceptable_nodes, (const smartlist_t *nodes, int direct)) { int num=0; + int flags = CRN_NEED_DESC; + + if (direct) + flags |= CRN_DIRECT_CONN; SMARTLIST_FOREACH_BEGIN(nodes, const node_t *, node) { // log_debug(LD_CIRC, -// "Contemplating whether router %d (%s) is a new option.", -// i, r->nickname); - if (! node->is_running) -// log_debug(LD_CIRC,"Nope, the directory says %d is not running.",i); - continue; - if (! node->is_valid) -// log_debug(LD_CIRC,"Nope, the directory says %d is not valid.",i); - continue; - if (! node_has_preferred_descriptor(node, direct)) - continue; - /* The node has a descriptor, so we can just check the ntor key directly */ - if (!node_has_curve25519_onion_key(node)) + // "Contemplating whether router %d (%s) is a new option.", + // i, r->nickname); + if (!router_can_choose_node(node, flags)) continue; ++num; } SMARTLIST_FOREACH_END(node); @@ -2278,10 +2309,8 @@ choose_good_middle_server(uint8_t purpose, excluded = build_middle_exclude_list(purpose, state, head, cur_len); - if (state->need_uptime) - flags |= CRN_NEED_UPTIME; - if (state->need_capacity) - flags |= CRN_NEED_CAPACITY; + flags |= cpath_build_state_to_crn_flags(state); + flags |= cpath_build_state_to_crn_ipv6_extend_flag(state, cur_len); /** If a hidden service circuit wants a specific middle node, pin it. */ if (middle_node_must_be_vanguard(options, purpose, cur_len)) { @@ -2357,10 +2386,7 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state, } if (state) { - if (state->need_uptime) - flags |= CRN_NEED_UPTIME; - if (state->need_capacity) - flags |= CRN_NEED_CAPACITY; + flags |= cpath_build_state_to_crn_flags(state); } choice = router_choose_random_node(excluded, options->ExcludeNodes, flags); @@ -2429,143 +2455,6 @@ onion_extend_cpath(origin_circuit_t *circ) return 0; } -/** Allocate a new extend_info object based on the various arguments. */ -extend_info_t * -extend_info_new(const char *nickname, - const char *rsa_id_digest, - const ed25519_public_key_t *ed_id, - crypto_pk_t *onion_key, - const curve25519_public_key_t *ntor_key, - const tor_addr_t *addr, uint16_t port) -{ - extend_info_t *info = tor_malloc_zero(sizeof(extend_info_t)); - memcpy(info->identity_digest, rsa_id_digest, DIGEST_LEN); - if (ed_id && !ed25519_public_key_is_zero(ed_id)) - memcpy(&info->ed_identity, ed_id, sizeof(ed25519_public_key_t)); - if (nickname) - strlcpy(info->nickname, nickname, sizeof(info->nickname)); - if (onion_key) - info->onion_key = crypto_pk_dup_key(onion_key); - if (ntor_key) - memcpy(&info->curve25519_onion_key, ntor_key, - sizeof(curve25519_public_key_t)); - tor_addr_copy(&info->addr, addr); - info->port = port; - return info; -} - -/** Allocate and return a new extend_info that can be used to build a - * circuit to or through the node <b>node</b>. Use the primary address - * of the node (i.e. its IPv4 address) unless - * <b>for_direct_connect</b> is true, in which case the preferred - * address is used instead. May return NULL if there is not enough - * info about <b>node</b> to extend to it--for example, if the preferred - * routerinfo_t or microdesc_t is missing, or if for_direct_connect is - * true and none of the node's addresses is allowed by tor's firewall - * and IP version config. - **/ -extend_info_t * -extend_info_from_node(const node_t *node, int for_direct_connect) -{ - crypto_pk_t *rsa_pubkey = NULL; - extend_info_t *info = NULL; - tor_addr_port_t ap; - int valid_addr = 0; - - if (!node_has_preferred_descriptor(node, for_direct_connect)) { - return NULL; - } - - /* Choose a preferred address first, but fall back to an allowed address. */ - if (for_direct_connect) - fascist_firewall_choose_address_node(node, FIREWALL_OR_CONNECTION, 0, &ap); - else { - node_get_prim_orport(node, &ap); - } - valid_addr = tor_addr_port_is_valid_ap(&ap, 0); - - 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); - - /* Every node we connect or extend to must support ntor */ - if (!node_has_curve25519_onion_key(node)) { - log_fn(LOG_PROTOCOL_WARN, LD_CIRC, - "Attempted to create extend_info for a node that does not support " - "ntor: %s", node_describe(node)); - return NULL; - } - - const ed25519_public_key_t *ed_pubkey = NULL; - - /* Don't send the ed25519 pubkey unless the target node actually supports - * authenticating with it. */ - if (node_supports_ed25519_link_authentication(node, 0)) { - log_info(LD_CIRC, "Including Ed25519 ID for %s", node_describe(node)); - ed_pubkey = node_get_ed25519_id(node); - } else if (node_get_ed25519_id(node)) { - log_info(LD_CIRC, "Not including the ed25519 ID for %s, since it won't " - "be able to authenticate it.", - node_describe(node)); - } - - /* Retrieve the curve25519 pubkey. */ - const curve25519_public_key_t *curve_pubkey = - node_get_curve25519_onion_key(node); - rsa_pubkey = node_get_rsa_onion_key(node); - - if (valid_addr && node->ri) { - info = extend_info_new(node->ri->nickname, - node->identity, - ed_pubkey, - rsa_pubkey, - curve_pubkey, - &ap.addr, - ap.port); - } else if (valid_addr && node->rs && node->md) { - info = extend_info_new(node->rs->nickname, - node->identity, - ed_pubkey, - rsa_pubkey, - curve_pubkey, - &ap.addr, - ap.port); - } - - crypto_pk_free(rsa_pubkey); - return info; -} - -/** Release storage held by an extend_info_t struct. */ -void -extend_info_free_(extend_info_t *info) -{ - if (!info) - return; - crypto_pk_free(info->onion_key); - tor_free(info); -} - -/** Allocate and return a new extend_info_t with the same contents as - * <b>info</b>. */ -extend_info_t * -extend_info_dup(extend_info_t *info) -{ - extend_info_t *newinfo; - tor_assert(info); - newinfo = tor_malloc(sizeof(extend_info_t)); - memcpy(newinfo, info, sizeof(extend_info_t)); - if (info->onion_key) - newinfo->onion_key = crypto_pk_dup_key(info->onion_key); - else - newinfo->onion_key = NULL; - return newinfo; -} - /** Return the node_t for the chosen exit router in <b>state</b>. * If there is no chosen exit, or if we don't know the node_t for * the chosen exit, return NULL. @@ -2601,43 +2490,6 @@ build_state_get_exit_nickname(cpath_build_state_t *state) return state->chosen_exit->nickname; } -/** Return true iff the given address can be used to extend to. */ -int -extend_info_addr_is_allowed(const tor_addr_t *addr) -{ - tor_assert(addr); - - /* Check if we have a private address and if we can extend to it. */ - if ((tor_addr_is_internal(addr, 0) || tor_addr_is_multicast(addr)) && - !get_options()->ExtendAllowPrivateAddresses) { - goto disallow; - } - /* Allowed! */ - return 1; - disallow: - return 0; -} - -/* Does ei have a valid TAP key? */ -int -extend_info_supports_tap(const extend_info_t* ei) -{ - tor_assert(ei); - /* Valid TAP keys are not NULL */ - return ei->onion_key != NULL; -} - -/* Does ei have a valid ntor key? */ -int -extend_info_supports_ntor(const extend_info_t* ei) -{ - tor_assert(ei); - /* Valid ntor keys have at least one non-zero byte */ - return !fast_mem_is_zero( - (const char*)ei->curve25519_onion_key.public_key, - CURVE25519_PUBKEY_LEN); -} - /* Is circuit purpose allowed to use the deprecated TAP encryption protocol? * The hidden service protocol still uses TAP for some connections, because * ntor onion keys aren't included in HS descriptors or INTRODUCE cells. */ @@ -2672,15 +2524,6 @@ circuit_has_usable_onion_key(const origin_circuit_t *circ) circuit_can_use_tap(circ)); } -/* Does ei have an onion key which it would prefer to use? - * Currently, we prefer ntor keys*/ -int -extend_info_has_preferred_onion_key(const extend_info_t* ei) -{ - tor_assert(ei); - return extend_info_supports_ntor(ei); -} - /** Find the circuits that are waiting to find out whether their guards are * usable, and if any are ready to become usable, mark them open and try * attaching streams as appropriate. */ diff --git a/src/core/or/circuitbuild.h b/src/core/or/circuitbuild.h index e62bb41de9..0cd1eb4f45 100644 --- a/src/core/or/circuitbuild.h +++ b/src/core/or/circuitbuild.h @@ -42,23 +42,8 @@ MOCK_DECL(int, circuit_all_predicted_ports_handled, (time_t now, int circuit_append_new_exit(origin_circuit_t *circ, extend_info_t *info); int circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *info); -extend_info_t *extend_info_new(const char *nickname, - const char *rsa_id_digest, - const struct ed25519_public_key_t *ed_id, - crypto_pk_t *onion_key, - const struct curve25519_public_key_t *ntor_key, - const tor_addr_t *addr, uint16_t port); -extend_info_t *extend_info_from_node(const node_t *r, int for_direct_connect); -extend_info_t *extend_info_dup(extend_info_t *info); -void extend_info_free_(extend_info_t *info); -#define extend_info_free(info) \ - FREE_AND_NULL(extend_info_t, extend_info_free_, (info)) -int extend_info_addr_is_allowed(const tor_addr_t *addr); -int extend_info_supports_tap(const extend_info_t* ei); -int extend_info_supports_ntor(const extend_info_t* ei); int circuit_can_use_tap(const origin_circuit_t *circ); int circuit_has_usable_onion_key(const origin_circuit_t *circ); -int extend_info_has_preferred_onion_key(const extend_info_t* ei); const uint8_t *build_state_get_exit_rsa_id(cpath_build_state_t *state); MOCK_DECL(const node_t *, build_state_get_exit_node,(cpath_build_state_t *state)); @@ -71,13 +56,7 @@ const node_t *choose_good_entry_server(uint8_t purpose, struct circuit_guard_state_t **guard_state_out); void circuit_upgrade_circuits_from_guard_wait(void); -struct ed25519_public_key_t; - -MOCK_DECL(channel_t *, -channel_connect_for_circuit,(const tor_addr_t *addr, - uint16_t port, - const char *id_digest, - const struct ed25519_public_key_t *ed_id)); +MOCK_DECL(channel_t *, channel_connect_for_circuit,(const extend_info_t *ei)); struct create_cell_t; MOCK_DECL(int, @@ -97,6 +76,10 @@ STATIC int onion_extend_cpath(origin_circuit_t *circ); STATIC int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei, int is_hs_v3_rp_circuit); +STATIC int cpath_build_state_to_crn_flags(const cpath_build_state_t *state); +STATIC int cpath_build_state_to_crn_ipv6_extend_flag( + const cpath_build_state_t *state, + int cur_len); #endif /* defined(CIRCUITBUILD_PRIVATE) */ diff --git a/src/core/or/circuitlist.c b/src/core/or/circuitlist.c index 90cce47490..f4d6cd3c12 100644 --- a/src/core/or/circuitlist.c +++ b/src/core/or/circuitlist.c @@ -64,6 +64,7 @@ #include "core/or/circuitstats.h" #include "core/or/circuitpadding.h" #include "core/or/crypt_path.h" +#include "core/or/extendinfo.h" #include "core/mainloop/connection.h" #include "app/config/config.h" #include "core/or/connection_edge.h" @@ -1944,7 +1945,7 @@ circuit_find_to_cannibalize(uint8_t purpose_to_produce, extend_info_t *info, /* Ignore any circuits for which we can't use the Guard. It is possible * that the Guard was removed from the sampled set after the circuit - * was created so avoid using it. */ + * was created, so avoid using it. */ if (!entry_guard_could_succeed(circ->guard_state)) { goto next; } diff --git a/src/core/or/circuitpadding.c b/src/core/or/circuitpadding.c index 43f4a31624..889ffb03f1 100644 --- a/src/core/or/circuitpadding.c +++ b/src/core/or/circuitpadding.c @@ -266,18 +266,31 @@ circpad_marked_circuit_for_padding(circuit_t *circ, int reason) /** * Free all the machineinfos in <b>circ</b> that match <b>machine_num</b>. * + * If machine_ctr is non-zero, also make sure it matches the padding_info's + * machine counter before freeing. + * * Returns true if any machineinfos with that number were freed. * False otherwise. */ static int -free_circ_machineinfos_with_machine_num(circuit_t *circ, int machine_num) +free_circ_machineinfos_with_machine_num(circuit_t *circ, int machine_num, + uint32_t machine_ctr) { int found = 0; FOR_EACH_CIRCUIT_MACHINE_BEGIN(i) { if (circ->padding_machine[i] && circ->padding_machine[i]->machine_num == machine_num) { - circpad_circuit_machineinfo_free_idx(circ, i); - circ->padding_machine[i] = NULL; - found = 1; + /* If machine_ctr is non-zero, make sure it matches too. This + * is to ensure that old STOP messages don't shutdown newer machines. */ + if (machine_ctr && circ->padding_info[i] && + circ->padding_info[i]->machine_ctr != machine_ctr) { + log_info(LD_CIRC, + "Padding shutdown for wrong (old?) machine ctr: %u vs %u", + machine_ctr, circ->padding_info[i]->machine_ctr); + } else { + circpad_circuit_machineinfo_free_idx(circ, i); + circ->padding_machine[i] = NULL; + found = 1; + } } } FOR_EACH_CIRCUIT_MACHINE_END; @@ -306,6 +319,7 @@ circpad_circuit_machineinfo_new(circuit_t *on_circ, int machine_index) mi->machine_index = machine_index; mi->on_circ = on_circ; mi->last_cell_time_sec = approx_time(); + mi->machine_ctr = on_circ->padding_machine_ctr; return mi; } @@ -1556,19 +1570,23 @@ circpad_machine_spec_transitioned_to_end(circpad_machine_runtime_t *mi) /* We free the machine info here so that we can be replaced * by a different machine. But we must leave the padding_machine * in place to wait for the negotiated response */ + uint32_t machine_ctr = mi->machine_ctr; circpad_circuit_machineinfo_free_idx(on_circ, machine->machine_index); circpad_negotiate_padding(TO_ORIGIN_CIRCUIT(on_circ), machine->machine_num, machine->target_hopnum, - CIRCPAD_COMMAND_STOP); + CIRCPAD_COMMAND_STOP, + machine_ctr); } else { + uint32_t machine_ctr = mi->machine_ctr; circpad_circuit_machineinfo_free_idx(on_circ, machine->machine_index); circpad_padding_negotiated(on_circ, machine->machine_num, CIRCPAD_COMMAND_STOP, - CIRCPAD_RESPONSE_OK); + CIRCPAD_RESPONSE_OK, + machine_ctr); on_circ->padding_machine[machine->machine_index] = NULL; } } @@ -1990,7 +2008,7 @@ circpad_internal_event_state_length_up(circpad_machine_runtime_t *mi) * Returns true if the circuit matches the conditions. */ static inline bool -circpad_machine_conditions_met(origin_circuit_t *circ, +circpad_machine_conditions_apply(origin_circuit_t *circ, const circpad_machine_spec_t *machine) { /* If padding is disabled, no machines should match/apply. This has @@ -2007,7 +2025,7 @@ circpad_machine_conditions_met(origin_circuit_t *circ, } if (!(circpad_circ_purpose_to_mask(TO_CIRCUIT(circ)->purpose) - & machine->conditions.purpose_mask)) + & machine->conditions.apply_purpose_mask)) return 0; if (machine->conditions.requires_vanguards) { @@ -2023,7 +2041,7 @@ circpad_machine_conditions_met(origin_circuit_t *circ, * "I want to apply to circuits with either streams or no streams"; OR * "I only want to apply to circuits with streams"; OR * "I only want to apply to circuits without streams". */ - if (!(circpad_circuit_state(circ) & machine->conditions.state_mask)) + if (!(circpad_circuit_state(circ) & machine->conditions.apply_state_mask)) return 0; if (circuit_get_cpath_opened_len(circ) < machine->conditions.min_hops) @@ -2033,6 +2051,26 @@ circpad_machine_conditions_met(origin_circuit_t *circ, } /** + * Check to see if any of the keep conditions still apply to this circuit. + * + * These conditions keep the machines active if they match, but do not + * cause new machines to start up. + */ +static inline bool +circpad_machine_conditions_keep(origin_circuit_t *circ, + const circpad_machine_spec_t *machine) +{ + if ((circpad_circ_purpose_to_mask(TO_CIRCUIT(circ)->purpose) + & machine->conditions.keep_purpose_mask)) + return 1; + + if ((circpad_circuit_state(circ) & machine->conditions.keep_state_mask)) + return 1; + + return 0; +} + +/** * Returns a minimized representation of the circuit state. * * The padding code only cares if the circuit is building, @@ -2097,15 +2135,22 @@ circpad_shutdown_old_machines(origin_circuit_t *on_circ) circuit_t *circ = TO_CIRCUIT(on_circ); FOR_EACH_ACTIVE_CIRCUIT_MACHINE_BEGIN(i, circ) { - if (!circpad_machine_conditions_met(on_circ, + /* We shut down a machine if neither the apply conditions + * nor the keep conditions match. If either set of conditions match, + * keep it around. */ + if (!circpad_machine_conditions_apply(on_circ, + circ->padding_machine[i]) && + !circpad_machine_conditions_keep(on_circ, circ->padding_machine[i])) { + uint32_t machine_ctr = circ->padding_info[i]->machine_ctr; // Clear machineinfo (frees timers) circpad_circuit_machineinfo_free_idx(circ, i); // Send padding negotiate stop circpad_negotiate_padding(on_circ, circ->padding_machine[i]->machine_num, circ->padding_machine[i]->target_hopnum, - CIRCPAD_COMMAND_STOP); + CIRCPAD_COMMAND_STOP, + machine_ctr); } } FOR_EACH_ACTIVE_CIRCUIT_MACHINE_END; } @@ -2154,7 +2199,7 @@ circpad_add_matching_machines(origin_circuit_t *on_circ, * machines installed on a circuit. Make sure we only * add this machine if its target machine index is free. */ if (machine->machine_index == i && - circpad_machine_conditions_met(on_circ, machine)) { + circpad_machine_conditions_apply(on_circ, machine)) { // We can only replace this machine if the target hopnum // is the same, otherwise we'll get invalid data @@ -2172,7 +2217,8 @@ circpad_add_matching_machines(origin_circuit_t *on_circ, circpad_setup_machine_on_circ(circ, machine); if (circpad_negotiate_padding(on_circ, machine->machine_num, machine->target_hopnum, - CIRCPAD_COMMAND_START) < 0) { + CIRCPAD_COMMAND_START, + circ->padding_machine_ctr) < 0) { log_info(LD_CIRC, "Padding not negotiated. Cleaning machine from circuit %u", CIRCUIT_IS_ORIGIN(circ) ? @@ -2463,6 +2509,17 @@ circpad_setup_machine_on_circ(circuit_t *on_circ, machine->name, on_circ->purpose); } + /* Padding machine ctr starts at 1, so we increment this ctr first. + * (machine ctr of 0 means "any machine"). + * + * See https://bugs.tororject.org/30992. */ + on_circ->padding_machine_ctr++; + + /* uint32 wraparound check: 0 is special, just wrap to 1 */ + if (on_circ->padding_machine_ctr == 0) { + on_circ->padding_machine_ctr = 1; + } + on_circ->padding_info[machine->machine_index] = circpad_circuit_machineinfo_new(on_circ, machine->machine_index); on_circ->padding_machine[machine->machine_index] = machine; @@ -2555,9 +2612,9 @@ circpad_circ_client_machine_init(void) = tor_malloc_zero(sizeof(circpad_machine_spec_t)); circ_client_machine->conditions.min_hops = 2; - circ_client_machine->conditions.state_mask = + circ_client_machine->conditions.apply_state_mask = CIRCPAD_CIRC_BUILDING|CIRCPAD_CIRC_OPENED|CIRCPAD_CIRC_HAS_RELAY_EARLY; - circ_client_machine->conditions.purpose_mask = CIRCPAD_PURPOSE_ALL; + circ_client_machine->conditions.apply_purpose_mask = CIRCPAD_PURPOSE_ALL; circ_client_machine->conditions.reduced_padding_ok = 1; circ_client_machine->target_hopnum = 2; @@ -2816,7 +2873,8 @@ signed_error_t circpad_negotiate_padding(origin_circuit_t *circ, circpad_machine_num_t machine, uint8_t target_hopnum, - uint8_t command) + uint8_t command, + uint32_t machine_ctr) { circpad_negotiate_t type; cell_t cell; @@ -2838,14 +2896,16 @@ circpad_negotiate_padding(origin_circuit_t *circ, circpad_negotiate_set_command(&type, command); circpad_negotiate_set_version(&type, 0); circpad_negotiate_set_machine_type(&type, machine); + circpad_negotiate_set_machine_ctr(&type, machine_ctr); if ((len = circpad_negotiate_encode(cell.payload, CELL_PAYLOAD_SIZE, &type)) < 0) return -1; log_fn(LOG_INFO,LD_CIRC, - "Negotiating padding on circuit %u (%d), command %d", - circ->global_identifier, TO_CIRCUIT(circ)->purpose, command); + "Negotiating padding on circuit %u (%d), command %d, for ctr %u", + circ->global_identifier, TO_CIRCUIT(circ)->purpose, command, + machine_ctr); return circpad_send_command_to_hop(circ, target_hopnum, RELAY_COMMAND_PADDING_NEGOTIATE, @@ -2861,7 +2921,8 @@ bool circpad_padding_negotiated(circuit_t *circ, circpad_machine_num_t machine, uint8_t command, - uint8_t response) + uint8_t response, + uint32_t machine_ctr) { circpad_negotiated_t type; cell_t cell; @@ -2878,6 +2939,7 @@ circpad_padding_negotiated(circuit_t *circ, circpad_negotiated_set_response(&type, response); circpad_negotiated_set_version(&type, 0); circpad_negotiated_set_machine_type(&type, machine); + circpad_negotiated_set_machine_ctr(&type, machine_ctr); if ((len = circpad_negotiated_encode(cell.payload, CELL_PAYLOAD_SIZE, &type)) < 0) @@ -2923,19 +2985,33 @@ circpad_handle_padding_negotiate(circuit_t *circ, cell_t *cell) if (negotiate->command == CIRCPAD_COMMAND_STOP) { /* Free the machine corresponding to this machine type */ if (free_circ_machineinfos_with_machine_num(circ, - negotiate->machine_type)) { - log_info(LD_CIRC, "Received STOP command for machine %u", - negotiate->machine_type); + negotiate->machine_type, + negotiate->machine_ctr)) { + log_info(LD_CIRC, "Received STOP command for machine %u, ctr %u", + negotiate->machine_type, negotiate->machine_ctr); goto done; } - log_fn(LOG_PROTOCOL_WARN, LD_CIRC, - "Received circuit padding stop command for unknown machine."); - goto err; - } else if (negotiate->command == CIRCPAD_COMMAND_START) { + if (negotiate->machine_ctr <= circ->padding_machine_ctr) { + log_info(LD_CIRC, "Received STOP command for old machine %u, ctr %u", + negotiate->machine_type, negotiate->machine_ctr); + goto done; + + } else { + log_fn(LOG_PROTOCOL_WARN, LD_CIRC, + "Received circuit padding stop command for unknown machine."); + goto err; + } + } else if (negotiate->command == CIRCPAD_COMMAND_START) { SMARTLIST_FOREACH_BEGIN(relay_padding_machines, const circpad_machine_spec_t *, m) { if (m->machine_num == negotiate->machine_type) { circpad_setup_machine_on_circ(circ, m); + if (negotiate->machine_ctr && + circ->padding_machine_ctr != negotiate->machine_ctr) { + log_fn(LOG_PROTOCOL_WARN, LD_CIRC, + "Client and relay have different counts for padding machines: " + "%u vs %u", circ->padding_machine_ctr, negotiate->machine_ctr); + } circpad_cell_event_nonpadding_received(circ); goto done; } @@ -2948,7 +3024,8 @@ circpad_handle_padding_negotiate(circuit_t *circ, cell_t *cell) done: circpad_padding_negotiated(circ, negotiate->machine_type, negotiate->command, - (retval == 0) ? CIRCPAD_RESPONSE_OK : CIRCPAD_RESPONSE_ERR); + (retval == 0) ? CIRCPAD_RESPONSE_OK : CIRCPAD_RESPONSE_ERR, + negotiate->machine_ctr); circpad_negotiate_free(negotiate); return retval; @@ -2999,17 +3076,22 @@ circpad_handle_padding_negotiated(circuit_t *circ, cell_t *cell, * circpad_add_matching_matchines() added a new machine, * there may be a padding_machine for a different machine num * than this response. */ - free_circ_machineinfos_with_machine_num(circ, negotiated->machine_type); + free_circ_machineinfos_with_machine_num(circ, negotiated->machine_type, + negotiated->machine_ctr); } else if (negotiated->command == CIRCPAD_COMMAND_START && negotiated->response == CIRCPAD_RESPONSE_ERR) { - // This can happen due to consensus drift.. free the machines + // This can still happen due to consensus drift.. free the machines // and be sad - free_circ_machineinfos_with_machine_num(circ, negotiated->machine_type); - TO_ORIGIN_CIRCUIT(circ)->padding_negotiation_failed = 1; - log_fn(LOG_PROTOCOL_WARN, LD_CIRC, - "Middle node did not accept our padding request on circuit %u (%d)", - TO_ORIGIN_CIRCUIT(circ)->global_identifier, - circ->purpose); + if (free_circ_machineinfos_with_machine_num(circ, negotiated->machine_type, + negotiated->machine_ctr)) { + // Only fail if a machine was there and matched the error cell + TO_ORIGIN_CIRCUIT(circ)->padding_negotiation_failed = 1; + log_fn(LOG_PROTOCOL_WARN, LD_CIRC, + "Middle node did not accept our padding request on circuit " + "%u (%d)", + TO_ORIGIN_CIRCUIT(circ)->global_identifier, + circ->purpose); + } } circpad_negotiated_free(negotiated); diff --git a/src/core/or/circuitpadding.h b/src/core/or/circuitpadding.h index 74b69a1c7a..3d2929cf74 100644 --- a/src/core/or/circuitpadding.h +++ b/src/core/or/circuitpadding.h @@ -173,11 +173,21 @@ typedef struct circpad_machine_conditions_t { /** Only apply the machine *if* the circuit's state matches any of * the bits set in this bitmask. */ - circpad_circuit_state_t state_mask; + circpad_circuit_state_t apply_state_mask; /** Only apply a machine *if* the circuit's purpose matches one * of the bits set in this bitmask */ - circpad_purpose_mask_t purpose_mask; + circpad_purpose_mask_t apply_purpose_mask; + + /** Keep a machine if any of the circuits's state machine's match + * the bits set in this bitmask, but don't apply new machines if + * they match this mask. */ + circpad_circuit_state_t keep_state_mask; + + /** Keep a machine if any of the circuits's state machine's match + * the bits set in this bitmask, but don't apply new machines if + * they match this mask. */ + circpad_purpose_mask_t keep_purpose_mask; } circpad_machine_conditions_t; @@ -565,6 +575,13 @@ typedef struct circpad_machine_runtime_t { /** What state is this machine in? */ circpad_statenum_t current_state; + /** Machine counter, for shutdown sync. + * + * Set from circuit_t.padding_machine_ctr, which is incremented each + * padding machine instantiation. + */ + uint32_t machine_ctr; + /** * True if we have scheduled a timer for padding. * @@ -726,11 +743,13 @@ signed_error_t circpad_handle_padding_negotiated(struct circuit_t *circ, signed_error_t circpad_negotiate_padding(struct origin_circuit_t *circ, circpad_machine_num_t machine, uint8_t target_hopnum, - uint8_t command); + uint8_t command, + uint32_t machine_ctr); bool circpad_padding_negotiated(struct circuit_t *circ, circpad_machine_num_t machine, uint8_t command, - uint8_t response); + uint8_t response, + uint32_t machine_ctr); circpad_purpose_mask_t circpad_circ_purpose_to_mask(uint8_t circ_purpose); diff --git a/src/core/or/circuitpadding_machines.c b/src/core/or/circuitpadding_machines.c index 98767f9e8f..1e6b580f5b 100644 --- a/src/core/or/circuitpadding_machines.c +++ b/src/core/or/circuitpadding_machines.c @@ -67,7 +67,7 @@ circpad_machine_client_hide_intro_circuits(smartlist_t *machines_sl) client_machine->name = "client_ip_circ"; - client_machine->conditions.state_mask = CIRCPAD_CIRC_OPENED; + client_machine->conditions.apply_state_mask = CIRCPAD_CIRC_OPENED; client_machine->target_hopnum = 2; /* This is a client machine */ @@ -102,9 +102,18 @@ circpad_machine_client_hide_intro_circuits(smartlist_t *machines_sl) * INTRO_MACHINE_MAXIMUM_PADDING cells, to match the "...(inbound data cells * continue)" portion of the trace (aka the rest of an HTTPS response body). */ - client_machine->conditions.purpose_mask = - circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT)| - circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_INTRODUCE_ACKED)| + + /* Start the machine on fresh intro circs. */ + client_machine->conditions.apply_purpose_mask = + circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT); + + /* If the client purpose changes back to CIRCUIT_PURPOSE_C_INTRODUCING, + * or transitions to CIRCUIT_PURPOSE_C_INTRODUCE_ACKED, keep the machine + * alive, but do not launch new machines for these purposes. Also + * keep the machine around if it is in the CIRCUIT_PADDING purpose + * (but do not try to take over other machines in that purpose). */ + client_machine->conditions.keep_purpose_mask = + circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_INTRODUCE_ACKED) | circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_CIRCUIT_PADDING); /* Keep the circuit alive even after the introduction has been finished, @@ -152,7 +161,7 @@ circpad_machine_relay_hide_intro_circuits(smartlist_t *machines_sl) relay_machine->name = "relay_ip_circ"; - relay_machine->conditions.state_mask = CIRCPAD_CIRC_OPENED; + relay_machine->conditions.apply_state_mask = CIRCPAD_CIRC_OPENED; /* This is a relay-side machine */ relay_machine->is_origin_side = 0; @@ -263,7 +272,7 @@ circpad_machine_client_hide_rend_circuits(smartlist_t *machines_sl) client_machine->name = "client_rp_circ"; /* Only pad after the circuit has been built and pad to the middle */ - client_machine->conditions.state_mask = CIRCPAD_CIRC_OPENED; + client_machine->conditions.apply_state_mask = CIRCPAD_CIRC_OPENED; client_machine->target_hopnum = 2; /* This is a client machine */ @@ -299,7 +308,7 @@ circpad_machine_client_hide_rend_circuits(smartlist_t *machines_sl) * * Hence this way we make rendezvous circuits look like general circuits up * till the end of the circuit setup. */ - client_machine->conditions.purpose_mask = + client_machine->conditions.apply_purpose_mask = circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_REND_JOINED)| circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_REND_READY)| circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED); @@ -383,7 +392,7 @@ circpad_machine_relay_hide_rend_circuits(smartlist_t *machines_sl) /* Only pad after the circuit has been built and pad to the middle */ relay_machine->conditions.min_hops = 2; - relay_machine->conditions.state_mask = CIRCPAD_CIRC_OPENED; + relay_machine->conditions.apply_state_mask = CIRCPAD_CIRC_OPENED; /* This is a relay-side machine */ relay_machine->is_origin_side = 0; diff --git a/src/core/or/circuitstats.c b/src/core/or/circuitstats.c index 822e5bd308..bc6c263798 100644 --- a/src/core/or/circuitstats.c +++ b/src/core/or/circuitstats.c @@ -53,9 +53,6 @@ #undef log #include <math.h> -static void cbt_control_event_buildtimeout_set( - const circuit_build_times_t *cbt, - buildtimeout_set_event_t type); static void circuit_build_times_scale_circ_counts(circuit_build_times_t *cbt); #define CBT_BIN_TO_MS(bin) ((bin)*CBT_BIN_WIDTH + (CBT_BIN_WIDTH/2)) @@ -402,7 +399,7 @@ circuit_build_times_initial_timeout(void) * and learn a new timeout. */ static int32_t -circuit_build_times_recent_circuit_count(networkstatus_t *ns) +circuit_build_times_recent_circuit_count(const networkstatus_t *ns) { int32_t num; num = networkstatus_get_param(ns, "cbtrecentcount", @@ -428,7 +425,7 @@ circuit_build_times_recent_circuit_count(networkstatus_t *ns) */ void circuit_build_times_new_consensus_params(circuit_build_times_t *cbt, - networkstatus_t *ns) + const networkstatus_t *ns) { int32_t num; @@ -545,7 +542,7 @@ circuit_build_times_get_initial_timeout(void) * Leave estimated parameters, timeout and network liveness intact * for future use. */ -STATIC void +void circuit_build_times_reset(circuit_build_times_t *cbt) { memset(cbt->circuit_build_times, 0, sizeof(cbt->circuit_build_times)); @@ -1893,61 +1890,3 @@ circuit_build_times_update_last_circ(circuit_build_times_t *cbt) { cbt->last_circ_at = approx_time(); } - -static void -cbt_control_event_buildtimeout_set(const circuit_build_times_t *cbt, - buildtimeout_set_event_t type) -{ - char *args = NULL; - double qnt; - double timeout_rate = 0.0; - double close_rate = 0.0; - - switch (type) { - case BUILDTIMEOUT_SET_EVENT_RESET: - case BUILDTIMEOUT_SET_EVENT_SUSPENDED: - case BUILDTIMEOUT_SET_EVENT_DISCARD: - qnt = 1.0; - break; - case BUILDTIMEOUT_SET_EVENT_COMPUTED: - case BUILDTIMEOUT_SET_EVENT_RESUME: - default: - qnt = circuit_build_times_quantile_cutoff(); - break; - } - - /* The timeout rate is the ratio of the timeout count over - * the total number of circuits attempted. The total number of - * circuits is (timeouts+succeeded), since every circuit - * either succeeds, or times out. "Closed" circuits are - * MEASURE_TIMEOUT circuits whose measurement period expired. - * All MEASURE_TIMEOUT circuits are counted in the timeouts stat - * before transitioning to MEASURE_TIMEOUT (in - * circuit_build_times_mark_circ_as_measurement_only()). - * MEASURE_TIMEOUT circuits that succeed are *not* counted as - * "succeeded". See circuit_build_times_handle_completed_hop(). - * - * We cast the denominator - * to promote it to double before the addition, to avoid int32 - * overflow. */ - const double total_circuits = - ((double)cbt->num_circ_timeouts) + cbt->num_circ_succeeded; - if (total_circuits >= 1.0) { - timeout_rate = cbt->num_circ_timeouts / total_circuits; - close_rate = cbt->num_circ_closed / total_circuits; - } - - tor_asprintf(&args, "TOTAL_TIMES=%lu " - "TIMEOUT_MS=%lu XM=%lu ALPHA=%f CUTOFF_QUANTILE=%f " - "TIMEOUT_RATE=%f CLOSE_MS=%lu CLOSE_RATE=%f", - (unsigned long)cbt->total_build_times, - (unsigned long)cbt->timeout_ms, - (unsigned long)cbt->Xm, cbt->alpha, qnt, - timeout_rate, - (unsigned long)cbt->close_ms, - close_rate); - - control_event_buildtimeout_set(type, args); - - tor_free(args); -} diff --git a/src/core/or/circuitstats.h b/src/core/or/circuitstats.h index 52c9100f53..930e0a9ba3 100644 --- a/src/core/or/circuitstats.h +++ b/src/core/or/circuitstats.h @@ -43,12 +43,13 @@ int circuit_build_times_needs_circuits_now(const circuit_build_times_t *cbt); void circuit_build_times_init(circuit_build_times_t *cbt); void circuit_build_times_free_timeouts(circuit_build_times_t *cbt); void circuit_build_times_new_consensus_params(circuit_build_times_t *cbt, - networkstatus_t *ns); + const networkstatus_t *ns); double circuit_build_times_timeout_rate(const circuit_build_times_t *cbt); double circuit_build_times_close_rate(const circuit_build_times_t *cbt); void circuit_build_times_update_last_circ(circuit_build_times_t *cbt); void circuit_build_times_mark_circ_as_measurement_only(origin_circuit_t *circ); +void circuit_build_times_reset(circuit_build_times_t *cbt); /** Total size of the circuit timeout history to accumulate. * 1000 is approx 2.5 days worth of continual-use circuits. */ @@ -137,7 +138,6 @@ int32_t circuit_build_times_initial_timeout(void); STATIC double circuit_build_times_calculate_timeout(circuit_build_times_t *cbt, double quantile); STATIC int circuit_build_times_update_alpha(circuit_build_times_t *cbt); -STATIC void circuit_build_times_reset(circuit_build_times_t *cbt); /* Network liveness functions */ STATIC int circuit_build_times_network_check_changed( @@ -158,7 +158,6 @@ void circuit_build_times_network_is_live(circuit_build_times_t *cbt); int circuit_build_times_network_check_live(const circuit_build_times_t *cbt); void circuit_build_times_network_circ_success(circuit_build_times_t *cbt); -#ifdef CIRCUITSTATS_PRIVATE /** Information about the state of our local network connection */ typedef struct { /** The timestamp we last completed a TLS handshake or received a cell */ @@ -208,6 +207,5 @@ struct circuit_build_times_t { uint32_t num_circ_closed; }; -#endif /* defined(CIRCUITSTATS_PRIVATE) */ #endif /* !defined(TOR_CIRCUITSTATS_H) */ diff --git a/src/core/or/circuituse.c b/src/core/or/circuituse.c index e2c4df25d0..6ff308dae2 100644 --- a/src/core/or/circuituse.c +++ b/src/core/or/circuituse.c @@ -37,6 +37,7 @@ #include "core/or/circuituse.h" #include "core/or/circuitpadding.h" #include "core/or/connection_edge.h" +#include "core/or/extendinfo.h" #include "core/or/policies.h" #include "feature/client/addressmap.h" #include "feature/client/bridges.h" @@ -202,8 +203,8 @@ circuit_is_acceptable(const origin_circuit_t *origin_circ, const int family = tor_addr_parse(&addr, conn->socks_request->address); if (family < 0 || - !tor_addr_eq(&build_state->chosen_exit->addr, &addr) || - build_state->chosen_exit->port != conn->socks_request->port) + !extend_info_has_orport(build_state->chosen_exit, &addr, + conn->socks_request->port)) return 0; } } @@ -1642,7 +1643,7 @@ static void circuit_testing_opened(origin_circuit_t *circ) { if (have_performed_bandwidth_test || - !check_whether_orport_reachable(get_options())) { + !router_all_orports_seem_reachable(get_options())) { /* either we've already done everything we want with testing circuits, * or this testing circuit became open due to a fluke, e.g. we picked * a last hop where we already had the connection open due to an @@ -1660,7 +1661,8 @@ static void circuit_testing_failed(origin_circuit_t *circ, int at_last_hop) { const or_options_t *options = get_options(); - if (server_mode(options) && check_whether_orport_reachable(options)) + if (server_mode(options) && + router_all_orports_seem_reachable(options)) return; log_info(LD_GENERAL, @@ -2092,11 +2094,18 @@ circuit_should_cannibalize_to_build(uint8_t purpose_to_build, } /** Launch a new circuit with purpose <b>purpose</b> and exit node - * <b>extend_info</b> (or NULL to select a random exit node). If flags - * contains CIRCLAUNCH_NEED_UPTIME, choose among routers with high uptime. If - * CIRCLAUNCH_NEED_CAPACITY is set, choose among routers with high bandwidth. - * If CIRCLAUNCH_IS_INTERNAL is true, the last hop need not be an exit node. - * If CIRCLAUNCH_ONEHOP_TUNNEL is set, the circuit will have only one hop. + * <b>extend_info</b> (or NULL to select a random exit node). + * + * If flags contains: + * - CIRCLAUNCH_ONEHOP_TUNNEL: the circuit will have only one hop; + * - CIRCLAUNCH_NEED_UPTIME: choose routers with high uptime; + * - CIRCLAUNCH_NEED_CAPACITY: choose routers with high bandwidth; + * - CIRCLAUNCH_IS_IPV6_SELFTEST: the second-last hop must support IPv6 + * extends; + * - CIRCLAUNCH_IS_INTERNAL: the last hop need not be an exit node; + * - CIRCLAUNCH_IS_V3_RP: the last hop must support v3 onion service + * rendezvous. + * * Return the newly allocated circuit on success, or NULL on failure. */ origin_circuit_t * circuit_launch_by_extend_info(uint8_t purpose, diff --git a/src/core/or/circuituse.h b/src/core/or/circuituse.h index 95d36d6474..028fe4aa48 100644 --- a/src/core/or/circuituse.h +++ b/src/core/or/circuituse.h @@ -36,17 +36,23 @@ void circuit_try_attaching_streams(origin_circuit_t *circ); void circuit_build_failed(origin_circuit_t *circ); /** Flag to set when a circuit should have only a single hop. */ -#define CIRCLAUNCH_ONEHOP_TUNNEL (1<<0) +#define CIRCLAUNCH_ONEHOP_TUNNEL (1<<0) /** Flag to set when a circuit needs to be built of high-uptime nodes */ -#define CIRCLAUNCH_NEED_UPTIME (1<<1) +#define CIRCLAUNCH_NEED_UPTIME (1<<1) /** Flag to set when a circuit needs to be built of high-capacity nodes */ -#define CIRCLAUNCH_NEED_CAPACITY (1<<2) +#define CIRCLAUNCH_NEED_CAPACITY (1<<2) /** Flag to set when the last hop of a circuit doesn't need to be an * exit node. */ -#define CIRCLAUNCH_IS_INTERNAL (1<<3) +#define CIRCLAUNCH_IS_INTERNAL (1<<3) /** Flag to set when we are trying to launch a v3 rendezvous circuit. We need * to apply some additional filters on the node picked. */ -#define CIRCLAUNCH_IS_V3_RP (1<<4) +#define CIRCLAUNCH_IS_V3_RP (1<<4) +/** Flag to set when we are trying to launch a self-testing circuit to our + * IPv6 ORPort. We need to apply some additional filters on the second-last + * node in the circuit. (We are both the client and the last node in the + * circuit.) */ +#define CIRCLAUNCH_IS_IPV6_SELFTEST (1<<5) + origin_circuit_t *circuit_launch_by_extend_info(uint8_t purpose, extend_info_t *info, int flags); diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index 26595003c1..45d2a9664a 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -70,6 +70,7 @@ #include "core/or/circuitpadding.h" #include "core/or/connection_edge.h" #include "core/or/connection_or.h" +#include "core/or/extendinfo.h" #include "core/or/policies.h" #include "core/or/reasons.h" #include "core/or/relay.h" @@ -1444,8 +1445,8 @@ connection_ap_fail_onehop(const char *failed_digest, continue; } if (tor_addr_parse(&addr, entry_conn->socks_request->address)<0 || - !tor_addr_eq(&build_state->chosen_exit->addr, &addr) || - build_state->chosen_exit->port != entry_conn->socks_request->port) + !extend_info_has_orport(build_state->chosen_exit, &addr, + entry_conn->socks_request->port)) continue; } log_info(LD_APP, "Closing one-hop stream to '%s/%s' because the OR conn " diff --git a/src/core/or/cpath_build_state_st.h b/src/core/or/cpath_build_state_st.h index ee9a0d972c..eb8e97edc5 100644 --- a/src/core/or/cpath_build_state_st.h +++ b/src/core/or/cpath_build_state_st.h @@ -24,6 +24,8 @@ struct cpath_build_state_t { unsigned int need_capacity : 1; /** Whether the last hop was picked with exiting in mind. */ unsigned int is_internal : 1; + /** Is this an IPv6 ORPort self-testing circuit? */ + unsigned int is_ipv6_selftest : 1; /** Did we pick this as a one-hop tunnel (not safe for other streams)? * These are for encrypted dir conns that exit to this router, not * for arbitrary exits from the circuit. */ diff --git a/src/core/or/crypt_path.c b/src/core/or/crypt_path.c index 8f41540848..e1bbd81251 100644 --- a/src/core/or/crypt_path.c +++ b/src/core/or/crypt_path.c @@ -30,6 +30,7 @@ #include "core/crypto/onion_crypto.h" #include "core/or/circuitbuild.h" #include "core/or/circuitlist.h" +#include "core/or/extendinfo.h" #include "lib/crypt_ops/crypto_dh.h" #include "lib/crypt_ops/crypto_util.h" @@ -259,4 +260,3 @@ cpath_get_n_hops(crypt_path_t **head_ptr) } #endif /* defined(TOR_UNIT_TESTS) */ - diff --git a/src/core/or/extend_info_st.h b/src/core/or/extend_info_st.h index a66ce24cfa..757c6a1771 100644 --- a/src/core/or/extend_info_st.h +++ b/src/core/or/extend_info_st.h @@ -15,9 +15,14 @@ #include "lib/crypt_ops/crypto_curve25519.h" #include "lib/crypt_ops/crypto_ed25519.h" +/** Largest number of addresses we handle in an extend_info. + * + * More are permitted in an EXTEND cell, but we won't handle them. */ +#define EXTEND_INFO_MAX_ADDRS 2 + /** Information on router used when extending a circuit. We don't need a * full routerinfo_t to extend: we only need addr:port:keyid to build an OR - * connection, and onion_key to create the onionskin. Note that for onehop + * connection, and onion_key to create the onionskin. Note that for one-hop * general-purpose tunnels, the onion_key is NULL. */ struct extend_info_t { char nickname[MAX_HEX_NICKNAME_LEN+1]; /**< This router's nickname for @@ -26,9 +31,12 @@ struct extend_info_t { char identity_digest[DIGEST_LEN]; /** Ed25519 identity for this router, if any. */ ed25519_public_key_t ed_identity; - uint16_t port; /**< OR port. */ - tor_addr_t addr; /**< IP address. */ - crypto_pk_t *onion_key; /**< Current onionskin key. */ + /** IP/Port values for this hop's ORPort(s). Any unused values are set + * to a null address. */ + tor_addr_port_t orports[EXTEND_INFO_MAX_ADDRS]; + /** TAP onion key for this hop. */ + crypto_pk_t *onion_key; + /** Ntor onion key for this hop. */ curve25519_public_key_t curve25519_onion_key; }; diff --git a/src/core/or/extendinfo.c b/src/core/or/extendinfo.c new file mode 100644 index 0000000000..bcdb57d5a0 --- /dev/null +++ b/src/core/or/extendinfo.c @@ -0,0 +1,305 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file extendinfo.c + * @brief Functions for creating and using extend_info_t objects. + * + * An extend_info_t is the information we hold about a relay in order to + * extend a circuit to it. + **/ + +#include "core/or/or.h" +#include "core/or/extendinfo.h" + +#include "app/config/config.h" +#include "core/or/policies.h" +#include "feature/nodelist/describe.h" +#include "feature/nodelist/nodelist.h" + +#include "core/or/extend_info_st.h" +#include "feature/nodelist/node_st.h" +#include "feature/nodelist/routerinfo_st.h" +#include "feature/nodelist/routerstatus_st.h" + +/** Allocate a new extend_info object based on the various arguments. */ +extend_info_t * +extend_info_new(const char *nickname, + const char *rsa_id_digest, + const ed25519_public_key_t *ed_id, + crypto_pk_t *onion_key, + const curve25519_public_key_t *ntor_key, + const tor_addr_t *addr, uint16_t port) +{ + extend_info_t *info = tor_malloc_zero(sizeof(extend_info_t)); + if (rsa_id_digest) + memcpy(info->identity_digest, rsa_id_digest, DIGEST_LEN); + if (ed_id && !ed25519_public_key_is_zero(ed_id)) + memcpy(&info->ed_identity, ed_id, sizeof(ed25519_public_key_t)); + if (nickname) + strlcpy(info->nickname, nickname, sizeof(info->nickname)); + if (onion_key) + info->onion_key = crypto_pk_dup_key(onion_key); + if (ntor_key) + memcpy(&info->curve25519_onion_key, ntor_key, + sizeof(curve25519_public_key_t)); + for (int i = 0; i < EXTEND_INFO_MAX_ADDRS; ++i) { + tor_addr_make_unspec(&info->orports[i].addr); + } + + if (addr) { + extend_info_add_orport(info, addr, port); + } + return info; +} + +/** + * Add another address:port pair to a given extend_info_t, if there is + * room. Return 0 on success, -1 on failure. + **/ +int +extend_info_add_orport(extend_info_t *ei, + const tor_addr_t *addr, + uint16_t port) +{ + for (int i = 0; i < EXTEND_INFO_MAX_ADDRS; ++i) { + if (tor_addr_is_unspec(&ei->orports[i].addr)) { + tor_addr_copy(&ei->orports[i].addr, addr); + ei->orports[i].port = port; + return 0; + } + } + return -1; +} + +/** Allocate and return a new extend_info that can be used to build a + * circuit to or through the node <b>node</b>. Use the primary address + * of the node (i.e. its IPv4 address) unless + * <b>for_direct_connect</b> is true, in which case the preferred + * address is used instead. May return NULL if there is not enough + * info about <b>node</b> to extend to it--for example, if the preferred + * routerinfo_t or microdesc_t is missing, or if for_direct_connect is + * true and none of the node's addresses is allowed by tor's firewall + * and IP version config. + **/ +extend_info_t * +extend_info_from_node(const node_t *node, int for_direct_connect) +{ + crypto_pk_t *rsa_pubkey = NULL; + extend_info_t *info = NULL; + tor_addr_port_t ap; + int valid_addr = 0; + + if (!node_has_preferred_descriptor(node, for_direct_connect)) { + return NULL; + } + + /* Choose a preferred address first, but fall back to an allowed address. */ + if (for_direct_connect) + fascist_firewall_choose_address_node(node, FIREWALL_OR_CONNECTION, 0, &ap); + else { + node_get_prim_orport(node, &ap); + } + valid_addr = tor_addr_port_is_valid_ap(&ap, 0); + + 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); + + /* Every node we connect or extend to must support ntor */ + if (!node_has_curve25519_onion_key(node)) { + log_fn(LOG_PROTOCOL_WARN, LD_CIRC, + "Attempted to create extend_info for a node that does not support " + "ntor: %s", node_describe(node)); + return NULL; + } + + const ed25519_public_key_t *ed_pubkey = NULL; + + /* Don't send the ed25519 pubkey unless the target node actually supports + * authenticating with it. */ + if (node_supports_ed25519_link_authentication(node, 0)) { + log_info(LD_CIRC, "Including Ed25519 ID for %s", node_describe(node)); + ed_pubkey = node_get_ed25519_id(node); + } else if (node_get_ed25519_id(node)) { + log_info(LD_CIRC, "Not including the ed25519 ID for %s, since it won't " + "be able to authenticate it.", + node_describe(node)); + } + + /* Retrieve the curve25519 pubkey. */ + const curve25519_public_key_t *curve_pubkey = + node_get_curve25519_onion_key(node); + rsa_pubkey = node_get_rsa_onion_key(node); + + if (valid_addr && node->ri) { + info = extend_info_new(node->ri->nickname, + node->identity, + ed_pubkey, + rsa_pubkey, + curve_pubkey, + &ap.addr, + ap.port); + } else if (valid_addr && node->rs && node->md) { + info = extend_info_new(node->rs->nickname, + node->identity, + ed_pubkey, + rsa_pubkey, + curve_pubkey, + &ap.addr, + ap.port); + } + + crypto_pk_free(rsa_pubkey); + return info; +} + +/** Release storage held by an extend_info_t struct. */ +void +extend_info_free_(extend_info_t *info) +{ + if (!info) + return; + crypto_pk_free(info->onion_key); + tor_free(info); +} + +/** Allocate and return a new extend_info_t with the same contents as + * <b>info</b>. */ +extend_info_t * +extend_info_dup(extend_info_t *info) +{ + extend_info_t *newinfo; + tor_assert(info); + newinfo = tor_malloc(sizeof(extend_info_t)); + memcpy(newinfo, info, sizeof(extend_info_t)); + if (info->onion_key) + newinfo->onion_key = crypto_pk_dup_key(info->onion_key); + else + newinfo->onion_key = NULL; + return newinfo; +} + +/* Does ei have a valid TAP key? */ +int +extend_info_supports_tap(const extend_info_t* ei) +{ + tor_assert(ei); + /* Valid TAP keys are not NULL */ + return ei->onion_key != NULL; +} + +/* Does ei have a valid ntor key? */ +int +extend_info_supports_ntor(const extend_info_t* ei) +{ + tor_assert(ei); + /* Valid ntor keys have at least one non-zero byte */ + return !fast_mem_is_zero( + (const char*)ei->curve25519_onion_key.public_key, + CURVE25519_PUBKEY_LEN); +} + +/* Does ei have an onion key which it would prefer to use? + * Currently, we prefer ntor keys*/ +int +extend_info_has_preferred_onion_key(const extend_info_t* ei) +{ + tor_assert(ei); + return extend_info_supports_ntor(ei); +} + +/** Return true iff the given address can be used to extend to. */ +int +extend_info_addr_is_allowed(const tor_addr_t *addr) +{ + tor_assert(addr); + + /* Check if we have a private address and if we can extend to it. */ + if ((tor_addr_is_internal(addr, 0) || tor_addr_is_multicast(addr)) && + !get_options()->ExtendAllowPrivateAddresses) { + goto disallow; + } + /* Allowed! */ + return 1; + disallow: + return 0; +} + +/** + * Return true if @a addr : @a port is a listed ORPort in @a ei. + **/ +bool +extend_info_has_orport(const extend_info_t *ei, + const tor_addr_t *addr, uint16_t port) +{ + IF_BUG_ONCE(ei == NULL) { + return false; + } + + for (int i = 0; i < EXTEND_INFO_MAX_ADDRS; ++i) { + const tor_addr_port_t *ei_ap = &ei->orports[i]; + if (tor_addr_eq(&ei_ap->addr, addr) && ei_ap->port == port) + return true; + } + return false; +} + +/** + * If the extend_info @a ei has an orport of the chosen family, then return + * that orport. Otherwise, return NULL. + **/ +const tor_addr_port_t * +extend_info_get_orport(const extend_info_t *ei, int family) +{ + for (int i = 0; i < EXTEND_INFO_MAX_ADDRS; ++i) { + if (tor_addr_is_unspec(&ei->orports[i].addr)) + continue; + if (tor_addr_family(&ei->orports[i].addr) == family) + return &ei->orports[i]; + } + return NULL; +} + +/** + * Chose an addr_port_t within @a ei to connect to. + **/ +const tor_addr_port_t * +extend_info_pick_orport(const extend_info_t *ei) +{ + // XXXX S55 -- for now, we just pick the first. We'll work on + // XXXX more choices as we move forward. + IF_BUG_ONCE(!ei) { + return NULL; + } + + if (tor_addr_is_unspec(&ei->orports[0].addr)) { + return NULL; + } + return &ei->orports[0]; +} + +/** + * Return true if any orport address in @a ei is an internal address. + **/ +bool +extend_info_any_orport_addr_is_internal(const extend_info_t *ei) +{ + IF_BUG_ONCE(ei == NULL) { + return false; + } + + for (int i = 0; i < EXTEND_INFO_MAX_ADDRS; ++i) { + if (! tor_addr_is_unspec(&ei->orports[i].addr) && + tor_addr_is_internal(&ei->orports[i].addr, 0)) + return true; + } + return false; +} diff --git a/src/core/or/extendinfo.h b/src/core/or/extendinfo.h new file mode 100644 index 0000000000..0049dd0189 --- /dev/null +++ b/src/core/or/extendinfo.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file extendinfo.h + * @brief Header for core/or/extendinfo.c + **/ + +#ifndef TOR_CORE_OR_EXTENDINFO_H +#define TOR_CORE_OR_EXTENDINFO_H + +extend_info_t *extend_info_new(const char *nickname, + const char *rsa_id_digest, + const struct ed25519_public_key_t *ed_id, + crypto_pk_t *onion_key, + const struct curve25519_public_key_t *ntor_key, + const tor_addr_t *addr, uint16_t port); +extend_info_t *extend_info_from_node(const node_t *r, int for_direct_connect); +extend_info_t *extend_info_dup(extend_info_t *info); +void extend_info_free_(extend_info_t *info); +#define extend_info_free(info) \ + FREE_AND_NULL(extend_info_t, extend_info_free_, (info)) +int extend_info_addr_is_allowed(const tor_addr_t *addr); +int extend_info_supports_tap(const extend_info_t* ei); +int extend_info_supports_ntor(const extend_info_t* ei); +int extend_info_has_preferred_onion_key(const extend_info_t* ei); +bool extend_info_has_orport(const extend_info_t *ei, + const tor_addr_t *addr, uint16_t port); +int extend_info_add_orport(extend_info_t *ei, + const tor_addr_t *addr, + uint16_t port); +const tor_addr_port_t *extend_info_get_orport(const extend_info_t *ei, + int family); +const tor_addr_port_t *extend_info_pick_orport(const extend_info_t *ei); +bool extend_info_any_orport_addr_is_internal(const extend_info_t *ei); + +#endif /* !defined(TOR_CORE_OR_EXTENDINFO_H) */ diff --git a/src/core/or/include.am b/src/core/or/include.am index 3626e76bed..af7c5a6f51 100644 --- a/src/core/or/include.am +++ b/src/core/or/include.am @@ -18,6 +18,7 @@ LIBTOR_APP_A_SOURCES += \ src/core/or/connection_edge.c \ src/core/or/connection_or.c \ src/core/or/dos.c \ + src/core/or/extendinfo.c \ src/core/or/onion.c \ src/core/or/ocirc_event.c \ src/core/or/or_periodic.c \ @@ -64,6 +65,7 @@ noinst_HEADERS += \ src/core/or/destroy_cell_queue_st.h \ src/core/or/dos.h \ src/core/or/edge_connection_st.h \ + src/core/or/extendinfo.h \ src/core/or/half_edge_st.h \ src/core/or/entry_connection_st.h \ src/core/or/entry_port_cfg_st.h \ diff --git a/src/core/or/onion.h b/src/core/or/onion.h index 256f0a3f31..600e67c3cd 100644 --- a/src/core/or/onion.h +++ b/src/core/or/onion.h @@ -48,8 +48,7 @@ typedef struct extend_cell_t { uint8_t cell_type; /** An IPv4 address and port for the node we're connecting to. */ tor_addr_port_t orport_ipv4; - /** An IPv6 address and port for the node we're connecting to. Not currently - * used. */ + /** An IPv6 address and port for the node we're connecting to. */ tor_addr_port_t orport_ipv6; /** Identity fingerprint of the node we're conecting to.*/ uint8_t node_id[DIGEST_LEN]; diff --git a/src/core/or/or.h b/src/core/or/or.h index 8758a2ec6f..d80c41371e 100644 --- a/src/core/or/or.h +++ b/src/core/or/or.h @@ -816,6 +816,18 @@ typedef struct protover_summary_flags_t { * accept EXTEND2 cells. This requires Relay=2. */ unsigned int supports_extend2_cells:1; + /** True iff this router has a version or protocol list that allows it to + * accept IPv6 connections. This requires Relay=2 or Relay=3. */ + unsigned int supports_accepting_ipv6_extends:1; + + /** True iff this router has a version or protocol list that allows it to + * initiate IPv6 connections. This requires Relay=3. */ + unsigned int supports_initiating_ipv6_extends:1; + + /** True iff this router has a version or protocol list that allows it to + * consider IPv6 connections canonical. This requires Relay=3. */ + unsigned int supports_canonical_ipv6_conns:1; + /** True iff this router has a protocol list that allows it to negotiate * ed25519 identity keys on a link handshake with us. This * requires LinkAuth=3. */ @@ -831,6 +843,10 @@ typedef struct protover_summary_flags_t { * the v3 protocol detailed in proposal 224. This requires HSIntro=4. */ unsigned int supports_ed25519_hs_intro : 1; + /** True iff this router has a protocol list that allows it to support the + * ESTABLISH_INTRO DoS cell extension. Requires HSIntro=5. */ + unsigned int supports_establish_intro_dos_extension : 1; + /** True iff this router has a protocol list that allows it to be an hidden * service directory supporting version 3 as seen in proposal 224. This * requires HSDir=2. */ @@ -842,12 +858,9 @@ typedef struct protover_summary_flags_t { unsigned int supports_v3_rendezvous_point: 1; /** True iff this router has a protocol list that allows clients to - * negotiate hs circuit setup padding. Requires Padding>=2. */ + * negotiate hs circuit setup padding. Requires Padding=2. */ unsigned int supports_hs_setup_padding : 1; - /** True iff this router has a protocol list that allows it to support the - * ESTABLISH_INTRO DoS cell extension. Requires HSIntro>=5. */ - unsigned int supports_establish_intro_dos_extension : 1; } protover_summary_flags_t; typedef struct routerinfo_t routerinfo_t; diff --git a/src/core/or/protover.c b/src/core/or/protover.c index c3f443631b..c6b0243693 100644 --- a/src/core/or/protover.c +++ b/src/core/or/protover.c @@ -326,6 +326,9 @@ protover_is_supported_here(protocol_type_t pr, uint32_t ver) /** * Return true iff "list" encodes a protocol list that includes support for * the indicated protocol and version. + * + * If the protocol list is unparseable, treat it as if it defines no + * protocols, and return 0. */ int protocol_list_supports_protocol(const char *list, protocol_type_t tp, @@ -348,6 +351,9 @@ protocol_list_supports_protocol(const char *list, protocol_type_t tp, /** * Return true iff "list" encodes a protocol list that includes support for * the indicated protocol and version, or some later version. + * + * If the protocol list is unparseable, treat it as if it defines no + * protocols, and return 0. */ int protocol_list_supports_protocol_or_later(const char *list, @@ -403,7 +409,7 @@ protover_get_supported_protocols(void) #endif "Microdesc=1-2 " "Padding=2 " - "Relay=1-2"; + "Relay=1-3"; } /** The protocols from protover_get_supported_protocols(), as parsed into a @@ -740,6 +746,9 @@ protover_compute_vote(const smartlist_t *list_of_proto_strings, * one that we support, and false otherwise. If <b>missing_out</b> is * provided, set it to the list of protocols we do not support. * + * If the protocol version string is unparseable, treat it as if it defines no + * protocols, and return 1. + * * NOTE: This is quadratic, but we don't do it much: only a few times per * consensus. Checking signatures should be way more expensive than this * ever would be. diff --git a/src/core/or/protover.h b/src/core/or/protover.h index 9509f3e8a3..2950147d1b 100644 --- a/src/core/or/protover.h +++ b/src/core/or/protover.h @@ -22,12 +22,32 @@ struct smartlist_t; /// `FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS` #define FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS "0.2.9.3-alpha" -/** The protover version number that signifies HSDir support for HSv3 */ -#define PROTOVER_HSDIR_V3 2 +/** The protover version number that signifies ed25519 link handshake support + */ +#define PROTOVER_LINKAUTH_ED25519_HANDSHAKE 3 + +/** The protover version number that signifies extend2 cell support */ +#define PROTOVER_RELAY_EXTEND2 2 +/** The protover version number where relays can accept IPv6 connections */ +#define PROTOVER_RELAY_ACCEPT_IPV6 2 +/** The protover version number where relays can initiate IPv6 extends */ +#define PROTOVER_RELAY_EXTEND_IPV6 3 +/** The protover version number where relays can consider IPv6 connections + * canonical */ +#define PROTOVER_RELAY_CANONICAL_IPV6 3 + /** The protover version number that signifies HSv3 intro point support */ #define PROTOVER_HS_INTRO_V3 4 +/** The protover version number where intro points support denial of service + * resistance */ +#define PROTOVER_HS_INTRO_DOS 5 + /** The protover version number that signifies HSv3 rendezvous point support */ #define PROTOVER_HS_RENDEZVOUS_POINT_V3 2 + +/** The protover version number that signifies HSDir support for HSv3 */ +#define PROTOVER_HSDIR_V3 2 + /** The protover that signals support for HS circuit setup padding machines */ #define PROTOVER_HS_SETUP_PADDING 2 diff --git a/src/core/or/relay.c b/src/core/or/relay.c index 75d2d479e7..6895591064 100644 --- a/src/core/or/relay.c +++ b/src/core/or/relay.c @@ -56,6 +56,7 @@ #include "core/or/circuitlist.h" #include "core/or/circuituse.h" #include "core/or/circuitpadding.h" +#include "core/or/extendinfo.h" #include "lib/compress/compress.h" #include "app/config/config.h" #include "core/mainloop/connection.h" diff --git a/src/core/or/scheduler_kist.c b/src/core/or/scheduler_kist.c index c73d768f88..5c1922847e 100644 --- a/src/core/or/scheduler_kist.c +++ b/src/core/or/scheduler_kist.c @@ -445,6 +445,11 @@ update_socket_written(socket_table_t *table, channel_t *chan, size_t bytes) * one cell for each and bouncing back and forth. This KIST impl avoids that * by only writing a channel's outbuf to the kernel if it has 8 cells or more * in it. + * + * Note: The number 8 has been picked for no particular reasons except that it + * is 4096 bytes which is a common number for buffering. A TLS record can hold + * up to 16KiB thus using 8 cells means that a relay will at most send a TLS + * record of 4KiB or 1/4 of the maximum capacity of a TLS record. */ MOCK_IMPL(int, channel_should_write_to_kernel, (outbuf_table_t *table, channel_t *chan)) diff --git a/src/core/or/versions.c b/src/core/or/versions.c index 31f1f5b997..5dfe0c2cc9 100644 --- a/src/core/or/versions.c +++ b/src/core/or/versions.c @@ -408,6 +408,10 @@ static strmap_t *protover_summary_map = NULL; /** * Helper. Given a non-NULL protover string <b>protocols</b>, set <b>out</b> * to its summary, and memoize the result in <b>protover_summary_map</b>. + * + * If the protover string does not contain any recognised protocols, sets + * protocols_known, but does not set any other flags. (Empty strings are also + * treated this way.) */ static void memoize_protover_summary(protover_summary_flags_t *out, @@ -434,25 +438,49 @@ memoize_protover_summary(protover_summary_flags_t *out, memset(out, 0, sizeof(*out)); out->protocols_known = 1; - out->supports_extend2_cells = - protocol_list_supports_protocol(protocols, PRT_RELAY, 2); + out->supports_ed25519_link_handshake_compat = - protocol_list_supports_protocol(protocols, PRT_LINKAUTH, 3); + protocol_list_supports_protocol(protocols, PRT_LINKAUTH, + PROTOVER_LINKAUTH_ED25519_HANDSHAKE); out->supports_ed25519_link_handshake_any = - protocol_list_supports_protocol_or_later(protocols, PRT_LINKAUTH, 3); + protocol_list_supports_protocol_or_later( + protocols, + PRT_LINKAUTH, + PROTOVER_LINKAUTH_ED25519_HANDSHAKE); + + out->supports_extend2_cells = + protocol_list_supports_protocol(protocols, PRT_RELAY, + PROTOVER_RELAY_EXTEND2); + out->supports_accepting_ipv6_extends = ( + protocol_list_supports_protocol(protocols, PRT_RELAY, + PROTOVER_RELAY_ACCEPT_IPV6) || + protocol_list_supports_protocol(protocols, PRT_RELAY, + PROTOVER_RELAY_EXTEND_IPV6)); + out->supports_initiating_ipv6_extends = + protocol_list_supports_protocol(protocols, PRT_RELAY, + PROTOVER_RELAY_EXTEND_IPV6); + out->supports_canonical_ipv6_conns = + protocol_list_supports_protocol(protocols, PRT_RELAY, + PROTOVER_RELAY_CANONICAL_IPV6); + out->supports_ed25519_hs_intro = - protocol_list_supports_protocol(protocols, PRT_HSINTRO, 4); - out->supports_v3_hsdir = - protocol_list_supports_protocol(protocols, PRT_HSDIR, - PROTOVER_HSDIR_V3); + protocol_list_supports_protocol(protocols, PRT_HSINTRO, + PROTOVER_HS_INTRO_V3); + out->supports_establish_intro_dos_extension = + protocol_list_supports_protocol(protocols, PRT_HSINTRO, + PROTOVER_HS_INTRO_DOS); + out->supports_v3_rendezvous_point = protocol_list_supports_protocol(protocols, PRT_HSREND, PROTOVER_HS_RENDEZVOUS_POINT_V3); + + out->supports_v3_hsdir = + protocol_list_supports_protocol(protocols, PRT_HSDIR, + PROTOVER_HSDIR_V3); + out->supports_hs_setup_padding = protocol_list_supports_protocol(protocols, PRT_PADDING, PROTOVER_HS_SETUP_PADDING); - out->supports_establish_intro_dos_extension = - protocol_list_supports_protocol(protocols, PRT_HSINTRO, 5); protover_summary_flags_t *new_cached = tor_memdup(out, sizeof(*out)); cached = strmap_set(protover_summary_map, protocols, new_cached); @@ -461,6 +489,13 @@ memoize_protover_summary(protover_summary_flags_t *out, /** Summarize the protocols listed in <b>protocols</b> into <b>out</b>, * falling back or correcting them based on <b>version</b> as appropriate. + * + * If protocols and version are both NULL or "", returns a summary with no + * flags set. + * + * If the protover string does not contain any recognised protocols, and the + * version is not recognised, sets protocols_known, but does not set any other + * flags. (Empty strings are also treated this way.) */ void summarize_protover_flags(protover_summary_flags_t *out, @@ -469,10 +504,10 @@ summarize_protover_flags(protover_summary_flags_t *out, { tor_assert(out); memset(out, 0, sizeof(*out)); - if (protocols) { + if (protocols && strcmp(protocols, "")) { memoize_protover_summary(out, protocols); } - if (version && !strcmpstart(version, "Tor ")) { + if (version && strcmp(version, "") && !strcmpstart(version, "Tor ")) { if (!out->protocols_known) { /* The version is a "Tor" version, and where there is no * list of protocol versions that we should be looking at instead. */ diff --git a/src/core/stA1RajU b/src/core/stA1RajU deleted file mode 100644 index e69de29bb2..0000000000 --- a/src/core/stA1RajU +++ /dev/null diff --git a/src/core/stiysZND b/src/core/stiysZND Binary files differdeleted file mode 100644 index faa365b769..0000000000 --- a/src/core/stiysZND +++ /dev/null diff --git a/src/ext/README b/src/ext/README deleted file mode 100644 index d7e5439c71..0000000000 --- a/src/ext/README +++ /dev/null @@ -1,79 +0,0 @@ - -OpenBSD_malloc_Linux.c: - - The OpenBSD malloc implementation, ported to Linux. Used only when - --enable-openbsd-malloc is passed to the configure script. - -strlcat.c -strlcpy.c - - Implementations of strlcat and strlcpy, the more sane replacements - for strcat and strcpy. These are nonstandard, and some libc - implementations refuse to add them for religious reasons. - -ht.h - - An implementation of a hash table in the style of Niels Provos's - tree.h. Shared with Libevent. - -tinytest.[ch] -tinytest_demos.c -tinytest_macros.h - - A unit testing framework. https://github.com/nmathewson/tinytest - -tor_queue.h - - A copy of sys/queue.h from OpenBSD. We keep our own copy rather - than using sys/queue.h, since some platforms don't have a - sys/queue.h, and the ones that do have diverged in incompatible - ways. (CIRCLEQ or no CIRCLEQ? SIMPLQ or STAILQ?) We also rename - the identifiers with a TOR_ prefix to avoid conflicts with - the system headers. - -curve25519_donna/*.c - - A copy of Adam Langley's curve25519-donna mostly-portable - implementations of curve25519. - -csiphash.c -siphash.h - - Marek Majkowski's implementation of siphash 2-4, a secure keyed - hash algorithm to avoid collision-based DoS attacks against hash - tables. - -trunnel/*.[ch] - - Headers and runtime code for Trunnel, a system for generating - code to encode and decode binary formats. - -ed25519/ref10/* - - Daniel Bernsten's portable ref10 implementation of ed25519. - Public domain. - -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 - 6.8p1. - -timeouts/ - - William Ahern's hierarchical timer-wheel implementation. MIT license. - -mulodi/ - - Contains an overflow-checking 64-bit signed integer multiply - from LLVM's compiler_rt. For some reason, this is missing from - 32-bit libclang in many places. Dual licensed MIT-license and - BSD-like license; see mulodi/LICENSE.TXT. diff --git a/src/ext/ext.md b/src/ext/ext.md new file mode 100644 index 0000000000..1eaaab605b --- /dev/null +++ b/src/ext/ext.md @@ -0,0 +1,88 @@ +@dir /ext +@brief Externally maintained code + +The "ext" directory holds code that was written elsewhere, and is not +reliably packaged as a library where we want to build, so we ship +it along with Tor. + +In general, you should not edit this code: we are not the maintainers. +Instead, you should submit patches upstream. + +OpenBSD_malloc_Linux.c: + +> The OpenBSD malloc implementation, ported to Linux. Used only when +> --enable-openbsd-malloc is passed to the configure script. + +strlcat.c +strlcpy.c + +> Implementations of strlcat and strlcpy, the more sane replacements +> for strcat and strcpy. These are nonstandard, and some libc +> implementations refuse to add them for religious reasons. + +ht.h + +> An implementation of a hash table in the style of Niels Provos's +> tree.h. Shared with Libevent. + +tinytest.c tinytest.h +tinytest_demos.c +tinytest_macros.h + +> A unit testing framework. https://github.com/nmathewson/tinytest + +tor_queue.h + +> A copy of sys/queue.h from OpenBSD. We keep our own copy rather +> than using sys/queue.h, since some platforms don't have a +> sys/queue.h, and the ones that do have diverged in incompatible +> ways. (CIRCLEQ or no CIRCLEQ? SIMPLQ or STAILQ?) We also rename +> the identifiers with a TOR_ prefix to avoid conflicts with +> the system headers. + +curve25519_donna/*.c + +> A copy of Adam Langley's curve25519-donna mostly-portable +> implementations of curve25519. + +csiphash.c +siphash.h + +> Marek Majkowski's implementation of siphash 2-4, a secure keyed +> hash algorithm to avoid collision-based DoS attacks against hash +> tables. + +trunnel/*.[ch] + +> Headers and runtime code for Trunnel, a system for generating +> code to encode and decode binary formats. + +ed25519/ref10/* + +> Daniel Bernsten's portable ref10 implementation of ed25519. +> Public domain. + +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 +> 6.8p1. + +timeouts/ + +> William Ahern's hierarchical timer-wheel implementation. MIT license. + +mulodi/ + +> Contains an overflow-checking 64-bit signed integer multiply +> from LLVM's compiler_rt. For some reason, this is missing from +> 32-bit libclang in many places. Dual licensed MIT-license and +> BSD-like license; see mulodi/LICENSE.TXT. diff --git a/src/ext/include.am b/src/ext/include.am index 317e25d78e..8b646b1b4e 100644 --- a/src/ext/include.am +++ b/src/ext/include.am @@ -1,7 +1,7 @@ AM_CPPFLAGS += -I$(srcdir)/src/ext -Isrc/ext -EXTRA_DIST += src/ext/README +EXTRA_DIST += src/ext/ext.md EXTHEADERS = \ src/ext/ht.h \ diff --git a/src/feature/client/bridges.c b/src/feature/client/bridges.c index 66b04f3bc2..caa4776493 100644 --- a/src/feature/client/bridges.c +++ b/src/feature/client/bridges.c @@ -259,12 +259,26 @@ addr_is_a_configured_bridge(const tor_addr_t *addr, /** If we have a bridge configured whose digest matches * <b>ei->identity_digest</b>, or a bridge with no known digest whose address * matches <b>ei->addr</b>:<b>ei->port</b>, return 1. Else return 0. - * If <b>ei->onion_key</b> is NULL, check for address/port matches only. */ + * If <b>ei->onion_key</b> is NULL, check for address/port matches only. + * + * Note that if the extend_info_t contains multiple addresses, we return true + * only if _every_ address is a bridge. + */ int extend_info_is_a_configured_bridge(const extend_info_t *ei) { const char *digest = ei->onion_key ? ei->identity_digest : NULL; - return addr_is_a_configured_bridge(&ei->addr, ei->port, digest); + const tor_addr_port_t *ap1 = NULL, *ap2 = NULL; + if (! tor_addr_is_null(&ei->orports[0].addr)) + ap1 = &ei->orports[0]; + if (! tor_addr_is_null(&ei->orports[1].addr)) + ap2 = &ei->orports[1]; + IF_BUG_ONCE(ap1 == NULL) { + return 0; + } + return addr_is_a_configured_bridge(&ap1->addr, ap1->port, digest) && + (ap2 == NULL || + addr_is_a_configured_bridge(&ap2->addr, ap2->port, digest)); } /** Wrapper around get_configured_bridge_by_addr_port_digest() to look diff --git a/src/feature/client/entrynodes.c b/src/feature/client/entrynodes.c index 223815e7b2..6e8259142d 100644 --- a/src/feature/client/entrynodes.c +++ b/src/feature/client/entrynodes.c @@ -1554,7 +1554,7 @@ guard_in_node_family(const entry_guard_t *guard, const node_t *node) if (get_options()->EnforceDistinctSubnets && guard->bridge_addr) { tor_addr_t node_addr; node_get_addr(node, &node_addr); - if (addrs_in_same_network_family(&node_addr, + if (router_addrs_in_same_network(&node_addr, &guard->bridge_addr->addr)) { return 1; } diff --git a/src/feature/control/control_cmd.c b/src/feature/control/control_cmd.c index d9a38011de..a8926c0b79 100644 --- a/src/feature/control/control_cmd.c +++ b/src/feature/control/control_cmd.c @@ -20,9 +20,12 @@ #include "core/or/circuitlist.h" #include "core/or/circuituse.h" #include "core/or/connection_edge.h" +#include "core/or/circuitstats.h" +#include "core/or/extendinfo.h" #include "feature/client/addressmap.h" #include "feature/client/dnsserv.h" #include "feature/client/entrynodes.h" +#include "feature/control/control_events.h" #include "feature/control/control.h" #include "feature/control/control_auth.h" #include "feature/control/control_cmd.h" @@ -55,6 +58,8 @@ #include "feature/rend/rend_encoded_v2_service_descriptor_st.h" #include "feature/rend/rend_service_descriptor_st.h" +#include "src/app/config/statefile.h" + static int control_setconf_helper(control_connection_t *conn, const control_cmd_args_t *args, int use_defaults); @@ -1396,6 +1401,34 @@ handle_control_dropguards(control_connection_t *conn, return 0; } +static const control_cmd_syntax_t droptimeouts_syntax = { + .max_args = 0, +}; + +/** Implementation for the DROPTIMEOUTS command. */ +static int +handle_control_droptimeouts(control_connection_t *conn, + const control_cmd_args_t *args) +{ + (void) args; /* We don't take arguments. */ + + static int have_warned = 0; + if (! have_warned) { + log_warn(LD_CONTROL, "DROPTIMEOUTS is dangerous; make sure you understand " + "the risks before using it. It may be removed in a future " + "version of Tor."); + have_warned = 1; + } + + circuit_build_times_reset(get_circuit_build_times_mutable()); + send_control_done(conn); + or_state_mark_dirty(get_or_state(), 0); + cbt_control_event_buildtimeout_set(get_circuit_build_times(), + BUILDTIMEOUT_SET_EVENT_RESET); + + return 0; +} + static const char *hsfetch_keywords[] = { "SERVER", NULL, }; @@ -1430,7 +1463,7 @@ handle_control_hsfetch(control_connection_t *conn, rend_valid_descriptor_id(arg1 + v2_str_len) && base32_decode(digest, sizeof(digest), arg1 + v2_str_len, REND_DESC_ID_V2_LEN_BASE32) == - REND_DESC_ID_V2_LEN_BASE32) { + sizeof(digest)) { /* We have a well formed version 2 descriptor ID. Keep the decoded value * of the id. */ desc_id = digest; @@ -2331,6 +2364,7 @@ static const control_cmd_def_t CONTROL_COMMANDS[] = ONE_LINE(protocolinfo, 0), ONE_LINE(authchallenge, CMD_FL_WIPE), ONE_LINE(dropguards, 0), + ONE_LINE(droptimeouts, 0), ONE_LINE(hsfetch, 0), MULTLINE(hspost, 0), ONE_LINE(add_onion, CMD_FL_WIPE), diff --git a/src/feature/control/control_events.c b/src/feature/control/control_events.c index 916ccea875..8e69c772f6 100644 --- a/src/feature/control/control_events.c +++ b/src/feature/control/control_events.c @@ -17,6 +17,7 @@ #include "core/mainloop/mainloop.h" #include "core/or/channeltls.h" #include "core/or/circuitlist.h" +#include "core/or/circuitstats.h" #include "core/or/command.h" #include "core/or/connection_edge.h" #include "core/or/connection_or.h" @@ -141,6 +142,64 @@ clear_circ_bw_fields(void) SMARTLIST_FOREACH_END(circ); } +/* Helper to emit the BUILDTIMEOUT_SET circuit build time event */ +void +cbt_control_event_buildtimeout_set(const circuit_build_times_t *cbt, + buildtimeout_set_event_t type) +{ + char *args = NULL; + double qnt; + double timeout_rate = 0.0; + double close_rate = 0.0; + + switch (type) { + case BUILDTIMEOUT_SET_EVENT_RESET: + case BUILDTIMEOUT_SET_EVENT_SUSPENDED: + case BUILDTIMEOUT_SET_EVENT_DISCARD: + qnt = 1.0; + break; + case BUILDTIMEOUT_SET_EVENT_COMPUTED: + case BUILDTIMEOUT_SET_EVENT_RESUME: + default: + qnt = circuit_build_times_quantile_cutoff(); + break; + } + + /* The timeout rate is the ratio of the timeout count over + * the total number of circuits attempted. The total number of + * circuits is (timeouts+succeeded), since every circuit + * either succeeds, or times out. "Closed" circuits are + * MEASURE_TIMEOUT circuits whose measurement period expired. + * All MEASURE_TIMEOUT circuits are counted in the timeouts stat + * before transitioning to MEASURE_TIMEOUT (in + * circuit_build_times_mark_circ_as_measurement_only()). + * MEASURE_TIMEOUT circuits that succeed are *not* counted as + * "succeeded". See circuit_build_times_handle_completed_hop(). + * + * We cast the denominator + * to promote it to double before the addition, to avoid int32 + * overflow. */ + const double total_circuits = + ((double)cbt->num_circ_timeouts) + cbt->num_circ_succeeded; + if (total_circuits >= 1.0) { + timeout_rate = cbt->num_circ_timeouts / total_circuits; + close_rate = cbt->num_circ_closed / total_circuits; + } + + tor_asprintf(&args, "TOTAL_TIMES=%lu " + "TIMEOUT_MS=%lu XM=%lu ALPHA=%f CUTOFF_QUANTILE=%f " + "TIMEOUT_RATE=%f CLOSE_MS=%lu CLOSE_RATE=%f", + (unsigned long)cbt->total_build_times, + (unsigned long)cbt->timeout_ms, + (unsigned long)cbt->Xm, cbt->alpha, qnt, + timeout_rate, + (unsigned long)cbt->close_ms, + close_rate); + + control_event_buildtimeout_set(type, args); + + tor_free(args); +} /** Set <b>global_event_mask*</b> to the bitwise OR of each live control * connection's event_mask field. */ void diff --git a/src/feature/control/control_events.h b/src/feature/control/control_events.h index 4a5492b510..0c8448e0f8 100644 --- a/src/feature/control/control_events.h +++ b/src/feature/control/control_events.h @@ -223,6 +223,8 @@ void control_event_hs_descriptor_content(const char *onion_address, const char *desc_id, const char *hsdir_fp, const char *content); +void cbt_control_event_buildtimeout_set(const circuit_build_times_t *cbt, + buildtimeout_set_event_t type); void control_events_free_all(void); diff --git a/src/feature/control/control_getinfo.c b/src/feature/control/control_getinfo.c index 0823acbe07..daf71f04c9 100644 --- a/src/feature/control/control_getinfo.c +++ b/src/feature/control/control_getinfo.c @@ -51,6 +51,7 @@ #include "feature/rend/rendcache.h" #include "feature/stats/geoip_stats.h" #include "feature/stats/predict_ports.h" +#include "feature/stats/rephist.h" #include "lib/version/torversion.h" #include "lib/encoding/kvline.h" @@ -1278,15 +1279,18 @@ getinfo_helper_events(control_connection_t *control_conn, *answer = tor_strdup(directories_have_accepted_server_descriptor() ? "1" : "0"); } else if (!strcmp(question, "status/reachability-succeeded/or")) { - *answer = tor_strdup(check_whether_orport_reachable(options) ? - "1" : "0"); + *answer = tor_strdup( + router_all_orports_seem_reachable(options) ? + "1" : "0"); } else if (!strcmp(question, "status/reachability-succeeded/dir")) { - *answer = tor_strdup(check_whether_dirport_reachable(options) ? - "1" : "0"); + *answer = tor_strdup( + router_dirport_seems_reachable(options) ? + "1" : "0"); } else if (!strcmp(question, "status/reachability-succeeded")) { - tor_asprintf(answer, "OR=%d DIR=%d", - check_whether_orport_reachable(options) ? 1 : 0, - check_whether_dirport_reachable(options) ? 1 : 0); + tor_asprintf( + answer, "OR=%d DIR=%d", + router_all_orports_seem_reachable(options) ? 1 : 0, + router_dirport_seems_reachable(options) ? 1 : 0); } else if (!strcmp(question, "status/bootstrap-phase")) { *answer = control_event_boot_last_msg(); } else if (!strcmpstart(question, "status/version/")) { @@ -1437,6 +1441,39 @@ getinfo_helper_liveness(control_connection_t *control_conn, return 0; } +/** Implementation helper for GETINFO: answers queries about circuit onion + * handshake rephist values */ +STATIC int +getinfo_helper_rephist(control_connection_t *control_conn, + const char *question, char **answer, + const char **errmsg) +{ + (void) control_conn; + (void) errmsg; + int result; + + if (!strcmp(question, "stats/ntor/assigned")) { + result = + rep_hist_get_circuit_handshake_assigned(ONION_HANDSHAKE_TYPE_NTOR); + } else if (!strcmp(question, "stats/ntor/requested")) { + result = + rep_hist_get_circuit_handshake_requested(ONION_HANDSHAKE_TYPE_NTOR); + } else if (!strcmp(question, "stats/tap/assigned")) { + result = + rep_hist_get_circuit_handshake_assigned(ONION_HANDSHAKE_TYPE_TAP); + } else if (!strcmp(question, "stats/tap/requested")) { + result = + rep_hist_get_circuit_handshake_requested(ONION_HANDSHAKE_TYPE_TAP); + } else { + *errmsg = "Unrecognized handshake type"; + return -1; + } + + tor_asprintf(answer, "%d", result); + + return 0; +} + /** Implementation helper for GETINFO: answers queries about shared random * value. */ static int @@ -1661,6 +1698,16 @@ static const getinfo_item_t getinfo_items[] = { "Onion services detached from the control connection."), ITEM("sr/current", sr, "Get current shared random value."), ITEM("sr/previous", sr, "Get previous shared random value."), + PREFIX("stats/ntor/", rephist, "NTor circuit handshake stats."), + ITEM("stats/ntor/assigned", rephist, + "Assigned NTor circuit handshake stats."), + ITEM("stats/ntor/requested", rephist, + "Requested NTor circuit handshake stats."), + PREFIX("stats/tap/", rephist, "TAP circuit handshake stats."), + ITEM("stats/tap/assigned", rephist, + "Assigned TAP circuit handshake stats."), + ITEM("stats/tap/requested", rephist, + "Requested TAP circuit handshake stats."), { NULL, NULL, NULL, 0 } }; diff --git a/src/feature/control/control_getinfo.h b/src/feature/control/control_getinfo.h index 0ada49258e..f61d632446 100644 --- a/src/feature/control/control_getinfo.h +++ b/src/feature/control/control_getinfo.h @@ -60,6 +60,10 @@ STATIC int getinfo_helper_current_time( control_connection_t *control_conn, const char *question, char **answer, const char **errmsg); +STATIC int getinfo_helper_rephist( + control_connection_t *control_conn, + const char *question, char **answer, + const char **errmsg); #endif /* defined(CONTROL_GETINFO_PRIVATE) */ #endif /* !defined(TOR_CONTROL_GETINFO_H) */ diff --git a/src/feature/dirauth/dirauth_config.c b/src/feature/dirauth/dirauth_config.c index a0b6de7eca..1ffd33e5f1 100644 --- a/src/feature/dirauth/dirauth_config.c +++ b/src/feature/dirauth/dirauth_config.c @@ -77,8 +77,8 @@ options_validate_dirauth_mode(const or_options_t *old_options, return 0; /* confirm that our address isn't broken, so we can complain now */ - uint32_t tmp; - if (resolve_my_address(LOG_WARN, options, &tmp, NULL, NULL) < 0) + tor_addr_t tmp; + if (!find_my_address(options, AF_INET, LOG_WARN, &tmp, NULL, NULL)) REJECT("Failed to resolve/guess local address. See logs for details."); if (!options->ContactInfo && !options->TestingTorNetwork) diff --git a/src/feature/dirauth/dirauth_options.inc b/src/feature/dirauth/dirauth_options.inc index 21f4996c39..40ef7c3bab 100644 --- a/src/feature/dirauth/dirauth_options.inc +++ b/src/feature/dirauth/dirauth_options.inc @@ -44,6 +44,13 @@ CONF_VAR(AuthDirSharedRandomness, BOOL, 0, "1") /* NOTE: remove this option someday. */ CONF_VAR(AuthDirTestEd25519LinkKeys, BOOL, 0, "1") +/** + * Bool (default 1): As an authority, should we launch tests for + * reachability, and use those results to vote on "Running"? If 0, + * we assume that every relay is Runnning. + **/ +CONF_VAR(AuthDirTestReachability, BOOL, 0, "1") + /** Authority only: key=value pairs that we add to our networkstatus * consensus vote on the 'params' line. */ CONF_VAR(ConsensusParams, STRING, 0, NULL) diff --git a/src/feature/dirauth/dirvote.c b/src/feature/dirauth/dirvote.c index 79651563b4..5fda842246 100644 --- a/src/feature/dirauth/dirvote.c +++ b/src/feature/dirauth/dirvote.c @@ -4463,7 +4463,7 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, const or_options_t *options = get_options(); const dirauth_options_t *d_options = dirauth_get_options(); networkstatus_t *v3_out = NULL; - uint32_t addr; + tor_addr_t addr; char *hostname = NULL, *client_versions = NULL, *server_versions = NULL; const char *contact; smartlist_t *routers, *routerstatuses; @@ -4492,13 +4492,13 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, log_err(LD_BUG, "Error computing identity key digest"); return NULL; } - if (resolve_my_address(LOG_WARN, options, &addr, NULL, &hostname)<0) { + if (!find_my_address(options, AF_INET, LOG_WARN, &addr, NULL, &hostname)) { log_warn(LD_NET, "Couldn't resolve my hostname"); return NULL; } if (!hostname || !strchr(hostname, '.')) { tor_free(hostname); - hostname = tor_dup_ip(addr); + hostname = tor_addr_to_str_dup(&addr); } if (!hostname) { @@ -4722,7 +4722,7 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, memcpy(voter->identity_digest, identity_digest, DIGEST_LEN); voter->sigs = smartlist_new(); voter->address = hostname; - voter->addr = addr; + voter->addr = tor_addr_to_ipv4h(&addr); voter->dir_port = router_get_advertised_dir_port(options, 0); voter->or_port = router_get_advertised_or_port(options); voter->contact = tor_strdup(contact); diff --git a/src/feature/dirauth/process_descs.c b/src/feature/dirauth/process_descs.c index 5025d0ae39..bc659d032d 100644 --- a/src/feature/dirauth/process_descs.c +++ b/src/feature/dirauth/process_descs.c @@ -409,11 +409,11 @@ dirserv_rejects_tor_version(const char *platform, return true; } - /* Series between Tor 0.3.6 and 0.4.1.4-rc inclusive are unsupported. - * Reject them. 0.3.6.0-alpha-dev only existed for a short time, before - * it was renamed to 0.4.0.0-alpha-dev. */ + /* Series between Tor 0.3.6 and 0.4.1 inclusive are unsupported. Reject + * them. 0.3.6.0-alpha-dev only existed for a short time, before it was + * renamed to 0.4.0.0-alpha-dev. */ if (tor_version_as_new_as(platform,"0.3.6.0-alpha-dev") && - !tor_version_as_new_as(platform,"0.4.1.5")) { + !tor_version_as_new_as(platform,"0.4.2.1-alpha")) { if (msg) { *msg = please_upgrade_string; } diff --git a/src/feature/dirauth/reachability.c b/src/feature/dirauth/reachability.c index 65fa27ed80..30938f528d 100644 --- a/src/feature/dirauth/reachability.c +++ b/src/feature/dirauth/reachability.c @@ -105,6 +105,8 @@ dirserv_should_launch_reachability_test(const routerinfo_t *ri, { if (!authdir_mode_handles_descs(get_options(), ri->purpose)) return 0; + if (! dirauth_get_options()->AuthDirTestReachability) + return 0; if (!ri_old) { /* New router: Launch an immediate reachability test, so we will have an * opinion soon in case we're generating a consensus soon */ @@ -189,6 +191,9 @@ dirserv_test_reachability(time_t now) * the testing, and directory authorities are easy to upgrade. Let's * wait til 0.2.0. -RD */ // time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH; + if (! dirauth_get_options()->AuthDirTestReachability) + return; + routerlist_t *rl = router_get_routerlist(); static char ctr = 0; int bridge_auth = authdir_mode_bridge(get_options()); diff --git a/src/feature/dirauth/voteflags.c b/src/feature/dirauth/voteflags.c index 477eb6f0b7..3938b61adb 100644 --- a/src/feature/dirauth/voteflags.c +++ b/src/feature/dirauth/voteflags.c @@ -487,7 +487,6 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now) unreachable. */ int answer; - const or_options_t *options = get_options(); const dirauth_options_t *dirauth_options = dirauth_get_options(); node_t *node = node_get_mutable_by_id(router->cache_info.identity_digest); tor_assert(node); @@ -501,8 +500,9 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now) /* A hibernating router is down unless we (somehow) had contact with it * since it declared itself to be hibernating. */ answer = 0; - } else if (options->AssumeReachable) { - /* If AssumeReachable, everybody is up unless they say they are down! */ + } else if (! dirauth_options->AuthDirTestReachability) { + /* If we aren't testing reachability, then everybody is up unless they say + * they are down. */ answer = 1; } else { /* Otherwise, a router counts as up if we found all announced OR diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c index ca127720f2..691c100974 100644 --- a/src/feature/dircache/dircache.c +++ b/src/feature/dircache/dircache.c @@ -142,7 +142,7 @@ write_http_response_header_impl(dir_connection_t *conn, ssize_t length, if (type) { buf_add_printf(buf, "Content-Type: %s\r\n", type); } - if (!is_local_addr(&conn->base_.addr)) { + if (!is_local_to_resolve_addr(&conn->base_.addr)) { /* Don't report the source address for a nearby/private connection. * Otherwise we tend to mis-report in cases where incoming ports are * being forwarded to a Tor server running behind the firewall. */ diff --git a/src/feature/feature.md b/src/feature/feature.md index acc3487e55..d9f7bd5c0e 100644 --- a/src/feature/feature.md +++ b/src/feature/feature.md @@ -5,3 +5,26 @@ The "feature" directory has modules that Tor uses only for a particular role or service, such as maintaining/using an onion service, operating as a relay or a client, or being a directory authority. +Current subdirectories are: + + - \refdir{feature/api} -- Support for making Tor embeddable + - \refdir{feature/client} -- Functionality which only Tor clients need + - \refdir{feature/control} -- Controller implementation + - \refdir{feature/dirauth} -- Directory authority + - \refdir{feature/dircache} -- Directory cache + - \refdir{feature/dirclient} -- Directory client + - \refdir{feature/dircommon} -- Shared code between the other directory modules + - \refdir{feature/dirparse} -- Directory parsing code. + - \refdir{feature/hibernate} -- Hibernating when Tor is out of bandwidth + or shutting down + - \refdir{feature/hs} -- v3 onion service implementation + - \refdir{feature/hs_common} -- shared code between both onion service + implementations + - \refdir{feature/keymgt} -- shared code for key management between + relays and onion services. + - \refdir{feature/nodelist} -- storing and accessing the list of relays on + the network. + - \refdir{feature/relay} -- code that only relay servers and exit servers + need. + - \refdir{feature/rend} -- v2 onion service implementation + - \refdir{feature/stats} -- statistics and history diff --git a/src/feature/hs/hs_circuit.c b/src/feature/hs/hs_circuit.c index 447f664f81..5e7a3ac9c8 100644 --- a/src/feature/hs/hs_circuit.c +++ b/src/feature/hs/hs_circuit.c @@ -16,6 +16,7 @@ #include "core/or/policies.h" #include "core/or/relay.h" #include "core/or/crypt_path.h" +#include "core/or/extendinfo.h" #include "feature/client/circpathbias.h" #include "feature/hs/hs_cell.h" #include "feature/hs/hs_circuit.h" diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c index 7f4d5385e9..80f4fb8484 100644 --- a/src/feature/hs/hs_client.c +++ b/src/feature/hs/hs_client.c @@ -16,6 +16,7 @@ #include "core/or/circuitlist.h" #include "core/or/circuituse.h" #include "core/or/connection_edge.h" +#include "core/or/extendinfo.h" #include "core/or/reasons.h" #include "feature/client/circpathbias.h" #include "feature/dirclient/dirclient.h" diff --git a/src/feature/hs/hs_common.c b/src/feature/hs/hs_common.c index 4639cdb68a..706b42529f 100644 --- a/src/feature/hs/hs_common.c +++ b/src/feature/hs/hs_common.c @@ -16,6 +16,7 @@ #include "app/config/config.h" #include "core/or/circuitbuild.h" #include "core/or/policies.h" +#include "core/or/extendinfo.h" #include "feature/dirauth/shared_random_state.h" #include "feature/hs/hs_cache.h" #include "feature/hs/hs_circuitmap.h" diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c index a42879a48f..995c1ca78b 100644 --- a/src/feature/hs/hs_service.c +++ b/src/feature/hs/hs_service.c @@ -16,6 +16,7 @@ #include "core/or/circuitbuild.h" #include "core/or/circuitlist.h" #include "core/or/circuituse.h" +#include "core/or/extendinfo.h" #include "core/or/relay.h" #include "feature/client/circpathbias.h" #include "feature/dirclient/dirclient.h" @@ -2873,6 +2874,9 @@ upload_descriptor_to_hsdir(const hs_service_t *service, hsdir->hsdir_index.store_first; char *blinded_pubkey_log_str = tor_strdup(hex_str((char*)&desc->blinded_kp.pubkey.pubkey, 32)); + /* This log message is used by Chutney as part of its bootstrap + * detection mechanism. Please don't change without first checking + * Chutney. */ log_info(LD_REND, "Service %s %s descriptor of revision %" PRIu64 " initiated upload request to %s with index %s (%s)", safe_str_client(service->onion_address), diff --git a/src/feature/nodelist/describe.c b/src/feature/nodelist/describe.c index 00896d5a44..f118436499 100644 --- a/src/feature/nodelist/describe.c +++ b/src/feature/nodelist/describe.c @@ -12,6 +12,7 @@ #define DESCRIBE_PRIVATE #include "core/or/or.h" +#include "core/or/extendinfo.h" #include "feature/nodelist/describe.h" #include "core/or/extend_info_st.h" @@ -208,11 +209,16 @@ extend_info_describe(const extend_info_t *ei) if (!ei) return "<null>"; + const tor_addr_port_t *ap4 = extend_info_get_orport(ei, AF_INET); + const tor_addr_port_t *ap6 = extend_info_get_orport(ei, AF_INET6); + uint32_t addr4 = ap4 ? tor_addr_to_ipv4h(&ap4->addr) : 0; + const tor_addr_t *addr6 = ap6 ? &ap6->addr : NULL; + return format_node_description(buf, ei->identity_digest, ei->nickname, - &ei->addr, - 0); + addr6, + addr4); } /** Set <b>buf</b> (which must have MAX_VERBOSE_NICKNAME_LEN+1 bytes) to the diff --git a/src/feature/nodelist/dirlist.c b/src/feature/nodelist/dirlist.c index 33d1bfc4d0..bd647ab530 100644 --- a/src/feature/nodelist/dirlist.c +++ b/src/feature/nodelist/dirlist.c @@ -294,7 +294,7 @@ dir_server_new(int is_authority, ent->is_authority = is_authority; ent->type = type; ent->weight = weight; - if (addrport_ipv6) { + if (addrport_ipv6 && tor_addr_port_is_valid_ap(addrport_ipv6, 0)) { 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); @@ -343,25 +343,25 @@ trusted_dir_server_new(const char *nickname, const char *address, const char *digest, const char *v3_auth_digest, dirinfo_type_t type, double weight) { - uint32_t a; tor_addr_t addr; char *hostname=NULL; dir_server_t *result; if (!address) { /* The address is us; we should guess. */ - if (resolve_my_address(LOG_WARN, get_options(), - &a, NULL, &hostname) < 0) { + if (!find_my_address(get_options(), AF_INET, LOG_WARN, &addr, + NULL, &hostname)) { log_warn(LD_CONFIG, "Couldn't find a suitable address when adding ourself as a " "trusted directory server."); return NULL; } if (!hostname) - hostname = tor_dup_ip(a); + hostname = tor_addr_to_str_dup(&addr); if (!hostname) return NULL; } else { + uint32_t a; if (tor_lookup_hostname(address, &a)) { log_warn(LD_CONFIG, "Unable to lookup address for directory server at '%s'", @@ -369,8 +369,8 @@ trusted_dir_server_new(const char *nickname, const char *address, return NULL; } hostname = tor_strdup(address); + tor_addr_from_ipv4h(&addr, a); } - tor_addr_from_ipv4h(&addr, a); result = dir_server_new(1, nickname, &addr, hostname, dir_port, or_port, diff --git a/src/feature/nodelist/networkstatus.c b/src/feature/nodelist/networkstatus.c index e07d58c91c..f63d598ef7 100644 --- a/src/feature/nodelist/networkstatus.c +++ b/src/feature/nodelist/networkstatus.c @@ -1670,7 +1670,35 @@ notify_before_networkstatus_changes(const networkstatus_t *old_c, static void notify_after_networkstatus_changes(void) { + const networkstatus_t *c = networkstatus_get_latest_consensus(); + const or_options_t *options = get_options(); + const time_t now = approx_time(); + scheduler_notify_networkstatus_changed(); + + /* The "current" consensus has just been set and it is a usable flavor so + * the first thing we need to do is recalculate the voting schedule static + * object so we can use the timings in there needed by some subsystems + * such as hidden service and shared random. */ + dirauth_sched_recalculate_timing(options, now); + reschedule_dirvote(options); + + nodelist_set_consensus(c); + + update_consensus_networkstatus_fetch_time(now); + + /* Change the cell EWMA settings */ + cmux_ewma_set_options(options, c); + + /* XXXX this call might be unnecessary here: can changing the + * current consensus really alter our view of any OR's rate limits? */ + connection_or_update_token_buckets(get_connection_array(), options); + + circuit_build_times_new_consensus_params( + get_circuit_build_times_mutable(), c); + channelpadding_new_consensus_params(c); + circpad_new_consensus_params(c); + router_new_consensus_params(c); } /** Copy all the ancillary information (like router download status and so on) @@ -2115,29 +2143,6 @@ networkstatus_set_current_consensus(const char *consensus, /* Notify that we just changed the consensus so the current global value * can be looked at. */ notify_after_networkstatus_changes(); - - /* The "current" consensus has just been set and it is a usable flavor so - * the first thing we need to do is recalculate the voting schedule static - * object so we can use the timings in there needed by some subsystems - * such as hidden service and shared random. */ - dirauth_sched_recalculate_timing(options, now); - reschedule_dirvote(options); - - nodelist_set_consensus(c); - - update_consensus_networkstatus_fetch_time(now); - - /* Change the cell EWMA settings */ - cmux_ewma_set_options(options, c); - - /* XXXX this call might be unnecessary here: can changing the - * current consensus really alter our view of any OR's rate limits? */ - connection_or_update_token_buckets(get_connection_array(), options); - - circuit_build_times_new_consensus_params( - get_circuit_build_times_mutable(), c); - channelpadding_new_consensus_params(c); - circpad_new_consensus_params(c); } /* Reset the failure count only if this consensus is actually valid. */ diff --git a/src/feature/nodelist/node_select.c b/src/feature/nodelist/node_select.c index e831248413..25904d4c63 100644 --- a/src/feature/nodelist/node_select.c +++ b/src/feature/nodelist/node_select.c @@ -321,8 +321,12 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags, overloaded_direct = smartlist_new(); overloaded_tunnel = smartlist_new(); - const int skip_or_fw = router_skip_or_reachability(options, try_ip_pref); - const int skip_dir_fw = router_skip_dir_reachability(options, try_ip_pref); + const int skip_or_fw = router_or_conn_should_skip_reachable_address_check( + options, + try_ip_pref); + const int skip_dir_fw = router_dir_conn_should_skip_reachable_address_check( + options, + try_ip_pref); const int must_have_or = dirclient_must_use_begindir(options); /* Find all the running dirservers we know about. */ @@ -926,64 +930,67 @@ nodelist_subtract(smartlist_t *sl, const smartlist_t *excluded) bitarray_free(excluded_idx); } -/** Return a random running node from the nodelist. Never - * pick a node that is in - * <b>excludedsmartlist</b>, or which matches <b>excludedset</b>, - * even if they are the only nodes available. - * If <b>CRN_NEED_UPTIME</b> is set in flags and any router has more than - * a minimum uptime, return one of those. - * If <b>CRN_NEED_CAPACITY</b> is set in flags, weight your choice by the - * advertised capacity of each router. - * If <b>CRN_NEED_GUARD</b> is set in flags, consider only Guard routers. - * If <b>CRN_WEIGHT_AS_EXIT</b> is set in flags, we weight bandwidths as if - * picking an exit node, otherwise we weight bandwidths for picking a relay - * node (that is, possibly discounting exit nodes). - * 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). +/* Node selection helper for router_choose_random_node(). + * + * Populates a node list based on <b>flags</b>, ignoring nodes in + * <b>excludednodes</b> and <b>excludedset</b>. Chooses the node based on + * <b>rule</b>. */ +static const node_t * +router_choose_random_node_helper(smartlist_t *excludednodes, + routerset_t *excludedset, + router_crn_flags_t flags, + bandwidth_weight_rule_t rule) +{ + smartlist_t *sl=smartlist_new(); + const node_t *choice = NULL; + + router_add_running_nodes_to_smartlist(sl, flags); + log_debug(LD_CIRC, + "We found %d running nodes.", + smartlist_len(sl)); + + nodelist_subtract(sl, excludednodes); + + if (excludedset) { + routerset_subtract_nodes(sl,excludedset); + log_debug(LD_CIRC, + "We removed excludedset, leaving %d nodes.", + smartlist_len(sl)); + } + + // Always weight by bandwidth + choice = node_sl_choose_by_bandwidth(sl, rule); + + smartlist_free(sl); + + return choice; +} + +/** Return a random running node from the nodelist. Never pick a node that is + * in <b>excludedsmartlist</b>, or which matches <b>excludedset</b>, even if + * they are the only nodes available. + * + * <b>flags</b> is a set of CRN_* flags, see + * router_add_running_nodes_to_smartlist() for details. */ const node_t * router_choose_random_node(smartlist_t *excludedsmartlist, routerset_t *excludedset, router_crn_flags_t flags) -{ /* XXXX MOVE */ - const int need_uptime = (flags & CRN_NEED_UPTIME) != 0; - const int need_capacity = (flags & CRN_NEED_CAPACITY) != 0; - const int need_guard = (flags & CRN_NEED_GUARD) != 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; - const int direct_conn = (flags & CRN_DIRECT_CONN) != 0; - const int rendezvous_v3 = (flags & CRN_RENDEZVOUS_V3) != 0; - - const smartlist_t *node_list = nodelist_get_list(); - smartlist_t *sl=smartlist_new(), - *excludednodes=smartlist_new(); +{ + /* A limited set of flags, used for fallback node selection. + */ + const bool need_uptime = (flags & CRN_NEED_UPTIME) != 0; + const bool need_capacity = (flags & CRN_NEED_CAPACITY) != 0; + const bool need_guard = (flags & CRN_NEED_GUARD) != 0; + const bool pref_addr = (flags & CRN_PREF_ADDR) != 0; + + smartlist_t *excludednodes=smartlist_new(); const node_t *choice = NULL; const routerinfo_t *r; bandwidth_weight_rule_t rule; - tor_assert(!(weight_for_exit && need_guard)); - rule = weight_for_exit ? WEIGHT_FOR_EXIT : - (need_guard ? WEIGHT_FOR_GUARD : WEIGHT_FOR_MID); - - SMARTLIST_FOREACH_BEGIN(node_list, const node_t *, node) { - if (node_allows_single_hop_exits(node)) { - /* Exclude relays that allow single hop exit circuits. This is an - * obsolete option since 0.2.9.2-alpha and done by default in - * 0.3.1.0-alpha. */ - smartlist_add(excludednodes, (node_t*)node); - } else if (rendezvous_v3 && - !node_supports_v3_rendezvous_point(node)) { - /* Exclude relays that do not support to rendezvous for a hidden service - * version 3. */ - smartlist_add(excludednodes, (node_t*)node); - } - } SMARTLIST_FOREACH_END(node); + rule = (need_guard ? WEIGHT_FOR_GUARD : WEIGHT_FOR_MID); /* If the node_t is not found we won't be to exclude ourself but we * won't be able to pick ourself in router_choose_random_node() so @@ -991,41 +998,30 @@ router_choose_random_node(smartlist_t *excludedsmartlist, if ((r = router_get_my_routerinfo())) routerlist_add_node_and_family(excludednodes, r); - router_add_running_nodes_to_smartlist(sl, need_uptime, need_capacity, - need_guard, need_desc, pref_addr, - direct_conn); - log_debug(LD_CIRC, - "We found %d running nodes.", - smartlist_len(sl)); - if (excludedsmartlist) { smartlist_add_all(excludednodes, excludedsmartlist); } - nodelist_subtract(sl, excludednodes); - if (excludedset) { - routerset_subtract_nodes(sl,excludedset); - log_debug(LD_CIRC, - "We removed excludedset, leaving %d nodes.", - smartlist_len(sl)); - } + choice = router_choose_random_node_helper(excludednodes, + excludedset, + flags, + rule); - // Always weight by bandwidth - choice = node_sl_choose_by_bandwidth(sl, rule); - - smartlist_free(sl); if (!choice && (need_uptime || need_capacity || need_guard || pref_addr)) { - /* try once more -- recurse but with fewer restrictions. */ + /* try once more, with fewer restrictions. */ log_info(LD_CIRC, - "We couldn't find any live%s%s%s routers; falling back " + "We couldn't find any live%s%s%s%s routers; falling back " "to list of all routers.", need_capacity?", fast":"", need_uptime?", stable":"", - need_guard?", guard":""); + need_guard?", guard":"", + pref_addr?", preferred address":""); flags &= ~ (CRN_NEED_UPTIME|CRN_NEED_CAPACITY|CRN_NEED_GUARD| CRN_PREF_ADDR); - choice = router_choose_random_node( - excludedsmartlist, excludedset, flags); + choice = router_choose_random_node_helper(excludednodes, + excludedset, + flags, + rule); } smartlist_free(excludednodes); if (!choice) { @@ -1120,8 +1116,12 @@ router_pick_trusteddirserver_impl(const smartlist_t *sourcelist, overloaded_direct = smartlist_new(); overloaded_tunnel = smartlist_new(); - const int skip_or_fw = router_skip_or_reachability(options, try_ip_pref); - const int skip_dir_fw = router_skip_dir_reachability(options, try_ip_pref); + const int skip_or_fw = router_or_conn_should_skip_reachable_address_check( + options, + try_ip_pref); + const int skip_dir_fw = router_dir_conn_should_skip_reachable_address_check( + options, + try_ip_pref); const int must_have_or = dirclient_must_use_begindir(options); SMARTLIST_FOREACH_BEGIN(sourcelist, const dir_server_t *, d) diff --git a/src/feature/nodelist/node_select.h b/src/feature/nodelist/node_select.h index 2e67f990f6..1776d8ea1a 100644 --- a/src/feature/nodelist/node_select.h +++ b/src/feature/nodelist/node_select.h @@ -14,20 +14,26 @@ /** Flags to be passed to control router_choose_random_node() to indicate what * kind of nodes to pick according to what algorithm. */ typedef enum router_crn_flags_t { + /* Try to choose stable nodes. */ CRN_NEED_UPTIME = 1<<0, + /* Try to choose nodes with a reasonable amount of bandwidth. */ CRN_NEED_CAPACITY = 1<<1, - CRN_NEED_GUARD = 1<<2, - /* XXXX not used, apparently. */ - CRN_WEIGHT_AS_EXIT = 1<<5, - CRN_NEED_DESC = 1<<6, - /* On clients, only provide nodes that satisfy ClientPreferIPv6OR */ - CRN_PREF_ADDR = 1<<7, + /* Only choose nodes if we have downloaded their descriptor or + * microdescriptor. */ + CRN_NEED_DESC = 1<<2, + /* Choose nodes that can be used as Guard relays. */ + CRN_NEED_GUARD = 1<<3, /* On clients, only provide nodes that we can connect to directly, based on - * our firewall rules */ - CRN_DIRECT_CONN = 1<<8, - /* On clients, only provide nodes with HSRend >= 2 protocol version which - * is required for hidden service version >= 3. */ - CRN_RENDEZVOUS_V3 = 1<<9, + * our firewall rules. */ + CRN_DIRECT_CONN = 1<<4, + /* On clients, if choosing a node for a direct connection, only provide + * nodes that satisfy ClientPreferIPv6OR. */ + CRN_PREF_ADDR = 1<<5, + /* On clients, only provide nodes with HSRend=2 protocol version which + * is required for hidden service version 3. */ + CRN_RENDEZVOUS_V3 = 1<<6, + /* On clients, only provide nodes that can initiate IPv6 extends. */ + CRN_INITIATE_IPV6_EXTEND = 1<<7, } router_crn_flags_t; /** Possible ways to weight routers when choosing one randomly. See diff --git a/src/feature/nodelist/node_st.h b/src/feature/nodelist/node_st.h index b1ec4db202..3769f9dc84 100644 --- a/src/feature/nodelist/node_st.h +++ b/src/feature/nodelist/node_st.h @@ -84,12 +84,11 @@ struct node_t { /* Local info: derived. */ - /** 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? */ + /** True if the IPv6 OR port is preferred over the IPv4 OR port. */ unsigned int ipv6_preferred:1; /** According to the geoip db what country is this router in? */ - /* XXXprop186 what is this suppose to mean with multiple OR ports? */ + /* IPv6: what is this supposed to mean with multiple OR ports? */ country_t country; /* The below items are used only by authdirservers for diff --git a/src/feature/nodelist/nodelist.c b/src/feature/nodelist/nodelist.c index 7454f342f9..a3c94554ec 100644 --- a/src/feature/nodelist/nodelist.c +++ b/src/feature/nodelist/nodelist.c @@ -621,7 +621,7 @@ get_estimated_address_per_node, (void)) * and grab microdescriptors into nodes as appropriate. */ void -nodelist_set_consensus(networkstatus_t *ns) +nodelist_set_consensus(const networkstatus_t *ns) { const or_options_t *options = get_options(); int authdir = authdir_mode_v3(options); @@ -952,7 +952,7 @@ nodelist_assert_ok(void) /** Ensure that the nodelist has been created with the most recent consensus. * If that's not the case, make it so. */ void -nodelist_ensure_freshness(networkstatus_t *ns) +nodelist_ensure_freshness(const networkstatus_t *ns) { tor_assert(ns); @@ -1133,7 +1133,7 @@ node_ed25519_id_matches(const node_t *node, const ed25519_public_key_t *id) /** Dummy object that should be unreturnable. Used to ensure that * node_get_protover_summary_flags() always returns non-NULL. */ static const protover_summary_flags_t zero_protover_flags = { - 0,0,0,0,0,0,0,0,0 + 0,0,0,0,0,0,0,0,0,0,0,0 }; /** Return the protover_summary_flags for a given node. */ @@ -1158,9 +1158,9 @@ node_get_protover_summary_flags(const node_t *node) * by ed25519 ID during the link handshake. If <b>compatible_with_us</b>, * it needs to be using a link authentication method that we understand. * If not, any plausible link authentication method will do. */ -MOCK_IMPL(int, +MOCK_IMPL(bool, node_supports_ed25519_link_authentication,(const node_t *node, - int compatible_with_us)) + bool compatible_with_us)) { if (! node_get_ed25519_id(node)) return 0; @@ -1175,7 +1175,7 @@ node_supports_ed25519_link_authentication,(const node_t *node, /** Return true iff <b>node</b> supports the hidden service directory version * 3 protocol (proposal 224). */ -int +bool node_supports_v3_hsdir(const node_t *node) { tor_assert(node); @@ -1185,7 +1185,7 @@ node_supports_v3_hsdir(const node_t *node) /** Return true iff <b>node</b> supports ed25519 authentication as an hidden * service introduction point.*/ -int +bool node_supports_ed25519_hs_intro(const node_t *node) { tor_assert(node); @@ -1193,9 +1193,24 @@ node_supports_ed25519_hs_intro(const node_t *node) return node_get_protover_summary_flags(node)->supports_ed25519_hs_intro; } +/** Return true iff <b>node</b> can be a rendezvous point for hidden + * service version 3 (HSRend=2). */ +bool +node_supports_v3_rendezvous_point(const node_t *node) +{ + tor_assert(node); + + /* We can't use a v3 rendezvous point without the curve25519 onion pk. */ + if (!node_get_curve25519_onion_key(node)) { + return 0; + } + + return node_get_protover_summary_flags(node)->supports_v3_rendezvous_point; +} + /** Return true iff <b>node</b> supports the DoS ESTABLISH_INTRO cell * extenstion. */ -int +bool node_supports_establish_intro_dos_extension(const node_t *node) { tor_assert(node); @@ -1204,19 +1219,54 @@ node_supports_establish_intro_dos_extension(const node_t *node) supports_establish_intro_dos_extension; } -/** Return true iff <b>node</b> supports to be a rendezvous point for hidden - * service version 3 (HSRend=2). */ -int -node_supports_v3_rendezvous_point(const node_t *node) +/** Return true iff <b>node</b> can initiate IPv6 extends (Relay=3). + * + * This check should only be performed by client path selection code. + * + * Extending relays should check their own IPv6 support using + * router_can_extend_over_ipv6(). Like other extends, they should not verify + * the link specifiers in the extend cell against the consensus, because it + * may be out of date. */ +bool +node_supports_initiating_ipv6_extends(const node_t *node) { tor_assert(node); - /* We can't use a v3 rendezvous point without the curve25519 onion pk. */ - if (!node_get_curve25519_onion_key(node)) { + /* Relays can't initiate an IPv6 extend, unless they have an IPv6 ORPort. */ + if (!node_has_ipv6_orport(node)) { return 0; } - return node_get_protover_summary_flags(node)->supports_v3_rendezvous_point; + /* Initiating relays also need to support the relevant protocol version. */ + return + node_get_protover_summary_flags(node)->supports_initiating_ipv6_extends; +} + +/** Return true iff <b>node</b> can accept IPv6 extends (Relay=2 or Relay=3) + * from other relays. If <b>need_canonical_ipv6_conn</b> is true, also check + * if the relay supports canonical IPv6 connections (Relay=3 only). + * + * This check should only be performed by client path selection code. + */ +bool +node_supports_accepting_ipv6_extends(const node_t *node, + bool need_canonical_ipv6_conn) +{ + tor_assert(node); + + /* Relays can't accept an IPv6 extend, unless they have an IPv6 ORPort. */ + if (!node_has_ipv6_orport(node)) { + return 0; + } + + /* Accepting relays also need to support the relevant protocol version. */ + if (need_canonical_ipv6_conn) { + return + node_get_protover_summary_flags(node)->supports_canonical_ipv6_conns; + } else { + return + node_get_protover_summary_flags(node)->supports_accepting_ipv6_extends; + } } /** Return the RSA ID key's SHA1 digest for the provided node. */ @@ -1984,7 +2034,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 */ int -addrs_in_same_network_family(const tor_addr_t *a1, +router_addrs_in_same_network(const tor_addr_t *a1, const tor_addr_t *a2) { if (tor_addr_is_null(a1) || tor_addr_is_null(a2)) @@ -2100,8 +2150,8 @@ nodes_in_same_family(const node_t *node1, const node_t *node2) node_get_pref_ipv6_orport(node1, &ap6_1); node_get_pref_ipv6_orport(node2, &ap6_2); - if (addrs_in_same_network_family(&a1, &a2) || - addrs_in_same_network_family(&ap6_1.addr, &ap6_2.addr)) + if (router_addrs_in_same_network(&a1, &a2) || + router_addrs_in_same_network(&ap6_1.addr, &ap6_2.addr)) return 1; } @@ -2159,8 +2209,8 @@ nodelist_add_node_and_family(smartlist_t *sl, const node_t *node) tor_addr_port_t ap6; node_get_addr(node2, &a); node_get_pref_ipv6_orport(node2, &ap6); - if (addrs_in_same_network_family(&a, &node_addr) || - addrs_in_same_network_family(&ap6.addr, &node_ap6.addr)) + if (router_addrs_in_same_network(&a, &node_addr) || + router_addrs_in_same_network(&ap6.addr, &node_ap6.addr)) smartlist_add(sl, (void*)node2); } SMARTLIST_FOREACH_END(node2); } diff --git a/src/feature/nodelist/nodelist.h b/src/feature/nodelist/nodelist.h index 57ab2d5913..06cd7916ff 100644 --- a/src/feature/nodelist/nodelist.h +++ b/src/feature/nodelist/nodelist.h @@ -32,8 +32,8 @@ const node_t *node_get_by_hex_id(const char *identity_digest, unsigned flags); node_t *nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out); node_t *nodelist_add_microdesc(microdesc_t *md); -void nodelist_set_consensus(networkstatus_t *ns); -void nodelist_ensure_freshness(networkstatus_t *ns); +void nodelist_set_consensus(const networkstatus_t *ns); +void nodelist_ensure_freshness(const networkstatus_t *ns); int nodelist_probably_contains_address(const tor_addr_t *addr); void nodelist_add_addr4_to_address_set(const uint32_t addr); void nodelist_add_addr6_to_address_set(const tor_addr_t *addr); @@ -74,13 +74,17 @@ MOCK_DECL(const struct ed25519_public_key_t *,node_get_ed25519_id, (const node_t *node)); int node_ed25519_id_matches(const node_t *node, const struct ed25519_public_key_t *id); -MOCK_DECL(int,node_supports_ed25519_link_authentication, +MOCK_DECL(bool,node_supports_ed25519_link_authentication, (const node_t *node, - int compatible_with_us)); -int node_supports_v3_hsdir(const node_t *node); -int node_supports_ed25519_hs_intro(const node_t *node); -int node_supports_v3_rendezvous_point(const node_t *node); -int node_supports_establish_intro_dos_extension(const node_t *node); + bool compatible_with_us)); +bool node_supports_v3_hsdir(const node_t *node); +bool node_supports_ed25519_hs_intro(const node_t *node); +bool node_supports_v3_rendezvous_point(const node_t *node); +bool node_supports_establish_intro_dos_extension(const node_t *node); +bool node_supports_initiating_ipv6_extends(const node_t *node); +bool node_supports_accepting_ipv6_extends(const node_t *node, + bool need_canonical_ipv6_conn); + const uint8_t *node_get_rsa_id_digest(const node_t *node); MOCK_DECL(smartlist_t *,node_get_link_specifier_smartlist,(const node_t *node, bool direct_conn)); @@ -124,7 +128,7 @@ int node_is_unreliable(const node_t *router, int need_uptime, int router_exit_policy_all_nodes_reject(const tor_addr_t *addr, uint16_t port, int need_uptime); void router_set_status(const char *digest, int up); -int addrs_in_same_network_family(const tor_addr_t *a1, +int router_addrs_in_same_network(const tor_addr_t *a1, const tor_addr_t *a2); /** router_have_minimum_dir_info tests to see if we have enough diff --git a/src/feature/nodelist/routerinfo.c b/src/feature/nodelist/routerinfo.c index 0bf2a977f5..55f21dfe67 100644 --- a/src/feature/nodelist/routerinfo.c +++ b/src/feature/nodelist/routerinfo.c @@ -17,14 +17,37 @@ #include "feature/nodelist/node_st.h" #include "feature/nodelist/routerinfo_st.h" -/** Copy the primary (IPv4) OR port (IP address and TCP port) for - * <b>router</b> into *<b>ap_out</b>. */ -void -router_get_prim_orport(const routerinfo_t *router, tor_addr_port_t *ap_out) +/** Copy the OR port (IP address and TCP port) for <b>router</b> and + * <b>family</b> into *<b>ap_out</b>. + * + * If the requested ORPort does not exist, sets *<b>ap_out</b> to the null + * address and port, and returns -1. Otherwise, returns 0. */ +int +router_get_orport(const routerinfo_t *router, + tor_addr_port_t *ap_out, + int family) { tor_assert(ap_out != NULL); - tor_addr_from_ipv4h(&ap_out->addr, router->addr); - ap_out->port = router->or_port; + if (family == AF_INET) { + tor_addr_from_ipv4h(&ap_out->addr, router->addr); + ap_out->port = router->or_port; + return 0; + } else if (family == AF_INET6) { + /* IPv6 addresses are optional, so check if it is valid. */ + if (tor_addr_port_is_valid(&router->ipv6_addr, router->ipv6_orport, 0)) { + tor_addr_copy(&ap_out->addr, &router->ipv6_addr); + ap_out->port = router->ipv6_orport; + return 0; + } else { + tor_addr_port_make_null_ap(ap_out, AF_INET6); + return -1; + } + } else { + /* Unsupported address family */ + tor_assert_nonfatal_unreached(); + tor_addr_port_make_null_ap(ap_out, AF_UNSPEC); + return -1; + } } int diff --git a/src/feature/nodelist/routerinfo.h b/src/feature/nodelist/routerinfo.h index 604e478999..2e12cbeba3 100644 --- a/src/feature/nodelist/routerinfo.h +++ b/src/feature/nodelist/routerinfo.h @@ -12,8 +12,9 @@ #ifndef TOR_ROUTERINFO_H #define TOR_ROUTERINFO_H -void router_get_prim_orport(const routerinfo_t *router, - tor_addr_port_t *addr_port_out); +int router_get_orport(const routerinfo_t *router, + tor_addr_port_t *addr_port_out, + int family); int router_has_orport(const routerinfo_t *router, const tor_addr_port_t *orport); diff --git a/src/feature/nodelist/routerlist.c b/src/feature/nodelist/routerlist.c index 96bea5a670..1afbdf056f 100644 --- a/src/feature/nodelist/routerlist.c +++ b/src/feature/nodelist/routerlist.c @@ -465,11 +465,20 @@ router_reload_router_list(void) return 0; } -/* When iterating through the routerlist, can OR address/port preference - * and reachability checks be skipped? +/* When selecting a router for a direct connection, can OR address/port + * preference and reachability checks be skipped? + * + * Servers never check ReachableAddresses or ClientPreferIPv6. Returns + * true for servers. + * + * Otherwise, if <b>try_ip_pref</b> is true, returns false. Used to make + * clients check ClientPreferIPv6, even if ReachableAddresses is not set. + * Finally, return true if ReachableAddresses is set. */ int -router_skip_or_reachability(const or_options_t *options, int try_ip_pref) +router_or_conn_should_skip_reachable_address_check( + 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, @@ -477,11 +486,15 @@ router_skip_or_reachability(const or_options_t *options, int try_ip_pref) return server_mode(options) || (!try_ip_pref && !firewall_is_fascist_or()); } -/* When iterating through the routerlist, can Dir address/port preference +/* When selecting a router for a direct connection, can Dir address/port * and reachability checks be skipped? + * + * This function is obsolete, because clients only use ORPorts. */ int -router_skip_dir_reachability(const or_options_t *options, int try_ip_pref) +router_dir_conn_should_skip_reachable_address_check( + 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, @@ -498,40 +511,109 @@ routers_have_same_or_addrs(const routerinfo_t *r1, const routerinfo_t *r2) r1->ipv6_orport == r2->ipv6_orport; } +/* Returns true if <b>node</b> can be chosen based on <b>flags</b>. + * + * The following conditions are applied to all nodes: + * - is running; + * - is valid; + * - supports EXTEND2 cells; + * - has an ntor circuit crypto key; and + * - does not allow single-hop exits. + * + * If the node has a routerinfo, we're checking for a direct connection, and + * we're using bridges, the following condition is applied: + * - has a bridge-purpose routerinfo; + * and for all other nodes: + * - has a general-purpose routerinfo (or no routerinfo). + * + * Nodes that don't have a routerinfo must be general-purpose nodes, because + * routerstatuses and microdescriptors only come via consensuses. + * + * The <b>flags</b> chech that <b>node</b>: + * - <b>CRN_NEED_UPTIME</b>: has more than a minimum uptime; + * - <b>CRN_NEED_CAPACITY</b>: has more than a minimum capacity; + * - <b>CRN_NEED_GUARD</b>: is a Guard; + * - <b>CRN_NEED_DESC</b>: has a routerinfo or microdescriptor -- that is, + * enough info to be used to build a circuit; + * - <b>CRN_DIRECT_CONN</b>: is suitable for direct connections. Checks + * for the relevant descriptors. Checks the address + * against ReachableAddresses, ClientUseIPv4 0, and + * fascist_firewall_use_ipv6() == 0); + * - <b>CRN_PREF_ADDR</b>: if we are connecting directly to the node, it has + * an address that is preferred by the + * ClientPreferIPv6ORPort setting; + * - <b>CRN_RENDEZVOUS_V3</b>: can become a v3 onion service rendezvous point; + * - <b>CRN_INITIATE_IPV6_EXTEND</b>: can initiate IPv6 extends. + */ +bool +router_can_choose_node(const node_t *node, int flags) +{ + /* The full set of flags used for node selection. */ + const bool need_uptime = (flags & CRN_NEED_UPTIME) != 0; + const bool need_capacity = (flags & CRN_NEED_CAPACITY) != 0; + const bool need_guard = (flags & CRN_NEED_GUARD) != 0; + const bool need_desc = (flags & CRN_NEED_DESC) != 0; + const bool pref_addr = (flags & CRN_PREF_ADDR) != 0; + const bool direct_conn = (flags & CRN_DIRECT_CONN) != 0; + const bool rendezvous_v3 = (flags & CRN_RENDEZVOUS_V3) != 0; + const bool initiate_ipv6_extend = (flags & CRN_INITIATE_IPV6_EXTEND) != 0; + + const or_options_t *options = get_options(); + const bool check_reach = + !router_or_conn_should_skip_reachable_address_check(options, pref_addr); + const bool direct_bridge = direct_conn && options->UseBridges; + + if (!node->is_running || !node->is_valid) + return false; + if (need_desc && !node_has_preferred_descriptor(node, direct_conn)) + return false; + if (node->ri) { + if (direct_bridge && node->ri->purpose != ROUTER_PURPOSE_BRIDGE) + return false; + else if (node->ri->purpose != ROUTER_PURPOSE_GENERAL) + return false; + } + if (node_is_unreliable(node, need_uptime, need_capacity, need_guard)) + return false; + /* Don't choose nodes if we are certain they can't do EXTEND2 cells */ + if (node->rs && !routerstatus_version_supports_extend2_cells(node->rs, 1)) + return false; + /* Don't choose nodes if we are certain they can't do ntor. */ + if ((node->ri || node->md) && !node_has_curve25519_onion_key(node)) + return false; + /* Exclude relays that allow single hop exit circuits. This is an + * obsolete option since 0.2.9.2-alpha and done by default in + * 0.3.1.0-alpha. */ + if (node_allows_single_hop_exits(node)) + return false; + /* Exclude relays that can not become a rendezvous for a hidden service + * version 3. */ + if (rendezvous_v3 && + !node_supports_v3_rendezvous_point(node)) + return false; + /* Choose a node with an OR address that matches the firewall rules */ + if (direct_conn && check_reach && + !fascist_firewall_allows_node(node, + FIREWALL_OR_CONNECTION, + pref_addr)) + return false; + if (initiate_ipv6_extend && !node_supports_initiating_ipv6_extends(node)) + return false; + + return true; +} + /** Add every suitable node from our nodelist to <b>sl</b>, so that - * we can pick a node for a circuit. + * we can pick a node for a circuit based on <b>flags</b>. + * + * See router_can_choose_node() for details of <b>flags</b>. */ void -router_add_running_nodes_to_smartlist(smartlist_t *sl, int need_uptime, - int need_capacity, int need_guard, - int need_desc, int pref_addr, - int direct_conn) -{ - const int check_reach = !router_skip_or_reachability(get_options(), - pref_addr); - /* XXXX MOVE */ +router_add_running_nodes_to_smartlist(smartlist_t *sl, int flags) +{ SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) { - if (!node->is_running || !node->is_valid) - continue; - if (need_desc && !node_has_preferred_descriptor(node, direct_conn)) - continue; - if (node->ri && node->ri->purpose != ROUTER_PURPOSE_GENERAL) + if (!router_can_choose_node(node, flags)) continue; - if (node_is_unreliable(node, need_uptime, need_capacity, need_guard)) - continue; - /* Don't choose nodes if we are certain they can't do EXTEND2 cells */ - if (node->rs && !routerstatus_version_supports_extend2_cells(node->rs, 1)) - continue; - /* Don't choose nodes if we are certain they can't do ntor. */ - if ((node->ri || node->md) && !node_has_curve25519_onion_key(node)) - continue; - /* Choose a node with an OR address that matches the firewall rules */ - if (direct_conn && check_reach && - !fascist_firewall_allows_node(node, - FIREWALL_OR_CONNECTION, - pref_addr)) - continue; - smartlist_add(sl, (void *)node); } SMARTLIST_FOREACH_END(node); } diff --git a/src/feature/nodelist/routerlist.h b/src/feature/nodelist/routerlist.h index 81a2343540..98472b2771 100644 --- a/src/feature/nodelist/routerlist.h +++ b/src/feature/nodelist/routerlist.h @@ -50,14 +50,16 @@ typedef enum was_router_added_t { int router_reload_router_list(void); -int router_skip_or_reachability(const or_options_t *options, int try_ip_pref); -int router_skip_dir_reachability(const or_options_t *options, int try_ip_pref); +int router_or_conn_should_skip_reachable_address_check( + const or_options_t *options, + int try_ip_pref); +int router_dir_conn_should_skip_reachable_address_check( + const or_options_t *options, + int try_ip_pref); 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 need_uptime, - int need_capacity, int need_guard, - int need_desc, int pref_addr, - int direct_conn); +bool router_can_choose_node(const node_t *node, int flags); +void router_add_running_nodes_to_smartlist(smartlist_t *sl, int flags); const routerinfo_t *routerlist_find_my_routerinfo(void); uint32_t router_get_advertised_bandwidth(const routerinfo_t *router); diff --git a/src/feature/nodelist/routerset.c b/src/feature/nodelist/routerset.c index 2e06ecbf04..cba7203d44 100644 --- a/src/feature/nodelist/routerset.c +++ b/src/feature/nodelist/routerset.c @@ -223,11 +223,11 @@ routerset_len(const routerset_t *set) * * (If country is -1, then we take the country * from addr.) */ -STATIC int -routerset_contains(const routerset_t *set, const tor_addr_t *addr, - uint16_t orport, - const char *nickname, const char *id_digest, - country_t country) +static int +routerset_contains2(const routerset_t *set, const tor_addr_t *addr, + uint16_t orport, const tor_addr_t *addr2, + uint16_t orport2, const char *nickname, + const char *id_digest, country_t country) { if (!set || !set->list) return 0; @@ -238,6 +238,9 @@ routerset_contains(const routerset_t *set, const tor_addr_t *addr, if (addr && compare_tor_addr_to_addr_policy(addr, orport, set->policies) == ADDR_POLICY_REJECTED) return 3; + if (addr2 && compare_tor_addr_to_addr_policy(addr2, orport2, set->policies) + == ADDR_POLICY_REJECTED) + return 3; if (set->countries) { if (country < 0 && addr) country = geoip_get_country_by_addr(addr); @@ -249,6 +252,17 @@ routerset_contains(const routerset_t *set, const tor_addr_t *addr, return 0; } +/** Helper. Like routerset_contains2() but for a single IP/port combo. + */ +STATIC int +routerset_contains(const routerset_t *set, const tor_addr_t *addr, + uint16_t orport, const char *nickname, + const char *id_digest, country_t country) +{ + return routerset_contains2(set, addr, orport, NULL, 0, + nickname, id_digest, country); +} + /** If *<b>setp</b> includes at least one country code, or if * <b>only_some_cc_set</b> is 0, add the ?? and A1 country codes to * *<b>setp</b>, creating it as needed. Return true iff *<b>setp</b> changed. @@ -292,12 +306,19 @@ routerset_add_unknown_ccs(routerset_t **setp, int only_if_some_cc_set) int routerset_contains_extendinfo(const routerset_t *set, const extend_info_t *ei) { - return routerset_contains(set, - &ei->addr, - ei->port, - ei->nickname, - ei->identity_digest, - -1 /*country*/); + const tor_addr_port_t *ap1 = NULL, *ap2 = NULL; + if (! tor_addr_is_null(&ei->orports[0].addr)) + ap1 = &ei->orports[0]; + if (! tor_addr_is_null(&ei->orports[1].addr)) + ap2 = &ei->orports[1]; + return routerset_contains2(set, + ap1 ? &ap1->addr : NULL, + ap1 ? ap1->port : 0, + ap2 ? &ap2->addr : NULL, + ap2 ? ap2->port : 0, + ei->nickname, + ei->identity_digest, + -1 /*country*/); } /** Return true iff <b>ri</b> is in <b>set</b>. If country is <b>-1</b>, we @@ -306,14 +327,11 @@ int routerset_contains_router(const routerset_t *set, const routerinfo_t *ri, country_t country) { - tor_addr_t addr; - tor_addr_from_ipv4h(&addr, ri->addr); - return routerset_contains(set, - &addr, - ri->or_port, - ri->nickname, - ri->cache_info.identity_digest, - country); + tor_addr_t addr_v4; + tor_addr_from_ipv4h(&addr_v4, ri->addr); + return routerset_contains2(set, &addr_v4, ri->or_port, &ri->ipv6_addr, + ri->ipv6_orport, ri->nickname, + ri->cache_info.identity_digest, country); } /** Return true iff <b>rs</b> is in <b>set</b>. If country is <b>-1</b>, we diff --git a/src/feature/relay/circuitbuild_relay.c b/src/feature/relay/circuitbuild_relay.c index b89866b477..881cbd51be 100644 --- a/src/feature/relay/circuitbuild_relay.c +++ b/src/feature/relay/circuitbuild_relay.c @@ -33,6 +33,7 @@ #include "core/or/channel.h" #include "core/or/circuitbuild.h" #include "core/or/circuitlist.h" +#include "core/or/extendinfo.h" #include "core/or/onion.h" #include "core/or/relay.h" @@ -122,6 +123,52 @@ circuit_extend_add_ed25519_helper(struct extend_cell_t *ec) return 0; } +/* Make sure the extend cell <b>ec</b> has an IPv4 address if the relay + * supports in, and if not, fill it in. */ +STATIC int +circuit_extend_add_ipv4_helper(struct extend_cell_t *ec) +{ + IF_BUG_ONCE(!ec) { + return -1; + } + + const node_t *node = node_get_by_id((const char *) ec->node_id); + if (node) { + tor_addr_port_t node_ipv4; + node_get_prim_orport(node, &node_ipv4); + if (tor_addr_is_null(&ec->orport_ipv4.addr) && + !tor_addr_is_null(&node_ipv4.addr)) { + tor_addr_copy(&ec->orport_ipv4.addr, &node_ipv4.addr); + ec->orport_ipv4.port = node_ipv4.port; + } + } + + return 0; +} + +/* Make sure the extend cell <b>ec</b> has an IPv6 address if the relay + * supports in, and if not, fill it in. */ +STATIC int +circuit_extend_add_ipv6_helper(struct extend_cell_t *ec) +{ + IF_BUG_ONCE(!ec) { + return -1; + } + + const node_t *node = node_get_by_id((const char *) ec->node_id); + if (node) { + tor_addr_port_t node_ipv6; + node_get_pref_ipv6_orport(node, &node_ipv6); + if (tor_addr_is_null(&ec->orport_ipv6.addr) && + !tor_addr_is_null(&node_ipv6.addr)) { + tor_addr_copy(&ec->orport_ipv6.addr, &node_ipv6.addr); + ec->orport_ipv6.port = node_ipv6.port; + } + } + + return 0; +} + /* Check if the address and port in the tor_addr_port_t <b>ap</b> are valid, * and are allowed by the current ExtendAllowPrivateAddresses config. * @@ -354,11 +401,7 @@ circuit_open_connection_for_extend(const struct extend_cell_t *ec, if (should_launch) { /* we should try to open a connection */ - channel_t *n_chan = channel_connect_for_circuit( - &circ->n_hop->addr, - circ->n_hop->port, - circ->n_hop->identity_digest, - &circ->n_hop->ed_identity); + channel_t *n_chan = channel_connect_for_circuit(circ->n_hop); if (!n_chan) { log_info(LD_CIRC,"Launching n_chan failed. Closing circuit."); circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED); @@ -412,6 +455,12 @@ circuit_extend(struct cell_t *cell, struct circuit_t *circ) if (circuit_extend_lspec_valid_helper(&ec, circ) < 0) return -1; + if (circuit_extend_add_ipv4_helper(&ec) < 0) + return -1; + + if (circuit_extend_add_ipv6_helper(&ec) < 0) + return -1; + /* Check the addresses, without logging */ const int ipv4_valid = circuit_extend_addr_port_is_valid(&ec.orport_ipv4, false, false, 0); @@ -542,7 +591,11 @@ onionskin_answer(struct or_circuit_t *circ, /* record that we could process create cells from a non-local conn * that we didn't initiate; presumably this means that create cells * can reach us too. */ - router_orport_found_reachable(); + tor_addr_t remote_addr; + if (channel_get_addr_if_possible(circ->p_chan, &remote_addr)) { + int family = tor_addr_family(&remote_addr); + router_orport_found_reachable(family); + } } return 0; diff --git a/src/feature/relay/circuitbuild_relay.h b/src/feature/relay/circuitbuild_relay.h index 0783161538..dc0b886a34 100644 --- a/src/feature/relay/circuitbuild_relay.h +++ b/src/feature/relay/circuitbuild_relay.h @@ -73,6 +73,8 @@ onionskin_answer(struct or_circuit_t *circ, STATIC int circuit_extend_state_valid_helper(const struct circuit_t *circ); STATIC int circuit_extend_add_ed25519_helper(struct extend_cell_t *ec); +STATIC int circuit_extend_add_ipv4_helper(struct extend_cell_t *ec); +STATIC int circuit_extend_add_ipv6_helper(struct extend_cell_t *ec); STATIC int circuit_extend_lspec_valid_helper(const struct extend_cell_t *ec, const struct circuit_t *circ); STATIC const tor_addr_port_t * circuit_choose_ip_ap_for_extend( diff --git a/src/feature/relay/relay_config.c b/src/feature/relay/relay_config.c index fac6a2f577..bd1c1e8fe8 100644 --- a/src/feature/relay/relay_config.c +++ b/src/feature/relay/relay_config.c @@ -1029,7 +1029,7 @@ options_transition_affects_descriptor(const or_options_t *old_options, YES_IF_CHANGED_STRING(DataDirectory); YES_IF_CHANGED_STRING(Nickname); - YES_IF_CHANGED_STRING(Address); + YES_IF_CHANGED_LINELIST(Address); YES_IF_CHANGED_LINELIST(ExitPolicy); YES_IF_CHANGED_BOOL(ExitRelay); YES_IF_CHANGED_BOOL(ExitPolicyRejectPrivate); @@ -1114,8 +1114,6 @@ options_act_relay(const or_options_t *old_options) if (server_mode_turned_on) { ip_address_changed(0); - if (have_completed_a_circuit() || !any_predicted_circuits(time(NULL))) - inform_testing_reachability(); } cpuworkers_rotate_keyinfo(); } diff --git a/src/feature/relay/relay_find_addr.c b/src/feature/relay/relay_find_addr.c index 86cd799d42..a51457ddbb 100644 --- a/src/feature/relay/relay_find_addr.c +++ b/src/feature/relay/relay_find_addr.c @@ -45,8 +45,7 @@ void router_new_address_suggestion(const char *suggestion, const dir_connection_t *d_conn) { - tor_addr_t addr; - uint32_t cur = 0; /* Current IPv4 address. */ + tor_addr_t addr, my_addr, last_resolved_addr; const or_options_t *options = get_options(); /* first, learn what the IP address actually is */ @@ -64,14 +63,22 @@ router_new_address_suggestion(const char *suggestion, } /* XXXX ipv6 */ - cur = get_last_resolved_addr(); - if (cur || - resolve_my_address(LOG_INFO, options, &cur, NULL, NULL) >= 0) { + resolved_addr_get_last(AF_INET, &last_resolved_addr); + if (!tor_addr_is_null(&last_resolved_addr)) { + /* Lets use this one. */ + tor_addr_copy(&last_guessed_ip, &last_resolved_addr); + return; + } + + /* Attempt to find our address. */ + if (find_my_address(options, AF_INET, LOG_INFO, &my_addr, NULL, NULL)) { /* We're all set -- we already know our address. Great. */ - tor_addr_from_ipv4h(&last_guessed_ip, cur); /* store it in case we - need it later */ + tor_addr_copy(&last_guessed_ip, &my_addr); /* store it in case we + need it later */ return; } + + /* Consider the suggestion from the directory. */ if (tor_addr_is_internal(&addr, 0)) { /* Don't believe anybody who says our IP is, say, 127.0.0.1. */ return; @@ -111,15 +118,21 @@ MOCK_IMPL(int, router_pick_published_address, (const or_options_t *options, uint32_t *addr, int cache_only)) { - /* First, check the cached output from resolve_my_address(). */ - *addr = get_last_resolved_addr(); - if (*addr) + tor_addr_t last_resolved_addr; + + /* First, check the cached output from find_my_address(). */ + resolved_addr_get_last(AF_INET, &last_resolved_addr); + if (!tor_addr_is_null(&last_resolved_addr)) { + *addr = tor_addr_to_ipv4h(&last_resolved_addr); return 0; + } /* Second, consider doing a resolve attempt right here. */ if (!cache_only) { - if (resolve_my_address(LOG_INFO, options, addr, NULL, NULL) >= 0) { - log_info(LD_CONFIG,"Success: chose address '%s'.", fmt_addr32(*addr)); + tor_addr_t my_addr; + if (find_my_address(options, AF_INET, LOG_INFO, &my_addr, NULL, NULL)) { + log_info(LD_CONFIG,"Success: chose address '%s'.", fmt_addr(&my_addr)); + *addr = tor_addr_to_ipv4h(&my_addr); return 0; } } diff --git a/src/feature/relay/relay_periodic.c b/src/feature/relay/relay_periodic.c index 08ad110cf6..0c056bdeb1 100644 --- a/src/feature/relay/relay_periodic.c +++ b/src/feature/relay/relay_periodic.c @@ -201,23 +201,49 @@ reachability_warnings_callback(time_t now, const or_options_t *options) have_completed_a_circuit()) { /* every 20 minutes, check and complain if necessary */ const routerinfo_t *me = router_get_my_routerinfo(); - if (me && !check_whether_orport_reachable(options)) { - char *address = tor_dup_ip(me->addr); - if (address) { + bool v4_ok = + router_orport_seems_reachable(options,AF_INET); + bool v6_ok = + router_orport_seems_reachable(options,AF_INET6); + if (me && !(v4_ok && v6_ok)) { + /* We need to warn that one or more of our ORPorts isn't reachable. + * Determine which, and give a reasonable warning. */ + char *address4 = tor_dup_ip(me->addr); + char *address6 = tor_addr_to_str_dup(&me->ipv6_addr); + if (address4 || address6) { + char *where4=NULL, *where6=NULL; + if (!v4_ok) + tor_asprintf(&where4, "%s:%d", address4, me->or_port); + if (!v6_ok) + tor_asprintf(&where6, "[%s]:%d", address6, me->or_port); + const char *opt_and = (!v4_ok && !v6_ok) ? "and" : ""; + log_warn(LD_CONFIG, - "Your server (%s:%d) has not managed to confirm that " - "its ORPort is reachable. Relays do not publish descriptors " + "Your server has not managed to confirm reachability for " + "its ORPort(s) at %s%s%s. Relays do not publish descriptors " "until their ORPort and DirPort are reachable. Please check " "your firewalls, ports, address, /etc/hosts file, etc.", - address, me->or_port); - control_event_server_status(LOG_WARN, - "REACHABILITY_FAILED ORADDRESS=%s:%d", - address, me->or_port); - tor_free(address); + where4?where4:"", + opt_and, + where6?where6:""); + tor_free(where4); + tor_free(where6); + if (!v4_ok) { + control_event_server_status(LOG_WARN, + "REACHABILITY_FAILED ORADDRESS=%s:%d", + address4, me->or_port); + } + if (!v6_ok) { + control_event_server_status(LOG_WARN, + "REACHABILITY_FAILED ORADDRESS=[%s]:%d", + address6, me->ipv6_orport); + } } + tor_free(address4); + tor_free(address6); } - if (me && !check_whether_dirport_reachable(options)) { + if (me && !router_dirport_seems_reachable(options)) { char *address = tor_dup_ip(me->addr); if (address) { log_warn(LD_CONFIG, diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index 34d8163c36..57da735e87 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -831,30 +831,37 @@ router_initialize_tls_context(void) * -1 if Tor should die, */ STATIC int -router_write_fingerprint(int hashed) +router_write_fingerprint(int hashed, int ed25519_identity) { char *keydir = NULL, *cp = NULL; const char *fname = hashed ? "hashed-fingerprint" : - "fingerprint"; + (ed25519_identity ? "fingerprint-ed25519" : + "fingerprint"); char fingerprint[FINGERPRINT_LEN+1]; const or_options_t *options = get_options(); char *fingerprint_line = NULL; int result = -1; keydir = get_datadir_fname(fname); - log_info(LD_GENERAL,"Dumping %sfingerprint to \"%s\"...", - hashed ? "hashed " : "", keydir); - if (!hashed) { - if (crypto_pk_get_fingerprint(get_server_identity_key(), - fingerprint, 0) < 0) { - log_err(LD_GENERAL,"Error computing fingerprint"); - goto done; - } - } else { - if (crypto_pk_get_hashed_fingerprint(get_server_identity_key(), - fingerprint) < 0) { - log_err(LD_GENERAL,"Error computing hashed fingerprint"); - goto done; + log_info(LD_GENERAL,"Dumping %s%s to \"%s\"...", hashed ? "hashed " : "", + ed25519_identity ? "ed25519 identity" : "fingerprint", keydir); + + if (ed25519_identity) { /* ed25519 identity */ + digest256_to_base64(fingerprint, (const char *) + get_master_identity_key()->pubkey); + } else { /* RSA identity */ + if (!hashed) { + if (crypto_pk_get_fingerprint(get_server_identity_key(), + fingerprint, 0) < 0) { + log_err(LD_GENERAL,"Error computing fingerprint"); + goto done; + } + } else { + if (crypto_pk_get_hashed_fingerprint(get_server_identity_key(), + fingerprint) < 0) { + log_err(LD_GENERAL,"Error computing hashed fingerprint"); + goto done; + } } } @@ -865,15 +872,17 @@ router_write_fingerprint(int hashed) cp = read_file_to_str(keydir, RFTS_IGNORE_MISSING, NULL); if (!cp || strcmp(cp, fingerprint_line)) { if (write_str_to_file(keydir, fingerprint_line, 0)) { - log_err(LD_FS, "Error writing %sfingerprint line to file", - hashed ? "hashed " : ""); + log_err(LD_FS, "Error writing %s%s line to file", + hashed ? "hashed " : "", + ed25519_identity ? "ed25519 identity" : "fingerprint"); goto done; } } - log_notice(LD_GENERAL, "Your Tor %s identity key fingerprint is '%s %s'", - hashed ? "bridge's hashed" : "server's", options->Nickname, - fingerprint); + log_notice(LD_GENERAL, "Your Tor %s identity key %s fingerprint is '%s %s'", + hashed ? "bridge's hashed" : "server's", + ed25519_identity ? "ed25519" : "", + options->Nickname, fingerprint); result = 0; done: @@ -1109,15 +1118,20 @@ init_keys(void) } } - /* 5. Dump fingerprint and possibly hashed fingerprint to files. */ - if (router_write_fingerprint(0)) { + /* 5. Dump fingerprint, ed25519 identity and possibly hashed fingerprint + * to files. */ + if (router_write_fingerprint(0, 0)) { log_err(LD_FS, "Error writing fingerprint to file"); return -1; } - if (!public_server_mode(options) && router_write_fingerprint(1)) { + if (!public_server_mode(options) && router_write_fingerprint(1, 0)) { log_err(LD_FS, "Error writing hashed fingerprint to file"); return -1; } + if (router_write_fingerprint(0, 1)) { + log_err(LD_FS, "Error writing ed25519 identity to file"); + return -1; + } if (!authdir_mode(options)) return 0; @@ -1134,10 +1148,12 @@ init_keys(void) ds = router_get_trusteddirserver_by_digest(digest); if (!ds) { + tor_addr_port_t ipv6_orport; + router_get_advertised_ipv6_or_ap(options, &ipv6_orport); ds = trusted_dir_server_new(options->Nickname, NULL, router_get_advertised_dir_port(options, 0), router_get_advertised_or_port(options), - NULL, + &ipv6_orport, digest, v3_digest, type, 0.0); @@ -1335,6 +1351,17 @@ should_refuse_unknown_exits(const or_options_t *options) } } +/** + * If true, then we will publish our descriptor even if our own IPv4 ORPort + * seems to be unreachable. + **/ +static bool publish_even_when_ipv4_orport_unreachable = false; +/** + * If true, then we will publish our descriptor even if our own IPv6 ORPort + * seems to be unreachable. + **/ +static bool publish_even_when_ipv6_orport_unreachable = false; + /** Decide if we're a publishable server. We are a publishable server if: * - We don't have the ClientOnly option set * and @@ -1363,14 +1390,24 @@ decide_if_publishable_server(void) return 1; if (!router_get_advertised_or_port(options)) return 0; - if (!check_whether_orport_reachable(options)) - return 0; + if (!router_orport_seems_reachable(options, AF_INET)) { + // We have an ipv4 orport, and it doesn't seem reachable. + if (!publish_even_when_ipv4_orport_unreachable) { + return 0; + } + } + if (!router_orport_seems_reachable(options, AF_INET6)) { + // We have an ipv6 orport, and it doesn't seem reachable. + if (!publish_even_when_ipv6_orport_unreachable) { + return 0; + } + } if (router_have_consensus_path() == CONSENSUS_PATH_INTERNAL) { /* All set: there are no exits in the consensus (maybe this is a tiny * test network), so we can't check our DirPort reachability. */ return 1; } else { - return check_whether_dirport_reachable(options); + return router_dirport_seems_reachable(options); } } @@ -1501,7 +1538,22 @@ router_has_advertised_ipv6_orport(const or_options_t *options) return tor_addr_port_is_valid_ap(&ipv6_ap, 0); } -/** Returns true if this router has an advertised IPv6 ORPort. */ +/** Returns true if this router can extend over IPv6. + * + * This check should only be performed by relay extend code. + * + * Clients should check if relays can initiate and accept IPv6 extends using + * node_supports_initiating_ipv6_extends() and + * node_supports_accepting_ipv6_extends(). + * + * As with other extends, relays should assume the client has already + * performed the relevant checks for the next hop. (Otherwise, relays that + * have just added IPv6 ORPorts won't be able to self-test those ORPorts.) + * + * Accepting relays don't need to perform any IPv6-specific checks before + * accepting a connection, because having an IPv6 ORPort implies support for + * the relevant protocol version. + */ MOCK_IMPL(bool, router_can_extend_over_ipv6,(const or_options_t *options)) { @@ -2373,6 +2425,24 @@ router_rebuild_descriptor(int force) return 0; } +/** Called when we have a new set of consensus parameters. */ +void +router_new_consensus_params(const networkstatus_t *ns) +{ + const int32_t DEFAULT_ASSUME_REACHABLE = 0; + const int32_t DEFAULT_ASSUME_REACHABLE_IPV6 = 0; + int ar, ar6; + ar = networkstatus_get_param(ns, + "assume-reachable", + DEFAULT_ASSUME_REACHABLE, 0, 1); + ar6 = networkstatus_get_param(ns, + "assume-reachable-ipv6", + DEFAULT_ASSUME_REACHABLE_IPV6, 0, 1); + + publish_even_when_ipv4_orport_unreachable = ar; + publish_even_when_ipv6_orport_unreachable = ar || ar6; +} + /** If our router descriptor ever goes this long without being regenerated * because something changed, we force an immediate regenerate-and-upload. */ #define FORCE_REGENERATE_DESCRIPTOR_INTERVAL (18*60*60) @@ -2525,6 +2595,7 @@ void check_descriptor_ipaddress_changed(time_t now) { uint32_t prev, cur; + tor_addr_t addr; const or_options_t *options = get_options(); const char *method = NULL; char *hostname = NULL; @@ -2537,10 +2608,12 @@ check_descriptor_ipaddress_changed(time_t now) /* XXXX ipv6 */ prev = my_ri->addr; - if (resolve_my_address(LOG_INFO, options, &cur, &method, &hostname) < 0) { + if (!find_my_address(options, AF_INET, LOG_INFO, &addr, &method, + &hostname)) { log_info(LD_CONFIG,"options->Address didn't resolve into an IP."); return; } + cur = tor_addr_to_ipv4h(&addr); if (prev != cur) { char *source; diff --git a/src/feature/relay/router.h b/src/feature/relay/router.h index 50790a73dd..fab109be7c 100644 --- a/src/feature/relay/router.h +++ b/src/feature/relay/router.h @@ -81,6 +81,7 @@ int router_should_advertise_dirport(const or_options_t *options, void consider_publishable_server(int force); int should_refuse_unknown_exits(const or_options_t *options); +void router_new_consensus_params(const networkstatus_t *); void router_upload_dir_desc_to_dirservers(int force); void mark_my_descriptor_dirty_if_too_old(time_t now); void mark_my_descriptor_dirty(const char *reason); @@ -124,7 +125,7 @@ void router_free_all(void); #ifdef ROUTER_PRIVATE /* Used only by router.c and the unit tests */ STATIC void get_platform_str(char *platform, size_t len); -STATIC int router_write_fingerprint(int hashed); +STATIC int router_write_fingerprint(int hashed, int ed25519_identity); STATIC smartlist_t *get_my_declared_family(const or_options_t *options); #ifdef TOR_UNIT_TESTS diff --git a/src/feature/relay/selftest.c b/src/feature/relay/selftest.c index 18fe25b989..77c04abdd7 100644 --- a/src/feature/relay/selftest.c +++ b/src/feature/relay/selftest.c @@ -15,38 +15,53 @@ #include "core/or/or.h" #include "app/config/config.h" + #include "core/mainloop/connection.h" #include "core/mainloop/mainloop.h" #include "core/mainloop/netstatus.h" + #include "core/or/circuitbuild.h" #include "core/or/circuitlist.h" #include "core/or/circuituse.h" #include "core/or/crypt_path_st.h" +#include "core/or/extendinfo.h" +#include "core/or/extend_info_st.h" #include "core/or/origin_circuit_st.h" #include "core/or/relay.h" + #include "feature/control/control_events.h" + #include "feature/dirclient/dirclient.h" #include "feature/dircommon/directory.h" + #include "feature/nodelist/authority_cert_st.h" #include "feature/nodelist/routerinfo.h" #include "feature/nodelist/routerinfo_st.h" #include "feature/nodelist/routerlist.h" // but... #include "feature/nodelist/routerset.h" #include "feature/nodelist/torcert.h" + #include "feature/relay/relay_periodic.h" #include "feature/relay/router.h" #include "feature/relay/selftest.h" -/** Whether we can reach our ORPort from the outside. */ -static int can_reach_or_port = 0; +static bool have_orport_for_family(int family); +static void inform_testing_reachability(const tor_addr_t *addr, + uint16_t port, + bool is_dirport); + +/** Whether we can reach our IPv4 ORPort from the outside. */ +static bool can_reach_or_port_ipv4 = false; +/** Whether we can reach our IPv6 ORPort from the outside. */ +static bool can_reach_or_port_ipv6 = false; /** Whether we can reach our DirPort from the outside. */ -static int can_reach_dir_port = 0; +static bool can_reach_dir_port = false; /** Forget what we have learned about our reachability status. */ void router_reset_reachability(void) { - can_reach_or_port = can_reach_dir_port = 0; + can_reach_or_port_ipv4 = can_reach_or_port_ipv6 = can_reach_dir_port = false; } /** Return 1 if we won't do reachability checks, because: @@ -68,13 +83,43 @@ router_reachability_checks_disabled(const or_options_t *options) * - we've seen a successful reachability check, or * - AssumeReachable is set, or * - the network is disabled. + + * If `family'`is AF_INET or AF_INET6, return true only when we should skip + * the given family's orport check (Because it's been checked, or because we + * aren't checking it.) If `family` is 0, return true if we can skip _all_ + * orport checks. */ int -check_whether_orport_reachable(const or_options_t *options) +router_orport_seems_reachable(const or_options_t *options, + int family) { + tor_assert_nonfatal(family == AF_INET || family == AF_INET6 || family == 0); int reach_checks_disabled = router_reachability_checks_disabled(options); - return reach_checks_disabled || - can_reach_or_port; + if (reach_checks_disabled) { + return true; + } + + // Note that we do a == 1 here, not just a boolean check. This value + // is also an autobool, so CFG_AUTO does not mean that we should + // assume IPv6 ports are reachable. + const bool ipv6_assume_reachable = (options->AssumeReachableIPv6 == 1); + + // Which reachability flags should we look at? + const bool checking_ipv4 = (family == AF_INET || family == 0); + const bool checking_ipv6 = (family == AF_INET6 || family == 0); + + if (checking_ipv4) { + if (have_orport_for_family(AF_INET) && !can_reach_or_port_ipv4) { + return false; + } + } + if (checking_ipv6 && !ipv6_assume_reachable) { + if (have_orport_for_family(AF_INET6) && !can_reach_or_port_ipv6) { + return false; + } + } + + return true; } /** Return 0 if we need to do a DirPort reachability check, because: @@ -87,7 +132,7 @@ check_whether_orport_reachable(const or_options_t *options) * - the network is disabled. */ int -check_whether_dirport_reachable(const or_options_t *options) +router_dirport_seems_reachable(const or_options_t *options) { int reach_checks_disabled = router_reachability_checks_disabled(options) || !options->DirPort_set; @@ -107,6 +152,7 @@ router_should_check_reachability(int test_or, int test_dir) if (!me) return 0; + /* Doesn't check our IPv6 address, see #34065. */ if (routerset_contains_router(options->ExcludeNodes, me, -1) && options->StrictNodes) { /* If we've excluded ourself, and StrictNodes is set, we can't test @@ -125,19 +171,51 @@ router_should_check_reachability(int test_or, int test_dir) return 1; } +/** + * Return true if we have configured an ORPort for the given family that + * we would like to advertise. + * + * Like other self-testing functions, this function looks at our most + * recently built descriptor. + **/ +static bool +have_orport_for_family(int family) +{ + const routerinfo_t *me = router_get_my_routerinfo(); + + if (!me) + return false; + + tor_addr_port_t ap; + if (router_get_orport(me, &ap, family) < 0) { + return false; + } + return true; +} + /** Allocate and return a new extend_info_t that can be used to build - * a circuit to or through the router <b>r</b>. Uses the primary - * address of the router, so should only be called on a server. */ + * a circuit to or through the router <b>r</b>, using an address from + * <b>family</b> (if available). + * + * Clients don't have routerinfos, so this function should only be called on a + * server. + * + * If the requested address is not available, returns NULL. */ static extend_info_t * -extend_info_from_router(const routerinfo_t *r) +extend_info_from_router(const routerinfo_t *r, int family) { crypto_pk_t *rsa_pubkey; extend_info_t *info; tor_addr_port_t ap; - tor_assert(r); - /* Make sure we don't need to check address reachability */ - tor_assert_nonfatal(router_skip_or_reachability(get_options(), 0)); + if (BUG(!r)) { + return NULL; + } + + /* Relays always assume that the first hop is reachable. They ignore + * ReachableAddresses. */ + tor_assert_nonfatal(router_or_conn_should_skip_reachable_address_check( + get_options(), 0)); const ed25519_public_key_t *ed_id_key; if (r->cache_info.signing_key_cert) @@ -145,7 +223,10 @@ extend_info_from_router(const routerinfo_t *r) else ed_id_key = NULL; - router_get_prim_orport(r, &ap); + if (router_get_orport(r, &ap, family) < 0) { + /* We don't have an ORPort for the requested family. */ + return NULL; + } rsa_pubkey = router_get_rsa_onion_pkey(r->onion_pkey, r->onion_pkey_len); info = extend_info_new(r->nickname, r->cache_info.identity_digest, ed_id_key, @@ -155,6 +236,78 @@ extend_info_from_router(const routerinfo_t *r) return info; } +/** Launch a self-testing circuit to one of our ORPorts, using an address from + * <b>family</b> (if available). The circuit can be used to test reachability + * or bandwidth. <b>me</b> is our own routerinfo. + * + * Logs an info-level status message. If <b>orport_reachable</b> is false, + * call it a reachability circuit. Otherwise, call it a bandwidth circuit. + * + * See router_do_reachability_checks() for details. */ +static void +router_do_orport_reachability_checks(const routerinfo_t *me, + int family, + int orport_reachable) +{ + extend_info_t *ei = extend_info_from_router(me, family); + int ipv6_flags = (family == AF_INET6 ? CIRCLAUNCH_IS_IPV6_SELFTEST : 0); + + /* If we're trying to test IPv6, but we don't have an IPv6 ORPort, ei will + * be NULL. */ + if (ei) { + const char *family_name = fmt_af_family(family); + const tor_addr_port_t *ap = extend_info_get_orport(ei, family); + log_info(LD_CIRC, "Testing %s of my %s ORPort: %s.", + !orport_reachable ? "reachability" : "bandwidth", + family_name, fmt_addrport_ap(ap)); + if (!orport_reachable) { + /* This is only a 'reachability test' if we don't already think that + * the port is reachable. If we _do_ think it's reachable, then + * it counts as a 'bandwidth test'. */ + inform_testing_reachability(&ap->addr, ap->port, false); + } + circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei, + CIRCLAUNCH_NEED_CAPACITY| + CIRCLAUNCH_IS_INTERNAL| + ipv6_flags); + extend_info_free(ei); + } +} + +/** Launch a self-testing circuit, and ask an exit to connect to our DirPort. + * <b>me</b> is our own routerinfo. + * + * Relays don't advertise IPv6 DirPorts, so this function only supports IPv4. + * + * See router_do_reachability_checks() for details. */ +static void +router_do_dirport_reachability_checks(const routerinfo_t *me) +{ + tor_addr_port_t my_dirport; + tor_addr_from_ipv4h(&my_dirport.addr, me->addr); + my_dirport.port = me->dir_port; + + /* If there is already a pending connection, don't open another one. */ + if (!connection_get_by_type_addr_port_purpose( + CONN_TYPE_DIR, + &my_dirport.addr, my_dirport.port, + DIR_PURPOSE_FETCH_SERVERDESC)) { + /* ask myself, via tor, for my server descriptor. */ + directory_request_t *req = + directory_request_new(DIR_PURPOSE_FETCH_SERVERDESC); + directory_request_set_dir_addr_port(req, &my_dirport); + directory_request_set_directory_id_digest(req, + me->cache_info.identity_digest); + /* ask via an anon circuit, connecting to our dirport. */ + directory_request_set_indirection(req, DIRIND_ANON_DIRPORT); + directory_request_set_resource(req, "authority.z"); + directory_initiate_request(req); + directory_request_free(req); + + inform_testing_reachability(&my_dirport.addr, my_dirport.port, true); + } +} + /** Some time has passed, or we just got new directory information. * See if we currently believe our ORPort or DirPort to be * unreachable. If so, launch a new test for it. @@ -171,104 +324,104 @@ router_do_reachability_checks(int test_or, int test_dir) { const routerinfo_t *me = router_get_my_routerinfo(); const or_options_t *options = get_options(); - int orport_reachable = check_whether_orport_reachable(options); - tor_addr_t addr; + int orport_reachable_v4 = + router_orport_seems_reachable(options, AF_INET); + int orport_reachable_v6 = + router_orport_seems_reachable(options, AF_INET6); if (router_should_check_reachability(test_or, test_dir)) { - if (test_or && (!orport_reachable || !circuit_enough_testing_circs())) { - extend_info_t *ei = extend_info_from_router(me); - /* XXX IPv6 self testing */ - log_info(LD_CIRC, "Testing %s of my ORPort: %s:%d.", - !orport_reachable ? "reachability" : "bandwidth", - fmt_addr32(me->addr), me->or_port); - circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei, - CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL); - extend_info_free(ei); + bool need_testing = !circuit_enough_testing_circs(); + /* At the moment, tor relays believe that they are reachable when they + * receive any create cell on an inbound connection, if the address + * family is correct. + */ + if (test_or && (!orport_reachable_v4 || need_testing)) { + router_do_orport_reachability_checks(me, AF_INET, orport_reachable_v4); + } + if (test_or && (!orport_reachable_v6 || need_testing)) { + router_do_orport_reachability_checks(me, AF_INET6, orport_reachable_v6); } - /* XXX IPv6 self testing */ - tor_addr_from_ipv4h(&addr, me->addr); - if (test_dir && !check_whether_dirport_reachable(options) && - !connection_get_by_type_addr_port_purpose( - CONN_TYPE_DIR, &addr, me->dir_port, - DIR_PURPOSE_FETCH_SERVERDESC)) { - tor_addr_port_t my_orport, my_dirport; - memcpy(&my_orport.addr, &addr, sizeof(addr)); - memcpy(&my_dirport.addr, &addr, sizeof(addr)); - my_orport.port = me->or_port; - my_dirport.port = me->dir_port; - /* ask myself, via tor, for my server descriptor. */ - directory_request_t *req = - directory_request_new(DIR_PURPOSE_FETCH_SERVERDESC); - directory_request_set_or_addr_port(req, &my_orport); - directory_request_set_dir_addr_port(req, &my_dirport); - directory_request_set_directory_id_digest(req, - me->cache_info.identity_digest); - // ask via an anon circuit, connecting to our dirport. - directory_request_set_indirection(req, DIRIND_ANON_DIRPORT); - directory_request_set_resource(req, "authority.z"); - directory_initiate_request(req); - directory_request_free(req); + if (test_dir && !router_dirport_seems_reachable(options)) { + router_do_dirport_reachability_checks(me); } } } -/** We've decided to start our reachability testing. If all - * is set, log this to the user. Return 1 if we did, or 0 if - * we chose not to log anything. */ -int -inform_testing_reachability(void) +/** Log a message informing the user that we are testing a port for + * reachability. + * + * If @a is_dirport is true, then the port is a DirPort; otherwise it is an + * ORPort. */ +static void +inform_testing_reachability(const tor_addr_t *addr, + uint16_t port, + bool is_dirport) { - char dirbuf[128]; - char *address; - const routerinfo_t *me = router_get_my_routerinfo(); - if (!me) - return 0; + if (!router_get_my_routerinfo()) + return; - address = tor_dup_ip(me->addr); - if (!address) - return 0; + char addr_buf[TOR_ADDRPORT_BUF_LEN]; + strlcpy(addr_buf, fmt_addrport(addr, port), sizeof(addr_buf)); + + const char *control_addr_type = is_dirport ? "DIRADDRESS" : "ORADDRESS"; + const char *port_type = is_dirport ? "DirPort" : "ORPort"; + const char *afname = fmt_af_family(tor_addr_family(addr)); control_event_server_status(LOG_NOTICE, - "CHECKING_REACHABILITY ORADDRESS=%s:%d", - address, me->or_port); - if (me->dir_port) { - tor_snprintf(dirbuf, sizeof(dirbuf), " and DirPort %s:%d", - address, me->dir_port); - control_event_server_status(LOG_NOTICE, - "CHECKING_REACHABILITY DIRADDRESS=%s:%d", - address, me->dir_port); - } - log_notice(LD_OR, "Now checking whether ORPort %s:%d%s %s reachable... " - "(this may take up to %d minutes -- look for log " - "messages indicating success)", - address, me->or_port, - me->dir_port ? dirbuf : "", - me->dir_port ? "are" : "is", - TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT/60); - - tor_free(address); - return 1; + "CHECKING_REACHABILITY %s=%s", + control_addr_type, addr_buf); + + log_notice(LD_OR, "Now checking whether %s %s %s is reachable... " + "(this may take up to %d minutes -- look for log " + "messages indicating success)", + afname, port_type, addr_buf, + TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT/60); } -/** Annotate that we found our ORPort reachable. */ +/** + * Return true if this module knows of no reason why we shouldn't publish + * a server descriptor. + **/ +static bool +ready_to_publish(const or_options_t *options) +{ + return options->PublishServerDescriptor_ != NO_DIRINFO && + router_dirport_seems_reachable(options) && + router_all_orports_seem_reachable(options); +} + +/** Annotate that we found our ORPort reachable with a given address + * family. */ void -router_orport_found_reachable(void) +router_orport_found_reachable(int family) { const routerinfo_t *me = router_get_my_routerinfo(); const or_options_t *options = get_options(); - if (!can_reach_or_port && me) { - char *address = tor_dup_ip(me->addr); - - if (!address) + bool *can_reach_ptr; + if (family == AF_INET) { + can_reach_ptr = &can_reach_or_port_ipv4; + } else if (family == AF_INET6) { + can_reach_ptr = &can_reach_or_port_ipv6; + } else { + tor_assert_nonfatal_unreached(); + return; + } + if (!*can_reach_ptr && me) { + tor_addr_port_t ap; + if (router_get_orport(me, &ap, family) < 0) { return; + } + char *address = tor_strdup(fmt_addrport_ap(&ap)); + + *can_reach_ptr = true; - log_notice(LD_OR,"Self-testing indicates your ORPort is reachable from " + log_notice(LD_OR,"Self-testing indicates your ORPort %s is reachable from " "the outside. Excellent.%s", - options->PublishServerDescriptor_ != NO_DIRINFO - && check_whether_dirport_reachable(options) ? - " Publishing server descriptor." : ""); - can_reach_or_port = 1; + address, + ready_to_publish(options) ? + " Publishing server descriptor." : ""); + mark_my_descriptor_dirty("ORPort found reachable"); /* This is a significant enough change to upload immediately, * at least in a test network */ @@ -276,8 +429,8 @@ router_orport_found_reachable(void) reschedule_descriptor_update_check(); } control_event_server_status(LOG_NOTICE, - "REACHABILITY_SUCCEEDED ORADDRESS=%s:%d", - address, me->or_port); + "REACHABILITY_SUCCEEDED ORADDRESS=%s", + address); tor_free(address); } } @@ -288,18 +441,19 @@ router_dirport_found_reachable(void) { const routerinfo_t *me = router_get_my_routerinfo(); const or_options_t *options = get_options(); + if (!can_reach_dir_port && me) { char *address = tor_dup_ip(me->addr); if (!address) return; + can_reach_dir_port = true; log_notice(LD_DIRSERV,"Self-testing indicates your DirPort is reachable " "from the outside. Excellent.%s", - options->PublishServerDescriptor_ != NO_DIRINFO - && check_whether_orport_reachable(options) ? + ready_to_publish(options) ? " Publishing server descriptor." : ""); - can_reach_dir_port = 1; + if (router_should_advertise_dirport(options, me->dir_port)) { mark_my_descriptor_dirty("DirPort found reachable"); /* This is a significant enough change to upload immediately, @@ -316,7 +470,9 @@ router_dirport_found_reachable(void) } /** We have enough testing circuits open. Send a bunch of "drop" - * cells down each of them, to exercise our bandwidth. */ + * cells down each of them, to exercise our bandwidth. + * + * May use IPv4 and IPv6 testing circuits (if available). */ void router_perform_bandwidth_test(int num_circs, time_t now) { diff --git a/src/feature/relay/selftest.h b/src/feature/relay/selftest.h index f5babc95da..e09c0e7898 100644 --- a/src/feature/relay/selftest.h +++ b/src/feature/relay/selftest.h @@ -15,23 +15,29 @@ #ifdef HAVE_MODULE_RELAY struct or_options_t; -int check_whether_orport_reachable(const struct or_options_t *options); -int check_whether_dirport_reachable(const struct or_options_t *options); +#define router_all_orports_seem_reachable(opts) \ + router_orport_seems_reachable((opts),0) +int router_orport_seems_reachable( + const struct or_options_t *options, + int family); +int router_dirport_seems_reachable( + const struct or_options_t *options); void router_do_reachability_checks(int test_or, int test_dir); void router_perform_bandwidth_test(int num_circs, time_t now); -int inform_testing_reachability(void); -void router_orport_found_reachable(void); +void router_orport_found_reachable(int family); void router_dirport_found_reachable(void); void router_reset_reachability(void); #else /* !defined(HAVE_MODULE_RELAY) */ -#define check_whether_orport_reachable(opts) \ +#define router_all_orports_seem_reachable(opts) \ ((void)(opts), 0) -#define check_whether_dirport_reachable(opts) \ +#define router_orport_seems_reachable(opts, fam) \ + ((void)(opts), (void)(fam), 0) +#define router_dirport_seems_reachable(opts) \ ((void)(opts), 0) static inline void diff --git a/src/feature/rend/rendclient.c b/src/feature/rend/rendclient.c index 09db7045fa..e171562d17 100644 --- a/src/feature/rend/rendclient.c +++ b/src/feature/rend/rendclient.c @@ -15,6 +15,7 @@ #include "core/or/circuitlist.h" #include "core/or/circuituse.h" #include "core/or/connection_edge.h" +#include "core/or/extendinfo.h" #include "core/or/relay.h" #include "feature/client/circpathbias.h" #include "feature/control/control_events.h" @@ -234,9 +235,15 @@ rend_client_send_introduction(origin_circuit_t *introcirc, /* version 2 format */ extend_info_t *extend_info = rendcirc->build_state->chosen_exit; int klen; + const tor_addr_port_t *orport = + extend_info_get_orport(extend_info, AF_INET); + IF_BUG_ONCE(! orport) { + /* we should never put an IPv6 address here. */ + goto perm_err; + } /* nul pads */ - set_uint32(tmp+v3_shift+1, tor_addr_to_ipv4n(&extend_info->addr)); - set_uint16(tmp+v3_shift+5, htons(extend_info->port)); + set_uint32(tmp+v3_shift+1, tor_addr_to_ipv4n(&orport->addr)); + set_uint16(tmp+v3_shift+5, htons(orport->port)); memcpy(tmp+v3_shift+7, extend_info->identity_digest, DIGEST_LEN); klen = crypto_pk_asn1_encode(extend_info->onion_key, tmp+v3_shift+7+DIGEST_LEN+2, diff --git a/src/feature/rend/rendcommon.c b/src/feature/rend/rendcommon.c index 5d04755819..775d487805 100644 --- a/src/feature/rend/rendcommon.c +++ b/src/feature/rend/rendcommon.c @@ -14,6 +14,7 @@ #include "core/or/circuitbuild.h" #include "core/or/circuitlist.h" #include "core/or/circuituse.h" +#include "core/or/extendinfo.h" #include "app/config/config.h" #include "feature/control/control_events.h" #include "lib/crypt_ops/crypto_rand.h" @@ -233,7 +234,12 @@ rend_encode_v2_intro_points(char **encoded, rend_service_descriptor_t *desc) goto done; } /* Assemble everything for this introduction point. */ - address = tor_addr_to_str_dup(&info->addr); + const tor_addr_port_t *orport = extend_info_get_orport(info, AF_INET); + IF_BUG_ONCE(!orport) { + /* There must be an IPv4 address for v2 hs. */ + goto done; + } + address = tor_addr_to_str_dup(&orport->addr); res = tor_snprintf(unenc + unenc_written, unenc_len - unenc_written, "introduction-point %s\n" "ip-address %s\n" @@ -242,7 +248,7 @@ rend_encode_v2_intro_points(char **encoded, rend_service_descriptor_t *desc) "service-key\n%s", id_base32, address, - info->port, + orport->port, onion_key, service_key); tor_free(address); diff --git a/src/feature/rend/rendparse.c b/src/feature/rend/rendparse.c index 0979d767a7..c28add5ca9 100644 --- a/src/feature/rend/rendparse.c +++ b/src/feature/rend/rendparse.c @@ -10,6 +10,7 @@ **/ #include "core/or/or.h" +#include "core/or/extendinfo.h" #include "feature/dirparse/parsecommon.h" #include "feature/dirparse/sigcommon.h" #include "feature/rend/rendcommon.h" @@ -428,7 +429,8 @@ rend_parse_introduction_points(rend_service_descriptor_t *parsed, } /* Allocate new intro point and extend info. */ intro = tor_malloc_zero(sizeof(rend_intro_point_t)); - info = intro->extend_info = tor_malloc_zero(sizeof(extend_info_t)); + info = intro->extend_info = + extend_info_new(NULL, NULL, NULL, NULL, NULL, NULL, 0); /* Parse identifier. */ tok = find_by_keyword(tokens, R_IPO_IDENTIFIER); if (base32_decode(info->identity_digest, DIGEST_LEN, @@ -446,12 +448,13 @@ rend_parse_introduction_points(rend_service_descriptor_t *parsed, info->identity_digest, DIGEST_LEN); /* Parse IP address. */ tok = find_by_keyword(tokens, R_IPO_IP_ADDRESS); - if (tor_addr_parse(&info->addr, tok->args[0])<0) { + tor_addr_t addr; + if (tor_addr_parse(&addr, tok->args[0])<0) { log_warn(LD_REND, "Could not parse introduction point address."); rend_intro_point_free(intro); goto err; } - if (tor_addr_family(&info->addr) != AF_INET) { + if (tor_addr_family(&addr) != AF_INET) { log_warn(LD_REND, "Introduction point address was not ipv4."); rend_intro_point_free(intro); goto err; @@ -459,14 +462,18 @@ rend_parse_introduction_points(rend_service_descriptor_t *parsed, /* Parse onion port. */ tok = find_by_keyword(tokens, R_IPO_ONION_PORT); - info->port = (uint16_t) tor_parse_long(tok->args[0],10,1,65535, + uint16_t port = (uint16_t) tor_parse_long(tok->args[0],10,1,65535, &num_ok,NULL); - if (!info->port || !num_ok) { + if (!port || !num_ok) { log_warn(LD_REND, "Introduction point onion port %s is invalid", escaped(tok->args[0])); rend_intro_point_free(intro); goto err; } + + /* Add the address and port. */ + extend_info_add_orport(info, &addr, port); + /* Parse onion key. */ tok = find_by_keyword(tokens, R_IPO_ONION_KEY); if (!crypto_pk_public_exponent_ok(tok->key)) { diff --git a/src/feature/rend/rendservice.c b/src/feature/rend/rendservice.c index 83388a72eb..585fdb532e 100644 --- a/src/feature/rend/rendservice.c +++ b/src/feature/rend/rendservice.c @@ -16,6 +16,7 @@ #include "core/or/circuitbuild.h" #include "core/or/circuitlist.h" #include "core/or/circuituse.h" +#include "core/or/extendinfo.h" #include "core/or/policies.h" #include "core/or/relay.h" #include "core/or/crypt_path.h" @@ -1850,8 +1851,11 @@ rend_service_use_direct_connection(const or_options_t* options, /* We'll connect directly all reachable addresses, whether preferred or not. * The prefer_ipv6 argument to fascist_firewall_allows_address_addr is * ignored, because pref_only is 0. */ + const tor_addr_port_t *ap = extend_info_get_orport(ei, AF_INET); + if (!ap) + return 0; return (rend_service_allow_non_anonymous_connection(options) && - fascist_firewall_allows_address_addr(&ei->addr, ei->port, + fascist_firewall_allows_address_addr(&ap->addr, ap->port, FIREWALL_OR_CONNECTION, 0, 0)); } @@ -2280,7 +2284,8 @@ find_rp_for_intro(const rend_intro_cell_t *intro, /* Make sure the RP we are being asked to connect to is _not_ a private * address unless it's allowed. Let's avoid to build a circuit to our * second middle node and fail right after when extending to the RP. */ - if (!extend_info_addr_is_allowed(&rp->addr)) { + const tor_addr_port_t *orport = extend_info_get_orport(rp, AF_INET); + if (! orport || !extend_info_addr_is_allowed(&orport->addr)) { if (err_msg_out) { tor_asprintf(&err_msg, "Relay IP in INTRODUCE2 cell is private address."); @@ -2549,9 +2554,11 @@ rend_service_parse_intro_for_v2( goto err; } - extend_info = tor_malloc_zero(sizeof(extend_info_t)); - tor_addr_from_ipv4n(&extend_info->addr, get_uint32(buf + 1)); - extend_info->port = ntohs(get_uint16(buf + 5)); + extend_info = extend_info_new(NULL, NULL, NULL, NULL, NULL, NULL, 0); + tor_addr_t addr; + tor_addr_from_ipv4n(&addr, get_uint32(buf + 1)); + uint16_t port = ntohs(get_uint16(buf + 5)); + extend_info_add_orport(extend_info, &addr, port); memcpy(extend_info->identity_digest, buf + 7, DIGEST_LEN); extend_info->nickname[0] = '$'; base16_encode(extend_info->nickname + 1, sizeof(extend_info->nickname) - 1, @@ -3839,6 +3846,9 @@ upload_service_descriptor(rend_service_t *service) rend_get_service_id(service->desc->pk, serviceid); if (get_options()->PublishHidServDescriptors) { /* Post the current descriptors to the hidden service directories. */ + /* This log message is used by Chutney as part of its bootstrap + * detection mechanism. Please don't change without first checking + * Chutney. */ log_info(LD_REND, "Launching upload for hidden service %s", serviceid); directory_post_to_hs_dir(service->desc, descs, NULL, serviceid, diff --git a/src/feature/stats/predict_ports.c b/src/feature/stats/predict_ports.c index d728f106a2..57463952e7 100644 --- a/src/feature/stats/predict_ports.c +++ b/src/feature/stats/predict_ports.c @@ -270,10 +270,10 @@ rep_hist_circbuilding_dormant(time_t now) /* see if we'll still need to build testing circuits */ if (server_mode(options) && - (!check_whether_orport_reachable(options) || + (!router_all_orports_seem_reachable(options) || !circuit_enough_testing_circs())) return 0; - if (!check_whether_dirport_reachable(options)) + if (!router_dirport_seems_reachable(options)) return 0; return 1; diff --git a/src/feature/stats/rephist.c b/src/feature/stats/rephist.c index 71e2e00086..d4826cd1a9 100644 --- a/src/feature/stats/rephist.c +++ b/src/feature/stats/rephist.c @@ -2455,6 +2455,26 @@ rep_hist_note_circuit_handshake_assigned(uint16_t type) onion_handshakes_assigned[type]++; } +/** Get the circuit handshake value that is requested. */ +MOCK_IMPL(int, +rep_hist_get_circuit_handshake_requested, (uint16_t type)) +{ + if (BUG(type > MAX_ONION_HANDSHAKE_TYPE)) { + return 0; + } + return onion_handshakes_requested[type]; +} + +/** Get the circuit handshake value that is assigned. */ +MOCK_IMPL(int, +rep_hist_get_circuit_handshake_assigned, (uint16_t type)) +{ + if (BUG(type > MAX_ONION_HANDSHAKE_TYPE)) { + return 0; + } + return onion_handshakes_assigned[type]; +} + /** Log our onionskin statistics since the last time we were called. */ void rep_hist_log_circuit_handshake_stats(time_t now) diff --git a/src/feature/stats/rephist.h b/src/feature/stats/rephist.h index 92c3d2a5a5..d08b8833cc 100644 --- a/src/feature/stats/rephist.h +++ b/src/feature/stats/rephist.h @@ -77,6 +77,9 @@ void rep_hist_note_circuit_handshake_requested(uint16_t type); void rep_hist_note_circuit_handshake_assigned(uint16_t type); void rep_hist_log_circuit_handshake_stats(time_t now); +MOCK_DECL(int, rep_hist_get_circuit_handshake_requested, (uint16_t type)); +MOCK_DECL(int, rep_hist_get_circuit_handshake_assigned, (uint16_t type)); + void rep_hist_hs_stats_init(time_t now); void rep_hist_hs_stats_term(void); time_t rep_hist_hs_stats_write(time_t now); diff --git a/src/lib/net/address.c b/src/lib/net/address.c index 6d46f9b955..b51fc7cb13 100644 --- a/src/lib/net/address.c +++ b/src/lib/net/address.c @@ -1217,20 +1217,28 @@ fmt_addr32(uint32_t addr) return buf; } -/** Return a string representing the family of <b>addr</b>. +/** Like fmt_addrport(), but takes <b>addr</b> as a host-order IPv4 + * addresses. Also not thread-safe, also clobbers its return buffer on + * repeated calls. */ +const char * +fmt_addr32_port(uint32_t addr, uint16_t port) +{ + static char buf[INET_NTOA_BUF_LEN + 6]; + snprintf(buf, sizeof(buf), "%s:%u", fmt_addr32(addr), port); + return buf; +} + +/** Return a string representing <b>family</b>. * * This string is a string constant, and must not be freed. * This function is thread-safe. */ const char * -fmt_addr_family(const tor_addr_t *addr) +fmt_af_family(sa_family_t family) { static int default_bug_once = 0; - IF_BUG_ONCE(!addr) - return "NULL pointer"; - - switch (tor_addr_family(addr)) { + switch (family) { case AF_INET6: return "IPv6"; case AF_INET: @@ -1242,7 +1250,7 @@ fmt_addr_family(const tor_addr_t *addr) default: if (!default_bug_once) { log_warn(LD_BUG, "Called with unknown address family %d", - (int)tor_addr_family(addr)); + (int)family); default_bug_once = 1; } return "unknown"; @@ -1250,6 +1258,20 @@ fmt_addr_family(const tor_addr_t *addr) //return "(unreachable code)"; } +/** Return a string representing the family of <b>addr</b>. + * + * This string is a string constant, and must not be freed. + * This function is thread-safe. + */ +const char * +fmt_addr_family(const tor_addr_t *addr) +{ + IF_BUG_ONCE(!addr) + return "NULL pointer"; + + return fmt_af_family(tor_addr_family(addr)); +} + /** Convert the string in <b>src</b> to a tor_addr_t <b>addr</b>. The string * may be an IPv4 address, or an IPv6 address surrounded by square brackets. * diff --git a/src/lib/net/address.h b/src/lib/net/address.h index e5016ee4fe..6b18919f6d 100644 --- a/src/lib/net/address.h +++ b/src/lib/net/address.h @@ -95,6 +95,7 @@ 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 bool tor_addr_is_unspec(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); @@ -188,6 +189,15 @@ tor_addr_family(const tor_addr_t *a) return a->family; } +/** + * Return true if the address @a is in the UNSPEC family. + **/ +static inline bool +tor_addr_is_unspec(const tor_addr_t *a) +{ + return a->family == AF_UNSPEC; +} + /** 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 * @@ -236,6 +246,8 @@ const char *fmt_addr_impl(const tor_addr_t *addr, int decorate); const char *fmt_addrport(const tor_addr_t *addr, uint16_t port); #define fmt_addrport_ap(ap) fmt_addrport(&(ap)->addr, (ap)->port) const char *fmt_addr32(uint32_t addr); +const char *fmt_addr32_port(uint32_t addr, uint16_t port); +const char *fmt_af_family(sa_family_t family); const char *fmt_addr_family(const tor_addr_t *addr); MOCK_DECL(int,get_interface_address6,(int severity, sa_family_t family, diff --git a/src/lib/tls/buffers_tls.c b/src/lib/tls/buffers_tls.c index b92a14d6a1..1b99467d2b 100644 --- a/src/lib/tls/buffers_tls.c +++ b/src/lib/tls/buffers_tls.c @@ -59,6 +59,9 @@ read_to_chunk_tls(buf_t *buf, chunk_t *chunk, tor_tls_t *tls, * Second, the TLS stream's events do not correspond directly to network * events: sometimes, before a TLS stream can read, the network must be * ready to write -- or vice versa. + * + * On success, return the number of bytes read. On error, a TOR_TLS_* negative + * code is returned (expect any of them except TOR_TLS_DONE). */ int buf_read_from_tls(buf_t *buf, tor_tls_t *tls, size_t at_most) @@ -92,8 +95,6 @@ buf_read_from_tls(buf_t *buf, tor_tls_t *tls, size_t at_most) return r; /* Error */ tor_assert(total_read+r <= BUF_MAX_LEN); total_read += r; - if ((size_t)r < readlen) /* eof, block, or no more to read. */ - break; } return (int)total_read; } diff --git a/src/mainpage.md b/src/mainpage.md index 2c4c494354..91ceb7dbf7 100644 --- a/src/mainpage.md +++ b/src/mainpage.md @@ -83,8 +83,9 @@ will be scheduled. The codebase is divided into a few top-level subdirectories, each of which contains several sub-modules. - - `ext` -- Code maintained elsewhere that we include in the Tor - source distribution. + - \refdir{ext} -- Code maintained elsewhere that we include in the Tor + source distribution. You should not edit this code if you can + avoid it: we try to keep it identical to the upstream versions. - \refdir{lib} -- Lower-level utility code, not necessarily tor-specific. diff --git a/src/rust/protover/ffi.rs b/src/rust/protover/ffi.rs index 14170d0353..2bf8d3a987 100644 --- a/src/rust/protover/ffi.rs +++ b/src/rust/protover/ffi.rs @@ -84,7 +84,7 @@ pub extern "C" fn protocol_list_supports_protocol( version: uint32_t, ) -> c_int { if c_protocol_list.is_null() { - return 1; + return 0; } // Require an unsafe block to read the version from a C string. The pointer @@ -93,7 +93,7 @@ pub extern "C" fn protocol_list_supports_protocol( let protocol_list = match c_str.to_str() { Ok(n) => n, - Err(_) => return 1, + Err(_) => return 0, }; let proto_entry: UnvalidatedProtoEntry = match protocol_list.parse() { Ok(n) => n, @@ -140,7 +140,7 @@ pub extern "C" fn protocol_list_supports_protocol_or_later( version: uint32_t, ) -> c_int { if c_protocol_list.is_null() { - return 1; + return 0; } // Require an unsafe block to read the version from a C string. The pointer @@ -149,7 +149,7 @@ pub extern "C" fn protocol_list_supports_protocol_or_later( let protocol_list = match c_str.to_str() { Ok(n) => n, - Err(_) => return 1, + Err(_) => return 0, }; let protocol = match translate_to_rust(c_protocol) { @@ -159,7 +159,7 @@ pub extern "C" fn protocol_list_supports_protocol_or_later( let proto_entry: UnvalidatedProtoEntry = match protocol_list.parse() { Ok(n) => n, - Err(_) => return 1, + Err(_) => return 0, }; if proto_entry.supports_protocol_or_later(&protocol.into(), &version) { diff --git a/src/rust/protover/protover.rs b/src/rust/protover/protover.rs index 6d2ef33eec..076cd5301e 100644 --- a/src/rust/protover/protover.rs +++ b/src/rust/protover/protover.rs @@ -163,13 +163,13 @@ pub(crate) fn get_supported_protocols_cstr() -> &'static CStr { DirCache=1-2 \ FlowCtrl=1 \ HSDir=1-2 \ - HSIntro=3-4 \ + HSIntro=3-5 \ HSRend=1-2 \ Link=1-5 \ LinkAuth=3 \ Microdesc=1-2 \ Padding=2 \ - Relay=1-2" + Relay=1-3" ) } else { cstr!( @@ -178,13 +178,13 @@ pub(crate) fn get_supported_protocols_cstr() -> &'static CStr { DirCache=1-2 \ FlowCtrl=1 \ HSDir=1-2 \ - HSIntro=3-4 \ + HSIntro=3-5 \ HSRend=1-2 \ Link=1-5 \ LinkAuth=1,3 \ Microdesc=1-2 \ Padding=2 \ - Relay=1-2" + Relay=1-3" ) } } diff --git a/src/test/conf_examples/dirauth_3/error_no_dirauth b/src/test/conf_examples/dirauth_3/error_no_dirauth new file mode 100644 index 0000000000..e6bd5db69c --- /dev/null +++ b/src/test/conf_examples/dirauth_3/error_no_dirauth @@ -0,0 +1 @@ +This tor was built with dirauth mode disabled. diff --git a/src/test/conf_examples/dirauth_3/error_no_dirauth_relay b/src/test/conf_examples/dirauth_3/error_no_dirauth_relay new file mode 100644 index 0000000000..e6bd5db69c --- /dev/null +++ b/src/test/conf_examples/dirauth_3/error_no_dirauth_relay @@ -0,0 +1 @@ +This tor was built with dirauth mode disabled. diff --git a/src/test/conf_examples/dirauth_3/expected b/src/test/conf_examples/dirauth_3/expected new file mode 100644 index 0000000000..23eac3a5f8 --- /dev/null +++ b/src/test/conf_examples/dirauth_3/expected @@ -0,0 +1,9 @@ +Address 192.0.2.1 +AuthoritativeDirectory 1 +ContactInfo tor_parse_test@example.net +DirPort 192.0.2.1:2 +DownloadExtraInfo 1 +Nickname Unnamed +ORPort 192.0.2.1:1 +ORPort [2001:DB8::1]:3 +V3AuthoritativeDirectory 1 diff --git a/src/test/conf_examples/dirauth_3/expected_log b/src/test/conf_examples/dirauth_3/expected_log new file mode 100644 index 0000000000..3127c9b125 --- /dev/null +++ b/src/test/conf_examples/dirauth_3/expected_log @@ -0,0 +1 @@ +Read configuration file .*dirauth_3[./]*torrc diff --git a/src/test/conf_examples/dirauth_3/torrc b/src/test/conf_examples/dirauth_3/torrc new file mode 100644 index 0000000000..9663a9bc0c --- /dev/null +++ b/src/test/conf_examples/dirauth_3/torrc @@ -0,0 +1,13 @@ +# Authority with IPv6 address + +AuthoritativeDirectory 1 +V3AuthoritativeDirectory 1 + +ContactInfo tor_parse_test@example.net + +Address 192.0.2.1 + +ORPort 192.0.2.1:1 +DirPort 192.0.2.1:2 + +ORPort [2001:DB8::1]:3 diff --git a/src/test/rend_test_helpers.c b/src/test/rend_test_helpers.c index 61bacb4d2e..8e40167aeb 100644 --- a/src/test/rend_test_helpers.c +++ b/src/test/rend_test_helpers.c @@ -2,6 +2,7 @@ /* See LICENSE for licensing information */ #include "core/or/or.h" +#include "core/or/extendinfo.h" #include "lib/crypt_ops/crypto_rand.h" #include "test/test.h" #include "feature/rend/rendcommon.h" @@ -58,7 +59,8 @@ create_descriptor(rend_service_descriptor_t **generated, char **service_id, 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 = + extend_info_new(NULL, NULL, NULL, NULL, NULL, NULL, 0); intro->extend_info->onion_key = okey; crypto_pk_get_digest(intro->extend_info->onion_key, intro->extend_info->identity_digest); @@ -66,8 +68,12 @@ create_descriptor(rend_service_descriptor_t **generated, char **service_id, 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); + tor_addr_t addr; + uint16_t port; + /* Does not cover all IP addresses. */ + tor_addr_from_ipv4h(&addr, crypto_rand_int(65536) + 1); + port = 1 + crypto_rand_int(65535); + extend_info_add_orport(intro->extend_info, &addr, port); intro->intro_key = crypto_pk_dup_key(pk2); smartlist_add((*generated)->intro_nodes, intro); } @@ -91,4 +97,3 @@ mock_rend_data(const char *onion_address) DIGEST_LEN)); return rend_query; } - diff --git a/src/test/test-memwipe.c b/src/test/test-memwipe.c index 4faf7bc5a1..5e4cc7678e 100644 --- a/src/test/test-memwipe.c +++ b/src/test/test-memwipe.c @@ -30,8 +30,8 @@ const char *s = NULL; #define BUF_LEN 2048 #define FILL_BUFFER_IMPL() \ + do { \ unsigned int i; \ - unsigned sum = 0; \ \ /* Fill up a 1k buffer with a recognizable pattern. */ \ for (i = 0; i < BUF_LEN; i += strlen(s)) { \ @@ -42,7 +42,8 @@ const char *s = NULL; /* optimized away. */ \ for (i = 0; i < BUF_LEN; ++i) { \ sum += (unsigned char)buf[i]; \ - } + } \ + } while (0) #ifdef OpenBSD /* Disable some of OpenBSD's malloc protections for this test. This helps @@ -55,7 +56,8 @@ static unsigned fill_a_buffer_memset(void) { char buf[BUF_LEN]; - FILL_BUFFER_IMPL() + unsigned sum = 0; + FILL_BUFFER_IMPL(); memset(buf, 0, sizeof(buf)); return sum; } @@ -64,7 +66,8 @@ static unsigned fill_a_buffer_memwipe(void) { char buf[BUF_LEN]; - FILL_BUFFER_IMPL() + unsigned sum = 0; + FILL_BUFFER_IMPL(); memwipe(buf, 0, sizeof(buf)); return sum; } @@ -73,7 +76,8 @@ static unsigned fill_a_buffer_nothing(void) { char buf[BUF_LEN]; - FILL_BUFFER_IMPL() + unsigned sum = 0; + FILL_BUFFER_IMPL(); return sum; } @@ -116,7 +120,8 @@ static unsigned fill_heap_buffer_memset(void) { char *buf = heap_buf = raw_malloc(BUF_LEN); - FILL_BUFFER_IMPL() + unsigned sum = 0; + FILL_BUFFER_IMPL(); memset(buf, 0, BUF_LEN); raw_free(buf); return sum; @@ -126,7 +131,8 @@ static unsigned fill_heap_buffer_memwipe(void) { char *buf = heap_buf = raw_malloc(BUF_LEN); - FILL_BUFFER_IMPL() + unsigned sum = 0; + FILL_BUFFER_IMPL(); memwipe(buf, 0, BUF_LEN); raw_free(buf); return sum; @@ -136,7 +142,8 @@ static unsigned fill_heap_buffer_nothing(void) { char *buf = heap_buf = raw_malloc(BUF_LEN); - FILL_BUFFER_IMPL() + unsigned sum = 0; + FILL_BUFFER_IMPL(); raw_free(buf); return sum; } diff --git a/src/test/test.c b/src/test/test.c index 4b6082ce4f..2961669c46 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1,5 +1,5 @@ /* Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. +->a * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ @@ -44,6 +44,7 @@ #include "lib/compress/compress.h" #include "app/config/config.h" #include "core/or/connection_edge.h" +#include "core/or/extendinfo.h" #include "feature/rend/rendcommon.h" #include "feature/rend/rendcache.h" #include "feature/rend/rendparse.h" @@ -564,7 +565,8 @@ test_rend_fns(void *arg) for (i = 0; i < 3; 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 = + extend_info_new(NULL, NULL, NULL, NULL, NULL, NULL, 0); intro->extend_info->onion_key = okey; crypto_pk_get_digest(intro->extend_info->onion_key, intro->extend_info->identity_digest); @@ -573,9 +575,12 @@ test_rend_fns(void *arg) base16_encode(intro->extend_info->nickname + 1, sizeof(intro->extend_info->nickname) - 1, intro->extend_info->identity_digest, DIGEST_LEN); + tor_addr_t addr; + uint16_t port; /* Does not cover all IP addresses. */ - tor_addr_from_ipv4h(&intro->extend_info->addr, crypto_rand_int(65536)); - intro->extend_info->port = 1 + crypto_rand_int(65535); + tor_addr_from_ipv4h(&addr, crypto_rand_int(65536) + 1); + port = 1 + crypto_rand_int(65535); + extend_info_add_orport(intro->extend_info, &addr, port); intro->intro_key = crypto_pk_dup_key(pk2); smartlist_add(generated->intro_nodes, intro); } @@ -613,8 +618,12 @@ test_rend_fns(void *arg) tt_mem_op(gen_info->identity_digest,OP_EQ, par_info->identity_digest, DIGEST_LEN); tt_str_op(gen_info->nickname,OP_EQ, par_info->nickname); - tt_assert(tor_addr_eq(&gen_info->addr, &par_info->addr)); - tt_int_op(gen_info->port,OP_EQ, par_info->port); + const tor_addr_port_t *a1, *a2; + a1 = extend_info_get_orport(gen_info, AF_INET); + a2 = extend_info_get_orport(par_info, AF_INET); + tt_assert(a1 && a2); + tt_assert(tor_addr_eq(&a1->addr, &a2->addr)); + tt_int_op(a2->port,OP_EQ, a2->port); } rend_service_descriptor_free(parsed); diff --git a/src/test/test_address.c b/src/test/test_address.c index 4cedbda347..8a46630088 100644 --- a/src/test/test_address.c +++ b/src/test/test_address.c @@ -1152,23 +1152,23 @@ test_address_tor_addr_in_same_network_family(void *ignored) tor_addr_parse(&a, "8.8.8.8"); tor_addr_parse(&b, "8.8.4.4"); - tt_int_op(addrs_in_same_network_family(&a, &b), OP_EQ, 1); + tt_int_op(router_addrs_in_same_network(&a, &b), OP_EQ, 1); tor_addr_parse(&a, "8.8.8.8"); tor_addr_parse(&b, "1.1.1.1"); - tt_int_op(addrs_in_same_network_family(&a, &b), OP_EQ, 0); + tt_int_op(router_addrs_in_same_network(&a, &b), OP_EQ, 0); tor_addr_parse(&a, "8.8.8.8"); tor_addr_parse(&b, "2001:4860:4860::8844"); - tt_int_op(addrs_in_same_network_family(&a, &b), OP_EQ, 0); + tt_int_op(router_addrs_in_same_network(&a, &b), OP_EQ, 0); tor_addr_parse(&a, "2001:4860:4860::8888"); tor_addr_parse(&b, "2001:4860:4860::8844"); - tt_int_op(addrs_in_same_network_family(&a, &b), OP_EQ, 1); + tt_int_op(router_addrs_in_same_network(&a, &b), OP_EQ, 1); tor_addr_parse(&a, "2001:4860:4860::8888"); tor_addr_parse(&b, "2001:470:20::2"); - tt_int_op(addrs_in_same_network_family(&a, &b), OP_EQ, 0); + tt_int_op(router_addrs_in_same_network(&a, &b), OP_EQ, 0); done: return; diff --git a/src/test/test_channeltls.c b/src/test/test_channeltls.c index f4f5cb447e..f682c57acf 100644 --- a/src/test/test_channeltls.c +++ b/src/test/test_channeltls.c @@ -38,13 +38,13 @@ static or_connection_t * tlschan_connection_or_connect_mock( const char *digest, const ed25519_public_key_t *ed_id, channel_tls_t *tlschan); -static int tlschan_is_local_addr_mock(const tor_addr_t *addr); +static bool tlschan_resolved_addr_is_local_mock(const tor_addr_t *addr); /* Fake close method */ static void tlschan_fake_close_method(channel_t *chan); /* Flags controlling behavior of channeltls unit test mocks */ -static int tlschan_local = 0; +static bool tlschan_local = false; static const buf_t * tlschan_buf_datalen_mock_target = NULL; static size_t tlschan_buf_datalen_mock_size = 0; @@ -67,9 +67,9 @@ test_channeltls_create(void *arg) test_addr.addr.in_addr.s_addr = htonl(0x01020304); /* For this test we always want the address to be treated as non-local */ - tlschan_local = 0; - /* Install is_local_addr() mock */ - MOCK(is_local_addr, tlschan_is_local_addr_mock); + tlschan_local = false; + /* Install is_local_to_resolve_addr() mock */ + MOCK(is_local_to_resolve_addr, tlschan_resolved_addr_is_local_mock); /* Install mock for connection_or_connect() */ MOCK(connection_or_connect, tlschan_connection_or_connect_mock); @@ -92,7 +92,7 @@ test_channeltls_create(void *arg) } UNMOCK(connection_or_connect); - UNMOCK(is_local_addr); + UNMOCK(is_local_to_resolve_addr); return; } @@ -116,9 +116,9 @@ test_channeltls_num_bytes_queued(void *arg) test_addr.addr.in_addr.s_addr = htonl(0x01020304); /* For this test we always want the address to be treated as non-local */ - tlschan_local = 0; - /* Install is_local_addr() mock */ - MOCK(is_local_addr, tlschan_is_local_addr_mock); + tlschan_local = false; + /* Install is_local_to_resolve_addr() mock */ + MOCK(is_local_to_resolve_addr, tlschan_resolved_addr_is_local_mock); /* Install mock for connection_or_connect() */ MOCK(connection_or_connect, tlschan_connection_or_connect_mock); @@ -178,7 +178,7 @@ test_channeltls_num_bytes_queued(void *arg) } UNMOCK(connection_or_connect); - UNMOCK(is_local_addr); + UNMOCK(is_local_to_resolve_addr); return; } @@ -201,9 +201,9 @@ test_channeltls_overhead_estimate(void *arg) test_addr.addr.in_addr.s_addr = htonl(0x01020304); /* For this test we always want the address to be treated as non-local */ - tlschan_local = 0; - /* Install is_local_addr() mock */ - MOCK(is_local_addr, tlschan_is_local_addr_mock); + tlschan_local = false; + /* Install is_local_to_resolve_addr() mock */ + MOCK(is_local_to_resolve_addr, tlschan_resolved_addr_is_local_mock); /* Install mock for connection_or_connect() */ MOCK(connection_or_connect, tlschan_connection_or_connect_mock); @@ -252,7 +252,7 @@ test_channeltls_overhead_estimate(void *arg) } UNMOCK(connection_or_connect); - UNMOCK(is_local_addr); + UNMOCK(is_local_to_resolve_addr); return; } @@ -321,8 +321,8 @@ tlschan_fake_close_method(channel_t *chan) return; } -static int -tlschan_is_local_addr_mock(const tor_addr_t *addr) +static bool +tlschan_resolved_addr_is_local_mock(const tor_addr_t *addr) { tt_ptr_op(addr, OP_NE, NULL); diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c index 03fd176ead..8f0e8b54e8 100644 --- a/src/test/test_circuitbuild.c +++ b/src/test/test_circuitbuild.c @@ -19,6 +19,7 @@ #include "core/or/channel.h" #include "core/or/circuitbuild.h" #include "core/or/circuitlist.h" +#include "core/or/circuituse.h" #include "core/or/onion.h" #include "core/or/cell_st.h" @@ -29,11 +30,13 @@ #include "feature/client/entrynodes.h" #include "feature/nodelist/nodelist.h" +#include "feature/nodelist/node_select.h" #include "feature/relay/circuitbuild_relay.h" #include "feature/relay/router.h" #include "feature/relay/routermode.h" #include "feature/nodelist/node_st.h" +#include "feature/nodelist/routerinfo_st.h" /* Dummy nodes smartlist for testing */ static smartlist_t dummy_nodes; @@ -279,10 +282,10 @@ mock_node_get_by_id(const char *identity_digest) return mocked_node; } -static int mocked_supports_ed25519_link_authentication = 0; -static int +static bool mocked_supports_ed25519_link_authentication = 0; +static bool mock_node_supports_ed25519_link_authentication(const node_t *node, - int compatible_with_us) + bool compatible_with_us) { (void)node; (void)compatible_with_us; @@ -821,6 +824,77 @@ test_circuit_extend_lspec_valid(void *arg) tor_free(p_chan); } +#define NODE_SET_IPV4(node, ipv4_addr, ipv4_port) { \ + tor_addr_t addr; \ + tor_addr_parse(&addr, ipv4_addr); \ + node->ri->addr = tor_addr_to_ipv4h(&addr); \ + node->ri->or_port = ipv4_port; \ + } + +#define NODE_CLEAR_IPV4(node) { \ + node->ri->addr = 0; \ + node->ri->or_port = 0; \ + } + +#define NODE_SET_IPV6(node, ipv6_addr_str, ipv6_port) { \ + tor_addr_parse(&node->ri->ipv6_addr, ipv6_addr_str); \ + node->ri->ipv6_orport = ipv6_port; \ + } + +/* Test the different cases in circuit_extend_add_ed25519_helper(). */ +static void +test_circuit_extend_add_ip(void *arg) +{ + (void) arg; + tor_addr_t ipv4_tmp; + extend_cell_t *ec = tor_malloc_zero(sizeof(extend_cell_t)); + extend_cell_t *old_ec = tor_malloc_zero(sizeof(extend_cell_t)); + + node_t *fake_node = tor_malloc_zero(sizeof(node_t)); + routerinfo_t *ri = tor_malloc_zero(sizeof(routerinfo_t)); + + MOCK(node_get_by_id, mock_node_get_by_id); + + /* Set up the fake variables for the IPv4 test */ + fake_node->ri = ri; + mocked_node = fake_node; + memset(ec->node_id, 0xAA, sizeof(ec->node_id)); + memcpy(old_ec, ec, sizeof(extend_cell_t)); + NODE_SET_IPV4(fake_node, PUBLIC_IPV4, VALID_PORT); + + /* Do the IPv4 test */ + tt_int_op(circuit_extend_add_ipv4_helper(ec), OP_EQ, 0); + tor_addr_from_ipv4h(&ipv4_tmp, fake_node->ri->addr); + /* The IPv4 should match */ + tt_int_op(tor_addr_compare(&ec->orport_ipv4.addr, &ipv4_tmp, CMP_SEMANTIC), + OP_EQ, 0); + tt_int_op(ec->orport_ipv4.port, OP_EQ, VALID_PORT); + + /* Set up the fake variables for the IPv6 test */ + memcpy(ec, old_ec, sizeof(extend_cell_t)); + NODE_CLEAR_IPV4(fake_node); + NODE_SET_IPV6(fake_node, PUBLIC_IPV6, VALID_PORT); + + /* Do the IPv6 test */ + tt_int_op(circuit_extend_add_ipv6_helper(ec), OP_EQ, 0); + /* The IPv6 should match */ + tt_int_op(tor_addr_compare(&ec->orport_ipv6.addr, &fake_node->ri->ipv6_addr, + CMP_SEMANTIC), OP_EQ, 0); + tt_int_op(ec->orport_ipv6.port, OP_EQ, VALID_PORT); + + /* Cleanup */ + mocked_node = NULL; + + done: + UNMOCK(node_get_by_id); + + tor_free(ec); + tor_free(old_ec); + + tor_free(ri); + tor_free(fake_node); +} + static bool can_extend_over_ipv6_result = false; static int mock_router_can_extend_over_ipv6_calls = 0; static bool @@ -927,15 +1001,9 @@ mock_circuit_mark_for_close_(circuit_t *circ, int reason, static int mock_channel_connect_calls = 0; static channel_t *mock_channel_connect_nchan = NULL; static channel_t * -mock_channel_connect_for_circuit(const tor_addr_t *addr, - uint16_t port, - const char *id_digest, - const struct ed25519_public_key_t *ed_id) +mock_channel_connect_for_circuit(const extend_info_t *ei) { - (void)addr; - (void)port; - (void)id_digest; - (void)ed_id; + (void)ei; mock_channel_connect_calls++; return mock_channel_connect_nchan; } @@ -1176,6 +1244,8 @@ mock_channel_get_canonical_remote_descr(channel_t *chan) return "mock_channel_get_canonical_remote_descr()"; } +/* Should mock_circuit_deliver_create_cell() expect a direct connection? */ +static bool mock_circuit_deliver_create_cell_expect_direct = false; static int mock_circuit_deliver_create_cell_calls = 0; static int mock_circuit_deliver_create_cell_result = 0; static int @@ -1188,10 +1258,13 @@ mock_circuit_deliver_create_cell(circuit_t *circ, /* circuit_deliver_create_cell() requires non-NULL arguments, * but we only check circ and circ->n_chan here. */ tt_ptr_op(circ, OP_NE, NULL); - tt_ptr_op(circ->n_chan, OP_NE, NULL); + /* We expect n_chan for relayed cells. But should we also expect it for + * direct connections? */ + if (!mock_circuit_deliver_create_cell_expect_direct) + tt_ptr_op(circ->n_chan, OP_NE, NULL); /* We should only ever get relayed cells from extends */ - tt_int_op(relayed, OP_EQ, 1); + tt_int_op(relayed, OP_EQ, !mock_circuit_deliver_create_cell_expect_direct); mock_circuit_deliver_create_cell_calls++; return mock_circuit_deliver_create_cell_result; @@ -1352,6 +1425,7 @@ test_circuit_extend(void *arg) /* Mock circuit_deliver_create_cell(), so it doesn't crash */ mock_circuit_deliver_create_cell_calls = 0; + mock_circuit_deliver_create_cell_expect_direct = false; MOCK(circuit_deliver_create_cell, mock_circuit_deliver_create_cell); /* Test circuit established, re-using channel, successful delivery */ @@ -1516,6 +1590,355 @@ test_onionskin_answer(void *arg) tor_free(or_circ); } +/* Test the different cases in origin_circuit_init(). */ +static void +test_origin_circuit_init(void *arg) +{ + (void)arg; + origin_circuit_t *origin_circ = NULL; + + /* Init with 0 purpose and 0 flags */ + origin_circ = origin_circuit_init(0, 0); + tt_int_op(origin_circ->base_.purpose, OP_EQ, 0); + tt_int_op(origin_circ->base_.state, OP_EQ, CIRCUIT_STATE_CHAN_WAIT); + tt_ptr_op(origin_circ->build_state, OP_NE, NULL); + tt_int_op(origin_circ->build_state->is_internal, OP_EQ, 0); + tt_int_op(origin_circ->build_state->is_ipv6_selftest, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_capacity, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_uptime, OP_EQ, 0); + tt_int_op(origin_circ->build_state->onehop_tunnel, OP_EQ, 0); + /* The circuits are automatically freed by the circuitlist. */ + + /* Init with a purpose */ + origin_circ = origin_circuit_init(CIRCUIT_PURPOSE_C_GENERAL, 0); + tt_int_op(origin_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_C_GENERAL); + + /* Init with each flag */ + origin_circ = origin_circuit_init(0, CIRCLAUNCH_IS_INTERNAL); + tt_ptr_op(origin_circ->build_state, OP_NE, NULL); + tt_int_op(origin_circ->build_state->is_internal, OP_EQ, 1); + tt_int_op(origin_circ->build_state->is_ipv6_selftest, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_capacity, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_uptime, OP_EQ, 0); + tt_int_op(origin_circ->build_state->onehop_tunnel, OP_EQ, 0); + + origin_circ = origin_circuit_init(0, CIRCLAUNCH_IS_IPV6_SELFTEST); + tt_ptr_op(origin_circ->build_state, OP_NE, NULL); + tt_int_op(origin_circ->build_state->is_internal, OP_EQ, 0); + tt_int_op(origin_circ->build_state->is_ipv6_selftest, OP_EQ, 1); + tt_int_op(origin_circ->build_state->need_capacity, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_uptime, OP_EQ, 0); + tt_int_op(origin_circ->build_state->onehop_tunnel, OP_EQ, 0); + + origin_circ = origin_circuit_init(0, CIRCLAUNCH_NEED_CAPACITY); + tt_ptr_op(origin_circ->build_state, OP_NE, NULL); + tt_int_op(origin_circ->build_state->is_internal, OP_EQ, 0); + tt_int_op(origin_circ->build_state->is_ipv6_selftest, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_capacity, OP_EQ, 1); + tt_int_op(origin_circ->build_state->need_uptime, OP_EQ, 0); + tt_int_op(origin_circ->build_state->onehop_tunnel, OP_EQ, 0); + + origin_circ = origin_circuit_init(0, CIRCLAUNCH_NEED_UPTIME); + tt_ptr_op(origin_circ->build_state, OP_NE, NULL); + tt_int_op(origin_circ->build_state->is_internal, OP_EQ, 0); + tt_int_op(origin_circ->build_state->is_ipv6_selftest, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_capacity, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_uptime, OP_EQ, 1); + tt_int_op(origin_circ->build_state->onehop_tunnel, OP_EQ, 0); + + origin_circ = origin_circuit_init(0, CIRCLAUNCH_ONEHOP_TUNNEL); + tt_ptr_op(origin_circ->build_state, OP_NE, NULL); + tt_int_op(origin_circ->build_state->is_internal, OP_EQ, 0); + tt_int_op(origin_circ->build_state->is_ipv6_selftest, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_capacity, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_uptime, OP_EQ, 0); + tt_int_op(origin_circ->build_state->onehop_tunnel, OP_EQ, 1); + + done: + /* The circuits are automatically freed by the circuitlist. */ + ; +} + +/* Test the different cases in circuit_send_next_onion_skin(). */ +static void +test_circuit_send_next_onion_skin(void *arg) +{ + (void)arg; + origin_circuit_t *origin_circ = NULL; + struct timeval circ_start_time; + memset(&circ_start_time, 0, sizeof(circ_start_time)); + + extend_info_t fakehop; + memset(&fakehop, 0, sizeof(fakehop)); + extend_info_t *single_fakehop = &fakehop; + extend_info_t *multi_fakehop[DEFAULT_ROUTE_LEN] = {&fakehop, + &fakehop, + &fakehop}; + + extend_info_t ipv6_hop; + memset(&ipv6_hop, 0, sizeof(ipv6_hop)); + tor_addr_parse(&ipv6_hop.orports[0].addr, "1::2"); + extend_info_t *multi_ipv6_hop[DEFAULT_ROUTE_LEN] = {&ipv6_hop, + &ipv6_hop, + &ipv6_hop}; + + extend_info_t ipv4_hop; + memset(&ipv4_hop, 0, sizeof(ipv4_hop)); + tor_addr_from_ipv4h(&ipv4_hop.orports[0].addr, 0x20304050); + extend_info_t *multi_ipv4_hop[DEFAULT_ROUTE_LEN] = {&ipv4_hop, + &ipv4_hop, + &ipv4_hop}; + + mock_circuit_deliver_create_cell_expect_direct = false; + MOCK(circuit_deliver_create_cell, mock_circuit_deliver_create_cell); + server = 0; + MOCK(server_mode, mock_server_mode); + + /* Try a direct connection, and succeed on a client */ + server = 0; + origin_circ = new_test_origin_circuit(false, + circ_start_time, + 1, + &single_fakehop); + tt_ptr_op(origin_circ, OP_NE, NULL); + /* Skip some of the multi-hop checks */ + origin_circ->build_state->onehop_tunnel = 1; + /* This is a direct connection */ + mock_circuit_deliver_create_cell_expect_direct = true; + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, 0); + /* The circuits are automatically freed by the circuitlist. */ + + /* Try a direct connection, and succeed on a server */ + server = 1; + origin_circ = new_test_origin_circuit(false, + circ_start_time, + 1, + &single_fakehop); + tt_ptr_op(origin_circ, OP_NE, NULL); + origin_circ->build_state->onehop_tunnel = 1; + mock_circuit_deliver_create_cell_expect_direct = true; + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, 0); + + /* Start capturing bugs */ + setup_full_capture_of_logs(LOG_WARN); + tor_capture_bugs_(1); + + /* Try an extend, but fail the client valid address family check */ + server = 0; + origin_circ = new_test_origin_circuit(true, + circ_start_time, + ARRAY_LENGTH(multi_fakehop), + multi_fakehop); + tt_ptr_op(origin_circ, OP_NE, NULL); + /* Fix the state */ + origin_circ->base_.state = 0; + /* This is an indirect connection */ + mock_circuit_deliver_create_cell_expect_direct = false; + /* Fail because the address family is invalid */ + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, + -END_CIRC_REASON_INTERNAL); + expect_log_msg("No supported address family found in extend_info.\n"); + mock_clean_saved_logs(); + + /* Try an extend, but fail the server valid address check */ + server = 1; + origin_circ = new_test_origin_circuit(true, + circ_start_time, + ARRAY_LENGTH(multi_fakehop), + multi_fakehop); + tt_ptr_op(origin_circ, OP_NE, NULL); + origin_circ->base_.state = 0; + mock_circuit_deliver_create_cell_expect_direct = false; + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, + -END_CIRC_REASON_INTERNAL); + expect_log_msg("No supported address family found in extend_info.\n"); + mock_clean_saved_logs(); + + /* Try an extend, but fail in the client code, with an IPv6 address */ + server = 0; + origin_circ = new_test_origin_circuit(true, + circ_start_time, + ARRAY_LENGTH(multi_ipv6_hop), + multi_ipv6_hop); + tt_ptr_op(origin_circ, OP_NE, NULL); + origin_circ->base_.state = 0; + mock_circuit_deliver_create_cell_expect_direct = false; + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, + -END_CIRC_REASON_INTERNAL); + expect_log_msg("No supported address family found in extend_info.\n"); + mock_clean_saved_logs(); + + /* Stop capturing bugs, but keep capturing logs */ + tor_end_capture_bugs_(); + + /* Try an extend, pass the client IPv4 check, but fail later */ + server = 0; + origin_circ = new_test_origin_circuit(true, + circ_start_time, + ARRAY_LENGTH(multi_ipv4_hop), + multi_ipv4_hop); + tt_ptr_op(origin_circ, OP_NE, NULL); + origin_circ->base_.state = 0; + mock_circuit_deliver_create_cell_expect_direct = false; + /* Fail because the circuit data is invalid */ + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, + -END_CIRC_REASON_INTERNAL); + expect_log_msg("onion_skin_create failed.\n"); + mock_clean_saved_logs(); + + /* Try an extend, pass the server IPv4 check, but fail later */ + server = 1; + origin_circ = new_test_origin_circuit(true, + circ_start_time, + ARRAY_LENGTH(multi_ipv4_hop), + multi_ipv4_hop); + tt_ptr_op(origin_circ, OP_NE, NULL); + origin_circ->base_.state = 0; + mock_circuit_deliver_create_cell_expect_direct = false; + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, + -END_CIRC_REASON_INTERNAL); + expect_log_msg("onion_skin_create failed.\n"); + mock_clean_saved_logs(); + + /* Try an extend, pass the server IPv6 check, but fail later */ + server = 1; + origin_circ = new_test_origin_circuit(true, + circ_start_time, + ARRAY_LENGTH(multi_ipv6_hop), + multi_ipv6_hop); + tt_ptr_op(origin_circ, OP_NE, NULL); + origin_circ->base_.state = 0; + mock_circuit_deliver_create_cell_expect_direct = false; + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, + -END_CIRC_REASON_INTERNAL); + expect_log_msg("onion_skin_create failed.\n"); + mock_clean_saved_logs(); + + /* Things we're not testing right now: + * - the addresses in the extend cell inside + * circuit_send_intermediate_onion_skin() matches the address in the + * supplied extend_info. + * - valid circuit data. + * - actually extending the circuit to each hop. */ + + done: + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + teardown_capture_of_logs(); + + UNMOCK(circuit_deliver_create_cell); + UNMOCK(server_mode); + server = 0; + + /* The circuits are automatically freed by the circuitlist. */ +} + +/* Test the different cases in cpath_build_state_to_crn_flags(). */ +static void +test_cpath_build_state_to_crn_flags(void *arg) +{ + (void)arg; + + cpath_build_state_t state; + memset(&state, 0, sizeof(state)); + + tt_int_op(cpath_build_state_to_crn_flags(&state), OP_EQ, + 0); + + memset(&state, 0, sizeof(state)); + state.need_uptime = 1; + tt_int_op(cpath_build_state_to_crn_flags(&state), OP_EQ, + CRN_NEED_UPTIME); + + memset(&state, 0, sizeof(state)); + state.need_capacity = 1; + tt_int_op(cpath_build_state_to_crn_flags(&state), OP_EQ, + CRN_NEED_CAPACITY); + + memset(&state, 0, sizeof(state)); + state.need_capacity = 1; + state.need_uptime = 1; + tt_int_op(cpath_build_state_to_crn_flags(&state), OP_EQ, + CRN_NEED_CAPACITY | CRN_NEED_UPTIME); + + /* Check that no other flags are handled */ + memset(&state, 0xff, sizeof(state)); + tt_int_op(cpath_build_state_to_crn_flags(&state), OP_EQ, + CRN_NEED_CAPACITY | CRN_NEED_UPTIME); + + done: + ; +} + +/* Test the different cases in cpath_build_state_to_crn_ipv6_extend_flag(). */ +static void +test_cpath_build_state_to_crn_ipv6_extend_flag(void *arg) +{ + (void)arg; + + cpath_build_state_t state; + + memset(&state, 0, sizeof(state)); + state.desired_path_len = DEFAULT_ROUTE_LEN; + tt_int_op(cpath_build_state_to_crn_ipv6_extend_flag(&state, 0), OP_EQ, + 0); + + /* Pass the state flag check, but not the length check */ + memset(&state, 0, sizeof(state)); + state.desired_path_len = DEFAULT_ROUTE_LEN; + state.is_ipv6_selftest = 1; + tt_int_op(cpath_build_state_to_crn_ipv6_extend_flag(&state, 0), OP_EQ, + 0); + + /* Pass the length check, but not the state flag check */ + memset(&state, 0, sizeof(state)); + state.desired_path_len = DEFAULT_ROUTE_LEN; + tt_int_op( + cpath_build_state_to_crn_ipv6_extend_flag(&state, + DEFAULT_ROUTE_LEN - 2), + OP_EQ, 0); + + /* Pass both checks */ + memset(&state, 0, sizeof(state)); + state.desired_path_len = DEFAULT_ROUTE_LEN; + state.is_ipv6_selftest = 1; + tt_int_op( + cpath_build_state_to_crn_ipv6_extend_flag(&state, + DEFAULT_ROUTE_LEN - 2), + OP_EQ, CRN_INITIATE_IPV6_EXTEND); + + /* Check that no other flags are handled */ + memset(&state, 0xff, sizeof(state)); + state.desired_path_len = INT_MAX; + tt_int_op(cpath_build_state_to_crn_ipv6_extend_flag(&state, INT_MAX), OP_EQ, + 0); + +#ifndef ALL_BUGS_ARE_FATAL + /* Start capturing bugs */ + setup_full_capture_of_logs(LOG_INFO); + tor_capture_bugs_(1); + + /* Now test the single hop circuit case */ +#define SINGLE_HOP_ROUTE_LEN 1 + memset(&state, 0, sizeof(state)); + state.desired_path_len = SINGLE_HOP_ROUTE_LEN; + state.is_ipv6_selftest = 1; + tt_int_op( + cpath_build_state_to_crn_ipv6_extend_flag(&state, + SINGLE_HOP_ROUTE_LEN - 2), + OP_EQ, 0); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); + tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, + "!(ASSERT_PREDICT_UNLIKELY_(state->desired_path_len < 2))"); + mock_clean_saved_logs(); +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ + + done: + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + teardown_capture_of_logs(); +} + #define TEST(name, flags, setup, cleanup) \ { #name, test_ ## name, flags, setup, cleanup } @@ -1525,6 +1948,9 @@ test_onionskin_answer(void *arg) #define TEST_CIRCUIT(name, flags) \ { #name, test_circuit_ ## name, flags, NULL, NULL } +#define TEST_CPATH(name, flags) \ + { #name, test_cpath_ ## name, flags, NULL, NULL } + #ifndef COCCI #define TEST_CIRCUIT_PASSTHROUGH(name, flags, arg) \ { #name "/" arg, test_circuit_ ## name, flags, \ @@ -1542,13 +1968,21 @@ struct testcase_t circuitbuild_tests[] = { TEST_CIRCUIT(extend_state_valid, TT_FORK), TEST_CIRCUIT(extend_add_ed25519, TT_FORK), TEST_CIRCUIT(extend_lspec_valid, TT_FORK), + TEST_CIRCUIT(extend_add_ip, TT_FORK), TEST_CIRCUIT(choose_ip_ap_for_extend, 0), + TEST_CIRCUIT_PASSTHROUGH(open_connection_for_extend, TT_FORK, "4"), TEST_CIRCUIT_PASSTHROUGH(open_connection_for_extend, TT_FORK, "6"), TEST_CIRCUIT_PASSTHROUGH(open_connection_for_extend, TT_FORK, "dual-stack"), + TEST_CIRCUIT(extend, TT_FORK), TEST(onionskin_answer, TT_FORK, NULL, NULL), + TEST(origin_circuit_init, TT_FORK, NULL, NULL), + TEST_CIRCUIT(send_next_onion_skin, TT_FORK), + TEST_CPATH(build_state_to_crn_flags, 0), + TEST_CPATH(build_state_to_crn_ipv6_extend_flag, TT_FORK), + END_OF_TESTCASES }; diff --git a/src/test/test_circuitpadding.c b/src/test/test_circuitpadding.c index cfb24c032c..c3346e2e03 100644 --- a/src/test/test_circuitpadding.c +++ b/src/test/test_circuitpadding.c @@ -23,6 +23,7 @@ #include "core/or/circuitbuild.h" #include "core/or/circuitpadding.h" #include "core/or/circuitpadding_machines.h" +#include "core/or/extendinfo.h" #include "core/mainloop/netstatus.h" #include "core/crypto/relay_crypto.h" #include "core/or/protover.h" @@ -1361,7 +1362,7 @@ test_circuitpadding_wronghop(void *arg) /* 5. Test that asking to stop the wrong machine does nothing */ circpad_negotiate_padding(TO_ORIGIN_CIRCUIT(client_side), - 255, 2, CIRCPAD_COMMAND_STOP); + 255, 2, CIRCPAD_COMMAND_STOP, 0); tt_ptr_op(client_side->padding_machine[0], OP_NE, NULL); tt_ptr_op(client_side->padding_info[0], OP_NE, NULL); tt_ptr_op(relay_side->padding_machine[0], OP_NE, NULL); @@ -1409,7 +1410,7 @@ test_circuitpadding_wronghop(void *arg) circpad_padding_negotiated(relay_side, CIRCPAD_MACHINE_CIRC_SETUP, CIRCPAD_COMMAND_START, - CIRCPAD_RESPONSE_OK); + CIRCPAD_RESPONSE_OK, 0); /* verify no padding was negotiated */ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL); @@ -1418,7 +1419,7 @@ test_circuitpadding_wronghop(void *arg) circpad_padding_negotiated(relay_side, CIRCPAD_MACHINE_CIRC_SETUP, CIRCPAD_COMMAND_START, - CIRCPAD_RESPONSE_ERR); + CIRCPAD_RESPONSE_ERR, 0); /* verify no padding was negotiated */ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL); @@ -1521,7 +1522,7 @@ test_circuitpadding_negotiation(void *arg) /* Force negotiate padding. */ circpad_negotiate_padding(TO_ORIGIN_CIRCUIT(client_side), CIRCPAD_MACHINE_CIRC_SETUP, - 2, CIRCPAD_COMMAND_START); + 2, CIRCPAD_COMMAND_START, 0); /* verify no padding was negotiated */ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL); @@ -1732,9 +1733,9 @@ helper_create_conditional_machines(void) add->conditions.requires_vanguards = 0; add->conditions.min_hops = 2; - add->conditions.state_mask = CIRCPAD_CIRC_BUILDING| + add->conditions.apply_state_mask = CIRCPAD_CIRC_BUILDING| CIRCPAD_CIRC_NO_STREAMS|CIRCPAD_CIRC_HAS_RELAY_EARLY; - add->conditions.purpose_mask = CIRCPAD_PURPOSE_ALL; + add->conditions.apply_purpose_mask = CIRCPAD_PURPOSE_ALL; circpad_register_padding_machine(add, origin_padding_machines); add = helper_create_conditional_machine(); @@ -1751,9 +1752,9 @@ helper_create_conditional_machines(void) add->conditions.requires_vanguards = 1; add->conditions.min_hops = 3; - add->conditions.state_mask = CIRCPAD_CIRC_OPENED| + add->conditions.apply_state_mask = CIRCPAD_CIRC_OPENED| CIRCPAD_CIRC_STREAMS|CIRCPAD_CIRC_HAS_NO_RELAY_EARLY; - add->conditions.purpose_mask = CIRCPAD_PURPOSE_ALL; + add->conditions.apply_purpose_mask = CIRCPAD_PURPOSE_ALL; circpad_register_padding_machine(add, origin_padding_machines); add = helper_create_conditional_machine(); @@ -2727,8 +2728,8 @@ helper_create_ender_machine(void) circ_client_machine.states[CIRCPAD_STATE_START]. next_state[CIRCPAD_EVENT_NONPADDING_RECV] = CIRCPAD_STATE_END; - circ_client_machine.conditions.state_mask = CIRCPAD_STATE_ALL; - circ_client_machine.conditions.purpose_mask = CIRCPAD_PURPOSE_ALL; + circ_client_machine.conditions.apply_state_mask = CIRCPAD_STATE_ALL; + circ_client_machine.conditions.apply_purpose_mask = CIRCPAD_PURPOSE_ALL; } static time_t mocked_timeofday; diff --git a/src/test/test_circuitstats.c b/src/test/test_circuitstats.c index e15dec5a01..00ca1b544c 100644 --- a/src/test/test_circuitstats.c +++ b/src/test/test_circuitstats.c @@ -17,18 +17,13 @@ #include "core/or/circuituse.h" #include "core/or/channel.h" -#include "core/or/cpath_build_state_st.h" #include "core/or/crypt_path_st.h" #include "core/or/extend_info_st.h" #include "core/or/origin_circuit_st.h" -void test_circuitstats_timeout(void *arg); -void test_circuitstats_hoplen(void *arg); -origin_circuit_t *subtest_fourhop_circuit(struct timeval, int); -origin_circuit_t *add_opened_threehop(void); -origin_circuit_t *build_unopened_fourhop(struct timeval); - -int cpath_append_hop(crypt_path_t **head_ptr, extend_info_t *choice); +static origin_circuit_t *add_opened_threehop(void); +static origin_circuit_t *build_unopened_fourhop(struct timeval); +static origin_circuit_t *subtest_fourhop_circuit(struct timeval, int); static int marked_for_close; /* Mock function because we are not trying to test the close circuit that does @@ -45,85 +40,71 @@ mock_circuit_mark_for_close(circuit_t *circ, int reason, int line, return; } -origin_circuit_t * +static origin_circuit_t * add_opened_threehop(void) { - origin_circuit_t *or_circ = origin_circuit_new(); + struct timeval circ_start_time; + memset(&circ_start_time, 0, sizeof(circ_start_time)); extend_info_t fakehop; memset(&fakehop, 0, sizeof(fakehop)); - - TO_CIRCUIT(or_circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; - - or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t)); - or_circ->build_state->desired_path_len = DEFAULT_ROUTE_LEN; - - cpath_append_hop(&or_circ->cpath, &fakehop); - cpath_append_hop(&or_circ->cpath, &fakehop); - cpath_append_hop(&or_circ->cpath, &fakehop); - - or_circ->has_opened = 1; - TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN; - TO_CIRCUIT(or_circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; - - return or_circ; + extend_info_t *fakehop_list[DEFAULT_ROUTE_LEN] = {&fakehop, + &fakehop, + &fakehop}; + + return new_test_origin_circuit(true, + circ_start_time, + DEFAULT_ROUTE_LEN, + fakehop_list); } -origin_circuit_t * +static origin_circuit_t * build_unopened_fourhop(struct timeval circ_start_time) { - origin_circuit_t *or_circ = origin_circuit_new(); - extend_info_t *fakehop = tor_malloc_zero(sizeof(extend_info_t)); - memset(fakehop, 0, sizeof(extend_info_t)); - - TO_CIRCUIT(or_circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; - TO_CIRCUIT(or_circ)->timestamp_began = circ_start_time; - TO_CIRCUIT(or_circ)->timestamp_created = circ_start_time; - - or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t)); - or_circ->build_state->desired_path_len = 4; - - cpath_append_hop(&or_circ->cpath, fakehop); - cpath_append_hop(&or_circ->cpath, fakehop); - cpath_append_hop(&or_circ->cpath, fakehop); - cpath_append_hop(&or_circ->cpath, fakehop); - - tor_free(fakehop); - - return or_circ; + extend_info_t fakehop; + memset(&fakehop, 0, sizeof(fakehop)); + extend_info_t *fakehop_list[4] = {&fakehop, + &fakehop, + &fakehop, + &fakehop}; + + return new_test_origin_circuit(false, + circ_start_time, + 4, + fakehop_list); } -origin_circuit_t * +static origin_circuit_t * subtest_fourhop_circuit(struct timeval circ_start_time, int should_timeout) { - origin_circuit_t *or_circ = build_unopened_fourhop(circ_start_time); + origin_circuit_t *origin_circ = build_unopened_fourhop(circ_start_time); // Now make them open one at a time and call // circuit_build_times_handle_completed_hop(); - or_circ->cpath->state = CPATH_STATE_OPEN; - circuit_build_times_handle_completed_hop(or_circ); + origin_circ->cpath->state = CPATH_STATE_OPEN; + circuit_build_times_handle_completed_hop(origin_circ); tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 0); - or_circ->cpath->next->state = CPATH_STATE_OPEN; - circuit_build_times_handle_completed_hop(or_circ); + origin_circ->cpath->next->state = CPATH_STATE_OPEN; + circuit_build_times_handle_completed_hop(origin_circ); tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 0); // Third hop: We should count it now. - or_circ->cpath->next->next->state = CPATH_STATE_OPEN; - circuit_build_times_handle_completed_hop(or_circ); + origin_circ->cpath->next->next->state = CPATH_STATE_OPEN; + circuit_build_times_handle_completed_hop(origin_circ); tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, !should_timeout); // 1 if counted, 0 otherwise // Fourth hop: Don't double count - or_circ->cpath->next->next->next->state = CPATH_STATE_OPEN; - circuit_build_times_handle_completed_hop(or_circ); + origin_circ->cpath->next->next->next->state = CPATH_STATE_OPEN; + circuit_build_times_handle_completed_hop(origin_circ); tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, !should_timeout); done: - return or_circ; + return origin_circ; } -void +static void test_circuitstats_hoplen(void *arg) { /* Plan: diff --git a/src/test/test_config.c b/src/test/test_config.c index 095eb24c49..0862ba5c44 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -53,6 +53,7 @@ #include "test/test_helpers.h" #include "test/resolve_test_helpers.h" +#include "test/log_test_helpers.h" #include "feature/dirclient/dir_server_st.h" #include "core/or/port_cfg_st.h" @@ -989,53 +990,72 @@ test_config_fix_my_family(void *arg) } static int n_hostname_01010101 = 0; +static const char *ret_addr_lookup_01010101[2] = { + "1.1.1.1", "0101::0101", +}; -/** This mock function is meant to replace tor_lookup_hostname(). +/** This mock function is meant to replace tor_addr_lookup(). * It answers with 1.1.1.1 as IP adddress that resulted from lookup. * This function increments <b>n_hostname_01010101</b> counter by one * every time it is called. */ static int -tor_lookup_hostname_01010101(const char *name, uint32_t *addr) +tor_addr_lookup_01010101(const char *name, uint16_t family, tor_addr_t *addr) { n_hostname_01010101++; - if (name && addr) { - *addr = ntohl(0x01010101); + if (family == AF_INET) { + if (name && addr) { + int ret = tor_addr_parse(addr, ret_addr_lookup_01010101[0]); + tt_int_op(ret, OP_EQ, family); + } + } else if (family == AF_INET6) { + if (name && addr) { + int ret = tor_addr_parse(addr, ret_addr_lookup_01010101[1]); + tt_int_op(ret, OP_EQ, family); + } } - + done: return 0; } static int n_hostname_localhost = 0; -/** This mock function is meant to replace tor_lookup_hostname(). +/** This mock function is meant to replace tor_addr_lookup(). * It answers with 127.0.0.1 as IP adddress that resulted from lookup. * This function increments <b>n_hostname_localhost</b> counter by one * every time it is called. */ static int -tor_lookup_hostname_localhost(const char *name, uint32_t *addr) +tor_addr_lookup_localhost(const char *name, uint16_t family, tor_addr_t *addr) { n_hostname_localhost++; - if (name && addr) { - *addr = 0x7f000001; + if (family == AF_INET) { + if (name && addr) { + tor_addr_from_ipv4h(addr, 0x7f000001); + } + } else if (family == AF_INET6) { + if (name && addr) { + int ret = tor_addr_parse(addr, "::1"); + tt_int_op(ret, OP_EQ, AF_INET6); + } } - + done: return 0; } static int n_hostname_failure = 0; -/** This mock function is meant to replace tor_lookup_hostname(). +/** This mock function is meant to replace tor_addr_lookup(). * It pretends to fail by returning -1 to caller. Also, this function * increments <b>n_hostname_failure</b> every time it is called. */ static int -tor_lookup_hostname_failure(const char *name, uint32_t *addr) +tor_addr_lookup_failure(const char *name, uint16_t family, tor_addr_t *addr) { (void)name; + (void)family; (void)addr; n_hostname_failure++; @@ -1043,6 +1063,46 @@ tor_lookup_hostname_failure(const char *name, uint32_t *addr) return -1; } +/** Mock function for tor_addr_lookup(). + * + * Depending on the given hostname and family, resolve either to IPv4 or IPv6. + * + * If the requested hostname family is not the same as the family provided, an + * error is returned. + * + * Possible hostnames: + * - www.torproject.org.v4 for IPv4 -> 1.1.1.1 + * - www.torproject.org.v6 for IPv6 -> [0101::0101] + */ +static int +tor_addr_lookup_mixed(const char *name, uint16_t family, tor_addr_t *addr) +{ + tt_assert(addr); + tt_assert(name); + + if (!strcmp(name, "www.torproject.org.v4")) { + if (family == AF_INET) { + tor_addr_from_ipv4h(addr, 0x01010101); + return 0; + } + /* Resolving AF_INET but the asked family is different. Failure. */ + return -1; + } + + if (!strcmp(name, "www.torproject.org.v6")) { + if (family == AF_INET6) { + int ret = tor_addr_parse(addr, "0101::0101"); + tt_int_op(ret, OP_EQ, AF_INET6); + return 0; + } + /* Resolving AF_INET6 but the asked family is not. Failure. */ + return -1; + } + + done: + return 0; +} + static int n_gethostname_replacement = 0; /** This mock function is meant to replace tor_gethostname(). It @@ -1097,29 +1157,39 @@ tor_gethostname_failure(char *name, size_t namelen) return -1; } -static int n_get_interface_address = 0; +static int n_get_interface_address6 = 0; +static sa_family_t last_address6_family; +static const char *ret_get_interface_address6_08080808[2] = { + "8.8.8.8", "0808::0808", +}; /** This mock function is meant to replace get_interface_address(). * It answers with address 8.8.8.8. This function increments * <b>n_get_interface_address</b> by one every time it is called. */ static int -get_interface_address_08080808(int severity, uint32_t *addr) +get_interface_address6_08080808(int severity, sa_family_t family, + tor_addr_t *addr) { (void)severity; - n_get_interface_address++; + n_get_interface_address6++; - if (addr) { - *addr = ntohl(0x08080808); + if (family == AF_INET) { + if (addr) { + int ret = tor_addr_parse(addr, ret_get_interface_address6_08080808[0]); + tt_int_op(ret, OP_EQ, AF_INET); + } + } else if (family == AF_INET6) { + if (addr) { + int ret = tor_addr_parse(addr, ret_get_interface_address6_08080808[1]); + tt_int_op(ret, OP_EQ, AF_INET6); + } } - + done: return 0; } -static int n_get_interface_address6 = 0; -static sa_family_t last_address6_family; - /** This mock function is meant to replace get_interface_address6(). * It answers with IP address 9.9.9.9 iff both of the following are true: * - <b>family</b> is AF_INET @@ -1127,6 +1197,7 @@ static sa_family_t last_address6_family; * This function increments <b>n_get_interface_address6</b> by one every * time it is called. */ +#if 0 static int get_interface_address6_replacement(int severity, sa_family_t family, tor_addr_t *addr) @@ -1144,25 +1215,7 @@ get_interface_address6_replacement(int severity, sa_family_t family, return 0; } - -static int n_get_interface_address_failure = 0; - -/** - * This mock function is meant to replace get_interface_address(). - * It pretends to fail getting interface address by returning -1. - * <b>n_get_interface_address_failure</b> is incremented by one - * every time this function is called. - */ -static int -get_interface_address_failure(int severity, uint32_t *addr) -{ - (void)severity; - (void)addr; - - n_get_interface_address_failure++; - - return -1; -} +#endif static int n_get_interface_address6_failure = 0; @@ -1185,24 +1238,44 @@ get_interface_address6_failure(int severity, sa_family_t family, return -1; } +/** Helper macro: to validate the returned value from find_my_address() so we + * don't copy those all the time. */ +#undef VALIDATE_FOUND_ADDRESS +#define VALIDATE_FOUND_ADDRESS(ret, method, hostname) \ + do { \ + tt_int_op(retval, OP_EQ, ret); \ + if (method == NULL) tt_assert(!method_used); \ + else tt_str_op(method_used, OP_EQ, method); \ + if (hostname == NULL) tt_assert(!hostname_out); \ + else tt_str_op(hostname_out, OP_EQ, hostname); \ + if (ret == true) { \ + tt_assert(tor_addr_eq(&resolved_addr, &test_addr)); \ + } \ + } while (0) + +/** Helper macro: Cleanup the address and variables used after a + * find_my_address() call. */ +#undef CLEANUP_FOUND_ADDRESS +#define CLEANUP_FOUND_ADDRESS \ + do { \ + config_free_lines(options->Address); \ + config_free_lines(options->ORPort_lines); \ + options->ORPort_set = 0; \ + tor_free(options->DirAuthorities); \ + tor_free(hostname_out); \ + tor_addr_make_unspec(&resolved_addr); \ + tor_addr_make_unspec(&test_addr); \ + } while (0) + +/** Test both IPv4 and IPv6 coexisting together in the configuration. */ static void -test_config_resolve_my_address(void *arg) +test_config_find_my_address_mixed(void *arg) { or_options_t *options; - uint32_t resolved_addr; + tor_addr_t resolved_addr, test_addr; const char *method_used; char *hostname_out = NULL; - int retval; - int prev_n_hostname_01010101; - int prev_n_hostname_localhost; - int prev_n_hostname_failure; - int prev_n_gethostname_replacement; - int prev_n_gethostname_failure; - int prev_n_gethostname_localhost; - int prev_n_get_interface_address; - int prev_n_get_interface_address_failure; - int prev_n_get_interface_address6; - int prev_n_get_interface_address6_failure; + bool retval; (void)arg; @@ -1210,369 +1283,538 @@ test_config_resolve_my_address(void *arg) options_init(options); - /* - * CASE 1: - * If options->Address is a valid IPv4 address string, we want - * the corresponding address to be parsed and returned. - */ + /* + * CASE 1: Only IPv6 address. Accepted. + */ + config_line_append(&options->Address, "Address", + "2a01:4f8:fff0:4f:266:37ff:fe2c:5d19"); + tor_addr_parse(&test_addr, "2a01:4f8:fff0:4f:266:37ff:fe2c:5d19"); - options->Address = tor_strdup("128.52.128.105"); + /* IPv4 address not guessed since one Address statement exists. */ + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(false, NULL, NULL); + /* IPv6 address should be found and considered configured. */ + retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "CONFIGURED", NULL); - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + CLEANUP_FOUND_ADDRESS; - tt_want(retval == 0); - tt_want_str_op(method_used,OP_EQ,"CONFIGURED"); - tt_want(hostname_out == NULL); - tt_assert(resolved_addr == 0x80348069); + /* + * Case 2: IPv4 _and_ IPv6 given. Accepted. + */ + config_line_append(&options->Address, "Address", + "2a01:4f8:fff0:4f:266:37ff:fe2c:5d19"); + config_line_append(&options->Address, "Address", "1.1.1.1"); + tor_addr_parse(&test_addr, "1.1.1.1"); - tor_free(options->Address); + /* IPv4 address should be found and considered configured. */ + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "CONFIGURED", NULL); -/* - * CASE 2: - * If options->Address is a valid DNS address, we want resolve_my_address() - * function to ask tor_lookup_hostname() for help with resolving it - * and return the address that was resolved (in host order). - */ + /* IPv6 address should be found and considered configured. */ + tor_addr_parse(&test_addr, "2a01:4f8:fff0:4f:266:37ff:fe2c:5d19"); + retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "CONFIGURED", NULL); - MOCK(tor_lookup_hostname,tor_lookup_hostname_01010101); + CLEANUP_FOUND_ADDRESS; - tor_free(options->Address); - options->Address = tor_strdup("www.torproject.org"); + /* + * Case 3: Two hostnames, IPv4 and IPv6. + */ + config_line_append(&options->Address, "Address", "www.torproject.org.v4"); + config_line_append(&options->Address, "Address", "www.torproject.org.v6"); - prev_n_hostname_01010101 = n_hostname_01010101; + /* Looks at specific hostname to learn which address family to use. */ + MOCK(tor_addr_lookup, tor_addr_lookup_mixed); - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + /* IPv4 address should be found and considered resolved. */ + tor_addr_parse(&test_addr, "1.1.1.1"); + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "RESOLVED", "www.torproject.org.v4"); + tor_free(hostname_out); - tt_want(retval == 0); - tt_want(n_hostname_01010101 == prev_n_hostname_01010101 + 1); - tt_want_str_op(method_used,OP_EQ,"RESOLVED"); - tt_want_str_op(hostname_out,OP_EQ,"www.torproject.org"); - tt_assert(resolved_addr == 0x01010101); + /* IPv6 address should be found and considered resolved. */ + tor_addr_parse(&test_addr, "0101::0101"); + retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "RESOLVED", "www.torproject.org.v6"); - UNMOCK(tor_lookup_hostname); + CLEANUP_FOUND_ADDRESS; + UNMOCK(tor_addr_lookup); - tor_free(options->Address); - tor_free(hostname_out); + /* + * Case 4: IPv4 address and a hostname resolving to IPV6. + */ + config_line_append(&options->Address, "Address", "1.1.1.1"); + config_line_append(&options->Address, "Address", "www.torproject.org.v6"); -/* - * CASE 3: - * Given that options->Address is NULL, we want resolve_my_address() - * to try and use tor_gethostname() to get hostname AND use - * tor_lookup_hostname() to get IP address. - */ + /* Looks at specific hostname to learn which address family to use. */ + MOCK(tor_addr_lookup, tor_addr_lookup_mixed); - resolved_addr = 0; - tor_free(options->Address); - options->Address = NULL; + /* IPv4 address should be found and configured. */ + tor_addr_parse(&test_addr, "1.1.1.1"); + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "CONFIGURED", NULL); - MOCK(tor_gethostname,tor_gethostname_replacement); - MOCK(tor_lookup_hostname,tor_lookup_hostname_01010101); + /* IPv6 address should be found and considered resolved. */ + tor_addr_parse(&test_addr, "0101::0101"); + retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "RESOLVED", "www.torproject.org.v6"); - prev_n_gethostname_replacement = n_gethostname_replacement; - prev_n_hostname_01010101 = n_hostname_01010101; + CLEANUP_FOUND_ADDRESS; + UNMOCK(tor_addr_lookup); + + /* + * Case 5: Hostname resolving to IPv4 and an IPv6 address. + */ + config_line_append(&options->Address, "Address", "0101::0101"); + config_line_append(&options->Address, "Address", "www.torproject.org.v4"); - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + /* Looks at specific hostname to learn which address family to use. */ + MOCK(tor_addr_lookup, tor_addr_lookup_mixed); - tt_want(retval == 0); - tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1); - tt_want(n_hostname_01010101 == prev_n_hostname_01010101 + 1); - tt_want_str_op(method_used,OP_EQ,"GETHOSTNAME"); - tt_want_str_op(hostname_out,OP_EQ,"onionrouter!"); - tt_assert(resolved_addr == 0x01010101); + /* IPv4 address should be found and resolved. */ + tor_addr_parse(&test_addr, "1.1.1.1"); + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "RESOLVED", "www.torproject.org.v4"); + tor_free(hostname_out); - UNMOCK(tor_gethostname); - UNMOCK(tor_lookup_hostname); + /* IPv6 address should be found and considered resolved. */ + tor_addr_parse(&test_addr, "0101::0101"); + retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "CONFIGURED", NULL); + CLEANUP_FOUND_ADDRESS; + UNMOCK(tor_addr_lookup); + + done: + config_free_lines(options->Address); + or_options_free(options); tor_free(hostname_out); -/* - * CASE 4: - * Given that options->Address is a local host address, we want - * resolve_my_address() function to fail. - */ + UNMOCK(tor_addr_lookup); +} - resolved_addr = 0; - tor_free(options->Address); - options->Address = tor_strdup("127.0.0.1"); +/** Parameters for the find_my_address() test. We test both AF_INET and + * AF_INET6 but we have one interface to do so thus we run the same exact unit + * tests for both without copying them. */ +typedef struct find_my_address_params_t { + /* Index where the mock function results are located. For intance, + * tor_addr_lookup_01010101() will have its returned value depending on the + * family in ret_addr_lookup_01010101[]. + * + * Values that can be found: + * AF_INET : index 0. + * AF_INET6: index 1. + */ + int idx; + int family; + const char *public_ip; + const char *internal_ip; + const char *orport; +} find_my_address_params_t; + +static find_my_address_params_t addr_param_v4 = { + .idx = 0, + .family = AF_INET, + .public_ip = "128.52.128.105", + .internal_ip = "127.0.0.1", +}; - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); +static find_my_address_params_t addr_param_v6 = { + .idx = 1, + .family = AF_INET6, + .public_ip = "[4242::4242]", + .internal_ip = "[::1]", +}; - tt_want(resolved_addr == 0); - tt_int_op(retval, OP_EQ, -1); +static void +test_config_find_my_address(void *arg) +{ + or_options_t *options; + tor_addr_t resolved_addr, test_addr; + const char *method_used; + char *hostname_out = NULL; + bool retval; + int prev_n_hostname_01010101; + int prev_n_hostname_failure; + int prev_n_hostname_localhost; + int prev_n_gethostname_replacement; + int prev_n_gethostname_failure; + int prev_n_gethostname_localhost; + int prev_n_get_interface_address6; + int prev_n_get_interface_address6_failure; - tor_free(options->Address); - tor_free(hostname_out); + const find_my_address_params_t *p = arg; -/* - * CASE 5: - * We want resolve_my_address() to fail if DNS address in options->Address - * cannot be resolved. - */ + options = options_new(); + options_init(options); - MOCK(tor_lookup_hostname,tor_lookup_hostname_failure); + /* + * Case 1: + * 1. Address is a valid address. + * + * Expected to succeed. + */ + config_line_append(&options->Address, "Address", p->public_ip); + tor_addr_parse(&test_addr, p->public_ip); - prev_n_hostname_failure = n_hostname_failure; + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - tor_free(options->Address); - options->Address = tor_strdup("www.tor-project.org"); + VALIDATE_FOUND_ADDRESS(true, "CONFIGURED", NULL); + CLEANUP_FOUND_ADDRESS; - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + /* + * Case 2: Address is a resolvable address. Expected to succeed. + */ + MOCK(tor_addr_lookup, tor_addr_lookup_01010101); - tt_want(n_hostname_failure == prev_n_hostname_failure + 1); - tt_int_op(retval, OP_EQ, -1); + config_line_append(&options->Address, "Address", "www.torproject.org"); + tor_addr_parse(&test_addr, ret_addr_lookup_01010101[p->idx]); - UNMOCK(tor_lookup_hostname); + prev_n_hostname_01010101 = n_hostname_01010101; - tor_free(options->Address); - tor_free(hostname_out); + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); -/* - * CASE 6: - * If options->Address is NULL AND gettting local hostname fails, we want - * resolve_my_address() to fail as well. - */ + tt_int_op(n_hostname_01010101, OP_EQ, ++prev_n_hostname_01010101); + VALIDATE_FOUND_ADDRESS(true, "RESOLVED", "www.torproject.org"); + CLEANUP_FOUND_ADDRESS; - MOCK(tor_gethostname,tor_gethostname_failure); + UNMOCK(tor_addr_lookup); - prev_n_gethostname_failure = n_gethostname_failure; + /* + * Case 3: Address is a local addressi (internal). Expected to fail. + */ + config_line_append(&options->Address, "Address", p->internal_ip); - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + setup_full_capture_of_logs(LOG_NOTICE); - tt_want(n_gethostname_failure == prev_n_gethostname_failure + 1); - tt_int_op(retval, OP_EQ, -1); + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - UNMOCK(tor_gethostname); - tor_free(hostname_out); + expect_log_msg_containing("is a private IP address. Tor relays that " + "use the default DirAuthorities must have " + "public IP addresses."); + teardown_capture_of_logs(); -/* - * CASE 7: - * We want resolve_my_address() to try and get network interface address via - * get_interface_address() if hostname returned by tor_gethostname() cannot be - * resolved into IP address. - */ + VALIDATE_FOUND_ADDRESS(false, NULL, NULL); + CLEANUP_FOUND_ADDRESS; - MOCK(tor_gethostname,tor_gethostname_replacement); - MOCK(tor_lookup_hostname,tor_lookup_hostname_failure); - MOCK(get_interface_address,get_interface_address_08080808); + /* + * Case 4: Address is a local address but custom authorities. Expected to + * succeed. + */ + config_line_append(&options->Address, "Address", p->internal_ip); + options->DirAuthorities = tor_malloc_zero(sizeof(config_line_t)); + tor_addr_parse(&test_addr, p->internal_ip); - prev_n_gethostname_replacement = n_gethostname_replacement; - prev_n_get_interface_address = n_get_interface_address; + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + VALIDATE_FOUND_ADDRESS(true, "CONFIGURED", NULL); + CLEANUP_FOUND_ADDRESS; - tt_want(retval == 0); - tt_want_int_op(n_gethostname_replacement, OP_EQ, - prev_n_gethostname_replacement + 1); - tt_want_int_op(n_get_interface_address, OP_EQ, - prev_n_get_interface_address + 1); - tt_want_str_op(method_used,OP_EQ,"INTERFACE"); - tt_want(hostname_out == NULL); - tt_assert(resolved_addr == 0x08080808); + /* + * Case 5: Multiple address in Address. Expected to fail. + */ + config_line_append(&options->Address, "Address", p->public_ip); + config_line_append(&options->Address, "Address", p->public_ip); - UNMOCK(get_interface_address); - tor_free(hostname_out); + setup_full_capture_of_logs(LOG_NOTICE); -/* - * CASE 8: - * Suppose options->Address is NULL AND hostname returned by tor_gethostname() - * is unresolvable. We want resolve_my_address to fail if - * get_interface_address() fails. - */ + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - MOCK(get_interface_address,get_interface_address_failure); + expect_log_msg_containing("Found 2 Address statement of address family"); + teardown_capture_of_logs(); - prev_n_get_interface_address_failure = n_get_interface_address_failure; - prev_n_gethostname_replacement = n_gethostname_replacement; + VALIDATE_FOUND_ADDRESS(false, NULL, NULL); + CLEANUP_FOUND_ADDRESS; - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + /* + * Case 6: Another address family is configured. Expected to fail. + */ + if (p->family == AF_INET) { + config_line_append(&options->Address, "Address", "4242::4242"); + } else { + config_line_append(&options->Address, "Address", "1.1.1.1"); + } - tt_want(n_get_interface_address_failure == - prev_n_get_interface_address_failure + 1); - tt_want(n_gethostname_replacement == - prev_n_gethostname_replacement + 1); - tt_int_op(retval, OP_EQ, -1); + setup_full_capture_of_logs(LOG_NOTICE); - UNMOCK(get_interface_address); - tor_free(hostname_out); + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); -/* - * CASE 9: - * Given that options->Address is NULL AND tor_lookup_hostname() - * fails AND hostname returned by gethostname() resolves - * to local IP address, we want resolve_my_address() function to - * call get_interface_address6(.,AF_INET,.) and return IP address - * the latter function has found. - */ + expect_log_msg_containing("No Address option found for family"); + teardown_capture_of_logs(); - MOCK(tor_lookup_hostname,tor_lookup_hostname_failure); - MOCK(tor_gethostname,tor_gethostname_replacement); - MOCK(get_interface_address6,get_interface_address6_replacement); + VALIDATE_FOUND_ADDRESS(false, NULL, NULL); + CLEANUP_FOUND_ADDRESS; - prev_n_gethostname_replacement = n_gethostname_replacement; + /* + * Case 7: Address is a non resolvable hostname. Expected to fail. + */ + MOCK(tor_addr_lookup, tor_addr_lookup_failure); + + config_line_append(&options->Address, "Address", "www.torproject.org"); prev_n_hostname_failure = n_hostname_failure; - prev_n_get_interface_address6 = n_get_interface_address6; - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + setup_full_capture_of_logs(LOG_NOTICE); - tt_want(last_address6_family == AF_INET); - tt_want(n_get_interface_address6 == prev_n_get_interface_address6 + 1); - tt_want(n_hostname_failure == prev_n_hostname_failure + 1); - tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1); - tt_want(retval == 0); - tt_want_str_op(method_used,OP_EQ,"INTERFACE"); - tt_assert(resolved_addr == 0x09090909); + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - UNMOCK(tor_lookup_hostname); - UNMOCK(tor_gethostname); - UNMOCK(get_interface_address6); + expect_log_msg_containing("Could not resolve local Address " + "'www.torproject.org'. Failing."); + teardown_capture_of_logs(); - tor_free(hostname_out); + tt_int_op(n_hostname_failure, OP_EQ, ++prev_n_hostname_failure); + VALIDATE_FOUND_ADDRESS(false, NULL, NULL); + CLEANUP_FOUND_ADDRESS; + + UNMOCK(tor_addr_lookup); /* - * CASE 10: We want resolve_my_address() to fail if all of the following - * are true: - * 1. options->Address is not NULL - * 2. ... but it cannot be converted to struct in_addr by - * tor_inet_aton() - * 3. ... and tor_lookup_hostname() fails to resolve the - * options->Address + * Case 8: + * 1. Address is NULL + * 2. Interface address is a valid address. + * + * Expected to succeed. */ + options->Address = NULL; + tor_addr_parse(&test_addr, ret_get_interface_address6_08080808[p->idx]); - MOCK(tor_lookup_hostname,tor_lookup_hostname_failure); + MOCK(get_interface_address6, get_interface_address6_08080808); - prev_n_hostname_failure = n_hostname_failure; + prev_n_get_interface_address6 = n_get_interface_address6; + + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - tor_free(options->Address); - options->Address = tor_strdup("some_hostname"); + tt_int_op(n_get_interface_address6, OP_EQ, ++prev_n_get_interface_address6); + VALIDATE_FOUND_ADDRESS(true, "INTERFACE", NULL); + CLEANUP_FOUND_ADDRESS; - retval = resolve_my_address(LOG_NOTICE, options, &resolved_addr, - &method_used,&hostname_out); + UNMOCK(get_interface_address6); - tt_want(n_hostname_failure == prev_n_hostname_failure + 1); - tt_int_op(retval, OP_EQ, -1); + /* + * Case 9: + * 1. Address is NULL + * 2. Interface address fails to be found. + * 3. Local hostname resolves to a valid address. + * + * Expected to succeed. + */ + options->Address = NULL; + tor_addr_parse(&test_addr, ret_addr_lookup_01010101[p->idx]); - UNMOCK(tor_gethostname); - UNMOCK(tor_lookup_hostname); + MOCK(get_interface_address6, get_interface_address6_failure); + MOCK(tor_gethostname, tor_gethostname_replacement); + MOCK(tor_addr_lookup, tor_addr_lookup_01010101); - tor_free(hostname_out); + prev_n_get_interface_address6_failure = n_get_interface_address6_failure; + prev_n_hostname_01010101 = n_hostname_01010101; + prev_n_gethostname_replacement = n_gethostname_replacement; + + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + + tt_int_op(n_get_interface_address6_failure, OP_EQ, + ++prev_n_get_interface_address6_failure); + tt_int_op(n_hostname_01010101, OP_EQ, + ++prev_n_hostname_01010101); + tt_int_op(n_gethostname_replacement, OP_EQ, + ++prev_n_gethostname_replacement); + VALIDATE_FOUND_ADDRESS(true, "GETHOSTNAME", "onionrouter!"); + CLEANUP_FOUND_ADDRESS; + + UNMOCK(get_interface_address6); + UNMOCK(tor_gethostname); + UNMOCK(tor_addr_lookup); /* - * CASE 11: - * Suppose the following sequence of events: - * 1. options->Address is NULL - * 2. tor_gethostname() succeeds to get hostname of machine Tor - * if running on. - * 3. Hostname from previous step cannot be converted to - * address by using tor_inet_aton() function. - * 4. However, tor_lookup_hostname() succeeds in resolving the - * hostname from step 2. - * 5. Unfortunately, tor_addr_is_internal() deems this address - * to be internal. - * 6. get_interface_address6(.,AF_INET,.) returns non-internal - * IPv4 + * Case 10: + * 1. Address is NULL + * 2. Interface address fails to be found. + * 3. Local hostname resolves to an internal address. * - * We want resolve_my_addr() to succeed with method "INTERFACE" - * and address from step 6. + * Expected to fail. */ - - tor_free(options->Address); options->Address = NULL; - MOCK(tor_gethostname,tor_gethostname_replacement); - MOCK(tor_lookup_hostname,tor_lookup_hostname_localhost); - MOCK(get_interface_address6,get_interface_address6_replacement); + MOCK(get_interface_address6, get_interface_address6_failure); + MOCK(tor_gethostname, tor_gethostname_localhost); + MOCK(tor_addr_lookup, tor_addr_lookup_localhost); - prev_n_gethostname_replacement = n_gethostname_replacement; + prev_n_get_interface_address6_failure = n_get_interface_address6_failure; prev_n_hostname_localhost = n_hostname_localhost; - prev_n_get_interface_address6 = n_get_interface_address6; + prev_n_gethostname_localhost = n_gethostname_localhost; - retval = resolve_my_address(LOG_DEBUG, options, &resolved_addr, - &method_used,&hostname_out); + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1); - tt_want(n_hostname_localhost == prev_n_hostname_localhost + 1); - tt_want(n_get_interface_address6 == prev_n_get_interface_address6 + 1); + tt_int_op(n_get_interface_address6_failure, OP_EQ, + ++prev_n_get_interface_address6_failure); + tt_int_op(n_hostname_localhost, OP_EQ, + ++prev_n_hostname_localhost); + tt_int_op(n_gethostname_localhost, OP_EQ, + ++prev_n_gethostname_localhost); + VALIDATE_FOUND_ADDRESS(false, NULL, NULL); + CLEANUP_FOUND_ADDRESS; - tt_str_op(method_used,OP_EQ,"INTERFACE"); - tt_ptr_op(hostname_out, OP_EQ, NULL); - tt_int_op(retval, OP_EQ, 0); + UNMOCK(get_interface_address6); + UNMOCK(tor_gethostname); + UNMOCK(tor_addr_lookup); /* - * CASE 11b: - * 1-5 as above. - * 6. get_interface_address6() fails. + * Case 11: + * 1. Address is NULL + * 2. Interface address fails to be found. + * 3. Local hostname fails to be found. * - * In this subcase, we want resolve_my_address() to fail. + * Expected to fail. */ + options->Address = NULL; - UNMOCK(get_interface_address6); - MOCK(get_interface_address6,get_interface_address6_failure); + MOCK(get_interface_address6, get_interface_address6_failure); + MOCK(tor_gethostname, tor_gethostname_failure); - prev_n_gethostname_replacement = n_gethostname_replacement; - prev_n_hostname_localhost = n_hostname_localhost; prev_n_get_interface_address6_failure = n_get_interface_address6_failure; + prev_n_gethostname_failure = n_gethostname_failure; - retval = resolve_my_address(LOG_DEBUG, options, &resolved_addr, - &method_used,&hostname_out); - - tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1); - tt_want(n_hostname_localhost == prev_n_hostname_localhost + 1); - tt_want(n_get_interface_address6_failure == - prev_n_get_interface_address6_failure + 1); + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - tt_int_op(retval, OP_EQ, -1); + tt_int_op(n_get_interface_address6_failure, OP_EQ, + ++prev_n_get_interface_address6_failure); + tt_int_op(n_gethostname_failure, OP_EQ, + ++prev_n_gethostname_failure); + VALIDATE_FOUND_ADDRESS(false, NULL, NULL); + CLEANUP_FOUND_ADDRESS; - UNMOCK(tor_gethostname); - UNMOCK(tor_lookup_hostname); UNMOCK(get_interface_address6); + UNMOCK(tor_gethostname); - /* CASE 12: - * Suppose the following happens: - * 1. options->Address is NULL AND options->DirAuthorities is non-NULL - * 2. tor_gethostname() succeeds in getting hostname of a machine ... - * 3. ... which is successfully parsed by tor_inet_aton() ... - * 4. into IPv4 address that tor_addr_is_inernal() considers to be - * internal. + /* + * Case 12: + * 1. Address is NULL + * 2. Interface address fails to be found. + * 3. Local hostname can't be resolved. * - * In this case, we want resolve_my_address() to fail. + * Expected to fail. */ - - tor_free(options->Address); options->Address = NULL; - options->DirAuthorities = tor_malloc_zero(sizeof(config_line_t)); - MOCK(tor_gethostname,tor_gethostname_localhost); + MOCK(get_interface_address6, get_interface_address6_failure); + MOCK(tor_gethostname, tor_gethostname_replacement); + MOCK(tor_addr_lookup, tor_addr_lookup_failure); - prev_n_gethostname_localhost = n_gethostname_localhost; + prev_n_get_interface_address6_failure = n_get_interface_address6_failure; + prev_n_gethostname_replacement = n_gethostname_replacement; + prev_n_hostname_failure = n_hostname_failure; - retval = resolve_my_address(LOG_DEBUG, options, &resolved_addr, - &method_used,&hostname_out); + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - tt_want(n_gethostname_localhost == prev_n_gethostname_localhost + 1); - tt_int_op(retval, OP_EQ, -1); + tt_int_op(n_get_interface_address6_failure, OP_EQ, + ++prev_n_get_interface_address6_failure); + tt_int_op(n_gethostname_replacement, OP_EQ, + ++prev_n_gethostname_replacement); + tt_int_op(n_hostname_failure, OP_EQ, + ++prev_n_hostname_failure); + VALIDATE_FOUND_ADDRESS(false, NULL, NULL); + CLEANUP_FOUND_ADDRESS; + /* + * Case 13: + * 1. Address is NULL. + * 2. ORPort has a valid public address. + */ + { + char *msg = NULL; + int n, w, ret; + char *orport_line = NULL; + + options->Address = NULL; + tor_asprintf(&orport_line, "%s:9001", p->public_ip); + config_line_append(&options->ORPort_lines, "ORPort", orport_line); + tor_free(orport_line); + + if (p->family == AF_INET6) { + /* XXX: Tor does _not_ allow an IPv6 only ORPort thus we need to add a + * bogus IPv4 at the moment. */ + config_line_append(&options->ORPort_lines, "ORPort", "1.1.1.1:9001"); + } + + ret = parse_ports(options, 0, &msg, &n, &w); + tt_int_op(ret, OP_EQ, 0); + tor_addr_parse(&test_addr, p->public_ip); + } + + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "CONFIGURED_ORPORT", NULL); + CLEANUP_FOUND_ADDRESS; + + /* + * Case 14: + * 1. Address is NULL. + * 2. ORPort has an internal address thus fails. + * 3. Interface as a valid address. + */ + { + char *msg = NULL; + int n, w, ret; + char *orport_line = NULL; + + options->Address = NULL; + tor_asprintf(&orport_line, "%s:9001", p->internal_ip); + config_line_append(&options->ORPort_lines, "ORPort", orport_line); + tor_free(orport_line); + + if (p->family == AF_INET6) { + /* XXX: Tor does _not_ allow an IPv6 only ORPort thus we need to add a + * bogus IPv4 at the moment. */ + config_line_append(&options->ORPort_lines, "ORPort", "1.1.1.1:9001"); + } + + ret = parse_ports(options, 0, &msg, &n, &w); + tt_int_op(ret, OP_EQ, 0); + } + tor_addr_parse(&test_addr, ret_get_interface_address6_08080808[p->idx]); + + MOCK(get_interface_address6, get_interface_address6_08080808); + + prev_n_get_interface_address6 = n_get_interface_address6; + + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + + tt_int_op(n_get_interface_address6, OP_EQ, ++prev_n_get_interface_address6); + VALIDATE_FOUND_ADDRESS(true, "INTERFACE", NULL); + CLEANUP_FOUND_ADDRESS; + + UNMOCK(get_interface_address6); UNMOCK(tor_gethostname); + UNMOCK(tor_addr_lookup); done: - tor_free(options->Address); - tor_free(options->DirAuthorities); or_options_free(options); - tor_free(hostname_out); UNMOCK(tor_gethostname); - UNMOCK(tor_lookup_hostname); - UNMOCK(get_interface_address); + UNMOCK(tor_addr_lookup); UNMOCK(get_interface_address6); - UNMOCK(tor_gethostname); } static void @@ -6243,9 +6485,14 @@ test_config_getinfo_config_names(void *arg) tor_free(answer); } +#ifndef COCCI #define CONFIG_TEST(name, flags) \ { #name, test_config_ ## name, flags, NULL, NULL } +#define CONFIG_TEST_SETUP(suffix, name, flags, setup, setup_data) \ + { #name#suffix, test_config_ ## name, flags, setup, setup_data } +#endif + struct testcase_t config_tests[] = { CONFIG_TEST(adding_trusted_dir_server, TT_FORK), CONFIG_TEST(adding_fallback_dir_server, TT_FORK), @@ -6256,7 +6503,11 @@ struct testcase_t config_tests[] = { 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_SETUP(_v4, find_my_address, TT_FORK, + &passthrough_setup, &addr_param_v4), + CONFIG_TEST_SETUP(_v6, find_my_address, TT_FORK, + &passthrough_setup, &addr_param_v6), + CONFIG_TEST(find_my_address_mixed, TT_FORK), CONFIG_TEST(addressmap, 0), CONFIG_TEST(parse_bridge_line, 0), CONFIG_TEST(parse_transport_options_line, 0), diff --git a/src/test/test_controller.c b/src/test/test_controller.c index a69ec17db8..49efeb5f88 100644 --- a/src/test/test_controller.c +++ b/src/test/test_controller.c @@ -19,6 +19,7 @@ #include "feature/rend/rendservice.h" #include "feature/nodelist/authcert.h" #include "feature/nodelist/nodelist.h" +#include "feature/stats/rephist.h" #include "test/test.h" #include "test/test_helpers.h" #include "lib/net/resolve.h" @@ -2112,6 +2113,91 @@ test_control_getconf(void *arg) smartlist_free(reply_strs); } +static int +mock_rep_hist_get_circuit_handshake(uint16_t type) +{ + int ret; + + switch (type) { + case ONION_HANDSHAKE_TYPE_NTOR: + ret = 80; + break; + case ONION_HANDSHAKE_TYPE_TAP: + ret = 86; + break; + default: + ret = 0; + break; + } + + return ret; +} + +static void +test_stats(void *arg) +{ + /* We just need one of these to pass, it doesn't matter what's in it */ + control_connection_t dummy; + /* Get results out */ + char *answer = NULL; + const char *errmsg = NULL; + + (void) arg; + + /* We need these for returning the (mock) rephist. */ + MOCK(rep_hist_get_circuit_handshake_requested, + mock_rep_hist_get_circuit_handshake); + MOCK(rep_hist_get_circuit_handshake_assigned, + mock_rep_hist_get_circuit_handshake); + + /* NTor tests */ + getinfo_helper_rephist(&dummy, "stats/ntor/requested", + &answer, &errmsg); + tt_ptr_op(answer, OP_NE, NULL); + tt_ptr_op(errmsg, OP_EQ, NULL); + tt_str_op(answer, OP_EQ, "80"); + tor_free(answer); + errmsg = NULL; + + getinfo_helper_rephist(&dummy, "stats/ntor/assigned", + &answer, &errmsg); + tt_ptr_op(answer, OP_NE, NULL); + tt_ptr_op(errmsg, OP_EQ, NULL); + tt_str_op(answer, OP_EQ, "80"); + tor_free(answer); + errmsg = NULL; + + /* TAP tests */ + getinfo_helper_rephist(&dummy, "stats/tap/requested", + &answer, &errmsg); + tt_ptr_op(answer, OP_NE, NULL); + tt_ptr_op(errmsg, OP_EQ, NULL); + tt_str_op(answer, OP_EQ, "86"); + tor_free(answer); + errmsg = NULL; + + getinfo_helper_rephist(&dummy, "stats/tap/assigned", + &answer, &errmsg); + tt_ptr_op(answer, OP_NE, NULL); + tt_ptr_op(errmsg, OP_EQ, NULL); + tt_str_op(answer, OP_EQ, "86"); + tor_free(answer); + errmsg = NULL; + + getinfo_helper_rephist(&dummy, "stats/tap/onion_circuits_ddosed", + &answer, &errmsg); + tt_ptr_op(answer, OP_EQ, NULL); + tt_str_op(errmsg, OP_EQ, "Unrecognized handshake type"); + errmsg = NULL; + + done: + UNMOCK(rep_hist_get_circuit_handshake_requested); + UNMOCK(rep_hist_get_circuit_handshake_assigned); + tor_free(answer); + + return; +} + #ifndef COCCI #define PARSER_TEST(type) \ { "parse/" #type, test_controller_parse_cmd, 0, &passthrough_setup, \ @@ -2146,5 +2232,6 @@ struct testcase_t controller_tests[] = { { "getinfo_md_all", test_getinfo_md_all, 0, NULL, NULL }, { "control_reply", test_control_reply, 0, NULL, NULL }, { "control_getconf", test_control_getconf, 0, NULL, NULL }, + { "stats", test_stats, 0, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c index f31c28b24d..14913b4b40 100644 --- a/src/test/test_helpers.c +++ b/src/test/test_helpers.c @@ -16,28 +16,35 @@ #include "core/or/or.h" #include "lib/buf/buffers.h" -#include "app/config/config.h" #include "lib/confmgt/confmgt.h" -#include "app/main/subsysmgr.h" -#include "core/mainloop/connection.h" -#include "core/or/connection_or.h" #include "lib/crypt_ops/crypto_rand.h" -#include "core/mainloop/mainloop.h" -#include "feature/nodelist/nodelist.h" -#include "core/or/relay.h" -#include "feature/nodelist/routerlist.h" #include "lib/dispatch/dispatch.h" #include "lib/dispatch/dispatch_naming.h" -#include "lib/pubsub/pubsub_build.h" -#include "lib/pubsub/pubsub_connect.h" #include "lib/encoding/confline.h" #include "lib/net/resolve.h" +#include "lib/pubsub/pubsub_build.h" +#include "lib/pubsub/pubsub_connect.h" + +#include "core/mainloop/connection.h" +#include "core/mainloop/mainloop.h" +#include "core/or/connection_or.h" +#include "core/or/crypt_path.h" +#include "core/or/relay.h" + +#include "feature/nodelist/nodelist.h" +#include "feature/nodelist/routerlist.h" + +#include "app/config/config.h" +#include "app/main/subsysmgr.h" #include "core/or/cell_st.h" #include "core/or/connection_st.h" +#include "core/or/cpath_build_state_st.h" +#include "core/or/crypt_path_st.h" +#include "core/or/origin_circuit_st.h" #include "core/or/or_connection_st.h" + #include "feature/nodelist/node_st.h" -#include "core/or/origin_circuit_st.h" #include "feature/nodelist/routerlist_st.h" #include "test/test.h" @@ -441,3 +448,36 @@ helper_cleanup_pubsub(const struct testcase_t *testcase, void *dispatcher_) const struct testcase_setup_t helper_pubsub_setup = { helper_setup_pubsub, helper_cleanup_pubsub }; + +origin_circuit_t * +new_test_origin_circuit(bool has_opened, + struct timeval circ_start_time, + int path_len, + extend_info_t **ei_list) +{ + origin_circuit_t *origin_circ = origin_circuit_new(); + + TO_CIRCUIT(origin_circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; + + origin_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t)); + origin_circ->build_state->desired_path_len = path_len; + + if (ei_list) { + for (int i = 0; i < path_len; i++) { + extend_info_t *ei = ei_list[i]; + cpath_append_hop(&origin_circ->cpath, ei); + } + } + + if (has_opened) { + origin_circ->has_opened = 1; + TO_CIRCUIT(origin_circ)->state = CIRCUIT_STATE_OPEN; + origin_circ->cpath->state = CPATH_STATE_OPEN; + } else { + TO_CIRCUIT(origin_circ)->timestamp_began = circ_start_time; + TO_CIRCUIT(origin_circ)->timestamp_created = circ_start_time; + origin_circ->cpath->state = CPATH_STATE_CLOSED; + } + + return origin_circ; +} diff --git a/src/test/test_helpers.h b/src/test/test_helpers.h index eaf18e19e2..66007873d1 100644 --- a/src/test/test_helpers.h +++ b/src/test/test_helpers.h @@ -40,5 +40,10 @@ int helper_cleanup_pubsub(const struct testcase_t *, void *); extern const struct testcase_setup_t helper_pubsub_setup; +origin_circuit_t *new_test_origin_circuit(bool has_opened, + struct timeval circ_start_time, + int path_len, + extend_info_t **ei_list); + #endif /* !defined(TOR_TEST_HELPERS_H) */ diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c index ae5cc5ed84..f965344ce0 100644 --- a/src/test/test_hs_client.c +++ b/src/test/test_hs_client.c @@ -41,6 +41,7 @@ #include "feature/rend/rendcache.h" #include "core/or/circuitlist.h" #include "core/or/circuitbuild.h" +#include "core/or/extendinfo.h" #include "core/mainloop/connection.h" #include "core/or/connection_edge.h" #include "feature/nodelist/networkstatus.h" @@ -529,7 +530,7 @@ test_client_pick_intro(void *arg) get_options_mutable()->ClientUseIPv6 = 1; intro_ei = hs_get_extend_info_from_lspecs(ip->link_specifiers, &ip->onion_key, 1); - tt_assert(tor_addr_family(&intro_ei->addr) == AF_INET6); + tt_assert(tor_addr_family(&intro_ei->orports[0].addr) == AF_INET6); } tt_assert(intro_ei); if (intro_ei) { @@ -537,7 +538,8 @@ test_client_pick_intro(void *arg) char ip_addr[TOR_ADDR_BUF_LEN]; /* We need to decorate in case it is an IPv6 else routerset_parse() * doesn't like it. */ - ptr = tor_addr_to_str(ip_addr, &intro_ei->addr, sizeof(ip_addr), 1); + ptr = tor_addr_to_str(ip_addr, &intro_ei->orports[0].addr, + sizeof(ip_addr), 1); tt_assert(ptr == ip_addr); ret = routerset_parse(get_options_mutable()->ExcludeNodes, ip_addr, ""); diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index 80383baff8..68ec5af391 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -1619,7 +1619,7 @@ test_build_update_descriptors(void *arg) /* We won't test the service IP object because there is a specific test * already for this but we'll make sure that the state is coherent.*/ - /* Three link specifiers are mandatoy so make sure we do have them. */ + /* Three link specifiers are mandatory so make sure we do have them. */ tt_int_op(smartlist_len(ip_cur->base.link_specifiers), OP_EQ, 3); /* Make sure we have a valid encryption keypair generated when we pick an * intro point in the update process. */ diff --git a/src/test/test_nodelist.c b/src/test/test_nodelist.c index fbbbf0a99f..93d1ce453b 100644 --- a/src/test/test_nodelist.c +++ b/src/test/test_nodelist.c @@ -1182,11 +1182,11 @@ test_nodelist_extend_info_describe(void *arg) sizeof(mock_ei_ipv4.identity_digest)); strlcpy(mock_ei_ipv4.nickname, "TestOR7890123456789", sizeof(mock_ei_ipv4.nickname)); - tor_addr_parse(&mock_ei_ipv4.addr, "111.222.233.244"); + tor_addr_parse(&mock_ei_ipv4.orports[0].addr, "111.222.233.244"); /* Create and modify the other extend info. */ memcpy(&mock_ei_ipv6, &mock_ei_ipv4, sizeof(mock_ei_ipv6)); - tor_addr_parse(&mock_ei_ipv6.addr, + tor_addr_parse(&mock_ei_ipv6.orports[0].addr, "[1111:2222:3333:4444:5555:6666:7777:8888]"); /* We don't test the no-nickname and no-IP cases, because they're covered by diff --git a/src/test/test_policy.c b/src/test/test_policy.c index 7949e90e9e..388b5c74c9 100644 --- a/src/test/test_policy.c +++ b/src/test/test_policy.c @@ -8,6 +8,7 @@ #include "app/config/config.h" #include "core/or/circuitbuild.h" #include "core/or/policies.h" +#include "core/or/extendinfo.h" #include "feature/dirparse/policy_parse.h" #include "feature/hs/hs_common.h" #include "feature/hs/hs_descriptor.h" @@ -2084,8 +2085,8 @@ test_policies_fascist_firewall_allows_address(void *arg) expect_single_log_msg("Specified link specifiers is null"); \ } else { \ expect_no_log_entry(); \ - tt_assert(tor_addr_eq(&(expect_ap).addr, &ei->addr)); \ - tt_int_op((expect_ap).port, OP_EQ, ei->port); \ + tt_assert(tor_addr_eq(&(expect_ap).addr, &ei->orports[0].addr)); \ + tt_int_op((expect_ap).port, OP_EQ, ei->orports[0].port); \ extend_info_free(ei); \ } \ teardown_capture_of_logs(); \ diff --git a/src/test/test_process_descs.c b/src/test/test_process_descs.c index 14865cff13..5c2301f873 100644 --- a/src/test/test_process_descs.c +++ b/src/test/test_process_descs.c @@ -38,10 +38,10 @@ test_process_descs_versions(void *arg) { "Tor 0.4.0.5", true }, { "Tor 0.4.1.1-alpha", true }, { "Tor 0.4.1.4-rc", true }, + { "Tor 0.4.1.5", true }, // new enough to be supported { "Tor 0.3.5.7", false }, { "Tor 0.3.5.8", false }, - { "Tor 0.4.1.5", false }, { "Tor 0.4.2.1-alpha", false }, { "Tor 0.4.2.4-rc", false }, { "Tor 0.4.3.0-alpha-dev", false }, diff --git a/src/test/test_protover.c b/src/test/test_protover.c index 8fccae1a45..5e74265550 100644 --- a/src/test/test_protover.c +++ b/src/test/test_protover.c @@ -7,14 +7,18 @@ #include "orconfig.h" #include "test/test.h" -#include "core/or/protover.h" +#include "lib/tls/tortls.h" #include "core/or/or.h" + #include "core/or/connection_or.h" -#include "lib/tls/tortls.h" +#include "core/or/protover.h" +#include "core/or/versions.h" #include "feature/dirauth/dirvote.h" +#include "feature/relay/relay_handshake.h" + static void test_protover_parse(void *arg) { @@ -409,23 +413,21 @@ test_protover_supports_version(void *arg) * Hard-coded here, because they are not in the code, or not exposed in the * headers. */ #define PROTOVER_LINKAUTH_V1 1 -#define PROTOVER_LINKAUTH_V3 3 - +#define PROTOVER_LINKAUTH_V2 2 #define PROTOVER_RELAY_V1 1 -#define PROTOVER_RELAY_V2 2 +/* Deprecated HSIntro versions */ +#define PROTOVER_HS_INTRO_DEPRECATED_1 1 +#define PROTOVER_HS_INTRO_DEPRECATED_2 2 /* Highest supported HSv2 introduce protocol version. - * Hard-coded here, because it does not appear anywhere in the code. * It's not clear if we actually support version 2, see #25068. */ -#define PROTOVER_HSINTRO_V2 3 +#define PROTOVER_HS_INTRO_V2 3 -/* HSv2 Rend and HSDir protocol versions. - * Hard-coded here, because they do not appear anywhere in the code. */ +/* HSv2 Rend and HSDir protocol versions. */ #define PROTOVER_HS_RENDEZVOUS_POINT_V2 1 #define PROTOVER_HSDIR_V2 1 -/* DirCache, Desc, Microdesc, and Cons protocol versions. - * Hard-coded here, because they do not appear anywhere in the code. */ +/* DirCache, Desc, Microdesc, and Cons protocol versions. */ #define PROTOVER_DIRCACHE_V1 1 #define PROTOVER_DIRCACHE_V2 2 @@ -438,6 +440,10 @@ test_protover_supports_version(void *arg) #define PROTOVER_CONS_V1 1 #define PROTOVER_CONS_V2 2 +#define PROTOVER_PADDING_V1 1 + +#define PROTOVER_FLOWCTRL_V1 1 + /* Make sure we haven't forgotten any supported protocols */ static void test_protover_supported_protocols(void *arg) @@ -452,24 +458,27 @@ test_protover_supported_protocols(void *arg) PRT_LINK, MAX_LINK_PROTO)); for (uint16_t i = 0; i < MAX_PROTOCOLS_TO_TEST; i++) { - if (is_or_protocol_version_known(i)) { - tt_assert(protocol_list_supports_protocol(supported_protocols, + tt_int_op(protocol_list_supports_protocol(supported_protocols, PRT_LINK, - i)); - } + i), + OP_EQ, + is_or_protocol_version_known(i)); } -#ifdef HAVE_WORKING_TOR_TLS_GET_TLSSECRETS - /* Legacy LinkAuth does not appear anywhere in the code. */ - tt_assert(protocol_list_supports_protocol(supported_protocols, - PRT_LINKAUTH, - PROTOVER_LINKAUTH_V1)); -#endif /* defined(HAVE_WORKING_TOR_TLS_GET_TLSSECRETS) */ - /* Latest LinkAuth is not exposed in the headers. */ - tt_assert(protocol_list_supports_protocol(supported_protocols, + /* Legacy LinkAuth is only supported on OpenSSL and similar. */ + tt_int_op(protocol_list_supports_protocol(supported_protocols, PRT_LINKAUTH, - PROTOVER_LINKAUTH_V3)); - /* Is there any way to test for new LinkAuth? */ + PROTOVER_LINKAUTH_V1), + OP_EQ, + authchallenge_type_is_supported(AUTHTYPE_RSA_SHA256_TLSSECRET)); + /* LinkAuth=2 is unused */ + tt_assert(!protocol_list_supports_protocol(supported_protocols, + PRT_LINKAUTH, + PROTOVER_LINKAUTH_V2)); + tt_assert( + protocol_list_supports_protocol(supported_protocols, + PRT_LINKAUTH, + PROTOVER_LINKAUTH_ED25519_HANDSHAKE)); /* Relay protovers do not appear anywhere in the code. */ tt_assert(protocol_list_supports_protocol(supported_protocols, @@ -477,20 +486,38 @@ test_protover_supported_protocols(void *arg) PROTOVER_RELAY_V1)); tt_assert(protocol_list_supports_protocol(supported_protocols, PRT_RELAY, - PROTOVER_RELAY_V2)); - /* Is there any way to test for new Relay? */ + PROTOVER_RELAY_EXTEND2)); + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_RELAY, + PROTOVER_RELAY_ACCEPT_IPV6)); + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_RELAY, + PROTOVER_RELAY_EXTEND_IPV6)); + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_RELAY, + PROTOVER_RELAY_CANONICAL_IPV6)); + /* These HSIntro versions are deprecated */ + tt_assert(!protocol_list_supports_protocol(supported_protocols, + PRT_HSINTRO, + PROTOVER_HS_INTRO_DEPRECATED_1)); + tt_assert(!protocol_list_supports_protocol(supported_protocols, + PRT_HSINTRO, + PROTOVER_HS_INTRO_DEPRECATED_2)); /* We could test legacy HSIntro by calling rend_service_update_descriptor(), * and checking the protocols field. But that's unlikely to change, so * we just use a hard-coded value. */ tt_assert(protocol_list_supports_protocol(supported_protocols, PRT_HSINTRO, - PROTOVER_HSINTRO_V2)); + PROTOVER_HS_INTRO_V2)); /* Test for HSv3 HSIntro */ tt_assert(protocol_list_supports_protocol(supported_protocols, PRT_HSINTRO, PROTOVER_HS_INTRO_V3)); - /* Is there any way to test for new HSIntro? */ + /* Test for HSIntro DoS */ + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_HSINTRO, + PROTOVER_HS_INTRO_DOS)); /* Legacy HSRend does not appear anywhere in the code. */ tt_assert(protocol_list_supports_protocol(supported_protocols, @@ -500,7 +527,6 @@ test_protover_supported_protocols(void *arg) tt_assert(protocol_list_supports_protocol(supported_protocols, PRT_HSREND, PROTOVER_HS_RENDEZVOUS_POINT_V3)); - /* Is there any way to test for new HSRend? */ /* Legacy HSDir does not appear anywhere in the code. */ tt_assert(protocol_list_supports_protocol(supported_protocols, @@ -510,7 +536,6 @@ test_protover_supported_protocols(void *arg) tt_assert(protocol_list_supports_protocol(supported_protocols, PRT_HSDIR, PROTOVER_HSDIR_V3)); - /* Is there any way to test for new HSDir? */ /* No DirCache versions appear anywhere in the code. */ tt_assert(protocol_list_supports_protocol(supported_protocols, @@ -519,7 +544,6 @@ test_protover_supported_protocols(void *arg) tt_assert(protocol_list_supports_protocol(supported_protocols, PRT_DIRCACHE, PROTOVER_DIRCACHE_V2)); - /* Is there any way to test for new DirCache? */ /* No Desc versions appear anywhere in the code. */ tt_assert(protocol_list_supports_protocol(supported_protocols, @@ -537,7 +561,6 @@ test_protover_supported_protocols(void *arg) tt_assert(protocol_list_supports_protocol(supported_protocols, PRT_MICRODESC, PROTOVER_MICRODESC_V2)); - /* Is there any way to test for new Microdesc? */ /* No Cons versions appear anywhere in the code. */ tt_assert(protocol_list_supports_protocol(supported_protocols, @@ -546,7 +569,19 @@ test_protover_supported_protocols(void *arg) tt_assert(protocol_list_supports_protocol(supported_protocols, PRT_CONS, PROTOVER_CONS_V2)); - /* Is there any way to test for new Cons? */ + + /* Padding=1 is deprecated. */ + tt_assert(!protocol_list_supports_protocol(supported_protocols, + PRT_PADDING, + PROTOVER_PADDING_V1)); + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_PADDING, + PROTOVER_HS_SETUP_PADDING)); + + /* FlowCtrl */ + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_FLOWCTRL, + PROTOVER_FLOWCTRL_V1)); done: ; @@ -676,6 +711,228 @@ test_protover_vote_roundtrip_ours(void *args) tor_free(result); } +/* Stringifies its argument. + * 4 -> "4" */ +#define STR(x) #x + +#ifdef COCCI +#define PROTOVER(proto_string, version_macro) +#else +/* Generate a protocol version string using proto_string and version_macro. + * PROTOVER("HSIntro", PROTOVER_HS_INTRO_DOS) -> "HSIntro" "=" "5" + * Uses two levels of macros to turn PROTOVER_HS_INTRO_DOS into "5". + */ +#define PROTOVER(proto_string, version_macro) \ + (proto_string "=" STR(version_macro)) +#endif + +#define DEBUG_PROTOVER(flags) \ + STMT_BEGIN \ + log_debug(LD_GENERAL, \ + "protovers:\n" \ + "protocols_known: %d,\n" \ + "supports_extend2_cells: %d,\n" \ + "supports_accepting_ipv6_extends: %d,\n" \ + "supports_initiating_ipv6_extends: %d,\n" \ + "supports_canonical_ipv6_conns: %d,\n" \ + "supports_ed25519_link_handshake_compat: %d,\n" \ + "supports_ed25519_link_handshake_any: %d,\n" \ + "supports_ed25519_hs_intro: %d,\n" \ + "supports_establish_intro_dos_extension: %d,\n" \ + "supports_v3_hsdir: %d,\n" \ + "supports_v3_rendezvous_point: %d,\n" \ + "supports_hs_setup_padding: %d.", \ + (flags).protocols_known, \ + (flags).supports_extend2_cells, \ + (flags).supports_accepting_ipv6_extends, \ + (flags).supports_initiating_ipv6_extends, \ + (flags).supports_canonical_ipv6_conns, \ + (flags).supports_ed25519_link_handshake_compat, \ + (flags).supports_ed25519_link_handshake_any, \ + (flags).supports_ed25519_hs_intro, \ + (flags).supports_establish_intro_dos_extension, \ + (flags).supports_v3_hsdir, \ + (flags).supports_v3_rendezvous_point, \ + (flags).supports_hs_setup_padding); \ + STMT_END + +/* Test that the proto_string version version_macro sets summary_flag. */ +#define TEST_PROTOVER(proto_string, version_macro, summary_flag) \ + STMT_BEGIN \ + memset(&flags, 0, sizeof(flags)); \ + summarize_protover_flags(&flags, \ + PROTOVER(proto_string, version_macro), \ + NULL); \ + DEBUG_PROTOVER(flags); \ + tt_int_op(flags.protocols_known, OP_EQ, 1); \ + tt_int_op(flags.summary_flag, OP_EQ, 1); \ + flags.protocols_known = 0; \ + flags.summary_flag = 0; \ + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); \ + STMT_END + +static void +test_protover_summarize_flags(void *args) +{ + (void) args; + char pv[30]; + memset(&pv, 0, sizeof(pv)); + + protover_summary_cache_free_all(); + + protover_summary_flags_t zero_flags; + memset(&zero_flags, 0, sizeof(zero_flags)); + protover_summary_flags_t flags; + + memset(&flags, 0, sizeof(flags)); + summarize_protover_flags(&flags, NULL, NULL); + DEBUG_PROTOVER(flags); + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); + + memset(&flags, 0, sizeof(flags)); + summarize_protover_flags(&flags, "", ""); + DEBUG_PROTOVER(flags); + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); + + /* Now check version exceptions */ + + /* EXTEND2 cell support */ + memset(&flags, 0, sizeof(flags)); + summarize_protover_flags(&flags, NULL, "Tor 0.2.4.8-alpha"); + DEBUG_PROTOVER(flags); + tt_int_op(flags.protocols_known, OP_EQ, 1); + tt_int_op(flags.supports_extend2_cells, OP_EQ, 1); + /* Now clear those flags, and check the rest are zero */ + flags.protocols_known = 0; + flags.supports_extend2_cells = 0; + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); + + /* disabling HSDir v3 support for buggy versions */ + memset(&flags, 0, sizeof(flags)); + summarize_protover_flags(&flags, + PROTOVER("HSDir", PROTOVER_HSDIR_V3), + NULL); + DEBUG_PROTOVER(flags); + tt_int_op(flags.protocols_known, OP_EQ, 1); + tt_int_op(flags.supports_v3_hsdir, OP_EQ, 1); + /* Now clear those flags, and check the rest are zero */ + flags.protocols_known = 0; + flags.supports_v3_hsdir = 0; + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); + + memset(&flags, 0, sizeof(flags)); + summarize_protover_flags(&flags, + PROTOVER("HSDir", PROTOVER_HSDIR_V3), + "Tor 0.3.0.7"); + DEBUG_PROTOVER(flags); + tt_int_op(flags.protocols_known, OP_EQ, 1); + /* Now clear that flag, and check the rest are zero */ + flags.protocols_known = 0; + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); + + /* Now check standard summaries */ + + /* LinkAuth */ + memset(&flags, 0, sizeof(flags)); + summarize_protover_flags(&flags, + PROTOVER("LinkAuth", + PROTOVER_LINKAUTH_ED25519_HANDSHAKE), + NULL); + DEBUG_PROTOVER(flags); + tt_int_op(flags.protocols_known, OP_EQ, 1); + tt_int_op(flags.supports_ed25519_link_handshake_compat, OP_EQ, 1); + tt_int_op(flags.supports_ed25519_link_handshake_any, OP_EQ, 1); + /* Now clear those flags, and check the rest are zero */ + flags.protocols_known = 0; + flags.supports_ed25519_link_handshake_compat = 0; + flags.supports_ed25519_link_handshake_any = 0; + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); + + /* Test one greater */ + memset(&flags, 0, sizeof(flags)); + snprintf(pv, sizeof(pv), + "%s=%d", "LinkAuth", PROTOVER_LINKAUTH_ED25519_HANDSHAKE + 1); + summarize_protover_flags(&flags, pv, NULL); + DEBUG_PROTOVER(flags); + tt_int_op(flags.protocols_known, OP_EQ, 1); + tt_int_op(flags.supports_ed25519_link_handshake_compat, OP_EQ, 0); + tt_int_op(flags.supports_ed25519_link_handshake_any, OP_EQ, 1); + /* Now clear those flags, and check the rest are zero */ + flags.protocols_known = 0; + flags.supports_ed25519_link_handshake_compat = 0; + flags.supports_ed25519_link_handshake_any = 0; + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); + + /* Test one less */ + memset(&flags, 0, sizeof(flags)); + snprintf(pv, sizeof(pv), + "%s=%d", "LinkAuth", PROTOVER_LINKAUTH_ED25519_HANDSHAKE - 1); + summarize_protover_flags(&flags, pv, NULL); + DEBUG_PROTOVER(flags); + tt_int_op(flags.protocols_known, OP_EQ, 1); + tt_int_op(flags.supports_ed25519_link_handshake_compat, OP_EQ, 0); + tt_int_op(flags.supports_ed25519_link_handshake_any, OP_EQ, 0); + /* Now clear those flags, and check the rest are zero */ + flags.protocols_known = 0; + flags.supports_ed25519_link_handshake_compat = 0; + flags.supports_ed25519_link_handshake_any = 0; + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); + + /* We don't test "one more" and "one less" for each protocol version. + * But that could be a useful thing to add. */ + + /* Relay */ + memset(&flags, 0, sizeof(flags)); + /* This test relies on these versions being equal */ + tt_int_op(PROTOVER_RELAY_EXTEND2, OP_EQ, PROTOVER_RELAY_ACCEPT_IPV6); + summarize_protover_flags(&flags, + PROTOVER("Relay", PROTOVER_RELAY_EXTEND2), NULL); + DEBUG_PROTOVER(flags); + tt_int_op(flags.protocols_known, OP_EQ, 1); + tt_int_op(flags.supports_extend2_cells, OP_EQ, 1); + tt_int_op(flags.supports_accepting_ipv6_extends, OP_EQ, 1); + /* Now clear those flags, and check the rest are zero */ + flags.protocols_known = 0; + flags.supports_extend2_cells = 0; + flags.supports_accepting_ipv6_extends = 0; + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); + + memset(&flags, 0, sizeof(flags)); + /* This test relies on these versions being equal */ + tt_int_op(PROTOVER_RELAY_EXTEND_IPV6, OP_EQ, PROTOVER_RELAY_CANONICAL_IPV6); + summarize_protover_flags(&flags, + PROTOVER("Relay", PROTOVER_RELAY_EXTEND_IPV6), + NULL); + DEBUG_PROTOVER(flags); + tt_int_op(flags.protocols_known, OP_EQ, 1); + tt_int_op(flags.supports_accepting_ipv6_extends, OP_EQ, 1); + tt_int_op(flags.supports_initiating_ipv6_extends, OP_EQ, 1); + tt_int_op(flags.supports_canonical_ipv6_conns, OP_EQ, 1); + /* Now clear those flags, and check the rest are zero */ + flags.protocols_known = 0; + flags.supports_accepting_ipv6_extends = 0; + flags.supports_initiating_ipv6_extends = 0; + flags.supports_canonical_ipv6_conns = 0; + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); + + TEST_PROTOVER("HSIntro", PROTOVER_HS_INTRO_V3, + supports_ed25519_hs_intro); + TEST_PROTOVER("HSIntro", PROTOVER_HS_INTRO_DOS, + supports_establish_intro_dos_extension); + + TEST_PROTOVER("HSRend", PROTOVER_HS_RENDEZVOUS_POINT_V3, + supports_v3_rendezvous_point); + + TEST_PROTOVER("HSDir", PROTOVER_HSDIR_V3, + supports_v3_hsdir); + + TEST_PROTOVER("Padding", PROTOVER_HS_SETUP_PADDING, + supports_hs_setup_padding); + + done: + ; +} + #define PV_TEST(name, flags) \ { #name, test_protover_ ##name, (flags), NULL, NULL } @@ -690,5 +947,7 @@ struct testcase_t protover_tests[] = { PV_TEST(supported_protocols, 0), PV_TEST(vote_roundtrip, 0), PV_TEST(vote_roundtrip_ours, 0), + /* fork, because we memoize flags internally */ + PV_TEST(summarize_flags, TT_FORK), END_OF_TESTCASES }; diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c index fc437dccc0..bafc886bc6 100644 --- a/src/test/test_routerkeys.c +++ b/src/test/test_routerkeys.c @@ -51,7 +51,7 @@ test_routerkeys_write_fingerprint(void *arg) tt_int_op(crypto_pk_cmp_keys(get_server_identity_key(),key),OP_EQ,0); /* Write fingerprint file */ - tt_int_op(0, OP_EQ, router_write_fingerprint(0)); + tt_int_op(0, OP_EQ, router_write_fingerprint(0, 0)); cp = read_file_to_str(get_fname("write_fingerprint/fingerprint"), 0, NULL); crypto_pk_get_fingerprint(key, fp, 0); @@ -61,7 +61,7 @@ test_routerkeys_write_fingerprint(void *arg) tor_free(cp2); /* Write hashed-fingerprint file */ - tt_int_op(0, OP_EQ, router_write_fingerprint(1)); + tt_int_op(0, OP_EQ, router_write_fingerprint(1, 0)); cp = read_file_to_str(get_fname("write_fingerprint/hashed-fingerprint"), 0, NULL); crypto_pk_get_hashed_fingerprint(key, fp); @@ -73,7 +73,7 @@ test_routerkeys_write_fingerprint(void *arg) /* Replace outdated file */ write_str_to_file(get_fname("write_fingerprint/hashed-fingerprint"), "junk goes here", 0); - tt_int_op(0, OP_EQ, router_write_fingerprint(1)); + tt_int_op(0, OP_EQ, router_write_fingerprint(1, 0)); cp = read_file_to_str(get_fname("write_fingerprint/hashed-fingerprint"), 0, NULL); crypto_pk_get_hashed_fingerprint(key, fp); @@ -90,6 +90,52 @@ test_routerkeys_write_fingerprint(void *arg) } static void +test_routerkeys_write_ed25519_identity(void *arg) +{ + crypto_pk_t *key = pk_generate(2); + or_options_t *options = get_options_mutable(); + time_t now = time(NULL); + const char *ddir = get_fname("write_fingerprint"); + char *cp = NULL, *cp2 = NULL; + char ed25519_id[BASE64_DIGEST256_LEN + 1]; + + (void) arg; + + tt_assert(key); + + options->ORPort_set = 1; /* So that we can get the server ID key */ + tor_free(options->DataDirectory); + options->DataDirectory = tor_strdup(ddir); + options->Nickname = tor_strdup("haflinger"); + set_server_identity_key(key); + set_client_identity_key(crypto_pk_dup_key(key)); + + load_ed_keys(options, now); + generate_ed_link_cert(options, now, 0); + tt_assert(get_master_identity_key()); + + tt_int_op(0, OP_EQ, check_private_dir(ddir, CPD_CREATE, NULL)); + + /* Write fingerprint file */ + tt_int_op(0, OP_EQ, router_write_fingerprint(0, 1)); + cp = read_file_to_str(get_fname("write_fingerprint/fingerprint-ed25519"), + 0, NULL); + digest256_to_base64(ed25519_id, + (const char *) get_master_identity_key()->pubkey); + tor_asprintf(&cp2, "haflinger %s\n", ed25519_id); + tt_str_op(cp, OP_EQ, cp2); + tor_free(cp); + tor_free(cp2); + + done: + crypto_pk_free(key); + set_client_identity_key(NULL); + tor_free(cp); + tor_free(cp2); + routerkeys_free_all(); +} + +static void test_routerkeys_ed_certs(void *args) { (void)args; @@ -695,6 +741,7 @@ test_routerkeys_rsa_ed_crosscert(void *arg) struct testcase_t routerkeys_tests[] = { TEST(write_fingerprint, TT_FORK), + TEST(write_ed25519_identity, TT_FORK), TEST(ed_certs, TT_FORK), TEST(ed_key_create, TT_FORK), TEST(ed_key_init_basic, TT_FORK), diff --git a/src/test/test_routerset.c b/src/test/test_routerset.c index 892ac6e210..7601d5ba0f 100644 --- a/src/test/test_routerset.c +++ b/src/test/test_routerset.c @@ -1417,12 +1417,62 @@ test_rset_contains_router(void *arg) ri.nickname = (char *)nickname; r = routerset_contains_router(set, &ri, country); - tt_int_op(r, OP_EQ, 4); + done: routerset_free(set); } +static void +test_rset_contains_router_ipv4(void *arg) +{ + routerset_t *set; + routerinfo_t ri; + country_t country = 1; + int r; + const char *s; + (void) arg; + + /* IPv4 address test. */ + memset(&ri, 0, sizeof(ri)); + set = routerset_new(); + s = "10.0.0.1"; + r = routerset_parse(set, s, ""); + ri.addr = htonl(0x0a000001); /* 10.0.0.1 */ + ri.or_port = 1234; + + r = routerset_contains_router(set, &ri, country); + tt_int_op(r, OP_EQ, 3); + + done: + routerset_free(set); +} + +static void +test_rset_contains_router_ipv6(void *arg) +{ + routerset_t *set; + routerinfo_t ri; + country_t country = 1; + int r; + const char *s; + (void) arg; + + /* IPv6 address test. */ + memset(&ri, 0, sizeof(ri)); + set = routerset_new(); + s = "2600::1"; + r = routerset_parse(set, s, ""); + tor_addr_parse(&ri.ipv6_addr, "2600::1"); + ri.ipv6_orport = 12345; + + r = routerset_contains_router(set, &ri, country); + tt_int_op(r, OP_EQ, 3); + + done: + routerset_free(set); +} + /* * Functional test for routerset_contains_routerstatus. */ @@ -2144,6 +2194,10 @@ struct testcase_t routerset_tests[] = { { "contains_extendinfo", test_rset_contains_extendinfo, TT_FORK, NULL, NULL }, { "contains_router", test_rset_contains_router, TT_FORK, NULL, NULL }, + { "contains_router_ipv4", test_rset_contains_router_ipv4, + TT_FORK, NULL, NULL }, + { "contains_router_ipv6", test_rset_contains_router_ipv6, + TT_FORK, NULL, NULL }, { "contains_routerstatus", test_rset_contains_routerstatus, TT_FORK, NULL, NULL }, { "contains_none", test_rset_contains_none, TT_FORK, NULL, NULL }, diff --git a/src/trunnel/circpad_negotiation.c b/src/trunnel/circpad_negotiation.c index 547818f2ec..4e3ee3d5bd 100644 --- a/src/trunnel/circpad_negotiation.c +++ b/src/trunnel/circpad_negotiation.c @@ -112,6 +112,17 @@ circpad_negotiate_set_echo_request(circpad_negotiate_t *inp, uint8_t val) inp->echo_request = val; return 0; } +uint32_t +circpad_negotiate_get_machine_ctr(const circpad_negotiate_t *inp) +{ + return inp->machine_ctr; +} +int +circpad_negotiate_set_machine_ctr(circpad_negotiate_t *inp, uint32_t val) +{ + inp->machine_ctr = val; + return 0; +} const char * circpad_negotiate_check(const circpad_negotiate_t *obj) { @@ -148,6 +159,9 @@ circpad_negotiate_encoded_len(const circpad_negotiate_t *obj) /* Length of u8 echo_request IN [0, 1] */ result += 1; + + /* Length of u32 machine_ctr */ + result += 4; return result; } int @@ -203,6 +217,13 @@ circpad_negotiate_encode(uint8_t *output, const size_t avail, const circpad_nego trunnel_set_uint8(ptr, (obj->echo_request)); written += 1; ptr += 1; + /* Encode u32 machine_ctr */ + trunnel_assert(written <= avail); + if (avail - written < 4) + goto truncated; + trunnel_set_uint32(ptr, trunnel_htonl(obj->machine_ctr)); + written += 4; ptr += 4; + trunnel_assert(ptr == output + written); #ifdef TRUNNEL_CHECK_ENCODED_LEN @@ -263,6 +284,11 @@ circpad_negotiate_parse_into(circpad_negotiate_t *obj, const uint8_t *input, con remaining -= 1; ptr += 1; if (! (obj->echo_request == 0 || obj->echo_request == 1)) goto fail; + + /* Parse u32 machine_ctr */ + CHECK_REMAINING(4, truncated); + obj->machine_ctr = trunnel_ntohl(trunnel_get_uint32(ptr)); + remaining -= 4; ptr += 4; trunnel_assert(ptr + remaining == input + len_in); return len_in - remaining; @@ -372,6 +398,17 @@ circpad_negotiated_set_machine_type(circpad_negotiated_t *inp, uint8_t val) inp->machine_type = val; return 0; } +uint32_t +circpad_negotiated_get_machine_ctr(const circpad_negotiated_t *inp) +{ + return inp->machine_ctr; +} +int +circpad_negotiated_set_machine_ctr(circpad_negotiated_t *inp, uint32_t val) +{ + inp->machine_ctr = val; + return 0; +} const char * circpad_negotiated_check(const circpad_negotiated_t *obj) { @@ -408,6 +445,9 @@ circpad_negotiated_encoded_len(const circpad_negotiated_t *obj) /* Length of u8 machine_type */ result += 1; + + /* Length of u32 machine_ctr */ + result += 4; return result; } int @@ -463,6 +503,13 @@ circpad_negotiated_encode(uint8_t *output, const size_t avail, const circpad_neg trunnel_set_uint8(ptr, (obj->machine_type)); written += 1; ptr += 1; + /* Encode u32 machine_ctr */ + trunnel_assert(written <= avail); + if (avail - written < 4) + goto truncated; + trunnel_set_uint32(ptr, trunnel_htonl(obj->machine_ctr)); + written += 4; ptr += 4; + trunnel_assert(ptr == output + written); #ifdef TRUNNEL_CHECK_ENCODED_LEN @@ -523,6 +570,11 @@ circpad_negotiated_parse_into(circpad_negotiated_t *obj, const uint8_t *input, c CHECK_REMAINING(1, truncated); obj->machine_type = (trunnel_get_uint8(ptr)); remaining -= 1; ptr += 1; + + /* Parse u32 machine_ctr */ + CHECK_REMAINING(4, truncated); + obj->machine_ctr = trunnel_ntohl(trunnel_get_uint32(ptr)); + remaining -= 4; ptr += 4; trunnel_assert(ptr + remaining == input + len_in); return len_in - remaining; diff --git a/src/trunnel/circpad_negotiation.h b/src/trunnel/circpad_negotiation.h index ba9155019e..9004540d43 100644 --- a/src/trunnel/circpad_negotiation.h +++ b/src/trunnel/circpad_negotiation.h @@ -26,6 +26,7 @@ struct circpad_negotiate_st { uint8_t machine_type; /** If true, send a relay_drop reply.. */ uint8_t echo_request; + uint32_t machine_ctr; uint8_t trunnel_error_code_; }; #endif @@ -42,6 +43,14 @@ struct circpad_negotiated_st { /** Machine type is left unbounded because we can specify * new machines in the consensus */ uint8_t machine_type; + /** + * This field is used for shutdown synchronization. It is OK if + * it wraps, because all we need to do is make sure the STOP + * command is actually for the currently active machine. + * For backward-compatibility, though, 0 has special meaning + * (it means match any machine). + */ + uint32_t machine_ctr; uint8_t trunnel_error_code_; }; #endif @@ -118,6 +127,15 @@ uint8_t circpad_negotiate_get_echo_request(const circpad_negotiate_t *inp); * code on 'inp' on failure. */ int circpad_negotiate_set_echo_request(circpad_negotiate_t *inp, uint8_t val); +/** Return the value of the machine_ctr field of the + * circpad_negotiate_t in 'inp' + */ +uint32_t circpad_negotiate_get_machine_ctr(const circpad_negotiate_t *inp); +/** Set the value of the machine_ctr field of the circpad_negotiate_t + * in 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int circpad_negotiate_set_machine_ctr(circpad_negotiate_t *inp, uint32_t val); /** Return a newly allocated circpad_negotiated with all elements set * to zero. */ @@ -190,6 +208,15 @@ uint8_t circpad_negotiated_get_machine_type(const circpad_negotiated_t *inp); * -1 and set the error code on 'inp' on failure. */ int circpad_negotiated_set_machine_type(circpad_negotiated_t *inp, uint8_t val); +/** Return the value of the machine_ctr field of the + * circpad_negotiated_t in 'inp' + */ +uint32_t circpad_negotiated_get_machine_ctr(const circpad_negotiated_t *inp); +/** Set the value of the machine_ctr field of the circpad_negotiated_t + * in 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int circpad_negotiated_set_machine_ctr(circpad_negotiated_t *inp, uint32_t val); #endif diff --git a/src/trunnel/circpad_negotiation.trunnel b/src/trunnel/circpad_negotiation.trunnel index abbc929cc5..68fed6a013 100644 --- a/src/trunnel/circpad_negotiation.trunnel +++ b/src/trunnel/circpad_negotiation.trunnel @@ -27,6 +27,13 @@ struct circpad_negotiate { // FIXME-MP-AP: Maybe we just say to transition to the first state // here instead.. Also what about delay before responding? u8 echo_request IN [0,1]; + + // This field is used for shutdown synchronization. It is OK if + // it wraps, because all we need to do is make sure the STOP + // command is actually for the currently active machine. + // For backward-compatibility, though, 0 has special meaning + // (it means match any machine). + u32 machine_ctr; }; /** @@ -41,4 +48,14 @@ struct circpad_negotiated { /** Machine type is left unbounded because we can specify * new machines in the consensus */ u8 machine_type; + + /** + * This field is used for shutdown synchronization. It is OK if + * it wraps, because all we need to do is make sure the STOP + * command is actually for the currently active machine. + * For backward-compatibility, though, 0 has special meaning + * (it means match any machine). + */ + u32 machine_ctr; + }; diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h index ddcc3205cd..3c7b2ab5fc 100644 --- a/src/win32/orconfig.h +++ b/src/win32/orconfig.h @@ -217,7 +217,7 @@ #define USING_TWOS_COMPLEMENT /* Version number of package */ -#define VERSION "0.4.4.1-alpha-dev" +#define VERSION "0.4.5.0-alpha-dev" #define HAVE_STRUCT_SOCKADDR_IN6 #define HAVE_STRUCT_IN6_ADDR |