diff options
402 files changed, 11352 insertions, 5372 deletions
diff --git a/.gitignore b/.gitignore index 4dc8867a89..c00fbe97e1 100644 --- a/.gitignore +++ b/.gitignore @@ -212,6 +212,7 @@ uptime-*.json /src/test/fuzz/lf-fuzz-* # /src/tools/ +/src/tools/libtorrunner.a /src/tools/tor-checkkey /src/tools/tor-resolve /src/tools/tor-cov-resolve diff --git a/.travis.yml b/.travis.yml index 8cc210827a..d3918875dd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,6 +56,7 @@ env: - MAKEFLAGS="-j 2" matrix: - RUST_OPTIONS="--enable-rust --enable-cargo-online-mode" + - RUST_OPTIONS="--enable-rust" TOR_RUST_DEPENDENCIES=true - RUST_OPTIONS="" matrix: @@ -87,6 +88,10 @@ install: - if [[ "$RUST_OPTIONS" != "" ]]; then which cargo; fi - if [[ "$RUST_OPTIONS" != "" ]]; then rustc --version; fi - if [[ "$RUST_OPTIONS" != "" ]]; then cargo --version; fi + ## If we're testing rust builds in offline-mode, then set up our vendored dependencies + - if [[ "$RUST_OPTIONS" == "--enable-rust" ]]; then git submodule init ; fi + - if [[ "$RUST_OPTIONS" == "--enable-rust" ]]; then git submodule update; fi + - if [[ "$TOR_RUST_DEPENDENCIES" == "true" ]]; then export TOR_RUST_DEPENDENCIES=$PWD/src/ext/rust/crates; fi script: - ./autogen.sh @@ -1,3 +1,1088 @@ +Changes in version 0.3.2.7-rc - 2017-12-14 + Tor 0.3.2.7-rc fixes various bugs in earlier versions of Tor, + including some that could affect reliability or correctness. + + This is the first release candidate in the 0.3.2 series. If we find no + new bugs or regression here, then the first stable 0.3.2. release will + be nearly identical to this. + + o Major bugfixes (circuit prediction): + - Fix circuit prediction logic so that a client doesn't treat a port + as being "handled" by a circuit if that circuit already has + isolation settings on it. This change should make Tor clients more + responsive by improving their chances of having a pre-created + circuit ready for use when a request arrives. Fixes bug 18859; + bugfix on 0.2.3.3-alpha. + + o Minor features (logging): + - Provide better warnings when the getrandom() syscall fails. Closes + ticket 24500. + + o Minor features (portability): + - Tor now compiles correctly on arm64 with libseccomp-dev installed. + (It doesn't yet work with the sandbox enabled.) Closes + ticket 24424. + + o Minor bugfixes (bridge clients, bootstrap): + - Retry directory downloads when we get our first bridge descriptor + during bootstrap or while reconnecting to the network. Keep + retrying every time we get a bridge descriptor, until we have a + reachable bridge. Fixes part of bug 24367; bugfix on 0.2.0.3-alpha. + - Stop delaying bridge descriptor fetches when we have cached bridge + descriptors. Instead, only delay bridge descriptor fetches when we + have at least one reachable bridge. Fixes part of bug 24367; + bugfix on 0.2.0.3-alpha. + - Stop delaying directory fetches when we have cached bridge + descriptors. Instead, only delay bridge descriptor fetches when + all our bridges are definitely unreachable. Fixes part of bug + 24367; bugfix on 0.2.0.3-alpha. + + o Minor bugfixes (compilation): + - Fix a signed/unsigned comparison warning introduced by our fix to + TROVE-2017-009. Fixes bug 24480; bugfix on 0.2.5.16. + + o Minor bugfixes (correctness): + - Fix several places in our codebase where a C compiler would be + likely to eliminate a check, based on assuming that undefined + behavior had not happened elsewhere in the code. These cases are + usually a sign of redundant checking or dubious arithmetic. Found + by Georg Koppen using the "STACK" tool from Wang, Zeldovich, + Kaashoek, and Solar-Lezama. Fixes bug 24423; bugfix on various + Tor versions. + + o Minor bugfixes (onion service v3): + - Fix a race where an onion service would launch a new intro circuit + after closing an old one, but fail to register it before freeing + the previously closed circuit. This bug was making the service + unable to find the established intro circuit and thus not upload + its descriptor, thus making a service unavailable for up to 24 + hours. Fixes bug 23603; bugfix on 0.3.2.1-alpha. + + o Minor bugfixes (scheduler, KIST): + - Properly set the scheduler state of an unopened channel in the + KIST scheduler main loop. This prevents a harmless but annoying + log warning. Fixes bug 24502; bugfix on 0.3.2.4-alpha. + - Avoid a possible integer overflow when computing the available + space on the TCP buffer of a channel. This had no security + implications; but could make KIST allow too many cells on a + saturated connection. Fixes bug 24590; bugfix on 0.3.2.1-alpha. + - Downgrade to "info" a harmless warning about the monotonic time + moving backwards: This can happen on platform not supporting + monotonic time. Fixes bug 23696; bugfix on 0.3.2.1-alpha. + + +Changes in version 0.3.2.6-alpha - 2017-12-01 + This version of Tor is the latest in the 0.3.2 alpha series. It + includes fixes for several important security issues. All Tor users + should upgrade to this release, or to one of the other releases coming + out today. + + o Major bugfixes (security): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - Fix a denial of service issue where an attacker could crash a + directory authority using a malformed router descriptor. Fixes bug + 24245; bugfix on 0.2.9.4-alpha. Also tracked as TROVE-2017-010 + and CVE-2017-8820. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, onion service v2): + - Fix a use-after-free error that could crash v2 Tor onion services + when they failed to open circuits while expiring introduction + points. Fixes bug 24313; bugfix on 0.2.7.2-alpha. This issue is + also tracked as TROVE-2017-013 and CVE-2017-8823. + + o Major bugfixes (security, relay): + - When running as a relay, make sure that we never build a path + through ourselves, even in the case where we have somehow lost the + version of our descriptor appearing in the consensus. Fixes part + of bug 21534; bugfix on 0.2.0.1-alpha. This issue is also tracked + as TROVE-2017-012 and CVE-2017-8822. + - When running as a relay, make sure that we never choose ourselves + as a guard. Fixes part of bug 21534; bugfix on 0.3.0.1-alpha. This + issue is also tracked as TROVE-2017-012 and CVE-2017-8822. + + o Minor feature (relay statistics): + - Change relay bandwidth reporting stats interval from 4 hours to 24 + hours in order to reduce the efficiency of guard discovery + attacks. Fixes ticket 23856. + + o Minor features (directory authority): + - Add an IPv6 address for the "bastet" directory authority. Closes + ticket 24394. + + o Minor bugfixes (client): + - By default, do not enable storage of client-side DNS values. These + values were unused by default previously, but they should not have + been cached at all. Fixes bug 24050; bugfix on 0.2.6.3-alpha. + + +Changes in version 0.3.1.9 - 2017-12-01: + Tor 0.3.1.9 backports important security and stability fixes from the + 0.3.2 development series. All Tor users should upgrade to this + release, or to another of the releases coming out today. + + o Major bugfixes (security, backport from 0.3.2.6-alpha): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - Fix a denial of service issue where an attacker could crash a + directory authority using a malformed router descriptor. Fixes bug + 24245; bugfix on 0.2.9.4-alpha. Also tracked as TROVE-2017-010 + and CVE-2017-8820. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, onion service v2, backport from 0.3.2.6-alpha): + - Fix a use-after-free error that could crash v2 Tor onion services + when they failed to open circuits while expiring introduction + points. Fixes bug 24313; bugfix on 0.2.7.2-alpha. This issue is + also tracked as TROVE-2017-013 and CVE-2017-8823. + + o Major bugfixes (security, relay, backport from 0.3.2.6-alpha): + - When running as a relay, make sure that we never build a path + through ourselves, even in the case where we have somehow lost the + version of our descriptor appearing in the consensus. Fixes part + of bug 21534; bugfix on 0.2.0.1-alpha. This issue is also tracked + as TROVE-2017-012 and CVE-2017-8822. + - When running as a relay, make sure that we never choose ourselves + as a guard. Fixes part of bug 21534; bugfix on 0.3.0.1-alpha. This + issue is also tracked as TROVE-2017-012 and CVE-2017-8822. + + o Major bugfixes (exit relays, DNS, backport from 0.3.2.4-alpha): + - Fix an issue causing DNS to fail on high-bandwidth exit nodes, + making them nearly unusable. Fixes bugs 21394 and 18580; bugfix on + 0.1.2.2-alpha, which introduced eventdns. Thanks to Dhalgren for + identifying and finding a workaround to this bug and to Moritz, + Arthur Edelstein, and Roger for helping to track it down and + analyze it. + + o Minor features (bridge): + - Bridges now include notice in their descriptors that they are + bridges, and notice of their distribution status, based on their + publication settings. Implements ticket 18329. For more fine- + grained control of how a bridge is distributed, upgrade to 0.3.2.x + or later. + + o Minor features (directory authority, backport from 0.3.2.6-alpha): + - Add an IPv6 address for the "bastet" directory authority. Closes + ticket 24394. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfix (relay address resolution, backport from 0.3.2.1-alpha): + - Avoid unnecessary calls to directory_fetches_from_authorities() on + relays, to prevent spurious address resolutions and descriptor + rebuilds. This is a mitigation for bug 21789. Fixes bug 23470; + bugfix on in 0.2.8.1-alpha. + + o Minor bugfixes (compilation, backport from 0.3.2.1-alpha): + - Fix unused variable warnings in donna's Curve25519 SSE2 code. + Fixes bug 22895; bugfix on 0.2.7.2-alpha. + + o Minor bugfixes (logging, relay shutdown, annoyance, backport from 0.3.2.2-alpha): + - When a circuit is marked for close, do not attempt to package any + cells for channels on that circuit. Previously, we would detect + this condition lower in the call stack, when we noticed that the + circuit had no attached channel, and log an annoying message. + Fixes bug 8185; bugfix on 0.2.5.4-alpha. + + o Minor bugfixes (onion service, backport from 0.3.2.5-alpha): + - Rename the consensus parameter "hsdir-interval" to "hsdir_interval" + so it matches dir-spec.txt. Fixes bug 24262; bugfix + on 0.3.1.1-alpha. + + o Minor bugfixes (relay, crash, backport from 0.3.2.4-alpha): + - Avoid a crash when transitioning from client mode to bridge mode. + Previously, we would launch the worker threads whenever our + "public server" mode changed, but not when our "server" mode + changed. Fixes bug 23693; bugfix on 0.2.6.3-alpha. + + +Changes in version 0.3.0.13 - 2017-12-01 + Tor 0.3.0.13 backports important security and stability bugfixes from + later Tor releases. All Tor users should upgrade to this release, or + to another of the releases coming out today. + + Note: the Tor 0.3.0 series will no longer be supported after 26 Jan + 2018. If you need a release with long-term support, please stick with + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Major bugfixes (security, backport from 0.3.2.6-alpha): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - Fix a denial of service issue where an attacker could crash a + directory authority using a malformed router descriptor. Fixes bug + 24245; bugfix on 0.2.9.4-alpha. Also tracked as TROVE-2017-010 + and CVE-2017-8820. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, onion service v2, backport from 0.3.2.6-alpha): + - Fix a use-after-free error that could crash v2 Tor onion services + when they failed to open circuits while expiring introduction + points. Fixes bug 24313; bugfix on 0.2.7.2-alpha. This issue is + also tracked as TROVE-2017-013 and CVE-2017-8823. + + o Major bugfixes (security, relay, backport from 0.3.2.6-alpha): + - When running as a relay, make sure that we never build a path + through ourselves, even in the case where we have somehow lost the + version of our descriptor appearing in the consensus. Fixes part + of bug 21534; bugfix on 0.2.0.1-alpha. This issue is also tracked + as TROVE-2017-012 and CVE-2017-8822. + - When running as a relay, make sure that we never choose ourselves + as a guard. Fixes part of bug 21534; bugfix on 0.3.0.1-alpha. This + issue is also tracked as TROVE-2017-012 and CVE-2017-8822. + + o Major bugfixes (exit relays, DNS, backport from 0.3.2.4-alpha): + - Fix an issue causing DNS to fail on high-bandwidth exit nodes, + making them nearly unusable. Fixes bugs 21394 and 18580; bugfix on + 0.1.2.2-alpha, which introduced eventdns. Thanks to Dhalgren for + identifying and finding a workaround to this bug and to Moritz, + Arthur Edelstein, and Roger for helping to track it down and + analyze it. + + o Minor features (security, windows, backport from 0.3.1.1-alpha): + - Enable a couple of pieces of Windows hardening: one + (HeapEnableTerminationOnCorruption) that has been on-by-default + since Windows 8, and unavailable before Windows 7; and one + (PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION) which we believe doesn't + affect us, but shouldn't do any harm. Closes ticket 21953. + + o Minor features (bridge, backport from 0.3.1.9): + - Bridges now include notice in their descriptors that they are + bridges, and notice of their distribution status, based on their + publication settings. Implements ticket 18329. For more fine- + grained control of how a bridge is distributed, upgrade to 0.3.2.x + or later. + + o Minor features (directory authority, backport from 0.3.2.6-alpha): + - Add an IPv6 address for the "bastet" directory authority. Closes + ticket 24394. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfix (relay address resolution, backport from 0.3.2.1-alpha): + - Avoid unnecessary calls to directory_fetches_from_authorities() on + relays, to prevent spurious address resolutions and descriptor + rebuilds. This is a mitigation for bug 21789. Fixes bug 23470; + bugfix on in 0.2.8.1-alpha. + + o Minor bugfixes (compilation, backport from 0.3.2.1-alpha): + - Fix unused variable warnings in donna's Curve25519 SSE2 code. + Fixes bug 22895; bugfix on 0.2.7.2-alpha. + + o Minor bugfixes (logging, relay shutdown, annoyance, backport from 0.3.2.2-alpha): + - When a circuit is marked for close, do not attempt to package any + cells for channels on that circuit. Previously, we would detect + this condition lower in the call stack, when we noticed that the + circuit had no attached channel, and log an annoying message. + Fixes bug 8185; bugfix on 0.2.5.4-alpha. + + o Minor bugfixes (relay, crash, backport from 0.3.2.4-alpha): + - Avoid a crash when transitioning from client mode to bridge mode. + Previously, we would launch the worker threads whenever our + "public server" mode changed, but not when our "server" mode + changed. Fixes bug 23693; bugfix on 0.2.6.3-alpha. + + o Minor bugfixes (testing, backport from 0.3.1.6-rc): + - Fix an undersized buffer in test-memwipe.c. Fixes bug 23291; + bugfix on 0.2.7.2-alpha. Found and patched by Ties Stuij. + + +Changes in version 0.2.9.14 - 2017-12-01 + Tor 0.3.0.13 backports important security and stability bugfixes from + later Tor releases. All Tor users should upgrade to this release, or + to another of the releases coming out today. + + o Major bugfixes (exit relays, DNS, backport from 0.3.2.4-alpha): + - Fix an issue causing DNS to fail on high-bandwidth exit nodes, + making them nearly unusable. Fixes bugs 21394 and 18580; bugfix on + 0.1.2.2-alpha, which introduced eventdns. Thanks to Dhalgren for + identifying and finding a workaround to this bug and to Moritz, + Arthur Edelstein, and Roger for helping to track it down and + analyze it. + + o Major bugfixes (security, backport from 0.3.2.6-alpha): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - Fix a denial of service issue where an attacker could crash a + directory authority using a malformed router descriptor. Fixes bug + 24245; bugfix on 0.2.9.4-alpha. Also tracked as TROVE-2017-010 + and CVE-2017-8820. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, onion service v2, backport from 0.3.2.6-alpha): + - Fix a use-after-free error that could crash v2 Tor onion services + when they failed to open circuits while expiring introduction + points. Fixes bug 24313; bugfix on 0.2.7.2-alpha. This issue is + also tracked as TROVE-2017-013 and CVE-2017-8823. + + o Major bugfixes (security, relay, backport from 0.3.2.6-alpha): + - When running as a relay, make sure that we never build a path + through ourselves, even in the case where we have somehow lost the + version of our descriptor appearing in the consensus. Fixes part + of bug 21534; bugfix on 0.2.0.1-alpha. This issue is also tracked + as TROVE-2017-012 and CVE-2017-8822. + + o Minor features (bridge, backport from 0.3.1.9): + - Bridges now include notice in their descriptors that they are + bridges, and notice of their distribution status, based on their + publication settings. Implements ticket 18329. For more fine- + grained control of how a bridge is distributed, upgrade to 0.3.2.x + or later. + + o Minor features (directory authority, backport from 0.3.2.6-alpha): + - Add an IPv6 address for the "bastet" directory authority. Closes + ticket 24394. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + o Minor features (security, windows, backport from 0.3.1.1-alpha): + - Enable a couple of pieces of Windows hardening: one + (HeapEnableTerminationOnCorruption) that has been on-by-default + since Windows 8, and unavailable before Windows 7; and one + (PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION) which we believe doesn't + affect us, but shouldn't do any harm. Closes ticket 21953. + + o Minor bugfix (relay address resolution, backport from 0.3.2.1-alpha): + - Avoid unnecessary calls to directory_fetches_from_authorities() on + relays, to prevent spurious address resolutions and descriptor + rebuilds. This is a mitigation for bug 21789. Fixes bug 23470; + bugfix on in 0.2.8.1-alpha. + + o Minor bugfixes (compilation, backport from 0.3.2.1-alpha): + - Fix unused variable warnings in donna's Curve25519 SSE2 code. + Fixes bug 22895; bugfix on 0.2.7.2-alpha. + + o Minor bugfixes (logging, relay shutdown, annoyance, backport from 0.3.2.2-alpha): + - When a circuit is marked for close, do not attempt to package any + cells for channels on that circuit. Previously, we would detect + this condition lower in the call stack, when we noticed that the + circuit had no attached channel, and log an annoying message. + Fixes bug 8185; bugfix on 0.2.5.4-alpha. + + o Minor bugfixes (relay, crash, backport from 0.3.2.4-alpha): + - Avoid a crash when transitioning from client mode to bridge mode. + Previously, we would launch the worker threads whenever our + "public server" mode changed, but not when our "server" mode + changed. Fixes bug 23693; bugfix on 0.2.6.3-alpha. + + o Minor bugfixes (testing, backport from 0.3.1.6-rc): + - Fix an undersized buffer in test-memwipe.c. Fixes bug 23291; + bugfix on 0.2.7.2-alpha. Found and patched by Ties Stuij. + + +Changes in version 0.2.8.17 - 2017-12-01 + Tor 0.2.8.17 backports important security and stability bugfixes from + later Tor releases. All Tor users should upgrade to this release, or + to another of the releases coming out today. + + Note: the Tor 0.2.8 series will no longer be supported after 1 Jan + 2018. If you need a release with long-term support, please upgrade with + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Major bugfixes (security, backport from 0.3.2.6-alpha): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, onion service v2, backport from 0.3.2.6-alpha): + - Fix a use-after-free error that could crash v2 Tor onion services + when they failed to open circuits while expiring introduction + points. Fixes bug 24313; bugfix on 0.2.7.2-alpha. This issue is + also tracked as TROVE-2017-013 and CVE-2017-8823. + + o Major bugfixes (security, relay, backport from 0.3.2.6-alpha): + - When running as a relay, make sure that we never build a path through + ourselves, even in the case where we have somehow lost the version of + our descriptor appearing in the consensus. Fixes part of bug 21534; + bugfix on 0.2.0.1-alpha. This issue is also tracked as TROVE-2017-012 + and CVE-2017-8822. + + o Minor features (bridge, backport from 0.3.1.9): + - Bridges now include notice in their descriptors that they are + bridges, and notice of their distribution status, based on their + publication settings. Implements ticket 18329. For more fine- + grained control of how a bridge is distributed, upgrade to 0.3.2.x + or later. + + o Minor features (directory authority, backport from 0.3.2.6-alpha): + - Add an IPv6 address for the "bastet" directory authority. Closes + ticket 24394. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (testing, backport from 0.3.1.6-rc): + - Fix an undersized buffer in test-memwipe.c. Fixes bug 23291; + bugfix on 0.2.7.2-alpha. Found and patched by Ties Stuij. + + +Changes in version 0.2.5.16 - 2017-12-01 + Tor 0.2.5.13 backports important security and stability bugfixes from + later Tor releases. All Tor users should upgrade to this release, or + to another of the releases coming out today. + + Note: the Tor 0.2.5 series will no longer be supported after 1 May + 2018. If you need a release with long-term support, please upgrade to + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Major bugfixes (security, backport from 0.3.2.6-alpha): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, relay, backport from 0.3.2.6-alpha): + - When running as a relay, make sure that we never build a path + through ourselves, even in the case where we have somehow lost the + version of our descriptor appearing in the consensus. Fixes part + of bug 21534; bugfix on 0.2.0.1-alpha. This issue is also tracked + as TROVE-2017-012 and CVE-2017-8822. + + o Minor features (bridge, backport from 0.3.1.9): + - Bridges now include notice in their descriptors that they are + bridges, and notice of their distribution status, based on their + publication settings. Implements ticket 18329. For more fine- + grained control of how a bridge is distributed, upgrade to 0.3.2.x + or later. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + +Changes in version 0.3.2.5-alpha - 2017-11-22 + Tor 0.3.2.5-alpha is the fifth alpha release in the 0.3.2.x series. It + fixes several stability and reliability bugs, including a fix for + intermittent bootstrapping failures that some people have been seeing + since the 0.3.0.x series. + + Please test this alpha out -- many of these fixes will soon be + backported to stable Tor versions if no additional bugs are found + in them. + + o Major bugfixes (bootstrapping): + - Fetch descriptors aggressively whenever we lack enough to build + circuits, regardless of how many descriptors we are missing. + Previously, we would delay launching the fetch when we had fewer + than 15 missing descriptors, even if some of those descriptors + were blocking circuits from building. Fixes bug 23985; bugfix on + 0.1.1.11-alpha. The effects of this bug became worse in + 0.3.0.3-alpha, when we began treating missing descriptors from our + primary guards as a reason to delay circuits. + - Don't try fetching microdescriptors from relays that have failed + to deliver them in the past. Fixes bug 23817; bugfix + on 0.3.0.1-alpha. + + o Minor features (directory authority): + - Make the "Exit" flag assignment only depend on whether the exit + policy allows connections to ports 80 and 443. Previously relays + would get the Exit flag if they allowed connections to one of + these ports and also port 6667. Resolves ticket 23637. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + o Minor features (linux seccomp2 sandbox): + - Update the sandbox rules so that they should now work correctly + with Glibc 2.26. Closes ticket 24315. + + o Minor features (logging): + - Downgrade a pair of log messages that could occur when an exit's + resolver gave us an unusual (but not forbidden) response. Closes + ticket 24097. + - Improve the message we log when re-enabling circuit build timeouts + after having received a consensus. Closes ticket 20963. + + o Minor bugfixes (compilation): + - Fix a memory leak warning in one of the libevent-related + configuration tests that could occur when manually specifying + -fsanitize=address. Fixes bug 24279; bugfix on 0.3.0.2-alpha. + Found and patched by Alex Xu. + - When detecting OpenSSL on Windows from our configure script, make + sure to try linking with the ws2_32 library. Fixes bug 23783; + bugfix on 0.3.2.2-alpha. + + o Minor bugfixes (control port, linux seccomp2 sandbox): + - Avoid a crash when attempting to use the seccomp2 sandbox together + with the OwningControllerProcess feature. Fixes bug 24198; bugfix + on 0.2.5.1-alpha. + + o Minor bugfixes (control port, onion services): + - Report "FAILED" instead of "UPLOAD_FAILED" "FAILED" for the + HS_DESC event when a service is not able to upload a descriptor. + Fixes bug 24230; bugfix on 0.2.7.1-alpha. + + o Minor bugfixes (directory cache): + - Recover better from empty or corrupt files in the consensus cache + directory. Fixes bug 24099; bugfix on 0.3.1.1-alpha. + - When a consensus diff calculation is only partially successful, + only record the successful parts as having succeeded. Partial + success can happen if (for example) one compression method fails + but the others succeed. Previously we misrecorded all the + calculations as having succeeded, which would later cause a + nonfatal assertion failure. Fixes bug 24086; bugfix + on 0.3.1.1-alpha. + + o Minor bugfixes (logging): + - Only log once if we notice that KIST support is gone. Fixes bug + 24158; bugfix on 0.3.2.1-alpha. + - Suppress a log notice when relay descriptors arrive. We already + have a bootstrap progress for this so no need to log notice + everytime tor receives relay descriptors. Microdescriptors behave + the same. Fixes bug 23861; bugfix on 0.2.8.2-alpha. + + o Minor bugfixes (network layer): + - When closing a connection via close_connection_immediately(), we + mark it as "not blocked on bandwidth", to prevent later calls from + trying to unblock it, and give it permission to read. This fixes a + backtrace warning that can happen on relays under various + circumstances. Fixes bug 24167; bugfix on 0.1.0.1-rc. + + o Minor bugfixes (onion services): + - The introduction circuit was being timed out too quickly while + waiting for the rendezvous circuit to complete. Keep the intro + circuit around longer instead of timing out and reopening new ones + constantly. Fixes bug 23681; bugfix on 0.2.4.8-alpha. + - Rename the consensus parameter "hsdir-interval" to "hsdir_interval" + so it matches dir-spec.txt. Fixes bug 24262; bugfix + on 0.3.1.1-alpha. + - Silence a warning about failed v3 onion descriptor uploads that + can happen naturally under certain edge cases. Fixes part of bug + 23662; bugfix on 0.3.2.1-alpha. + + o Minor bugfixes (tests): + - Fix a memory leak in one of the bridge-distribution test cases. + Fixes bug 24345; bugfix on 0.3.2.3-alpha. + - Fix a bug in our fuzzing mock replacement for crypto_pk_checksig(), + to correctly handle cases where a caller gives it an RSA key of + under 160 bits. (This is not actually a bug in Tor itself, but + rather in our fuzzing code.) Fixes bug 24247; bugfix on + 0.3.0.3-alpha. Found by OSS-Fuzz as issue 4177. + + o Documentation: + - Add notes in man page regarding OS support for the various + scheduler types. Attempt to use less jargon in the scheduler + section. Closes ticket 24254. + + +Changes in version 0.3.2.4-alpha - 2017-11-08 + Tor 0.3.2.4-alpha is the fourth alpha release in the 0.3.2.x series. + It fixes several stability and reliability bugs, especially including + a major reliability issue that has been plaguing fast exit relays in + recent months. + + o Major bugfixes (exit relays, DNS): + - Fix an issue causing DNS to fail on high-bandwidth exit nodes, + making them nearly unusable. Fixes bugs 21394 and 18580; bugfix on + 0.1.2.2-alpha, which introduced eventdns. Thanks to Dhalgren for + identifying and finding a workaround to this bug and to Moritz, + Arthur Edelstein, and Roger for helping to track it down and + analyze it. + + o Major bugfixes (scheduler, channel): + - Stop processing scheduled channels if they closed while flushing + cells. This can happen if the write on the connection fails + leading to the channel being closed while in the scheduler loop. + Fixes bug 23751; bugfix on 0.3.2.1-alpha. + + o Minor features (logging, scheduler): + - Introduce a SCHED_BUG() function to log extra information about + the scheduler state if we ever catch a bug in the scheduler. + Closes ticket 23753. + + o Minor features (removed deprecations): + - The ClientDNSRejectInternalAddresses flag can once again be set in + non-testing Tor networks, so long as they do not use the default + directory authorities. This change also removes the deprecation of + this flag from 0.2.9.2-alpha. Closes ticket 21031. + + o Minor features (testing): + - Our fuzzing tests now test the encrypted portions of v3 onion + service descriptors. Implements more of 21509. + + o Minor bugfixes (directory client): + - On failure to download directory information, delay retry attempts + by a random amount based on the "decorrelated jitter" algorithm. + Our previous delay algorithm tended to produce extra-long delays + too easily. Fixes bug 23816; bugfix on 0.2.9.1-alpha. + + o Minor bugfixes (IPv6, v3 single onion services): + - Remove buggy code for IPv6-only v3 single onion services, and + reject attempts to configure them. This release supports IPv4, + dual-stack, and IPv6-only v3 onion services; and IPv4 and dual- + stack v3 single onion services. Fixes bug 23820; bugfix + on 0.3.2.1-alpha. + + o Minor bugfixes (logging, relay): + - Give only a protocol warning when the ed25519 key is not + consistent between the descriptor and microdescriptor of a relay. + This can happen, for instance, if the relay has been flagged + NoEdConsensus. Fixes bug 24025; bugfix on 0.3.2.1-alpha. + + o Minor bugfixes (manpage, onion service): + - Document that the HiddenServiceNumIntroductionPoints option is + 0-10 for v2 services and 0-20 for v3 services. Fixes bug 24115; + bugfix on 0.3.2.1-alpha. + + o Minor bugfixes (memory leaks): + - Fix a minor memory leak at exit in the KIST scheduler. This bug + should have no user-visible impact. Fixes bug 23774; bugfix + on 0.3.2.1-alpha. + - Fix a memory leak when decrypting a badly formatted v3 onion + service descriptor. Fixes bug 24150; bugfix on 0.3.2.1-alpha. + Found by OSS-Fuzz; this is OSS-Fuzz issue 3994. + + o Minor bugfixes (onion services): + - Cache some needed onion service client information instead of + constantly computing it over and over again. Fixes bug 23623; + bugfix on 0.3.2.1-alpha. + - Properly retry HSv3 descriptor fetches when missing required + directory information. Fixes bug 23762; bugfix on 0.3.2.1-alpha. + + o Minor bugfixes (path selection): + - When selecting relays by bandwidth, avoid a rounding error that + could sometimes cause load to be imbalanced incorrectly. + Previously, we would always round upwards; now, we round towards + the nearest integer. This had the biggest effect when a relay's + weight adjustments should have given it weight 0, but it got + weight 1 instead. Fixes bug 23318; bugfix on 0.2.4.3-alpha. + - When calculating the fraction of nodes that have descriptors, and + all nodes in the network have zero bandwidths, count the number of + nodes instead. Fixes bug 23318; bugfix on 0.2.4.10-alpha. + - Actually log the total bandwidth in compute_weighted_bandwidths(). + Fixes bug 24170; bugfix on 0.2.4.3-alpha. + + o Minor bugfixes (relay, crash): + - Avoid a crash when transitioning from client mode to bridge mode. + Previously, we would launch the worker threads whenever our + "public server" mode changed, but not when our "server" mode + changed. Fixes bug 23693; bugfix on 0.2.6.3-alpha. + + o Minor bugfixes (testing): + - Fix a spurious fuzzing-only use of an uninitialized value. Found + by Brian Carpenter. Fixes bug 24082; bugfix on 0.3.0.3-alpha. + - Test that IPv6-only clients can use microdescriptors when running + "make test-network-all". Requires chutney master 61c28b9 or later. + Closes ticket 24109. + + +Changes in version 0.3.2.3-alpha - 2017-10-27 + Tor 0.3.2.3-alpha is the third release in the 0.3.2 series. It fixes + numerous small bugs in earlier versions of 0.3.2.x, and adds a new + directory authority, Bastet. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Minor features (bridge): + - Bridge relays can now set the BridgeDistribution config option to + add a "bridge-distribution-request" line to their bridge + descriptor, which tells BridgeDB how they'd like their bridge + address to be given out. (Note that as of Oct 2017, BridgeDB does + not yet implement this feature.) As a side benefit, this feature + provides a way to distinguish bridge descriptors from non-bridge + descriptors. Implements tickets 18329. + + o Minor features (client, entry guards): + - Improve log messages when missing descriptors for primary guards. + Resolves ticket 23670. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (bridge): + - Overwrite the bridge address earlier in the process of retrieving + its descriptor, to make sure we reach it on the configured + address. Fixes bug 20532; bugfix on 0.2.0.10-alpha. + + o Minor bugfixes (documentation): + - Document better how to read gcov, and what our gcov postprocessing + scripts do. Fixes bug 23739; bugfix on 0.2.9.1-alpha. + + o Minor bugfixes (entry guards): + - Tor now updates its guard state when it reads a consensus + regardless of whether it's missing descriptors. That makes tor use + its primary guards to fetch descriptors in some edge cases where + it would previously have used fallback directories. Fixes bug + 23862; bugfix on 0.3.0.1-alpha. + + o Minor bugfixes (hidden service client): + - When handling multiple SOCKS request for the same .onion address, + only fetch the service descriptor once. + - When a descriptor fetch fails with a non-recoverable error, close + all pending SOCKS requests for that .onion. Fixes bug 23653; + bugfix on 0.3.2.1-alpha. + + o Minor bugfixes (hidden service): + - Always regenerate missing hidden service public key files. Prior + to this, if the public key was deleted from disk, it wouldn't get + recreated. Fixes bug 23748; bugfix on 0.3.2.2-alpha. Patch + from "cathugger". + - Make sure that we have a usable ed25519 key when the intro point + relay supports ed25519 link authentication. Fixes bug 24002; + bugfix on 0.3.2.1-alpha. + + o Minor bugfixes (hidden service, v2): + - When reloading configured hidden services, copy all information + from the old service object. Previously, some data was omitted, + causing delays in descriptor upload, and other bugs. Fixes bug + 23790; bugfix on 0.2.1.9-alpha. + + o Minor bugfixes (memory safety, defensive programming): + - Clear the target address when node_get_prim_orport() returns + early. Fixes bug 23874; bugfix on 0.2.8.2-alpha. + + o Minor bugfixes (relay): + - Avoid a BUG warning when receiving a dubious CREATE cell while an + option transition is in progress. Fixes bug 23952; bugfix + on 0.3.2.1-alpha. + + o Minor bugfixes (testing): + - Adjust the GitLab CI configuration to more closely match that of + Travis CI. Fixes bug 23757; bugfix on 0.3.2.2-alpha. + - Prevent scripts/test/coverage from attempting to move gcov output + to the root directory. Fixes bug 23741; bugfix on 0.2.5.1-alpha. + - When running unit tests as root, skip a test that would fail + because it expects a permissions error. This affects some + continuous integration setups. Fixes bug 23758; bugfix + on 0.3.2.2-alpha. + - Stop unconditionally mirroring the tor repository in GitLab CI. + This prevented developers from enabling GitLab CI on master. Fixes + bug 23755; bugfix on 0.3.2.2-alpha. + - Fix the hidden service v3 descriptor decoding fuzzing to use the + latest decoding API correctly. Fixes bug 21509; bugfix + on 0.3.2.1-alpha. + + o Minor bugfixes (warnings): + - When we get an HTTP request on a SOCKS port, tell the user about + the new HTTPTunnelPort option. Previously, we would give a "Tor is + not an HTTP Proxy" message, which stopped being true when + HTTPTunnelPort was introduced. Fixes bug 23678; bugfix + on 0.3.2.1-alpha. + + +Changes in version 0.2.5.15 - 2017-10-25 + Tor 0.2.5.15 backports a collection of bugfixes from later Tor release + series. It also adds a new directory authority, Bastet. + + Note: the Tor 0.2.5 series will no longer be supported after 1 May + 2018. If you need a release with long-term support, please upgrade to + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Major bugfixes (openbsd, denial-of-service, backport from 0.3.1.5-alpha): + - Avoid an assertion failure bug affecting our implementation of + inet_pton(AF_INET6) on certain OpenBSD systems whose strtol() + handling of "0xx" differs from what we had expected. Fixes bug + 22789; bugfix on 0.2.3.8-alpha. Also tracked as TROVE-2017-007. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (defensive programming, undefined behavior, backport from 0.3.1.4-alpha): + - Fix a memset() off the end of an array when packing cells. This + bug should be harmless in practice, since the corrupted bytes are + still in the same structure, and are always padding bytes, + ignored, or immediately overwritten, depending on compiler + behavior. Nevertheless, because the memset()'s purpose is to make + sure that any other cell-handling bugs can't expose bytes to the + network, we need to fix it. Fixes bug 22737; bugfix on + 0.2.4.11-alpha. Fixes CID 1401591. + + o Build features (backport from 0.3.1.5-alpha): + - Tor's repository now includes a Travis Continuous Integration (CI) + configuration file (.travis.yml). This is meant to help new + developers and contributors who fork Tor to a Github repository be + better able to test their changes, and understand what we expect + to pass. To use this new build feature, you must fork Tor to your + Github account, then go into the "Integrations" menu in the + repository settings for your fork and enable Travis, then push + your changes. Closes ticket 22636. + + +Changes in version 0.2.8.16 - 2017-10-25 + Tor 0.2.8.16 backports a collection of bugfixes from later Tor release + series, including a bugfix for a crash issue that had affected relays + under memory pressure. It also adds a new directory authority, Bastet. + + Note: the Tor 0.2.8 series will no longer be supported after 1 Jan + 2018. If you need a release with long-term support, please stick with + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Major bugfixes (relay, crash, assertion failure, backport from 0.3.2.2-alpha): + - Fix a timing-based assertion failure that could occur when the + circuit out-of-memory handler freed a connection's output buffer. + Fixes bug 23690; bugfix on 0.2.6.1-alpha. + + o Minor features (directory authorities, backport from 0.3.2.2-alpha): + - Remove longclaw's IPv6 address, as it will soon change. Authority + IPv6 addresses were originally added in 0.2.8.1-alpha. This leaves + 3/8 directory authorities with IPv6 addresses, but there are also + 52 fallback directory mirrors with IPv6 addresses. Resolves 19760. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + +Changes in version 0.2.9.13 - 2017-10-25 + Tor 0.2.9.13 backports a collection of bugfixes from later Tor release + series, including a bugfix for a crash issue that had affected relays + under memory pressure. It also adds a new directory authority, Bastet. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Major bugfixes (relay, crash, assertion failure, backport from 0.3.2.2-alpha): + - Fix a timing-based assertion failure that could occur when the + circuit out-of-memory handler freed a connection's output buffer. + Fixes bug 23690; bugfix on 0.2.6.1-alpha. + + o Minor features (directory authorities, backport from 0.3.2.2-alpha): + - Remove longclaw's IPv6 address, as it will soon change. Authority + IPv6 addresses were originally added in 0.2.8.1-alpha. This leaves + 3/8 directory authorities with IPv6 addresses, but there are also + 52 fallback directory mirrors with IPv6 addresses. Resolves 19760. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (directory authority, backport from 0.3.1.5-alpha): + - When a directory authority rejects a descriptor or extrainfo with + a given digest, mark that digest as undownloadable, so that we do + not attempt to download it again over and over. We previously + tried to avoid downloading such descriptors by other means, but we + didn't notice if we accidentally downloaded one anyway. This + behavior became problematic in 0.2.7.2-alpha, when authorities + began pinning Ed25519 keys. Fixes bug 22349; bugfix + on 0.2.1.19-alpha. + + o Minor bugfixes (memory safety, backport from 0.3.2.3-alpha): + - Clear the address when node_get_prim_orport() returns early. + Fixes bug 23874; bugfix on 0.2.8.2-alpha. + + o Minor bugfixes (Windows service, backport from 0.3.1.6-rc): + - When running as a Windows service, set the ID of the main thread + correctly. Failure to do so made us fail to send log messages to + the controller in 0.2.1.16-rc, slowed down controller event + delivery in 0.2.7.3-rc and later, and crash with an assertion + failure in 0.3.1.1-alpha. Fixes bug 23081; bugfix on 0.2.1.6-alpha. + Patch and diagnosis from "Vort". + + +Changes in version 0.3.0.12 - 2017-10-25 + Tor 0.3.0.12 backports a collection of bugfixes from later Tor release + series, including a bugfix for a crash issue that had affected relays + under memory pressure. It also adds a new directory authority, Bastet. + + Note: the Tor 0.3.0 series will no longer be supported after 26 Jan + 2018. If you need a release with long-term support, please stick with + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Major bugfixes (relay, crash, assertion failure, backport from 0.3.2.2-alpha): + - Fix a timing-based assertion failure that could occur when the + circuit out-of-memory handler freed a connection's output buffer. + Fixes bug 23690; bugfix on 0.2.6.1-alpha. + + o Minor features (directory authorities, backport from 0.3.2.2-alpha): + - Remove longclaw's IPv6 address, as it will soon change. Authority + IPv6 addresses were originally added in 0.2.8.1-alpha. This leaves + 3/8 directory authorities with IPv6 addresses, but there are also + 52 fallback directory mirrors with IPv6 addresses. Resolves 19760. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (directory authority, backport from 0.3.1.5-alpha): + - When a directory authority rejects a descriptor or extrainfo with + a given digest, mark that digest as undownloadable, so that we do + not attempt to download it again over and over. We previously + tried to avoid downloading such descriptors by other means, but we + didn't notice if we accidentally downloaded one anyway. This + behavior became problematic in 0.2.7.2-alpha, when authorities + began pinning Ed25519 keys. Fixes bug 22349; bugfix + on 0.2.1.19-alpha. + + o Minor bugfixes (hidden service, relay, backport from 0.3.2.2-alpha): + - Avoid a possible double close of a circuit by the intro point on + error of sending the INTRO_ESTABLISHED cell. Fixes bug 23610; + bugfix on 0.3.0.1-alpha. + + o Minor bugfixes (memory safety, backport from 0.3.2.3-alpha): + - Clear the address when node_get_prim_orport() returns early. + Fixes bug 23874; bugfix on 0.2.8.2-alpha. + + o Minor bugfixes (Windows service, backport from 0.3.1.6-rc): + - When running as a Windows service, set the ID of the main thread + correctly. Failure to do so made us fail to send log messages to + the controller in 0.2.1.16-rc, slowed down controller event + delivery in 0.2.7.3-rc and later, and crash with an assertion + failure in 0.3.1.1-alpha. Fixes bug 23081; bugfix on 0.2.1.6-alpha. + Patch and diagnosis from "Vort". + + +Changes in version 0.3.1.8 - 2017-10-25 + Tor 0.3.1.8 is the second stable release in the 0.3.1 series. + It includes several bugfixes, including a bugfix for a crash issue + that had affected relays under memory pressure. It also adds + a new directory authority, Bastet. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Major bugfixes (relay, crash, assertion failure, backport from 0.3.2.2-alpha): + - Fix a timing-based assertion failure that could occur when the + circuit out-of-memory handler freed a connection's output buffer. + Fixes bug 23690; bugfix on 0.2.6.1-alpha. + + o Minor features (directory authorities, backport from 0.3.2.2-alpha): + - Remove longclaw's IPv6 address, as it will soon change. Authority + IPv6 addresses were originally added in 0.2.8.1-alpha. This leaves + 3/8 directory authorities with IPv6 addresses, but there are also + 52 fallback directory mirrors with IPv6 addresses. Resolves 19760. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (compilation, backport from 0.3.2.2-alpha): + - Fix a compilation warning when building with zstd support on + 32-bit platforms. Fixes bug 23568; bugfix on 0.3.1.1-alpha. Found + and fixed by Andreas Stieger. + + o Minor bugfixes (compression, backport from 0.3.2.2-alpha): + - Handle a pathological case when decompressing Zstandard data when + the output buffer size is zero. Fixes bug 23551; bugfix + on 0.3.1.1-alpha. + + o Minor bugfixes (directory authority, backport from 0.3.2.1-alpha): + - Remove the length limit on HTTP status lines that authorities can + send in their replies. Fixes bug 23499; bugfix on 0.3.1.6-rc. + + o Minor bugfixes (hidden service, relay, backport from 0.3.2.2-alpha): + - Avoid a possible double close of a circuit by the intro point on + error of sending the INTRO_ESTABLISHED cell. Fixes bug 23610; + bugfix on 0.3.0.1-alpha. + + o Minor bugfixes (memory safety, backport from 0.3.2.3-alpha): + - Clear the address when node_get_prim_orport() returns early. + Fixes bug 23874; bugfix on 0.2.8.2-alpha. + + o Minor bugfixes (unit tests, backport from 0.3.2.2-alpha): + - Fix additional channelpadding unit test failures by using mocked + time instead of actual time for all tests. Fixes bug 23608; bugfix + on 0.3.1.1-alpha. + + Changes in version 0.3.2.2-alpha - 2017-09-29 Tor 0.3.2.2-alpha is the second release in the 0.3.2 series. This release fixes several minor bugs in the new scheduler and next- @@ -3220,7 +4305,7 @@ Changes in version 0.3.0.1-alpha - 2016-12-19 initial code by Alec Heifetz. - Relays now support the HSDir version 3 protocol, so that they can can store and serve v3 descriptors. This is part of the next- - generation onion service work detailled in proposal 224. Closes + generation onion service work detailed in proposal 224. Closes ticket 17238. o Major features (protocol, ed25519 identity keys): diff --git a/Makefile.am b/Makefile.am index ad2ceb66a9..3445fc064d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,7 +26,7 @@ TESTING_TOR_BINARY=$(top_builddir)/src/or/tor$(EXEEXT) endif if USE_RUST -rust_ldadd=$(top_builddir)/src/rust/target/release/@TOR_RUST_UTIL_STATIC_NAME@ +rust_ldadd=$(top_builddir)/src/rust/target/release/@TOR_RUST_STATIC_NAME@ else rust_ldadd= endif @@ -246,3 +246,8 @@ mostlyclean-local: clean-local: rm -rf $(top_builddir)/src/rust/target rm -rf $(top_builddir)/src/rust/.cargo/registry + +if USE_RUST +distclean-local: distclean-rust +endif + diff --git a/ReleaseNotes b/ReleaseNotes index 07a3881acb..e11eebee9e 100644 --- a/ReleaseNotes +++ b/ReleaseNotes @@ -2,6 +2,644 @@ This document summarizes new features and bugfixes in each stable release of Tor. If you want to see more detailed descriptions of the changes in each development snapshot, see the ChangeLog file. +Changes in version 0.3.1.9 - 2017-12-01: + Tor 0.3.1.9 backports important security and stability fixes from the + 0.3.2 development series. All Tor users should upgrade to this + release, or to another of the releases coming out today. + + o Major bugfixes (security, backport from 0.3.2.6-alpha): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - Fix a denial of service issue where an attacker could crash a + directory authority using a malformed router descriptor. Fixes bug + 24245; bugfix on 0.2.9.4-alpha. Also tracked as TROVE-2017-010 + and CVE-2017-8820. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, onion service v2, backport from 0.3.2.6-alpha): + - Fix a use-after-free error that could crash v2 Tor onion services + when they failed to open circuits while expiring introduction + points. Fixes bug 24313; bugfix on 0.2.7.2-alpha. This issue is + also tracked as TROVE-2017-013 and CVE-2017-8823. + + o Major bugfixes (security, relay, backport from 0.3.2.6-alpha): + - When running as a relay, make sure that we never build a path + through ourselves, even in the case where we have somehow lost the + version of our descriptor appearing in the consensus. Fixes part + of bug 21534; bugfix on 0.2.0.1-alpha. This issue is also tracked + as TROVE-2017-012 and CVE-2017-8822. + - When running as a relay, make sure that we never choose ourselves + as a guard. Fixes part of bug 21534; bugfix on 0.3.0.1-alpha. This + issue is also tracked as TROVE-2017-012 and CVE-2017-8822. + + o Major bugfixes (exit relays, DNS, backport from 0.3.2.4-alpha): + - Fix an issue causing DNS to fail on high-bandwidth exit nodes, + making them nearly unusable. Fixes bugs 21394 and 18580; bugfix on + 0.1.2.2-alpha, which introduced eventdns. Thanks to Dhalgren for + identifying and finding a workaround to this bug and to Moritz, + Arthur Edelstein, and Roger for helping to track it down and + analyze it. + + o Minor features (bridge): + - Bridges now include notice in their descriptors that they are + bridges, and notice of their distribution status, based on their + publication settings. Implements ticket 18329. For more fine- + grained control of how a bridge is distributed, upgrade to 0.3.2.x + or later. + + o Minor features (directory authority, backport from 0.3.2.6-alpha): + - Add an IPv6 address for the "bastet" directory authority. Closes + ticket 24394. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfix (relay address resolution, backport from 0.3.2.1-alpha): + - Avoid unnecessary calls to directory_fetches_from_authorities() on + relays, to prevent spurious address resolutions and descriptor + rebuilds. This is a mitigation for bug 21789. Fixes bug 23470; + bugfix on in 0.2.8.1-alpha. + + o Minor bugfixes (compilation, backport from 0.3.2.1-alpha): + - Fix unused variable warnings in donna's Curve25519 SSE2 code. + Fixes bug 22895; bugfix on 0.2.7.2-alpha. + + o Minor bugfixes (logging, relay shutdown, annoyance, backport from 0.3.2.2-alpha): + - When a circuit is marked for close, do not attempt to package any + cells for channels on that circuit. Previously, we would detect + this condition lower in the call stack, when we noticed that the + circuit had no attached channel, and log an annoying message. + Fixes bug 8185; bugfix on 0.2.5.4-alpha. + + o Minor bugfixes (onion service, backport from 0.3.2.5-alpha): + - Rename the consensus parameter "hsdir-interval" to "hsdir_interval" + so it matches dir-spec.txt. Fixes bug 24262; bugfix + on 0.3.1.1-alpha. + + o Minor bugfixes (relay, crash, backport from 0.3.2.4-alpha): + - Avoid a crash when transitioning from client mode to bridge mode. + Previously, we would launch the worker threads whenever our + "public server" mode changed, but not when our "server" mode + changed. Fixes bug 23693; bugfix on 0.2.6.3-alpha. + + +Changes in version 0.3.0.13 - 2017-12-01 + Tor 0.3.0.13 backports important security and stability bugfixes from + later Tor releases. All Tor users should upgrade to this release, or + to another of the releases coming out today. + + Note: the Tor 0.3.0 series will no longer be supported after 26 Jan + 2018. If you need a release with long-term support, please stick with + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Major bugfixes (security, backport from 0.3.2.6-alpha): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - Fix a denial of service issue where an attacker could crash a + directory authority using a malformed router descriptor. Fixes bug + 24245; bugfix on 0.2.9.4-alpha. Also tracked as TROVE-2017-010 + and CVE-2017-8820. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, onion service v2, backport from 0.3.2.6-alpha): + - Fix a use-after-free error that could crash v2 Tor onion services + when they failed to open circuits while expiring introduction + points. Fixes bug 24313; bugfix on 0.2.7.2-alpha. This issue is + also tracked as TROVE-2017-013 and CVE-2017-8823. + + o Major bugfixes (security, relay, backport from 0.3.2.6-alpha): + - When running as a relay, make sure that we never build a path + through ourselves, even in the case where we have somehow lost the + version of our descriptor appearing in the consensus. Fixes part + of bug 21534; bugfix on 0.2.0.1-alpha. This issue is also tracked + as TROVE-2017-012 and CVE-2017-8822. + - When running as a relay, make sure that we never choose ourselves + as a guard. Fixes part of bug 21534; bugfix on 0.3.0.1-alpha. This + issue is also tracked as TROVE-2017-012 and CVE-2017-8822. + + o Major bugfixes (exit relays, DNS, backport from 0.3.2.4-alpha): + - Fix an issue causing DNS to fail on high-bandwidth exit nodes, + making them nearly unusable. Fixes bugs 21394 and 18580; bugfix on + 0.1.2.2-alpha, which introduced eventdns. Thanks to Dhalgren for + identifying and finding a workaround to this bug and to Moritz, + Arthur Edelstein, and Roger for helping to track it down and + analyze it. + + o Minor features (security, windows, backport from 0.3.1.1-alpha): + - Enable a couple of pieces of Windows hardening: one + (HeapEnableTerminationOnCorruption) that has been on-by-default + since Windows 8, and unavailable before Windows 7; and one + (PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION) which we believe doesn't + affect us, but shouldn't do any harm. Closes ticket 21953. + + o Minor features (bridge, backport from 0.3.1.9): + - Bridges now include notice in their descriptors that they are + bridges, and notice of their distribution status, based on their + publication settings. Implements ticket 18329. For more fine- + grained control of how a bridge is distributed, upgrade to 0.3.2.x + or later. + + o Minor features (directory authority, backport from 0.3.2.6-alpha): + - Add an IPv6 address for the "bastet" directory authority. Closes + ticket 24394. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfix (relay address resolution, backport from 0.3.2.1-alpha): + - Avoid unnecessary calls to directory_fetches_from_authorities() on + relays, to prevent spurious address resolutions and descriptor + rebuilds. This is a mitigation for bug 21789. Fixes bug 23470; + bugfix on in 0.2.8.1-alpha. + + o Minor bugfixes (compilation, backport from 0.3.2.1-alpha): + - Fix unused variable warnings in donna's Curve25519 SSE2 code. + Fixes bug 22895; bugfix on 0.2.7.2-alpha. + + o Minor bugfixes (logging, relay shutdown, annoyance, backport from 0.3.2.2-alpha): + - When a circuit is marked for close, do not attempt to package any + cells for channels on that circuit. Previously, we would detect + this condition lower in the call stack, when we noticed that the + circuit had no attached channel, and log an annoying message. + Fixes bug 8185; bugfix on 0.2.5.4-alpha. + + o Minor bugfixes (relay, crash, backport from 0.3.2.4-alpha): + - Avoid a crash when transitioning from client mode to bridge mode. + Previously, we would launch the worker threads whenever our + "public server" mode changed, but not when our "server" mode + changed. Fixes bug 23693; bugfix on 0.2.6.3-alpha. + + o Minor bugfixes (testing, backport from 0.3.1.6-rc): + - Fix an undersized buffer in test-memwipe.c. Fixes bug 23291; + bugfix on 0.2.7.2-alpha. Found and patched by Ties Stuij. + + +Changes in version 0.2.9.14 - 2017-12-01 + Tor 0.3.0.13 backports important security and stability bugfixes from + later Tor releases. All Tor users should upgrade to this release, or + to another of the releases coming out today. + + o Major bugfixes (exit relays, DNS, backport from 0.3.2.4-alpha): + - Fix an issue causing DNS to fail on high-bandwidth exit nodes, + making them nearly unusable. Fixes bugs 21394 and 18580; bugfix on + 0.1.2.2-alpha, which introduced eventdns. Thanks to Dhalgren for + identifying and finding a workaround to this bug and to Moritz, + Arthur Edelstein, and Roger for helping to track it down and + analyze it. + + o Major bugfixes (security, backport from 0.3.2.6-alpha): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - Fix a denial of service issue where an attacker could crash a + directory authority using a malformed router descriptor. Fixes bug + 24245; bugfix on 0.2.9.4-alpha. Also tracked as TROVE-2017-010 + and CVE-2017-8820. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, onion service v2, backport from 0.3.2.6-alpha): + - Fix a use-after-free error that could crash v2 Tor onion services + when they failed to open circuits while expiring introduction + points. Fixes bug 24313; bugfix on 0.2.7.2-alpha. This issue is + also tracked as TROVE-2017-013 and CVE-2017-8823. + + o Major bugfixes (security, relay, backport from 0.3.2.6-alpha): + - When running as a relay, make sure that we never build a path + through ourselves, even in the case where we have somehow lost the + version of our descriptor appearing in the consensus. Fixes part + of bug 21534; bugfix on 0.2.0.1-alpha. This issue is also tracked + as TROVE-2017-012 and CVE-2017-8822. + + o Minor features (bridge, backport from 0.3.1.9): + - Bridges now include notice in their descriptors that they are + bridges, and notice of their distribution status, based on their + publication settings. Implements ticket 18329. For more fine- + grained control of how a bridge is distributed, upgrade to 0.3.2.x + or later. + + o Minor features (directory authority, backport from 0.3.2.6-alpha): + - Add an IPv6 address for the "bastet" directory authority. Closes + ticket 24394. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + o Minor features (security, windows, backport from 0.3.1.1-alpha): + - Enable a couple of pieces of Windows hardening: one + (HeapEnableTerminationOnCorruption) that has been on-by-default + since Windows 8, and unavailable before Windows 7; and one + (PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION) which we believe doesn't + affect us, but shouldn't do any harm. Closes ticket 21953. + + o Minor bugfix (relay address resolution, backport from 0.3.2.1-alpha): + - Avoid unnecessary calls to directory_fetches_from_authorities() on + relays, to prevent spurious address resolutions and descriptor + rebuilds. This is a mitigation for bug 21789. Fixes bug 23470; + bugfix on in 0.2.8.1-alpha. + + o Minor bugfixes (compilation, backport from 0.3.2.1-alpha): + - Fix unused variable warnings in donna's Curve25519 SSE2 code. + Fixes bug 22895; bugfix on 0.2.7.2-alpha. + + o Minor bugfixes (logging, relay shutdown, annoyance, backport from 0.3.2.2-alpha): + - When a circuit is marked for close, do not attempt to package any + cells for channels on that circuit. Previously, we would detect + this condition lower in the call stack, when we noticed that the + circuit had no attached channel, and log an annoying message. + Fixes bug 8185; bugfix on 0.2.5.4-alpha. + + o Minor bugfixes (relay, crash, backport from 0.3.2.4-alpha): + - Avoid a crash when transitioning from client mode to bridge mode. + Previously, we would launch the worker threads whenever our + "public server" mode changed, but not when our "server" mode + changed. Fixes bug 23693; bugfix on 0.2.6.3-alpha. + + o Minor bugfixes (testing, backport from 0.3.1.6-rc): + - Fix an undersized buffer in test-memwipe.c. Fixes bug 23291; + bugfix on 0.2.7.2-alpha. Found and patched by Ties Stuij. + + +Changes in version 0.2.8.17 - 2017-12-01 + Tor 0.2.8.17 backports important security and stability bugfixes from + later Tor releases. All Tor users should upgrade to this release, or + to another of the releases coming out today. + + Note: the Tor 0.2.8 series will no longer be supported after 1 Jan + 2018. If you need a release with long-term support, please upgrade with + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Major bugfixes (security, backport from 0.3.2.6-alpha): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, onion service v2, backport from 0.3.2.6-alpha): + - Fix a use-after-free error that could crash v2 Tor onion services + when they failed to open circuits while expiring introduction + points. Fixes bug 24313; bugfix on 0.2.7.2-alpha. This issue is + also tracked as TROVE-2017-013 and CVE-2017-8823. + + o Major bugfixes (security, relay, backport from 0.3.2.6-alpha): + - When running as a relay, make sure that we never build a path through + ourselves, even in the case where we have somehow lost the version of + our descriptor appearing in the consensus. Fixes part of bug 21534; + bugfix on 0.2.0.1-alpha. This issue is also tracked as TROVE-2017-012 + and CVE-2017-8822. + + o Minor features (bridge, backport from 0.3.1.9): + - Bridges now include notice in their descriptors that they are + bridges, and notice of their distribution status, based on their + publication settings. Implements ticket 18329. For more fine- + grained control of how a bridge is distributed, upgrade to 0.3.2.x + or later. + + o Minor features (directory authority, backport from 0.3.2.6-alpha): + - Add an IPv6 address for the "bastet" directory authority. Closes + ticket 24394. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (testing, backport from 0.3.1.6-rc): + - Fix an undersized buffer in test-memwipe.c. Fixes bug 23291; + bugfix on 0.2.7.2-alpha. Found and patched by Ties Stuij. + + +Changes in version 0.2.5.16 - 2017-12-01 + Tor 0.2.5.13 backports important security and stability bugfixes from + later Tor releases. All Tor users should upgrade to this release, or + to another of the releases coming out today. + + Note: the Tor 0.2.5 series will no longer be supported after 1 May + 2018. If you need a release with long-term support, please upgrade to + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Major bugfixes (security, backport from 0.3.2.6-alpha): + - Fix a denial of service bug where an attacker could use a + malformed directory object to cause a Tor instance to pause while + OpenSSL would try to read a passphrase from the terminal. (Tor + instances run without a terminal, which is the case for most Tor + packages, are not impacted.) Fixes bug 24246; bugfix on every + version of Tor. Also tracked as TROVE-2017-011 and CVE-2017-8821. + Found by OSS-Fuzz as testcase 6360145429790720. + - When checking for replays in the INTRODUCE1 cell data for a + (legacy) onion service, correctly detect replays in the RSA- + encrypted part of the cell. We were previously checking for + replays on the entire cell, but those can be circumvented due to + the malleability of Tor's legacy hybrid encryption. This fix helps + prevent a traffic confirmation attack. Fixes bug 24244; bugfix on + 0.2.4.1-alpha. This issue is also tracked as TROVE-2017-009 + and CVE-2017-8819. + + o Major bugfixes (security, relay, backport from 0.3.2.6-alpha): + - When running as a relay, make sure that we never build a path + through ourselves, even in the case where we have somehow lost the + version of our descriptor appearing in the consensus. Fixes part + of bug 21534; bugfix on 0.2.0.1-alpha. This issue is also tracked + as TROVE-2017-012 and CVE-2017-8822. + + o Minor features (bridge, backport from 0.3.1.9): + - Bridges now include notice in their descriptors that they are + bridges, and notice of their distribution status, based on their + publication settings. Implements ticket 18329. For more fine- + grained control of how a bridge is distributed, upgrade to 0.3.2.x + or later. + + o Minor features (geoip): + - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 + Country database. + + +Changes in version 0.2.5.15 - 2017-10-25 + Tor 0.2.5.15 backports a collection of bugfixes from later Tor release + series. It also adds a new directory authority, Bastet. + + Note: the Tor 0.2.5 series will no longer be supported after 1 May + 2018. If you need a release with long-term support, please upgrade to + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Major bugfixes (openbsd, denial-of-service, backport from 0.3.1.5-alpha): + - Avoid an assertion failure bug affecting our implementation of + inet_pton(AF_INET6) on certain OpenBSD systems whose strtol() + handling of "0xx" differs from what we had expected. Fixes bug + 22789; bugfix on 0.2.3.8-alpha. Also tracked as TROVE-2017-007. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (defensive programming, undefined behavior, backport from 0.3.1.4-alpha): + - Fix a memset() off the end of an array when packing cells. This + bug should be harmless in practice, since the corrupted bytes are + still in the same structure, and are always padding bytes, + ignored, or immediately overwritten, depending on compiler + behavior. Nevertheless, because the memset()'s purpose is to make + sure that any other cell-handling bugs can't expose bytes to the + network, we need to fix it. Fixes bug 22737; bugfix on + 0.2.4.11-alpha. Fixes CID 1401591. + + o Build features (backport from 0.3.1.5-alpha): + - Tor's repository now includes a Travis Continuous Integration (CI) + configuration file (.travis.yml). This is meant to help new + developers and contributors who fork Tor to a Github repository be + better able to test their changes, and understand what we expect + to pass. To use this new build feature, you must fork Tor to your + Github account, then go into the "Integrations" menu in the + repository settings for your fork and enable Travis, then push + your changes. Closes ticket 22636. + + +Changes in version 0.2.8.16 - 2017-10-25 + Tor 0.2.8.16 backports a collection of bugfixes from later Tor release + series, including a bugfix for a crash issue that had affected relays + under memory pressure. It also adds a new directory authority, Bastet. + + Note: the Tor 0.2.8 series will no longer be supported after 1 Jan + 2018. If you need a release with long-term support, please stick with + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Major bugfixes (relay, crash, assertion failure, backport from 0.3.2.2-alpha): + - Fix a timing-based assertion failure that could occur when the + circuit out-of-memory handler freed a connection's output buffer. + Fixes bug 23690; bugfix on 0.2.6.1-alpha. + + o Minor features (directory authorities, backport from 0.3.2.2-alpha): + - Remove longclaw's IPv6 address, as it will soon change. Authority + IPv6 addresses were originally added in 0.2.8.1-alpha. This leaves + 3/8 directory authorities with IPv6 addresses, but there are also + 52 fallback directory mirrors with IPv6 addresses. Resolves 19760. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + +Changes in version 0.2.9.13 - 2017-10-25 + Tor 0.2.9.13 backports a collection of bugfixes from later Tor release + series, including a bugfix for a crash issue that had affected relays + under memory pressure. It also adds a new directory authority, Bastet. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Major bugfixes (relay, crash, assertion failure, backport from 0.3.2.2-alpha): + - Fix a timing-based assertion failure that could occur when the + circuit out-of-memory handler freed a connection's output buffer. + Fixes bug 23690; bugfix on 0.2.6.1-alpha. + + o Minor features (directory authorities, backport from 0.3.2.2-alpha): + - Remove longclaw's IPv6 address, as it will soon change. Authority + IPv6 addresses were originally added in 0.2.8.1-alpha. This leaves + 3/8 directory authorities with IPv6 addresses, but there are also + 52 fallback directory mirrors with IPv6 addresses. Resolves 19760. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (directory authority, backport from 0.3.1.5-alpha): + - When a directory authority rejects a descriptor or extrainfo with + a given digest, mark that digest as undownloadable, so that we do + not attempt to download it again over and over. We previously + tried to avoid downloading such descriptors by other means, but we + didn't notice if we accidentally downloaded one anyway. This + behavior became problematic in 0.2.7.2-alpha, when authorities + began pinning Ed25519 keys. Fixes bug 22349; bugfix + on 0.2.1.19-alpha. + + o Minor bugfixes (memory safety, backport from 0.3.2.3-alpha): + - Clear the address when node_get_prim_orport() returns early. + Fixes bug 23874; bugfix on 0.2.8.2-alpha. + + o Minor bugfixes (Windows service, backport from 0.3.1.6-rc): + - When running as a Windows service, set the ID of the main thread + correctly. Failure to do so made us fail to send log messages to + the controller in 0.2.1.16-rc, slowed down controller event + delivery in 0.2.7.3-rc and later, and crash with an assertion + failure in 0.3.1.1-alpha. Fixes bug 23081; bugfix on 0.2.1.6-alpha. + Patch and diagnosis from "Vort". + + +Changes in version 0.3.0.12 - 2017-10-25 + Tor 0.3.0.12 backports a collection of bugfixes from later Tor release + series, including a bugfix for a crash issue that had affected relays + under memory pressure. It also adds a new directory authority, Bastet. + + Note: the Tor 0.3.0 series will no longer be supported after 26 Jan + 2018. If you need a release with long-term support, please stick with + the 0.2.9 series. Otherwise, please upgrade to 0.3.1 or later. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Major bugfixes (relay, crash, assertion failure, backport from 0.3.2.2-alpha): + - Fix a timing-based assertion failure that could occur when the + circuit out-of-memory handler freed a connection's output buffer. + Fixes bug 23690; bugfix on 0.2.6.1-alpha. + + o Minor features (directory authorities, backport from 0.3.2.2-alpha): + - Remove longclaw's IPv6 address, as it will soon change. Authority + IPv6 addresses were originally added in 0.2.8.1-alpha. This leaves + 3/8 directory authorities with IPv6 addresses, but there are also + 52 fallback directory mirrors with IPv6 addresses. Resolves 19760. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (directory authority, backport from 0.3.1.5-alpha): + - When a directory authority rejects a descriptor or extrainfo with + a given digest, mark that digest as undownloadable, so that we do + not attempt to download it again over and over. We previously + tried to avoid downloading such descriptors by other means, but we + didn't notice if we accidentally downloaded one anyway. This + behavior became problematic in 0.2.7.2-alpha, when authorities + began pinning Ed25519 keys. Fixes bug 22349; bugfix + on 0.2.1.19-alpha. + + o Minor bugfixes (hidden service, relay, backport from 0.3.2.2-alpha): + - Avoid a possible double close of a circuit by the intro point on + error of sending the INTRO_ESTABLISHED cell. Fixes bug 23610; + bugfix on 0.3.0.1-alpha. + + o Minor bugfixes (memory safety, backport from 0.3.2.3-alpha): + - Clear the address when node_get_prim_orport() returns early. + Fixes bug 23874; bugfix on 0.2.8.2-alpha. + + o Minor bugfixes (Windows service, backport from 0.3.1.6-rc): + - When running as a Windows service, set the ID of the main thread + correctly. Failure to do so made us fail to send log messages to + the controller in 0.2.1.16-rc, slowed down controller event + delivery in 0.2.7.3-rc and later, and crash with an assertion + failure in 0.3.1.1-alpha. Fixes bug 23081; bugfix on 0.2.1.6-alpha. + Patch and diagnosis from "Vort". + + +Changes in version 0.3.1.8 - 2017-10-25 + Tor 0.3.1.8 is the second stable release in the 0.3.1 series. + It includes several bugfixes, including a bugfix for a crash issue + that had affected relays under memory pressure. It also adds + a new directory authority, Bastet. + + o Directory authority changes: + - Add "Bastet" as a ninth directory authority to the default list. + Closes ticket 23910. + - The directory authority "Longclaw" has changed its IP address. + Closes ticket 23592. + + o Major bugfixes (relay, crash, assertion failure, backport from 0.3.2.2-alpha): + - Fix a timing-based assertion failure that could occur when the + circuit out-of-memory handler freed a connection's output buffer. + Fixes bug 23690; bugfix on 0.2.6.1-alpha. + + o Minor features (directory authorities, backport from 0.3.2.2-alpha): + - Remove longclaw's IPv6 address, as it will soon change. Authority + IPv6 addresses were originally added in 0.2.8.1-alpha. This leaves + 3/8 directory authorities with IPv6 addresses, but there are also + 52 fallback directory mirrors with IPv6 addresses. Resolves 19760. + + o Minor features (geoip): + - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 + Country database. + + o Minor bugfixes (compilation, backport from 0.3.2.2-alpha): + - Fix a compilation warning when building with zstd support on + 32-bit platforms. Fixes bug 23568; bugfix on 0.3.1.1-alpha. Found + and fixed by Andreas Stieger. + + o Minor bugfixes (compression, backport from 0.3.2.2-alpha): + - Handle a pathological case when decompressing Zstandard data when + the output buffer size is zero. Fixes bug 23551; bugfix + on 0.3.1.1-alpha. + + o Minor bugfixes (directory authority, backport from 0.3.2.1-alpha): + - Remove the length limit on HTTP status lines that authorities can + send in their replies. Fixes bug 23499; bugfix on 0.3.1.6-rc. + + o Minor bugfixes (hidden service, relay, backport from 0.3.2.2-alpha): + - Avoid a possible double close of a circuit by the intro point on + error of sending the INTRO_ESTABLISHED cell. Fixes bug 23610; + bugfix on 0.3.0.1-alpha. + + o Minor bugfixes (memory safety, backport from 0.3.2.3-alpha): + - Clear the address when node_get_prim_orport() returns early. + Fixes bug 23874; bugfix on 0.2.8.2-alpha. + + o Minor bugfixes (unit tests, backport from 0.3.2.2-alpha): + - Fix additional channelpadding unit test failures by using mocked + time instead of actual time for all tests. Fixes bug 23608; bugfix + on 0.3.1.1-alpha. + + Changes in version 0.2.8.15 - 2017-09-18 Tor 0.2.8.15 backports a collection of bugfixes from later Tor series. @@ -1345,7 +1983,7 @@ Changes in version 0.3.0.6 - 2017-04-26 initial code by Alec Heifetz. - Relays now support the HSDir version 3 protocol, so that they can can store and serve v3 descriptors. This is part of the next- - generation onion service work detailled in proposal 224. Closes + generation onion service work detailed in proposal 224. Closes ticket 17238. o Major features (protocol, ed25519 identity keys): diff --git a/changes/bastet_v6 b/changes/bastet_v6 deleted file mode 100644 index ee4e2c8094..0000000000 --- a/changes/bastet_v6 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (directory authority): - - Add an IPv6 address for the "bastet" directory authority. - Closes ticket 24394. - diff --git a/changes/bug12062 b/changes/bug12062 new file mode 100644 index 0000000000..8972929d84 --- /dev/null +++ b/changes/bug12062 @@ -0,0 +1,16 @@ + o Minor bugfixes (hibernation, bandwidth accounting, shutdown): + + - When hibernating, do not attempt to launch DNS checks. Fixes a + case of bug 12062; bugfix on 0.1.2.2-alpha. + + - Resolve several bugs related to descriptor fetching on bridge + clients with bandwidth accounting enabled. (This combination is + not recommended!) Fixes a case of bug 12062; bugfix on + 0.2.0.3-alpha. + + - When hibernating, do not try to upload or download + descriptors. Fixes a case of bug 12062; bugfix on 0.0.9pre5. + + - Do not attempt to launch self-reachability tests when entering + hibernation. Fixes a base of bug 12062; bugfix on 0.0.9pre5. + diff --git a/changes/bug18859 b/changes/bug18859 deleted file mode 100644 index 1fe5bc2107..0000000000 --- a/changes/bug18859 +++ /dev/null @@ -1,7 +0,0 @@ - o Major bugfixes (circuit prediction): - - Fix circuit prediction logic so that a client doesn't treat a stream as - being "handled" by a circuit if that circuit already has isolation - settings on it that might make it incompatible with the stream. This - change should make Tor clients more responsive by improving their - chances of having a pre-created circuit ready for use when a new client - request arrives. Fixes bug 18859; bugfix on 0.2.3.3-alpha. diff --git a/changes/bug20532 b/changes/bug20532 deleted file mode 100644 index 7c190ea032..0000000000 --- a/changes/bug20532 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (bridges): - - Overwrite the bridge address earlier in the process of directly - retrieving its descriptor, to make sure we reach it on the configured - address. Fixes bug 20532; bugfix on 0.2.0.10-alpha. diff --git a/changes/bug20963 b/changes/bug20963 deleted file mode 100644 index a65c58399c..0000000000 --- a/changes/bug20963 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (logging): - - Improve the message we log when re-enabling circuit build timeouts - after having received a consensus. Closes ticket 20963. - diff --git a/changes/bug21394 b/changes/bug21394 deleted file mode 100644 index e5452e20ba..0000000000 --- a/changes/bug21394 +++ /dev/null @@ -1,9 +0,0 @@ - o Major bugfixes (Exit nodes): - - Fix an issue causing high-bandwidth exit nodes to fail a majority - or all of their DNS requests, making them basically unsuitable for - regular usage in Tor circuits. The problem is related to - libevent's DNS handling, but we can work around it in Tor. Fixes - bugs 21394 and 18580; bugfix on 0.1.2.2-alpha which introduced - eventdns. Credit goes to Dhalgren for identifying and finding a - workaround to this bug and to gamambel, arthuredelstein and - arma in helping to track it down and analyze it. diff --git a/changes/bug21509 b/changes/bug21509 deleted file mode 100644 index 593a01ef20..0000000000 --- a/changes/bug21509 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (hidden service v3, fuzzing): - - Fix the hidden service v3 descriptor decoding fuzzing to use the latest - decoding API correctly. Fixes bug 21509; bugfix on 0.3.2.1-alpha. diff --git a/changes/bug22605 b/changes/bug22605 new file mode 100644 index 0000000000..fdfe202021 --- /dev/null +++ b/changes/bug22605 @@ -0,0 +1,5 @@ + o Minor bugfixes (linux seccomp2 sandbox): + - When running with the sandbox enabled, reload configuration files + correctly even when %include was used. Previously we + would crash. Fixes bug 22605; bugfix on 0.3.1. + Patch from Daniel Pinto. diff --git a/changes/bug22907 b/changes/bug22907 new file mode 100644 index 0000000000..ff6ab8375b --- /dev/null +++ b/changes/bug22907 @@ -0,0 +1,3 @@ + o Documentation: + - Add documentation on how to build tor with Rust dependencies without + requiring being online. Closes ticket 22907; bugfix on tor-0.3.0.3-alpha. diff --git a/changes/bug23100 b/changes/bug23100 new file mode 100644 index 0000000000..22e2485d64 --- /dev/null +++ b/changes/bug23100 @@ -0,0 +1,7 @@ + o Minor bugfixes (Performance): + - Use hidden service circuits (and other circuits longer than 3 hops) + to calculate a circuit build timeout. Previously, Tor only calculated + its build timeout based on circuits that planned to be exactly 3 hops + long. With this change, we include measurements from all circuits at + the point where they complete their third hop. Fixes bug 23100; + bugfix on 0.2.2.2-alpha. diff --git a/changes/bug23114 b/changes/bug23114 new file mode 100644 index 0000000000..b67f75a5aa --- /dev/null +++ b/changes/bug23114 @@ -0,0 +1,6 @@ + o Minor bugfixes (Performance): + - Consider circuits for timeout as soon as they complete a hop. This + is more accurate than applying the timeout in circuit_expire_building() + because that function is only called once per second, which is now + too slow for typical timeouts on the current network. Fixes bug 23114; + bugfix on 0.2.2.2-alpha. diff --git a/changes/bug23318 b/changes/bug23318 deleted file mode 100644 index 7fcb8d4487..0000000000 --- a/changes/bug23318 +++ /dev/null @@ -1,11 +0,0 @@ - o Minor bugfixes (path selection): - - When selecting relays by bandwidth, avoid a rounding error that - could sometimes cause load to be imbalanced incorrectly. Previously, - we would always round upwards; now, we round towards the nearest - integer. This had the biggest effect when a relay's weight adjustments - should have given it weight 0, but it got weight 1 instead. - Fixes bug 23318; bugfix on 0.2.4.3-alpha. - - When calculating the fraction of nodes that have descriptors, and all - all nodes in the network have zero bandwidths, count the number of nodes - instead. - Fixes bug 23318; bugfix on 0.2.4.10-alpha. diff --git a/changes/bug23571 b/changes/bug23571 new file mode 100644 index 0000000000..f2efbdfb96 --- /dev/null +++ b/changes/bug23571 @@ -0,0 +1,3 @@ + o Minor bugfixes (hibernation): + - When hibernating, close connections normally and allow them to flush. + Fixes bug 23571; bugfix on 0.2.4.7-alpha. Also fixes bug 7267. diff --git a/changes/bug23603 b/changes/bug23603 deleted file mode 100644 index dfb2052c9a..0000000000 --- a/changes/bug23603 +++ /dev/null @@ -1,7 +0,0 @@ - o Minor bugfixes (hidden service v3): - - Fix a race between the circuit close and free where the service would - launch a new intro circuit after the close, and then fail to register it - before the free of the previously closed circuit. This was making the - service unable to find the established intro circuit and thus not upload - its descriptor. It can make a service unavailable for up to 24 hours. - Fixes bug 23603; bugfix on 0.3.2.1-alpha. diff --git a/changes/bug23623 b/changes/bug23623 deleted file mode 100644 index 1e2e5c2ac0..0000000000 --- a/changes/bug23623 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (onion services): - - Cache some needed onion service client information instead of - continuously computing it over and over again. Fixes bug 23623; bugfix - on 0.3.2.1-alpha. diff --git a/changes/bug23653 b/changes/bug23653 deleted file mode 100644 index 81760cbb82..0000000000 --- a/changes/bug23653 +++ /dev/null @@ -1,7 +0,0 @@ - o Minor bugfixes (hidden service client): - - When getting multiple SOCKS request for the same .onion address, don't - trigger multiple descriptor fetches. - - When the descriptor fetch fails with an internal error, no more HSDir to - query or we aren't allowed to fetch (FetchHidServDescriptors 0), close - all pending SOCKS request for that .onion. Fixes bug 23653; bugfix on - 0.3.2.1-alpha. diff --git a/changes/bug23662 b/changes/bug23662 deleted file mode 100644 index 1000bde3d6..0000000000 --- a/changes/bug23662 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (onion services): - - Silence a warning about failed v3 onion descriptor uploads since it can - happen naturally under certain edge-cases. Fixes part of bug 23662; - bugfix on 0.3.2.1-alpha. diff --git a/changes/bug23670 b/changes/bug23670 deleted file mode 100644 index 039bc39478..0000000000 --- a/changes/bug23670 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor features (entry guards): - - Improve logs issued when we are missing descriptors of primary guards. - Resolves ticket 23670. diff --git a/changes/bug23678 b/changes/bug23678 deleted file mode 100644 index 8138ea71ea..0000000000 --- a/changes/bug23678 +++ /dev/null @@ -1,7 +0,0 @@ - o Minor bugfixes (warnings): - - When we get an HTTP request on a SOCKS port, tell the user about - the new HTTPTunnelPort option. Previously, we would give a - "Tor is not an HTTP Proxy" message, which stopped being true when - HTTPTunnelPort was introduced. Fixes bug 23678; bugfix on - 0.3.2.1-alpha. - diff --git a/changes/bug23681 b/changes/bug23681 deleted file mode 100644 index e317f36d50..0000000000 --- a/changes/bug23681 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (hidden service client): - - The introduction circuit was being timed out too quickly while waiting - for the rendezvous circuit to complete. Keep the intro circuit around - longer instead of timing out and reopening new ones constantly. Fixes - bug 23681; bugfix on 0.2.4.8-alpha. diff --git a/changes/bug23693 b/changes/bug23693 deleted file mode 100644 index 796398be51..0000000000 --- a/changes/bug23693 +++ /dev/null @@ -1,6 +0,0 @@ - o Minor bugfixes (relay, crash): - - Avoid a crash when transitioning from client mode to bridge mode. - Previously, we would launch the worker threads whenever our "public - server" mode changed, but not when our "server" mode changed. - Fixes bug 23693; bugfix on 0.2.6.3-alpha. - diff --git a/changes/bug23696 b/changes/bug23696 deleted file mode 100644 index c5d18583d4..0000000000 --- a/changes/bug23696 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfix (KIST scheduler): - - Downgrade a warning to log info when the monotonic time diff is - negative. This can happen on platform not supporting monotonic time. The - scheduler recovers from this without any problem. Fixes bug 23696; - bugfix on 0.3.2.1-alpha. diff --git a/changes/bug23739 b/changes/bug23739 deleted file mode 100644 index 3207b5eaf3..0000000000 --- a/changes/bug23739 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (documentation): - - Document better how to read gcov and what our postprocessing scripts do. - Fixes bug 23739; bugfix on 0.2.9.1-alpha. diff --git a/changes/bug23741 b/changes/bug23741 deleted file mode 100644 index 92f06f5270..0000000000 --- a/changes/bug23741 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (testing): - - Prevent scripts/test/coverage from attempting to move gcov - output to the root directory. Fixes bug 23741; bugfix on - 0.2.5.1-alpha. diff --git a/changes/bug23748 b/changes/bug23748 deleted file mode 100644 index 0bd3f3f8ff..0000000000 --- a/changes/bug23748 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (hidden service): - - Always make sure the hidden service generate the public key file if it - is missing. Prior to this, if the public key was deleted from disk, it - wouldn't get recreated. Fixes bug 23748; bugfix on 0.3.2.2-alpha. - Patch from "cathugger". diff --git a/changes/bug23751 b/changes/bug23751 deleted file mode 100644 index 2fd7021664..0000000000 --- a/changes/bug23751 +++ /dev/null @@ -1,6 +0,0 @@ - o Minor bugfixes (scheduler, channel): - - Ignore channels that have been closed while flushing cells. This can - happen if the write on the connection fails leading to the channel being - closed while in the scheduler loop. This is not a complete fix, it is a - bandaid until we are able to refactor those interactions. Fixes bug - 23751; bugfix on 0.3.2.1-alpha. diff --git a/changes/bug23753 b/changes/bug23753 deleted file mode 100644 index 8782a8e2d0..0000000000 --- a/changes/bug23753 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (logging, scheduler): - - Introduce a SCHED_BUG() function to log extra information about the - scheduler state if we ever catch a bug in the scheduler. Closes ticket - 23753. diff --git a/changes/bug23755 b/changes/bug23755 deleted file mode 100644 index 98f0970344..0000000000 --- a/changes/bug23755 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (testing): - - Stop unconditionally mirroring the tor repository in GitLab CI. - This prevented developers from enabling GitLab CI on master. - Fixes bug 23755; bugfix on 0.3.2.2-alpha. diff --git a/changes/bug23757 b/changes/bug23757 deleted file mode 100644 index 02507a0b4d..0000000000 --- a/changes/bug23757 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (testing): - - Adjust the GitLab CI configuration to more closely match that of Travis - CI. Fixes bug 23757; bugfix on 0.3.2.2-alpha. - diff --git a/changes/bug23758 b/changes/bug23758 deleted file mode 100644 index 565791e8f4..0000000000 --- a/changes/bug23758 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (testing): - - Skip a test that would fail if run as root (because it expects a - permissions error). This affects some continuous integration setups. - Fixes bug 23758; bugfix on 0.3.2.2-alpha. diff --git a/changes/bug23762 b/changes/bug23762 deleted file mode 100644 index 741a88e21f..0000000000 --- a/changes/bug23762 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (hidden service v3): - - Properly retry HSv3 descriptor fetches in the case where we were initially - missing required directory information. Fixes bug 23762; bugfix on - 0.3.2.1-alpha. diff --git a/changes/bug23774 b/changes/bug23774 deleted file mode 100644 index 2ea5c0122a..0000000000 --- a/changes/bug23774 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (memory leak): - - Fix a minor memory-leak-at-exit in the KIST scheduler. This - bug should have no user-visible impact. Fixes bug 23774; - bugfix on 0.3.2.1-alpha. diff --git a/changes/bug23783 b/changes/bug23783 deleted file mode 100644 index 98c583a12b..0000000000 --- a/changes/bug23783 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (compilation, windows): - - When detecting OpenSSL on Windows from our configure script, make sure - to try linking with the ws2_32 library. Fixes bug 23783; bugfix on - 0.3.2.2-alpha. - diff --git a/changes/bug23790 b/changes/bug23790 deleted file mode 100644 index 5ebe77f806..0000000000 --- a/changes/bug23790 +++ /dev/null @@ -1,6 +0,0 @@ - o Minor bugfixes (hidden service v2): - - When reloading tor (HUP) configured with hidden service(s), some - information weren't copy to the new service object. One problem with - this was that tor would wait at least the RendPostPeriod time before - uploading the descriptor if the reload happened before the descriptor - needed to be published. Fixes bug 23790; bugfix on 0.2.1.9-alpha. diff --git a/changes/bug23816 b/changes/bug23816 deleted file mode 100644 index 6139dec9e8..0000000000 --- a/changes/bug23816 +++ /dev/null @@ -1,6 +0,0 @@ - o Minor bugfixes (directory client): - - On failure to download directory information, delay retry attempts - by a random amount based on the "decorrelated jitter" algorithm. - Our previous delay algorithm tended to produce extra-long delays too - easily. Fixes bug 23816; bugfix on 0.2.9.1-alpha. - diff --git a/changes/bug23817 b/changes/bug23817 deleted file mode 100644 index 4740942799..0000000000 --- a/changes/bug23817 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (descriptors): - - Don't try fetching microdescriptors from relays that have failed to - deliver them in the past. Fixes bug 23817; bugfix on 0.3.0.1-alpha. diff --git a/changes/bug23820 b/changes/bug23820 deleted file mode 100644 index 4e920d0498..0000000000 --- a/changes/bug23820 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (IPv6, v3 single onion services): - - Remove buggy code for IPv6-only v3 single onion services, and reject - attempts to configure them. This release supports IPv4, dual-stack, and - IPv6-only v3 hidden services; and IPv4 and dual-stack v3 single onion - services. Fixes bug 23820; bugfix on 0.3.2.1-alpha. diff --git a/changes/bug23826-23828 b/changes/bug23826-23828 new file mode 100644 index 0000000000..2b991e5c1b --- /dev/null +++ b/changes/bug23826-23828 @@ -0,0 +1,14 @@ + o Major features (IPv6, directory documents): + - Add consensus method 27, which adds IPv6 ORPorts to the microdesc + consensus. This makes it easier for IPv6 clients to bootstrap and + choose reachable entry guards. + Implements 23826. + - Add consensus method 28, which removes IPv6 ORPorts from + microdescriptors. Now that there are IPv6 ORPorts in the microdesc + consensus, they are redundant in microdescs. This change is compatible + with tor clients on 0.2.8.x and later. (0.2.8.x introduced client IPv6 + bootstrap and guard support.) + Implements 23828. + - Expand the documentation for AuthDirHasIPv6Connectivity when it is set + by different numbers of authorities. + Fixes 23870 on 0.2.4.1-alpha. diff --git a/changes/bug23827 b/changes/bug23827 new file mode 100644 index 0000000000..75279abb85 --- /dev/null +++ b/changes/bug23827 @@ -0,0 +1,8 @@ + o Minor feature (IPv6): + - When a consensus has IPv6 ORPorts, make IPv6-only clients use them, + rather than waiting to download microdescriptors. + Implements 23827. + - Make IPv6-only clients wait for microdescs for relays, even if we were + previously using descriptors (or were using them as a bridge) and have + a cached descriptor for them. + Implements 23827. diff --git a/changes/bug23848 b/changes/bug23848 new file mode 100644 index 0000000000..e2aec687ca --- /dev/null +++ b/changes/bug23848 @@ -0,0 +1,8 @@ + o Minor features (embedding): + - On most errors that would cause Tor to exit, it now tries to return + from the tor_main() function, rather than calling the system exit() + function. Most users won't notice a difference here, but it should + make a significant difference on platforms that try to run Tor inside + a separate thread: they should now be able to survive Tor's exit + conditions rather than having Tor shut down the entire process. + Closes ticket 23848. diff --git a/changes/bug23861 b/changes/bug23861 deleted file mode 100644 index c6f017640d..0000000000 --- a/changes/bug23861 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (logging, relay): - - Suppress a log notice when relay descriptors arrive. We already have a - bootstrap progress for this so no need to log notice everytime tor - receives relay descriptors. Microdescriptors behave the same. Fixes bug - 23861; bugfix on 0.2.8.2-alpha. diff --git a/changes/bug23862 b/changes/bug23862 deleted file mode 100644 index 301ce73672..0000000000 --- a/changes/bug23862 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (entry guards): - - Tor now updates its guard state when it reads a consensus regardless of - whether it's missing descriptors. That makes tor use its primary guards - to fetch descriptors in some edge cases where it would have used fallback - directories in the past. Fixes bug 23862; bugfix on 0.3.0.1-alpha.
\ No newline at end of file diff --git a/changes/bug23874 b/changes/bug23874 deleted file mode 100644 index bf6620553d..0000000000 --- a/changes/bug23874 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (memory safety): - - Clear the address when node_get_prim_orport() returns early. - Fixes bug 23874; bugfix on 0.2.8.2-alpha. diff --git a/changes/bug23952 b/changes/bug23952 deleted file mode 100644 index ab1462e522..0000000000 --- a/changes/bug23952 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (relay): - - Avoid a BUG warning when receiving a dubious CREATE cell while - an option transition is in progress. Fixes bug 23952; bugfix on - 0.3.2.1-alpha. diff --git a/changes/bug23953 b/changes/bug23953 new file mode 100644 index 0000000000..10d41a00d2 --- /dev/null +++ b/changes/bug23953 @@ -0,0 +1,3 @@ + o Minor features (performance): + - Use stdatomic.h where available, rather than mutexes, to implement + atomic_counter_t. Closes ticket 23953. diff --git a/changes/bug23985 b/changes/bug23985 deleted file mode 100644 index 9cb5937962..0000000000 --- a/changes/bug23985 +++ /dev/null @@ -1,9 +0,0 @@ - o Minor bugfixes (bootstrapping): - - Fetch descriptors aggressively whenever we lack enough - to build circuits, regardless of how many descriptors we are missing. - Previously, we would delay launching the fetch when we had fewer than - 15 missing descriptors, even if some of those descriptors were - blocking circuits from building. Fixes bug 23985; bugfix on - 0.1.1.11-alpha. The effects of this bug became worse in 0.3.0.3-alpha, - when we began treating missing descriptors from our primary guards - as a reason to delay circuits. diff --git a/changes/bug24002 b/changes/bug24002 deleted file mode 100644 index cdb6081110..0000000000 --- a/changes/bug24002 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (hidden service): - - Make sure that we have a usable ed25519 key when the intro point relay - does support ed25519 link authentication. We do check for an empty key - when the relay does not support it so this makes it nice and symmetric. - Fixes bug 24002; bugfix on 0.3.2.1-alpha. diff --git a/changes/bug24025 b/changes/bug24025 deleted file mode 100644 index 1d7841af53..0000000000 --- a/changes/bug24025 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (logging, relay): - - Downgrade a warning to a protocol warning in the case the ed25519 key is - not consistent between the descriptor and micro descriptor of a relay. - This can happen for instance if the relay has been flagged - NoEdConsensus. Fixes bug 24025; bugfix on 0.3.2.1-alpha. diff --git a/changes/bug24050 b/changes/bug24050 deleted file mode 100644 index d184a77ac0..0000000000 --- a/changes/bug24050 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (client): - - By default, do not enable storage of client-side DNS values. - These values were unused by default previously, but they should - not have been cached at all. Fixes bug 24050; bugfix on - 0.2.6.3-alpha. diff --git a/changes/bug24082 b/changes/bug24082 deleted file mode 100644 index 1523239351..0000000000 --- a/changes/bug24082 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (testing): - - Fix a spurious fuzzing-only use of an uninitialized value. - Found by Brian Carpenter. Fixes bug 24082; bugfix on 0.3.0.3-alpha. diff --git a/changes/bug24086 b/changes/bug24086 deleted file mode 100644 index 2ae0b37e65..0000000000 --- a/changes/bug24086 +++ /dev/null @@ -1,7 +0,0 @@ - o Minor bugfixes (directory cache): - - When a consensus diff calculation is only partially successful, only - record the successful parts as having succeeded. Partial success - can happen if (for example) one compression method fails but - the others succeed. Previously we misrecorded all the calculations as - having succeeded, which would later cause a nonfatal assertion failure. - Fixes bug 24086; bugfix on 0.3.1.1-alpha. diff --git a/changes/bug24099 b/changes/bug24099 deleted file mode 100644 index dca3992664..0000000000 --- a/changes/bug24099 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (directory cache): - - Recover better from empty or corrupt files in the consensus cache - directory. Fixes bug 24099; bugfix on 0.3.1.1-alpha. - diff --git a/changes/bug24115 b/changes/bug24115 deleted file mode 100644 index 767f13840b..0000000000 --- a/changes/bug24115 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (manpage, hidden service): - - Mention that the HiddenServiceNumIntroductionPoints option is 0-10 for - v2 service and 0-20 for v3 service. Fixes bug 24115; bugfix on - 0.3.2.1-alpha. diff --git a/changes/bug24119 b/changes/bug24119 new file mode 100644 index 0000000000..5014257602 --- /dev/null +++ b/changes/bug24119 @@ -0,0 +1,4 @@ + o Code simplification and refactoring: + - Rewrite channel_rsa_id_group_set_badness to reduce temporary memory + allocations with large numbers of OR connections (e.g. relays). Closes + ticket 24119. diff --git a/changes/bug24137 b/changes/bug24137 new file mode 100644 index 0000000000..588e68d199 --- /dev/null +++ b/changes/bug24137 @@ -0,0 +1,3 @@ + o Minor bugfixes (Private Networks): + - Give out Exit flags in bootstrapping networks. Fixes bug 24137; + bugfix on 0.2.3.1-alpha. diff --git a/changes/bug24150 b/changes/bug24150 deleted file mode 100644 index cfda7c40da..0000000000 --- a/changes/bug24150 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (v3 onion services): - - Fix a memory leak when decrypting a badly formatted v3 onion - service descriptor. Fixes bug 24150; bugfix on 0.3.2.1-alpha. - Found by OSS-Fuzz; this is OSS-Fuzz issue 3994. diff --git a/changes/bug24167 b/changes/bug24167 deleted file mode 100644 index fd0d87efff..0000000000 --- a/changes/bug24167 +++ /dev/null @@ -1,7 +0,0 @@ - o Minor bugfixes (network layer): - - When closing a connection via close_connection_immediately(), we - mark it as "not blocked on bandwidth", to prevent later calls - from trying to unblock it, and give it permission to read. This - fixes a backtrace warning that can happen on relays under various - circumstances. Fixes bug 24167; bugfix on 0.1.0.1-rc. - diff --git a/changes/bug24170 b/changes/bug24170 deleted file mode 100644 index d3d7347693..0000000000 --- a/changes/bug24170 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (path selection): - - Actually log the total bandwidth in compute_weighted_bandwidths(). - Fixes bug 24170; bugfix on 0.2.4.3-alpha. diff --git a/changes/bug24198 b/changes/bug24198 deleted file mode 100644 index 6790706872..0000000000 --- a/changes/bug24198 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (controller, linux seccomp2 sandbox): - - Avoid a crash when attempting to use the seccomp2 sandbox - together with the OwningControllerProcess feature. - Fixes bug 24198; bugfix on 0.2.5.1-alpha. diff --git a/changes/bug24230 b/changes/bug24230 deleted file mode 100644 index b08c4cde24..0000000000 --- a/changes/bug24230 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (control port, hidden service): - - Control port was reporting the action "UPLOAD_FAILED" instead of - "FAILED" for the HS_DESC event when a service was not able to upload a - descriptor. Fixes bug 24230; bugfix on 0.2.7.1-alpha. diff --git a/changes/bug24247 b/changes/bug24247 deleted file mode 100644 index 1f4ddcdde2..0000000000 --- a/changes/bug24247 +++ /dev/null @@ -1,6 +0,0 @@ - o Minor bugfixes (fuzzing): - - Fix a bug in our fuzzing mock replacement for crypto_pk_checksig(), to - correctly handle cases where a caller gives it an RSA key of under 160 - bits. (This is not actually a bug in Tor itself, but wrather in our - fuzzing code.) Fixes bug 24247; bugfix on 0.3.0.3-alpha. - Found by OSS-Fuzz as issue 4177. diff --git a/changes/bug24262 b/changes/bug24262 deleted file mode 100644 index eee69512e4..0000000000 --- a/changes/bug24262 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (hidden service): - - Fix the consensus parameter "hsdir-interval" to "hsdir_interval" so it - matches the dir-spec.txt. Fixes bug 24262; bugfix on 0.3.1.1-alpha. diff --git a/changes/bug24279 b/changes/bug24279 deleted file mode 100644 index ab2932b341..0000000000 --- a/changes/bug24279 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (compilation, hardening): - - Fix a memory leak warning in one of the libevent-related - configuration tests that could occur when manually specifying - -fsanitize=address. Fixes bug 24279; bugfix on 0.3.0.2-alpha. - Found and patched by Alex Xu. diff --git a/changes/bug24313 b/changes/bug24313 deleted file mode 100644 index b927ec3ba6..0000000000 --- a/changes/bug24313 +++ /dev/null @@ -1,5 +0,0 @@ - o Major bugfixes (security, hidden service v2): - - Fix a use-after-free error that could crash v2 Tor hidden services - when it failed to open circuits while expiring introductions - points. Fixes bug 24313; bugfix on 0.2.7.2-alpha. This - issue is also tracked as TROVE-2017-013 and CVE-2017-8823. diff --git a/changes/bug24318 b/changes/bug24318 new file mode 100644 index 0000000000..c92f7209f1 --- /dev/null +++ b/changes/bug24318 @@ -0,0 +1,3 @@ + o Documentation: + - Clarify the behavior of RelayBandwidth{Rate,Burst} with client traffic. + Closes ticket 24318. diff --git a/changes/bug24337 b/changes/bug24337 new file mode 100644 index 0000000000..82b430425d --- /dev/null +++ b/changes/bug24337 @@ -0,0 +1,8 @@ + o Minor features (defensive programming): + - Most of the functions in Tor that free objects have been replaced + with macros that free the objects and set the corresponding pointers + to NULL. This change should help prevent a large class of dangling + pointer bugs. Closes ticket 24337. + + - Where possible, the tor_free() macro now only evaluates its input once. + Part of ticket 24337. diff --git a/changes/bug24345 b/changes/bug24345 deleted file mode 100644 index 22eb412514..0000000000 --- a/changes/bug24345 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (tests): - - Fix a unit test in one of the bridge-distribution test cases. - Fixes bug 24345; bugfix on 0.3.2.3-alpha. diff --git a/changes/bug24362 b/changes/bug24362 new file mode 100644 index 0000000000..15e393cf3f --- /dev/null +++ b/changes/bug24362 @@ -0,0 +1,2 @@ + o Minor features (logging, android): + - Added support for the Android logging subsystem. Closes ticket 24362. diff --git a/changes/bug24367 b/changes/bug24367 deleted file mode 100644 index 09ef3bb877..0000000000 --- a/changes/bug24367 +++ /dev/null @@ -1,13 +0,0 @@ - o Minor bugfixes (bridge clients, bootstrap): - - Retry directory downloads when we get our first bridge descriptor - during bootstrap or while reconnecting to the network. Keep retrying - every time we get a bridge descriptor, until we have a reachable bridge. - Fixes bug 24367; bugfix on 0.2.0.3-alpha. - - Stop delaying bridge descriptor fetches when we have cached bridge - descriptors. Instead, only delay bridge descriptor fetches when we - have at least one reachable bridge. - Fixes bug 24367; bugfix on 0.2.0.3-alpha. - - Stop delaying directory fetches when we have cached bridge descriptors. - Instead, only delay bridge descriptor fetches when all our bridges are - definitely unreachable. - Fixes bug 24367; bugfix on 0.2.0.3-alpha. diff --git a/changes/bug24374 b/changes/bug24374 new file mode 100644 index 0000000000..d0b07f12da --- /dev/null +++ b/changes/bug24374 @@ -0,0 +1,4 @@ + o Minor features (performance, 32-bit): + - Improve performance on 32-bit systems by avoiding 64-bit division + when timestamping cells and buffer chunks for OOM calculations. + Implements ticket 24374. diff --git a/changes/bug24424 b/changes/bug24424 deleted file mode 100644 index 63c2d39ba1..0000000000 --- a/changes/bug24424 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor features (portability): - - Tor now compiles correctly on arm64 with libseccomp-dev installed. - (It doesn't yet work with the sandbox enabled.) Closes ticket 24424. diff --git a/changes/bug24480 b/changes/bug24480 deleted file mode 100644 index 94e5b91a0c..0000000000 --- a/changes/bug24480 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (compilation): - - Fix a signed/unsigned comparison warning introduced by our - fix to TROVE-2017-009. Fixes bug 24480; bugfix on 0.2.5.16. diff --git a/changes/bug24488 b/changes/bug24488 new file mode 100644 index 0000000000..b8094e29e6 --- /dev/null +++ b/changes/bug24488 @@ -0,0 +1,4 @@ + o Minor bugfixes (directory authorities, IPv6): + - When creating a routerstatus (vote) from a routerinfo (descriptor), + set the IPv6 address to the unspecified IPv6 address, and explicitly + initialise the port to zero. Fixes bug 24488; bugfix on 0.2.4.1-alpha. diff --git a/changes/bug24489 b/changes/bug24489 new file mode 100644 index 0000000000..6864ba6f57 --- /dev/null +++ b/changes/bug24489 @@ -0,0 +1,3 @@ + o Code simplification and refactoring (controller): + - Make most of the variables in networkstatus_getinfo_by_purpose() const. + Implements ticket 24489. diff --git a/changes/bug24502 b/changes/bug24502 deleted file mode 100644 index 3fa6fb58dd..0000000000 --- a/changes/bug24502 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (scheduler): - - Properly set the scheduler state of an unopened channel in the KIST - scheduler main loop. This prevents a harmless but annoying log warning. - Fixes bug 24502; bugfix on 0.3.2.4-alpha. diff --git a/changes/bug24590 b/changes/bug24590 deleted file mode 100644 index 77e039f8d2..0000000000 --- a/changes/bug24590 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes (scheduler, KIST): - - Avoid a possible integer overflow when computing the available space on - the TCP buffer of a channel. This has no security implications but can - make KIST not behave properly by allowing more cells on a already - saturated connection. Fixes bug 24590; bugfix on 0.3.2.1-alpha. diff --git a/changes/bug24605 b/changes/bug24605 new file mode 100644 index 0000000000..7ee292b27b --- /dev/null +++ b/changes/bug24605 @@ -0,0 +1,4 @@ + o Minor features (instrumentation): + - Add the MainloopStats option to Tor that allows developers to get + instrumentation information from the main event loop via the heartbeat + messages. Closes ticket 24605. diff --git a/changes/bug24612 b/changes/bug24612 new file mode 100644 index 0000000000..83db7f1df5 --- /dev/null +++ b/changes/bug24612 @@ -0,0 +1,4 @@ + o Minor bugfixes (build): + - Fix output of autoconf checks to display success messages for + Rust dependencies and a suitable rustc compiler version. Fixes + bug 24612; bugfix on 0.3.1.3-alpha. diff --git a/changes/bug24613 b/changes/bug24613 new file mode 100644 index 0000000000..d718f8f942 --- /dev/null +++ b/changes/bug24613 @@ -0,0 +1,4 @@ + o Minor features (performance, 32-bit): + - Improve performance on 32-bit systems by avoiding 64-bit division + to calculate the current timestamp in milliseconds for channel + padding computations. Implements ticket 24613. diff --git a/changes/bug24665 b/changes/bug24665 deleted file mode 100644 index f950d9dd01..0000000000 --- a/changes/bug24665 +++ /dev/null @@ -1,6 +0,0 @@ - o Major bugfixes (KIST, scheduler): - - The KIST scheduler did not correctly account for data already enqueued - in each connection's send socket buffer, particularly in cases when the - TCP/IP congestion window was reduced between scheduler calls. This - situation lead to excessive per-connection buffering in the kernel, and - a potential memory DoS. Fixes bug 24665; bugfix on 0.3.2.1-alpha. diff --git a/changes/bug24666 b/changes/bug24666 deleted file mode 100644 index 830775f5f6..0000000000 --- a/changes/bug24666 +++ /dev/null @@ -1,7 +0,0 @@ - o Minor bugfixes (memory usage): - - - When queuing DESTROY cells on a channel, only queue the - circuit-id and reason fields: not the entire 514-byte - cell. This fix should help mitigate any bugs or attacks that - fill up these queues, and free more RAM for other uses. Fixes - bug 24666; bugfix on 0.2.5.1-alpha. diff --git a/changes/bug24671 b/changes/bug24671 deleted file mode 100644 index 34d09e704d..0000000000 --- a/changes/bug24671 +++ /dev/null @@ -1,6 +0,0 @@ - o Minor bugfixes (scheduler, KIST): - - Use a sane write limit for KISTLite when writing onto a connection - buffer instead of using INT_MAX and shoving as much as it can. Because - the OOM handler cleans up circuit queues, we are better off at keeping - them in that queue instead of the connection's buffer. Fixes bug 24671; - bugfix on 0.3.2.1-alpha. diff --git a/changes/cargo-build-problem b/changes/cargo-build-problem new file mode 100644 index 0000000000..6691b0efca --- /dev/null +++ b/changes/cargo-build-problem @@ -0,0 +1,3 @@ + o Minor bugfixes (compilation, rust): + - Build correctly when building from outside Tor's source tree with the + TOR_RUST_DEPENDENCIES option set. Fixes bug 22768; bugfix on 0.3.1.7. diff --git a/changes/feature18329 b/changes/feature18329 deleted file mode 100644 index 1dabf50244..0000000000 --- a/changes/feature18329 +++ /dev/null @@ -1,9 +0,0 @@ - o Minor features (bridge): - - Bridge relays can now set the BridgeDistribution config option to - add a "bridge-distribution-request" line to their bridge descriptor, - which tells BridgeDB how they'd like their bridge address to be - given out. (Note that as of Oct 2017, BridgeDB does not yet implement - this feature.) As a side benefit, this feature provides a way - to distinguish bridge descriptors from non-bridge descriptors. - Implements tickets 18329. - diff --git a/changes/feature24427 b/changes/feature24427 new file mode 100644 index 0000000000..8650c45d31 --- /dev/null +++ b/changes/feature24427 @@ -0,0 +1,5 @@ + o Minor features (OSX, iOS, performance): + - Use the mach_approximate_time() function (when available) to + implement coarse monotonic time. Having a coarse time function + should avoid a large number of system calls, and improve + performance slightly, especially under load. Closes ticket 24427. diff --git a/changes/geoip-2017-11-06 b/changes/geoip-2017-11-06 deleted file mode 100644 index f034be9006..0000000000 --- a/changes/geoip-2017-11-06 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (geoip): - - Update geoip and geoip6 to the November 6 2017 Maxmind GeoLite2 - Country database. - diff --git a/changes/geoip-2017-12-06 b/changes/geoip-2017-12-06 deleted file mode 100644 index ae4fb1149f..0000000000 --- a/changes/geoip-2017-12-06 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (geoip): - - Update geoip and geoip6 to the December 6 2017 Maxmind GeoLite2 - Country database. - diff --git a/changes/geoip-october2017 b/changes/geoip-october2017 deleted file mode 100644 index 11f623e85f..0000000000 --- a/changes/geoip-october2017 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (geoip): - - Update geoip and geoip6 to the October 4 2017 Maxmind GeoLite2 - Country database. - diff --git a/changes/hsdescv3_fuzz_more b/changes/hsdescv3_fuzz_more deleted file mode 100644 index 25626bb9a4..0000000000 --- a/changes/hsdescv3_fuzz_more +++ /dev/null @@ -1,3 +0,0 @@ - o Minor features (testing): - - Our fuzzing tests now test the encrypted portions of the - v3 hidden service descriptors. Implements more of 21509. diff --git a/changes/longclaw_23592 b/changes/longclaw_23592 deleted file mode 100644 index 91e2da8972..0000000000 --- a/changes/longclaw_23592 +++ /dev/null @@ -1,3 +0,0 @@ - o Directory authority changes: - - The directory authority "Longclaw" has changed its IP address. - Closes ticket 23592. diff --git a/changes/stack b/changes/stack deleted file mode 100644 index ffdf536cb9..0000000000 --- a/changes/stack +++ /dev/null @@ -1,7 +0,0 @@ - o Minor bugfixes (correctness): - - Fix several places in our codebase where a C compiler would be likely - to eliminate a check, based on assuming that undefined behavior had not - happened elsewhere in the code. These cases are usually a sign of - redundant checking, or dubious arithmetic. Found by Georg Koppen using - the "STACK" tool from Wang, Zeldovich, Kaashoek, and - Solar-Lezama. Fixes bug 24423; bugfix on various Tor versions. diff --git a/changes/ticket13605 b/changes/ticket13605 new file mode 100644 index 0000000000..0456881ff9 --- /dev/null +++ b/changes/ticket13605 @@ -0,0 +1,5 @@ + o Major features (relay): + - Implement an option, ReducedExitPolicy, to allow an Tor exit relay + operator to use a more reasonable ("reduced") exit policy, rather + than the default one. Closes ticket 13605. Patch from Neel Chauhan. + diff --git a/changes/ticket20020 b/changes/ticket20020 new file mode 100644 index 0000000000..737fb95980 --- /dev/null +++ b/changes/ticket20020 @@ -0,0 +1,4 @@ + o Minor features (logging): + - Improve a warning message that happens when we fail to re-parse + an old router because of an expired certificate. Closes ticket + 20020. diff --git a/changes/ticket20699 b/changes/ticket20699 new file mode 100644 index 0000000000..a93236ba40 --- /dev/null +++ b/changes/ticket20699 @@ -0,0 +1,14 @@ + o Major features (hidden service v3, control port): + - Control port now supports command and events for hidden service v3. See + proposal 284 for more information on what has been done exactly. Only + the HSFETCH command hasn't been implemented at this stage because of a + lack of use case with v3. + + It is now possible to create ephemeral v3 services using the ADD_ONION + command. Here is a summary of the events and commands that have been + modified to support v3: + + Events: HS_DESC, HS_DESC_CONTENT, CIRC and CIRC_MINOR The + Commands: GETINFO, HSPOST, ADD_ONION and DEL_ONION. + + This closes ticket 20699. diff --git a/changes/ticket20895 b/changes/ticket20895 new file mode 100644 index 0000000000..a1d8204997 --- /dev/null +++ b/changes/ticket20895 @@ -0,0 +1,6 @@ + o Minor features (forward-compatibility): + - If a relay supports some link authentication protocol that we do not + recognize, then include that relay's ed25519 key when telling other + relays to extend to it. Previously, we treated future versions as if + they were too old to support ed25519 link authentication. + Closes ticket 20895. diff --git a/changes/ticket21031 b/changes/ticket21031 deleted file mode 100644 index b081fb018f..0000000000 --- a/changes/ticket21031 +++ /dev/null @@ -1,7 +0,0 @@ - o Minor features (removed deprecations): - - The ClientDNSRejectInternalAddresses flag can once again be set in - non-testing Tor networks, so long as they do not use the default - directory authorities. - This change also removes the deprecation of this - flag in 0.2.9.2-alpha. Closes ticket 21031. - diff --git a/changes/ticket22342 b/changes/ticket22342 new file mode 100644 index 0000000000..53505509d2 --- /dev/null +++ b/changes/ticket22342 @@ -0,0 +1,3 @@ + o Code simplification and refactoring: + - Small changes to Tor's buf_t API to make it suitable for use as + a general-purpose safe string constructor. Closes ticket 22342. diff --git a/changes/ticket22703 b/changes/ticket22703 new file mode 100644 index 0000000000..c1eda615f5 --- /dev/null +++ b/changes/ticket22703 @@ -0,0 +1,6 @@ + o Major features (storage): + - Users can choose to store cached directory documents somewhere other + than the DataDirectory by using the CacheDirectory option. + Similarly, the storage location for relay's keys can be overridden + with the KeyDirectory option. + Closes ticket 22703. diff --git a/changes/ticket22840 b/changes/ticket22840 new file mode 100644 index 0000000000..6d234fb0d4 --- /dev/null +++ b/changes/ticket22840 @@ -0,0 +1,8 @@ + o Major features (Rust experimentation): + - Tor now ships with an optional implementation of one of its smaller + modules (protover.c) in the Rust programming language. To try it + out, install a Rust build environment, and configure Tor with + "--enable-rust --enable-cargo-online-mode". This should not + cause any user-visible changes, but should help us gain more experience + with Rust, and plan future Rust integration work. + Implementation by Chelsea Komlo. Closes ticket 22840. diff --git a/changes/ticket23271 b/changes/ticket23271 new file mode 100644 index 0000000000..42d5921a70 --- /dev/null +++ b/changes/ticket23271 @@ -0,0 +1,6 @@ + o Code simplification and refactoring: + - Separate the function that deletes ephemeral files when Tor stops + gracefully. + o Minor features (cleanup): + - Tor now deletes the CookieAuthFile and ExtORPortCookieAuthFile when it + stops. Closes ticket 23271. diff --git a/changes/ticket23459 b/changes/ticket23459 new file mode 100644 index 0000000000..a6452e4ee3 --- /dev/null +++ b/changes/ticket23459 @@ -0,0 +1,4 @@ + o Code simplification and refactoring (circuit rendezvous): + - Split get rendezvous circuit on client side on two different functions. + One that returns only established circuits and another that returns all + kinds of circuits. Closes ticket 23459. diff --git a/changes/ticket23577 b/changes/ticket23577 new file mode 100644 index 0000000000..7cd80bcb69 --- /dev/null +++ b/changes/ticket23577 @@ -0,0 +1,7 @@ + o Major features (v3 onion services): + - When v3 onion service clients send introduce cells, include the IPv6 + address of the rendezvous point, if it has one. v3 onion services running + 0.3.2 ignore IPv6 addresses. In future Tor versions, IPv6-only v3 single + onion services can use IPv6 addresses to connect directly to the + rendezvous point. Closes ticket 23577. Patch by Neel Chauhan. + diff --git a/changes/ticket23709 b/changes/ticket23709 new file mode 100644 index 0000000000..7948f9ae03 --- /dev/null +++ b/changes/ticket23709 @@ -0,0 +1,11 @@ + o Major feature (channel): + - Remove the incoming and outgoing channel queues. The reason to do so was + due to the fact that they were always empty meaning never used but still + looked at in our fast path. Bottom line, it was an unused code path. + - We've simplify a lot the channel subsystem by removing those queues but + also by removing a lot of unused code or dead code around it. Overall + this is a cleanup removing more than 1500 lines of code overall and + adding very little except for unit test. + - The majority ot the channel unit tests have been rewritten and the code + coverage has now been raised to 83.6% for channel.c. + Closes ticket 23709. diff --git a/changes/ticket23760 b/changes/ticket23760 new file mode 100644 index 0000000000..9213b14627 --- /dev/null +++ b/changes/ticket23760 @@ -0,0 +1,4 @@ + o Code simplification and refactoring: + - We make extend_info_from_node() use node_get_curve25519_onion_key() + introduced in ticket 23577 to access the curve25519 public keys rather + than accessing it directly. Closes ticket 23760. Patch by Neel Chauhan. diff --git a/changes/ticket23845 b/changes/ticket23845 new file mode 100644 index 0000000000..93c150bdb0 --- /dev/null +++ b/changes/ticket23845 @@ -0,0 +1,9 @@ + o Major features (embedding): + - There is now a documented stable API for programs that need to + embed Tor. See tor_api.h for full documentation and known bugs. + Closes ticket 23684. + + o Code simplification and refactoring: + - The tor_git_revision[] constant no longer needs to be redeclared + by everything that links against the rest of Tor. Done as part + of ticket 23845, to simplify our external API. diff --git a/changes/ticket23856 b/changes/ticket23856 deleted file mode 100644 index 049da18d06..0000000000 --- a/changes/ticket23856 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor feature (relay statistics): - - Change relay bandwidth reporting stats interval from 4 hours to 24 hours - in order to reduce the efficiency of guard discovery attacks. Fixes - ticket 23856. diff --git a/changes/ticket23900 b/changes/ticket23900 new file mode 100644 index 0000000000..0f949f4f4e --- /dev/null +++ b/changes/ticket23900 @@ -0,0 +1,7 @@ + o Minor features (API, embedding): + - Tor can now start with a preauthenticated control connection + created by the process that launched it. This feature is meant + for use by programs that want to launch and manage a Tor process + without allowing other programs to manage it as well. + For more information, see the __OwningControllerFD option + documented in control-spec.txt. Closes ticket 23900. diff --git a/changes/ticket23910 b/changes/ticket23910 deleted file mode 100644 index eb38fcf32f..0000000000 --- a/changes/ticket23910 +++ /dev/null @@ -1,3 +0,0 @@ - o Directory authority changes: - - Add bastet as a ninth directory authority to the default list. Closes - ticket 23910. diff --git a/changes/ticket24097 b/changes/ticket24097 deleted file mode 100644 index 36547a8ddb..0000000000 --- a/changes/ticket24097 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (logging): - - Downgrade a pair of log messages that could occur when an exit's - resolver gave us an unusual (but not forbidden) response. - Closes ticket 24097. diff --git a/changes/ticket24109 b/changes/ticket24109 deleted file mode 100644 index f66271817d..0000000000 --- a/changes/ticket24109 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor features (integration tests): - - Test that IPv6-only clients can use microdescriptors when running - "make test-network-all". Requires chutney master 61c28b9 or later. - Closes ticket 24109. diff --git a/changes/ticket24158 b/changes/ticket24158 deleted file mode 100644 index 3cdc06afae..0000000000 --- a/changes/ticket24158 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor bugfixes (logging): - Only log about no longer having KIST support once. Fixes bug 24158; bugfix - on 0.3.2 diff --git a/changes/ticket24254 b/changes/ticket24254 deleted file mode 100644 index 98d5d6bacd..0000000000 --- a/changes/ticket24254 +++ /dev/null @@ -1,3 +0,0 @@ - o Documentation: - Add notes in man page regarding OS support for the various scheduler types. - Attempt to use less jargon in the scheduler section. Closes ticket 24254. diff --git a/changes/ticket24315 b/changes/ticket24315 deleted file mode 100644 index df34dbf412..0000000000 --- a/changes/ticket24315 +++ /dev/null @@ -1,3 +0,0 @@ - o Major features (linux seccomp2 sandbox): - - Update the sandbox rules so that they should now work correctly with - Glibc 2.26. Closes ticket 24315. diff --git a/changes/ticket24363 b/changes/ticket24363 new file mode 100644 index 0000000000..6f90fc066e --- /dev/null +++ b/changes/ticket24363 @@ -0,0 +1,2 @@ + o Code simplification and refactoring: + - Remove /usr/athena from search path in configure.ac. Closes ticket 24363. diff --git a/changes/ticket24425 b/changes/ticket24425 deleted file mode 100644 index aa6f082bcc..0000000000 --- a/changes/ticket24425 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes (hidden service v3): - - Bump hsdir_spread_store parameter from 3 to 4 in order to increase the - probability of reaching a service for a client missing microdescriptors. - Fixes bug 24425; bugfix on 0.3.2.1-alpha. diff --git a/changes/ticket24467 b/changes/ticket24467 new file mode 100644 index 0000000000..1b1c223f5f --- /dev/null +++ b/changes/ticket24467 @@ -0,0 +1,3 @@ + o Code simplification and refactoring: + - Switch -Wnormalized=id to -Wnormalized=nfkc in configure.ac to avoid + source code identifier confusion. Closes ticket 24467. diff --git a/changes/ticket24500 b/changes/ticket24500 deleted file mode 100644 index b49b7a5551..0000000000 --- a/changes/ticket24500 +++ /dev/null @@ -1,3 +0,0 @@ - o Minor features (logging): - - Provide better warnings when the getrandom() syscall fails. - Closes ticket 24500. diff --git a/changes/ticket24518 b/changes/ticket24518 new file mode 100644 index 0000000000..28d40a3f26 --- /dev/null +++ b/changes/ticket24518 @@ -0,0 +1,4 @@ + o Minor bugfixes (build, rust): + - Don't pass the --quiet option to cargo: it seems to suppress some + errors, which is not what we want to do when building. + Fixes bug 24518; bugfix on 0.3.1.7. diff --git a/changes/trove-2017-009 b/changes/trove-2017-009 deleted file mode 100644 index 166a5faec6..0000000000 --- a/changes/trove-2017-009 +++ /dev/null @@ -1,10 +0,0 @@ - o Major bugfixes (security): - - When checking for replays in the INTRODUCE1 cell data for a (legacy) - hiddden service, correctly detect replays in the RSA-encrypted part of - the cell. We were previously checking for replays on the entire cell, - but those can be circumvented due to the malleability of Tor's legacy - hybrid encryption. This fix helps prevent a traffic confirmation - attack. Fixes bug 24244; bugfix on 0.2.4.1-alpha. This issue is also - tracked as TROVE-2017-009 and CVE-2017-8819. - - diff --git a/changes/trove-2017-010 b/changes/trove-2017-010 deleted file mode 100644 index d5bf9333da..0000000000 --- a/changes/trove-2017-010 +++ /dev/null @@ -1,6 +0,0 @@ - o Major bugfixes (security): - - Fix a denial-of-service issue where an attacker could crash - a directory authority using a malformed router descriptor. - Fixes bug 24245; bugfix on 0.2.9.4-alpha. Also tracked - as TROVE-2017-010 and CVE-2017-8820. - diff --git a/changes/trove-2017-011 b/changes/trove-2017-011 deleted file mode 100644 index 82d20d9e78..0000000000 --- a/changes/trove-2017-011 +++ /dev/null @@ -1,8 +0,0 @@ - o Major bugfixes (security): - - Fix a denial of service bug where an attacker could use a malformed - directory object to cause a Tor instance to pause while OpenSSL would - try to read a passphrase from the terminal. (If the terminal was not - available, tor would continue running.) Fixes bug 24246; bugfix on - every version of Tor. Also tracked as TROVE-2017-011 and - CVE-2017-8821. Found by OSS-Fuzz as testcase 6360145429790720. - diff --git a/changes/trove-2017-012-part1 b/changes/trove-2017-012-part1 deleted file mode 100644 index 9fccc2cf65..0000000000 --- a/changes/trove-2017-012-part1 +++ /dev/null @@ -1,6 +0,0 @@ - o Major bugfixes (security, relay): - - When running as a relay, make sure that we never build a path through - ourselves, even in the case where we have somehow lost the version of - our descriptor appearing in the consensus. Fixes part of bug 21534; - bugfix on 0.2.0.1-alpha. This issue is also tracked as TROVE-2017-012 - and CVE-2017-8822. diff --git a/changes/trove-2017-012-part2 b/changes/trove-2017-012-part2 deleted file mode 100644 index ed994c5b02..0000000000 --- a/changes/trove-2017-012-part2 +++ /dev/null @@ -1,5 +0,0 @@ - o Major bugfixes (security, relay): - - When running as a relay, make sure that we never ever choose ourselves - as a guard. Previously, this was possible. Fixes part of bug 21534; - bugfix on 0.3.0.1-alpha. This issue is also tracked as TROVE-2017-012 - and CVE-2017-8822. diff --git a/configure.ac b/configure.ac index f8c8884450..e471f8f914 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ dnl Copyright (c) 2007-2017, The Tor Project, Inc. dnl See LICENSE for licensing information AC_PREREQ([2.63]) -AC_INIT([tor],[0.3.2.8-rc]) +AC_INIT([tor],[0.3.3.0-alpha-dev]) AC_CONFIG_SRCDIR([src/or/main.c]) AC_CONFIG_MACRO_DIR([m4]) @@ -204,6 +204,20 @@ if test x$enable_event_tracing_debug = xyes; then AC_DEFINE([TOR_EVENT_TRACING_ENABLED], [1], [Compile the event tracing instrumentation]) fi +dnl Enable Android only features. +AC_ARG_ENABLE(android, + AS_HELP_STRING(--enable-android, [build with Android features enabled])) +AM_CONDITIONAL([USE_ANDROID], [test "x$enable_android" = "xyes"]) + +if test "x$enable_android" = "xyes"; then + AC_DEFINE([USE_ANDROID], [1], [Compile with Android specific features enabled]) + + dnl Check if the Android log library is available. + AC_CHECK_HEADERS([android/log.h]) + AC_SEARCH_LIBS(__android_log_write, [log]) + +fi + dnl check for the correct "ar" when cross-compiling. dnl (AM_PROG_AR was new in automake 1.11.2, which we do not yet require, dnl so kludge up a replacement for the case where it isn't there yet.) @@ -375,6 +389,7 @@ AH_BOTTOM([ AM_CONDITIONAL(BUILD_NT_SERVICES, test "x$bwin32" = "xtrue") +AM_CONDITIONAL(BUILD_LIBTORRUNNER, test "x$bwin32" != "xtrue") dnl Enable C99 when compiling with MIPSpro AC_MSG_CHECKING([for MIPSpro compiler]) @@ -417,39 +432,45 @@ if test "x$enable_rust" = "xyes"; then dnl When we're not allowed to touch the network, we need crate dependencies dnl locally available. AC_MSG_CHECKING([rust crate dependencies]) - AC_ARG_VAR([RUST_DEPENDENCIES], [path to directory with local crate mirror]) - if test "x$RUST_DEPENDENCIES" = "x"; then - RUST_DEPENDENCIES="$srcdir/src/ext/rust/" + AC_ARG_VAR([TOR_RUST_DEPENDENCIES], [path to directory with local crate mirror]) + if test "x$TOR_RUST_DEPENDENCIES" = "x"; then + TOR_RUST_DEPENDENCIES="$srcdir/src/ext/rust/" NEED_MOD=1 fi - if test ! -d "$RUST_DEPENDENCIES"; then - AC_MSG_ERROR([Rust dependency directory $RUST_DEPENDENCIES does not exist. Specify a dependency directory using the RUST_DEPENDENCIES variable or allow cargo to fetch crates using --enable-cargo-online-mode.]) + if test ! -d "$TOR_RUST_DEPENDENCIES"; then + AC_MSG_ERROR([Rust dependency directory $TOR_RUST_DEPENDENCIES does not exist. Specify a dependency directory using the TOR_RUST_DEPENDENCIES variable or allow cargo to fetch crates using --enable-cargo-online-mode.]) + ERRORED=1 fi for dep in $rust_crates; do - if test ! -d "$RUST_DEPENDENCIES"/"$dep"; then - AC_MSG_ERROR([Failure to find rust dependency $RUST_DEPENDENCIES/$dep. Specify a dependency directory using the RUST_DEPENDENCIES variable or allow cargo to fetch crates using --enable-cargo-online-mode.]) + if test ! -d "$TOR_RUST_DEPENDENCIES"/"$dep"; then + AC_MSG_ERROR([Failure to find rust dependency $TOR_RUST_DEPENDENCIES/$dep. Specify a dependency directory using the TOR_RUST_DEPENDENCIES variable or allow cargo to fetch crates using --enable-cargo-online-mode.]) + ERRORED=1 fi done if test "x$NEED_MOD" = "x1"; then dnl When looking for dependencies from cargo, pick right directory - RUST_DEPENDENCIES="../../src/ext/rust" + TOR_RUST_DEPENDENCIES="../../src/ext/rust" + fi + if test "x$ERRORED" = "x"; then + AC_MSG_RESULT([yes]) fi fi dnl For now both MSVC and MinGW rust libraries will output static libs with dnl the MSVC naming convention. if test "$bwin32" = "true"; then - TOR_RUST_UTIL_STATIC_NAME=tor_util.lib + TOR_RUST_STATIC_NAME=tor_rust.lib else - TOR_RUST_UTIL_STATIC_NAME=libtor_util.a + TOR_RUST_STATIC_NAME=libtor_rust.a fi - AC_SUBST(TOR_RUST_UTIL_STATIC_NAME) + AC_SUBST(TOR_RUST_STATIC_NAME) AC_SUBST(CARGO_ONLINE) AC_SUBST(RUST_DL) dnl Let's check the rustc version, too AC_MSG_CHECKING([rust version]) + RUSTC_VERSION=`$RUSTC --version` RUSTC_VERSION_MAJOR=`$RUSTC --version | cut -d ' ' -f 2 | cut -d '.' -f 1` RUSTC_VERSION_MINOR=`$RUSTC --version | cut -d ' ' -f 2 | cut -d '.' -f 2` if test "x$RUSTC_VERSION_MAJOR" = "x" -o "x$RUSTC_VERSION_MINOR" = "x"; then @@ -458,6 +479,7 @@ if test "x$enable_rust" = "xyes"; then if test "$RUSTC_VERSION_MAJOR" -lt 2 -a "$RUSTC_VERSION_MINOR" -lt 14; then AC_MSG_ERROR([rustc must be at least version 1.14]) fi + AC_MSG_RESULT([$RUSTC_VERSION]) fi AC_SEARCH_LIBS(socket, [socket network]) @@ -505,6 +527,7 @@ AC_CHECK_FUNCS( llround \ localtime_r \ lround \ + mach_approximate_time \ memmem \ memset_s \ pipe \ @@ -728,7 +751,7 @@ TOR_SEARCH_LIBRARY(openssl, $tryssldir, [-lssl -lcrypto $TOR_LIB_GDI $TOR_LIB_WS [#include <openssl/ssl.h>], [struct ssl_method_st; const struct ssl_method_st *TLSv1_1_method(void);], [TLSv1_1_method();], [], - [/usr/local/opt/openssl /usr/local/openssl /usr/lib/openssl /usr/local/ssl /usr/lib/ssl /usr/local /usr/athena /opt/openssl]) + [/usr/local/opt/openssl /usr/local/openssl /usr/lib/openssl /usr/local/ssl /usr/lib/ssl /usr/local /opt/openssl]) dnl XXXX check for OPENSSL_VERSION_NUMBER == SSLeay() @@ -986,12 +1009,12 @@ if test "$fragile_hardening" = "yes"; then TOR_TRY_COMPILE_WITH_CFLAGS([-fsanitize=address], also_link, CFLAGS_ASAN="-fsanitize=address", true) if test "$tor_cv_cflags__fsanitize_address" = "yes" && test "$tor_can_link__fsanitize_address" != "yes"; then - AC_MSG_ERROR([The compiler supports -fsanitize=address, but for some reason I was not able to link when using it. Are you missing run-time support? With GCC you need libubsan.so, and with Clang you need libclang_rt.ubsan*]) + AC_MSG_ERROR([The compiler supports -fsanitize=address, but for some reason I was not able to link when using it. Are you missing run-time support? With GCC you need libubsan.*, and with Clang you need libclang_rt.ubsan*]) fi TOR_TRY_COMPILE_WITH_CFLAGS([-fsanitize=undefined], also_link, CFLAGS_UBSAN="-fsanitize=undefined", true) if test "$tor_cv_cflags__fsanitize_address" = "yes" && test "$tor_can_link__fsanitize_address" != "yes"; then - AC_MSG_ERROR([The compiler supports -fsanitize=undefined, but for some reason I was not able to link when using it. Are you missing run-time support? With GCC you need libasan.so, and with Clang you need libclang_rt.ubsan*]) + AC_MSG_ERROR([The compiler supports -fsanitize=undefined, but for some reason I was not able to link when using it. Are you missing run-time support? With GCC you need libasan.*, and with Clang you need libclang_rt.ubsan*]) fi TOR_CHECK_CFLAGS([-fno-omit-frame-pointer]) @@ -1232,6 +1255,7 @@ AC_CHECK_HEADERS([assert.h \ pwd.h \ readpassphrase.h \ stdint.h \ + stdatomic.h \ sys/eventfd.h \ sys/file.h \ sys/ioctl.h \ @@ -1989,7 +2013,6 @@ if test "x$enable_gcc_warnings_advisory" != "xno"; then -Winvalid-source-encoding -Winvalid-token-paste -Wknr-promoted-parameter - -Wlanguage-extension-token -Wlarge-by-value-copy -Wliteral-conversion -Wliteral-range @@ -2015,7 +2038,7 @@ if test "x$enable_gcc_warnings_advisory" != "xno"; then -Wnon-literal-null-conversion -Wnon-pod-varargs -Wnonportable-cfstrings - -Wnormalized=id + -Wnormalized=nfkc -Wnull-arithmetic -Wnull-character -Wnull-conversion diff --git a/contrib/win32build/tor-mingw.nsi.in b/contrib/win32build/tor-mingw.nsi.in index 40459a951e..f3c23ef335 100644 --- a/contrib/win32build/tor-mingw.nsi.in +++ b/contrib/win32build/tor-mingw.nsi.in @@ -8,7 +8,7 @@ !include "LogicLib.nsh" !include "FileFunc.nsh" !insertmacro GetParameters -!define VERSION "0.3.2.8-rc" +!define VERSION "0.3.3.0-alpha-dev" !define INSTALLER "tor-${VERSION}-win32.exe" !define WEBSITE "https://www.torproject.org/" !define LICENSE "LICENSE" diff --git a/doc/HACKING/CodingStandards.md b/doc/HACKING/CodingStandards.md index dd21d6fd2c..79a6a9f0ce 100644 --- a/doc/HACKING/CodingStandards.md +++ b/doc/HACKING/CodingStandards.md @@ -346,6 +346,46 @@ macro, as in: if (BUG(ptr == NULL)) return -1; +Allocator conventions +--------------------- + +By convention, any tor type with a name like `abc_t` should be allocated +by a function named `abc_new()`. This function should never return +NULL. + +Also, a type named `abc_t` should be freed by a function named `abc_free_()`. +Don't call this `abc_free_()` function directly -- instead, wrap it in a +macro called `abc_free()`, using the `FREE_AND_NULL` macro: + + void abc_free_(abc_t *obj); + #define abc_free(obj) FREE_AND_NULL(abc_t, abc_free_, (obj)) + +This macro will free the underlying `abc_t` object, and will also set +the object pointer to NULL. + +You should define all `abc_free_()` functions to accept NULL inputs: + + void + abc_free_(abc_t *obj) + { + if (!obj) + return; + tor_free(obj->name); + thing_free(obj->thing); + tor_free(obj); + } + +If you need a free function that takes a `void *` argument (for example, +to use it as a function callback), define it with a name like +`abc_free_void()`: + + static void + abc_free_void_(void *obj) + { + abc_free_(obj); + } + + Doxygen comment conventions --------------------------- diff --git a/doc/HACKING/GettingStartedRust.md b/doc/HACKING/GettingStartedRust.md index a5253b46a6..3d00388314 100644 --- a/doc/HACKING/GettingStartedRust.md +++ b/doc/HACKING/GettingStartedRust.md @@ -60,30 +60,28 @@ or specifying a local directory. **Using a local dependency cache** -**NOTE**: local dependency caches which were not *originally* created via - `--enable-cargo-online-mode` are broken. See https://bugs.torproject.org/22907 +You'll need the following Rust dependencies (as of this writing): -To specify a local directory: + libc==0.2.22 - RUST_DEPENDENCIES='path_to_dependencies_directory' ./configure --enable-rust +We vendor our Rust dependencies in a separate repo using +[cargo-vendor](https://github.com/alexcrichton/cargo-vendor). To use them, do: -(Note that RUST_DEPENDENCIES must be the full path to the directory; it cannot -be relative.) + git submodule init + git submodule update -You'll need the following Rust dependencies (as of this writing): +To specify the local directory containing the dependencies, (assuming you are in +the top level of the repository) configure tor with: - libc==0.2.22 + TOR_RUST_DEPENDENCIES='path_to_dependencies_directory' ./configure --enable-rust -To get them, do: +(Note that RUST_DEPENDENCIES must be the full path to the directory; it cannot +be relative.) + +Assuming you used the above `git submodule` commands and you're in the topmost +directory of the repository, this would be: - mkdir path_to_dependencies_directory - cd path_to_dependencies_directory - git clone https://github.com/rust-lang/libc - cd libc - git checkout 0.2.22 - cargo package - cd .. - ln -s libc/target/package/libc-0.2.22 libc-0.2.22 + TOR_RUST_DEPENDENCIES=`pwd`/src/ext/rust/crates ./configure --enable-rust Identifying which modules to rewrite @@ -125,6 +123,16 @@ is on our TODO list to try to cultivate good standing with various distro maintainers of `rustc` and `cargo`, in order to ensure that whatever version we solidify on is readily available. +If parts of your Rust code needs to stay in sync with C code (such as handling +enums across the FFI boundary), annonotate these places in a comment structured +as follows: + + /// C_RUST_COUPLED: <path_to_file> `<name_of_c_object>` + +Where <name_of_c_object> can be an enum, struct, constant, etc. Then, do the +same in the C code, to note that rust will need to be changed when the C +does. + Adding your Rust module to Tor's build system ----------------------------------------------- @@ -132,8 +140,13 @@ solidify on is readily available. in the `.../tor/src/rust/` directory. 1. Add your crate to `.../tor/src/rust/Cargo.toml`, in the `[workspace.members]` section. -2. Append your crate's static library to the `rust_ldadd` definition - (underneath `if USE_RUST`) in `.../tor/Makefile.am`. +2. Add your crate's files to src/rust/include.am + +If your crate should be available to C (rather than just being included as a +dependency of other Rust modules): +0. Declare the crate as a dependency of tor_rust in + `src/rust/tor_util/Cargo.toml` and include it in + `src/rust/tor_rust/lib.rs` How to test your Rust code ---------------------------- diff --git a/doc/HACKING/ReleasingTor.md b/doc/HACKING/ReleasingTor.md index 62029b44f0..9cbc8710bb 100644 --- a/doc/HACKING/ReleasingTor.md +++ b/doc/HACKING/ReleasingTor.md @@ -171,7 +171,8 @@ new Tor release: - {mike} at tig dot as - {tails-rm} at boum dot org - {simon} at sdeziel.info - - {yuri} at rawbw.com + - {yuri} at freebsd.org + - {mh+tor} at scrit.ch 4. Add the version number to Trac. To do this, go to Trac, log in, select "Admin" near the top of the screen, then select "Versions" from diff --git a/doc/HACKING/android/Simpleperf.md b/doc/HACKING/android/Simpleperf.md new file mode 100644 index 0000000000..c12558aed4 --- /dev/null +++ b/doc/HACKING/android/Simpleperf.md @@ -0,0 +1,98 @@ +# Using `simpleperf` to collect CPU profiling on Android + +This document describes how you can use Android's `simpleperf` +command-line tool to get CPU profiling information from Tor via the +Orbot application. The tool is particularly useful for Tor development +because it is able to profile native applications on the platform +whereas a lot of the normal tooling for the Android platform is only +able to collect information from Java-based applications. + +## Prerequisites + +Before using `simpleperf` there is a couple of steps that must be +followed. You should make sure you have both a recent installation of +the Android Software Development Kit (SDK) and Native Development Kit +(NDK) installed. These can be found on the Android Developers website. + +1. Follow the build instructions from the `BUILD` file in the Orbot + repository and build an Orbot APK (Android Package) file with + debugging enabled. Make sure that when you build the native content of + the Orbot application that you run the `make -C external` command with + an additional `DEBUG=1` as paramter to ensure that the Orbot build + process does not strip the debug symbols from the Tor binary. + +2. (Optional) Uninstall and clean-up your old Orbot installation that + is most likely downloaded from Google's Play Store or via fdroid: + + $ adb shell pm clear org.torproject.android + $ adb uninstall org.torproject.android + +3. Install the Android Package you generated in step 1: + + $ adb install /path/to/your/app-fullperm-debug.apk + +4. Check on your device that the newly installed Orbot actually works + and behaves in the way you expect it to. + +## Profiling using `simpleperf` + +The `simpleperf` tool can be found in the `simpleperf/` directory in +the directory where you installed the Android NDK to. In this +directory there is a set of Python files that will help you deploy the +tool to a device and collect the measurement data such that you can +analyze the results on your computer rather than on your phone. + +1. Change directory to the location of the `simpleperf` directory. +2. Open the `app_profiler.config` file and change + `app_package_name` to `org.torproject.android`, `apk_file_path` to + the path of your Orbot Android Package (APK file). +3. Optionally change the duration parameter in the `record_options` + variable in `app_profiler.config` to the duration which you would like + to collect samples in. The value is specified in seconds. +4. Run the app profiler using `python app_profiler.py`. This helper + script will push the `simpleperf` tool to your device, start the + profiler, and once it has completed copy the generated `perf.data` + file over to your computer with the results. + +### Analyzing the results + +You can inspect your resulting `perf.data` file via a simple GUI +program `python report.py` or via the command-line tool `simpleperf +report`. I've found the GUI tool to be easier to navigate around with +than the command-line tool. + +The `-g` option can be passed to the command line `simpleperf report` +tool allows you to see the call graph of functions and how much time +was spend on the call. + +## Tips & Tricks + +- When you have installed Orbot the first time, you will notice that + if you get a shell on the Android device that there is no Tor binary + available. This is because Orbot unpacks the Tor binary first time it + is executed and places it under the `app_bin/` directory on the + device. + + To access binaries, `torrc` files, and other useful information on + the device do the following: + + $ adb shell + (device):/ $ run-as org.torproject.android + (device):/data/data/org.torproject.android $ ls + app_bin app_data cache databases files lib shared_prefs + + Descriptors, control authentication cookie, state, and other files can be + found in the `app_data` directory. The `torrc` can be found in the `app_bin/` + directory. + +- You can enable logging in Tor via the syslog (or android) log + mechanism with: + + $ adb shell + (device):/ $ run-as org.torproject.android + (device):/data/data/org.torproject.android $ echo -e "\nLog info syslog" >> app_bin/torrc + + Start Tor the normal way via Orbot and collect the logs from your computer using + + $ adb logcat + diff --git a/doc/tor.1.txt b/doc/tor.1.txt index fc285ebe1b..64114d6e6b 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -231,12 +231,15 @@ GENERAL OPTIONS usage for \_relayed traffic_ on this node to the specified number of bytes per second, and the average outgoing bandwidth usage to that same value. Relayed traffic currently is calculated to include answers to directory - requests, but that may change in future versions. (Default: 0) + requests, but that may change in future versions. They do not include directory + fetches by the relay (from authority or other relays), because that is considered + "client" activity. (Default: 0) [[RelayBandwidthBurst]] **RelayBandwidthBurst** __N__ **bytes**|**KBytes**|**MBytes**|**GBytes**|**TBytes**|**KBits**|**MBits**|**GBits**|**TBits**:: If not 0, limit the maximum token bucket size (also known as the burst) for \_relayed traffic_ to the given number of bytes in each direction. - (Default: 0) + They do not include directory fetches by the relay (from authority + or other relays), because that is considered "client" activity. (Default: 0) [[PerConnBWRate]] **PerConnBWRate** __N__ **bytes**|**KBytes**|**MBytes**|**GBytes**|**TBytes**|**KBits**|**MBits**|**GBits**|**TBits**:: If set, do separate rate limiting for each connection from a non-relay. @@ -415,6 +418,16 @@ GENERAL OPTIONS DataDirectory. If the option is set to 1, make the DataDirectory readable by the default GID. (Default: 0) +[[CacheDirectory]] **CacheDirectory** __DIR__:: + Store cached directory data in DIR. Can not be changed while tor is + running. + (Default: uses the value of DataDirectory.) + +[[CacheDirectoryGroupReadable]] **CacheDirectoryGroupReadable** **0**|**1**:: + If this option is set to 0, don't allow the filesystem group to read the + CacheDirectory. If the option is set to 1, make the CacheDirectory readable + by the default GID. (Default: 0) + [[FallbackDir]] **FallbackDir** __ipv4address__:__port__ orport=__port__ id=__fingerprint__ [weight=__num__] [ipv6=**[**__ipv6address__**]**:__orport__]:: When we're unable to connect to any directory cache for directory info (usually because we don't know about any yet) we try a directory authority. @@ -714,6 +727,11 @@ GENERAL OPTIONS log entries are marked with "Tor-__tag__". Can not be changed while tor is running. (Default: none) +[[AndroidIdentityTag]] **AndroidIdentityTag** __tag__:: + When logging to Android's logging subsystem, adds a tag to the log identity + such that log entries are marked with "Tor-__tag__". Can not be changed while + tor is running. (Default: none) + [[SafeLogging]] **SafeLogging** **0**|**1**|**relay**:: Tor can scrub potentially sensitive strings from log messages (e.g. addresses) by replacing them with the string [scrubbed]. This way logs can @@ -1223,8 +1241,8 @@ The following options are useful only for clients (that is, if nodes via this connection. **UseIPv4Cache**;; Tells the client to use any cached IPv4 DNS answers we have when making - requests via this connection. (NOTE: This option, along UseIPv6Cache - and UseDNSCache, can harm your anonymity, and probably + requests via this connection. (NOTE: This option, or UseIPv6Cache + or UseDNSCache, can harm your anonymity, and probably won't help performance as much as you might expect. Use with care!) **UseIPv6Cache**;; Tells the client to use any cached IPv6 DNS answers we have when making @@ -1771,8 +1789,15 @@ is non-zero): write your IPv6 rules using accept6/reject6 \*6, and your IPv4 rules using accept/reject \*4. If you want to \_replace_ the default exit policy, end your exit policy with either a reject \*:* or an accept \*:*. Otherwise, - you're \_augmenting_ (prepending to) the default exit policy. The default - exit policy is: + + you're \_augmenting_ (prepending to) the default exit policy. + + + + If you want to use a reduced exit policy rather than the default exit + policy, set "ReducedExitPolicy 1". If you want to _replace_ the default + exit policy with your custom exit policy, end your exit policy with either + a reject *:* or an accept *:*. Otherwise, you’re _augmenting_ (prepending + to) the default or reduced exit policy. + + + + The default exit policy is: reject *:25 reject *:119 @@ -1786,7 +1811,7 @@ is non-zero): reject *:6881-6999 accept *:* -[[ExitPolicyDefault]]:: +[[ExitPolicyDefault]] **ExitPolicyDefault**:: Since the default exit policy uses accept/reject *, it applies to both IPv4 and IPv6 addresses. @@ -1808,6 +1833,99 @@ is non-zero): to disclose. (Default: 0) +[[ReducedExitPolicy]] **ReducedExitPolicy** **0**|**1**:: + If set, use a reduced exit policy rather than the default one. + + + + The reduced exit policy is an alternative to the default exit policy. It + allows as many Internet services as possible while still blocking the + majority of TCP ports. Currently, the policy allows approximately 65 ports. + This reduces the odds that your node will be used for peer-to-peer + applications. + + + + The reduced exit policy is: + + accept *:20-21 + accept *:22 + accept *:23 + accept *:43 + accept *:53 + accept *:79 + accept *:80-81 + accept *:88 + accept *:110 + accept *:143 + accept *:194 + accept *:220 + accept *:389 + accept *:443 + accept *:464 + accept *:465 + accept *:531 + accept *:543-544 + accept *:554 + accept *:563 + accept *:587 + accept *:636 + accept *:706 + accept *:749 + accept *:873 + accept *:902-904 + accept *:981 + accept *:989-990 + accept *:991 + accept *:992 + accept *:993 + accept *:994 + accept *:995 + accept *:1194 + accept *:1220 + accept *:1293 + accept *:1500 + accept *:1533 + accept *:1677 + accept *:1723 + accept *:1755 + accept *:1863 + accept *:2082 + accept *:2083 + accept *:2086-2087 + accept *:2095-2096 + accept *:2102-2104 + accept *:3128 + accept *:3389 + accept *:3690 + accept *:4321 + accept *:4643 + accept *:5050 + accept *:5190 + accept *:5222-5223 + accept *:5228 + accept *:5900 + accept *:6660-6669 + accept *:6679 + accept *:6697 + accept *:8000 + accept *:8008 + accept *:8074 + accept *:8080 + accept *:8082 + accept *:8087-8088 + accept *:8232-8233 + accept *:8332-8333 + accept *:8443 + accept *:8888 + accept *:9418 + accept *:9999 + accept *:10000 + accept *:11371 + accept *:19294 + accept *:19638 + accept *:50002 + accept *:64738 + reject *:* + + (Default: 0) + [[IPv6Exit]] **IPv6Exit** **0**|**1**:: If set, and we are an exit node, allow clients to use us for IPv6 traffic. (Default: 0) @@ -1914,6 +2032,11 @@ is non-zero): to 0 will disable the heartbeat. Otherwise, it must be at least 30 minutes. (Default: 6 hours) +[[MainloopStats]] **MainloopStats** **0**|**1**:: + Log main loop statistics every **HeartbeatPeriod** seconds. This is a log + level __notice__ message designed to help developers instrumenting Tor's + main event loop. (Default: 0) + [[AccountingMax]] **AccountingMax** __N__ **bytes**|**KBytes**|**MBytes**|**GBytes**|**TBytes**|**KBits**|**MBits**|**GBits**|**TBits**:: Limits the max number of bytes sent and received within a set time period using a given calculation rule (see: AccountingStart, AccountingRule). @@ -2127,6 +2250,17 @@ is non-zero): ed25519 master identity key, as well as the corresponding temporary signing keys and certificates. (Default: 0) +[[KeyDirectory]] **KeyDirectory** __DIR__:: + Store secret keys in DIR. Can not be changed while tor is + running. + (Default: the "keys" subdirectory of DataDirectory.) + +[[KeyDirectoryGroupReadable]] **KeyDirectoryGroupReadable** **0**|**1**:: + If this option is set to 0, don't allow the filesystem group to read the + KeywDirectory. If the option is set to 1, make the KeyDirectory readable + by the default GID. (Default: 0) + + DIRECTORY SERVER OPTIONS ------------------------ @@ -2370,9 +2504,29 @@ on the public Tor network. [[AuthDirHasIPv6Connectivity]] **AuthDirHasIPv6Connectivity** **0**|**1**:: Authoritative directories only. When set to 0, OR ports with an - IPv6 address are being accepted without reachability testing. - When set to 1, IPv6 OR ports are being tested just like IPv4 OR - ports. (Default: 0) + IPv6 address are not included in the authority's votes. When set to 1, + IPv6 OR ports are tested for reachability like IPv4 OR ports. If the + reachability test succeeds, the authority votes for the IPv6 ORPort, and + votes Running for the relay. If the reachability test fails, the authority + does not vote for the IPv6 ORPort, and does not vote Running (Default: 0) + ++ + The content of the consensus depends on the number of voting authorities + that set AuthDirHasIPv6Connectivity: + + If no authorities set AuthDirHasIPv6Connectivity 1, there will be no + IPv6 ORPorts in the consensus. + + If a minority of authorities set AuthDirHasIPv6Connectivity 1, + unreachable IPv6 ORPorts will be removed from the consensus. But the + majority of IPv4-only authorities will still vote the relay as Running. + Reachable IPv6 ORPort lines will be included in the consensus + + If a majority of voting authorities set AuthDirHasIPv6Connectivity 1, + relays with unreachable IPv6 ORPorts will not be listed as Running. + Reachable IPv6 ORPort lines will be included in the consensus + (To ensure that any valid majority will vote relays with unreachable + IPv6 ORPorts not Running, 75% of authorities must set + AuthDirHasIPv6Connectivity 1.) [[MinMeasuredBWsForAuthToIgnoreAdvertised]] **MinMeasuredBWsForAuthToIgnoreAdvertised** __N__:: A total value, in abstract bandwidth units, describing how much @@ -2791,40 +2945,35 @@ FILES **@LOCALSTATEDIR@/lib/tor/**:: The tor process stores keys and other data here. -__DataDirectory__**/cached-status/**:: - The most recently downloaded network status document for each authority. - Each file holds one such document; the filenames are the hexadecimal - identity key fingerprints of the directory authorities. Obsolete; - no longer in use. -__DataDirectory__**/cached-certs**:: +__CacheDirectory__**/cached-certs**:: This file holds downloaded directory key certificates that are used to verify authenticity of documents generated by Tor directory authorities. -__DataDirectory__**/cached-consensus** and/or **cached-microdesc-consensus**:: +__CacheDirectory__**/cached-consensus** and/or **cached-microdesc-consensus**:: The most recent consensus network status document we've downloaded. -__DataDirectory__**/cached-descriptors** and **cached-descriptors.new**:: +__CacheDirectory__**/cached-descriptors** and **cached-descriptors.new**:: These files hold downloaded router statuses. Some routers may appear more than once; if so, the most recently published descriptor is used. Lines beginning with @-signs are annotations that contain more information about a given router. The ".new" file is an append-only journal; when it gets too large, all entries are merged into a new cached-descriptors file. -__DataDirectory__**/cached-extrainfo** and **cached-extrainfo.new**:: +__CacheDirectory__**/cached-extrainfo** and **cached-extrainfo.new**:: As "cached-descriptors", but holds optionally-downloaded "extra-info" documents. Relays use these documents to send inessential information about statistics, bandwidth history, and network health to the authorities. They aren't fetched by default; see the DownloadExtraInfo option for more info. -__DataDirectory__**/cached-microdescs** and **cached-microdescs.new**:: +__CacheDirectory__**/cached-microdescs** and **cached-microdescs.new**:: These files hold downloaded microdescriptors. Lines beginning with @-signs are annotations that contain more information about a given router. The ".new" file is an append-only journal; when it gets too large, all entries are merged into a new cached-microdescs file. -__DataDirectory__**/cached-routers** and **cached-routers.new**:: +__CacheDirectory__**/cached-routers** and **cached-routers.new**:: Obsolete versions of cached-descriptors and cached-descriptors.new. When Tor can't find the newer files, it looks here instead. @@ -2842,7 +2991,7 @@ __DataDirectory__**/sr-state**:: Authority only. State file used to record information about the current status of the shared-random-value voting state. -__DataDirectory__**/diff-cache**:: +__CacheDirectory__**/diff-cache**:: Directory cache only. Holds older consensuses, and diffs from older consensuses to the most recent consensus of each type, compressed in various ways. Each file contains a set of key-value arguments @@ -2872,63 +3021,60 @@ __DataDirectory__**/key-pinning-journal**:: or factoring the RSA1024 key will no longer let an attacker impersonate the relay. -__DataDirectory__**/keys/***:: - Only used by servers. Holds identity keys and onion keys. - -__DataDirectory__**/keys/authority_identity_key**:: +__KeyDirectory__**/authority_identity_key**:: A v3 directory authority's master identity key, used to authenticate its signing key. Tor doesn't use this while it's running. The tor-gencert program uses this. If you're running an authority, you should keep this key offline, and not actually put it here. -__DataDirectory__**/keys/authority_certificate**:: +__KeyDirectory__**/authority_certificate**:: A v3 directory authority's certificate, which authenticates the authority's current vote- and consensus-signing key using its master identity key. Only directory authorities use this file. -__DataDirectory__**/keys/authority_signing_key**:: +__KeyDirectory__**/authority_signing_key**:: A v3 directory authority's signing key, used to sign votes and consensuses. Only directory authorities use this file. Corresponds to the **authority_certificate** cert. -__DataDirectory__**/keys/legacy_certificate**:: +__KeyDirectory__**/legacy_certificate**:: As authority_certificate: used only when V3AuthUseLegacyKey is set. See documentation for V3AuthUseLegacyKey. -__DataDirectory__**/keys/legacy_signing_key**:: +__KeyDirectory__**/legacy_signing_key**:: As authority_signing_key: used only when V3AuthUseLegacyKey is set. See documentation for V3AuthUseLegacyKey. -__DataDirectory__**/keys/secret_id_key**:: +__KeyDirectory__**/secret_id_key**:: A relay's RSA1024 permanent identity key, including private and public components. Used to sign router descriptors, and to sign other keys. -__DataDirectory__**/keys/ed25519_master_id_public_key**:: +__KeyDirectory__**/ed25519_master_id_public_key**:: The public part of a relay's Ed25519 permanent identity key. -__DataDirectory__**/keys/ed25519_master_id_secret_key**:: +__KeyDirectory__**/ed25519_master_id_secret_key**:: The private part of a relay's Ed25519 permanent identity key. This key is used to sign the medium-term ed25519 signing key. This file can be kept offline, or kept encrypted. If so, Tor will not be able to generate new signing keys itself; you'll need to use tor --keygen yourself to do so. -__DataDirectory__**/keys/ed25519_signing_secret_key**:: +__KeyDirectory__**/ed25519_signing_secret_key**:: The private and public components of a relay's medium-term Ed25519 signing key. This key is authenticated by the Ed25519 master key, in turn authenticates other keys (and router descriptors). -__DataDirectory__**/keys/ed25519_signing_cert**:: +__KeyDirectory__**/ed25519_signing_cert**:: The certificate which authenticates "ed25519_signing_secret_key" as having been signed by the Ed25519 master key. -__DataDirectory__**/keys/secret_onion_key** and **secret_onion_key.old**:: +__KeyDirectory__**/secret_onion_key** and **secret_onion_key.old**:: A relay's RSA1024 short-term onion key. Used to decrypt old-style ("TAP") circuit extension requests. The ".old" file holds the previously generated key, which the relay uses to handle any requests that were made by clients that didn't have the new one. -__DataDirectory__**/keys/secret_onion_key_ntor** and **secret_onion_key_ntor.old**:: +__KeyDirectory__**/secret_onion_key_ntor** and **secret_onion_key_ntor.old**:: A relay's Curve25519 short-term onion key. Used to handle modern ("ntor") circuit extension requests. The ".old" file holds the previously generated key, which the relay uses to handle any requests that were @@ -2955,11 +3101,11 @@ __DataDirectory__**/v3-status-votes**:: Only for v3 authoritative directory servers. This file contains status votes from all the authoritative directory servers. -__DataDirectory__**/unverified-consensus**:: +__CacheDirectory__**/unverified-consensus**:: This file contains a network consensus document that has been downloaded, but which we didn't have the right certificates to check yet. -__DataDirectory__**/unverified-microdesc-consensus**:: +__CacheDirectory__**/unverified-microdesc-consensus**:: This file contains a microdescriptor-flavored network consensus document that has been downloaded, but which we didn't have the right certificates to check yet. diff --git a/scripts/codegen/fuzzing_include_am.py b/scripts/codegen/fuzzing_include_am.py index fee6d4494a..fda57d2ae8 100755 --- a/scripts/codegen/fuzzing_include_am.py +++ b/scripts/codegen/fuzzing_include_am.py @@ -9,7 +9,7 @@ FUZZERS = """ hsdescv2 hsdescv3 http - http-connect + http-connect iptsv2 microdesc vrs diff --git a/scripts/maint/updateFallbackDirs.py b/scripts/maint/updateFallbackDirs.py index 82a60420b4..3efd408ec7 100755 --- a/scripts/maint/updateFallbackDirs.py +++ b/scripts/maint/updateFallbackDirs.py @@ -3,10 +3,10 @@ # Usage: # # Regenerate the list: -# scripts/maint/updateFallbackDirs.py > src/or/fallback_dirs.inc +# scripts/maint/updateFallbackDirs.py > src/or/fallback_dirs.inc 2> fallback_dirs.log # # Check the existing list: -# scripts/maint/updateFallbackDirs.py check_existing > fallback_dirs.inc.ok +# scripts/maint/updateFallbackDirs.py check_existing > fallback_dirs.inc.ok 2> fallback_dirs.log # mv fallback_dirs.inc.ok src/or/fallback_dirs.inc # # This script should be run from a stable, reliable network connection, diff --git a/src/common/address.c b/src/common/address.c index dbe129be59..0c0ba782ae 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -1759,14 +1759,14 @@ get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr)) break; } SMARTLIST_FOREACH_END(a); - free_interface_address6_list(addrs); + interface_address6_list_free(addrs); return rv; } /** Free a smartlist of IP addresses returned by get_interface_address6_list. */ void -free_interface_address6_list(smartlist_t *addrs) +interface_address6_list_free_(smartlist_t *addrs) { if (addrs != NULL) { SMARTLIST_FOREACH(addrs, tor_addr_t *, a, tor_free(a)); @@ -1781,7 +1781,7 @@ free_interface_address6_list(smartlist_t *addrs) * An empty smartlist means that there are no addresses of the selected type * matching these criteria. * Returns NULL on failure. - * Use free_interface_address6_list to free the returned list. + * Use interface_address6_list_free to free the returned list. */ MOCK_IMPL(smartlist_t *, get_interface_address6_list,(int severity, diff --git a/src/common/address.h b/src/common/address.h index c7f6935754..7607c76bae 100644 --- a/src/common/address.h +++ b/src/common/address.h @@ -206,7 +206,9 @@ const char * fmt_addr32(uint32_t addr); MOCK_DECL(int,get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr)); -void free_interface_address6_list(smartlist_t * addrs); +void interface_address6_list_free_(smartlist_t * addrs);// XXXX +#define interface_address6_list_free(addrs) \ + FREE_AND_NULL(smartlist_t, interface_address6_list_free_, (addrs)) MOCK_DECL(smartlist_t *,get_interface_address6_list,(int severity, sa_family_t family, int include_internal)); @@ -321,13 +323,8 @@ int addr_mask_get_bits(uint32_t mask); int tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len); char *tor_dup_ip(uint32_t addr) ATTR_MALLOC; MOCK_DECL(int,get_interface_address,(int severity, uint32_t *addr)); -/** Free a smartlist of IP addresses returned by get_interface_address_list. - */ -static inline void -free_interface_address_list(smartlist_t *addrs) -{ - free_interface_address6_list(addrs); -} +#define interface_address_list_free(lst)\ + interface_address6_list_free(lst) /** Return a smartlist of the IPv4 addresses of all interfaces on the server. * Excludes loopback and multicast addresses. Only includes internal addresses * if include_internal is true. (Note that a relay behind NAT may use an diff --git a/src/common/aes.c b/src/common/aes.c index 20b51a6758..9b5b0197ea 100644 --- a/src/common/aes.c +++ b/src/common/aes.c @@ -110,7 +110,7 @@ aes_new_cipher(const uint8_t *key, const uint8_t *iv, int key_bits) return (aes_cnt_cipher_t *) cipher; } void -aes_cipher_free(aes_cnt_cipher_t *cipher_) +aes_cipher_free_(aes_cnt_cipher_t *cipher_) { if (!cipher_) return; @@ -254,7 +254,7 @@ evaluate_ctr_for_aes(void) /* LCOV_EXCL_START */ log_err(LD_CRYPTO, "This OpenSSL has a buggy version of counter mode; " "quitting tor."); - exit(1); + exit(1); // exit ok: openssl is broken. /* LCOV_EXCL_STOP */ } return 0; @@ -324,7 +324,7 @@ aes_set_key(aes_cnt_cipher_t *cipher, const uint8_t *key, int key_bits) /** Release storage held by <b>cipher</b> */ void -aes_cipher_free(aes_cnt_cipher_t *cipher) +aes_cipher_free_(aes_cnt_cipher_t *cipher) { if (!cipher) return; diff --git a/src/common/aes.h b/src/common/aes.h index 1e400a56e0..0b17cd55a4 100644 --- a/src/common/aes.h +++ b/src/common/aes.h @@ -17,7 +17,9 @@ typedef struct aes_cnt_cipher aes_cnt_cipher_t; aes_cnt_cipher_t* aes_new_cipher(const uint8_t *key, const uint8_t *iv, int key_bits); -void aes_cipher_free(aes_cnt_cipher_t *cipher); +void aes_cipher_free_(aes_cnt_cipher_t *cipher); +#define aes_cipher_free(cipher) \ + FREE_AND_NULL(aes_cnt_cipher_t, aes_cipher_free_, (cipher)) void aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len); int evaluate_evp_for_aes(int force_value); diff --git a/src/common/buffers.c b/src/common/buffers.c index c45e13d551..a01add9bef 100644 --- a/src/common/buffers.c +++ b/src/common/buffers.c @@ -409,7 +409,7 @@ buf_slack(const buf_t *buf) /** Release storage held by <b>buf</b>. */ void -buf_free(buf_t *buf) +buf_free_(buf_t *buf) { if (!buf) return; @@ -472,7 +472,7 @@ buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped) chunk = chunk_new_with_alloc_size(buf_preferred_chunk_size(capacity)); } - chunk->inserted_time = (uint32_t)monotime_coarse_absolute_msec(); + chunk->inserted_time = monotime_coarse_get_stamp(); if (buf->tail) { tor_assert(buf->head); @@ -487,8 +487,8 @@ buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped) } /** Return the age of the oldest chunk in the buffer <b>buf</b>, in - * milliseconds. Requires the current monotonic time, in truncated msec, - * as its input <b>now</b>. + * timestamp units. Requires the current monotonic timestamp as its + * input <b>now</b>. */ uint32_t buf_get_oldest_chunk_timestamp(const buf_t *buf, uint32_t now) @@ -714,6 +714,53 @@ buf_add(buf_t *buf, const char *string, size_t string_len) return (int)buf->datalen; } +/** Add a nul-terminated <b>string</b> to <b>buf</b>, not including the + * terminating NUL. */ +void +buf_add_string(buf_t *buf, const char *string) +{ + buf_add(buf, string, strlen(string)); +} + +/** As tor_snprintf, but write the results into a buf_t */ +void +buf_add_printf(buf_t *buf, const char *format, ...) +{ + va_list ap; + va_start(ap,format); + buf_add_vprintf(buf, format, ap); + va_end(ap); +} + +/** As tor_vsnprintf, but write the results into a buf_t. */ +void +buf_add_vprintf(buf_t *buf, const char *format, va_list args) +{ + /* XXXX Faster implementations are easy enough, but let's optimize later */ + char *tmp; + tor_vasprintf(&tmp, format, args); + buf_add(buf, tmp, strlen(tmp)); + tor_free(tmp); +} + +/** Return a heap-allocated string containing the contents of <b>buf</b>, plus + * a NUL byte. If <b>sz_out</b> is provided, set *<b>sz_out</b> to the length + * of the returned string, not including the terminating NUL. */ +char * +buf_extract(buf_t *buf, size_t *sz_out) +{ + tor_assert(buf); + + size_t sz = buf_datalen(buf); + char *result; + result = tor_malloc(sz+1); + buf_peek(buf, result, sz); + result[sz] = 0; + if (sz_out) + *sz_out = sz; + return result; +} + /** Helper: copy the first <b>string_len</b> bytes from <b>buf</b> * onto <b>string</b>. */ @@ -795,6 +842,28 @@ buf_move_to_buf(buf_t *buf_out, buf_t *buf_in, size_t *buf_flushlen) return (int)cp; } +/** Moves all data from <b>buf_in</b> to <b>buf_out</b>, without copying. + */ +void +buf_move_all(buf_t *buf_out, buf_t *buf_in) +{ + tor_assert(buf_out); + if (!buf_in) + return; + + if (buf_out->head == NULL) { + buf_out->head = buf_in->head; + buf_out->tail = buf_in->tail; + } else { + buf_out->tail->next = buf_in->head; + buf_out->tail = buf_in->tail; + } + + buf_out->datalen += buf_in->datalen; + buf_in->head = buf_in->tail = NULL; + buf_in->datalen = 0; +} + /** Internal structure: represents a position in a buffer. */ typedef struct buf_pos_t { const chunk_t *chunk; /**< Which chunk are we pointing to? */ diff --git a/src/common/buffers.h b/src/common/buffers.h index 1eaa5f2d04..22a5f7bfa3 100644 --- a/src/common/buffers.h +++ b/src/common/buffers.h @@ -24,7 +24,8 @@ struct tor_compress_state_t; buf_t *buf_new(void); buf_t *buf_new_with_capacity(size_t size); size_t buf_get_default_chunk_size(const buf_t *buf); -void buf_free(buf_t *buf); +void buf_free_(buf_t *buf); +#define buf_free(b) FREE_AND_NULL(buf_t, buf_free_, (b)) void buf_clear(buf_t *buf); buf_t *buf_copy(const buf_t *buf); @@ -43,9 +44,15 @@ int buf_flush_to_socket(buf_t *buf, tor_socket_t s, size_t sz, size_t *buf_flushlen); int buf_add(buf_t *buf, const char *string, size_t string_len); +void buf_add_string(buf_t *buf, const char *string); +void buf_add_printf(buf_t *buf, const char *format, ...) + CHECK_PRINTF(2, 3); +void buf_add_vprintf(buf_t *buf, const char *format, va_list args) + CHECK_PRINTF(2, 0); int buf_add_compress(buf_t *buf, struct tor_compress_state_t *state, const char *data, size_t data_len, int done); int buf_move_to_buf(buf_t *buf_out, buf_t *buf_in, size_t *buf_flushlen); +void buf_move_all(buf_t *buf_out, buf_t *buf_in); void buf_peek(const buf_t *buf, char *string, size_t string_len); void buf_drain(buf_t *buf, size_t n); int buf_get_bytes(buf_t *buf, char *string, size_t string_len); @@ -62,6 +69,7 @@ void buf_assert_ok(buf_t *buf); int buf_find_string_offset(const buf_t *buf, const char *s, size_t n); void buf_pullup(buf_t *buf, size_t bytes, const char **head_out, size_t *len_out); +char *buf_extract(buf_t *buf, size_t *sz_out); #ifdef BUFFERS_PRIVATE #ifdef TOR_UNIT_TESTS @@ -79,8 +87,7 @@ typedef struct chunk_t { size_t DBG_alloc; #endif char *data; /**< A pointer to the first byte of data stored in <b>mem</b>. */ - uint32_t inserted_time; /**< Timestamp in truncated ms since epoch - * when this chunk was inserted. */ + uint32_t inserted_time; /**< Timestamp when this chunk was inserted. */ char mem[FLEXIBLE_ARRAY_MEMBER]; /**< The actual memory used for storage in * this chunk. */ } chunk_t; diff --git a/src/common/compat.c b/src/common/compat.c index 7fe97488e3..b4bd6eba06 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -1186,7 +1186,7 @@ mark_socket_open(tor_socket_t s) bitarray_set(open_sockets, s); } #else /* !(defined(DEBUG_SOCKET_COUNTING)) */ -#define mark_socket_open(s) STMT_NIL +#define mark_socket_open(s) ((void) (s)) #endif /* defined(DEBUG_SOCKET_COUNTING) */ /** @} */ @@ -1273,11 +1273,22 @@ tor_open_socket_with_extensions(int domain, int type, int protocol, goto socket_ok; /* So that socket_ok will not be unused. */ socket_ok: + tor_take_socket_ownership(s); + return s; +} + +/** + * For socket accounting: remember that we are the owner of the socket + * <b>s</b>. This will prevent us from overallocating sockets, and prevent us + * from asserting later when we close the socket <b>s</b>. + */ +void +tor_take_socket_ownership(tor_socket_t s) +{ socket_accounting_lock(); ++n_sockets_open; mark_socket_open(s); socket_accounting_unlock(); - return s; } /** As accept(), but counts the number of open sockets. */ @@ -1358,10 +1369,7 @@ tor_accept_socket_with_extensions(tor_socket_t sockfd, struct sockaddr *addr, goto socket_ok; /* So that socket_ok will not be unused. */ socket_ok: - socket_accounting_lock(); - ++n_sockets_open; - mark_socket_open(s); - socket_accounting_unlock(); + tor_take_socket_ownership(s); return s; } @@ -1897,9 +1905,12 @@ tor_passwd_dup(const struct passwd *pw) return new_pw; } +#define tor_passwd_free(pw) \ + FREE_AND_NULL(struct passwd, tor_passwd_free_, (pw)) + /** Helper: free one of our cached 'struct passwd' values. */ static void -tor_passwd_free(struct passwd *pw) +tor_passwd_free_(struct passwd *pw) { if (!pw) return; diff --git a/src/common/compat.h b/src/common/compat.h index fee9e6587d..0aabee68c8 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -483,6 +483,7 @@ typedef int socklen_t; int tor_close_socket_simple(tor_socket_t s); MOCK_DECL(int, tor_close_socket, (tor_socket_t s)); +void tor_take_socket_ownership(tor_socket_t s); tor_socket_t tor_open_socket_with_extensions( int domain, int type, int protocol, int cloexec, int nonblock); diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c index 740cc2a11d..10489bf296 100644 --- a/src/common/compat_libevent.c +++ b/src/common/compat_libevent.c @@ -69,7 +69,7 @@ suppress_libevent_log_msg(const char *msg) /* Wrapper for event_free() that tolerates tor_event_free(NULL) */ void -tor_event_free(struct event *ev) +tor_event_free_(struct event *ev) { if (ev == NULL) return; @@ -126,7 +126,7 @@ tor_libevent_initialize(tor_libevent_cfg *torcfg) if (!the_event_base) { /* LCOV_EXCL_START */ log_err(LD_GENERAL, "Unable to initialize Libevent: cannot continue."); - exit(1); + exit(1); // exit ok: libevent is broken. /* LCOV_EXCL_STOP */ } @@ -213,7 +213,7 @@ periodic_timer_new(struct event_base *base, /** Stop and free a periodic timer */ void -periodic_timer_free(periodic_timer_t *timer) +periodic_timer_free_(periodic_timer_t *timer) { if (!timer) return; diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h index 834354c405..0cdb73fbb9 100644 --- a/src/common/compat_libevent.h +++ b/src/common/compat_libevent.h @@ -19,7 +19,9 @@ void suppress_libevent_log_msg(const char *msg); evdns_add_server_port_with_base(tor_libevent_get_base(), \ (sock),(tcp),(cb),(data)); -void tor_event_free(struct event *ev); +void tor_event_free_(struct event *ev); +#define tor_event_free(ev) \ + FREE_AND_NULL(struct event, tor_event_free_, (ev)) typedef struct periodic_timer_t periodic_timer_t; @@ -27,9 +29,12 @@ periodic_timer_t *periodic_timer_new(struct event_base *base, const struct timeval *tv, void (*cb)(periodic_timer_t *timer, void *data), void *data); -void periodic_timer_free(periodic_timer_t *); +void periodic_timer_free_(periodic_timer_t *); +#define periodic_timer_free(t) \ + FREE_AND_NULL(periodic_timer_t, periodic_timer_free_, (t)) #define tor_event_base_loopexit event_base_loopexit +#define tor_event_base_loopbreak event_base_loopbreak /** Defines a configuration for using libevent with Tor: passed as an argument * to tor_libevent_initialize() to describe how we want to set up. */ diff --git a/src/common/compat_rust.c b/src/common/compat_rust.c deleted file mode 100644 index 366fd4037b..0000000000 --- a/src/common/compat_rust.c +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright (c) 2017, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file rust_compat.c - * \brief Rust FFI compatibility functions and helpers. This file is only built - * if Rust is not used. - **/ - -#include "compat_rust.h" -#include "util.h" - -/** - * Free storage pointed to by <b>str</b>, and itself. - */ -void -rust_str_free(rust_str_t str) -{ - char *s = (char *)str; - tor_free(s); -} - -/** - * Return zero-terminated contained string. - */ -const char * -rust_str_get(const rust_str_t str) -{ - return (const char *)str; -} - -/* If we were using Rust, we'd say so on startup. */ -rust_str_t -rust_welcome_string(void) -{ - char *s = tor_malloc_zero(1); - return (rust_str_t)s; -} - diff --git a/src/common/compat_rust.h b/src/common/compat_rust.h deleted file mode 100644 index 72fde39296..0000000000 --- a/src/common/compat_rust.h +++ /dev/null @@ -1,28 +0,0 @@ -/* Copyright (c) 2017, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file rust_compat.h - * \brief Headers for rust_compat.c - **/ - -#ifndef TOR_RUST_COMPAT_H -#define TOR_RUST_COMPAT_H - -#include "torint.h" - -/** - * Strings allocated in Rust must be freed from Rust code again. Let's make - * it less likely to accidentally mess up and call tor_free() on it, because - * currently it'll just work but might break at any time. - */ -typedef uintptr_t rust_str_t; - -void rust_str_free(rust_str_t); - -const char *rust_str_get(const rust_str_t); - -rust_str_t rust_welcome_string(void); - -#endif /* !defined(TOR_RUST_COMPAT_H) */ - diff --git a/src/common/compat_threads.c b/src/common/compat_threads.c index 208d3138d9..3171c4b2f2 100644 --- a/src/common/compat_threads.c +++ b/src/common/compat_threads.c @@ -48,7 +48,7 @@ tor_mutex_new_nonrecursive(void) } /** Release all storage and system resources held by <b>m</b>. */ void -tor_mutex_free(tor_mutex_t *m) +tor_mutex_free_(tor_mutex_t *m) { if (!m) return; @@ -68,7 +68,7 @@ tor_cond_new(void) /** Free all storage held in <b>c</b>. */ void -tor_cond_free(tor_cond_t *c) +tor_cond_free_(tor_cond_t *c) { if (!c) return; @@ -352,12 +352,7 @@ alert_sockets_close(alert_sockets_t *socks) socks->read_fd = socks->write_fd = -1; } -/* - * XXXX We might be smart to move to compiler intrinsics or real atomic - * XXXX operations at some point. But not yet. - * - */ - +#ifndef HAVE_STDATOMIC_H /** Initialize a new atomic counter with the value 0 */ void atomic_counter_init(atomic_counter_t *counter) @@ -397,4 +392,16 @@ atomic_counter_get(atomic_counter_t *counter) tor_mutex_release(&counter->mutex); return val; } +/** Replace the value of an atomic counter; return the old one. */ +size_t +atomic_counter_exchange(atomic_counter_t *counter, size_t newval) +{ + size_t oldval; + tor_mutex_acquire(&counter->mutex); + oldval = counter->val; + counter->val = newval; + tor_mutex_release(&counter->mutex); + return oldval; +} +#endif /* !defined(HAVE_STDATOMIC_H) */ diff --git a/src/common/compat_threads.h b/src/common/compat_threads.h index 42f14eab2a..c93e601ec5 100644 --- a/src/common/compat_threads.h +++ b/src/common/compat_threads.h @@ -14,6 +14,10 @@ #include <pthread.h> #endif +#ifdef HAVE_STDATOMIC_H +#include <stdatomic.h> +#endif + #if defined(_WIN32) #define USE_WIN32_THREADS #elif defined(HAVE_PTHREAD_H) && defined(HAVE_PTHREAD_CREATE) @@ -50,7 +54,8 @@ void tor_mutex_init(tor_mutex_t *m); void tor_mutex_init_nonrecursive(tor_mutex_t *m); void tor_mutex_acquire(tor_mutex_t *m); void tor_mutex_release(tor_mutex_t *m); -void tor_mutex_free(tor_mutex_t *m); +void tor_mutex_free_(tor_mutex_t *m); +#define tor_mutex_free(m) FREE_AND_NULL(tor_mutex_t, tor_mutex_free_, (m)) void tor_mutex_uninit(tor_mutex_t *m); unsigned long tor_get_thread_id(void); void tor_threads_init(void); @@ -77,7 +82,8 @@ typedef struct tor_cond_t { } tor_cond_t; tor_cond_t *tor_cond_new(void); -void tor_cond_free(tor_cond_t *cond); +void tor_cond_free_(tor_cond_t *cond); +#define tor_cond_free(c) FREE_AND_NULL(tor_cond_t, tor_cond_free_, (c)) int tor_cond_init(tor_cond_t *cond); void tor_cond_uninit(tor_cond_t *cond); int tor_cond_wait(tor_cond_t *cond, tor_mutex_t *mutex, @@ -150,16 +156,68 @@ void tor_threadlocal_set(tor_threadlocal_t *threadlocal, void *value); /** * Atomic counter type; holds a size_t value. */ +#ifdef HAVE_STDATOMIC_H +typedef struct atomic_counter_t { + atomic_size_t val; +} atomic_counter_t; +#define ATOMIC_LINKAGE static +#else /* !(defined(HAVE_STDATOMIC_H)) */ typedef struct atomic_counter_t { tor_mutex_t mutex; size_t val; } atomic_counter_t; - -void atomic_counter_init(atomic_counter_t *counter); -void atomic_counter_destroy(atomic_counter_t *counter); -void atomic_counter_add(atomic_counter_t *counter, size_t add); -void atomic_counter_sub(atomic_counter_t *counter, size_t sub); -size_t atomic_counter_get(atomic_counter_t *counter); +#define ATOMIC_LINKAGE +#endif /* defined(HAVE_STDATOMIC_H) */ + +ATOMIC_LINKAGE void atomic_counter_init(atomic_counter_t *counter); +ATOMIC_LINKAGE void atomic_counter_destroy(atomic_counter_t *counter); +ATOMIC_LINKAGE void atomic_counter_add(atomic_counter_t *counter, size_t add); +ATOMIC_LINKAGE void atomic_counter_sub(atomic_counter_t *counter, size_t sub); +ATOMIC_LINKAGE size_t atomic_counter_get(atomic_counter_t *counter); +ATOMIC_LINKAGE size_t atomic_counter_exchange(atomic_counter_t *counter, + size_t newval); +#undef ATOMIC_LINKAGE + +#ifdef HAVE_STDATOMIC_H +/** Initialize a new atomic counter with the value 0 */ +static inline void +atomic_counter_init(atomic_counter_t *counter) +{ + atomic_init(&counter->val, 0); +} +/** Clean up all resources held by an atomic counter. */ +static inline void +atomic_counter_destroy(atomic_counter_t *counter) +{ + (void)counter; +} +/** Add a value to an atomic counter. */ +static inline void +atomic_counter_add(atomic_counter_t *counter, size_t add) +{ + (void) atomic_fetch_add(&counter->val, add); +} +/** Subtract a value from an atomic counter. */ +static inline void +atomic_counter_sub(atomic_counter_t *counter, size_t sub) +{ + (void) atomic_fetch_sub(&counter->val, sub); +} +/** Return the current value of an atomic counter */ +static inline size_t +atomic_counter_get(atomic_counter_t *counter) +{ + return atomic_load(&counter->val); +} +/** Replace the value of an atomic counter; return the old one. */ +static inline size_t +atomic_counter_exchange(atomic_counter_t *counter, size_t newval) +{ + return atomic_exchange(&counter->val, newval); +} + +#else /* !(defined(HAVE_STDATOMIC_H)) */ +#endif /* defined(HAVE_STDATOMIC_H) */ #endif /* !defined(TOR_COMPAT_THREADS_H) */ diff --git a/src/common/compat_time.c b/src/common/compat_time.c index 1ce6f5ce4e..8cecff2783 100644 --- a/src/common/compat_time.c +++ b/src/common/compat_time.c @@ -90,7 +90,7 @@ tor_gettimeofday(struct timeval *timeval) if (ft.ft_64 < EPOCH_BIAS) { /* LCOV_EXCL_START */ log_err(LD_GENERAL,"System time is before 1970; failing."); - exit(1); + exit(1); // exit ok: system clock is broken. /* LCOV_EXCL_STOP */ } ft.ft_64 -= EPOCH_BIAS; @@ -102,7 +102,7 @@ tor_gettimeofday(struct timeval *timeval) log_err(LD_GENERAL,"gettimeofday failed."); /* If gettimeofday dies, we have either given a bad timezone (we didn't), or segfaulted.*/ - exit(1); + exit(1); // exit ok: gettimeofday failed. /* LCOV_EXCL_STOP */ } #elif defined(HAVE_FTIME) @@ -279,6 +279,7 @@ monotime_reset_ratchets_for_testing(void) * nanoseconds. */ static struct mach_timebase_info mach_time_info; +static int monotime_shift = 0; static void monotime_init_internal(void) @@ -287,6 +288,14 @@ monotime_init_internal(void) int r = mach_timebase_info(&mach_time_info); tor_assert(r == 0); tor_assert(mach_time_info.denom != 0); + + { + // approximate only. + uint64_t ns_per_tick = mach_time_info.numer / mach_time_info.denom; + uint64_t ms_per_tick = ns_per_tick * ONE_MILLION; + // requires that tor_log2(0) == 0. + monotime_shift = tor_log2(ms_per_tick); + } } /** @@ -305,6 +314,21 @@ monotime_get(monotime_t *out) out->abstime_ = mach_absolute_time(); } +#if defined(HAVE_MACH_APPROXIMATE_TIME) +void +monotime_coarse_get(monotime_coarse_t *out) +{ +#ifdef TOR_UNIT_TESTS + if (monotime_mocking_enabled) { + out->abstime_ = (mock_time_nsec_coarse * mach_time_info.denom) + / mach_time_info.numer; + return; + } +#endif /* defined(TOR_UNIT_TESTS) */ + out->abstime_ = mach_approximate_time(); +} +#endif + /** * Return the number of nanoseconds between <b>start</b> and <b>end</b>. */ @@ -321,6 +345,26 @@ monotime_diff_nsec(const monotime_t *start, return diff_nsec; } +uint32_t +monotime_coarse_to_stamp(const monotime_coarse_t *t) +{ + return (uint32_t)(t->abstime_ >> monotime_shift); +} + +int +monotime_is_zero(const monotime_t *val) +{ + return val->abstime_ == 0; +} + +void +monotime_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec) +{ + const uint64_t nsec = msec * ONE_MILLION; + const uint64_t ticks = (nsec * mach_time_info.denom) / mach_time_info.numer; + out->abstime_ = val->abstime_ + ticks; +} + /* end of "__APPLE__" */ #elif defined(HAVE_CLOCK_GETTIME) @@ -399,6 +443,37 @@ monotime_diff_nsec(const monotime_t *start, return diff_nsec; } +/* This value is ONE_BILLION >> 20. */ +static const uint32_t STAMP_TICKS_PER_SECOND = 953; + +uint32_t +monotime_coarse_to_stamp(const monotime_coarse_t *t) +{ + uint32_t nsec = (uint32_t)t->ts_.tv_nsec; + uint32_t sec = (uint32_t)t->ts_.tv_sec; + + return (sec * STAMP_TICKS_PER_SECOND) + (nsec >> 20); +} + +int +monotime_is_zero(const monotime_t *val) +{ + return val->ts_.tv_sec == 0 && val->ts_.tv_nsec == 0; +} + +void +monotime_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec) +{ + const uint32_t sec = msec / 1000; + const uint32_t msec_remainder = msec % 1000; + out->ts_.tv_sec = val->ts_.tv_sec + sec; + out->ts_.tv_nsec = val->ts_.tv_nsec + (msec_remainder * ONE_MILLION); + if (out->ts_.tv_nsec > ONE_BILLION) { + out->ts_.tv_nsec -= ONE_BILLION; + out->ts_.tv_sec += 1; + } +} + /* end of "HAVE_CLOCK_GETTIME" */ #elif defined (_WIN32) @@ -531,6 +606,41 @@ monotime_coarse_diff_nsec(const monotime_coarse_t *start, return monotime_coarse_diff_msec(start, end) * ONE_MILLION; } +static const uint32_t STAMP_TICKS_PER_SECOND = 1000; + +uint32_t +monotime_coarse_to_stamp(const monotime_coarse_t *t) +{ + return (uint32_t) t->tick_count_; +} + +int +monotime_is_zero(const monotime_t *val) +{ + return val->pcount_ == 0; +} + +int +monotime_coarse_is_zero(const monotime_coarse_t *val) +{ + return val->tick_count_ == 0; +} + +void +monotime_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec) +{ + const uint64_t nsec = msec * ONE_MILLION; + const uint64_t ticks = (nsec * nsec_per_tick_denom) / nsec_per_tick_numer; + out->pcount_ = val->pcount_ + ticks; +} + +void +monotime_coarse_add_msec(monotime_coarse_t *out, const monotime_coarse_t *val, + uint32_t msec) +{ + out->tick_count_ = val->tick_count_ + msec; +} + /* end of "_WIN32" */ #elif defined(MONOTIME_USING_GETTIMEOFDAY) @@ -567,6 +677,36 @@ monotime_diff_nsec(const monotime_t *start, return (diff.tv_sec * ONE_BILLION + diff.tv_usec * 1000); } +/* This value is ONE_MILLION >> 10. */ +static const uint32_t STAMP_TICKS_PER_SECOND = 976; + +uint32_t +monotime_coarse_to_stamp(const monotime_coarse_t *t) +{ + const uint32_t usec = (uint32_t)t->tv_.tv_usec; + const uint32_t sec = (uint32_t)t->tv_.tv_sec; + return (sec * STAMP_TICKS_PER_SECOND) | (nsec >> 10); +} + +int +monotime_is_zero(const monotime_t *val) +{ + return val->tv_.tv_sec == 0 && val->tv_.tv_usec == 0; +} + +void +monotime_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec) +{ + const uint32_t sec = msec / 1000; + const uint32_t msec_remainder = msec % 1000; + out->tv_.tv_sec = val->tv_.tv_sec + sec; + out->tv_.tv_usec = val->tv_.tv_nsec + (msec_remainder * 1000); + if (out->tv_.tv_usec > ONE_MILLION) { + out->tv_.tv_usec -= ONE_MILLION; + out->tv_.tv_sec += 1; + } +} + /* end of "MONOTIME_USING_GETTIMEOFDAY" */ #else #error "No way to implement monotonic timers." @@ -589,6 +729,19 @@ monotime_init(void) } } +void +monotime_zero(monotime_t *out) +{ + memset(out, 0, sizeof(*out)); +} +#ifdef MONOTIME_COARSE_TYPE_IS_DIFFERENT +void +monotime_coarse_zero(monotime_coarse_t *out) +{ + memset(out, 0, sizeof(*out)); +} +#endif + int64_t monotime_diff_usec(const monotime_t *start, const monotime_t *end) @@ -653,5 +806,35 @@ monotime_coarse_absolute_msec(void) { return monotime_coarse_absolute_nsec() / ONE_MILLION; } +#else +#define initalized_at_coarse initialized_at #endif /* defined(MONOTIME_COARSE_FN_IS_DIFFERENT) */ +/** + * Return the current time "stamp" as described by monotime_coarse_to_stamp. + */ +uint32_t +monotime_coarse_get_stamp(void) +{ + monotime_coarse_t now; + monotime_coarse_get(&now); + return monotime_coarse_to_stamp(&now); +} + +#ifdef __APPLE__ +uint64_t +monotime_coarse_stamp_units_to_approx_msec(uint64_t units) +{ + /* Recover as much precision as we can. */ + uint64_t abstime_diff = (units << monotime_shift); + return (abstime_diff * mach_time_info.numer) / + (mach_time_info.denom * ONE_MILLION); +} +#else +uint64_t +monotime_coarse_stamp_units_to_approx_msec(uint64_t units) +{ + return (units * 1000) / STAMP_TICKS_PER_SECOND; +} +#endif + diff --git a/src/common/compat_time.h b/src/common/compat_time.h index 5ea4aae42b..6ddd11883d 100644 --- a/src/common/compat_time.h +++ b/src/common/compat_time.h @@ -65,6 +65,9 @@ typedef struct monotime_t { typedef struct monotime_coarse_t { uint64_t tick_count_; } monotime_coarse_t; +#elif defined(__APPLE__) && defined(HAVE_MACH_APPROXIMATE_TIME) +#define MONOTIME_COARSE_FN_IS_DIFFERENT +#define monotime_coarse_t monotime_t #else #define monotime_coarse_t monotime_t #endif /* defined(CLOCK_MONOTONIC_COARSE) && ... || ... */ @@ -102,6 +105,21 @@ uint64_t monotime_absolute_usec(void); */ uint64_t monotime_absolute_msec(void); +/** + * Set <b>out</b> to zero. + */ +void monotime_zero(monotime_t *out); +/** + * Return true iff <b>out</b> is zero + */ +int monotime_is_zero(const monotime_t *out); + +/** + * Set <b>out</b> to N milliseconds after <b>val</b>. + */ +/* XXXX We should add a more generic function here if we ever need to */ +void monotime_add_msec(monotime_t *out, const monotime_t *val, uint32_t msec); + #if defined(MONOTIME_COARSE_FN_IS_DIFFERENT) /** * Set <b>out</b> to the current coarse time. @@ -117,6 +135,23 @@ uint64_t monotime_coarse_absolute_msec(void); #define monotime_coarse_absolute_msec monotime_absolute_msec #endif /* defined(MONOTIME_COARSE_FN_IS_DIFFERENT) */ +/** + * Return a "timestamp" approximation for a coarse monotonic timer. + * This timestamp is meant to be fast to calculate and easy to + * compare, and have a unit of something roughly around 1 msec. + * + * It will wrap over from time to time. + * + * It has no defined zero point. + */ +uint32_t monotime_coarse_to_stamp(const monotime_coarse_t *t); +/** + * Convert a difference, expressed in the units of monotime_coarse_to_stamp, + * into an approximate number of milliseconds. + */ +uint64_t monotime_coarse_stamp_units_to_approx_msec(uint64_t units); +uint32_t monotime_coarse_get_stamp(void); + #if defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT) int64_t monotime_coarse_diff_nsec(const monotime_coarse_t *start, const monotime_coarse_t *end); @@ -124,10 +159,17 @@ int64_t monotime_coarse_diff_usec(const monotime_coarse_t *start, const monotime_coarse_t *end); int64_t monotime_coarse_diff_msec(const monotime_coarse_t *start, const monotime_coarse_t *end); +void monotime_coarse_zero(monotime_coarse_t *out); +int monotime_coarse_is_zero(const monotime_coarse_t *val); +void monotime_coarse_add_msec(monotime_coarse_t *out, + const monotime_coarse_t *val, uint32_t msec); #else /* !(defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT)) */ #define monotime_coarse_diff_nsec monotime_diff_nsec #define monotime_coarse_diff_usec monotime_diff_usec #define monotime_coarse_diff_msec monotime_diff_msec +#define monotime_coarse_zero monotime_zero +#define monotime_coarse_is_zero monotime_is_zero +#define monotime_coarse_add_msec monotime_add_msec #endif /* defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT) */ void tor_gettimeofday(struct timeval *timeval); diff --git a/src/common/compat_winthreads.c b/src/common/compat_winthreads.c index 50a3c498ca..5f7ec94c23 100644 --- a/src/common/compat_winthreads.c +++ b/src/common/compat_winthreads.c @@ -48,10 +48,12 @@ void spawn_exit(void) { _endthread(); + // LCOV_EXCL_START //we should never get here. my compiler thinks that _endthread returns, this //is an attempt to fool it. tor_assert(0); - _exit(0); + _exit(0); // exit ok: unreachable. + // LCOV_EXCL_STOP } void diff --git a/src/common/compress.c b/src/common/compress.c index bc12a58ad6..47c93cf6a9 100644 --- a/src/common/compress.c +++ b/src/common/compress.c @@ -598,7 +598,7 @@ tor_compress_process(tor_compress_state_t *state, /** Deallocate <b>state</b>. */ void -tor_compress_free(tor_compress_state_t *state) +tor_compress_free_(tor_compress_state_t *state) { if (state == NULL) return; diff --git a/src/common/compress.h b/src/common/compress.h index 23a9817479..952102bf97 100644 --- a/src/common/compress.h +++ b/src/common/compress.h @@ -80,7 +80,9 @@ tor_compress_output_t tor_compress_process(tor_compress_state_t *state, char **out, size_t *out_len, const char **in, size_t *in_len, int finish); -void tor_compress_free(tor_compress_state_t *state); +void tor_compress_free_(tor_compress_state_t *state); +#define tor_compress_free(st) \ + FREE_AND_NULL(tor_compress_state_t, tor_compress_free_, (st)) size_t tor_compress_state_size(const tor_compress_state_t *state); diff --git a/src/common/compress_lzma.c b/src/common/compress_lzma.c index 6426ede4fd..051c59ba2d 100644 --- a/src/common/compress_lzma.c +++ b/src/common/compress_lzma.c @@ -323,7 +323,7 @@ tor_lzma_compress_process(tor_lzma_compress_state_t *state, /** Deallocate <b>state</b>. */ void -tor_lzma_compress_free(tor_lzma_compress_state_t *state) +tor_lzma_compress_free_(tor_lzma_compress_state_t *state) { if (state == NULL) return; diff --git a/src/common/compress_lzma.h b/src/common/compress_lzma.h index 7639d98a70..38a447c1f3 100644 --- a/src/common/compress_lzma.h +++ b/src/common/compress_lzma.h @@ -31,7 +31,10 @@ tor_lzma_compress_process(tor_lzma_compress_state_t *state, const char **in, size_t *in_len, int finish); -void tor_lzma_compress_free(tor_lzma_compress_state_t *state); +void tor_lzma_compress_free_(tor_lzma_compress_state_t *state); +#define tor_lzma_compress_free(st) \ + FREE_AND_NULL(tor_lzma_compress_state_t, \ + tor_lzma_compress_free_, (st)) size_t tor_lzma_compress_state_size(const tor_lzma_compress_state_t *state); diff --git a/src/common/compress_zlib.c b/src/common/compress_zlib.c index 284542e885..23d71d27be 100644 --- a/src/common/compress_zlib.c +++ b/src/common/compress_zlib.c @@ -265,7 +265,7 @@ tor_zlib_compress_process(tor_zlib_compress_state_t *state, /** Deallocate <b>state</b>. */ void -tor_zlib_compress_free(tor_zlib_compress_state_t *state) +tor_zlib_compress_free_(tor_zlib_compress_state_t *state) { if (state == NULL) return; diff --git a/src/common/compress_zlib.h b/src/common/compress_zlib.h index 8ace467bf0..e3c1a2b339 100644 --- a/src/common/compress_zlib.h +++ b/src/common/compress_zlib.h @@ -31,7 +31,10 @@ tor_zlib_compress_process(tor_zlib_compress_state_t *state, const char **in, size_t *in_len, int finish); -void tor_zlib_compress_free(tor_zlib_compress_state_t *state); +void tor_zlib_compress_free_(tor_zlib_compress_state_t *state); +#define tor_zlib_compress_free(st) \ + FREE_AND_NULL(tor_zlib_compress_state_t, \ + tor_zlib_compress_free_, (st)) size_t tor_zlib_compress_state_size(const tor_zlib_compress_state_t *state); diff --git a/src/common/compress_zstd.c b/src/common/compress_zstd.c index c1cdaf17ad..0db87d61b7 100644 --- a/src/common/compress_zstd.c +++ b/src/common/compress_zstd.c @@ -399,7 +399,7 @@ tor_zstd_compress_process(tor_zstd_compress_state_t *state, /** Deallocate <b>state</b>. */ void -tor_zstd_compress_free(tor_zstd_compress_state_t *state) +tor_zstd_compress_free_(tor_zstd_compress_state_t *state) { if (state == NULL) return; diff --git a/src/common/compress_zstd.h b/src/common/compress_zstd.h index 02466010ff..9bca24ded7 100644 --- a/src/common/compress_zstd.h +++ b/src/common/compress_zstd.h @@ -31,7 +31,10 @@ tor_zstd_compress_process(tor_zstd_compress_state_t *state, const char **in, size_t *in_len, int finish); -void tor_zstd_compress_free(tor_zstd_compress_state_t *state); +void tor_zstd_compress_free_(tor_zstd_compress_state_t *state); +#define tor_zstd_compress_free(st) \ + FREE_AND_NULL(tor_zstd_compress_state_t, \ + tor_zstd_compress_free_, (st)) size_t tor_zstd_compress_state_size(const tor_zstd_compress_state_t *state); diff --git a/src/common/confline.c b/src/common/confline.c index 04545bc2c3..bf613ab742 100644 --- a/src/common/confline.c +++ b/src/common/confline.c @@ -12,15 +12,18 @@ static int config_get_lines_aux(const char *string, config_line_t **result, int extended, int allow_include, - int *has_include, int recursion_level, - config_line_t **last); -static smartlist_t *config_get_file_list(const char *path); -static int config_get_included_list(const char *path, int recursion_level, - int extended, config_line_t **list, - config_line_t **list_last); + int *has_include, smartlist_t *opened_lst, + int recursion_level, config_line_t **last); +static smartlist_t *config_get_file_list(const char *path, + smartlist_t *opened_files); +static int config_get_included_config(const char *path, int recursion_level, + int extended, config_line_t **config, + config_line_t **config_last, + smartlist_t *opened_lst); static int config_process_include(const char *path, int recursion_level, int extended, config_line_t **list, - config_line_t **list_last); + config_line_t **list_last, + smartlist_t *opened_lst); /** Helper: allocate a new configuration option mapping 'key' to 'val', * append it to *<b>lst</b>. */ @@ -80,11 +83,13 @@ config_line_find(const config_line_t *lines, /** Auxiliary function that does all the work of config_get_lines. * <b>recursion_level</b> is the count of how many nested %includes we have. + * <b>opened_lst</b> will have a list of opened files if provided. * Returns the a pointer to the last element of the <b>result</b> in * <b>last</b>. */ static int config_get_lines_aux(const char *string, config_line_t **result, int extended, - int allow_include, int *has_include, int recursion_level, + int allow_include, int *has_include, + smartlist_t *opened_lst, int recursion_level, config_line_t **last) { config_line_t *list = NULL, **next, *list_last = NULL; @@ -134,7 +139,7 @@ config_get_lines_aux(const char *string, config_line_t **result, int extended, config_line_t *include_list; if (config_process_include(v, recursion_level, extended, &include_list, - &list_last) < 0) { + &list_last, opened_lst) < 0) { log_warn(LD_CONFIG, "Error reading included configuration " "file or directory: \"%s\".", v); config_free_lines(list); @@ -176,24 +181,27 @@ config_get_lines_aux(const char *string, config_line_t **result, int extended, /** Helper: parse the config string and strdup into key/value * strings. Set *result to the list, or NULL if parsing the string * failed. Set *has_include to 1 if <b>result</b> has values from - * %included files. Return 0 on success, -1 on failure. Warn and ignore any + * %included files. <b>opened_lst</b> will have a list of opened files if + * provided. Return 0 on success, -1 on failure. Warn and ignore any * misformatted lines. * * If <b>extended</b> is set, then treat keys beginning with / and with + as * indicating "clear" and "append" respectively. */ int config_get_lines_include(const char *string, config_line_t **result, - int extended, int *has_include) + int extended, int *has_include, + smartlist_t *opened_lst) { - return config_get_lines_aux(string, result, extended, 1, has_include, 1, - NULL); + return config_get_lines_aux(string, result, extended, 1, has_include, + opened_lst, 1, NULL); } /** Same as config_get_lines_include but does not allow %include */ int config_get_lines(const char *string, config_line_t **result, int extended) { - return config_get_lines_aux(string, result, extended, 0, NULL, 1, NULL); + return config_get_lines_aux(string, result, extended, 0, NULL, NULL, 1, + NULL); } /** Adds a list of configuration files present on <b>path</b> to @@ -201,12 +209,18 @@ config_get_lines(const char *string, config_line_t **result, int extended) * only that file will be added to <b>file_list</b>. If it is a directory, * all paths for files on that directory root (no recursion) except for files * whose name starts with a dot will be added to <b>file_list</b>. - * Return 0 on success, -1 on failure. Ignores empty files. + * <b>opened_files</b> will have a list of files opened by this function + * if provided. Return 0 on success, -1 on failure. Ignores empty files. */ static smartlist_t * -config_get_file_list(const char *path) +config_get_file_list(const char *path, smartlist_t *opened_files) { smartlist_t *file_list = smartlist_new(); + + if (opened_files) { + smartlist_add_strdup(opened_files, path); + } + file_status_t file_type = file_status(path); if (file_type == FN_FILE) { smartlist_add_strdup(file_list, path); @@ -228,6 +242,10 @@ config_get_file_list(const char *path) tor_asprintf(&fullname, "%s"PATH_SEPARATOR"%s", path, f); tor_free(f); + if (opened_files) { + smartlist_add_strdup(opened_files, fullname); + } + if (file_status(fullname) != FN_FILE) { tor_free(fullname); continue; @@ -245,19 +263,21 @@ config_get_file_list(const char *path) } /** Creates a list of config lines present on included <b>path</b>. - * Set <b>list</b> to the list and <b>list_last</b> to the last element of - * <b>list</b>. Return 0 on success, -1 on failure. */ + * Set <b>config</b> to the list and <b>config_last</b> to the last element of + * <b>config</b>. <b>opened_lst</b> will have a list of opened files if + * provided. Return 0 on success, -1 on failure. */ static int -config_get_included_list(const char *path, int recursion_level, int extended, - config_line_t **list, config_line_t **list_last) +config_get_included_config(const char *path, int recursion_level, int extended, + config_line_t **config, config_line_t **config_last, + smartlist_t *opened_lst) { char *included_conf = read_file_to_str(path, 0, NULL); if (!included_conf) { return -1; } - if (config_get_lines_aux(included_conf, list, extended, 1, NULL, - recursion_level+1, list_last) < 0) { + if (config_get_lines_aux(included_conf, config, extended, 1, NULL, + opened_lst, recursion_level+1, config_last) < 0) { tor_free(included_conf); return -1; } @@ -268,41 +288,31 @@ config_get_included_list(const char *path, int recursion_level, int extended, /** Process an %include <b>path</b> in a config file. Set <b>list</b> to the * list of configuration settings obtained and <b>list_last</b> to the last - * element of the same list. Return 0 on success, -1 on failure. */ + * element of the same list. <b>opened_lst</b> will have a list of opened + * files if provided. Return 0 on success, -1 on failure. */ static int config_process_include(const char *path, int recursion_level, int extended, - config_line_t **list, config_line_t **list_last) + config_line_t **list, config_line_t **list_last, + smartlist_t *opened_lst) { config_line_t *ret_list = NULL; config_line_t **next = &ret_list; -#if 0 - // Disabled -- we already unescape_string() on the result. */ - char *unquoted_path = get_unquoted_path(path); - if (!unquoted_path) { - return -1; - } - smartlist_t *config_files = config_get_file_list(unquoted_path); - if (!config_files) { - tor_free(unquoted_path); - return -1; - } - tor_free(unquoted_path); -#endif /* 0 */ - smartlist_t *config_files = config_get_file_list(path); + smartlist_t *config_files = config_get_file_list(path, opened_lst); if (!config_files) { return -1; } int rv = -1; SMARTLIST_FOREACH_BEGIN(config_files, const char *, config_file) { - config_line_t *included_list = NULL; - if (config_get_included_list(config_file, recursion_level, extended, - &included_list, list_last) < 0) { + config_line_t *included_config = NULL; + if (config_get_included_config(config_file, recursion_level, extended, + &included_config, list_last, + opened_lst) < 0) { goto done; } - *next = included_list; + *next = included_config; if (*list_last) next = &(*list_last)->next; @@ -320,7 +330,7 @@ config_process_include(const char *path, int recursion_level, int extended, * Free all the configuration lines on the linked list <b>front</b>. */ void -config_free_lines(config_line_t *front) +config_free_lines_(config_line_t *front) { config_line_t *tmp; diff --git a/src/common/confline.h b/src/common/confline.h index 8256326f2d..772a9bbbdc 100644 --- a/src/common/confline.h +++ b/src/common/confline.h @@ -7,6 +7,8 @@ #ifndef TOR_CONFLINE_H #define TOR_CONFLINE_H +#include "container.h" + /** Ordinary configuration line. */ #define CONFIG_LINE_NORMAL 0 /** Appends to previous configuration for the same option, even if we @@ -44,8 +46,14 @@ int config_lines_eq(config_line_t *a, config_line_t *b); int config_count_key(const config_line_t *a, const char *key); int config_get_lines(const char *string, config_line_t **result, int extended); int config_get_lines_include(const char *string, config_line_t **result, - int extended, int *has_include); -void config_free_lines(config_line_t *front); + int extended, int *has_include, + smartlist_t *opened_lst); +void config_free_lines_(config_line_t *front); +#define config_free_lines(front) \ + do { \ + config_free_lines_(front); \ + (front) = NULL; \ + } while (0) const char *parse_config_line_from_str_verbose(const char *line, char **key_out, char **value_out, const char **err_out); diff --git a/src/common/container.c b/src/common/container.c index 8645cb4826..54b0b2028f 100644 --- a/src/common/container.c +++ b/src/common/container.c @@ -42,7 +42,7 @@ smartlist_new,(void)) * list's elements. */ MOCK_IMPL(void, -smartlist_free,(smartlist_t *sl)) +smartlist_free_,(smartlist_t *sl)) { if (!sl) return; @@ -1163,19 +1163,26 @@ HT_GENERATE2(digest256map_impl, digest256map_entry_t, node, digest256map_entry_hash, digest256map_entries_eq, 0.6, tor_reallocarray_, tor_free_) +#define strmap_entry_free(ent) \ + FREE_AND_NULL(strmap_entry_t, strmap_entry_free_, (ent)) +#define digestmap_entry_free(ent) \ + FREE_AND_NULL(digestmap_entry_t, digestmap_entry_free_, (ent)) +#define digest256map_entry_free(ent) \ + FREE_AND_NULL(digest256map_entry_t, digest256map_entry_free_, (ent)) + static inline void -strmap_entry_free(strmap_entry_t *ent) +strmap_entry_free_(strmap_entry_t *ent) { tor_free(ent->key); tor_free(ent); } static inline void -digestmap_entry_free(digestmap_entry_t *ent) +digestmap_entry_free_(digestmap_entry_t *ent) { tor_free(ent); } static inline void -digest256map_entry_free(digest256map_entry_t *ent) +digest256map_entry_free_(digest256map_entry_t *ent) { tor_free(ent); } @@ -1335,7 +1342,7 @@ digest256map_assign_key(digest256map_entry_t *ent, const uint8_t *key) * those entries. If free_val is provided, invoked it every value in \ * <b>map</b>. */ \ MOCK_IMPL(void, \ - prefix##_free, (maptype *map, void (*free_val)(void*))) \ + prefix##_free_, (maptype *map, void (*free_val)(void*))) \ { \ prefix##_entry_t **ent, **next, *this; \ if (!map) \ @@ -1525,7 +1532,7 @@ digestset_new(int max_elements) /** Free all storage held in <b>set</b>. */ void -digestset_free(digestset_t *set) +digestset_free_(digestset_t *set) { if (!set) return; diff --git a/src/common/container.h b/src/common/container.h index f6affd3bc6..5d2dce5416 100644 --- a/src/common/container.h +++ b/src/common/container.h @@ -28,7 +28,9 @@ typedef struct smartlist_t { } smartlist_t; MOCK_DECL(smartlist_t *, smartlist_new, (void)); -MOCK_DECL(void, smartlist_free, (smartlist_t *sl)); +MOCK_DECL(void, smartlist_free_, (smartlist_t *sl)); +#define smartlist_free(sl) FREE_AND_NULL(smartlist_t, smartlist_free_, (sl)) + void smartlist_clear(smartlist_t *sl); void smartlist_add(smartlist_t *sl, void *element); void smartlist_add_all(smartlist_t *sl, const smartlist_t *s2); @@ -350,7 +352,7 @@ char *smartlist_join_strings2(smartlist_t *sl, const char *join, void* prefix##set(maptype *map, keytype key, void *val); \ void* prefix##get(const maptype *map, keytype key); \ void* prefix##remove(maptype *map, keytype key); \ - MOCK_DECL(void, prefix##free, (maptype *map, void (*free_val)(void*))); \ + MOCK_DECL(void, prefix##free_, (maptype *map, void (*free_val)(void*))); \ int prefix##isempty(const maptype *map); \ int prefix##size(const maptype *map); \ prefix##iter_t *prefix##iter_init(maptype *map); \ @@ -368,6 +370,16 @@ DECLARE_MAP_FNS(digestmap_t, const char *, digestmap_); * table. */ DECLARE_MAP_FNS(digest256map_t, const uint8_t *, digest256map_); +#define MAP_FREE_AND_NULL(maptype, map, fn) \ + do { \ + maptype ## _free_((map), (fn)); \ + (map) = NULL; \ + } while (0) + +#define strmap_free(map, fn) MAP_FREE_AND_NULL(strmap, (map), (fn)) +#define digestmap_free(map, fn) MAP_FREE_AND_NULL(digestmap, (map), (fn)) +#define digest256map_free(map, fn) MAP_FREE_AND_NULL(digest256map, (map), (fn)) + #undef DECLARE_MAP_FNS /** Iterates over the key-value pairs in a map <b>map</b> in order. @@ -528,9 +540,9 @@ void* strmap_remove_lc(strmap_t *map, const char *key); return (valtype*)digestmap_remove((digestmap_t*)map, key); \ } \ ATTR_UNUSED static inline void \ - prefix##f##ree(maptype *map, void (*free_val)(void*)) \ + prefix##f##ree_(maptype *map, void (*free_val)(void*)) \ { \ - digestmap_free((digestmap_t*)map, free_val); \ + digestmap_free_((digestmap_t*)map, free_val); \ } \ ATTR_UNUSED static inline int \ prefix##isempty(maptype *map) \ @@ -614,10 +626,12 @@ bitarray_expand(bitarray_t *ba, } /** Free the bit array <b>ba</b>. */ static inline void -bitarray_free(bitarray_t *ba) +bitarray_free_(bitarray_t *ba) { tor_free(ba); } +#define bitarray_free(ba) FREE_AND_NULL(bitarray_t, bitarray_free_, (ba)) + /** Set the <b>bit</b>th bit in <b>b</b> to 1. */ static inline void bitarray_set(bitarray_t *b, int bit) @@ -679,7 +693,8 @@ digestset_contains(const digestset_t *set, const char *digest) #undef BIT digestset_t *digestset_new(int max_elements); -void digestset_free(digestset_t* set); +void digestset_free_(digestset_t* set); +#define digestset_free(set) FREE_AND_NULL(digestset_t, digestset_free_, (set)) /* These functions, given an <b>array</b> of <b>n_elements</b>, return the * <b>nth</b> lowest element. <b>nth</b>=0 gives the lowest element; diff --git a/src/common/crypto.c b/src/common/crypto.c index 380f038f42..575bfd0c06 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -529,7 +529,7 @@ crypto_pk_new,(void)) * are released, free the key. */ void -crypto_pk_free(crypto_pk_t *env) +crypto_pk_free_(crypto_pk_t *env) { if (!env) return; @@ -592,7 +592,7 @@ crypto_cipher_new(const char *key) /** Free a symmetric cipher. */ void -crypto_cipher_free(crypto_cipher_t *env) +crypto_cipher_free_(crypto_cipher_t *env) { if (!env) return; @@ -1977,7 +1977,7 @@ crypto_digest512_new(digest_algorithm_t algorithm) /** Deallocate a digest object. */ void -crypto_digest_free(crypto_digest_t *digest) +crypto_digest_free_(crypto_digest_t *digest) { if (!digest) return; @@ -2224,7 +2224,7 @@ crypto_xof_squeeze_bytes(crypto_xof_t *xof, uint8_t *out, size_t len) /** Cleanse and deallocate a XOF object. */ void -crypto_xof_free(crypto_xof_t *xof) +crypto_xof_free_(crypto_xof_t *xof) { if (!xof) return; @@ -2777,7 +2777,7 @@ crypto_expand_key_material_rfc5869_sha256( /** Free a DH key exchange object. */ void -crypto_dh_free(crypto_dh_t *dh) +crypto_dh_free_(crypto_dh_t *dh) { if (!dh) return; diff --git a/src/common/crypto.h b/src/common/crypto.h index f9aeeee2c0..eca115fa79 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -19,6 +19,7 @@ #include "torint.h" #include "testsupport.h" #include "compat.h" +#include "util.h" #include <openssl/engine.h> #include "keccak-tiny/keccak-tiny.h" @@ -146,7 +147,8 @@ int crypto_global_cleanup(void); /* environment setup */ MOCK_DECL(crypto_pk_t *,crypto_pk_new,(void)); -void crypto_pk_free(crypto_pk_t *env); +void crypto_pk_free_(crypto_pk_t *env); +#define crypto_pk_free(pk) FREE_AND_NULL(crypto_pk_t, crypto_pk_free_, (pk)) void crypto_set_tls_dh_prime(void); crypto_cipher_t *crypto_cipher_new(const char *key); @@ -155,7 +157,9 @@ crypto_cipher_t *crypto_cipher_new_with_iv(const char *key, const char *iv); crypto_cipher_t *crypto_cipher_new_with_iv_and_bits(const uint8_t *key, const uint8_t *iv, int bits); -void crypto_cipher_free(crypto_cipher_t *env); +void crypto_cipher_free_(crypto_cipher_t *env); +#define crypto_cipher_free(c) \ + FREE_AND_NULL(crypto_cipher_t, crypto_cipher_free_, (c)) /* public key crypto */ MOCK_DECL(int, crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits)); @@ -258,7 +262,9 @@ int crypto_digest_algorithm_parse_name(const char *name); crypto_digest_t *crypto_digest_new(void); crypto_digest_t *crypto_digest256_new(digest_algorithm_t algorithm); crypto_digest_t *crypto_digest512_new(digest_algorithm_t algorithm); -void crypto_digest_free(crypto_digest_t *digest); +void crypto_digest_free_(crypto_digest_t *digest); +#define crypto_digest_free(d) \ + FREE_AND_NULL(crypto_digest_t, crypto_digest_free_, (d)) void crypto_digest_add_bytes(crypto_digest_t *digest, const char *data, size_t len); void crypto_digest_get_digest(crypto_digest_t *digest, @@ -276,7 +282,9 @@ void crypto_mac_sha3_256(uint8_t *mac_out, size_t len_out, crypto_xof_t *crypto_xof_new(void); void crypto_xof_add_bytes(crypto_xof_t *xof, const uint8_t *data, size_t len); void crypto_xof_squeeze_bytes(crypto_xof_t *xof, uint8_t *out, size_t len); -void crypto_xof_free(crypto_xof_t *xof); +void crypto_xof_free_(crypto_xof_t *xof); +#define crypto_xof_free(xof) \ + FREE_AND_NULL(crypto_xof_t, crypto_xof_free_, (xof)) /* Key negotiation */ #define DH_TYPE_CIRCUIT 1 @@ -291,7 +299,8 @@ int crypto_dh_get_public(crypto_dh_t *dh, char *pubkey_out, ssize_t crypto_dh_compute_secret(int severity, crypto_dh_t *dh, const char *pubkey, size_t pubkey_len, char *secret_out, size_t secret_out_len); -void crypto_dh_free(crypto_dh_t *dh); +void crypto_dh_free_(crypto_dh_t *dh); +#define crypto_dh_free(dh) FREE_AND_NULL(crypto_dh_t, crypto_dh_free_, (dh)) int crypto_expand_key_material_TAP(const uint8_t *key_in, size_t key_in_len, diff --git a/src/common/crypto_ed25519.c b/src/common/crypto_ed25519.c index 94b23e31b9..26523e3126 100644 --- a/src/common/crypto_ed25519.c +++ b/src/common/crypto_ed25519.c @@ -622,7 +622,7 @@ ed25519_pubkey_read_from_file(ed25519_public_key_t *pubkey_out, /** Release all storage held for <b>kp</b>. */ void -ed25519_keypair_free(ed25519_keypair_t *kp) +ed25519_keypair_free_(ed25519_keypair_t *kp) { if (! kp) return; diff --git a/src/common/crypto_ed25519.h b/src/common/crypto_ed25519.h index 8d13a487d6..74269ccffd 100644 --- a/src/common/crypto_ed25519.h +++ b/src/common/crypto_ed25519.h @@ -7,6 +7,7 @@ #include "testsupport.h" #include "torint.h" #include "crypto_curve25519.h" +#include "util.h" #define ED25519_PUBKEY_LEN 32 #define ED25519_SECKEY_LEN 64 @@ -117,7 +118,9 @@ int ed25519_pubkey_read_from_file(ed25519_public_key_t *pubkey_out, char **tag_out, const char *filename); -void ed25519_keypair_free(ed25519_keypair_t *kp); +void ed25519_keypair_free_(ed25519_keypair_t *kp); +#define ed25519_keypair_free(kp) \ + FREE_AND_NULL(ed25519_keypair_t, ed25519_keypair_free_, (kp)) int ed25519_pubkey_eq(const ed25519_public_key_t *key1, const ed25519_public_key_t *key2); diff --git a/src/common/crypto_pwbox.h b/src/common/crypto_pwbox.h index cee8653587..a26b6d2c17 100644 --- a/src/common/crypto_pwbox.h +++ b/src/common/crypto_pwbox.h @@ -1,3 +1,6 @@ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + #ifndef CRYPTO_PWBOX_H_INCLUDED_ #define CRYPTO_PWBOX_H_INCLUDED_ diff --git a/src/common/di_ops.c b/src/common/di_ops.c index 7c0b4e7630..90e9357c8e 100644 --- a/src/common/di_ops.c +++ b/src/common/di_ops.c @@ -148,7 +148,7 @@ struct di_digest256_map_t { /** Release all storage held in <b>map</b>, calling free_fn on each value * as we go. */ void -dimap_free(di_digest256_map_t *map, dimap_free_fn free_fn) +dimap_free_(di_digest256_map_t *map, dimap_free_fn free_fn) { while (map) { di_digest256_map_t *victim = map; diff --git a/src/common/di_ops.h b/src/common/di_ops.h index e79973ba52..67d9c9f0df 100644 --- a/src/common/di_ops.h +++ b/src/common/di_ops.h @@ -37,7 +37,12 @@ int safe_mem_is_zero(const void *mem, size_t sz); typedef struct di_digest256_map_t di_digest256_map_t; typedef void (*dimap_free_fn)(void *); -void dimap_free(di_digest256_map_t *map, dimap_free_fn free_fn); +void dimap_free_(di_digest256_map_t *map, dimap_free_fn free_fn); +#define dimap_free(map, free_fn) \ + do { \ + dimap_free_((map), (free_fn)); \ + (map) = NULL; \ + } while (0) void dimap_add_entry(di_digest256_map_t **map, const uint8_t *key, void *val); void *dimap_search(const di_digest256_map_t *map, const uint8_t *key, diff --git a/src/common/handles.h b/src/common/handles.h index a610753a1c..aef8cd89ef 100644 --- a/src/common/handles.h +++ b/src/common/handles.h @@ -59,7 +59,7 @@ #define HANDLE_DECL(name, structname, linkage) \ typedef struct name ## _handle_t name ## _handle_t; \ linkage name ## _handle_t *name ## _handle_new(struct structname *object); \ - linkage void name ## _handle_free(name ## _handle_t *); \ + linkage void name ## _handle_free_(name ## _handle_t *); \ linkage struct structname *name ## _handle_get(name ## _handle_t *); \ linkage void name ## _handles_clear(struct structname *object); @@ -113,7 +113,7 @@ } \ \ linkage void \ - name ## _handle_free(struct name ## _handle_t *ref) \ + name ## _handle_free_(struct name ## _handle_t *ref) \ { \ if (! ref) return; \ name ## _handle_head_t *head = ref->head; \ diff --git a/src/common/include.am b/src/common/include.am index cd5eea3404..2856c40fdc 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -101,11 +101,6 @@ LIBOR_A_SRC = \ $(threads_impl_source) \ $(readpassphrase_source) -if USE_RUST -else -LIBOR_A_SRC += src/common/compat_rust.c -endif - src/common/src_common_libor_testing_a-log.$(OBJEXT) \ src/common/log.$(OBJEXT): micro-revision.i @@ -156,7 +151,6 @@ COMMONHEADERS = \ src/common/compat.h \ src/common/compat_libevent.h \ src/common/compat_openssl.h \ - src/common/compat_rust.h \ src/common/compat_threads.h \ src/common/compat_time.h \ src/common/compress.h \ diff --git a/src/common/log.c b/src/common/log.c index 0becb5ce03..ac6d07a929 100644 --- a/src/common/log.c +++ b/src/common/log.c @@ -35,6 +35,9 @@ #define LOG_PRIVATE #include "torlog.h" #include "container.h" +#ifdef HAVE_ANDROID_LOG_H +#include <android/log.h> +#endif // HAVE_ANDROID_LOG_H. /** Given a severity, yields an index into log_severity_list_t.masks to use * for that severity. */ @@ -58,12 +61,16 @@ typedef struct logfile_t { int needs_close; /**< Boolean: true if the stream gets closed on shutdown. */ int is_temporary; /**< Boolean: close after initializing logging subsystem.*/ int is_syslog; /**< Boolean: send messages to syslog. */ + int is_android; /**< Boolean: send messages to Android's log subsystem. */ + char *android_tag; /**< Identity Tag used in Android's log subsystem. */ log_callback callback; /**< If not NULL, send messages to this function. */ log_severity_list_t *severities; /**< Which severity of messages should we * log for each log domain? */ } logfile_t; -static void log_free(logfile_t *victim); +static void log_free_(logfile_t *victim); +#define log_free(lg) \ + FREE_AND_NULL(logfile_t, log_free_, (lg)) /** Helper: map a log severity to descriptive string. */ static inline const char * @@ -101,6 +108,33 @@ should_log_function_name(log_domain_mask_t domain, int severity) } } +#ifdef HAVE_ANDROID_LOG_H +/** Helper function to convert Tor's log severity into the matching + * Android log priority. + */ +static int +severity_to_android_log_priority(int severity) +{ + switch (severity) { + case LOG_DEBUG: + return ANDROID_LOG_VERBOSE; + case LOG_INFO: + return ANDROID_LOG_DEBUG; + case LOG_NOTICE: + return ANDROID_LOG_INFO; + case LOG_WARN: + return ANDROID_LOG_WARN; + case LOG_ERR: + return ANDROID_LOG_ERROR; + default: + // LCOV_EXCL_START + raw_assert(0); + return 0; + // LCOV_EXCL_STOP + } +} +#endif // HAVE_ANDROID_LOG_H. + /** A mutex to guard changes to logfiles and logging. */ static tor_mutex_t log_mutex; /** True iff we have initialized log_mutex */ @@ -385,9 +419,12 @@ pending_log_message_new(int severity, log_domain_mask_t domain, return m; } +#define pending_log_message_free(msg) \ + FREE_AND_NULL(pending_log_message_t, pending_log_message_free_, (msg)) + /** Release all storage held by <b>msg</b>. */ static void -pending_log_message_free(pending_log_message_t *msg) +pending_log_message_free_(pending_log_message_t *msg) { if (!msg) return; @@ -396,6 +433,16 @@ pending_log_message_free(pending_log_message_t *msg) tor_free(msg); } +/** Helper function: returns true iff the log file, given in <b>lf</b>, is + * handled externally via the system log API, the Android logging API, or is an + * external callback function. */ +static inline int +logfile_is_external(const logfile_t *lf) +{ + raw_assert(lf); + return lf->is_syslog || lf->is_android || lf->callback; +} + /** Return true iff <b>lf</b> would like to receive a message with the * specified <b>severity</b> in the specified <b>domain</b>. */ @@ -406,7 +453,7 @@ logfile_wants_message(const logfile_t *lf, int severity, if (! (lf->severities->masks[SEVERITY_MASK_IDX(severity)] & domain)) { return 0; } - if (! (lf->fd >= 0 || lf->is_syslog || lf->callback)) { + if (! (lf->fd >= 0 || logfile_is_external(lf))) { return 0; } if (lf->seems_dead) { @@ -449,6 +496,11 @@ logfile_deliver(logfile_t *lf, const char *buf, size_t msg_len, syslog(severity, "%s", msg_after_prefix); #endif /* defined(MAXLINE) */ #endif /* defined(HAVE_SYSLOG_H) */ + } else if (lf->is_android) { +#ifdef HAVE_ANDROID_LOG_H + int priority = severity_to_android_log_priority(severity); + __android_log_write(priority, lf->android_tag, msg_after_prefix); +#endif // HAVE_ANDROID_LOG_H. } else if (lf->callback) { if (domain & LD_NOCB) { if (!*callbacks_deferred && pending_cb_messages) { @@ -641,8 +693,8 @@ tor_log_update_sigsafe_err_fds(void) /* Don't try callback to the control port, or syslogs: We can't * do them from a signal handler. Don't try stdout: we always do stderr. */ - if (lf->is_temporary || lf->is_syslog || - lf->callback || lf->seems_dead || lf->fd < 0) + if (lf->is_temporary || logfile_is_external(lf) + || lf->seems_dead || lf->fd < 0) continue; if (lf->severities->masks[SEVERITY_MASK_IDX(LOG_ERR)] & (LD_BUG|LD_GENERAL)) { @@ -678,7 +730,7 @@ tor_log_get_logfile_names(smartlist_t *out) LOCK_LOGS(); for (lf = logfiles; lf; lf = lf->next) { - if (lf->is_temporary || lf->is_syslog || lf->callback) + if (lf->is_temporary || logfile_is_external(lf)) continue; if (lf->filename == NULL) continue; @@ -721,12 +773,13 @@ log_fn_ratelim_(ratelim_t *ratelim, int severity, log_domain_mask_t domain, /** Free all storage held by <b>victim</b>. */ static void -log_free(logfile_t *victim) +log_free_(logfile_t *victim) { if (!victim) return; tor_free(victim->severities); tor_free(victim->filename); + tor_free(victim->android_tag); tor_free(victim); } @@ -1146,6 +1199,39 @@ add_syslog_log(const log_severity_list_t *severity, } #endif /* defined(HAVE_SYSLOG_H) */ +#ifdef HAVE_ANDROID_LOG_H +/** + * Add a log handler to send messages to the Android platform log facility. + */ +int +add_android_log(const log_severity_list_t *severity, + const char *android_tag) +{ + logfile_t *lf = NULL; + + lf = tor_malloc_zero(sizeof(logfile_t)); + lf->fd = -1; + lf->severities = tor_memdup(severity, sizeof(log_severity_list_t)); + lf->filename = tor_strdup("<android>"); + lf->is_android = 1; + + if (android_tag == NULL) + lf->android_tag = tor_strdup("Tor"); + else { + char buf[256]; + tor_snprintf(buf, sizeof(buf), "Tor-%s", android_tag); + lf->android_tag = tor_strdup(buf); + } + + LOCK_LOGS(); + lf->next = logfiles; + logfiles = lf; + log_global_min_severity_ = get_min_log_level(); + UNLOCK_LOGS(); + return 0; +} +#endif // HAVE_ANDROID_LOG_H. + /** If <b>level</b> is a valid log severity, return the corresponding * numeric value. Otherwise, return -1. */ int @@ -1313,7 +1399,8 @@ parse_log_severity_config(const char **cfg_ptr, if (!strcasecmpstart(cfg, "file") || !strcasecmpstart(cfg, "stderr") || !strcasecmpstart(cfg, "stdout") || - !strcasecmpstart(cfg, "syslog")) { + !strcasecmpstart(cfg, "syslog") || + !strcasecmpstart(cfg, "android")) { goto done; } if (got_an_unqualified_range > 1) diff --git a/src/common/memarea.c b/src/common/memarea.c index b059987e0e..68c1625fe4 100644 --- a/src/common/memarea.c +++ b/src/common/memarea.c @@ -153,7 +153,7 @@ memarea_new(void) /** Free <b>area</b>, invalidating all pointers returned from memarea_alloc() * and friends for this area */ void -memarea_drop_all(memarea_t *area) +memarea_drop_all_(memarea_t *area) { memarea_chunk_t *chunk, *next; for (chunk = area->first; chunk; chunk = next) { @@ -322,7 +322,7 @@ memarea_new(void) return ma; } void -memarea_drop_all(memarea_t *area) +memarea_drop_all_(memarea_t *area) { memarea_clear(area); smartlist_free(area->pieces); diff --git a/src/common/memarea.h b/src/common/memarea.h index c3d954e1ce..5207e8a5bd 100644 --- a/src/common/memarea.h +++ b/src/common/memarea.h @@ -8,7 +8,12 @@ typedef struct memarea_t memarea_t; memarea_t *memarea_new(void); -void memarea_drop_all(memarea_t *area); +void memarea_drop_all_(memarea_t *area); +#define memarea_drop_all(area) \ + do { \ + memarea_drop_all_(area); \ + (area) = NULL; \ + } while (0) void memarea_clear(memarea_t *area); int memarea_owns_ptr(const memarea_t *area, const void *ptr); void *memarea_alloc(memarea_t *area, size_t sz); diff --git a/src/common/procmon.c b/src/common/procmon.c index 26c11823e8..abcbbeaa21 100644 --- a/src/common/procmon.c +++ b/src/common/procmon.c @@ -325,7 +325,7 @@ tor_process_monitor_poll_cb(evutil_socket_t unused1, short unused2, /** Free the process-termination monitor <b>procmon</b>. */ void -tor_process_monitor_free(tor_process_monitor_t *procmon) +tor_process_monitor_free_(tor_process_monitor_t *procmon) { if (procmon == NULL) return; diff --git a/src/common/procmon.h b/src/common/procmon.h index 10ead11ba8..63777e4111 100644 --- a/src/common/procmon.h +++ b/src/common/procmon.h @@ -27,7 +27,9 @@ tor_process_monitor_t *tor_process_monitor_new(struct event_base *base, tor_procmon_callback_t cb, void *cb_arg, const char **msg); -void tor_process_monitor_free(tor_process_monitor_t *procmon); +void tor_process_monitor_free_(tor_process_monitor_t *procmon); +#define tor_process_monitor_free(procmon) \ + FREE_AND_NULL(tor_process_monitor_t, tor_process_monitor_free_, (procmon)) #endif /* !defined(TOR_PROCMON_H) */ diff --git a/src/common/sandbox.c b/src/common/sandbox.c index 8827370c24..37f582048c 100644 --- a/src/common/sandbox.c +++ b/src/common/sandbox.c @@ -162,6 +162,7 @@ static int filter_nopar_gen[] = { SCMP_SYS(fstat64), #endif SCMP_SYS(futex), + SCMP_SYS(getdents), SCMP_SYS(getdents64), SCMP_SYS(getegid), #ifdef __NR_getegid32 @@ -433,9 +434,9 @@ libc_uses_openat_for_everything(void) return 1; else return 0; -#else +#else /* !(defined(CHECK_LIBC_VERSION)) */ return 0; -#endif +#endif /* defined(CHECK_LIBC_VERSION) */ } /** Allow a single file to be opened. If <b>use_openat</b> is true, @@ -1130,7 +1131,7 @@ sb_kill(scmp_filter_ctx ctx, sandbox_cfg_t *filter) SCMP_CMP(1, SCMP_CMP_EQ, 0)); #else return 0; -#endif +#endif /* defined(__NR_kill) */ } /** @@ -1500,8 +1501,12 @@ cached_getaddrinfo_items_eq(const cached_getaddrinfo_item_t *a, return (a->family == b->family) && 0 == strcmp(a->name, b->name); } +#define cached_getaddrinfo_item_free(item) \ + FREE_AND_NULL(cached_getaddrinfo_item_t, \ + cached_getaddrinfo_item_free_, (item)) + static void -cached_getaddrinfo_item_free(cached_getaddrinfo_item_t *item) +cached_getaddrinfo_item_free_(cached_getaddrinfo_item_t *item) { if (item == NULL) return; @@ -1812,7 +1817,7 @@ sigsys_debugging(int nr, siginfo_t *info, void *void_context) #endif #if defined(DEBUGGING_CLOSE) - _exit(1); + _exit(1); // exit ok: programming error has led to sandbox failure. #endif // DEBUGGING_CLOSE } diff --git a/src/common/storagedir.c b/src/common/storagedir.c index c471ea911f..e2c7b4bb87 100644 --- a/src/common/storagedir.c +++ b/src/common/storagedir.c @@ -59,7 +59,7 @@ storage_dir_new(const char *dirname, int max_files) * Drop all in-RAM storage for <b>d</b>. Does not delete any files. */ void -storage_dir_free(storage_dir_t *d) +storage_dir_free_(storage_dir_t *d) { if (d == NULL) return; diff --git a/src/common/storagedir.h b/src/common/storagedir.h index 3de0afc361..d99bd7ec52 100644 --- a/src/common/storagedir.h +++ b/src/common/storagedir.h @@ -9,7 +9,10 @@ struct config_line_t; struct sandbox_cfg_elem; storage_dir_t * storage_dir_new(const char *dirname, int n_files); -void storage_dir_free(storage_dir_t *d); +void storage_dir_free_(storage_dir_t *d); +#define storage_dir_free(d) \ + FREE_AND_NULL(storage_dir_t, storage_dir_free_, (d)) + int storage_dir_register_with_sandbox(storage_dir_t *d, struct sandbox_cfg_elem **cfg); const smartlist_t *storage_dir_list(storage_dir_t *d); diff --git a/src/common/timers.c b/src/common/timers.c index c8e09414f4..93cde7de5f 100644 --- a/src/common/timers.c +++ b/src/common/timers.c @@ -245,7 +245,7 @@ timer_new(timer_cb_fn_t cb, void *arg) * scheduled. */ void -timer_free(tor_timer_t *t) +timer_free_(tor_timer_t *t) { if (! t) return; diff --git a/src/common/timers.h b/src/common/timers.h index d4d4fb00a9..6d27f3e01e 100644 --- a/src/common/timers.h +++ b/src/common/timers.h @@ -17,7 +17,8 @@ void timer_get_cb(const tor_timer_t *t, timer_cb_fn_t *cb_out, void **arg_out); void timer_schedule(tor_timer_t *t, const struct timeval *delay); void timer_disable(tor_timer_t *t); -void timer_free(tor_timer_t *t); +void timer_free_(tor_timer_t *t); +#define timer_free(t) FREE_AND_NULL(tor_timer_t, timer_free_, (t)) void timers_initialize(void); void timers_shutdown(void); diff --git a/src/common/torlog.h b/src/common/torlog.h index 65bf41702a..b7d033adb9 100644 --- a/src/common/torlog.h +++ b/src/common/torlog.h @@ -146,7 +146,11 @@ int add_file_log(const log_severity_list_t *severity, const char *filename, #ifdef HAVE_SYSLOG_H int add_syslog_log(const log_severity_list_t *severity, const char* syslog_identity_tag); -#endif +#endif // HAVE_SYSLOG_H. +#ifdef HAVE_ANDROID_LOG_H +int add_android_log(const log_severity_list_t *severity, + const char *android_identity_tag); +#endif // HAVE_ANDROID_LOG_H. int add_callback_log(const log_severity_list_t *severity, log_callback cb); void logs_set_domain_logging(int enabled); int get_min_log_level(void); diff --git a/src/common/tortls.c b/src/common/tortls.c index 9d2d0240a2..86a876780a 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -647,7 +647,7 @@ static const char CLIENT_CIPHER_LIST[] = /** Free all storage held in <b>cert</b> */ void -tor_x509_cert_free(tor_x509_cert_t *cert) +tor_x509_cert_free_(tor_x509_cert_t *cert) { if (! cert) return; @@ -1795,7 +1795,7 @@ tor_tls_is_server(tor_tls_t *tls) * underlying file descriptor. */ void -tor_tls_free(tor_tls_t *tls) +tor_tls_free_(tor_tls_t *tls) { if (!tls) return; diff --git a/src/common/tortls.h b/src/common/tortls.h index 6145f7dbc9..1dbf0b332f 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -216,7 +216,8 @@ void tor_tls_set_renegotiate_callback(tor_tls_t *tls, void (*cb)(tor_tls_t *, void *arg), void *arg); int tor_tls_is_server(tor_tls_t *tls); -void tor_tls_free(tor_tls_t *tls); +void tor_tls_free_(tor_tls_t *tls); +#define tor_tls_free(tls) FREE_AND_NULL(tor_tls_t, tor_tls_free_, (tls)) int tor_tls_peer_has_cert(tor_tls_t *tls); MOCK_DECL(tor_x509_cert_t *,tor_tls_get_peer_cert,(tor_tls_t *tls)); MOCK_DECL(tor_x509_cert_t *,tor_tls_get_own_cert,(tor_tls_t *tls)); @@ -263,7 +264,9 @@ void check_no_tls_errors_(const char *fname, int line); void tor_tls_log_one_error(tor_tls_t *tls, unsigned long err, int severity, int domain, const char *doing); -void tor_x509_cert_free(tor_x509_cert_t *cert); +void tor_x509_cert_free_(tor_x509_cert_t *cert); +#define tor_x509_cert_free(c) \ + FREE_AND_NULL(tor_x509_cert_t, tor_x509_cert_free_, (c)) tor_x509_cert_t *tor_x509_cert_decode(const uint8_t *certificate, size_t certificate_len); void tor_x509_cert_get_der(const tor_x509_cert_t *cert, diff --git a/src/common/util.c b/src/common/util.c index 5ff7e104d6..67e97811c6 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -156,7 +156,7 @@ tor_malloc_(size_t size DMALLOC_PARAMS) /* If these functions die within a worker process, they won't call * spawn_exit, but that's ok, since the parent will run out of memory soon * anyway. */ - exit(1); + exit(1); // exit ok: alloc failed. /* LCOV_EXCL_STOP */ } return result; @@ -244,7 +244,7 @@ tor_realloc_(void *ptr, size_t size DMALLOC_PARAMS) if (PREDICT_UNLIKELY(result == NULL)) { /* LCOV_EXCL_START */ log_err(LD_MM,"Out of memory on realloc(). Dying."); - exit(1); + exit(1); // exit ok: alloc failed. /* LCOV_EXCL_STOP */ } return result; @@ -282,7 +282,7 @@ tor_strdup_(const char *s DMALLOC_PARAMS) if (PREDICT_UNLIKELY(duplicate == NULL)) { /* LCOV_EXCL_START */ log_err(LD_MM,"Out of memory on strdup(). Dying."); - exit(1); + exit(1); // exit ok: alloc failed. /* LCOV_EXCL_STOP */ } return duplicate; @@ -3590,14 +3590,14 @@ start_daemon(void) if (pipe(daemon_filedes)) { /* LCOV_EXCL_START */ log_err(LD_GENERAL,"pipe failed; exiting. Error was %s", strerror(errno)); - exit(1); + exit(1); // exit ok: during daemonize, pipe failed. /* LCOV_EXCL_STOP */ } pid = fork(); if (pid < 0) { /* LCOV_EXCL_START */ log_err(LD_GENERAL,"fork failed. Exiting."); - exit(1); + exit(1); // exit ok: during daemonize, fork failed /* LCOV_EXCL_STOP */ } if (pid) { /* Parent */ @@ -3612,9 +3612,9 @@ start_daemon(void) } fflush(stdout); if (ok == 1) - exit(0); + exit(0); // exit ok: during daemonize, daemonizing. else - exit(1); /* child reported error */ + exit(1); /* child reported error. exit ok: daemonize failed. */ } else { /* Child */ close(daemon_filedes[0]); /* we only write */ @@ -3626,7 +3626,7 @@ start_daemon(void) * _Advanced Programming in the Unix Environment_. */ if (fork() != 0) { - exit(0); + exit(0); // exit ok: during daemonize, fork failed (2) } set_main_thread(); /* We are now the main thread. */ @@ -3655,14 +3655,14 @@ finish_daemon(const char *desired_cwd) /* Don't hold the wrong FS mounted */ if (chdir(desired_cwd) < 0) { log_err(LD_GENERAL,"chdir to \"%s\" failed. Exiting.",desired_cwd); - exit(1); + exit(1); // exit ok: during daemonize, chdir failed. } nullfd = tor_open_cloexec("/dev/null", O_RDWR, 0); if (nullfd < 0) { /* LCOV_EXCL_START */ log_err(LD_GENERAL,"/dev/null can't be opened. Exiting."); - exit(1); + exit(1); // exit ok: during daemonize, couldn't open /dev/null /* LCOV_EXCL_STOP */ } /* close fds linking to invoking terminal, but @@ -3674,7 +3674,7 @@ finish_daemon(const char *desired_cwd) dup2(nullfd,2) < 0) { /* LCOV_EXCL_START */ log_err(LD_GENERAL,"dup2 failed. Exiting."); - exit(1); + exit(1); // exit ok: during daemonize, dup2 failed. /* LCOV_EXCL_STOP */ } if (nullfd > 2) @@ -4474,7 +4474,7 @@ tor_spawn_background(const char *const filename, const char **argv, err += (nbytes < 0); } - _exit(err?254:255); + _exit(err?254:255); // exit ok: in child. } /* Never reached, but avoids compiler warning */ @@ -4713,7 +4713,7 @@ environment_variable_names_equal(const char *s1, const char *s2) /** Free <b>env</b> (assuming it was produced by * process_environment_make). */ void -process_environment_free(process_environment_t *env) +process_environment_free_(process_environment_t *env) { if (env == NULL) return; diff --git a/src/common/util.h b/src/common/util.h index 6bc853da26..8dc64ce9fa 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -80,12 +80,22 @@ extern int dmalloc_free(const char *file, const int line, void *pnt, * This is a macro. If you need a function pointer to release memory from * tor_malloc(), use tor_free_(). */ +#ifdef __GNUC__ +#define tor_free(p) STMT_BEGIN \ + typeof(&(p)) tor_free__tmpvar = &(p); \ + if (PREDICT_LIKELY((*tor_free__tmpvar)!=NULL)) { \ + raw_free(*tor_free__tmpvar); \ + *tor_free__tmpvar=NULL; \ + } \ + STMT_END +#else #define tor_free(p) STMT_BEGIN \ if (PREDICT_LIKELY((p)!=NULL)) { \ raw_free(p); \ (p)=NULL; \ } \ STMT_END +#endif #endif /* defined(USE_DMALLOC) */ #define tor_malloc(size) tor_malloc_(size DMALLOC_ARGS) @@ -109,6 +119,17 @@ extern int dmalloc_free(const char *file, const int line, void *pnt, void tor_log_mallinfo(int severity); +/* Helper macro: free a variable of type 'typename' using freefn, and + * set the variable to NULL. + */ +#define FREE_AND_NULL(typename, freefn, var) \ + do { \ + /* only evaluate (var) once. */ \ + typename **tmp__free__ptr ## freefn = &(var); \ + freefn(*tmp__free__ptr ## freefn); \ + (*tmp__free__ptr ## freefn) = NULL; \ + } while (0) + /** Macro: yield a pointer to the field at position <b>off</b> within the * structure <b>st</b>. Example: * <pre> @@ -423,7 +444,9 @@ struct process_environment_t { }; process_environment_t *process_environment_make(struct smartlist_t *env_vars); -void process_environment_free(process_environment_t *env); +void process_environment_free_(process_environment_t *env); +#define process_environment_free(env) \ + FREE_AND_NULL(process_environment_t, process_environment_free_, (env)) struct smartlist_t *get_current_process_environment_variables(void); diff --git a/src/common/workqueue.c b/src/common/workqueue.c index 42723224d3..ec96959b7d 100644 --- a/src/common/workqueue.c +++ b/src/common/workqueue.c @@ -148,12 +148,15 @@ workqueue_entry_new(workqueue_reply_t (*fn)(void*, void*), return ent; } +#define workqueue_entry_free(ent) \ + FREE_AND_NULL(workqueue_entry_t, workqueue_entry_free_, (ent)) + /** * Release all storage held in <b>ent</b>. Call only when <b>ent</b> is not on * any queue. */ static void -workqueue_entry_free(workqueue_entry_t *ent) +workqueue_entry_free_(workqueue_entry_t *ent) { if (!ent) return; diff --git a/src/or/addressmap.c b/src/or/addressmap.c index 7e92633601..96ce275578 100644 --- a/src/or/addressmap.c +++ b/src/or/addressmap.c @@ -90,34 +90,47 @@ addressmap_init(void) virtaddress_reversemap = strmap_new(); } +#define addressmap_ent_free(ent) \ + FREE_AND_NULL(addressmap_entry_t, addressmap_ent_free_, (ent)) + /** Free the memory associated with the addressmap entry <b>_ent</b>. */ static void -addressmap_ent_free(void *_ent) +addressmap_ent_free_(addressmap_entry_t *ent) { - addressmap_entry_t *ent; - if (!_ent) + if (!ent) return; - ent = _ent; tor_free(ent->new_address); tor_free(ent); } +static void +addressmap_ent_free_void(void *ent) +{ + addressmap_ent_free_(ent); +} + +#define addressmap_virtaddress_ent_free(ent) \ + FREE_AND_NULL(virtaddress_entry_t, addressmap_virtaddress_ent_free_, (ent)) + /** Free storage held by a virtaddress_entry_t* entry in <b>_ent</b>. */ static void -addressmap_virtaddress_ent_free(void *_ent) +addressmap_virtaddress_ent_free_(virtaddress_entry_t *ent) { - virtaddress_entry_t *ent; - if (!_ent) + if (!ent) return; - - ent = _ent; tor_free(ent->ipv4_address); tor_free(ent->ipv6_address); tor_free(ent->hostname_address); tor_free(ent); } +static void +addressmap_virtaddress_ent_free_void(void *ent) +{ + addressmap_virtaddress_ent_free_(ent); +} + /** Remove <b>address</b> (which must map to <b>ent</b>) from the * virtual address map. */ static void @@ -311,10 +324,10 @@ addressmap_clean(time_t now) void addressmap_free_all(void) { - strmap_free(addressmap, addressmap_ent_free); + strmap_free(addressmap, addressmap_ent_free_void); addressmap = NULL; - strmap_free(virtaddress_reversemap, addressmap_virtaddress_ent_free); + strmap_free(virtaddress_reversemap, addressmap_virtaddress_ent_free_void); virtaddress_reversemap = NULL; } @@ -541,7 +554,7 @@ addressmap_have_mapping(const char *address, int update_expiry) * (virtual address mapping) from the controller.) * * <b>new_address</b> should be a newly dup'ed string, which we'll use or - * free as appropriate. We will leave address alone. + * free as appropriate. We will leave <b>address</b> alone. * * If <b>wildcard_addr</b> is true, then the mapping will match any address * equal to <b>address</b>, or any address ending with a period followed by @@ -554,7 +567,6 @@ addressmap_have_mapping(const char *address, int update_expiry) * <b>wildcard_new_addr</b>, remove any mappings that exist from * <b>address</b>. * - * * It is an error to set <b>wildcard_new_addr</b> if <b>wildcard_addr</b> is * not set. */ void diff --git a/src/or/bridges.c b/src/or/bridges.c index 0b1bbbd158..19a23ea86b 100644 --- a/src/or/bridges.c +++ b/src/or/bridges.c @@ -53,7 +53,10 @@ struct bridge_info_t { smartlist_t *socks_args; }; -static void bridge_free(bridge_info_t *bridge); +#define bridge_free(bridge) \ + FREE_AND_NULL(bridge_info_t, bridge_free_, (bridge)) + +static void bridge_free_(bridge_info_t *bridge); static void rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node); @@ -101,7 +104,7 @@ clear_bridge_list(void) /** Free the bridge <b>bridge</b>. */ static void -bridge_free(bridge_info_t *bridge) +bridge_free_(bridge_info_t *bridge) { if (!bridge) return; diff --git a/src/or/channel.c b/src/or/channel.c index 0b5a7fde90..845fc3cb44 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -1,3 +1,4 @@ + /* * Copyright (c) 2012-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ @@ -5,9 +6,8 @@ * \file channel.c * * \brief OR/OP-to-OR channel abstraction layer. A channel's job is to - * transfer cells from Tor instance to Tor instance. - * Currently, there is only one implementation of the channel abstraction: in - * channeltls.c. + * transfer cells from Tor instance to Tor instance. Currently, there is only + * one implementation of the channel abstraction: in channeltls.c. * * Channels are a higher-level abstraction than or_connection_t: In general, * any means that two Tor relays use to exchange cells, or any means that a @@ -24,16 +24,28 @@ * connection. * * Every channel implementation is responsible for being able to transmit - * cells that are added to it with channel_write_cell() and related functions, - * and to receive incoming cells with the channel_queue_cell() and related - * functions. See the channel_t documentation for more information. - * - * When new cells arrive on a channel, they are passed to cell handler - * functions, which can be set by channel_set_cell_handlers() - * functions. (Tor's cell handlers are in command.c.) - * - * Tor flushes cells to channels from relay.c in - * channel_flush_from_first_active_circuit(). + * cells that are passed to it + * + * For *inbound* cells, the entry point is: channel_process_cell(). It takes a + * cell and will pass it to the cell handler set by + * channel_set_cell_handlers(). Currently, this is passed back to the command + * subsystem which is command_process_cell(). + * + * NOTE: For now, the seperation between channels and specialized channels + * (like channeltls) is not that well defined. So the channeltls layer calls + * channel_process_cell() which originally comes from the connection subsytem. + * This should be hopefully be fixed with #23993. + * + * For *outbound* cells, the entry point is: channel_write_packed_cell(). + * Only packed cells are dequeued from the circuit queue by the scheduler + * which uses channel_flush_from_first_active_circuit() to decide which cells + * to flush from which circuit on the channel. They are then passed down to + * the channel subsystem. This calls the low layer with the function pointer + * .write_packed_cell(). + * + * Each specialized channel (currently only channeltls_t) MUST implement a + * series of function found in channel_t. See channel.h for more + * documentation. **/ /* @@ -112,59 +124,6 @@ HANDLE_IMPL(channel, channel_s,) /* Counter for ID numbers */ static uint64_t n_channels_allocated = 0; -/* - * Channel global byte/cell counters, for statistics and for scheduler high - * /low-water marks. - */ - -/* - * Total number of cells ever given to any channel with the - * channel_write_*_cell() functions. - */ - -static uint64_t n_channel_cells_queued = 0; - -/* - * Total number of cells ever passed to a channel lower layer with the - * write_*_cell() methods. - */ - -static uint64_t n_channel_cells_passed_to_lower_layer = 0; - -/* - * Current number of cells in all channel queues; should be - * n_channel_cells_queued - n_channel_cells_passed_to_lower_layer. - */ - -static uint64_t n_channel_cells_in_queues = 0; - -/* - * Total number of bytes for all cells ever queued to a channel and - * counted in n_channel_cells_queued. - */ - -static uint64_t n_channel_bytes_queued = 0; - -/* - * Total number of bytes for all cells ever passed to a channel lower layer - * and counted in n_channel_cells_passed_to_lower_layer. - */ - -static uint64_t n_channel_bytes_passed_to_lower_layer = 0; - -/* - * Current number of bytes in all channel queues; should be - * n_channel_bytes_queued - n_channel_bytes_passed_to_lower_layer. - */ - -static uint64_t n_channel_bytes_in_queues = 0; - -/* - * Current total estimated queue size *including lower layer queues and - * transmit overhead* - */ - -STATIC uint64_t estimated_total_queue_size = 0; /* Digest->channel map * @@ -201,40 +160,15 @@ HT_PROTOTYPE(channel_idmap, channel_idmap_entry_s, node, channel_idmap_hash, HT_GENERATE2(channel_idmap, channel_idmap_entry_s, node, channel_idmap_hash, channel_idmap_eq, 0.5, tor_reallocarray_, tor_free_) -static cell_queue_entry_t * cell_queue_entry_dup(cell_queue_entry_t *q); -#if 0 -static int cell_queue_entry_is_padding(cell_queue_entry_t *q); -#endif -static cell_queue_entry_t * -cell_queue_entry_new_fixed(cell_t *cell); -static cell_queue_entry_t * -cell_queue_entry_new_var(var_cell_t *var_cell); -static int is_destroy_cell(channel_t *chan, - const cell_queue_entry_t *q, circid_t *circid_out); - -static void channel_assert_counter_consistency(void); - /* Functions to maintain the digest map */ -static void channel_add_to_digest_map(channel_t *chan); static void channel_remove_from_digest_map(channel_t *chan); -/* - * Flush cells from just the outgoing queue without trying to get them - * from circuits; used internall by channel_flush_some_cells(). - */ -static ssize_t -channel_flush_some_cells_from_outgoing_queue(channel_t *chan, - ssize_t num_cells); -static void channel_force_free(channel_t *chan); -static void -channel_free_list(smartlist_t *channels, int mark_for_close); -static void -channel_listener_free_list(smartlist_t *channels, int mark_for_close); -static void channel_listener_force_free(channel_listener_t *chan_l); -static size_t channel_get_cell_queue_entry_size(channel_t *chan, - cell_queue_entry_t *q); -static void -channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q); +static void channel_force_xfree(channel_t *chan); +static void channel_free_list(smartlist_t *channels, + int mark_for_close); +static void channel_listener_free_list(smartlist_t *channels, + int mark_for_close); +static void channel_listener_force_xfree(channel_listener_t *chan_l); /*********************************** * Channel state utility functions * @@ -628,7 +562,7 @@ channel_listener_unregister(channel_listener_t *chan_l) * already exist. */ -static void +STATIC void channel_add_to_digest_map(channel_t *chan) { channel_idmap_entry_t *ent, search; @@ -676,33 +610,6 @@ channel_remove_from_digest_map(channel_t *chan) /* Assert that there is a digest */ tor_assert(!tor_digest_is_zero(chan->identity_digest)); -#if 0 - /* Make sure we have a map */ - if (!channel_identity_map) { - /* - * No identity map, so we can't find it by definition. This - * case is similar to digestmap_get() failing below. - */ - log_warn(LD_BUG, - "Trying to remove channel %p (global ID " U64_FORMAT ") " - "with digest %s from identity map, but didn't have any identity " - "map", - chan, U64_PRINTF_ARG(chan->global_identifier), - hex_str(chan->identity_digest, DIGEST_LEN)); - /* Clear out its next/prev pointers */ - if (chan->next_with_same_id) { - chan->next_with_same_id->prev_with_same_id = chan->prev_with_same_id; - } - if (chan->prev_with_same_id) { - chan->prev_with_same_id->next_with_same_id = chan->next_with_same_id; - } - chan->next_with_same_id = NULL; - chan->prev_with_same_id = NULL; - - return; - } -#endif /* 0 */ - /* Pull it out of its list, wherever that list is */ TOR_LIST_REMOVE(chan, next_with_same_id); @@ -936,10 +843,6 @@ channel_init(channel_t *chan) /* Warn about exhausted circuit IDs no more than hourly. */ chan->last_warned_circ_ids_exhausted.rate = 3600; - /* Initialize queues. */ - TOR_SIMPLEQ_INIT(&chan->incoming_queue); - TOR_SIMPLEQ_INIT(&chan->outgoing_queue); - /* Initialize list entries. */ memset(&chan->next_with_same_id, 0, sizeof(chan->next_with_same_id)); @@ -979,7 +882,7 @@ channel_init_listener(channel_listener_t *chan_l) */ void -channel_free(channel_t *chan) +channel_free_(channel_t *chan) { if (!chan) return; @@ -1022,8 +925,6 @@ channel_free(channel_t *chan) chan->cmux = NULL; } - /* We're in CLOSED or ERROR, so the cell queue is already empty */ - tor_free(chan); } @@ -1034,7 +935,7 @@ channel_free(channel_t *chan) */ void -channel_listener_free(channel_listener_t *chan_l) +channel_listener_free_(channel_listener_t *chan_l) { if (!chan_l) return; @@ -1052,11 +953,6 @@ channel_listener_free(channel_listener_t *chan_l) /* Call a free method if there is one */ if (chan_l->free_fn) chan_l->free_fn(chan_l); - /* - * We're in CLOSED or ERROR, so the incoming channel queue is already - * empty. - */ - tor_free(chan_l); } @@ -1067,9 +963,8 @@ channel_listener_free(channel_listener_t *chan_l) */ static void -channel_force_free(channel_t *chan) +channel_force_xfree(channel_t *chan) { - cell_queue_entry_t *cell, *cell_tmp; tor_assert(chan); log_debug(LD_CHANNEL, @@ -1103,18 +998,6 @@ channel_force_free(channel_t *chan) chan->cmux = NULL; } - /* We might still have a cell queue; kill it */ - TOR_SIMPLEQ_FOREACH_SAFE(cell, &chan->incoming_queue, next, cell_tmp) { - cell_queue_entry_free(cell, 0); - } - TOR_SIMPLEQ_INIT(&chan->incoming_queue); - - /* Outgoing cell queue is similar, but we can have to free packed cells */ - TOR_SIMPLEQ_FOREACH_SAFE(cell, &chan->outgoing_queue, next, cell_tmp) { - cell_queue_entry_free(cell, 0); - } - TOR_SIMPLEQ_INIT(&chan->outgoing_queue); - tor_free(chan); } @@ -1125,7 +1008,7 @@ channel_force_free(channel_t *chan) */ static void -channel_listener_force_free(channel_listener_t *chan_l) +channel_listener_force_xfree(channel_listener_t *chan_l) { tor_assert(chan_l); @@ -1156,24 +1039,6 @@ channel_listener_force_free(channel_listener_t *chan_l) } /** - * Return the current registered listener for a channel listener - * - * This function returns a function pointer to the current registered - * handler for new incoming channels on a channel listener. - */ - -channel_listener_fn_ptr -channel_listener_get_listener_fn(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - if (chan_l->state == CHANNEL_LISTENER_STATE_LISTENING) - return chan_l->listener; - - return NULL; -} - -/** * Set the listener for a channel listener * * This function sets the handler for new incoming channels on a channel @@ -1237,8 +1102,7 @@ channel_get_var_cell_handler(channel_t *chan) * Set both cell handlers for a channel * * This function sets both the fixed-length and variable length cell handlers - * for a channel and processes any incoming cells that had been blocked in the - * queue because none were available. + * for a channel. */ void @@ -1247,8 +1111,6 @@ channel_set_cell_handlers(channel_t *chan, channel_var_cell_handler_fn_ptr var_cell_handler) { - int try_again = 0; - tor_assert(chan); tor_assert(CHANNEL_CAN_HANDLE_CELLS(chan)); @@ -1259,21 +1121,9 @@ channel_set_cell_handlers(channel_t *chan, "Setting var_cell_handler callback for channel %p to %p", chan, var_cell_handler); - /* Should we try the queue? */ - if (cell_handler && - cell_handler != chan->cell_handler) try_again = 1; - if (var_cell_handler && - var_cell_handler != chan->var_cell_handler) try_again = 1; - /* Change them */ chan->cell_handler = cell_handler; chan->var_cell_handler = var_cell_handler; - - /* Re-run the queue if we have one and there's any reason to */ - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue) && - try_again && - (chan->cell_handler || - chan->var_cell_handler)) channel_process_cells(chan); } /* @@ -1400,36 +1250,6 @@ channel_close_from_lower_layer(channel_t *chan) } /** - * Close a channel listener from the lower layer - * - * Notify the channel code that the channel listener is being closed due to a - * non-error condition in the lower layer. This does not call the close() - * method, since the lower layer already knows. - */ - -void -channel_listener_close_from_lower_layer(channel_listener_t *chan_l) -{ - tor_assert(chan_l != NULL); - - /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */ - if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSING || - chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return; - - log_debug(LD_CHANNEL, - "Closing channel listener %p (global ID " U64_FORMAT ") " - "due to lower-layer event", - chan_l, U64_PRINTF_ARG(chan_l->global_identifier)); - - /* Note closing by event from below */ - chan_l->reason_for_closing = CHANNEL_LISTENER_CLOSE_FROM_BELOW; - - /* Change state to CLOSING */ - channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSING); -} - -/** * Notify that the channel is being closed due to an error condition * * This function is called by the lower layer implementing the transport @@ -1458,37 +1278,6 @@ channel_close_for_error(channel_t *chan) } /** - * Notify that the channel listener is being closed due to an error condition - * - * This function is called by the lower layer implementing the transport - * when a channel listener must be closed due to an error condition. This - * does not call the channel listener's close method, since the lower layer - * already knows. - */ - -void -channel_listener_close_for_error(channel_listener_t *chan_l) -{ - tor_assert(chan_l != NULL); - - /* If it's already in CLOSING, CLOSED or ERROR, this is a no-op */ - if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSING || - chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return; - - log_debug(LD_CHANNEL, - "Closing channel listener %p (global ID " U64_FORMAT ") " - "due to lower-layer error", - chan_l, U64_PRINTF_ARG(chan_l->global_identifier)); - - /* Note closing by event from below */ - chan_l->reason_for_closing = CHANNEL_LISTENER_CLOSE_FOR_ERROR; - - /* Change state to CLOSING */ - channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSING); -} - -/** * Notify that the lower layer is finished closing the channel * * This function should be called by the lower layer when a channel @@ -1522,33 +1311,6 @@ channel_closed(channel_t *chan) } /** - * Notify that the lower layer is finished closing the channel listener - * - * This function should be called by the lower layer when a channel listener - * is finished closing and it should be regarded as inactive and - * freed by the channel code. - */ - -void -channel_listener_closed(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - tor_assert(chan_l->state == CHANNEL_LISTENER_STATE_CLOSING || - chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR); - - /* No-op if already inactive */ - if (chan_l->state == CHANNEL_LISTENER_STATE_CLOSED || - chan_l->state == CHANNEL_LISTENER_STATE_ERROR) return; - - if (chan_l->reason_for_closing != CHANNEL_LISTENER_CLOSE_FOR_ERROR) { - channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_CLOSED); - } else { - channel_listener_change_state(chan_l, CHANNEL_LISTENER_STATE_ERROR); - } -} - -/** * Clear the identity_digest of a channel * * This function clears the identity digest of the remote endpoint for a @@ -1638,7 +1400,7 @@ channel_set_identity_digest(channel_t *chan, } /** - * Clear the remote end metadata (identity_digest/nickname) of a channel + * Clear the remote end metadata (identity_digest) of a channel * * This function clears all the remote end info from a channel; this is * intended for use by the lower layer. @@ -1665,419 +1427,95 @@ channel_clear_remote_end(channel_t *chan) memset(chan->identity_digest, 0, sizeof(chan->identity_digest)); - tor_free(chan->nickname); } /** - * Set the remote end metadata (identity_digest/nickname) of a channel + * Write to a channel the given packed cell. * - * This function sets new remote end info on a channel; this is intended - * for use by the lower layer. - */ - -void -channel_set_remote_end(channel_t *chan, - const char *identity_digest, - const char *nickname) -{ - int was_in_digest_map, should_be_in_digest_map, state_not_in_map; - - tor_assert(chan); - - log_debug(LD_CHANNEL, - "Setting remote endpoint identity on channel %p with " - "global ID " U64_FORMAT " to nickname %s, digest %s", - chan, U64_PRINTF_ARG(chan->global_identifier), - nickname ? nickname : "(null)", - identity_digest ? - hex_str(identity_digest, DIGEST_LEN) : "(null)"); - - state_not_in_map = CHANNEL_CONDEMNED(chan); - - was_in_digest_map = - !state_not_in_map && - chan->registered && - !tor_digest_is_zero(chan->identity_digest); - should_be_in_digest_map = - !state_not_in_map && - chan->registered && - (identity_digest && - !tor_digest_is_zero(identity_digest)); - - if (was_in_digest_map) - /* We should always remove it; we'll add it back if we're writing - * in a new digest. - */ - channel_remove_from_digest_map(chan); - - if (identity_digest) { - memcpy(chan->identity_digest, - identity_digest, - sizeof(chan->identity_digest)); - - } else { - memset(chan->identity_digest, 0, - sizeof(chan->identity_digest)); - } - - tor_free(chan->nickname); - if (nickname) - chan->nickname = tor_strdup(nickname); - - /* Put it in the digest map if we should */ - if (should_be_in_digest_map) - channel_add_to_digest_map(chan); -} - -/** - * Duplicate a cell queue entry; this is a shallow copy intended for use - * in channel_write_cell_queue_entry(). - */ - -static cell_queue_entry_t * -cell_queue_entry_dup(cell_queue_entry_t *q) -{ - cell_queue_entry_t *rv = NULL; - - tor_assert(q); - - rv = tor_malloc(sizeof(*rv)); - memcpy(rv, q, sizeof(*rv)); - - return rv; -} - -/** - * Free a cell_queue_entry_t; the handed_off parameter indicates whether - * the contents were passed to the lower layer (it is responsible for - * them) or not (we should free). - */ - -STATIC void -cell_queue_entry_free(cell_queue_entry_t *q, int handed_off) -{ - if (!q) return; - - if (!handed_off) { - /* - * If we handed it off, the recipient becomes responsible (or - * with packed cells the channel_t subclass calls packed_cell - * free after writing out its contents; see, e.g., - * channel_tls_write_packed_cell_method(). Otherwise, we have - * to take care of it here if possible. - */ - switch (q->type) { - case CELL_QUEUE_FIXED: - if (q->u.fixed.cell) { - /* - * There doesn't seem to be a cell_free() function anywhere in the - * pre-channel code; just use tor_free() - */ - tor_free(q->u.fixed.cell); - } - break; - case CELL_QUEUE_PACKED: - if (q->u.packed.packed_cell) { - packed_cell_free(q->u.packed.packed_cell); - } - break; - case CELL_QUEUE_VAR: - if (q->u.var.var_cell) { - /* - * This one's in connection_or.c; it'd be nice to figure out the - * whole flow of cells from one end to the other and factor the - * cell memory management functions like this out of the specific - * TLS lower layer. - */ - var_cell_free(q->u.var.var_cell); - } - break; - default: - /* - * Nothing we can do if we don't know the type; this will - * have been warned about elsewhere. - */ - break; - } - } - tor_free(q); -} - -#if 0 -/** - * Check whether a cell queue entry is padding; this is a helper function - * for channel_write_cell_queue_entry() - */ - -static int -cell_queue_entry_is_padding(cell_queue_entry_t *q) -{ - tor_assert(q); - - if (q->type == CELL_QUEUE_FIXED) { - if (q->u.fixed.cell) { - if (q->u.fixed.cell->command == CELL_PADDING || - q->u.fixed.cell->command == CELL_VPADDING) { - return 1; - } - } - } else if (q->type == CELL_QUEUE_VAR) { - if (q->u.var.var_cell) { - if (q->u.var.var_cell->command == CELL_PADDING || - q->u.var.var_cell->command == CELL_VPADDING) { - return 1; - } - } - } - - return 0; -} -#endif /* 0 */ - -/** - * Allocate a new cell queue entry for a fixed-size cell - */ - -static cell_queue_entry_t * -cell_queue_entry_new_fixed(cell_t *cell) -{ - cell_queue_entry_t *q = NULL; - - tor_assert(cell); - - q = tor_malloc(sizeof(*q)); - q->type = CELL_QUEUE_FIXED; - q->u.fixed.cell = cell; - - return q; -} - -/** - * Allocate a new cell queue entry for a variable-size cell - */ - -static cell_queue_entry_t * -cell_queue_entry_new_var(var_cell_t *var_cell) -{ - cell_queue_entry_t *q = NULL; - - tor_assert(var_cell); - - q = tor_malloc(sizeof(*q)); - q->type = CELL_QUEUE_VAR; - q->u.var.var_cell = var_cell; - - return q; -} - -/** - * Ask how big the cell contained in a cell_queue_entry_t is - */ - -static size_t -channel_get_cell_queue_entry_size(channel_t *chan, cell_queue_entry_t *q) -{ - size_t rv = 0; - - tor_assert(chan); - tor_assert(q); - - switch (q->type) { - case CELL_QUEUE_FIXED: - rv = get_cell_network_size(chan->wide_circ_ids); - break; - case CELL_QUEUE_VAR: - rv = get_var_cell_header_size(chan->wide_circ_ids) + - (q->u.var.var_cell ? q->u.var.var_cell->payload_len : 0); - break; - case CELL_QUEUE_PACKED: - rv = get_cell_network_size(chan->wide_circ_ids); - break; - default: - tor_assert_nonfatal_unreached_once(); - } - - return rv; -} - -/** - * Write to a channel based on a cell_queue_entry_t * - * Given a cell_queue_entry_t filled out by the caller, try to send the cell - * and queue it if we can't. + * Two possible errors can happen. Either the channel is not opened or the + * lower layer (specialized channel) failed to write it. In both cases, it is + * the caller responsability to free the cell. */ - -static void -channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q) +static int +write_packed_cell(channel_t *chan, packed_cell_t *cell) { - int result = 0, sent = 0; - cell_queue_entry_t *tmp = NULL; + int ret = -1; size_t cell_bytes; tor_assert(chan); - tor_assert(q); + tor_assert(cell); /* Assert that the state makes sense for a cell write */ tor_assert(CHANNEL_CAN_HANDLE_CELLS(chan)); { circid_t circ_id; - if (is_destroy_cell(chan, q, &circ_id)) { + if (packed_cell_is_destroy(chan, cell, &circ_id)) { channel_note_destroy_not_pending(chan, circ_id); } } /* For statistical purposes, figure out how big this cell is */ - cell_bytes = channel_get_cell_queue_entry_size(chan, q); + cell_bytes = get_cell_network_size(chan->wide_circ_ids); /* Can we send it right out? If so, try */ - if (TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue) && - CHANNEL_IS_OPEN(chan)) { - /* Pick the right write function for this cell type and save the result */ - switch (q->type) { - case CELL_QUEUE_FIXED: - tor_assert(chan->write_cell); - tor_assert(q->u.fixed.cell); - result = chan->write_cell(chan, q->u.fixed.cell); - break; - case CELL_QUEUE_PACKED: - tor_assert(chan->write_packed_cell); - tor_assert(q->u.packed.packed_cell); - result = chan->write_packed_cell(chan, q->u.packed.packed_cell); - break; - case CELL_QUEUE_VAR: - tor_assert(chan->write_var_cell); - tor_assert(q->u.var.var_cell); - result = chan->write_var_cell(chan, q->u.var.var_cell); - break; - default: - tor_assert(1); - } - - /* Check if we got it out */ - if (result > 0) { - sent = 1; - /* Timestamp for transmission */ - channel_timestamp_xmit(chan); - /* If we're here the queue is empty, so it's drained too */ - channel_timestamp_drained(chan); - /* Update the counter */ - ++(chan->n_cells_xmitted); - chan->n_bytes_xmitted += cell_bytes; - /* Update global counters */ - ++n_channel_cells_queued; - ++n_channel_cells_passed_to_lower_layer; - n_channel_bytes_queued += cell_bytes; - n_channel_bytes_passed_to_lower_layer += cell_bytes; - channel_assert_counter_consistency(); - } + if (!CHANNEL_IS_OPEN(chan)) { + goto done; } - if (!sent) { - /* Not sent, queue it */ - /* - * We have to copy the queue entry passed in, since the caller probably - * used the stack. - */ - tmp = cell_queue_entry_dup(q); - TOR_SIMPLEQ_INSERT_TAIL(&chan->outgoing_queue, tmp, next); - /* Update global counters */ - ++n_channel_cells_queued; - ++n_channel_cells_in_queues; - n_channel_bytes_queued += cell_bytes; - n_channel_bytes_in_queues += cell_bytes; - channel_assert_counter_consistency(); - /* Update channel queue size */ - chan->bytes_in_queue += cell_bytes; - /* Try to process the queue? */ - if (CHANNEL_IS_OPEN(chan)) channel_flush_cells(chan); + /* Write the cell on the connection's outbuf. */ + if (chan->write_packed_cell(chan, cell) < 0) { + goto done; } + /* Timestamp for transmission */ + channel_timestamp_xmit(chan); + /* Update the counter */ + ++(chan->n_cells_xmitted); + chan->n_bytes_xmitted += cell_bytes; + /* Successfully sent the cell. */ + ret = 0; + + done: + return ret; } -/** Write a generic cell type to a channel +/** + * Write a packed cell to a channel + * + * Write a packed cell to a channel using the write_cell() method. This is + * called by the transport-independent code to deliver a packed cell to a + * channel for transmission. * - * Write a generic cell to a channel. It is called by channel_write_cell(), - * channel_write_var_cell() and channel_write_packed_cell() in order to reduce - * code duplication. Notice that it takes cell as pointer of type void, - * this can be dangerous because no type check is performed. + * Return 0 on success else a negative value. In both cases, the caller should + * not access the cell anymore, it is freed both on success and error. */ - -void -channel_write_cell_generic_(channel_t *chan, const char *cell_type, - void *cell, cell_queue_entry_t *q) +int +channel_write_packed_cell(channel_t *chan, packed_cell_t *cell) { + int ret = -1; tor_assert(chan); tor_assert(cell); if (CHANNEL_IS_CLOSING(chan)) { - log_debug(LD_CHANNEL, "Discarding %c %p on closing channel %p with " - "global ID "U64_FORMAT, *cell_type, cell, chan, + log_debug(LD_CHANNEL, "Discarding %p on closing channel %p with " + "global ID "U64_FORMAT, cell, chan, U64_PRINTF_ARG(chan->global_identifier)); - tor_free(cell); - return; + goto end; } log_debug(LD_CHANNEL, - "Writing %c %p to channel %p with global ID " - U64_FORMAT, *cell_type, - cell, chan, U64_PRINTF_ARG(chan->global_identifier)); - - channel_write_cell_queue_entry(chan, q); - /* Update the queue size estimate */ - channel_update_xmit_queue_size(chan); -} - -/** - * Write a cell to a channel - * - * Write a fixed-length cell to a channel using the write_cell() method. - * This is equivalent to the pre-channels connection_or_write_cell_to_buf(); - * it is called by the transport-independent code to deliver a cell to a - * channel for transmission. - */ + "Writing %p to channel %p with global ID " + U64_FORMAT, cell, chan, U64_PRINTF_ARG(chan->global_identifier)); -void -channel_write_cell(channel_t *chan, cell_t *cell) -{ - cell_queue_entry_t q; - q.type = CELL_QUEUE_FIXED; - q.u.fixed.cell = cell; - channel_write_cell_generic_(chan, "cell_t", cell, &q); -} + ret = write_packed_cell(chan, cell); -/** - * Write a packed cell to a channel - * - * Write a packed cell to a channel using the write_cell() method. This is - * called by the transport-independent code to deliver a packed cell to a - * channel for transmission. - */ - -void -channel_write_packed_cell(channel_t *chan, packed_cell_t *packed_cell) -{ - cell_queue_entry_t q; - q.type = CELL_QUEUE_PACKED; - q.u.packed.packed_cell = packed_cell; - channel_write_cell_generic_(chan, "packed_cell_t", packed_cell, &q); -} - -/** - * Write a variable-length cell to a channel - * - * Write a variable-length cell to a channel using the write_cell() method. - * This is equivalent to the pre-channels - * connection_or_write_var_cell_to_buf(); it's called by the transport- - * independent code to deliver a var_cell to a channel for transmission. - */ - -void -channel_write_var_cell(channel_t *chan, var_cell_t *var_cell) -{ - cell_queue_entry_t q; - q.type = CELL_QUEUE_VAR; - q.u.var.var_cell = var_cell; - channel_write_cell_generic_(chan, "var_cell_t", var_cell, &q); + end: + /* Whatever happens, we free the cell. Either an error occured or the cell + * was put on the connection outbuf, both cases we have ownership of the + * cell and we free it. */ + packed_cell_free(cell); + return ret; } /** @@ -2119,15 +1557,6 @@ channel_change_state_(channel_t *chan, channel_state_t to_state) tor_assert(chan->reason_for_closing != CHANNEL_NOT_CLOSING); } - /* - * We need to maintain the queues here for some transitions: - * when we enter CHANNEL_STATE_OPEN (especially from CHANNEL_STATE_MAINT) - * we may have a backlog of cells to transmit, so drain the queues in - * that case, and when going to CHANNEL_STATE_CLOSED the subclass - * should have made sure to finish sending things (or gone to - * CHANNEL_STATE_ERROR if not possible), so we assert for that here. - */ - log_debug(LD_CHANNEL, "Changing state of channel %p (global ID " U64_FORMAT ") from \"%s\" to \"%s\"", @@ -2184,36 +1613,6 @@ channel_change_state_(channel_t *chan, channel_state_t to_state) } else if (to_state == CHANNEL_STATE_MAINT) { scheduler_channel_doesnt_want_writes(chan); } - - /* - * If we're closing, this channel no longer counts toward the global - * estimated queue size; if we're open, it now does. - */ - if ((to_state == CHANNEL_STATE_CLOSING || - to_state == CHANNEL_STATE_CLOSED || - to_state == CHANNEL_STATE_ERROR) && - (from_state == CHANNEL_STATE_OPEN || - from_state == CHANNEL_STATE_MAINT)) { - estimated_total_queue_size -= chan->bytes_in_queue; - } - - /* - * If we're opening, this channel now does count toward the global - * estimated queue size. - */ - if ((to_state == CHANNEL_STATE_OPEN || - to_state == CHANNEL_STATE_MAINT) && - !(from_state == CHANNEL_STATE_OPEN || - from_state == CHANNEL_STATE_MAINT)) { - estimated_total_queue_size += chan->bytes_in_queue; - } - - if (to_state == CHANNEL_STATE_CLOSED || - to_state == CHANNEL_STATE_ERROR) { - /* Assert that all queues are empty */ - tor_assert(TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)); - tor_assert(TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue)); - } } /** @@ -2237,12 +1636,6 @@ channel_change_state_open(channel_t *chan) /* Tell circuits if we opened and stuff */ channel_do_open_actions(chan); chan->has_been_open = 1; - - /* Check for queued cells to process */ - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) - channel_process_cells(chan); - if (! TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue)) - channel_flush_cells(chan); } /** @@ -2284,15 +1677,6 @@ channel_listener_change_state(channel_listener_t *chan_l, tor_assert(chan_l->reason_for_closing != CHANNEL_LISTENER_NOT_CLOSING); } - /* - * We need to maintain the queues here for some transitions: - * when we enter CHANNEL_STATE_OPEN (especially from CHANNEL_STATE_MAINT) - * we may have a backlog of cells to transmit, so drain the queues in - * that case, and when going to CHANNEL_STATE_CLOSED the subclass - * should have made sure to finish sending things (or gone to - * CHANNEL_STATE_ERROR if not possible), so we assert for that here. - */ - log_debug(LD_CHANNEL, "Changing state of channel listener %p (global ID " U64_FORMAT "from \"%s\" to \"%s\"", @@ -2325,30 +1709,38 @@ channel_listener_change_state(channel_listener_t *chan_l, if (to_state == CHANNEL_LISTENER_STATE_CLOSED || to_state == CHANNEL_LISTENER_STATE_ERROR) { - /* Assert that the queue is empty */ tor_assert(!(chan_l->incoming_list) || smartlist_len(chan_l->incoming_list) == 0); } } -/** - * Try to flush cells to the lower layer - * - * this is called by the lower layer to indicate that it wants more cells; - * it will try to write up to num_cells cells from the channel's cell queue or - * from circuits active on that channel, or as many as it has available if - * num_cells == -1. - */ - +/* Maximum number of cells that is allowed to flush at once withing + * channel_flush_some_cells(). */ #define MAX_CELLS_TO_GET_FROM_CIRCUITS_FOR_UNLIMITED 256 +/* Try to flush cells of the given channel chan up to a maximum of num_cells. + * + * This is called by the scheduler when it wants to flush cells from the + * channel's circuit queue(s) to the connection outbuf (not yet on the wire). + * + * If the channel is not in state CHANNEL_STATE_OPEN, this does nothing and + * will return 0 meaning no cells were flushed. + * + * If num_cells is -1, we'll try to flush up to the maximum cells allowed + * defined in MAX_CELLS_TO_GET_FROM_CIRCUITS_FOR_UNLIMITED. + * + * On success, the number of flushed cells are returned and it can never be + * above num_cells. If 0 is returned, no cells were flushed either because the + * channel was not opened or we had no cells on the channel. A negative number + * can NOT be sent back. + * + * This function is part of the fast path. */ MOCK_IMPL(ssize_t, channel_flush_some_cells, (channel_t *chan, ssize_t num_cells)) { unsigned int unlimited = 0; ssize_t flushed = 0; - int num_cells_from_circs, clamped_num_cells; - int q_len_before, q_len_after; + int clamped_num_cells; tor_assert(chan); @@ -2357,11 +1749,6 @@ channel_flush_some_cells, (channel_t *chan, ssize_t num_cells)) /* If we aren't in CHANNEL_STATE_OPEN, nothing goes through */ if (CHANNEL_IS_OPEN(chan)) { - /* Try to flush as much as we can that's already queued */ - flushed += channel_flush_some_cells_from_outgoing_queue(chan, - (unlimited ? -1 : num_cells - flushed)); - if (!unlimited && num_cells <= flushed) goto done; - if (circuitmux_num_cells(chan->cmux) > 0) { /* Calculate number of cells, including clamp */ if (unlimited) { @@ -2375,45 +1762,9 @@ channel_flush_some_cells, (channel_t *chan, ssize_t num_cells)) } } - /* - * Keep track of the change in queue size; we have to count cells - * channel_flush_from_first_active_circuit() writes out directly, - * but not double-count ones we might get later in - * channel_flush_some_cells_from_outgoing_queue() - */ - q_len_before = chan_cell_queue_len(&(chan->outgoing_queue)); - /* Try to get more cells from any active circuits */ - num_cells_from_circs = channel_flush_from_first_active_circuit( + flushed = channel_flush_from_first_active_circuit( chan, clamped_num_cells); - - q_len_after = chan_cell_queue_len(&(chan->outgoing_queue)); - - /* - * If it claims we got some, adjust the flushed counter and consider - * processing the queue again - */ - if (num_cells_from_circs > 0) { - /* - * Adjust flushed by the number of cells counted in - * num_cells_from_circs that didn't go to the cell queue. - */ - - if (q_len_after > q_len_before) { - num_cells_from_circs -= (q_len_after - q_len_before); - if (num_cells_from_circs < 0) num_cells_from_circs = 0; - } - - flushed += num_cells_from_circs; - - /* Now process the queue if necessary */ - - if ((q_len_after > q_len_before) && - (unlimited || (flushed < num_cells))) { - flushed += channel_flush_some_cells_from_outgoing_queue(chan, - (unlimited ? -1 : num_cells - flushed)); - } - } } } @@ -2422,197 +1773,16 @@ channel_flush_some_cells, (channel_t *chan, ssize_t num_cells)) } /** - * Flush cells from just the channel's outgoing cell queue - * - * This gets called from channel_flush_some_cells() above to flush cells - * just from the queue without trying for active_circuits. - */ - -static ssize_t -channel_flush_some_cells_from_outgoing_queue(channel_t *chan, - ssize_t num_cells) -{ - unsigned int unlimited = 0; - ssize_t flushed = 0; - cell_queue_entry_t *q = NULL; - size_t cell_size; - int free_q = 0, handed_off = 0; - - tor_assert(chan); - tor_assert(chan->write_cell); - tor_assert(chan->write_packed_cell); - tor_assert(chan->write_var_cell); - - if (num_cells < 0) unlimited = 1; - if (!unlimited && num_cells <= flushed) return 0; - - /* If we aren't in CHANNEL_STATE_OPEN, nothing goes through */ - if (CHANNEL_IS_OPEN(chan)) { - while ((unlimited || num_cells > flushed) && - NULL != (q = TOR_SIMPLEQ_FIRST(&chan->outgoing_queue))) { - free_q = 0; - handed_off = 0; - - /* Figure out how big it is for statistical purposes */ - cell_size = channel_get_cell_queue_entry_size(chan, q); - /* - * Okay, we have a good queue entry, try to give it to the lower - * layer. - */ - switch (q->type) { - case CELL_QUEUE_FIXED: - if (q->u.fixed.cell) { - if (chan->write_cell(chan, - q->u.fixed.cell)) { - ++flushed; - channel_timestamp_xmit(chan); - ++(chan->n_cells_xmitted); - chan->n_bytes_xmitted += cell_size; - free_q = 1; - handed_off = 1; - } - /* Else couldn't write it; leave it on the queue */ - } else { - /* This shouldn't happen */ - log_info(LD_CHANNEL, - "Saw broken cell queue entry of type CELL_QUEUE_FIXED " - "with no cell on channel %p " - "(global ID " U64_FORMAT ").", - chan, U64_PRINTF_ARG(chan->global_identifier)); - /* Throw it away */ - free_q = 1; - handed_off = 0; - } - break; - case CELL_QUEUE_PACKED: - if (q->u.packed.packed_cell) { - if (chan->write_packed_cell(chan, - q->u.packed.packed_cell)) { - ++flushed; - channel_timestamp_xmit(chan); - ++(chan->n_cells_xmitted); - chan->n_bytes_xmitted += cell_size; - free_q = 1; - handed_off = 1; - } - /* Else couldn't write it; leave it on the queue */ - } else { - /* This shouldn't happen */ - log_info(LD_CHANNEL, - "Saw broken cell queue entry of type CELL_QUEUE_PACKED " - "with no cell on channel %p " - "(global ID " U64_FORMAT ").", - chan, U64_PRINTF_ARG(chan->global_identifier)); - /* Throw it away */ - free_q = 1; - handed_off = 0; - } - break; - case CELL_QUEUE_VAR: - if (q->u.var.var_cell) { - if (chan->write_var_cell(chan, - q->u.var.var_cell)) { - ++flushed; - channel_timestamp_xmit(chan); - ++(chan->n_cells_xmitted); - chan->n_bytes_xmitted += cell_size; - free_q = 1; - handed_off = 1; - } - /* Else couldn't write it; leave it on the queue */ - } else { - /* This shouldn't happen */ - log_info(LD_CHANNEL, - "Saw broken cell queue entry of type CELL_QUEUE_VAR " - "with no cell on channel %p " - "(global ID " U64_FORMAT ").", - chan, U64_PRINTF_ARG(chan->global_identifier)); - /* Throw it away */ - free_q = 1; - handed_off = 0; - } - break; - default: - /* Unknown type, log and free it */ - log_info(LD_CHANNEL, - "Saw an unknown cell queue entry type %d on channel %p " - "(global ID " U64_FORMAT "; ignoring it." - " Someone should fix this.", - q->type, chan, U64_PRINTF_ARG(chan->global_identifier)); - free_q = 1; - handed_off = 0; - } - - /* - * if free_q is set, we used it and should remove the queue entry; - * we have to do the free down here so TOR_SIMPLEQ_REMOVE_HEAD isn't - * accessing freed memory - */ - if (free_q) { - TOR_SIMPLEQ_REMOVE_HEAD(&chan->outgoing_queue, next); - /* - * ...and we handed a cell off to the lower layer, so we should - * update the counters. - */ - ++n_channel_cells_passed_to_lower_layer; - --n_channel_cells_in_queues; - n_channel_bytes_passed_to_lower_layer += cell_size; - n_channel_bytes_in_queues -= cell_size; - channel_assert_counter_consistency(); - /* Update the channel's queue size too */ - chan->bytes_in_queue -= cell_size; - /* Finally, free q */ - cell_queue_entry_free(q, handed_off); - q = NULL; - } else { - /* No cell removed from list, so we can't go on any further */ - break; - } - } - } - - /* Did we drain the queue? */ - if (TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue)) { - channel_timestamp_drained(chan); - } - - /* Update the estimate queue size */ - channel_update_xmit_queue_size(chan); - - return flushed; -} - -/** - * Flush as many cells as we possibly can from the queue - * - * This tries to flush as many cells from the queue as the lower layer - * will take. It just calls channel_flush_some_cells_from_outgoing_queue() - * in unlimited mode. - */ - -void -channel_flush_cells(channel_t *chan) -{ - channel_flush_some_cells_from_outgoing_queue(chan, -1); -} - -/** * Check if any cells are available * - * This gets used from the lower layer to check if any more cells are - * available. + * This is used by the scheduler to know if the channel has more to flush + * after a scheduling round. */ - MOCK_IMPL(int, channel_more_to_flush, (channel_t *chan)) { tor_assert(chan); - /* Check if we have any queued */ - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) - return 1; - - /* Check if any circuits would like to queue some */ if (circuitmux_num_cells(chan->cmux) > 0) return 1; /* Else no */ @@ -2816,207 +1986,31 @@ channel_listener_queue_incoming(channel_listener_t *listener, } /** - * Process queued incoming cells - * - * Process as many queued cells as we can from the incoming - * cell queue. + * Process a cell from the given channel. */ - void -channel_process_cells(channel_t *chan) +channel_process_cell(channel_t *chan, cell_t *cell) { - cell_queue_entry_t *q; tor_assert(chan); tor_assert(CHANNEL_IS_CLOSING(chan) || CHANNEL_IS_MAINT(chan) || CHANNEL_IS_OPEN(chan)); - - log_debug(LD_CHANNEL, - "Processing as many incoming cells as we can for channel %p", - chan); - - /* Nothing we can do if we have no registered cell handlers */ - if (!(chan->cell_handler || - chan->var_cell_handler)) return; - /* Nothing we can do if we have no cells */ - if (TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) return; - - /* - * Process cells until we're done or find one we have no current handler - * for. - * - * We must free the cells here after calling the handler, since custody - * of the buffer was given to the channel layer when they were queued; - * see comments on memory management in channel_queue_cell() and in - * channel_queue_var_cell() below. - */ - while (NULL != (q = TOR_SIMPLEQ_FIRST(&chan->incoming_queue))) { - tor_assert(q); - tor_assert(q->type == CELL_QUEUE_FIXED || - q->type == CELL_QUEUE_VAR); - - if (q->type == CELL_QUEUE_FIXED && - chan->cell_handler) { - /* Handle a fixed-length cell */ - TOR_SIMPLEQ_REMOVE_HEAD(&chan->incoming_queue, next); - tor_assert(q->u.fixed.cell); - log_debug(LD_CHANNEL, - "Processing incoming cell_t %p for channel %p (global ID " - U64_FORMAT ")", - q->u.fixed.cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - chan->cell_handler(chan, q->u.fixed.cell); - tor_free(q->u.fixed.cell); - tor_free(q); - } else if (q->type == CELL_QUEUE_VAR && - chan->var_cell_handler) { - /* Handle a variable-length cell */ - TOR_SIMPLEQ_REMOVE_HEAD(&chan->incoming_queue, next); - tor_assert(q->u.var.var_cell); - log_debug(LD_CHANNEL, - "Processing incoming var_cell_t %p for channel %p (global ID " - U64_FORMAT ")", - q->u.var.var_cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - chan->var_cell_handler(chan, q->u.var.var_cell); - tor_free(q->u.var.var_cell); - tor_free(q); - } else { - /* Can't handle this one */ - break; - } - } -} - -/** - * Queue incoming cell - * - * This should be called by a channel_t subclass to queue an incoming fixed- - * length cell for processing, and process it if possible. - */ - -void -channel_queue_cell(channel_t *chan, cell_t *cell) -{ - int need_to_queue = 0; - cell_queue_entry_t *q; - cell_t *cell_copy = NULL; - - tor_assert(chan); tor_assert(cell); - tor_assert(CHANNEL_IS_OPEN(chan)); - /* Do we need to queue it, or can we just call the handler right away? */ - if (!(chan->cell_handler)) need_to_queue = 1; - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) - need_to_queue = 1; + /* Nothing we can do if we have no registered cell handlers */ + if (!chan->cell_handler) + return; /* Timestamp for receiving */ channel_timestamp_recv(chan); - - /* Update the counters */ + /* Update received counter. */ ++(chan->n_cells_recved); chan->n_bytes_recved += get_cell_network_size(chan->wide_circ_ids); - /* If we don't need to queue we can just call cell_handler */ - if (!need_to_queue) { - tor_assert(chan->cell_handler); - log_debug(LD_CHANNEL, - "Directly handling incoming cell_t %p for channel %p " - "(global ID " U64_FORMAT ")", - cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - chan->cell_handler(chan, cell); - } else { - /* - * Otherwise queue it and then process the queue if possible. - * - * We queue a copy, not the original pointer - it might have been on the - * stack in connection_or_process_cells_from_inbuf() (or another caller - * if we ever have a subclass other than channel_tls_t), or be freed - * there after we return. This is the uncommon case; the non-copying - * fast path occurs in the if (!need_to_queue) case above when the - * upper layer has installed cell handlers. - */ - cell_copy = tor_malloc_zero(sizeof(cell_t)); - memcpy(cell_copy, cell, sizeof(cell_t)); - q = cell_queue_entry_new_fixed(cell_copy); - log_debug(LD_CHANNEL, - "Queueing incoming cell_t %p for channel %p " - "(global ID " U64_FORMAT ")", - cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - TOR_SIMPLEQ_INSERT_TAIL(&chan->incoming_queue, q, next); - if (chan->cell_handler || - chan->var_cell_handler) { - channel_process_cells(chan); - } - } -} - -/** - * Queue incoming variable-length cell - * - * This should be called by a channel_t subclass to queue an incoming - * variable-length cell for processing, and process it if possible. - */ - -void -channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell) -{ - int need_to_queue = 0; - cell_queue_entry_t *q; - var_cell_t *cell_copy = NULL; - - tor_assert(chan); - tor_assert(var_cell); - tor_assert(CHANNEL_IS_OPEN(chan)); - - /* Do we need to queue it, or can we just call the handler right away? */ - if (!(chan->var_cell_handler)) need_to_queue = 1; - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) - need_to_queue = 1; - - /* Timestamp for receiving */ - channel_timestamp_recv(chan); - - /* Update the counter */ - ++(chan->n_cells_recved); - chan->n_bytes_recved += get_var_cell_header_size(chan->wide_circ_ids) + - var_cell->payload_len; - - /* If we don't need to queue we can just call cell_handler */ - if (!need_to_queue) { - tor_assert(chan->var_cell_handler); - log_debug(LD_CHANNEL, - "Directly handling incoming var_cell_t %p for channel %p " - "(global ID " U64_FORMAT ")", - var_cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - chan->var_cell_handler(chan, var_cell); - } else { - /* - * Otherwise queue it and then process the queue if possible. - * - * We queue a copy, not the original pointer - it might have been on the - * stack in connection_or_process_cells_from_inbuf() (or another caller - * if we ever have a subclass other than channel_tls_t), or be freed - * there after we return. This is the uncommon case; the non-copying - * fast path occurs in the if (!need_to_queue) case above when the - * upper layer has installed cell handlers. - */ - cell_copy = var_cell_copy(var_cell); - q = cell_queue_entry_new_var(cell_copy); - log_debug(LD_CHANNEL, - "Queueing incoming var_cell_t %p for channel %p " - "(global ID " U64_FORMAT ")", - var_cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - TOR_SIMPLEQ_INSERT_TAIL(&chan->incoming_queue, q, next); - if (chan->cell_handler || - chan->var_cell_handler) { - channel_process_cells(chan); - } - } + log_debug(LD_CHANNEL, + "Processing incoming cell_t %p for channel %p (global ID " + U64_FORMAT ")", cell, chan, + U64_PRINTF_ARG(chan->global_identifier)); + chan->cell_handler(chan, cell); } /** If <b>packed_cell</b> on <b>chan</b> is a destroy cell, then set @@ -3043,44 +2037,6 @@ packed_cell_is_destroy(channel_t *chan, } /** - * Assert that the global channel stats counters are internally consistent - */ - -static void -channel_assert_counter_consistency(void) -{ - tor_assert(n_channel_cells_queued == - (n_channel_cells_in_queues + n_channel_cells_passed_to_lower_layer)); - tor_assert(n_channel_bytes_queued == - (n_channel_bytes_in_queues + n_channel_bytes_passed_to_lower_layer)); -} - -/* DOCDOC */ -static int -is_destroy_cell(channel_t *chan, - const cell_queue_entry_t *q, circid_t *circid_out) -{ - *circid_out = 0; - switch (q->type) { - case CELL_QUEUE_FIXED: - if (q->u.fixed.cell->command == CELL_DESTROY) { - *circid_out = q->u.fixed.cell->circ_id; - return 1; - } - break; - case CELL_QUEUE_VAR: - if (q->u.var.var_cell->command == CELL_DESTROY) { - *circid_out = q->u.var.var_cell->circ_id; - return 1; - } - break; - case CELL_QUEUE_PACKED: - return packed_cell_is_destroy(chan, q->u.packed.packed_cell, circid_out); - } - return 0; -} - -/** * Send destroy cell on a channel * * Write a destroy cell with circ ID <b>circ_id</b> and reason <b>reason</b> @@ -3134,19 +2090,6 @@ channel_dumpstats(int severity) { if (all_channels && smartlist_len(all_channels) > 0) { tor_log(severity, LD_GENERAL, - "Channels have queued " U64_FORMAT " bytes in " U64_FORMAT " cells, " - "and handed " U64_FORMAT " bytes in " U64_FORMAT " cells to the lower" - " layer.", - U64_PRINTF_ARG(n_channel_bytes_queued), - U64_PRINTF_ARG(n_channel_cells_queued), - U64_PRINTF_ARG(n_channel_bytes_passed_to_lower_layer), - U64_PRINTF_ARG(n_channel_cells_passed_to_lower_layer)); - tor_log(severity, LD_GENERAL, - "There are currently " U64_FORMAT " bytes in " U64_FORMAT " cells " - "in channel queues.", - U64_PRINTF_ARG(n_channel_bytes_in_queues), - U64_PRINTF_ARG(n_channel_cells_in_queues)); - tor_log(severity, LD_GENERAL, "Dumping statistics about %d channels:", smartlist_len(all_channels)); tor_log(severity, LD_GENERAL, @@ -3296,7 +2239,7 @@ channel_free_list(smartlist_t *channels, int mark_for_close) if (!CHANNEL_CONDEMNED(curr)) { channel_mark_for_close(curr); } - channel_force_free(curr); + channel_force_xfree(curr); } else channel_free(curr); } SMARTLIST_FOREACH_END(curr); } @@ -3325,7 +2268,7 @@ channel_listener_free_list(smartlist_t *listeners, int mark_for_close) curr->state == CHANNEL_LISTENER_STATE_ERROR)) { channel_listener_mark_for_close(curr); } - channel_listener_force_free(curr); + channel_listener_force_xfree(curr); } else channel_listener_free(curr); } SMARTLIST_FOREACH_END(curr); } @@ -3629,19 +2572,6 @@ channel_listener_describe_transport(channel_listener_t *chan_l) } /** - * Return the number of entries in <b>queue</b> - */ -STATIC int -chan_cell_queue_len(const chan_cell_queue_t *queue) -{ - int r = 0; - cell_queue_entry_t *cell; - TOR_SIMPLEQ_FOREACH(cell, queue, next) - ++r; - return r; -} - -/** * Dump channel statistics * * Dump statistics for one channel to the log @@ -3676,35 +2606,18 @@ channel_dump_statistics, (channel_t *chan, int severity)) U64_PRINTF_ARG(chan->timestamp_active), U64_PRINTF_ARG(now - chan->timestamp_active)); - /* Handle digest and nickname */ + /* Handle digest. */ if (!tor_digest_is_zero(chan->identity_digest)) { - if (chan->nickname) { - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " says it is connected " - "to an OR with digest %s and nickname %s", - U64_PRINTF_ARG(chan->global_identifier), - hex_str(chan->identity_digest, DIGEST_LEN), - chan->nickname); - } else { - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " says it is connected " - "to an OR with digest %s and no known nickname", - U64_PRINTF_ARG(chan->global_identifier), - hex_str(chan->identity_digest, DIGEST_LEN)); - } + tor_log(severity, LD_GENERAL, + " * Channel " U64_FORMAT " says it is connected " + "to an OR with digest %s", + U64_PRINTF_ARG(chan->global_identifier), + hex_str(chan->identity_digest, DIGEST_LEN)); } else { - if (chan->nickname) { - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " does not know the digest" - " of the OR it is connected to, but reports its nickname is %s", - U64_PRINTF_ARG(chan->global_identifier), - chan->nickname); - } else { - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " does not know the digest" - " or the nickname of the OR it is connected to", - U64_PRINTF_ARG(chan->global_identifier)); - } + tor_log(severity, LD_GENERAL, + " * Channel " U64_FORMAT " does not know the digest" + " of the OR it is connected to", + U64_PRINTF_ARG(chan->global_identifier)); } /* Handle remote address and descriptions */ @@ -3753,14 +2666,6 @@ channel_dump_statistics, (channel_t *chan, int severity)) channel_is_incoming(chan) ? "incoming" : "outgoing"); - /* Describe queues */ - tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " has %d queued incoming cells" - " and %d queued outgoing cells", - U64_PRINTF_ARG(chan->global_identifier), - chan_cell_queue_len(&chan->incoming_queue), - chan_cell_queue_len(&chan->outgoing_queue)); - /* Describe circuits */ tor_log(severity, LD_GENERAL, " * Channel " U64_FORMAT " has %d active circuits out of" @@ -3779,12 +2684,6 @@ channel_dump_statistics, (channel_t *chan, int severity)) U64_PRINTF_ARG(chan->timestamp_client), U64_PRINTF_ARG(now - chan->timestamp_client)); tor_log(severity, LD_GENERAL, - " * Channel " U64_FORMAT " was last drained at " - U64_FORMAT " (" U64_FORMAT " seconds ago)", - U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(chan->timestamp_drained), - U64_PRINTF_ARG(now - chan->timestamp_drained)); - tor_log(severity, LD_GENERAL, " * Channel " U64_FORMAT " last received a cell " "at " U64_FORMAT " (" U64_FORMAT " seconds ago)", U64_PRINTF_ARG(chan->global_identifier), @@ -4027,29 +2926,18 @@ channel_get_addr_if_possible(channel_t *chan, tor_addr_t *addr_out) else return 0; } -/** - * Check if there are outgoing queue writes on this channel - * - * Indicate if either we have queued cells, or if not, whether the underlying - * lower-layer transport thinks it has an output queue. +/* + * Return true iff the channel has any cells on the connection outbuf waiting + * to be sent onto the network. */ - int channel_has_queued_writes(channel_t *chan) { - int has_writes = 0; - tor_assert(chan); tor_assert(chan->has_queued_writes); - if (! TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue)) { - has_writes = 1; - } else { - /* Check with the lower layer */ - has_writes = chan->has_queued_writes(chan); - } - - return has_writes; + /* Check with the lower layer */ + return chan->has_queued_writes(chan); } /** @@ -4274,23 +3162,10 @@ channel_mark_outgoing(channel_t *chan) ***********************/ /* - * Get the latest estimate for the total queue size of all open channels - */ - -uint64_t -channel_get_global_queue_estimate(void) -{ - return estimated_total_queue_size; -} - -/* * Estimate the number of writeable cells * - * Ask the lower layer for an estimate of how many cells it can accept, and - * then subtract the length of our outgoing_queue, if any, to produce an - * estimate of the number of cells this channel can accept for writes. + * Ask the lower layer for an estimate of how many cells it can accept. */ - int channel_num_cells_writeable(channel_t *chan) { @@ -4302,8 +3177,6 @@ channel_num_cells_writeable(channel_t *chan) if (chan->state == CHANNEL_STATE_OPEN) { /* Query lower layer */ result = chan->num_cells_writeable(chan); - /* Subtract cell queue length, if any */ - result -= chan_cell_queue_len(&chan->outgoing_queue); if (result < 0) result = 0; } else { /* No cells are writeable in any other state */ @@ -4368,12 +3241,12 @@ channel_timestamp_active(channel_t *chan) time_t now = time(NULL); tor_assert(chan); - chan->timestamp_xfer_ms = monotime_coarse_absolute_msec(); + monotime_coarse_get(&chan->timestamp_xfer); chan->timestamp_active = now; /* Clear any potential netflow padding timer. We're active */ - chan->next_padding_time_ms = 0; + monotime_coarse_zero(&chan->next_padding_time); } /** @@ -4427,25 +3300,6 @@ channel_timestamp_client(channel_t *chan) } /** - * Update the last drained timestamp - * - * This is called whenever we transmit a cell which leaves the outgoing cell - * queue completely empty. It also updates the xmit time and the active time. - */ - -void -channel_timestamp_drained(channel_t *chan) -{ - time_t now = time(NULL); - - tor_assert(chan); - - chan->timestamp_active = now; - chan->timestamp_drained = now; - chan->timestamp_xmit = now; -} - -/** * Update the recv timestamp * * This is called whenever we get an incoming cell from the lower layer. @@ -4457,13 +3311,13 @@ channel_timestamp_recv(channel_t *chan) { time_t now = time(NULL); tor_assert(chan); - chan->timestamp_xfer_ms = monotime_coarse_absolute_msec(); + monotime_coarse_get(&chan->timestamp_xfer); chan->timestamp_active = now; chan->timestamp_recv = now; /* Clear any potential netflow padding timer. We're active */ - chan->next_padding_time_ms = 0; + monotime_coarse_zero(&chan->next_padding_time); } /** @@ -4478,13 +3332,13 @@ channel_timestamp_xmit(channel_t *chan) time_t now = time(NULL); tor_assert(chan); - chan->timestamp_xfer_ms = monotime_coarse_absolute_msec(); + monotime_coarse_get(&chan->timestamp_xfer); chan->timestamp_active = now; chan->timestamp_xmit = now; /* Clear any potential netflow padding timer. We're active */ - chan->next_padding_time_ms = 0; + monotime_coarse_zero(&chan->next_padding_time); } /*************************************************************** @@ -4504,54 +3358,6 @@ channel_when_created(channel_t *chan) } /** - * Query created timestamp for a channel listener - */ - -time_t -channel_listener_when_created(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - return chan_l->timestamp_created; -} - -/** - * Query last active timestamp for a channel - */ - -time_t -channel_when_last_active(channel_t *chan) -{ - tor_assert(chan); - - return chan->timestamp_active; -} - -/** - * Query last active timestamp for a channel listener - */ - -time_t -channel_listener_when_last_active(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - return chan_l->timestamp_active; -} - -/** - * Query last accepted timestamp for a channel listener - */ - -time_t -channel_listener_when_last_accepted(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - return chan_l->timestamp_accepted; -} - -/** * Query client timestamp */ @@ -4564,30 +3370,6 @@ channel_when_last_client(channel_t *chan) } /** - * Query drained timestamp - */ - -time_t -channel_when_last_drained(channel_t *chan) -{ - tor_assert(chan); - - return chan->timestamp_drained; -} - -/** - * Query recv timestamp - */ - -time_t -channel_when_last_recv(channel_t *chan) -{ - tor_assert(chan); - - return chan->timestamp_recv; -} - -/** * Query xmit timestamp */ @@ -4600,42 +3382,6 @@ channel_when_last_xmit(channel_t *chan) } /** - * Query accepted counter - */ - -uint64_t -channel_listener_count_accepted(channel_listener_t *chan_l) -{ - tor_assert(chan_l); - - return chan_l->n_accepted; -} - -/** - * Query received cell counter - */ - -uint64_t -channel_count_recved(channel_t *chan) -{ - tor_assert(chan); - - return chan->n_cells_recved; -} - -/** - * Query transmitted cell counter - */ - -uint64_t -channel_count_xmitted(channel_t *chan) -{ - tor_assert(chan); - - return chan->n_cells_xmitted; -} - -/** * Check if a channel matches an extend_info_t * * This function calls the lower layer and asks if this channel matches a @@ -4726,6 +3472,16 @@ channel_set_circid_type,(channel_t *chan, } } +static int +channel_sort_by_ed25519_identity(const void **a_, const void **b_) +{ + const channel_t *a = *a_, + *b = *b_; + return fast_memcmp(&a->ed25519_identity.pubkey, + &b->ed25519_identity.pubkey, + sizeof(a->ed25519_identity.pubkey)); +} + /** Helper for channel_update_bad_for_new_circs(): Perform the * channel_update_bad_for_new_circs operation on all channels in <b>lst</b>, * all of which MUST have the same RSA ID. (They MAY have different @@ -4734,44 +3490,52 @@ static void channel_rsa_id_group_set_badness(struct channel_list_s *lst, int force) { /*XXXX This function should really be about channels. 15056 */ - channel_t *chan; + channel_t *chan = TOR_LIST_FIRST(lst); + + if (!chan) + return; + + /* if there is only one channel, don't bother looping */ + if (PREDICT_LIKELY(!TOR_LIST_NEXT(chan, next_with_same_id))) { + connection_or_single_set_badness_( + time(NULL), BASE_CHAN_TO_TLS(chan)->conn, force); + return; + } + + smartlist_t *channels = smartlist_new(); - /* First, get a minimal list of the ed25519 identites */ - smartlist_t *ed_identities = smartlist_new(); TOR_LIST_FOREACH(chan, lst, next_with_same_id) { - uint8_t *id_copy = - tor_memdup(&chan->ed25519_identity.pubkey, DIGEST256_LEN); - smartlist_add(ed_identities, id_copy); + if (BASE_CHAN_TO_TLS(chan)->conn) { + smartlist_add(channels, chan); + } } - smartlist_sort_digests256(ed_identities); - smartlist_uniq_digests256(ed_identities); - /* Now, for each Ed identity, build a smartlist and find the best entry on - * it. */ + smartlist_sort(channels, channel_sort_by_ed25519_identity); + + const ed25519_public_key_t *common_ed25519_identity = NULL; + /* it would be more efficient to do a slice, but this case is rare */ smartlist_t *or_conns = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(ed_identities, const uint8_t *, ed_id) { - TOR_LIST_FOREACH(chan, lst, next_with_same_id) { - channel_tls_t *chantls = BASE_CHAN_TO_TLS(chan); - if (tor_memneq(ed_id, &chan->ed25519_identity.pubkey, DIGEST256_LEN)) - continue; - or_connection_t *orconn = chantls->conn; - if (orconn) { - tor_assert(orconn->chan == chantls); - smartlist_add(or_conns, orconn); - } + SMARTLIST_FOREACH_BEGIN(channels, channel_t *, channel) { + if (!common_ed25519_identity) + common_ed25519_identity = &channel->ed25519_identity; + + if (! ed25519_pubkey_eq(&channel->ed25519_identity, + common_ed25519_identity)) { + connection_or_group_set_badness_(or_conns, force); + smartlist_clear(or_conns); + common_ed25519_identity = &channel->ed25519_identity; } - connection_or_group_set_badness_(or_conns, force); - smartlist_clear(or_conns); - } SMARTLIST_FOREACH_END(ed_id); + smartlist_add(or_conns, BASE_CHAN_TO_TLS(channel)->conn); + } SMARTLIST_FOREACH_END(channel); + + connection_or_group_set_badness_(or_conns, force); /* XXXX 15056 we may want to do something special with connections that have * no set Ed25519 identity! */ smartlist_free(or_conns); - - SMARTLIST_FOREACH(ed_identities, uint8_t *, ed_id, tor_free(ed_id)); - smartlist_free(ed_identities); + smartlist_free(channels); } /** Go through all the channels (or if <b>digest</b> is non-NULL, just @@ -4801,83 +3565,3 @@ channel_update_bad_for_new_circs(const char *digest, int force) } } -/** - * Update the estimated number of bytes queued to transmit for this channel, - * and notify the scheduler. The estimate includes both the channel queue and - * the queue size reported by the lower layer, and an overhead estimate - * optionally provided by the lower layer. - */ - -void -channel_update_xmit_queue_size(channel_t *chan) -{ - uint64_t queued, adj; - double overhead; - - tor_assert(chan); - tor_assert(chan->num_bytes_queued); - - /* - * First, get the number of bytes we have queued without factoring in - * lower-layer overhead. - */ - queued = chan->num_bytes_queued(chan) + chan->bytes_in_queue; - /* Next, adjust by the overhead factor, if any is available */ - if (chan->get_overhead_estimate) { - overhead = chan->get_overhead_estimate(chan); - if (overhead >= 1.0) { - queued = (uint64_t)(queued * overhead); - } else { - /* Ignore silly overhead factors */ - log_notice(LD_CHANNEL, "Ignoring silly overhead factor %f", overhead); - } - } - - /* Now, compare to the previous estimate */ - if (queued > chan->bytes_queued_for_xmit) { - adj = queued - chan->bytes_queued_for_xmit; - log_debug(LD_CHANNEL, - "Increasing queue size for channel " U64_FORMAT " by " U64_FORMAT - " from " U64_FORMAT " to " U64_FORMAT, - U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(adj), - U64_PRINTF_ARG(chan->bytes_queued_for_xmit), - U64_PRINTF_ARG(queued)); - /* Update the channel's estimate */ - chan->bytes_queued_for_xmit = queued; - - /* Update the global queue size estimate if appropriate */ - if (chan->state == CHANNEL_STATE_OPEN || - chan->state == CHANNEL_STATE_MAINT) { - estimated_total_queue_size += adj; - log_debug(LD_CHANNEL, - "Increasing global queue size by " U64_FORMAT " for channel " - U64_FORMAT ", new size is " U64_FORMAT, - U64_PRINTF_ARG(adj), U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(estimated_total_queue_size)); - } - } else if (queued < chan->bytes_queued_for_xmit) { - adj = chan->bytes_queued_for_xmit - queued; - log_debug(LD_CHANNEL, - "Decreasing queue size for channel " U64_FORMAT " by " U64_FORMAT - " from " U64_FORMAT " to " U64_FORMAT, - U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(adj), - U64_PRINTF_ARG(chan->bytes_queued_for_xmit), - U64_PRINTF_ARG(queued)); - /* Update the channel's estimate */ - chan->bytes_queued_for_xmit = queued; - - /* Update the global queue size estimate if appropriate */ - if (chan->state == CHANNEL_STATE_OPEN || - chan->state == CHANNEL_STATE_MAINT) { - estimated_total_queue_size -= adj; - log_debug(LD_CHANNEL, - "Decreasing global queue size by " U64_FORMAT " for channel " - U64_FORMAT ", new size is " U64_FORMAT, - U64_PRINTF_ARG(adj), U64_PRINTF_ARG(chan->global_identifier), - U64_PRINTF_ARG(estimated_total_queue_size)); - } - } -} - diff --git a/src/or/channel.h b/src/or/channel.h index 32336fe1d2..4b60b7a7d6 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -19,10 +19,6 @@ typedef void (*channel_listener_fn_ptr)(channel_listener_t *, channel_t *); typedef void (*channel_cell_handler_fn_ptr)(channel_t *, cell_t *); typedef void (*channel_var_cell_handler_fn_ptr)(channel_t *, var_cell_t *); -struct cell_queue_entry_s; -TOR_SIMPLEQ_HEAD(chan_cell_queue, cell_queue_entry_s); -typedef struct chan_cell_queue chan_cell_queue_t; - /** * This enum is used by channelpadding to decide when to pad channels. * Don't add values to it without updating the checks in @@ -92,10 +88,9 @@ struct channel_s { * Used to decide what channels to pad, and when. */ channel_usage_info_t channel_usage; - /** When should we send a cell for netflow padding, in absolute - * milliseconds since monotime system start. 0 means no padding - * is scheduled. */ - uint64_t next_padding_time_ms; + /** When should we send a cell for netflow padding? 0 means no padding is + * scheduled. */ + monotime_coarse_t next_padding_time; /** The callback pointer for the padding callbacks */ tor_timer_t *padding_timer; @@ -162,7 +157,7 @@ struct channel_s { time_t timestamp_active; /* Any activity */ /** - * This is a high-resolution monotonic timestamp that marks when we + * This is a monotonic timestamp that marks when we * believe the channel has actually sent or received data to/from * the wire. Right now, it is used to determine when we should send * a padding cell for channelpadding. @@ -171,7 +166,7 @@ struct channel_s { * accurately reflect actual network data transfer? Or might this be * very wrong wrt when bytes actually go on the wire? */ - uint64_t timestamp_xfer_ms; + monotime_coarse_t timestamp_xfer; /* Methods implemented by the lower layer */ @@ -259,21 +254,12 @@ struct channel_s { */ ed25519_public_key_t ed25519_identity; - /** Nickname of the OR on the other side, or NULL if none. */ - char *nickname; - /** * Linked list of channels with the same RSA identity digest, for use with * the digest->channel map */ TOR_LIST_ENTRY(channel_s) next_with_same_id; - /** List of incoming cells to handle */ - chan_cell_queue_t incoming_queue; - - /** List of queued outgoing cells */ - chan_cell_queue_t outgoing_queue; - /** Circuit mux for circuits sending on this channel */ circuitmux_t *cmux; @@ -320,7 +306,6 @@ struct channel_s { /** Channel timestamps for cell channels */ time_t timestamp_client; /* Client used this, according to relay.c */ - time_t timestamp_drained; /* Output queue empty */ time_t timestamp_recv; /* Cell received from lower layer */ time_t timestamp_xmit; /* Cell sent to lower layer */ @@ -337,14 +322,6 @@ struct channel_s { /** Channel counters for cell channels */ uint64_t n_cells_recved, n_bytes_recved; uint64_t n_cells_xmitted, n_bytes_xmitted; - - /** Our current contribution to the scheduler's total xmit queue */ - uint64_t bytes_queued_for_xmit; - - /** Number of bytes in this channel's cell queue; does not include - * lower-layer queueing. - */ - uint64_t bytes_in_queue; }; struct channel_listener_s { @@ -412,18 +389,13 @@ channel_listener_state_to_string(channel_listener_state_t state); /* Abstract channel operations */ void channel_mark_for_close(channel_t *chan); -void channel_write_cell(channel_t *chan, cell_t *cell); -void channel_write_packed_cell(channel_t *chan, packed_cell_t *cell); -void channel_write_var_cell(channel_t *chan, var_cell_t *cell); +int channel_write_packed_cell(channel_t *chan, packed_cell_t *cell); void channel_listener_mark_for_close(channel_listener_t *chan_l); /* Channel callback registrations */ /* Listener callback */ -channel_listener_fn_ptr -channel_listener_get_listener_fn(channel_listener_t *chan); - void channel_listener_set_listener_fn(channel_listener_t *chan, channel_listener_fn_ptr listener); @@ -457,36 +429,9 @@ void channel_set_cmux_policy_everywhere(circuitmux_policy_t *pol); #ifdef TOR_CHANNEL_INTERNAL_ #ifdef CHANNEL_PRIVATE_ -/* Cell queue structure (here rather than channel.c for test suite use) */ - -typedef struct cell_queue_entry_s cell_queue_entry_t; -struct cell_queue_entry_s { - TOR_SIMPLEQ_ENTRY(cell_queue_entry_s) next; - enum { - CELL_QUEUE_FIXED, - CELL_QUEUE_VAR, - CELL_QUEUE_PACKED - } type; - union { - struct { - cell_t *cell; - } fixed; - struct { - var_cell_t *var_cell; - } var; - struct { - packed_cell_t *packed_cell; - } packed; - } u; -}; - -/* Cell queue functions for benefit of test suite */ -STATIC int chan_cell_queue_len(const chan_cell_queue_t *queue); -STATIC void cell_queue_entry_free(cell_queue_entry_t *q, int handed_off); +STATIC void channel_add_to_digest_map(channel_t *chan); -void channel_write_cell_generic_(channel_t *chan, const char *cell_type, - void *cell, cell_queue_entry_t *q); #endif /* defined(CHANNEL_PRIVATE_) */ /* Channel operations for subclasses and internal use only */ @@ -511,13 +456,12 @@ void channel_close_from_lower_layer(channel_t *chan); void channel_close_for_error(channel_t *chan); void channel_closed(channel_t *chan); -void channel_listener_close_from_lower_layer(channel_listener_t *chan_l); -void channel_listener_close_for_error(channel_listener_t *chan_l); -void channel_listener_closed(channel_listener_t *chan_l); - /* Free a channel */ -void channel_free(channel_t *chan); -void channel_listener_free(channel_listener_t *chan_l); +void channel_free_(channel_t *chan); +#define channel_free(chan) FREE_AND_NULL(channel_t, channel_free_, (chan)) +void channel_listener_free_(channel_listener_t *chan_l); +#define channel_listener_free(chan_l) \ + FREE_AND_NULL(channel_listener_t, channel_listener_free_, (chan_l)) /* State/metadata setters */ @@ -532,9 +476,6 @@ void channel_mark_remote(channel_t *chan); void channel_set_identity_digest(channel_t *chan, const char *identity_digest, const ed25519_public_key_t *ed_identity); -void channel_set_remote_end(channel_t *chan, - const char *identity_digest, - const char *nickname); void channel_listener_change_state(channel_listener_t *chan_l, channel_listener_state_t to_state); @@ -542,7 +483,6 @@ void channel_listener_change_state(channel_listener_t *chan_l, /* Timestamp updates */ void channel_timestamp_created(channel_t *chan); void channel_timestamp_active(channel_t *chan); -void channel_timestamp_drained(channel_t *chan); void channel_timestamp_recv(channel_t *chan); void channel_timestamp_xmit(channel_t *chan); @@ -556,12 +496,7 @@ void channel_listener_queue_incoming(channel_listener_t *listener, channel_t *incoming); /* Incoming cell handling */ -void channel_process_cells(channel_t *chan); -void channel_queue_cell(channel_t *chan, cell_t *cell); -void channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell); - -/* Outgoing cell handling */ -void channel_flush_cells(channel_t *chan); +void channel_process_cell(channel_t *chan, cell_t *cell); /* Request from lower layer for more cells if available */ MOCK_DECL(ssize_t, channel_flush_some_cells, @@ -576,10 +511,6 @@ void channel_notify_flushed(channel_t *chan); /* Handle stuff we need to do on open like notifying circuits */ void channel_do_open_actions(channel_t *chan); -#ifdef TOR_UNIT_TESTS -extern uint64_t estimated_total_queue_size; -#endif - #endif /* defined(TOR_CHANNEL_INTERNAL_) */ /* Helper functions to perform operations on channels */ @@ -680,7 +611,6 @@ MOCK_DECL(void,channel_set_circid_type,(channel_t *chan, crypto_pk_t *identity_rcvd, int consider_identity)); void channel_timestamp_client(channel_t *chan); -void channel_update_xmit_queue_size(channel_t *chan); const char * channel_listener_describe_transport(channel_listener_t *chan_l); void channel_listener_dump_statistics(channel_listener_t *chan_l, @@ -692,33 +622,22 @@ void channel_check_for_duplicates(void); void channel_update_bad_for_new_circs(const char *digest, int force); /* Flow control queries */ -uint64_t channel_get_global_queue_estimate(void); int channel_num_cells_writeable(channel_t *chan); /* Timestamp queries */ time_t channel_when_created(channel_t *chan); -time_t channel_when_last_active(channel_t *chan); time_t channel_when_last_client(channel_t *chan); -time_t channel_when_last_drained(channel_t *chan); -time_t channel_when_last_recv(channel_t *chan); time_t channel_when_last_xmit(channel_t *chan); -time_t channel_listener_when_created(channel_listener_t *chan_l); -time_t channel_listener_when_last_active(channel_listener_t *chan_l); -time_t channel_listener_when_last_accepted(channel_listener_t *chan_l); - /* Counter queries */ -uint64_t channel_count_recved(channel_t *chan); -uint64_t channel_count_xmitted(channel_t *chan); - -uint64_t channel_listener_count_accepted(channel_listener_t *chan_l); - int packed_cell_is_destroy(channel_t *chan, const packed_cell_t *packed_cell, circid_t *circid_out); /* Declare the handle helpers */ HANDLE_DECL(channel, channel_s,) +#define channel_handle_free(h) \ + FREE_AND_NULL(channel_handle_t, channel_handle_free_, (h)) #endif /* !defined(TOR_CHANNEL_H) */ diff --git a/src/or/channelpadding.c b/src/or/channelpadding.c index 435436c45c..70a906d232 100644 --- a/src/or/channelpadding.c +++ b/src/or/channelpadding.c @@ -23,7 +23,8 @@ #include <event2/event.h> #include "rendservice.h" -STATIC int channelpadding_get_netflow_inactive_timeout_ms(const channel_t *); +STATIC int32_t channelpadding_get_netflow_inactive_timeout_ms( + const channel_t *); STATIC int channelpadding_send_disable_command(channel_t *); STATIC int64_t channelpadding_compute_time_until_pad_for_netflow(channel_t *); @@ -165,7 +166,7 @@ channelpadding_new_consensus_params(networkstatus_t *ns) * Returns the next timeout period (in milliseconds) after which we should * send a padding packet, or 0 if padding is disabled. */ -STATIC int +STATIC int32_t channelpadding_get_netflow_inactive_timeout_ms(const channel_t *chan) { int low_timeout = consensus_nf_ito_low; @@ -377,15 +378,16 @@ channelpadding_send_padding_cell_for_callback(channel_t *chan) chan->pending_padding_callback = 0; - if (!chan->next_padding_time_ms || + if (monotime_coarse_is_zero(&chan->next_padding_time) || chan->has_queued_writes(chan)) { /* We must have been active before the timer fired */ - chan->next_padding_time_ms = 0; + monotime_coarse_zero(&chan->next_padding_time); return; } { - uint64_t now = monotime_coarse_absolute_msec(); + monotime_coarse_t now; + monotime_coarse_get(&now); log_fn(LOG_INFO,LD_OR, "Sending netflow keepalive on "U64_FORMAT" to %s (%s) after " @@ -393,12 +395,13 @@ channelpadding_send_padding_cell_for_callback(channel_t *chan) U64_PRINTF_ARG(chan->global_identifier), safe_str_client(chan->get_remote_descr(chan, 0)), safe_str_client(hex_str(chan->identity_digest, DIGEST_LEN)), - U64_PRINTF_ARG(now - chan->timestamp_xfer_ms), - U64_PRINTF_ARG(now - chan->next_padding_time_ms)); + I64_PRINTF_ARG(monotime_coarse_diff_msec(&chan->timestamp_xfer,&now)), + I64_PRINTF_ARG( + monotime_coarse_diff_msec(&chan->next_padding_time,&now))); } /* Clear the timer */ - chan->next_padding_time_ms = 0; + monotime_coarse_zero(&chan->next_padding_time); /* Send the padding cell. This will cause the channel to get a * fresh timestamp_active */ @@ -500,38 +503,42 @@ channelpadding_schedule_padding(channel_t *chan, int in_ms) STATIC int64_t channelpadding_compute_time_until_pad_for_netflow(channel_t *chan) { - uint64_t long_now = monotime_coarse_absolute_msec(); + monotime_coarse_t now; + monotime_coarse_get(&now); - if (!chan->next_padding_time_ms) { + if (monotime_coarse_is_zero(&chan->next_padding_time)) { /* If the below line or crypto_rand_int() shows up on a profile, * we can avoid getting a timeout until we're at least nf_ito_lo * from a timeout window. That will prevent us from setting timers * on connections that were active up to 1.5 seconds ago. * Idle connections should only call this once every 5.5s on average * though, so that might be a micro-optimization for little gain. */ - int64_t padding_timeout = + int32_t padding_timeout = channelpadding_get_netflow_inactive_timeout_ms(chan); if (!padding_timeout) return CHANNELPADDING_TIME_DISABLED; - chan->next_padding_time_ms = padding_timeout - + chan->timestamp_xfer_ms; + monotime_coarse_add_msec(&chan->next_padding_time, + &chan->timestamp_xfer, + padding_timeout); } + const int64_t ms_till_pad = + monotime_coarse_diff_msec(&now, &chan->next_padding_time); + /* If the next padding time is beyond the maximum possible consensus value, * then this indicates a clock jump, so just send padding now. This is * better than using monotonic time because we want to avoid the situation * where we wait around forever for monotonic time to move forward after * a clock jump far into the past. */ - if (chan->next_padding_time_ms > long_now + - DFLT_NETFLOW_INACTIVE_KEEPALIVE_MAX) { + if (ms_till_pad > DFLT_NETFLOW_INACTIVE_KEEPALIVE_MAX) { tor_fragile_assert(); log_warn(LD_BUG, "Channel padding timeout scheduled "I64_FORMAT"ms in the future. " "Did the monotonic clock just jump?", - I64_PRINTF_ARG(chan->next_padding_time_ms - long_now)); + I64_PRINTF_ARG(ms_till_pad)); return 0; /* Clock jumped: Send padding now */ } @@ -540,11 +547,8 @@ channelpadding_compute_time_until_pad_for_netflow(channel_t *chan) from now which we should send padding, so we can schedule a callback then. */ - if (long_now + - (TOR_HOUSEKEEPING_CALLBACK_MSEC + TOR_HOUSEKEEPING_CALLBACK_SLACK_MSEC) - >= chan->next_padding_time_ms) { - int64_t ms_until_pad_for_netflow = chan->next_padding_time_ms - - long_now; + if (ms_till_pad < (TOR_HOUSEKEEPING_CALLBACK_MSEC + + TOR_HOUSEKEEPING_CALLBACK_SLACK_MSEC)) { /* If the padding time is in the past, that means that libevent delayed * calling the once-per-second callback due to other work taking too long. * See https://bugs.torproject.org/22212 and @@ -554,16 +558,16 @@ channelpadding_compute_time_until_pad_for_netflow(channel_t *chan) * and allowed a router to emit a netflow frame, just so we don't forget * about it entirely.. */ #define NETFLOW_MISSED_WINDOW (150000 - DFLT_NETFLOW_INACTIVE_KEEPALIVE_HIGH) - if (ms_until_pad_for_netflow < 0) { - int severity = (ms_until_pad_for_netflow < -NETFLOW_MISSED_WINDOW) + if (ms_till_pad < 0) { + int severity = (ms_till_pad < -NETFLOW_MISSED_WINDOW) ? LOG_NOTICE : LOG_INFO; log_fn(severity, LD_OR, "Channel padding timeout scheduled "I64_FORMAT"ms in the past. ", - I64_PRINTF_ARG(-ms_until_pad_for_netflow)); + I64_PRINTF_ARG(-ms_till_pad)); return 0; /* Clock jumped: Send padding now */ } - return ms_until_pad_for_netflow; + return ms_till_pad; } return CHANNELPADDING_TIME_LATER; } diff --git a/src/or/channeltls.c b/src/or/channeltls.c index 8277813186..023ccdefd3 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -832,6 +832,9 @@ channel_tls_write_cell_method(channel_t *chan, cell_t *cell) * * This implements the write_packed_cell method for channel_tls_t; given a * channel_tls_t and a packed_cell_t, transmit the packed_cell_t. + * + * Return 0 on success or negative value on error. The caller must free the + * packed cell. */ static int @@ -841,7 +844,6 @@ channel_tls_write_packed_cell_method(channel_t *chan, tor_assert(chan); channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); size_t cell_network_size = get_cell_network_size(chan->wide_circ_ids); - int written = 0; tor_assert(tlschan); tor_assert(packed_cell); @@ -849,18 +851,15 @@ channel_tls_write_packed_cell_method(channel_t *chan, if (tlschan->conn) { connection_buf_add(packed_cell->body, cell_network_size, TO_CONN(tlschan->conn)); - - /* This is where the cell is finished; used to be done from relay.c */ - packed_cell_free(packed_cell); - ++written; } else { log_info(LD_CHANNEL, "something called write_packed_cell on a tlschan " "(%p with ID " U64_FORMAT " but no conn", chan, U64_PRINTF_ARG(chan->global_identifier)); + return -1; } - return written; + return 0; } /** @@ -1149,7 +1148,7 @@ channel_tls_handle_cell(cell_t *cell, or_connection_t *conn) * These are all transport independent and we pass them up through the * channel_t mechanism. They are ultimately handled in command.c. */ - channel_queue_cell(TLS_CHAN_TO_BASE(chan), cell); + channel_process_cell(TLS_CHAN_TO_BASE(chan), cell); break; default: log_fn(LOG_INFO, LD_PROTOCOL, diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index b36fed63b3..a350f6c142 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -75,7 +75,7 @@ static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit, int is_hs_v3_rp_circuit); static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath); static int onion_extend_cpath(origin_circuit_t *circ); -static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice); +STATIC int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice); static int circuit_send_first_onion_skin(origin_circuit_t *circ); static int circuit_build_no_more_hops(origin_circuit_t *circ); static int circuit_send_intermediate_onion_skin(origin_circuit_t *circ, @@ -631,8 +631,7 @@ circuit_n_chan_done(channel_t *chan, int status, int close_origin_circuits) tor_assert(chan); - log_debug(LD_CIRC,"chan to %s/%s, status=%d", - chan->nickname ? chan->nickname : "NULL", + log_debug(LD_CIRC,"chan to %s, status=%d", channel_get_canonical_remote_descr(chan), status); pending_circs = smartlist_new(); @@ -825,16 +824,25 @@ should_use_create_fast_for_circuit(origin_circuit_t *circ) return networkstatus_get_param(NULL, "usecreatefast", 0, 0, 1); } -/** Return true if <b>circ</b> is the type of circuit we want to count - * timeouts from. In particular, we want it to have not completed yet - * (already completing indicates we cannibalized it), and we want it to - * have exactly three hops. +/** + * Return true if <b>circ</b> is the type of circuit we want to count + * timeouts from. + * + * In particular, we want to consider any circuit that plans to build + * at least 3 hops (but maybe more), but has 3 or fewer hops built + * so far. + * + * We still want to consider circuits before 3 hops, because we need + * to decide if we should convert them to a measurement circuit in + * circuit_build_times_handle_completed_hop(), rather than letting + * slow circuits get killed right away. */ int -circuit_timeout_want_to_count_circ(origin_circuit_t *circ) +circuit_timeout_want_to_count_circ(const origin_circuit_t *circ) { return !circ->has_opened - && circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN; + && circ->build_state->desired_path_len >= DEFAULT_ROUTE_LEN + && circuit_get_cpath_opened_len(circ) <= DEFAULT_ROUTE_LEN; } /** Decide whether to use a TAP or ntor handshake for connecting to <b>ei</b> @@ -938,7 +946,9 @@ circuit_send_next_onion_skin(origin_circuit_t *circ) tor_assert(circ->cpath->state == CPATH_STATE_OPEN); tor_assert(circ->base_.state == CIRCUIT_STATE_BUILDING); + crypt_path_t *hop = onion_next_hop_in_cpath(circ->cpath); + circuit_build_times_handle_completed_hop(circ); if (hop) { /* Case two: we're on a hop after the first. */ @@ -1053,38 +1063,6 @@ circuit_build_no_more_hops(origin_circuit_t *circ) * I think I got them right, but more checking would be wise. -NM */ - if (circuit_timeout_want_to_count_circ(circ)) { - struct timeval end; - long timediff; - tor_gettimeofday(&end); - timediff = tv_mdiff(&circ->base_.timestamp_began, &end); - - /* - * If the circuit build time is much greater than we would have cut - * it off at, we probably had a suspend event along this codepath, - * and we should discard the value. - */ - if (timediff < 0 || - timediff > 2*get_circuit_build_close_time_ms()+1000) { - log_notice(LD_CIRC, "Strange value for circuit build time: %ldmsec. " - "Assuming clock jump. Purpose %d (%s)", timediff, - circ->base_.purpose, - circuit_purpose_to_string(circ->base_.purpose)); - } else if (!circuit_build_times_disabled(get_options())) { - /* Only count circuit times if the network is live */ - if (circuit_build_times_network_check_live( - get_circuit_build_times())) { - circuit_build_times_add_time(get_circuit_build_times_mutable(), - (build_time_t)timediff); - circuit_build_times_set_timeout(get_circuit_build_times_mutable()); - } - - if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { - circuit_build_times_network_circ_success( - get_circuit_build_times_mutable()); - } - } - } log_info(LD_CIRC,"circuit built!"); circuit_reset_failure_count(0); @@ -1290,7 +1268,7 @@ circuit_extend(cell_t *cell, circuit_t *circ) const node_t *node = node_get_by_id((const char*)ec.node_id); const ed25519_public_key_t *node_ed_id = NULL; if (node && - node_supports_ed25519_link_authentication(node) && + node_supports_ed25519_link_authentication(node, 1) && (node_ed_id = node_get_ed25519_id(node))) { ed25519_pubkey_copy(&ec.ed_pubkey, node_ed_id); } @@ -2607,7 +2585,7 @@ onion_extend_cpath(origin_circuit_t *circ) /** Create a new hop, annotate it with information about its * corresponding router <b>choice</b>, and append it to the * end of the cpath <b>head_ptr</b>. */ -static int +STATIC int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice) { crypt_path_t *hop = tor_malloc_zero(sizeof(crypt_path_t)); @@ -2698,7 +2676,7 @@ extend_info_from_node(const node_t *node, int for_direct_connect) /* Don't send the ed25519 pubkey unless the target node actually supports * authenticating with it. */ - if (node_supports_ed25519_link_authentication(node)) { + if (node_supports_ed25519_link_authentication(node, 0)) { log_info(LD_CIRC, "Including Ed25519 ID for %s", node_describe(node)); ed_pubkey = node_get_ed25519_id(node); } else if (node_get_ed25519_id(node)) { @@ -2707,12 +2685,16 @@ extend_info_from_node(const node_t *node, int for_direct_connect) node_describe(node)); } + /* Retrieve the curve25519 pubkey. */ + const curve25519_public_key_t *curve_pubkey = + node_get_curve25519_onion_key(node); + if (valid_addr && node->ri) return extend_info_new(node->ri->nickname, node->identity, ed_pubkey, node->ri->onion_pkey, - node->ri->onion_curve25519_pkey, + curve_pubkey, &ap.addr, ap.port); else if (valid_addr && node->rs && node->md) @@ -2720,7 +2702,7 @@ extend_info_from_node(const node_t *node, int for_direct_connect) node->identity, ed_pubkey, node->md->onion_pkey, - node->md->onion_curve25519_pkey, + curve_pubkey, &ap.addr, ap.port); else @@ -2729,7 +2711,7 @@ extend_info_from_node(const node_t *node, int for_direct_connect) /** Release storage held by an extend_info_t struct. */ void -extend_info_free(extend_info_t *info) +extend_info_free_(extend_info_t *info) { if (!info) return; diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index b8a651e055..a85ef672f9 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -27,7 +27,7 @@ int circuit_handle_first_hop(origin_circuit_t *circ); void circuit_n_chan_done(channel_t *chan, int status, int close_origin_circuits); int inform_testing_reachability(void); -int circuit_timeout_want_to_count_circ(origin_circuit_t *circ); +int circuit_timeout_want_to_count_circ(const origin_circuit_t *circ); int circuit_send_next_onion_skin(origin_circuit_t *circ); void circuit_note_clock_jumped(int seconds_elapsed); int circuit_extend(cell_t *cell, circuit_t *circ); @@ -58,7 +58,9 @@ extend_info_t *extend_info_new(const char *nickname, const tor_addr_t *addr, uint16_t port); extend_info_t *extend_info_from_node(const node_t *r, int for_direct_connect); extend_info_t *extend_info_dup(extend_info_t *info); -void extend_info_free(extend_info_t *info); +void extend_info_free_(extend_info_t *info); +#define extend_info_free(info) \ + FREE_AND_NULL(extend_info_t, extend_info_free_, (info)) int extend_info_addr_is_allowed(const tor_addr_t *addr); int extend_info_supports_tap(const extend_info_t* ei); int extend_info_supports_ntor(const extend_info_t* ei); diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index d442887c9e..f074025a39 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -108,6 +108,14 @@ static void cpath_ref_decref(crypt_path_reference_t *cpath_ref); static void circuit_about_to_free_atexit(circuit_t *circ); static void circuit_about_to_free(circuit_t *circ); +/** + * A cached value of the current state of the origin circuit list. Has the + * value 1 if we saw any opened circuits recently (since the last call to + * circuit_any_opened_circuits(), which gets called around once a second by + * circuit_expire_building). 0 otherwise. + */ +static int any_opened_circs_cached_val = 0; + /********* END VARIABLES ************/ /** A map from channel and circuit ID to circuit. (Lookup performance is @@ -504,8 +512,7 @@ circuit_count_pending_on_channel(channel_t *chan) circuit_get_all_pending_on_channel(sl, chan); cnt = smartlist_len(sl); smartlist_free(sl); - log_debug(LD_CIRC,"or_conn to %s at %s, %d pending circs", - chan->nickname ? chan->nickname : "NULL", + log_debug(LD_CIRC,"or_conn to %s, %d pending circs", channel_get_canonical_remote_descr(chan), cnt); return cnt; @@ -596,6 +603,56 @@ circuit_get_global_origin_circuit_list(void) return global_origin_circuit_list; } +/** + * Return true if we have any opened general-purpose 3 hop + * origin circuits. + * + * The result from this function is cached for use by + * circuit_any_opened_circuits_cached(). + */ +int +circuit_any_opened_circuits(void) +{ + SMARTLIST_FOREACH_BEGIN(circuit_get_global_origin_circuit_list(), + const origin_circuit_t *, next_circ) { + if (!TO_CIRCUIT(next_circ)->marked_for_close && + next_circ->has_opened && + TO_CIRCUIT(next_circ)->state == CIRCUIT_STATE_OPEN && + TO_CIRCUIT(next_circ)->purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT && + next_circ->build_state && + next_circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN) { + circuit_cache_opened_circuit_state(1); + return 1; + } + } SMARTLIST_FOREACH_END(next_circ); + + circuit_cache_opened_circuit_state(0); + return 0; +} + +/** + * Cache the "any circuits opened" state, as specified in param + * circuits_are_opened. This is a helper function to update + * the circuit opened status whenever we happen to look at the + * circuit list. + */ +void +circuit_cache_opened_circuit_state(int circuits_are_opened) +{ + any_opened_circs_cached_val = circuits_are_opened; +} + +/** + * Return true if there were any opened circuits since the last call to + * circuit_any_opened_circuits(), or since circuit_expire_building() last + * ran (it runs roughly once per second). + */ +int +circuit_any_opened_circuits_cached(void) +{ + return any_opened_circs_cached_val; +} + /** Function to make circ-\>state human-readable */ const char * circuit_state_to_string(int state) @@ -923,7 +980,7 @@ circuit_clear_testing_cell_stats(circuit_t *circ) /** Deallocate space associated with circ. */ STATIC void -circuit_free(circuit_t *circ) +circuit_free_(circuit_t *circ) { circid_t n_circ_id = 0; void *mem; @@ -1091,7 +1148,7 @@ circuit_free_all(void) while (or_circ->resolving_streams) { edge_connection_t *next_conn; next_conn = or_circ->resolving_streams->next_stream; - connection_free(TO_CONN(or_circ->resolving_streams)); + connection_free_(TO_CONN(or_circ->resolving_streams)); or_circ->resolving_streams = next_conn; } } @@ -1793,6 +1850,25 @@ circuit_get_cpath_len(origin_circuit_t *circ) return n; } +/** Return the number of opened hops in circuit's path. + * If circ has no entries, or is NULL, returns 0. */ +int +circuit_get_cpath_opened_len(const origin_circuit_t *circ) +{ + int n = 0; + if (circ && circ->cpath) { + crypt_path_t *cpath, *cpath_next = NULL; + for (cpath = circ->cpath; + cpath->state == CPATH_STATE_OPEN + && cpath_next != circ->cpath; + cpath = cpath_next) { + cpath_next = cpath->next; + ++n; + } + } + return n; +} + /** Return the <b>hopnum</b>th hop in <b>circ</b>->cpath, or NULL if there * aren't that many hops in the list. <b>hopnum</b> starts at 1. * Returns NULL if <b>hopnum</b> is 0 or negative. */ @@ -2176,12 +2252,12 @@ n_cells_in_circ_queues(const circuit_t *c) } /** - * Return the age of the oldest cell queued on <b>c</b>, in milliseconds. + * Return the age of the oldest cell queued on <b>c</b>, in timestamp units. * Return 0 if there are no cells queued on c. Requires that <b>now</b> be - * the current time in milliseconds since the epoch, truncated. + * the current coarse timestamp. * * This function will return incorrect results if the oldest cell queued on - * the circuit is older than 2**32 msec (about 49 days) old. + * the circuit is older than about 2**32 msec (about 49 days) old. */ STATIC uint32_t circuit_max_queued_cell_age(const circuit_t *c, uint32_t now) @@ -2190,12 +2266,12 @@ circuit_max_queued_cell_age(const circuit_t *c, uint32_t now) packed_cell_t *cell; if (NULL != (cell = TOR_SIMPLEQ_FIRST(&c->n_chan_cells.head))) - age = now - cell->inserted_time; + age = now - cell->inserted_timestamp; if (! CIRCUIT_IS_ORIGIN(c)) { const or_circuit_t *orcirc = CONST_TO_OR_CIRCUIT(c); if (NULL != (cell = TOR_SIMPLEQ_FIRST(&orcirc->p_chan_cells.head))) { - uint32_t age2 = now - cell->inserted_time; + uint32_t age2 = now - cell->inserted_timestamp; if (age2 > age) return age2; } @@ -2203,31 +2279,30 @@ circuit_max_queued_cell_age(const circuit_t *c, uint32_t now) return age; } -/** Return the age in milliseconds of the oldest buffer chunk on <b>conn</b>, - * where age is taken in milliseconds before the time <b>now</b> (in truncated - * absolute monotonic msec). If the connection has no data, treat - * it as having age zero. +/** Return the age of the oldest buffer chunk on <b>conn</b>, where age is + * taken in timestamp units before the time <b>now</b>. If the connection has + * no data, treat it as having age zero. **/ static uint32_t -conn_get_buffer_age(const connection_t *conn, uint32_t now) +conn_get_buffer_age(const connection_t *conn, uint32_t now_ts) { uint32_t age = 0, age2; if (conn->outbuf) { - age2 = buf_get_oldest_chunk_timestamp(conn->outbuf, now); + age2 = buf_get_oldest_chunk_timestamp(conn->outbuf, now_ts); if (age2 > age) age = age2; } if (conn->inbuf) { - age2 = buf_get_oldest_chunk_timestamp(conn->inbuf, now); + age2 = buf_get_oldest_chunk_timestamp(conn->inbuf, now_ts); if (age2 > age) age = age2; } return age; } -/** Return the age in milliseconds of the oldest buffer chunk on any stream in - * the linked list <b>stream</b>, where age is taken in milliseconds before - * the time <b>now</b> (in truncated milliseconds since the epoch). */ +/** Return the age in timestamp units of the oldest buffer chunk on any stream + * in the linked list <b>stream</b>, where age is taken in timestamp units + * before the timestamp <b>now</b>. */ static uint32_t circuit_get_streams_max_data_age(const edge_connection_t *stream, uint32_t now) { @@ -2246,9 +2321,9 @@ circuit_get_streams_max_data_age(const edge_connection_t *stream, uint32_t now) return age; } -/** Return the age in milliseconds of the oldest buffer chunk on any stream - * attached to the circuit <b>c</b>, where age is taken in milliseconds before - * the time <b>now</b> (in truncated milliseconds since the epoch). */ +/** Return the age in timestamp units of the oldest buffer chunk on any stream + * attached to the circuit <b>c</b>, where age is taken before the timestamp + * <b>now</b>. */ STATIC uint32_t circuit_max_queued_data_age(const circuit_t *c, uint32_t now) { @@ -2262,8 +2337,8 @@ circuit_max_queued_data_age(const circuit_t *c, uint32_t now) } /** Return the age of the oldest cell or stream buffer chunk on the circuit - * <b>c</b>, where age is taken in milliseconds before the time <b>now</b> (in - * truncated milliseconds since the epoch). */ + * <b>c</b>, where age is taken in timestamp units before the timestamp + * <b>now</b> */ STATIC uint32_t circuit_max_queued_item_age(const circuit_t *c, uint32_t now) { @@ -2293,7 +2368,7 @@ circuits_compare_by_oldest_queued_item_(const void **a_, const void **b_) return -1; } -static uint32_t now_ms_for_buf_cmp; +static uint32_t now_ts_for_buf_cmp; /** Helper to sort a list of circuit_t by age of oldest item, in descending * order. */ @@ -2302,8 +2377,8 @@ conns_compare_by_buffer_age_(const void **a_, const void **b_) { const connection_t *a = *a_; const connection_t *b = *b_; - time_t age_a = conn_get_buffer_age(a, now_ms_for_buf_cmp); - time_t age_b = conn_get_buffer_age(b, now_ms_for_buf_cmp); + time_t age_a = conn_get_buffer_age(a, now_ts_for_buf_cmp); + time_t age_b = conn_get_buffer_age(b, now_ts_for_buf_cmp); if (age_a < age_b) return 1; @@ -2328,7 +2403,7 @@ circuits_handle_oom(size_t current_allocation) size_t mem_recovered=0; int n_circuits_killed=0; int n_dirconns_killed=0; - uint32_t now_ms; + uint32_t now_ts; log_notice(LD_GENERAL, "We're low on memory. Killing circuits with " "over-long queues. (This behavior is controlled by " "MaxMemInQueues.)"); @@ -2341,11 +2416,11 @@ circuits_handle_oom(size_t current_allocation) mem_to_recover = current_allocation - mem_target; } - now_ms = (uint32_t)monotime_coarse_absolute_msec(); + now_ts = monotime_coarse_get_stamp(); circlist = circuit_get_global_list(); SMARTLIST_FOREACH_BEGIN(circlist, circuit_t *, circ) { - circ->age_tmp = circuit_max_queued_item_age(circ, now_ms); + circ->age_tmp = circuit_max_queued_item_age(circ, now_ts); } SMARTLIST_FOREACH_END(circ); /* This is O(n log n); there are faster algorithms we could use instead. @@ -2358,9 +2433,9 @@ circuits_handle_oom(size_t current_allocation) } SMARTLIST_FOREACH_END(circ); /* Now sort the connection array ... */ - now_ms_for_buf_cmp = now_ms; + now_ts_for_buf_cmp = now_ts; smartlist_sort(connection_array, conns_compare_by_buffer_age_); - now_ms_for_buf_cmp = 0; + now_ts_for_buf_cmp = 0; /* Fix up the connection array to its new order. */ SMARTLIST_FOREACH_BEGIN(connection_array, connection_t *, conn) { @@ -2379,7 +2454,7 @@ circuits_handle_oom(size_t current_allocation) * data older than this circuit. */ while (conn_idx < smartlist_len(connection_array)) { connection_t *conn = smartlist_get(connection_array, conn_idx); - uint32_t conn_age = conn_get_buffer_age(conn, now_ms); + uint32_t conn_age = conn_get_buffer_age(conn, now_ts); if (conn_age < circ->age_tmp) { break; } diff --git a/src/or/circuitlist.h b/src/or/circuitlist.h index 4190c1f82e..246f0c8815 100644 --- a/src/or/circuitlist.h +++ b/src/or/circuitlist.h @@ -17,6 +17,10 @@ MOCK_DECL(smartlist_t *, circuit_get_global_list, (void)); smartlist_t *circuit_get_global_origin_circuit_list(void); +int circuit_any_opened_circuits(void); +int circuit_any_opened_circuits_cached(void); +void circuit_cache_opened_circuit_state(int circuits_are_opened); + const char *circuit_state_to_string(int state); const char *circuit_purpose_to_controller_string(uint8_t purpose); const char *circuit_purpose_to_controller_hs_state_string(uint8_t purpose); @@ -58,6 +62,7 @@ void circuit_mark_all_dirty_circs_as_unusable(void); MOCK_DECL(void, circuit_mark_for_close_, (circuit_t *circ, int reason, int line, const char *file)); int circuit_get_cpath_len(origin_circuit_t *circ); +int circuit_get_cpath_opened_len(const origin_circuit_t *); void circuit_clear_cpath(origin_circuit_t *circ); crypt_path_t *circuit_get_cpath_hop(origin_circuit_t *circ, int hopnum); void circuit_get_all_pending_on_channel(smartlist_t *out, @@ -81,7 +86,8 @@ MOCK_DECL(void, channel_note_destroy_not_pending, smartlist_t *circuit_find_circuits_to_upgrade_from_guard_wait(void); #ifdef CIRCUITLIST_PRIVATE -STATIC void circuit_free(circuit_t *circ); +STATIC void circuit_free_(circuit_t *circ); +#define circuit_free(circ) FREE_AND_NULL(circuit_t, circuit_free_, (circ)) STATIC size_t n_cells_in_circ_queues(const circuit_t *c); STATIC uint32_t circuit_max_queued_data_age(const circuit_t *c, uint32_t now); STATIC uint32_t circuit_max_queued_cell_age(const circuit_t *c, uint32_t now); diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index 2cc0e8fc44..fe3d8f1332 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -537,7 +537,7 @@ circuitmux_mark_destroyed_circids_usable(circuitmux_t *cmux, channel_t *chan) */ void -circuitmux_free(circuitmux_t *cmux) +circuitmux_free_(circuitmux_t *cmux) { if (!cmux) return; diff --git a/src/or/circuitmux.h b/src/or/circuitmux.h index a95edb99d8..336e128c76 100644 --- a/src/or/circuitmux.h +++ b/src/or/circuitmux.h @@ -104,7 +104,9 @@ void circuitmux_assert_okay(circuitmux_t *cmux); circuitmux_t * circuitmux_alloc(void); void circuitmux_detach_all_circuits(circuitmux_t *cmux, smartlist_t *detached_out); -void circuitmux_free(circuitmux_t *cmux); +void circuitmux_free_(circuitmux_t *cmux); +#define circuitmux_free(cmux) \ + FREE_AND_NULL(circuitmux_t, circuitmux_free_, (cmux)) /* Policy control */ void circuitmux_clear_policy(circuitmux_t *cmux); diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c index b8421a3c7e..f1df19eb25 100644 --- a/src/or/circuitstats.c +++ b/src/or/circuitstats.c @@ -36,6 +36,8 @@ #include "rendclient.h" #include "rendservice.h" #include "statefile.h" +#include "circuitlist.h" +#include "circuituse.h" #undef log #include <math.h> @@ -43,6 +45,7 @@ static void cbt_control_event_buildtimeout_set( const circuit_build_times_t *cbt, buildtimeout_set_event_t type); +static void circuit_build_times_scale_circ_counts(circuit_build_times_t *cbt); #define CBT_BIN_TO_MS(bin) ((bin)*CBT_BIN_WIDTH + (CBT_BIN_WIDTH/2)) @@ -540,6 +543,11 @@ circuit_build_times_reset(circuit_build_times_t *cbt) cbt->total_build_times = 0; cbt->build_times_idx = 0; cbt->have_computed_timeout = 0; + + // Reset timeout and close counts + cbt->num_circ_succeeded = 0; + cbt->num_circ_closed = 0; + cbt->num_circ_timeouts = 0; } /** @@ -616,6 +624,123 @@ circuit_build_times_rewind_history(circuit_build_times_t *cbt, int n) #endif /* 0 */ /** + * Mark this circuit as timed out, but change its purpose + * so that it continues to build, allowing us to measure + * its full build time. + */ +void +circuit_build_times_mark_circ_as_measurement_only(origin_circuit_t *circ) +{ + control_event_circuit_status(circ, + CIRC_EVENT_FAILED, + END_CIRC_REASON_TIMEOUT); + circuit_change_purpose(TO_CIRCUIT(circ), + CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT); + /* Record this event to check for too many timeouts + * in a row. This function does not record a time value yet + * (we do that later); it only counts the fact that we did + * have a timeout. We also want to avoid double-counting + * already "relaxed" circuits, which are counted in + * circuit_expire_building(). */ + if (!circ->relaxed_timeout) { + int first_hop_succeeded = circ->cpath && + circ->cpath->state == CPATH_STATE_OPEN; + + circuit_build_times_count_timeout( + get_circuit_build_times_mutable(), + first_hop_succeeded); + } +} + +/** + * Perform the build time work that needs to be done when a circuit + * completes a hop. + * + * This function decides if we should record a circuit's build time + * in our histogram data and other statistics, and if so, records it. + * It also will mark circuits that have already timed out as + * measurement-only circuits, so they can continue to build but + * not get used. + * + * For this, we want to consider circuits that will eventually make + * it to the third hop. For circuits longer than 3 hops, we want to + * record their build time when they reach the third hop, but let + * them continue (and not count them later). For circuits that are + * exactly 3 hops, this will count them when they are completed. We + * do this so that CBT is always gathering statistics on circuits + * of the same length, regardless of their type. + */ +void +circuit_build_times_handle_completed_hop(origin_circuit_t *circ) +{ + struct timeval end; + long timediff; + + /* If circuit build times are disabled, let circuit_expire_building() + * handle it.. */ + if (circuit_build_times_disabled(get_options())) { + return; + } + + /* Is this a circuit for which the timeout applies in a straight-forward + * way? If so, handle it below. If not, just return (and let + * circuit_expire_building() eventually take care of it). + */ + if (!circuit_timeout_want_to_count_circ(circ)) { + return; + } + + tor_gettimeofday(&end); + timediff = tv_mdiff(&circ->base_.timestamp_began, &end); + + /* Check if we would have timed out already. If so, change the + * purpose here. But don't do any timeout handling here if there + * are no circuits opened yet. Save it for circuit_expire_building() + * (to allow it to handle timeout "relaxing" over there). */ + if (timediff > get_circuit_build_timeout_ms() && + circuit_any_opened_circuits_cached()) { + + /* Circuits are allowed to last longer for measurement. + * Switch their purpose and wait. */ + if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { + log_info(LD_CIRC, + "Deciding to timeout circuit "U64_FORMAT"\n", + U64_PRINTF_ARG(circ->global_identifier)); + circuit_build_times_mark_circ_as_measurement_only(circ); + } + } + + /* If the circuit is built to exactly the DEFAULT_ROUTE_LEN, + * add it to our buildtimes. */ + if (circuit_get_cpath_opened_len(circ) == DEFAULT_ROUTE_LEN) { + /* If the circuit build time is much greater than we would have cut + * it off at, we probably had a suspend event along this codepath, + * and we should discard the value. + */ + if (timediff < 0 || + timediff > 2*get_circuit_build_close_time_ms()+1000) { + log_notice(LD_CIRC, "Strange value for circuit build time: %ldmsec. " + "Assuming clock jump. Purpose %d (%s)", timediff, + circ->base_.purpose, + circuit_purpose_to_string(circ->base_.purpose)); + } else { + /* Only count circuit times if the network is live */ + if (circuit_build_times_network_check_live( + get_circuit_build_times())) { + circuit_build_times_add_time(get_circuit_build_times_mutable(), + (build_time_t)timediff); + circuit_build_times_set_timeout(get_circuit_build_times_mutable()); + } + + if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { + circuit_build_times_network_circ_success( + get_circuit_build_times_mutable()); + } + } + } +} + +/** * Add a new build time value <b>time</b> to the set of build times. Time * units are milliseconds. * @@ -1290,9 +1415,32 @@ circuit_build_times_network_is_live(circuit_build_times_t *cbt) } /** - * Called to indicate that we completed a circuit. Because this circuit + * Non-destructively scale all of our circuit success, timeout, and close + * counts down by a factor of two. Scaling in this way preserves the + * ratios between succeeded vs timed out vs closed circuits, so that + * our statistics don't change when we scale. + * + * This is used only in the rare event that we build more than + * INT32_MAX circuits. Since the num_circ_* variables are + * uint32_t, we won't even be close to overflowing them. + */ +void +circuit_build_times_scale_circ_counts(circuit_build_times_t *cbt) +{ + cbt->num_circ_succeeded /= 2; + cbt->num_circ_timeouts /= 2; + cbt->num_circ_closed /= 2; +} + +/** + * Called to indicate that we "completed" a circuit. Because this circuit * succeeded, it doesn't count as a timeout-after-the-first-hop. * + * (For the purposes of the cbt code, we consider a circuit "completed" if + * it has 3 hops, regardless of its final hop count. We do this because + * we're trying to answer the question, "how long should a circuit take to + * reach the 3-hop count".) + * * This is used by circuit_build_times_network_check_changed() to determine * if we had too many recent timeouts and need to reset our learned timeout * to something higher. @@ -1300,6 +1448,14 @@ circuit_build_times_network_is_live(circuit_build_times_t *cbt) void circuit_build_times_network_circ_success(circuit_build_times_t *cbt) { + // Count circuit success + cbt->num_circ_succeeded++; + + // If we're going to wrap int32, scale everything + if (cbt->num_circ_succeeded >= INT32_MAX) { + circuit_build_times_scale_circ_counts(cbt); + } + /* Check for NULLness because we might not be using adaptive timeouts */ if (cbt->liveness.timeouts_after_firsthop && cbt->liveness.num_recent_circs > 0) { @@ -1322,6 +1478,14 @@ static void circuit_build_times_network_timeout(circuit_build_times_t *cbt, int did_onehop) { + // Count circuit timeout + cbt->num_circ_timeouts++; + + // If we're going to wrap int32, scale everything + if (cbt->num_circ_timeouts >= INT32_MAX) { + circuit_build_times_scale_circ_counts(cbt); + } + /* Check for NULLness because we might not be using adaptive timeouts */ if (cbt->liveness.timeouts_after_firsthop && cbt->liveness.num_recent_circs > 0) { @@ -1347,6 +1511,15 @@ circuit_build_times_network_close(circuit_build_times_t *cbt, int did_onehop, time_t start_time) { time_t now = time(NULL); + + // Count circuit close + cbt->num_circ_closed++; + + // If we're going to wrap int32, scale everything + if (cbt->num_circ_closed >= INT32_MAX) { + circuit_build_times_scale_circ_counts(cbt); + } + /* * Check if this is a timeout that was for a circuit that spent its * entire existence during a time where we have had no network activity. @@ -1701,6 +1874,8 @@ cbt_control_event_buildtimeout_set(const circuit_build_times_t *cbt, { char *args = NULL; double qnt; + double timeout_rate = 0.0; + double close_rate = 0.0; switch (type) { case BUILDTIMEOUT_SET_EVENT_RESET: @@ -1715,15 +1890,29 @@ cbt_control_event_buildtimeout_set(const circuit_build_times_t *cbt, break; } + /* The timeout rate is the ratio of the timeout count over + * the total number of circuits attempted. The total number of + * circuits is (timeouts+succeeded+closed), since a circuit can + * either timeout, close, or succeed. 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 + + cbt->num_circ_closed; + if (total_circuits >= 1.0) { + timeout_rate = cbt->num_circ_timeouts / total_circuits; + close_rate = cbt->num_circ_closed / total_circuits; + } + tor_asprintf(&args, "TOTAL_TIMES=%lu " "TIMEOUT_MS=%lu XM=%lu ALPHA=%f CUTOFF_QUANTILE=%f " "TIMEOUT_RATE=%f CLOSE_MS=%lu CLOSE_RATE=%f", (unsigned long)cbt->total_build_times, (unsigned long)cbt->timeout_ms, (unsigned long)cbt->Xm, cbt->alpha, qnt, - circuit_build_times_timeout_rate(cbt), + timeout_rate, (unsigned long)cbt->close_ms, - circuit_build_times_close_rate(cbt)); + close_rate); control_event_buildtimeout_set(type, args); diff --git a/src/or/circuitstats.h b/src/or/circuitstats.h index 92dc6405ba..86116cb7f8 100644 --- a/src/or/circuitstats.h +++ b/src/or/circuitstats.h @@ -34,6 +34,7 @@ void circuit_build_times_set_timeout(circuit_build_times_t *cbt); int circuit_build_times_add_time(circuit_build_times_t *cbt, build_time_t time); int circuit_build_times_needs_circuits(const circuit_build_times_t *cbt); +void circuit_build_times_handle_completed_hop(origin_circuit_t *circ); int circuit_build_times_needs_circuits_now(const circuit_build_times_t *cbt); void circuit_build_times_init(circuit_build_times_t *cbt); @@ -44,6 +45,7 @@ double circuit_build_times_timeout_rate(const circuit_build_times_t *cbt); double circuit_build_times_close_rate(const circuit_build_times_t *cbt); void circuit_build_times_update_last_circ(circuit_build_times_t *cbt); +void circuit_build_times_mark_circ_as_measurement_only(origin_circuit_t *circ); #ifdef CIRCUITSTATS_PRIVATE STATIC double circuit_build_times_calculate_timeout(circuit_build_times_t *cbt, @@ -94,6 +96,16 @@ struct circuit_build_times_s { double timeout_ms; /** How long we wait before actually closing the circuit. */ double close_ms; + /** Total succeeded counts. Old measurements may be scaled downward if + * we've seen a lot of circuits. */ + uint32_t num_circ_succeeded; + /** Total timeout counts. Old measurements may be scaled downward if + * we've seen a lot of circuits. */ + uint32_t num_circ_timeouts; + /** Total closed counts. Old measurements may be scaled downward if + * we've seen a lot of circuits.*/ + uint32_t num_circ_closed; + }; #endif /* defined(CIRCUITSTATS_PRIVATE) */ diff --git a/src/or/circuituse.c b/src/or/circuituse.c index 7baa035696..f04448ffce 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -412,8 +412,17 @@ circuit_conforms_to_options(const origin_circuit_t *circ, } #endif /* 0 */ -/** Close all circuits that start at us, aren't open, and were born +/** + * Close all circuits that start at us, aren't open, and were born * at least CircuitBuildTimeout seconds ago. + * + * TODO: This function is now partially redundant to + * circuit_build_times_handle_completed_hop(), but that function only + * covers circuits up to and including 3 hops that are still actually + * completing hops. However, circuit_expire_building() also handles longer + * circuits, as well as circuits that are completely stalled. + * In the future (after prop247/other path selection revamping), we probably + * want to eliminate this rats nest in favor of a simpler approach. */ void circuit_expire_building(void) @@ -435,21 +444,7 @@ circuit_expire_building(void) * we want to be more lenient with timeouts, in case the * user has relocated and/or changed network connections. * See bug #3443. */ - SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, next_circ) { - if (!CIRCUIT_IS_ORIGIN(next_circ) || /* didn't originate here */ - next_circ->marked_for_close) { /* don't mess with marked circs */ - continue; - } - - if (TO_ORIGIN_CIRCUIT(next_circ)->has_opened && - next_circ->state == CIRCUIT_STATE_OPEN && - TO_ORIGIN_CIRCUIT(next_circ)->build_state && - TO_ORIGIN_CIRCUIT(next_circ)->build_state->desired_path_len - == DEFAULT_ROUTE_LEN) { - any_opened_circs = 1; - break; - } - } SMARTLIST_FOREACH_END(next_circ); + any_opened_circs = circuit_any_opened_circuits(); #define SET_CUTOFF(target, msec) do { \ long ms = tor_lround(msec); \ @@ -493,6 +488,10 @@ circuit_expire_building(void) SET_CUTOFF(general_cutoff, get_circuit_build_timeout_ms()); SET_CUTOFF(begindir_cutoff, get_circuit_build_timeout_ms()); + // TODO: We should probably use route_len_for_purpose() here instead, + // except that does not count the extra round trip for things like server + // intros and rends. + /* > 3hop circs seem to have a 1.0 second delay on their cannibalized * 4th hop. */ SET_CUTOFF(fourhop_cutoff, get_circuit_build_timeout_ms() * (10/6.0) + 1000); @@ -585,7 +584,8 @@ circuit_expire_building(void) TO_ORIGIN_CIRCUIT(victim)->build_state->desired_path_len : -1, circuit_state_to_string(victim->state), - channel_state_to_string(victim->n_chan->state)); + victim->n_chan ? + channel_state_to_string(victim->n_chan->state) : "none"); /* We count the timeout here for CBT, because technically this * was a timeout, and the timeout value needs to reset if we @@ -610,7 +610,8 @@ circuit_expire_building(void) TO_ORIGIN_CIRCUIT(victim)->build_state->desired_path_len : -1, circuit_state_to_string(victim->state), - channel_state_to_string(victim->n_chan->state), + victim->n_chan ? + channel_state_to_string(victim->n_chan->state) : "none", (long)build_close_ms); } } @@ -694,23 +695,17 @@ circuit_expire_building(void) if (circuit_timeout_want_to_count_circ(TO_ORIGIN_CIRCUIT(victim)) && circuit_build_times_enough_to_compute(get_circuit_build_times())) { + + log_info(LD_CIRC, + "Deciding to count the timeout for circuit "U64_FORMAT"\n", + U64_PRINTF_ARG( + TO_ORIGIN_CIRCUIT(victim)->global_identifier)); + /* Circuits are allowed to last longer for measurement. * Switch their purpose and wait. */ if (victim->purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) { - control_event_circuit_status(TO_ORIGIN_CIRCUIT(victim), - CIRC_EVENT_FAILED, - END_CIRC_REASON_TIMEOUT); - circuit_change_purpose(victim, CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT); - /* Record this failure to check for too many timeouts - * in a row. This function does not record a time value yet - * (we do that later); it only counts the fact that we did - * have a timeout. We also want to avoid double-counting - * already "relaxed" circuits, which are counted above. */ - if (!TO_ORIGIN_CIRCUIT(victim)->relaxed_timeout) { - circuit_build_times_count_timeout( - get_circuit_build_times_mutable(), - first_hop_succeeded); - } + circuit_build_times_mark_circ_as_measurement_only(TO_ORIGIN_CIRCUIT( + victim)); continue; } diff --git a/src/or/config.c b/src/or/config.c index d8f71a0193..3ae3af55a3 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -18,7 +18,7 @@ * inspecting values that are calculated as a result of the * configured options. * - * <h3>How to add new options</h3> + * <h3>How to add new options</h3> * * To add new items to the torrc, there are a minimum of three places to edit: * <ul> @@ -82,6 +82,7 @@ #include "dirvote.h" #include "dns.h" #include "entrynodes.h" +#include "git_revision.h" #include "geoip.h" #include "hibernate.h" #include "main.h" @@ -252,6 +253,8 @@ static config_var_t option_vars_[] = { V(BridgeRecordUsageByCountry, BOOL, "1"), V(BridgeRelay, BOOL, "0"), V(BridgeDistribution, STRING, NULL), + VAR("CacheDirectory", FILENAME, CacheDirectory_option, NULL), + V(CacheDirectoryGroupReadable, BOOL, "0"), V(CellStatistics, BOOL, "0"), V(PaddingStatistics, BOOL, "1"), V(LearnCircuitBuildTimeout, BOOL, "1"), @@ -285,7 +288,7 @@ static config_var_t option_vars_[] = { V(CookieAuthFileGroupReadable, BOOL, "0"), V(CookieAuthFile, STRING, NULL), V(CountPrivateBandwidth, BOOL, "0"), - V(DataDirectory, FILENAME, NULL), + VAR("DataDirectory", FILENAME, DataDirectory_option, NULL), V(DataDirectoryGroupReadable, BOOL, "0"), V(DisableOOSCheck, BOOL, "1"), V(DisableNetwork, BOOL, "0"), @@ -357,6 +360,7 @@ static config_var_t option_vars_[] = { V(GuardLifetime, INTERVAL, "0 minutes"), V(HardwareAccel, BOOL, "0"), V(HeartbeatPeriod, INTERVAL, "6 hours"), + V(MainloopStats, BOOL, "0"), V(AccelName, STRING, NULL), V(AccelDir, FILENAME, NULL), V(HashedControlPassword, LINELIST, NULL), @@ -391,6 +395,8 @@ static config_var_t option_vars_[] = { V(Socks5Proxy, STRING, NULL), V(Socks5ProxyUsername, STRING, NULL), V(Socks5ProxyPassword, STRING, NULL), + VAR("KeyDirectory", FILENAME, KeyDirectory_option, NULL), + V(KeyDirectoryGroupReadable, BOOL, "0"), V(KeepalivePeriod, INTERVAL, "5 minutes"), V(KeepBindCapabilities, AUTOBOOL, "auto"), VAR("Log", LINELIST, Logs, NULL), @@ -398,6 +404,7 @@ static config_var_t option_vars_[] = { V(LogTimeGranularity, MSEC_INTERVAL, "1 second"), V(TruncateLogFile, BOOL, "0"), V(SyslogIdentityTag, STRING, NULL), + V(AndroidIdentityTag, STRING, NULL), V(LongLivedPorts, CSV, "21,22,706,1863,5050,5190,5222,5223,6523,6667,6697,8300"), VAR("MapAddress", LINELIST, AddressMap, NULL), @@ -483,6 +490,7 @@ static config_var_t option_vars_[] = { V(RendPostPeriod, INTERVAL, "1 hour"), V(RephistTrackTime, INTERVAL, "24 hours"), V(RunAsDaemon, BOOL, "0"), + V(ReducedExitPolicy, BOOL, "0"), OBSOLETE("RunTesting"), // currently unused V(Sandbox, BOOL, "0"), V(SafeLogging, STRING, "1"), @@ -561,6 +569,7 @@ static config_var_t option_vars_[] = { VAR("__HashedControlSessionPassword", LINELIST, HashedControlSessionPassword, NULL), VAR("__OwningControllerProcess",STRING,OwningControllerProcess, NULL), + VAR("__OwningControllerFD",INT,OwningControllerFD, "-1"), V(MinUptimeHidServDirectoryV2, INTERVAL, "96 hours"), V(TestingServerDownloadSchedule, CSV_INTERVAL, "0, 0, 0, 60, 60, 120, " "300, 900, 2147483647"), @@ -730,7 +739,7 @@ static int parse_ports(or_options_t *options, int validate_only, static int check_server_ports(const smartlist_t *ports, const or_options_t *options, int *num_low_ports_out); -static int validate_data_directory(or_options_t *options); +static int validate_data_directories(or_options_t *options); static int write_configuration_file(const char *fname, const or_options_t *options); static int options_init_logs(const or_options_t *old_options, @@ -775,7 +784,7 @@ static or_options_t *global_default_options = NULL; /** Name of most recently read torrc file. */ static char *torrc_fname = NULL; /** Name of the most recently read torrc-defaults file.*/ -static char *torrc_defaults_fname; +static char *torrc_defaults_fname = NULL; /** Configuration options set by command line. */ static config_line_t *global_cmdline_options = NULL; /** Non-configuration options set by the command line */ @@ -833,9 +842,12 @@ set_options(or_options_t *new_val, char **msg) return -1; } if (options_act(old_options) < 0) { /* acting on the options failed. die. */ - log_err(LD_BUG, - "Acting on config options left us in a broken state. Dying."); - exit(1); + if (! tor_event_loop_shutdown_is_pending()) { + log_err(LD_BUG, + "Acting on config options left us in a broken state. Dying."); + tor_shutdown_event_loop_and_exit(1); + } + return -1; } /* Issues a CONF_CHANGED event to notify controller of the change. If Tor is * just starting up then the old_options will be undefined. */ @@ -877,8 +889,6 @@ set_options(or_options_t *new_val, char **msg) return 0; } -extern const char tor_git_revision[]; /* from tor_main.c */ - /** The version of this Tor process, as parsed. */ static char *the_tor_version = NULL; /** A shorter version of this Tor process's version, for export in our router @@ -918,7 +928,7 @@ get_short_version(void) /** Release additional memory allocated in options */ STATIC void -or_options_free(or_options_t *options) +or_options_free_(or_options_t *options) { if (!options) return; @@ -933,6 +943,13 @@ or_options_free(or_options_t *options) SMARTLIST_FOREACH(options->SchedulerTypes_, int *, i, tor_free(i)); smartlist_free(options->SchedulerTypes_); } + if (options->FilesOpenedByIncludes) { + SMARTLIST_FOREACH(options->FilesOpenedByIncludes, char *, f, tor_free(f)); + smartlist_free(options->FilesOpenedByIncludes); + } + tor_free(options->DataDirectory); + tor_free(options->CacheDirectory); + tor_free(options->KeyDirectory); tor_free(options->BridgePassword_AuthDigest_); tor_free(options->command_arg); tor_free(options->master_key_fname); @@ -969,6 +986,8 @@ config_free_all(void) tor_free(the_short_tor_version); tor_free(the_tor_version); + + have_parsed_cmdline = 0; } /** Make <b>address</b> -- a piece of information related to our operation as @@ -1242,6 +1261,69 @@ consider_adding_dir_servers(const or_options_t *options, return 0; } +/** + * Make sure that <b>directory</b> exists, with appropriate ownership and + * permissions (as modified by <b>group_readable</b>). If <b>create</b>, + * create the directory if it is missing. Return 0 on success. + * On failure, return -1 and set *<b>msg_out</b>. + */ +static int +check_and_create_data_directory(int create, + const char *directory, + int group_readable, + const char *owner, + char **msg_out) +{ + cpd_check_t cpd_opts = create ? CPD_CREATE : CPD_CHECK; + if (group_readable) + cpd_opts |= CPD_GROUP_READ; + if (check_private_dir(directory, + cpd_opts, + owner) < 0) { + tor_asprintf(msg_out, + "Couldn't %s private data directory \"%s\"", + create ? "create" : "access", + directory); + return -1; + } + +#ifndef _WIN32 + if (group_readable) { + /* Only new dirs created get new opts, also enforce group read. */ + if (chmod(directory, 0750)) { + log_warn(LD_FS,"Unable to make %s group-readable: %s", + directory, strerror(errno)); + } + } +#endif /* !defined(_WIN32) */ + + return 0; +} + +/** + * Ensure that our keys directory exists, with appropriate permissions. + * Return 0 on success, -1 on failure. + */ +int +create_keys_directory(const or_options_t *options) +{ + /* Make sure DataDirectory exists, and is private. */ + cpd_check_t cpd_opts = CPD_CREATE; + if (options->DataDirectoryGroupReadable) + cpd_opts |= CPD_GROUP_READ; + if (check_private_dir(options->DataDirectory, cpd_opts, options->User)) { + log_err(LD_OR, "Can't create/check datadirectory %s", + options->DataDirectory); + return -1; + } + + /* Check the key directory. */ + if (check_private_dir(options->KeyDirectory, CPD_CREATE, options->User)) { + return -1; + } + return 0; +} + /* Helps determine flags to pass to switch_id. */ static int have_low_ports = -1; @@ -1396,29 +1478,30 @@ options_act_reversible(const or_options_t *old_options, char **msg) } /* Ensure data directory is private; create if possible. */ - cpd_check_t cpd_opts = running_tor ? CPD_CREATE : CPD_CHECK; - if (options->DataDirectoryGroupReadable) - cpd_opts |= CPD_GROUP_READ; - if (check_private_dir(options->DataDirectory, - cpd_opts, - options->User)<0) { - tor_asprintf(msg, - "Couldn't access/create private data directory \"%s\"", - options->DataDirectory); - + /* It's okay to do this in "options_act_reversible()" even though it isn't + * actually reversible, since you can't change the DataDirectory while + * Tor is running. */ + if (check_and_create_data_directory(running_tor /* create */, + options->DataDirectory, + options->DataDirectoryGroupReadable, + options->User, + msg) < 0) { goto done; - /* No need to roll back, since you can't change the value. */ } - -#ifndef _WIN32 - if (options->DataDirectoryGroupReadable) { - /* Only new dirs created get new opts, also enforce group read. */ - if (chmod(options->DataDirectory, 0750)) { - log_warn(LD_FS,"Unable to make %s group-readable: %s", - options->DataDirectory, strerror(errno)); - } + if (check_and_create_data_directory(running_tor /* create */, + options->KeyDirectory, + options->KeyDirectoryGroupReadable, + options->User, + msg) < 0) { + goto done; + } + if (check_and_create_data_directory(running_tor /* create */, + options->CacheDirectory, + options->CacheDirectoryGroupReadable, + options->User, + msg) < 0) { + goto done; } -#endif /* !defined(_WIN32) */ /* Bail out at this point if we're not going to be a client or server: * we don't run Tor itself. */ @@ -1604,7 +1687,7 @@ options_transition_affects_guards(const or_options_t *old, const or_options_t *new) { /* NOTE: Make sure this function stays in sync with - * entry_guards_set_filtered_flags */ + * node_passes_guard_filter */ tor_assert(old); tor_assert(new); @@ -1688,8 +1771,11 @@ options_act(const or_options_t *old_options) else protocol_warning_severity_level = LOG_INFO; - if (consider_adding_dir_servers(options, old_options) < 0) + if (consider_adding_dir_servers(options, old_options) < 0) { + // XXXX This should get validated earlier, and committed here, to + // XXXX lower opportunities for reaching an error case. return -1; + } if (rend_non_anonymous_mode_enabled(options)) { log_warn(LD_GENERAL, "This copy of Tor was compiled or configured to run " @@ -1698,6 +1784,7 @@ options_act(const or_options_t *old_options) #ifdef ENABLE_TOR2WEB_MODE /* LCOV_EXCL_START */ + // XXXX This should move into options_validate() if (!options->Tor2webMode) { log_err(LD_CONFIG, "This copy of Tor was compiled to run in " "'tor2web mode'. It can only be run with the Tor2webMode torrc " @@ -1706,6 +1793,7 @@ options_act(const or_options_t *old_options) } /* LCOV_EXCL_STOP */ #else /* !(defined(ENABLE_TOR2WEB_MODE)) */ + // XXXX This should move into options_validate() if (options->Tor2webMode) { log_err(LD_CONFIG, "This copy of Tor was not compiled to run in " "'tor2web mode'. It cannot be run with the Tor2webMode torrc " @@ -1733,9 +1821,11 @@ options_act(const or_options_t *old_options) for (cl = options->Bridges; cl; cl = cl->next) { bridge_line_t *bridge_line = parse_bridge_line(cl->value); if (!bridge_line) { + // LCOV_EXCL_START log_warn(LD_BUG, "Previously validated Bridge line could not be added!"); return -1; + // LCOV_EXCL_STOP } bridge_add_from_config(bridge_line); } @@ -1743,15 +1833,37 @@ options_act(const or_options_t *old_options) } if (running_tor && hs_config_service_all(options, 0)<0) { + // LCOV_EXCL_START log_warn(LD_BUG, "Previously validated hidden services line could not be added!"); return -1; + // LCOV_EXCL_STOP } if (running_tor && rend_parse_service_authorization(options, 0) < 0) { + // LCOV_EXCL_START log_warn(LD_BUG, "Previously validated client authorization for " "hidden services could not be added!"); return -1; + // LCOV_EXCL_STOP + } + + if (running_tor && !old_options && options->OwningControllerFD != -1) { +#ifdef _WIN32 + log_warn(LD_CONFIG, "OwningControllerFD is not supported on Windows. " + "If you need it, tell the Tor developers."); + return -1; +#else + const unsigned ctrl_flags = + CC_LOCAL_FD_IS_OWNER | + CC_LOCAL_FD_IS_AUTHENTICATED; + tor_socket_t ctrl_sock = (tor_socket_t)options->OwningControllerFD; + if (control_connection_add_local_fd(ctrl_sock, ctrl_flags) < 0) { + log_warn(LD_CONFIG, "Could not add local controller connection with " + "given FD."); + return -1; + } +#endif /* defined(_WIN32) */ } /* Load state */ @@ -1774,10 +1886,12 @@ options_act(const or_options_t *old_options) if (options->ClientTransportPlugin) { for (cl = options->ClientTransportPlugin; cl; cl = cl->next) { if (parse_transport_line(options, cl->value, 0, 0) < 0) { + // LCOV_EXCL_START log_warn(LD_BUG, "Previously validated ClientTransportPlugin line " "could not be added!"); return -1; + // LCOV_EXCL_STOP } } } @@ -1785,10 +1899,12 @@ options_act(const or_options_t *old_options) if (options->ServerTransportPlugin && server_mode(options)) { for (cl = options->ServerTransportPlugin; cl; cl = cl->next) { if (parse_transport_line(options, cl->value, 0, 1) < 0) { + // LCOV_EXCL_START log_warn(LD_BUG, "Previously validated ServerTransportPlugin line " "could not be added!"); return -1; + // LCOV_EXCL_STOP } } } @@ -1874,8 +1990,10 @@ options_act(const or_options_t *old_options) /* Set up accounting */ if (accounting_parse_options(options, 0)<0) { - log_warn(LD_CONFIG,"Error in accounting options"); + // LCOV_EXCL_START + log_warn(LD_BUG,"Error in previously validated accounting options"); return -1; + // LCOV_EXCL_STOP } if (accounting_is_enabled(options)) configure_accounting(time(NULL)); @@ -1898,6 +2016,7 @@ options_act(const or_options_t *old_options) char *http_authenticator; http_authenticator = alloc_http_authenticator(options->BridgePassword); if (!http_authenticator) { + // XXXX This should get validated in options_validate(). log_warn(LD_BUG, "Unable to allocate HTTP authenticator. Not setting " "BridgePassword."); return -1; @@ -1910,9 +2029,12 @@ options_act(const or_options_t *old_options) } if (parse_outbound_addresses(options, 0, &msg) < 0) { - log_warn(LD_BUG, "Failed parsing outbound bind addresses: %s", msg); + // LCOV_EXCL_START + log_warn(LD_BUG, "Failed parsing previously validated outbound " + "bind addresses: %s", msg); tor_free(msg); return -1; + // LCOV_EXCL_STOP } config_maybe_load_geoip_files_(options, old_options); @@ -2036,6 +2158,10 @@ options_act(const or_options_t *old_options) if (options->PerConnBWRate != old_options->PerConnBWRate || options->PerConnBWBurst != old_options->PerConnBWBurst) connection_or_update_token_buckets(get_connection_array(), options); + + if (options->MainloopStats != old_options->MainloopStats) { + reset_main_loop_counters(); + } } /* Only collect directory-request statistics on relays and bridges. */ @@ -3141,7 +3267,7 @@ options_validate(or_options_t *old_options, or_options_t *options, if (parse_outbound_addresses(options, 1, msg) < 0) return -1; - if (validate_data_directory(options)<0) + if (validate_data_directories(options)<0) REJECT("Invalid DataDirectory"); if (options->Nickname == NULL) { @@ -4539,6 +4665,22 @@ options_transition_allowed(const or_options_t *old, return -1; } + if (!opt_streq(old->KeyDirectory, new_val->KeyDirectory)) { + tor_asprintf(msg, + "While Tor is running, changing KeyDirectory " + "(\"%s\"->\"%s\") is not allowed.", + old->KeyDirectory, new_val->KeyDirectory); + return -1; + } + + if (!opt_streq(old->CacheDirectory, new_val->CacheDirectory)) { + tor_asprintf(msg, + "While Tor is running, changing CacheDirectory " + "(\"%s\"->\"%s\") is not allowed.", + old->CacheDirectory, new_val->CacheDirectory); + return -1; + } + if (!opt_streq(old->User, new_val->User)) { *msg = tor_strdup("While Tor is running, changing User is not allowed."); return -1; @@ -4556,6 +4698,12 @@ options_transition_allowed(const or_options_t *old, return -1; } + if (!opt_streq(old->AndroidIdentityTag, new_val->AndroidIdentityTag)) { + *msg = tor_strdup("While Tor is running, changing " + "AndroidIdentityTag is not allowed."); + return -1; + } + if ((old->HardwareAccel != new_val->HardwareAccel) || !opt_streq(old->AccelName, new_val->AccelName) || !opt_streq(old->AccelDir, new_val->AccelDir)) { @@ -4608,6 +4756,12 @@ options_transition_allowed(const or_options_t *old, return -1; } + if (old->OwningControllerFD != new_val->OwningControllerFD) { + *msg = tor_strdup("While Tor is running, changing OwningControllerFD " + "is not allowed."); + return -1; + } + if (sandbox_is_active()) { #define SB_NOCHANGE_STR(opt) \ do { \ @@ -5020,7 +5174,8 @@ load_torrc_from_disk(config_line_t *cmd_arg, int defaults_file) /** Read a configuration file into <b>options</b>, finding the configuration * file location based on the command line. After loading the file * call options_init_from_string() to load the config. - * Return 0 if success, -1 if failure. */ + * Return 0 if success, -1 if failure, and 1 if we succeeded but should exit + * anyway. */ int options_init_from_torrc(int argc, char **argv) { @@ -5047,22 +5202,22 @@ options_init_from_torrc(int argc, char **argv) if (config_line_find(cmdline_only_options, "-h") || config_line_find(cmdline_only_options, "--help")) { print_usage(); - exit(0); + return 1; } if (config_line_find(cmdline_only_options, "--list-torrc-options")) { /* For validating whether we've documented everything. */ list_torrc_options(); - exit(0); + return 1; } if (config_line_find(cmdline_only_options, "--list-deprecated-options")) { /* For validating whether what we have deprecated really exists. */ list_deprecated_options(); - exit(0); + return 1; } if (config_line_find(cmdline_only_options, "--version")) { printf("Tor version %s.\n",get_version()); - exit(0); + return 1; } if (config_line_find(cmdline_only_options, "--library-versions")) { @@ -5090,7 +5245,7 @@ options_init_from_torrc(int argc, char **argv) tor_compress_header_version_str(ZSTD_METHOD)); } //TODO: Hex versions? - exit(0); + return 1; } command = CMD_RUN_TOR; @@ -5151,7 +5306,8 @@ options_init_from_torrc(int argc, char **argv) get_options_mutable()->keygen_force_passphrase = FORCE_PASSPHRASE_OFF; } else { log_err(LD_CONFIG, "--no-passphrase specified without --keygen!"); - exit(1); + retval = -1; + goto err; } } @@ -5160,7 +5316,8 @@ options_init_from_torrc(int argc, char **argv) get_options_mutable()->change_key_passphrase = 1; } else { log_err(LD_CONFIG, "--newpass specified without --keygen!"); - exit(1); + retval = -1; + goto err; } } @@ -5170,17 +5327,20 @@ options_init_from_torrc(int argc, char **argv) if (fd_line) { if (get_options()->keygen_force_passphrase == FORCE_PASSPHRASE_OFF) { log_err(LD_CONFIG, "--no-passphrase specified with --passphrase-fd!"); - exit(1); + retval = -1; + goto err; } else if (command != CMD_KEYGEN) { log_err(LD_CONFIG, "--passphrase-fd specified without --keygen!"); - exit(1); + retval = -1; + goto err; } else { const char *v = fd_line->value; int ok = 1; long fd = tor_parse_long(v, 10, 0, INT_MAX, &ok, NULL); if (fd < 0 || ok == 0) { log_err(LD_CONFIG, "Invalid --passphrase-fd value %s", escaped(v)); - exit(1); + retval = -1; + goto err; } get_options_mutable()->keygen_passphrase_fd = (int)fd; get_options_mutable()->use_keygen_passphrase_fd = 1; @@ -5195,7 +5355,8 @@ options_init_from_torrc(int argc, char **argv) if (key_line) { if (command != CMD_KEYGEN) { log_err(LD_CONFIG, "--master-key without --keygen!"); - exit(1); + retval = -1; + goto err; } else { get_options_mutable()->master_key_fname = tor_strdup(key_line->value); } @@ -5243,13 +5404,16 @@ options_init_from_string(const char *cf_defaults, const char *cf, newoptions->command = command; newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL; + smartlist_t *opened_files = smartlist_new(); for (int i = 0; i < 2; ++i) { const char *body = i==0 ? cf_defaults : cf; if (!body) continue; + /* get config lines, assign them */ retval = config_get_lines_include(body, &cl, 1, - body == cf ? &cf_has_include : NULL); + body == cf ? &cf_has_include : NULL, + opened_files); if (retval < 0) { err = SETOPT_ERR_PARSE; goto err; @@ -5278,6 +5442,7 @@ options_init_from_string(const char *cf_defaults, const char *cf, } newoptions->IncludeUsed = cf_has_include; + newoptions->FilesOpenedByIncludes = opened_files; /* If this is a testing network configuration, change defaults * for a list of dependent config options, re-initialize newoptions @@ -5317,13 +5482,16 @@ options_init_from_string(const char *cf_defaults, const char *cf, newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL; /* Assign all options a second time. */ + opened_files = smartlist_new(); for (int i = 0; i < 2; ++i) { const char *body = i==0 ? cf_defaults : cf; if (!body) continue; + /* get config lines, assign them */ retval = config_get_lines_include(body, &cl, 1, - body == cf ? &cf_has_include : NULL); + body == cf ? &cf_has_include : NULL, + opened_files); if (retval < 0) { err = SETOPT_ERR_PARSE; goto err; @@ -5348,6 +5516,7 @@ options_init_from_string(const char *cf_defaults, const char *cf, newoptions->IncludeUsed = cf_has_include; in_option_validation = 1; + newoptions->FilesOpenedByIncludes = opened_files; /* Validate newoptions */ if (options_validate(oldoptions, newoptions, newdefaultoptions, @@ -5374,6 +5543,12 @@ options_init_from_string(const char *cf_defaults, const char *cf, err: in_option_validation = 0; + if (opened_files) { + SMARTLIST_FOREACH(opened_files, char *, f, tor_free(f)); + smartlist_free(opened_files); + } + // may have been set to opened_files, avoid double free + newoptions->FilesOpenedByIncludes = NULL; or_options_free(newoptions); or_options_free(newdefaultoptions); if (*msg) { @@ -5569,16 +5744,29 @@ options_init_logs(const or_options_t *old_options, or_options_t *options, } goto cleanup; } - if (smartlist_len(elts) == 1 && - !strcasecmp(smartlist_get(elts,0), "syslog")) { + if (smartlist_len(elts) == 1) { + if (!strcasecmp(smartlist_get(elts,0), "syslog")) { #ifdef HAVE_SYSLOG_H - if (!validate_only) { - add_syslog_log(severity, options->SyslogIdentityTag); - } + if (!validate_only) { + add_syslog_log(severity, options->SyslogIdentityTag); + } #else - log_warn(LD_CONFIG, "Syslog is not supported on this system. Sorry."); + log_warn(LD_CONFIG, "Syslog is not supported on this system. Sorry."); #endif /* defined(HAVE_SYSLOG_H) */ - goto cleanup; + goto cleanup; + } + + if (!strcasecmp(smartlist_get(elts, 0), "android")) { +#ifdef HAVE_ANDROID_LOG_H + if (!validate_only) { + add_android_log(severity, options->AndroidIdentityTag); + } +#else + log_warn(LD_CONFIG, "Android logging is not supported" + " on this system. Sorry."); +#endif // HAVE_ANDROID_LOG_H. + goto cleanup; + } } if (smartlist_len(elts) == 2 && @@ -5664,7 +5852,7 @@ validate_transport_socks_arguments(const smartlist_t *args) /** Deallocate a bridge_line_t structure. */ /* private */ void -bridge_line_free(bridge_line_t *bridge_line) +bridge_line_free_(bridge_line_t *bridge_line) { if (!bridge_line) return; @@ -6430,7 +6618,7 @@ port_cfg_new(size_t namelen) /** Free all storage held in <b>port</b> */ STATIC void -port_cfg_free(port_cfg_t *port) +port_cfg_free_(port_cfg_t *port) { tor_free(port); } @@ -7608,60 +7796,81 @@ port_exists_by_type_addr32h_port(int listener_type, uint32_t addr_ipv4h, check_wildcard); } -/** Adjust the value of options->DataDirectory, or fill it in if it's - * absent. Return 0 on success, -1 on failure. */ -static int -normalize_data_directory(or_options_t *options) +/** Allocate and return a good value for the DataDirectory based on + * <b>val</b>, which may be NULL. Return NULL on failure. */ +static char * +get_data_directory(const char *val) { #ifdef _WIN32 - char *p; - if (options->DataDirectory) - return 0; /* all set */ - p = tor_malloc(MAX_PATH); - strlcpy(p,get_windows_conf_root(),MAX_PATH); - options->DataDirectory = p; - return 0; + if (val) { + return tor_strdup(val); + } else { + return tor_strdup(get_windows_conf_root()); + } #else /* !(defined(_WIN32)) */ - const char *d = options->DataDirectory; + const char *d = val; if (!d) d = "~/.tor"; - if (strncmp(d,"~/",2) == 0) { - char *fn = expand_filename(d); - if (!fn) { - log_warn(LD_CONFIG,"Failed to expand filename \"%s\".", d); - return -1; - } - if (!options->DataDirectory && !strcmp(fn,"/.tor")) { - /* If our homedir is /, we probably don't want to use it. */ - /* Default to LOCALSTATEDIR/tor which is probably closer to what we - * want. */ - log_warn(LD_CONFIG, - "Default DataDirectory is \"~/.tor\". This expands to " - "\"%s\", which is probably not what you want. Using " - "\"%s"PATH_SEPARATOR"tor\" instead", fn, LOCALSTATEDIR); - tor_free(fn); - fn = tor_strdup(LOCALSTATEDIR PATH_SEPARATOR "tor"); - } - tor_free(options->DataDirectory); - options->DataDirectory = fn; - } - return 0; + if (!strcmpstart(d, "~/")) { + char *fn = expand_filename(d); + if (!fn) { + log_warn(LD_CONFIG,"Failed to expand filename \"%s\".", d); + return NULL; + } + if (!val && !strcmp(fn,"/.tor")) { + /* If our homedir is /, we probably don't want to use it. */ + /* Default to LOCALSTATEDIR/tor which is probably closer to what we + * want. */ + log_warn(LD_CONFIG, + "Default DataDirectory is \"~/.tor\". This expands to " + "\"%s\", which is probably not what you want. Using " + "\"%s"PATH_SEPARATOR"tor\" instead", fn, LOCALSTATEDIR); + tor_free(fn); + fn = tor_strdup(LOCALSTATEDIR PATH_SEPARATOR "tor"); + } + return fn; + } + return tor_strdup(d); #endif /* defined(_WIN32) */ } -/** Check and normalize the value of options->DataDirectory; return 0 if it - * is sane, -1 otherwise. */ +/** Check and normalize the values of options->{Key,Data,Cache}Directory; + * return 0 if it is sane, -1 otherwise. */ static int -validate_data_directory(or_options_t *options) +validate_data_directories(or_options_t *options) { - if (normalize_data_directory(options) < 0) + tor_free(options->DataDirectory); + options->DataDirectory = get_data_directory(options->DataDirectory_option); + if (!options->DataDirectory) return -1; - tor_assert(options->DataDirectory); if (strlen(options->DataDirectory) > (512-128)) { log_warn(LD_CONFIG, "DataDirectory is too long."); return -1; } + + tor_free(options->KeyDirectory); + if (options->KeyDirectory_option) { + options->KeyDirectory = get_data_directory(options->KeyDirectory_option); + if (!options->KeyDirectory) + return -1; + } else { + /* Default to the data directory's keys subdir */ + tor_asprintf(&options->KeyDirectory, "%s"PATH_SEPARATOR"keys", + options->DataDirectory); + } + + tor_free(options->CacheDirectory); + if (options->CacheDirectory_option) { + options->CacheDirectory = get_data_directory( + options->CacheDirectory_option); + if (!options->CacheDirectory) + return -1; + } else { + /* Default to the data directory. */ + options->CacheDirectory = tor_strdup(options->DataDirectory); + } + return 0; } @@ -7802,53 +8011,56 @@ init_libevent(const or_options_t *options) suppress_libevent_log_msg(NULL); } -/** Return a newly allocated string holding a filename relative to the data - * directory. If <b>sub1</b> is present, it is the first path component after +/** Return a newly allocated string holding a filename relative to the + * directory in <b>options</b> specified by <b>roottype</b>. + * If <b>sub1</b> is present, it is the first path component after * the data directory. If <b>sub2</b> is also present, it is the second path * component after the data directory. If <b>suffix</b> is present, it * is appended to the filename. * - * Examples: - * get_datadir_fname2_suffix("a", NULL, NULL) -> $DATADIR/a - * get_datadir_fname2_suffix("a", NULL, ".tmp") -> $DATADIR/a.tmp - * get_datadir_fname2_suffix("a", "b", ".tmp") -> $DATADIR/a/b/.tmp - * get_datadir_fname2_suffix("a", "b", NULL) -> $DATADIR/a/b - * - * Note: Consider using the get_datadir_fname* macros in or.h. + * Note: Consider using macros in config.h that wrap this function; + * you should probably never need to call it as-is. */ MOCK_IMPL(char *, -options_get_datadir_fname2_suffix,(const or_options_t *options, - const char *sub1, const char *sub2, - const char *suffix)) +options_get_dir_fname2_suffix,(const or_options_t *options, + directory_root_t roottype, + const char *sub1, const char *sub2, + const char *suffix)) { - char *fname = NULL; - size_t len; tor_assert(options); - tor_assert(options->DataDirectory); - tor_assert(sub1 || !sub2); /* If sub2 is present, sub1 must be present. */ - len = strlen(options->DataDirectory); - if (sub1) { - len += strlen(sub1)+1; - if (sub2) - len += strlen(sub2)+1; - } - if (suffix) - len += strlen(suffix); - len++; - fname = tor_malloc(len); - if (sub1) { - if (sub2) { - tor_snprintf(fname, len, "%s"PATH_SEPARATOR"%s"PATH_SEPARATOR"%s", - options->DataDirectory, sub1, sub2); - } else { - tor_snprintf(fname, len, "%s"PATH_SEPARATOR"%s", - options->DataDirectory, sub1); - } + + const char *rootdir = NULL; + switch (roottype) { + case DIRROOT_DATADIR: + rootdir = options->DataDirectory; + break; + case DIRROOT_CACHEDIR: + rootdir = options->CacheDirectory; + break; + case DIRROOT_KEYDIR: + rootdir = options->KeyDirectory; + break; + default: + tor_assert_unreached(); + break; + } + tor_assert(rootdir); + + if (!suffix) + suffix = ""; + + char *fname = NULL; + + if (sub1 == NULL) { + tor_asprintf(&fname, "%s%s", rootdir, suffix); + tor_assert(!sub2); /* If sub2 is present, sub1 must be present. */ + } else if (sub2 == NULL) { + tor_asprintf(&fname, "%s"PATH_SEPARATOR"%s%s", rootdir, sub1, suffix); } else { - strlcpy(fname, options->DataDirectory, len); + tor_asprintf(&fname, "%s"PATH_SEPARATOR"%s"PATH_SEPARATOR"%s%s", + rootdir, sub1, sub2, suffix); } - if (suffix) - strlcat(fname, suffix, len); + return fname; } @@ -7889,28 +8101,6 @@ write_to_data_subdir(const char* subdir, const char* fname, return return_val; } -/** Given a file name check to see whether the file exists but has not been - * modified for a very long time. If so, remove it. */ -void -remove_file_if_very_old(const char *fname, time_t now) -{ -#define VERY_OLD_FILE_AGE (28*24*60*60) - struct stat st; - - log_debug(LD_FS, "stat()ing %s", fname); - if (stat(sandbox_intern_string(fname), &st)==0 && - st.st_mtime < now-VERY_OLD_FILE_AGE) { - char buf[ISO_TIME_LEN+1]; - format_local_iso_time(buf, st.st_mtime); - log_notice(LD_GENERAL, "Obsolete file %s hasn't been modified since %s. " - "Removing it.", fname, buf); - if (unlink(fname) != 0) { - log_warn(LD_FS, "Failed to unlink %s: %s", - fname, strerror(errno)); - } - } -} - /** Return a smartlist of ports that must be forwarded by * tor-fw-helper. The smartlist contains the ports in a string format * that is understandable by tor-fw-helper. */ diff --git a/src/or/config.h b/src/or/config.h index efdd8c59b0..7c7ef1825a 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -58,31 +58,77 @@ config_line_t *option_get_assignment(const or_options_t *options, const char *key); int options_save_current(void); const char *get_torrc_fname(int defaults_fname); +typedef enum { + DIRROOT_DATADIR, + DIRROOT_CACHEDIR, + DIRROOT_KEYDIR +} directory_root_t; + MOCK_DECL(char *, - options_get_datadir_fname2_suffix, + options_get_dir_fname2_suffix, (const or_options_t *options, + directory_root_t roottype, const char *sub1, const char *sub2, const char *suffix)); + +/* These macros wrap options_get_dir_fname2_suffix to provide a more + * convenient API for finding filenames that Tor uses inside its storage + * They are named according to a pattern: + * (options_)?get_(cache|key|data)dir_fname(2)?(_suffix)? + * + * Macros that begin with options_ take an options argument; the others + * work with respect to the global options. + * + * Each macro works relative to the data directory, the key directory, + * or the cache directory, as determined by which one is mentioned. + * + * Macro variants with "2" in their name take two path components; others + * take one. + * + * Macro variants with "_suffix" at the end take an additional suffix + * that gets appended to the end of the file + */ +#define options_get_datadir_fname2_suffix(options, sub1, sub2, suffix) \ + options_get_dir_fname2_suffix((options), DIRROOT_DATADIR, \ + (sub1), (sub2), (suffix)) +#define options_get_cachedir_fname2_suffix(options, sub1, sub2, suffix) \ + options_get_dir_fname2_suffix((options), DIRROOT_CACHEDIR, \ + (sub1), (sub2), (suffix)) +#define options_get_keydir_fname2_suffix(options, sub1, sub2, suffix) \ + options_get_dir_fname2_suffix((options), DIRROOT_KEYDIR, \ + (sub1), (sub2), (suffix)) + +#define options_get_datadir_fname(opts,sub1) \ + options_get_datadir_fname2_suffix((opts),(sub1), NULL, NULL) +#define options_get_datadir_fname2(opts,sub1,sub2) \ + options_get_datadir_fname2_suffix((opts),(sub1), (sub2), NULL) + #define get_datadir_fname2_suffix(sub1, sub2, suffix) \ options_get_datadir_fname2_suffix(get_options(), (sub1), (sub2), (suffix)) -/** Return a newly allocated string containing datadir/sub1. See - * get_datadir_fname2_suffix. */ -#define get_datadir_fname(sub1) get_datadir_fname2_suffix((sub1), NULL, NULL) -/** Return a newly allocated string containing datadir/sub1/sub2. See - * get_datadir_fname2_suffix. */ +#define get_datadir_fname(sub1) \ + get_datadir_fname2_suffix((sub1), NULL, NULL) #define get_datadir_fname2(sub1,sub2) \ get_datadir_fname2_suffix((sub1), (sub2), NULL) -/** Return a newly allocated string containing datadir/sub1/sub2 relative to - * opts. See get_datadir_fname2_suffix. */ -#define options_get_datadir_fname2(opts,sub1,sub2) \ - options_get_datadir_fname2_suffix((opts),(sub1), (sub2), NULL) -/** Return a newly allocated string containing datadir/sub1suffix. See - * get_datadir_fname2_suffix. */ #define get_datadir_fname_suffix(sub1, suffix) \ get_datadir_fname2_suffix((sub1), NULL, (suffix)) +/** DOCDOC */ +#define options_get_keydir_fname(options, sub1) \ + options_get_keydir_fname2_suffix((options), (sub1), NULL, NULL) +#define get_keydir_fname_suffix(sub1, suffix) \ + options_get_keydir_fname2_suffix(get_options(), (sub1), NULL, suffix) +#define get_keydir_fname(sub1) \ + options_get_keydir_fname2_suffix(get_options(), (sub1), NULL, NULL) + +#define get_cachedir_fname(sub1) \ + options_get_cachedir_fname2_suffix(get_options(), (sub1), NULL, NULL) +#define get_cachedir_fname_suffix(sub1, suffix) \ + options_get_cachedir_fname2_suffix(get_options(), (sub1), NULL, (suffix)) + int using_default_dir_authorities(const or_options_t *options); +int create_keys_directory(const or_options_t *options); + int check_or_create_data_subdir(const char *subdir); int write_to_data_subdir(const char* subdir, const char* fname, const char* str, const char* descr); @@ -152,7 +198,9 @@ typedef struct bridge_line_t { transport proxy. */ } bridge_line_t; -void bridge_line_free(bridge_line_t *bridge_line); +void bridge_line_free_(bridge_line_t *bridge_line); +#define bridge_line_free(line) \ + FREE_AND_NULL(bridge_line_t, bridge_line_free_, (line)) bridge_line_t *parse_bridge_line(const char *line); smartlist_t *get_options_from_transport_options_line(const char *line, const char *transport); @@ -175,8 +223,12 @@ extern struct config_format_t options_format; #endif STATIC port_cfg_t *port_cfg_new(size_t namelen); -STATIC void port_cfg_free(port_cfg_t *port); -STATIC void or_options_free(or_options_t *options); +#define port_cfg_free(port) \ + FREE_AND_NULL(port_cfg_t, port_cfg_free_, (port)) +STATIC void port_cfg_free_(port_cfg_t *port); +#define or_options_free(opt) \ + FREE_AND_NULL(or_options_t, or_options_free_, (opt)) +STATIC void or_options_free_(or_options_t *options); STATIC int options_validate_single_onion(or_options_t *options, char **msg); STATIC int options_validate(or_options_t *old_options, diff --git a/src/or/confparse.c b/src/or/confparse.c index abae7e33dc..64ed9ee6bb 100644 --- a/src/or/confparse.c +++ b/src/or/confparse.c @@ -863,7 +863,7 @@ config_reset(const config_format_t *fmt, void *options, /** Release storage held by <b>options</b>. */ void -config_free(const config_format_t *fmt, void *options) +config_free_(const config_format_t *fmt, void *options) { int i; diff --git a/src/or/confparse.h b/src/or/confparse.h index 6f0b3b325c..fc4a7b2d06 100644 --- a/src/or/confparse.h +++ b/src/or/confparse.h @@ -68,7 +68,7 @@ typedef union { config_line_t **LINELIST_V; routerset_t **ROUTERSET; } confparse_dummy_values_t; -#endif +#endif /* defined(TOR_UNIT_TESTS) */ /** An abbreviation for a configuration option allowed on the command line. */ typedef struct config_abbrev_t { @@ -132,13 +132,13 @@ typedef struct config_var_t { { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL, { .INT=NULL } } #define DUMMY_TYPECHECK_INSTANCE(tp) \ static tp tp ## _dummy -#else +#else /* !(defined(TOR_UNIT_TESTS)) */ #define CONF_TEST_MEMBERS(tp, conftype, member) #define END_OF_CONFIG_VARS { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } /* Repeatedly declarable incomplete struct to absorb redundant semicolons */ #define DUMMY_TYPECHECK_INSTANCE(tp) \ struct tor_semicolon_eater -#endif +#endif /* defined(TOR_UNIT_TESTS) */ /** Type of a callback to validate whether a given configuration is * well-formed and consistent. See options_trial_assign() for documentation @@ -177,7 +177,12 @@ typedef struct config_format_t { #define CAL_WARN_DEPRECATIONS (1u<<2) void *config_new(const config_format_t *fmt); -void config_free(const config_format_t *fmt, void *options); +void config_free_(const config_format_t *fmt, void *options); +#define config_free(fmt, options) do { \ + config_free_((fmt), (options)); \ + (options) = NULL; \ + } while (0) + config_line_t *config_get_assigned_option(const config_format_t *fmt, const void *options, const char *key, int escape_val); diff --git a/src/or/connection.c b/src/or/connection.c index d2cf4fb416..3c37c59b40 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2001 Matej Pfajfar. + /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. * Copyright (c) 2007-2017, The Tor Project, Inc. */ @@ -118,8 +118,6 @@ static connection_t *connection_listener_new( const port_cfg_t *portcfg); static void connection_init(time_t now, connection_t *conn, int type, int socket_family); -static int connection_init_accepted_conn(connection_t *conn, - const listener_connection_t *listener); static int connection_handle_listener_read(connection_t *conn, int new_type); static int connection_bucket_should_increase(int bucket, or_connection_t *conn); @@ -501,7 +499,7 @@ conn_listener_type_supports_af_unix(int type) * if <b>conn</b> is an OR or OP connection. */ STATIC void -connection_free_(connection_t *conn) +connection_free_minimal(connection_t *conn) { void *mem; size_t memlen; @@ -677,7 +675,7 @@ connection_free_(connection_t *conn) /** Make sure <b>conn</b> isn't in any of the global conn lists; then free it. */ MOCK_IMPL(void, -connection_free,(connection_t *conn)) +connection_free_,(connection_t *conn)) { if (!conn) return; @@ -706,7 +704,7 @@ connection_free,(connection_t *conn)) } #endif /* 1 */ connection_unregister_events(conn); - connection_free_(conn); + connection_free_minimal(conn); } /** @@ -1666,11 +1664,15 @@ connection_handle_listener_read(connection_t *conn, int new_type) } /** Initialize states for newly accepted connection <b>conn</b>. + * * If conn is an OR, start the TLS handshake. + * * If conn is a transparent AP, get its original destination * and place it in circuit_wait. + * + * The <b>listener</b> parameter is only used for AP connections. */ -static int +int connection_init_accepted_conn(connection_t *conn, const listener_connection_t *listener) { @@ -1750,7 +1752,11 @@ connection_connect_sockaddr,(connection_t *conn, if (get_options()->DisableNetwork) { /* We should never even try to connect anyplace if DisableNetwork is set. - * Warn if we do, and refuse to make the connection. */ + * Warn if we do, and refuse to make the connection. + * + * We only check DisableNetwork here, not we_are_hibernating(), since + * we'll still try to fulfill client requests sometimes in the latter case + * (if it is soft hibernation) */ static ratelim_t disablenet_violated = RATELIM_INIT(30*60); *socket_error = SOCK_ERRNO(ENETUNREACH); log_fn_ratelim(&disablenet_violated, LOG_WARN, LD_BUG, @@ -4049,6 +4055,68 @@ connection_flush(connection_t *conn) return connection_handle_write(conn, 1); } +/** Helper for connection_write_to_buf_impl and connection_write_buf_to_buf: + * + * Return true iff it is okay to queue bytes on <b>conn</b>'s outbuf for + * writing. + */ +static int +connection_may_write_to_buf(connection_t *conn) +{ + /* if it's marked for close, only allow write if we mean to flush it */ + if (conn->marked_for_close && !conn->hold_open_until_flushed) + return 0; + + return 1; +} + +/** Helper for connection_write_to_buf_impl and connection_write_buf_to_buf: + * + * Called when an attempt to add bytes on <b>conn</b>'s outbuf has failed; + * mark the connection and warn as appropriate. + */ +static void +connection_write_to_buf_failed(connection_t *conn) +{ + if (CONN_IS_EDGE(conn)) { + /* if it failed, it means we have our package/delivery windows set + wrong compared to our max outbuf size. close the whole circuit. */ + log_warn(LD_NET, + "write_to_buf failed. Closing circuit (fd %d).", (int)conn->s); + circuit_mark_for_close(circuit_get_by_edge_conn(TO_EDGE_CONN(conn)), + END_CIRC_REASON_INTERNAL); + } else if (conn->type == CONN_TYPE_OR) { + or_connection_t *orconn = TO_OR_CONN(conn); + log_warn(LD_NET, + "write_to_buf failed on an orconn; notifying of error " + "(fd %d)", (int)(conn->s)); + connection_or_close_for_error(orconn, 0); + } else { + log_warn(LD_NET, + "write_to_buf failed. Closing connection (fd %d).", + (int)conn->s); + connection_mark_for_close(conn); + } +} + +/** Helper for connection_write_to_buf_impl and connection_write_buf_to_buf: + * + * Called when an attempt to add bytes on <b>conn</b>'s outbuf has succeeded: + * record the number of bytes added. + */ +static void +connection_write_to_buf_commit(connection_t *conn, size_t len) +{ + /* If we receive optimistic data in the EXIT_CONN_STATE_RESOLVING + * state, we don't want to try to write it right away, since + * conn->write_event won't be set yet. Otherwise, write data from + * this conn as the socket is available. */ + if (conn->write_event) { + connection_start_writing(conn); + } + conn->outbuf_flushlen += len; +} + /** Append <b>len</b> bytes of <b>string</b> onto <b>conn</b>'s * outbuf, and ask it to start writing. * @@ -4063,58 +4131,52 @@ connection_write_to_buf_impl_,(const char *string, size_t len, { /* XXXX This function really needs to return -1 on failure. */ int r; - size_t old_datalen; if (!len && !(zlib<0)) return; - /* if it's marked for close, only allow write if we mean to flush it */ - if (conn->marked_for_close && !conn->hold_open_until_flushed) + + if (!connection_may_write_to_buf(conn)) return; - old_datalen = buf_datalen(conn->outbuf); + size_t written; + if (zlib) { + size_t old_datalen = buf_datalen(conn->outbuf); dir_connection_t *dir_conn = TO_DIR_CONN(conn); int done = zlib < 0; CONN_LOG_PROTECT(conn, r = buf_add_compress(conn->outbuf, - dir_conn->compress_state, - string, len, done)); + dir_conn->compress_state, + string, len, done)); + written = buf_datalen(conn->outbuf) - old_datalen; } else { CONN_LOG_PROTECT(conn, r = buf_add(conn->outbuf, string, len)); + written = len; } if (r < 0) { - if (CONN_IS_EDGE(conn)) { - /* if it failed, it means we have our package/delivery windows set - wrong compared to our max outbuf size. close the whole circuit. */ - log_warn(LD_NET, - "write_to_buf failed. Closing circuit (fd %d).", (int)conn->s); - circuit_mark_for_close(circuit_get_by_edge_conn(TO_EDGE_CONN(conn)), - END_CIRC_REASON_INTERNAL); - } else if (conn->type == CONN_TYPE_OR) { - or_connection_t *orconn = TO_OR_CONN(conn); - log_warn(LD_NET, - "write_to_buf failed on an orconn; notifying of error " - "(fd %d)", (int)(conn->s)); - connection_or_close_for_error(orconn, 0); - } else { - log_warn(LD_NET, - "write_to_buf failed. Closing connection (fd %d).", - (int)conn->s); - connection_mark_for_close(conn); - } + connection_write_to_buf_failed(conn); return; } + connection_write_to_buf_commit(conn, written); +} - /* If we receive optimistic data in the EXIT_CONN_STATE_RESOLVING - * state, we don't want to try to write it right away, since - * conn->write_event won't be set yet. Otherwise, write data from - * this conn as the socket is available. */ - if (conn->write_event) { - connection_start_writing(conn); - } - if (zlib) { - conn->outbuf_flushlen += buf_datalen(conn->outbuf) - old_datalen; - } else { - conn->outbuf_flushlen += len; - } +/** + * Add all bytes from <b>buf</b> to <b>conn</b>'s outbuf, draining them + * from <b>buf</b>. (If the connection is marked and will soon be closed, + * nothing is drained.) + */ +void +connection_buf_add_buf(connection_t *conn, buf_t *buf) +{ + tor_assert(conn); + tor_assert(buf); + size_t len = buf_datalen(buf); + if (len == 0) + return; + + if (!connection_may_write_to_buf(conn)) + return; + + buf_move_all(conn->outbuf, buf); + connection_write_to_buf_commit(conn, len); } #define CONN_GET_ALL_TEMPLATE(var, test) \ @@ -5177,8 +5239,8 @@ proxy_type_to_string(int proxy_type) return NULL; /*Unreached*/ } -/** Call connection_free_() on every connection in our array, and release all - * storage held by connection.c. +/** Call connection_free_minimal() on every connection in our array, and + * release all storage held by connection.c. * * Don't do the checks in connection_free(), because they will * fail. @@ -5202,7 +5264,8 @@ connection_free_all(void) /* Clear out our list of broken connections */ clear_broken_connection_map(0); - SMARTLIST_FOREACH(conns, connection_t *, conn, connection_free_(conn)); + SMARTLIST_FOREACH(conns, connection_t *, conn, + connection_free_minimal(conn)); if (outgoing_addrs) { SMARTLIST_FOREACH(outgoing_addrs, tor_addr_t *, addr, tor_free(addr)); diff --git a/src/or/connection.h b/src/or/connection.h index 4a5bd6971b..6bc5a7cfd0 100644 --- a/src/or/connection.h +++ b/src/or/connection.h @@ -26,9 +26,12 @@ entry_connection_t *entry_connection_new(int type, int socket_family); control_connection_t *control_connection_new(int socket_family); listener_connection_t *listener_connection_new(int type, int socket_family); connection_t *connection_new(int type, int socket_family); - +int connection_init_accepted_conn(connection_t *conn, + const listener_connection_t *listener); void connection_link_connections(connection_t *conn_a, connection_t *conn_b); -MOCK_DECL(void,connection_free,(connection_t *conn)); +MOCK_DECL(void,connection_free_,(connection_t *conn)); +#define connection_free(conn) \ + FREE_AND_NULL(connection_t, connection_free_, (conn)) void connection_free_all(void); void connection_about_to_close_connection(connection_t *conn); void connection_close_immediate(connection_t *conn); @@ -155,6 +158,7 @@ connection_buf_add_compress(const char *string, size_t len, { connection_write_to_buf_impl_(string, len, TO_CONN(conn), done ? -1 : 1); } +void connection_buf_add_buf(connection_t *conn, buf_t *buf); /* DOCDOC connection_get_inbuf_len */ static size_t connection_get_inbuf_len(connection_t *conn); @@ -243,7 +247,6 @@ char *alloc_http_authenticator(const char *authenticator); void assert_connection_ok(connection_t *conn, time_t now); int connection_or_nonopen_was_started_here(or_connection_t *conn); void connection_dump_buffer_mem_stats(int severity); -void remove_file_if_very_old(const char *fname, time_t now); void clock_skew_warning(const connection_t *conn, long apparent_skew, int trusted, log_domain_mask_t domain, @@ -266,7 +269,7 @@ connection_is_moribund(connection_t *conn) void connection_check_oos(int n_socks, int failed); #ifdef CONNECTION_PRIVATE -STATIC void connection_free_(connection_t *conn); +STATIC void connection_free_minimal(connection_t *conn); /* Used only by connection.c and test*.c */ uint32_t bucket_millis_empty(int tokens_before, uint32_t last_empty_time, diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index f178917f0b..0c4352ea13 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -339,6 +339,10 @@ relay_send_end_cell_from_edge(streamid_t stream_id, circuit_t *circ, payload[0] = (char) reason; + /* Note: we have to use relay_send_command_from_edge here, not + * connection_edge_end or connection_edge_send_command, since those require + * that we have a stream connected to a circuit, and we don't connect to a + * circuit until we have a pending/successful resolve. */ return relay_send_command_from_edge(stream_id, circ, RELAY_COMMAND_END, payload, 1, cpath_layer); } @@ -999,7 +1003,7 @@ connection_ap_mark_as_pending_circuit_(entry_connection_t *entry_conn, * So the fix is to tell it right now that it ought to finish its loop at * its next available opportunity. */ - tell_event_loop_to_finish(); + tell_event_loop_to_run_external_code(); } /** Mark <b>entry_conn</b> as no longer waiting for a circuit. */ @@ -3327,7 +3331,7 @@ handle_hs_exit_conn(circuit_t *circ, edge_connection_t *conn) relay_send_end_cell_from_edge(conn->stream_id, circ, END_STREAM_REASON_DONE, origin_circ->cpath->prev); - connection_free(TO_CONN(conn)); + connection_free_(TO_CONN(conn)); /* Drop the circuit here since it might be someone deliberately * scanning the hidden service ports. Note that this mitigates port @@ -3405,11 +3409,6 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) if (rh.length > RELAY_PAYLOAD_SIZE) return -END_CIRC_REASON_TORPROTOCOL; - /* Note: we have to use relay_send_command_from_edge here, not - * connection_edge_end or connection_edge_send_command, since those require - * that we have a stream connected to a circuit, and we don't connect to a - * circuit until we have a pending/successful resolve. */ - if (!server_mode(options) && circ->purpose != CIRCUIT_PURPOSE_S_REND_JOINED) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, @@ -3524,7 +3523,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) if (we_are_hibernating()) { relay_send_end_cell_from_edge(rh.stream_id, circ, END_STREAM_REASON_HIBERNATING, NULL); - connection_free(TO_CONN(n_stream)); + connection_free_(TO_CONN(n_stream)); return 0; } @@ -3602,7 +3601,7 @@ connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ) return 0; case 1: /* The result was cached; a resolved cell was sent. */ if (!dummy_conn->base_.marked_for_close) - connection_free(TO_CONN(dummy_conn)); + connection_free_(TO_CONN(dummy_conn)); return 0; case 0: /* resolve added to pending list */ assert_circuit_ok(TO_CIRCUIT(circ)); @@ -3775,8 +3774,8 @@ connection_exit_connect_dir(edge_connection_t *exitconn) if (connection_add(TO_CONN(exitconn))<0) { connection_edge_end(exitconn, END_STREAM_REASON_RESOURCELIMIT); - connection_free(TO_CONN(exitconn)); - connection_free(TO_CONN(dirconn)); + connection_free_(TO_CONN(exitconn)); + connection_free_(TO_CONN(dirconn)); return 0; } @@ -3788,7 +3787,7 @@ connection_exit_connect_dir(edge_connection_t *exitconn) connection_edge_end(exitconn, END_STREAM_REASON_RESOURCELIMIT); connection_close_immediate(TO_CONN(exitconn)); connection_mark_for_close(TO_CONN(exitconn)); - connection_free(TO_CONN(dirconn)); + connection_free_(TO_CONN(dirconn)); return 0; } diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 9e34063609..a877761491 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -505,7 +505,7 @@ var_cell_copy(const var_cell_t *src) /** Release all space held by <b>cell</b>. */ void -var_cell_free(var_cell_t *cell) +var_cell_free_(var_cell_t *cell) { tor_free(cell); } @@ -592,9 +592,6 @@ connection_or_flushed_some(or_connection_t *conn) { size_t datalen; - /* The channel will want to update its estimated queue size */ - channel_update_xmit_queue_size(TLS_CHAN_TO_BASE(conn->chan)); - /* If we're under the low water mark, add cells until we're just over the * high water mark. */ datalen = connection_get_outbuf_len(TO_CONN(conn)); @@ -886,7 +883,7 @@ connection_or_check_canonicity(or_connection_t *conn, int started_here) const node_t *r = node_get_by_id(id_digest); if (r && - node_supports_ed25519_link_authentication(r) && + node_supports_ed25519_link_authentication(r, 1) && ! node_ed25519_id_matches(r, ed_id)) { /* If this node is capable of proving an ed25519 ID, * we can't call this a canonical connection unless both IDs match. */ @@ -965,6 +962,36 @@ connection_or_mark_bad_for_new_circs(or_connection_t *or_conn) * too old for new circuits? */ #define TIME_BEFORE_OR_CONN_IS_TOO_OLD (60*60*24*7) +/** Expire an or_connection if it is too old. Helper for + * connection_or_group_set_badness_ and fast path for + * channel_rsa_id_group_set_badness. + * + * Returns 1 if the connection was already expired, else 0. + */ +int +connection_or_single_set_badness_(time_t now, + or_connection_t *or_conn, + int force) +{ + /* XXXX this function should also be about channels? */ + if (or_conn->base_.marked_for_close || + connection_or_is_bad_for_new_circs(or_conn)) + return 1; + + if (force || + or_conn->base_.timestamp_created + TIME_BEFORE_OR_CONN_IS_TOO_OLD + < now) { + log_info(LD_OR, + "Marking OR conn to %s:%d as too old for new circuits " + "(fd "TOR_SOCKET_T_FORMAT", %d secs old).", + or_conn->base_.address, or_conn->base_.port, or_conn->base_.s, + (int)(now - or_conn->base_.timestamp_created)); + connection_or_mark_bad_for_new_circs(or_conn); + } + + return 0; +} + /** Given a list of all the or_connections with a given * identity, set elements of that list as is_bad_for_new_circs as * appropriate. Helper for connection_or_set_bad_connections(). @@ -995,19 +1022,8 @@ connection_or_group_set_badness_(smartlist_t *group, int force) /* Pass 1: expire everything that's old, and see what the status of * everything else is. */ SMARTLIST_FOREACH_BEGIN(group, or_connection_t *, or_conn) { - if (or_conn->base_.marked_for_close || - connection_or_is_bad_for_new_circs(or_conn)) + if (connection_or_single_set_badness_(now, or_conn, force)) continue; - if (force || - or_conn->base_.timestamp_created + TIME_BEFORE_OR_CONN_IS_TOO_OLD - < now) { - log_info(LD_OR, - "Marking OR conn to %s:%d as too old for new circuits " - "(fd "TOR_SOCKET_T_FORMAT", %d secs old).", - or_conn->base_.address, or_conn->base_.port, or_conn->base_.s, - (int)(now - or_conn->base_.timestamp_created)); - connection_or_mark_bad_for_new_circs(or_conn); - } if (connection_or_is_bad_for_new_circs(or_conn)) { ++n_old; @@ -1247,7 +1263,7 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port, fmt_addrport(&TO_CONN(conn)->addr, TO_CONN(conn)->port)); } - connection_free(TO_CONN(conn)); + connection_free_(TO_CONN(conn)); return NULL; } @@ -1260,7 +1276,7 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port, connection_or_connect_failed(conn, errno_to_orconn_end_reason(socket_error), tor_socket_strerror(socket_error)); - connection_free(TO_CONN(conn)); + connection_free_(TO_CONN(conn)); return NULL; case 0: connection_watch_events(TO_CONN(conn), READ_EVENT | WRITE_EVENT); @@ -1854,7 +1870,7 @@ connection_init_or_handshake_state(or_connection_t *conn, int started_here) /** Free all storage held by <b>state</b>. */ void -or_handshake_state_free(or_handshake_state_t *state) +or_handshake_state_free_(or_handshake_state_t *state) { if (!state) return; diff --git a/src/or/connection_or.h b/src/or/connection_or.h index ee66b7c528..7c1dced631 100644 --- a/src/or/connection_or.h +++ b/src/or/connection_or.h @@ -68,7 +68,9 @@ int connection_or_client_learned_peer_id(or_connection_t *conn, const ed25519_public_key_t *ed_peer_id); time_t connection_or_client_used(or_connection_t *conn); MOCK_DECL(int, connection_or_get_num_circuits, (or_connection_t *conn)); -void or_handshake_state_free(or_handshake_state_t *state); +void or_handshake_state_free_(or_handshake_state_t *state); +#define or_handshake_state_free(state) \ + FREE_AND_NULL(or_handshake_state_t, or_handshake_state_free_, (state)) void or_handshake_state_record_cell(or_connection_t *conn, or_handshake_state_t *state, const cell_t *cell, @@ -105,13 +107,17 @@ int var_cell_pack_header(const var_cell_t *cell, char *hdr_out, int wide_circ_ids); var_cell_t *var_cell_new(uint16_t payload_len); var_cell_t *var_cell_copy(const var_cell_t *src); -void var_cell_free(var_cell_t *cell); +void var_cell_free_(var_cell_t *cell); +#define var_cell_free(cell) FREE_AND_NULL(var_cell_t, var_cell_free_, (cell)) /* DOCDOC */ #define MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS 4 #define MIN_LINK_PROTO_FOR_CHANNEL_PADDING 5 #define MAX_LINK_PROTO MIN_LINK_PROTO_FOR_CHANNEL_PADDING +int connection_or_single_set_badness_(time_t now, + or_connection_t *or_conn, + int force); void connection_or_group_set_badness_(smartlist_t *group, int force); #ifdef TOR_UNIT_TESTS diff --git a/src/or/conscache.c b/src/or/conscache.c index 1a15126f97..e25ac5f40b 100644 --- a/src/or/conscache.c +++ b/src/or/conscache.c @@ -79,7 +79,7 @@ consensus_cache_open(const char *subdir, int max_entries) { int storagedir_max_entries; consensus_cache_t *cache = tor_malloc_zero(sizeof(consensus_cache_t)); - char *directory = get_datadir_fname(subdir); + char *directory = get_cachedir_fname(subdir); cache->max_entries = max_entries; #ifdef MUST_UNMAP_TO_UNLINK @@ -170,7 +170,7 @@ consensus_cache_clear(consensus_cache_t *cache) * Drop all storage held by <b>cache</b>. */ void -consensus_cache_free(consensus_cache_t *cache) +consensus_cache_free_(consensus_cache_t *cache) { if (! cache) return; diff --git a/src/or/conscache.h b/src/or/conscache.h index 3c89dedf43..08a5c5a37b 100644 --- a/src/or/conscache.h +++ b/src/or/conscache.h @@ -10,9 +10,14 @@ typedef struct consensus_cache_entry_t consensus_cache_entry_t; typedef struct consensus_cache_t consensus_cache_t; HANDLE_DECL(consensus_cache_entry, consensus_cache_entry_t, ) +#define consensus_cache_entry_handle_free(h) \ + FREE_AND_NULL(consensus_cache_entry_handle_t, \ + consensus_cache_entry_handle_free_, (h)) consensus_cache_t *consensus_cache_open(const char *subdir, int max_entries); -void consensus_cache_free(consensus_cache_t *cache); +void consensus_cache_free_(consensus_cache_t *cache); +#define consensus_cache_free(cache) \ + FREE_AND_NULL(consensus_cache_t, consensus_cache_free_, (cache)) struct sandbox_cfg_elem; int consensus_cache_may_overallocate(consensus_cache_t *cache); int consensus_cache_register_with_sandbox(consensus_cache_t *cache, diff --git a/src/or/consdiffmgr.c b/src/or/consdiffmgr.c index e539b61484..38d901a3ae 100644 --- a/src/or/consdiffmgr.c +++ b/src/or/consdiffmgr.c @@ -207,9 +207,12 @@ HT_PROTOTYPE(cdm_diff_ht, cdm_diff_t, node, cdm_diff_hash, cdm_diff_eq) HT_GENERATE2(cdm_diff_ht, cdm_diff_t, node, cdm_diff_hash, cdm_diff_eq, 0.6, tor_reallocarray, tor_free_) +#define cdm_diff_free(diff) \ + FREE_AND_NULL(cdm_diff_t, cdm_diff_free_, (diff)) + /** Release all storage held in <b>diff</b>. */ static void -cdm_diff_free(cdm_diff_t *diff) +cdm_diff_free_(cdm_diff_t *diff) { if (!diff) return; @@ -1506,11 +1509,15 @@ consensus_diff_worker_threadfn(void *state_, void *work_) return WQ_RPL_REPLY; } +#define consensus_diff_worker_job_free(job) \ + FREE_AND_NULL(consensus_diff_worker_job_t, \ + consensus_diff_worker_job_free_, (job)) + /** * Helper: release all storage held in <b>job</b>. */ static void -consensus_diff_worker_job_free(consensus_diff_worker_job_t *job) +consensus_diff_worker_job_free_(consensus_diff_worker_job_t *job) { if (!job) return; @@ -1658,11 +1665,15 @@ typedef struct consensus_compress_worker_job_t { compressed_result_t out[ARRAY_LENGTH(compress_consensus_with)]; } consensus_compress_worker_job_t; +#define consensus_compress_worker_job_free(job) \ + FREE_AND_NULL(consensus_compress_worker_job_t, \ + consensus_compress_worker_job_free_, (job)) + /** * Free all resources held in <b>job</b> */ static void -consensus_compress_worker_job_free(consensus_compress_worker_job_t *job) +consensus_compress_worker_job_free_(consensus_compress_worker_job_t *job) { if (!job) return; diff --git a/src/or/control.c b/src/or/control.c index 202366aaec..cce5c7953b 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -58,7 +58,9 @@ #include "entrynodes.h" #include "geoip.h" #include "hibernate.h" +#include "hs_cache.h" #include "hs_common.h" +#include "hs_control.h" #include "main.h" #include "microdesc.h" #include "networkstatus.h" @@ -549,6 +551,49 @@ decode_escaped_string(const char *start, size_t in_len_max, return end+1; } +/** Create and add a new controller connection on <b>sock</b>. If + * <b>CC_LOCAL_FD_IS_OWNER</b> is set in <b>flags</b>, this Tor process should + * exit when the connection closes. If <b>CC_LOCAL_FD_IS_AUTHENTICATED</b> + * is set, then the connection does not need to authenticate. + */ +int +control_connection_add_local_fd(tor_socket_t sock, unsigned flags) +{ + if (BUG(! SOCKET_OK(sock))) + return -1; + const int is_owner = !!(flags & CC_LOCAL_FD_IS_OWNER); + const int is_authenticated = !!(flags & CC_LOCAL_FD_IS_AUTHENTICATED); + control_connection_t *control_conn = control_connection_new(AF_UNSPEC); + connection_t *conn = TO_CONN(control_conn); + conn->s = sock; + tor_addr_make_unspec(&conn->addr); + conn->port = 1; + conn->address = tor_strdup("<local socket>"); + + /* We take ownership of this socket so that later, when we close it, + * we don't freak out. */ + tor_take_socket_ownership(sock); + + if (set_socket_nonblocking(sock) < 0 || + connection_add(conn) < 0) { + connection_free(conn); + return -1; + } + + control_conn->is_owning_control_connection = is_owner; + + if (connection_init_accepted_conn(conn, NULL) < 0) { + connection_mark_for_close(conn); + return -1; + } + + if (is_authenticated) { + conn->state = CONTROL_CONN_STATE_OPEN; + } + + return 0; +} + /** Acts like sprintf, but writes its formatted string to the end of * <b>conn</b>-\>outbuf. */ static void @@ -740,9 +785,12 @@ queue_control_event_string,(uint16_t event, char *msg)) } } +#define queued_event_free(ev) \ + FREE_AND_NULL(queued_event_t, queued_event_free_, (ev)) + /** Release all storage held by <b>ev</b>. */ static void -queued_event_free(queued_event_t *ev) +queued_event_free_(queued_event_t *ev) { if (ev == NULL) return; @@ -1971,36 +2019,89 @@ getinfo_helper_dir(control_connection_t *control_conn, SMARTLIST_FOREACH(sl, char *, c, tor_free(c)); smartlist_free(sl); } else if (!strcmpstart(question, "hs/client/desc/id/")) { - rend_cache_entry_t *e = NULL; + hostname_type_t addr_type; question += strlen("hs/client/desc/id/"); - if (strlen(question) != REND_SERVICE_ID_LEN_BASE32) { + if (rend_valid_v2_service_id(question)) { + addr_type = ONION_V2_HOSTNAME; + } else if (hs_address_is_valid(question)) { + addr_type = ONION_V3_HOSTNAME; + } else { *errmsg = "Invalid address"; return -1; } - if (!rend_cache_lookup_entry(question, -1, &e)) { - /* Descriptor found in cache */ - *answer = tor_strdup(e->desc); + if (addr_type == ONION_V2_HOSTNAME) { + rend_cache_entry_t *e = NULL; + if (!rend_cache_lookup_entry(question, -1, &e)) { + /* Descriptor found in cache */ + *answer = tor_strdup(e->desc); + } else { + *errmsg = "Not found in cache"; + return -1; + } } else { - *errmsg = "Not found in cache"; - return -1; + ed25519_public_key_t service_pk; + const char *desc; + + /* The check before this if/else makes sure of this. */ + tor_assert(addr_type == ONION_V3_HOSTNAME); + + if (hs_parse_address(question, &service_pk, NULL, NULL) < 0) { + *errmsg = "Invalid v3 address"; + return -1; + } + + desc = hs_cache_lookup_encoded_as_client(&service_pk); + if (desc) { + *answer = tor_strdup(desc); + } else { + *errmsg = "Not found in cache"; + return -1; + } } } else if (!strcmpstart(question, "hs/service/desc/id/")) { - rend_cache_entry_t *e = NULL; + hostname_type_t addr_type; question += strlen("hs/service/desc/id/"); - if (strlen(question) != REND_SERVICE_ID_LEN_BASE32) { + if (rend_valid_v2_service_id(question)) { + addr_type = ONION_V2_HOSTNAME; + } else if (hs_address_is_valid(question)) { + addr_type = ONION_V3_HOSTNAME; + } else { *errmsg = "Invalid address"; return -1; } + rend_cache_entry_t *e = NULL; - if (!rend_cache_lookup_v2_desc_as_service(question, &e)) { - /* Descriptor found in cache */ - *answer = tor_strdup(e->desc); + if (addr_type == ONION_V2_HOSTNAME) { + if (!rend_cache_lookup_v2_desc_as_service(question, &e)) { + /* Descriptor found in cache */ + *answer = tor_strdup(e->desc); + } else { + *errmsg = "Not found in cache"; + return -1; + } } else { - *errmsg = "Not found in cache"; - return -1; + ed25519_public_key_t service_pk; + char *desc; + + /* The check before this if/else makes sure of this. */ + tor_assert(addr_type == ONION_V3_HOSTNAME); + + if (hs_parse_address(question, &service_pk, NULL, NULL) < 0) { + *errmsg = "Invalid v3 address"; + return -1; + } + + desc = hs_service_lookup_current_desc(&service_pk); + if (desc) { + /* Newly allocated string, we have ownership. */ + *answer = desc; + } else { + *errmsg = "Not found in cache"; + return -1; + } } } else if (!strcmpstart(question, "md/id/")) { const node_t *node = node_get_by_hex_id(question+strlen("md/id/"), 0); @@ -2072,7 +2173,7 @@ getinfo_helper_dir(control_connection_t *control_conn, *answer = tor_strdup(consensus->dir); } if (!*answer) { /* try loading it from disk */ - char *filename = get_datadir_fname("cached-consensus"); + char *filename = get_cachedir_fname("cached-consensus"); *answer = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL); tor_free(filename); if (!*answer) { /* generate an error */ @@ -2581,9 +2682,16 @@ circuit_describe_status_for_controller(origin_circuit_t *circ) } } - if (circ->rend_data != NULL) { - smartlist_add_asprintf(descparts, "REND_QUERY=%s", - rend_data_get_address(circ->rend_data)); + if (circ->rend_data != NULL || circ->hs_ident != NULL) { + char addr[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + const char *onion_address; + if (circ->rend_data) { + onion_address = rend_data_get_address(circ->rend_data); + } else { + hs_build_address(&circ->hs_ident->identity_pk, HS_VERSION_THREE, addr); + onion_address = addr; + } + smartlist_add_asprintf(descparts, "REND_QUERY=%s", onion_address); } { @@ -4234,9 +4342,11 @@ handle_control_hspost(control_connection_t *conn, const char *body) { static const char *opt_server = "SERVER="; + static const char *opt_hsaddress = "HSADDRESS="; smartlist_t *hs_dirs = NULL; const char *encoded_desc = body; size_t encoded_desc_len = len; + const char *onion_address = NULL; char *cp = memchr(body, '\n', len); if (cp == NULL) { @@ -4266,15 +4376,16 @@ handle_control_hspost(control_connection_t *conn, server); goto done; } - if (!node->rs->is_hs_dir) { - connection_printf_to_buf(conn, "552 Server \"%s\" is not a HSDir" - "\r\n", server); - goto done; - } /* Valid server, add it to our local list. */ if (!hs_dirs) hs_dirs = smartlist_new(); smartlist_add(hs_dirs, node->rs); + } else if (!strcasecmpstart(arg, opt_hsaddress)) { + if (!hs_address_is_valid(arg)) { + connection_printf_to_buf(conn, "512 Malformed onion address\r\n"); + goto done; + } + onion_address = arg; } else { connection_printf_to_buf(conn, "512 Unexpected argument \"%s\"\r\n", arg); @@ -4283,6 +4394,19 @@ handle_control_hspost(control_connection_t *conn, } SMARTLIST_FOREACH_END(arg); } + /* Handle the v3 case. */ + if (onion_address) { + char *desc_str = NULL; + read_escaped_data(encoded_desc, encoded_desc_len, &desc_str); + if (hs_control_hspost_command(desc_str, onion_address, hs_dirs) < 0) { + connection_printf_to_buf(conn, "554 Invalid descriptor\r\n"); + } + tor_free(desc_str); + goto done; + } + + /* From this point on, it is only v2. */ + /* Read the dot encoded descriptor, and parse it. */ rend_encoded_v2_service_descriptor_t *desc = tor_malloc_zero(sizeof(rend_encoded_v2_service_descriptor_t)); @@ -4327,6 +4451,52 @@ handle_control_hspost(control_connection_t *conn, return 0; } +/* Helper function for ADD_ONION that adds an ephemeral service depending on + * the given hs_version. + * + * The secret key in pk depends on the hs_version. The ownership of the key + * used in pk is given to the HS subsystem so the caller must stop accessing + * it after. + * + * The port_cfgs is a list of service port. Ownership transfered to service. + * The max_streams refers to the MaxStreams= key. + * The max_streams_close_circuit refers to the MaxStreamsCloseCircuit key. + * The auth_type is the authentication type of the clients in auth_clients. + * The ownership of that list is transfered to the service. + * + * On success (RSAE_OKAY), the address_out points to a newly allocated string + * containing the onion address without the .onion part. On error, address_out + * is untouched. */ +static hs_service_add_ephemeral_status_t +add_onion_helper_add_service(int hs_version, + add_onion_secret_key_t *pk, + smartlist_t *port_cfgs, int max_streams, + int max_streams_close_circuit, int auth_type, + smartlist_t *auth_clients, char **address_out) +{ + hs_service_add_ephemeral_status_t ret; + + tor_assert(pk); + tor_assert(port_cfgs); + tor_assert(address_out); + + switch (hs_version) { + case HS_VERSION_TWO: + ret = rend_service_add_ephemeral(pk->v2, port_cfgs, max_streams, + max_streams_close_circuit, auth_type, + auth_clients, address_out); + break; + case HS_VERSION_THREE: + ret = hs_service_add_ephemeral(pk->v3, port_cfgs, max_streams, + max_streams_close_circuit, address_out); + break; + default: + tor_assert_unreached(); + } + + return ret; +} + /** Called when we get a ADD_ONION command; parse the body, and set up * the new ephemeral Onion Service. */ static int @@ -4508,15 +4678,15 @@ handle_control_add_onion(control_connection_t *conn, } /* Parse the "keytype:keyblob" argument. */ - crypto_pk_t *pk = NULL; + int hs_version = 0; + add_onion_secret_key_t pk = { NULL }; const char *key_new_alg = NULL; char *key_new_blob = NULL; char *err_msg = NULL; - pk = add_onion_helper_keyarg(smartlist_get(args, 0), discard_pk, - &key_new_alg, &key_new_blob, - &err_msg); - if (!pk) { + if (add_onion_helper_keyarg(smartlist_get(args, 0), discard_pk, + &key_new_alg, &key_new_blob, &pk, &hs_version, + &err_msg) < 0) { if (err_msg) { connection_write_str_to_buf(err_msg, conn); tor_free(err_msg); @@ -4525,16 +4695,23 @@ handle_control_add_onion(control_connection_t *conn, } tor_assert(!err_msg); + /* Hidden service version 3 don't have client authentication support so if + * ClientAuth was given, send back an error. */ + if (hs_version == HS_VERSION_THREE && auth_clients) { + connection_printf_to_buf(conn, "513 ClientAuth not supported\r\n"); + goto out; + } + /* Create the HS, using private key pk, client authentication auth_type, * the list of auth_clients, and port config port_cfg. * rend_service_add_ephemeral() will take ownership of pk and port_cfg, * regardless of success/failure. */ char *service_id = NULL; - int ret = rend_service_add_ephemeral(pk, port_cfgs, max_streams, - max_streams_close_circuit, - auth_type, auth_clients, - &service_id); + int ret = add_onion_helper_add_service(hs_version, &pk, port_cfgs, + max_streams, + max_streams_close_circuit, auth_type, + auth_clients, &service_id); port_cfgs = NULL; /* port_cfgs is now owned by the rendservice code. */ auth_clients = NULL; /* so is auth_clients */ switch (ret) { @@ -4627,9 +4804,10 @@ handle_control_add_onion(control_connection_t *conn, * Note: The error messages returned are deliberately vague to avoid echoing * key material. */ -STATIC crypto_pk_t * +STATIC int add_onion_helper_keyarg(const char *arg, int discard_pk, const char **key_new_alg_out, char **key_new_blob_out, + add_onion_secret_key_t *decoded_key, int *hs_version, char **err_msg_out) { smartlist_t *key_args = smartlist_new(); @@ -4637,7 +4815,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, const char *key_new_alg = NULL; char *key_new_blob = NULL; char *err_msg = NULL; - int ok = 0; + int ret = -1; smartlist_split_string(key_args, arg, ":", SPLIT_IGNORE_BLANK, 0); if (smartlist_len(key_args) != 2) { @@ -4649,6 +4827,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, static const char *key_type_new = "NEW"; static const char *key_type_best = "BEST"; static const char *key_type_rsa1024 = "RSA1024"; + static const char *key_type_ed25519_v3 = "ED25519-V3"; const char *key_type = smartlist_get(key_args, 0); const char *key_blob = smartlist_get(key_args, 1); @@ -4661,9 +4840,23 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, goto err; } if (crypto_pk_num_bits(pk) != PK_BYTES*8) { + crypto_pk_free(pk); err_msg = tor_strdup("512 Invalid RSA key size\r\n"); goto err; } + decoded_key->v2 = pk; + *hs_version = HS_VERSION_TWO; + } else if (!strcasecmp(key_type_ed25519_v3, key_type)) { + /* "ED25519-V3:<Base64 Blob>" - Loading a pre-existing ed25519 key. */ + ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk)); + if (base64_decode((char *) sk->seckey, sizeof(sk->seckey), key_blob, + strlen(key_blob)) != sizeof(sk->seckey)) { + tor_free(sk); + err_msg = tor_strdup("512 Failed to decode ED25519-V3 key\r\n"); + goto err; + } + decoded_key->v3 = sk; + *hs_version = HS_VERSION_THREE; } else if (!strcasecmp(key_type_new, key_type)) { /* "NEW:<Algorithm>" - Generating a new key, blob as algorithm. */ if (!strcasecmp(key_type_rsa1024, key_blob) || @@ -4677,12 +4870,38 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, } if (!discard_pk) { if (crypto_pk_base64_encode(pk, &key_new_blob)) { + crypto_pk_free(pk); tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n", key_type_rsa1024); goto err; } key_new_alg = key_type_rsa1024; } + decoded_key->v2 = pk; + *hs_version = HS_VERSION_TWO; + } else if (!strcasecmp(key_type_ed25519_v3, key_blob)) { + ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk)); + if (ed25519_secret_key_generate(sk, 1) < 0) { + tor_free(sk); + tor_asprintf(&err_msg, "551 Failed to generate %s key\r\n", + key_type_ed25519_v3); + goto err; + } + if (!discard_pk) { + ssize_t len = base64_encode_size(sizeof(sk->seckey), 0) + 1; + key_new_blob = tor_malloc_zero(len); + if (base64_encode(key_new_blob, len, (const char *) sk->seckey, + sizeof(sk->seckey), 0) != (len - 1)) { + tor_free(sk); + tor_free(key_new_blob); + tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n", + key_type_ed25519_v3); + goto err; + } + key_new_alg = key_type_ed25519_v3; + } + decoded_key->v3 = sk; + *hs_version = HS_VERSION_THREE; } else { err_msg = tor_strdup("513 Invalid key type\r\n"); goto err; @@ -4693,8 +4912,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, } /* Succeded in loading or generating a private key. */ - tor_assert(pk); - ok = 1; + ret = 0; err: SMARTLIST_FOREACH(key_args, char *, cp, { @@ -4703,10 +4921,6 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, }); smartlist_free(key_args); - if (!ok) { - crypto_pk_free(pk); - pk = NULL; - } if (err_msg_out) { *err_msg_out = err_msg; } else { @@ -4715,7 +4929,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, *key_new_alg_out = key_new_alg; *key_new_blob_out = key_new_blob; - return pk; + return ret; } /** Helper function to handle parsing a ClientAuth argument to the @@ -4784,6 +4998,7 @@ handle_control_del_onion(control_connection_t *conn, uint32_t len, const char *body) { + int hs_version = 0; smartlist_t *args; (void) len; /* body is nul-terminated; it's safe to ignore the length */ args = getargs_helper("DEL_ONION", conn, body, 1, 1); @@ -4791,7 +5006,11 @@ handle_control_del_onion(control_connection_t *conn, return 0; const char *service_id = smartlist_get(args, 0); - if (!rend_valid_v2_service_id(service_id)) { + if (rend_valid_v2_service_id(service_id)) { + hs_version = HS_VERSION_TWO; + } else if (hs_address_is_valid(service_id)) { + hs_version = HS_VERSION_THREE; + } else { connection_printf_to_buf(conn, "512 Malformed Onion Service id\r\n"); goto out; } @@ -4818,8 +5037,20 @@ handle_control_del_onion(control_connection_t *conn, if (onion_services == NULL) { connection_printf_to_buf(conn, "552 Unknown Onion Service id\r\n"); } else { - int ret = rend_service_del_ephemeral(service_id); - if (ret) { + int ret = -1; + switch (hs_version) { + case HS_VERSION_TWO: + ret = rend_service_del_ephemeral(service_id); + break; + case HS_VERSION_THREE: + ret = hs_service_del_ephemeral(service_id); + break; + default: + /* The ret value will be -1 thus hitting the warning below. This should + * never happen because of the check at the start of the function. */ + break; + } + if (ret < 0) { /* This should *NEVER* fail, since the service is on either the * per-control connection list, or the global one. */ @@ -4889,9 +5120,16 @@ connection_control_closed(control_connection_t *conn) * The list and it's contents are scrubbed/freed in connection_free_. */ if (conn->ephemeral_onion_services) { - SMARTLIST_FOREACH(conn->ephemeral_onion_services, char *, cp, { - rend_service_del_ephemeral(cp); - }); + SMARTLIST_FOREACH_BEGIN(conn->ephemeral_onion_services, char *, cp) { + if (rend_valid_v2_service_id(cp)) { + rend_service_del_ephemeral(cp); + } else if (hs_address_is_valid(cp)) { + hs_service_del_ephemeral(cp); + } else { + /* An invalid .onion in our list should NEVER happen */ + tor_fragile_assert(); + } + } SMARTLIST_FOREACH_END(cp); } if (conn->is_owning_control_connection) { @@ -6571,8 +6809,7 @@ monitor_owning_controller_process(const char *process_spec) "owning controller: %s. Exiting.", msg); owning_controller_process_spec = NULL; - tor_cleanup(); - exit(1); + tor_shutdown_event_loop_and_exit(1); } } @@ -6970,27 +7207,33 @@ rend_hsaddress_str_or_unknown(const char *onion_address) * <b>rend_query</b> is used to fetch requested onion address and auth type. * <b>hs_dir</b> is the description of contacting hs directory. * <b>desc_id_base32</b> is the ID of requested hs descriptor. + * <b>hsdir_index</b> is the HSDir fetch index value for v3, an hex string. */ void -control_event_hs_descriptor_requested(const rend_data_t *rend_query, +control_event_hs_descriptor_requested(const char *onion_address, + rend_auth_type_t auth_type, const char *id_digest, - const char *desc_id_base32) + const char *desc_id, + const char *hsdir_index) { - if (!id_digest || !rend_query || !desc_id_base32) { - log_warn(LD_BUG, "Called with rend_query==%p, " - "id_digest==%p, desc_id_base32==%p", - rend_query, id_digest, desc_id_base32); + char *hsdir_index_field = NULL; + + if (BUG(!id_digest || !desc_id)) { return; } + if (hsdir_index) { + tor_asprintf(&hsdir_index_field, " HSDIR_INDEX=%s", hsdir_index); + } + send_control_event(EVENT_HS_DESC, - "650 HS_DESC REQUESTED %s %s %s %s\r\n", - rend_hsaddress_str_or_unknown( - rend_data_get_address(rend_query)), - rend_auth_type_to_string( - TO_REND_DATA_V2(rend_query)->auth_type), + "650 HS_DESC REQUESTED %s %s %s %s%s\r\n", + rend_hsaddress_str_or_unknown(onion_address), + rend_auth_type_to_string(auth_type), node_describe_longname_by_id(id_digest), - desc_id_base32); + desc_id, + hsdir_index_field ? hsdir_index_field : ""); + tor_free(hsdir_index_field); } /** For an HS descriptor query <b>rend_data</b>, using the @@ -7039,89 +7282,87 @@ get_desc_id_from_query(const rend_data_t *rend_data, const char *hsdir_fp) /** send HS_DESC CREATED event when a local service generates a descriptor. * - * <b>service_id</b> is the descriptor onion address. - * <b>desc_id_base32</b> is the descriptor ID. - * <b>replica</b> is the the descriptor replica number. + * <b>onion_address</b> is service address. + * <b>desc_id</b> is the descriptor ID. + * <b>replica</b> is the the descriptor replica number. If it is negative, it + * is ignored. */ void -control_event_hs_descriptor_created(const char *service_id, - const char *desc_id_base32, +control_event_hs_descriptor_created(const char *onion_address, + const char *desc_id, int replica) { - if (!service_id || !desc_id_base32) { - log_warn(LD_BUG, "Called with service_digest==%p, " - "desc_id_base32==%p", service_id, desc_id_base32); + char *replica_field = NULL; + + if (BUG(!onion_address || !desc_id)) { return; } + if (replica >= 0) { + tor_asprintf(&replica_field, " REPLICA=%d", replica); + } + send_control_event(EVENT_HS_DESC, - "650 HS_DESC CREATED %s UNKNOWN UNKNOWN %s " - "REPLICA=%d\r\n", - service_id, - desc_id_base32, - replica); + "650 HS_DESC CREATED %s UNKNOWN UNKNOWN %s%s\r\n", + onion_address, desc_id, + replica_field ? replica_field : ""); + tor_free(replica_field); } /** send HS_DESC upload event. * - * <b>service_id</b> is the descriptor onion address. + * <b>onion_address</b> is service address. * <b>hs_dir</b> is the description of contacting hs directory. - * <b>desc_id_base32</b> is the ID of requested hs descriptor. + * <b>desc_id</b> is the ID of requested hs descriptor. */ void -control_event_hs_descriptor_upload(const char *service_id, +control_event_hs_descriptor_upload(const char *onion_address, const char *id_digest, - const char *desc_id_base32) + const char *desc_id, + const char *hsdir_index) { - if (!service_id || !id_digest || !desc_id_base32) { - log_warn(LD_BUG, "Called with service_digest==%p, " - "desc_id_base32==%p, id_digest==%p", service_id, - desc_id_base32, id_digest); + char *hsdir_index_field = NULL; + + if (BUG(!onion_address || !id_digest || !desc_id)) { return; } + if (hsdir_index) { + tor_asprintf(&hsdir_index_field, " HSDIR_INDEX=%s", hsdir_index); + } + send_control_event(EVENT_HS_DESC, - "650 HS_DESC UPLOAD %s UNKNOWN %s %s\r\n", - service_id, + "650 HS_DESC UPLOAD %s UNKNOWN %s %s%s\r\n", + onion_address, node_describe_longname_by_id(id_digest), - desc_id_base32); + desc_id, + hsdir_index_field ? hsdir_index_field : ""); + tor_free(hsdir_index_field); } /** send HS_DESC event after got response from hs directory. * * NOTE: this is an internal function used by following functions: - * control_event_hs_descriptor_received - * control_event_hs_descriptor_failed + * control_event_hsv2_descriptor_received + * control_event_hsv2_descriptor_failed + * control_event_hsv3_descriptor_failed * * So do not call this function directly. */ -void -control_event_hs_descriptor_receive_end(const char *action, - const char *onion_address, - const rend_data_t *rend_data, - const char *id_digest, - const char *reason) +static void +event_hs_descriptor_receive_end(const char *action, + const char *onion_address, + const char *desc_id, + rend_auth_type_t auth_type, + const char *hsdir_id_digest, + const char *reason) { - char *desc_id_field = NULL; char *reason_field = NULL; - char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; - const char *desc_id = NULL; - if (!action || !rend_data || !onion_address) { - log_warn(LD_BUG, "Called with action==%p, rend_data==%p, " - "onion_address==%p", action, rend_data, onion_address); + if (BUG(!action || !onion_address)) { return; } - desc_id = get_desc_id_from_query(rend_data, id_digest); - if (desc_id != NULL) { - /* Set the descriptor ID digest to base32 so we can send it. */ - base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id, - DIGEST_LEN); - /* Extra whitespace is needed before the value. */ - tor_asprintf(&desc_id_field, " %s", desc_id_base32); - } - if (reason) { tor_asprintf(&reason_field, " REASON=%s", reason); } @@ -7130,14 +7371,13 @@ control_event_hs_descriptor_receive_end(const char *action, "650 HS_DESC %s %s %s %s%s%s\r\n", action, rend_hsaddress_str_or_unknown(onion_address), - rend_auth_type_to_string( - TO_REND_DATA_V2(rend_data)->auth_type), - id_digest ? - node_describe_longname_by_id(id_digest) : "UNKNOWN", - desc_id_field ? desc_id_field : "", + rend_auth_type_to_string(auth_type), + hsdir_id_digest ? + node_describe_longname_by_id(hsdir_id_digest) : + "UNKNOWN", + desc_id ? desc_id : "", reason_field ? reason_field : ""); - tor_free(desc_id_field); tor_free(reason_field); } @@ -7157,9 +7397,7 @@ control_event_hs_descriptor_upload_end(const char *action, { char *reason_field = NULL; - if (!action || !id_digest) { - log_warn(LD_BUG, "Called with action==%p, id_digest==%p", action, - id_digest); + if (BUG(!action || !id_digest)) { return; } @@ -7182,17 +7420,54 @@ control_event_hs_descriptor_upload_end(const char *action, * called when we successfully received a hidden service descriptor. */ void -control_event_hs_descriptor_received(const char *onion_address, - const rend_data_t *rend_data, - const char *id_digest) +control_event_hsv2_descriptor_received(const char *onion_address, + const rend_data_t *rend_data, + const char *hsdir_id_digest) +{ + char *desc_id_field = NULL; + const char *desc_id; + + if (BUG(!rend_data || !hsdir_id_digest || !onion_address)) { + return; + } + + desc_id = get_desc_id_from_query(rend_data, hsdir_id_digest); + if (desc_id != NULL) { + char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; + /* Set the descriptor ID digest to base32 so we can send it. */ + base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id, + DIGEST_LEN); + /* Extra whitespace is needed before the value. */ + tor_asprintf(&desc_id_field, " %s", desc_id_base32); + } + + event_hs_descriptor_receive_end("RECEIVED", onion_address, desc_id_field, + TO_REND_DATA_V2(rend_data)->auth_type, + hsdir_id_digest, NULL); + tor_free(desc_id_field); +} + +/* Send HS_DESC RECEIVED event + * + * Called when we successfully received a hidden service descriptor. */ +void +control_event_hsv3_descriptor_received(const char *onion_address, + const char *desc_id, + const char *hsdir_id_digest) { - if (!rend_data || !id_digest || !onion_address) { - log_warn(LD_BUG, "Called with rend_data==%p, id_digest==%p, " - "onion_address==%p", rend_data, id_digest, onion_address); + char *desc_id_field = NULL; + + if (BUG(!onion_address || !desc_id || !hsdir_id_digest)) { return; } - control_event_hs_descriptor_receive_end("RECEIVED", onion_address, - rend_data, id_digest, NULL); + + /* Because DescriptorID is an optional positional value, we need to add a + * whitespace before in order to not be next to the HsDir value. */ + tor_asprintf(&desc_id_field, " %s", desc_id); + + event_hs_descriptor_receive_end("RECEIVED", onion_address, desc_id_field, + REND_NO_AUTH, hsdir_id_digest, NULL); + tor_free(desc_id_field); } /** send HS_DESC UPLOADED event @@ -7203,9 +7478,7 @@ void control_event_hs_descriptor_uploaded(const char *id_digest, const char *onion_address) { - if (!id_digest) { - log_warn(LD_BUG, "Called with id_digest==%p", - id_digest); + if (BUG(!id_digest)) { return; } @@ -7219,17 +7492,58 @@ control_event_hs_descriptor_uploaded(const char *id_digest, * add it to REASON= field. */ void -control_event_hs_descriptor_failed(const rend_data_t *rend_data, - const char *id_digest, - const char *reason) +control_event_hsv2_descriptor_failed(const rend_data_t *rend_data, + const char *hsdir_id_digest, + const char *reason) +{ + char *desc_id_field = NULL; + const char *desc_id; + + if (BUG(!rend_data)) { + return; + } + + desc_id = get_desc_id_from_query(rend_data, hsdir_id_digest); + if (desc_id != NULL) { + char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; + /* Set the descriptor ID digest to base32 so we can send it. */ + base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id, + DIGEST_LEN); + /* Extra whitespace is needed before the value. */ + tor_asprintf(&desc_id_field, " %s", desc_id_base32); + } + + event_hs_descriptor_receive_end("FAILED", rend_data_get_address(rend_data), + desc_id_field, + TO_REND_DATA_V2(rend_data)->auth_type, + hsdir_id_digest, reason); + tor_free(desc_id_field); +} + +/** Send HS_DESC event to inform controller that the query to + * <b>onion_address</b> failed to retrieve hidden service descriptor + * <b>desc_id</b> from directory identified by <b>hsdir_id_digest</b>. If + * NULL, "UNKNOWN" is used. If <b>reason</b> is not NULL, add it to REASON= + * field. */ +void +control_event_hsv3_descriptor_failed(const char *onion_address, + const char *desc_id, + const char *hsdir_id_digest, + const char *reason) { - if (!rend_data) { - log_warn(LD_BUG, "Called with rend_data==%p", rend_data); + char *desc_id_field = NULL; + + if (BUG(!onion_address || !desc_id || !reason)) { return; } - control_event_hs_descriptor_receive_end("FAILED", - rend_data_get_address(rend_data), - rend_data, id_digest, reason); + + /* Because DescriptorID is an optional positional value, we need to add a + * whitespace before in order to not be next to the HsDir value. */ + tor_asprintf(&desc_id_field, " %s", desc_id); + + event_hs_descriptor_receive_end("FAILED", onion_address, desc_id_field, + REND_NO_AUTH, hsdir_id_digest, reason); + tor_free(desc_id_field); } /** Send HS_DESC_CONTENT event after completion of a successful fetch from hs @@ -7279,9 +7593,7 @@ control_event_hs_descriptor_upload_failed(const char *id_digest, const char *onion_address, const char *reason) { - if (!id_digest) { - log_warn(LD_BUG, "Called with id_digest==%p", - id_digest); + if (BUG(!id_digest)) { return; } control_event_hs_descriptor_upload_end("FAILED", onion_address, diff --git a/src/or/control.h b/src/or/control.h index e957b593a6..28ffeaed86 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -27,6 +27,10 @@ void control_ports_write_to_file(void); #define LOG_FN_CONN(conn, args) \ CONN_LOG_PROTECT(conn, log_fn args) +#define CC_LOCAL_FD_IS_OWNER (1u<<0) +#define CC_LOCAL_FD_IS_AUTHENTICATED (1u<<1) +int control_connection_add_local_fd(tor_socket_t sock, unsigned flags); + int connection_control_finished_flushing(control_connection_t *conn); int connection_control_reached_eof(control_connection_t *conn); void connection_control_closed(control_connection_t *conn); @@ -111,32 +115,39 @@ void control_event_transport_launched(const char *mode, tor_addr_t *addr, uint16_t port); const char *rend_auth_type_to_string(rend_auth_type_t auth_type); MOCK_DECL(const char *, node_describe_longname_by_id,(const char *id_digest)); -void control_event_hs_descriptor_requested(const rend_data_t *rend_query, - const char *desc_id_base32, - const char *hs_dir); -void control_event_hs_descriptor_created(const char *service_id, - const char *desc_id_base32, +void control_event_hs_descriptor_requested(const char *onion_address, + rend_auth_type_t auth_type, + const char *id_digest, + const char *desc_id, + const char *hsdir_index); +void control_event_hs_descriptor_created(const char *onion_address, + const char *desc_id, int replica); -void control_event_hs_descriptor_upload(const char *service_id, - const char *desc_id_base32, - const char *hs_dir); -void control_event_hs_descriptor_receive_end(const char *action, - const char *onion_address, - const rend_data_t *rend_data, - const char *id_digest, - const char *reason); +void control_event_hs_descriptor_upload(const char *onion_address, + const char *desc_id, + const char *hs_dir, + const char *hsdir_index); void control_event_hs_descriptor_upload_end(const char *action, const char *onion_address, const char *hs_dir, const char *reason); -void control_event_hs_descriptor_received(const char *onion_address, - const rend_data_t *rend_data, - const char *id_digest); void control_event_hs_descriptor_uploaded(const char *hs_dir, const char *onion_address); -void control_event_hs_descriptor_failed(const rend_data_t *rend_data, - const char *id_digest, - const char *reason); +/* Hidden service v2 HS_DESC specific. */ +void control_event_hsv2_descriptor_failed(const rend_data_t *rend_data, + const char *id_digest, + const char *reason); +void control_event_hsv2_descriptor_received(const char *onion_address, + const rend_data_t *rend_data, + const char *id_digest); +/* Hidden service v3 HS_DESC specific. */ +void control_event_hsv3_descriptor_failed(const char *onion_address, + const char *desc_id, + const char *hsdir_id_digest, + const char *reason); +void control_event_hsv3_descriptor_received(const char *onion_address, + const char *desc_id, + const char *hsdir_id_digest); void control_event_hs_descriptor_upload_failed(const char *hs_dir, const char *onion_address, const char *reason); @@ -252,10 +263,22 @@ void format_cell_stats(char **event_string, circuit_t *circ, cell_stats_t *cell_stats); STATIC char *get_bw_samples(void); -STATIC crypto_pk_t *add_onion_helper_keyarg(const char *arg, int discard_pk, - const char **key_new_alg_out, - char **key_new_blob_out, - char **err_msg_out); +/* ADD_ONION secret key to create an ephemeral service. The command supports + * multiple versions so this union stores the key and passes it to the HS + * subsystem depending on the requested version. */ +typedef union add_onion_secret_key_t { + /* Hidden service v2 secret key. */ + crypto_pk_t *v2; + /* Hidden service v3 secret key. */ + ed25519_secret_key_t *v3; +} add_onion_secret_key_t; + +STATIC int add_onion_helper_keyarg(const char *arg, int discard_pk, + const char **key_new_alg_out, + char **key_new_blob_out, + add_onion_secret_key_t *decoded_key, + int *hs_version, char **err_msg_out); + STATIC rend_authorized_client_t * add_onion_helper_clientauth(const char *arg, int *created, char **err_msg_out); diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c index d9371b3446..9bec04d790 100644 --- a/src/or/cpuworker.c +++ b/src/or/cpuworker.c @@ -48,14 +48,25 @@ worker_state_new(void *arg) ws->onion_keys = server_onion_keys_new(); return ws; } + +#define worker_state_free(ws) \ + FREE_AND_NULL(worker_state_t, worker_state_free_, (ws)) + static void -worker_state_free(void *arg) +worker_state_free_(worker_state_t *ws) { - worker_state_t *ws = arg; + if (!ws) + return; server_onion_keys_free(ws->onion_keys); tor_free(ws); } +static void +worker_state_free_void(void *arg) +{ + worker_state_free_(arg); +} + static replyqueue_t *replyqueue = NULL; static threadpool_t *threadpool = NULL; static struct event *reply_event = NULL; @@ -102,7 +113,7 @@ cpu_init(void) threadpool = threadpool_new(n_threads, replyqueue, worker_state_new, - worker_state_free, + worker_state_free_void, NULL); } /* Total voodoo. Can we make this more sensible? */ @@ -198,7 +209,7 @@ cpuworkers_rotate_keyinfo(void) if (threadpool_queue_update(threadpool, worker_state_new, update_state_threadfn, - worker_state_free, + worker_state_free_void, NULL)) { log_warn(LD_OR, "Failed to queue key update for worker threads."); } diff --git a/src/or/dircollate.c b/src/or/dircollate.c index d34ebe8af5..ce4534ff6c 100644 --- a/src/or/dircollate.c +++ b/src/or/dircollate.c @@ -41,9 +41,12 @@ typedef struct ddmap_entry_s { vote_routerstatus_t *vrs_lst[FLEXIBLE_ARRAY_MEMBER]; } ddmap_entry_t; +#define ddmap_entry_free(e) \ + FREE_AND_NULL(ddmap_entry_t, ddmap_entry_free_, (e)) + /** Release all storage held by e. */ static void -ddmap_entry_free(ddmap_entry_t *e) +ddmap_entry_free_(ddmap_entry_t *e) { tor_free(e); } @@ -158,7 +161,7 @@ dircollator_new(int n_votes, int n_authorities) /** Release all storage held by <b>dc</b>. */ void -dircollator_free(dircollator_t *dc) +dircollator_free_(dircollator_t *dc) { if (!dc) return; diff --git a/src/or/dircollate.h b/src/or/dircollate.h index 7932e18998..0584b2fe06 100644 --- a/src/or/dircollate.h +++ b/src/or/dircollate.h @@ -18,7 +18,9 @@ typedef struct dircollator_s dircollator_t; dircollator_t *dircollator_new(int n_votes, int n_authorities); -void dircollator_free(dircollator_t *obj); +void dircollator_free_(dircollator_t *obj); +#define dircollator_free(c) \ + FREE_AND_NULL(dircollator_t, dircollator_free_, (c)) void dircollator_add_vote(dircollator_t *dc, networkstatus_t *v); void dircollator_collate(dircollator_t *dc, int consensus_method); diff --git a/src/or/directory.c b/src/or/directory.c index f14bdde90b..c55f81bc62 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -25,6 +25,7 @@ #include "geoip.h" #include "hs_cache.h" #include "hs_common.h" +#include "hs_control.h" #include "hs_client.h" #include "main.h" #include "microdesc.h" @@ -1101,7 +1102,7 @@ directory_request_new(uint8_t dir_purpose) * Release all resources held by <b>req</b>. */ void -directory_request_free(directory_request_t *req) +directory_request_free_(directory_request_t *req) { if (req == NULL) return; @@ -2196,8 +2197,6 @@ load_downloaded_routers(const char *body, smartlist_t *which, return added; } -static int handle_response_fetch_consensus(dir_connection_t *, - const response_handler_args_t *); static int handle_response_fetch_certificate(dir_connection_t *, const response_handler_args_t *); static int handle_response_fetch_status_vote(dir_connection_t *, @@ -2542,7 +2541,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn) * consensus document by checking the consensus, storing it, and marking * router requests as reachable. **/ -static int +STATIC int handle_response_fetch_consensus(dir_connection_t *conn, const response_handler_args_t *args) { @@ -3092,10 +3091,19 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn, /* We got something: Try storing it in the cache. */ if (hs_cache_store_as_client(body, &conn->hs_ident->identity_pk) < 0) { log_warn(LD_REND, "Failed to store hidden service descriptor"); + /* Fire control port FAILED event. */ + hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, + "BAD_DESC"); + hs_control_desc_event_content(conn->hs_ident, conn->identity_digest, + NULL); } else { log_info(LD_REND, "Stored hidden service descriptor successfully."); TO_CONN(conn)->purpose = DIR_PURPOSE_HAS_FETCHED_HSDESC; hs_client_desc_has_arrived(conn->hs_ident); + /* Fire control port RECEIVED event. */ + hs_control_desc_event_received(conn->hs_ident, conn->identity_digest); + hs_control_desc_event_content(conn->hs_ident, conn->identity_digest, + body); } break; case 404: @@ -3103,13 +3111,22 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn, * tries to clean this conn up. */ log_info(LD_REND, "Fetching hidden service v3 descriptor not found: " "Retrying at another directory."); - /* TODO: Inform the control port */ + /* Fire control port FAILED event. */ + hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, + "NOT_FOUND"); + hs_control_desc_event_content(conn->hs_ident, conn->identity_digest, + NULL); break; case 400: log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: " "http status 400 (%s). Dirserver didn't like our " "query? Retrying at another directory.", escaped(reason)); + /* Fire control port FAILED event. */ + hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, + "QUERY_REJECTED"); + hs_control_desc_event_content(conn->hs_ident, conn->identity_digest, + NULL); break; default: log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: " @@ -3117,6 +3134,11 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn, "'%s:%d'. Retrying at another directory.", status_code, escaped(reason), TO_CONN(conn)->address, TO_CONN(conn)->port); + /* Fire control port FAILED event. */ + hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, + "UNEXPECTED"); + hs_control_desc_event_content(conn->hs_ident, conn->identity_digest, + NULL); break; } @@ -3138,9 +3160,9 @@ handle_response_fetch_renddesc_v2(dir_connection_t *conn, const size_t body_len = args->body_len; #define SEND_HS_DESC_FAILED_EVENT(reason) \ - (control_event_hs_descriptor_failed(conn->rend_data, \ - conn->identity_digest, \ - reason)) + (control_event_hsv2_descriptor_failed(conn->rend_data, \ + conn->identity_digest, \ + reason)) #define SEND_HS_DESC_FAILED_CONTENT() \ (control_event_hs_descriptor_content( \ rend_data_get_address(conn->rend_data), \ @@ -3175,9 +3197,9 @@ handle_response_fetch_renddesc_v2(dir_connection_t *conn, /* success. notify pending connections about this. */ log_info(LD_REND, "Successfully fetched v2 rendezvous " "descriptor."); - control_event_hs_descriptor_received(service_id, - conn->rend_data, - conn->identity_digest); + control_event_hsv2_descriptor_received(service_id, + conn->rend_data, + conn->identity_digest); control_event_hs_descriptor_content(service_id, conn->requested_resource, conn->identity_digest, @@ -3294,7 +3316,7 @@ handle_response_upload_hsdesc(dir_connection_t *conn, case 200: log_info(LD_REND, "Uploading hidden service descriptor: " "finished with status 200 (%s)", escaped(reason)); - /* XXX: Trigger control event. */ + hs_control_desc_event_uploaded(conn->hs_ident, conn->identity_digest); break; case 400: log_fn(LOG_PROTOCOL_WARN, LD_REND, @@ -3302,7 +3324,8 @@ handle_response_upload_hsdesc(dir_connection_t *conn, "status 400 (%s) response from dirserver " "'%s:%d'. Malformed hidden service descriptor?", escaped(reason), conn->base_.address, conn->base_.port); - /* XXX: Trigger control event. */ + hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, + "UPLOAD_REJECTED"); break; default: log_warn(LD_REND, "Uploading hidden service descriptor: http " @@ -3310,7 +3333,8 @@ handle_response_upload_hsdesc(dir_connection_t *conn, "'%s:%d').", status_code, escaped(reason), conn->base_.address, conn->base_.port); - /* XXX: Trigger control event. */ + hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, + "UNEXPECTED"); break; } @@ -3476,63 +3500,47 @@ write_http_response_header_impl(dir_connection_t *conn, ssize_t length, long cache_lifetime) { char date[RFC1123_TIME_LEN+1]; - char tmp[1024]; - char *cp; time_t now = time(NULL); + buf_t *buf = buf_new_with_capacity(1024); tor_assert(conn); format_rfc1123_time(date, now); - cp = tmp; - tor_snprintf(cp, sizeof(tmp), - "HTTP/1.0 200 OK\r\nDate: %s\r\n", - date); - cp += strlen(tmp); + + buf_add_printf(buf, "HTTP/1.0 200 OK\r\nDate: %s\r\n", date); if (type) { - tor_snprintf(cp, sizeof(tmp)-(cp-tmp), "Content-Type: %s\r\n", type); - cp += strlen(cp); + buf_add_printf(buf, "Content-Type: %s\r\n", type); } if (!is_local_addr(&conn->base_.addr)) { /* Don't report the source address for a nearby/private connection. * Otherwise we tend to mis-report in cases where incoming ports are * being forwarded to a Tor server running behind the firewall. */ - tor_snprintf(cp, sizeof(tmp)-(cp-tmp), - X_ADDRESS_HEADER "%s\r\n", conn->base_.address); - cp += strlen(cp); + buf_add_printf(buf, X_ADDRESS_HEADER "%s\r\n", conn->base_.address); } if (encoding) { - tor_snprintf(cp, sizeof(tmp)-(cp-tmp), - "Content-Encoding: %s\r\n", encoding); - cp += strlen(cp); + buf_add_printf(buf, "Content-Encoding: %s\r\n", encoding); } if (length >= 0) { - tor_snprintf(cp, sizeof(tmp)-(cp-tmp), - "Content-Length: %ld\r\n", (long)length); - cp += strlen(cp); + buf_add_printf(buf, "Content-Length: %ld\r\n", (long)length); } if (cache_lifetime > 0) { char expbuf[RFC1123_TIME_LEN+1]; format_rfc1123_time(expbuf, (time_t)(now + cache_lifetime)); /* We could say 'Cache-control: max-age=%d' here if we start doing * http/1.1 */ - tor_snprintf(cp, sizeof(tmp)-(cp-tmp), - "Expires: %s\r\n", expbuf); - cp += strlen(cp); + buf_add_printf(buf, "Expires: %s\r\n", expbuf); } else if (cache_lifetime == 0) { /* We could say 'Cache-control: no-cache' here if we start doing * http/1.1 */ - strlcpy(cp, "Pragma: no-cache\r\n", sizeof(tmp)-(cp-tmp)); - cp += strlen(cp); + buf_add_string(buf, "Pragma: no-cache\r\n"); } if (extra_headers) { - strlcpy(cp, extra_headers, sizeof(tmp)-(cp-tmp)); - cp += strlen(cp); + buf_add_string(buf, extra_headers); } - if (sizeof(tmp)-(cp-tmp) > 3) - memcpy(cp, "\r\n", 3); - else - tor_assert(0); - connection_buf_add(tmp, strlen(tmp), TO_CONN(conn)); + buf_add_string(buf, "\r\n"); + + connection_buf_add_buf(TO_CONN(conn), buf); + buf_free(buf); } /** As write_http_response_header_impl, but sets encoding and content-typed diff --git a/src/or/directory.h b/src/or/directory.h index 5e6a91d3e7..edf75ffe13 100644 --- a/src/or/directory.h +++ b/src/or/directory.h @@ -51,7 +51,9 @@ int directory_must_use_begindir(const or_options_t *options); */ typedef struct directory_request_t directory_request_t; directory_request_t *directory_request_new(uint8_t dir_purpose); -void directory_request_free(directory_request_t *req); +void directory_request_free_(directory_request_t *req); +#define directory_request_free(req) \ + FREE_AND_NULL(directory_request_t, directory_request_free_, (req)) void directory_request_set_or_addr_port(directory_request_t *req, const tor_addr_port_t *p); void directory_request_set_dir_addr_port(directory_request_t *req, @@ -238,6 +240,9 @@ STATIC int handle_response_fetch_hsdesc_v3(dir_connection_t *conn, STATIC int handle_response_fetch_microdesc(dir_connection_t *conn, const response_handler_args_t *args); +STATIC int handle_response_fetch_consensus(dir_connection_t *conn, + const response_handler_args_t *args); + #endif /* defined(DIRECTORY_PRIVATE) */ #ifdef TOR_UNIT_TESTS diff --git a/src/or/dirserv.c b/src/or/dirserv.c index ddee92da55..d3bae241f9 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -1519,15 +1519,21 @@ dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil) node->ri && node->ri->purpose != ROUTER_PURPOSE_BRIDGE) continue; + + routerinfo_t *ri = node->ri; + if (ri) { + node->is_exit = (!router_exit_policy_rejects_all(ri) && + exit_policy_is_general_exit(ri->exit_policy)); + } + if (router_counts_toward_thresholds(node, now, omit_as_sybil, require_mbw)) { - routerinfo_t *ri = node->ri; const char *id = node->identity; uint32_t bw_kb; + /* resolve spurious clang shallow analysis null pointer errors */ tor_assert(ri); - node->is_exit = (!router_exit_policy_rejects_all(ri) && - exit_policy_is_general_exit(ri->exit_policy)); + uptimes[n_active] = (uint32_t)real_uptime(ri, now); mtbfs[n_active] = rep_hist_get_stability(id, now); tks [n_active] = rep_hist_get_weighted_time_known(id, now); @@ -1897,21 +1903,28 @@ version_from_platform(const char *platform) /** Helper: write the router-status information in <b>rs</b> into a newly * allocated character buffer. Use the same format as in network-status * documents. If <b>version</b> is non-NULL, add a "v" line for the platform. + * + * consensus_method is the current consensus method when format is + * NS_V3_CONSENSUS or NS_V3_CONSENSUS_MICRODESC. It is ignored for other + * formats: pass ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD. + * * Return 0 on success, -1 on failure. * * The format argument has one of the following values: * NS_V2 - Output an entry suitable for a V2 NS opinion document * NS_V3_CONSENSUS - Output the first portion of a V3 NS consensus entry + * for consensus_method. * NS_V3_CONSENSUS_MICRODESC - Output the first portion of a V3 microdesc - * consensus entry. + * consensus entry for consensus_method. * NS_V3_VOTE - Output a complete V3 NS vote. If <b>vrs</b> is present, * it contains additional information for the vote. - * NS_CONTROL_PORT - Output a NS document for the control port + * NS_CONTROL_PORT - Output a NS document for the control port. */ char * routerstatus_format_entry(const routerstatus_t *rs, const char *version, const char *protocols, routerstatus_format_type_t format, + int consensus_method, const vote_routerstatus_t *vrs) { char *summary; @@ -1942,8 +1955,10 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, * networkstatus_type_t values, with an additional control port value * added -MP */ - /* V3 microdesc consensuses don't have "a" lines. */ - if (format == NS_V3_CONSENSUS_MICRODESC) + /* V3 microdesc consensuses only have "a" lines in later consensus methods + */ + if (format == NS_V3_CONSENSUS_MICRODESC && + consensus_method < MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS) goto done; /* Possible "a" line. At most one for now. */ @@ -1952,7 +1967,7 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, fmt_addrport(&rs->ipv6_addr, rs->ipv6_orport)); } - if (format == NS_V3_CONSENSUS) + if (format == NS_V3_CONSENSUS || format == NS_V3_CONSENSUS_MICRODESC) goto done; smartlist_add_asprintf(chunks, @@ -2213,7 +2228,8 @@ routers_make_ed_keys_unique(smartlist_t *routers) } /** Extract status information from <b>ri</b> and from other authority - * functions and store it in <b>rs</b>>. + * functions and store it in <b>rs</b>. <b>rs</b> is zeroed out before it is + * set. * * We assume that ri-\>is_running has already been set, e.g. by * dirserv_set_router_is_running(ri, now); @@ -2279,6 +2295,9 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs, OR port and it's reachable so copy it to the routerstatus. */ tor_addr_copy(&rs->ipv6_addr, &ri->ipv6_addr); rs->ipv6_orport = ri->ipv6_orport; + } else { + tor_addr_make_null(&rs->ipv6_addr, AF_INET6); + rs->ipv6_orport = 0; } if (options->TestingTorNetwork) { @@ -3284,7 +3303,7 @@ dirserv_orconn_tls_done(const tor_addr_t *addr, ri = node->ri; if (get_options()->AuthDirTestEd25519LinkKeys && - node_supports_ed25519_link_authentication(node) && + node_supports_ed25519_link_authentication(node, 1) && ri->cache_info.signing_key_cert) { /* We allow the node to have an ed25519 key if we haven't been told one in * the routerinfo, but if we *HAVE* been told one in the routerinfo, it @@ -3367,7 +3386,7 @@ dirserv_single_reachability_test(time_t now, routerinfo_t *router) tor_assert(node); if (options->AuthDirTestEd25519LinkKeys && - node_supports_ed25519_link_authentication(node)) { + node_supports_ed25519_link_authentication(node, 1)) { ed_id_key = &router->cache_info.signing_key_cert->signing_key; } else { ed_id_key = NULL; @@ -3496,7 +3515,7 @@ spooled_resource_new_from_cache_entry(consensus_cache_entry_t *entry) /** Release all storage held by <b>spooled</b>. */ void -spooled_resource_free(spooled_resource_t *spooled) +spooled_resource_free_(spooled_resource_t *spooled) { if (spooled == NULL) return; diff --git a/src/or/dirserv.h b/src/or/dirserv.h index 46967a6cb2..0fd7b79150 100644 --- a/src/or/dirserv.h +++ b/src/or/dirserv.h @@ -150,6 +150,7 @@ char *routerstatus_format_entry( const char *version, const char *protocols, routerstatus_format_type_t format, + int consensus_method, const vote_routerstatus_t *vrs); void dirserv_free_all(void); void cached_dir_decref(cached_dir_t *d); @@ -195,7 +196,9 @@ spooled_resource_t *spooled_resource_new(dir_spool_source_t source, size_t digestlen); spooled_resource_t *spooled_resource_new_from_cache_entry( struct consensus_cache_entry_t *entry); -void spooled_resource_free(spooled_resource_t *spooled); +void spooled_resource_free_(spooled_resource_t *spooled); +#define spooled_resource_free(sp) \ + FREE_AND_NULL(spooled_resource_t, spooled_resource_free_, (sp)) void dirserv_spool_remove_missing_and_guess_size(dir_connection_t *conn, time_t cutoff, int compression, diff --git a/src/or/dirvote.c b/src/or/dirvote.c index ce82a5ef4a..a75d9e55a5 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -278,7 +278,9 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, vote_microdesc_hash_t *h; rsf = routerstatus_format_entry(&vrs->status, vrs->version, vrs->protocols, - NS_V3_VOTE, vrs); + NS_V3_VOTE, + ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD, + vrs); if (rsf) smartlist_add(chunks, rsf); @@ -519,7 +521,7 @@ compute_routerstatus_consensus(smartlist_t *votes, int consensus_method, /* compare_vote_rs_() sorts the items by identity digest (all the same), * then by SD digest. That way, if we have a tie that the published_on - * date cannot tie, we use the descriptor with the smaller digest. + * date cannot break, we use the descriptor with the smaller digest. */ smartlist_sort(votes, compare_vote_rs_); SMARTLIST_FOREACH_BEGIN(votes, vote_routerstatus_t *, rs) { @@ -795,6 +797,9 @@ dirvote_compute_params(smartlist_t *votes, int method, int total_authorities) output = smartlist_new(); SMARTLIST_FOREACH_BEGIN(param_list, const char *, param) { + /* resolve spurious clang shallow analysis null pointer errors */ + tor_assert(param); + const char *next_param; int ok=0; eq = strchr(param, '='); @@ -807,8 +812,7 @@ dirvote_compute_params(smartlist_t *votes, int method, int total_authorities) next_param = NULL; else next_param = smartlist_get(param_list, param_sl_idx+1); - /* resolve spurious clang shallow analysis null pointer errors */ - tor_assert(param); + if (!next_param || strncmp(next_param, param, cur_param_len)) { /* We've reached the end of a series. */ /* Make sure enough authorities voted on this param, unless the @@ -1315,8 +1319,9 @@ compute_nth_protocol_set(int n, int n_voters, const smartlist_t *votes) /** Given a list of vote networkstatus_t in <b>votes</b>, our public * authority <b>identity_key</b>, our private authority <b>signing_key</b>, * and the number of <b>total_authorities</b> that we believe exist in our - * voting quorum, generate the text of a new v3 consensus vote, and return the - * value in a newly allocated string. + * voting quorum, generate the text of a new v3 consensus or microdescriptor + * consensus (depending on <b>flavor</b>), and return the value in a newly + * allocated string. * * Note: this function DOES NOT check whether the votes are from * recognized authorities. (dirvote_add_vote does that.) @@ -2099,7 +2104,8 @@ networkstatus_compute_consensus(smartlist_t *votes, char *buf; /* Okay!! Now we can write the descriptor... */ /* First line goes into "buf". */ - buf = routerstatus_format_entry(&rs_out, NULL, NULL, rs_format, NULL); + buf = routerstatus_format_entry(&rs_out, NULL, NULL, + rs_format, consensus_method, NULL); if (buf) smartlist_add(chunks, buf); } @@ -2685,7 +2691,7 @@ get_detached_signatures_from_pending_consensuses(pending_consensus_t *pending, /** Release all storage held in <b>s</b>. */ void -ns_detached_signatures_free(ns_detached_signatures_t *s) +ns_detached_signatures_free_(ns_detached_signatures_t *s) { if (!s) return; @@ -2843,10 +2849,13 @@ get_voting_schedule(const or_options_t *options, time_t now, int severity) return new_voting_schedule; } +#define voting_schedule_free(s) \ + FREE_AND_NULL(voting_schedule_t, voting_schedule_free_, (s)) + /** Frees a voting_schedule_t. This should be used instead of the generic * tor_free. */ static void -voting_schedule_free(voting_schedule_t *voting_schedule_to_free) +voting_schedule_free_(voting_schedule_t *voting_schedule_to_free) { if (!voting_schedule_to_free) return; @@ -3829,7 +3838,10 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method) smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf); } + /* We originally put a lines in the micrdescriptors, but then we worked out + * that we needed them in the microdesc consensus. See #20916. */ if (consensus_method >= MIN_METHOD_FOR_A_LINES && + consensus_method < MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC && !tor_addr_is_null(&ri->ipv6_addr) && ri->ipv6_orport) smartlist_add_asprintf(chunks, "a %s\n", fmt_addrport(&ri->ipv6_addr, ri->ipv6_orport)); @@ -3938,7 +3950,9 @@ static const struct consensus_method_range_t { {MIN_METHOD_FOR_P6_LINES, MIN_METHOD_FOR_NTOR_KEY - 1}, {MIN_METHOD_FOR_NTOR_KEY, MIN_METHOD_FOR_ID_HASH_IN_MD - 1}, {MIN_METHOD_FOR_ID_HASH_IN_MD, MIN_METHOD_FOR_ED25519_ID_IN_MD - 1}, - {MIN_METHOD_FOR_ED25519_ID_IN_MD, MAX_SUPPORTED_CONSENSUS_METHOD}, + {MIN_METHOD_FOR_ED25519_ID_IN_MD, + MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC - 1}, + {MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC, MAX_SUPPORTED_CONSENSUS_METHOD}, {-1, -1} }; diff --git a/src/or/dirvote.h b/src/or/dirvote.h index 72a35fea6d..deeb27bfe1 100644 --- a/src/or/dirvote.h +++ b/src/or/dirvote.h @@ -51,11 +51,15 @@ #define MIN_VOTE_INTERVAL_TESTING_INITIAL \ ((MIN_VOTE_SECONDS_TESTING)+(MIN_DIST_SECONDS_TESTING)+1) +/* A placeholder for routerstatus_format_entry() when the consensus method + * argument is not applicable. */ +#define ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD 0 + /** The lowest consensus method that we currently support. */ #define MIN_SUPPORTED_CONSENSUS_METHOD 13 /** The highest consensus method that we currently support. */ -#define MAX_SUPPORTED_CONSENSUS_METHOD 26 +#define MAX_SUPPORTED_CONSENSUS_METHOD 28 /** Lowest consensus method where microdesc consensuses omit any entry * with no microdesc. */ @@ -115,6 +119,14 @@ * instead of 0. See #14881 */ #define MIN_METHOD_FOR_INIT_BW_WEIGHTS_ONE 26 +/** Lowest consensus method where the microdesc consensus contains relay IPv6 + * addresses. See #23826 and #20916. */ +#define MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS 27 + +/** Lowest consensus method where microdescriptors do not contain relay IPv6 + * addresses. See #23828 and #20916. */ +#define MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC 28 + /** Default bandwidth to clip unmeasured bandwidths to using method >= * MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not * get confused with the above macros.) */ @@ -136,7 +148,9 @@ int networkstatus_add_detached_signatures(networkstatus_t *target, int severity, const char **msg_out); char *networkstatus_get_detached_signatures(smartlist_t *consensuses); -void ns_detached_signatures_free(ns_detached_signatures_t *s); +void ns_detached_signatures_free_(ns_detached_signatures_t *s); +#define ns_detached_signatures_free(s) \ + FREE_AND_NULL(ns_detached_signatures_t, ns_detached_signatures_free_, (s)) /* cert manipulation */ authority_cert_t *authority_cert_dup(authority_cert_t *cert); diff --git a/src/or/dns.c b/src/or/dns.c index 7dc3575f53..1f00fe5b4d 100644 --- a/src/or/dns.c +++ b/src/or/dns.c @@ -457,7 +457,7 @@ purge_expired_resolves(time_t now) if (!pendconn->base_.marked_for_close) { connection_edge_end(pendconn, END_STREAM_REASON_TIMEOUT); circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn); - connection_free(TO_CONN(pendconn)); + connection_free_(TO_CONN(pendconn)); } tor_free(pend); } @@ -670,7 +670,7 @@ dns_resolve(edge_connection_t *exitconn) /* If we made the connection pending, then we freed it already in * dns_cancel_pending_resolve(). If we marked it for close, it'll * get freed from the main loop. Otherwise, can free it now. */ - connection_free(TO_CONN(exitconn)); + connection_free_(TO_CONN(exitconn)); } break; default: @@ -1101,7 +1101,7 @@ dns_cancel_pending_resolve,(const char *address)) if (circ) circuit_detach_stream(circ, pendconn); if (!pendconn->base_.marked_for_close) - connection_free(TO_CONN(pendconn)); + connection_free_(TO_CONN(pendconn)); resolve->pending_connections = pend->next; tor_free(pend); } @@ -1230,7 +1230,7 @@ inform_pending_connections(cached_resolve_t *resolve) /* This detach must happen after we send the resolved cell. */ circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn); } - connection_free(TO_CONN(pendconn)); + connection_free_(TO_CONN(pendconn)); } else { circuit_t *circ; if (pendconn->base_.purpose == EXIT_PURPOSE_CONNECT) { @@ -1259,7 +1259,7 @@ inform_pending_connections(cached_resolve_t *resolve) circ = circuit_get_by_edge_conn(pendconn); tor_assert(circ); circuit_detach_stream(circ, pendconn); - connection_free(TO_CONN(pendconn)); + connection_free_(TO_CONN(pendconn)); } } resolve->pending_connections = pend->next; @@ -1666,7 +1666,7 @@ launch_resolve,(cached_resolve_t *resolve)) tor_addr_t a; int r; - if (get_options()->DisableNetwork) + if (net_is_disabled()) return -1; /* What? Nameservers not configured? Sounds like a bug. */ @@ -1901,7 +1901,7 @@ launch_test_addresses(evutil_socket_t fd, short event, void *args) (void)event; (void)args; - if (options->DisableNetwork) + if (net_is_disabled()) return; log_info(LD_EXIT, "Launching checks to see whether our nameservers like to " diff --git a/src/or/dnsserv.c b/src/or/dnsserv.c index d254717a54..9385561d99 100644 --- a/src/or/dnsserv.c +++ b/src/or/dnsserv.c @@ -172,7 +172,7 @@ evdns_server_callback(struct evdns_server_request *req, void *data_) if (connection_add(ENTRY_TO_CONN(entry_conn)) < 0) { log_warn(LD_APP, "Couldn't register dummy connection for DNS request"); evdns_server_request_respond(req, DNS_ERR_SERVERFAILED); - connection_free(ENTRY_TO_CONN(entry_conn)); + connection_free_(ENTRY_TO_CONN(entry_conn)); return; } @@ -249,7 +249,7 @@ dnsserv_launch_request(const char *name, int reverse, if (connection_add(TO_CONN(conn))<0) { log_warn(LD_APP, "Couldn't register dummy connection for RESOLVE request"); - connection_free(TO_CONN(conn)); + connection_free_(TO_CONN(conn)); return -1; } diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c index 67b0259243..292a393e51 100644 --- a/src/or/entrynodes.c +++ b/src/or/entrynodes.c @@ -1094,7 +1094,11 @@ select_and_add_guard_item_for_sample(guard_selection_t *gs, return added_guard; } -/** Return true iff we need a consensus to maintain our */ +/** + * Return true iff we need a consensus to update our guards, but we don't + * have one. (We can return 0 here either if the consensus is _not_ missing, + * or if we don't need a consensus because we're using bridges.) + */ static int live_consensus_is_missing(const guard_selection_t *gs) { @@ -2196,7 +2200,7 @@ entry_guard_has_higher_priority(entry_guard_t *a, entry_guard_t *b) /** Release all storage held in <b>restriction</b> */ STATIC void -entry_guard_restriction_free(entry_guard_restriction_t *rst) +entry_guard_restriction_free_(entry_guard_restriction_t *rst) { tor_free(rst); } @@ -2205,7 +2209,7 @@ entry_guard_restriction_free(entry_guard_restriction_t *rst) * Release all storage held in <b>state</b>. */ void -circuit_guard_state_free(circuit_guard_state_t *state) +circuit_guard_state_free_(circuit_guard_state_t *state) { if (!state) return; @@ -2216,9 +2220,9 @@ circuit_guard_state_free(circuit_guard_state_t *state) /** Allocate and return a new circuit_guard_state_t to track the result * of using <b>guard</b> for a given operation. */ -static circuit_guard_state_t * -circuit_guard_state_new(entry_guard_t *guard, unsigned state, - entry_guard_restriction_t *rst) +MOCK_IMPL(STATIC circuit_guard_state_t *, +circuit_guard_state_new,(entry_guard_t *guard, unsigned state, + entry_guard_restriction_t *rst)) { circuit_guard_state_t *result; @@ -3108,7 +3112,7 @@ get_guard_state_for_bridge_desc_fetch(const char *digest) /** Release all storage held by <b>e</b>. */ STATIC void -entry_guard_free(entry_guard_t *e) +entry_guard_free_(entry_guard_t *e) { if (!e) return; @@ -3615,7 +3619,7 @@ entry_guards_get_err_str_if_dir_info_missing(int using_mds, /** Free one guard selection context */ STATIC void -guard_selection_free(guard_selection_t *gs) +guard_selection_free_(guard_selection_t *gs) { if (!gs) return; diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h index d909115b1f..aa9c8fe193 100644 --- a/src/or/entrynodes.h +++ b/src/or/entrynodes.h @@ -356,7 +356,10 @@ typedef enum { GUARD_USAGE_DIRGUARD = 1 } guard_usage_t; -void circuit_guard_state_free(circuit_guard_state_t *state); +#define circuit_guard_state_free(val) \ + FREE_AND_NULL(circuit_guard_state_t, circuit_guard_state_free_, (val)) + +void circuit_guard_state_free_(circuit_guard_state_t *state); int entry_guard_pick_for_circuit(guard_selection_t *gs, guard_usage_t usage, entry_guard_restriction_t *rst, @@ -475,6 +478,9 @@ STATIC double get_meaningful_restriction_threshold(void); STATIC double get_extreme_restriction_threshold(void); HANDLE_DECL(entry_guard, entry_guard_t, STATIC) +#define entry_guard_handle_free(h) \ + FREE_AND_NULL(entry_guard_handle_t, entry_guard_handle_free_, (h)) + STATIC guard_selection_type_t guard_selection_infer_type( guard_selection_type_t type_in, const char *name); @@ -482,7 +488,9 @@ STATIC guard_selection_t *guard_selection_new(const char *name, guard_selection_type_t type); STATIC guard_selection_t *get_guard_selection_by_name( const char *name, guard_selection_type_t type, int create_if_absent); -STATIC void guard_selection_free(guard_selection_t *gs); +STATIC void guard_selection_free_(guard_selection_t *gs); +#define guard_selection_free(gs) \ + FREE_AND_NULL(guard_selection_t, guard_selection_free_, (gs)) MOCK_DECL(STATIC int, entry_guard_is_listed, (guard_selection_t *gs, const entry_guard_t *guard)); STATIC const char *choose_guard_selection(const or_options_t *options, @@ -494,12 +502,18 @@ STATIC entry_guard_t *get_sampled_guard_with_id(guard_selection_t *gs, MOCK_DECL(STATIC time_t, randomize_time, (time_t now, time_t max_backdate)); +MOCK_DECL(STATIC circuit_guard_state_t *, + circuit_guard_state_new,(entry_guard_t *guard, unsigned state, + entry_guard_restriction_t *rst)); + STATIC entry_guard_t *entry_guard_add_to_sample(guard_selection_t *gs, const node_t *node); STATIC entry_guard_t *entry_guards_expand_sample(guard_selection_t *gs); STATIC char *entry_guard_encode_for_state(entry_guard_t *guard); STATIC entry_guard_t *entry_guard_parse_from_state(const char *s); -STATIC void entry_guard_free(entry_guard_t *e); +#define entry_guard_free(e) \ + FREE_AND_NULL(entry_guard_t, entry_guard_free_, (e)) +STATIC void entry_guard_free_(entry_guard_t *e); STATIC void entry_guards_update_filtered_sets(guard_selection_t *gs); STATIC int entry_guards_all_primary_guards_are_down(guard_selection_t *gs); /** @@ -562,7 +576,10 @@ STATIC entry_guard_restriction_t *guard_create_exit_restriction( STATIC entry_guard_restriction_t *guard_create_dirserver_md_restriction(void); -STATIC void entry_guard_restriction_free(entry_guard_restriction_t *rst); +STATIC void entry_guard_restriction_free_(entry_guard_restriction_t *rst); +#define entry_guard_restriction_free(rst) \ + FREE_AND_NULL(entry_guard_restriction_t, \ + entry_guard_restriction_free_, (rst)) #endif /* defined(ENTRYNODES_PRIVATE) */ diff --git a/src/or/ext_orport.c b/src/or/ext_orport.c index 28377a3f36..16a250fa58 100644 --- a/src/or/ext_orport.c +++ b/src/or/ext_orport.c @@ -40,7 +40,7 @@ ext_or_cmd_new(uint16_t len) /** Deallocate the Extended ORPort message in <b>cmd</b>. */ void -ext_or_cmd_free(ext_or_cmd_t *cmd) +ext_or_cmd_free_(ext_or_cmd_t *cmd) { tor_free(cmd); } diff --git a/src/or/ext_orport.h b/src/or/ext_orport.h index af2b97e77c..09acbc407e 100644 --- a/src/or/ext_orport.h +++ b/src/or/ext_orport.h @@ -10,7 +10,11 @@ int connection_ext_or_start_auth(or_connection_t *or_conn); ext_or_cmd_t *ext_or_cmd_new(uint16_t len); -void ext_or_cmd_free(ext_or_cmd_t *cmd); + +#define ext_or_cmd_free(cmd) \ + FREE_AND_NULL(ext_or_cmd_t, ext_or_cmd_free_, (cmd)) + +void ext_or_cmd_free_(ext_or_cmd_t *cmd); void connection_or_set_ext_or_identifier(or_connection_t *conn); void connection_or_remove_from_ext_or_id_map(or_connection_t *conn); void connection_or_clear_ext_or_id_map(void); diff --git a/src/or/fp_pair.c b/src/or/fp_pair.c index f730106d06..c938e76678 100644 --- a/src/or/fp_pair.c +++ b/src/or/fp_pair.c @@ -196,7 +196,7 @@ fp_pair_map_remove(fp_pair_map_t *map, const fp_pair_t *key) */ void -fp_pair_map_free(fp_pair_map_t *map, void (*free_val)(void*)) +fp_pair_map_free_(fp_pair_map_t *map, void (*free_val)(void*)) { fp_pair_map_entry_t **ent, **next, *this; diff --git a/src/or/fp_pair.h b/src/or/fp_pair.h index f7c060b459..4498a16101 100644 --- a/src/or/fp_pair.h +++ b/src/or/fp_pair.h @@ -26,7 +26,12 @@ void * fp_pair_map_get(const fp_pair_map_t *map, const fp_pair_t *key); void * fp_pair_map_get_by_digests(const fp_pair_map_t *map, const char *first, const char *second); void * fp_pair_map_remove(fp_pair_map_t *map, const fp_pair_t *key); -void fp_pair_map_free(fp_pair_map_t *map, void (*free_val)(void*)); +void fp_pair_map_free_(fp_pair_map_t *map, void (*free_val)(void*)); +#define fp_pair_map_free(map, free_val) do { \ + fp_pair_map_free_((map), (free_val)); \ + (map) = NULL; \ + } while (0) + int fp_pair_map_isempty(const fp_pair_map_t *map); int fp_pair_map_size(const fp_pair_map_t *map); fp_pair_map_iter_t * fp_pair_map_iter_init(fp_pair_map_t *map); diff --git a/src/or/geoip.c b/src/or/geoip.c index 3944b2cf69..d7411e6aaa 100644 --- a/src/or/geoip.c +++ b/src/or/geoip.c @@ -30,6 +30,7 @@ #define GEOIP_PRIVATE #include "or.h" #include "ht.h" +#include "buffers.h" #include "config.h" #include "control.h" #include "dnsserv.h" @@ -526,9 +527,12 @@ HT_PROTOTYPE(clientmap, clientmap_entry_t, node, clientmap_entry_hash, HT_GENERATE2(clientmap, clientmap_entry_t, node, clientmap_entry_hash, clientmap_entries_eq, 0.6, tor_reallocarray_, tor_free_) +#define clientmap_entry_free(ent) \ + FREE_AND_NULL(clientmap_entry_t, clientmap_entry_free_, ent) + /** Free all storage held by <b>ent</b>. */ static void -clientmap_entry_free(clientmap_entry_t *ent) +clientmap_entry_free_(clientmap_entry_t *ent) { if (!ent) return; @@ -930,9 +934,9 @@ static char * geoip_get_dirreq_history(dirreq_type_t type) { char *result = NULL; + buf_t *buf = NULL; smartlist_t *dirreq_completed = NULL; uint32_t complete = 0, timeouts = 0, running = 0; - int bufsize = 1024, written; dirreq_map_entry_t **ptr, **next; struct timeval now; @@ -965,13 +969,9 @@ geoip_get_dirreq_history(dirreq_type_t type) DIR_REQ_GRANULARITY); running = round_uint32_to_next_multiple_of(running, DIR_REQ_GRANULARITY); - result = tor_malloc_zero(bufsize); - written = tor_snprintf(result, bufsize, "complete=%u,timeout=%u," - "running=%u", complete, timeouts, running); - if (written < 0) { - tor_free(result); - goto done; - } + buf = buf_new_with_capacity(1024); + buf_add_printf(buf, "complete=%u,timeout=%u," + "running=%u", complete, timeouts, running); #define MIN_DIR_REQ_RESPONSES 16 if (complete >= MIN_DIR_REQ_RESPONSES) { @@ -992,7 +992,7 @@ geoip_get_dirreq_history(dirreq_type_t type) dltimes[ent_sl_idx] = bytes_per_second; } SMARTLIST_FOREACH_END(ent); median_uint32(dltimes, complete); /* sorts as a side effect. */ - written = tor_snprintf(result + written, bufsize - written, + buf_add_printf(buf, ",min=%u,d1=%u,d2=%u,q1=%u,d3=%u,d4=%u,md=%u," "d6=%u,d7=%u,q3=%u,d8=%u,d9=%u,max=%u", dltimes[0], @@ -1008,14 +1008,15 @@ geoip_get_dirreq_history(dirreq_type_t type) dltimes[8*complete/10-1], dltimes[9*complete/10-1], dltimes[complete-1]); - if (written<0) - tor_free(result); tor_free(dltimes); } - done: + + result = buf_extract(buf, NULL); + SMARTLIST_FOREACH(dirreq_completed, dirreq_map_entry_t *, ent, tor_free(ent)); smartlist_free(dirreq_completed); + buf_free(buf); return result; } diff --git a/src/or/git_revision.c b/src/or/git_revision.c new file mode 100644 index 0000000000..8f326b8751 --- /dev/null +++ b/src/or/git_revision.c @@ -0,0 +1,17 @@ +/* Copyright 2001-2004 Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "git_revision.h" + +/** String describing which Tor Git repository version the source was + * built from. This string is generated by a bit of shell kludging in + * src/or/include.am, and is usually right. + */ +const char tor_git_revision[] = +#ifndef _MSC_VER +#include "micro-revision.i" +#endif + ""; + diff --git a/src/or/git_revision.h b/src/or/git_revision.h new file mode 100644 index 0000000000..5613cb4335 --- /dev/null +++ b/src/or/git_revision.h @@ -0,0 +1,12 @@ +/* Copyright 2001-2004 Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_GIT_REVISION_H +#define TOR_GIT_REVISION_H + +extern const char tor_git_revision[]; + +#endif /* !defined(TOR_GIT_REVISION_H) */ + diff --git a/src/or/hibernate.c b/src/or/hibernate.c index 74ab766468..4dc35f68d0 100644 --- a/src/or/hibernate.c +++ b/src/or/hibernate.c @@ -34,6 +34,7 @@ hibernating, phase 2: #include "config.h" #include "connection.h" #include "connection_edge.h" +#include "connection_or.h" #include "control.h" #include "hibernate.h" #include "main.h" @@ -818,8 +819,8 @@ hibernate_begin(hibernate_state_t new_state, time_t now) log_notice(LD_GENERAL,"SIGINT received %s; exiting now.", hibernate_state == HIBERNATE_STATE_EXITING ? "a second time" : "while hibernating"); - tor_cleanup(); - exit(0); + tor_shutdown_event_loop_and_exit(0); + return; } if (new_state == HIBERNATE_STATE_LOWBANDWIDTH && @@ -906,20 +907,23 @@ hibernate_go_dormant(time_t now) while ((conn = connection_get_by_type(CONN_TYPE_OR)) || (conn = connection_get_by_type(CONN_TYPE_AP)) || (conn = connection_get_by_type(CONN_TYPE_EXIT))) { - if (CONN_IS_EDGE(conn)) + if (CONN_IS_EDGE(conn)) { connection_edge_end(TO_EDGE_CONN(conn), END_STREAM_REASON_HIBERNATING); + } log_info(LD_NET,"Closing conn type %d", conn->type); - if (conn->type == CONN_TYPE_AP) /* send socks failure if needed */ + if (conn->type == CONN_TYPE_AP) { + /* send socks failure if needed */ connection_mark_unattached_ap(TO_ENTRY_CONN(conn), END_STREAM_REASON_HIBERNATING); - else if (conn->type == CONN_TYPE_OR) { + } else if (conn->type == CONN_TYPE_OR) { if (TO_OR_CONN(conn)->chan) { - channel_mark_for_close(TLS_CHAN_TO_BASE(TO_OR_CONN(conn)->chan)); + connection_or_close_normally(TO_OR_CONN(conn), 0); } else { connection_mark_for_close(conn); } - } else + } else { connection_mark_for_close(conn); + } } if (now < interval_wakeup_time) @@ -980,8 +984,7 @@ consider_hibernation(time_t now) tor_assert(shutdown_time); if (shutdown_time <= now) { log_notice(LD_GENERAL, "Clean shutdown finished. Exiting."); - tor_cleanup(); - exit(0); + tor_shutdown_event_loop_and_exit(0); } return; /* if exiting soon, don't worry about bandwidth limits */ } diff --git a/src/or/hs_cache.c b/src/or/hs_cache.c index 3ebe13fb4d..1a8fdbd03b 100644 --- a/src/or/hs_cache.c +++ b/src/or/hs_cache.c @@ -52,9 +52,12 @@ lookup_v3_desc_as_dir(const uint8_t *key) return digest256map_get(hs_cache_v3_dir, key); } +#define cache_dir_desc_free(val) \ + FREE_AND_NULL(hs_cache_dir_descriptor_t, cache_dir_desc_free_, (val)) + /* Free a directory descriptor object. */ static void -cache_dir_desc_free(hs_cache_dir_descriptor_t *desc) +cache_dir_desc_free_(hs_cache_dir_descriptor_t *desc) { if (desc == NULL) { return; @@ -67,10 +70,9 @@ cache_dir_desc_free(hs_cache_dir_descriptor_t *desc) /* Helper function: Use by the free all function using the digest256map * interface to cache entries. */ static void -cache_dir_desc_free_(void *ptr) +cache_dir_desc_free_void(void *ptr) { - hs_cache_dir_descriptor_t *desc = ptr; - cache_dir_desc_free(desc); + cache_dir_desc_free_(ptr); } /* Create a new directory cache descriptor object from a encoded descriptor. @@ -417,9 +419,12 @@ cache_client_desc_new(const char *desc_str, return client_desc; } +#define cache_client_desc_free(val) \ + FREE_AND_NULL(hs_cache_client_descriptor_t, cache_client_desc_free_, (val)) + /** Free memory allocated by <b>desc</b>. */ static void -cache_client_desc_free(hs_cache_client_descriptor_t *desc) +cache_client_desc_free_(hs_cache_client_descriptor_t *desc) { if (desc == NULL) { return; @@ -433,7 +438,7 @@ cache_client_desc_free(hs_cache_client_descriptor_t *desc) /** Helper function: Use by the free all function to clear the client cache */ static void -cache_client_desc_free_(void *ptr) +cache_client_desc_free_void(void *ptr) { hs_cache_client_descriptor_t *desc = ptr; cache_client_desc_free(desc); @@ -448,18 +453,21 @@ cache_intro_state_new(void) return state; } +#define cache_intro_state_free(val) \ + FREE_AND_NULL(hs_cache_intro_state_t, cache_intro_state_free_, (val)) + /* Free an hs_cache_intro_state_t object. */ static void -cache_intro_state_free(hs_cache_intro_state_t *state) +cache_intro_state_free_(hs_cache_intro_state_t *state) { tor_free(state); } /* Helper function: use by the free all function. */ static void -cache_intro_state_free_(void *state) +cache_intro_state_free_void(void *state) { - cache_intro_state_free(state); + cache_intro_state_free_(state); } /* Return a newly allocated and initialized hs_cache_client_intro_state_t @@ -472,22 +480,26 @@ cache_client_intro_state_new(void) return cache; } +#define cache_client_intro_state_free(val) \ + FREE_AND_NULL(hs_cache_client_intro_state_t, \ + cache_client_intro_state_free_, (val)) + /* Free a cache client intro state object. */ static void -cache_client_intro_state_free(hs_cache_client_intro_state_t *cache) +cache_client_intro_state_free_(hs_cache_client_intro_state_t *cache) { if (cache == NULL) { return; } - digest256map_free(cache->intro_points, cache_intro_state_free_); + digest256map_free(cache->intro_points, cache_intro_state_free_void); tor_free(cache); } /* Helper function: use by the free all function. */ static void -cache_client_intro_state_free_(void *entry) +cache_client_intro_state_free_void(void *entry) { - cache_client_intro_state_free(entry); + cache_client_intro_state_free_(entry); } /* For the given service identity key service_pk and an introduction @@ -706,6 +718,24 @@ cache_clean_v3_as_client(time_t now) } /** Public API: Given the HS ed25519 identity public key in <b>key</b>, return + * its HS encoded descriptor if it's stored in our cache, or NULL if not. */ +const char * +hs_cache_lookup_encoded_as_client(const ed25519_public_key_t *key) +{ + hs_cache_client_descriptor_t *cached_desc = NULL; + + tor_assert(key); + + cached_desc = lookup_v3_desc_as_client(key->pubkey); + if (cached_desc) { + tor_assert(cached_desc->encoded_desc); + return cached_desc->encoded_desc; + } + + return NULL; +} + +/** Public API: Given the HS ed25519 identity public key in <b>key</b>, return * its HS descriptor if it's stored in our cache, or NULL if not. */ const hs_descriptor_t * hs_cache_lookup_as_client(const ed25519_public_key_t *key) @@ -933,14 +963,14 @@ hs_cache_init(void) void hs_cache_free_all(void) { - digest256map_free(hs_cache_v3_dir, cache_dir_desc_free_); + digest256map_free(hs_cache_v3_dir, cache_dir_desc_free_void); hs_cache_v3_dir = NULL; - digest256map_free(hs_cache_v3_client, cache_client_desc_free_); + digest256map_free(hs_cache_v3_client, cache_client_desc_free_void); hs_cache_v3_client = NULL; digest256map_free(hs_cache_client_intro_state, - cache_client_intro_state_free_); + cache_client_intro_state_free_void); hs_cache_client_intro_state = NULL; } diff --git a/src/or/hs_cache.h b/src/or/hs_cache.h index 2dcc518a71..a141634cc4 100644 --- a/src/or/hs_cache.h +++ b/src/or/hs_cache.h @@ -81,6 +81,8 @@ int hs_cache_lookup_as_dir(uint32_t version, const char *query, const hs_descriptor_t * hs_cache_lookup_as_client(const ed25519_public_key_t *key); +const char * +hs_cache_lookup_encoded_as_client(const ed25519_public_key_t *key); int hs_cache_store_as_client(const char *desc_str, const ed25519_public_key_t *identity_pk); void hs_cache_clean_as_client(time_t now); diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c index a58166ccde..95073522ed 100644 --- a/src/or/hs_circuit.c +++ b/src/or/hs_circuit.c @@ -13,6 +13,7 @@ #include "circuitlist.h" #include "circuituse.h" #include "config.h" +#include "nodelist.h" #include "policies.h" #include "relay.h" #include "rendservice.h" @@ -553,76 +554,99 @@ retry_service_rendezvous_point(const origin_circuit_t *circ) return; } -/* Using an extend info object ei, set all possible link specifiers in lspecs. - * legacy ID is mandatory thus MUST be present in ei. If IPv4 is not present, - * logs a BUG() warning, and returns an empty smartlist. Clients never make - * direct connections to rendezvous points, so they should always have an - * IPv4 address in ei. */ +/* Add all possible link specifiers in node to lspecs. + * legacy ID is mandatory thus MUST be present in node. If the primary address + * is not IPv4, log a BUG() warning, and return an empty smartlist. + * Includes ed25519 id and IPv6 link specifiers if present in the node. */ static void -get_lspecs_from_extend_info(const extend_info_t *ei, smartlist_t *lspecs) +get_lspecs_from_node(const node_t *node, smartlist_t *lspecs) { link_specifier_t *ls; + tor_addr_port_t ap; - tor_assert(ei); + tor_assert(node); tor_assert(lspecs); - /* We require IPv4, we will add IPv6 support in a later tor version */ - if (BUG(!tor_addr_is_v4(&ei->addr))) { + /* Get the relay's IPv4 address. */ + node_get_prim_orport(node, &ap); + + /* We expect the node's primary address to be a valid IPv4 address. + * This conforms to the protocol, which requires either an IPv4 or IPv6 + * address (or both). */ + if (BUG(!tor_addr_is_v4(&ap.addr)) || + BUG(!tor_addr_port_is_valid_ap(&ap, 0))) { return; } ls = link_specifier_new(); link_specifier_set_ls_type(ls, LS_IPV4); - link_specifier_set_un_ipv4_addr(ls, tor_addr_to_ipv4h(&ei->addr)); - link_specifier_set_un_ipv4_port(ls, ei->port); + link_specifier_set_un_ipv4_addr(ls, tor_addr_to_ipv4h(&ap.addr)); + link_specifier_set_un_ipv4_port(ls, ap.port); /* Four bytes IPv4 and two bytes port. */ - link_specifier_set_ls_len(ls, sizeof(ei->addr.addr.in_addr) + - sizeof(ei->port)); + link_specifier_set_ls_len(ls, sizeof(ap.addr.addr.in_addr) + + sizeof(ap.port)); smartlist_add(lspecs, ls); - /* Legacy ID is mandatory. */ + /* Legacy ID is mandatory and will always be present in node. */ ls = link_specifier_new(); link_specifier_set_ls_type(ls, LS_LEGACY_ID); - memcpy(link_specifier_getarray_un_legacy_id(ls), ei->identity_digest, + memcpy(link_specifier_getarray_un_legacy_id(ls), node->identity, link_specifier_getlen_un_legacy_id(ls)); link_specifier_set_ls_len(ls, link_specifier_getlen_un_legacy_id(ls)); smartlist_add(lspecs, ls); - /* ed25519 ID is only included if the extend_info has it. */ - if (!ed25519_public_key_is_zero(&ei->ed_identity)) { + /* ed25519 ID is only included if the node has it. */ + if (!ed25519_public_key_is_zero(&node->ed25519_id)) { ls = link_specifier_new(); link_specifier_set_ls_type(ls, LS_ED25519_ID); - memcpy(link_specifier_getarray_un_ed25519_id(ls), &ei->ed_identity, + memcpy(link_specifier_getarray_un_ed25519_id(ls), &node->ed25519_id, link_specifier_getlen_un_ed25519_id(ls)); link_specifier_set_ls_len(ls, link_specifier_getlen_un_ed25519_id(ls)); smartlist_add(lspecs, ls); } + + /* Check for IPv6. If so, include it as well. */ + if (node_has_ipv6_orport(node)) { + ls = link_specifier_new(); + node_get_pref_ipv6_orport(node, &ap); + link_specifier_set_ls_type(ls, LS_IPV6); + size_t addr_len = link_specifier_getlen_un_ipv6_addr(ls); + const uint8_t *in6_addr = tor_addr_to_in6_addr8(&ap.addr); + uint8_t *ipv6_array = link_specifier_getarray_un_ipv6_addr(ls); + memcpy(ipv6_array, in6_addr, addr_len); + link_specifier_set_un_ipv6_port(ls, ap.port); + /* Sixteen bytes IPv6 and two bytes port. */ + link_specifier_set_ls_len(ls, addr_len + sizeof(ap.port)); + smartlist_add(lspecs, ls); + } } -/* Using the given descriptor intro point ip, the extend information of the - * rendezvous point rp_ei and the service's subcredential, populate the +/* Using the given descriptor intro point ip, the node of the + * rendezvous point rp_node and the service's subcredential, populate the * already allocated intro1_data object with the needed key material and link * specifiers. * - * This can't fail but the ip MUST be a valid object containing the needed - * keys and authentication method. */ + * If rp_node has an invalid primary address, intro1_data->link_specifiers + * will be an empty list. Otherwise, this function can't fail. The ip + * MUST be a valid object containing the needed keys and authentication + * method. */ static void setup_introduce1_data(const hs_desc_intro_point_t *ip, - const extend_info_t *rp_ei, + const node_t *rp_node, const uint8_t *subcredential, hs_cell_introduce1_data_t *intro1_data) { smartlist_t *rp_lspecs; tor_assert(ip); - tor_assert(rp_ei); + tor_assert(rp_node); tor_assert(subcredential); tor_assert(intro1_data); /* Build the link specifiers from the extend information of the rendezvous * circuit that we've picked previously. */ rp_lspecs = smartlist_new(); - get_lspecs_from_extend_info(rp_ei, rp_lspecs); + get_lspecs_from_node(rp_node, rp_lspecs); /* Populate the introduce1 data object. */ memset(intro1_data, 0, sizeof(hs_cell_introduce1_data_t)); @@ -633,7 +657,7 @@ setup_introduce1_data(const hs_desc_intro_point_t *ip, intro1_data->auth_pk = &ip->auth_key_cert->signed_key; intro1_data->enc_pk = &ip->enc_key; intro1_data->subcredential = subcredential; - intro1_data->onion_pk = &rp_ei->curve25519_onion_key; + intro1_data->onion_pk = node_get_curve25519_onion_key(rp_node); intro1_data->link_specifiers = rp_lspecs; } @@ -1077,11 +1101,21 @@ hs_circ_send_introduce1(origin_circuit_t *intro_circ, tor_assert(ip); tor_assert(subcredential); + /* It is undefined behavior in hs_cell_introduce1_data_clear() if intro1_data + * has been declared on the stack but not initialized. Here, we set it to 0. + */ + memset(&intro1_data, 0, sizeof(hs_cell_introduce1_data_t)); + /* This takes various objects in order to populate the introduce1 data * object which is used to build the content of the cell. */ - setup_introduce1_data(ip, rend_circ->build_state->chosen_exit, - subcredential, &intro1_data); - /* If we didn't get any link specifiers, it's because our extend info was + const node_t *exit_node = build_state_get_exit_node(rend_circ->build_state); + if (exit_node == NULL) { + log_info(LD_REND, "Unable to get rendezvous point for circuit %u. " + "Failing.", TO_CIRCUIT(intro_circ)->n_circ_id); + goto done; + } + setup_introduce1_data(ip, exit_node, subcredential, &intro1_data); + /* If we didn't get any link specifiers, it's because our node was * bad. */ if (BUG(!intro1_data.link_specifiers) || !smartlist_len(intro1_data.link_specifiers)) { diff --git a/src/or/hs_circuit.h b/src/or/hs_circuit.h index 63ff5e463c..2f5beaa168 100644 --- a/src/or/hs_circuit.h +++ b/src/or/hs_circuit.h @@ -70,7 +70,7 @@ create_rp_circuit_identifier(const hs_service_t *service, const curve25519_public_key_t *server_pk, const hs_ntor_rend_cell_keys_t *keys); -#endif +#endif /* defined(HS_CIRCUIT_PRIVATE) */ #endif /* !defined(TOR_HS_CIRCUIT_H) */ diff --git a/src/or/hs_circuitmap.c b/src/or/hs_circuitmap.c index 97d6053e9b..112c8bdced 100644 --- a/src/or/hs_circuitmap.c +++ b/src/or/hs_circuitmap.c @@ -106,9 +106,12 @@ hs_token_new(hs_token_type_t type, size_t token_len, return hs_token; } +#define hs_token_free(val) \ + FREE_AND_NULL(hs_token_t, hs_token_free_, (val)) + /** Free memory allocated by this <b>hs_token</b>. */ static void -hs_token_free(hs_token_t *hs_token) +hs_token_free_(hs_token_t *hs_token) { if (!hs_token) { return; @@ -428,30 +431,54 @@ hs_circuitmap_get_rend_circ_client_side(const uint8_t *cookie) { origin_circuit_t *circ = NULL; - circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE, - REND_TOKEN_LEN, cookie, - CIRCUIT_PURPOSE_C_REND_READY); + circ = hs_circuitmap_get_established_rend_circ_client_side(cookie); if (circ) { return circ; } circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE, REND_TOKEN_LEN, cookie, - CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED); + CIRCUIT_PURPOSE_C_ESTABLISH_REND); + return circ; +} + +/* Public function: Return client-side established rendezvous circuit with + * rendezvous <b>cookie</b>. It will look for circuits with the following + * purposes: + * + * a) CIRCUIT_PURPOSE_C_REND_READY: Established rend circuit (received + * RENDEZVOUS_ESTABLISHED). Waiting for RENDEZVOUS2 from service, and for + * INTRODUCE_ACK from intro point. + * + * b) CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: Established rend circuit and + * introduce circuit acked. Waiting for RENDEZVOUS2 from service. + * + * c) CIRCUIT_PURPOSE_C_REND_JOINED: Established rend circuit and received + * RENDEZVOUS2 from service. + * + * Return NULL if no such circuit is found in the circuitmap. */ +origin_circuit_t * +hs_circuitmap_get_established_rend_circ_client_side(const uint8_t *cookie) +{ + origin_circuit_t *circ = NULL; + + circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE, + REND_TOKEN_LEN, cookie, + CIRCUIT_PURPOSE_C_REND_READY); if (circ) { return circ; } circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE, REND_TOKEN_LEN, cookie, - CIRCUIT_PURPOSE_C_REND_JOINED); + CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED); if (circ) { return circ; } circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE, REND_TOKEN_LEN, cookie, - CIRCUIT_PURPOSE_C_ESTABLISH_REND); + CIRCUIT_PURPOSE_C_REND_JOINED); return circ; } diff --git a/src/or/hs_circuitmap.h b/src/or/hs_circuitmap.h index 43b2947c17..9e653480b5 100644 --- a/src/or/hs_circuitmap.h +++ b/src/or/hs_circuitmap.h @@ -45,6 +45,8 @@ struct origin_circuit_t * hs_circuitmap_get_rend_circ_service_side(const uint8_t *cookie); struct origin_circuit_t * hs_circuitmap_get_rend_circ_client_side(const uint8_t *cookie); +struct origin_circuit_t * +hs_circuitmap_get_established_rend_circ_client_side(const uint8_t *cookie); void hs_circuitmap_register_intro_circ_v2_service_side( struct origin_circuit_t *circ, diff --git a/src/or/hs_client.c b/src/or/hs_client.c index 9ac653c721..c863475aff 100644 --- a/src/or/hs_client.c +++ b/src/or/hs_client.c @@ -21,6 +21,7 @@ #include "config.h" #include "directory.h" #include "hs_client.h" +#include "hs_control.h" #include "router.h" #include "routerset.h" #include "circuitlist.h" @@ -349,6 +350,10 @@ directory_launch_v3_desc_fetch(const ed25519_public_key_t *onion_identity_pk, safe_str_client(base64_blinded_pubkey), safe_str_client(routerstatus_describe(hsdir))); + /* Fire a REQUESTED event on the control port. */ + hs_control_desc_event_requested(onion_identity_pk, base64_blinded_pubkey, + hsdir); + /* Cleanup memory. */ memwipe(&blinded_pubkey, 0, sizeof(blinded_pubkey)); memwipe(base64_blinded_pubkey, 0, sizeof(base64_blinded_pubkey)); @@ -940,7 +945,8 @@ handle_introduce_ack_success(origin_circuit_t *intro_circ) /* Get the rendezvous circuit for this rendezvous cookie. */ uint8_t *rendezvous_cookie = intro_circ->hs_ident->rendezvous_cookie; - rend_circ = hs_circuitmap_get_rend_circ_client_side(rendezvous_cookie); + rend_circ = + hs_circuitmap_get_established_rend_circ_client_side(rendezvous_cookie); if (rend_circ == NULL) { log_warn(LD_REND, "Can't find any rendezvous circuit. Stopping"); goto end; diff --git a/src/or/hs_common.c b/src/or/hs_common.c index ad783a36fb..2c0c2aecd2 100644 --- a/src/or/hs_common.c +++ b/src/or/hs_common.c @@ -330,7 +330,7 @@ rend_data_alloc(uint32_t version) /** Free all storage associated with <b>data</b> */ void -rend_data_free(rend_data_t *data) +rend_data_free_(rend_data_t *data) { if (!data) { return; diff --git a/src/or/hs_common.h b/src/or/hs_common.h index b81c03d508..f59c2b6050 100644 --- a/src/or/hs_common.h +++ b/src/or/hs_common.h @@ -130,6 +130,17 @@ typedef enum { HS_AUTH_KEY_TYPE_ED25519 = 2, } hs_auth_key_type_t; +/* Return value when adding an ephemeral service through the ADD_ONION + * control port command. Both v2 and v3 share these. */ +typedef enum { + RSAE_BADAUTH = -5, /**< Invalid auth_type/auth_clients */ + RSAE_BADVIRTPORT = -4, /**< Invalid VIRTPORT/TARGET(s) */ + RSAE_ADDREXISTS = -3, /**< Onion address collision */ + RSAE_BADPRIVKEY = -2, /**< Invalid public key */ + RSAE_INTERNAL = -1, /**< Internal error */ + RSAE_OKAY = 0 /**< Service added as expected */ +} hs_service_add_ephemeral_status_t; + /* Represents the mapping from a virtual port of a rendezvous service to a * real port on some IP. */ typedef struct rend_service_port_config_t { @@ -183,7 +194,9 @@ void hs_build_blinded_keypair(const ed25519_keypair_t *kp, ed25519_keypair_t *kp_out); int hs_service_requires_uptime_circ(const smartlist_t *ports); -void rend_data_free(rend_data_t *data); +void rend_data_free_(rend_data_t *data); +#define rend_data_free(data) \ + FREE_AND_NULL(rend_data_t, rend_data_free_, (data)) rend_data_t *rend_data_dup(const rend_data_t *data); rend_data_t *rend_data_client_create(const char *onion_address, const char *desc_id, diff --git a/src/or/hs_control.c b/src/or/hs_control.c new file mode 100644 index 0000000000..87b4e3fca8 --- /dev/null +++ b/src/or/hs_control.c @@ -0,0 +1,258 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_control.c + * \brief Contains control port event related code. + **/ + +#include "or.h" +#include "control.h" +#include "hs_common.h" +#include "hs_control.h" +#include "hs_descriptor.h" +#include "hs_service.h" +#include "nodelist.h" + +/* Send on the control port the "HS_DESC REQUESTED [...]" event. + * + * The onion_pk is the onion service public key, base64_blinded_pk is the + * base64 encoded blinded key for the service and hsdir_rs is the routerstatus + * object of the HSDir that this request is for. */ +void +hs_control_desc_event_requested(const ed25519_public_key_t *onion_pk, + const char *base64_blinded_pk, + const routerstatus_t *hsdir_rs) +{ + char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + const uint8_t *hsdir_index; + const node_t *hsdir_node; + + tor_assert(onion_pk); + tor_assert(base64_blinded_pk); + tor_assert(hsdir_rs); + + hs_build_address(onion_pk, HS_VERSION_THREE, onion_address); + + /* Get the node from the routerstatus object to get the HSDir index used for + * this request. We can't have a routerstatus entry without a node and we + * can't pick a node without an hsdir_index. */ + hsdir_node = node_get_by_id(hsdir_rs->identity_digest); + tor_assert(hsdir_node); + tor_assert(hsdir_node->hsdir_index); + /* This is a fetch event. */ + hsdir_index = hsdir_node->hsdir_index->fetch; + + /* Trigger the event. */ + control_event_hs_descriptor_requested(onion_address, REND_NO_AUTH, + hsdir_rs->identity_digest, + base64_blinded_pk, + hex_str((const char *) hsdir_index, + DIGEST256_LEN)); + memwipe(onion_address, 0, sizeof(onion_address)); +} + +/* Send on the control port the "HS_DESC FAILED [...]" event. + * + * Using a directory connection identifier, the HSDir identity digest and a + * reason for the failure. None can be NULL. */ +void +hs_control_desc_event_failed(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest, + const char *reason) +{ + char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + char base64_blinded_pk[ED25519_BASE64_LEN + 1]; + + tor_assert(ident); + tor_assert(hsdir_id_digest); + tor_assert(reason); + + /* Build onion address and encoded blinded key. */ + IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, + &ident->blinded_pk) < 0) { + return; + } + hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address); + + control_event_hsv3_descriptor_failed(onion_address, base64_blinded_pk, + hsdir_id_digest, reason); +} + +/* Send on the control port the "HS_DESC RECEIVED [...]" event. + * + * Using a directory connection identifier and the HSDir identity digest. + * None can be NULL. */ +void +hs_control_desc_event_received(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest) +{ + char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + char base64_blinded_pk[ED25519_BASE64_LEN + 1]; + + tor_assert(ident); + tor_assert(hsdir_id_digest); + + /* Build onion address and encoded blinded key. */ + IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, + &ident->blinded_pk) < 0) { + return; + } + hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address); + + control_event_hsv3_descriptor_received(onion_address, base64_blinded_pk, + hsdir_id_digest); +} + +/* Send on the control port the "HS_DESC CREATED [...]" event. + * + * Using the onion address of the descriptor's service and the blinded public + * key of the descriptor as a descriptor ID. None can be NULL. */ +void +hs_control_desc_event_created(const char *onion_address, + const ed25519_public_key_t *blinded_pk) +{ + char base64_blinded_pk[ED25519_BASE64_LEN + 1]; + + tor_assert(onion_address); + tor_assert(blinded_pk); + + /* Build base64 encoded blinded key. */ + IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, blinded_pk) < 0) { + return; + } + + /* Version 3 doesn't use the replica number in its descriptor ID computation + * so we pass negative value so the control port subsystem can ignore it. */ + control_event_hs_descriptor_created(onion_address, base64_blinded_pk, -1); +} + +/* Send on the control port the "HS_DESC UPLOAD [...]" event. + * + * Using the onion address of the descriptor's service, the HSDir identity + * digest, the blinded public key of the descriptor as a descriptor ID and the + * HSDir index for this particular request. None can be NULL. */ +void +hs_control_desc_event_upload(const char *onion_address, + const char *hsdir_id_digest, + const ed25519_public_key_t *blinded_pk, + const uint8_t *hsdir_index) +{ + char base64_blinded_pk[ED25519_BASE64_LEN + 1]; + + tor_assert(onion_address); + tor_assert(hsdir_id_digest); + tor_assert(blinded_pk); + tor_assert(hsdir_index); + + /* Build base64 encoded blinded key. */ + IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, blinded_pk) < 0) { + return; + } + + control_event_hs_descriptor_upload(onion_address, hsdir_id_digest, + base64_blinded_pk, + hex_str((const char *) hsdir_index, + DIGEST256_LEN)); +} + +/* Send on the control port the "HS_DESC UPLOADED [...]" event. + * + * Using the directory connection identifier and the HSDir identity digest. + * None can be NULL. */ +void +hs_control_desc_event_uploaded(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest) +{ + char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + + tor_assert(ident); + tor_assert(hsdir_id_digest); + + hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address); + + control_event_hs_descriptor_uploaded(hsdir_id_digest, onion_address); +} + +/* Send on the control port the "HS_DESC_CONTENT [...]" event. + * + * Using the directory connection identifier, the HSDir identity digest and + * the body of the descriptor (as it was received from the directory). None + * can be NULL. */ +void +hs_control_desc_event_content(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest, + const char *body) +{ + char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + char base64_blinded_pk[ED25519_BASE64_LEN + 1]; + + tor_assert(ident); + tor_assert(hsdir_id_digest); + + /* Build onion address and encoded blinded key. */ + IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, + &ident->blinded_pk) < 0) { + return; + } + hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address); + + control_event_hs_descriptor_content(onion_address, base64_blinded_pk, + hsdir_id_digest, body); +} + +/* Handle the "HSPOST [...]" command. The body is an encoded descriptor for + * the given onion_address. The descriptor will be uploaded to each directory + * in hsdirs_rs. If NULL, the responsible directories for the current time + * period will be selected. + * + * Return -1 on if the descriptor plaintext section is not decodable. Else, 0 + * on success. */ +int +hs_control_hspost_command(const char *body, const char *onion_address, + const smartlist_t *hsdirs_rs) +{ + int ret = -1; + ed25519_public_key_t identity_pk; + hs_desc_plaintext_data_t plaintext; + smartlist_t *hsdirs = NULL; + + tor_assert(body); + tor_assert(onion_address); + + /* This can't fail because we require the caller to pass us a valid onion + * address that has passed hs_address_is_valid(). */ + if (BUG(hs_parse_address(onion_address, &identity_pk, NULL, NULL) < 0)) { + goto done; // LCOV_EXCL_LINE + } + + /* Only decode the plaintext part which is what the directory will do to + * validate before caching. */ + if (hs_desc_decode_plaintext(body, &plaintext) < 0) { + goto done; + } + + /* No HSDir(s) given, we'll compute what the current ones should be. */ + if (hsdirs_rs == NULL) { + hsdirs = smartlist_new(); + hs_get_responsible_hsdirs(&plaintext.blinded_pubkey, + hs_get_time_period_num(0), + 0, /* Always the current descriptor which uses + * the first hsdir index. */ + 0, /* It is for storing on a directory. */ + hsdirs); + hsdirs_rs = hsdirs; + } + + SMARTLIST_FOREACH_BEGIN(hsdirs_rs, const routerstatus_t *, rs) { + hs_service_upload_desc_to_dir(body, plaintext.version, &identity_pk, + &plaintext.blinded_pubkey, rs); + } SMARTLIST_FOREACH_END(rs); + ret = 0; + + done: + /* We don't have ownership of the objects in this list. */ + smartlist_free(hsdirs); + return ret; +} + diff --git a/src/or/hs_control.h b/src/or/hs_control.h new file mode 100644 index 0000000000..95c46e655e --- /dev/null +++ b/src/or/hs_control.h @@ -0,0 +1,52 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_control.h + * \brief Header file containing control port event related code. + **/ + +#ifndef TOR_HS_CONTROL_H +#define TOR_HS_CONTROL_H + +#include "hs_ident.h" + +/* Event "HS_DESC REQUESTED [...]" */ +void hs_control_desc_event_requested(const ed25519_public_key_t *onion_pk, + const char *base64_blinded_pk, + const routerstatus_t *hsdir_rs); + +/* Event "HS_DESC FAILED [...]" */ +void hs_control_desc_event_failed(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest, + const char *reason); + +/* Event "HS_DESC RECEIVED [...]" */ +void hs_control_desc_event_received(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest); + +/* Event "HS_DESC CREATED [...]" */ +void hs_control_desc_event_created(const char *onion_address, + const ed25519_public_key_t *blinded_pk); + +/* Event "HS_DESC UPLOAD [...]" */ +void hs_control_desc_event_upload(const char *onion_address, + const char *hsdir_id_digest, + const ed25519_public_key_t *blinded_pk, + const uint8_t *hsdir_index); + +/* Event "HS_DESC UPLOADED [...]" */ +void hs_control_desc_event_uploaded(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest); + +/* Event "HS_DESC_CONTENT [...]" */ +void hs_control_desc_event_content(const hs_ident_dir_conn_t *ident, + const char *hsdir_id_digest, + const char *body); + +/* Command "HSPOST [...]" */ +int hs_control_hspost_command(const char *body, const char *onion_address, + const smartlist_t *hsdirs_rs); + +#endif /* !defined(TOR_HS_CONTROL_H) */ + diff --git a/src/or/hs_descriptor.c b/src/or/hs_descriptor.c index 1708866944..789bc1d046 100644 --- a/src/or/hs_descriptor.c +++ b/src/or/hs_descriptor.c @@ -2367,7 +2367,7 @@ hs_desc_encode_descriptor,(const hs_descriptor_t *desc, /* Free the descriptor plaintext data object. */ void -hs_desc_plaintext_data_free(hs_desc_plaintext_data_t *desc) +hs_desc_plaintext_data_free_(hs_desc_plaintext_data_t *desc) { desc_plaintext_data_free_contents(desc); tor_free(desc); @@ -2375,7 +2375,7 @@ hs_desc_plaintext_data_free(hs_desc_plaintext_data_t *desc) /* Free the descriptor encrypted data object. */ void -hs_desc_encrypted_data_free(hs_desc_encrypted_data_t *desc) +hs_desc_encrypted_data_free_(hs_desc_encrypted_data_t *desc) { desc_encrypted_data_free_contents(desc); tor_free(desc); @@ -2383,7 +2383,7 @@ hs_desc_encrypted_data_free(hs_desc_encrypted_data_t *desc) /* Free the given descriptor object. */ void -hs_descriptor_free(hs_descriptor_t *desc) +hs_descriptor_free_(hs_descriptor_t *desc) { if (!desc) { return; @@ -2448,7 +2448,7 @@ hs_desc_intro_point_new(void) /* Free a descriptor intro point object. */ void -hs_desc_intro_point_free(hs_desc_intro_point_t *ip) +hs_desc_intro_point_free_(hs_desc_intro_point_t *ip) { if (ip == NULL) { return; @@ -2467,7 +2467,7 @@ hs_desc_intro_point_free(hs_desc_intro_point_t *ip) /* Free the given descriptor link specifier. */ void -hs_desc_link_specifier_free(hs_desc_link_specifier_t *ls) +hs_desc_link_specifier_free_(hs_desc_link_specifier_t *ls) { if (ls == NULL) { return; diff --git a/src/or/hs_descriptor.h b/src/or/hs_descriptor.h index 52bec8e244..09979410e1 100644 --- a/src/or/hs_descriptor.h +++ b/src/or/hs_descriptor.h @@ -208,11 +208,20 @@ hs_desc_is_supported_version(uint32_t version) /* Public API. */ -void hs_descriptor_free(hs_descriptor_t *desc); -void hs_desc_plaintext_data_free(hs_desc_plaintext_data_t *desc); -void hs_desc_encrypted_data_free(hs_desc_encrypted_data_t *desc); +void hs_descriptor_free_(hs_descriptor_t *desc); +#define hs_descriptor_free(desc) \ + FREE_AND_NULL(hs_descriptor_t, hs_descriptor_free_, (desc)) +void hs_desc_plaintext_data_free_(hs_desc_plaintext_data_t *desc); +#define hs_desc_plaintext_data_free(desc) \ + FREE_AND_NULL(hs_desc_plaintext_data_t, hs_desc_plaintext_data_free_, (desc)) +void hs_desc_encrypted_data_free_(hs_desc_encrypted_data_t *desc); +#define hs_desc_encrypted_data_free(desc) \ + FREE_AND_NULL(hs_desc_encrypted_data_t, hs_desc_encrypted_data_free_, (desc)) + +void hs_desc_link_specifier_free_(hs_desc_link_specifier_t *ls); +#define hs_desc_link_specifier_free(ls) \ + FREE_AND_NULL(hs_desc_link_specifier_t, hs_desc_link_specifier_free_, (ls)) -void hs_desc_link_specifier_free(hs_desc_link_specifier_t *ls); hs_desc_link_specifier_t *hs_desc_link_specifier_new( const extend_info_t *info, uint8_t type); void hs_descriptor_clear_intro_points(hs_descriptor_t *desc); @@ -234,7 +243,9 @@ size_t hs_desc_obj_size(const hs_descriptor_t *data); size_t hs_desc_plaintext_obj_size(const hs_desc_plaintext_data_t *data); hs_desc_intro_point_t *hs_desc_intro_point_new(void); -void hs_desc_intro_point_free(hs_desc_intro_point_t *ip); +void hs_desc_intro_point_free_(hs_desc_intro_point_t *ip); +#define hs_desc_intro_point_free(ip) \ + FREE_AND_NULL(hs_desc_intro_point_t, hs_desc_intro_point_free_, (ip)) link_specifier_t *hs_desc_lspec_to_trunnel( const hs_desc_link_specifier_t *spec); diff --git a/src/or/hs_ident.c b/src/or/hs_ident.c index b0e4e36a9b..0bce2f625b 100644 --- a/src/or/hs_ident.c +++ b/src/or/hs_ident.c @@ -25,7 +25,7 @@ hs_ident_circuit_new(const ed25519_public_key_t *identity_pk, /* Free the given circuit identifier. */ void -hs_ident_circuit_free(hs_ident_circuit_t *ident) +hs_ident_circuit_free_(hs_ident_circuit_t *ident) { if (ident == NULL) { return; @@ -56,7 +56,7 @@ hs_ident_dir_conn_dup(const hs_ident_dir_conn_t *src) /* Free the given directory connection identifier. */ void -hs_ident_dir_conn_free(hs_ident_dir_conn_t *ident) +hs_ident_dir_conn_free_(hs_ident_dir_conn_t *ident) { if (ident == NULL) { return; @@ -93,7 +93,7 @@ hs_ident_edge_conn_new(const ed25519_public_key_t *identity_pk) /* Free the given edge connection identifier. */ void -hs_ident_edge_conn_free(hs_ident_edge_conn_t *ident) +hs_ident_edge_conn_free_(hs_ident_edge_conn_t *ident) { if (ident == NULL) { return; diff --git a/src/or/hs_ident.h b/src/or/hs_ident.h index 03150d25ea..91ec389aa4 100644 --- a/src/or/hs_ident.h +++ b/src/or/hs_ident.h @@ -119,12 +119,16 @@ typedef struct hs_ident_edge_conn_t { hs_ident_circuit_t *hs_ident_circuit_new( const ed25519_public_key_t *identity_pk, hs_ident_circuit_type_t circuit_type); -void hs_ident_circuit_free(hs_ident_circuit_t *ident); +void hs_ident_circuit_free_(hs_ident_circuit_t *ident); +#define hs_ident_circuit_free(id) \ + FREE_AND_NULL(hs_ident_circuit_t, hs_ident_circuit_free_, (id)) hs_ident_circuit_t *hs_ident_circuit_dup(const hs_ident_circuit_t *src); /* Directory connection identifier API. */ hs_ident_dir_conn_t *hs_ident_dir_conn_dup(const hs_ident_dir_conn_t *src); -void hs_ident_dir_conn_free(hs_ident_dir_conn_t *ident); +void hs_ident_dir_conn_free_(hs_ident_dir_conn_t *ident); +#define hs_ident_dir_conn_free(id) \ + FREE_AND_NULL(hs_ident_dir_conn_t, hs_ident_dir_conn_free_, (id)) void hs_ident_dir_conn_init(const ed25519_public_key_t *identity_pk, const ed25519_public_key_t *blinded_pk, hs_ident_dir_conn_t *ident); @@ -132,7 +136,9 @@ void hs_ident_dir_conn_init(const ed25519_public_key_t *identity_pk, /* Edge connection identifier API. */ hs_ident_edge_conn_t *hs_ident_edge_conn_new( const ed25519_public_key_t *identity_pk); -void hs_ident_edge_conn_free(hs_ident_edge_conn_t *ident); +void hs_ident_edge_conn_free_(hs_ident_edge_conn_t *ident); +#define hs_ident_edge_conn_free(id) \ + FREE_AND_NULL(hs_ident_edge_conn_t, hs_ident_edge_conn_free_, (id)) /* Validators */ int hs_ident_intro_circ_is_valid(const hs_ident_circuit_t *ident); diff --git a/src/or/hs_service.c b/src/or/hs_service.c index dd836bf842..80f3e50201 100644 --- a/src/or/hs_service.c +++ b/src/or/hs_service.c @@ -30,6 +30,7 @@ #include "hs_circuit.h" #include "hs_common.h" #include "hs_config.h" +#include "hs_control.h" #include "hs_circuit.h" #include "hs_descriptor.h" #include "hs_ident.h" @@ -352,7 +353,7 @@ service_free_all(void) /* Free a given service intro point object. */ STATIC void -service_intro_point_free(hs_service_intro_point_t *ip) +service_intro_point_free_(hs_service_intro_point_t *ip) { if (!ip) { return; @@ -368,9 +369,9 @@ service_intro_point_free(hs_service_intro_point_t *ip) /* Helper: free an hs_service_intro_point_t object. This function is used by * digest256map_free() which requires a void * pointer. */ static void -service_intro_point_free_(void *obj) +service_intro_point_free_void(void *obj) { - service_intro_point_free(obj); + service_intro_point_free_(obj); } /* Return a newly allocated service intro point and fully initialized from the @@ -1027,7 +1028,7 @@ load_service_keys(hs_service_t *service) /* Free a given service descriptor object and all key material is wiped. */ STATIC void -service_descriptor_free(hs_service_descriptor_t *desc) +service_descriptor_free_(hs_service_descriptor_t *desc) { if (!desc) { return; @@ -1036,7 +1037,7 @@ service_descriptor_free(hs_service_descriptor_t *desc) memwipe(&desc->signing_kp, 0, sizeof(desc->signing_kp)); memwipe(&desc->blinded_kp, 0, sizeof(desc->blinded_kp)); /* Cleanup all intro points. */ - digest256map_free(desc->intro_points.map, service_intro_point_free_); + digest256map_free(desc->intro_points.map, service_intro_point_free_void); digestmap_free(desc->intro_points.failed_id, tor_free_); if (desc->previous_hsdirs) { SMARTLIST_FOREACH(desc->previous_hsdirs, char *, s, tor_free(s)); @@ -1431,6 +1432,9 @@ build_service_descriptor(hs_service_t *service, time_t now, /* Assign newly built descriptor to the next slot. */ *desc_out = desc; + /* Fire a CREATED control port event. */ + hs_control_desc_event_created(service->onion_address, + &desc->blinded_kp.pubkey); return; err: @@ -1572,7 +1576,7 @@ pick_intro_point(unsigned int direct_conn, smartlist_t *exclude_nodes) /* Let's do a basic sanity check here so that we don't end up advertising the * ed25519 identity key of relays that don't actually support the link * protocol */ - if (!node_supports_ed25519_link_authentication(node)) { + if (!node_supports_ed25519_link_authentication(node, 0)) { tor_assert_nonfatal(ed25519_public_key_is_zero(&info->ed_identity)); } else { /* Make sure we *do* have an ed key if we support the link authentication. @@ -2221,16 +2225,12 @@ static void upload_descriptor_to_hsdir(const hs_service_t *service, hs_service_descriptor_t *desc, const node_t *hsdir) { - char version_str[4] = {0}, *encoded_desc = NULL; - directory_request_t *dir_req; - hs_ident_dir_conn_t ident; + char *encoded_desc = NULL; tor_assert(service); tor_assert(desc); tor_assert(hsdir); - memset(&ident, 0, sizeof(ident)); - /* Let's avoid doing that if tor is configured to not publish. */ if (!get_options()->PublishHidServDescriptors) { log_info(LD_REND, "Service %s not publishing descriptor. " @@ -2246,29 +2246,10 @@ upload_descriptor_to_hsdir(const hs_service_t *service, goto end; } - /* Setup the connection identifier. */ - hs_ident_dir_conn_init(&service->keys.identity_pk, &desc->blinded_kp.pubkey, - &ident); - - /* This is our resource when uploading which is used to construct the URL - * with the version number: "/tor/hs/<version>/publish". */ - tor_snprintf(version_str, sizeof(version_str), "%u", - service->config.version); - - /* Build the directory request for this HSDir. */ - dir_req = directory_request_new(DIR_PURPOSE_UPLOAD_HSDESC); - directory_request_set_routerstatus(dir_req, hsdir->rs); - directory_request_set_indirection(dir_req, DIRIND_ANONYMOUS); - directory_request_set_resource(dir_req, version_str); - directory_request_set_payload(dir_req, encoded_desc, - strlen(encoded_desc)); - /* The ident object is copied over the directory connection object once - * the directory request is initiated. */ - directory_request_upload_set_hs_ident(dir_req, &ident); - - /* Initiate the directory request to the hsdir.*/ - directory_initiate_request(dir_req); - directory_request_free(dir_req); + /* Time to upload the descriptor to the directory. */ + hs_service_upload_desc_to_dir(encoded_desc, service->config.version, + &service->keys.identity_pk, + &desc->blinded_kp.pubkey, hsdir->rs); /* Add this node to previous_hsdirs list */ service_desc_note_upload(desc, hsdir); @@ -2285,9 +2266,12 @@ upload_descriptor_to_hsdir(const hs_service_t *service, desc->desc->plaintext_data.revision_counter, safe_str_client(node_describe(hsdir)), safe_str_client(hex_str((const char *) index, 32))); + + /* Fire a UPLOAD control port event. */ + hs_control_desc_event_upload(service->onion_address, hsdir->identity, + &desc->blinded_kp.pubkey, index); } - /* XXX: Inform control port of the upload event (#20699). */ end: tor_free(encoded_desc); return; @@ -2922,6 +2906,205 @@ service_add_fnames_to_list(const hs_service_t *service, smartlist_t *list) /* Public API */ /* ========== */ +/* Upload an encoded descriptor in encoded_desc of the given version. This + * descriptor is for the service identity_pk and blinded_pk used to setup the + * directory connection identifier. It is uploaded to the directory hsdir_rs + * routerstatus_t object. + * + * NOTE: This function does NOT check for PublishHidServDescriptors because it + * is only used by the control port command HSPOST outside of this subsystem. + * Inside this code, upload_descriptor_to_hsdir() should be used. */ +void +hs_service_upload_desc_to_dir(const char *encoded_desc, + const uint8_t version, + const ed25519_public_key_t *identity_pk, + const ed25519_public_key_t *blinded_pk, + const routerstatus_t *hsdir_rs) +{ + char version_str[4] = {0}; + directory_request_t *dir_req; + hs_ident_dir_conn_t ident; + + tor_assert(encoded_desc); + tor_assert(identity_pk); + tor_assert(blinded_pk); + tor_assert(hsdir_rs); + + /* Setup the connection identifier. */ + memset(&ident, 0, sizeof(ident)); + hs_ident_dir_conn_init(identity_pk, blinded_pk, &ident); + + /* This is our resource when uploading which is used to construct the URL + * with the version number: "/tor/hs/<version>/publish". */ + tor_snprintf(version_str, sizeof(version_str), "%u", version); + + /* Build the directory request for this HSDir. */ + dir_req = directory_request_new(DIR_PURPOSE_UPLOAD_HSDESC); + directory_request_set_routerstatus(dir_req, hsdir_rs); + directory_request_set_indirection(dir_req, DIRIND_ANONYMOUS); + directory_request_set_resource(dir_req, version_str); + directory_request_set_payload(dir_req, encoded_desc, + strlen(encoded_desc)); + /* The ident object is copied over the directory connection object once + * the directory request is initiated. */ + directory_request_upload_set_hs_ident(dir_req, &ident); + + /* Initiate the directory request to the hsdir.*/ + directory_initiate_request(dir_req); + directory_request_free(dir_req); +} + +/* Add the ephemeral service using the secret key sk and ports. Both max + * streams parameter will be set in the newly created service. + * + * Ownership of sk and ports is passed to this routine. Regardless of + * success/failure, callers should not touch these values after calling this + * routine, and may assume that correct cleanup has been done on failure. + * + * Return an appropriate hs_service_add_ephemeral_status_t. */ +hs_service_add_ephemeral_status_t +hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports, + int max_streams_per_rdv_circuit, + int max_streams_close_circuit, char **address_out) +{ + hs_service_add_ephemeral_status_t ret; + hs_service_t *service = NULL; + + tor_assert(sk); + tor_assert(ports); + tor_assert(address_out); + + service = hs_service_new(get_options()); + + /* Setup the service configuration with specifics. A default service is + * HS_VERSION_TWO so explicitely set it. */ + service->config.version = HS_VERSION_THREE; + service->config.max_streams_per_rdv_circuit = max_streams_per_rdv_circuit; + service->config.max_streams_close_circuit = !!max_streams_close_circuit; + service->config.is_ephemeral = 1; + smartlist_free(service->config.ports); + service->config.ports = ports; + + /* Handle the keys. */ + memcpy(&service->keys.identity_sk, sk, sizeof(service->keys.identity_sk)); + if (ed25519_public_key_generate(&service->keys.identity_pk, + &service->keys.identity_sk) < 0) { + log_warn(LD_CONFIG, "Unable to generate ed25519 public key" + "for v3 service."); + ret = RSAE_BADPRIVKEY; + goto err; + } + + /* Make sure we have at least one port. */ + if (smartlist_len(service->config.ports) == 0) { + log_warn(LD_CONFIG, "At least one VIRTPORT/TARGET must be specified " + "for v3 service."); + ret = RSAE_BADVIRTPORT; + goto err; + } + + /* The only way the registration can fail is if the service public key + * already exists. */ + if (BUG(register_service(hs_service_map, service) < 0)) { + log_warn(LD_CONFIG, "Onion Service private key collides with an " + "existing v3 service."); + ret = RSAE_ADDREXISTS; + goto err; + } + + /* Last step is to build the onion address. */ + hs_build_address(&service->keys.identity_pk, + (uint8_t) service->config.version, + service->onion_address); + *address_out = tor_strdup(service->onion_address); + + log_info(LD_CONFIG, "Added ephemeral v3 onion service: %s", + safe_str_client(service->onion_address)); + ret = RSAE_OKAY; + goto end; + + err: + hs_service_free(service); + + end: + memwipe(sk, 0, sizeof(ed25519_secret_key_t)); + tor_free(sk); + return ret; +} + +/* For the given onion address, delete the ephemeral service. Return 0 on + * success else -1 on error. */ +int +hs_service_del_ephemeral(const char *address) +{ + uint8_t version; + ed25519_public_key_t pk; + hs_service_t *service = NULL; + + tor_assert(address); + + if (hs_parse_address(address, &pk, NULL, &version) < 0) { + log_warn(LD_CONFIG, "Requested malformed v3 onion address for removal."); + goto err; + } + + if (version != HS_VERSION_THREE) { + log_warn(LD_CONFIG, "Requested version of onion address for removal " + "is not supported."); + goto err; + } + + service = find_service(hs_service_map, &pk); + if (service == NULL) { + log_warn(LD_CONFIG, "Requested non-existent v3 hidden service for " + "removal."); + goto err; + } + + if (!service->config.is_ephemeral) { + log_warn(LD_CONFIG, "Requested non-ephemeral v3 hidden service for " + "removal."); + goto err; + } + + /* Close circuits, remove from map and finally free. */ + close_service_circuits(service); + remove_service(hs_service_map, service); + hs_service_free(service); + + log_info(LD_CONFIG, "Removed ephemeral v3 hidden service: %s", + safe_str_client(address)); + return 0; + + err: + return -1; +} + +/* Using the ed25519 public key pk, find a service for that key and return the + * current encoded descriptor as a newly allocated string or NULL if not + * found. This is used by the control port subsystem. */ +char * +hs_service_lookup_current_desc(const ed25519_public_key_t *pk) +{ + const hs_service_t *service; + + tor_assert(pk); + + service = find_service(hs_service_map, pk); + if (service && service->desc_current) { + char *encoded_desc = NULL; + /* No matter what is the result (which should never be a failure), return + * the encoded variable, if success it will contain the right thing else + * it will be NULL. */ + hs_desc_encode_descriptor(service->desc_current->desc, + &service->desc_current->signing_kp, + &encoded_desc); + return encoded_desc; + } + + return NULL; +} + /* Return the number of service we have configured and usable. */ unsigned int hs_service_get_num_services(void) @@ -2950,7 +3133,9 @@ hs_service_intro_circ_has_closed(origin_circuit_t *circ) get_objects_from_ident(circ->hs_ident, &service, &ip, &desc); if (service == NULL) { - log_warn(LD_REND, "Unable to find any hidden service associated " + /* This is possible if the circuits are closed and the service is + * immediately deleted. */ + log_info(LD_REND, "Unable to find any hidden service associated " "identity key %s on intro circuit %u.", ed25519_fmt(&circ->hs_ident->identity_pk), TO_CIRCUIT(circ)->n_circ_id); @@ -3269,7 +3454,7 @@ hs_service_new(const or_options_t *options) * also takes care of wiping service keys from memory. It is safe to pass a * NULL pointer. */ void -hs_service_free(hs_service_t *service) +hs_service_free_(hs_service_t *service) { if (service == NULL) { return; diff --git a/src/or/hs_service.h b/src/or/hs_service.h index ed1053d850..d163eeef28 100644 --- a/src/or/hs_service.h +++ b/src/or/hs_service.h @@ -249,7 +249,8 @@ void hs_service_free_all(void); /* Service new/free functions. */ hs_service_t *hs_service_new(const or_options_t *options); -void hs_service_free(hs_service_t *service); +void hs_service_free_(hs_service_t *service); +#define hs_service_free(s) FREE_AND_NULL(hs_service_t, hs_service_free_, (s)) unsigned int hs_service_get_num_services(void); void hs_service_stage_services(const smartlist_t *service_list); @@ -271,15 +272,33 @@ int hs_service_receive_introduce2(origin_circuit_t *circ, void hs_service_intro_circ_has_closed(origin_circuit_t *circ); +char *hs_service_lookup_current_desc(const ed25519_public_key_t *pk); + +hs_service_add_ephemeral_status_t +hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports, + int max_streams_per_rdv_circuit, + int max_streams_close_circuit, char **address_out); +int hs_service_del_ephemeral(const char *address); + +/* Used outside of the HS subsystem by the control port command HSPOST. */ +void hs_service_upload_desc_to_dir(const char *encoded_desc, + const uint8_t version, + const ed25519_public_key_t *identity_pk, + const ed25519_public_key_t *blinded_pk, + const routerstatus_t *hsdir_rs); + #ifdef HS_SERVICE_PRIVATE #ifdef TOR_UNIT_TESTS - /* Useful getters for unit tests. */ STATIC unsigned int get_hs_service_map_size(void); STATIC int get_hs_service_staging_list_size(void); STATIC hs_service_ht *get_hs_service_map(void); STATIC hs_service_t *get_first_service(void); +STATIC hs_service_intro_point_t *service_intro_point_find_by_ident( + const hs_service_t *service, + const hs_ident_circuit_t *ident); +#endif /* Service accessors. */ STATIC hs_service_t *find_service(hs_service_ht *map, @@ -290,7 +309,10 @@ STATIC int register_service(hs_service_ht *map, hs_service_t *service); STATIC hs_service_intro_point_t *service_intro_point_new( const extend_info_t *ei, unsigned int is_legacy); -STATIC void service_intro_point_free(hs_service_intro_point_t *ip); +STATIC void service_intro_point_free_(hs_service_intro_point_t *ip); +#define service_intro_point_free(ip) \ + FREE_AND_NULL(hs_service_intro_point_t, \ + service_intro_point_free_, (ip)) STATIC void service_intro_point_add(digest256map_t *map, hs_service_intro_point_t *ip); STATIC void service_intro_point_remove(const hs_service_t *service, @@ -298,9 +320,6 @@ STATIC void service_intro_point_remove(const hs_service_t *service, STATIC hs_service_intro_point_t *service_intro_point_find( const hs_service_t *service, const ed25519_public_key_t *auth_key); -STATIC hs_service_intro_point_t *service_intro_point_find_by_ident( - const hs_service_t *service, - const hs_ident_circuit_t *ident); /* Service descriptor functions. */ STATIC hs_service_descriptor_t *service_descriptor_new(void); STATIC hs_service_descriptor_t *service_desc_find_by_intro( @@ -326,7 +345,10 @@ STATIC void run_upload_descriptor_event(time_t now); STATIC char * encode_desc_rev_counter_for_state(const hs_service_descriptor_t *desc); -STATIC void service_descriptor_free(hs_service_descriptor_t *desc); +STATIC void service_descriptor_free_(hs_service_descriptor_t *desc); +#define service_descriptor_free(d) \ + FREE_AND_NULL(hs_service_descriptor_t, \ + service_descriptor_free_, (d)) STATIC uint64_t check_state_line_for_service_rev_counter(const char *state_line, @@ -346,8 +368,6 @@ STATIC void service_desc_schedule_upload(hs_service_descriptor_t *desc, STATIC int service_desc_hsdirs_changed(const hs_service_t *service, const hs_service_descriptor_t *desc); -#endif /* defined(TOR_UNIT_TESTS) */ - #endif /* defined(HS_SERVICE_PRIVATE) */ #endif /* !defined(TOR_HS_SERVICE_H) */ diff --git a/src/or/include.am b/src/or/include.am index 7216aba9af..1c66cd2de3 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -51,6 +51,7 @@ LIBTOR_A_SOURCES = \ src/or/geoip.c \ src/or/entrynodes.c \ src/or/ext_orport.c \ + src/or/git_revision.c \ src/or/hibernate.c \ src/or/hs_cache.c \ src/or/hs_cell.c \ @@ -59,6 +60,7 @@ LIBTOR_A_SOURCES = \ src/or/hs_client.c \ src/or/hs_common.c \ src/or/hs_config.c \ + src/or/hs_control.c \ src/or/hs_descriptor.c \ src/or/hs_ident.c \ src/or/hs_intropoint.c \ @@ -78,6 +80,7 @@ LIBTOR_A_SOURCES = \ src/or/parsecommon.c \ src/or/periodic.c \ src/or/protover.c \ + src/or/protover_rust.c \ src/or/proto_cell.c \ src/or/proto_control0.c \ src/or/proto_ext_or.c \ @@ -104,6 +107,7 @@ LIBTOR_A_SOURCES = \ src/or/statefile.c \ src/or/status.c \ src/or/torcert.c \ + src/or/tor_api.c \ src/or/onion_ntor.c \ $(tor_platform_source) @@ -189,14 +193,16 @@ ORHEADERS = \ src/or/fp_pair.h \ src/or/geoip.h \ src/or/entrynodes.h \ + src/or/git_revision.h \ src/or/hibernate.h \ src/or/hs_cache.h \ src/or/hs_cell.h \ - src/or/hs_config.h \ src/or/hs_circuit.h \ src/or/hs_circuitmap.h \ src/or/hs_client.h \ src/or/hs_common.h \ + src/or/hs_config.h \ + src/or/hs_control.h \ src/or/hs_descriptor.h \ src/or/hs_ident.h \ src/or/hs_intropoint.h \ @@ -243,9 +249,13 @@ ORHEADERS = \ src/or/scheduler.h \ src/or/statefile.h \ src/or/status.h \ - src/or/torcert.h + src/or/torcert.h \ + src/or/tor_api_internal.h + +# This may someday want to be an installed file? +noinst_HEADERS += src/or/tor_api.h -noinst_HEADERS+= $(ORHEADERS) micro-revision.i +noinst_HEADERS += $(ORHEADERS) micro-revision.i micro-revision.i: FORCE $(AM_V_at)rm -f micro-revision.tmp; \ diff --git a/src/or/main.c b/src/or/main.c index c340e4128b..e449b95b91 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -60,7 +60,6 @@ #include "circuitlist.h" #include "circuituse.h" #include "command.h" -#include "compat_rust.h" #include "compress.h" #include "config.h" #include "confparse.h" @@ -106,6 +105,8 @@ #include "shared_random.h" #include "statefile.h" #include "status.h" +#include "tor_api.h" +#include "tor_api_internal.h" #include "util_process.h" #include "ext_orport.h" #ifdef USE_DMALLOC @@ -128,6 +129,12 @@ void evdns_shutdown(int); +#ifdef HAVE_RUST +// helper function defined in Rust to output a log message indicating if tor is +// running with Rust enabled. See src/rust/tor_util +char *rust_welcome_string(void); +#endif + /********* PROTOTYPES **********/ static void dumpmemusage(int severity); @@ -140,6 +147,8 @@ static void connection_start_reading_from_linked_conn(connection_t *conn); static int connection_should_read_from_linked_conn(connection_t *conn); static int run_main_loop_until_done(void); static void process_signal(int sig); +static void shutdown_did_not_work_callback(evutil_socket_t fd, short event, + void *arg) ATTR_NORETURN; /********* START VARIABLES **********/ int global_read_bucket; /**< Max number of bytes I can read this second. */ @@ -170,6 +179,12 @@ static uint64_t stats_n_bytes_written = 0; time_t time_of_process_start = 0; /** How many seconds have we been running? */ long stats_n_seconds_working = 0; +/** How many times have we returned from the main loop successfully? */ +static uint64_t stats_n_main_loop_successes = 0; +/** How many times have we received an error from the main loop? */ +static uint64_t stats_n_main_loop_errors = 0; +/** How many times have we returned from the main loop with no events. */ +static uint64_t stats_n_main_loop_idle = 0; /** How often will we honor SIGNEWNYM requests? */ #define MAX_SIGNEWNYM_RATE 10 @@ -192,6 +207,14 @@ static smartlist_t *active_linked_connection_lst = NULL; * <b>loop_once</b>. If so, there's no need to trigger a loopexit in order * to handle linked connections. */ static int called_loop_once = 0; +/** Flag: if true, it's time to shut down, so the main loop should exit as + * soon as possible. + */ +static int main_loop_should_exit = 0; +/** The return value that the main loop should yield when it exits, if + * main_loop_should_exit is true. + */ +static int main_loop_exit_value = 0; /** We set this to 1 when we've opened a circuit, so we can print a log * entry to inform the user that Tor is working. We set it to 0 when @@ -476,6 +499,57 @@ connection_is_reading(connection_t *conn) (conn->read_event && event_pending(conn->read_event, EV_READ, NULL)); } +/** Reset our main loop counters. */ +void +reset_main_loop_counters(void) +{ + stats_n_main_loop_successes = 0; + stats_n_main_loop_errors = 0; + stats_n_main_loop_idle = 0; +} + +/** Increment the main loop success counter. */ +static void +increment_main_loop_success_count(void) +{ + ++stats_n_main_loop_successes; +} + +/** Get the main loop success counter. */ +uint64_t +get_main_loop_success_count(void) +{ + return stats_n_main_loop_successes; +} + +/** Increment the main loop error counter. */ +static void +increment_main_loop_error_count(void) +{ + ++stats_n_main_loop_errors; +} + +/** Get the main loop error counter. */ +uint64_t +get_main_loop_error_count(void) +{ + return stats_n_main_loop_errors; +} + +/** Increment the main loop idle counter. */ +static void +increment_main_loop_idle_count(void) +{ + ++stats_n_main_loop_idle; +} + +/** Get the main loop idle counter. */ +uint64_t +get_main_loop_idle_count(void) +{ + return stats_n_main_loop_idle; +} + /** Check whether <b>conn</b> is correct in having (or not having) a * read/write event (passed in <b>ev</b>). On success, return 0. On failure, * log a warning and return -1. */ @@ -637,9 +711,10 @@ connection_should_read_from_linked_conn(connection_t *conn) /** If we called event_base_loop() and told it to never stop until it * runs out of events, now we've changed our mind: tell it we want it to - * finish. */ + * exit once the current round of callbacks is done, so that we can + * run external code, and then return to the main loop. */ void -tell_event_loop_to_finish(void) +tell_event_loop_to_run_external_code(void) { if (!called_loop_once) { struct timeval tv = { 0, 0 }; @@ -648,6 +723,54 @@ tell_event_loop_to_finish(void) } } +/** Failsafe measure that should never actually be necessary: If + * tor_shutdown_event_loop_and_exit() somehow doesn't successfully exit the + * event loop, then this callback will kill Tor with an assertion failure + * seconds later + */ +static void +shutdown_did_not_work_callback(evutil_socket_t fd, short event, void *arg) +{ + // LCOV_EXCL_START + (void) fd; + (void) event; + (void) arg; + tor_assert_unreached(); + // LCOV_EXCL_STOP +} + +/** + * After finishing the current callback (if any), shut down the main loop, + * clean up the process, and exit with <b>exitcode</b>. + */ +void +tor_shutdown_event_loop_and_exit(int exitcode) +{ + if (main_loop_should_exit) + return; /* Ignore multiple calls to this function. */ + + main_loop_should_exit = 1; + main_loop_exit_value = exitcode; + + /* Die with an assertion failure in ten seconds, if for some reason we don't + * exit normally. */ + /* XXXX We should consider this code if it's never used. */ + struct timeval ten_seconds = { 10, 0 }; + event_base_once(tor_libevent_get_base(), -1, EV_TIMEOUT, + shutdown_did_not_work_callback, NULL, + &ten_seconds); + + /* Unlike loopexit, loopbreak prevents other callbacks from running. */ + tor_event_base_loopbreak(tor_libevent_get_base()); +} + +/** Return true iff tor_shutdown_event_loop_and_exit() has been called. */ +int +tor_event_loop_shutdown_is_pending(void) +{ + return main_loop_should_exit; +} + /** Helper: Tell the main loop to begin reading bytes into <b>conn</b> from * its linked connection, if it is not doing so already. Called by * connection_start_reading and connection_start_writing as appropriate. */ @@ -663,7 +786,7 @@ connection_start_reading_from_linked_conn(connection_t *conn) /* make sure that the event_base_loop() function exits at * the end of its run through the current connections, so we can * activate read events for linked connections. */ - tell_event_loop_to_finish(); + tell_event_loop_to_run_external_code(); } else { tor_assert(smartlist_contains(active_linked_connection_lst, conn)); } @@ -1405,7 +1528,9 @@ run_scheduled_events(time_t now) /* Maybe enough time elapsed for us to reconsider a circuit. */ circuit_upgrade_circuits_from_guard_wait(); - if (options->UseBridges && !options->DisableNetwork) { + if (options->UseBridges && !net_is_disabled()) { + /* Note: this check uses net_is_disabled(), not should_delay_dir_fetches() + * -- the latter is only for fetching consensus-derived directory info. */ fetch_bridge_descriptors(options, now); } @@ -1511,7 +1636,7 @@ rotate_onion_key_callback(time_t now, const or_options_t *options) if (router_rebuild_descriptor(1)<0) { log_info(LD_CONFIG, "Couldn't rebuild router descriptor"); } - if (advertised_server_mode() && !options->DisableNetwork) + if (advertised_server_mode() && !net_is_disabled()) router_upload_dir_desc_to_dirservers(0); return ONION_KEY_CONSENSUS_CHECK_INTERVAL; } @@ -1554,8 +1679,7 @@ check_ed_keys_callback(time_t now, const or_options_t *options) if (new_signing_key < 0 || generate_ed_link_cert(options, now, new_signing_key > 0)) { log_err(LD_OR, "Unable to update Ed25519 keys! Exiting."); - tor_cleanup(); - exit(1); + tor_shutdown_event_loop_and_exit(1); } } return 30; @@ -1877,9 +2001,11 @@ check_descriptor_callback(time_t now, const or_options_t *options) * address has changed. */ #define CHECK_DESCRIPTOR_INTERVAL (60) + (void)options; + /* 2b. Once per minute, regenerate and upload the descriptor if the old * one is inaccurate. */ - if (!options->DisableNetwork) { + if (!net_is_disabled()) { check_descriptor_bandwidth_changed(now); check_descriptor_ipaddress_changed(now); mark_my_descriptor_dirty_if_too_old(now); @@ -1911,7 +2037,7 @@ check_for_reachability_bw_callback(time_t now, const or_options_t *options) * 20 minutes of our uptime. */ if (server_mode(options) && (have_completed_a_circuit() || !any_predicted_circuits(now)) && - !we_are_hibernating()) { + !net_is_disabled()) { if (stats_n_seconds_working < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) { consider_testing_reachability(1, dirport_reachability_count==0); if (++dirport_reachability_count > 5) @@ -2366,10 +2492,18 @@ do_hup(void) /* first, reload config variables, in case they've changed */ if (options->ReloadTorrcOnSIGHUP) { /* no need to provide argc/v, they've been cached in init_from_config */ - if (options_init_from_torrc(0, NULL) < 0) { + int init_rv = options_init_from_torrc(0, NULL); + if (init_rv < 0) { log_err(LD_CONFIG,"Reading config failed--see warnings above. " "For usage, try -h."); return -1; + } else if (BUG(init_rv > 0)) { + // LCOV_EXCL_START + /* This should be impossible: the only "return 1" cases in + * options_init_from_torrc are ones caused by command-line arguments; + * but they can't change while Tor is running. */ + return -1; + // LCOV_EXCL_STOP } options = get_options(); /* they have changed now */ /* Logs are only truncated the first time they are opened, but were @@ -2405,7 +2539,7 @@ do_hup(void) /* retry appropriate downloads */ router_reset_status_download_failures(); router_reset_descriptor_download_failures(); - if (!options->DisableNetwork) + if (!net_is_disabled()) update_networkstatus_downloads(time(NULL)); /* We'll retry routerstatus downloads in about 10 seconds; no need to @@ -2595,6 +2729,9 @@ do_main_loop(void) } #endif /* defined(HAVE_SYSTEMD) */ + main_loop_should_exit = 0; + main_loop_exit_value = 0; + return run_main_loop_until_done(); } @@ -2610,6 +2747,11 @@ run_main_loop_once(void) if (nt_service_is_stopping()) return 0; + if (main_loop_should_exit) + return 0; + + const or_options_t *options = get_options(); + #ifndef _WIN32 /* Make it easier to tell whether libevent failure is our fault or not. */ errno = 0; @@ -2619,7 +2761,14 @@ run_main_loop_once(void) * so that libevent knows to run their callbacks. */ SMARTLIST_FOREACH(active_linked_connection_lst, connection_t *, conn, event_active(conn->read_event, EV_READ, 1)); - called_loop_once = smartlist_len(active_linked_connection_lst) ? 1 : 0; + + if (options->MainloopStats) { + /* We always enforce that EVLOOP_ONCE is passed to event_base_loop() if we + * are collecting main loop statistics. */ + called_loop_once = 1; + } else { + called_loop_once = smartlist_len(active_linked_connection_lst) ? 1 : 0; + } /* Make sure we know (about) what time it is. */ update_approx_time(time(NULL)); @@ -2631,6 +2780,21 @@ run_main_loop_once(void) loop_result = event_base_loop(tor_libevent_get_base(), called_loop_once ? EVLOOP_ONCE : 0); + if (options->MainloopStats) { + /* Update our main loop counters. */ + if (loop_result == 0) { + // The call was succesful. + increment_main_loop_success_count(); + } else if (loop_result == -1) { + // The call was erroneous. + increment_main_loop_error_count(); + } else if (loop_result == 1) { + // The call didn't have any active or pending events + // to handle. + increment_main_loop_idle_count(); + } + } + /* Oh, the loop failed. That might be an error that we need to * catch, but more likely, it's just an interrupted poll() call or something, * and we should try again. */ @@ -2656,6 +2820,9 @@ run_main_loop_once(void) } } + if (main_loop_should_exit) + return 0; + /* And here is where we put callbacks that happen "every time the event loop * runs." They must be very fast, or else the whole Tor process will get * slowed down. @@ -2684,7 +2851,11 @@ run_main_loop_until_done(void) do { loop_result = run_main_loop_once(); } while (loop_result == 1); - return loop_result; + + if (main_loop_should_exit) + return main_loop_exit_value; + else + return loop_result; } /** Libevent callback: invoked when we get a signal. @@ -2708,14 +2879,13 @@ process_signal(int sig) { case SIGTERM: log_notice(LD_GENERAL,"Catching signal TERM, exiting cleanly."); - tor_cleanup(); - exit(0); + tor_shutdown_event_loop_and_exit(0); break; case SIGINT: if (!server_mode(get_options())) { /* do it now */ log_notice(LD_GENERAL,"Interrupt: exiting cleanly."); - tor_cleanup(); - exit(0); + tor_shutdown_event_loop_and_exit(0); + return; } #ifdef HAVE_SYSTEMD sd_notify(0, "STOPPING=1"); @@ -2744,8 +2914,8 @@ process_signal(int sig) #endif if (do_hup() < 0) { log_warn(LD_CONFIG,"Restart failed (config error?). Exiting."); - tor_cleanup(); - exit(1); + tor_shutdown_event_loop_and_exit(1); + return; } #ifdef HAVE_SYSTEMD sd_notify(0, "READY=1"); @@ -3019,7 +3189,8 @@ activate_signal(int signal_num) } } -/** Main entry point for the Tor command-line client. +/** Main entry point for the Tor command-line client. Return 0 on "success", + * negative on "failure", and positive on "success and exit". */ int tor_init(int argc, char *argv[]) @@ -3111,14 +3282,13 @@ tor_init(int argc, char *argv[]) "Expect more bugs than usual."); } - { - rust_str_t rust_str = rust_welcome_string(); - const char *s = rust_str_get(rust_str); - if (strlen(s) > 0) { - log_notice(LD_GENERAL, "%s", s); - } - rust_str_free(rust_str); +#ifdef HAVE_RUST + char *rust_str = rust_welcome_string(); + if (rust_str != NULL && strlen(rust_str) > 0) { + log_notice(LD_GENERAL, "%s", rust_str); } + tor_free(rust_str); +#endif /* defined(HAVE_RUST) */ if (network_init()<0) { log_err(LD_BUG,"Error initializing network; exiting."); @@ -3126,9 +3296,14 @@ tor_init(int argc, char *argv[]) } atexit(exit_function); - if (options_init_from_torrc(argc,argv) < 0) { + int init_rv = options_init_from_torrc(argc,argv); + if (init_rv < 0) { log_err(LD_CONFIG,"Reading config failed--see warnings above."); return -1; + } else if (init_rv > 0) { + // We succeeded, and should exit anyway -- probably the user just said + // "--version" or something like that. + return 1; } /* The options are now initialised */ @@ -3180,7 +3355,7 @@ try_locking(const or_options_t *options, int err_if_locked) if (lockfile) return 0; else { - char *fname = options_get_datadir_fname2_suffix(options, "lock",NULL,NULL); + char *fname = options_get_datadir_fname(options, "lock"); int already_locked = 0; tor_lockfile_t *lf = tor_lockfile_lock(fname, 0, &already_locked); tor_free(fname); @@ -3198,7 +3373,7 @@ try_locking(const or_options_t *options, int err_if_locked) r = try_locking(options, 0); if (r<0) { log_err(LD_GENERAL, "No, it's still there. Exiting."); - exit(1); + return -1; } return r; } @@ -3303,6 +3478,19 @@ tor_free_all(int postfork) } } +/** + * Remove the specified file, and log a warning if the operation fails for + * any reason other than the file not existing. + */ +void +tor_remove_file(const char *filename) +{ + if (tor_unlink(filename) != 0 && errno != ENOENT) { + log_warn(LD_FS, "Couldn't unlink %s: %s", + filename, strerror(errno)); + } +} + /** Do whatever cleanup is necessary before shutting Tor down. */ void tor_cleanup(void) @@ -3312,18 +3500,20 @@ tor_cleanup(void) time_t now = time(NULL); /* Remove our pid file. We don't care if there was an error when we * unlink, nothing we could do about it anyways. */ - if (options->PidFile) { - if (unlink(options->PidFile) != 0) { - log_warn(LD_FS, "Couldn't unlink pid file %s: %s", - options->PidFile, strerror(errno)); - } + tor_remove_file(options->PidFile); + /* Remove control port file */ + tor_remove_file(options->ControlPortWriteToFile); + /* Remove cookie authentication file */ + { + char *cookie_fname = get_controller_cookie_file_name(); + tor_remove_file(cookie_fname); + tor_free(cookie_fname); } - if (options->ControlPortWriteToFile) { - if (unlink(options->ControlPortWriteToFile) != 0) { - log_warn(LD_FS, "Couldn't unlink control port file %s: %s", - options->ControlPortWriteToFile, - strerror(errno)); - } + /* Remove Extended ORPort cookie authentication file */ + { + char *cookie_fname = get_ext_or_auth_cookie_file_name(); + tor_remove_file(cookie_fname); + tor_free(cookie_fname); } if (accounting_is_enabled(options)) accounting_record_bandwidth_usage(now, get_or_state()); @@ -3456,7 +3646,7 @@ sandbox_init_filter(void) int i; sandbox_cfg_allow_openat_filename(&cfg, - get_datadir_fname("cached-status")); + get_cachedir_fname("cached-status")); #define OPEN(name) \ sandbox_cfg_allow_open_filename(&cfg, tor_strdup(name)) @@ -3477,21 +3667,38 @@ sandbox_init_filter(void) OPEN_DATADIR2(name, name2 suffix); \ } while (0) +#define OPEN_KEY_DIRECTORY() \ + sandbox_cfg_allow_open_filename(&cfg, tor_strdup(options->KeyDirectory)) +#define OPEN_CACHEDIR(name) \ + sandbox_cfg_allow_open_filename(&cfg, get_cachedir_fname(name)) +#define OPEN_CACHEDIR_SUFFIX(name, suffix) do { \ + OPEN_CACHEDIR(name); \ + OPEN_CACHEDIR(name suffix); \ + } while (0) +#define OPEN_KEYDIR(name) \ + sandbox_cfg_allow_open_filename(&cfg, get_keydir_fname(name)) +#define OPEN_KEYDIR_SUFFIX(name, suffix) do { \ + OPEN_KEYDIR(name); \ + OPEN_KEYDIR(name suffix); \ + } while (0) + OPEN(options->DataDirectory); - OPEN_DATADIR("keys"); - OPEN_DATADIR_SUFFIX("cached-certs", ".tmp"); - OPEN_DATADIR_SUFFIX("cached-consensus", ".tmp"); - OPEN_DATADIR_SUFFIX("unverified-consensus", ".tmp"); - OPEN_DATADIR_SUFFIX("unverified-microdesc-consensus", ".tmp"); - OPEN_DATADIR_SUFFIX("cached-microdesc-consensus", ".tmp"); - OPEN_DATADIR_SUFFIX("cached-microdescs", ".tmp"); - OPEN_DATADIR_SUFFIX("cached-microdescs.new", ".tmp"); - OPEN_DATADIR_SUFFIX("cached-descriptors", ".tmp"); - OPEN_DATADIR_SUFFIX("cached-descriptors.new", ".tmp"); - OPEN_DATADIR("cached-descriptors.tmp.tmp"); - OPEN_DATADIR_SUFFIX("cached-extrainfo", ".tmp"); - OPEN_DATADIR_SUFFIX("cached-extrainfo.new", ".tmp"); - OPEN_DATADIR("cached-extrainfo.tmp.tmp"); + OPEN_KEY_DIRECTORY(); + + OPEN_CACHEDIR_SUFFIX("cached-certs", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-consensus", ".tmp"); + OPEN_CACHEDIR_SUFFIX("unverified-consensus", ".tmp"); + OPEN_CACHEDIR_SUFFIX("unverified-microdesc-consensus", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-microdesc-consensus", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-microdescs", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-microdescs.new", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-descriptors", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-descriptors.new", ".tmp"); + OPEN_CACHEDIR("cached-descriptors.tmp.tmp"); + OPEN_CACHEDIR_SUFFIX("cached-extrainfo", ".tmp"); + OPEN_CACHEDIR_SUFFIX("cached-extrainfo.new", ".tmp"); + OPEN_CACHEDIR("cached-extrainfo.tmp.tmp"); + OPEN_DATADIR_SUFFIX("state", ".tmp"); OPEN_DATADIR_SUFFIX("sr-state", ".tmp"); OPEN_DATADIR_SUFFIX("unparseable-desc", ".tmp"); @@ -3521,6 +3728,10 @@ sandbox_init_filter(void) } } + SMARTLIST_FOREACH(options->FilesOpenedByIncludes, char *, f, { + OPEN(f); + }); + #define RENAME_SUFFIX(name, suffix) \ sandbox_cfg_allow_rename(&cfg, \ get_datadir_fname(name suffix), \ @@ -3531,20 +3742,31 @@ sandbox_init_filter(void) get_datadir_fname2(prefix, name suffix), \ get_datadir_fname2(prefix, name)) - RENAME_SUFFIX("cached-certs", ".tmp"); - RENAME_SUFFIX("cached-consensus", ".tmp"); - RENAME_SUFFIX("unverified-consensus", ".tmp"); - RENAME_SUFFIX("unverified-microdesc-consensus", ".tmp"); - RENAME_SUFFIX("cached-microdesc-consensus", ".tmp"); - RENAME_SUFFIX("cached-microdescs", ".tmp"); - RENAME_SUFFIX("cached-microdescs", ".new"); - RENAME_SUFFIX("cached-microdescs.new", ".tmp"); - RENAME_SUFFIX("cached-descriptors", ".tmp"); - RENAME_SUFFIX("cached-descriptors", ".new"); - RENAME_SUFFIX("cached-descriptors.new", ".tmp"); - RENAME_SUFFIX("cached-extrainfo", ".tmp"); - RENAME_SUFFIX("cached-extrainfo", ".new"); - RENAME_SUFFIX("cached-extrainfo.new", ".tmp"); +#define RENAME_CACHEDIR_SUFFIX(name, suffix) \ + sandbox_cfg_allow_rename(&cfg, \ + get_cachedir_fname(name suffix), \ + get_cachedir_fname(name)) + +#define RENAME_KEYDIR_SUFFIX(name, suffix) \ + sandbox_cfg_allow_rename(&cfg, \ + get_keydir_fname(name suffix), \ + get_keydir_fname(name)) + + RENAME_CACHEDIR_SUFFIX("cached-certs", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-consensus", ".tmp"); + RENAME_CACHEDIR_SUFFIX("unverified-consensus", ".tmp"); + RENAME_CACHEDIR_SUFFIX("unverified-microdesc-consensus", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-microdesc-consensus", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-microdescs", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-microdescs", ".new"); + RENAME_CACHEDIR_SUFFIX("cached-microdescs.new", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-descriptors", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-descriptors", ".new"); + RENAME_CACHEDIR_SUFFIX("cached-descriptors.new", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-extrainfo", ".tmp"); + RENAME_CACHEDIR_SUFFIX("cached-extrainfo", ".new"); + RENAME_CACHEDIR_SUFFIX("cached-extrainfo.new", ".tmp"); + RENAME_SUFFIX("state", ".tmp"); RENAME_SUFFIX("sr-state", ".tmp"); RENAME_SUFFIX("unparseable-desc", ".tmp"); @@ -3556,14 +3778,21 @@ sandbox_init_filter(void) #define STAT_DATADIR(name) \ sandbox_cfg_allow_stat_filename(&cfg, get_datadir_fname(name)) +#define STAT_CACHEDIR(name) \ + sandbox_cfg_allow_stat_filename(&cfg, get_cachedir_fname(name)) + #define STAT_DATADIR2(name, name2) \ sandbox_cfg_allow_stat_filename(&cfg, get_datadir_fname2((name), (name2))) +#define STAT_KEY_DIRECTORY() \ + sandbox_cfg_allow_stat_filename(&cfg, tor_strdup(options->KeyDirectory)) + STAT_DATADIR(NULL); STAT_DATADIR("lock"); STAT_DATADIR("state"); STAT_DATADIR("router-stability"); - STAT_DATADIR("cached-extrainfo.new"); + + STAT_CACHEDIR("cached-extrainfo.new"); { smartlist_t *files = smartlist_new(); @@ -3628,22 +3857,20 @@ sandbox_init_filter(void) // orport if (server_mode(get_options())) { - OPEN_DATADIR2_SUFFIX("keys", "secret_id_key", ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "secret_onion_key", ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "secret_onion_key_ntor", ".tmp"); - OPEN_DATADIR2("keys", "secret_id_key.old"); - OPEN_DATADIR2("keys", "secret_onion_key.old"); - OPEN_DATADIR2("keys", "secret_onion_key_ntor.old"); - - OPEN_DATADIR2_SUFFIX("keys", "ed25519_master_id_secret_key", ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "ed25519_master_id_secret_key_encrypted", - ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "ed25519_master_id_public_key", ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_secret_key", ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_secret_key_encrypted", - ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_public_key", ".tmp"); - OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_cert", ".tmp"); + OPEN_KEYDIR_SUFFIX("secret_id_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("secret_onion_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("secret_onion_key_ntor", ".tmp"); + OPEN_KEYDIR("secret_id_key.old"); + OPEN_KEYDIR("secret_onion_key.old"); + OPEN_KEYDIR("secret_onion_key_ntor.old"); + + OPEN_KEYDIR_SUFFIX("ed25519_master_id_secret_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_master_id_secret_key_encrypted", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_master_id_public_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_signing_secret_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_signing_secret_key_encrypted", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_signing_public_key", ".tmp"); + OPEN_KEYDIR_SUFFIX("ed25519_signing_cert", ".tmp"); OPEN_DATADIR2_SUFFIX("stats", "bridge-stats", ".tmp"); OPEN_DATADIR2_SUFFIX("stats", "dirreq-stats", ".tmp"); @@ -3662,11 +3889,13 @@ sandbox_init_filter(void) OPEN("/etc/resolv.conf"); RENAME_SUFFIX("fingerprint", ".tmp"); - RENAME_SUFFIX2("keys", "secret_onion_key_ntor", ".tmp"); - RENAME_SUFFIX2("keys", "secret_id_key", ".tmp"); - RENAME_SUFFIX2("keys", "secret_id_key.old", ".tmp"); - RENAME_SUFFIX2("keys", "secret_onion_key", ".tmp"); - RENAME_SUFFIX2("keys", "secret_onion_key.old", ".tmp"); + RENAME_KEYDIR_SUFFIX("secret_onion_key_ntor", ".tmp"); + + RENAME_KEYDIR_SUFFIX("secret_id_key", ".tmp"); + RENAME_KEYDIR_SUFFIX("secret_id_key.old", ".tmp"); + RENAME_KEYDIR_SUFFIX("secret_onion_key", ".tmp"); + RENAME_KEYDIR_SUFFIX("secret_onion_key.old", ".tmp"); + RENAME_SUFFIX2("stats", "bridge-stats", ".tmp"); RENAME_SUFFIX2("stats", "dirreq-stats", ".tmp"); RENAME_SUFFIX2("stats", "entry-stats", ".tmp"); @@ -3677,20 +3906,20 @@ sandbox_init_filter(void) RENAME_SUFFIX("hashed-fingerprint", ".tmp"); RENAME_SUFFIX("router-stability", ".tmp"); - RENAME_SUFFIX2("keys", "ed25519_master_id_secret_key", ".tmp"); - RENAME_SUFFIX2("keys", "ed25519_master_id_secret_key_encrypted", ".tmp"); - RENAME_SUFFIX2("keys", "ed25519_master_id_public_key", ".tmp"); - RENAME_SUFFIX2("keys", "ed25519_signing_secret_key", ".tmp"); - RENAME_SUFFIX2("keys", "ed25519_signing_cert", ".tmp"); + RENAME_KEYDIR_SUFFIX("ed25519_master_id_secret_key", ".tmp"); + RENAME_KEYDIR_SUFFIX("ed25519_master_id_secret_key_encrypted", ".tmp"); + RENAME_KEYDIR_SUFFIX("ed25519_master_id_public_key", ".tmp"); + RENAME_KEYDIR_SUFFIX("ed25519_signing_secret_key", ".tmp"); + RENAME_KEYDIR_SUFFIX("ed25519_signing_cert", ".tmp"); sandbox_cfg_allow_rename(&cfg, - get_datadir_fname2("keys", "secret_onion_key"), - get_datadir_fname2("keys", "secret_onion_key.old")); + get_keydir_fname("secret_onion_key"), + get_keydir_fname("secret_onion_key.old")); sandbox_cfg_allow_rename(&cfg, - get_datadir_fname2("keys", "secret_onion_key_ntor"), - get_datadir_fname2("keys", "secret_onion_key_ntor.old")); + get_keydir_fname("secret_onion_key_ntor"), + get_keydir_fname("secret_onion_key_ntor.old")); - STAT_DATADIR("keys"); + STAT_KEY_DIRECTORY(); OPEN_DATADIR("stats"); STAT_DATADIR("stats"); STAT_DATADIR2("stats", "dirreq-stats"); @@ -3703,14 +3932,16 @@ sandbox_init_filter(void) return cfg; } -/** Main entry point for the Tor process. Called from main(). */ -/* This function is distinct from main() only so we can link main.c into - * the unittest binary without conflicting with the unittests' main. */ +/* Main entry point for the Tor process. Called from tor_main(), and by + * anybody embedding Tor. */ int -tor_main(int argc, char *argv[]) +tor_run_main(const tor_main_configuration_t *tor_cfg) { int result = 0; + int argc = tor_cfg->argc; + char **argv = tor_cfg->argv; + #ifdef _WIN32 #ifndef HeapEnableTerminationOnCorruption #define HeapEnableTerminationOnCorruption 1 @@ -3754,8 +3985,13 @@ tor_main(int argc, char *argv[]) if (done) return result; } #endif /* defined(NT_SERVICE) */ - if (tor_init(argc, argv)<0) - return -1; + { + int init_rv = tor_init(argc, argv); + if (init_rv < 0) + return -1; + else if (init_rv > 0) + return 0; + } if (get_options()->Sandbox && get_options()->command == CMD_RUN_TOR) { sandbox_cfg_t* cfg = sandbox_init_filter(); diff --git a/src/or/main.h b/src/or/main.h index 132ab12bbb..d8593b57d9 100644 --- a/src/or/main.h +++ b/src/or/main.h @@ -45,7 +45,9 @@ int connection_is_writing(connection_t *conn); MOCK_DECL(void,connection_stop_writing,(connection_t *conn)); MOCK_DECL(void,connection_start_writing,(connection_t *conn)); -void tell_event_loop_to_finish(void); +void tell_event_loop_to_run_external_code(void); +void tor_shutdown_event_loop_and_exit(int exitcode); +int tor_event_loop_shutdown_is_pending(void); void connection_stop_reading_from_linked_conn(connection_t *conn); @@ -71,14 +73,19 @@ int try_locking(const or_options_t *options, int err_if_locked); int have_lockfile(void); void release_lockfile(void); +void tor_remove_file(const char *filename); + void tor_cleanup(void); void tor_free_all(int postfork); -int tor_main(int argc, char *argv[]); - int do_main_loop(void); int tor_init(int argc, char **argv); +void reset_main_loop_counters(void); +uint64_t get_main_loop_success_count(void); +uint64_t get_main_loop_error_count(void); +uint64_t get_main_loop_idle_count(void); + extern time_t time_of_process_start; extern long stats_n_seconds_working; extern int quiet_level; diff --git a/src/or/microdesc.c b/src/or/microdesc.c index fe327c6c82..d8a4660af1 100644 --- a/src/or/microdesc.c +++ b/src/or/microdesc.c @@ -238,8 +238,8 @@ get_microdesc_cache_noload(void) if (PREDICT_UNLIKELY(the_microdesc_cache==NULL)) { microdesc_cache_t *cache = tor_malloc_zero(sizeof(*cache)); HT_INIT(microdesc_map, &cache->map); - cache->cache_fname = get_datadir_fname("cached-microdescs"); - cache->journal_fname = get_datadir_fname("cached-microdescs.new"); + cache->cache_fname = get_cachedir_fname("cached-microdescs"); + cache->journal_fname = get_cachedir_fname("cached-microdescs.new"); the_microdesc_cache = cache; } return the_microdesc_cache; diff --git a/src/or/microdesc.h b/src/or/microdesc.h index 1be12156a4..83a90bd8ff 100644 --- a/src/or/microdesc.h +++ b/src/or/microdesc.h @@ -38,8 +38,10 @@ smartlist_t *microdesc_list_missing_digest256(networkstatus_t *ns, digest256map_t *skip); void microdesc_free_(microdesc_t *md, const char *fname, int line); -#define microdesc_free(md) \ - microdesc_free_((md), __FILE__, __LINE__) +#define microdesc_free(md) do { \ + microdesc_free_((md), __FILE__, __LINE__); \ + (md) = NULL; \ + } while (0) void microdesc_free_all(void); void update_microdesc_downloads(time_t now); diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index e0a3e4cdc6..deef62bd40 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -52,6 +52,7 @@ #include "dirserv.h" #include "dirvote.h" #include "entrynodes.h" +#include "hibernate.h" #include "main.h" #include "microdesc.h" #include "networkstatus.h" @@ -196,7 +197,7 @@ networkstatus_read_cached_consensus_impl(int flav, tor_snprintf(buf, sizeof(buf), "%s-%s-consensus", prefix, flavorname); } - char *filename = get_datadir_fname(buf); + char *filename = get_cachedir_fname(buf); char *result = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL); tor_free(filename); return result; @@ -254,7 +255,7 @@ router_reload_consensus_networkstatus(void) /** Free all storage held by the vote_routerstatus object <b>rs</b>. */ void -vote_routerstatus_free(vote_routerstatus_t *rs) +vote_routerstatus_free_(vote_routerstatus_t *rs) { vote_microdesc_hash_t *h, *next; if (!rs) @@ -272,7 +273,7 @@ vote_routerstatus_free(vote_routerstatus_t *rs) /** Free all storage held by the routerstatus object <b>rs</b>. */ void -routerstatus_free(routerstatus_t *rs) +routerstatus_free_(routerstatus_t *rs) { if (!rs) return; @@ -282,7 +283,7 @@ routerstatus_free(routerstatus_t *rs) /** Free all storage held in <b>sig</b> */ void -document_signature_free(document_signature_t *sig) +document_signature_free_(document_signature_t *sig) { tor_free(sig->signature); tor_free(sig); @@ -300,7 +301,7 @@ document_signature_dup(const document_signature_t *sig) /** Free all storage held in <b>ns</b>. */ void -networkstatus_vote_free(networkstatus_t *ns) +networkstatus_vote_free_(networkstatus_t *ns) { if (!ns) return; @@ -1208,6 +1209,14 @@ should_delay_dir_fetches(const or_options_t *options, const char **msg_out) return 1; } + if (we_are_hibernating()) { + if (msg_out) { + *msg_out = "We are hibernating or shutting down."; + } + log_info(LD_DIR, "Delaying dir fetches (Hibernating or shutting down)"); + return 1; + } + if (options->UseBridges) { /* If we know that none of our bridges can possibly work, avoid fetching * directory documents. But if some of them might work, try again. */ @@ -1500,6 +1509,32 @@ networkstatus_consensus_is_already_downloading(const char *resource) return answer; } +/* Does the current, reasonably live consensus have IPv6 addresses? + * Returns 1 if there is a reasonably live consensus and its consensus method + * includes IPv6 addresses in the consensus. + * Otherwise, if there is no consensus, or the method does not include IPv6 + * addresses, returns 0. */ +int +networkstatus_consensus_has_ipv6(const or_options_t* options) +{ + const networkstatus_t *cons = networkstatus_get_reasonably_live_consensus( + approx_time(), + usable_consensus_flavor()); + + /* If we have no consensus, we have no IPv6 in it */ + if (!cons) { + return 0; + } + + /* Different flavours of consensus gained IPv6 at different times */ + if (we_use_microdescriptors_for_circuits(options)) { + return + cons->consensus_method >= MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS; + } else { + return cons->consensus_method >= MIN_METHOD_FOR_A_LINES; + } +} + /** Given two router status entries for the same router identity, return 1 if * if the contents have changed between them. Otherwise, return 0. */ static int @@ -1673,7 +1708,7 @@ handle_missing_protocol_warning_impl(const networkstatus_t *c, } tor_free(protocol_warning); if (should_exit) - exit(1); + exit(1); // XXXX bad exit: should return from main. } /** Called when we have received a networkstatus <b>c</b>. If there are @@ -1722,7 +1757,7 @@ networkstatus_set_current_consensus(const char *consensus, { networkstatus_t *c=NULL; int r, result = -1; - time_t now = time(NULL); + time_t now = approx_time(); const or_options_t *options = get_options(); char *unverified_fname = NULL, *consensus_fname = NULL; int flav = networkstatus_parse_flavor_name(flavor); @@ -1787,15 +1822,15 @@ networkstatus_set_current_consensus(const char *consensus, } if (!strcmp(flavor, "ns")) { - consensus_fname = get_datadir_fname("cached-consensus"); - unverified_fname = get_datadir_fname("unverified-consensus"); + consensus_fname = get_cachedir_fname("cached-consensus"); + unverified_fname = get_cachedir_fname("unverified-consensus"); if (current_ns_consensus) { current_digests = ¤t_ns_consensus->digests; current_valid_after = current_ns_consensus->valid_after; } } else if (!strcmp(flavor, "microdesc")) { - consensus_fname = get_datadir_fname("cached-microdesc-consensus"); - unverified_fname = get_datadir_fname("unverified-microdesc-consensus"); + consensus_fname = get_cachedir_fname("cached-microdesc-consensus"); + unverified_fname = get_cachedir_fname("unverified-microdesc-consensus"); if (current_md_consensus) { current_digests = ¤t_md_consensus->digests; current_valid_after = current_md_consensus->valid_after; @@ -1804,9 +1839,9 @@ networkstatus_set_current_consensus(const char *consensus, cached_dir_t *cur; char buf[128]; tor_snprintf(buf, sizeof(buf), "cached-%s-consensus", flavor); - consensus_fname = get_datadir_fname(buf); + consensus_fname = get_cachedir_fname(buf); tor_snprintf(buf, sizeof(buf), "unverified-%s-consensus", flavor); - unverified_fname = get_datadir_fname(buf); + unverified_fname = get_cachedir_fname(buf); cur = dirserv_get_consensus(flavor); if (cur) { current_digests = &cur->digests; @@ -2202,7 +2237,9 @@ signed_descs_update_status_from_consensus_networkstatus(smartlist_t *descs) char * networkstatus_getinfo_helper_single(const routerstatus_t *rs) { - return routerstatus_format_entry(rs, NULL, NULL, NS_CONTROL_PORT, NULL); + return routerstatus_format_entry(rs, NULL, NULL, NS_CONTROL_PORT, + ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD, + NULL); } /** Alloc and return a string describing routerstatuses for the most @@ -2215,13 +2252,13 @@ networkstatus_getinfo_helper_single(const routerstatus_t *rs) char * networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now) { - time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH; + const time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH; char *answer; routerlist_t *rl = router_get_routerlist(); smartlist_t *statuses; - uint8_t purpose = router_purpose_from_string(purpose_string); + const uint8_t purpose = router_purpose_from_string(purpose_string); routerstatus_t rs; - int bridge_auth = authdir_mode_bridge(get_options()); + const int bridge_auth = authdir_mode_bridge(get_options()); if (purpose == ROUTER_PURPOSE_UNKNOWN) { log_info(LD_DIR, "Unrecognized purpose '%s' when listing router statuses.", @@ -2238,6 +2275,7 @@ networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now) continue; if (ri->purpose != purpose) continue; + /* TODO: modifying the running flag in a getinfo is a bad idea */ if (bridge_auth && ri->purpose == ROUTER_PURPOSE_BRIDGE) dirserv_set_router_is_running(ri, now); /* then generate and write out status lines for each of them */ @@ -2256,7 +2294,6 @@ void networkstatus_dump_bridge_status_to_file(time_t now) { char *status = networkstatus_getinfo_by_purpose("bridge", now); - const or_options_t *options = get_options(); char *fname = NULL; char *thresholds = NULL; char *published_thresholds_and_status = NULL; @@ -2278,8 +2315,7 @@ networkstatus_dump_bridge_status_to_file(time_t now) "published %s\nflag-thresholds %s\n%s%s", published, thresholds, fingerprint_line ? fingerprint_line : "", status); - tor_asprintf(&fname, "%s"PATH_SEPARATOR"networkstatus-bridges", - options->DataDirectory); + fname = get_datadir_fname("networkstatus-bridges"); write_str_to_file(fname,published_thresholds_and_status,0); tor_free(thresholds); tor_free(published_thresholds_and_status); diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h index 39a0f753d8..1851a55e82 100644 --- a/src/or/networkstatus.h +++ b/src/or/networkstatus.h @@ -18,8 +18,12 @@ void networkstatus_reset_warnings(void); void networkstatus_reset_download_failures(void); char *networkstatus_read_cached_consensus(const char *flavorname); int router_reload_consensus_networkstatus(void); -void routerstatus_free(routerstatus_t *rs); -void networkstatus_vote_free(networkstatus_t *ns); +void routerstatus_free_(routerstatus_t *rs); +#define routerstatus_free(rs) \ + FREE_AND_NULL(routerstatus_t, routerstatus_free_, (rs)) +void networkstatus_vote_free_(networkstatus_t *ns); +#define networkstatus_vote_free(ns) \ + FREE_AND_NULL(networkstatus_t, networkstatus_vote_free_, (ns)) networkstatus_voter_info_t *networkstatus_get_voter_by_id( networkstatus_t *vote, const char *identity); @@ -89,6 +93,7 @@ int networkstatus_consensus_can_use_multiple_directories( MOCK_DECL(int, networkstatus_consensus_can_use_extra_fallbacks,( const or_options_t *options)); int networkstatus_consensus_is_already_downloading(const char *resource); +int networkstatus_consensus_has_ipv6(const or_options_t* options); #define NSSET_FROM_CACHE 1 #define NSSET_WAS_WAITING_FOR_CERTS 2 @@ -124,12 +129,16 @@ int32_t networkstatus_get_bw_weight(networkstatus_t *ns, const char *weight, int32_t default_val); const char *networkstatus_get_flavor_name(consensus_flavor_t flav); int networkstatus_parse_flavor_name(const char *flavname); -void document_signature_free(document_signature_t *sig); +void document_signature_free_(document_signature_t *sig); +#define document_signature_free(sig) \ + FREE_AND_NULL(document_signature_t, document_signature_free_, (sig)) document_signature_t *document_signature_dup(const document_signature_t *sig); void networkstatus_free_all(void); int networkstatus_get_weight_scale_param(networkstatus_t *ns); -void vote_routerstatus_free(vote_routerstatus_t *rs); +void vote_routerstatus_free_(vote_routerstatus_t *rs); +#define vote_routerstatus_free(rs) \ + FREE_AND_NULL(vote_routerstatus_t, vote_routerstatus_free_, (rs)) #ifdef NETWORKSTATUS_PRIVATE #ifdef TOR_UNIT_TESTS diff --git a/src/or/nodelist.c b/src/or/nodelist.c index f2e979be8b..e6eaefb216 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -65,7 +65,9 @@ #include <string.h> static void nodelist_drop_node(node_t *node, int remove_from_ht); -static void node_free(node_t *node); +#define node_free(val) \ + FREE_AND_NULL(node_t, node_free_, (val)) +static void node_free_(node_t *node); /** count_usable_descriptors counts descriptors with these flag(s) */ @@ -656,7 +658,7 @@ nodelist_find_nodes_with_microdesc(const microdesc_t *md) /** Release storage held by <b>node</b> */ static void -node_free(node_t *node) +node_free_(node_t *node) { if (!node) return; @@ -951,23 +953,29 @@ node_ed25519_id_matches(const node_t *node, const ed25519_public_key_t *id) } /** Return true iff <b>node</b> supports authenticating itself - * by ed25519 ID during the link handshake in a way that we can understand - * when we probe it. */ + * by ed25519 ID during the link handshake. If <b>compatible_with_us</b>, + * it needs to be using a link authentication method that we understand. + * If not, any plausible link authentication method will do. */ int -node_supports_ed25519_link_authentication(const node_t *node) +node_supports_ed25519_link_authentication(const node_t *node, + int compatible_with_us) { - /* XXXX Oh hm. What if some day in the future there are link handshake - * versions that aren't 3 but which are ed25519 */ if (! node_get_ed25519_id(node)) return 0; if (node->ri) { const char *protos = node->ri->protocol_list; if (protos == NULL) return 0; - return protocol_list_supports_protocol(protos, PRT_LINKAUTH, 3); + if (compatible_with_us) + return protocol_list_supports_protocol(protos, PRT_LINKAUTH, 3); + else + return protocol_list_supports_protocol_or_later(protos, PRT_LINKAUTH, 3); } if (node->rs) { - return node->rs->supports_ed25519_link_handshake; + if (compatible_with_us) + return node->rs->supports_ed25519_link_handshake_compat; + else + return node->rs->supports_ed25519_link_handshake_any; } tor_assert_nonfatal_unreached_once(); return 0; @@ -1633,6 +1641,18 @@ node_has_curve25519_onion_key(const node_t *node) return 0; } +/** Return the curve25519 key of <b>node</b>, or NULL if none. */ +const curve25519_public_key_t * +node_get_curve25519_onion_key(const node_t *node) +{ + if (node->ri) + return node->ri->onion_curve25519_pkey; + else if (node->md) + return node->md->onion_curve25519_pkey; + else + return NULL; +} + /** Refresh the country code of <b>ri</b>. This function MUST be called on * each router when the GeoIP database is reloaded, and on all new routers. */ void diff --git a/src/or/nodelist.h b/src/or/nodelist.h index 754990ac8d..e879b4e8ff 100644 --- a/src/or/nodelist.h +++ b/src/or/nodelist.h @@ -65,7 +65,8 @@ const smartlist_t *node_get_declared_family(const node_t *node); const ed25519_public_key_t *node_get_ed25519_id(const node_t *node); int node_ed25519_id_matches(const node_t *node, const ed25519_public_key_t *id); -int node_supports_ed25519_link_authentication(const node_t *node); +int node_supports_ed25519_link_authentication(const node_t *node, + int compatible_with_us); int node_supports_v3_hsdir(const node_t *node); int node_supports_ed25519_hs_intro(const node_t *node); int node_supports_v3_rendezvous_point(const node_t *node); @@ -85,6 +86,8 @@ int node_get_prim_dirport(const node_t *node, tor_addr_port_t *ap_out); void node_get_pref_dirport(const node_t *node, tor_addr_port_t *ap_out); void node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out); int node_has_curve25519_onion_key(const node_t *node); +const curve25519_public_key_t *node_get_curve25519_onion_key( + const node_t *node); MOCK_DECL(smartlist_t *, nodelist_get_list, (void)); diff --git a/src/or/ntmain.c b/src/or/ntmain.c index 508e5844eb..ebbe0018bd 100644 --- a/src/or/ntmain.c +++ b/src/or/ntmain.c @@ -195,7 +195,7 @@ nt_service_loadlibrary(void) return; err: printf("Unable to load library support for NT services: exiting.\n"); - exit(1); + exit(1); // exit ok: ntmain can't read libraries } /** If we're compiled to run as an NT service, and the service wants to @@ -318,7 +318,7 @@ nt_service_main(void) printf("Service error %d : %s\n", (int) result, errmsg); tor_free(errmsg); if (result == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) { - if (tor_init(backup_argc, backup_argv) < 0) + if (tor_init(backup_argc, backup_argv)) return; switch (get_options()->command) { case CMD_RUN_TOR: diff --git a/src/or/onion.c b/src/or/onion.c index 7e1e89df1b..bd80c2f503 100644 --- a/src/or/onion.c +++ b/src/or/onion.c @@ -423,7 +423,7 @@ server_onion_keys_new(void) /** Release all storage held in <b>keys</b>. */ void -server_onion_keys_free(server_onion_keys_t *keys) +server_onion_keys_free_(server_onion_keys_t *keys) { if (! keys) return; diff --git a/src/or/onion.h b/src/or/onion.h index 95544dfac1..3b738debeb 100644 --- a/src/or/onion.h +++ b/src/or/onion.h @@ -31,7 +31,9 @@ typedef struct server_onion_keys_t { #define MAX_ONIONSKIN_REPLY_LEN 255 server_onion_keys_t *server_onion_keys_new(void); -void server_onion_keys_free(server_onion_keys_t *keys); +void server_onion_keys_free_(server_onion_keys_t *keys); +#define server_onion_keys_free(keys) \ + FREE_AND_NULL(server_onion_keys_t, server_onion_keys_free_, (keys)) void onion_handshake_state_release(onion_handshake_state_t *state); diff --git a/src/or/onion_fast.c b/src/or/onion_fast.c index 146943a273..56c954829e 100644 --- a/src/or/onion_fast.c +++ b/src/or/onion_fast.c @@ -32,7 +32,7 @@ /** Release all state held in <b>victim</b>. */ void -fast_handshake_state_free(fast_handshake_state_t *victim) +fast_handshake_state_free_(fast_handshake_state_t *victim) { if (! victim) return; diff --git a/src/or/onion_fast.h b/src/or/onion_fast.h index 3a5aefea3f..c56712e2c2 100644 --- a/src/or/onion_fast.h +++ b/src/or/onion_fast.h @@ -19,7 +19,9 @@ typedef struct fast_handshake_state_t { uint8_t state[DIGEST_LEN]; } fast_handshake_state_t; -void fast_handshake_state_free(fast_handshake_state_t *victim); +void fast_handshake_state_free_(fast_handshake_state_t *victim); +#define fast_handshake_state_free(st) \ + FREE_AND_NULL(fast_handshake_state_t, fast_handshake_state_free_, (st)) int fast_onionskin_create(fast_handshake_state_t **handshake_state_out, uint8_t *handshake_out); diff --git a/src/or/onion_ntor.c b/src/or/onion_ntor.c index 902260b54b..b167cb61fb 100644 --- a/src/or/onion_ntor.c +++ b/src/or/onion_ntor.c @@ -28,7 +28,7 @@ /** Free storage held in an ntor handshake state. */ void -ntor_handshake_state_free(ntor_handshake_state_t *state) +ntor_handshake_state_free_(ntor_handshake_state_t *state) { if (!state) return; diff --git a/src/or/onion_ntor.h b/src/or/onion_ntor.h index 02dea2dfc1..f7c962b7d0 100644 --- a/src/or/onion_ntor.h +++ b/src/or/onion_ntor.h @@ -17,7 +17,9 @@ typedef struct ntor_handshake_state_t ntor_handshake_state_t; /** Length of an ntor reply, as sent from server to client. */ #define NTOR_REPLY_LEN 64 -void ntor_handshake_state_free(ntor_handshake_state_t *state); +void ntor_handshake_state_free_(ntor_handshake_state_t *state); +#define ntor_handshake_state_free(state) \ + FREE_AND_NULL(ntor_handshake_state_t, ntor_handshake_state_free_, (state)) int onion_skin_ntor_create(const uint8_t *router_id, const curve25519_public_key_t *router_key, diff --git a/src/or/or.h b/src/or/or.h index 03e20b80de..2617d2d87d 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1166,8 +1166,8 @@ typedef struct packed_cell_t { /** Next cell queued on this circuit. */ TOR_SIMPLEQ_ENTRY(packed_cell_t) next; char body[CELL_MAX_NETWORK_SIZE]; /**< Cell as packed for network. */ - uint32_t inserted_time; /**< Time (in milliseconds since epoch, with high - * bits truncated) when this cell was inserted. */ + uint32_t inserted_timestamp; /**< Time (in timestamp units) when this cell + * was inserted */ } packed_cell_t; /** A queue of cells on a circuit, waiting to be added to the @@ -1182,7 +1182,8 @@ typedef struct cell_queue_t { typedef struct destroy_cell_t { TOR_SIMPLEQ_ENTRY(destroy_cell_t) next; circid_t circid; - uint32_t inserted_time; /** Timestamp when this was queued. */ + uint32_t inserted_timestamp; /**< Time (in timestamp units) when this cell + * was inserted */ uint8_t reason; } destroy_cell_t; @@ -2249,18 +2250,18 @@ typedef struct { * uploaded it. */ #define ROUTER_PURPOSE_GENERAL 0 /** Tor should avoid using this router for circuit-building: we got it - * from a crontroller. If the controller wants to use it, it'll have to + * from a controller. If the controller wants to use it, it'll have to * ask for it by identity. */ #define ROUTER_PURPOSE_CONTROLLER 1 /** Tor should use this router only for bridge positions in circuits: we got * it via a directory request from the bridge itself, or a bridge - * authority. x*/ + * authority. */ #define ROUTER_PURPOSE_BRIDGE 2 /** Tor should not use this router; it was marked in cached-descriptors with * a purpose we didn't recognize. */ #define ROUTER_PURPOSE_UNKNOWN 255 - /* In what way did we find out about this router? One of ROUTER_PURPOSE_*. + /** In what way did we find out about this router? One of ROUTER_PURPOSE_*. * Routers of different purposes are kept segregated and used for different * things; see notes on ROUTER_PURPOSE_* macros above. */ @@ -2331,8 +2332,12 @@ typedef struct routerstatus_t { unsigned int supports_extend2_cells:1; /** True iff this router has a protocol list that allows it to negotiate - * ed25519 identity keys on a link handshake. */ - unsigned int supports_ed25519_link_handshake:1; + * ed25519 identity keys on a link handshake with us. */ + unsigned int supports_ed25519_link_handshake_compat:1; + + /** True iff this router has a protocol list that allows it to negotiate + * ed25519 identity keys on a link handshake, at all. */ + unsigned int supports_ed25519_link_handshake_any:1; /** True iff this router has a protocol list that allows it to be an * introduction point supporting ed25519 authentication key which is part of @@ -3650,10 +3655,24 @@ typedef struct { int TruncateLogFile; /**< Boolean: Should we truncate the log file before we start writing? */ char *SyslogIdentityTag; /**< Identity tag to add for syslog logging. */ + char *AndroidIdentityTag; /**< Identity tag to add for Android logging. */ char *DebugLogFile; /**< Where to send verbose log messages. */ - char *DataDirectory; /**< OR only: where to store long-term data. */ + char *DataDirectory_option; /**< Where to store long-term data, as + * configured by the user. */ + char *DataDirectory; /**< Where to store long-term data, as modified. */ int DataDirectoryGroupReadable; /**< Boolean: Is the DataDirectory g+r? */ + + char *KeyDirectory_option; /**< Where to store keys, as + * configured by the user. */ + char *KeyDirectory; /**< Where to store keys data, as modified. */ + int KeyDirectoryGroupReadable; /**< Boolean: Is the KeyDirectory g+r? */ + + char *CacheDirectory_option; /**< Where to store cached data, as + * configured by the user. */ + char *CacheDirectory; /**< Where to store cached data, as modified. */ + int CacheDirectoryGroupReadable; /**< Boolean: Is the CacheDirectory g+r? */ + char *Nickname; /**< OR only: nickname of this onion router. */ char *Address; /**< OR only: configured address for this onion router. */ char *PidFile; /**< Where to store PID of Tor process. */ @@ -3689,6 +3708,7 @@ typedef struct { * interface addresses? * Includes OutboundBindAddresses and * configured ports. */ + int ReducedExitPolicy; /**<Should we use the Reduced Exit Policy? */ config_line_t *SocksPolicy; /**< Lists of socks policy components */ config_line_t *DirPolicy; /**< Lists of dir policy components */ /** Local address to bind outbound sockets */ @@ -3968,6 +3988,8 @@ typedef struct { int HeartbeatPeriod; /**< Log heartbeat messages after this many seconds * have passed. */ + int MainloopStats; /**< Log main loop statistics as part of the + * heartbeat messages. */ char *HTTPProxy; /**< hostname[:port] to use as http proxy, if any. */ tor_addr_t HTTPProxyAddr; /**< Parsed IPv4 addr for http proxy, if any. */ @@ -4085,6 +4107,8 @@ typedef struct { /** Process specifier for a controller that ‘owns’ this Tor * instance. Tor will terminate if its owning controller does. */ char *OwningControllerProcess; + /** FD specifier for a controller that owns this Tor instance. */ + int OwningControllerFD; int ShutdownWaitLength; /**< When we get a SIGINT and we're a server, how * long do we wait before exiting? */ @@ -4642,6 +4666,9 @@ typedef struct { smartlist_t *Schedulers; /* An ordered list of scheduler_types mapped from Schedulers. */ smartlist_t *SchedulerTypes_; + + /** List of files that were opened by %include in torrc and torrc-defaults */ + smartlist_t *FilesOpenedByIncludes; } or_options_t; #define LOG_PROTOCOL_WARN (get_protocol_warning_severity_level()) diff --git a/src/or/policies.c b/src/or/policies.c index 78451db8fc..a5bde2fe7a 100644 --- a/src/or/policies.c +++ b/src/or/policies.c @@ -18,6 +18,7 @@ #define POLICIES_PRIVATE #include "or.h" +#include "bridges.h" #include "config.h" #include "dirserv.h" #include "microdesc.h" @@ -81,7 +82,8 @@ static int policies_parse_exit_policy_internal( const smartlist_t *configured_addresses, int reject_interface_addresses, int reject_configured_port_addresses, - int add_default_policy); + int add_default_policy, + int add_reduced_policy); /** Replace all "private" entries in *<b>policy</b> with their expanded * equivalents. */ @@ -892,9 +894,10 @@ fascist_firewall_choose_address_ipv4h(uint32_t ipv4h_addr, pref_ipv6, ap); } -/* The microdescriptor consensus has no IPv6 addresses in rs: they are in - * the microdescriptors. This means we can't rely on the node's IPv6 address - * until its microdescriptor is available (when using microdescs). +/* Some microdescriptor consensus methods have no IPv6 addresses in rs: they + * are in the microdescriptors. For these consensus methods, we can't rely on + * the node's IPv6 address until its microdescriptor is available (when using + * microdescs). * But for bridges, rewrite_node_address_for_bridge() updates node->ri with * the configured address, so we can trust bridge addresses. * (Bridges could gain an IPv6 address if their microdescriptor arrives, but @@ -912,11 +915,26 @@ node_awaiting_ipv6(const or_options_t* options, const node_t *node) return 0; } + /* If the node has an IPv6 address, we're not waiting */ + if (node_has_ipv6_addr(node)) { + return 0; + } + + /* If the current consensus method and flavour has IPv6 addresses, we're not + * waiting */ + if (networkstatus_consensus_has_ipv6(options)) { + return 0; + } + + /* Bridge clients never use the address from a bridge's md, so there's no + * need to wait for it. */ + if (node_is_a_configured_bridge(node)) { + return 0; + } + /* We are waiting if we_use_microdescriptors_for_circuits() and we have no - * md. Bridges have a ri based on their config. They would never use the - * address from their md, so there's no need to wait for it. */ - return (!node->md && we_use_microdescriptors_for_circuits(options) && - !node->ri); + * md. */ + return (!node->md && we_use_microdescriptors_for_circuits(options)); } /** Like fascist_firewall_choose_address_base(), but takes <b>rs</b>. @@ -1144,7 +1162,7 @@ validate_addr_policies(const or_options_t *options, char **msg) "to 1 to disable this warning, and for forward compatibility.", options->ExitPolicy == NULL ? " with the default exit policy" : ""); - if (options->ExitPolicy == NULL) { + if (options->ExitPolicy == NULL && options->ReducedExitPolicy == 0) { log_warn(LD_CONFIG, "In a future version of Tor, ExitRelay 0 may become the " "default when no ExitPolicy is given."); @@ -1791,14 +1809,14 @@ policies_parse_exit_policy_reject_private( /* Reject public IPv4 addresses on any interface */ public_addresses = get_interface_address6_list(LOG_INFO, AF_INET, 0); addr_policy_append_reject_addr_list_filter(dest, public_addresses, 1, 0); - free_interface_address6_list(public_addresses); + interface_address6_list_free(public_addresses); /* Don't look for IPv6 addresses if we're configured as IPv4-only */ if (ipv6_exit) { /* Reject public IPv6 addresses on any interface */ public_addresses = get_interface_address6_list(LOG_INFO, AF_INET6, 0); addr_policy_append_reject_addr_list_filter(dest, public_addresses, 0, 1); - free_interface_address6_list(public_addresses); + interface_address6_list_free(public_addresses); } } @@ -1877,6 +1895,24 @@ policies_log_first_redundant_entry(const smartlist_t *policy) "reject *:563,reject *:1214,reject *:4661-4666," \ "reject *:6346-6429,reject *:6699,reject *:6881-6999,accept *:*" +#define REDUCED_EXIT_POLICY \ + "accept *:20-23,accept *:43,accept *:53,accept *:79-81,accept *:88," \ + "accept *:110,accept *:143,accept *:194,accept *:220,accept *:389," \ + "accept *:443,accept *:464,accept *:465,accept *:531,accept *:543-544," \ + "accept *:554,accept *:563,accept *:587,accept *:636,accept *:706," \ + "accept *:749,accept *:873,accept *:902-904,accept *:981,accept *:989-995," \ + "accept *:1194,accept *:1220,accept *:1293,accept *:1500,accept *:1533," \ + "accept *:1677,accept *:1723,accept *:1755,accept *:1863," \ + "accept *:2082-2083,accept *:2086-2087,accept *:2095-2096," \ + "accept *:2102-2104,accept *:3128,accept *:3389,accept *:3690," \ + "accept *:4321,accept *:4643,accept *:5050,accept *:5190," \ + "accept *:5222-5223,accept *:5228,accept *:5900,accept *:6660-6669," \ + "accept *:6679,accept *:6697,accept *:8000,accept *:8008,accept *:8074," \ + "accept *:8080,accept *:8082,accept *:8087-8088,accept *:8232-8233," \ + "accept *:8332-8333,accept *:8443,accept *:8888,accept *:9418," \ + "accept *:9999,accept *:10000,accept *:11371,accept *:19294," \ + "accept *:19638,accept *:50002,accept *:64738,reject *:*" + /** Parse the exit policy <b>cfg</b> into the linked list *<b>dest</b>. * * If <b>ipv6_exit</b> is false, prepend "reject *6:*" to the policy. @@ -1912,7 +1948,8 @@ policies_parse_exit_policy_internal(config_line_t *cfg, const smartlist_t *configured_addresses, int reject_interface_addresses, int reject_configured_port_addresses, - int add_default_policy) + int add_default_policy, + int add_reduced_policy) { if (!ipv6_exit) { append_exit_policy_string(dest, "reject *6:*"); @@ -1938,7 +1975,9 @@ policies_parse_exit_policy_internal(config_line_t *cfg, * effect, and are most likely an error. */ policies_log_first_redundant_entry(*dest); - if (add_default_policy) { + if (add_reduced_policy) { + append_exit_policy_string(dest, REDUCED_EXIT_POLICY); + } else if (add_default_policy) { append_exit_policy_string(dest, DEFAULT_EXIT_POLICY); } else { append_exit_policy_string(dest, "reject *4:*"); @@ -1979,13 +2018,15 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest, int add_default = (options & EXIT_POLICY_ADD_DEFAULT) ? 1 : 0; int reject_local_interfaces = (options & EXIT_POLICY_REJECT_LOCAL_INTERFACES) ? 1 : 0; + int add_reduced = (options & EXIT_POLICY_ADD_REDUCED) ? 1 : 0; return policies_parse_exit_policy_internal(cfg,dest,ipv6_enabled, reject_private, configured_addresses, reject_local_interfaces, reject_local_interfaces, - add_default); + add_default, + add_reduced); } /** Helper function that adds a copy of addr to a smartlist as long as it is @@ -2095,7 +2136,10 @@ policies_parse_exit_policy_from_options(const or_options_t *or_options, } if (!or_options->BridgeRelay) { - parser_cfg |= EXIT_POLICY_ADD_DEFAULT; + if (or_options->ReducedExitPolicy) + parser_cfg |= EXIT_POLICY_ADD_REDUCED; + else + parser_cfg |= EXIT_POLICY_ADD_DEFAULT; } if (or_options->ExitPolicyRejectLocalInterfaces) { @@ -2767,7 +2811,7 @@ write_short_policy(const short_policy_t *policy) /** Release all storage held in <b>policy</b>. */ void -short_policy_free(short_policy_t *policy) +short_policy_free_(short_policy_t *policy) { tor_free(policy); } @@ -3017,7 +3061,7 @@ getinfo_helper_policies(control_connection_t *conn, /** Release all storage held by <b>p</b>. */ void -addr_policy_list_free(smartlist_t *lst) +addr_policy_list_free_(smartlist_t *lst) { if (!lst) return; @@ -3027,7 +3071,7 @@ addr_policy_list_free(smartlist_t *lst) /** Release all storage held by <b>p</b>. */ void -addr_policy_free(addr_policy_t *p) +addr_policy_free_(addr_policy_t *p) { if (!p) return; diff --git a/src/or/policies.h b/src/or/policies.h index 52ff4e2f99..35220a812f 100644 --- a/src/or/policies.h +++ b/src/or/policies.h @@ -22,7 +22,8 @@ #define EXIT_POLICY_REJECT_PRIVATE (1 << 1) #define EXIT_POLICY_ADD_DEFAULT (1 << 2) #define EXIT_POLICY_REJECT_LOCAL_INTERFACES (1 << 3) -#define EXIT_POLICY_OPTION_MAX EXIT_POLICY_REJECT_LOCAL_INTERFACES +#define EXIT_POLICY_ADD_REDUCED (1 << 4) +#define EXIT_POLICY_OPTION_MAX EXIT_POLICY_ADD_REDUCED /* All options set: used for unit testing */ #define EXIT_POLICY_OPTION_ALL ((EXIT_POLICY_OPTION_MAX << 1) - 1) @@ -114,15 +115,21 @@ int getinfo_helper_policies(control_connection_t *conn, int policy_write_item(char *buf, size_t buflen, const addr_policy_t *item, int format_for_desc); -void addr_policy_list_free(smartlist_t *p); -void addr_policy_free(addr_policy_t *p); +void addr_policy_list_free_(smartlist_t *p); +#define addr_policy_list_free(lst) \ + FREE_AND_NULL(smartlist_t, addr_policy_list_free_, (lst)) +void addr_policy_free_(addr_policy_t *p); +#define addr_policy_free(p) \ + FREE_AND_NULL(addr_policy_t, addr_policy_free_, (p)) void policies_free_all(void); char *policy_summarize(smartlist_t *policy, sa_family_t family); short_policy_t *parse_short_policy(const char *summary); char *write_short_policy(const short_policy_t *policy); -void short_policy_free(short_policy_t *policy); +void short_policy_free_(short_policy_t *policy); +#define short_policy_free(p) \ + FREE_AND_NULL(short_policy_t, short_policy_free_, (p)) int short_policy_is_reject_star(const short_policy_t *policy); addr_policy_result_t compare_tor_addr_to_short_policy( const tor_addr_t *addr, uint16_t port, diff --git a/src/or/proto_socks.c b/src/or/proto_socks.c index 7649fcc4be..91633d02af 100644 --- a/src/or/proto_socks.c +++ b/src/or/proto_socks.c @@ -66,7 +66,7 @@ socks_request_new(void) /** Free all storage held in the socks_request_t <b>req</b>. */ void -socks_request_free(socks_request_t *req) +socks_request_free_(socks_request_t *req) { if (!req) return; diff --git a/src/or/proto_socks.h b/src/or/proto_socks.h index a714151414..02e0aca7e9 100644 --- a/src/or/proto_socks.h +++ b/src/or/proto_socks.h @@ -11,7 +11,9 @@ struct socks_request_t; struct buf_t; struct socks_request_t *socks_request_new(void); -void socks_request_free(struct socks_request_t *req); +void socks_request_free_(struct socks_request_t *req); +#define socks_request_free(req) \ + FREE_AND_NULL(socks_request_t, socks_request_free_, (req)) int fetch_from_buf_socks(struct buf_t *buf, socks_request_t *req, int log_sockstype, int safe_socks); int fetch_from_buf_socks_client(buf_t *buf, int state, char **reason); diff --git a/src/or/protover.c b/src/or/protover.c index 0946092692..1a5f4ac91f 100644 --- a/src/or/protover.c +++ b/src/or/protover.c @@ -27,11 +27,14 @@ #include "protover.h" #include "routerparse.h" +#ifndef HAVE_RUST + static const smartlist_t *get_supported_protocol_list(void); static int protocol_list_contains(const smartlist_t *protos, protocol_type_t pr, uint32_t ver); /** Mapping between protocol type string and protocol type. */ +/// C_RUST_COUPLED: src/rust/protover/protover.rs `PROTOCOL_NAMES` static const struct { protocol_type_t protover_type; const char *name; @@ -93,7 +96,7 @@ str_to_protocol_type(const char *s, protocol_type_t *pr_out) * Release all space held by a single proto_entry_t structure */ STATIC void -proto_entry_free(proto_entry_t *entry) +proto_entry_free_(proto_entry_t *entry) { if (!entry) return; @@ -280,8 +283,45 @@ protocol_list_supports_protocol(const char *list, protocol_type_t tp, return contains; } +/** + * Return true iff "list" encodes a protocol list that includes support for + * the indicated protocol and version, or some later version. + */ +int +protocol_list_supports_protocol_or_later(const char *list, + protocol_type_t tp, + uint32_t version) +{ + /* NOTE: This is a pretty inefficient implementation. If it ever shows + * up in profiles, we should memoize it. + */ + smartlist_t *protocols = parse_protocol_list(list); + if (!protocols) { + return 0; + } + const char *pr_name = protocol_type_to_str(tp); + + int contains = 0; + SMARTLIST_FOREACH_BEGIN(protocols, proto_entry_t *, proto) { + if (strcasecmp(proto->name, pr_name)) + continue; + SMARTLIST_FOREACH_BEGIN(proto->ranges, const proto_range_t *, range) { + if (range->high >= version) { + contains = 1; + goto found; + } + } SMARTLIST_FOREACH_END(range); + } SMARTLIST_FOREACH_END(proto); + + found: + SMARTLIST_FOREACH(protocols, proto_entry_t *, ent, proto_entry_free(ent)); + smartlist_free(protocols); + return contains; +} + /** Return the canonical string containing the list of protocols * that we support. */ +/// C_RUST_COUPLED: src/rust/protover/protover.rs `SUPPORTED_PROTOCOLS` const char * protover_get_supported_protocols(void) { @@ -365,6 +405,8 @@ encode_protocol_list(const smartlist_t *sl) /* We treat any protocol list with more than this many subprotocols in it * as a DoS attempt. */ +/// C_RUST_COUPLED: src/rust/protover/protover.rs +/// `MAX_PROTOCOLS_TO_EXPAND` static const int MAX_PROTOCOLS_TO_EXPAND = (1<<16); /** Voting helper: Given a list of proto_entry_t, return a newly allocated @@ -691,6 +733,7 @@ protocol_list_contains(const smartlist_t *protos, * Note that this is only used to infer protocols for Tor versions that * can't declare their own. **/ +/// C_RUST_COUPLED: src/rust/protover/protover.rs `compute_for_old_tor` const char * protover_compute_for_old_tor(const char *version) { @@ -740,3 +783,5 @@ protover_free_all(void) } } +#endif /* !defined(HAVE_RUST) */ + diff --git a/src/or/protover.h b/src/or/protover.h index 657977279e..477274e293 100644 --- a/src/or/protover.h +++ b/src/or/protover.h @@ -15,6 +15,8 @@ * descriptors. Authorities should use this to decide whether to * guess proto lines. */ /* This is a guess. */ +/// C_RUST_COUPLED: src/rust/protover/protover.rs +/// `FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS` #define FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS "0.2.9.3-alpha" /** The protover version number that signifies HSDir support for HSv3 */ @@ -25,6 +27,8 @@ #define PROTOVER_HS_RENDEZVOUS_POINT_V3 2 /** List of recognized subprotocols. */ +/// C_RUST_COUPLED: src/rust/protover/ffi.rs `translate_to_rust` +/// C_RUST_COUPLED: src/rust/protover/protover.rs `Proto` typedef enum protocol_type_t { PRT_LINK, PRT_LINKAUTH, @@ -47,6 +51,9 @@ char *protover_compute_vote(const smartlist_t *list_of_proto_strings, const char *protover_compute_for_old_tor(const char *version); int protocol_list_supports_protocol(const char *list, protocol_type_t tp, uint32_t version); +int protocol_list_supports_protocol_or_later(const char *list, + protocol_type_t tp, + uint32_t version); void protover_free_all(void); @@ -70,11 +77,17 @@ typedef struct proto_entry_t { smartlist_t *ranges; } proto_entry_t; +#if !defined(HAVE_RUST) && defined(TOR_UNIT_TESTS) STATIC smartlist_t *parse_protocol_list(const char *s); -STATIC void proto_entry_free(proto_entry_t *entry); STATIC char *encode_protocol_list(const smartlist_t *sl); STATIC const char *protocol_type_to_str(protocol_type_t pr); STATIC int str_to_protocol_type(const char *s, protocol_type_t *pr_out); +STATIC void proto_entry_free_(proto_entry_t *entry); +#endif /* !defined(HAVE_RUST) && defined(TOR_UNIT_TESTS) */ + +#define proto_entry_free(entry) \ + FREE_AND_NULL(proto_entry_t, proto_entry_free_, (entry)) + #endif /* defined(PROTOVER_PRIVATE) */ #endif /* !defined(TOR_PROTOVER_H) */ diff --git a/src/or/protover_rust.c b/src/or/protover_rust.c new file mode 100644 index 0000000000..26e21cc1c5 --- /dev/null +++ b/src/or/protover_rust.c @@ -0,0 +1,19 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/* + * \file protover_rust.c + * \brief Provide a C wrapper for functions exposed in /src/rust/protover, + * and safe translation/handling between the Rust/C boundary. + */ + +#include "or.h" +#include "protover.h" + +#ifdef HAVE_RUST + +/* Define for compatibility, used in main.c */ +void protover_free_all(void) {} + +#endif /* defined(HAVE_RUST) */ + diff --git a/src/or/relay.c b/src/or/relay.c index 66e10567c1..ac2a98e125 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -1158,7 +1158,7 @@ connected_cell_parse(const relay_header_t *rh, const cell_t *cell, /** Drop all storage held by <b>addr</b>. */ STATIC void -address_ttl_free(address_ttl_t *addr) +address_ttl_free_(address_ttl_t *addr) { if (!addr) return; @@ -2425,7 +2425,7 @@ packed_cell_new(void) /** Return a packed cell used outside by channel_t lower layer */ void -packed_cell_free(packed_cell_t *cell) +packed_cell_free_(packed_cell_t *cell) { if (!cell) return; @@ -2482,7 +2482,7 @@ cell_queue_append_packed_copy(circuit_t *circ, cell_queue_t *queue, (void)exitward; (void)use_stats; - copy->inserted_time = (uint32_t) monotime_coarse_absolute_msec(); + copy->inserted_timestamp = monotime_coarse_get_stamp(); cell_queue_append(queue, copy); } @@ -2565,7 +2565,7 @@ destroy_cell_queue_append(destroy_cell_queue_t *queue, cell->circid = circid; cell->reason = reason; /* Not yet used, but will be required for OOM handling. */ - cell->inserted_time = (uint32_t) monotime_coarse_absolute_msec(); + cell->inserted_timestamp = monotime_coarse_get_stamp(); TOR_SIMPLEQ_INSERT_TAIL(&queue->head, cell, next); ++queue->n; @@ -2820,8 +2820,13 @@ channel_flush_from_first_active_circuit, (channel_t *chan, int max)) tor_assert(dcell); /* frees dcell */ cell = destroy_cell_to_packed_cell(dcell, chan->wide_circ_ids); - /* frees cell */ - channel_write_packed_cell(chan, cell); + /* Send the DESTROY cell. It is very unlikely that this fails but just + * in case, get rid of the channel. */ + if (channel_write_packed_cell(chan, cell) < 0) { + /* The cell has been freed. */ + channel_mark_for_close(chan); + continue; + } /* Update the cmux destroy counter */ circuitmux_notify_xmit_destroy(cmux); cell = NULL; @@ -2864,9 +2869,10 @@ channel_flush_from_first_active_circuit, (channel_t *chan, int max)) /* Calculate the exact time that this cell has spent in the queue. */ if (get_options()->CellStatistics || get_options()->TestingEnableCellStatsEvent) { - uint32_t msec_waiting; - uint32_t msec_now = (uint32_t)monotime_coarse_absolute_msec(); - msec_waiting = msec_now - cell->inserted_time; + uint32_t timestamp_now = monotime_coarse_get_stamp(); + uint32_t msec_waiting = + (uint32_t) monotime_coarse_stamp_units_to_approx_msec( + timestamp_now - cell->inserted_timestamp); if (get_options()->CellStatistics && !CIRCUIT_IS_ORIGIN(circ)) { or_circ = TO_OR_CIRCUIT(circ); @@ -2897,8 +2903,13 @@ channel_flush_from_first_active_circuit, (channel_t *chan, int max)) DIRREQ_TUNNELED, DIRREQ_CIRC_QUEUE_FLUSHED); - /* Now send the cell */ - channel_write_packed_cell(chan, cell); + /* Now send the cell. It is very unlikely that this fails but just in + * case, get rid of the channel. */ + if (channel_write_packed_cell(chan, cell) < 0) { + /* The cell has been freed at this point. */ + channel_mark_for_close(chan); + continue; + } cell = NULL; /* @@ -2933,22 +2944,13 @@ channel_flush_from_first_active_circuit, (channel_t *chan, int max)) return n_flushed; } -#if 0 -/** Indicate the current preferred cap for middle circuits; zero disables - * the cap. Right now it's just a constant, ORCIRC_MAX_MIDDLE_CELLS, but - * the logic in append_cell_to_circuit_queue() is written to be correct - * if we want to base it on a consensus param or something that might change - * in the future. - */ -static int -get_max_middle_cells(void) -{ - return ORCIRC_MAX_MIDDLE_CELLS; -} -#endif /* 0 */ - /** Add <b>cell</b> to the queue of <b>circ</b> writing to <b>chan</b> - * transmitting in <b>direction</b>. */ + * transmitting in <b>direction</b>. + * + * The given <b>cell</b> is copied over the circuit queue so the caller must + * cleanup the memory. + * + * This function is part of the fast path. */ void append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, cell_t *cell, cell_direction_t direction, @@ -2957,10 +2959,6 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, or_circuit_t *orcirc = NULL; cell_queue_t *queue; int streams_blocked; -#if 0 - uint32_t tgt_max_middle_cells, p_len, n_len, tmp, hard_max_middle_cells; -#endif - int exitward; if (circ->marked_for_close) return; @@ -2975,93 +2973,14 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan, streams_blocked = circ->streams_blocked_on_p_chan; } - /* - * Disabling this for now because of a possible guard discovery attack - */ -#if 0 - /* Are we a middle circuit about to exceed ORCIRC_MAX_MIDDLE_CELLS? */ - if ((circ->n_chan != NULL) && CIRCUIT_IS_ORCIRC(circ)) { - orcirc = TO_OR_CIRCUIT(circ); - if (orcirc->p_chan) { - /* We are a middle circuit if we have both n_chan and p_chan */ - /* We'll need to know the current preferred maximum */ - tgt_max_middle_cells = get_max_middle_cells(); - if (tgt_max_middle_cells > 0) { - /* Do we need to initialize middle_max_cells? */ - if (orcirc->max_middle_cells == 0) { - orcirc->max_middle_cells = tgt_max_middle_cells; - } else { - if (tgt_max_middle_cells > orcirc->max_middle_cells) { - /* If we want to increase the cap, we can do so right away */ - orcirc->max_middle_cells = tgt_max_middle_cells; - } else if (tgt_max_middle_cells < orcirc->max_middle_cells) { - /* - * If we're shrinking the cap, we can't shrink past either queue; - * compare tgt_max_middle_cells rather than tgt_max_middle_cells * - * ORCIRC_MAX_MIDDLE_KILL_THRESH so the queues don't shrink enough - * to generate spurious warnings, either. - */ - n_len = circ->n_chan_cells.n; - p_len = orcirc->p_chan_cells.n; - tmp = tgt_max_middle_cells; - if (tmp < n_len) tmp = n_len; - if (tmp < p_len) tmp = p_len; - orcirc->max_middle_cells = tmp; - } - /* else no change */ - } - } else { - /* tgt_max_middle_cells == 0 indicates we should disable the cap */ - orcirc->max_middle_cells = 0; - } - - /* Now we know orcirc->max_middle_cells is set correctly */ - if (orcirc->max_middle_cells > 0) { - hard_max_middle_cells = - (uint32_t)(((double)orcirc->max_middle_cells) * - ORCIRC_MAX_MIDDLE_KILL_THRESH); - - if ((unsigned)queue->n + 1 >= hard_max_middle_cells) { - /* Queueing this cell would put queue over the kill theshold */ - log_warn(LD_CIRC, - "Got a cell exceeding the hard cap of %u in the " - "%s direction on middle circ ID %u on chan ID " - U64_FORMAT "; killing the circuit.", - hard_max_middle_cells, - (direction == CELL_DIRECTION_OUT) ? "n" : "p", - (direction == CELL_DIRECTION_OUT) ? - circ->n_circ_id : orcirc->p_circ_id, - U64_PRINTF_ARG( - (direction == CELL_DIRECTION_OUT) ? - circ->n_chan->global_identifier : - orcirc->p_chan->global_identifier)); - circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT); - return; - } else if ((unsigned)queue->n + 1 == orcirc->max_middle_cells) { - /* Only use ==, not >= for this test so we don't spam the log */ - log_warn(LD_CIRC, - "While trying to queue a cell, reached the soft cap of %u " - "in the %s direction on middle circ ID %u " - "on chan ID " U64_FORMAT ".", - orcirc->max_middle_cells, - (direction == CELL_DIRECTION_OUT) ? "n" : "p", - (direction == CELL_DIRECTION_OUT) ? - circ->n_circ_id : orcirc->p_circ_id, - U64_PRINTF_ARG( - (direction == CELL_DIRECTION_OUT) ? - circ->n_chan->global_identifier : - orcirc->p_chan->global_identifier)); - } - } - } - } -#endif /* 0 */ - + /* Very important that we copy to the circuit queue because all calls to + * this function use the stack for the cell memory. */ cell_queue_append_packed_copy(circ, queue, exitward, cell, chan->wide_circ_ids, 1); + /* Check and run the OOM if needed. */ if (PREDICT_UNLIKELY(cell_queues_check_size())) { - /* We ran the OOM handler */ + /* We ran the OOM handler which might have closed this circuit. */ if (circ->marked_for_close) return; } diff --git a/src/or/relay.h b/src/or/relay.h index 4cc1a0fbdb..daf789c855 100644 --- a/src/or/relay.h +++ b/src/or/relay.h @@ -51,7 +51,9 @@ size_t packed_cell_mem_cost(void); int have_been_under_memory_pressure(void); /* For channeltls.c */ -void packed_cell_free(packed_cell_t *cell); +void packed_cell_free_(packed_cell_t *cell); +#define packed_cell_free(cell) \ + FREE_AND_NULL(packed_cell_t, packed_cell_free_, (cell)) void cell_queue_init(cell_queue_t *queue); void cell_queue_clear(cell_queue_t *queue); @@ -101,7 +103,9 @@ typedef struct address_ttl_s { char *hostname; int ttl; } address_ttl_t; -STATIC void address_ttl_free(address_ttl_t *addr); +STATIC void address_ttl_free_(address_ttl_t *addr); +#define address_ttl_free(addr) \ + FREE_AND_NULL(address_ttl_t, address_ttl_free_, (addr)) STATIC int resolved_cell_parse(const cell_t *cell, const relay_header_t *rh, smartlist_t *addresses_out, int *errcode_out); STATIC int connection_edge_process_resolved_cell(edge_connection_t *conn, diff --git a/src/or/rendcache.c b/src/or/rendcache.c index b98b2bccfa..6f56df6718 100644 --- a/src/or/rendcache.c +++ b/src/or/rendcache.c @@ -120,7 +120,7 @@ rend_cache_increment_allocation(size_t n) /** Helper: free a rend cache failure intro object. */ STATIC void -rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t *entry) +rend_cache_failure_intro_entry_free_(rend_cache_failure_intro_t *entry) { if (entry == NULL) { return; @@ -129,9 +129,9 @@ rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t *entry) } static void -rend_cache_failure_intro_entry_free_(void *entry) +rend_cache_failure_intro_entry_free_void(void *entry) { - rend_cache_failure_intro_entry_free(entry); + rend_cache_failure_intro_entry_free_(entry); } /** Allocate a rend cache failure intro object and return it. <b>failure</b> @@ -147,7 +147,7 @@ rend_cache_failure_intro_entry_new(rend_intro_point_failure_t failure) /** Helper: free a rend cache failure object. */ STATIC void -rend_cache_failure_entry_free(rend_cache_failure_t *entry) +rend_cache_failure_entry_free_(rend_cache_failure_t *entry) { if (entry == NULL) { return; @@ -155,7 +155,7 @@ rend_cache_failure_entry_free(rend_cache_failure_t *entry) /* Free and remove every intro failure object. */ digestmap_free(entry->intro_failures, - rend_cache_failure_intro_entry_free_); + rend_cache_failure_intro_entry_free_void); tor_free(entry); } @@ -163,9 +163,9 @@ rend_cache_failure_entry_free(rend_cache_failure_t *entry) /** Helper: deallocate a rend_cache_failure_t. (Used with strmap_free(), * which requires a function pointer whose argument is void*). */ STATIC void -rend_cache_failure_entry_free_(void *entry) +rend_cache_failure_entry_free_void(void *entry) { - rend_cache_failure_entry_free(entry); + rend_cache_failure_entry_free_(entry); } /** Allocate a rend cache failure object and return it. This function can @@ -201,7 +201,7 @@ rend_cache_failure_remove(rend_service_descriptor_t *desc) /** Helper: free storage held by a single service descriptor cache entry. */ STATIC void -rend_cache_entry_free(rend_cache_entry_t *e) +rend_cache_entry_free_(rend_cache_entry_t *e) { if (!e) return; @@ -217,19 +217,19 @@ rend_cache_entry_free(rend_cache_entry_t *e) /** Helper: deallocate a rend_cache_entry_t. (Used with strmap_free(), which * requires a function pointer whose argument is void*). */ static void -rend_cache_entry_free_(void *p) +rend_cache_entry_free_void(void *p) { - rend_cache_entry_free(p); + rend_cache_entry_free_(p); } /** Free all storage held by the service descriptor cache. */ void rend_cache_free_all(void) { - strmap_free(rend_cache, rend_cache_entry_free_); - digestmap_free(rend_cache_v2_dir, rend_cache_entry_free_); - strmap_free(rend_cache_local_service, rend_cache_entry_free_); - strmap_free(rend_cache_failure, rend_cache_failure_entry_free_); + strmap_free(rend_cache, rend_cache_entry_free_void); + digestmap_free(rend_cache_v2_dir, rend_cache_entry_free_void); + strmap_free(rend_cache_local_service, rend_cache_entry_free_void); + strmap_free(rend_cache_failure, rend_cache_failure_entry_free_void); rend_cache = NULL; rend_cache_v2_dir = NULL; rend_cache_local_service = NULL; @@ -304,7 +304,7 @@ rend_cache_purge(void) { if (rend_cache) { log_info(LD_REND, "Purging HS v2 descriptor cache"); - strmap_free(rend_cache, rend_cache_entry_free_); + strmap_free(rend_cache, rend_cache_entry_free_void); } rend_cache = strmap_new(); } @@ -316,7 +316,7 @@ rend_cache_failure_purge(void) { if (rend_cache_failure) { log_info(LD_REND, "Purging HS v2 failure cache"); - strmap_free(rend_cache_failure, rend_cache_failure_entry_free_); + strmap_free(rend_cache_failure, rend_cache_failure_entry_free_void); } rend_cache_failure = strmap_new(); } diff --git a/src/or/rendcache.h b/src/or/rendcache.h index 5b13eadfa1..66e9785645 100644 --- a/src/or/rendcache.h +++ b/src/or/rendcache.h @@ -90,10 +90,18 @@ void rend_cache_increment_allocation(size_t n); #ifdef RENDCACHE_PRIVATE STATIC size_t rend_cache_entry_allocation(const rend_cache_entry_t *e); -STATIC void rend_cache_entry_free(rend_cache_entry_t *e); -STATIC void rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t - *entry); -STATIC void rend_cache_failure_entry_free(rend_cache_failure_t *entry); +STATIC void rend_cache_entry_free_(rend_cache_entry_t *e); +#define rend_cache_entry_free(e) \ + FREE_AND_NULL(rend_cache_entry_t, rend_cache_entry_free_, (e)) +STATIC void rend_cache_failure_intro_entry_free_(rend_cache_failure_intro_t + *entry); +#define rend_cache_failure_intro_entry_free(e) \ + FREE_AND_NULL(rend_cache_failure_intro_t, \ + rend_cache_failure_intro_entry_free_, (e)) +STATIC void rend_cache_failure_entry_free_(rend_cache_failure_t *entry); +#define rend_cache_failure_entry_free(e) \ + FREE_AND_NULL(rend_cache_failure_t, \ + rend_cache_failure_entry_free_, (e)) STATIC int cache_failure_intro_lookup(const uint8_t *identity, const char *service_id, rend_cache_failure_intro_t @@ -108,7 +116,7 @@ STATIC void cache_failure_intro_add(const uint8_t *identity, STATIC void validate_intro_point_failure(const rend_service_descriptor_t *desc, const char *service_id); -STATIC void rend_cache_failure_entry_free_(void *entry); +STATIC void rend_cache_failure_entry_free_void(void *entry); #ifdef TOR_UNIT_TESTS extern strmap_t *rend_cache; diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 3274819241..07fa33306d 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -459,7 +459,8 @@ directory_get_from_hs_dir(const char *desc_id, hs_dir = hs_pick_hsdir(responsible_dirs, desc_id_base32); if (!hs_dir) { /* No suitable hs dir can be found, stop right now. */ - control_event_hs_descriptor_failed(rend_query, NULL, "QUERY_NO_HSDIR"); + control_event_hsv2_descriptor_failed(rend_query, NULL, + "QUERY_NO_HSDIR"); control_event_hs_descriptor_content(rend_data_get_address(rend_query), desc_id_base32, NULL, NULL); return 0; @@ -482,7 +483,7 @@ directory_get_from_hs_dir(const char *desc_id, REND_DESC_COOKIE_LEN, 0)<0) { log_warn(LD_BUG, "Could not base64-encode descriptor cookie."); - control_event_hs_descriptor_failed(rend_query, hsdir_fp, "BAD_DESC"); + control_event_hsv2_descriptor_failed(rend_query, hsdir_fp, "BAD_DESC"); control_event_hs_descriptor_content(rend_data_get_address(rend_query), desc_id_base32, hsdir_fp, NULL); return 0; @@ -515,9 +516,10 @@ directory_get_from_hs_dir(const char *desc_id, (rend_data->auth_type == REND_NO_AUTH ? "[none]" : escaped_safe_str_client(descriptor_cookie_base64)), routerstatus_describe(hs_dir)); - control_event_hs_descriptor_requested(rend_query, + control_event_hs_descriptor_requested(rend_data->onion_address, + rend_data->auth_type, hs_dir->identity_digest, - desc_id_base32); + desc_id_base32, NULL); return 1; } @@ -1102,18 +1104,22 @@ rend_client_lookup_service_authorization(const char *onion_address) return strmap_get(auth_hid_servs, onion_address); } +#define rend_service_authorization_free(val) \ + FREE_AND_NULL(rend_service_authorization_t, \ + rend_service_authorization_free_, (val)) + /** Helper: Free storage held by rend_service_authorization_t. */ static void -rend_service_authorization_free(rend_service_authorization_t *auth) +rend_service_authorization_free_(rend_service_authorization_t *auth) { tor_free(auth); } /** Helper for strmap_free. */ static void -rend_service_authorization_strmap_item_free(void *service_auth) +rend_service_authorization_free_void(void *service_auth) { - rend_service_authorization_free(service_auth); + rend_service_authorization_free_(service_auth); } /** Release all the storage held in auth_hid_servs. @@ -1124,7 +1130,7 @@ rend_service_authorization_free_all(void) if (!auth_hid_servs) { return; } - strmap_free(auth_hid_servs, rend_service_authorization_strmap_item_free); + strmap_free(auth_hid_servs, rend_service_authorization_free_void); auth_hid_servs = NULL; } @@ -1199,7 +1205,7 @@ rend_parse_service_authorization(const or_options_t *options, rend_service_authorization_free_all(); auth_hid_servs = parsed; } else { - strmap_free(parsed, rend_service_authorization_strmap_item_free); + strmap_free(parsed, rend_service_authorization_free_void); } return res; } diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c index 458a90058f..230da4be5c 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -37,7 +37,7 @@ rend_cmp_service_ids(const char *one, const char *two) /** Free the storage held by the service descriptor <b>desc</b>. */ void -rend_service_descriptor_free(rend_service_descriptor_t *desc) +rend_service_descriptor_free_(rend_service_descriptor_t *desc) { if (!desc) return; @@ -419,7 +419,7 @@ rend_desc_v2_is_parsable(rend_encoded_v2_service_descriptor_t *desc) /** Free the storage held by an encoded v2 service descriptor. */ void -rend_encoded_v2_service_descriptor_free( +rend_encoded_v2_service_descriptor_free_( rend_encoded_v2_service_descriptor_t *desc) { if (!desc) @@ -430,7 +430,7 @@ rend_encoded_v2_service_descriptor_free( /** Free the storage held by an introduction point info. */ void -rend_intro_point_free(rend_intro_point_t *intro) +rend_intro_point_free_(rend_intro_point_t *intro) { if (!intro) return; diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h index c35d7272fd..1ed0f62609 100644 --- a/src/or/rendcommon.h +++ b/src/or/rendcommon.h @@ -24,11 +24,19 @@ void rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint, int command, size_t length, const uint8_t *payload); -void rend_service_descriptor_free(rend_service_descriptor_t *desc); +void rend_service_descriptor_free_(rend_service_descriptor_t *desc); +#define rend_service_descriptor_free(desc) \ + FREE_AND_NULL(rend_service_descriptor_t, rend_service_descriptor_free_, \ + (desc)) int rend_get_service_id(crypto_pk_t *pk, char *out); -void rend_encoded_v2_service_descriptor_free( +void rend_encoded_v2_service_descriptor_free_( rend_encoded_v2_service_descriptor_t *desc); -void rend_intro_point_free(rend_intro_point_t *intro); +#define rend_encoded_v2_service_descriptor_free(desc) \ + FREE_AND_NULL(rend_encoded_v2_service_descriptor_t, \ + rend_encoded_v2_service_descriptor_free_, (desc)) +void rend_intro_point_free_(rend_intro_point_t *intro); +#define rend_intro_point_free(intro) \ + FREE_AND_NULL(rend_intro_point_t, rend_intro_point_free_, (intro)) int rend_valid_v2_service_id(const char *query); int rend_valid_descriptor_id(const char *query); diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 2cdafa6666..06166d7cf3 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -157,7 +157,7 @@ rend_num_services(void) /** Helper: free storage held by a single service authorized client entry. */ void -rend_authorized_client_free(rend_authorized_client_t *client) +rend_authorized_client_free_(rend_authorized_client_t *client) { if (!client) return; @@ -172,15 +172,15 @@ rend_authorized_client_free(rend_authorized_client_t *client) /** Helper for strmap_free. */ static void -rend_authorized_client_strmap_item_free(void *authorized_client) +rend_authorized_client_free_void(void *authorized_client) { - rend_authorized_client_free(authorized_client); + rend_authorized_client_free_(authorized_client); } /** Release the storage held by <b>service</b>. */ STATIC void -rend_service_free(rend_service_t *service) +rend_service_free_(rend_service_t *service) { if (!service) return; @@ -470,7 +470,7 @@ rend_service_parse_port_config(const char *string, const char *sep, /** Release all storage held in a rend_service_port_config_t. */ void -rend_service_port_config_free(rend_service_port_config_t *p) +rend_service_port_config_free_(rend_service_port_config_t *p) { tor_free(p); } @@ -847,9 +847,9 @@ rend_config_service(const config_line_t *line_, * after calling this routine, and may assume that correct cleanup has * been done on failure. * - * Return an appropriate rend_service_add_ephemeral_status_t. + * Return an appropriate hs_service_add_ephemeral_status_t. */ -rend_service_add_ephemeral_status_t +hs_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk, smartlist_t *ports, int max_streams_per_circuit, @@ -1675,7 +1675,7 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname) memwipe(client_keys_str, 0, strlen(client_keys_str)); tor_free(client_keys_str); } - strmap_free(parsed_clients, rend_authorized_client_strmap_item_free); + strmap_free(parsed_clients, rend_authorized_client_free_void); if (cfname) { memwipe(cfname, 0, strlen(cfname)); @@ -2223,7 +2223,7 @@ find_rp_for_intro(const rend_intro_cell_t *intro, * rend_service_parse_intro(). */ void -rend_service_free_intro(rend_intro_cell_t *request) +rend_service_free_intro_(rend_intro_cell_t *request) { if (!request) { return; @@ -3576,7 +3576,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, "directories to post descriptors to."); control_event_hs_descriptor_upload(service_id, "UNKNOWN", - "UNKNOWN"); + "UNKNOWN", NULL); goto done; } } @@ -3631,7 +3631,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, hs_dir->or_port); control_event_hs_descriptor_upload(service_id, hs_dir->identity_digest, - desc_id_base32); + desc_id_base32, NULL); tor_free(hs_dir_ip); /* Remember successful upload to this router for next time. */ if (!smartlist_contains_digest(successful_uploads, @@ -3728,7 +3728,7 @@ upload_service_descriptor(rend_service_t *service) } /* Free memory for descriptors. */ for (i = 0; i < smartlist_len(descs); i++) - rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i)); + rend_encoded_v2_service_descriptor_free_(smartlist_get(descs, i)); smartlist_clear(descs); /* Update next upload time. */ if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS @@ -3759,7 +3759,7 @@ upload_service_descriptor(rend_service_t *service) } /* Free memory for descriptors. */ for (i = 0; i < smartlist_len(descs); i++) - rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i)); + rend_encoded_v2_service_descriptor_free_(smartlist_get(descs, i)); smartlist_clear(descs); } } diff --git a/src/or/rendservice.h b/src/or/rendservice.h index 5946e31861..88da7b8665 100644 --- a/src/or/rendservice.h +++ b/src/or/rendservice.h @@ -117,7 +117,9 @@ typedef struct rend_service_t { int max_streams_close_circuit; } rend_service_t; -STATIC void rend_service_free(rend_service_t *service); +STATIC void rend_service_free_(rend_service_t *service); +#define rend_service_free(s) \ + FREE_AND_NULL(rend_service_t, rend_service_free_, (s)) STATIC char *rend_service_sos_poison_path(const rend_service_t *service); STATIC int rend_service_verify_single_onion_poison( const rend_service_t *s, @@ -160,7 +162,11 @@ int rend_service_receive_introduction(origin_circuit_t *circuit, int rend_service_decrypt_intro(rend_intro_cell_t *request, crypto_pk_t *key, char **err_msg_out); -void rend_service_free_intro(rend_intro_cell_t *request); +void rend_service_free_intro_(rend_intro_cell_t *request); +#define rend_service_free_intro(req) do { \ + rend_service_free_intro_(req); \ + (req) = NULL; \ + } while (0) rend_intro_cell_t * rend_service_begin_parse_intro(const uint8_t *request, size_t request_len, uint8_t type, @@ -183,20 +189,17 @@ void rend_service_init(void); rend_service_port_config_t *rend_service_parse_port_config(const char *string, const char *sep, char **err_msg_out); -void rend_service_port_config_free(rend_service_port_config_t *p); - -void rend_authorized_client_free(rend_authorized_client_t *client); - -/** Return value from rend_service_add_ephemeral. */ -typedef enum { - RSAE_BADAUTH = -5, /**< Invalid auth_type/auth_clients */ - RSAE_BADVIRTPORT = -4, /**< Invalid VIRTPORT/TARGET(s) */ - RSAE_ADDREXISTS = -3, /**< Onion address collision */ - RSAE_BADPRIVKEY = -2, /**< Invalid public key */ - RSAE_INTERNAL = -1, /**< Internal error */ - RSAE_OKAY = 0 /**< Service added as expected */ -} rend_service_add_ephemeral_status_t; -rend_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk, +void rend_service_port_config_free_(rend_service_port_config_t *p); +#define rend_service_port_config_free(p) \ + FREE_AND_NULL(rend_service_port_config_t, rend_service_port_config_free_, \ + (p)) + +void rend_authorized_client_free_(rend_authorized_client_t *client); +#define rend_authorized_client_free(client) \ + FREE_AND_NULL(rend_authorized_client_t, rend_authorized_client_free_, \ + (client)) + +hs_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk, smartlist_t *ports, int max_streams_per_circuit, int max_streams_close_circuit, diff --git a/src/or/rephist.c b/src/or/rephist.c index 345722d8ce..15fb674fff 100644 --- a/src/or/rephist.c +++ b/src/or/rephist.c @@ -1358,9 +1358,12 @@ bw_array_new(void) return b; } +#define bw_array_free(val) \ + FREE_AND_NULL(bw_array_t, bw_array_free_, (val)) + /** Free storage held by bandwidth array <b>b</b>. */ static void -bw_array_free(bw_array_t *b) +bw_array_free_(bw_array_t *b) { if (!b) { return; @@ -1885,7 +1888,7 @@ predicted_ports_init(void) * be used. */ static void -predicted_ports_free(void) +predicted_ports_free_all(void) { rephist_total_alloc -= smartlist_len(predicted_ports_list)*sizeof(predicted_port_t); @@ -2830,7 +2833,7 @@ HT_GENERATE2(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash, /* DOCDOC bidi_map_free */ static void -bidi_map_free(void) +bidi_map_free_all(void) { bidi_map_entry_t **ptr, **next, *ent; for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) { @@ -2850,7 +2853,7 @@ rep_hist_reset_conn_stats(time_t now) mostly_read = 0; mostly_written = 0; both_read_and_written = 0; - bidi_map_free(); + bidi_map_free_all(); } /** Stop collecting connection stats in a way that we can re-start doing @@ -3039,9 +3042,12 @@ hs_stats_new(void) return new_hs_stats; } +#define hs_stats_free(val) \ + FREE_AND_NULL(hs_stats_t, hs_stats_free_, (val)) + /** Free an hs_stats_t structure. */ static void -hs_stats_free(hs_stats_t *victim_hs_stats) +hs_stats_free_(hs_stats_t *victim_hs_stats) { if (!victim_hs_stats) { return; @@ -3453,8 +3459,8 @@ rep_hist_free_all(void) tor_free(exit_bytes_read); tor_free(exit_bytes_written); tor_free(exit_streams); - predicted_ports_free(); - bidi_map_free(); + predicted_ports_free_all(); + bidi_map_free_all(); if (circuits_for_buffer_stats) { SMARTLIST_FOREACH(circuits_for_buffer_stats, circ_buffer_stats_t *, s, diff --git a/src/or/replaycache.c b/src/or/replaycache.c index 3d42deb90a..4a56bfd7d4 100644 --- a/src/or/replaycache.c +++ b/src/or/replaycache.c @@ -28,7 +28,7 @@ */ void -replaycache_free(replaycache_t *r) +replaycache_free_(replaycache_t *r) { if (!r) { log_info(LD_BUG, "replaycache_free() called on NULL"); diff --git a/src/or/replaycache.h b/src/or/replaycache.h index 1cae3497ae..81a8d907fd 100644 --- a/src/or/replaycache.h +++ b/src/or/replaycache.h @@ -33,7 +33,9 @@ struct replaycache_s { /* replaycache_t free/new */ -void replaycache_free(replaycache_t *r); +void replaycache_free_(replaycache_t *r); +#define replaycache_free(r) \ + FREE_AND_NULL(replaycache_t, replaycache_free_, (r)) replaycache_t * replaycache_new(time_t horizon, time_t interval); #ifdef REPLAYCACHE_PRIVATE diff --git a/src/or/router.c b/src/or/router.c index 010ee339a0..9c053cad46 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -174,7 +174,7 @@ expire_old_onion_keys(void) tor_mutex_release(key_lock); - fname = get_datadir_fname2("keys", "secret_onion_key.old"); + fname = get_keydir_fname("secret_onion_key.old"); if (file_status(fname) == FN_FILE) { if (tor_unlink(fname) != 0) { log_warn(LD_FS, "Couldn't unlink old onion key file %s: %s", @@ -183,7 +183,7 @@ expire_old_onion_keys(void) } tor_free(fname); - fname = get_datadir_fname2("keys", "secret_onion_key_ntor.old"); + fname = get_keydir_fname("secret_onion_key_ntor.old"); if (file_status(fname) == FN_FILE) { if (tor_unlink(fname) != 0) { log_warn(LD_FS, "Couldn't unlink old ntor onion key file %s: %s", @@ -233,7 +233,7 @@ ntor_key_map_free_helper(void *arg) } /** Release all storage from a keymap returned by construct_ntor_key_map. */ void -ntor_key_map_free(di_digest256_map_t *map) +ntor_key_map_free_(di_digest256_map_t *map) { if (!map) return; @@ -378,8 +378,8 @@ rotate_onion_key(void) or_state_t *state = get_or_state(); curve25519_keypair_t new_curve25519_keypair; time_t now; - fname = get_datadir_fname2("keys", "secret_onion_key"); - fname_prev = get_datadir_fname2("keys", "secret_onion_key.old"); + fname = get_keydir_fname("secret_onion_key"); + fname_prev = get_keydir_fname("secret_onion_key.old"); /* There isn't much point replacing an old key with an empty file */ if (file_status(fname) == FN_FILE) { if (replace_file(fname, fname_prev)) @@ -399,8 +399,8 @@ rotate_onion_key(void) } tor_free(fname); tor_free(fname_prev); - fname = get_datadir_fname2("keys", "secret_onion_key_ntor"); - fname_prev = get_datadir_fname2("keys", "secret_onion_key_ntor.old"); + fname = get_keydir_fname("secret_onion_key_ntor"); + fname_prev = get_keydir_fname("secret_onion_key_ntor.old"); if (curve25519_keypair_generate(&new_curve25519_keypair, 1) < 0) goto error; /* There isn't much point replacing an old key with an empty file */ @@ -624,7 +624,7 @@ load_authority_keyset(int legacy, crypto_pk_t **key_out, crypto_pk_t *signing_key = NULL; authority_cert_t *parsed = NULL; - fname = get_datadir_fname2("keys", + fname = get_keydir_fname( legacy ? "legacy_signing_key" : "authority_signing_key"); signing_key = init_key_from_file(fname, 0, LOG_ERR, 0); if (!signing_key) { @@ -632,7 +632,7 @@ load_authority_keyset(int legacy, crypto_pk_t **key_out, goto done; } tor_free(fname); - fname = get_datadir_fname2("keys", + fname = get_keydir_fname( legacy ? "legacy_certificate" : "authority_certificate"); cert = read_file_to_str(fname, 0, NULL); if (!cert) { @@ -932,22 +932,9 @@ init_keys(void) } if (init_keys_common() < 0) return -1; - /* Make sure DataDirectory exists, and is private. */ - cpd_check_t cpd_opts = CPD_CREATE; - if (options->DataDirectoryGroupReadable) - cpd_opts |= CPD_GROUP_READ; - if (check_private_dir(options->DataDirectory, cpd_opts, options->User)) { - log_err(LD_OR, "Can't create/check datadirectory %s", - options->DataDirectory); - return -1; - } - /* Check the key directory. */ - keydir = get_datadir_fname("keys"); - if (check_private_dir(keydir, CPD_CREATE, options->User)) { - tor_free(keydir); + + if (create_keys_directory(options) < 0) return -1; - } - tor_free(keydir); /* 1a. Read v3 directory authority key/cert information. */ memset(v3_digest, 0, sizeof(v3_digest)); @@ -971,7 +958,7 @@ init_keys(void) } /* 1b. Read identity key. Make it if none is found. */ - keydir = get_datadir_fname2("keys", "secret_id_key"); + keydir = get_keydir_fname("secret_id_key"); log_info(LD_GENERAL,"Reading/making identity key \"%s\"...",keydir); prkey = init_key_from_file(keydir, 1, LOG_ERR, 1); tor_free(keydir); @@ -999,7 +986,7 @@ init_keys(void) return -1; /* 2. Read onion key. Make it if none is found. */ - keydir = get_datadir_fname2("keys", "secret_onion_key"); + keydir = get_keydir_fname("secret_onion_key"); log_info(LD_GENERAL,"Reading/making onion key \"%s\"...",keydir); prkey = init_key_from_file(keydir, 1, LOG_ERR, 1); tor_free(keydir); @@ -1024,7 +1011,7 @@ init_keys(void) } } - keydir = get_datadir_fname2("keys", "secret_onion_key.old"); + keydir = get_keydir_fname("secret_onion_key.old"); if (!lastonionkey && file_status(keydir) == FN_FILE) { /* Load keys from non-empty files only. * Missing old keys won't be replaced with freshly generated keys. */ @@ -1037,14 +1024,14 @@ init_keys(void) { /* 2b. Load curve25519 onion keys. */ int r; - keydir = get_datadir_fname2("keys", "secret_onion_key_ntor"); + keydir = get_keydir_fname("secret_onion_key_ntor"); r = init_curve25519_keypair_from_file(&curve25519_onion_key, keydir, 1, LOG_ERR, "onion"); tor_free(keydir); if (r<0) return -1; - keydir = get_datadir_fname2("keys", "secret_onion_key_ntor.old"); + keydir = get_keydir_fname("secret_onion_key_ntor.old"); if (tor_mem_is_zero((const char *) last_curve25519_onion_key.pubkey.public_key, CURVE25519_PUBKEY_LEN) && @@ -3698,6 +3685,7 @@ router_free_all(void) crypto_pk_free(lastonionkey); crypto_pk_free(server_identitykey); crypto_pk_free(client_identitykey); + tor_mutex_free(key_lock); routerinfo_free(desc_routerinfo); extrainfo_free(desc_extrainfo); diff --git a/src/or/router.h b/src/or/router.h index 3351400911..696e983662 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -36,7 +36,9 @@ int get_onion_key_lifetime(void); int get_onion_key_grace_period(void); di_digest256_map_t *construct_ntor_key_map(void); -void ntor_key_map_free(di_digest256_map_t *map); +void ntor_key_map_free_(di_digest256_map_t *map); +#define ntor_key_map_free(map) \ + FREE_AND_NULL(di_digest256_map_t, ntor_key_map_free_, (map)) int router_initialize_tls_context(void); int init_keys(void); diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index 7295c19653..af230f07bf 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -718,7 +718,7 @@ load_ed_keys(const or_options_t *options, time_t now) /* First try to get the signing key to see how it is. */ { char *fname = - options_get_datadir_fname2(options, "keys", "ed25519_signing"); + options_get_keydir_fname(options, "ed25519_signing"); sign = ed_key_init_from_file( fname, INIT_ED_KEY_NEEDCERT| @@ -813,26 +813,15 @@ load_ed_keys(const or_options_t *options, time_t now) flags |= INIT_ED_KEY_TRY_ENCRYPTED; /* Check/Create the key directory */ - cpd_check_t cpd_opts = CPD_CREATE; - if (options->DataDirectoryGroupReadable) - cpd_opts |= CPD_GROUP_READ; - if (check_private_dir(options->DataDirectory, cpd_opts, options->User)) { - log_err(LD_OR, "Can't create/check datadirectory %s", - options->DataDirectory); - goto err; - } - char *fname = get_datadir_fname("keys"); - if (check_private_dir(fname, CPD_CREATE, options->User) < 0) { - log_err(LD_OR, "Problem creating/checking key directory %s", fname); - tor_free(fname); - goto err; - } - tor_free(fname); + if (create_keys_directory(options) < 0) + return -1; + + char *fname; if (options->master_key_fname) { fname = tor_strdup(options->master_key_fname); flags |= INIT_ED_KEY_EXPLICIT_FNAME; } else { - fname = options_get_datadir_fname2(options, "keys", "ed25519_master_id"); + fname = options_get_keydir_fname(options, "ed25519_master_id"); } id = ed_key_init_from_file( fname, @@ -852,8 +841,8 @@ load_ed_keys(const or_options_t *options, time_t now) id = tor_malloc_zero(sizeof(*id)); memcpy(&id->pubkey, &check_signing_cert->signing_key, sizeof(ed25519_public_key_t)); - fname = options_get_datadir_fname2(options, "keys", - "ed25519_master_id_public_key"); + fname = options_get_keydir_fname(options, + "ed25519_master_id_public_key"); if (ed25519_pubkey_write_to_file(&id->pubkey, fname, "type0") < 0) { log_warn(LD_OR, "Error while attempting to write master public key " "to disk"); @@ -894,7 +883,7 @@ load_ed_keys(const or_options_t *options, time_t now) INIT_ED_KEY_NEEDCERT| INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT); char *fname = - options_get_datadir_fname2(options, "keys", "ed25519_signing"); + options_get_keydir_fname(options, "ed25519_signing"); ed25519_keypair_free(sign); tor_cert_free(sign_cert); sign = ed_key_init_from_file(fname, @@ -1185,7 +1174,7 @@ log_master_signing_key_cert_expiration(const or_options_t *options) int failed = 0; time_t now = approx_time(); - fn = options_get_datadir_fname2(options, "keys", "ed25519_signing_cert"); + fn = options_get_keydir_fname(options, "ed25519_signing_cert"); /* Try to grab our cached copy of the key. */ signing_key = get_master_signing_key_cert(); diff --git a/src/or/routerlist.c b/src/or/routerlist.c index af4f67dc12..d8c8a81738 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -143,6 +143,10 @@ DECLARE_TYPED_DIGESTMAP_FNS(dsmap_, digest_ds_map_t, download_status_t) #define DSMAP_FOREACH(map, keyvar, valvar) \ DIGESTMAP_FOREACH(dsmap_to_digestmap(map), keyvar, download_status_t *, \ valvar) +#define eimap_free(map, fn) MAP_FREE_AND_NULL(eimap, (map), (fn)) +#define rimap_free(map, fn) MAP_FREE_AND_NULL(rimap, (map), (fn)) +#define dsmap_free(map, fn) MAP_FREE_AND_NULL(dsmap, (map), (fn)) +#define sdmap_free(map, fn) MAP_FREE_AND_NULL(sdmap, (map), (fn)) /* Forward declaration for cert_list_t */ typedef struct cert_list_t cert_list_t; @@ -159,7 +163,6 @@ static const routerstatus_t *router_pick_dirserver_generic( smartlist_t *sourcelist, dirinfo_type_t type, int flags); static void mark_all_dirservers_up(smartlist_t *server_list); -static void dir_server_free(dir_server_t *ds); static int signed_desc_digest_is_recognized(signed_descriptor_t *desc); static const char *signed_descriptor_get_body_impl( const signed_descriptor_t *desc, @@ -443,9 +446,12 @@ download_status_for_authority_id_and_sk,(const char *id_digest, return dl; } +#define cert_list_free(val) \ + FREE_AND_NULL(cert_list_t, cert_list_free_, (val)) + /** Release all space held by a cert_list_t */ static void -cert_list_free(cert_list_t *cl) +cert_list_free_(cert_list_t *cl) { if (!cl) return; @@ -459,9 +465,9 @@ cert_list_free(cert_list_t *cl) /** Wrapper for cert_list_free so we can pass it to digestmap_free */ static void -cert_list_free_(void *cl) +cert_list_free_void(void *cl) { - cert_list_free(cl); + cert_list_free_(cl); } /** Reload the cached v3 key certificates from the cached-certs file in @@ -473,7 +479,7 @@ trusted_dirs_reload_certs(void) char *contents; int r; - filename = get_datadir_fname("cached-certs"); + filename = get_cachedir_fname("cached-certs"); contents = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL); tor_free(filename); if (!contents) @@ -662,7 +668,7 @@ trusted_dirs_flush_certs_to_disk(void) }); } DIGESTMAP_FOREACH_END; - filename = get_datadir_fname("cached-certs"); + filename = get_cachedir_fname("cached-certs"); if (write_chunks_to_file(filename, chunks, 0, 0)) { log_warn(LD_FS, "Error writing certificates to disk."); } @@ -1339,7 +1345,7 @@ static int signed_desc_append_to_journal(signed_descriptor_t *desc, desc_store_t *store) { - char *fname = get_datadir_fname_suffix(store->fname_base, ".new"); + char *fname = get_cachedir_fname_suffix(store->fname_base, ".new"); const char *body = signed_descriptor_get_body_impl(desc,1); size_t len = desc->signed_descriptor_len + desc->annotations_len; @@ -1410,8 +1416,8 @@ router_rebuild_store(int flags, desc_store_t *store) log_info(LD_DIR, "Rebuilding %s cache", store->description); - fname = get_datadir_fname(store->fname_base); - fname_tmp = get_datadir_fname_suffix(store->fname_base, ".tmp"); + fname = get_cachedir_fname(store->fname_base); + fname_tmp = get_cachedir_fname_suffix(store->fname_base, ".tmp"); chunk_list = smartlist_new(); @@ -1508,7 +1514,7 @@ router_rebuild_store(int flags, desc_store_t *store) } SMARTLIST_FOREACH_END(sd); tor_free(fname); - fname = get_datadir_fname_suffix(store->fname_base, ".new"); + fname = get_cachedir_fname_suffix(store->fname_base, ".new"); write_str_to_file(fname, "", 1); r = 0; @@ -1538,7 +1544,7 @@ router_reload_router_list_impl(desc_store_t *store) int extrainfo = (store->type == EXTRAINFO_STORE); store->journal_len = store->store_len = 0; - fname = get_datadir_fname(store->fname_base); + fname = get_cachedir_fname(store->fname_base); if (store->mmap) { /* get rid of it first */ @@ -1565,7 +1571,7 @@ router_reload_router_list_impl(desc_store_t *store) } tor_free(fname); - fname = get_datadir_fname_suffix(store->fname_base, ".new"); + fname = get_cachedir_fname_suffix(store->fname_base, ".new"); /* don't load empty files - we wouldn't get any data, even if we tried */ if (file_status(fname) == FN_FILE) contents = read_file_to_str(fname, RFTS_BIN|RFTS_IGNORE_MISSING, &st); @@ -3099,7 +3105,7 @@ signed_descriptor_get_body_impl(const signed_descriptor_t *desc, log_err(LD_DIR, "We couldn't read a descriptor that is supposedly " "mmaped in our cache. Is another process running in our data " "directory? Exiting."); - exit(1); + exit(1); // XXXX bad exit: should recover. } } if (!r) /* no mmap, or not in cache. */ @@ -3113,7 +3119,7 @@ signed_descriptor_get_body_impl(const signed_descriptor_t *desc, log_err(LD_DIR, "descriptor at %p begins with unexpected string %s. " "Is another process running in our data directory? Exiting.", desc, escaped(cp)); - exit(1); + exit(1); // XXXX bad exit: should recover. } } @@ -3167,7 +3173,7 @@ router_get_routerlist(void) /** Free all storage held by <b>router</b>. */ void -routerinfo_free(routerinfo_t *router) +routerinfo_free_(routerinfo_t *router) { if (!router) return; @@ -3197,7 +3203,7 @@ routerinfo_free(routerinfo_t *router) /** Release all storage held by <b>extrainfo</b> */ void -extrainfo_free(extrainfo_t *extrainfo) +extrainfo_free_(extrainfo_t *extrainfo) { if (!extrainfo) return; @@ -3209,9 +3215,12 @@ extrainfo_free(extrainfo_t *extrainfo) tor_free(extrainfo); } +#define signed_descriptor_free(val) \ + FREE_AND_NULL(signed_descriptor_t, signed_descriptor_free_, (val)) + /** Release storage held by <b>sd</b>. */ static void -signed_descriptor_free(signed_descriptor_t *sd) +signed_descriptor_free_(signed_descriptor_t *sd) { if (!sd) return; @@ -3265,21 +3274,21 @@ signed_descriptor_from_routerinfo(routerinfo_t *ri) /** Helper: free the storage held by the extrainfo_t in <b>e</b>. */ static void -extrainfo_free_(void *e) +extrainfo_free_void(void *e) { - extrainfo_free(e); + extrainfo_free_(e); } /** Free all storage held by a routerlist <b>rl</b>. */ void -routerlist_free(routerlist_t *rl) +routerlist_free_(routerlist_t *rl) { if (!rl) return; rimap_free(rl->identity_map, NULL); sdmap_free(rl->desc_digest_map, NULL); sdmap_free(rl->desc_by_eid_map, NULL); - eimap_free(rl->extra_info_map, extrainfo_free_); + eimap_free(rl->extra_info_map, extrainfo_free_void); SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r, routerinfo_free(r)); SMARTLIST_FOREACH(rl->old_routers, signed_descriptor_t *, sd, @@ -3771,7 +3780,7 @@ routerlist_free_all(void) smartlist_free(fallback_dir_servers); trusted_dir_servers = fallback_dir_servers = NULL; if (trusted_dir_certs) { - digestmap_free(trusted_dir_certs, cert_list_free_); + digestmap_free(trusted_dir_certs, cert_list_free_void); trusted_dir_certs = NULL; } } @@ -4548,7 +4557,7 @@ signed_desc_digest_is_recognized(signed_descriptor_t *desc) void update_all_descriptor_downloads(time_t now) { - if (get_options()->DisableNetwork) + if (should_delay_dir_fetches(get_options(), NULL)) return; update_router_descriptor_downloads(now); update_microdesc_downloads(now); @@ -4739,7 +4748,7 @@ dir_server_add(dir_server_t *ent) /** Free storage held in <b>cert</b>. */ void -authority_cert_free(authority_cert_t *cert) +authority_cert_free_(authority_cert_t *cert) { if (!cert) return; @@ -4751,9 +4760,12 @@ authority_cert_free(authority_cert_t *cert) tor_free(cert); } +#define dir_server_free(val) \ + FREE_AND_NULL(dir_server_t, dir_server_free_, (val)) + /** Free storage held in <b>ds</b>. */ static void -dir_server_free(dir_server_t *ds) +dir_server_free_(dir_server_t *ds) { if (!ds) return; @@ -5200,10 +5212,23 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote, log_warn(LD_BUG, "Failed to re-parse a router."); continue; } + /* need to compute this now, since add_to_routerlist may free. */ + char time_cert_expires[ISO_TIME_LEN+1]; + format_iso_time(time_cert_expires, ri->cert_expiration_time); + r = router_add_to_routerlist(ri, &msg, 1, 0); if (WRA_WAS_OUTDATED(r)) { - log_warn(LD_DIR, "Couldn't add re-parsed router: %s", + log_warn(LD_DIR, "Couldn't add re-parsed router: %s. This isn't " + "usually a big deal, but you should make sure that your " + "clock and timezone are set correctly.", msg?msg:"???"); + if (r == ROUTER_CERTS_EXPIRED) { + char time_cons[ISO_TIME_LEN+1]; + format_iso_time(time_cons, consensus->valid_after); + log_warn(LD_DIR, " (I'm looking at a consensus from %s; This " + "router's certificates began expiring at %s.)", + time_cons, time_cert_expires); + } } } SMARTLIST_FOREACH_END(sd); routerlist_assert_ok(rl); @@ -5380,8 +5405,10 @@ update_extrainfo_downloads(time_t now) smartlist_free(wanted); } -/** Reset the descriptor download failure count on all routers, so that we - * can retry any long-failed routers immediately. +/** Reset the consensus and extra-info download failure count on all routers. + * When we get a new consensus, + * routers_update_status_from_consensus_networkstatus() will reset the + * download statuses on the descriptors in that consensus. */ void router_reset_descriptor_download_failures(void) @@ -5393,6 +5420,8 @@ router_reset_descriptor_download_failures(void) last_descriptor_download_attempted = 0; if (!routerlist) return; + /* We want to download *all* extra-info descriptors, not just those in + * the consensus we currently have (or are about to have) */ SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, ri, { download_status_reset(&ri->cache_info.ei_dl_status); diff --git a/src/or/routerlist.h b/src/or/routerlist.h index 8384c7eb8c..83f4d1002f 100644 --- a/src/or/routerlist.h +++ b/src/or/routerlist.h @@ -96,9 +96,13 @@ MOCK_DECL(signed_descriptor_t *,extrainfo_get_by_descriptor_digest, const char *signed_descriptor_get_body(const signed_descriptor_t *desc); const char *signed_descriptor_get_annotations(const signed_descriptor_t *desc); routerlist_t *router_get_routerlist(void); -void routerinfo_free(routerinfo_t *router); -void extrainfo_free(extrainfo_t *extrainfo); -void routerlist_free(routerlist_t *rl); +void routerinfo_free_(routerinfo_t *router); +#define routerinfo_free(router) \ + FREE_AND_NULL(routerinfo_t, routerinfo_free_, (router)) +void extrainfo_free_(extrainfo_t *extrainfo); +#define extrainfo_free(ei) FREE_AND_NULL(extrainfo_t, extrainfo_free_, (ei)) +void routerlist_free_(routerlist_t *rl); +#define routerlist_free(rl) FREE_AND_NULL(routerlist_t, routerlist_free_, (rl)) void dump_routerlist_mem_usage(int severity); void routerlist_remove(routerlist_t *rl, routerinfo_t *ri, int make_old, time_t now); @@ -191,7 +195,9 @@ dir_server_t *fallback_dir_server_new(const tor_addr_t *addr, const char *id_digest, double weight); void dir_server_add(dir_server_t *ent); -void authority_cert_free(authority_cert_t *cert); +void authority_cert_free_(authority_cert_t *cert); +#define authority_cert_free(cert) \ + FREE_AND_NULL(authority_cert_t, authority_cert_free_, (cert)) void clear_dir_servers(void); void update_consensus_router_descriptor_downloads(time_t now, int is_vote, networkstatus_t *consensus); diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 15cdb0bbde..f1895ce313 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -2701,8 +2701,10 @@ routerstatus_parse_entry_from_string(memarea_t *area, rs->protocols_known = 1; rs->supports_extend2_cells = protocol_list_supports_protocol(tok->args[0], PRT_RELAY, 2); - rs->supports_ed25519_link_handshake = + rs->supports_ed25519_link_handshake_compat = protocol_list_supports_protocol(tok->args[0], PRT_LINKAUTH, 3); + rs->supports_ed25519_link_handshake_any = + protocol_list_supports_protocol_or_later(tok->args[0], PRT_LINKAUTH, 3); rs->supports_ed25519_hs_intro = protocol_list_supports_protocol(tok->args[0], PRT_HSINTRO, 4); rs->supports_v3_hsdir = @@ -4027,7 +4029,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, /** Return the common_digests_t that holds the digests of the * <b>flavor_name</b>-flavored networkstatus according to the detached * signatures document <b>sigs</b>, allocating a new common_digests_t as - * neeeded. */ + * needed. */ static common_digests_t * detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name) { @@ -4041,7 +4043,7 @@ detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name) /** Return the list of signatures of the <b>flavor_name</b>-flavored * networkstatus according to the detached signatures document <b>sigs</b>, - * allocating a new common_digests_t as neeeded. */ + * allocating a new common_digests_t as needed. */ static smartlist_t * detached_get_signatures(ns_detached_signatures_t *sigs, const char *flavor_name) diff --git a/src/or/routerset.c b/src/or/routerset.c index 54e26ef943..a2599b316c 100644 --- a/src/or/routerset.c +++ b/src/or/routerset.c @@ -437,7 +437,7 @@ routerset_equal(const routerset_t *old, const routerset_t *new) /** Free all storage held in <b>routerset</b>. */ void -routerset_free(routerset_t *routerset) +routerset_free_(routerset_t *routerset) { if (!routerset) return; diff --git a/src/or/routerset.h b/src/or/routerset.h index d8819ef3fd..53e8c66c5e 100644 --- a/src/or/routerset.h +++ b/src/or/routerset.h @@ -40,7 +40,8 @@ void routerset_subtract_nodes(smartlist_t *out, char *routerset_to_string(const routerset_t *routerset); int routerset_equal(const routerset_t *old, const routerset_t *new); -void routerset_free(routerset_t *routerset); +void routerset_free_(routerset_t *routerset); +#define routerset_free(rs) FREE_AND_NULL(routerset_t, routerset_free_, (rs)) int routerset_len(const routerset_t *set); #ifdef ROUTERSET_PRIVATE diff --git a/src/or/scheduler.c b/src/or/scheduler.c index cbf51447bf..cd047d5a75 100644 --- a/src/or/scheduler.c +++ b/src/or/scheduler.c @@ -305,7 +305,7 @@ select_scheduler(void) * wishes of using what it has been configured and don't do a sneaky * fallback. Because this can be changed at runtime, we have to stop tor * right now. */ - exit(1); + exit(1); // XXXX bad exit } /* Set the chosen scheduler. */ diff --git a/src/or/scheduler_kist.c b/src/or/scheduler_kist.c index f50f3aa9e9..a707ab2758 100644 --- a/src/or/scheduler_kist.c +++ b/src/or/scheduler_kist.c @@ -474,7 +474,7 @@ kist_free_all(void) /* Function of the scheduler interface: on_channel_free() */ static void -kist_on_channel_free(const channel_t *chan) +kist_on_channel_free_fn(const channel_t *chan) { free_socket_info_by_chan(&socket_table, chan); } @@ -744,7 +744,7 @@ kist_scheduler_run(void) static scheduler_t kist_scheduler = { .type = SCHEDULER_KIST, .free_all = kist_free_all, - .on_channel_free = kist_on_channel_free, + .on_channel_free = kist_on_channel_free_fn, .init = kist_scheduler_init, .on_new_consensus = kist_scheduler_on_new_consensus, .schedule = kist_scheduler_schedule, diff --git a/src/or/shared_random.c b/src/or/shared_random.c index b3f62a8fd8..7723ad4611 100644 --- a/src/or/shared_random.c +++ b/src/or/shared_random.c @@ -384,7 +384,7 @@ commit_encode(const sr_commit_t *commit, char *dst, size_t len) static void sr_cleanup(void) { - sr_state_free(); + sr_state_free_all(); } /* Using <b>commit</b>, return a newly allocated string containing the commit @@ -897,7 +897,7 @@ sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv) /* Free a commit object. */ void -sr_commit_free(sr_commit_t *commit) +sr_commit_free_(sr_commit_t *commit) { if (commit == NULL) { return; diff --git a/src/or/shared_random.h b/src/or/shared_random.h index c0992489cb..675a8d8b06 100644 --- a/src/or/shared_random.h +++ b/src/or/shared_random.h @@ -113,7 +113,8 @@ sr_srv_t *sr_parse_srv(const smartlist_t *args); char *sr_get_string_for_vote(void); char *sr_get_string_for_consensus(const smartlist_t *votes, int32_t num_srv_agreements); -void sr_commit_free(sr_commit_t *commit); +void sr_commit_free_(sr_commit_t *commit); +#define sr_commit_free(sr) FREE_AND_NULL(sr_commit_t, sr_commit_free_, (sr)) void sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv); /* Private methods (only used by shared_random_state.c): */ diff --git a/src/or/shared_random_state.c b/src/or/shared_random_state.c index ae904cfda3..d6109b2de7 100644 --- a/src/or/shared_random_state.c +++ b/src/or/shared_random_state.c @@ -271,12 +271,15 @@ commit_add_to_state(sr_commit_t *commit, sr_state_t *state) static void commit_free_(void *p) { - sr_commit_free(p); + sr_commit_free_(p); } +#define state_free(val) \ + FREE_AND_NULL(sr_state_t, state_free_, (val)) + /* Free a state that was allocated with state_new(). */ static void -state_free(sr_state_t *state) +state_free_(sr_state_t *state) { if (state == NULL) { return; @@ -318,9 +321,12 @@ state_set(sr_state_t *state) sr_state = state; } +#define disk_state_free(val) \ + FREE_AND_NULL(sr_disk_state_t, disk_state_free_, (val)) + /* Free an allocated disk state. */ static void -disk_state_free(sr_disk_state_t *state) +disk_state_free_(sr_disk_state_t *state) { if (state == NULL) { return; @@ -1286,7 +1292,7 @@ sr_state_srv_is_fresh(void) /* Cleanup and free our disk and memory state. */ void -sr_state_free(void) +sr_state_free_all(void) { state_free(sr_state); disk_state_free(sr_disk_state); diff --git a/src/or/shared_random_state.h b/src/or/shared_random_state.h index 866725c435..fdbbf4919a 100644 --- a/src/or/shared_random_state.h +++ b/src/or/shared_random_state.h @@ -119,7 +119,7 @@ void sr_state_unset_fresh_srv(void); int sr_state_init(int save_to_disk, int read_from_disk); int sr_state_is_initialized(void); void sr_state_save(void); -void sr_state_free(void); +void sr_state_free_all(void); time_t sr_state_get_start_time_of_current_protocol_run(time_t now); unsigned int sr_state_get_phase_duration(void); diff --git a/src/or/statefile.c b/src/or/statefile.c index 97bd9cac36..cc114f0a2b 100644 --- a/src/or/statefile.c +++ b/src/or/statefile.c @@ -681,7 +681,7 @@ save_transport_to_state(const char *transport, } STATIC void -or_state_free(or_state_t *state) +or_state_free_(or_state_t *state) { if (!state) return; diff --git a/src/or/statefile.h b/src/or/statefile.h index 574afb3622..b4cc4d1dc6 100644 --- a/src/or/statefile.h +++ b/src/or/statefile.h @@ -20,7 +20,8 @@ void or_state_free_all(void); #ifdef STATEFILE_PRIVATE STATIC config_line_t *get_transport_in_state_by_name(const char *transport); -STATIC void or_state_free(or_state_t *state); +STATIC void or_state_free_(or_state_t *state); +#define or_state_free(st) FREE_AND_NULL(or_state_t, or_state_free_, (st)) STATIC or_state_t *or_state_new(void); #endif diff --git a/src/or/status.c b/src/or/status.c index f7be41e412..1a8885e83e 100644 --- a/src/or/status.c +++ b/src/or/status.c @@ -157,6 +157,20 @@ log_heartbeat(time_t now) tor_free(msg); } + if (options->MainloopStats) { + const uint64_t main_loop_success_count = get_main_loop_success_count(); + const uint64_t main_loop_error_count = get_main_loop_error_count(); + const uint64_t main_loop_idle_count = get_main_loop_idle_count(); + + log_fn(LOG_NOTICE, LD_HEARTBEAT, "Main event loop statistics: " + U64_FORMAT " succesful returns, " + U64_FORMAT " erroneous returns, and " + U64_FORMAT " idle returns.", + U64_PRINTF_ARG(main_loop_success_count), + U64_PRINTF_ARG(main_loop_error_count), + U64_PRINTF_ARG(main_loop_idle_count)); + } + tor_free(uptime); tor_free(bw_sent); tor_free(bw_rcvd); diff --git a/src/or/tor_api.c b/src/or/tor_api.c new file mode 100644 index 0000000000..4260cc88f4 --- /dev/null +++ b/src/or/tor_api.c @@ -0,0 +1,88 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file tor_api.c + **/ + +#include "tor_api.h" +#include "tor_api_internal.h" + +// Include this after the above headers, to insure that they don't +// depend on anything else. +#include "orconfig.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// We don't want to use tor_malloc and tor_free here, since this needs +// to run before anything is initialized at all, and ought to run when +// we're not linked to anything at all. + +#define raw_malloc malloc +#define raw_free free + +tor_main_configuration_t * +tor_main_configuration_new(void) +{ + static const char *fake_argv[] = { "tor" }; + tor_main_configuration_t *cfg = raw_malloc(sizeof(*cfg)); + if (cfg == NULL) + return NULL; + + memset(cfg, 0, sizeof(*cfg)); + + cfg->argc = 1; + cfg->argv = (char **) fake_argv; + + return cfg; +} + +int +tor_main_configuration_set_command_line(tor_main_configuration_t *cfg, + int argc, char *argv[]) +{ + if (cfg == NULL) + return -1; + cfg->argc = argc; + cfg->argv = argv; + return 0; +} + +void +tor_main_configuration_free(tor_main_configuration_t *cfg) +{ + if (cfg == NULL) + return; + raw_free(cfg); +} + +/* Main entry point for the Tor process. Called from main(). + * + * This function is distinct from main() only so we can link main.c into + * the unittest binary without conflicting with the unittests' main. + * + * Some embedders have historically called this function; but that usage is + * deprecated: they should use tor_run_main() instead. + */ +int +tor_main(int argc, char *argv[]) +{ + tor_main_configuration_t *cfg = tor_main_configuration_new(); + if (!cfg) { + puts("INTERNAL ERROR: Allocation failure. Cannot proceed"); + return 1; + } + if (tor_main_configuration_set_command_line(cfg, argc, argv) < 0) { + puts("INTERNAL ERROR: Can't set command line. Cannot proceed."); + return 1; + } + int rv = tor_run_main(cfg); + tor_main_configuration_free(cfg); + return rv; +} + diff --git a/src/or/tor_api.h b/src/or/tor_api.h new file mode 100644 index 0000000000..b12ed718c1 --- /dev/null +++ b/src/or/tor_api.h @@ -0,0 +1,102 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file tor_api.h + * \brief Public C API for the Tor network service. + * + * This interface is intended for use by programs that need to link Tor as + * a library, and launch it in a separate thread. If you have the ability + * to run Tor as a separate executable, you should probably do that instead + * of embedding it as a library. + * + * To use this API, first construct a tor_main_configuration_t object using + * tor_main_configuration_new(). Then, you use one or more other function + * calls (such as tor_main_configuration_set_command_line() to configure how + * Tor should be run. Finally, you pass the configuration object to + * tor_run_main(). + * + * At this point, tor_run_main() will block its thread to run a Tor daemon; + * when the Tor daemon exits, it will return. See notes on bugs and + * limitations below. + * + * There is no other public C API to Tor: calling any C Tor function not + * documented in this file is not guaranteed to be stable. + **/ + +#ifndef TOR_API_H +#define TOR_API_H + +typedef struct tor_main_configuration_t tor_main_configuration_t; + +/** + * Create and return a new tor_main_configuration(). + */ +tor_main_configuration_t *tor_main_configuration_new(void); + +/** + * Set the command-line arguments in <b>cfg</b>. + * + * The <b>argc</b> and <b>argv</b> values here are as for main(). The + * contents of the argv pointer must remain unchanged until tor_run_main() has + * finished and you call tor_main_configuration_free(). + * + * Return 0 on success, -1 on failure. + */ +int tor_main_configuration_set_command_line(tor_main_configuration_t *cfg, + int argc, char *argv[]); + +/** + * Release all storage held in <b>cfg</b>. + * + * Once you have passed a tor_main_configuration_t to tor_run_main(), you + * must not free it until tor_run_main() has finished. + */ +void tor_main_configuration_free(tor_main_configuration_t *cfg); + +/** + * Run the tor process, as if from the command line. + * + * The command line arguments from tor_main_configuration_set_command_line() + * are taken as if they had been passed to main(). + * + * This function will not return until Tor is done running. It returns zero + * on success, and nonzero on failure. + * + * BUG 23848: In many cases, tor_main will call exit() or abort() instead of + * returning. This is not the intended long-term behavior; we are trying to + * fix it. + * + * BUG 23847: You can only call tor_main() once in a single process; if it + * returns and you call it again, you may crash. This is not intended + * long-term behavior; we are trying to fix it. + * + * LIMITATION: You cannot run more than one instance of Tor in the same + * process at the same time. Concurrent calls will cause undefined behavior. + * We do not currently have plans to change this. + * + * LIMITATION: While we will try to fix any problems found here, you + * should be aware that Tor was originally written to run as its own + * process, and that the functionality of this file was added later. If + * you find any bugs or strange behavior, please report them, and we'll + * try to straighten them out. + */ +int tor_run_main(const tor_main_configuration_t *); + +/** + * Run the tor process, as if from the command line. + * + * @deprecated Using this function from outside Tor is deprecated; you should + * use tor_run_main() instead. + * + * BUGS: This function has all the same bugs as tor_run_main(). + * + * LIMITATIONS: This function has all the limitations of tor_run_main(). + */ +int tor_main(int argc, char **argv); + +#endif /* !defined(TOR_API_H) */ + diff --git a/src/or/tor_api_internal.h b/src/or/tor_api_internal.h new file mode 100644 index 0000000000..10b6278b7b --- /dev/null +++ b/src/or/tor_api_internal.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_API_INTERNAL_H +#define TOR_API_INTERNAL_H + +/* The contents of this type are private; don't mess with them from outside + * Tor. */ +struct tor_main_configuration_t { + /** As in main() */ + int argc; + /** As in main(). This pointer is owned by the caller */ + char **argv; +}; + +#endif /* !defined(TOR_API_INTERNAL_H) */ + diff --git a/src/or/tor_main.c b/src/or/tor_main.c index a3a8838602..c203d8248f 100644 --- a/src/or/tor_main.c +++ b/src/or/tor_main.c @@ -3,18 +3,6 @@ * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ -extern const char tor_git_revision[]; - -/** String describing which Tor Git repository version the source was - * built from. This string is generated by a bit of shell kludging in - * src/or/include.am, and is usually right. - */ -const char tor_git_revision[] = -#ifndef _MSC_VER -#include "micro-revision.i" -#endif - ""; - /** * \file tor_main.c * \brief Stub module containing a main() function. @@ -26,7 +14,7 @@ const char tor_git_revision[] = int tor_main(int argc, char *argv[]); /** We keep main() in a separate file so that our unit tests can use - * functions from main.c) + * functions from main.c. */ int main(int argc, char *argv[]) diff --git a/src/or/torcert.c b/src/or/torcert.c index befb39d6e8..bd677d1f4a 100644 --- a/src/or/torcert.c +++ b/src/or/torcert.c @@ -137,7 +137,7 @@ tor_cert_create(const ed25519_keypair_t *signing_key, /** Release all storage held for <b>cert</b>. */ void -tor_cert_free(tor_cert_t *cert) +tor_cert_free_(tor_cert_t *cert) { if (! cert) return; @@ -453,7 +453,7 @@ or_handshake_certs_new(void) /** Release all storage held in <b>certs</b> */ void -or_handshake_certs_free(or_handshake_certs_t *certs) +or_handshake_certs_free_(or_handshake_certs_t *certs) { if (!certs) return; diff --git a/src/or/torcert.h b/src/or/torcert.h index c77ae2089d..0a8a252049 100644 --- a/src/or/torcert.h +++ b/src/or/torcert.h @@ -57,7 +57,8 @@ tor_cert_t *tor_cert_create(const ed25519_keypair_t *signing_key, tor_cert_t *tor_cert_parse(const uint8_t *cert, size_t certlen); -void tor_cert_free(tor_cert_t *cert); +void tor_cert_free_(tor_cert_t *cert); +#define tor_cert_free(cert) FREE_AND_NULL(tor_cert_t, tor_cert_free_, (cert)) int tor_cert_get_checkable_sig(ed25519_checkable_t *checkable_out, const tor_cert_t *out, @@ -83,7 +84,9 @@ rsa_ed25519_crosscert_check, (const uint8_t *crosscert, const time_t reject_if_expired_before)); or_handshake_certs_t *or_handshake_certs_new(void); -void or_handshake_certs_free(or_handshake_certs_t *certs); +void or_handshake_certs_free_(or_handshake_certs_t *certs); +#define or_handshake_certs_free(certs) \ + FREE_AND_NULL(or_handshake_certs_t, or_handshake_certs_free_, (certs)) int or_handshake_certs_rsa_ok(int severity, or_handshake_certs_t *certs, tor_tls_t *tls, diff --git a/src/or/transports.c b/src/or/transports.c index 5fb24e11a0..1e6307b7d0 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -154,7 +154,7 @@ transport_new(const tor_addr_t *addr, uint16_t port, /** Free the pluggable transport struct <b>transport</b>. */ void -transport_free(transport_t *transport) +transport_free_(transport_t *transport) { if (!transport) return; diff --git a/src/or/transports.h b/src/or/transports.h index e368e447c3..1b2786472c 100644 --- a/src/or/transports.h +++ b/src/or/transports.h @@ -35,7 +35,8 @@ void sweep_transport_list(void); MOCK_DECL(int, transport_add_from_config, (const tor_addr_t *addr, uint16_t port, const char *name, int socks_ver)); -void transport_free(transport_t *transport); +void transport_free_(transport_t *transport); +#define transport_free(tr) FREE_AND_NULL(transport_t, transport_free_, (tr)) transport_t *transport_get_by_name(const char *name); diff --git a/src/rust/.cargo/config.in b/src/rust/.cargo/config.in index 414b253a57..301e7fdbe7 100644 --- a/src/rust/.cargo/config.in +++ b/src/rust/.cargo/config.in @@ -5,4 +5,4 @@ @RUST_DL@ replace-with = 'vendored-sources' @RUST_DL@ [source.vendored-sources] -@RUST_DL@ directory = '@RUST_DEPENDENCIES@' +@RUST_DL@ directory = '@TOR_RUST_DEPENDENCIES@' diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock index 4ac9606ce8..224d2135bf 100644 --- a/src/rust/Cargo.lock +++ b/src/rust/Cargo.lock @@ -3,6 +3,14 @@ name = "tor_util" version = "0.0.1" dependencies = [ "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", + "tor_allocate 0.0.1", +] + +[[package]] +name = "external" +version = "0.0.1" +dependencies = [ + "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -10,5 +18,38 @@ name = "libc" version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "protover" +version = "0.0.1" +dependencies = [ + "external 0.0.1", + "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", + "smartlist 0.0.1", + "tor_allocate 0.0.1", + "tor_util 0.0.1", +] + +[[package]] +name = "smartlist" +version = "0.0.1" +dependencies = [ + "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tor_allocate" +version = "0.0.1" +dependencies = [ + "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tor_rust" +version = "0.1.0" +dependencies = [ + "protover 0.0.1", + "tor_util 0.0.1", +] + [metadata] "checksum libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)" = "babb8281da88cba992fa1f4ddec7d63ed96280a1a53ec9b919fd37b53d71e502" diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml index fc4377e8b4..953c9b96b7 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["tor_util"] +members = ["tor_util", "protover", "smartlist", "external", "tor_allocate", "tor_rust"] [profile.release] debug = true diff --git a/src/rust/external/Cargo.toml b/src/rust/external/Cargo.toml new file mode 100644 index 0000000000..bccd7033a7 --- /dev/null +++ b/src/rust/external/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["The Tor Project"] +version = "0.0.1" +name = "external" + +[dependencies] +libc = "0.2.22" + +[lib] +name = "external" +path = "lib.rs" +crate_type = ["rlib", "staticlib"] + diff --git a/src/rust/external/external.rs b/src/rust/external/external.rs new file mode 100644 index 0000000000..b9e17f021d --- /dev/null +++ b/src/rust/external/external.rs @@ -0,0 +1,33 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + +use libc::{c_char, c_int}; +use std::ffi::CString; + +extern "C" { + fn tor_version_as_new_as( + platform: *const c_char, + cutoff: *const c_char, + ) -> c_int; +} + +/// Wrap calls to tor_version_as_new_as, defined in src/or/routerparse.c +pub fn c_tor_version_as_new_as(platform: &str, cutoff: &str) -> bool { + // CHK: These functions should log a warning if an error occurs. This + // can be added when integration with tor's logger is added to rust + let c_platform = match CString::new(platform) { + Ok(n) => n, + Err(_) => return false, + }; + + let c_cutoff = match CString::new(cutoff) { + Ok(n) => n, + Err(_) => return false, + }; + + let result: c_int = unsafe { + tor_version_as_new_as(c_platform.as_ptr(), c_cutoff.as_ptr()) + }; + + result == 1 +} diff --git a/src/rust/external/lib.rs b/src/rust/external/lib.rs new file mode 100644 index 0000000000..0af0d6452d --- /dev/null +++ b/src/rust/external/lib.rs @@ -0,0 +1,14 @@ +//! Copyright (c) 2016-2017, The Tor Project, Inc. */ +//! See LICENSE for licensing information */ + +//! Interface for external calls to tor C ABI +//! +//! The purpose of this module is to provide a clean interface for when Rust +//! modules need to interact with functionality in tor C code rather than each +//! module implementing this functionality repeatedly. + +extern crate libc; + +mod external; + +pub use external::*; diff --git a/src/rust/include.am b/src/rust/include.am index 20afc6c4db..9c4337484a 100644 --- a/src/rust/include.am +++ b/src/rust/include.am @@ -1,6 +1,26 @@ -include src/rust/tor_util/include.am +include src/rust/tor_rust/include.am EXTRA_DIST +=\ src/rust/Cargo.toml \ src/rust/Cargo.lock \ - src/rust/.cargo/config.in + src/rust/.cargo/config.in \ + src/rust/external/Cargo.toml \ + src/rust/external/external.rs \ + src/rust/external/lib.rs \ + src/rust/protover/Cargo.toml \ + src/rust/protover/ffi.rs \ + src/rust/protover/lib.rs \ + src/rust/protover/protover.rs \ + src/rust/protover/tests/protover.rs \ + src/rust/smartlist/Cargo.toml \ + src/rust/smartlist/lib.rs \ + src/rust/smartlist/smartlist.rs \ + src/rust/tor_allocate/Cargo.toml \ + src/rust/tor_allocate/lib.rs \ + src/rust/tor_allocate/tor_allocate.rs \ + src/rust/tor_rust/Cargo.toml \ + src/rust/tor_rust/include.am \ + src/rust/tor_rust/lib.rs \ + src/rust/tor_util/Cargo.toml \ + src/rust/tor_util/ffi.rs \ + src/rust/tor_util/lib.rs diff --git a/src/rust/protover/Cargo.toml b/src/rust/protover/Cargo.toml new file mode 100644 index 0000000000..04d2f2ed7d --- /dev/null +++ b/src/rust/protover/Cargo.toml @@ -0,0 +1,25 @@ +[package] +authors = ["The Tor Project"] +version = "0.0.1" +name = "protover" + +[dependencies] +libc = "0.2.22" + +[dependencies.smartlist] +path = "../smartlist" + +[dependencies.external] +path = "../external" + +[dependencies.tor_util] +path = "../tor_util" + +[dependencies.tor_allocate] +path = "../tor_allocate" + +[lib] +name = "protover" +path = "lib.rs" +crate_type = ["rlib", "staticlib"] + diff --git a/src/rust/protover/ffi.rs b/src/rust/protover/ffi.rs new file mode 100644 index 0000000000..3eb22c933e --- /dev/null +++ b/src/rust/protover/ffi.rs @@ -0,0 +1,219 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + +//! FFI functions, only to be called from C. +//! +//! Equivalent C versions of this api are in `src/or/protover.c` + +use libc::{c_char, c_int, uint32_t}; +use std::ffi::CStr; +use std::ffi::CString; + +use protover::*; +use smartlist::*; +use tor_allocate::allocate_and_copy_string; + +/// Translate C enums to Rust Proto enums, using the integer value of the C +/// enum to map to its associated Rust enum +/// +/// C_RUST_COUPLED: src/or/protover.h `protocol_type_t` +fn translate_to_rust(c_proto: uint32_t) -> Result<Proto, &'static str> { + match c_proto { + 0 => Ok(Proto::Link), + 1 => Ok(Proto::LinkAuth), + 2 => Ok(Proto::Relay), + 3 => Ok(Proto::DirCache), + 4 => Ok(Proto::HSDir), + 5 => Ok(Proto::HSIntro), + 6 => Ok(Proto::HSRend), + 7 => Ok(Proto::Desc), + 8 => Ok(Proto::Microdesc), + 9 => Ok(Proto::Cons), + _ => Err("Invalid protocol type"), + } +} + +/// Provide an interface for C to translate arguments and return types for +/// protover::all_supported +#[no_mangle] +pub extern "C" fn protover_all_supported( + c_relay_version: *const c_char, + missing_out: *mut *mut c_char, +) -> c_int { + + if c_relay_version.is_null() { + return 1; + } + + // Require an unsafe block to read the version from a C string. The pointer + // is checked above to ensure it is not null. + let c_str: &CStr = unsafe { CStr::from_ptr(c_relay_version) }; + + let relay_version = match c_str.to_str() { + Ok(n) => n, + Err(_) => return 1, + }; + + let (is_supported, unsupported) = all_supported(relay_version); + + if unsupported.len() > 0 { + let c_unsupported = match CString::new(unsupported) { + Ok(n) => n, + Err(_) => return 1, + }; + + let ptr = c_unsupported.into_raw(); + unsafe { *missing_out = ptr }; + } + + return if is_supported { 1 } else { 0 }; +} + +/// Provide an interface for C to translate arguments and return types for +/// protover::list_supports_protocol +#[no_mangle] +pub extern "C" fn protocol_list_supports_protocol( + c_protocol_list: *const c_char, + c_protocol: uint32_t, + version: uint32_t, +) -> c_int { + if c_protocol_list.is_null() { + return 1; + } + + // Require an unsafe block to read the version from a C string. The pointer + // is checked above to ensure it is not null. + let c_str: &CStr = unsafe { CStr::from_ptr(c_protocol_list) }; + + let protocol_list = match c_str.to_str() { + Ok(n) => n, + Err(_) => return 1, + }; + + let protocol = match translate_to_rust(c_protocol) { + Ok(n) => n, + Err(_) => return 0, + }; + + let is_supported = + protover_string_supports_protocol(protocol_list, protocol, version); + + return if is_supported { 1 } else { 0 }; +} + +/// Provide an interface for C to translate arguments and return types for +/// protover::list_supports_protocol_or_later +#[no_mangle] +pub extern "C" fn protocol_list_supports_protocol_or_later( + c_protocol_list: *const c_char, + c_protocol: uint32_t, + version: uint32_t, +) -> c_int { + if c_protocol_list.is_null() { + return 1; + } + + // Require an unsafe block to read the version from a C string. The pointer + // is checked above to ensure it is not null. + let c_str: &CStr = unsafe { CStr::from_ptr(c_protocol_list) }; + + let protocol_list = match c_str.to_str() { + Ok(n) => n, + Err(_) => return 1, + }; + + let protocol = match translate_to_rust(c_protocol) { + Ok(n) => n, + Err(_) => return 0, + }; + + let is_supported = + protover_string_supports_protocol_or_later( + protocol_list, protocol, version); + + return if is_supported { 1 } else { 0 }; +} + +/// Provide an interface for C to translate arguments and return types for +/// protover::get_supported_protocols +#[no_mangle] +pub extern "C" fn protover_get_supported_protocols() -> *mut c_char { + // Not handling errors when unwrapping as the content is controlled + // and is an empty string + let empty = CString::new("").unwrap(); + + let supported = get_supported_protocols(); + let c_supported = match CString::new(supported) { + Ok(n) => n, + Err(_) => return empty.into_raw(), + }; + + c_supported.into_raw() +} + +/// Provide an interface for C to translate arguments and return types for +/// protover::compute_vote +#[no_mangle] +pub extern "C" fn protover_compute_vote( + list: *const Stringlist, + threshold: c_int, +) -> *mut c_char { + + if list.is_null() { + let empty = String::new(); + return allocate_and_copy_string(&empty); + } + + // Dereference of raw pointer requires an unsafe block. The pointer is + // checked above to ensure it is not null. + let data: Vec<String> = unsafe { (*list).get_list() }; + + let vote = compute_vote(data, threshold); + + allocate_and_copy_string(&vote) +} + +/// Provide an interface for C to translate arguments and return types for +/// protover::is_supported_here +#[no_mangle] +pub extern "C" fn protover_is_supported_here( + c_protocol: uint32_t, + version: uint32_t, +) -> c_int { + let protocol = match translate_to_rust(c_protocol) { + Ok(n) => n, + Err(_) => return 0, + }; + + let is_supported = is_supported_here(protocol, version); + + return if is_supported { 1 } else { 0 }; +} + +/// Provide an interface for C to translate arguments and return types for +/// protover::compute_for_old_tor +#[no_mangle] +pub extern "C" fn protover_compute_for_old_tor( + version: *const c_char, +) -> *mut c_char { + // Not handling errors when unwrapping as the content is controlled + // and is an empty string + let empty = String::new(); + + if version.is_null() { + return allocate_and_copy_string(&empty); + } + + // Require an unsafe block to read the version from a C string. The pointer + // is checked above to ensure it is not null. + let c_str: &CStr = unsafe { CStr::from_ptr(version) }; + + let version = match c_str.to_str() { + Ok(n) => n, + Err(_) => return allocate_and_copy_string(&empty), + }; + + let supported = compute_for_old_tor(&version); + + allocate_and_copy_string(&supported) +} diff --git a/src/rust/protover/lib.rs b/src/rust/protover/lib.rs new file mode 100644 index 0000000000..5a5dea4408 --- /dev/null +++ b/src/rust/protover/lib.rs @@ -0,0 +1,33 @@ +//! Copyright (c) 2016-2017, The Tor Project, Inc. */ +//! See LICENSE for licensing information */ + +//! Versioning information for different pieces of the Tor protocol. +//! +//! The below description is taken from src/rust/protover.c, which is currently +//! enabled by default. We are in the process of experimenting with Rust in +//! tor, and this protover module is implemented to help achieve this goal. +//! +//! Starting in version 0.2.9.3-alpha, Tor places separate version numbers on +//! each of the different components of its protocol. Relays use these numbers +//! to advertise what versions of the protocols they can support, and clients +//! use them to find what they can ask a given relay to do. Authorities vote +//! on the supported protocol versions for each relay, and also vote on the +//! which protocols you should have to support in order to be on the Tor +//! network. All Tor instances use these required/recommended protocol versions +//! to tell what level of support for recent protocols each relay has, and +//! to decide whether they should be running given their current protocols. +//! +//! The main advantage of these protocol versions numbers over using Tor +//! version numbers is that they allow different implementations of the Tor +//! protocols to develop independently, without having to claim compatibility +//! with specific versions of Tor. + +extern crate libc; +extern crate smartlist; +extern crate external; +extern crate tor_allocate; + +mod protover; +pub mod ffi; + +pub use protover::*; diff --git a/src/rust/protover/protover.rs b/src/rust/protover/protover.rs new file mode 100644 index 0000000000..7d5947ca2f --- /dev/null +++ b/src/rust/protover/protover.rs @@ -0,0 +1,906 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + +use external::c_tor_version_as_new_as; + +use std::str::FromStr; +use std::fmt; +use std::collections::{HashMap, HashSet}; +use std::string::String; + +/// The first version of Tor that included "proto" entries in its descriptors. +/// Authorities should use this to decide whether to guess proto lines. +/// +/// C_RUST_COUPLED: +/// src/or/protover.h `FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS` +const FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS: &'static str = "0.2.9.3-alpha"; + +/// The maximum number of subprotocol version numbers we will attempt to expand +/// before concluding that someone is trying to DoS us +/// +/// C_RUST_COUPLED: src/or/protover.c `MAX_PROTOCOLS_TO_EXPAND` +const MAX_PROTOCOLS_TO_EXPAND: u32 = 500; + +/// Currently supported protocols and their versions +/// +/// C_RUST_COUPLED: src/or/protover.c `protover_get_supported_protocols` +const SUPPORTED_PROTOCOLS: &'static [&'static str] = &[ + "Cons=1-2", + "Desc=1-2", + "DirCache=1-2", + "HSDir=1-2", + "HSIntro=3-4", + "HSRend=1-2", + "Link=1-4", + "LinkAuth=1,3", + "Microdesc=1-2", + "Relay=1-2", +]; + +/// Known subprotocols in Tor. Indicates which subprotocol a relay supports. +/// +/// C_RUST_COUPLED: src/or/protover.h `protocol_type_t` +#[derive(Hash, Eq, PartialEq, Debug)] +pub enum Proto { + Cons, + Desc, + DirCache, + HSDir, + HSIntro, + HSRend, + Link, + LinkAuth, + Microdesc, + Relay, +} + +impl fmt::Display for Proto { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +/// Translates a string representation of a protocol into a Proto type. +/// Error if the string is an unrecognized protocol name. +/// +/// C_RUST_COUPLED: src/or/protover.c `PROTOCOL_NAMES` +impl FromStr for Proto { + type Err = &'static str; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "Cons" => Ok(Proto::Cons), + "Desc" => Ok(Proto::Desc), + "DirCache" => Ok(Proto::DirCache), + "HSDir" => Ok(Proto::HSDir), + "HSIntro" => Ok(Proto::HSIntro), + "HSRend" => Ok(Proto::HSRend), + "Link" => Ok(Proto::Link), + "LinkAuth" => Ok(Proto::LinkAuth), + "Microdesc" => Ok(Proto::Microdesc), + "Relay" => Ok(Proto::Relay), + _ => Err("Not a valid protocol type"), + } + } +} + +/// Get the string representation of current supported protocols +/// +/// # Returns +/// +/// A `String` whose value is the existing protocols supported by tor. +/// Returned data is in the format as follows: +/// +/// "HSDir=1-1 LinkAuth=1" +/// +pub fn get_supported_protocols() -> String { + SUPPORTED_PROTOCOLS.join(" ") +} + +/// Translates a vector representation of a protocol list into a HashMap +fn parse_protocols( + protocols: &[&str], +) -> Result<HashMap<Proto, HashSet<u32>>, &'static str> { + let mut parsed = HashMap::new(); + + for subproto in protocols { + let (name, version) = get_proto_and_vers(subproto)?; + parsed.insert(name, version); + } + Ok(parsed) +} + +/// Translates a string representation of a protocol list to a HashMap +fn parse_protocols_from_string<'a>( + protocol_string: &'a str, +) -> Result<HashMap<Proto, HashSet<u32>>, &'static str> { + let protocols: &[&'a str] = + &protocol_string.split(" ").collect::<Vec<&'a str>>()[..]; + + parse_protocols(protocols) +} + +/// Translates supported tor versions from a string into a HashMap, which is +/// useful when looking up a specific subprotocol. +/// +/// # Returns +/// +/// A `Result` whose `Ok` value is a `HashMap<Proto, <u32>>` holding all +/// subprotocols and versions currently supported by tor. +/// +/// The returned `Result`'s `Err` value is an `&'static str` with a description +/// of the error. +/// +fn tor_supported() -> Result<HashMap<Proto, HashSet<u32>>, &'static str> { + parse_protocols(&SUPPORTED_PROTOCOLS) +} + +/// Get the unique version numbers supported by a subprotocol. +/// +/// # Inputs +/// +/// * `version_string`, a string comprised of "[0-9,-]" +/// +/// # Returns +/// +/// A `Result` whose `Ok` value is a `HashSet<u32>` holding all of the unique +/// version numbers. If there were ranges in the `version_string`, then these +/// are expanded, i.e. `"1-3"` would expand to `HashSet<u32>::new([1, 2, 3])`. +/// The returned HashSet is *unordered*. +/// +/// The returned `Result`'s `Err` value is an `&'static str` with a description +/// of the error. +/// +/// # Errors +/// +/// This function will error if: +/// +/// * the `version_string` is empty or contains an equals (`"="`) sign, +/// * the expansion of a version range produces an error (see +/// `expand_version_range`), +/// * any single version number is not parseable as an `u32` in radix 10, or +/// * there are greater than 2^16 version numbers to expand. +/// +fn get_versions(version_string: &str) -> Result<HashSet<u32>, &'static str> { + if version_string.is_empty() { + return Err("version string is empty"); + } + + let mut versions = HashSet::<u32>::new(); + + for piece in version_string.split(",") { + if piece.contains("-") { + for p in expand_version_range(piece)? { + versions.insert(p); + } + } else { + versions.insert(u32::from_str(piece).or( + Err("invalid protocol entry"), + )?); + } + + if versions.len() > MAX_PROTOCOLS_TO_EXPAND as usize { + return Err("Too many versions to expand"); + } + } + Ok(versions) +} + + +/// Parse the subprotocol type and its version numbers. +/// +/// # Inputs +/// +/// * A `protocol_entry` string, comprised of a keyword, an "=" sign, and one +/// or more version numbers. +/// +/// # Returns +/// +/// A `Result` whose `Ok` value is a tuple of `(Proto, HashSet<u32>)`, where the +/// first element is the subprotocol type (see `protover::Proto`) and the last +/// element is a(n unordered) set of unique version numbers which are supported. +/// Otherwise, the `Err` value of this `Result` is a description of the error +/// +fn get_proto_and_vers<'a>( + protocol_entry: &'a str, +) -> Result<(Proto, HashSet<u32>), &'static str> { + let mut parts = protocol_entry.splitn(2, "="); + + let proto = match parts.next() { + Some(n) => n, + None => return Err("invalid protover entry"), + }; + + let vers = match parts.next() { + Some(n) => n, + None => return Err("invalid protover entry"), + }; + + let versions = get_versions(vers)?; + let proto_name = proto.parse()?; + + Ok((proto_name, versions)) +} + +/// Parses a single subprotocol entry string into subprotocol and version +/// parts, and then checks whether any of those versions are unsupported. +/// Helper for protover::all_supported +/// +/// # Inputs +/// +/// Accepted data is in the string format as follows: +/// +/// "HSDir=1-1" +/// +/// # Returns +/// +/// Returns `true` if the protocol entry is well-formatted and only contains +/// versions that are also supported in tor. Otherwise, returns false +/// +fn contains_only_supported_protocols(proto_entry: &str) -> bool { + let (name, mut vers) = match get_proto_and_vers(proto_entry) { + Ok(n) => n, + Err(_) => return false, + }; + + let currently_supported: HashMap<Proto, HashSet<u32>> = + match tor_supported() { + Ok(n) => n, + Err(_) => return false, + }; + + let supported_versions = match currently_supported.get(&name) { + Some(n) => n, + None => return false, + }; + + vers.retain(|x| !supported_versions.contains(x)); + vers.is_empty() +} + +/// Determine if we support every protocol a client supports, and if not, +/// determine which protocols we do not have support for. +/// +/// # Inputs +/// +/// Accepted data is in the string format as follows: +/// +/// "HSDir=1-1 LinkAuth=1-2" +/// +/// # Returns +/// +/// Return `true` if every protocol version is one that we support. +/// Otherwise, return `false`. +/// Optionally, return parameters which the client supports but which we do not +/// +/// # Examples +/// ``` +/// use protover::all_supported; +/// +/// let (is_supported, unsupported) = all_supported("Link=1"); +/// assert_eq!(true, is_supported); +/// +/// let (is_supported, unsupported) = all_supported("Link=5-6"); +/// assert_eq!(false, is_supported); +/// assert_eq!("Link=5-6", unsupported); +/// +pub fn all_supported(protocols: &str) -> (bool, String) { + let unsupported = protocols + .split_whitespace() + .filter(|v| !contains_only_supported_protocols(v)) + .collect::<Vec<&str>>(); + + (unsupported.is_empty(), unsupported.join(" ")) +} + +/// Return true iff the provided protocol list includes support for the +/// indicated protocol and version. +/// Otherwise, return false +/// +/// # Inputs +/// +/// * `list`, a string representation of a list of protocol entries. +/// * `proto`, a `Proto` to test support for +/// * `vers`, a `u32` version which we will go on to determine whether the +/// specified protocol supports. +/// +/// # Examples +/// ``` +/// use protover::*; +/// +/// let is_supported = protover_string_supports_protocol("Link=3-4 Cons=1", +/// Proto::Cons,1); +/// assert_eq!(true, is_supported); +/// +/// let is_not_supported = protover_string_supports_protocol("Link=3-4 Cons=1", +/// Proto::Cons,5); +/// assert_eq!(false, is_not_supported) +/// ``` +pub fn protover_string_supports_protocol( + list: &str, + proto: Proto, + vers: u32, +) -> bool { + let supported: HashMap<Proto, HashSet<u32>>; + + match parse_protocols_from_string(list) { + Ok(result) => supported = result, + Err(_) => return false, + } + + let supported_versions = match supported.get(&proto) { + Some(n) => n, + None => return false, + }; + + supported_versions.contains(&vers) +} + +/// As protover_string_supports_protocol(), but also returns True if +/// any later version of the protocol is supported. +/// +/// # Examples +/// ``` +/// use protover::*; +/// +/// let is_supported = protover_string_supports_protocol_or_later( +/// "Link=3-4 Cons=5", Proto::Cons, 5); +/// +/// assert_eq!(true, is_supported); +/// +/// let is_supported = protover_string_supports_protocol_or_later( +/// "Link=3-4 Cons=5", Proto::Cons, 4); +/// +/// assert_eq!(true, is_supported); +/// +/// let is_supported = protover_string_supports_protocol_or_later( +/// "Link=3-4 Cons=5", Proto::Cons, 6); +/// +/// assert_eq!(false, is_supported); +/// ``` +pub fn protover_string_supports_protocol_or_later( + list: &str, + proto: Proto, + vers: u32, +) -> bool { + let supported: HashMap<Proto, HashSet<u32>>; + + match parse_protocols_from_string(list) { + Ok(result) => supported = result, + Err(_) => return false, + } + + let supported_versions = match supported.get(&proto) { + Some(n) => n, + None => return false, + }; + + supported_versions.iter().any(|v| v >= &vers) +} + +/// Fully expand a version range. For example, 1-3 expands to 1,2,3 +/// Helper for get_versions +/// +/// # Inputs +/// +/// `range`, a string comprised of "[0-9,-]" +/// +/// # Returns +/// +/// A `Result` whose `Ok` value a vector of unsigned integers representing the +/// expanded range of supported versions by a single protocol. +/// Otherwise, the `Err` value of this `Result` is a description of the error +/// +/// # Errors +/// +/// This function will error if: +/// +/// * the specified range is empty +/// * the version range does not contain both a valid lower and upper bound. +/// +fn expand_version_range(range: &str) -> Result<Vec<u32>, &'static str> { + if range.is_empty() { + return Err("version string empty"); + } + + let mut parts = range.split("-"); + + let lower_string = parts.next().ok_or( + "cannot parse protocol range lower bound", + )?; + + let lower = u32::from_str_radix(lower_string, 10).or(Err( + "cannot parse protocol range lower bound", + ))?; + + let higher_string = parts.next().ok_or( + "cannot parse protocol range upper bound", + )?; + + let higher = u32::from_str_radix(higher_string, 10).or(Err( + "cannot parse protocol range upper bound", + ))?; + + // We can use inclusive range syntax when it becomes stable. + Ok((lower..higher + 1).collect()) +} + +/// Checks to see if there is a continuous range of integers, starting at the +/// first in the list. Returns the last integer in the range if a range exists. +/// Helper for compute_vote +/// +/// # Inputs +/// +/// `list`, an ordered vector of `u32` integers of "[0-9,-]" representing the +/// supported versions for a single protocol. +/// +/// # Returns +/// +/// A `bool` indicating whether the list contains a range, starting at the +/// first in the list, and an `u32` of the last integer in the range. +/// +/// For example, if given vec![1, 2, 3, 5], find_range will return true, +/// as there is a continuous range, and 3, which is the last number in the +/// continuous range. +/// +fn find_range(list: &Vec<u32>) -> (bool, u32) { + if list.len() == 0 { + return (false, 0); + } + + let mut iterable = list.iter().peekable(); + let mut range_end = match iterable.next() { + Some(n) => *n, + None => return (false, 0), + }; + + let mut has_range = false; + + while iterable.peek().is_some() { + let n = *iterable.next().unwrap(); + if n != range_end + 1 { + break; + } + + has_range = true; + range_end = n; + } + + (has_range, range_end) +} + +/// Contracts a HashSet representation of supported versions into a string. +/// Helper for compute_vote +/// +/// # Inputs +/// +/// `supported_set`, a set of integers of "[0-9,-]" representing the +/// supported versions for a single protocol. +/// +/// # Returns +/// +/// A `String` representation of this set in ascending order. +/// +fn contract_protocol_list<'a>(supported_set: &'a HashSet<u32>) -> String { + let mut supported: Vec<u32> = supported_set.iter() + .map(|x| *x) + .collect(); + supported.sort(); + + let mut final_output: Vec<String> = Vec::new(); + + while supported.len() != 0 { + let (has_range, end) = find_range(&supported); + let current = supported.remove(0); + + if has_range { + final_output.push(format!( + "{}-{}", + current.to_string(), + &end.to_string(), + )); + supported.retain(|&x| x > end); + } else { + final_output.push(current.to_string()); + } + } + + final_output.join(",") +} + +/// Parses a protocol list without validating the protocol names +/// +/// # Inputs +/// +/// * `protocol_string`, a string comprised of keys and values, both which are +/// strings. The keys are the protocol names while values are a string +/// representation of the supported versions. +/// +/// The input is _not_ expected to be a subset of the Proto types +/// +/// # Returns +/// +/// A `Result` whose `Ok` value is a `HashSet<u32>` holding all of the unique +/// version numbers. +/// +/// The returned `Result`'s `Err` value is an `&'static str` with a description +/// of the error. +/// +/// # Errors +/// +/// This function will error if: +/// +/// * The protocol string does not follow the "protocol_name=version_list" +/// expected format +/// * If the version string is malformed. See `get_versions`. +/// +fn parse_protocols_from_string_with_no_validation<'a>( + protocol_string: &'a str, +) -> Result<HashMap<String, HashSet<u32>>, &'static str> { + let protocols = &protocol_string.split(" ").collect::<Vec<&'a str>>()[..]; + + let mut parsed: HashMap<String, HashSet<u32>> = HashMap::new(); + + for subproto in protocols { + let mut parts = subproto.splitn(2, "="); + + let name = match parts.next() { + Some(n) => n, + None => return Err("invalid protover entry"), + }; + + let vers = match parts.next() { + Some(n) => n, + None => return Err("invalid protover entry"), + }; + + let versions = get_versions(vers)?; + + parsed.insert(String::from(name), versions); + } + Ok(parsed) +} + +/// Protocol voting implementation. +/// +/// Given a list of strings describing protocol versions, return a new +/// string encoding all of the protocols that are listed by at +/// least threshold of the inputs. +/// +/// The string is sorted according to the following conventions: +/// - Protocols names are alphabetized +/// - Protocols are in order low to high +/// - Individual and ranges are listed together. For example, +/// "3, 5-10,13" +/// - All entries are unique +/// +/// # Examples +/// ``` +/// use protover::compute_vote; +/// +/// let protos = vec![String::from("Link=3-4"), String::from("Link=3")]; +/// let vote = compute_vote(protos, 2); +/// assert_eq!("Link=3", vote) +/// ``` +pub fn compute_vote( + list_of_proto_strings: Vec<String>, + threshold: i32, +) -> String { + let empty = String::from(""); + + if list_of_proto_strings.is_empty() { + return empty; + } + + // all_count is a structure to represent the count of the number of + // supported versions for a specific protocol. For example, in JSON format: + // { + // "FirstSupportedProtocol": { + // "1": "3", + // "2": "1" + // } + // } + // means that FirstSupportedProtocol has three votes which support version + // 1, and one vote that supports version 2 + let mut all_count: HashMap<String, HashMap<u32, usize>> = HashMap::new(); + + // parse and collect all of the protos and their versions and collect them + for vote in list_of_proto_strings { + let this_vote: HashMap<String, HashSet<u32>> = + match parse_protocols_from_string_with_no_validation(&vote) { + Ok(result) => result, + Err(_) => continue, + }; + + for (protocol, versions) in this_vote { + let supported_vers: &mut HashMap<u32, usize> = + all_count.entry(protocol).or_insert(HashMap::new()); + + for version in versions { + let counter: &mut usize = + supported_vers.entry(version).or_insert(0); + *counter += 1; + } + } + } + + let mut final_output: HashMap<String, String> = + HashMap::with_capacity(SUPPORTED_PROTOCOLS.len()); + + // Go through and remove verstions that are less than the threshold + for (protocol, versions) in all_count { + let mut meets_threshold = HashSet::new(); + for (version, count) in versions { + if count >= threshold as usize { + meets_threshold.insert(version); + } + } + + // For each protocol, compress its version list into the expected + // protocol version string format + let contracted = contract_protocol_list(&meets_threshold); + if !contracted.is_empty() { + final_output.insert(protocol, contracted); + } + } + + write_vote_to_string(&final_output) +} + +/// Return a String comprised of protocol entries in alphabetical order +/// +/// # Inputs +/// +/// * `vote`, a `HashMap` comprised of keys and values, both which are strings. +/// The keys are the protocol names while values are a string representation of +/// the supported versions. +/// +/// # Returns +/// +/// A `String` whose value is series of pairs, comprising of the protocol name +/// and versions that it supports. The string takes the following format: +/// +/// "first_protocol_name=1,2-5, second_protocol_name=4,5" +/// +/// Sorts the keys in alphabetical order and creates the expected subprotocol +/// entry format. +/// +fn write_vote_to_string(vote: &HashMap<String, String>) -> String { + let mut keys: Vec<&String> = vote.keys().collect(); + keys.sort(); + + let mut output = Vec::new(); + for key in keys { + // TODO error in indexing here? + output.push(format!("{}={}", key, vote[key])); + } + output.join(" ") +} + +/// Returns a boolean indicating whether the given protocol and version is +/// supported in any of the existing Tor protocols +/// +/// # Examples +/// ``` +/// use protover::*; +/// +/// let is_supported = is_supported_here(Proto::Link, 5); +/// assert_eq!(false, is_supported); +/// +/// let is_supported = is_supported_here(Proto::Link, 1); +/// assert_eq!(true, is_supported); +/// ``` +pub fn is_supported_here(proto: Proto, vers: u32) -> bool { + let currently_supported: HashMap<Proto, HashSet<u32>>; + + match tor_supported() { + Ok(result) => currently_supported = result, + Err(_) => return false, + } + + let supported_versions = match currently_supported.get(&proto) { + Some(n) => n, + None => return false, + }; + + supported_versions.contains(&vers) +} + +/// Older versions of Tor cannot infer their own subprotocols +/// Used to determine which subprotocols are supported by older Tor versions. +/// +/// # Inputs +/// +/// * `version`, a string comprised of "[0-9,-]" +/// +/// # Returns +/// +/// A `String` whose value is series of pairs, comprising of the protocol name +/// and versions that it supports. The string takes the following format: +/// +/// "HSDir=1-1 LinkAuth=1" +/// +/// This function returns the protocols that are supported by the version input, +/// only for tor versions older than FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS. +/// +/// C_RUST_COUPLED: src/rust/protover.c `compute_for_old_tor` +pub fn compute_for_old_tor(version: &str) -> String { + if c_tor_version_as_new_as( + version, + FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS, + ) + { + return String::new(); + } + + if c_tor_version_as_new_as(version, "0.2.9.1-alpha") { + let ret = "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1-2 \ + Link=1-4 LinkAuth=1 Microdesc=1-2 Relay=1-2"; + return String::from(ret); + } + + if c_tor_version_as_new_as(version, "0.2.7.5") { + let ret = "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 \ + Link=1-4 LinkAuth=1 Microdesc=1-2 Relay=1-2"; + return String::from(ret); + } + + if c_tor_version_as_new_as(version, "0.2.4.19") { + let ret = "Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 \ + Link=1-4 LinkAuth=1 Microdesc=1 Relay=1-2"; + return String::from(ret); + } + String::new() +} + +#[cfg(test)] +mod test { + #[test] + fn test_get_versions() { + use std::collections::HashSet; + + use super::get_versions; + + assert_eq!(Err("version string is empty"), get_versions("")); + assert_eq!(Err("invalid protocol entry"), get_versions("a,b")); + assert_eq!(Err("invalid protocol entry"), get_versions("1,!")); + + { + let mut versions: HashSet<u32> = HashSet::new(); + versions.insert(1); + assert_eq!(Ok(versions), get_versions("1")); + } + { + let mut versions: HashSet<u32> = HashSet::new(); + versions.insert(1); + versions.insert(2); + assert_eq!(Ok(versions), get_versions("1,2")); + } + { + let mut versions: HashSet<u32> = HashSet::new(); + versions.insert(1); + versions.insert(2); + versions.insert(3); + assert_eq!(Ok(versions), get_versions("1-3")); + } + { + let mut versions: HashSet<u32> = HashSet::new(); + versions.insert(1); + versions.insert(2); + versions.insert(5); + assert_eq!(Ok(versions), get_versions("1-2,5")); + } + { + let mut versions: HashSet<u32> = HashSet::new(); + versions.insert(1); + versions.insert(3); + versions.insert(4); + versions.insert(5); + assert_eq!(Ok(versions), get_versions("1,3-5")); + } + } + + #[test] + fn test_contains_only_supported_protocols() { + use super::contains_only_supported_protocols; + + assert_eq!(false, contains_only_supported_protocols("")); + assert_eq!(false, contains_only_supported_protocols("Cons=")); + assert_eq!(true, contains_only_supported_protocols("Cons=1")); + assert_eq!(false, contains_only_supported_protocols("Cons=0")); + assert_eq!(false, contains_only_supported_protocols("Cons=0-1")); + assert_eq!(false, contains_only_supported_protocols("Cons=5")); + assert_eq!(false, contains_only_supported_protocols("Cons=1-5")); + assert_eq!(false, contains_only_supported_protocols("Cons=1,5")); + assert_eq!(false, contains_only_supported_protocols("Cons=5,6")); + assert_eq!(false, contains_only_supported_protocols("Cons=1,5,6")); + assert_eq!(true, contains_only_supported_protocols("Cons=1,2")); + assert_eq!(true, contains_only_supported_protocols("Cons=1-2")); + } + + #[test] + fn test_find_range() { + use super::find_range; + + assert_eq!((false, 0), find_range(&vec![])); + assert_eq!((false, 1), find_range(&vec![1])); + assert_eq!((true, 2), find_range(&vec![1, 2])); + assert_eq!((true, 3), find_range(&vec![1, 2, 3])); + assert_eq!((true, 3), find_range(&vec![1, 2, 3, 5])); + } + + #[test] + fn test_expand_version_range() { + use super::expand_version_range; + + assert_eq!(Err("version string empty"), expand_version_range("")); + assert_eq!(Ok(vec![1, 2]), expand_version_range("1-2")); + assert_eq!(Ok(vec![1, 2, 3, 4]), expand_version_range("1-4")); + assert_eq!( + Err("cannot parse protocol range lower bound"), + expand_version_range("a") + ); + assert_eq!( + Err("cannot parse protocol range upper bound"), + expand_version_range("1-a") + ); + } + + #[test] + fn test_contract_protocol_list() { + use std::collections::HashSet; + use super::contract_protocol_list; + + { + let mut versions = HashSet::<u32>::new(); + assert_eq!(String::from(""), contract_protocol_list(&versions)); + + versions.insert(1); + assert_eq!(String::from("1"), contract_protocol_list(&versions)); + + versions.insert(2); + assert_eq!(String::from("1-2"), contract_protocol_list(&versions)); + } + + { + let mut versions = HashSet::<u32>::new(); + versions.insert(1); + versions.insert(3); + assert_eq!(String::from("1,3"), contract_protocol_list(&versions)); + } + + { + let mut versions = HashSet::<u32>::new(); + versions.insert(1); + versions.insert(2); + versions.insert(3); + versions.insert(4); + assert_eq!(String::from("1-4"), contract_protocol_list(&versions)); + } + + { + let mut versions = HashSet::<u32>::new(); + versions.insert(1); + versions.insert(3); + versions.insert(5); + versions.insert(6); + versions.insert(7); + assert_eq!( + String::from("1,3,5-7"), + contract_protocol_list(&versions) + ); + } + + { + let mut versions = HashSet::<u32>::new(); + versions.insert(1); + versions.insert(2); + versions.insert(3); + versions.insert(500); + assert_eq!( + String::from("1-3,500"), + contract_protocol_list(&versions) + ); + } + } +} diff --git a/src/rust/protover/tests/protover.rs b/src/rust/protover/tests/protover.rs new file mode 100644 index 0000000000..f4e394b3e2 --- /dev/null +++ b/src/rust/protover/tests/protover.rs @@ -0,0 +1,291 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + +extern crate protover; + +#[test] +fn parse_protocol_list_with_single_proto_and_single_version() { + let protocol = "Cons=1"; + let (is_supported, unsupported) = protover::all_supported(protocol); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn parse_protocol_list_with_single_protocol_and_multiple_versions() { + let protocol = "Cons=1-2"; + let (is_supported, unsupported) = protover::all_supported(protocol); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn parse_protocol_list_with_different_single_protocol_and_single_version() { + let protocol = "HSDir=1"; + let (is_supported, unsupported) = protover::all_supported(protocol); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn parse_protocol_list_with_single_protocol_and_supported_version() { + let protocol = "Desc=2"; + let (is_supported, unsupported) = protover::all_supported(protocol); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn parse_protocol_list_with_two_protocols_and_single_version() { + let protocols = "Cons=1 HSDir=1"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + + +#[test] +fn parse_protocol_list_with_single_protocol_and_two_nonsequential_versions() { + let protocol = "Desc=1,2"; + let (is_supported, unsupported) = protover::all_supported(protocol); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + + +#[test] +fn parse_protocol_list_with_single_protocol_and_two_sequential_versions() { + let protocol = "Desc=1-2"; + let (is_supported, unsupported) = protover::all_supported(protocol); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn parse_protocol_list_with_single_protocol_and_protocol_range_returns_set() { + let protocol = "Link=1-4"; + let (is_supported, unsupported) = protover::all_supported(protocol); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn parse_protocol_list_with_single_protocol_and_protocol_set() { + let protocols = "Link=3-4 Desc=2"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn protover_all_supported_with_two_values() { + let protocols = "Microdesc=1-2 Relay=2"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!("", &unsupported); + assert_eq!(true, is_supported); +} + +#[test] +fn protover_all_supported_with_one_value() { + let protocols = "Microdesc=1-2"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!("", &unsupported); + assert_eq!(true, is_supported); +} + +#[test] +fn protover_all_supported_with_empty() { + let protocols = ""; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(true, is_supported); + assert_eq!("", &unsupported); +} + +#[test] +fn protover_all_supported_with_three_values() { + let protocols = "LinkAuth=1 Microdesc=1-2 Relay=2"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!("", &unsupported); + assert_eq!(true, is_supported); +} + +#[test] +fn protover_all_supported_with_unsupported_protocol() { + let protocols = "Wombat=9"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(false, is_supported); + assert_eq!("Wombat=9", &unsupported); +} + +#[test] +fn protover_all_supported_with_unsupported_versions() { + let protocols = "Link=3-999"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(false, is_supported); + assert_eq!("Link=3-999", &unsupported); +} + +#[test] +fn protover_all_supported_with_unsupported_low_version() { + let protocols = "Cons=0-1"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(false, is_supported); + assert_eq!("Cons=0-1", &unsupported); +} + +#[test] +fn protover_all_supported_with_unsupported_high_version() { + let protocols = "Cons=1-3"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(false, is_supported); + assert_eq!("Cons=1-3", &unsupported); +} + +#[test] +fn protover_all_supported_with_mix_of_supported_and_unsupproted() { + let protocols = "Link=3-4 Wombat=9"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(false, is_supported); + assert_eq!("Wombat=9", &unsupported); +} + +#[test] +fn protover_string_supports_protocol_returns_true_for_single_supported() { + let protocols = "Link=3-4 Cons=1"; + let is_supported = protover::protover_string_supports_protocol( + protocols, + protover::Proto::Cons, + 1, + ); + assert_eq!(true, is_supported); +} + +#[test] +fn protover_string_supports_protocol_returns_false_for_single_unsupported() { + let protocols = "Link=3-4 Cons=1"; + let is_supported = protover::protover_string_supports_protocol( + protocols, + protover::Proto::Cons, + 2, + ); + assert_eq!(false, is_supported); +} + +#[test] +fn protover_string_supports_protocol_returns_false_for_unsupported() { + let protocols = "Link=3-4"; + let is_supported = protover::protover_string_supports_protocol( + protocols, + protover::Proto::Cons, + 2, + ); + assert_eq!(false, is_supported); +} + +#[test] +fn protover_all_supported_with_unexpected_characters() { + let protocols = "Cons=*-%"; + let (is_supported, unsupported) = protover::all_supported(protocols); + assert_eq!(false, is_supported); + assert_eq!("Cons=*-%", &unsupported); +} + +#[test] +fn protover_compute_vote_returns_empty_for_empty_string() { + let protocols = vec![String::from("")]; + let listed = protover::compute_vote(protocols, 1); + assert_eq!("", listed); +} + +#[test] +fn protover_compute_vote_returns_single_protocol_for_matching() { + let protocols = vec![String::from("Cons=1")]; + let listed = protover::compute_vote(protocols, 1); + assert_eq!("Cons=1", listed); +} + +#[test] +fn protover_compute_vote_returns_two_protocols_for_two_matching() { + let protocols = vec![String::from("Link=1 Cons=1")]; + let listed = protover::compute_vote(protocols, 1); + assert_eq!("Cons=1 Link=1", listed); +} + +#[test] +fn protover_compute_vote_returns_one_protocol_when_one_out_of_two_matches() { + let protocols = vec![String::from("Cons=1 Link=2"), String::from("Cons=1")]; + let listed = protover::compute_vote(protocols, 2); + assert_eq!("Cons=1", listed); +} + +#[test] +fn protover_compute_vote_returns_protocols_that_it_doesnt_currently_support() { + let protocols = vec![String::from("Foo=1 Cons=2"), String::from("Bar=1")]; + let listed = protover::compute_vote(protocols, 1); + assert_eq!("Bar=1 Cons=2 Foo=1", listed); +} + +#[test] +fn protover_compute_vote_returns_matching_for_mix() { + let protocols = vec![String::from("Link=1-10,500 Cons=1,3-7,8")]; + let listed = protover::compute_vote(protocols, 1); + assert_eq!("Cons=1,3-8 Link=1-10,500", listed); +} + +#[test] +fn protover_compute_vote_returns_matching_for_longer_mix() { + let protocols = vec![ + String::from("Desc=1-10,500 Cons=1,3-7,8"), + String::from("Link=123-456,78 Cons=2-6,8 Desc=9"), + ]; + + let listed = protover::compute_vote(protocols, 1); + assert_eq!("Cons=1-8 Desc=1-10,500 Link=78,123-456", listed); +} + +#[test] +fn protover_compute_vote_returns_matching_for_longer_mix_with_threshold_two() { + let protocols = vec![ + String::from("Desc=1-10,500 Cons=1,3-7,8"), + String::from("Link=123-456,78 Cons=2-6,8 Desc=9"), + ]; + + let listed = protover::compute_vote(protocols, 2); + assert_eq!("Cons=3-6,8 Desc=9", listed); +} + +#[test] +fn protover_compute_vote_handles_duplicated_versions() { + let protocols = vec![String::from("Cons=1"), String::from("Cons=1")]; + assert_eq!("Cons=1", protover::compute_vote(protocols, 2)); + + let protocols = vec![String::from("Cons=1-2"), String::from("Cons=1-2")]; + assert_eq!("Cons=1-2", protover::compute_vote(protocols, 2)); +} + +#[test] +fn protover_compute_vote_handles_invalid_proto_entries() { + let protocols = vec![ + String::from("Cons=1"), + String::from("Cons=1"), + String::from("Link=a"), + ]; + assert_eq!("Cons=1", protover::compute_vote(protocols, 2)); + + let protocols = vec![ + String::from("Cons=1"), + String::from("Cons=1"), + String::from("Link=1-%"), + ]; + assert_eq!("Cons=1", protover::compute_vote(protocols, 2)); +} + +#[test] +fn protover_is_supported_here_returns_true_for_supported_protocol() { + assert_eq!(true, protover::is_supported_here(protover::Proto::Cons, 1)); +} + +#[test] +fn protover_is_supported_here_returns_false_for_unsupported_protocol() { + assert_eq!(false, protover::is_supported_here(protover::Proto::Cons, 5)); +} diff --git a/src/rust/smartlist/Cargo.toml b/src/rust/smartlist/Cargo.toml new file mode 100644 index 0000000000..51f486c4d7 --- /dev/null +++ b/src/rust/smartlist/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["The Tor Project"] +version = "0.0.1" +name = "smartlist" + +[dependencies] +libc = "0.2.22" + +[lib] +name = "smartlist" +path = "lib.rs" +crate_type = ["rlib", "staticlib"] + diff --git a/src/rust/smartlist/lib.rs b/src/rust/smartlist/lib.rs new file mode 100644 index 0000000000..14a8148315 --- /dev/null +++ b/src/rust/smartlist/lib.rs @@ -0,0 +1,8 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + +extern crate libc; + +mod smartlist; + +pub use smartlist::*; diff --git a/src/rust/smartlist/smartlist.rs b/src/rust/smartlist/smartlist.rs new file mode 100644 index 0000000000..ec5d7a57f5 --- /dev/null +++ b/src/rust/smartlist/smartlist.rs @@ -0,0 +1,115 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + +use std::slice; +use libc::{c_char, c_int}; +use std::ffi::CStr; + +/// Smartlists are a type used in C code in tor to define a collection of a +/// generic type, which has a capacity and a number used. Each Smartlist +/// defines how to extract the list of values from the underlying C structure +/// +/// Implementations are required to have a C representation, as this module +/// serves purely to translate smartlists as defined in tor to vectors in Rust. +pub trait Smartlist<T> { + fn get_list(&self) -> Vec<T>; +} + +#[repr(C)] +pub struct Stringlist { + pub list: *const *const c_char, + pub num_used: c_int, + pub capacity: c_int, +} + +impl Smartlist<String> for Stringlist { + fn get_list(&self) -> Vec<String> { + let empty: Vec<String> = Vec::new(); + let mut rust_list: Vec<String> = Vec::new(); + + if self.list.is_null() || self.num_used == 0 { + return empty; + } + + // unsafe, as we need to extract the smartlist list into a vector of + // pointers, and then transform each element into a Rust string. + let elems: &[*const i8] = + unsafe { slice::from_raw_parts(self.list, self.num_used as usize) }; + + for elem in elems.iter() { + if elem.is_null() { + continue; + } + + // unsafe, as we need to create a cstring from the referenced + // element + let c_string = unsafe { CStr::from_ptr(*elem) }; + + let r_string = match c_string.to_str() { + Ok(n) => n, + Err(_) => return empty, + }; + + rust_list.push(String::from(r_string)); + } + + rust_list + } +} + +// TODO: CHK: this module maybe should be tested from a test in C with a +// smartlist as defined in tor. +#[cfg(test)] +mod test { + #[test] + fn test_get_list_of_strings() { + extern crate libc; + + use std::ffi::CString; + use libc::c_char; + + use super::Smartlist; + use super::Stringlist; + + { + // test to verify that null pointers are gracefully handled + use std::ptr; + + let sl = Stringlist { + list: ptr::null(), + num_used: 0, + capacity: 0, + }; + + let data = sl.get_list(); + assert_eq!(0, data.len()); + } + + { + let args = vec![String::from("a"), String::from("b")]; + + // for each string, transform it into a CString + let c_strings: Vec<_> = args.iter() + .map(|arg| CString::new(arg.as_str()).unwrap()) + .collect(); + + // then, collect a pointer for each CString + let p_args: Vec<_> = + c_strings.iter().map(|arg| arg.as_ptr()).collect(); + + let p: *const *const c_char = p_args.as_ptr(); + + // This is the representation that we expect when receiving a + // smartlist at the Rust/C FFI layer. + let sl = Stringlist { + list: p, + num_used: 2, + capacity: 2, + }; + + let data = sl.get_list(); + assert_eq!("a", &data[0]); + assert_eq!("b", &data[1]); + } + } +} diff --git a/src/rust/tor_allocate/Cargo.toml b/src/rust/tor_allocate/Cargo.toml new file mode 100644 index 0000000000..ceb08b78ab --- /dev/null +++ b/src/rust/tor_allocate/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["The Tor Project"] +version = "0.0.1" +name = "tor_allocate" + +[dependencies] +libc = "0.2.22" + +[lib] +name = "tor_allocate" +path = "lib.rs" +crate_type = ["rlib", "staticlib"] + diff --git a/src/rust/tor_allocate/lib.rs b/src/rust/tor_allocate/lib.rs new file mode 100644 index 0000000000..937a5dcf63 --- /dev/null +++ b/src/rust/tor_allocate/lib.rs @@ -0,0 +1,15 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + +//! Allocation helper functions that allow data to be allocated in Rust +//! using tor's specified allocator. In doing so, this can be later freed +//! from C. +//! +//! This is currently a temporary solution, we will later use tor's allocator +//! by default for any allocation that occurs in Rust. However, as this will +//! stabalize in 2018, we can use this as a temporary measure. + +extern crate libc; + +mod tor_allocate; +pub use tor_allocate::*; diff --git a/src/rust/tor_allocate/tor_allocate.rs b/src/rust/tor_allocate/tor_allocate.rs new file mode 100644 index 0000000000..359df1cd7a --- /dev/null +++ b/src/rust/tor_allocate/tor_allocate.rs @@ -0,0 +1,97 @@ +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + +use libc::{c_char, c_void}; +use std::{ptr, slice, mem}; + +#[cfg(not(test))] +extern "C" { + fn tor_malloc_(size: usize) -> *mut c_void; +} + +// Defined only for tests, used for testing purposes, so that we don't need +// to link to tor C files. Uses the system allocator +#[cfg(test)] +unsafe extern "C" fn tor_malloc_(size: usize) -> *mut c_void { + use libc::malloc; + malloc(size) +} + +/// Allocate memory using tor_malloc_ and copy an existing string into the +/// allocated buffer, returning a pointer that can later be called in C. +/// +/// # Inputs +/// +/// * `src`, a reference to a String. +/// +/// # Returns +/// +/// A `*mut c_char` that should be freed by tor_free in C +/// +pub fn allocate_and_copy_string(src: &String) -> *mut c_char { + let bytes: &[u8] = src.as_bytes(); + + let size = mem::size_of_val::<[u8]>(bytes); + let size_one_byte = mem::size_of::<u8>(); + + // handle integer overflow when adding one to the calculated length + let size_with_null_byte = match size.checked_add(size_one_byte) { + Some(n) => n, + None => return ptr::null_mut(), + }; + + let dest = unsafe { tor_malloc_(size_with_null_byte) as *mut u8 }; + + if dest.is_null() { + return ptr::null_mut(); + } + + unsafe { ptr::copy_nonoverlapping(bytes.as_ptr(), dest, size) }; + + // set the last byte as null, using the ability to index into a slice + // rather than doing pointer arithmatic + let slice = unsafe { slice::from_raw_parts_mut(dest, size_with_null_byte) }; + slice[size] = 0; // add a null terminator + + dest as *mut c_char +} + +#[cfg(test)] +mod test { + + #[test] + fn test_allocate_and_copy_string_with_empty() { + use std::ffi::CStr; + use libc::{free, c_void}; + + use tor_allocate::allocate_and_copy_string; + + let empty = String::new(); + let allocated_empty = allocate_and_copy_string(&empty); + + let allocated_empty_rust = + unsafe { CStr::from_ptr(allocated_empty).to_str().unwrap() }; + + assert_eq!("", allocated_empty_rust); + + unsafe { free(allocated_empty as *mut c_void) }; + } + + #[test] + fn test_allocate_and_copy_string_with_not_empty_string() { + use std::ffi::CStr; + use libc::{free, c_void}; + + use tor_allocate::allocate_and_copy_string; + + let empty = String::from("foo bar biz"); + let allocated_empty = allocate_and_copy_string(&empty); + + let allocated_empty_rust = + unsafe { CStr::from_ptr(allocated_empty).to_str().unwrap() }; + + assert_eq!("foo bar biz", allocated_empty_rust); + + unsafe { free(allocated_empty as *mut c_void) }; + } +} diff --git a/src/rust/tor_rust/Cargo.toml b/src/rust/tor_rust/Cargo.toml new file mode 100644 index 0000000000..86fad3ee76 --- /dev/null +++ b/src/rust/tor_rust/Cargo.toml @@ -0,0 +1,16 @@ +[package] +authors = ["The Tor Project"] +name = "tor_rust" +version = "0.1.0" + +[lib] +name = "tor_rust" +path = "lib.rs" +crate_type = ["rlib", "staticlib"] + +[dependencies.tor_util] +path = "../tor_util" + +[dependencies.protover] +path = "../protover" + diff --git a/src/rust/tor_rust/include.am b/src/rust/tor_rust/include.am new file mode 100644 index 0000000000..c02324cb77 --- /dev/null +++ b/src/rust/tor_rust/include.am @@ -0,0 +1,30 @@ +EXTRA_DIST +=\ + src/rust/tor_rust/Cargo.toml \ + src/rust/tor_rust/lib.rs + +EXTRA_CARGO_OPTIONS= + +src/rust/target/release/@TOR_RUST_STATIC_NAME@: FORCE + ( cd "$(abs_top_builddir)/src/rust" ; \ + CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \ + CARGO_HOME="$(abs_top_builddir)/src/rust" \ + $(CARGO) build --release $(EXTRA_CARGO_OPTIONS) \ + $(CARGO_ONLINE) \ + --manifest-path "$(abs_top_srcdir)/src/rust/tor_rust/Cargo.toml" ) + +distclean-rust: + ( cd "$(abs_top_builddir)/src/rust" ; \ + CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \ + CARGO_HOME="$(abs_top_builddir)/src/rust" \ + $(CARGO) clean $(EXTRA_CARGO_OPTIONS) \ + $(CARGO_ONLINE) \ + --manifest-path "$(abs_top_srcdir)/src/rust/tor_rust/Cargo.toml" ) + rm -rf "$(abs_top_builddir)/src/rust/registry" + +if USE_RUST +build-rust: src/rust/target/release/@TOR_RUST_STATIC_NAME@ +else +build-rust: +endif + +FORCE: diff --git a/src/rust/tor_rust/lib.rs b/src/rust/tor_rust/lib.rs new file mode 100644 index 0000000000..c1585c0480 --- /dev/null +++ b/src/rust/tor_rust/lib.rs @@ -0,0 +1,5 @@ +extern crate tor_util; +extern crate protover; + +pub use tor_util::*; +pub use protover::*; diff --git a/src/rust/tor_util/Cargo.toml b/src/rust/tor_util/Cargo.toml index f175fbdfb0..d7379a5988 100644 --- a/src/rust/tor_util/Cargo.toml +++ b/src/rust/tor_util/Cargo.toml @@ -8,6 +8,9 @@ name = "tor_util" path = "lib.rs" crate_type = ["rlib", "staticlib"] +[dependencies.tor_allocate] +path = "../tor_allocate" + [dependencies] -libc = "*" +libc = "0.2.22" diff --git a/src/rust/tor_util/ffi.rs b/src/rust/tor_util/ffi.rs index af4bfc41af..5c3cdba4be 100644 --- a/src/rust/tor_util/ffi.rs +++ b/src/rust/tor_util/ffi.rs @@ -1,56 +1,26 @@ -//! FFI functions, only to be called from C. -//! -//! Equivalent C versions of these live in `src/common/compat_rust.c` - -use std::mem::forget; -use std::ffi::CString; +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ -use libc; -use rust_string::RustString; - -/// Free the passed `RustString` (`rust_str_t` in C), to be used in place of -/// `tor_free`(). -/// -/// # Examples -/// ```c -/// rust_str_t r_s = rust_welcome_string(); -/// rust_str_free(r_s); -/// ``` -#[no_mangle] -#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))] -pub unsafe extern "C" fn rust_str_free(_str: RustString) { - // Empty body: Just drop _str and we're done (Drop takes care of it). -} +//! FFI functions to announce Rust support during tor startup, only to be +//! called from C. +//! -/// Lends an immutable, NUL-terminated C String. -/// -/// # Examples -/// ```c -/// rust_str_t r_s = rust_welcome_string(); -/// const char *s = rust_str_get(r_s); -/// printf("%s", s); -/// rust_str_free(r_s); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn rust_str_get(str: RustString) -> *const libc::c_char { - let res = str.as_ptr(); - forget(str); - res -} +use libc::c_char; +use tor_allocate::allocate_and_copy_string; /// Returns a short string to announce Rust support during startup. /// /// # Examples /// ```c -/// rust_str_t r_s = rust_welcome_string(); -/// const char *s = rust_str_get(r_s); -/// printf("%s", s); -/// rust_str_free(r_s); +/// char *rust_str = rust_welcome_string(); +/// printf("%s", rust_str); +/// tor_free(rust_str); /// ``` #[no_mangle] -pub extern "C" fn rust_welcome_string() -> RustString { - let s = CString::new("Tor is running with Rust integration. Please report \ - any bugs you encouter.") - .unwrap(); - RustString::from(s) +pub extern "C" fn rust_welcome_string() -> *mut c_char { + let rust_welcome = String::from( + "Tor is running with Rust integration. Please report \ + any bugs you encounter.", + ); + allocate_and_copy_string(&rust_welcome) } diff --git a/src/rust/tor_util/include.am b/src/rust/tor_util/include.am deleted file mode 100644 index ec3898577b..0000000000 --- a/src/rust/tor_util/include.am +++ /dev/null @@ -1,13 +0,0 @@ -EXTRA_DIST +=\ - src/rust/tor_util/Cargo.toml \ - src/rust/tor_util/lib.rs \ - src/rust/tor_util/ffi.rs \ - src/rust/tor_util/rust_string.rs - -src/rust/target/release/@TOR_RUST_UTIL_STATIC_NAME@: FORCE - ( cd "$(abs_top_srcdir)/src/rust/tor_util" ; \ - CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \ - CARGO_HOME="$(abs_top_builddir)/src/rust" \ - $(CARGO) build --release --quiet $(CARGO_ONLINE) ) - -FORCE: diff --git a/src/rust/tor_util/lib.rs b/src/rust/tor_util/lib.rs index 79d583d1ae..42fa9d5ad0 100644 --- a/src/rust/tor_util/lib.rs +++ b/src/rust/tor_util/lib.rs @@ -1,13 +1,11 @@ -//! C <-> Rust compatibility helpers and types. +// Copyright (c) 2016-2017, The Tor Project, Inc. */ +// See LICENSE for licensing information */ + +//! Small module to announce Rust support during startup for demonstration +//! purposes. //! -//! Generically useful, small scale helpers should go here. This goes for both -//! the C side (in the form of the ffi module) as well as the Rust side -//! (individual modules per functionality). The corresponding C stuff lives in -//! `src/common/compat_rust.{c,h}`. extern crate libc; +extern crate tor_allocate; -mod rust_string; pub mod ffi; - -pub use rust_string::*; diff --git a/src/rust/tor_util/rust_string.rs b/src/rust/tor_util/rust_string.rs deleted file mode 100644 index 46ec3fd7a8..0000000000 --- a/src/rust/tor_util/rust_string.rs +++ /dev/null @@ -1,101 +0,0 @@ -use std::ffi::CString; -use std::mem::forget; -use libc; - -/// Compatibility wrapper for strings allocated in Rust and passed to C. -/// -/// Rust doesn't ensure the safety of freeing memory across an FFI boundary, so -/// we need to take special care to ensure we're not accidentally calling -/// `tor_free`() on any string allocated in Rust. To more easily differentiate -/// between strings that possibly (if Rust support is enabled) were allocated -/// in Rust, C has the `rust_str_t` helper type. The equivalent on the Rust -/// side is `RustString`. -/// -/// Note: This type must not be used for strings allocated in C. -#[repr(C)] -#[derive(Debug)] -pub struct RustString(*mut libc::c_char); - -impl RustString { - /// Returns a pointer to the underlying NUL-terminated byte array. - /// - /// Note that this function is not typically useful for Rust callers, - /// except in a direct FFI context. - /// - /// # Examples - /// ``` - /// # use tor_util::RustString; - /// use std::ffi::CString; - /// - /// let r = RustString::from(CString::new("asdf").unwrap()); - /// let c_str = r.as_ptr(); - /// assert_eq!(b'a', unsafe { *c_str as u8}); - /// ``` - pub fn as_ptr(&self) -> *const libc::c_char { - self.0 as *const libc::c_char - } -} - -impl From<CString> for RustString { - /// Constructs a new `RustString` - /// - /// # Examples - /// ``` - /// # use tor_util::RustString; - /// use std::ffi::CString; - /// - /// let r = RustString::from(CString::new("asdf").unwrap()); - /// ``` - fn from(str: CString) -> RustString { - RustString(str.into_raw()) - } -} - -impl Into<CString> for RustString { - /// Reconstructs a `CString` from this `RustString`. - /// - /// Useful to take ownership back from a `RustString` that was given to C - /// code. - /// - /// # Examples - /// ``` - /// # use tor_util::RustString; - /// use std::ffi::CString; - /// - /// let cs = CString::new("asdf").unwrap(); - /// let r = RustString::from(cs.clone()); - /// let cs2 = r.into(); - /// assert_eq!(cs, cs2); - /// ``` - fn into(self) -> CString { - // Calling from_raw is always OK here: We only construct self using - // valid CStrings and don't expose anything that could mutate it - let ret = unsafe { CString::from_raw(self.0) }; - forget(self); - ret - } -} - -impl Drop for RustString { - fn drop(&mut self) { - // Don't use into() here, because we would need to move out of - // self. Same safety consideration. Immediately drop the created - // CString, which takes care of freeing the wrapped string. - unsafe { CString::from_raw(self.0) }; - } -} - -#[cfg(test)] -mod test { - use std::mem; - use super::*; - - use libc; - - /// Ensures we're not adding overhead by using RustString. - #[test] - fn size_of() { - assert_eq!(mem::size_of::<*mut libc::c_char>(), - mem::size_of::<RustString>()) - } -} diff --git a/src/rust/tor_util/tests/rust_string.rs b/src/rust/tor_util/tests/rust_string.rs deleted file mode 100644 index 1ff605a43c..0000000000 --- a/src/rust/tor_util/tests/rust_string.rs +++ /dev/null @@ -1,37 +0,0 @@ -extern crate tor_util; -extern crate libc; - -use std::ffi::CString; -use tor_util::RustString; - -#[test] -fn rust_string_conversions_preserve_c_string() { - let s = CString::new("asdf foo").unwrap(); - let r = RustString::from(s.clone()); - let r2 = RustString::from(s.clone()); - let c = r2.as_ptr(); - assert_eq!(unsafe { libc::strlen(c) }, 8); - let c_str = r.into(); - assert_eq!(s, c_str); -} - -#[test] -fn empty_string() { - let s = CString::new("").unwrap(); - let r = RustString::from(s.clone()); - let c = r.as_ptr(); - assert_eq!(unsafe { libc::strlen(c) }, 0); - let c_str = r.into(); - assert_eq!(s, c_str); -} - -#[test] -fn c_string_with_unicode() { - // The euro sign is three bytes - let s = CString::new("asd€asd").unwrap(); - let r = RustString::from(s.clone()); - let c = r.as_ptr(); - assert_eq!(unsafe { libc::strlen(c) }, 9); - let c_str = r.into(); - assert_eq!(s, c_str); -} diff --git a/src/test/Makefile.nmake b/src/test/Makefile.nmake index 605f1a92c3..cfbe281b94 100644 --- a/src/test/Makefile.nmake +++ b/src/test/Makefile.nmake @@ -18,6 +18,7 @@ TEST_OBJECTS = test.obj test_addr.obj test_channel.obj test_channeltls.obj \ test_config.obj test_connection.obj \ test_cell_formats.obj test_relay.obj test_replay.obj \ test_channelpadding.obj \ + test_circuitstats.obj \ test_scheduler.obj test_introduce.obj test_hs.obj tinytest.obj tinytest.obj: ..\ext\tinytest.c diff --git a/src/test/bench.c b/src/test/bench.c index b7b123eee2..f30b609900 100644 --- a/src/test/bench.c +++ b/src/test/bench.c @@ -3,11 +3,6 @@ * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ -extern const char tor_git_revision[]; -/* Ordinarily defined in tor_main.c; this bit is just here to provide one - * since we're not linking to tor_main.c */ -const char tor_git_revision[] = ""; - /** * \file bench.c * \brief Benchmarks for lower level Tor modules. diff --git a/src/test/fakechans.h b/src/test/fakechans.h index c0de430e3d..ab5d8461b6 100644 --- a/src/test/fakechans.h +++ b/src/test/fakechans.h @@ -20,7 +20,6 @@ void scheduler_release_channel_mock(channel_t *ch); /* Query some counters used by the exposed mocks */ int get_mock_scheduler_has_waiting_cells_count(void); -int get_mock_scheduler_release_channel_count(void); #endif /* !defined(TOR_FAKECHANS_H) */ diff --git a/src/test/fuzz/fuzzing_common.c b/src/test/fuzz/fuzzing_common.c index 1e98eb6c85..1d54e41dbd 100644 --- a/src/test/fuzz/fuzzing_common.c +++ b/src/test/fuzz/fuzzing_common.c @@ -9,9 +9,6 @@ #include "crypto.h" #include "crypto_ed25519.h" -extern const char tor_git_revision[]; -const char tor_git_revision[] = ""; - static or_options_t *mock_options = NULL; static const or_options_t * mock_get_options(void) diff --git a/src/test/include.am b/src/test/include.am index 610918e9da..a4b9705fdd 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -98,6 +98,7 @@ src_test_test_SOURCES = \ src/test/test_circuitmux.c \ src/test/test_circuitbuild.c \ src/test/test_circuituse.c \ + src/test/test_circuitstats.c \ src/test/test_compat_libevent.c \ src/test/test_config.c \ src/test/test_connection.c \ @@ -125,6 +126,7 @@ src_test_test_SOURCES = \ src/test/test_hs_service.c \ src/test/test_hs_client.c \ src/test/test_hs_intropoint.c \ + src/test/test_hs_control.c \ src/test/test_handles.c \ src/test/test_hs_cache.c \ src/test/test_hs_descriptor.c \ @@ -152,7 +154,6 @@ src_test_test_SOURCES = \ src/test/test_routerkeys.c \ src/test/test_routerlist.c \ src/test/test_routerset.c \ - src/test/test_rust.c \ src/test/test_scheduler.c \ src/test/test_shared_random.c \ src/test/test_socks.c \ diff --git a/src/test/test-memwipe.c b/src/test/test-memwipe.c index 484f13dd05..89d946d506 100644 --- a/src/test/test-memwipe.c +++ b/src/test/test-memwipe.c @@ -1,3 +1,6 @@ +/* Copyright (c) 2015-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + #include "orconfig.h" #include <string.h> #include <stdio.h> diff --git a/src/test/test-timers.c b/src/test/test-timers.c index 99715f4333..a0b5b535c2 100644 --- a/src/test/test-timers.c +++ b/src/test/test-timers.c @@ -133,7 +133,7 @@ main(int argc, char **argv) ret = 0; } - timer_free(NULL); + timer_free_(NULL); for (i = 0; i < N_TIMERS; ++i) { timer_free(timers[i]); diff --git a/src/test/test.c b/src/test/test.c index da4f7a0553..f225fd277d 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -345,8 +345,8 @@ test_onion_queues(void *arg) tt_int_op(0,OP_EQ, onion_num_pending(ONION_HANDSHAKE_TYPE_NTOR)); done: - circuit_free(TO_CIRCUIT(circ1)); - circuit_free(TO_CIRCUIT(circ2)); + circuit_free_(TO_CIRCUIT(circ1)); + circuit_free_(TO_CIRCUIT(circ2)); tor_free(create1); tor_free(create2); tor_free(onionskin); @@ -616,7 +616,7 @@ test_rend_fns(void *arg) done: if (descs) { for (i = 0; i < smartlist_len(descs); i++) - rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i)); + rend_encoded_v2_service_descriptor_free_(smartlist_get(descs, i)); smartlist_free(descs); } if (parsed) @@ -1178,6 +1178,7 @@ struct testgroup_t testgroups[] = { { "circuitlist/", circuitlist_tests }, { "circuitmux/", circuitmux_tests }, { "circuituse/", circuituse_tests }, + { "circuitstats/", circuitstats_tests }, { "compat/libevent/", compat_libevent_tests }, { "config/", config_tests }, { "connection/", connection_tests }, @@ -1201,6 +1202,7 @@ struct testgroup_t testgroups[] = { { "hs_cell/", hs_cell_tests }, { "hs_common/", hs_common_tests }, { "hs_config/", hs_config_tests }, + { "hs_control/", hs_control_tests }, { "hs_descriptor/", hs_descriptor }, { "hs_ntor/", hs_ntor_tests }, { "hs_service/", hs_service_tests }, @@ -1227,7 +1229,6 @@ struct testgroup_t testgroups[] = { { "routerkeys/", routerkeys_tests }, { "routerlist/", routerlist_tests }, { "routerset/" , routerset_tests }, - { "rust/", rust_tests }, { "scheduler/", scheduler_tests }, { "socks/", socks_tests }, { "shared-random/", sr_tests }, diff --git a/src/test/test.h b/src/test/test.h index 634f3e7b02..93d527ba17 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -188,6 +188,7 @@ extern struct testcase_t circuitbuild_tests[]; extern struct testcase_t circuitlist_tests[]; extern struct testcase_t circuitmux_tests[]; extern struct testcase_t circuituse_tests[]; +extern struct testcase_t circuitstats_tests[]; extern struct testcase_t compat_libevent_tests[]; extern struct testcase_t config_tests[]; extern struct testcase_t connection_tests[]; @@ -210,6 +211,7 @@ extern struct testcase_t hs_cache[]; extern struct testcase_t hs_cell_tests[]; extern struct testcase_t hs_common_tests[]; extern struct testcase_t hs_config_tests[]; +extern struct testcase_t hs_control_tests[]; extern struct testcase_t hs_descriptor[]; extern struct testcase_t hs_ntor_tests[]; extern struct testcase_t hs_service_tests[]; @@ -239,7 +241,6 @@ extern struct testcase_t router_tests[]; extern struct testcase_t routerkeys_tests[]; extern struct testcase_t routerlist_tests[]; extern struct testcase_t routerset_tests[]; -extern struct testcase_t rust_tests[]; extern struct testcase_t scheduler_tests[]; extern struct testcase_t storagedir_tests[]; extern struct testcase_t socks_tests[]; diff --git a/src/test/test_accounting.c b/src/test/test_accounting.c index 7edba988a6..b0d37b2989 100644 --- a/src/test/test_accounting.c +++ b/src/test/test_accounting.c @@ -1,3 +1,6 @@ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + #include "or.h" #include "test.h" #define HIBERNATE_PRIVATE diff --git a/src/test/test_address.c b/src/test/test_address.c index f36ff6998b..9c88d37a41 100644 --- a/src/test/test_address.c +++ b/src/test/test_address.c @@ -763,7 +763,7 @@ test_address_get_if_addrs_list_internal(void *arg) tt_assert(!smartlist_contains_ipv6_tor_addr(results)); done: - free_interface_address_list(results); + interface_address_list_free(results); return; } @@ -792,7 +792,7 @@ test_address_get_if_addrs_list_no_internal(void *arg) tt_assert(!smartlist_contains_ipv6_tor_addr(results)); done: - free_interface_address_list(results); + interface_address_list_free(results); return; } @@ -834,7 +834,7 @@ test_address_get_if_addrs6_list_internal(void *arg) } done: - free_interface_address6_list(results); + interface_address6_list_free(results); teardown_capture_of_logs(); return; } @@ -878,7 +878,7 @@ test_address_get_if_addrs6_list_no_internal(void *arg) done: teardown_capture_of_logs(); - free_interface_address6_list(results); + interface_address6_list_free(results); return; } @@ -943,8 +943,8 @@ test_address_get_if_addrs_internal_fail(void *arg) done: UNMOCK(get_interface_addresses_raw); UNMOCK(get_interface_address6_via_udp_socket_hack); - free_interface_address6_list(results1); - free_interface_address6_list(results2); + interface_address6_list_free(results1); + interface_address6_list_free(results2); return; } @@ -971,8 +971,8 @@ test_address_get_if_addrs_no_internal_fail(void *arg) done: UNMOCK(get_interface_addresses_raw); UNMOCK(get_interface_address6_via_udp_socket_hack); - free_interface_address6_list(results1); - free_interface_address6_list(results2); + interface_address6_list_free(results1); + interface_address6_list_free(results2); return; } diff --git a/src/test/test_buffers.c b/src/test/test_buffers.c index 9f4e19bc5f..057d9fa2dc 100644 --- a/src/test/test_buffers.c +++ b/src/test/test_buffers.c @@ -450,52 +450,54 @@ test_buffer_time_tracking(void *arg) tt_assert(buf); monotime_coarse_set_mock_time_nsec(START_NSEC); - const uint32_t START_MSEC = (uint32_t)monotime_coarse_absolute_msec(); + const uint32_t START_TS = monotime_coarse_get_stamp(); /* Empty buffer means the timestamp is 0. */ - tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC)); - tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+1000)); + tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS)); + tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS+1000)); buf_add(buf, "ABCDEFG", 7); - tt_int_op(1000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+1000)); + tt_int_op(1000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS+1000)); buf2 = buf_copy(buf); tt_assert(buf2); tt_int_op(1234, OP_EQ, - buf_get_oldest_chunk_timestamp(buf2, START_MSEC+1234)); + buf_get_oldest_chunk_timestamp(buf2, START_TS+1234)); /* Now add more bytes; enough to overflow the first chunk. */ monotime_coarse_set_mock_time_nsec(START_NSEC + 123 * (uint64_t)1000000); + const uint32_t TS2 = monotime_coarse_get_stamp(); for (i = 0; i < 600; ++i) buf_add(buf, "ABCDEFG", 7); tt_int_op(4207, OP_EQ, buf_datalen(buf)); /* The oldest bytes are still in the front. */ - tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2000)); + tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS+2000)); /* Once those bytes are dropped, the chunk is still on the first * timestamp. */ buf_get_bytes(buf, tmp, 100); - tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2000)); + tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS+2000)); /* But once we discard the whole first chunk, we get the data in the second * chunk. */ buf_get_bytes(buf, tmp, 4000); tt_int_op(107, OP_EQ, buf_datalen(buf)); - tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2123)); + tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, TS2+2000)); /* This time we'll be grabbing a chunk from the freelist, and making sure its time gets updated */ monotime_coarse_set_mock_time_nsec(START_NSEC + 5617 * (uint64_t)1000000); + const uint32_t TS3 = monotime_coarse_get_stamp(); for (i = 0; i < 600; ++i) buf_add(buf, "ABCDEFG", 7); tt_int_op(4307, OP_EQ, buf_datalen(buf)); - tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2123)); + tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, TS2+2000)); buf_get_bytes(buf, tmp, 4000); buf_get_bytes(buf, tmp, 306); - tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+5617)); - tt_int_op(383, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+6000)); + tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, TS3)); + tt_int_op(383, OP_EQ, buf_get_oldest_chunk_timestamp(buf, TS3+383)); done: buf_free(buf); diff --git a/src/test/test_cell_queue.c b/src/test/test_cell_queue.c index 69e89b69b0..df987f82ce 100644 --- a/src/test/test_cell_queue.c +++ b/src/test/test_cell_queue.c @@ -130,8 +130,8 @@ test_circuit_n_cells(void *arg) tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(origin_c)), OP_EQ, 2); done: - circuit_free(TO_CIRCUIT(or_c)); - circuit_free(TO_CIRCUIT(origin_c)); + circuit_free_(TO_CIRCUIT(or_c)); + circuit_free_(TO_CIRCUIT(origin_c)); } struct testcase_t cell_queue_tests[] = { diff --git a/src/test/test_channel.c b/src/test/test_channel.c index 023c2950c9..bdc9d32f78 100644 --- a/src/test/test_channel.c +++ b/src/test/test_channel.c @@ -6,8 +6,10 @@ #include "or.h" #include "channel.h" /* For channel_note_destroy_not_pending */ +#define CIRCUITLIST_PRIVATE #include "circuitlist.h" #include "circuitmux.h" +#include "circuitmux_ewma.h" /* For var_cell_free */ #include "connection_or.h" /* For packed_cell stuff */ @@ -15,8 +17,10 @@ #include "relay.h" /* For init/free stuff */ #include "scheduler.h" +#include "networkstatus.h" /* Test suite stuff */ +#include "log_test_helpers.h" #include "test.h" #include "fakechans.h" @@ -26,62 +30,18 @@ static cell_t * test_chan_last_seen_fixed_cell_ptr = NULL; static int test_chan_var_cells_recved = 0; static var_cell_t * test_chan_last_seen_var_cell_ptr = NULL; static int test_cells_written = 0; -static int test_destroy_not_pending_calls = 0; static int test_doesnt_want_writes_count = 0; static int test_dumpstats_calls = 0; static int test_has_waiting_cells_count = 0; -static double test_overhead_estimate = 1.0; static int test_releases_count = 0; -static circuitmux_t *test_target_cmux = NULL; -static unsigned int test_cmux_cells = 0; static channel_t *dump_statistics_mock_target = NULL; static int dump_statistics_mock_matches = 0; - -static void chan_test_channel_dump_statistics_mock( - channel_t *chan, int severity); -static int chan_test_channel_flush_from_first_active_circuit_mock( - channel_t *chan, int max); -static unsigned int chan_test_circuitmux_num_cells_mock(circuitmux_t *cmux); -static void channel_note_destroy_not_pending_mock(channel_t *ch, - circid_t circid); -static void chan_test_cell_handler(channel_t *ch, - cell_t *cell); -static const char * chan_test_describe_transport(channel_t *ch); -static void chan_test_dumpstats(channel_t *ch, int severity); -static void chan_test_var_cell_handler(channel_t *ch, - var_cell_t *var_cell); -static void chan_test_close(channel_t *ch); -static void chan_test_error(channel_t *ch); -static void chan_test_finish_close(channel_t *ch); -static const char * chan_test_get_remote_descr(channel_t *ch, int flags); -static int chan_test_is_canonical(channel_t *ch, int req); -static size_t chan_test_num_bytes_queued(channel_t *ch); -static int chan_test_num_cells_writeable(channel_t *ch); -static int chan_test_write_cell(channel_t *ch, cell_t *cell); -static int chan_test_write_packed_cell(channel_t *ch, - packed_cell_t *packed_cell); -static int chan_test_write_var_cell(channel_t *ch, var_cell_t *var_cell); -static void scheduler_channel_doesnt_want_writes_mock(channel_t *ch); - -static void test_channel_dumpstats(void *arg); -static void test_channel_flush(void *arg); -static void test_channel_flushmux(void *arg); -static void test_channel_incoming(void *arg); -static void test_channel_lifecycle(void *arg); -static void test_channel_multi(void *arg); -static void test_channel_queue_incoming(void *arg); -static void test_channel_queue_size(void *arg); -static void test_channel_write(void *arg); - -static void -channel_note_destroy_not_pending_mock(channel_t *ch, - circid_t circid) -{ - (void)ch; - (void)circid; - - ++test_destroy_not_pending_calls; -} +static int test_close_called = 0; +static int test_chan_should_be_canonical = 0; +static int test_chan_should_match_target = 0; +static int test_chan_canonical_should_be_reliable = 0; +static int test_chan_listener_close_fn_called = 0; +static int test_chan_listener_fn_called = 0; static const char * chan_test_describe_transport(channel_t *ch) @@ -112,71 +72,14 @@ chan_test_channel_dump_statistics_mock(channel_t *chan, int severity) return; } -/** - * If the target cmux is the cmux for chan, make fake cells up to the - * target number of cells and write them to chan. Otherwise, invoke - * the real channel_flush_from_first_active_circuit(). - */ - -static int -chan_test_channel_flush_from_first_active_circuit_mock(channel_t *chan, - int max) -{ - int result = 0, c = 0; - packed_cell_t *cell = NULL; - - tt_ptr_op(chan, OP_NE, NULL); - if (test_target_cmux != NULL && - test_target_cmux == chan->cmux) { - while (c <= max && test_cmux_cells > 0) { - cell = packed_cell_new(); - channel_write_packed_cell(chan, cell); - ++c; - --test_cmux_cells; - } - result = c; - } else { - result = channel_flush_from_first_active_circuit__real(chan, max); - } - - done: - return result; -} - -/** - * If we have a target cmux set and this matches it, lie about how - * many cells we have according to the number indicated; otherwise - * pass to the real circuitmux_num_cells(). - */ - -static unsigned int -chan_test_circuitmux_num_cells_mock(circuitmux_t *cmux) -{ - unsigned int result = 0; - - tt_ptr_op(cmux, OP_NE, NULL); - if (cmux != NULL) { - if (cmux == test_target_cmux) { - result = test_cmux_cells; - } else { - result = circuitmux_num_cells__real(cmux); - } - } - - done: - - return result; -} - /* * Handle an incoming fixed-size cell for unit tests */ static void -chan_test_cell_handler(channel_t *ch, - cell_t *cell) +chan_test_cell_handler(channel_t *chan, cell_t *cell) { - tt_assert(ch); + tt_assert(chan); tt_assert(cell); test_chan_last_seen_fixed_cell_ptr = cell; @@ -226,6 +129,8 @@ chan_test_close(channel_t *ch) { tt_assert(ch); + ++test_close_called; + done: return; } @@ -274,35 +179,6 @@ chan_test_get_remote_descr(channel_t *ch, int flags) return "Fake channel for unit tests; no real endpoint"; } -static double -chan_test_get_overhead_estimate(channel_t *ch) -{ - tt_assert(ch); - - done: - return test_overhead_estimate; -} - -static int -chan_test_is_canonical(channel_t *ch, int req) -{ - tt_ptr_op(ch, OP_NE, NULL); - tt_assert(req == 0 || req == 1); - - done: - /* Fake channels are always canonical */ - return 1; -} - -static size_t -chan_test_num_bytes_queued(channel_t *ch) -{ - tt_assert(ch); - - done: - return 0; -} - static int chan_test_num_cells_writeable(channel_t *ch) { @@ -313,26 +189,6 @@ chan_test_num_cells_writeable(channel_t *ch) } static int -chan_test_write_cell(channel_t *ch, cell_t *cell) -{ - int rv = 0; - - tt_assert(ch); - tt_assert(cell); - - if (test_chan_accept_cells) { - /* Free the cell and bump the counter */ - tor_free(cell); - ++test_cells_written; - rv = 1; - } - /* else return 0, we didn't accept it */ - - done: - return rv; -} - -static int chan_test_write_packed_cell(channel_t *ch, packed_cell_t *packed_cell) { @@ -343,7 +199,6 @@ chan_test_write_packed_cell(channel_t *ch, if (test_chan_accept_cells) { /* Free the cell and bump the counter */ - packed_cell_free(packed_cell); ++test_cells_written; rv = 1; } @@ -419,36 +274,26 @@ new_fake_channel(void) channel_init(chan); chan->close = chan_test_close; - chan->get_overhead_estimate = chan_test_get_overhead_estimate; - chan->get_remote_descr = chan_test_get_remote_descr; - chan->num_bytes_queued = chan_test_num_bytes_queued; chan->num_cells_writeable = chan_test_num_cells_writeable; - chan->write_cell = chan_test_write_cell; + chan->get_remote_descr = chan_test_get_remote_descr; chan->write_packed_cell = chan_test_write_packed_cell; chan->write_var_cell = chan_test_write_var_cell; chan->state = CHANNEL_STATE_OPEN; + chan->cmux = circuitmux_alloc(); + return chan; } void free_fake_channel(channel_t *chan) { - cell_queue_entry_t *cell, *cell_tmp; - if (! chan) return; if (chan->cmux) circuitmux_free(chan->cmux); - TOR_SIMPLEQ_FOREACH_SAFE(cell, &chan->incoming_queue, next, cell_tmp) { - cell_queue_entry_free(cell, 0); - } - TOR_SIMPLEQ_FOREACH_SAFE(cell, &chan->outgoing_queue, next, cell_tmp) { - cell_queue_entry_free(cell, 0); - } - tor_free(chan); } @@ -489,16 +334,6 @@ scheduler_channel_doesnt_want_writes_mock(channel_t *ch) } /** - * Counter query for scheduler_release_channel_mock() - */ - -int -get_mock_scheduler_release_channel_count(void) -{ - return test_releases_count; -} - -/** * Mock for scheduler_release_channel() */ @@ -513,6 +348,58 @@ scheduler_release_channel_mock(channel_t *ch) return; } +static int +test_chan_is_canonical(channel_t *chan, int req) +{ + tor_assert(chan); + + if (req && test_chan_canonical_should_be_reliable) { + return 1; + } + + if (test_chan_should_be_canonical) { + return 1; + } + return 0; +} + +static int +test_chan_matches_target(channel_t *chan, const tor_addr_t *target) +{ + (void) chan; + (void) target; + + if (test_chan_should_match_target) { + return 1; + } + return 0; +} + +static void +test_chan_listener_close(channel_listener_t *chan) +{ + (void) chan; + ++test_chan_listener_close_fn_called; + return; +} + +static void +test_chan_listener_fn(channel_listener_t *listener, channel_t *chan) +{ + (void) listener; + (void) chan; + + ++test_chan_listener_fn_called; + return; +} + +static const char * +test_chan_listener_describe_transport(channel_listener_t *chan) +{ + (void) chan; + return "Fake listener channel."; +} + /** * Test for channel_dumpstats() and limited test for * channel_dump_statistics() @@ -523,6 +410,7 @@ test_channel_dumpstats(void *arg) { channel_t *ch = NULL; cell_t *cell = NULL; + packed_cell_t *p_cell = NULL; int old_count; (void)arg; @@ -536,7 +424,6 @@ test_channel_dumpstats(void *arg) /* Set up a new fake channel */ ch = new_fake_channel(); tt_assert(ch); - ch->cmux = circuitmux_alloc(); /* Try to register it */ channel_register(ch); @@ -579,24 +466,21 @@ test_channel_dumpstats(void *arg) /* Now make another channel */ ch = new_fake_channel(); tt_assert(ch); - ch->cmux = circuitmux_alloc(); channel_register(ch); - tt_assert(ch->registered); + tt_int_op(ch->registered, OP_EQ, 1); /* Lie about its age so dumpstats gets coverage for rate calculations */ ch->timestamp_created = time(NULL) - 30; - tt_assert(ch->timestamp_created > 0); - tt_assert(time(NULL) > ch->timestamp_created); + tt_int_op(ch->timestamp_created, OP_GT, 0); + tt_int_op(time(NULL), OP_GT, ch->timestamp_created); /* Put cells through it both ways to make the counters non-zero */ - cell = tor_malloc_zero(sizeof(*cell)); - make_fake_cell(cell); + p_cell = packed_cell_new(); test_chan_accept_cells = 1; old_count = test_cells_written; - channel_write_cell(ch, cell); - cell = NULL; + channel_write_packed_cell(ch, p_cell); tt_int_op(test_cells_written, OP_EQ, old_count + 1); - tt_assert(ch->n_bytes_xmitted > 0); - tt_assert(ch->n_cells_xmitted > 0); + tt_u64_op(ch->n_bytes_xmitted, OP_GT, 0); + tt_u64_op(ch->n_cells_xmitted, OP_GT, 0); /* Receive path */ channel_set_cell_handlers(ch, @@ -605,19 +489,18 @@ test_channel_dumpstats(void *arg) tt_ptr_op(channel_get_cell_handler(ch), OP_EQ, chan_test_cell_handler); tt_ptr_op(channel_get_var_cell_handler(ch), OP_EQ, chan_test_var_cell_handler); - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); + cell = tor_malloc_zero(sizeof(*cell)); old_count = test_chan_fixed_cells_recved; - channel_queue_cell(ch, cell); - tor_free(cell); + channel_process_cell(ch, cell); tt_int_op(test_chan_fixed_cells_recved, OP_EQ, old_count + 1); - tt_assert(ch->n_bytes_recved > 0); - tt_assert(ch->n_cells_recved > 0); + tt_u64_op(ch->n_bytes_recved, OP_GT, 0); + tt_u64_op(ch->n_cells_recved, OP_GT, 0); /* Test channel_dump_statistics */ ch->describe_transport = chan_test_describe_transport; ch->dumpstats = chan_test_dumpstats; - ch->is_canonical = chan_test_is_canonical; + test_chan_should_be_canonical = 1; + ch->is_canonical = test_chan_is_canonical; old_count = test_dumpstats_calls; channel_dump_statistics(ch, LOG_DEBUG); tt_int_op(test_dumpstats_calls, OP_EQ, old_count + 1); @@ -631,8 +514,8 @@ test_channel_dumpstats(void *arg) ch = NULL; done: - tor_free(cell); free_fake_channel(ch); + tor_free(cell); UNMOCK(scheduler_channel_doesnt_want_writes); UNMOCK(scheduler_release_channel); @@ -640,215 +523,229 @@ test_channel_dumpstats(void *arg) return; } +/* Test outbound cell. The callstack is: + * channel_flush_some_cells() + * -> channel_flush_from_first_active_circuit() + * -> channel_write_packed_cell() + * -> write_packed_cell() + * -> chan->write_packed_cell() fct ptr. + * + * This test goes from a cell in a circuit up to the channel write handler + * that should put them on the connection outbuf. */ static void -test_channel_flush(void *arg) +test_channel_outbound_cell(void *arg) { - channel_t *ch = NULL; - cell_t *cell = NULL; - packed_cell_t *p_cell = NULL; - var_cell_t *v_cell = NULL; - int init_count; - - (void)arg; + int old_count; + channel_t *chan = NULL; + packed_cell_t *p_cell = NULL, *p_cell2 = NULL; + origin_circuit_t *circ = NULL; + cell_queue_t *queue; - ch = new_fake_channel(); - tt_assert(ch); + (void) arg; - /* Cache the original count */ - init_count = test_cells_written; + /* The channel will be freed so we need to hijack this so the scheduler + * doesn't get confused. */ + MOCK(scheduler_release_channel, scheduler_release_channel_mock); - /* Stop accepting so we can queue some */ - test_chan_accept_cells = 0; + /* Accept cells to lower layer */ + test_chan_accept_cells = 1; - /* Queue a regular cell */ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - channel_write_cell(ch, cell); - /* It should be queued, so assert that we didn't write it */ - tt_int_op(test_cells_written, OP_EQ, init_count); - - /* Queue a var cell */ - v_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE); - make_fake_var_cell(v_cell); - channel_write_var_cell(ch, v_cell); - /* It should be queued, so assert that we didn't write it */ - tt_int_op(test_cells_written, OP_EQ, init_count); - - /* Try a packed cell now */ + /* Setup a valid circuit to queue a cell. */ + circ = origin_circuit_new(); + tt_assert(circ); + /* Circuit needs an origin purpose to be considered origin. */ + TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; + TO_CIRCUIT(circ)->n_circ_id = 42; + /* This is the outbound test so use the next channel queue. */ + queue = &TO_CIRCUIT(circ)->n_chan_cells; + /* Setup packed cell to queue on the circuit. */ p_cell = packed_cell_new(); tt_assert(p_cell); - channel_write_packed_cell(ch, p_cell); - /* It should be queued, so assert that we didn't write it */ - tt_int_op(test_cells_written, OP_EQ, init_count); - - /* Now allow writes through again */ - test_chan_accept_cells = 1; - - /* ...and flush */ - channel_flush_cells(ch); - - /* All three should have gone through */ - tt_int_op(test_cells_written, OP_EQ, init_count + 3); - - done: - tor_free(ch); - - return; -} - -/** - * Channel flush tests that require cmux mocking - */ - -static void -test_channel_flushmux(void *arg) -{ - channel_t *ch = NULL; - int old_count, q_len_before, q_len_after; - ssize_t result; - - (void)arg; - - /* Install mocks we need for this test */ - MOCK(channel_flush_from_first_active_circuit, - chan_test_channel_flush_from_first_active_circuit_mock); - MOCK(circuitmux_num_cells, - chan_test_circuitmux_num_cells_mock); - - ch = new_fake_channel(); - tt_assert(ch); - ch->cmux = circuitmux_alloc(); - + p_cell2 = packed_cell_new(); + tt_assert(p_cell2); + /* Setup a channel to put the circuit on. */ + chan = new_fake_channel(); + tt_assert(chan); + chan->state = CHANNEL_STATE_OPENING; + channel_change_state_open(chan); + /* Outbound channel. */ + channel_mark_outgoing(chan); + /* Try to register it so we can clean it through the channel cleanup + * process. */ + channel_register(chan); + tt_int_op(chan->registered, OP_EQ, 1); + /* Set EWMA policy so we can pick it when flushing. */ + channel_set_cmux_policy_everywhere(&ewma_policy); + tt_ptr_op(circuitmux_get_policy(chan->cmux), OP_EQ, &ewma_policy); + + /* Register circuit to the channel circid map which will attach the circuit + * to the channel's cmux as well. */ + circuit_set_n_circid_chan(TO_CIRCUIT(circ), 42, chan); + tt_int_op(channel_num_circuits(chan), OP_EQ, 1); + tt_assert(!TO_CIRCUIT(circ)->next_active_on_n_chan); + tt_assert(!TO_CIRCUIT(circ)->prev_active_on_n_chan); + /* Test the cmux state. */ + tt_ptr_op(TO_CIRCUIT(circ)->n_mux, OP_EQ, chan->cmux); + tt_int_op(circuitmux_is_circuit_attached(chan->cmux, TO_CIRCUIT(circ)), + OP_EQ, 1); + + /* Flush the channel without any cell on it. */ old_count = test_cells_written; - - test_target_cmux = ch->cmux; - test_cmux_cells = 1; - - /* Enable cell acceptance */ - test_chan_accept_cells = 1; - - result = channel_flush_some_cells(ch, 1); - - tt_int_op(result, OP_EQ, 1); + ssize_t flushed = channel_flush_some_cells(chan, 1); + tt_i64_op(flushed, OP_EQ, 0); + tt_int_op(test_cells_written, OP_EQ, old_count); + tt_int_op(channel_more_to_flush(chan), OP_EQ, 0); + tt_int_op(circuitmux_num_active_circuits(chan->cmux), OP_EQ, 0); + tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 0); + tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)), + OP_EQ, 0); + tt_u64_op(chan->n_cells_xmitted, OP_EQ, 0); + tt_u64_op(chan->n_bytes_xmitted, OP_EQ, 0); + + /* Queue cell onto the next queue that is the outbound direction. Than + * update its cmux so the circuit can be picked when flushing cells. */ + cell_queue_append(queue, p_cell); + p_cell = NULL; + tt_int_op(queue->n, OP_EQ, 1); + cell_queue_append(queue, p_cell2); + p_cell2 = NULL; + tt_int_op(queue->n, OP_EQ, 2); + + update_circuit_on_cmux(TO_CIRCUIT(circ), CELL_DIRECTION_OUT); + tt_int_op(circuitmux_num_active_circuits(chan->cmux), OP_EQ, 1); + tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 2); + tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)), + OP_EQ, 1); + + /* From this point on, we have a queued cell on an active circuit attached + * to the channel's cmux. */ + + /* Flush the first cell. This is going to go down the call stack. */ + old_count = test_cells_written; + flushed = channel_flush_some_cells(chan, 1); + tt_i64_op(flushed, OP_EQ, 1); tt_int_op(test_cells_written, OP_EQ, old_count + 1); - tt_int_op(test_cmux_cells, OP_EQ, 0); - - /* Now try it without accepting to force them into the queue */ - test_chan_accept_cells = 0; - test_cmux_cells = 1; - q_len_before = chan_cell_queue_len(&(ch->outgoing_queue)); - - result = channel_flush_some_cells(ch, 1); - - /* We should not have actually flushed any */ - tt_int_op(result, OP_EQ, 0); + tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 1); + tt_int_op(channel_more_to_flush(chan), OP_EQ, 1); + /* Circuit should remain active because there is a second cell queued. */ + tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)), + OP_EQ, 1); + /* Should still be attached. */ + tt_int_op(circuitmux_is_circuit_attached(chan->cmux, TO_CIRCUIT(circ)), + OP_EQ, 1); + tt_u64_op(chan->n_cells_xmitted, OP_EQ, 1); + tt_u64_op(chan->n_bytes_xmitted, OP_EQ, get_cell_network_size(0)); + + /* Flush second cell. This is going to go down the call stack. */ + old_count = test_cells_written; + flushed = channel_flush_some_cells(chan, 1); + tt_i64_op(flushed, OP_EQ, 1); tt_int_op(test_cells_written, OP_EQ, old_count + 1); - /* But we should have gotten to the fake cellgen loop */ - tt_int_op(test_cmux_cells, OP_EQ, 0); - /* ...and we should have a queued cell */ - q_len_after = chan_cell_queue_len(&(ch->outgoing_queue)); - tt_int_op(q_len_after, OP_EQ, q_len_before + 1); - - /* Now accept cells again and drain the queue */ - test_chan_accept_cells = 1; - channel_flush_cells(ch); - tt_int_op(test_cells_written, OP_EQ, old_count + 2); - tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0); - - test_target_cmux = NULL; - test_cmux_cells = 0; + tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 0); + tt_int_op(channel_more_to_flush(chan), OP_EQ, 0); + /* No more cells should make the circuit inactive. */ + tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)), + OP_EQ, 0); + /* Should still be attached. */ + tt_int_op(circuitmux_is_circuit_attached(chan->cmux, TO_CIRCUIT(circ)), + OP_EQ, 1); + tt_u64_op(chan->n_cells_xmitted, OP_EQ, 2); + tt_u64_op(chan->n_bytes_xmitted, OP_EQ, get_cell_network_size(0) * 2); done: - if (ch) - circuitmux_free(ch->cmux); - tor_free(ch); - - UNMOCK(channel_flush_from_first_active_circuit); - UNMOCK(circuitmux_num_cells); - - test_chan_accept_cells = 0; - - return; + if (circ) { + circuit_free_(TO_CIRCUIT(circ)); + } + tor_free(p_cell); + channel_free_all(); + UNMOCK(scheduler_release_channel); } +/* Test inbound cell. The callstack is: + * channel_process_cell() + * -> chan->cell_handler() + * + * This test is about checking if we can process an inbound cell down to the + * channel handler. */ static void -test_channel_incoming(void *arg) +test_channel_inbound_cell(void *arg) { - channel_t *ch = NULL; + channel_t *chan = NULL; cell_t *cell = NULL; - var_cell_t *var_cell = NULL; int old_count; - (void)arg; + (void) arg; - /* Mock these for duration of the test */ - MOCK(scheduler_channel_doesnt_want_writes, - scheduler_channel_doesnt_want_writes_mock); - MOCK(scheduler_release_channel, - scheduler_release_channel_mock); + /* The channel will be freed so we need to hijack this so the scheduler + * doesn't get confused. */ + MOCK(scheduler_release_channel, scheduler_release_channel_mock); /* Accept cells to lower layer */ test_chan_accept_cells = 1; - /* Use default overhead factor */ - test_overhead_estimate = 1.0; - ch = new_fake_channel(); - tt_assert(ch); + chan = new_fake_channel(); + tt_assert(chan); /* Start it off in OPENING */ - ch->state = CHANNEL_STATE_OPENING; - /* We'll need a cmux */ - ch->cmux = circuitmux_alloc(); - - /* Install incoming cell handlers */ - channel_set_cell_handlers(ch, - chan_test_cell_handler, - chan_test_var_cell_handler); - /* Test cell handler getters */ - tt_ptr_op(channel_get_cell_handler(ch), OP_EQ, chan_test_cell_handler); - tt_ptr_op(channel_get_var_cell_handler(ch), OP_EQ, - chan_test_var_cell_handler); + chan->state = CHANNEL_STATE_OPENING; /* Try to register it */ - channel_register(ch); - tt_assert(ch->registered); + channel_register(chan); + tt_int_op(chan->registered, OP_EQ, 1); /* Open it */ - channel_change_state_open(ch); - tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_OPEN); + channel_change_state_open(chan); + tt_int_op(chan->state, OP_EQ, CHANNEL_STATE_OPEN); + tt_int_op(chan->has_been_open, OP_EQ, 1); - /* Receive a fixed cell */ - cell = tor_malloc_zero(sizeof(cell_t)); + /* Receive a cell now. */ + cell = tor_malloc_zero(sizeof(*cell)); make_fake_cell(cell); old_count = test_chan_fixed_cells_recved; - channel_queue_cell(ch, cell); - tor_free(cell); + channel_process_cell(chan, cell); + tt_int_op(test_chan_fixed_cells_recved, OP_EQ, old_count); + tt_assert(monotime_coarse_is_zero(&chan->timestamp_xfer)); + tt_u64_op(chan->timestamp_active, OP_EQ, 0); + tt_u64_op(chan->timestamp_recv, OP_EQ, 0); + + /* Setup incoming cell handlers. We don't care about var cell, the channel + * layers is not handling those. */ + channel_set_cell_handlers(chan, chan_test_cell_handler, NULL); + tt_ptr_op(chan->cell_handler, OP_EQ, chan_test_cell_handler); + /* Now process the cell, we should see it. */ + old_count = test_chan_fixed_cells_recved; + channel_process_cell(chan, cell); tt_int_op(test_chan_fixed_cells_recved, OP_EQ, old_count + 1); - - /* Receive a variable-size cell */ - var_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE); - make_fake_var_cell(var_cell); - old_count = test_chan_var_cells_recved; - channel_queue_var_cell(ch, var_cell); - tor_free(cell); - tt_int_op(test_chan_var_cells_recved, OP_EQ, old_count + 1); + /* We should have a series of timestamp set. */ + tt_assert(!monotime_coarse_is_zero(&chan->timestamp_xfer)); + tt_u64_op(chan->timestamp_active, OP_NE, 0); + tt_u64_op(chan->timestamp_recv, OP_NE, 0); + tt_assert(monotime_coarse_is_zero(&chan->next_padding_time)); + tt_u64_op(chan->n_cells_recved, OP_EQ, 1); + tt_u64_op(chan->n_bytes_recved, OP_EQ, get_cell_network_size(0)); /* Close it */ - channel_mark_for_close(ch); - tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING); - chan_test_finish_close(ch); - tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSED); + old_count = test_close_called; + channel_mark_for_close(chan); + tt_int_op(chan->state, OP_EQ, CHANNEL_STATE_CLOSING); + tt_int_op(chan->reason_for_closing, OP_EQ, CHANNEL_CLOSE_REQUESTED); + tt_int_op(test_close_called, OP_EQ, old_count + 1); + + /* This closes the channe so it calls in the scheduler, make sure of it. */ + old_count = test_releases_count; + chan_test_finish_close(chan); + tt_int_op(test_releases_count, OP_EQ, old_count + 1); + tt_int_op(chan->state, OP_EQ, CHANNEL_STATE_CLOSED); + + /* The channel will be free, lets make sure it is not accessible. */ + uint64_t chan_id = chan->global_identifier; + tt_ptr_op(channel_find_by_global_id(chan_id), OP_EQ, chan); channel_run_cleanup(); - ch = NULL; + chan = channel_find_by_global_id(chan_id); + tt_assert(chan == NULL); done: - free_fake_channel(ch); tor_free(cell); - tor_free(var_cell); - - UNMOCK(scheduler_channel_doesnt_want_writes); UNMOCK(scheduler_release_channel); - - return; } /** @@ -861,7 +758,7 @@ static void test_channel_lifecycle(void *arg) { channel_t *ch1 = NULL, *ch2 = NULL; - cell_t *cell = NULL; + packed_cell_t *p_cell = NULL; int old_count, init_doesnt_want_writes_count; int init_releases_count; @@ -879,38 +776,29 @@ test_channel_lifecycle(void *arg) /* Accept cells to lower layer */ test_chan_accept_cells = 1; - /* Use default overhead factor */ - test_overhead_estimate = 1.0; ch1 = new_fake_channel(); tt_assert(ch1); /* Start it off in OPENING */ ch1->state = CHANNEL_STATE_OPENING; - /* We'll need a cmux */ - ch1->cmux = circuitmux_alloc(); /* Try to register it */ channel_register(ch1); tt_assert(ch1->registered); /* Try to write a cell through (should queue) */ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); + p_cell = packed_cell_new(); old_count = test_cells_written; - channel_write_cell(ch1, cell); + channel_write_packed_cell(ch1, p_cell); tt_int_op(old_count, OP_EQ, test_cells_written); /* Move it to OPEN and flush */ channel_change_state_open(ch1); - /* Queue should drain */ - tt_int_op(old_count + 1, OP_EQ, test_cells_written); - - /* Get another one */ +/* Get another one */ ch2 = new_fake_channel(); tt_assert(ch2); ch2->state = CHANNEL_STATE_OPENING; - ch2->cmux = circuitmux_alloc(); /* Register */ channel_register(ch2); @@ -960,8 +848,6 @@ test_channel_lifecycle(void *arg) UNMOCK(scheduler_channel_doesnt_want_writes); UNMOCK(scheduler_release_channel); - - return; } /** @@ -988,15 +874,11 @@ test_channel_lifecycle_2(void *arg) /* Accept cells to lower layer */ test_chan_accept_cells = 1; - /* Use default overhead factor */ - test_overhead_estimate = 1.0; ch = new_fake_channel(); tt_assert(ch); /* Start it off in OPENING */ ch->state = CHANNEL_STATE_OPENING; - /* The full lifecycle test needs a cmux */ - ch->cmux = circuitmux_alloc(); /* Try to register it */ channel_register(ch); @@ -1016,7 +898,6 @@ test_channel_lifecycle_2(void *arg) ch = new_fake_channel(); tt_assert(ch); ch->state = CHANNEL_STATE_OPENING; - ch->cmux = circuitmux_alloc(); channel_register(ch); tt_assert(ch->registered); @@ -1035,7 +916,6 @@ test_channel_lifecycle_2(void *arg) ch = new_fake_channel(); tt_assert(ch); ch->state = CHANNEL_STATE_OPENING; - ch->cmux = circuitmux_alloc(); channel_register(ch); tt_assert(ch->registered); @@ -1064,7 +944,6 @@ test_channel_lifecycle_2(void *arg) ch = new_fake_channel(); tt_assert(ch); ch->state = CHANNEL_STATE_OPENING; - ch->cmux = circuitmux_alloc(); channel_register(ch); tt_assert(ch->registered); @@ -1090,7 +969,6 @@ test_channel_lifecycle_2(void *arg) ch = new_fake_channel(); tt_assert(ch); ch->state = CHANNEL_STATE_OPENING; - ch->cmux = circuitmux_alloc(); channel_register(ch); tt_assert(ch->registered); @@ -1125,655 +1003,6 @@ test_channel_lifecycle_2(void *arg) } static void -test_channel_multi(void *arg) -{ - channel_t *ch1 = NULL, *ch2 = NULL; - uint64_t global_queue_estimate; - cell_t *cell = NULL; - - (void)arg; - - /* Accept cells to lower layer */ - test_chan_accept_cells = 1; - /* Use default overhead factor */ - test_overhead_estimate = 1.0; - - ch1 = new_fake_channel(); - tt_assert(ch1); - ch2 = new_fake_channel(); - tt_assert(ch2); - - /* Initial queue size update */ - channel_update_xmit_queue_size(ch1); - tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 0); - channel_update_xmit_queue_size(ch2); - tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 0); - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 0); - - /* Queue some cells, check queue estimates */ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - channel_write_cell(ch1, cell); - - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - channel_write_cell(ch2, cell); - - channel_update_xmit_queue_size(ch1); - channel_update_xmit_queue_size(ch2); - tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 0); - tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 0); - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 0); - - /* Stop accepting cells at lower layer */ - test_chan_accept_cells = 0; - - /* Queue some cells and check queue estimates */ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - channel_write_cell(ch1, cell); - - channel_update_xmit_queue_size(ch1); - tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 512); - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 512); - - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - channel_write_cell(ch2, cell); - - channel_update_xmit_queue_size(ch2); - tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 512); - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 1024); - - /* Allow cells through again */ - test_chan_accept_cells = 1; - - /* Flush chan 2 */ - channel_flush_cells(ch2); - - /* Update and check queue sizes */ - channel_update_xmit_queue_size(ch1); - channel_update_xmit_queue_size(ch2); - tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 512); - tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 0); - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 512); - - /* Flush chan 1 */ - channel_flush_cells(ch1); - - /* Update and check queue sizes */ - channel_update_xmit_queue_size(ch1); - channel_update_xmit_queue_size(ch2); - tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 0); - tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 0); - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 0); - - /* Now block again */ - test_chan_accept_cells = 0; - - /* Queue some cells */ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - channel_write_cell(ch1, cell); - - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - channel_write_cell(ch2, cell); - cell = NULL; - - /* Check the estimates */ - channel_update_xmit_queue_size(ch1); - channel_update_xmit_queue_size(ch2); - tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 512); - tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 512); - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 1024); - - /* Now close channel 2; it should be subtracted from the global queue */ - MOCK(scheduler_release_channel, scheduler_release_channel_mock); - channel_mark_for_close(ch2); - UNMOCK(scheduler_release_channel); - - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 512); - - /* - * Since the fake channels aren't registered, channel_free_all() can't - * see them properly. - */ - MOCK(scheduler_release_channel, scheduler_release_channel_mock); - channel_mark_for_close(ch1); - UNMOCK(scheduler_release_channel); - - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 0); - - /* Now free everything */ - MOCK(scheduler_release_channel, scheduler_release_channel_mock); - channel_free_all(); - UNMOCK(scheduler_release_channel); - - done: - free_fake_channel(ch1); - free_fake_channel(ch2); - - return; -} - -/** - * Check some hopefully-impossible edge cases in the channel queue we - * can only trigger by doing evil things to the queue directly. - */ - -static void -test_channel_queue_impossible(void *arg) -{ - channel_t *ch = NULL; - cell_t *cell = NULL; - packed_cell_t *packed_cell = NULL; - var_cell_t *var_cell = NULL; - int old_count; - cell_queue_entry_t *q = NULL; - uint64_t global_queue_estimate; - uintptr_t cellintptr; - - /* Cache the global queue size (see below) */ - global_queue_estimate = channel_get_global_queue_estimate(); - - (void)arg; - - ch = new_fake_channel(); - tt_assert(ch); - - /* We test queueing here; tell it not to accept cells */ - test_chan_accept_cells = 0; - /* ...and keep it from trying to flush the queue */ - ch->state = CHANNEL_STATE_MAINT; - - /* Cache the cell written count */ - old_count = test_cells_written; - - /* Assert that the queue is initially empty */ - tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0); - - /* Get a fresh cell and write it to the channel*/ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - cellintptr = (uintptr_t)(void*)cell; - channel_write_cell(ch, cell); - - /* Now it should be queued */ - tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 1); - q = TOR_SIMPLEQ_FIRST(&(ch->outgoing_queue)); - tt_assert(q); - if (q) { - tt_int_op(q->type, OP_EQ, CELL_QUEUE_FIXED); - tt_assert((uintptr_t)q->u.fixed.cell == cellintptr); - } - /* Do perverse things to it */ - tor_free(q->u.fixed.cell); - q->u.fixed.cell = NULL; - - /* - * Now change back to open with channel_change_state() and assert that it - * gets thrown away properly. - */ - test_chan_accept_cells = 1; - channel_change_state_open(ch); - tt_assert(test_cells_written == old_count); - tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0); - - /* Same thing but for a var_cell */ - - test_chan_accept_cells = 0; - ch->state = CHANNEL_STATE_MAINT; - var_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE); - make_fake_var_cell(var_cell); - cellintptr = (uintptr_t)(void*)var_cell; - channel_write_var_cell(ch, var_cell); - - /* Check that it's queued */ - tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 1); - q = TOR_SIMPLEQ_FIRST(&(ch->outgoing_queue)); - tt_assert(q); - if (q) { - tt_int_op(q->type, OP_EQ, CELL_QUEUE_VAR); - tt_assert((uintptr_t)q->u.var.var_cell == cellintptr); - } - - /* Remove the cell from the queue entry */ - tor_free(q->u.var.var_cell); - q->u.var.var_cell = NULL; - - /* Let it drain and check that the bad entry is discarded */ - test_chan_accept_cells = 1; - channel_change_state_open(ch); - tt_assert(test_cells_written == old_count); - tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0); - - /* Same thing with a packed_cell */ - - test_chan_accept_cells = 0; - ch->state = CHANNEL_STATE_MAINT; - packed_cell = packed_cell_new(); - tt_assert(packed_cell); - cellintptr = (uintptr_t)(void*)packed_cell; - channel_write_packed_cell(ch, packed_cell); - - /* Check that it's queued */ - tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 1); - q = TOR_SIMPLEQ_FIRST(&(ch->outgoing_queue)); - tt_assert(q); - if (q) { - tt_int_op(q->type, OP_EQ, CELL_QUEUE_PACKED); - tt_assert((uintptr_t)q->u.packed.packed_cell == cellintptr); - } - - /* Remove the cell from the queue entry */ - packed_cell_free(q->u.packed.packed_cell); - q->u.packed.packed_cell = NULL; - - /* Let it drain and check that the bad entry is discarded */ - test_chan_accept_cells = 1; - channel_change_state_open(ch); - tt_assert(test_cells_written == old_count); - tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0); - - /* Unknown cell type case */ - test_chan_accept_cells = 0; - ch->state = CHANNEL_STATE_MAINT; - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - cellintptr = (uintptr_t)(void*)cell; - channel_write_cell(ch, cell); - - /* Check that it's queued */ - tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 1); - q = TOR_SIMPLEQ_FIRST(&(ch->outgoing_queue)); - tt_assert(q); - if (q) { - tt_int_op(q->type, OP_EQ, CELL_QUEUE_FIXED); - tt_assert((uintptr_t)q->u.fixed.cell == cellintptr); - } - /* Clobber it, including the queue entry type */ - tor_free(q->u.fixed.cell); - q->u.fixed.cell = NULL; - q->type = CELL_QUEUE_PACKED + 1; - - /* Let it drain and check that the bad entry is discarded */ - test_chan_accept_cells = 1; - tor_capture_bugs_(1); - channel_change_state_open(ch); - tt_assert(test_cells_written == old_count); - tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0); - - tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); - tor_end_capture_bugs_(); - - done: - free_fake_channel(ch); - - /* - * Doing that meant that we couldn't correctly adjust the queue size - * for the var cell, so manually reset the global queue size estimate - * so the next test doesn't break if we run with --no-fork. - */ - estimated_total_queue_size = global_queue_estimate; - - return; -} - -static void -test_channel_queue_incoming(void *arg) -{ - channel_t *ch = NULL; - cell_t *cell = NULL; - var_cell_t *var_cell = NULL; - int old_fixed_count, old_var_count; - - (void)arg; - - /* Mock these for duration of the test */ - MOCK(scheduler_channel_doesnt_want_writes, - scheduler_channel_doesnt_want_writes_mock); - MOCK(scheduler_release_channel, - scheduler_release_channel_mock); - - /* Accept cells to lower layer */ - test_chan_accept_cells = 1; - /* Use default overhead factor */ - test_overhead_estimate = 1.0; - - ch = new_fake_channel(); - tt_assert(ch); - /* Start it off in OPENING */ - ch->state = CHANNEL_STATE_OPENING; - /* We'll need a cmux */ - ch->cmux = circuitmux_alloc(); - - /* Test cell handler getters */ - tt_ptr_op(channel_get_cell_handler(ch), OP_EQ, NULL); - tt_ptr_op(channel_get_var_cell_handler(ch), OP_EQ, NULL); - - /* Try to register it */ - channel_register(ch); - tt_assert(ch->registered); - - /* Open it */ - channel_change_state_open(ch); - tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_OPEN); - - /* Assert that the incoming queue is empty */ - tt_assert(TOR_SIMPLEQ_EMPTY(&(ch->incoming_queue))); - - /* Queue an incoming fixed-length cell */ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - channel_queue_cell(ch, cell); - - /* Assert that the incoming queue has one entry */ - tt_int_op(chan_cell_queue_len(&(ch->incoming_queue)), OP_EQ, 1); - - /* Queue an incoming var cell */ - var_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE); - make_fake_var_cell(var_cell); - channel_queue_var_cell(ch, var_cell); - - /* Assert that the incoming queue has two entries */ - tt_int_op(chan_cell_queue_len(&(ch->incoming_queue)), OP_EQ, 2); - - /* - * Install cell handlers; this will drain the queue, so save the old - * cell counters first - */ - old_fixed_count = test_chan_fixed_cells_recved; - old_var_count = test_chan_var_cells_recved; - channel_set_cell_handlers(ch, - chan_test_cell_handler, - chan_test_var_cell_handler); - tt_ptr_op(channel_get_cell_handler(ch), OP_EQ, chan_test_cell_handler); - tt_ptr_op(channel_get_var_cell_handler(ch), OP_EQ, - chan_test_var_cell_handler); - - /* Assert cells were received */ - tt_int_op(test_chan_fixed_cells_recved, OP_EQ, old_fixed_count + 1); - tt_int_op(test_chan_var_cells_recved, OP_EQ, old_var_count + 1); - - /* - * Assert that the pointers are different from the cells we allocated; - * when queueing cells with no incoming cell handlers installed, the - * channel layer should copy them to a new buffer, and free them after - * delivery. These pointers will have already been freed by the time - * we get here, so don't dereference them. - */ - tt_ptr_op(test_chan_last_seen_fixed_cell_ptr, OP_NE, cell); - tt_ptr_op(test_chan_last_seen_var_cell_ptr, OP_NE, var_cell); - - /* Assert queue is now empty */ - tt_assert(TOR_SIMPLEQ_EMPTY(&(ch->incoming_queue))); - - /* Close it; this contains an assertion that the incoming queue is empty */ - channel_mark_for_close(ch); - tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING); - chan_test_finish_close(ch); - tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSED); - channel_run_cleanup(); - ch = NULL; - - done: - free_fake_channel(ch); - tor_free(cell); - tor_free(var_cell); - - UNMOCK(scheduler_channel_doesnt_want_writes); - UNMOCK(scheduler_release_channel); - - return; -} - -static void -test_channel_queue_size(void *arg) -{ - channel_t *ch = NULL; - cell_t *cell = NULL; - int n, old_count; - uint64_t global_queue_estimate; - - (void)arg; - - ch = new_fake_channel(); - tt_assert(ch); - - /* Initial queue size update */ - channel_update_xmit_queue_size(ch); - tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 0); - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 0); - - /* Test the call-through to our fake lower layer */ - n = channel_num_cells_writeable(ch); - /* chan_test_num_cells_writeable() always returns 32 */ - tt_int_op(n, OP_EQ, 32); - - /* - * Now we queue some cells and check that channel_num_cells_writeable() - * adjusts properly - */ - - /* tell it not to accept cells */ - test_chan_accept_cells = 0; - /* ...and keep it from trying to flush the queue */ - ch->state = CHANNEL_STATE_MAINT; - - /* Get a fresh cell */ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - - old_count = test_cells_written; - channel_write_cell(ch, cell); - /* Assert that it got queued, not written through, correctly */ - tt_int_op(test_cells_written, OP_EQ, old_count); - - /* Now check chan_test_num_cells_writeable() again */ - n = channel_num_cells_writeable(ch); - /* Should return 0 since we're in CHANNEL_STATE_MAINT */ - tt_int_op(n, OP_EQ, 0); - - /* Update queue size estimates */ - channel_update_xmit_queue_size(ch); - /* One cell, times an overhead factor of 1.0 */ - tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 512); - /* Try a different overhead factor */ - test_overhead_estimate = 0.5; - /* This one should be ignored since it's below 1.0 */ - channel_update_xmit_queue_size(ch); - tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 512); - /* Now try a larger one */ - test_overhead_estimate = 2.0; - channel_update_xmit_queue_size(ch); - tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 1024); - /* Go back to 1.0 */ - test_overhead_estimate = 1.0; - channel_update_xmit_queue_size(ch); - tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 512); - /* Check the global estimate too */ - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 512); - - /* Go to open */ - old_count = test_cells_written; - channel_change_state_open(ch); - - /* - * It should try to write, but we aren't accepting cells right now, so - * it'll requeue - */ - tt_int_op(test_cells_written, OP_EQ, old_count); - - /* Check the queue size again */ - channel_update_xmit_queue_size(ch); - tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 512); - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 512); - - /* - * Now the cell is in the queue, and we're open, so we should get 31 - * writeable cells. - */ - n = channel_num_cells_writeable(ch); - tt_int_op(n, OP_EQ, 31); - - /* Accept cells again */ - test_chan_accept_cells = 1; - /* ...and re-process the queue */ - old_count = test_cells_written; - channel_flush_cells(ch); - tt_int_op(test_cells_written, OP_EQ, old_count + 1); - - /* Should have 32 writeable now */ - n = channel_num_cells_writeable(ch); - tt_int_op(n, OP_EQ, 32); - - /* Should have queue size estimate of zero */ - channel_update_xmit_queue_size(ch); - tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 0); - global_queue_estimate = channel_get_global_queue_estimate(); - tt_u64_op(global_queue_estimate, OP_EQ, 0); - - /* Okay, now we're done with this one */ - MOCK(scheduler_release_channel, scheduler_release_channel_mock); - channel_mark_for_close(ch); - UNMOCK(scheduler_release_channel); - - done: - free_fake_channel(ch); - - return; -} - -static void -test_channel_write(void *arg) -{ - channel_t *ch = NULL; - cell_t *cell = tor_malloc_zero(sizeof(cell_t)); - packed_cell_t *packed_cell = NULL; - var_cell_t *var_cell = - tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE); - int old_count; - - (void)arg; - - packed_cell = packed_cell_new(); - tt_assert(packed_cell); - - ch = new_fake_channel(); - tt_assert(ch); - make_fake_cell(cell); - make_fake_var_cell(var_cell); - - /* Tell it to accept cells */ - test_chan_accept_cells = 1; - - old_count = test_cells_written; - channel_write_cell(ch, cell); - cell = NULL; - tt_assert(test_cells_written == old_count + 1); - - channel_write_var_cell(ch, var_cell); - var_cell = NULL; - tt_assert(test_cells_written == old_count + 2); - - channel_write_packed_cell(ch, packed_cell); - packed_cell = NULL; - tt_assert(test_cells_written == old_count + 3); - - /* Now we test queueing; tell it not to accept cells */ - test_chan_accept_cells = 0; - /* ...and keep it from trying to flush the queue */ - ch->state = CHANNEL_STATE_MAINT; - - /* Get a fresh cell */ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - - old_count = test_cells_written; - channel_write_cell(ch, cell); - tt_assert(test_cells_written == old_count); - - /* - * Now change back to open with channel_change_state() and assert that it - * gets drained from the queue. - */ - test_chan_accept_cells = 1; - channel_change_state_open(ch); - tt_assert(test_cells_written == old_count + 1); - - /* - * Check the note destroy case - */ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - cell->command = CELL_DESTROY; - - /* Set up the mock */ - MOCK(channel_note_destroy_not_pending, - channel_note_destroy_not_pending_mock); - - old_count = test_destroy_not_pending_calls; - channel_write_cell(ch, cell); - tt_assert(test_destroy_not_pending_calls == old_count + 1); - - /* Now send a non-destroy and check we don't call it */ - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - channel_write_cell(ch, cell); - tt_assert(test_destroy_not_pending_calls == old_count + 1); - - UNMOCK(channel_note_destroy_not_pending); - - /* - * Now switch it to CLOSING so we can test the discard-cells case - * in the channel_write_*() functions. - */ - MOCK(scheduler_release_channel, scheduler_release_channel_mock); - channel_mark_for_close(ch); - UNMOCK(scheduler_release_channel); - - /* Send cells that will drop in the closing state */ - old_count = test_cells_written; - - cell = tor_malloc_zero(sizeof(cell_t)); - make_fake_cell(cell); - channel_write_cell(ch, cell); - cell = NULL; - tt_assert(test_cells_written == old_count); - - var_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE); - make_fake_var_cell(var_cell); - channel_write_var_cell(ch, var_cell); - var_cell = NULL; - tt_assert(test_cells_written == old_count); - - packed_cell = packed_cell_new(); - channel_write_packed_cell(ch, packed_cell); - packed_cell = NULL; - tt_assert(test_cells_written == old_count); - - done: - free_fake_channel(ch); - tor_free(var_cell); - tor_free(cell); - packed_cell_free(packed_cell); - return; -} - -static void test_channel_id_map(void *arg) { (void)arg; @@ -1879,19 +1108,449 @@ test_channel_id_map(void *arg) #undef N_CHAN } +static void +test_channel_state(void *arg) +{ + (void) arg; + + /* Test state validity. */ + tt_int_op(channel_state_is_valid(CHANNEL_STATE_CLOSED), OP_EQ, 1); + tt_int_op(channel_state_is_valid(CHANNEL_STATE_CLOSING), OP_EQ, 1); + tt_int_op(channel_state_is_valid(CHANNEL_STATE_ERROR), OP_EQ, 1); + tt_int_op(channel_state_is_valid(CHANNEL_STATE_OPEN), OP_EQ, 1); + tt_int_op(channel_state_is_valid(CHANNEL_STATE_OPENING), OP_EQ, 1); + tt_int_op(channel_state_is_valid(CHANNEL_STATE_MAINT), OP_EQ, 1); + tt_int_op(channel_state_is_valid(CHANNEL_STATE_LAST), OP_EQ, 0); + tt_int_op(channel_state_is_valid(INT_MAX), OP_EQ, 0); + + /* Test listener state validity. */ + tt_int_op(channel_listener_state_is_valid(CHANNEL_LISTENER_STATE_CLOSED), + OP_EQ, 1); + tt_int_op(channel_listener_state_is_valid(CHANNEL_LISTENER_STATE_LISTENING), + OP_EQ, 1); + tt_int_op(channel_listener_state_is_valid(CHANNEL_LISTENER_STATE_CLOSING), + OP_EQ, 1); + tt_int_op(channel_listener_state_is_valid(CHANNEL_LISTENER_STATE_ERROR), + OP_EQ, 1); + tt_int_op(channel_listener_state_is_valid(CHANNEL_LISTENER_STATE_LAST), + OP_EQ, 0); + tt_int_op(channel_listener_state_is_valid(INT_MAX), OP_EQ, 0); + + /* Test state transition. */ + tt_int_op(channel_state_can_transition(CHANNEL_STATE_CLOSED, + CHANNEL_STATE_OPENING), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_CLOSED, + CHANNEL_STATE_ERROR), OP_EQ, 0); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_CLOSING, + CHANNEL_STATE_ERROR), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_CLOSING, + CHANNEL_STATE_CLOSED), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_CLOSING, + CHANNEL_STATE_OPEN), OP_EQ, 0); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_MAINT, + CHANNEL_STATE_CLOSING), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_MAINT, + CHANNEL_STATE_ERROR), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_MAINT, + CHANNEL_STATE_OPEN), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_MAINT, + CHANNEL_STATE_OPENING), OP_EQ, 0); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_OPENING, + CHANNEL_STATE_OPEN), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_OPENING, + CHANNEL_STATE_CLOSING), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_OPENING, + CHANNEL_STATE_ERROR), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_OPEN, + CHANNEL_STATE_ERROR), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_OPEN, + CHANNEL_STATE_CLOSING), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_OPEN, + CHANNEL_STATE_ERROR), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_OPEN, + CHANNEL_STATE_MAINT), OP_EQ, 1); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_LAST, + CHANNEL_STATE_MAINT), OP_EQ, 0); + tt_int_op(channel_state_can_transition(CHANNEL_STATE_LAST, INT_MAX), + OP_EQ, 0); + + /* Test listener state transition. */ + tt_int_op(channel_listener_state_can_transition( + CHANNEL_LISTENER_STATE_CLOSED, + CHANNEL_LISTENER_STATE_LISTENING), + OP_EQ, 1); + tt_int_op(channel_listener_state_can_transition( + CHANNEL_LISTENER_STATE_CLOSED, + CHANNEL_LISTENER_STATE_ERROR), + OP_EQ, 0); + + tt_int_op(channel_listener_state_can_transition( + CHANNEL_LISTENER_STATE_CLOSING, + CHANNEL_LISTENER_STATE_CLOSED), + OP_EQ, 1); + + tt_int_op(channel_listener_state_can_transition( + CHANNEL_LISTENER_STATE_CLOSING, + CHANNEL_LISTENER_STATE_ERROR), + OP_EQ, 1); + tt_int_op(channel_listener_state_can_transition( + CHANNEL_LISTENER_STATE_ERROR, + CHANNEL_LISTENER_STATE_CLOSING), + OP_EQ, 0); + + tt_int_op(channel_listener_state_can_transition( + CHANNEL_LISTENER_STATE_LISTENING, + CHANNEL_LISTENER_STATE_CLOSING), + OP_EQ, 1); + tt_int_op(channel_listener_state_can_transition( + CHANNEL_LISTENER_STATE_LISTENING, + CHANNEL_LISTENER_STATE_ERROR), + OP_EQ, 1); + tt_int_op(channel_listener_state_can_transition( + CHANNEL_LISTENER_STATE_LAST, + INT_MAX), + OP_EQ, 0); + + /* Test state string. */ + tt_str_op(channel_state_to_string(CHANNEL_STATE_CLOSING), OP_EQ, + "closing"); + tt_str_op(channel_state_to_string(CHANNEL_STATE_ERROR), OP_EQ, + "channel error"); + tt_str_op(channel_state_to_string(CHANNEL_STATE_CLOSED), OP_EQ, + "closed"); + tt_str_op(channel_state_to_string(CHANNEL_STATE_OPEN), OP_EQ, + "open"); + tt_str_op(channel_state_to_string(CHANNEL_STATE_OPENING), OP_EQ, + "opening"); + tt_str_op(channel_state_to_string(CHANNEL_STATE_MAINT), OP_EQ, + "temporarily suspended for maintenance"); + tt_str_op(channel_state_to_string(CHANNEL_STATE_LAST), OP_EQ, + "unknown or invalid channel state"); + tt_str_op(channel_state_to_string(INT_MAX), OP_EQ, + "unknown or invalid channel state"); + + /* Test listener state string. */ + tt_str_op(channel_listener_state_to_string(CHANNEL_LISTENER_STATE_CLOSING), + OP_EQ, "closing"); + tt_str_op(channel_listener_state_to_string(CHANNEL_LISTENER_STATE_ERROR), + OP_EQ, "channel listener error"); + tt_str_op(channel_listener_state_to_string(CHANNEL_LISTENER_STATE_LISTENING), + OP_EQ, "listening"); + tt_str_op(channel_listener_state_to_string(CHANNEL_LISTENER_STATE_LAST), + OP_EQ, "unknown or invalid channel listener state"); + tt_str_op(channel_listener_state_to_string(INT_MAX), + OP_EQ, "unknown or invalid channel listener state"); + + done: + ; +} + +static networkstatus_t *mock_ns = NULL; + +static networkstatus_t * +mock_networkstatus_get_latest_consensus(void) +{ + return mock_ns; +} + +static void +test_channel_duplicates(void *arg) +{ + channel_t *chan = NULL; + routerstatus_t rs; + + (void) arg; + + setup_full_capture_of_logs(LOG_INFO); + /* Try a flat call with channel nor connections. */ + channel_check_for_duplicates(); + expect_log_msg_containing( + "Found 0 connections to 0 relays. Found 0 current canonical " + "connections, in 0 of which we were a non-canonical peer. " + "0 relays had more than 1 connection, 0 had more than 2, and " + "0 had more than 4 connections."); + + mock_ns = tor_malloc_zero(sizeof(*mock_ns)); + mock_ns->routerstatus_list = smartlist_new(); + MOCK(networkstatus_get_latest_consensus, + mock_networkstatus_get_latest_consensus); + + chan = new_fake_channel(); + tt_assert(chan); + chan->is_canonical = test_chan_is_canonical; + memset(chan->identity_digest, 'A', sizeof(chan->identity_digest)); + channel_add_to_digest_map(chan); + tt_ptr_op(channel_find_by_remote_identity(chan->identity_digest, NULL), + OP_EQ, chan); + + /* No relay has been associated with this channel. */ + channel_check_for_duplicates(); + expect_log_msg_containing( + "Found 0 connections to 0 relays. Found 0 current canonical " + "connections, in 0 of which we were a non-canonical peer. " + "0 relays had more than 1 connection, 0 had more than 2, and " + "0 had more than 4 connections."); + + /* Associate relay to this connection in the consensus. */ + memset(&rs, 0, sizeof(rs)); + memset(rs.identity_digest, 'A', sizeof(rs.identity_digest)); + smartlist_add(mock_ns->routerstatus_list, &rs); + + /* Non opened channel. */ + chan->state = CHANNEL_STATE_CLOSING; + channel_check_for_duplicates(); + expect_log_msg_containing( + "Found 0 connections to 0 relays. Found 0 current canonical " + "connections, in 0 of which we were a non-canonical peer. " + "0 relays had more than 1 connection, 0 had more than 2, and " + "0 had more than 4 connections."); + chan->state = CHANNEL_STATE_OPEN; + + channel_check_for_duplicates(); + expect_log_msg_containing( + "Found 1 connections to 1 relays. Found 0 current canonical " + "connections, in 0 of which we were a non-canonical peer. " + "0 relays had more than 1 connection, 0 had more than 2, and " + "0 had more than 4 connections."); + + test_chan_should_be_canonical = 1; + channel_check_for_duplicates(); + expect_log_msg_containing( + "Found 1 connections to 1 relays. Found 1 current canonical " + "connections, in 1 of which we were a non-canonical peer. " + "0 relays had more than 1 connection, 0 had more than 2, and " + "0 had more than 4 connections."); + teardown_capture_of_logs(); + + done: + free_fake_channel(chan); + smartlist_clear(mock_ns->routerstatus_list); + networkstatus_vote_free(mock_ns); + UNMOCK(networkstatus_get_latest_consensus); +} + +static void +test_channel_for_extend(void *arg) +{ + channel_t *chan1 = NULL, *chan2 = NULL; + channel_t *ret_chan = NULL; + char digest[DIGEST_LEN]; + ed25519_public_key_t ed_id; + tor_addr_t addr; + const char *msg; + int launch; + time_t now = time(NULL); + + (void) arg; + + memset(digest, 'A', sizeof(digest)); + memset(&ed_id, 'B', sizeof(ed_id)); + + chan1 = new_fake_channel(); + tt_assert(chan1); + /* Need to be registered to get added to the id map. */ + channel_register(chan1); + tt_int_op(chan1->registered, OP_EQ, 1); + /* We need those for the test. */ + chan1->is_canonical = test_chan_is_canonical; + chan1->matches_target = test_chan_matches_target; + chan1->timestamp_created = now - 9; + + chan2 = new_fake_channel(); + tt_assert(chan2); + /* Need to be registered to get added to the id map. */ + channel_register(chan2); + tt_int_op(chan2->registered, OP_EQ, 1); + /* We need those for the test. */ + chan2->is_canonical = test_chan_is_canonical; + chan2->matches_target = test_chan_matches_target; + /* Make it older than chan1. */ + chan2->timestamp_created = chan1->timestamp_created - 1; + + /* Set channel identities and add it to the channel map. The last one to be + * added is made the first one in the list so the lookup will always return + * that one first. */ + channel_set_identity_digest(chan2, digest, &ed_id); + channel_set_identity_digest(chan1, digest, &ed_id); + tt_ptr_op(channel_find_by_remote_identity(digest, NULL), OP_EQ, chan1); + tt_ptr_op(channel_find_by_remote_identity(digest, &ed_id), OP_EQ, chan1); + + /* The expected result is chan2 because it is older than chan1. */ + ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + tt_assert(ret_chan); + tt_ptr_op(ret_chan, OP_EQ, chan2); + tt_int_op(launch, OP_EQ, 0); + tt_str_op(msg, OP_EQ, "Connection is fine; using it."); + + /* Switch that around from previous test. */ + chan2->timestamp_created = chan1->timestamp_created + 1; + ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + tt_assert(ret_chan); + tt_ptr_op(ret_chan, OP_EQ, chan1); + tt_int_op(launch, OP_EQ, 0); + tt_str_op(msg, OP_EQ, "Connection is fine; using it."); + + /* Same creation time, num circuits will be used and they both have 0 so the + * channel 2 should be picked due to how channel_is_better() work. */ + chan2->timestamp_created = chan1->timestamp_created; + ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + tt_assert(ret_chan); + tt_ptr_op(ret_chan, OP_EQ, chan1); + tt_int_op(launch, OP_EQ, 0); + tt_str_op(msg, OP_EQ, "Connection is fine; using it."); + + /* For the rest of the tests, we need channel 1 to be the older. */ + chan2->timestamp_created = chan1->timestamp_created + 1; + + /* Condemned the older channel. */ + chan1->state = CHANNEL_STATE_CLOSING; + ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + tt_assert(ret_chan); + tt_ptr_op(ret_chan, OP_EQ, chan2); + tt_int_op(launch, OP_EQ, 0); + tt_str_op(msg, OP_EQ, "Connection is fine; using it."); + chan1->state = CHANNEL_STATE_OPEN; + + /* Make the older channel a client one. */ + channel_mark_client(chan1); + ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + tt_assert(ret_chan); + tt_ptr_op(ret_chan, OP_EQ, chan2); + tt_int_op(launch, OP_EQ, 0); + tt_str_op(msg, OP_EQ, "Connection is fine; using it."); + channel_clear_client(chan1); + + /* Non matching ed identity with valid digest. */ + ed25519_public_key_t dumb_ed_id; + memset(&dumb_ed_id, 0, sizeof(dumb_ed_id)); + ret_chan = channel_get_for_extend(digest, &dumb_ed_id, &addr, &msg, + &launch); + tt_assert(!ret_chan); + tt_str_op(msg, OP_EQ, "Not connected. Connecting."); + tt_int_op(launch, OP_EQ, 1); + + /* Opening channel, we'll check if the target address matches. */ + test_chan_should_match_target = 1; + chan1->state = CHANNEL_STATE_OPENING; + chan2->state = CHANNEL_STATE_OPENING; + ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + tt_assert(!ret_chan); + tt_str_op(msg, OP_EQ, "Connection in progress; waiting."); + tt_int_op(launch, OP_EQ, 0); + chan1->state = CHANNEL_STATE_OPEN; + chan2->state = CHANNEL_STATE_OPEN; + + /* Mark channel 1 as bad for circuits. */ + channel_mark_bad_for_new_circs(chan1); + ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + tt_assert(ret_chan); + tt_ptr_op(ret_chan, OP_EQ, chan2); + tt_int_op(launch, OP_EQ, 0); + tt_str_op(msg, OP_EQ, "Connection is fine; using it."); + chan1->is_bad_for_new_circs = 0; + + /* Mark both channels as unusable. */ + channel_mark_bad_for_new_circs(chan1); + channel_mark_bad_for_new_circs(chan2); + ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + tt_assert(!ret_chan); + tt_str_op(msg, OP_EQ, "Connections all too old, or too non-canonical. " + " Launching a new one."); + tt_int_op(launch, OP_EQ, 1); + chan1->is_bad_for_new_circs = 0; + chan2->is_bad_for_new_circs = 0; + + /* Non canonical channels. */ + test_chan_should_match_target = 0; + test_chan_canonical_should_be_reliable = 1; + ret_chan = channel_get_for_extend(digest, &ed_id, &addr, &msg, &launch); + tt_assert(!ret_chan); + tt_str_op(msg, OP_EQ, "Connections all too old, or too non-canonical. " + " Launching a new one."); + tt_int_op(launch, OP_EQ, 1); + + done: + free_fake_channel(chan1); + free_fake_channel(chan2); +} + +static void +test_channel_listener(void *arg) +{ + int old_count; + time_t now = time(NULL); + channel_listener_t *chan = NULL; + + (void) arg; + + chan = tor_malloc_zero(sizeof(*chan)); + tt_assert(chan); + channel_init_listener(chan); + tt_u64_op(chan->global_identifier, OP_EQ, 1); + tt_int_op(chan->timestamp_created, OP_GE, now); + chan->close = test_chan_listener_close; + + /* Register it. At this point, it is not open so it will be put in the + * finished list. */ + channel_listener_register(chan); + tt_int_op(chan->registered, OP_EQ, 1); + channel_listener_unregister(chan); + + /* Register it as listening now thus active. */ + chan->state = CHANNEL_LISTENER_STATE_LISTENING; + channel_listener_register(chan); + tt_int_op(chan->registered, OP_EQ, 1); + + /* Set the listener function. */ + channel_listener_set_listener_fn(chan, test_chan_listener_fn); + tt_ptr_op(chan->listener, OP_EQ, test_chan_listener_fn); + + /* Put a channel in the listener incoming list and queue it. + * function. By doing this, the listener() handler will be called. */ + channel_t *in_chan = new_fake_channel(); + old_count = test_chan_listener_fn_called; + channel_listener_queue_incoming(chan, in_chan); + free_fake_channel(in_chan); + tt_int_op(test_chan_listener_fn_called, OP_EQ, old_count + 1); + + /* Put listener channel in CLOSING state. */ + old_count = test_chan_listener_close_fn_called; + channel_listener_mark_for_close(chan); + tt_int_op(test_chan_listener_close_fn_called, OP_EQ, old_count + 1); + channel_listener_change_state(chan, CHANNEL_LISTENER_STATE_CLOSED); + + /* Dump stats so we at least hit the code path. */ + chan->describe_transport = test_chan_listener_describe_transport; + /* There is a check for "now > timestamp_created" when dumping the stats so + * make sure we go in. */ + chan->timestamp_created = now - 10; + channel_listener_dump_statistics(chan, LOG_INFO); + + done: + channel_free_all(); +} + struct testcase_t channel_tests[] = { - { "dumpstats", test_channel_dumpstats, TT_FORK, NULL, NULL }, - { "flush", test_channel_flush, TT_FORK, NULL, NULL }, - { "flushmux", test_channel_flushmux, TT_FORK, NULL, NULL }, - { "incoming", test_channel_incoming, TT_FORK, NULL, NULL }, - { "lifecycle", test_channel_lifecycle, TT_FORK, NULL, NULL }, - { "lifecycle_2", test_channel_lifecycle_2, TT_FORK, NULL, NULL }, - { "multi", test_channel_multi, TT_FORK, NULL, NULL }, - { "queue_impossible", test_channel_queue_impossible, TT_FORK, NULL, NULL }, - { "queue_incoming", test_channel_queue_incoming, TT_FORK, NULL, NULL }, - { "queue_size", test_channel_queue_size, TT_FORK, NULL, NULL }, - { "write", test_channel_write, TT_FORK, NULL, NULL }, - { "id_map", test_channel_id_map, TT_FORK, NULL, NULL }, + { "inbound_cell", test_channel_inbound_cell, TT_FORK, + NULL, NULL }, + { "outbound_cell", test_channel_outbound_cell, TT_FORK, + NULL, NULL }, + { "id_map", test_channel_id_map, TT_FORK, + NULL, NULL }, + { "lifecycle", test_channel_lifecycle, TT_FORK, + NULL, NULL }, + { "lifecycle_2", test_channel_lifecycle_2, TT_FORK, + NULL, NULL }, + { "dumpstats", test_channel_dumpstats, TT_FORK, + NULL, NULL }, + { "state", test_channel_state, TT_FORK, + NULL, NULL }, + { "duplicates", test_channel_duplicates, TT_FORK, + NULL, NULL }, + { "get_channel_for_extend", test_channel_for_extend, TT_FORK, + NULL, NULL }, + { "listener", test_channel_listener, TT_FORK, + NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_channelpadding.c b/src/test/test_channelpadding.c index d5713688a0..4cc33cbe70 100644 --- a/src/test/test_channelpadding.c +++ b/src/test/test_channelpadding.c @@ -1,3 +1,6 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + #define TOR_CHANNEL_INTERNAL_ #define MAIN_PRIVATE #define NETWORKSTATUS_PRIVATE @@ -276,7 +279,6 @@ test_channelpadding_timers(void *arg) { channelpadding_decision_t decision; channel_t *chans[CHANNELS_TO_TEST]; - int64_t new_time; (void)arg; tor_libevent_postfork(); @@ -286,8 +288,9 @@ test_channelpadding_timers(void *arg) monotime_init(); monotime_enable_test_mocking(); - monotime_set_mock_time_nsec(1); - monotime_coarse_set_mock_time_nsec(1); + uint64_t nsec_mock = 1; + monotime_set_mock_time_nsec(nsec_mock); + monotime_coarse_set_mock_time_nsec(nsec_mock); timers_initialize(); channelpadding_new_consensus_params(NULL); @@ -301,11 +304,14 @@ test_channelpadding_timers(void *arg) tried_to_write_cell = 0; int i = 0; + monotime_coarse_t now; + monotime_coarse_get(&now); + /* This loop fills our timerslot array with timers of increasing time * until they fire */ for (; i < CHANNELPADDING_MAX_TIMERS; i++) { - chans[i]->next_padding_time_ms = monotime_coarse_absolute_msec() - + 10 + i*4; + monotime_coarse_add_msec(&chans[i]->next_padding_time, + &now, 10 + i*4); decision = channelpadding_decide_to_pad_channel(chans[i]); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(chans[i]->pending_padding_callback); @@ -315,7 +321,8 @@ test_channelpadding_timers(void *arg) /* This loop should add timers to the first position in the timerslot * array, since its timeout is before all other timers. */ for (; i < CHANNELS_TO_TEST/3; i++) { - chans[i]->next_padding_time_ms = monotime_coarse_absolute_msec() + 1; + monotime_coarse_add_msec(&chans[i]->next_padding_time, + &now, 1); decision = channelpadding_decide_to_pad_channel(chans[i]); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(chans[i]->pending_padding_callback); @@ -326,8 +333,8 @@ test_channelpadding_timers(void *arg) * pseudorandom pattern. It ensures that the lists can grow with multiple * timers in them. */ for (; i < CHANNELS_TO_TEST/2; i++) { - chans[i]->next_padding_time_ms = monotime_coarse_absolute_msec() + 10 + - i*3 % CHANNELPADDING_MAX_TIMERS; + monotime_coarse_add_msec(&chans[i]->next_padding_time, + &now, 10 + i*3 % CHANNELPADDING_MAX_TIMERS); decision = channelpadding_decide_to_pad_channel(chans[i]); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(chans[i]->pending_padding_callback); @@ -337,8 +344,8 @@ test_channelpadding_timers(void *arg) /* This loop should add timers to the last position in the timerslot * array, since its timeout is after all other timers. */ for (; i < CHANNELS_TO_TEST; i++) { - chans[i]->next_padding_time_ms = monotime_coarse_absolute_msec() + 500 + - i % CHANNELPADDING_MAX_TIMERS; + monotime_coarse_add_msec(&chans[i]->next_padding_time, + &now, 500 + i % CHANNELPADDING_MAX_TIMERS); decision = channelpadding_decide_to_pad_channel(chans[i]); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(chans[i]->pending_padding_callback); @@ -346,9 +353,9 @@ test_channelpadding_timers(void *arg) } // Wait for the timers and then kill the event loop. - new_time = (monotime_coarse_absolute_msec()+1001)*NSEC_PER_MSEC; - monotime_coarse_set_mock_time_nsec(new_time); - monotime_set_mock_time_nsec(new_time); + nsec_mock += 1001 * NSEC_PER_MSEC; + monotime_coarse_set_mock_time_nsec(nsec_mock); + monotime_set_mock_time_nsec(nsec_mock); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, CHANNELS_TO_TEST); @@ -385,6 +392,7 @@ test_channelpadding_killonehop(void *arg) monotime_enable_test_mocking(); monotime_set_mock_time_nsec(1); monotime_coarse_set_mock_time_nsec(1); + new_time = 1; timers_initialize(); setup_mock_consensus(); @@ -398,9 +406,12 @@ test_channelpadding_killonehop(void *arg) smartlist_clear(current_md_consensus->net_params); channelpadding_new_consensus_params(current_md_consensus); + monotime_coarse_t now; + monotime_coarse_get(&now); + tried_to_write_cell = 0; get_options_mutable()->Tor2webMode = 1; - client_relay3->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + monotime_coarse_add_msec(&client_relay3->next_padding_time, &now, 100); decision = channelpadding_decide_to_pad_channel(client_relay3); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(client_relay3->pending_padding_callback); @@ -410,9 +421,10 @@ test_channelpadding_killonehop(void *arg) tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_ALREADY_SCHEDULED); // Wait for the timer - new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + new_time += 101*NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(new_time); monotime_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 1); tt_assert(!client_relay3->pending_padding_callback); @@ -424,7 +436,7 @@ test_channelpadding_killonehop(void *arg) // Before the client tries to pad, the relay will still pad: tried_to_write_cell = 0; - relay3_client->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + monotime_coarse_add_msec(&relay3_client->next_padding_time, &now, 100); get_options_mutable()->ORPort_set = 1; get_options_mutable()->Tor2webMode = 0; decision = channelpadding_decide_to_pad_channel(relay3_client); @@ -432,9 +444,10 @@ test_channelpadding_killonehop(void *arg) tt_assert(relay3_client->pending_padding_callback); // Wait for the timer - new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + new_time += 101*NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(new_time); monotime_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 1); tt_assert(!client_relay3->pending_padding_callback); @@ -471,7 +484,8 @@ test_channelpadding_killonehop(void *arg) get_options_mutable()->ORPort_set = 0; get_options_mutable()->HiddenServiceSingleHopMode = 1; get_options_mutable()->HiddenServiceNonAnonymousMode = 1; - client_relay3->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + + monotime_coarse_add_msec(&client_relay3->next_padding_time, &now, 100); decision = channelpadding_decide_to_pad_channel(client_relay3); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(client_relay3->pending_padding_callback); @@ -481,9 +495,10 @@ test_channelpadding_killonehop(void *arg) tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_ALREADY_SCHEDULED); // Wait for the timer - new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + new_time += 101 * NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(new_time); monotime_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 1); tt_assert(!client_relay3->pending_padding_callback); @@ -495,7 +510,7 @@ test_channelpadding_killonehop(void *arg) // Before the client tries to pad, the relay will still pad: tried_to_write_cell = 0; - relay3_client->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + monotime_coarse_add_msec(&relay3_client->next_padding_time, &now, 100); get_options_mutable()->ORPort_set = 1; get_options_mutable()->HiddenServiceSingleHopMode = 0; get_options_mutable()->HiddenServiceNonAnonymousMode = 0; @@ -504,9 +519,10 @@ test_channelpadding_killonehop(void *arg) tt_assert(relay3_client->pending_padding_callback); // Wait for the timer - new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + new_time += 101 * NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(new_time); monotime_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 1); tt_assert(!client_relay3->pending_padding_callback); @@ -570,6 +586,9 @@ test_channelpadding_consensus(void *arg) monotime_enable_test_mocking(); monotime_set_mock_time_nsec(1); monotime_coarse_set_mock_time_nsec(1); + new_time = 1; + monotime_coarse_t now; + monotime_coarse_get(&now); timers_initialize(); if (!connection_array) @@ -583,7 +602,7 @@ test_channelpadding_consensus(void *arg) /* Test 1: Padding can be completely disabled via consensus */ tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + monotime_coarse_add_msec(&chan->next_padding_time, &now, 100); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(chan->pending_padding_callback); @@ -593,9 +612,10 @@ test_channelpadding_consensus(void *arg) tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_ALREADY_SCHEDULED); // Wait for the timer - new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + new_time += 101*NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(new_time); monotime_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 1); tt_assert(!chan->pending_padding_callback); @@ -625,7 +645,7 @@ test_channelpadding_consensus(void *arg) tt_i64_op(val, OP_EQ, 0); val = channelpadding_compute_time_until_pad_for_netflow(chan); tt_i64_op(val, OP_EQ, -2); - tt_assert(!chan->next_padding_time_ms); + tt_assert(monotime_coarse_is_zero(&chan->next_padding_time)); smartlist_clear(current_md_consensus->net_params); @@ -638,7 +658,7 @@ test_channelpadding_consensus(void *arg) channelpadding_new_consensus_params(current_md_consensus); tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + monotime_coarse_add_msec(&chan->next_padding_time, &now, 100); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(chan->pending_padding_callback); @@ -650,9 +670,10 @@ test_channelpadding_consensus(void *arg) tt_i64_op(val, OP_LE, 200); // Wait for the timer - new_time = (monotime_coarse_absolute_msec()+201)*NSEC_PER_MSEC; + new_time += 201*NSEC_PER_MSEC; monotime_set_mock_time_nsec(new_time); monotime_coarse_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 1); tt_assert(!chan->pending_padding_callback); @@ -941,6 +962,9 @@ test_channelpadding_decide_to_pad_channel(void *arg) monotime_enable_test_mocking(); monotime_set_mock_time_nsec(1); monotime_coarse_set_mock_time_nsec(1); + new_time = 1; + monotime_coarse_t now; + monotime_coarse_get(&now); timers_initialize(); setup_full_capture_of_logs(LOG_WARN); channelpadding_new_consensus_params(NULL); @@ -957,7 +981,7 @@ test_channelpadding_decide_to_pad_channel(void *arg) /* Test case #2a: > 1.1s until timeout */ tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 1200; + monotime_coarse_add_msec(&chan->next_padding_time, &now, 1200); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADLATER); tt_assert(!chan->pending_padding_callback); @@ -965,23 +989,27 @@ test_channelpadding_decide_to_pad_channel(void *arg) /* Test case #2b: >= 1.0s until timeout */ tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 1000; + monotime_coarse_add_msec(&chan->next_padding_time, &now, 1000); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(chan->pending_padding_callback); tt_int_op(tried_to_write_cell, OP_EQ, 0); + // Set up a timer for the <0 case below. + monotime_coarse_t now_minus_100s; + monotime_coarse_add_msec(&now_minus_100s, &now, 900); // Wait for the timer from case #2b - new_time = (monotime_coarse_absolute_msec() + 1000)*NSEC_PER_MSEC; + new_time += 1000*NSEC_PER_MSEC; monotime_set_mock_time_nsec(new_time); monotime_coarse_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 1); tt_assert(!chan->pending_padding_callback); /* Test case #2c: > 0.1s until timeout */ tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + monotime_coarse_add_msec(&chan->next_padding_time, &now, 100); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(chan->pending_padding_callback); @@ -992,16 +1020,17 @@ test_channelpadding_decide_to_pad_channel(void *arg) tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_ALREADY_SCHEDULED); // Wait for the timer - new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + new_time += 101*NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(new_time); monotime_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 1); tt_assert(!chan->pending_padding_callback); /* Test case #2e: 0s until timeout */ tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec(); + monotime_coarse_add_msec(&chan->next_padding_time, &now, 0); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SENT); tt_int_op(tried_to_write_cell, OP_EQ, 1); @@ -1009,7 +1038,7 @@ test_channelpadding_decide_to_pad_channel(void *arg) /* Test case #2f: <0s until timeout */ tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec() - 100; + monotime_coarse_add_msec(&chan->next_padding_time, &now_minus_100s, 0); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SENT); tt_int_op(tried_to_write_cell, OP_EQ, 1); @@ -1017,7 +1046,7 @@ test_channelpadding_decide_to_pad_channel(void *arg) /* Test case #3: Channel that sends a packet while timeout is scheduled */ tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + monotime_coarse_add_msec(&chan->next_padding_time, &now, 100); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_int_op(tried_to_write_cell, OP_EQ, 0); @@ -1028,9 +1057,10 @@ test_channelpadding_decide_to_pad_channel(void *arg) // We don't expect any timer callbacks here. Make a dummy one to be sure. // Wait for the timer - new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + new_time += 101*NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(new_time); monotime_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 0); @@ -1038,7 +1068,7 @@ test_channelpadding_decide_to_pad_channel(void *arg) /* Test case #4: Channel that closes while a timeout is scheduled */ tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + monotime_coarse_add_msec(&chan->next_padding_time, &now, 100); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_int_op(tried_to_write_cell, OP_EQ, 0); @@ -1048,9 +1078,10 @@ test_channelpadding_decide_to_pad_channel(void *arg) chan->state = CHANNEL_STATE_MAINT; // We don't expect any timer callbacks here. Make a dummy one to be sure. - new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + new_time += 101*NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(new_time); monotime_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 0); @@ -1059,16 +1090,17 @@ test_channelpadding_decide_to_pad_channel(void *arg) /* Test case #5: Make sure previous test case didn't break everything */ tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + monotime_coarse_add_msec(&chan->next_padding_time, &now, 100); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_assert(chan->pending_padding_callback); tt_int_op(tried_to_write_cell, OP_EQ, 0); // Wait for the timer - new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + new_time += 101*NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(new_time); monotime_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 1); @@ -1087,7 +1119,7 @@ test_channelpadding_decide_to_pad_channel(void *arg) * It must be last. */ tried_to_write_cell = 0; - chan->next_padding_time_ms = monotime_coarse_absolute_msec() + 100; + monotime_coarse_add_msec(&chan->next_padding_time, &now, 100); decision = channelpadding_decide_to_pad_channel(chan); tt_int_op(decision, OP_EQ, CHANNELPADDING_PADDING_SCHEDULED); tt_int_op(tried_to_write_cell, OP_EQ, 0); @@ -1097,9 +1129,10 @@ test_channelpadding_decide_to_pad_channel(void *arg) free_fake_channeltls((channel_tls_t*)chan); // We don't expect any timer callbacks here. Make a dummy one to be sure. - new_time = (monotime_coarse_absolute_msec()+101)*NSEC_PER_MSEC; + new_time = 101*NSEC_PER_MSEC; monotime_coarse_set_mock_time_nsec(new_time); monotime_set_mock_time_nsec(new_time); + monotime_coarse_get(&now); timers_run_pending(); tt_int_op(tried_to_write_cell, OP_EQ, 0); diff --git a/src/test/test_circuitlist.c b/src/test/test_circuitlist.c index f622704ec5..d170009a9c 100644 --- a/src/test/test_circuitlist.c +++ b/src/test/test_circuitlist.c @@ -141,7 +141,7 @@ test_clist_maps(void *arg) /* Okay, now free ch2 and make sure that the circuit ID is STILL not * usable, because we haven't declared the destroy to be nonpending */ tt_int_op(cdm.ncalls, OP_EQ, 0); - circuit_free(TO_CIRCUIT(or_c2)); + circuit_free_(TO_CIRCUIT(or_c2)); or_c2 = NULL; /* prevent free */ tt_int_op(cdm.ncalls, OP_EQ, 2); memset(&cdm, 0, sizeof(cdm)); @@ -160,9 +160,9 @@ test_clist_maps(void *arg) done: if (or_c1) - circuit_free(TO_CIRCUIT(or_c1)); + circuit_free_(TO_CIRCUIT(or_c1)); if (or_c2) - circuit_free(TO_CIRCUIT(or_c2)); + circuit_free_(TO_CIRCUIT(or_c2)); if (ch1) tor_free(ch1->cmux); if (ch2) @@ -234,11 +234,11 @@ test_rend_token_maps(void *arg) /* Marking a circuit makes it not get returned any more */ circuit_mark_for_close(TO_CIRCUIT(c1), END_CIRC_REASON_FINISHED); tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_relay_side(tok1)); - circuit_free(TO_CIRCUIT(c1)); + circuit_free_(TO_CIRCUIT(c1)); c1 = NULL; /* Freeing a circuit makes it not get returned any more. */ - circuit_free(TO_CIRCUIT(c2)); + circuit_free_(TO_CIRCUIT(c2)); c2 = NULL; tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok2)); @@ -275,15 +275,15 @@ test_rend_token_maps(void *arg) done: if (c1) - circuit_free(TO_CIRCUIT(c1)); + circuit_free_(TO_CIRCUIT(c1)); if (c2) - circuit_free(TO_CIRCUIT(c2)); + circuit_free_(TO_CIRCUIT(c2)); if (c3) - circuit_free(TO_CIRCUIT(c3)); + circuit_free_(TO_CIRCUIT(c3)); if (c4) - circuit_free(TO_CIRCUIT(c4)); + circuit_free_(TO_CIRCUIT(c4)); if (c5) - circuit_free(TO_CIRCUIT(c5)); + circuit_free_(TO_CIRCUIT(c5)); } static void @@ -452,10 +452,10 @@ test_hs_circuitmap_isolation(void *arg) } done: - circuit_free(TO_CIRCUIT(circ1)); - circuit_free(TO_CIRCUIT(circ2)); - circuit_free(TO_CIRCUIT(circ3)); - circuit_free(TO_CIRCUIT(circ4)); + circuit_free_(TO_CIRCUIT(circ1)); + circuit_free_(TO_CIRCUIT(circ2)); + circuit_free_(TO_CIRCUIT(circ3)); + circuit_free_(TO_CIRCUIT(circ4)); } struct testcase_t circuitlist_tests[] = { diff --git a/src/test/test_circuitstats.c b/src/test/test_circuitstats.c new file mode 100644 index 0000000000..9d87c325b4 --- /dev/null +++ b/src/test/test_circuitstats.c @@ -0,0 +1,199 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CIRCUITBUILD_PRIVATE +#define CIRCUITSTATS_PRIVATE +#define CIRCUITLIST_PRIVATE +#define CHANNEL_PRIVATE_ + +#include "or.h" +#include "test.h" +#include "test_helpers.h" +#include "log_test_helpers.h" +#include "config.h" +#include "circuitlist.h" +#include "circuitbuild.h" +#include "circuitstats.h" +#include "circuituse.h" +#include "channel.h" + +void test_circuitstats_timeout(void *arg); +void test_circuitstats_hoplen(void *arg); +origin_circuit_t *subtest_fourhop_circuit(struct timeval, int); +origin_circuit_t *add_opened_threehop(void); +origin_circuit_t *build_unopened_fourhop(struct timeval); + +int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice); + +static int marked_for_close; +/* Mock function because we are not trying to test the close circuit that does + * an awful lot of checks on the circuit object. */ +static void +mock_circuit_mark_for_close(circuit_t *circ, int reason, int line, + const char *file) +{ + (void) circ; + (void) reason; + (void) line; + (void) file; + marked_for_close = 1; + return; +} + +origin_circuit_t * +add_opened_threehop(void) +{ + origin_circuit_t *or_circ = origin_circuit_new(); + extend_info_t fakehop; + memset(&fakehop, 0, sizeof(fakehop)); + + TO_CIRCUIT(or_circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; + + or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t)); + or_circ->build_state->desired_path_len = DEFAULT_ROUTE_LEN; + + onion_append_hop(&or_circ->cpath, &fakehop); + onion_append_hop(&or_circ->cpath, &fakehop); + onion_append_hop(&or_circ->cpath, &fakehop); + + or_circ->has_opened = 1; + TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN; + TO_CIRCUIT(or_circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; + + return or_circ; +} + +origin_circuit_t * +build_unopened_fourhop(struct timeval circ_start_time) +{ + origin_circuit_t *or_circ = origin_circuit_new(); + extend_info_t *fakehop = tor_malloc_zero(sizeof(extend_info_t)); + memset(fakehop, 0, sizeof(extend_info_t)); + + TO_CIRCUIT(or_circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; + TO_CIRCUIT(or_circ)->timestamp_began = circ_start_time; + TO_CIRCUIT(or_circ)->timestamp_created = circ_start_time; + + or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t)); + or_circ->build_state->desired_path_len = 4; + + onion_append_hop(&or_circ->cpath, fakehop); + onion_append_hop(&or_circ->cpath, fakehop); + onion_append_hop(&or_circ->cpath, fakehop); + onion_append_hop(&or_circ->cpath, fakehop); + + return or_circ; +} + +origin_circuit_t * +subtest_fourhop_circuit(struct timeval circ_start_time, int should_timeout) +{ + origin_circuit_t *or_circ = build_unopened_fourhop(circ_start_time); + + // Now make them open one at a time and call + // circuit_build_times_handle_completed_hop(); + or_circ->cpath->state = CPATH_STATE_OPEN; + circuit_build_times_handle_completed_hop(or_circ); + tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 0); + + or_circ->cpath->next->state = CPATH_STATE_OPEN; + circuit_build_times_handle_completed_hop(or_circ); + tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 0); + + // Third hop: We should count it now. + or_circ->cpath->next->next->state = CPATH_STATE_OPEN; + circuit_build_times_handle_completed_hop(or_circ); + tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, + !should_timeout); // 1 if counted, 0 otherwise + + // Fourth hop: Don't double count + or_circ->cpath->next->next->next->state = CPATH_STATE_OPEN; + circuit_build_times_handle_completed_hop(or_circ); + tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, + !should_timeout); + + done: + return or_circ; +} + +void +test_circuitstats_hoplen(void *arg) +{ + /* Plan: + * 0. Test no other opened circs (relaxed timeout) + * 1. Check >3 hop circ building w/o timeout + * 2. Check >3 hop circs w/ timeouts.. + */ + struct timeval circ_start_time; + origin_circuit_t *threehop = NULL; + origin_circuit_t *fourhop = NULL; + (void)arg; + MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close); + + circuit_build_times_init(get_circuit_build_times_mutable()); + + // Let's set a close_ms to 2X the initial timeout, so we can + // test relaxed functionality (which uses the close_ms timeout) + get_circuit_build_times_mutable()->close_ms *= 2; + + tor_gettimeofday(&circ_start_time); + circ_start_time.tv_sec -= 119; // make us hit "relaxed" cutoff + + // Test 1: Build a fourhop circuit that should get marked + // as relaxed and eventually counted by circuit_expire_building + // (but not before) + fourhop = subtest_fourhop_circuit(circ_start_time, 0); + tt_int_op(fourhop->relaxed_timeout, OP_EQ, 0); + tt_int_op(marked_for_close, OP_EQ, 0); + circuit_expire_building(); + tt_int_op(marked_for_close, OP_EQ, 0); + tt_int_op(fourhop->relaxed_timeout, OP_EQ, 1); + TO_CIRCUIT(fourhop)->timestamp_began.tv_sec -= 119; + circuit_expire_building(); + tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1); + tt_int_op(marked_for_close, OP_EQ, 1); + + circuit_free_(TO_CIRCUIT(fourhop)); + circuit_build_times_reset(get_circuit_build_times_mutable()); + + // Test 2: Add a threehop circuit for non-relaxed timeouts + threehop = add_opened_threehop(); + + /* This circuit should not timeout */ + tor_gettimeofday(&circ_start_time); + circ_start_time.tv_sec -= 59; + fourhop = subtest_fourhop_circuit(circ_start_time, 0); + circuit_expire_building(); + tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1); + tt_int_op(TO_CIRCUIT(fourhop)->purpose, OP_NE, + CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT); + + circuit_free_((circuit_t *)fourhop); + circuit_build_times_reset(get_circuit_build_times_mutable()); + + /* Test 3: This circuit should now time out and get marked as a + * measurement circuit, but still get counted (and counted only once) + */ + circ_start_time.tv_sec -= 2; + fourhop = subtest_fourhop_circuit(circ_start_time, 0); + tt_int_op(TO_CIRCUIT(fourhop)->purpose, OP_EQ, + CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT); + tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1); + circuit_expire_building(); + tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1); + + done: + UNMOCK(circuit_mark_for_close_); + circuit_free_(TO_CIRCUIT(threehop)); + circuit_free_(TO_CIRCUIT(fourhop)); + circuit_build_times_free_timeouts(get_circuit_build_times_mutable()); +} + +#define TEST_CIRCUITSTATS(name, flags) \ + { #name, test_##name, (flags), NULL, NULL } + +struct testcase_t circuitstats_tests[] = { + TEST_CIRCUITSTATS(circuitstats_hoplen, TT_FORK), + END_OF_TESTCASES +}; + diff --git a/src/test/test_config.c b/src/test/test_config.c index e7380c1d14..4290d0dc6d 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -4833,7 +4833,7 @@ test_config_include_limit(void *data) torrc_path); tt_int_op(write_str_to_file(torrc_path, torrc_contents, 0), OP_EQ, 0); - tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL), + tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL, NULL), OP_EQ, -1); done: @@ -4863,7 +4863,7 @@ test_config_include_does_not_exist(void *data) tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s", missing_path); - tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL), + tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL, NULL), OP_EQ, -1); done: @@ -4895,7 +4895,7 @@ test_config_include_error_in_included_file(void *data) tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s", invalid_path); - tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL), + tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL, NULL), OP_EQ, -1); done: @@ -4937,8 +4937,8 @@ test_config_include_empty_file_folder(void *data) folder_path, file_path); int include_used; - tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used), - OP_EQ, 0); + tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used, + NULL), OP_EQ, 0); tt_ptr_op(result, OP_EQ, NULL); tt_int_op(include_used, OP_EQ, 1); @@ -4975,7 +4975,8 @@ test_config_include_no_permission(void *data) folder_path); int include_used; - tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used), + tt_int_op(config_get_lines_include(torrc_contents, &result, 0, + &include_used, NULL), OP_EQ, -1); tt_ptr_op(result, OP_EQ, NULL); @@ -5031,8 +5032,8 @@ test_config_include_recursion_before_after(void *data) } int include_used; - tt_int_op(config_get_lines_include(file_contents, &result, 0, &include_used), - OP_EQ, 0); + tt_int_op(config_get_lines_include(file_contents, &result, 0, &include_used, + NULL), OP_EQ, 0); tt_ptr_op(result, OP_NE, NULL); tt_int_op(include_used, OP_EQ, 1); @@ -5096,8 +5097,8 @@ test_config_include_recursion_after_only(void *data) } int include_used; - tt_int_op(config_get_lines_include(file_contents, &result, 0, &include_used), - OP_EQ, 0); + tt_int_op(config_get_lines_include(file_contents, &result, 0, &include_used, + NULL), OP_EQ, 0); tt_ptr_op(result, OP_NE, NULL); tt_int_op(include_used, OP_EQ, 1); @@ -5185,8 +5186,8 @@ test_config_include_folder_order(void *data) torrcd); int include_used; - tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used), - OP_EQ, 0); + tt_int_op(config_get_lines_include(torrc_contents, &result, 0, &include_used, + NULL), OP_EQ, 0); tt_ptr_op(result, OP_NE, NULL); tt_int_op(include_used, OP_EQ, 1); @@ -5239,8 +5240,8 @@ test_config_include_path_syntax(void *data) esc_dir_with_pathsep); int include_used; - tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used), - OP_EQ, 0); + tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used, + NULL), OP_EQ, 0); tt_ptr_op(result, OP_EQ, NULL); tt_int_op(include_used, OP_EQ, 1); @@ -5294,14 +5295,14 @@ test_config_include_has_include(void *data) char torrc_contents[1000] = "Test 1\n"; int include_used; - tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used), - OP_EQ, 0); + tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used, + NULL), OP_EQ, 0); tt_int_op(include_used, OP_EQ, 0); config_free_lines(result); tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s\n", dir); - tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used), - OP_EQ, 0); + tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used, + NULL), OP_EQ, 0); tt_int_op(include_used, OP_EQ, 1); done: @@ -5516,6 +5517,85 @@ test_config_check_bridge_distribution_setting_unrecognised(void *arg) return; } +static void +test_config_include_opened_file_list(void *data) +{ + (void)data; + + config_line_t *result = NULL; + smartlist_t *opened_files = smartlist_new(); + char *dir = tor_strdup(get_fname("test_include_opened_file_list")); + tt_ptr_op(dir, OP_NE, NULL); + +#ifdef _WIN32 + tt_int_op(mkdir(dir), OP_EQ, 0); +#else + tt_int_op(mkdir(dir, 0700), OP_EQ, 0); +#endif + + char torrcd[PATH_MAX+1]; + tor_snprintf(torrcd, sizeof(torrcd), "%s"PATH_SEPARATOR"%s", dir, "torrc.d"); + +#ifdef _WIN32 + tt_int_op(mkdir(torrcd), OP_EQ, 0); +#else + tt_int_op(mkdir(torrcd, 0700), OP_EQ, 0); +#endif + + char subfolder[PATH_MAX+1]; + tor_snprintf(subfolder, sizeof(subfolder), "%s"PATH_SEPARATOR"%s", torrcd, + "subfolder"); + +#ifdef _WIN32 + tt_int_op(mkdir(subfolder), OP_EQ, 0); +#else + tt_int_op(mkdir(subfolder, 0700), OP_EQ, 0); +#endif + + char path[PATH_MAX+1]; + tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", subfolder, + "01_file_in_subfolder"); + tt_int_op(write_str_to_file(path, "Test 1\n", 0), OP_EQ, 0); + + char empty[PATH_MAX+1]; + tor_snprintf(empty, sizeof(empty), "%s"PATH_SEPARATOR"%s", torrcd, "empty"); + tt_int_op(write_str_to_file(empty, "", 0), OP_EQ, 0); + + char file[PATH_MAX+1]; + tor_snprintf(file, sizeof(file), "%s"PATH_SEPARATOR"%s", torrcd, "file"); + tt_int_op(write_str_to_file(file, "Test 2\n", 0), OP_EQ, 0); + + char dot[PATH_MAX+1]; + tor_snprintf(dot, sizeof(dot), "%s"PATH_SEPARATOR"%s", torrcd, ".dot"); + tt_int_op(write_str_to_file(dot, "Test 3\n", 0), OP_EQ, 0); + + char torrc_contents[1000]; + tor_snprintf(torrc_contents, sizeof(torrc_contents), + "%%include %s\n", + torrcd); + + int include_used; + tt_int_op(config_get_lines_include(torrc_contents, &result, 0, &include_used, + opened_files), OP_EQ, 0); + tt_ptr_op(result, OP_NE, NULL); + tt_int_op(include_used, OP_EQ, 1); + + tt_int_op(smartlist_len(opened_files), OP_EQ, 4); + tt_int_op(smartlist_contains_string(opened_files, torrcd), OP_EQ, 1); + tt_int_op(smartlist_contains_string(opened_files, subfolder), OP_EQ, 1); + // files inside subfolders are not opended, only the subfolder is opened + tt_int_op(smartlist_contains_string(opened_files, empty), OP_EQ, 1); + tt_int_op(smartlist_contains_string(opened_files, file), OP_EQ, 1); + // dot files are not opened as we ignore them when we get their name from + // their parent folder + + done: + SMARTLIST_FOREACH(opened_files, char *, f, tor_free(f)); + smartlist_free(opened_files); + config_free_lines(result); + tor_free(dir); +} + #define CONFIG_TEST(name, flags) \ { #name, test_config_ ## name, flags, NULL, NULL } @@ -5563,6 +5643,7 @@ struct testcase_t config_tests[] = { CONFIG_TEST(check_bridge_distribution_setting_valid, 0), CONFIG_TEST(check_bridge_distribution_setting_invalid, 0), CONFIG_TEST(check_bridge_distribution_setting_unrecognised, 0), + CONFIG_TEST(include_opened_file_list, 0), END_OF_TESTCASES }; diff --git a/src/test/test_conscache.c b/src/test/test_conscache.c index ddb1bc53c1..ffec3149b0 100644 --- a/src/test/test_conscache.c +++ b/src/test/test_conscache.c @@ -31,8 +31,8 @@ test_conscache_simple_usage(void *arg) /* Make a temporary datadir for these tests */ char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache")); - tor_free(get_options_mutable()->DataDirectory); - get_options_mutable()->DataDirectory = tor_strdup(ddir_fname); + tor_free(get_options_mutable()->CacheDirectory); + get_options_mutable()->CacheDirectory = tor_strdup(ddir_fname); check_private_dir(ddir_fname, CPD_CREATE, NULL); consensus_cache_t *cache = consensus_cache_open("cons", 128); @@ -124,8 +124,8 @@ test_conscache_cleanup(void *arg) /* Make a temporary datadir for these tests */ char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache")); - tor_free(get_options_mutable()->DataDirectory); - get_options_mutable()->DataDirectory = tor_strdup(ddir_fname); + tor_free(get_options_mutable()->CacheDirectory); + get_options_mutable()->CacheDirectory = tor_strdup(ddir_fname); check_private_dir(ddir_fname, CPD_CREATE, NULL); consensus_cache_t *cache = consensus_cache_open("cons", 128); @@ -267,8 +267,8 @@ test_conscache_filter(void *arg) /* Make a temporary datadir for these tests */ char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache")); - tor_free(get_options_mutable()->DataDirectory); - get_options_mutable()->DataDirectory = tor_strdup(ddir_fname); + tor_free(get_options_mutable()->CacheDirectory); + get_options_mutable()->CacheDirectory = tor_strdup(ddir_fname); check_private_dir(ddir_fname, CPD_CREATE, NULL); consensus_cache_t *cache = consensus_cache_open("cons", 128); diff --git a/src/test/test_consdiffmgr.c b/src/test/test_consdiffmgr.c index 80d3f943ab..a9a4b6a98e 100644 --- a/src/test/test_consdiffmgr.c +++ b/src/test/test_consdiffmgr.c @@ -24,8 +24,8 @@ consdiffmgr_test_setup(const struct testcase_t *arg) { (void)arg; char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cdm")); - tor_free(get_options_mutable()->DataDirectory); - get_options_mutable()->DataDirectory = ddir_fname; // now owns the pointer. + tor_free(get_options_mutable()->CacheDirectory); + get_options_mutable()->CacheDirectory = ddir_fname; // now owns the pointer. check_private_dir(ddir_fname, CPD_CREATE, NULL); consdiff_cfg_t consdiff_cfg = { 300 }; @@ -215,8 +215,8 @@ test_consdiffmgr_init_failure(void *arg) /* As in ...test_setup, but do not create the datadir. The missing directory * will cause a failure. */ char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cdm")); - tor_free(get_options_mutable()->DataDirectory); - get_options_mutable()->DataDirectory = ddir_fname; // now owns the pointer. + tor_free(get_options_mutable()->CacheDirectory); + get_options_mutable()->CacheDirectory = ddir_fname; // now owns the pointer. consdiff_cfg_t consdiff_cfg = { 7200, 300 }; diff --git a/src/test/test_controller.c b/src/test/test_controller.c index 472fcb8c53..af19f63f6c 100644 --- a/src/test/test_controller.c +++ b/src/test/test_controller.c @@ -6,6 +6,7 @@ #include "bridges.h" #include "control.h" #include "entrynodes.h" +#include "hs_common.h" #include "networkstatus.h" #include "rendservice.h" #include "routerlist.h" @@ -13,10 +14,87 @@ #include "test_helpers.h" static void -test_add_onion_helper_keyarg(void *arg) +test_add_onion_helper_keyarg_v3(void *arg) { - crypto_pk_t *pk = NULL; - crypto_pk_t *pk2 = NULL; + int ret, hs_version; + add_onion_secret_key_t pk; + char *key_new_blob = NULL; + char *err_msg = NULL; + const char *key_new_alg = NULL; + + (void) arg; + + memset(&pk, 0, sizeof(pk)); + + /* Test explicit ED25519-V3 key generation. */ + ret = add_onion_helper_keyarg("NEW:ED25519-V3", 0, &key_new_alg, + &key_new_blob, &pk, &hs_version, + &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE); + tt_assert(pk.v3); + tt_str_op(key_new_alg, OP_EQ, "ED25519-V3"); + tt_assert(key_new_blob); + tt_ptr_op(err_msg, OP_EQ, NULL); + tor_free(pk.v3); pk.v3 = NULL; + tor_free(key_new_blob); + + /* Test discarding the private key. */ + ret = add_onion_helper_keyarg("NEW:ED25519-V3", 1, &key_new_alg, + &key_new_blob, &pk, &hs_version, + &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE); + tt_assert(pk.v3); + tt_ptr_op(key_new_alg, OP_EQ, NULL); + tt_ptr_op(key_new_blob, OP_EQ, NULL); + tt_ptr_op(err_msg, OP_EQ, NULL); + tor_free(pk.v3); pk.v3 = NULL; + tor_free(key_new_blob); + + /* Test passing a key blob. */ + { + /* The base64 key and hex key are the same. Hex key is 64 bytes long. The + * sk has been generated randomly using python3. */ + const char *base64_sk = + "a9bT19PqGC9Y+BmOo1IQvCGjjwxMiaaxEXZ+FKMxpEQW" + "6AmSV5roThUGMRCaqQSCnR2jI1vL2QxHORzI4RxMmw=="; + const char *hex_sk = + "\x6b\xd6\xd3\xd7\xd3\xea\x18\x2f\x58\xf8\x19\x8e\xa3\x52\x10\xbc" + "\x21\xa3\x8f\x0c\x4c\x89\xa6\xb1\x11\x76\x7e\x14\xa3\x31\xa4\x44" + "\x16\xe8\x09\x92\x57\x9a\xe8\x4e\x15\x06\x31\x10\x9a\xa9\x04\x82" + "\x9d\x1d\xa3\x23\x5b\xcb\xd9\x0c\x47\x39\x1c\xc8\xe1\x1c\x4c\x9b"; + char *key_blob = NULL; + + tor_asprintf(&key_blob, "ED25519-V3:%s", base64_sk); + tt_assert(key_blob); + ret = add_onion_helper_keyarg(key_blob, 1, &key_new_alg, + &key_new_blob, &pk, &hs_version, + &err_msg); + tor_free(key_blob); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE); + tt_assert(pk.v3); + tt_mem_op(pk.v3, OP_EQ, hex_sk, 64); + tt_ptr_op(key_new_alg, OP_EQ, NULL); + tt_ptr_op(key_new_blob, OP_EQ, NULL); + tt_ptr_op(err_msg, OP_EQ, NULL); + tor_free(pk.v3); pk.v3 = NULL; + tor_free(key_new_blob); + } + + done: + tor_free(pk.v3); + tor_free(key_new_blob); + tor_free(err_msg); +} + +static void +test_add_onion_helper_keyarg_v2(void *arg) +{ + int ret, hs_version; + add_onion_secret_key_t pk; + crypto_pk_t *pk1 = NULL; const char *key_new_alg = NULL; char *key_new_blob = NULL; char *err_msg = NULL; @@ -25,83 +103,100 @@ test_add_onion_helper_keyarg(void *arg) (void) arg; + memset(&pk, 0, sizeof(pk)); + /* Test explicit RSA1024 key generation. */ - pk = add_onion_helper_keyarg("NEW:RSA1024", 0, &key_new_alg, &key_new_blob, - &err_msg); - tt_assert(pk); + ret = add_onion_helper_keyarg("NEW:RSA1024", 0, &key_new_alg, &key_new_blob, + &pk, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(pk.v2); tt_str_op(key_new_alg, OP_EQ, "RSA1024"); tt_assert(key_new_blob); tt_ptr_op(err_msg, OP_EQ, NULL); /* Test "BEST" key generation (Assumes BEST = RSA1024). */ - crypto_pk_free(pk); + crypto_pk_free(pk.v2); pk.v2 = NULL; tor_free(key_new_blob); - pk = add_onion_helper_keyarg("NEW:BEST", 0, &key_new_alg, &key_new_blob, - &err_msg); - tt_assert(pk); + ret = add_onion_helper_keyarg("NEW:BEST", 0, &key_new_alg, &key_new_blob, + &pk, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(pk.v2); tt_str_op(key_new_alg, OP_EQ, "RSA1024"); tt_assert(key_new_blob); tt_ptr_op(err_msg, OP_EQ, NULL); /* Test discarding the private key. */ - crypto_pk_free(pk); + crypto_pk_free(pk.v2); pk.v2 = NULL; tor_free(key_new_blob); - pk = add_onion_helper_keyarg("NEW:BEST", 1, &key_new_alg, &key_new_blob, - &err_msg); - tt_assert(pk); + ret = add_onion_helper_keyarg("NEW:BEST", 1, &key_new_alg, &key_new_blob, + &pk, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(pk.v2); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); tt_ptr_op(err_msg, OP_EQ, NULL); /* Test generating a invalid key type. */ - crypto_pk_free(pk); - pk = add_onion_helper_keyarg("NEW:RSA512", 0, &key_new_alg, &key_new_blob, - &err_msg); - tt_ptr_op(pk, OP_EQ, NULL); + crypto_pk_free(pk.v2); pk.v2 = NULL; + ret = add_onion_helper_keyarg("NEW:RSA512", 0, &key_new_alg, &key_new_blob, + &pk, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, -1); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(!pk.v2); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); tt_assert(err_msg); /* Test loading a RSA1024 key. */ tor_free(err_msg); - pk = pk_generate(0); - tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk, &encoded)); + pk1 = pk_generate(0); + tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk1, &encoded)); tor_asprintf(&arg_str, "RSA1024:%s", encoded); - pk2 = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, - &err_msg); - tt_assert(pk2); + ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, + &pk, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(pk.v2); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); tt_ptr_op(err_msg, OP_EQ, NULL); - tt_int_op(crypto_pk_cmp_keys(pk, pk2), OP_EQ, 0); + tt_int_op(crypto_pk_cmp_keys(pk1, pk.v2), OP_EQ, 0); /* Test loading a invalid key type. */ tor_free(arg_str); - crypto_pk_free(pk); pk = NULL; + crypto_pk_free(pk1); pk1 = NULL; + crypto_pk_free(pk.v2); pk.v2 = NULL; tor_asprintf(&arg_str, "RSA512:%s", encoded); - pk = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, - &err_msg); - tt_ptr_op(pk, OP_EQ, NULL); + ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, + &pk, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, -1); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(!pk.v2); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); tt_assert(err_msg); /* Test loading a invalid key. */ tor_free(arg_str); - crypto_pk_free(pk); pk = NULL; + crypto_pk_free(pk.v2); pk.v2 = NULL; tor_free(err_msg); encoded[strlen(encoded)/2] = '\0'; tor_asprintf(&arg_str, "RSA1024:%s", encoded); - pk = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, - &err_msg); - tt_ptr_op(pk, OP_EQ, NULL); + ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, + &pk, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, -1); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(!pk.v2); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); tt_assert(err_msg); done: - crypto_pk_free(pk); - crypto_pk_free(pk2); + crypto_pk_free(pk1); + crypto_pk_free(pk.v2); tor_free(key_new_blob); tor_free(err_msg); tor_free(encoded); @@ -1370,7 +1465,10 @@ test_download_status_bridge(void *arg) } struct testcase_t controller_tests[] = { - { "add_onion_helper_keyarg", test_add_onion_helper_keyarg, 0, NULL, NULL }, + { "add_onion_helper_keyarg_v2", test_add_onion_helper_keyarg_v2, 0, + NULL, NULL }, + { "add_onion_helper_keyarg_v3", test_add_onion_helper_keyarg_v3, 0, + NULL, NULL }, { "getinfo_helper_onion", test_getinfo_helper_onion, 0, NULL, NULL }, { "rend_service_parse_port_config", test_rend_service_parse_port_config, 0, NULL, NULL }, diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 997b110d62..6092a5a4d0 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -567,7 +567,7 @@ test_dir_routerinfo_parsing(void *arg) static void routerinfo_free_wrapper_(void *arg) { - routerinfo_free(arg); + routerinfo_free_(arg); } static void @@ -664,7 +664,7 @@ test_dir_extrainfo_parsing(void *arg) escaped(NULL); extrainfo_free(ei); routerinfo_free(ri); - digestmap_free((digestmap_t*)map, routerinfo_free_wrapper_); + digestmap_free_((digestmap_t*)map, routerinfo_free_wrapper_); } static void @@ -760,7 +760,7 @@ test_dir_parse_router_list(void *arg) smartlist_free(chunks); routerinfo_free(ri); if (map) { - digestmap_free((digestmap_t*)map, routerinfo_free_wrapper_); + digestmap_free_((digestmap_t*)map, routerinfo_free_wrapper_); router_get_routerlist()->identity_map = (struct digest_ri_map_t*)digestmap_new(); } @@ -4874,9 +4874,11 @@ mock_check_private_dir(const char *dirname, cpd_check_t check, static char * mock_get_datadir_fname(const or_options_t *options, + directory_root_t roottype, const char *sub1, const char *sub2, const char *suffix) { + (void) roottype; char *rv = NULL; /* @@ -5033,7 +5035,7 @@ test_dir_dump_unparseable_descriptors(void *data) mock_options->MaxUnparseableDescSizeToLog = 1536; MOCK(get_options, mock_get_options); MOCK(check_private_dir, mock_check_private_dir); - MOCK(options_get_datadir_fname2_suffix, + MOCK(options_get_dir_fname2_suffix, mock_get_datadir_fname); /* @@ -5551,7 +5553,7 @@ test_dir_dump_unparseable_descriptors(void *data) mock_unlink_reset(); UNMOCK(write_str_to_file); mock_write_str_to_file_reset(); - UNMOCK(options_get_datadir_fname2_suffix); + UNMOCK(options_get_dir_fname2_suffix); UNMOCK(check_private_dir); UNMOCK(get_options); tor_free(mock_options); @@ -6174,6 +6176,106 @@ test_dir_platform_str(void *arg) ; } +static networkstatus_t *mock_networkstatus; + +static networkstatus_t * +mock_networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f) +{ + (void)f; + return mock_networkstatus; +} + +static void +test_dir_networkstatus_consensus_has_ipv6(void *arg) +{ + (void)arg; + + int has_ipv6 = 0; + + /* Init options and networkstatus */ + or_options_t our_options; + mock_options = &our_options; + reset_options(mock_options, &mock_get_options_calls); + MOCK(get_options, mock_get_options); + + networkstatus_t our_networkstatus; + mock_networkstatus = &our_networkstatus; + memset(mock_networkstatus, 0, sizeof(*mock_networkstatus)); + MOCK(networkstatus_get_latest_consensus_by_flavor, + mock_networkstatus_get_latest_consensus_by_flavor); + + /* A live consensus */ + mock_networkstatus->valid_after = time(NULL) - 3600; + mock_networkstatus->valid_until = time(NULL) + 3600; + + /* Test the bounds for A lines in the NS consensus */ + mock_options->UseMicrodescriptors = 0; + + mock_networkstatus->consensus_method = MIN_METHOD_FOR_A_LINES; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(has_ipv6); + + mock_networkstatus->consensus_method = MIN_METHOD_FOR_A_LINES + 1; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(has_ipv6); + + mock_networkstatus->consensus_method = MIN_METHOD_FOR_A_LINES + 20; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(has_ipv6); + + mock_networkstatus->consensus_method = MIN_METHOD_FOR_A_LINES - 1; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(!has_ipv6); + + /* Test the bounds for A lines in the microdesc consensus */ + mock_options->UseMicrodescriptors = 1; + + mock_networkstatus->consensus_method = + MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(has_ipv6); + + mock_networkstatus->consensus_method = + MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS + 1; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(has_ipv6); + + mock_networkstatus->consensus_method = + MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS + 20; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(has_ipv6); + + mock_networkstatus->consensus_method = + MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS - 1; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(!has_ipv6); + + /* Test the edge cases */ + mock_options->UseMicrodescriptors = 1; + mock_networkstatus->consensus_method = + MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS; + + /* Reasonably live */ + mock_networkstatus->valid_until = time(NULL) - 60; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(has_ipv6); + + /* Not reasonably live */ + mock_networkstatus->valid_after = time(NULL) - 24*60*60 - 3600; + mock_networkstatus->valid_until = time(NULL) - 24*60*60 - 60; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(!has_ipv6); + + /* NULL consensus */ + mock_networkstatus = NULL; + has_ipv6 = networkstatus_consensus_has_ipv6(get_options()); + tt_assert(!has_ipv6); + + done: + UNMOCK(get_options); + UNMOCK(networkstatus_get_latest_consensus_by_flavor); +} + #define DIR_LEGACY(name) \ { #name, test_dir_ ## name , TT_FORK, NULL, NULL } @@ -6241,6 +6343,7 @@ struct testcase_t dir_tests[] = { DIR(assumed_flags, 0), DIR(networkstatus_compute_bw_weights_v10, 0), DIR(platform_str, 0), + DIR(networkstatus_consensus_has_ipv6, 0), END_OF_TESTCASES }; diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c index fe26657ad8..ca64dce5fe 100644 --- a/src/test/test_dir_handle_get.c +++ b/src/test/test_dir_handle_get.c @@ -88,7 +88,7 @@ test_dir_handle_get_bad_request(void *data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -117,7 +117,7 @@ test_dir_handle_get_v1_command_not_found(void *data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -164,7 +164,7 @@ test_dir_handle_get_v1_command(void *data) done: UNMOCK(connection_write_to_buf_impl_); UNMOCK(get_dirportfrontpage); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); } @@ -190,7 +190,7 @@ test_dir_handle_get_not_found(void *data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -225,7 +225,7 @@ test_dir_handle_get_robots_txt(void *data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); } @@ -254,7 +254,7 @@ test_dir_handle_get_rendezvous2_not_found_if_not_encrypted(void *data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -282,7 +282,7 @@ test_dir_handle_get_rendezvous2_on_encrypted_conn_with_invalid_desc_id( done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -315,7 +315,7 @@ test_dir_handle_get_rendezvous2_on_encrypted_conn_not_well_formed(void *data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -344,7 +344,7 @@ test_dir_handle_get_rendezvous2_not_found(void *data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); rend_cache_free_all(); } @@ -424,7 +424,7 @@ test_dir_handle_get_rendezvous2_on_encrypted_conn_success(void *data) UNMOCK(connection_write_to_buf_impl_); NS_UNMOCK(router_get_my_routerinfo); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); rend_encoded_v2_service_descriptor_free(desc_holder); @@ -457,7 +457,7 @@ test_dir_handle_get_micro_d_not_found(void *data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -469,6 +469,7 @@ init_mock_options(void) memset(mock_options, 0, sizeof(or_options_t)); mock_options->TestingTorNetwork = 1; mock_options->DataDirectory = tor_strdup(get_fname_rnd("datadir_tmp")); + mock_options->CacheDirectory = tor_strdup(mock_options->DataDirectory); check_private_dir(mock_options->DataDirectory, CPD_CREATE, NULL); } @@ -541,7 +542,7 @@ test_dir_handle_get_micro_d(void *data) UNMOCK(connection_write_to_buf_impl_); or_options_free(mock_options); mock_options = NULL; - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); smartlist_free(list); @@ -595,7 +596,7 @@ test_dir_handle_get_micro_d_server_busy(void *data) UNMOCK(connection_write_to_buf_impl_); or_options_free(mock_options); mock_options = NULL; - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); smartlist_free(list); microdesc_free_all(); @@ -632,7 +633,7 @@ test_dir_handle_get_networkstatus_bridges_not_found_without_auth(void *data) UNMOCK(get_options); UNMOCK(connection_write_to_buf_impl_); or_options_free(mock_options); mock_options = NULL; - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -672,7 +673,7 @@ test_dir_handle_get_networkstatus_bridges(void *data) UNMOCK(get_options); UNMOCK(connection_write_to_buf_impl_); or_options_free(mock_options); mock_options = NULL; - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -709,7 +710,7 @@ test_dir_handle_get_networkstatus_bridges_not_found_wrong_auth(void *data) UNMOCK(get_options); UNMOCK(connection_write_to_buf_impl_); or_options_free(mock_options); mock_options = NULL; - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -737,7 +738,7 @@ test_dir_handle_get_server_descriptors_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); or_options_free(mock_options); mock_options = NULL; - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -798,7 +799,7 @@ test_dir_handle_get_server_descriptors_all(void* data) done: NS_UNMOCK(router_get_my_routerinfo); UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); @@ -902,7 +903,7 @@ test_dir_handle_get_server_descriptors_authority(void* data) UNMOCK(connection_write_to_buf_impl_); tor_free(mock_routerinfo->cache_info.signed_descriptor_body); tor_free(mock_routerinfo); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); crypto_pk_free(identity_pkey); @@ -974,7 +975,7 @@ test_dir_handle_get_server_descriptors_fp(void* data) UNMOCK(connection_write_to_buf_impl_); tor_free(mock_routerinfo->cache_info.signed_descriptor_body); tor_free(mock_routerinfo); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); crypto_pk_free(identity_pkey); @@ -1038,7 +1039,7 @@ test_dir_handle_get_server_descriptors_d(void* data) done: UNMOCK(connection_write_to_buf_impl_); tor_free(mock_routerinfo); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); crypto_pk_free(identity_pkey); @@ -1094,7 +1095,7 @@ test_dir_handle_get_server_descriptors_busy(void* data) UNMOCK(get_options); UNMOCK(connection_write_to_buf_impl_); tor_free(mock_routerinfo); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); crypto_pk_free(identity_pkey); @@ -1125,7 +1126,7 @@ test_dir_handle_get_server_keys_bad_req(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -1151,7 +1152,7 @@ test_dir_handle_get_server_keys_all_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -1210,7 +1211,7 @@ test_dir_handle_get_server_keys_all(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); @@ -1240,7 +1241,7 @@ test_dir_handle_get_server_keys_authority_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -1288,7 +1289,7 @@ test_dir_handle_get_server_keys_authority(void* data) done: UNMOCK(get_my_v3_authority_cert); UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); authority_cert_free(mock_cert); mock_cert = NULL; @@ -1316,7 +1317,7 @@ test_dir_handle_get_server_keys_fp_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -1370,7 +1371,7 @@ test_dir_handle_get_server_keys_fp(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); clear_dir_servers(); @@ -1399,7 +1400,7 @@ test_dir_handle_get_server_keys_sk_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -1444,7 +1445,7 @@ test_dir_handle_get_server_keys_sk(void* data) done: UNMOCK(get_my_v3_authority_cert); UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); authority_cert_free(mock_cert); mock_cert = NULL; tor_free(header); tor_free(body); @@ -1472,7 +1473,7 @@ test_dir_handle_get_server_keys_fpsk_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -1529,7 +1530,7 @@ test_dir_handle_get_server_keys_fpsk(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); @@ -1583,7 +1584,7 @@ test_dir_handle_get_server_keys_busy(void* data) done: UNMOCK(get_options); UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); or_options_free(mock_options); mock_options = NULL; @@ -1649,7 +1650,7 @@ test_dir_handle_get_status_vote_current_consensus_ns_not_enough_sigs(void* d) UNMOCK(connection_write_to_buf_impl_); UNMOCK(get_options); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(stats); smartlist_free(mock_ns_val->voters); @@ -1690,7 +1691,7 @@ test_dir_handle_get_status_vote_current_consensus_ns_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); UNMOCK(get_options); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(stats); or_options_free(mock_options); mock_options = NULL; @@ -1764,7 +1765,7 @@ test_dir_handle_get_status_vote_current_consensus_too_old(void *data) UNMOCK(networkstatus_get_latest_consensus_by_flavor); UNMOCK(connection_write_to_buf_impl_); UNMOCK(get_options); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(mock_ns_val); or_options_free(mock_options); mock_options = NULL; @@ -1825,7 +1826,7 @@ status_vote_current_consensus_ns_test(char **header, char **body, done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); } static void @@ -1948,7 +1949,7 @@ test_dir_handle_get_status_vote_current_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -1971,7 +1972,7 @@ status_vote_current_d_test(char **header, char **body, size_t *body_l) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); } static void @@ -1991,7 +1992,7 @@ status_vote_next_d_test(char **header, char **body, size_t *body_l) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); } static void @@ -2116,7 +2117,7 @@ test_dir_handle_get_status_vote_next_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -2135,7 +2136,7 @@ status_vote_next_consensus_test(char **header, char **body, size_t *body_used) body, body_used, 18, 0); done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); } static void @@ -2175,7 +2176,7 @@ test_dir_handle_get_status_vote_current_authority_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -2199,7 +2200,7 @@ test_dir_handle_get_status_vote_next_authority_not_found(void* data) done: UNMOCK(connection_write_to_buf_impl_); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); } @@ -2281,7 +2282,7 @@ status_vote_next_consensus_signatures_test(char **header, char **body, body, body_used, 22, 0); done: - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); UNMOCK(connection_write_to_buf_impl_); } @@ -2429,7 +2430,7 @@ test_dir_handle_get_status_vote_next_authority(void* data) done: UNMOCK(connection_write_to_buf_impl_); UNMOCK(get_my_v3_authority_cert); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); authority_cert_free(mock_cert); mock_cert = NULL; @@ -2511,7 +2512,7 @@ test_dir_handle_get_status_vote_current_authority(void* data) done: UNMOCK(connection_write_to_buf_impl_); UNMOCK(get_my_v3_authority_cert); - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); authority_cert_free(mock_cert); mock_cert = NULL; diff --git a/src/test/test_dns.c b/src/test/test_dns.c index 19dcb02931..1fee01d2c0 100644 --- a/src/test/test_dns.c +++ b/src/test/test_dns.c @@ -1,3 +1,6 @@ +/* Copyright (c) 2015-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + #include "or.h" #include "test.h" @@ -121,7 +124,7 @@ static int n_connection_free = 0; static connection_t *last_freed_conn = NULL; static void -NS(connection_free)(connection_t *conn) +NS(connection_free_)(connection_t *conn) { n_connection_free++; @@ -264,7 +267,7 @@ NS(test_main)(void *arg) */ NS_MOCK(dns_cancel_pending_resolve); - NS_MOCK(connection_free); + NS_MOCK(connection_free_); exitconn->on_circuit = &(on_circuit->base_); exitconn->base_.purpose = EXIT_PURPOSE_RESOLVE; @@ -291,7 +294,7 @@ NS(test_main)(void *arg) NS_UNMOCK(send_resolved_cell); NS_UNMOCK(send_resolved_hostname_cell); NS_UNMOCK(dns_cancel_pending_resolve); - NS_UNMOCK(connection_free); + NS_UNMOCK(connection_free_); tor_free(on_circuit); tor_free(exitconn); tor_free(nextconn); diff --git a/src/test/test_entryconn.c b/src/test/test_entryconn.c index c29b1a7126..9d8a072c77 100644 --- a/src/test/test_entryconn.c +++ b/src/test/test_entryconn.c @@ -34,7 +34,7 @@ entryconn_rewrite_teardown(const struct testcase_t *tc, void *arg) (void)tc; entry_connection_t *ec = arg; if (ec) - connection_free_(ENTRY_TO_CONN(ec)); + connection_free_minimal(ENTRY_TO_CONN(ec)); addressmap_free_all(); return 1; } @@ -156,8 +156,8 @@ test_entryconn_rewrite_automap_ipv4(void *arg) ec->socks_request->address); done: - connection_free_(ENTRY_TO_CONN(ec2)); - connection_free_(ENTRY_TO_CONN(ec3)); + connection_free_minimal(ENTRY_TO_CONN(ec2)); + connection_free_minimal(ENTRY_TO_CONN(ec3)); } /* Automap on resolve, connect to automapped address, resolve again and get @@ -230,9 +230,9 @@ test_entryconn_rewrite_automap_ipv6(void *arg) ec->socks_request->address); done: - connection_free_(ENTRY_TO_CONN(ec)); - connection_free_(ENTRY_TO_CONN(ec2)); - connection_free_(ENTRY_TO_CONN(ec3)); + connection_free_minimal(ENTRY_TO_CONN(ec)); + connection_free_minimal(ENTRY_TO_CONN(ec2)); + connection_free_minimal(ENTRY_TO_CONN(ec3)); } #if 0 @@ -283,7 +283,7 @@ test_entryconn_rewrite_automap_reverse(void *arg) tt_int_op(rr.exit_source, OP_EQ, ADDRMAPSRC_NONE); done: - connection_free_(ENTRY_TO_CONN(ec2)); + connection_free_minimal(ENTRY_TO_CONN(ec2)); } #endif /* 0 */ @@ -333,7 +333,7 @@ test_entryconn_rewrite_cached_dns_ipv4(void *arg) tt_str_op(ec2->socks_request->address, OP_EQ, "240.240.241.241"); done: - connection_free_(ENTRY_TO_CONN(ec2)); + connection_free_minimal(ENTRY_TO_CONN(ec2)); } /* Rewrite because of cached DNS entry. */ @@ -385,8 +385,8 @@ test_entryconn_rewrite_cached_dns_ipv6(void *arg) tt_str_op(ec2->socks_request->address, OP_EQ, "[::f00f]"); done: - connection_free_(ENTRY_TO_CONN(ec)); - connection_free_(ENTRY_TO_CONN(ec2)); + connection_free_minimal(ENTRY_TO_CONN(ec)); + connection_free_minimal(ENTRY_TO_CONN(ec2)); } /* Fail to connect to unmapped address in virtual range. */ @@ -426,7 +426,7 @@ test_entryconn_rewrite_unmapped_virtual(void *arg) tt_int_op(rr.exit_source, OP_EQ, ADDRMAPSRC_NONE); done: - connection_free_(ENTRY_TO_CONN(ec2)); + connection_free_minimal(ENTRY_TO_CONN(ec2)); } /* Rewrite because of mapaddress option */ @@ -507,7 +507,7 @@ test_entryconn_rewrite_automap_exit(void *arg) tt_int_op(rr.end_reason, OP_EQ, END_STREAM_REASON_TORPROTOCOL); done: - connection_free_(ENTRY_TO_CONN(ec2)); + connection_free_minimal(ENTRY_TO_CONN(ec2)); } /* Rewrite into .exit because of mapaddress */ @@ -618,9 +618,9 @@ test_entryconn_rewrite_mapaddress_automap_onion(void *arg) */ done: - connection_free_(ENTRY_TO_CONN(ec2)); - connection_free_(ENTRY_TO_CONN(ec3)); - connection_free_(ENTRY_TO_CONN(ec4)); + connection_free_minimal(ENTRY_TO_CONN(ec2)); + connection_free_minimal(ENTRY_TO_CONN(ec3)); + connection_free_minimal(ENTRY_TO_CONN(ec4)); } static void @@ -678,8 +678,8 @@ test_entryconn_rewrite_mapaddress_automap_onion_common(entry_connection_t *ec, "abcdefghijklmnop.onion")); done: - connection_free_(ENTRY_TO_CONN(ec2)); - connection_free_(ENTRY_TO_CONN(ec3)); + connection_free_minimal(ENTRY_TO_CONN(ec2)); + connection_free_minimal(ENTRY_TO_CONN(ec3)); } /* This time is the same, but we start with a mapping from a non-onion diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c index 80ebebe3f8..1cff3828c4 100644 --- a/src/test/test_entrynodes.c +++ b/src/test/test_entrynodes.c @@ -2372,8 +2372,8 @@ upgrade_circuits_cleanup(const struct testcase_t *testcase, void *ptr) // circuit_guard_state_free(data->guard2_state); // held in circ2 guard_selection_free(data->gs); smartlist_free(data->all_origin_circuits); - circuit_free(TO_CIRCUIT(data->circ1)); - circuit_free(TO_CIRCUIT(data->circ2)); + circuit_free_(TO_CIRCUIT(data->circ1)); + circuit_free_(TO_CIRCUIT(data->circ2)); tor_free(data); return big_fake_network_cleanup(testcase, NULL); } diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c index e18deb2700..cadef257f1 100644 --- a/src/test/test_extorport.c +++ b/src/test/test_extorport.c @@ -58,11 +58,11 @@ test_ext_or_id_map(void *arg) done: if (c1) - connection_free_(TO_CONN(c1)); + connection_free_minimal(TO_CONN(c1)); if (c2) - connection_free_(TO_CONN(c2)); + connection_free_minimal(TO_CONN(c2)); if (c3) - connection_free_(TO_CONN(c3)); + connection_free_minimal(TO_CONN(c3)); tor_free(idp); tor_free(idp2); connection_or_clear_ext_or_id_map(); @@ -145,7 +145,7 @@ test_ext_or_write_command(void *arg) done: if (c1) - connection_free_(TO_CONN(c1)); + connection_free_minimal(TO_CONN(c1)); tor_free(cp); tor_free(buf); UNMOCK(connection_write_to_buf_impl_); @@ -591,7 +591,7 @@ test_ext_or_handshake(void *arg) UNMOCK(connection_write_to_buf_impl_); UNMOCK(crypto_rand); if (conn) - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); #undef CONTAINS #undef WRITE } diff --git a/src/test/test_handles.c b/src/test/test_handles.c index 7ddee6e376..eb1e1f1bbe 100644 --- a/src/test/test_handles.c +++ b/src/test/test_handles.c @@ -13,6 +13,8 @@ typedef struct demo_t { } demo_t; HANDLE_DECL(demo, demo_t, static) +#define demo_handle_free(h) \ + FREE_AND_NULL(demo_handle_t, demo_handle_free_, (h)) HANDLE_IMPL(demo, demo_t, static) static demo_t * diff --git a/src/test/test_hs.c b/src/test/test_hs.c index 7737499f50..55c6218dd1 100644 --- a/src/test/test_hs.c +++ b/src/test/test_hs.c @@ -258,8 +258,9 @@ test_hs_desc_event(void *arg) sizeof(desc_id_base32)); /* test request event */ - control_event_hs_descriptor_requested(&rend_query.base_, HSDIR_EXIST_ID, - STR_DESC_ID_BASE32); + control_event_hs_descriptor_requested(rend_query.onion_address, + rend_query.auth_type, HSDIR_EXIST_ID, + STR_DESC_ID_BASE32, NULL); expected_msg = "650 HS_DESC REQUESTED "STR_HS_ADDR" NO_AUTH "\ STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32 "\r\n"; tt_assert(received_msg); @@ -268,8 +269,8 @@ test_hs_desc_event(void *arg) /* test received event */ rend_query.auth_type = REND_BASIC_AUTH; - control_event_hs_descriptor_received(rend_query.onion_address, - &rend_query.base_, HSDIR_EXIST_ID); + control_event_hsv2_descriptor_received(rend_query.onion_address, + &rend_query.base_, HSDIR_EXIST_ID); expected_msg = "650 HS_DESC RECEIVED "STR_HS_ADDR" BASIC_AUTH "\ STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32"\r\n"; tt_assert(received_msg); @@ -278,7 +279,7 @@ test_hs_desc_event(void *arg) /* test failed event */ rend_query.auth_type = REND_STEALTH_AUTH; - control_event_hs_descriptor_failed(&rend_query.base_, + control_event_hsv2_descriptor_failed(&rend_query.base_, HSDIR_NONE_EXIST_ID, "QUERY_REJECTED"); expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" STEALTH_AUTH "\ @@ -289,7 +290,7 @@ test_hs_desc_event(void *arg) /* test invalid auth type */ rend_query.auth_type = 999; - control_event_hs_descriptor_failed(&rend_query.base_, + control_event_hsv2_descriptor_failed(&rend_query.base_, HSDIR_EXIST_ID, "QUERY_REJECTED"); expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" UNKNOWN "\ @@ -301,7 +302,7 @@ test_hs_desc_event(void *arg) /* test no HSDir fingerprint type */ rend_query.auth_type = REND_NO_AUTH; - control_event_hs_descriptor_failed(&rend_query.base_, NULL, + control_event_hsv2_descriptor_failed(&rend_query.base_, NULL, "QUERY_NO_HSDIR"); expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" NO_AUTH " \ "UNKNOWN REASON=QUERY_NO_HSDIR\r\n"; diff --git a/src/test/test_hs_cache.c b/src/test/test_hs_cache.c index 91b13be862..458ce1a92e 100644 --- a/src/test/test_hs_cache.c +++ b/src/test/test_hs_cache.c @@ -259,7 +259,7 @@ helper_fetch_desc_from_hsdir(const ed25519_public_key_t *blinded_key) done: tor_free(hsdir_query_str); if (conn) - connection_free_(TO_CONN(conn)); + connection_free_minimal(TO_CONN(conn)); return received_desc; } diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c index 750920fac0..7ee7210bc9 100644 --- a/src/test/test_hs_client.c +++ b/src/test/test_hs_client.c @@ -227,10 +227,10 @@ test_e2e_rend_circuit_setup_legacy(void *arg) tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, TO_CIRCUIT(or_circ)); done: - connection_free_(conn); + connection_free_minimal(conn); if (or_circ) tor_free(TO_CIRCUIT(or_circ)->n_chan); - circuit_free(TO_CIRCUIT(or_circ)); + circuit_free_(TO_CIRCUIT(or_circ)); } /* Test: Ensure that setting up v3 rendezvous circuits works correctly. */ @@ -297,10 +297,10 @@ test_e2e_rend_circuit_setup(void *arg) tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, TO_CIRCUIT(or_circ)); done: - connection_free_(conn); + connection_free_minimal(conn); if (or_circ) tor_free(TO_CIRCUIT(or_circ)->n_chan); - circuit_free(TO_CIRCUIT(or_circ)); + circuit_free_(TO_CIRCUIT(or_circ)); } /** Test client logic for picking intro points from a descriptor. Also test how @@ -560,7 +560,7 @@ test_descriptor_fetch(void *arg) smartlist_add(get_connection_array(), TO_CONN(dir_conn)); ret = hs_client_refetch_hsdesc(&service_pk); smartlist_remove(get_connection_array(), TO_CONN(dir_conn)); - connection_free_(TO_CONN(dir_conn)); + connection_free_minimal(TO_CONN(dir_conn)); tt_int_op(ret, OP_EQ, HS_CLIENT_FETCH_PENDING); } @@ -579,7 +579,7 @@ test_descriptor_fetch(void *arg) tt_int_op(ec->edge_.end_reason, OP_EQ, END_STREAM_REASON_RESOLVEFAILED); done: - connection_free_(ENTRY_TO_CONN(ec)); + connection_free_minimal(ENTRY_TO_CONN(ec)); UNMOCK(networkstatus_get_live_consensus); UNMOCK(router_have_minimum_dir_info); hs_free_all(); diff --git a/src/test/test_hs_control.c b/src/test/test_hs_control.c new file mode 100644 index 0000000000..207a55de6d --- /dev/null +++ b/src/test/test_hs_control.c @@ -0,0 +1,199 @@ +/* Copyright (c) 2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_hs_control.c + * \brief Unit tests for hidden service control port event and command. + **/ + +#define CONTROL_PRIVATE +#define CIRCUITBUILD_PRIVATE +#define RENDCOMMON_PRIVATE +#define RENDSERVICE_PRIVATE +#define HS_SERVICE_PRIVATE + +#include "or.h" +#include "test.h" +#include "control.h" +#include "config.h" +#include "hs_common.h" +#include "hs_control.h" +#include "nodelist.h" +//#include "rendcommon.h" +//#include "rendservice.h" +//#include "routerset.h" +//#include "circuitbuild.h" +#include "test_helpers.h" + +/* mock ID digest and longname for node that's in nodelist */ +#define HSDIR_EXIST_ID \ + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" \ + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" +#define STR_HSDIR_EXIST_LONGNAME \ + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=TestDir" +#define STR_HSDIR_NONE_EXIST_LONGNAME \ + "$BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + +/* Helper global variable for hidden service descriptor event test. + * It's used as a pointer to dynamically created message buffer in + * send_control_event_string_replacement function, which mocks + * send_control_event_string function. + * + * Always free it after use! */ +static char *received_msg = NULL; + +/** Mock function for send_control_event_string + */ +static void +queue_control_event_string_replacement(uint16_t event, char *msg) +{ + (void) event; + tor_free(received_msg); + received_msg = msg; +} + +/** Mock function for node_describe_longname_by_id, it returns either + * STR_HSDIR_EXIST_LONGNAME or STR_HSDIR_NONE_EXIST_LONGNAME + */ +static const char * +node_describe_longname_by_id_replacement(const char *id_digest) +{ + if (!strcmp(id_digest, HSDIR_EXIST_ID)) { + return STR_HSDIR_EXIST_LONGNAME; + } else { + return STR_HSDIR_NONE_EXIST_LONGNAME; + } +} + +/* HSDir fetch index is a series of 'D' */ +#define HSDIR_INDEX_FETCH_HEX \ + "4343434343434343434343434343434343434343434343434343434343434343" +#define HSDIR_INDEX_STORE_HEX \ + "4444444444444444444444444444444444444444444444444444444444444444" + +static const node_t * +mock_node_get_by_id(const char *digest) +{ + static node_t node; + memcpy(node.identity, digest, DIGEST_LEN); + node.hsdir_index = tor_malloc_zero(sizeof(hsdir_index_t)); + memset(node.hsdir_index->fetch, 'C', DIGEST256_LEN); + memset(node.hsdir_index->store_first, 'D', DIGEST256_LEN); + return &node; +} + +static void +test_hs_desc_event(void *arg) +{ + int ret; + char *expected_msg = NULL; + char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + ed25519_keypair_t identity_kp; + ed25519_public_key_t blinded_pk; + char base64_blinded_pk[ED25519_BASE64_LEN + 1]; + routerstatus_t hsdir_rs; + hs_ident_dir_conn_t ident; + + (void) arg; + MOCK(queue_control_event_string, + queue_control_event_string_replacement); + MOCK(node_describe_longname_by_id, + node_describe_longname_by_id_replacement); + MOCK(node_get_by_id, mock_node_get_by_id); + + /* Setup what we need for this test. */ + ed25519_keypair_generate(&identity_kp, 0); + hs_build_address(&identity_kp.pubkey, HS_VERSION_THREE, onion_address); + ret = hs_address_is_valid(onion_address); + tt_int_op(ret, OP_EQ, 1); + memset(&blinded_pk, 'B', sizeof(blinded_pk)); + memset(&hsdir_rs, 0, sizeof(hsdir_rs)); + memcpy(hsdir_rs.identity_digest, HSDIR_EXIST_ID, DIGEST_LEN); + ret = ed25519_public_to_base64(base64_blinded_pk, &blinded_pk); + tt_int_op(ret, OP_EQ, 0); + memcpy(&ident.identity_pk, &identity_kp.pubkey, + sizeof(ed25519_public_key_t)); + memcpy(&ident.blinded_pk, &blinded_pk, sizeof(blinded_pk)); + + /* HS_DESC REQUESTED ... */ + hs_control_desc_event_requested(&identity_kp.pubkey, base64_blinded_pk, + &hsdir_rs); + tor_asprintf(&expected_msg, "650 HS_DESC REQUESTED %s NO_AUTH " + STR_HSDIR_EXIST_LONGNAME " %s HSDIR_INDEX=" + HSDIR_INDEX_FETCH_HEX "\r\n", + onion_address, base64_blinded_pk); + tt_assert(received_msg); + tt_str_op(received_msg, OP_EQ, expected_msg); + tor_free(received_msg); + tor_free(expected_msg); + + /* HS_DESC CREATED... */ + hs_control_desc_event_created(onion_address, &blinded_pk); + tor_asprintf(&expected_msg, "650 HS_DESC CREATED %s UNKNOWN " + "UNKNOWN %s\r\n", + onion_address, base64_blinded_pk); + tt_assert(received_msg); + tt_str_op(received_msg, OP_EQ, expected_msg); + tor_free(received_msg); + tor_free(expected_msg); + + /* HS_DESC UPLOAD... */ + uint8_t hsdir_index_store[DIGEST256_LEN]; + memset(hsdir_index_store, 'D', sizeof(hsdir_index_store)); + hs_control_desc_event_upload(onion_address, HSDIR_EXIST_ID, + &blinded_pk, hsdir_index_store); + tor_asprintf(&expected_msg, "650 HS_DESC UPLOAD %s UNKNOWN " + STR_HSDIR_EXIST_LONGNAME " %s " + "HSDIR_INDEX=" HSDIR_INDEX_STORE_HEX "\r\n", + onion_address, base64_blinded_pk); + tt_assert(received_msg); + tt_str_op(received_msg, OP_EQ, expected_msg); + tor_free(received_msg); + tor_free(expected_msg); + + /* HS_DESC FAILED... */ + hs_control_desc_event_failed(&ident, HSDIR_EXIST_ID, "BAD_DESC"); + tor_asprintf(&expected_msg, "650 HS_DESC FAILED %s NO_AUTH " + STR_HSDIR_EXIST_LONGNAME " %s " + "REASON=BAD_DESC\r\n", + onion_address, base64_blinded_pk); + tt_assert(received_msg); + tt_str_op(received_msg, OP_EQ, expected_msg); + tor_free(received_msg); + tor_free(expected_msg); + + /* HS_DESC RECEIVED... */ + hs_control_desc_event_received(&ident, HSDIR_EXIST_ID); + tor_asprintf(&expected_msg, "650 HS_DESC RECEIVED %s NO_AUTH " + STR_HSDIR_EXIST_LONGNAME " %s\r\n", + onion_address, base64_blinded_pk); + tt_assert(received_msg); + tt_str_op(received_msg, OP_EQ, expected_msg); + tor_free(received_msg); + tor_free(expected_msg); + + /* HS_DESC UPLOADED... */ + hs_control_desc_event_uploaded(&ident, HSDIR_EXIST_ID); + tor_asprintf(&expected_msg, "650 HS_DESC UPLOADED %s UNKNOWN " + STR_HSDIR_EXIST_LONGNAME "\r\n", + onion_address); + tt_assert(received_msg); + tt_str_op(received_msg, OP_EQ, expected_msg); + tor_free(received_msg); + tor_free(expected_msg); + + done: + UNMOCK(queue_control_event_string); + UNMOCK(node_describe_longname_by_id); + UNMOCK(node_get_by_id); + tor_free(received_msg); + tor_free(expected_msg); +} + +struct testcase_t hs_control_tests[] = { + { "hs_desc_event", test_hs_desc_event, TT_FORK, + NULL, NULL }, + + END_OF_TESTCASES +}; + diff --git a/src/test/test_hs_intropoint.c b/src/test/test_hs_intropoint.c index 0cae2de7e1..6a7962b212 100644 --- a/src/test/test_hs_intropoint.c +++ b/src/test/test_hs_intropoint.c @@ -194,7 +194,7 @@ test_establish_intro_wrong_purpose(void *arg) tt_int_op(retval, OP_EQ, -1); done: - circuit_free(TO_CIRCUIT(intro_circ)); + circuit_free_(TO_CIRCUIT(intro_circ)); } /* Prepare a circuit for accepting an ESTABLISH_INTRO cell */ @@ -228,7 +228,7 @@ test_establish_intro_wrong_keytype(void *arg) tt_int_op(retval, OP_EQ, -1); done: - circuit_free(TO_CIRCUIT(intro_circ)); + circuit_free_(TO_CIRCUIT(intro_circ)); } /* Send an ESTABLISH_INTRO cell with an unknown auth key type. Should fail. */ @@ -263,7 +263,7 @@ test_establish_intro_wrong_keytype2(void *arg) tt_int_op(retval, OP_EQ, -1); done: - circuit_free(TO_CIRCUIT(intro_circ)); + circuit_free_(TO_CIRCUIT(intro_circ)); } /* Send a legit ESTABLISH_INTRO cell but with a wrong MAC. Should fail. */ @@ -333,7 +333,7 @@ test_establish_intro_wrong_mac(void *arg) done: trn_cell_establish_intro_free(cell); - circuit_free(TO_CIRCUIT(intro_circ)); + circuit_free_(TO_CIRCUIT(intro_circ)); } /* Send a legit ESTABLISH_INTRO cell but with a wrong auth key length. Should @@ -378,7 +378,7 @@ test_establish_intro_wrong_auth_key_len(void *arg) done: trn_cell_establish_intro_free(cell); - circuit_free(TO_CIRCUIT(intro_circ)); + circuit_free_(TO_CIRCUIT(intro_circ)); } /* Send a legit ESTABLISH_INTRO cell but with a wrong sig length. Should @@ -423,7 +423,7 @@ test_establish_intro_wrong_sig_len(void *arg) done: trn_cell_establish_intro_free(cell); - circuit_free(TO_CIRCUIT(intro_circ)); + circuit_free_(TO_CIRCUIT(intro_circ)); } /* Send a legit ESTABLISH_INTRO cell but slightly change the signature. Should @@ -460,7 +460,7 @@ test_establish_intro_wrong_sig(void *arg) tt_int_op(retval, OP_EQ, -1); done: - circuit_free(TO_CIRCUIT(intro_circ)); + circuit_free_(TO_CIRCUIT(intro_circ)); } /* Helper function: Send a well-formed v3 ESTABLISH_INTRO cell to @@ -629,8 +629,8 @@ test_intro_point_registration(void *arg) done: crypto_pk_free(legacy_auth_key); - circuit_free(TO_CIRCUIT(intro_circ)); - circuit_free(TO_CIRCUIT(legacy_intro_circ)); + circuit_free_(TO_CIRCUIT(intro_circ)); + circuit_free_(TO_CIRCUIT(legacy_intro_circ)); trn_cell_establish_intro_free(establish_intro_cell); test_circuitmap_free_all(); @@ -650,7 +650,7 @@ test_introduce1_suitable_circuit(void *arg) circ = or_circuit_new(0, NULL); circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); ret = circuit_is_suitable_for_introduce1(circ); - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); tt_int_op(ret, OP_EQ, 1); } @@ -659,7 +659,7 @@ test_introduce1_suitable_circuit(void *arg) circ = or_circuit_new(0, NULL); circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT); ret = circuit_is_suitable_for_introduce1(circ); - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); tt_int_op(ret, OP_EQ, 0); } @@ -670,7 +670,7 @@ test_introduce1_suitable_circuit(void *arg) /* Bogus pointer, the check is against NULL on n_chan. */ circ->base_.n_chan = (channel_t *) circ; ret = circuit_is_suitable_for_introduce1(circ); - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); tt_int_op(ret, OP_EQ, 0); } @@ -681,7 +681,7 @@ test_introduce1_suitable_circuit(void *arg) circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); circ->already_received_introduce1 = 1; ret = circuit_is_suitable_for_introduce1(circ); - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); tt_int_op(ret, OP_EQ, 0); } @@ -800,7 +800,7 @@ test_received_introduce1_handling(void *arg) circ = helper_create_intro_circuit(); ret = hs_intro_received_introduce1(circ, buf, DIGEST_LEN - 1); tt_int_op(ret, OP_EQ, -1); - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); } /* We have a unit test only for the suitability of a circuit to receive an @@ -813,7 +813,7 @@ test_received_introduce1_handling(void *arg) memset(test, 0, sizeof(test)); ret = handle_introduce1(circ, test, sizeof(test)); tor_free(circ->p_chan); - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); tt_int_op(ret, OP_EQ, -1); } @@ -838,8 +838,8 @@ test_received_introduce1_handling(void *arg) memcpy(auth_key.pubkey, cell_auth_key, ED25519_PUBKEY_LEN); hs_circuitmap_register_intro_circ_v3_relay_side(service_circ, &auth_key); ret = hs_intro_received_introduce1(circ, request, request_len); - circuit_free(TO_CIRCUIT(circ)); - circuit_free(TO_CIRCUIT(service_circ)); + circuit_free_(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(service_circ)); tt_int_op(ret, OP_EQ, 0); } @@ -867,8 +867,8 @@ test_received_introduce1_handling(void *arg) memcpy(token, legacy_key_id, sizeof(token)); hs_circuitmap_register_intro_circ_v2_relay_side(service_circ, token); ret = hs_intro_received_introduce1(circ, request, request_len); - circuit_free(TO_CIRCUIT(circ)); - circuit_free(TO_CIRCUIT(service_circ)); + circuit_free_(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(service_circ)); tt_int_op(ret, OP_EQ, 0); } diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index 8407eccfa8..73200a5a63 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -182,7 +182,7 @@ test_e2e_rend_circuit_setup(void *arg) tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_S_REND_JOINED); done: - circuit_free(TO_CIRCUIT(or_circ)); + circuit_free_(TO_CIRCUIT(or_circ)); } /* Helper: Return a newly allocated and initialized origin circuit with @@ -655,7 +655,7 @@ test_intro_circuit_opened(void *arg) teardown_capture_of_logs(); done: - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); hs_free_all(); UNMOCK(circuit_mark_for_close_); UNMOCK(relay_send_command_from_edge_); @@ -730,7 +730,7 @@ test_intro_established(void *arg) done: if (circ) - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); hs_free_all(); UNMOCK(circuit_mark_for_close_); } @@ -772,7 +772,7 @@ test_rdv_circuit_opened(void *arg) tt_int_op(TO_CIRCUIT(circ)->purpose, OP_EQ, CIRCUIT_PURPOSE_S_REND_JOINED); done: - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); hs_free_all(); UNMOCK(circuit_mark_for_close_); UNMOCK(relay_send_command_from_edge_); @@ -825,7 +825,7 @@ test_closing_intro_circs(void *arg) /* Now pretend we are freeing this intro circuit. We want to see that our * destructor is not gonna kill our intro point structure since that's the * job of the cleanup routine. */ - circuit_free(TO_CIRCUIT(intro_circ)); + circuit_free_(TO_CIRCUIT(intro_circ)); intro_circ = NULL; entry = service_intro_point_find(service, &ip->auth_key_kp.pubkey); tt_assert(entry); @@ -857,7 +857,7 @@ test_closing_intro_circs(void *arg) done: if (intro_circ) { - circuit_free(TO_CIRCUIT(intro_circ)); + circuit_free_(TO_CIRCUIT(intro_circ)); } /* Frees the service object. */ hs_free_all(); @@ -938,7 +938,7 @@ test_introduce2(void *arg) or_state_free(dummy_state); dummy_state = NULL; if (circ) - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); hs_free_all(); UNMOCK(circuit_mark_for_close_); } @@ -1022,7 +1022,7 @@ test_service_event(void *arg) done: hs_circuitmap_remove_circuit(TO_CIRCUIT(circ)); - circuit_free(TO_CIRCUIT(circ)); + circuit_free_(TO_CIRCUIT(circ)); hs_free_all(); UNMOCK(circuit_mark_for_close_); } @@ -1576,8 +1576,8 @@ test_rendezvous1_parsing(void *arg) * would need an extra circuit and some more stuff but it's doable. */ done: - circuit_free(TO_CIRCUIT(service_circ)); - circuit_free(TO_CIRCUIT(client_circ)); + circuit_free_(TO_CIRCUIT(service_circ)); + circuit_free_(TO_CIRCUIT(client_circ)); hs_service_free(service); hs_free_all(); UNMOCK(relay_send_command_from_edge_); diff --git a/src/test/test_keygen.sh b/src/test/test_keygen.sh index 87012cd283..b3d4d8e39a 100755 --- a/src/test/test_keygen.sh +++ b/src/test/test_keygen.sh @@ -40,6 +40,12 @@ fi CASE8=$dflt CASE9=$dflt CASE10=$dflt + CASE11A=$dflt + CASE11B=$dflt + CASE11C=$dflt + CASE11D=$dflt + CASE11E=$dflt + CASE11F=$dflt if [ $# -ge 1 ]; then eval "CASE${1}"=1 @@ -363,6 +369,109 @@ echo "==== Case 10 ok" fi +# Case 11a: -passphrase-fd without --keygen + +if [ "$CASE11A" = 1 ]; then + +ME="${DATA_DIR}/case11a" + +mkdir -p "${ME}/keys" + +${TOR} --DataDirectory "${ME}" --passphrase-fd 1 > "${ME}/stdout" && die "Successfully started with passphrase-fd but no keygen?" || true + +grep "passphrase-fd specified without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments." + +echo "==== Case 11A ok" + +fi + +# Case 11b: --no-passphrase without --keygen + +if [ "$CASE11B" = 1 ]; then + +ME="${DATA_DIR}/case11b" + +mkdir -p "${ME}/keys" + +${TOR} --DataDirectory "${ME}" --no-passphrase > "${ME}/stdout" && die "Successfully started with no-passphrase but no keygen?" || true + +grep "no-passphrase specified without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments." + +echo "==== Case 11B ok" + +fi + +# Case 11c: --newpass without --keygen + +if [ "$CASE11C" = 1 ]; then + +ME="${DATA_DIR}/case11C" + +mkdir -p "${ME}/keys" + +${TOR} --DataDirectory "${ME}" --newpass > "${ME}/stdout" && die "Successfully started with newpass but no keygen?" || true + +grep "newpass specified without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments." + +echo "==== Case 11C ok" + +fi + +######## --master-key does not work yet, but this will test the error case +######## when it does. +# +# Case 11d: --master-key without --keygen +# +if [ "$CASE11D" = 1 ]; then +# +# ME="${DATA_DIR}/case11d" +# +# mkdir -p "${ME}/keys" +# +# ${TOR} --DataDirectory "${ME}" --master-key "${ME}/foobar" > "${ME}/stdout" && die "Successfully started with master-key but no keygen?" || true +# +# cat "${ME}/stdout" +# +# grep "master-key without --keygen" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments." + + echo "==== Case 11D skipped" + +fi + + +# Case 11E: Silly passphrase-fd + +if [ "$CASE11E" = 1 ]; then + +ME="${DATA_DIR}/case11E" + +mkdir -p "${ME}/keys" + +${TOR} --DataDirectory "${ME}" --keygen --passphrase-fd ewigeblumenkraft > "${ME}/stdout" && die "Successfully started with bogus passphrase-fd?" || true + +grep "Invalid --passphrase-fd value" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments." + +echo "==== Case 11E ok" + +fi + + +# Case 11F: --no-passphrase with --passphrase-fd + +if [ "$CASE11F" = 1 ]; then + +ME="${DATA_DIR}/case11F" + +mkdir -p "${ME}/keys" + +${TOR} --DataDirectory "${ME}" --keygen --passphrase-fd 1 --no-passphrase > "${ME}/stdout" && die "Successfully started with bogus passphrase-fd combination?" || true + +grep "no-passphrase specified with --passphrase-fd" "${ME}/stdout" >/dev/null || die "Tor didn't declare that there was a problem with the arguments." + +echo "==== Case 11F ok" + +fi + # Check cert-only. diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c index 422d419078..6840072d76 100644 --- a/src/test/test_link_handshake.c +++ b/src/test/test_link_handshake.c @@ -302,8 +302,8 @@ test_link_handshake_certs_ok(void *arg) mock_own_cert = mock_peer_cert = NULL; memset(c1->identity_digest, 0, sizeof(c1->identity_digest)); memset(c2->identity_digest, 0, sizeof(c2->identity_digest)); - connection_free_(TO_CONN(c1)); - connection_free_(TO_CONN(c2)); + connection_free_minimal(TO_CONN(c1)); + connection_free_minimal(TO_CONN(c2)); tor_free(cell1); tor_free(cell2); certs_cell_free(cc1); @@ -343,7 +343,7 @@ recv_certs_cleanup(const struct testcase_t *test, void *obj) tor_free(d->cell); certs_cell_free(d->ccell); connection_or_clear_identity(d->c); - connection_free_(TO_CONN(d->c)); + connection_free_minimal(TO_CONN(d->c)); circuitmux_free(d->chan->base_.cmux); tor_free(d->chan); crypto_pk_free(d->key1); @@ -930,7 +930,7 @@ test_link_handshake_send_authchallenge(void *arg) done: UNMOCK(connection_or_write_var_cell_to_buf); - connection_free_(TO_CONN(c1)); + connection_free_minimal(TO_CONN(c1)); tor_free(cell1); tor_free(cell2); crypto_pk_free(rsa0); @@ -955,7 +955,7 @@ recv_authchallenge_cleanup(const struct testcase_t *test, void *obj) if (d) { tor_free(d->cell); - connection_free_(TO_CONN(d->c)); + connection_free_minimal(TO_CONN(d->c)); circuitmux_free(d->chan->base_.cmux); tor_free(d->chan); tor_free(d); @@ -1158,8 +1158,8 @@ authenticate_data_cleanup(const struct testcase_t *test, void *arg) tor_free(d->cell); connection_or_clear_identity(d->c1); connection_or_clear_identity(d->c2); - connection_free_(TO_CONN(d->c1)); - connection_free_(TO_CONN(d->c2)); + connection_free_minimal(TO_CONN(d->c1)); + connection_free_minimal(TO_CONN(d->c2)); circuitmux_free(d->chan2->base_.cmux); tor_free(d->chan2); crypto_pk_free(d->key1); diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c index 4f0ecd778b..59b28f7580 100644 --- a/src/test/test_microdesc.c +++ b/src/test/test_microdesc.c @@ -69,12 +69,12 @@ test_md_cache(void *data) time3 = time(NULL) - 15*24*60*60; /* Possibly, turn this into a test setup/cleanup pair */ - tor_free(options->DataDirectory); - options->DataDirectory = tor_strdup(get_fname("md_datadir_test")); + tor_free(options->CacheDirectory); + options->CacheDirectory = tor_strdup(get_fname("md_datadir_test")); #ifdef _WIN32 - tt_int_op(0, OP_EQ, mkdir(options->DataDirectory)); + tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory)); #else - tt_int_op(0, OP_EQ, mkdir(options->DataDirectory, 0700)); + tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory, 0700)); #endif tt_assert(!strcmpstart(test_md3_noannotation, "onion-key")); @@ -152,7 +152,7 @@ test_md_cache(void *data) strlen(test_md3_noannotation)); tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs.new", - options->DataDirectory); + options->CacheDirectory); s = read_file_to_str(fn, RFTS_BIN, NULL); tt_assert(s); tt_mem_op(md1->body, OP_EQ, s + md1->off, md1->bodylen); @@ -180,7 +180,7 @@ test_md_cache(void *data) /* read the cache. */ tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs", - options->DataDirectory); + options->CacheDirectory); s = read_file_to_str(fn, RFTS_BIN, NULL); tt_mem_op(md1->body, OP_EQ, s + md1->off, strlen(test_md1)); tt_mem_op(md2->body, OP_EQ, s + md2->off, strlen(test_md2)); @@ -234,7 +234,7 @@ test_md_cache(void *data) done: if (options) - tor_free(options->DataDirectory); + tor_free(options->CacheDirectory); microdesc_free_all(); smartlist_free(added); @@ -266,17 +266,17 @@ test_md_cache_broken(void *data) options = get_options_mutable(); tt_assert(options); - tor_free(options->DataDirectory); - options->DataDirectory = tor_strdup(get_fname("md_datadir_test2")); + tor_free(options->CacheDirectory); + options->CacheDirectory = tor_strdup(get_fname("md_datadir_test2")); #ifdef _WIN32 - tt_int_op(0, OP_EQ, mkdir(options->DataDirectory)); + tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory)); #else - tt_int_op(0, OP_EQ, mkdir(options->DataDirectory, 0700)); + tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory, 0700)); #endif tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs", - options->DataDirectory); + options->CacheDirectory); write_str_to_file(fn, truncated_md, 1); @@ -285,7 +285,7 @@ test_md_cache_broken(void *data) done: if (options) - tor_free(options->DataDirectory); + tor_free(options->CacheDirectory); tor_free(fn); microdesc_free_all(); } @@ -754,8 +754,8 @@ test_md_reject_cache(void *arg) or_options_t *options = get_options_mutable(); char buf[DIGEST256_LEN]; - tor_free(options->DataDirectory); - options->DataDirectory = tor_strdup(get_fname("md_datadir_test_rej")); + tor_free(options->CacheDirectory); + options->CacheDirectory = tor_strdup(get_fname("md_datadir_test_rej")); mock_rgsbd_val_a = tor_malloc_zero(sizeof(routerstatus_t)); mock_rgsbd_val_b = tor_malloc_zero(sizeof(routerstatus_t)); mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t)); @@ -765,9 +765,9 @@ test_md_reject_cache(void *arg) mock_ns_val->flavor = FLAV_MICRODESC; #ifdef _WIN32 - tt_int_op(0, OP_EQ, mkdir(options->DataDirectory)); + tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory)); #else - tt_int_op(0, OP_EQ, mkdir(options->DataDirectory, 0700)); + tt_int_op(0, OP_EQ, mkdir(options->CacheDirectory, 0700)); #endif MOCK(router_get_mutable_consensus_status_by_descriptor_digest, @@ -802,7 +802,7 @@ test_md_reject_cache(void *arg) done: UNMOCK(networkstatus_get_latest_consensus_by_flavor); UNMOCK(router_get_mutable_consensus_status_by_descriptor_digest); - tor_free(options->DataDirectory); + tor_free(options->CacheDirectory); microdesc_free_all(); smartlist_free(added); SMARTLIST_FOREACH(wanted, char *, cp, tor_free(cp)); diff --git a/src/test/test_oom.c b/src/test/test_oom.c index cf28690a28..c172fe60c7 100644 --- a/src/test/test_oom.c +++ b/src/test/test_oom.c @@ -202,7 +202,7 @@ test_oom_streambuf(void *arg) { or_options_t *options = get_options_mutable(); circuit_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *c4 = NULL, *c5 = NULL; - uint32_t tvms; + uint32_t tvts; int i; smartlist_t *edgeconns = smartlist_new(); const uint64_t start_ns = 1389641159 * (uint64_t)1000000000; @@ -270,22 +270,28 @@ test_oom_streambuf(void *arg) now_ns -= now_ns % 1000000000; now_ns += 1000000000; monotime_coarse_set_mock_time_nsec(now_ns); - tvms = (uint32_t) monotime_coarse_absolute_msec(); + tvts = monotime_coarse_get_stamp(); - tt_int_op(circuit_max_queued_cell_age(c1, tvms), OP_EQ, 500); - tt_int_op(circuit_max_queued_cell_age(c2, tvms), OP_EQ, 490); - tt_int_op(circuit_max_queued_cell_age(c3, tvms), OP_EQ, 480); - tt_int_op(circuit_max_queued_cell_age(c4, tvms), OP_EQ, 0); +#define ts_is_approx(ts, val) do { \ + uint32_t x_ = (uint32_t) monotime_coarse_stamp_units_to_approx_msec(ts); \ + tt_int_op(x_, OP_GE, val - 5); \ + tt_int_op(x_, OP_LE, val + 5); \ + } while (0) - tt_int_op(circuit_max_queued_data_age(c1, tvms), OP_EQ, 390); - tt_int_op(circuit_max_queued_data_age(c2, tvms), OP_EQ, 380); - tt_int_op(circuit_max_queued_data_age(c3, tvms), OP_EQ, 0); - tt_int_op(circuit_max_queued_data_age(c4, tvms), OP_EQ, 370); + ts_is_approx(circuit_max_queued_cell_age(c1, tvts), 500); + ts_is_approx(circuit_max_queued_cell_age(c2, tvts), 490); + ts_is_approx(circuit_max_queued_cell_age(c3, tvts), 480); + ts_is_approx(circuit_max_queued_cell_age(c4, tvts), 0); - tt_int_op(circuit_max_queued_item_age(c1, tvms), OP_EQ, 500); - tt_int_op(circuit_max_queued_item_age(c2, tvms), OP_EQ, 490); - tt_int_op(circuit_max_queued_item_age(c3, tvms), OP_EQ, 480); - tt_int_op(circuit_max_queued_item_age(c4, tvms), OP_EQ, 370); + ts_is_approx(circuit_max_queued_data_age(c1, tvts), 390); + ts_is_approx(circuit_max_queued_data_age(c2, tvts), 380); + ts_is_approx(circuit_max_queued_data_age(c3, tvts), 0); + ts_is_approx(circuit_max_queued_data_age(c4, tvts), 370); + + ts_is_approx(circuit_max_queued_item_age(c1, tvts), 500); + ts_is_approx(circuit_max_queued_item_age(c2, tvts), 490); + ts_is_approx(circuit_max_queued_item_age(c3, tvts), 480); + ts_is_approx(circuit_max_queued_item_age(c4, tvts), 370); tt_int_op(cell_queues_get_total_allocation(), OP_EQ, packed_cell_mem_cost() * 80); @@ -301,7 +307,7 @@ test_oom_streambuf(void *arg) smartlist_add(edgeconns, ec); } tt_int_op(buf_get_total_allocation(), OP_EQ, 4096*17*2); - tt_int_op(circuit_max_queued_item_age(c4, tvms), OP_EQ, 1000); + ts_is_approx(circuit_max_queued_item_age(c4, tvts), 1000); tt_int_op(cell_queues_check_size(), OP_EQ, 0); @@ -335,7 +341,7 @@ test_oom_streambuf(void *arg) circuit_free(c5); SMARTLIST_FOREACH(edgeconns, edge_connection_t *, ec, - connection_free_(TO_CONN(ec))); + connection_free_minimal(TO_CONN(ec))); smartlist_free(edgeconns); UNMOCK(circuit_mark_for_close_); diff --git a/src/test/test_policy.c b/src/test/test_policy.c index 83dca2d431..f8aa8ac40b 100644 --- a/src/test/test_policy.c +++ b/src/test/test_policy.c @@ -1318,7 +1318,7 @@ mock_get_interface_address6_list(int severity, return clone_list; done: - free_interface_address6_list(clone_list); + interface_address6_list_free(clone_list); return NULL; } @@ -1393,11 +1393,11 @@ test_policies_reject_interface_address(void *arg) done: addr_policy_list_free(policy); - free_interface_address6_list(public_ipv4_addrs); - free_interface_address6_list(public_ipv6_addrs); + interface_address6_list_free(public_ipv4_addrs); + interface_address6_list_free(public_ipv6_addrs); UNMOCK(get_interface_address6_list); - /* we don't use free_interface_address6_list on these lists because their + /* we don't use interface_address6_list_free on these lists because their * address pointers are stack-based */ smartlist_free(mock_ipv4_addrs); smartlist_free(mock_ipv6_addrs); diff --git a/src/test/test_protover.c b/src/test/test_protover.c index 6ce54890d6..6865362115 100644 --- a/src/test/test_protover.c +++ b/src/test/test_protover.c @@ -12,6 +12,13 @@ static void test_protover_parse(void *arg) { (void) arg; +#ifdef HAVE_RUST + /** This test is disabled on rust builds, because it only exists to test + * internal C functions. */ + tt_skip(); + done: + ; +#else char *re_encoded = NULL; const char *orig = "Foo=1,3 Bar=3 Baz= Quux=9-12,14,15-16,900"; @@ -78,12 +85,18 @@ test_protover_parse(void *arg) SMARTLIST_FOREACH(elts, proto_entry_t *, ent, proto_entry_free(ent)); smartlist_free(elts); tor_free(re_encoded); +#endif } static void test_protover_parse_fail(void *arg) { (void)arg; +#ifdef HAVE_RUST + /** This test is disabled on rust builds, because it only exists to test + * internal C functions. */ + tt_skip(); +#else smartlist_t *elts; /* random junk */ @@ -109,7 +122,7 @@ test_protover_parse_fail(void *arg) /* Broken range */ elts = parse_protocol_list("Link=1,9-8,3"); tt_ptr_op(elts, OP_EQ, NULL); - +#endif done: ; } @@ -182,6 +195,61 @@ test_protover_all_supported(void *arg) tor_free(msg); } +static void +test_protover_list_supports_protocol_returns_true(void *arg) +{ + (void)arg; + + const char *protocols = "Link=1"; + int is_supported = protocol_list_supports_protocol(protocols, PRT_LINK, 1); + tt_int_op(is_supported, OP_EQ, 1); + + done: + ; +} + +static void +test_protover_list_supports_protocol_for_unsupported_returns_false(void *arg) +{ + (void)arg; + + const char *protocols = "Link=1"; + int is_supported = protocol_list_supports_protocol(protocols, PRT_LINK, 10); + tt_int_op(is_supported, OP_EQ, 0); + + done: + ; +} + +static void +test_protover_supports_version(void *arg) +{ + (void)arg; + + tt_assert(protocol_list_supports_protocol("Link=3-6", PRT_LINK, 3)); + tt_assert(protocol_list_supports_protocol("Link=3-6", PRT_LINK, 6)); + tt_assert(!protocol_list_supports_protocol("Link=3-6", PRT_LINK, 7)); + tt_assert(!protocol_list_supports_protocol("Link=3-6", PRT_LINKAUTH, 3)); + + tt_assert(!protocol_list_supports_protocol("Link=4-6 LinkAuth=3", + PRT_LINKAUTH, 2)); + tt_assert(protocol_list_supports_protocol("Link=4-6 LinkAuth=3", + PRT_LINKAUTH, 3)); + tt_assert(!protocol_list_supports_protocol("Link=4-6 LinkAuth=3", + PRT_LINKAUTH, 4)); + tt_assert(!protocol_list_supports_protocol_or_later("Link=4-6 LinkAuth=3", + PRT_LINKAUTH, 4)); + tt_assert(protocol_list_supports_protocol_or_later("Link=4-6 LinkAuth=3", + PRT_LINKAUTH, 3)); + tt_assert(protocol_list_supports_protocol_or_later("Link=4-6 LinkAuth=3", + PRT_LINKAUTH, 2)); + + tt_assert(!protocol_list_supports_protocol_or_later("Link=4-6 LinkAuth=3", + PRT_DESC, 2)); + done: + ; +} + #define PV_TEST(name, flags) \ { #name, test_protover_ ##name, (flags), NULL, NULL } @@ -190,6 +258,9 @@ struct testcase_t protover_tests[] = { PV_TEST(parse_fail, 0), PV_TEST(vote, 0), PV_TEST(all_supported, 0), + PV_TEST(list_supports_protocol_for_unsupported_returns_false, 0), + PV_TEST(list_supports_protocol_returns_true, 0), + PV_TEST(supports_version, 0), END_OF_TESTCASES }; diff --git a/src/test/test_relay.c b/src/test/test_relay.c index e3489627a0..73c0ed5586 100644 --- a/src/test/test_relay.c +++ b/src/test/test_relay.c @@ -67,10 +67,6 @@ test_relay_append_cell_to_circuit_queue(void *arg) pchan = new_fake_channel(); tt_assert(pchan); - /* We'll need chans with working cmuxes */ - nchan->cmux = circuitmux_alloc(); - pchan->cmux = circuitmux_alloc(); - /* Make a fake orcirc */ orcirc = new_fake_orcirc(nchan, pchan); tt_assert(orcirc); diff --git a/src/test/test_rendcache.c b/src/test/test_rendcache.c index 9354dd0480..9f6cfc4a22 100644 --- a/src/test/test_rendcache.c +++ b/src/test/test_rendcache.c @@ -834,7 +834,7 @@ test_rend_cache_failure_entry_free(void *data) (void)data; // Test that it can deal with a NULL argument - rend_cache_failure_entry_free(NULL); + rend_cache_failure_entry_free_(NULL); /* done: */ /* (void)0; */ @@ -963,7 +963,7 @@ test_rend_cache_entry_free(void *data) rend_cache_entry_t *e; // Handles NULL correctly - rend_cache_entry_free(NULL); + rend_cache_entry_free_(NULL); // Handles NULL descriptor correctly e = tor_malloc_zero(sizeof(rend_cache_entry_t)); @@ -1135,7 +1135,7 @@ test_rend_cache_failure_intro_entry_free(void *data) rend_cache_failure_intro_t *entry; // Handles a null argument - rend_cache_failure_intro_entry_free(NULL); + rend_cache_failure_intro_entry_free_(NULL); // Handles a non-null argument entry = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT); @@ -1148,7 +1148,7 @@ test_rend_cache_failure_purge(void *data) (void)data; // Handles a null failure cache - strmap_free(rend_cache_failure, rend_cache_failure_entry_free_); + strmap_free(rend_cache_failure, rend_cache_failure_entry_free_void); rend_cache_failure = NULL; rend_cache_failure_purge(); diff --git a/src/test/test_replay.c b/src/test/test_replay.c index c379cafa7c..d8dcc7370c 100644 --- a/src/test/test_replay.c +++ b/src/test/test_replay.c @@ -74,7 +74,7 @@ static void test_replaycache_free_null(void *arg) { (void)arg; - replaycache_free(NULL); + replaycache_free_(NULL); /* Assert that we're here without horrible death */ tt_assert(1); diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c index d8b72651a0..e4abcdb92d 100644 --- a/src/test/test_routerkeys.c +++ b/src/test/test_routerkeys.c @@ -421,6 +421,7 @@ test_routerkeys_ed_keys_init_all(void *arg) { (void)arg; char *dir = tor_strdup(get_fname("test_ed_keys_init_all")); + char *keydir = tor_strdup(get_fname("test_ed_keys_init_all/KEYS")); or_options_t *options = tor_malloc_zero(sizeof(or_options_t)); time_t now = time(NULL); ed25519_public_key_t id; @@ -445,13 +446,14 @@ test_routerkeys_ed_keys_init_all(void *arg) #ifdef _WIN32 mkdir(dir); - mkdir(get_fname("test_ed_keys_init_all/keys")); + mkdir(keydir); #else mkdir(dir, 0700); - mkdir(get_fname("test_ed_keys_init_all/keys"), 0700); + mkdir(keydir, 0700); #endif /* defined(_WIN32) */ options->DataDirectory = dir; + options->KeyDirectory = keydir; tt_int_op(1, OP_EQ, load_ed_keys(options, now)); tt_int_op(0, OP_EQ, generate_ed_link_cert(options, now, 0)); @@ -521,7 +523,7 @@ test_routerkeys_ed_keys_init_all(void *arg) /* Demonstrate that we can start up with no secret identity key */ routerkeys_free_all(); - unlink(get_fname("test_ed_keys_init_all/keys/" + unlink(get_fname("test_ed_keys_init_all/KEYS/" "ed25519_master_id_secret_key")); tt_int_op(1, OP_EQ, load_ed_keys(options, now)); tt_int_op(0, OP_EQ, generate_ed_link_cert(options, now, 0)); @@ -542,6 +544,7 @@ test_routerkeys_ed_keys_init_all(void *arg) done: tor_free(dir); + tor_free(keydir); tor_free(options); tor_cert_free(link_cert); routerkeys_free_all(); diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c index 3b0e943ce5..c19d66ef9d 100644 --- a/src/test/test_routerlist.c +++ b/src/test/test_routerlist.c @@ -5,7 +5,11 @@ #include <math.h> #include <time.h> +#define CONNECTION_PRIVATE +#define DIRECTORY_PRIVATE #define DIRVOTE_PRIVATE +#define ENTRYNODES_PRIVATE +#define HIBERNATE_PRIVATE #define NETWORKSTATUS_PRIVATE #define ROUTERLIST_PRIVATE #define TOR_UNIT_TESTING @@ -13,19 +17,24 @@ #include "config.h" #include "connection.h" #include "container.h" +#include "control.h" #include "directory.h" #include "dirvote.h" #include "entrynodes.h" +#include "hibernate.h" #include "microdesc.h" #include "networkstatus.h" #include "nodelist.h" #include "policies.h" #include "router.h" #include "routerlist.h" +#include "routerset.h" #include "routerparse.h" #include "shared_random.h" +#include "statefile.h" #include "test.h" #include "test_dir_common.h" +#include "log_test_helpers.h" void construct_consensus(char **consensus_text_md); @@ -411,6 +420,111 @@ test_router_pick_directory_server_impl(void *arg) networkstatus_vote_free(con_md); } +static or_state_t *dummy_state = NULL; +static or_state_t * +get_or_state_replacement(void) +{ + return dummy_state; +} + +static void +mock_directory_initiate_request(directory_request_t *req) +{ + (void)req; + return; +} + +static circuit_guard_state_t * +mock_circuit_guard_state_new(entry_guard_t *guard, unsigned state, + entry_guard_restriction_t *rst) +{ + (void) guard; + (void) state; + (void) rst; + return NULL; +} + +/** Test that we will use our directory guards to fetch mds even if we don't + * have any dirinfo (tests bug #23862). */ +static void +test_directory_guard_fetch_with_no_dirinfo(void *arg) +{ + int retval; + char *consensus_text_md = NULL; + or_options_t *options = get_options_mutable(); + + (void) arg; + + hibernate_set_state_for_testing_(HIBERNATE_STATE_LIVE); + + /* Initialize the SRV subsystem */ + MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m); + mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL); + sr_init(0); + UNMOCK(get_my_v3_authority_cert); + + /* Initialize the entry node configuration from the ticket */ + options->UseEntryGuards = 1; + options->StrictNodes = 1; + get_options_mutable()->EntryNodes = routerset_new(); + routerset_parse(get_options_mutable()->EntryNodes, + "2121212121212121212121212121212121212121", "foo"); + + /* Mock some functions */ + dummy_state = tor_malloc_zero(sizeof(or_state_t)); + MOCK(get_or_state, get_or_state_replacement); + MOCK(directory_initiate_request, mock_directory_initiate_request); + /* we need to mock this one to avoid memleaks */ + MOCK(circuit_guard_state_new, mock_circuit_guard_state_new); + + /* Call guards_update_all() to simulate loading our state file (see + * entry_guards_load_guards_from_state() and ticket #23989). */ + guards_update_all(); + + /* Test logic: Simulate the arrival of a new consensus when we have no + * dirinfo at all. Tor will need to fetch the mds from the consensus. Make + * sure that Tor will use the specified entry guard instead of relying on the + * fallback directories. */ + + /* Fixup the dirconn that will deliver the consensus */ + dir_connection_t *conn = dir_connection_new(AF_INET); + tor_addr_from_ipv4h(&conn->base_.addr, 0x7f000001); + conn->base_.port = 8800; + TO_CONN(conn)->address = tor_strdup("127.0.0.1"); + conn->base_.purpose = DIR_PURPOSE_FETCH_CONSENSUS; + conn->requested_resource = tor_strdup("ns"); + + /* Construct a consensus */ + construct_consensus(&consensus_text_md); + tt_assert(consensus_text_md); + + /* Place the consensus in the dirconn */ + response_handler_args_t args; + memset(&args, 0, sizeof(response_handler_args_t)); + args.status_code = 200; + args.body = consensus_text_md; + args.body_len = strlen(consensus_text_md); + + /* Update approx time so that the consensus is considered live */ + update_approx_time(time(NULL)+1010); + + setup_capture_of_logs(LOG_DEBUG); + + /* Now handle the consensus */ + retval = handle_response_fetch_consensus(conn, &args); + tt_int_op(retval, OP_EQ, 0); + + /* Make sure that our primary guard was chosen */ + expect_log_msg_containing("Selected primary guard router3"); + + done: + tor_free(consensus_text_md); + tor_free(dummy_state); + connection_free_minimal(TO_CONN(conn)); + entry_guards_free_all(); + teardown_capture_of_logs(); +} + static connection_t *mocked_connection = NULL; /* Mock connection_get_by_type_addr_port_purpose by returning @@ -494,6 +608,8 @@ struct testcase_t routerlist_tests[] = { NODE(launch_descriptor_downloads, 0), NODE(router_is_already_dir_fetching, TT_FORK), ROUTER(pick_directory_server_impl, TT_FORK), + { "directory_guard_fetch_with_no_dirinfo", + test_directory_guard_fetch_with_no_dirinfo, TT_FORK, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_routerset.c b/src/test/test_routerset.c index c9c69911da..c541324674 100644 --- a/src/test/test_routerset.c +++ b/src/test/test_routerset.c @@ -1,3 +1,6 @@ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + #define ROUTERSET_PRIVATE #include "or.h" @@ -696,7 +699,7 @@ NS(test_main)(void *arg) static void NS(test_main)(void *arg) { - const routerset_t *set; + routerset_t *set; int needs_geoip; (void)arg; @@ -706,14 +709,14 @@ NS(test_main)(void *arg) set = routerset_new(); needs_geoip = routerset_needs_geoip(set); - routerset_free((routerset_t *)set); + routerset_free(set); tt_int_op(needs_geoip, OP_EQ, 0); set = NULL; set = routerset_new(); smartlist_add(set->country_names, tor_strndup("xx", 2)); needs_geoip = routerset_needs_geoip(set); - routerset_free((routerset_t *)set); + routerset_free(set); set = NULL; tt_int_op(needs_geoip, OP_NE, 0); @@ -1944,7 +1947,7 @@ NS(test_main)(void *arg) done: tor_free(s); - routerset_free((routerset_t *)set); + routerset_free(set); } #undef NS_SUBMODULE @@ -2081,28 +2084,28 @@ NS(test_main)(void *arg) * Structural test for routerset_free, where the routerset is NULL. */ -NS_DECL(void, smartlist_free, (smartlist_t *sl)); +NS_DECL(void, smartlist_free_, (smartlist_t *sl)); static void NS(test_main)(void *arg) { (void)arg; - NS_MOCK(smartlist_free); + NS_MOCK(smartlist_free_); - routerset_free(NULL); + routerset_free_(NULL); - tt_int_op(CALLED(smartlist_free), OP_EQ, 0); + tt_int_op(CALLED(smartlist_free_), OP_EQ, 0); done: ; } void -NS(smartlist_free)(smartlist_t *s) +NS(smartlist_free_)(smartlist_t *s) { (void)s; - CALLED(smartlist_free)++; + CALLED(smartlist_free_)++; } #undef NS_SUBMODULE @@ -2112,9 +2115,9 @@ NS(smartlist_free)(smartlist_t *s) * Structural test for routerset_free. */ -NS_DECL(void, smartlist_free, (smartlist_t *sl)); -NS_DECL(void, strmap_free,(strmap_t *map, void (*free_val)(void*))); -NS_DECL(void, digestmap_free, (digestmap_t *map, void (*free_val)(void*))); +NS_DECL(void, smartlist_free_, (smartlist_t *sl)); +NS_DECL(void, strmap_free_,(strmap_t *map, void (*free_val)(void*))); +NS_DECL(void, digestmap_free_, (digestmap_t *map, void (*free_val)(void*))); static void NS(test_main)(void *arg) @@ -2122,39 +2125,39 @@ NS(test_main)(void *arg) routerset_t *routerset = routerset_new(); (void)arg; - NS_MOCK(smartlist_free); - NS_MOCK(strmap_free); - NS_MOCK(digestmap_free); + NS_MOCK(smartlist_free_); + NS_MOCK(strmap_free_); + NS_MOCK(digestmap_free_); routerset_free(routerset); - tt_int_op(CALLED(smartlist_free), OP_NE, 0); - tt_int_op(CALLED(strmap_free), OP_NE, 0); - tt_int_op(CALLED(digestmap_free), OP_NE, 0); + tt_int_op(CALLED(smartlist_free_), OP_NE, 0); + tt_int_op(CALLED(strmap_free_), OP_NE, 0); + tt_int_op(CALLED(digestmap_free_), OP_NE, 0); done: ; } void -NS(smartlist_free)(smartlist_t *s) +NS(smartlist_free_)(smartlist_t *s) { - CALLED(smartlist_free)++; - smartlist_free__real(s); + CALLED(smartlist_free_)++; + smartlist_free___real(s); } void -NS(strmap_free)(strmap_t *map, void (*free_val)(void*)) +NS(strmap_free_)(strmap_t *map, void (*free_val)(void*)) { - CALLED(strmap_free)++; - strmap_free__real(map, free_val); + CALLED(strmap_free_)++; + strmap_free___real(map, free_val); } void -NS(digestmap_free)(digestmap_t *map, void (*free_val)(void*)) +NS(digestmap_free_)(digestmap_t *map, void (*free_val)(void*)) { - CALLED(digestmap_free)++; - digestmap_free__real(map, free_val); + CALLED(digestmap_free_)++; + digestmap_free___real(map, free_val); } #undef NS_SUBMODULE diff --git a/src/test/test_rust.c b/src/test/test_rust.c deleted file mode 100644 index 6ad57d6fcb..0000000000 --- a/src/test/test_rust.c +++ /dev/null @@ -1,31 +0,0 @@ -/* Copyright (c) 2017, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -#include "orconfig.h" -#include "compat_rust.h" -#include "test.h" -#include "util.h" - -static void -test_welcome_string(void *arg) -{ - (void)arg; - rust_str_t s = rust_welcome_string(); - const char *c_str = rust_str_get(s); - tt_assert(c_str); - size_t len = strlen(c_str); -#ifdef HAVE_RUST - tt_assert(len > 0); -#else - tt_assert(len == 0); -#endif - - done: - rust_str_free(s); -} - -struct testcase_t rust_tests[] = { - { "welcome_string", test_welcome_string, 0, NULL, NULL }, - END_OF_TESTCASES -}; - diff --git a/src/test/test_rust.sh b/src/test/test_rust.sh index d559f94ce0..133f2bb940 100755 --- a/src/test/test_rust.sh +++ b/src/test/test_rust.sh @@ -1,13 +1,20 @@ #!/bin/sh -# Test all the Rust crates we're using +# Test all Rust crates -crates=tor_util +crates="protover tor_util smartlist tor_allocate" exitcode=0 +set -e + for crate in $crates; do - cd "${abs_top_srcdir:-.}/src/rust/${crate}" - CARGO_TARGET_DIR="${abs_top_builddir}/src/rust/target" CARGO_HOME="${abs_top_builddir}/src/rust" "${CARGO:-cargo}" test ${CARGO_ONLINE-"--frozen"} || exitcode=1 + cd "${abs_top_builddir:-../../..}/src/rust" + CARGO_TARGET_DIR="${abs_top_builddir:-../../..}/src/rust/target" \ + CARGO_HOME="${abs_top_builddir:-../../..}/src/rust" \ + "${CARGO:-cargo}" test ${CARGO_ONLINE-"--frozen"} \ + --manifest-path "${abs_top_srcdir:-.}/src/rust/${crate}/Cargo.toml" \ + || exitcode=1 + cd - done exit $exitcode diff --git a/src/test/test_scheduler.c b/src/test/test_scheduler.c index 63add2f382..0d8a9eaa1f 100644 --- a/src/test/test_scheduler.c +++ b/src/test/test_scheduler.c @@ -299,10 +299,6 @@ channel_more_to_flush_mock(channel_t *chan) flush_mock_channel_t *found_mock_ch = NULL; - /* Check if we have any queued */ - if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue)) - return 1; - SMARTLIST_FOREACH_BEGIN(chans_for_flush_mock, flush_mock_channel_t *, flush_mock_ch) { @@ -444,8 +440,6 @@ perform_channel_state_tests(int KISTSchedRunInterval, int sched_type) /* Start it off in OPENING */ ch1->state = CHANNEL_STATE_OPENING; - /* We'll need a cmux */ - ch1->cmux = circuitmux_alloc(); /* Try to register it */ channel_register(ch1); tt_assert(ch1->registered); @@ -457,7 +451,6 @@ perform_channel_state_tests(int KISTSchedRunInterval, int sched_type) ch2 = new_fake_channel(); tt_assert(ch2); ch2->state = CHANNEL_STATE_OPENING; - ch2->cmux = circuitmux_alloc(); channel_register(ch2); tt_assert(ch2->registered); @@ -656,8 +649,6 @@ test_scheduler_loop_vanilla(void *arg) /* Start it off in OPENING */ ch1->state = CHANNEL_STATE_OPENING; - /* We'll need a cmux */ - ch1->cmux = circuitmux_alloc(); /* Try to register it */ channel_register(ch1); tt_assert(ch1->registered); @@ -672,7 +663,6 @@ test_scheduler_loop_vanilla(void *arg) ch2->magic = TLS_CHAN_MAGIC; tt_assert(ch2); ch2->state = CHANNEL_STATE_OPENING; - ch2->cmux = circuitmux_alloc(); channel_register(ch2); tt_assert(ch2->registered); /* @@ -824,7 +814,6 @@ test_scheduler_loop_kist(void *arg) tt_assert(ch1); ch1->magic = TLS_CHAN_MAGIC; ch1->state = CHANNEL_STATE_OPENING; - ch1->cmux = circuitmux_alloc(); channel_register(ch1); tt_assert(ch1->registered); channel_change_state_open(ch1); @@ -835,7 +824,6 @@ test_scheduler_loop_kist(void *arg) tt_assert(ch2); ch2->magic = TLS_CHAN_MAGIC; ch2->state = CHANNEL_STATE_OPENING; - ch2->cmux = circuitmux_alloc(); channel_register(ch2); tt_assert(ch2->registered); channel_change_state_open(ch2); diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c index cac78baecf..96494904e3 100644 --- a/src/test/test_shared_random.c +++ b/src/test/test_shared_random.c @@ -1,3 +1,6 @@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + #define SHARED_RANDOM_PRIVATE #define SHARED_RANDOM_STATE_PRIVATE #define CONFIG_PRIVATE @@ -1348,7 +1351,7 @@ test_state_update(void *arg) tt_assert(state->current_srv); done: - sr_state_free(); + sr_state_free_all(); UNMOCK(get_my_v3_authority_cert); } diff --git a/src/test/test_status.c b/src/test/test_status.c index f86f8e3b9e..50ea203e4d 100644 --- a/src/test/test_status.c +++ b/src/test/test_status.c @@ -1,3 +1,6 @@ +/* Copyright (c) 2014-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + #define STATUS_PRIVATE #define HIBERNATE_PRIVATE #define LOG_PRIVATE diff --git a/src/test/test_util.c b/src/test/test_util.c index 0519a4758f..1fd41a348a 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -5801,6 +5801,7 @@ test_util_monotonic_time(void *arg) monotime_coarse_t mtc1, mtc2; uint64_t nsec1, nsec2, usec1, msec1; uint64_t nsecc1, nsecc2, usecc1, msecc1; + uint32_t stamp1, stamp2; monotime_init(); @@ -5812,6 +5813,7 @@ test_util_monotonic_time(void *arg) nsecc1 = monotime_coarse_absolute_nsec(); usecc1 = monotime_coarse_absolute_usec(); msecc1 = monotime_coarse_absolute_msec(); + stamp1 = monotime_coarse_to_stamp(&mtc1); tor_sleep_msec(200); @@ -5819,6 +5821,7 @@ test_util_monotonic_time(void *arg) monotime_coarse_get(&mtc2); nsec2 = monotime_absolute_nsec(); nsecc2 = monotime_coarse_absolute_nsec(); + stamp2 = monotime_coarse_to_stamp(&mtc2); /* We need to be a little careful here since we don't know the system load. */ @@ -5840,6 +5843,11 @@ test_util_monotonic_time(void *arg) tt_u64_op(msecc1, OP_LE, nsecc1 / 1000000 + 1); tt_u64_op(usecc1, OP_LE, nsecc1 / 1000 + 1000); + uint64_t coarse_stamp_diff = + monotime_coarse_stamp_units_to_approx_msec(stamp2-stamp1); + tt_u64_op(coarse_stamp_diff, OP_GE, 120); + tt_u64_op(coarse_stamp_diff, OP_LE, 1200); + done: ; } @@ -5918,6 +5926,64 @@ test_util_monotonic_time_ratchet(void *arg) } static void +test_util_monotonic_time_zero(void *arg) +{ + (void) arg; + monotime_t t1; + monotime_coarse_t ct1; + monotime_init(); + /* Check 1: The current time is not zero. */ + monotime_get(&t1); + monotime_coarse_get(&ct1); + tt_assert(!monotime_is_zero(&t1)); + tt_assert(!monotime_coarse_is_zero(&ct1)); + + /* Check 2: The _zero() makes the time zero. */ + monotime_zero(&t1); + monotime_coarse_zero(&ct1); + tt_assert(monotime_is_zero(&t1)); + tt_assert(monotime_coarse_is_zero(&ct1)); + done: + ; +} + +static void +test_util_monotonic_time_add_msec(void *arg) +{ + (void) arg; + monotime_t t1, t2; + monotime_coarse_t ct1, ct2; + monotime_init(); + + monotime_get(&t1); + monotime_coarse_get(&ct1); + + /* adding zero does nothing */ + monotime_add_msec(&t2, &t1, 0); + monotime_coarse_add_msec(&ct2, &ct1, 0); + tt_i64_op(monotime_diff_msec(&t1, &t2), OP_EQ, 0); + tt_i64_op(monotime_coarse_diff_msec(&ct1, &ct2), OP_EQ, 0); + + /* Add 1337 msec; see if the diff function agree */ + monotime_add_msec(&t2, &t1, 1337); + monotime_coarse_add_msec(&ct2, &ct1, 1337); + tt_i64_op(monotime_diff_msec(&t1, &t2), OP_EQ, 1337); + tt_i64_op(monotime_coarse_diff_msec(&ct1, &ct2), OP_EQ, 1337); + + /* Add 1337 msec twice more; make sure that any second rollover issues + * worked. */ + monotime_add_msec(&t2, &t2, 1337); + monotime_coarse_add_msec(&ct2, &ct2, 1337); + monotime_add_msec(&t2, &t2, 1337); + monotime_coarse_add_msec(&ct2, &ct2, 1337); + tt_i64_op(monotime_diff_msec(&t1, &t2), OP_EQ, 1337*3); + tt_i64_op(monotime_coarse_diff_msec(&ct1, &ct2), OP_EQ, 1337*3); + + done: + ; +} + +static void test_util_htonll(void *arg) { (void)arg; @@ -6150,6 +6216,8 @@ struct testcase_t util_tests[] = { UTIL_TEST(calloc_check, 0), UTIL_TEST(monotonic_time, 0), UTIL_TEST(monotonic_time_ratchet, TT_FORK), + UTIL_TEST(monotonic_time_zero, 0), + UTIL_TEST(monotonic_time_add_msec, 0), UTIL_TEST(htonll, 0), UTIL_TEST(get_unquoted_path, 0), END_OF_TESTCASES diff --git a/src/test/testing_common.c b/src/test/testing_common.c index 7e9c47b48d..142c681079 100644 --- a/src/test/testing_common.c +++ b/src/test/testing_common.c @@ -3,12 +3,6 @@ * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ -extern const char tor_git_revision[]; - -/* Ordinarily defined in tor_main.c; this bit is just here to provide one - * since we're not linking to tor_main.c */ -const char tor_git_revision[] = ""; - /** * \file test_common.c * \brief Common pieces to implement unit tests. @@ -299,6 +293,9 @@ main(int c, const char **v) setup_directory(); options_init(options); options->DataDirectory = tor_strdup(temp_dir); + tor_asprintf(&options->KeyDirectory, "%s"PATH_SEPARATOR"keys", + options->DataDirectory); + options->CacheDirectory = tor_strdup(temp_dir); options->EntryStatistics = 1; if (set_options(options, &errmsg) < 0) { printf("Failed to set initial options: %s\n", errmsg); diff --git a/src/tools/include.am b/src/tools/include.am index 37936c742f..92cc3f10a2 100644 --- a/src/tools/include.am +++ b/src/tools/include.am @@ -45,3 +45,8 @@ src_tools_tor_cov_gencert_LDADD = src/common/libor-testing.a \ endif EXTRA_DIST += src/tools/tor-fw-helper/README + +if BUILD_LIBTORRUNNER +noinst_LIBRARIES += src/tools/libtorrunner.a +src_tools_libtorrunner_a_SOURCES = src/tools/tor_runner.c src/or/tor_api.c +endif diff --git a/src/tools/tor_runner.c b/src/tools/tor_runner.c new file mode 100644 index 0000000000..9ed2ee5775 --- /dev/null +++ b/src/tools/tor_runner.c @@ -0,0 +1,101 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file tor_runner.c + * @brief Experimental module to emulate tor_run_main() API with fork+exec + * + * The functions here are meant to allow the application developer to + * use the tor_run_main() API without having to care whether Tor is + * running in-process or out-of-process. For in-process usage, the + * developer can link Tor as a library and call tor_run_main(); for + * out-of-process usage, the developer can link this library instead. + * + * This interface is EXPERIMENTAL; please let us know if you would like + * to depend on it. We don't know yet whether it will be reliable in + * practice. + */ + +/* NOTE: This module is supposed to work without the standard Tor utility + * functions. Don't add more dependencies! + */ + +#include "tor_api.h" +#include "tor_api_internal.h" + +#include "orconfig.h" +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <stdlib.h> +#include <string.h> + +#ifndef __GNUC__ +#define __attribute__(x) +#endif + +static void child(const tor_main_configuration_t *cfg) + __attribute__((noreturn)); + +int +tor_run_main(const tor_main_configuration_t *cfg) +{ + pid_t pid = fork(); + if (pid == 0) { + child(cfg); + exit(0); /* Unreachable */ + } + + pid_t stopped_pid; + int status = 0; + do { + stopped_pid = waitpid(pid, &status, 0); + } while (stopped_pid == -1); + + /* Note: these return values are not documented. No return value is + * documented! */ + + if (stopped_pid != pid) { + return -99999; + } + if (WIFSTOPPED(status)) { + return WEXITSTATUS(status); + } + if (WIFSIGNALED(status)) { + return -WTERMSIG(status); + } + + return -999988; +} + +/* circumlocution to avoid getting warned about calling calloc instead of + * tor_calloc. */ +#define real_calloc calloc + +static void +child(const tor_main_configuration_t *cfg) +{ + /* XXXX Close unused file descriptors. */ + + char **args = real_calloc(cfg->argc+1, sizeof(char *)); + memcpy(args, cfg->argv, cfg->argc * sizeof(char *)); + args[cfg->argc] = NULL; + + int rv = execv(BINDIR "/tor", args); + + if (rv < 0) { + exit(254); + } else { + abort(); /* Unreachable */ + } +} + diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h index 674c324f87..5e3c5d87fe 100644 --- a/src/win32/orconfig.h +++ b/src/win32/orconfig.h @@ -218,7 +218,7 @@ #define USING_TWOS_COMPLEMENT /* Version number of package */ -#define VERSION "0.3.2.8-rc" +#define VERSION "0.3.3.0-alpha-dev" |